diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a19a2decb6..4c4ad89643 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -40,13 +40,13 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Setup Java - uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4.5.0 + uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0 with: distribution: temurin java-version: '17.0.13' - name: Setup Gradle - uses: gradle/actions/setup-gradle@cc4fc85e6b35bafd578d5ffbc76a5518407e1af0 # v4.2.1 + uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2 with: cache-read-only: false @@ -75,13 +75,13 @@ jobs: node-version: 18 - name: Setup Java - uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4.5.0 + uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0 with: distribution: temurin java-version: '17.0.13' - name: Setup Gradle - uses: gradle/actions/setup-gradle@cc4fc85e6b35bafd578d5ffbc76a5518407e1af0 # v4.2.1 + uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2 - name: Install Local Node run: npm install @hashgraph/hedera-local @@ -101,11 +101,13 @@ jobs: OPERATOR_ID: '0.0.1022' HEDERA_NETWORK: 'localhost' run: | - ./gradlew -POPERATOR_ID=$OPERATOR_ID -POPERATOR_KEY=$OPERATOR_KEY -PHEDERA_NETWORK=$HEDERA_NETWORK jacocoTestReport --scan + ./gradlew -POPERATOR_ID=$OPERATOR_ID -POPERATOR_KEY=$OPERATOR_KEY -PHEDERA_NETWORK=$HEDERA_NETWORK :aggregation:testCodeCoverageReport --scan - name: Upload coverage to Codecov if: ${{ github.event_name == 'push' || (github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]') }} uses: codecov/codecov-action@5c47607acb93fed5485fdbf7232e8a31425f672a # v5.0.2 + with: + files: gradle/aggregation/build/reports/jacoco/testCodeCoverageReport/testCodeCoverageReport.xml - name: Stop the local node run: npx @hashgraph/hedera-local stop @@ -130,7 +132,7 @@ jobs: node-version: 18 - name: Setup Java - uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4.5.0 + uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0 with: distribution: temurin java-version: '17.0.13' @@ -139,7 +141,7 @@ jobs: uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3.2.2 - name: Setup Gradle - uses: gradle/actions/setup-gradle@cc4fc85e6b35bafd578d5ffbc76a5518407e1af0 # v4.2.1 + uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2 - name: Install Local Node run: npm install @hashgraph/hedera-local @@ -187,13 +189,13 @@ jobs: node-version: 18 - name: Setup Java - uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4.5.0 + uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0 with: distribution: temurin java-version: '17.0.13' - name: Setup Gradle - uses: gradle/actions/setup-gradle@cc4fc85e6b35bafd578d5ffbc76a5518407e1af0 # v4.2.1 + uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2 - name: Install Local Node run: npm install @hashgraph/hedera-local diff --git a/.github/workflows/disabled/testnet.yml b/.github/workflows/disabled/testnet.yml index fea9861da1..6901e5d212 100644 --- a/.github/workflows/disabled/testnet.yml +++ b/.github/workflows/disabled/testnet.yml @@ -93,7 +93,7 @@ jobs: OPERATOR_ID: ${{ secrets.TESTNET_OPERATOR_ID }} CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} run: | - ./gradlew -POPERATOR_ID=$OPERATOR_ID -POPERATOR_KEY=$OPERATOR_KEY -PHEDERA_NETWORK=testnet jacocoTestReport && bash <(curl -s https://codecov.io/bash) + ./gradlew -POPERATOR_ID=$OPERATOR_ID -POPERATOR_KEY=$OPERATOR_KEY -PHEDERA_NETWORK=testnet :aggregation:testCodeCoverageReport && bash <(curl -s https://codecov.io/bash) - uses: codecov/codecov-action@v1 with: token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 7c6ef0c81a..1d8d2b1032 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -33,13 +33,13 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Setup Java - uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4.5.0 + uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0 with: distribution: temurin java-version: '17.0.13' - name: Setup Gradle - uses: gradle/actions/setup-gradle@cc4fc85e6b35bafd578d5ffbc76a5518407e1af0 # v4.2.1 + uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2 - name: Compile SDK run: ./gradlew compileJava --scan diff --git a/.github/workflows/release-artifacts.yml b/.github/workflows/release-artifacts.yml index 56e3f6226a..5a9a05f3bc 100644 --- a/.github/workflows/release-artifacts.yml +++ b/.github/workflows/release-artifacts.yml @@ -49,13 +49,13 @@ jobs: run: echo "version=${{github.event.inputs.version}}" >> "${GITHUB_OUTPUT}" - name: Setup Java - uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4.5.0 + uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0 with: distribution: temurin java-version: '17.0.13' - name: Setup Gradle - uses: gradle/actions/setup-gradle@cc4fc85e6b35bafd578d5ffbc76a5518407e1af0 # v4.2.1 + uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2 with: cache-read-only: false @@ -111,13 +111,13 @@ jobs: git_tag_gpgsign: true - name: Setup Java - uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4.5.0 + uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0 with: distribution: temurin java-version: '17.0.13' - name: Setup Gradle - uses: gradle/actions/setup-gradle@cc4fc85e6b35bafd578d5ffbc76a5518407e1af0 # v4.2.1 + uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2 with: cache-read-only: false @@ -129,4 +129,4 @@ jobs: env: NEXUS_USERNAME: ${{ secrets.SONATYPE_USERNAME }} NEXUS_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} - run: ./gradlew releaseMavenCentral -PpublishingPackageGroup=com.hedera.hashgraph -PpublishSigningEnabled=true --scan --no-configuration-cache + run: ./gradlew releaseMavenCentral -PpublishingPackageGroup=org.hiero -PpublishSigningEnabled=true --scan --no-configuration-cache diff --git a/example-android/app/build.gradle.kts b/example-android/app/build.gradle.kts index afca29380e..fc46e3df53 100644 --- a/example-android/app/build.gradle.kts +++ b/example-android/app/build.gradle.kts @@ -1,22 +1,4 @@ -/*- - * - * Hedera Java SDK - * - * 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. - * - */ +// SPDX-License-Identifier: Apache-2.0 plugins { id("com.android.application") diff --git a/example-android/app/src/main/java/com/hedera/android_example/MainActivity.kt b/example-android/app/src/main/java/com/hedera/android_example/MainActivity.kt index b367b12886..64c81df301 100644 --- a/example-android/app/src/main/java/com/hedera/android_example/MainActivity.kt +++ b/example-android/app/src/main/java/com/hedera/android_example/MainActivity.kt @@ -1,22 +1,4 @@ -/*- - * - * Hedera Java SDK - * - * 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. - * - */ +// SPDX-License-Identifier: Apache-2.0 package com.hedera.android_example import android.os.Bundle diff --git a/example-android/app/src/main/java/com/hedera/android_example/ui/main/AccountBalanceFragment.kt b/example-android/app/src/main/java/com/hedera/android_example/ui/main/AccountBalanceFragment.kt index d872270144..ced61c3ddd 100644 --- a/example-android/app/src/main/java/com/hedera/android_example/ui/main/AccountBalanceFragment.kt +++ b/example-android/app/src/main/java/com/hedera/android_example/ui/main/AccountBalanceFragment.kt @@ -1,22 +1,4 @@ -/*- - * - * Hedera Java SDK - * - * 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. - * - */ +// SPDX-License-Identifier: Apache-2.0 package com.hedera.android_example.ui.main import android.os.Bundle diff --git a/example-android/app/src/main/java/com/hedera/android_example/ui/main/CryptoTransferFragment.kt b/example-android/app/src/main/java/com/hedera/android_example/ui/main/CryptoTransferFragment.kt index b2b08de330..d5916527cb 100644 --- a/example-android/app/src/main/java/com/hedera/android_example/ui/main/CryptoTransferFragment.kt +++ b/example-android/app/src/main/java/com/hedera/android_example/ui/main/CryptoTransferFragment.kt @@ -1,22 +1,4 @@ -/*- - * - * Hedera Java SDK - * - * 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. - * - */ +// SPDX-License-Identifier: Apache-2.0 package com.hedera.android_example.ui.main import android.os.Bundle diff --git a/example-android/app/src/main/java/com/hedera/android_example/ui/main/PrivateKeyFragment.kt b/example-android/app/src/main/java/com/hedera/android_example/ui/main/PrivateKeyFragment.kt index b3153f97e8..73d443816a 100644 --- a/example-android/app/src/main/java/com/hedera/android_example/ui/main/PrivateKeyFragment.kt +++ b/example-android/app/src/main/java/com/hedera/android_example/ui/main/PrivateKeyFragment.kt @@ -1,22 +1,4 @@ -/*- - * - * Hedera Java SDK - * - * 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. - * - */ +// SPDX-License-Identifier: Apache-2.0 package com.hedera.android_example.ui.main import android.widget.TextView diff --git a/example-android/app/src/main/java/com/hedera/android_example/ui/main/SectionsPagerAdapter.kt b/example-android/app/src/main/java/com/hedera/android_example/ui/main/SectionsPagerAdapter.kt index 90dc07fee1..1a13004187 100644 --- a/example-android/app/src/main/java/com/hedera/android_example/ui/main/SectionsPagerAdapter.kt +++ b/example-android/app/src/main/java/com/hedera/android_example/ui/main/SectionsPagerAdapter.kt @@ -1,22 +1,4 @@ -/*- - * - * Hedera Java SDK - * - * 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. - * - */ +// SPDX-License-Identifier: Apache-2.0 package com.hedera.android_example.ui.main import androidx.fragment.app.FragmentActivity diff --git a/example-android/build.gradle.kts b/example-android/build.gradle.kts index 6c77d9a899..2ef81a7325 100644 --- a/example-android/build.gradle.kts +++ b/example-android/build.gradle.kts @@ -1,22 +1,4 @@ -/*- - * - * Hedera Java SDK - * - * 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. - * - */ +// SPDX-License-Identifier: Apache-2.0 plugins { id("com.android.application") version "8.2.2" apply false diff --git a/example-android/settings.gradle.kts b/example-android/settings.gradle.kts index 06a0497537..dd9df81581 100644 --- a/example-android/settings.gradle.kts +++ b/example-android/settings.gradle.kts @@ -1,22 +1,4 @@ -/*- - * - * Hedera Java SDK - * - * 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. - * - */ +// SPDX-License-Identifier: Apache-2.0 pluginManagement { repositories { diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts index 9194b96042..90c5f84aca 100644 --- a/examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -18,6 +18,7 @@ dependencyAnalysis { } dependencies.constraints { + implementation("com.google.guava:guava:33.3.1-android") implementation("io.github.cdimascio:dotenv-java:3.0.2") implementation("com.hedera.hashgraph:sdk:2.46.0") implementation("com.hedera.hashgraph:sdk-full:2.46.0") diff --git a/examples/src/main/java/com/hedera/hashgraph/sdk/examples/ContractHelper.java b/examples/src/main/java/com/hedera/hashgraph/sdk/examples/ContractHelper.java index 931cdbc3f0..965eb854a4 100644 --- a/examples/src/main/java/com/hedera/hashgraph/sdk/examples/ContractHelper.java +++ b/examples/src/main/java/com/hedera/hashgraph/sdk/examples/ContractHelper.java @@ -1,22 +1,4 @@ -/*- - * - * Hedera Java SDK - * - * 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. - * - */ +// SPDX-License-Identifier: Apache-2.0 package com.hedera.hashgraph.sdk.examples; import com.google.gson.Gson; @@ -36,9 +18,7 @@ import com.hedera.hashgraph.sdk.Status; import com.hedera.hashgraph.sdk.TransactionId; import com.hedera.hashgraph.sdk.TransactionRecord; - import java.io.IOException; -import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.nio.charset.StandardCharsets; @@ -48,7 +28,6 @@ import java.util.function.Function; import java.util.function.Supplier; - /* ContractHelper declutters PrecompileExample.java @@ -78,13 +57,14 @@ public class ContractHelper { public static String getBytecodeHex(String filename) throws IOException { try (Reader reader = new InputStreamReader( - Optional.ofNullable(ContractHelper.class.getResourceAsStream(filename)) - .orElseThrow(() -> new RuntimeException("Failed to find: " + filename)), - StandardCharsets.UTF_8)) { + Optional.ofNullable(ContractHelper.class.getResourceAsStream(filename)) + .orElseThrow(() -> new RuntimeException("Failed to find: " + filename)), + StandardCharsets.UTF_8)) { JsonObject json = new Gson().fromJson(reader, JsonObject.class); - JsonElement bytecodeElement = Optional.ofNullable(json.has("object") ? json.get("object") : json.get("bytecode")) - .orElseThrow(() -> new RuntimeException("No bytecode or object found in json.")); + JsonElement bytecodeElement = Optional.ofNullable( + json.has("object") ? json.get("object") : json.get("bytecode")) + .orElseThrow(() -> new RuntimeException("No bytecode or object found in json.")); if (bytecodeElement.isJsonObject()) { bytecodeElement = bytecodeElement.getAsJsonObject().get("object"); @@ -93,22 +73,20 @@ public static String getBytecodeHex(String filename) throws IOException { } } - public ContractHelper( - String filename, - ContractFunctionParameters constructorParameters, - Client client - ) throws PrecheckStatusException, TimeoutException, ReceiptStatusException, IOException { + public ContractHelper(String filename, ContractFunctionParameters constructorParameters, Client client) + throws PrecheckStatusException, TimeoutException, ReceiptStatusException, IOException { contractId = Objects.requireNonNull(new ContractCreateFlow() - .setBytecode(getBytecodeHex(filename)) - .setMaxChunks(30) - .setGas(8_000_000) - .setConstructorParameters(constructorParameters) - .execute(client) - .getReceipt(client) - .contractId); + .setBytecode(getBytecodeHex(filename)) + .setMaxChunks(30) + .setGas(8_000_000) + .setConstructorParameters(constructorParameters) + .execute(client) + .getReceipt(client) + .contractId); } - public ContractHelper setResultValidatorForStep(int stepIndex, Function validator) { + public ContractHelper setResultValidatorForStep( + int stepIndex, Function validator) { stepResultValidators.put(stepIndex, validator); return this; } @@ -146,17 +124,17 @@ public ContractHelper setStepLogic(int stepIndex, Consumer stepLogic) { private Function getResultValidator(int stepIndex) { return stepResultValidators.getOrDefault( - stepIndex, - // if no custom validator is given, assume that the step returns a response code which ought to be SUCCESS - contractFunctionResult -> { - Status responseStatus = Status.fromResponseCode(contractFunctionResult.getInt32(0)); - boolean isValid = responseStatus == Status.SUCCESS; - if (!isValid) { - System.out.println("Encountered invalid response status " + responseStatus); - } - return isValid; - } - ); + stepIndex, + // if no custom validator is given, assume that the step returns a response code which ought to be + // SUCCESS + contractFunctionResult -> { + Status responseStatus = Status.fromResponseCode(contractFunctionResult.getInt32(0)); + boolean isValid = responseStatus == Status.SUCCESS; + if (!isValid) { + System.out.println("Encountered invalid response status " + responseStatus); + } + return isValid; + }); } private Supplier getParameterSupplier(int stepIndex) { @@ -171,16 +149,11 @@ private List getSigners(int stepIndex) { return stepSigners.getOrDefault(stepIndex, Collections.emptyList()); } - public ContractHelper executeSteps( - int firstStepToExecute, - int lastStepToExecute, - Client client - ) throws Exception { + public ContractHelper executeSteps(int firstStepToExecute, int lastStepToExecute, Client client) throws Exception { for (int stepIndex = firstStepToExecute; stepIndex <= lastStepToExecute; stepIndex++) { System.out.println("Attempting to execute step " + stepIndex); - ContractExecuteTransaction tx = new ContractExecuteTransaction() - .setContractId(contractId) - .setGas(10_000_000); + ContractExecuteTransaction tx = + new ContractExecuteTransaction().setContractId(contractId).setGas(10_000_000); Hbar payableAmount = getPayableAmount(stepIndex); if (payableAmount != null) { @@ -188,7 +161,8 @@ public ContractHelper executeSteps( } String functionName = "step" + stepIndex; - ContractFunctionParameters parameters = getParameterSupplier(stepIndex).get(); + ContractFunctionParameters parameters = + getParameterSupplier(stepIndex).get(); if (parameters != null) { tx.setFunction(functionName, parameters); } else { @@ -205,14 +179,13 @@ public ContractHelper executeSteps( tx.sign(signer); } - TransactionRecord record = tx - .execute(client) - .setValidateStatus(false) - .getRecord(client); + TransactionRecord record = + tx.execute(client).setValidateStatus(false).getRecord(client); try { if (record.receipt.status != Status.SUCCESS) { - throw new Exception("transaction receipt yielded unsuccessful response code " + record.receipt.status); + throw new Exception( + "transaction receipt yielded unsuccessful response code " + record.receipt.status); } ContractFunctionResult functionResult = Objects.requireNonNull(record.contractFunctionResult); @@ -224,12 +197,14 @@ public ContractHelper executeSteps( } if (getResultValidator(stepIndex).apply(functionResult)) { - System.out.println("step " + stepIndex + " completed, and returned valid result. (TransactionId \"" + record.transactionId + "\")"); + System.out.println("step " + stepIndex + " completed, and returned valid result. (TransactionId \"" + + record.transactionId + "\")"); } else { throw new Exception("returned invalid result"); } } catch (Throwable error) { - throw new Exception("Error occurred in step " + stepIndex + ": " + error.getMessage() + "\n" + "Transaction record: " + record); + throw new Exception("Error occurred in step " + stepIndex + ": " + error.getMessage() + "\n" + + "Transaction record: " + record); } // otherwise will meet local-node throttle diff --git a/examples/src/main/java/com/hedera/hashgraph/sdk/examples/SolidityPrecompileExample.java b/examples/src/main/java/com/hedera/hashgraph/sdk/examples/SolidityPrecompileExample.java index 54c762abdc..4d380acb74 100644 --- a/examples/src/main/java/com/hedera/hashgraph/sdk/examples/SolidityPrecompileExample.java +++ b/examples/src/main/java/com/hedera/hashgraph/sdk/examples/SolidityPrecompileExample.java @@ -1,27 +1,8 @@ -/*- - * - * Hedera Java SDK - * - * 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. - * - */ +// SPDX-License-Identifier: Apache-2.0 package com.hedera.hashgraph.sdk.examples; import com.hedera.hashgraph.sdk.*; import io.github.cdimascio.dotenv.Dotenv; - import java.util.Arrays; import java.util.Objects; import java.util.function.Consumer; @@ -43,13 +24,14 @@ public class SolidityPrecompileExample { // see `.env.sample` in the repository root for how to specify these values // or set environment variables with the same names - private static final AccountId OPERATOR_ID = AccountId.fromString(Objects.requireNonNull(Dotenv.load().get("OPERATOR_ID"))); - private static final PrivateKey OPERATOR_KEY = PrivateKey.fromString(Objects.requireNonNull(Dotenv.load().get("OPERATOR_KEY"))); + private static final AccountId OPERATOR_ID = + AccountId.fromString(Objects.requireNonNull(Dotenv.load().get("OPERATOR_ID"))); + private static final PrivateKey OPERATOR_KEY = + PrivateKey.fromString(Objects.requireNonNull(Dotenv.load().get("OPERATOR_KEY"))); // HEDERA_NETWORK defaults to testnet if not specified in dotenv private static final String HEDERA_NETWORK = Dotenv.load().get("HEDERA_NETWORK", "testnet"); - private SolidityPrecompileExample() { - } + private SolidityPrecompileExample() {} public static void main(String[] args) throws Exception { Client client = ClientHelper.forName(HEDERA_NETWORK); @@ -62,48 +44,49 @@ public static void main(String[] args) throws Exception { PrivateKey alicePrivateKey = PrivateKey.generateED25519(); PublicKey alicePublicKey = alicePrivateKey.getPublicKey(); AccountId aliceAccountId = Objects.requireNonNull(new AccountCreateTransaction() - .setKey(alicePublicKey) - .setInitialBalance(Hbar.fromTinybars(1000)) - .execute(client) - .getReceipt(client) - .accountId - ); + .setKey(alicePublicKey) + .setInitialBalance(Hbar.fromTinybars(1000)) + .execute(client) + .getReceipt(client) + .accountId); // Instantiate ContractHelper ContractHelper contractHelper = new ContractHelper( - "contracts/precompile/PrecompileExample.json", - new ContractFunctionParameters() - .addAddress(OPERATOR_ID.toSolidityAddress()) - .addAddress(aliceAccountId.toSolidityAddress()), - client - ); + "contracts/precompile/PrecompileExample.json", + new ContractFunctionParameters() + .addAddress(OPERATOR_ID.toSolidityAddress()) + .addAddress(aliceAccountId.toSolidityAddress()), + client); // Update the signer to have contractId KeyList (this is by security requirement) new AccountUpdateTransaction() - .setAccountId(OPERATOR_ID) - .setKey(KeyList.of(OPERATOR_KEY.getPublicKey(), contractHelper.contractId).setThreshold(1)) - .execute(client) - .getReceipt(client); + .setAccountId(OPERATOR_ID) + .setKey(KeyList.of(OPERATOR_KEY.getPublicKey(), contractHelper.contractId) + .setThreshold(1)) + .execute(client) + .getReceipt(client); // Update the Alice account to have contractId KeyList (this is by security requirement) new AccountUpdateTransaction() - .setAccountId(aliceAccountId) - .setKey(KeyList.of(alicePublicKey, contractHelper.contractId).setThreshold(1)) - .freezeWith(client) - .sign(alicePrivateKey) - .execute(client) - .getReceipt(client); + .setAccountId(aliceAccountId) + .setKey(KeyList.of(alicePublicKey, contractHelper.contractId).setThreshold(1)) + .freezeWith(client) + .sign(alicePrivateKey) + .execute(client) + .getReceipt(client); Consumer additionalLogic = tokenAddress -> { try { var tokenUpdateTransactionReceipt = new TokenUpdateTransaction() - .setTokenId(TokenId.fromSolidityAddress(tokenAddress)) - .setAdminKey(KeyList.of(OPERATOR_KEY.getPublicKey(), contractHelper.contractId).setThreshold(1)) - .setSupplyKey(KeyList.of(OPERATOR_KEY.getPublicKey(), contractHelper.contractId).setThreshold(1)) - .freezeWith(client) - .sign(alicePrivateKey) - .execute(client) - .getReceipt(client); + .setTokenId(TokenId.fromSolidityAddress(tokenAddress)) + .setAdminKey(KeyList.of(OPERATOR_KEY.getPublicKey(), contractHelper.contractId) + .setThreshold(1)) + .setSupplyKey(KeyList.of(OPERATOR_KEY.getPublicKey(), contractHelper.contractId) + .setThreshold(1)) + .freezeWith(client) + .sign(alicePrivateKey) + .execute(client) + .getReceipt(client); System.out.println("Status of Token Update Transaction: " + tokenUpdateTransactionReceipt.status); } catch (Exception e) { @@ -113,39 +96,43 @@ public static void main(String[] args) throws Exception { // Configure steps in ContractHelper contractHelper - .setResultValidatorForStep(0, contractFunctionResult -> { - System.out.println("getPseudoRandomSeed() returned " + Arrays.toString(contractFunctionResult.getBytes32(0))); - return true; - }).setPayableAmountForStep(1, Hbar.from(20)) - // step 3 associates Alice with the token, which requires Alice's signature - .addSignerForStep(3, alicePrivateKey) - .addSignerForStep(5, alicePrivateKey) - .setParameterSupplierForStep(11, () -> { - return new ContractFunctionParameters() - // when contracts work with a public key, they handle the raw bytes of the public key - .addBytes(alicePublicKey.toBytesRaw()); - }).setPayableAmountForStep(11, Hbar.from(40)) - // Because we're setting the adminKey for the created NFT token to Alice's key, - // Alice must sign the ContractExecuteTransaction. - .addSignerForStep(11, alicePrivateKey) - .setStepLogic(11, additionalLogic) - // and Alice must sign for minting because her key is the supply key. - .addSignerForStep(12, alicePrivateKey) - .setParameterSupplierForStep(12, () -> { - return new ContractFunctionParameters() - // add three metadatas - .addBytesArray(new byte[][]{new byte[]{0x01b}, new byte[]{0x02b}, new byte[]{0x03b}}); - }) // and alice must sign to become associated with the token. - .addSignerForStep(13, alicePrivateKey) - // Alice must sign to burn the token because her key is the supply key - .addSignerForStep(16, alicePrivateKey); + .setResultValidatorForStep(0, contractFunctionResult -> { + System.out.println( + "getPseudoRandomSeed() returned " + Arrays.toString(contractFunctionResult.getBytes32(0))); + return true; + }) + .setPayableAmountForStep(1, Hbar.from(20)) + // step 3 associates Alice with the token, which requires Alice's signature + .addSignerForStep(3, alicePrivateKey) + .addSignerForStep(5, alicePrivateKey) + .setParameterSupplierForStep(11, () -> { + return new ContractFunctionParameters() + // when contracts work with a public key, they handle the raw bytes of the public key + .addBytes(alicePublicKey.toBytesRaw()); + }) + .setPayableAmountForStep(11, Hbar.from(40)) + // Because we're setting the adminKey for the created NFT token to Alice's key, + // Alice must sign the ContractExecuteTransaction. + .addSignerForStep(11, alicePrivateKey) + .setStepLogic(11, additionalLogic) + // and Alice must sign for minting because her key is the supply key. + .addSignerForStep(12, alicePrivateKey) + .setParameterSupplierForStep(12, () -> { + return new ContractFunctionParameters() + // add three metadatas + .addBytesArray(new byte[][] {new byte[] {0x01b}, new byte[] {0x02b}, new byte[] {0x03b}}); + }) // and alice must sign to become associated with the token. + .addSignerForStep(13, alicePrivateKey) + // Alice must sign to burn the token because her key is the supply key + .addSignerForStep(16, alicePrivateKey); // step 0 tests pseudo random number generator (PRNG) // step 1 creates a fungible token // step 2 mints it // step 3 associates Alice with it // step 4 transfers it to Alice. - // step 5 approves an allowance of the fungible token with operator as the owner and Alice as the spender [NOT WORKING] + // step 5 approves an allowance of the fungible token with operator as the owner and Alice as the spender [NOT + // WORKING] // steps 6 - 10 test misc functions on the fungible token (see PrecompileExample.sol for details). // step 11 creates an NFT token with a custom fee, and with the admin and supply set to Alice's key // step 12 mints some NFTs diff --git a/examples/src/main/java/module-info.java b/examples/src/main/java/module-info.java index 7242d5b8cf..858487390a 100644 --- a/examples/src/main/java/module-info.java +++ b/examples/src/main/java/module-info.java @@ -1,22 +1,4 @@ -/*- - * - * Hedera Java SDK - * - * 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. - * - */ +// SPDX-License-Identifier: Apache-2.0 module com.hedera.hashgraph.examples { requires com.hedera.hashgraph.sdk; // .full; diff --git a/gradle/aggregation/build.gradle.kts b/gradle/aggregation/build.gradle.kts index aa94166ca9..317b7608c5 100644 --- a/gradle/aggregation/build.gradle.kts +++ b/gradle/aggregation/build.gradle.kts @@ -10,3 +10,31 @@ dependencies { implementation(project(":sdk")) implementation("io.grpc:grpc-protobuf") } + +tasks.testCodeCoverageReport { + // Integrate coverage data from integration tests into the report + @Suppress("UnstableApiUsage") + val testIntegrationExecutionData = + configurations.aggregateCodeCoverageReportResults + .get() + .incoming + .artifactView { + withVariantReselection() + componentFilter { id -> id is ProjectComponentIdentifier } + attributes.attribute( + Category.CATEGORY_ATTRIBUTE, + objects.named(Category.VERIFICATION) + ) + attributes.attribute( + VerificationType.VERIFICATION_TYPE_ATTRIBUTE, + objects.named(VerificationType.JACOCO_RESULTS) + ) + attributes.attribute( + TestSuiteType.TEST_SUITE_TYPE_ATTRIBUTE, + objects.named(TestSuiteType.INTEGRATION_TEST) + ) + } + .files + + executionData.from(testIntegrationExecutionData) +} diff --git a/scripts/update_protobufs.py b/scripts/update_protobufs.py index 7ce6beaa7a..38e805d332 100755 --- a/scripts/update_protobufs.py +++ b/scripts/update_protobufs.py @@ -75,13 +75,13 @@ def go_to_script_dir(): PROTO_REPLACEMENTS = ( ("option java_package = \"com.hederahashgraph.api.proto.java\";", - "option java_package = \"com.hedera.hashgraph.sdk.proto\";"), + "option java_package = \"org.hiero.sdk.proto\";"), ("option java_package = \"com.hederahashgraph.service.proto.java\";", - "option java_package = \"com.hedera.hashgraph.sdk.proto\";"), + "option java_package = \"org.hiero.sdk.proto\";"), ("option java_package = \"com.hedera.mirror.api.proto\";", - "option java_package = \"com.hedera.hashgraph.sdk.proto.mirror\";") + "option java_package = \"org.hiero.sdk.proto.mirror\";") ) PROTO_REPLACEMENTS_IMPORTS = ( diff --git a/sdk-full/src/main/java/module-info.java b/sdk-full/src/main/java/module-info.java index 19a4674b68..252a0434af 100644 --- a/sdk-full/src/main/java/module-info.java +++ b/sdk-full/src/main/java/module-info.java @@ -18,7 +18,7 @@ * */ -module com.hedera.hashgraph.sdk.full { +module org.hiero.sdk.full { requires transitive com.google.protobuf; requires com.esaulpaugh.headlong; requires com.google.common; @@ -33,9 +33,9 @@ requires org.slf4j; requires static transitive java.annotation; - exports com.hedera.hashgraph.sdk; - exports com.hedera.hashgraph.sdk.logger; - exports com.hedera.hashgraph.sdk.proto; + exports org.hiero.sdk; + exports org.hiero.sdk.logger; + exports org.hiero.sdk.proto; - opens com.hedera.hashgraph.sdk; + opens org.hiero.sdk; } diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountAllowanceApproveTransaction.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountAllowanceApproveTransaction.java deleted file mode 100644 index 9b32072df4..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountAllowanceApproveTransaction.java +++ /dev/null @@ -1,488 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.CryptoApproveAllowanceTransactionBody; -import com.hedera.hashgraph.sdk.proto.CryptoServiceGrpc; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -/** - * This transaction type is for approving account allowance. - */ -public class AccountAllowanceApproveTransaction extends Transaction { - private final List hbarAllowances = new ArrayList<>(); - private final List tokenAllowances = new ArrayList<>(); - private final List nftAllowances = new ArrayList<>(); - // key is "{ownerId}:{spenderId}". OwnerId may be "FEE_PAYER" - // > - private final Map> nftMap = new HashMap<>(); - - /** - * Constructor. - */ - public AccountAllowanceApproveTransaction() { - } - - /** - * Constructor. - * - * @param txs Compound list of transaction id's list of (AccountId, Transaction) records - */ - AccountAllowanceApproveTransaction( - LinkedHashMap> txs - ) throws InvalidProtocolBufferException { - super(txs); - initFromTransactionBody(); - } - - /** - * Constructor. - * - * @param txBody protobuf TransactionBody - */ - AccountAllowanceApproveTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { - super(txBody); - initFromTransactionBody(); - } - - private void initFromTransactionBody() { - var body = sourceTransactionBody.getCryptoApproveAllowance(); - for (var allowanceProto : body.getCryptoAllowancesList()) { - hbarAllowances.add(HbarAllowance.fromProtobuf(allowanceProto)); - } - for (var allowanceProto : body.getTokenAllowancesList()) { - tokenAllowances.add(TokenAllowance.fromProtobuf(allowanceProto)); - } - for (var allowanceProto : body.getNftAllowancesList()) { - if (allowanceProto.hasApprovedForAll() && allowanceProto.getApprovedForAll().getValue()) { - nftAllowances.add(TokenNftAllowance.fromProtobuf(allowanceProto)); - } else { - getNftSerials( - allowanceProto.hasOwner() ? AccountId.fromProtobuf(allowanceProto.getOwner()) : null, - AccountId.fromProtobuf(allowanceProto.getSpender()), - allowanceProto.hasDelegatingSpender() ? AccountId.fromProtobuf(allowanceProto.getDelegatingSpender()) : null, - TokenId.fromProtobuf(allowanceProto.getTokenId()) - ).addAll(allowanceProto.getSerialNumbersList()); - } - } - } - - /** - * @deprecated - Use {@link #approveHbarAllowance(AccountId, AccountId, Hbar)} instead - * - * @param spenderAccountId the spender account id - * @param amount the amount of hbar - * @return an account allowance approve transaction - */ - @Deprecated - public AccountAllowanceApproveTransaction addHbarAllowance(AccountId spenderAccountId, Hbar amount) { - return approveHbarAllowance(null, spenderAccountId, amount); - } - - /** - * Approves the Hbar allowance. - * - * @param ownerAccountId owner's account id - * @param spenderAccountId spender's account id - * @param amount amount of hbar add - * @return {@code this} - */ - public AccountAllowanceApproveTransaction approveHbarAllowance( - @Nullable AccountId ownerAccountId, - AccountId spenderAccountId, - Hbar amount - ) { - requireNotFrozen(); - Objects.requireNonNull(amount); - if (amount.compareTo(Hbar.ZERO) < 0) { - throw new IllegalArgumentException("amount passed to approveHbarAllowance must be positive"); - } - hbarAllowances.add(new HbarAllowance(ownerAccountId, Objects.requireNonNull(spenderAccountId), amount)); - return this; - } - - /** - * @deprecated - Use {@link #getHbarApprovals()} instead - * - * @return list of hbar allowance records - */ - @Deprecated - public List getHbarAllowances() { - return getHbarApprovals(); - } - - /** - * Extract the list of hbar allowances. - * - * @return array list of hbar allowances - */ - public List getHbarApprovals() { - return new ArrayList<>(hbarAllowances); - } - - /** - * @deprecated - Use {@link #approveTokenAllowance(TokenId, AccountId, AccountId, long)} instead - * - * @param tokenId the token id - * @param spenderAccountId the spenders account id - * @param amount the hbar amount - * @return an account allowance approve transaction - */ - @Deprecated - public AccountAllowanceApproveTransaction addTokenAllowance( - TokenId tokenId, - AccountId spenderAccountId, - long amount - ) { - return approveTokenAllowance(tokenId, null, spenderAccountId, amount); - } - - /** - * Approves the Token allowance. - * - * @param tokenId the token's id - * @param ownerAccountId owner's account id - * @param spenderAccountId spender's account id - * @param amount amount of tokens - * @return {@code this} - */ - public AccountAllowanceApproveTransaction approveTokenAllowance( - TokenId tokenId, - @Nullable AccountId ownerAccountId, - AccountId spenderAccountId, - long amount - ) { - requireNotFrozen(); - if (amount < 0) { - throw new IllegalArgumentException("amount given to approveTokenAllowance must be positive"); - } - tokenAllowances.add(new TokenAllowance( - Objects.requireNonNull(tokenId), - ownerAccountId, - Objects.requireNonNull(spenderAccountId), - amount - )); - return this; - } - - /** - * @deprecated - Use {@link #getTokenApprovals()} instead - * - * @return a list of token allowances - */ - @Deprecated - public List getTokenAllowances() { - return getTokenApprovals(); - } - - /** - * Extract a list of token allowance approvals. - * - * @return array list of token approvals. - */ - public List getTokenApprovals() { - return new ArrayList<>(tokenAllowances); - } - - /** - * Extract the owner as a string. - * - * @param ownerAccountId owner's account id - * @return a string representation of the account id - * or FEE_PAYER - */ - private static String ownerToString(@Nullable AccountId ownerAccountId) { - return ownerAccountId != null ? ownerAccountId.toString() : "FEE_PAYER"; - } - - /** - * Return a list of NFT serial numbers. - * - * @param ownerAccountId owner's account id - * @param spenderAccountId spender's account id - * @param delegatingSpender delegating spender's account id - * @param tokenId the token's id - * @return list of NFT serial numbers - */ - private List getNftSerials(@Nullable AccountId ownerAccountId, AccountId spenderAccountId, @Nullable AccountId delegatingSpender, TokenId tokenId) { - var key = ownerToString(ownerAccountId) + ":" + spenderAccountId; - if (nftMap.containsKey(key)) { - var innerMap = nftMap.get(key); - if (innerMap.containsKey(tokenId)) { - return Objects.requireNonNull(nftAllowances.get(innerMap.get(tokenId)).serialNumbers); - } else { - return newNftSerials(ownerAccountId, spenderAccountId, delegatingSpender, tokenId, innerMap); - } - } else { - Map innerMap = new HashMap<>(); - nftMap.put(key, innerMap); - return newNftSerials(ownerAccountId, spenderAccountId, delegatingSpender, tokenId, innerMap); - } - } - - /** - * Add NFT serials. - * - * @param ownerAccountId owner's account id - * @param spenderAccountId spender's account id - * @param delegatingSpender delegating spender's account id - * @param tokenId the token's id - * @param innerMap list of token id's and serial number records - * @return list of NFT serial numbers - */ - private List newNftSerials( - @Nullable AccountId ownerAccountId, - AccountId spenderAccountId, - @Nullable AccountId delegatingSpender, - TokenId tokenId, - Map innerMap - ) { - innerMap.put(tokenId, nftAllowances.size()); - TokenNftAllowance newAllowance = new TokenNftAllowance( - tokenId, - ownerAccountId, - spenderAccountId, - delegatingSpender, - new ArrayList<>(), - null - ); - nftAllowances.add(newAllowance); - return newAllowance.serialNumbers; - } - - /** - * @deprecated - Use {@link #approveTokenNftAllowance(NftId, AccountId, AccountId, AccountId)} instead - * - * @param nftId the nft id - * @param spenderAccountId the spender's account id - * @return {@code this} - */ - @Deprecated - public AccountAllowanceApproveTransaction addTokenNftAllowance(NftId nftId, AccountId spenderAccountId) { - requireNotFrozen(); - getNftSerials(null, spenderAccountId, null, nftId.tokenId).add(nftId.serial); - return this; - } - - /** - * @deprecated - Use {@link #approveTokenNftAllowanceAllSerials(TokenId, AccountId, AccountId)} instead - * - * @param tokenId the token id - * @param spenderAccountId the spender's account id - * @return {@code this} - */ - @Deprecated - public AccountAllowanceApproveTransaction addAllTokenNftAllowance(TokenId tokenId, AccountId spenderAccountId) { - requireNotFrozen(); - nftAllowances.add(new TokenNftAllowance( - tokenId, - null, - spenderAccountId, - null, - Collections.emptyList(), - true - )); - return this; - } - - /** - * Approve the NFT allowance. - * - * @param nftId nft's id - * @param ownerAccountId owner's account id - * @param spenderAccountId spender's account id - * @param delegatingSpender delegating spender's account id - * @return {@code this} - */ - public AccountAllowanceApproveTransaction approveTokenNftAllowance( - NftId nftId, - @Nullable AccountId ownerAccountId, - AccountId spenderAccountId, - @Nullable AccountId delegatingSpender - ) { - requireNotFrozen(); - Objects.requireNonNull(nftId); - getNftSerials( - ownerAccountId, - Objects.requireNonNull(spenderAccountId), - delegatingSpender, - nftId.tokenId - ).add(nftId.serial); - return this; - } - - /** - * Approve the NFT allowance. - * - * @param nftId nft's id - * @param ownerAccountId owner's account id - * @param spenderAccountId spender's account id - * @return {@code this} - */ - public AccountAllowanceApproveTransaction approveTokenNftAllowance( - NftId nftId, - @Nullable AccountId ownerAccountId, - AccountId spenderAccountId - ) { - requireNotFrozen(); - Objects.requireNonNull(nftId); - getNftSerials( - ownerAccountId, - Objects.requireNonNull(spenderAccountId), - null, - nftId.tokenId - ).add(nftId.serial); - return this; - } - - /** - * Approve the token nft allowance on all serials. - * - * @param tokenId the token's id - * @param ownerAccountId owner's account id - * @param spenderAccountId spender's account id - * @return {@code this} - */ - public AccountAllowanceApproveTransaction approveTokenNftAllowanceAllSerials( - TokenId tokenId, - @Nullable AccountId ownerAccountId, - AccountId spenderAccountId - ) { - requireNotFrozen(); - nftAllowances.add(new TokenNftAllowance( - Objects.requireNonNull(tokenId), - ownerAccountId, - Objects.requireNonNull(spenderAccountId), - null, - Collections.emptyList(), - true - )); - return this; - } - - /** - * Delete the token nft allowance on all serials. - * - * @param tokenId the token's id - * @param ownerAccountId owner's account id - * @param spenderAccountId spender's account id - * @return {@code this} - */ - public AccountAllowanceApproveTransaction deleteTokenNftAllowanceAllSerials( - TokenId tokenId, - @Nullable AccountId ownerAccountId, - AccountId spenderAccountId - ) { - requireNotFrozen(); - nftAllowances.add(new TokenNftAllowance( - Objects.requireNonNull(tokenId), - ownerAccountId, - Objects.requireNonNull(spenderAccountId), - null, - Collections.emptyList(), - false - )); - return this; - } - - /** - * @deprecated - Use {@link #getTokenNftApprovals()} instead - * - * @return {@code this} - */ - @Deprecated - public List getTokenNftAllowances() { - return getTokenNftApprovals(); - } - - /** - * Returns the list of token nft allowances. - * - * @return list of token nft allowances. - */ - public List getTokenNftApprovals() { - List retval = new ArrayList<>(nftAllowances.size()); - for (var allowance : nftAllowances) { - retval.add(TokenNftAllowance.copyFrom(allowance)); - } - return retval; - } - - @Override - MethodDescriptor getMethodDescriptor() { - return CryptoServiceGrpc.getApproveAllowancesMethod(); - } - - /** - * Build the correct transaction body. - * - * @return {@link com.hedera.hashgraph.sdk.proto.CryptoApproveAllowanceTransactionBody builder } - */ - CryptoApproveAllowanceTransactionBody.Builder build() { - var builder = CryptoApproveAllowanceTransactionBody.newBuilder(); - - for (var allowance : hbarAllowances) { - builder.addCryptoAllowances(allowance.toProtobuf()); - } - for (var allowance : tokenAllowances) { - builder.addTokenAllowances(allowance.toProtobuf()); - } - for (var allowance : nftAllowances) { - builder.addNftAllowances(allowance.toProtobuf()); - } - return builder; - } - - @Override - void onFreeze(TransactionBody.Builder bodyBuilder) { - bodyBuilder.setCryptoApproveAllowance(build()); - } - - @Override - void onScheduled(SchedulableTransactionBody.Builder scheduled) { - scheduled.setCryptoApproveAllowance(build()); - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - for (var allowance : hbarAllowances) { - allowance.validateChecksums(client); - } - for (var allowance : tokenAllowances) { - allowance.validateChecksums(client); - } - for (var allowance : nftAllowances) { - allowance.validateChecksums(client); - } - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountAllowanceDeleteTransaction.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountAllowanceDeleteTransaction.java deleted file mode 100644 index a8aea45bce..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountAllowanceDeleteTransaction.java +++ /dev/null @@ -1,250 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.CryptoDeleteAllowanceTransactionBody; -import com.hedera.hashgraph.sdk.proto.CryptoServiceGrpc; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.Transaction; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -/** - * This transaction type is for deleting an account allowance. - */ -public class AccountAllowanceDeleteTransaction extends com.hedera.hashgraph.sdk.Transaction { - private final List hbarAllowances = new ArrayList<>(); - private final List tokenAllowances = new ArrayList<>(); - private final List nftAllowances = new ArrayList<>(); - // > - private final Map> nftMap = new HashMap<>(); - - /** - * Constructor. - */ - public AccountAllowanceDeleteTransaction() { - } - - /** - * Constructor. - * - * @param txs Compound list of transaction id's list of (AccountId, Transaction) records - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - AccountAllowanceDeleteTransaction( - LinkedHashMap> txs - ) throws InvalidProtocolBufferException { - super(txs); - initFromTransactionBody(); - } - - /** - * Constructor. - * - * @param txBody protobuf TransactionBody - */ - AccountAllowanceDeleteTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { - super(txBody); - initFromTransactionBody(); - } - - private void initFromTransactionBody() { - var body = sourceTransactionBody.getCryptoDeleteAllowance(); - for (var allowanceProto : body.getNftAllowancesList()) { - getNftSerials( - AccountId.fromProtobuf(allowanceProto.getOwner()), - TokenId.fromProtobuf(allowanceProto.getTokenId()) - ).addAll(allowanceProto.getSerialNumbersList()); - } - } - - /** - * @deprecated with no replacement - * - * @param ownerAccountId the owner's account id - * @return {@code this} - */ - @Deprecated - public AccountAllowanceDeleteTransaction deleteAllHbarAllowances(AccountId ownerAccountId) { - requireNotFrozen(); - hbarAllowances.add(new HbarAllowance(Objects.requireNonNull(ownerAccountId), null, null)); - return this; - } - - /** - * @deprecated with no replacement - * - * @return a list of hbar allowance records - */ - @Deprecated - public List getHbarAllowanceDeletions() { - return new ArrayList<>(hbarAllowances); - } - - /** - * @deprecated with no replacement - * - * @param tokenId the token id - * @param ownerAccountId the owner's account id - * @return {@code this} - */ - @Deprecated - public AccountAllowanceDeleteTransaction deleteAllTokenAllowances(TokenId tokenId, AccountId ownerAccountId) { - requireNotFrozen(); - tokenAllowances.add(new TokenAllowance( - Objects.requireNonNull(tokenId), - Objects.requireNonNull(ownerAccountId), - null, - 0 - )); - return this; - } - - /** - * @deprecated with no replacement - * - * @return a list of token allowance records - */ - @Deprecated - public List getTokenAllowanceDeletions() { - return new ArrayList<>(tokenAllowances); - } - - /** - * Remove all nft token allowances. - * - * @param nftId nft's id - * @param ownerAccountId owner's account id - * @return {@code this} - */ - public AccountAllowanceDeleteTransaction deleteAllTokenNftAllowances(NftId nftId, AccountId ownerAccountId) { - requireNotFrozen(); - Objects.requireNonNull(nftId); - getNftSerials(Objects.requireNonNull(ownerAccountId), nftId.tokenId).add(nftId.serial); - return this; - } - - /** - * Return list of nft tokens to be deleted. - * - * @return list of token nft allowances - */ - public List getTokenNftAllowanceDeletions() { - List retval = new ArrayList<>(nftAllowances.size()); - for (var allowance : nftAllowances) { - retval.add(TokenNftAllowance.copyFrom(allowance)); - } - return retval; - } - - /** - * Return list of nft serial numbers. - * - * @param ownerAccountId owner's account id - * @param tokenId the token's id - * @return list of nft serial numbers - */ - private List getNftSerials(@Nullable AccountId ownerAccountId, TokenId tokenId) { - var key = ownerAccountId; - if (nftMap.containsKey(key)) { - var innerMap = nftMap.get(key); - if (innerMap.containsKey(tokenId)) { - return Objects.requireNonNull(nftAllowances.get(innerMap.get(tokenId)).serialNumbers); - } else { - return newNftSerials(ownerAccountId, tokenId, innerMap); - } - } else { - Map innerMap = new HashMap<>(); - nftMap.put(key, innerMap); - return newNftSerials(ownerAccountId, tokenId, innerMap); - } - } - - /** - * Return serial numbers of new nft's. - * - * @param ownerAccountId owner's account id - * @param tokenId the token's id - * @param innerMap list of token id's and serial number records - * @return list of nft serial numbers - */ - private List newNftSerials( - @Nullable AccountId ownerAccountId, - TokenId tokenId, - Map innerMap - ) { - innerMap.put(tokenId, nftAllowances.size()); - TokenNftAllowance newAllowance = new TokenNftAllowance( - tokenId, - ownerAccountId, - null, - null, - new ArrayList<>(), - null - ); - nftAllowances.add(newAllowance); - return newAllowance.serialNumbers; - } - - @Override - MethodDescriptor getMethodDescriptor() { - return CryptoServiceGrpc.getDeleteAllowancesMethod(); - } - - /** - * Build the transaction body. - * - * @return {@link CryptoDeleteAllowanceTransactionBody} - */ - CryptoDeleteAllowanceTransactionBody.Builder build() { - var builder = CryptoDeleteAllowanceTransactionBody.newBuilder(); - for (var allowance : nftAllowances) { - builder.addNftAllowances(allowance.toRemoveProtobuf()); - } - return builder; - } - - @Override - void onFreeze(TransactionBody.Builder bodyBuilder) { - bodyBuilder.setCryptoDeleteAllowance(build()); - } - - @Override - void onScheduled(SchedulableTransactionBody.Builder scheduled) { - scheduled.setCryptoDeleteAllowance(build()); - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - for (var allowance : nftAllowances) { - allowance.validateChecksums(client); - } - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountBalance.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountBalance.java deleted file mode 100644 index b6ffce6283..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountBalance.java +++ /dev/null @@ -1,126 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.CryptoGetAccountBalanceResponse; -import com.hedera.hashgraph.sdk.proto.TokenBalance; - -import javax.annotation.Nonnegative; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - -/** - * This class represents the account balance object - */ -public class AccountBalance { - /** - * The Hbar balance of the account - */ - @Nonnegative - public final Hbar hbars; - - /** - * @deprecated - Use `tokens` instead - */ - @Deprecated - @Nonnegative - public final Map token = new HashMap<>(); - - public final Map tokens; - - @Nonnegative - public final Map tokenDecimals; - - AccountBalance(Hbar hbars, Map token, Map decimal) { - this.hbars = hbars; - this.tokens = token; - this.tokenDecimals = decimal; - } - - /** - * Convert the protobuf object to an account balance object. - * - * @param protobuf protobuf response object - * @return the converted account balance object - */ - static AccountBalance fromProtobuf(CryptoGetAccountBalanceResponse protobuf) { - var balanceList = protobuf.getTokenBalancesList(); - Map map = new HashMap<>(); - Map decimalMap = new HashMap<>(); - for (int i = 0; i < protobuf.getTokenBalancesCount(); i++) { - map.put(TokenId.fromProtobuf(balanceList.get(i).getTokenId()), balanceList.get(i).getBalance()); - decimalMap.put(TokenId.fromProtobuf(balanceList.get(i).getTokenId()), balanceList.get(i).getDecimals()); - } - - return new AccountBalance(Hbar.fromTinybars(protobuf.getBalance()), map, decimalMap); - } - - /** - * Convert a byte array to an account balance object. - * - * @param data the byte array - * @return the converted account balance object - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - public static AccountBalance fromBytes(byte[] data) throws InvalidProtocolBufferException { - return fromProtobuf(CryptoGetAccountBalanceResponse.parseFrom(data)); - } - - /** - * Convert an account balance object into a protobuf. - * - * @return the protobuf object - */ - CryptoGetAccountBalanceResponse toProtobuf() { - var protobuf = CryptoGetAccountBalanceResponse.newBuilder() - .setBalance(hbars.toTinybars()); - - for (var entry : tokens.entrySet()) { - protobuf.addTokenBalances(TokenBalance.newBuilder() - .setTokenId(entry.getKey().toProtobuf()) - .setBalance(entry.getValue()) - .setDecimals(Objects.requireNonNull(tokenDecimals.get(entry.getKey()))) - ); - } - - return protobuf.build(); - } - - /** - * Convert the account balance object to a byte array. - * - * @return the converted account balance object - */ - public ByteString toBytes() { - return toProtobuf().toByteString(); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("hbars", hbars) - .add("tokens", tokens) - .toString(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountBalanceQuery.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountBalanceQuery.java deleted file mode 100644 index f5e00fb4e7..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountBalanceQuery.java +++ /dev/null @@ -1,147 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.CryptoGetAccountBalanceQuery; -import com.hedera.hashgraph.sdk.proto.CryptoServiceGrpc; -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import com.hedera.hashgraph.sdk.proto.Response; -import com.hedera.hashgraph.sdk.proto.ResponseHeader; -import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; -import java.util.Objects; - -/** - * Get the balance of a Hedera™ crypto-currency account. This returns only the balance, so it is a - * smaller and faster reply than {@link AccountInfoQuery}. - * - *

This query is free. - */ -public final class AccountBalanceQuery extends Query { - @Nullable - private AccountId accountId = null; - @Nullable - private ContractId contractId = null; - - /** - * Constructor. - */ - public AccountBalanceQuery() { - } - - /** - * Return the account's id. - * - * @return {@code accountId} - */ - @Nullable - public AccountId getAccountId() { - return accountId; - } - - /** - * The account ID for which the balance is being requested. - *

- * This is mutually exclusive with {@link #setContractId(ContractId)}. - * - * @param accountId The AccountId to set - * @return {@code this} - */ - public AccountBalanceQuery setAccountId(AccountId accountId) { - Objects.requireNonNull(accountId); - this.accountId = accountId; - return this; - } - - /** - * Extract the contract id. - * - * @return the contract id - */ - @Nullable - public ContractId getContractId() { - return contractId; - } - - /** - * The contract ID for which the balance is being requested. - *

- * This is mutually exclusive with {@link #setAccountId(AccountId)}. - * - * @param contractId The ContractId to set - * @return {@code this} - */ - public AccountBalanceQuery setContractId(ContractId contractId) { - Objects.requireNonNull(contractId); - this.contractId = contractId; - return this; - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (accountId != null) { - accountId.validateChecksum(client); - } - - if (contractId != null) { - contractId.validateChecksum(client); - } - } - - @Override - boolean isPaymentRequired() { - return false; - } - - @Override - void onMakeRequest(com.hedera.hashgraph.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { - var builder = CryptoGetAccountBalanceQuery.newBuilder(); - if (accountId != null) { - builder.setAccountID(accountId.toProtobuf()); - } - - if (contractId != null) { - builder.setContractID(contractId.toProtobuf()); - } - - queryBuilder.setCryptogetAccountBalance(builder.setHeader(header)); - } - - @Override - AccountBalance mapResponse(Response response, AccountId nodeId, com.hedera.hashgraph.sdk.proto.Query request) { - return AccountBalance.fromProtobuf(response.getCryptogetAccountBalance()); - } - - @Override - ResponseHeader mapResponseHeader(Response response) { - return response.getCryptogetAccountBalance().getHeader(); - } - - @Override - QueryHeader mapRequestHeader(com.hedera.hashgraph.sdk.proto.Query request) { - return request.getCryptogetAccountBalance().getHeader(); - } - - @Override - MethodDescriptor getMethodDescriptor() { - return CryptoServiceGrpc.getCryptoGetBalanceMethod(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountDeleteTransaction.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountDeleteTransaction.java deleted file mode 100644 index 72b71f19ca..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountDeleteTransaction.java +++ /dev/null @@ -1,178 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.CryptoDeleteTransactionBody; -import com.hedera.hashgraph.sdk.proto.CryptoServiceGrpc; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; -import java.util.LinkedHashMap; -import java.util.Objects; - -/** - * Marks an account as deleted, moving all its current hbars to another account. - *

- * It will remain in the ledger, marked as deleted, until it expires. - * Transfers into it a deleted account fail. But a deleted account can still have its - * expiration extended in the normal way. - */ -public final class AccountDeleteTransaction extends Transaction { - @Nullable - private AccountId accountId = null; - @Nullable - private AccountId transferAccountId = null; - - /** - * Constructor. - */ - public AccountDeleteTransaction() { - } - - /** - * Constructor. - * - * @param txs Compound list of transaction id's list of (AccountId, Transaction) records - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - AccountDeleteTransaction(LinkedHashMap> txs) throws InvalidProtocolBufferException { - super(txs); - initFromTransactionBody(); - } - - /** - * Constructor. - * - * @param txBody protobuf TransactionBody - */ - AccountDeleteTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { - super(txBody); - initFromTransactionBody(); - } - - /** - * Extract the account id. - * - * @return the account id - */ - @Nullable - public AccountId getAccountId() { - return accountId; - } - - /** - * Sets the account ID which should be deleted. - * - * @param deleteAccountId The AccountId to be set - * @return {@code this} - */ - public AccountDeleteTransaction setAccountId(AccountId deleteAccountId) { - Objects.requireNonNull(deleteAccountId); - requireNotFrozen(); - this.accountId = deleteAccountId; - return this; - } - - /** - * Extract the receiving account id. - * - * @return the account id that receives the hbar - */ - @Nullable - public AccountId getTransferAccountId() { - return transferAccountId; - } - - /** - * Sets the account ID which will receive all remaining hbars. - * - * @param transferAccountId The AccountId to be set - * @return {@code this} - */ - public AccountDeleteTransaction setTransferAccountId(AccountId transferAccountId) { - requireNotFrozen(); - Objects.requireNonNull(transferAccountId); - this.transferAccountId = transferAccountId; - return this; - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (accountId != null) { - accountId.validateChecksum(client); - } - - if (transferAccountId != null) { - transferAccountId.validateChecksum(client); - } - } - - @Override - MethodDescriptor getMethodDescriptor() { - return CryptoServiceGrpc.getCryptoDeleteMethod(); - } - - /** - * Build the transaction body. - * - * @return {@link CryptoDeleteTransactionBody} - */ - CryptoDeleteTransactionBody.Builder build() { - var builder = CryptoDeleteTransactionBody.newBuilder(); - - if (accountId != null) { - builder.setDeleteAccountID(accountId.toProtobuf()); - } - - if (transferAccountId != null) { - builder.setTransferAccountID(transferAccountId.toProtobuf()); - } - - return builder; - } - - /** - * Initialize from the transaction body. - */ - void initFromTransactionBody() { - var body = sourceTransactionBody.getCryptoDelete(); - if (body.hasDeleteAccountID()) { - accountId = AccountId.fromProtobuf(body.getDeleteAccountID()); - } - - if (body.hasTransferAccountID()) { - transferAccountId = AccountId.fromProtobuf(body.getTransferAccountID()); - } - } - - @Override - void onFreeze(TransactionBody.Builder bodyBuilder) { - bodyBuilder.setCryptoDelete(build()); - } - - @Override - void onScheduled(SchedulableTransactionBody.Builder scheduled) { - scheduled.setCryptoDelete(build()); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountInfo.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountInfo.java deleted file mode 100644 index 75d0986e5e..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountInfo.java +++ /dev/null @@ -1,392 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import static java.util.stream.Collectors.toList; - -import com.google.common.base.MoreObjects; -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.CryptoGetInfoResponse; -import java.time.Duration; -import java.time.Instant; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import javax.annotation.Nullable; - -/** - * Current information about an account, including the balance. - */ -public final class AccountInfo { - /** - * The account ID for which this information applies. - */ - public final AccountId accountId; - - /** - * The Contract Account ID comprising both the contract instance and the cryptocurrency account owned by the - * contract instance, in the format used by Solidity. - */ - public final String contractAccountId; - - /** - * If true, then this account has been deleted, it will disappear when it expires, and all transactions for it will - * fail except the transaction to extend its expiration date. - */ - public final boolean isDeleted; - - /** - * The Account ID of the account to which this is proxy staked. If proxyAccountID is null, or is an invalid account, - * or is an account that isn't a node, then this account is automatically proxy staked to a node chosen by the - * network, but without earning payments. If the proxyAccountID account refuses to accept proxy staking , or if it - * is not currently running a node, then it will behave as if proxyAccountID was null. - */ - @Nullable - public final AccountId proxyAccountId; - - /** - * The total proxy staked to this account. - */ - public final Hbar proxyReceived; - - /** - * The key for the account, which must sign in order to transfer out, or to modify the account in any way other than - * extending its expiration date. - */ - public final Key key; - - /** - * The current balance of account. - */ - public final Hbar balance; - - /** - * The threshold amount for which an account record is created (and this account charged for them) for any - * send/withdraw transaction. - */ - public final Hbar sendRecordThreshold; - - /** - * The threshold amount for which an account record is created (and this account charged for them) for any - * transaction above this amount. - */ - public final Hbar receiveRecordThreshold; - - /** - * If true, no transaction can transfer to this account unless signed by this account's key. - */ - public final boolean isReceiverSignatureRequired; - - /** - * The time at which this account is set to expire. - */ - public final Instant expirationTime; - - /** - * The duration for expiration time will extend every this many seconds. If there are insufficient funds, then it - * extends as long as possible. If it is empty when it expires, then it is deleted. - */ - public final Duration autoRenewPeriod; - - /** - * All the livehashes attached to the account (each of which is a hash along with the keys that authorized it and - * can delete it) - */ - public final List liveHashes; - - public final Map tokenRelationships; - - /** - * The memo associated with the account - */ - public final String accountMemo; - - /** - * The number of NFTs owned by this account - */ - public final long ownedNfts; - - /** - * The maximum number of tokens that an Account can be implicitly associated with. - */ - public final int maxAutomaticTokenAssociations; - - /** - * The public key which aliases to this account. - */ - @Nullable - public final PublicKey aliasKey; - - /** - * The ledger ID the response was returned from; please see HIP-198 for the - * network-specific IDs. - */ - public final LedgerId ledgerId; - - /** - * The ethereum transaction nonce associated with this account. - */ - public final long ethereumNonce; - - /** - * List of Hbar allowances - */ - @Deprecated - public final List hbarAllowances; - - /** - * List of token allowances - */ - @Deprecated - public final List tokenAllowances; - - /** - * List of NFT allowances - */ - @Deprecated - public final List tokenNftAllowances; - - /** - * Staking metadata for this account. - */ - @Nullable - public final StakingInfo stakingInfo; - - /** - * Constructor. - * - * @param accountId the account id - * @param contractAccountId the contracts account id - * @param isDeleted is it deleted - * @param proxyAccountId the proxy account's id - * @param proxyReceived amount of proxy received - * @param key signing key - * @param balance account balance - * @param sendRecordThreshold @depreciated no replacement - * @param receiveRecordThreshold @depreciated no replacement - * @param receiverSignatureRequired is the receiver's signature required - * @param expirationTime the expiration time - * @param autoRenewPeriod the auto renew period - * @param liveHashes the live hashes - * @param tokenRelationships list of token id and token relationship records - * @param accountMemo the account memo - * @param ownedNfts number of nft's - * @param maxAutomaticTokenAssociations max number of token associations - * @param aliasKey public alias key - * @param ledgerId the ledger id - */ - private AccountInfo( - AccountId accountId, - String contractAccountId, - boolean isDeleted, - @Nullable AccountId proxyAccountId, - long proxyReceived, - Key key, - long balance, - long sendRecordThreshold, - long receiveRecordThreshold, - boolean receiverSignatureRequired, - Instant expirationTime, - Duration autoRenewPeriod, - List liveHashes, - Map tokenRelationships, - String accountMemo, - long ownedNfts, - int maxAutomaticTokenAssociations, - @Nullable PublicKey aliasKey, - LedgerId ledgerId, - long ethereumNonce, - @Nullable StakingInfo stakingInfo - ) { - this.accountId = accountId; - this.contractAccountId = contractAccountId; - this.isDeleted = isDeleted; - this.proxyAccountId = proxyAccountId; - this.proxyReceived = Hbar.fromTinybars(proxyReceived); - this.key = key; - this.balance = Hbar.fromTinybars(balance); - this.sendRecordThreshold = Hbar.fromTinybars(sendRecordThreshold); - this.receiveRecordThreshold = Hbar.fromTinybars(receiveRecordThreshold); - this.isReceiverSignatureRequired = receiverSignatureRequired; - this.expirationTime = expirationTime; - this.autoRenewPeriod = autoRenewPeriod; - this.liveHashes = liveHashes; - this.tokenRelationships = Collections.unmodifiableMap(tokenRelationships); - this.accountMemo = accountMemo; - this.ownedNfts = ownedNfts; - this.maxAutomaticTokenAssociations = maxAutomaticTokenAssociations; - this.aliasKey = aliasKey; - this.ledgerId = ledgerId; - this.ethereumNonce = ethereumNonce; - this.hbarAllowances = Collections.emptyList(); - this.tokenAllowances = Collections.emptyList(); - this.tokenNftAllowances = Collections.emptyList(); - this.stakingInfo = stakingInfo; - } - - /** - * Retrieve the account info from a protobuf. - * - * @param accountInfo the account info protobuf - * @return the account info object - */ - static AccountInfo fromProtobuf(CryptoGetInfoResponse.AccountInfo accountInfo) { - var accountId = AccountId.fromProtobuf(accountInfo.getAccountID()); - - var proxyAccountId = accountInfo.getProxyAccountID().getAccountNum() > 0 - ? AccountId.fromProtobuf(accountInfo.getProxyAccountID()) - : null; - - var liveHashes = Arrays.stream(accountInfo.getLiveHashesList().toArray()) - .map((liveHash) -> LiveHash.fromProtobuf((com.hedera.hashgraph.sdk.proto.LiveHash) liveHash)) - .collect(toList()); - - Map relationships = new HashMap<>(); - - for (com.hedera.hashgraph.sdk.proto.TokenRelationship relationship : accountInfo.getTokenRelationshipsList()) { - TokenId tokenId = TokenId.fromProtobuf(relationship.getTokenId()); - relationships.put(tokenId, TokenRelationship.fromProtobuf(relationship)); - } - - @Nullable - var aliasKey = PublicKey.fromAliasBytes(accountInfo.getAlias()); - - return new AccountInfo( - accountId, - accountInfo.getContractAccountID(), - accountInfo.getDeleted(), - proxyAccountId, - accountInfo.getProxyReceived(), - Key.fromProtobufKey(accountInfo.getKey()), - accountInfo.getBalance(), - accountInfo.getGenerateSendRecordThreshold(), - accountInfo.getGenerateReceiveRecordThreshold(), - accountInfo.getReceiverSigRequired(), - InstantConverter.fromProtobuf(accountInfo.getExpirationTime()), - DurationConverter.fromProtobuf(accountInfo.getAutoRenewPeriod()), - liveHashes, - relationships, - accountInfo.getMemo(), - accountInfo.getOwnedNfts(), - accountInfo.getMaxAutomaticTokenAssociations(), - aliasKey, - LedgerId.fromByteString(accountInfo.getLedgerId()), - accountInfo.getEthereumNonce(), - accountInfo.hasStakingInfo() ? StakingInfo.fromProtobuf(accountInfo.getStakingInfo()) : null - ); - } - - /** - * Retrieve the account info from a protobuf byte array. - * - * @param bytes a byte array representing the protobuf - * @return the account info object - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - public static AccountInfo fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return fromProtobuf(CryptoGetInfoResponse.AccountInfo.parseFrom(bytes).toBuilder().build()); - } - - /** - * Convert an account info object into a protobuf. - * - * @return the protobuf object - */ - CryptoGetInfoResponse.AccountInfo toProtobuf() { - var hashes = Arrays.stream(liveHashes.toArray()) - .map((liveHash) -> ((LiveHash) liveHash).toProtobuf()) - .collect(toList()); - - var accountInfoBuilder = CryptoGetInfoResponse.AccountInfo.newBuilder() - .setAccountID(accountId.toProtobuf()) - .setDeleted(isDeleted) - .setProxyReceived(proxyReceived.toTinybars()) - .setKey(key.toProtobufKey()) - .setBalance(balance.toTinybars()) - .setGenerateSendRecordThreshold(sendRecordThreshold.toTinybars()) - .setGenerateReceiveRecordThreshold(receiveRecordThreshold.toTinybars()) - .setReceiverSigRequired(isReceiverSignatureRequired) - .setExpirationTime(InstantConverter.toProtobuf(expirationTime)) - .setAutoRenewPeriod(DurationConverter.toProtobuf(autoRenewPeriod)) - .addAllLiveHashes(hashes) - .setMemo(accountMemo) - .setOwnedNfts(ownedNfts) - .setMaxAutomaticTokenAssociations(maxAutomaticTokenAssociations) - .setLedgerId(ledgerId.toByteString()) - .setEthereumNonce(ethereumNonce); - - if (contractAccountId != null) { - accountInfoBuilder.setContractAccountID(contractAccountId); - } - - if (proxyAccountId != null) { - accountInfoBuilder.setProxyAccountID(proxyAccountId.toProtobuf()); - } - - if (aliasKey != null) { - accountInfoBuilder.setAlias(aliasKey.toProtobufKey().toByteString()); - } - - if (stakingInfo != null) { - accountInfoBuilder.setStakingInfo(stakingInfo.toProtobuf()); - } - - return accountInfoBuilder.build(); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("accountId", accountId) - .add("contractAccountId", contractAccountId) - .add("deleted", isDeleted) - .add("proxyAccountId", proxyAccountId) - .add("proxyReceived", proxyReceived) - .add("key", key) - .add("balance", balance) - .add("sendRecordThreshold", sendRecordThreshold) - .add("receiveRecordThreshold", receiveRecordThreshold) - .add("receiverSignatureRequired", isReceiverSignatureRequired) - .add("expirationTime", expirationTime) - .add("autoRenewPeriod", autoRenewPeriod) - .add("liveHashes", liveHashes) - .add("tokenRelationships", tokenRelationships) - .add("accountMemo", accountMemo) - .add("ownedNfts", ownedNfts) - .add("maxAutomaticTokenAssociations", maxAutomaticTokenAssociations) - .add("aliasKey", aliasKey) - .add("ledgerId", ledgerId) - .add("ethereumNonce", ethereumNonce) - .add("stakingInfo", stakingInfo) - .toString(); - } - - /** - * Extract a byte array representation. - * - * @return a byte array representation - */ - public byte[] toBytes() { - return toProtobuf().toByteArray(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountInfoFlow.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountInfoFlow.java deleted file mode 100644 index c18fdc7ea5..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountInfoFlow.java +++ /dev/null @@ -1,121 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeoutException; - -/** - * Account Info Flow object. - */ -public class AccountInfoFlow { - - private static PublicKey getAccountPublicKey( - Client client, - AccountId accountId - ) throws PrecheckStatusException, TimeoutException { - return requirePublicKey(accountId, new AccountInfoQuery().setAccountId(accountId).execute(client).key); - } - - private static CompletableFuture getAccountPublicKeyAsync(Client client, AccountId accountId) { - return new AccountInfoQuery().setAccountId(accountId).executeAsync(client).thenApply(accountInfo -> { - return requirePublicKey(accountId, accountInfo.key); - }); - } - - private static PublicKey requirePublicKey(AccountId accountId, Key key) { - if (key instanceof PublicKey k) { - return k; - } - throw new UnsupportedOperationException("Account " + accountId + " has a KeyList key, which is not supported"); - } - - /** - * Is the signature valid. - * - * @param client the client - * @param accountId the account id - * @param message the message - * @param signature the signature - * @return is the signature valid - * @throws PrecheckStatusException when the precheck fails - * @throws TimeoutException when the transaction times out - */ - public static boolean verifySignature( - Client client, - AccountId accountId, - byte[] message, - byte[] signature - ) throws PrecheckStatusException, TimeoutException { - return getAccountPublicKey(client, accountId).verify(message, signature); - } - - /** - * Is the transaction signature valid. - * - * @param client the client - * @param accountId the account id - * @param transaction the signed transaction - * @return is the transaction signature valid - * @throws PrecheckStatusException when the precheck fails - * @throws TimeoutException when the transaction times out - */ - public static boolean verifyTransactionSignature( - Client client, - AccountId accountId, - Transaction transaction - ) throws PrecheckStatusException, TimeoutException { - return getAccountPublicKey(client, accountId).verifyTransaction(transaction); - } - - /** - * Asynchronously determine if the signature is valid. - * - * @param client the client - * @param accountId the account id - * @param message the message - * @param signature the signature - * @return is the signature valid - */ - public static CompletableFuture verifySignatureAsync( - Client client, - AccountId accountId, - byte[] message, - byte[] signature - ) { - return getAccountPublicKeyAsync(client, accountId).thenApply(pubKey -> pubKey.verify(message, signature)); - } - - /** - * Asynchronously determine if the signature is valid. - * - * @param client the client - * @param accountId the account id - * @param transaction the signed transaction - * @return is the signature valid - */ - public static CompletableFuture verifyTransactionSignatureAsync( - Client client, - AccountId accountId, - Transaction transaction - ) { - return getAccountPublicKeyAsync(client, accountId).thenApply(pubKey -> pubKey.verifyTransaction(transaction)); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountInfoQuery.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountInfoQuery.java deleted file mode 100644 index 8b9aa64096..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountInfoQuery.java +++ /dev/null @@ -1,114 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.CryptoGetInfoQuery; -import com.hedera.hashgraph.sdk.proto.CryptoServiceGrpc; -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import com.hedera.hashgraph.sdk.proto.Response; -import com.hedera.hashgraph.sdk.proto.ResponseHeader; -import io.grpc.MethodDescriptor; -import java.util.Objects; -import java.util.concurrent.CompletableFuture; -import javax.annotation.Nullable; - -/** - * Get all the information about an account, including the balance. - * This does not get the list of account records. - */ -public final class AccountInfoQuery extends Query { - - @Nullable - private AccountId accountId = null; - - /** - * Constructor. - */ - public AccountInfoQuery() { - } - - /** - * Extract the account id. - * - * @return the account id - */ - @Nullable - public AccountId getAccountId() { - return accountId; - } - - /** - * Sets the account ID for which information is requested. - * - * @param accountId The AccountId to be set - * @return {@code this} - */ - public AccountInfoQuery setAccountId(AccountId accountId) { - Objects.requireNonNull(accountId); - this.accountId = accountId; - return this; - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (accountId != null) { - accountId.validateChecksum(client); - } - } - - @Override - void onMakeRequest(com.hedera.hashgraph.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { - var builder = CryptoGetInfoQuery.newBuilder(); - - if (accountId != null) { - builder.setAccountID(accountId.toProtobuf()); - } - - queryBuilder.setCryptoGetInfo(builder.setHeader(header)); - } - - @Override - ResponseHeader mapResponseHeader(Response response) { - return response.getCryptoGetInfo().getHeader(); - } - - @Override - QueryHeader mapRequestHeader(com.hedera.hashgraph.sdk.proto.Query request) { - return request.getCryptoGetInfo().getHeader(); - } - - @Override - AccountInfo mapResponse(Response response, AccountId nodeId, com.hedera.hashgraph.sdk.proto.Query request) { - return AccountInfo.fromProtobuf(response.getCryptoGetInfo().getAccountInfo()); - } - - @Override - MethodDescriptor getMethodDescriptor() { - return CryptoServiceGrpc.getGetAccountInfoMethod(); - } - - @Override - public CompletableFuture getCostAsync(Client client) { - // deleted accounts return a COST_ANSWER of zero which triggers `INSUFFICIENT_TX_FEE` - // if you set that as the query payment; 25 tinybar seems to be enough to get - // `ACCOUNT_DELETED` back instead. - return super.getCostAsync(client).thenApply((cost) -> Hbar.fromTinybars(Math.max(cost.toTinybars(), 25))); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountRecordsQuery.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountRecordsQuery.java deleted file mode 100644 index d8237bf2a1..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountRecordsQuery.java +++ /dev/null @@ -1,114 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.CryptoGetAccountRecordsQuery; -import com.hedera.hashgraph.sdk.proto.CryptoServiceGrpc; -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import com.hedera.hashgraph.sdk.proto.Response; -import com.hedera.hashgraph.sdk.proto.ResponseHeader; -import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -/** - * Get all the records for an account for any transfers into it and out of it, - * that were above the threshold, during the last 25 hours. - */ -public final class AccountRecordsQuery extends Query, AccountRecordsQuery> { - @Nullable - private AccountId accountId = null; - - /** - * Constructor. - */ - public AccountRecordsQuery() { - } - - /** - * Extract the account id. - * - * @return the account id - */ - @Nullable - public AccountId getAccountId() { - return accountId; - } - - /** - * Sets the account ID for which the records should be retrieved. - * - * @param accountId The AccountId to be set - * @return {@code this} - */ - public AccountRecordsQuery setAccountId(AccountId accountId) { - Objects.requireNonNull(accountId); - this.accountId = accountId; - return this; - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (accountId != null) { - accountId.validateChecksum(client); - } - } - - @Override - void onMakeRequest(com.hedera.hashgraph.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { - var builder = CryptoGetAccountRecordsQuery.newBuilder(); - - if (accountId != null) { - builder.setAccountID(accountId.toProtobuf()); - } - - queryBuilder.setCryptoGetAccountRecords(builder.setHeader(header)); - } - - @Override - ResponseHeader mapResponseHeader(Response response) { - return response.getCryptoGetAccountRecords().getHeader(); - } - - @Override - QueryHeader mapRequestHeader(com.hedera.hashgraph.sdk.proto.Query request) { - return request.getCryptoGetAccountRecords().getHeader(); - } - - @Override - List mapResponse(Response response, AccountId nodeId, com.hedera.hashgraph.sdk.proto.Query request) { - var rawTransactionRecords = response.getCryptoGetAccountRecords().getRecordsList(); - var transactionRecords = new ArrayList(rawTransactionRecords.size()); - - for (var record : rawTransactionRecords) { - transactionRecords.add(TransactionRecord.fromProtobuf(record)); - } - - return transactionRecords; - } - - @Override - MethodDescriptor getMethodDescriptor() { - return CryptoServiceGrpc.getGetAccountRecordsMethod(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountStakersQuery.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountStakersQuery.java deleted file mode 100644 index 0ba4772d60..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountStakersQuery.java +++ /dev/null @@ -1,116 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.CryptoGetStakersQuery; -import com.hedera.hashgraph.sdk.proto.CryptoServiceGrpc; -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import com.hedera.hashgraph.sdk.proto.Response; -import com.hedera.hashgraph.sdk.proto.ResponseHeader; -import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -/** - * Get all the accounts that are proxy staking to this account. - * For each of them, give the amount currently staked. - *

- * This is not yet implemented, but will be in a future version of the API. - */ -public final class AccountStakersQuery extends Query, AccountStakersQuery> { - @Nullable - private AccountId accountId = null; - - /** - * Constructor. - */ - public AccountStakersQuery() { - } - - /** - * Extract the account id. - * - * @return the account id - */ - @Nullable - public AccountId getAccountId() { - return accountId; - } - - /** - * Sets the Account ID for which the records should be retrieved. - * - * @param accountId The AccountId to be set - * @return {@code this} - */ - public AccountStakersQuery setAccountId(AccountId accountId) { - Objects.requireNonNull(accountId); - this.accountId = accountId; - return this; - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (accountId != null) { - accountId.validateChecksum(client); - } - } - - @Override - void onMakeRequest(com.hedera.hashgraph.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { - var builder = CryptoGetStakersQuery.newBuilder(); - - if (accountId != null) { - builder.setAccountID(accountId.toProtobuf()); - } - - queryBuilder.setCryptoGetProxyStakers(builder.setHeader(header)); - } - - @Override - ResponseHeader mapResponseHeader(Response response) { - return response.getCryptoGetProxyStakers().getHeader(); - } - - @Override - QueryHeader mapRequestHeader(com.hedera.hashgraph.sdk.proto.Query request) { - return request.getCryptoGetProxyStakers().getHeader(); - } - - @Override - List mapResponse(Response response, AccountId nodeId, com.hedera.hashgraph.sdk.proto.Query request) { - var rawStakers = response.getCryptoGetProxyStakers().getStakers(); - var stakers = new ArrayList(rawStakers.getProxyStakerCount()); - - for (var i = 0; i < rawStakers.getProxyStakerCount(); ++i) { - stakers.add(ProxyStaker.fromProtobuf(rawStakers.getProxyStaker(i))); - } - - return stakers; - } - - @Override - MethodDescriptor getMethodDescriptor() { - return CryptoServiceGrpc.getGetStakersByAccountIDMethod(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/AddressBookQuery.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/AddressBookQuery.java deleted file mode 100644 index aa101b384c..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/AddressBookQuery.java +++ /dev/null @@ -1,293 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.mirror.NetworkServiceGrpc; -import io.grpc.CallOptions; -import io.grpc.ClientCall; -import io.grpc.Deadline; -import io.grpc.Status; -import io.grpc.StatusRuntimeException; -import io.grpc.stub.ClientCalls; -import io.grpc.stub.StreamObserver; -import java.time.Duration; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; -import javax.annotation.Nonnegative; -import javax.annotation.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Query the mirror node for the address book. - */ -public class AddressBookQuery { - private static final Logger LOGGER = LoggerFactory.getLogger(AddressBookQuery.class); - - @Nullable - private FileId fileId = null; - @Nullable - private Integer limit = null; - private int maxAttempts = 10; - private Duration maxBackoff = Duration.ofSeconds(8L); - - /** - * Constructor. - */ - public AddressBookQuery() { - } - - private static boolean shouldRetry(Throwable throwable) { - if (throwable instanceof StatusRuntimeException statusRuntimeException) { - var code = statusRuntimeException.getStatus().getCode(); - var description = statusRuntimeException.getStatus().getDescription(); - - return (code == io.grpc.Status.Code.UNAVAILABLE) || - (code == io.grpc.Status.Code.RESOURCE_EXHAUSTED) || - (code == Status.Code.INTERNAL && description != null && Executable.RST_STREAM.matcher(description) - .matches()); - } - - return false; - } - - /** - * Extract the file id. - * - * @return the file id that was assigned - */ - @Nullable - public FileId getFileId() { - return fileId; - } - - /** - * Assign the file id of address book to retrieve. - * - * @param fileId the file id of the address book - * @return {@code this} - */ - public AddressBookQuery setFileId(FileId fileId) { - this.fileId = fileId; - return this; - } - - /** - * Extract the limit number. - * - * @return the limit number that was assigned - */ - @Nullable - public Integer getLimit() { - return limit; - } - - /** - * Assign the number of node addresses to retrieve or all nodes set to 0. - * - * @param limit number of node addresses to get - * @return {@code this} - */ - public AddressBookQuery setLimit(@Nullable @Nonnegative Integer limit) { - this.limit = limit; - return this; - } - - /** - * Extract the maximum number of attempts. - * - * @return the maximum number of attempts - */ - public int getMaxAttempts() { - return maxAttempts; - } - - /** - * Assign the maximum number of attempts. - * - * @param maxAttempts the maximum number of attempts - * @return {@code this} - */ - public AddressBookQuery setMaxAttempts(@Nonnegative int maxAttempts) { - this.maxAttempts = maxAttempts; - return this; - } - - /** - * Assign the maximum backoff duration. - * - * @param maxBackoff the maximum backoff duration - * @return {@code this} - */ - public AddressBookQuery setMaxBackoff(Duration maxBackoff) { - Objects.requireNonNull(maxBackoff); - if (maxBackoff.toMillis() < 500L) { - throw new IllegalArgumentException("maxBackoff must be at least 500 ms"); - } - this.maxBackoff = maxBackoff; - return this; - } - - /** - * Execute the query with preset timeout. - * - * @param client the client object - * @return the node address book - */ - public NodeAddressBook execute(Client client) { - return execute(client, client.getRequestTimeout()); - } - - /** - * Execute the query with user supplied timeout. - * - * @param client the client object - * @param timeout the user supplied timeout - * @return the node address book - */ - public NodeAddressBook execute(Client client, Duration timeout) { - var deadline = Deadline.after(timeout.toMillis(), TimeUnit.MILLISECONDS); - for (int attempt = 1; true; attempt++) { - try { - var addressProtoIter = ClientCalls.blockingServerStreamingCall( - buildCall(client, deadline), - buildQuery() - ); - List addresses = new ArrayList<>(); - while (addressProtoIter.hasNext()) { - addresses.add(NodeAddress.fromProtobuf(addressProtoIter.next())); - } - return new NodeAddressBook().setNodeAddresses(addresses); - } catch (Throwable error) { - if (!shouldRetry(error) || attempt >= maxAttempts) { - LOGGER.error("Error attempting to get address book at FileId {}", fileId, error); - throw error; - } - warnAndDelay(attempt, error); - } - } - } - - /** - * Execute the query with preset timeout asynchronously. - * - * @param client the client object - * @return the node address book - */ - public CompletableFuture executeAsync(Client client) { - return executeAsync(client, client.getRequestTimeout()); - } - - /** - * Execute the query with user supplied timeout. - * - * @param client the client object - * @param timeout the user supplied timeout - * @return the node address book - */ - public CompletableFuture executeAsync(Client client, Duration timeout) { - var deadline = Deadline.after(timeout.toMillis(), TimeUnit.MILLISECONDS); - CompletableFuture returnFuture = new CompletableFuture<>(); - executeAsync(client, deadline, returnFuture, 1); - return returnFuture; - } - - /** - * Execute the query. - * - * @param client the client object - * @param deadline the user supplied timeout - * @param returnFuture returned promise callback - * @param attempt maximum number of attempts - */ - void executeAsync(Client client, Deadline deadline, CompletableFuture returnFuture, int attempt) { - List addresses = new ArrayList<>(); - ClientCalls.asyncServerStreamingCall( - buildCall(client, deadline), - buildQuery(), - new StreamObserver() { - @Override - public void onNext(com.hedera.hashgraph.sdk.proto.NodeAddress addressProto) { - addresses.add(NodeAddress.fromProtobuf(addressProto)); - } - - @Override - public void onError(Throwable error) { - if (attempt >= maxAttempts || !shouldRetry(error)) { - LOGGER.error("Error attempting to get address book at FileId {}", fileId, error); - returnFuture.completeExceptionally(error); - return; - } - warnAndDelay(attempt, error); - addresses.clear(); - executeAsync(client, deadline, returnFuture, attempt + 1); - } - - @Override - public void onCompleted() { - returnFuture.complete(new NodeAddressBook().setNodeAddresses(addresses)); - } - }); - } - - /** - * Build the address book query. - * - * @return {@link com.hedera.hashgraph.sdk.proto.mirror.AddressBookQuery buildQuery } - */ - com.hedera.hashgraph.sdk.proto.mirror.AddressBookQuery buildQuery() { - var builder = com.hedera.hashgraph.sdk.proto.mirror.AddressBookQuery.newBuilder(); - if (fileId != null) { - builder.setFileId(fileId.toProtobuf()); - } - if (limit != null) { - builder.setLimit(limit); - } - return builder.build(); - } - - private ClientCall - buildCall(Client client, Deadline deadline) { - try { - return client.mirrorNetwork.getNextMirrorNode().getChannel().newCall( - NetworkServiceGrpc.getGetNodesMethod(), - CallOptions.DEFAULT.withDeadline(deadline) - ); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - - private void warnAndDelay(int attempt, Throwable error) { - var delay = Math.min(500 * (long) Math.pow(2, attempt), maxBackoff.toMillis()); - LOGGER.warn( - "Error fetching address book at FileId {} during attempt #{}. Waiting {} ms before next attempt: {}", - fileId, attempt, delay, error.getMessage()); - - try { - Thread.sleep(delay); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/AssessedCustomFee.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/AssessedCustomFee.java deleted file mode 100644 index ccd64c17a6..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/AssessedCustomFee.java +++ /dev/null @@ -1,134 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.google.protobuf.InvalidProtocolBufferException; - -import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.List; - -/** - * A custom transfer fee that was assessed during the handling of a CryptoTransfer. - */ -public class AssessedCustomFee { - /** - * The number of units assessed for the fee - */ - public final long amount; - - /** - * The denomination of the fee; taken as hbar if left unset - */ - @Nullable - public final TokenId tokenId; - - /** - * The account to receive the assessed fee - */ - @Nullable - public final AccountId feeCollectorAccountId; - - /** - * The account(s) whose final balances would have been higher in the absence of this assessed fee - */ - public final List payerAccountIdList; - - AssessedCustomFee( - long amount, - @Nullable TokenId tokenId, - @Nullable AccountId feeCollectorAccountId, - List payerAccountIdList - ) { - this.amount = amount; - this.tokenId = tokenId; - this.feeCollectorAccountId = feeCollectorAccountId; - this.payerAccountIdList = payerAccountIdList; - } - - /** - * Convert the protobuf object to an assessed custom fee object. - * - * @param assessedCustomFee protobuf response object - * @return the converted assessed custom fee object - */ - static AssessedCustomFee fromProtobuf(com.hedera.hashgraph.sdk.proto.AssessedCustomFee assessedCustomFee) { - var payerList = new ArrayList(assessedCustomFee.getEffectivePayerAccountIdCount()); - for (var payerId : assessedCustomFee.getEffectivePayerAccountIdList()) { - payerList.add(AccountId.fromProtobuf(payerId)); - } - return new AssessedCustomFee( - assessedCustomFee.getAmount(), - assessedCustomFee.hasTokenId() ? TokenId.fromProtobuf(assessedCustomFee.getTokenId()) : null, - assessedCustomFee.hasFeeCollectorAccountId() ? AccountId.fromProtobuf(assessedCustomFee.getFeeCollectorAccountId()) : null, - payerList - ); - } - - /** - * Convert a byte array into an assessed custom fee object. - * - * @param bytes the byte array - * @return the converted assessed custom fee object - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - public static AssessedCustomFee fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return fromProtobuf(com.hedera.hashgraph.sdk.proto.AssessedCustomFee.parseFrom(bytes).toBuilder().build()); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("amount", amount) - .add("tokenId", tokenId) - .add("feeCollectorAccountId", feeCollectorAccountId) - .add("payerAccountIdList", payerAccountIdList) - .toString(); - } - - /** - * Create the protobuf representation. - * - * @return {@link com.hedera.hashgraph.sdk.proto.AssessedCustomFee} - */ - com.hedera.hashgraph.sdk.proto.AssessedCustomFee toProtobuf() { - var builder = com.hedera.hashgraph.sdk.proto.AssessedCustomFee.newBuilder().setAmount(amount); - if (tokenId != null) { - builder.setTokenId(tokenId.toProtobuf()); - } - if (feeCollectorAccountId != null) { - builder.setFeeCollectorAccountId(feeCollectorAccountId.toProtobuf()); - } - for (var payerId : payerAccountIdList) { - builder.addEffectivePayerAccountId(payerId.toProtobuf()); - } - return builder.build(); - } - - /** - * Create a byte array representation. - * - * @return the converted assessed custom fees - */ - public byte[] toBytes() { - return toProtobuf().toByteArray(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/BadEntityIdException.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/BadEntityIdException.java deleted file mode 100644 index 70067aec06..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/BadEntityIdException.java +++ /dev/null @@ -1,64 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -/** - * Custom exception thrown by the entity helper validate method when the account id and checksum are invalid. - */ -public class BadEntityIdException extends Exception { - /** - * the shard portion of the account id - */ - public final long shard; - /** - * the realm portion of the account id - */ - public final long realm; - /** - * the num portion of the account id - */ - public final long num; - /** - * the user supplied checksum - */ - public final String presentChecksum; - /** - * the calculated checksum - */ - public final String expectedChecksum; - - /** - * Constructor. - * - * @param shard the shard portion of the account id - * @param realm the realm portion of the account id - * @param num the num portion of the account id - * @param presentChecksum the user supplied checksum - * @param expectedChecksum the calculated checksum - */ - BadEntityIdException(long shard, long realm, long num, String presentChecksum, String expectedChecksum) { - super(String.format("Entity ID %d.%d.%d-%s was incorrect.", shard, realm, num, presentChecksum)); - this.shard = shard; - this.realm = realm; - this.num = num; - this.presentChecksum = presentChecksum; - this.expectedChecksum = expectedChecksum; - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/BadKeyException.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/BadKeyException.java deleted file mode 100644 index 594566d8ce..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/BadKeyException.java +++ /dev/null @@ -1,42 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -/** - * Signals that a key could not be realized from the given input. - *

- * This exception can be raised by any of the {@code from} methods - * on {@link PrivateKey} or {@link PublicKey}. - */ -public final class BadKeyException extends IllegalArgumentException { - /** - * @param message the message - */ - BadKeyException(String message) { - super(message); - } - - /** - * @param cause the cause - */ - BadKeyException(Throwable cause) { - super(cause); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/BadMnemonicException.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/BadMnemonicException.java deleted file mode 100644 index 3e005650ae..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/BadMnemonicException.java +++ /dev/null @@ -1,72 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import javax.annotation.Nullable; -import java.util.List; - -/** - * Custom exception for when there are issues with the mnemonic. - */ -public class BadMnemonicException extends Exception { - /** - * The mnemonic that failed validation. - */ - public final Mnemonic mnemonic; - - /** - * The reason for which the mnemonic failed validation. - */ - public final BadMnemonicReason reason; - - /** - * If not null, these are the indices in the mnemonic that were not found in the - * BIP-39 standard English word list. - *

- * If {@code reason == BadMnemonicReason.UnknownWords} then this will be not null. - */ - @Nullable - public final List unknownWordIndices; - - /** - * Constructor. - * - * @param mnemonic the mnemonic - * @param reason the reason - * @param unknownWordIndices the indices - */ - BadMnemonicException(Mnemonic mnemonic, BadMnemonicReason reason, List unknownWordIndices) { - this.mnemonic = mnemonic; - this.reason = reason; - this.unknownWordIndices = unknownWordIndices; - } - - /** - * Constructor. - * - * @param mnemonic the mnemonic - * @param reason the reason - */ - BadMnemonicException(Mnemonic mnemonic, BadMnemonicReason reason) { - this.mnemonic = mnemonic; - this.reason = reason; - this.unknownWordIndices = null; - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/BadMnemonicReason.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/BadMnemonicReason.java deleted file mode 100644 index 7be4ec48ff..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/BadMnemonicReason.java +++ /dev/null @@ -1,61 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -/** - * Possible reason why a {@link Mnemonic} failed validation. - */ -public enum BadMnemonicReason { - /** - * The mnemonic did not contain exactly 24 words. - */ - BadLength, - - /** - * The mnemonic contained words which were not found in the BIP-39 standard English word list. - *

- * {@link BadMnemonicException#unknownWordIndices} will be set with the list of word indices - * in {@link Mnemonic#words} which were not found in the standard word list. - * - * @see BIP-39 - * English word list. - */ - UnknownWords, - - /** - * The checksum encoded in the mnemonic did not match the checksum we just calculated for - * that mnemonic. - *

- * 24-word mnemonics have an 8-bit checksum that is appended to the 32 bytes of source entropy - * after being calculated from it, before being encoded into words. This status is returned if - * {@link Mnemonic#validate()} calculated a different checksum for the mnemonic than that which - * was encoded into it. - *

- * This could happen if two or more of the words were entered out of the original order or - * replaced with another from the standard word list (as this is only returned if all the words - * exist in the word list). - */ - ChecksumMismatch, - /** - * The given mnemonic doesn't contain 22 words required to be a legacy mnemonic, or the words are - * not in the legacy list. - */ - NotLegacy -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/ChunkedTransaction.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/ChunkedTransaction.java deleted file mode 100644 index 876845179d..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/ChunkedTransaction.java +++ /dev/null @@ -1,533 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.SignatureMap; -import com.hedera.hashgraph.sdk.proto.SignedTransaction; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionID; -import java.time.Duration; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; -import java.util.concurrent.TimeoutException; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Function; -import javax.annotation.Nullable; - -/** - * A common base for file and topic message transactions. - */ -abstract class ChunkedTransaction> extends Transaction { - private int chunkSize = 1024; - - /** - * The transaction data - */ - protected ByteString data = ByteString.EMPTY; - - /** - * Maximum number of chunks this message will get broken up into when - * it's frozen. - */ - private int maxChunks = 20; - - /** - * Constructor. - * - * @param txs Compound list of transaction id's list of (AccountId, Transaction) records - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - ChunkedTransaction(LinkedHashMap> txs) throws InvalidProtocolBufferException { - super(txs); - } - - /** - * Constructor. - * - * @param txBody protobuf TransactionBody - */ - ChunkedTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { - super(txBody); - } - - /** - * Constructor. - */ - ChunkedTransaction() { - super(); - } - - /** - * Extract the data. - * - * @return the data - */ - ByteString getData() { - return data; - } - - /** - * Assign the data via a byte array. - * - * @param data the byte array - * @return {@code this} - */ - T setData(byte[] data) { - requireNotFrozen(); - this.data = ByteString.copyFrom(data); - - // noinspection unchecked - return (T) this; - } - - /** - * Assign the data via a byte string. - * - * @param data the byte string - * @return {@code this} - */ - T setData(ByteString data) { - requireNotFrozen(); - this.data = data; - - // noinspection unchecked - return (T) this; - } - - /** - * Assign the data via a string. - * - * @param text the byte array - * @return {@code this} - */ - T setData(String text) { - requireNotFrozen(); - this.data = ByteString.copyFromUtf8(text); - - // noinspection unchecked - return (T) this; - } - - /** - * Retrieve the maximum number of chunks. - * - * @return the number of chunks - */ - public int getMaxChunks() { - return maxChunks; - } - - /** - * Assign the max number of chunks. - * - * @param maxChunks the number of chunks - * @return {@code this} - */ - public T setMaxChunks(int maxChunks) { - requireNotFrozen(); - this.maxChunks = maxChunks; - - // noinspection unchecked - return (T) this; - } - - /** - * Retrieve the chunk size. - * - * @return the chunk size - */ - public int getChunkSize() { - return chunkSize; - } - - /** - * Assign the chunk size. - * - * @param chunkSize the chunk size - * @return {@code this} - */ - public T setChunkSize(int chunkSize) { - requireNotFrozen(); - this.chunkSize = chunkSize; - - // noinspection unchecked - return (T) this; - } - - @Override - public byte[] getTransactionHash() { - if (outerTransactions.size() > nodeAccountIds.size()) { - throw new IllegalStateException("a single transaction hash can not be calculated for a chunked transaction, try calling `getAllTransactionHashesPerNode`"); - } - - return super.getTransactionHash(); - } - - @Override - public Map getTransactionHashPerNode() { - if (outerTransactions.size() > nodeAccountIds.size()) { - throw new IllegalStateException("a single transaction hash can not be calculated for a chunked transaction, try calling `getAllTransactionHashesPerNode`"); - } - - return super.getTransactionHashPerNode(); - } - - /** - * Extract the list of transaction hashes. - * - * @return the list of transaction hashes - */ - public final List> getAllTransactionHashesPerNode() { - if (!this.isFrozen()) { - throw new IllegalStateException("transaction must have been frozen before calculating the hash will be stable, try calling `freeze`"); - } - - transactionIds.setLocked(true); - nodeAccountIds.setLocked(true); - - buildAllTransactions(); - - var txCount = transactionIds.size(); - var nodeCount = nodeAccountIds.size(); - var transactionHashes = new ArrayList>(txCount); - - for (var txIndex = 0; txIndex < txCount; ++txIndex) { - var hashes = new HashMap(); - var offset = txIndex * nodeCount; - - for (var nodeIndex = 0; nodeIndex < nodeCount; ++nodeIndex) { - hashes.put( - nodeAccountIds.get(nodeIndex), - hash(outerTransactions.get(offset + nodeIndex).getSignedTransactionBytes().toByteArray())); - } - - transactionHashes.add(hashes); - } - - return transactionHashes; - } - - @Override - public T addSignature(PublicKey publicKey, byte[] signature) { - if (data.size() > chunkSize) { - throw new IllegalStateException("Cannot manually add signature to chunked transaction with length greater than " + chunkSize); - } - return super.addSignature(publicKey, signature); - } - - @Override - public Map> getSignatures() { - if (data.size() > chunkSize) { - throw new IllegalStateException("Cannot call getSignatures() on a chunked transaction with length greater than " + chunkSize); - } - return super.getSignatures(); - } - - /** - * Extract the list of all signers. - * - * @return the list of all signatures - */ - public List>> getAllSignatures() { - if (publicKeys.isEmpty()) { - return new ArrayList<>(); - } - - buildAllTransactions(); - - var txCount = transactionIds.size(); - var nodeCount = nodeAccountIds.size(); - - var retval = new ArrayList>>(txCount); - - for (int i = 0; i < txCount; i++) { - retval.add(getSignaturesAtOffset(i * nodeCount)); - } - - return retval; - } - - private void freezeAndSign(Client client) { - if (!isFrozen()) { - freezeWith(client); - } - - var operatorId = client.getOperatorAccountId(); - - if (operatorId != null && operatorId.equals(Objects.requireNonNull(getTransactionIdInternal().accountId))) { - // on execute, sign each transaction with the operator, if present - // and we are signing a transaction that used the default transaction ID - signWithOperator(client); - } - } - - @Override - public TransactionResponse execute(Client client, Duration timeoutPerChunk) throws TimeoutException, PrecheckStatusException { - return executeAll(client, timeoutPerChunk).get(0); - } - - /** - * Execute this transaction or query - * - * @param client The client with which this will be executed. - * @return Result of execution for each chunk - * @throws TimeoutException when the transaction times out - * @throws PrecheckStatusException when the precheck fails - */ - public List executeAll(Client client) throws PrecheckStatusException, TimeoutException { - return executeAll(client, client.getRequestTimeout()); - } - - /** - * Execute this transaction or query - * - * @param client The client with which this will be executed. - * @param timeoutPerChunk The timeout after which the execution attempt will be cancelled. - * @return Result of execution for each chunk - * @throws TimeoutException when the transaction times out - * @throws PrecheckStatusException when the precheck fails - */ - public List executeAll(Client client, Duration timeoutPerChunk) throws PrecheckStatusException, TimeoutException { - freezeAndSign(client); - - var responses = new ArrayList(transactionIds.size()); - - for (var i = 0; i < transactionIds.size(); i++) { - var response = super.execute(client, timeoutPerChunk); - - if (shouldGetReceipt()) { - new TransactionReceiptQuery() - .setNodeAccountIds(Collections.singletonList(response.nodeId)) - .setTransactionId(response.transactionId) - .execute(client, timeoutPerChunk); - } - - responses.add(response); - } - - return responses; - } - - /** - * Execute this transaction or query asynchronously. - * - * @param client The client with which this will be executed. - * @return Future result of execution for each chunk - */ - public CompletableFuture> executeAllAsync(Client client) { - return executeAllAsync(client, client.getRequestTimeout()); - } - - /** - * Execute this transaction or query asynchronously. - * - * @param client The client with which this will be executed. - * @param timeoutPerChunk The timeout after which the execution attempt will be cancelled. - * @return Future result of execution for each chunk - */ - public CompletableFuture> executeAllAsync(Client client, Duration timeoutPerChunk) { - freezeAndSign(client); - - CompletableFuture> future = - CompletableFuture.supplyAsync(() -> new ArrayList<>(transactionIds.size())); - - for (var i = 0; i < transactionIds.size(); i++) { - future = future.thenCompose(list -> { - var responseFuture = super.executeAsync(client, timeoutPerChunk); - - Function> receiptFuture = - (TransactionResponse response) -> response.getReceiptAsync(client, timeoutPerChunk) - .thenApply(receipt -> response); - - Function> addToList = - (response) -> { - list.add(response); - return list; - }; - - if (shouldGetReceipt()) { - return responseFuture.thenCompose(receiptFuture).thenApply(addToList); - } else { - return responseFuture.thenApply(addToList); - } - }); - } - - return future; - } - - /** - * Execute this transaction or query asynchronously. - * - * @param client The client with which this will be executed. - * @param callback a BiConsumer which handles the result or error. - */ - public void executeAllAsync(Client client, BiConsumer, Throwable> callback) { - ConsumerHelper.biConsumer(executeAllAsync(client), callback); - } - - /** - * Execute this transaction or query asynchronously. - * - * @param client The client with which this will be executed. - * @param timeout The timeout after which the execution attempt will be cancelled. - * @param callback a BiConsumer which handles the result or error. - */ - public void executeAllAsync(Client client, Duration timeout, BiConsumer, Throwable> callback) { - ConsumerHelper.biConsumer(executeAllAsync(client, timeout), callback); - } - - /** - * Execute this transaction or query asynchronously. - * - * @param client The client with which this will be executed. - * @param onSuccess a Consumer which consumes the result on success. - * @param onFailure a Consumer which consumes the error on failure. - */ - public void executeAllAsync(Client client, Consumer> onSuccess, Consumer onFailure) { - ConsumerHelper.twoConsumers(executeAllAsync(client), onSuccess, onFailure); - } - - /** - * Execute this transaction or query asynchronously. - * - * @param client The client with which this will be executed. - * @param timeout The timeout after which the execution attempt will be cancelled. - * @param onSuccess a Consumer which consumes the result on success. - * @param onFailure a Consumer which consumes the error on failure. - */ - public void executeAllAsync(Client client, Duration timeout, Consumer> onSuccess, Consumer onFailure) { - ConsumerHelper.twoConsumers(executeAllAsync(client, timeout), onSuccess, onFailure); - } - - @Override - public CompletableFuture executeAsync(Client client, Duration timeoutPerChunk) { - return executeAllAsync(client, timeoutPerChunk).thenApply(responses -> responses.get(0)); - } - - @Override - public ScheduleCreateTransaction schedule() { - requireNotFrozen(); - if (!nodeAccountIds.isEmpty()) { - throw new IllegalStateException( - "The underlying transaction for a scheduled transaction cannot have node account IDs set" - ); - } - if (data.size() > chunkSize) { - throw new IllegalStateException("Cannot schedule a chunked transaction with length greater than " + chunkSize); - } - - var bodyBuilder = spawnBodyBuilder(null); - - onFreeze(bodyBuilder); - - onFreezeChunk( - bodyBuilder, - null, - 0, - data.size(), - 1, - 1 - ); - - return doSchedule(bodyBuilder); - } - - @Override - int getRequiredChunks() { - var requiredChunks = (this.data.size() + (chunkSize - 1)) / chunkSize; - - if (requiredChunks == 0) { - requiredChunks = 1; - } - - if (requiredChunks > maxChunks) { - throw new IllegalArgumentException( - "message of " + this.data.size() + " bytes requires " + requiredChunks - + " chunks but the maximum allowed chunks is " + maxChunks + ", try using setMaxChunks"); - } - return requiredChunks; - } - - @Override - void wipeTransactionLists(int requiredChunks) { - sigPairLists = new ArrayList<>(requiredChunks * nodeAccountIds.size()); - outerTransactions = new ArrayList<>(requiredChunks * nodeAccountIds.size()); - innerSignedTransactions = new ArrayList<>(requiredChunks * nodeAccountIds.size()); - - for (int i = 0; i < requiredChunks; i++) { - if (!transactionIds.isEmpty()) { - var startIndex = i * chunkSize; - var endIndex = startIndex + chunkSize; - - if (endIndex > this.data.size()) { - endIndex = this.data.size(); - } - - onFreezeChunk( - Objects.requireNonNull(frozenBodyBuilder).setTransactionID(transactionIds.get(i).toProtobuf()), - transactionIds.get(0).toProtobuf(), - startIndex, - endIndex, - i, - requiredChunks - ); - } - - // For each node we add a transaction with that node - for (var nodeId : nodeAccountIds) { - sigPairLists.add(SignatureMap.newBuilder()); - innerSignedTransactions.add(SignedTransaction.newBuilder() - .setBodyBytes( - frozenBodyBuilder - .setNodeAccountID(nodeId.toProtobuf()) - .build() - .toByteString() - ) - ); - outerTransactions.add(null); - } - } - } - - /** - * A common base for file and topic message transactions. - */ - abstract void onFreezeChunk(TransactionBody.Builder body, @Nullable TransactionID initialTransactionId, int startIndex, int endIndex, int chunk, int total); - - /** - * Should the receipt be retrieved? - * - * @return by default do not get a receipt - */ - boolean shouldGetReceipt() { - return false; - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/ConsumerHelper.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/ConsumerHelper.java deleted file mode 100644 index 4d3c75b38c..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/ConsumerHelper.java +++ /dev/null @@ -1,40 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import java.util.concurrent.CompletableFuture; -import java.util.function.BiConsumer; -import java.util.function.Consumer; - -class ConsumerHelper { - static void biConsumer(CompletableFuture future, BiConsumer consumer) { - future.whenComplete(consumer); - } - - static void twoConsumers(CompletableFuture future, Consumer onSuccess, Consumer onFailure) { - future.whenComplete((output, error) -> { - if (error != null) { - onFailure.accept(error); - } else { - onSuccess.accept(output); - } - }); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractByteCodeQuery.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractByteCodeQuery.java deleted file mode 100644 index 12e368e096..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractByteCodeQuery.java +++ /dev/null @@ -1,104 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import com.hedera.hashgraph.sdk.proto.ContractGetBytecodeQuery; -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import com.hedera.hashgraph.sdk.proto.Response; -import com.hedera.hashgraph.sdk.proto.ResponseHeader; -import com.hedera.hashgraph.sdk.proto.SmartContractServiceGrpc; -import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; -import java.util.Objects; - -/** - * Get the bytecode for a smart contract instance. - */ -public final class ContractByteCodeQuery extends Query { - @Nullable - private ContractId contractId = null; - - /** - * Constructor. - */ - public ContractByteCodeQuery() { - } - - /** - * Extract the contract id. - * - * @return the contract id - */ - @Nullable - public ContractId getContractId() { - return contractId; - } - - /** - * Sets the contract ID for which information is requested. - * - * @param contractId The ContractId to be set - * @return {@code this} - */ - public ContractByteCodeQuery setContractId(ContractId contractId) { - Objects.requireNonNull(contractId); - this.contractId = contractId; - return this; - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (contractId != null) { - contractId.validateChecksum(client); - } - } - - @Override - void onMakeRequest(com.hedera.hashgraph.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { - var builder = ContractGetBytecodeQuery.newBuilder(); - if (contractId != null) { - builder.setContractID(contractId.toProtobuf()); - } - - queryBuilder.setContractGetBytecode(builder.setHeader(header)); - } - - @Override - ResponseHeader mapResponseHeader(Response response) { - return response.getContractGetBytecodeResponse().getHeader(); - } - - @Override - QueryHeader mapRequestHeader(com.hedera.hashgraph.sdk.proto.Query request) { - return request.getContractGetBytecode().getHeader(); - } - - @Override - ByteString mapResponse(Response response, AccountId nodeId, com.hedera.hashgraph.sdk.proto.Query request) { - return response.getContractGetBytecodeResponse().getBytecode(); - } - - @Override - MethodDescriptor getMethodDescriptor() { - return SmartContractServiceGrpc.getContractGetBytecodeMethod(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractInfo.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractInfo.java deleted file mode 100644 index b9eb02bb29..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractInfo.java +++ /dev/null @@ -1,279 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.ContractGetInfoResponse; -import java.time.Duration; -import java.time.Instant; - -import javax.annotation.Nullable; -import java.util.HashMap; -import java.util.Map; - -/** - * Current information on the smart contract instance, including its balance. - */ -public final class ContractInfo { - /** - * ID of the contract instance, in the format used in transactions. - */ - public final ContractId contractId; - - /** - * ID of the cryptocurrency account owned by the contract instance, - * in the format used in transactions. - */ - public final AccountId accountId; - - /** - * ID of both the contract instance and the cryptocurrency account owned by the contract - * instance, in the format used by Solidity. - */ - public final String contractAccountId; - - /** - * The state of the instance and its fields can be modified arbitrarily if this key signs a - * transaction to modify it. If this is null, then such modifications are not possible, - * and there is no administrator that can override the normal operation of this smart - * contract instance. Note that if it is created with no admin keys, then there is no - * administrator to authorize changing the admin keys, so there can never be any admin keys - * for that instance. - */ - @Nullable - public final Key adminKey; - - /** - * The current time at which this contract instance (and its account) is set to expire. - */ - public final Instant expirationTime; - - /** - * The expiration time will extend every this many seconds. If there are insufficient funds, - * then it extends as long as possible. If the account is empty when it expires, - * then it is deleted. - */ - public final Duration autoRenewPeriod; - - /** - * ID of the an account to charge for auto-renewal of this contract. If not set, or set to - * an account with zero hbar balance, the contract's own hbar balance will be used to cover - * auto-renewal fees. - */ - @Nullable - public final AccountId autoRenewAccountId; - - /** - * Number of bytes of storage being used by this instance (which affects the cost to - * extend the expiration time). - */ - public final long storage; - - /** - * The memo associated with the contract (max 100 bytes). - */ - public final String contractMemo; - - /** - * The current balance of the contract. - */ - public final Hbar balance; - - /** - * Whether the contract has been deleted - */ - public final boolean isDeleted; - - /** - * The tokens associated to the contract - */ - public final Map tokenRelationships; - - /** - * The ledger ID the response was returned from; please see HIP-198 for the network-specific IDs. - */ - public final LedgerId ledgerId; - - /** - * Staking metadata for this account. - */ - @Nullable - public final StakingInfo stakingInfo; - - /** - * Constructor. - * - * @param contractId the contract id - * @param accountId the account id - * @param contractAccountId the account id of the owner - * @param adminKey the key that can modify the contract - * @param expirationTime the time that contract will expire - * @param autoRenewPeriod seconds before contract is renewed (funds must be available) - * @param autoRenewAccountId account ID which will be charged for renewing this account - * @param storage number of bytes used by this contract - * @param contractMemo the memo field 100 bytes - * @param balance current balance - * @param isDeleted does it still exist - * @param tokenRelationships list of compound token id and relationship records - * @param ledgerId the ledger id - */ - private ContractInfo( - ContractId contractId, - AccountId accountId, - String contractAccountId, - @Nullable Key adminKey, - Instant expirationTime, - Duration autoRenewPeriod, - @Nullable AccountId autoRenewAccountId, - long storage, - String contractMemo, - Hbar balance, - boolean isDeleted, - Map tokenRelationships, - LedgerId ledgerId, - @Nullable StakingInfo stakingInfo - ) { - this.contractId = contractId; - this.accountId = accountId; - this.contractAccountId = contractAccountId; - this.adminKey = adminKey; - this.expirationTime = expirationTime; - this.autoRenewPeriod = autoRenewPeriod; - this.autoRenewAccountId = autoRenewAccountId; - this.storage = storage; - this.contractMemo = contractMemo; - this.balance = balance; - this.isDeleted = isDeleted; - this.tokenRelationships = tokenRelationships; - this.ledgerId = ledgerId; - this.stakingInfo = stakingInfo; - } - - /** - * Extract the contract from the protobuf. - * - * @param contractInfo the protobuf - * @return the contract object - */ - static ContractInfo fromProtobuf(ContractGetInfoResponse.ContractInfo contractInfo) { - var adminKey = contractInfo.hasAdminKey() - ? Key.fromProtobufKey(contractInfo.getAdminKey()) - : null; - - var tokenRelationships = new HashMap(contractInfo.getTokenRelationshipsCount()); - - for (var relationship : contractInfo.getTokenRelationshipsList()) { - tokenRelationships.put( - TokenId.fromProtobuf(relationship.getTokenId()), - TokenRelationship.fromProtobuf(relationship) - ); - } - - return new ContractInfo( - ContractId.fromProtobuf(contractInfo.getContractID()), - AccountId.fromProtobuf(contractInfo.getAccountID()), - contractInfo.getContractAccountID(), - adminKey, - InstantConverter.fromProtobuf(contractInfo.getExpirationTime()), - DurationConverter.fromProtobuf(contractInfo.getAutoRenewPeriod()), - contractInfo.hasAutoRenewAccountId() ? AccountId.fromProtobuf(contractInfo.getAutoRenewAccountId()) : null, - contractInfo.getStorage(), - contractInfo.getMemo(), - Hbar.fromTinybars(contractInfo.getBalance()), - contractInfo.getDeleted(), - tokenRelationships, - LedgerId.fromByteString(contractInfo.getLedgerId()), - contractInfo.hasStakingInfo() ? StakingInfo.fromProtobuf(contractInfo.getStakingInfo()) : null - ); - } - - /** - * Extract the contract from a byte array. - * - * @param bytes the byte array - * @return the extracted contract - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - public static ContractInfo fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return fromProtobuf(ContractGetInfoResponse.ContractInfo.parseFrom(bytes).toBuilder().build()); - } - - /** - * Build the protobuf. - * - * @return the protobuf representation - */ - ContractGetInfoResponse.ContractInfo toProtobuf() { - var contractInfoBuilder = ContractGetInfoResponse.ContractInfo.newBuilder() - .setContractID(contractId.toProtobuf()) - .setAccountID(accountId.toProtobuf()) - .setContractAccountID(contractAccountId) - .setExpirationTime(InstantConverter.toProtobuf(expirationTime)) - .setAutoRenewPeriod(DurationConverter.toProtobuf(autoRenewPeriod)) - .setStorage(storage) - .setMemo(contractMemo) - .setBalance(balance.toTinybars()) - .setLedgerId(ledgerId.toByteString()); - - if (adminKey != null) { - contractInfoBuilder.setAdminKey(adminKey.toProtobufKey()); - } - - if (stakingInfo != null) { - contractInfoBuilder.setStakingInfo(stakingInfo.toProtobuf()); - } - - if (autoRenewAccountId != null) { - contractInfoBuilder.setAutoRenewAccountId(autoRenewAccountId.toProtobuf()); - } - - return contractInfoBuilder.build(); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("contractId", contractId) - .add("accountId", accountId) - .add("contractAccountId", contractAccountId) - .add("adminKey", adminKey) - .add("expirationTime", expirationTime) - .add("autoRenewPeriod", autoRenewPeriod) - .add("autoRenewAccountId", autoRenewAccountId) - .add("storage", storage) - .add("contractMemo", contractMemo) - .add("balance", balance) - .add("isDeleted", isDeleted) - .add("tokenRelationships", tokenRelationships) - .add("ledgerId", ledgerId) - .add("stakingInfo", stakingInfo) - .toString(); - } - - /** - * Create a byte array representation. - * - * @return the byte array representation - */ - public byte[] toBytes() { - return toProtobuf().toByteArray(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractInfoQuery.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractInfoQuery.java deleted file mode 100644 index 8af4b3d154..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractInfoQuery.java +++ /dev/null @@ -1,114 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.ContractGetInfoQuery; -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import com.hedera.hashgraph.sdk.proto.Response; -import com.hedera.hashgraph.sdk.proto.ResponseHeader; -import com.hedera.hashgraph.sdk.proto.SmartContractServiceGrpc; -import io.grpc.MethodDescriptor; -import java.util.Objects; -import java.util.concurrent.CompletableFuture; -import javax.annotation.Nullable; - -/** - * Get information about a smart contract instance. - *

- * This includes the account that it uses, the file containing its bytecode, - * and the time when it will expire. - */ -public final class ContractInfoQuery extends Query { - @Nullable - private ContractId contractId = null; - - /** - * Constructor. - */ - public ContractInfoQuery() { - } - - /** - * Extract the contract id. - * - * @return the contract id - */ - @Nullable - public ContractId getContractId() { - return contractId; - } - - /** - * Sets the contract ID for which information is requested. - * - * @param contractId The ContractId to be set - * @return {@code this} - */ - public ContractInfoQuery setContractId(ContractId contractId) { - Objects.requireNonNull(contractId); - this.contractId = contractId; - return this; - } - - @Override - public CompletableFuture getCostAsync(Client client) { - // deleted accounts return a COST_ANSWER of zero which triggers `INSUFFICIENT_TX_FEE` - // if you set that as the query payment; 25 tinybar seems to be enough to get - // `CONTRACT_DELETED` back instead. - return super.getCostAsync(client).thenApply((cost) -> Hbar.fromTinybars(Math.max(cost.toTinybars(), 25))); - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (contractId != null) { - contractId.validateChecksum(client); - } - } - - @Override - void onMakeRequest(com.hedera.hashgraph.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { - var builder = ContractGetInfoQuery.newBuilder(); - if (contractId != null) { - builder.setContractID(contractId.toProtobuf()); - } - - queryBuilder.setContractGetInfo(builder.setHeader(header)); - } - - @Override - ResponseHeader mapResponseHeader(Response response) { - return response.getContractGetInfo().getHeader(); - } - - @Override - QueryHeader mapRequestHeader(com.hedera.hashgraph.sdk.proto.Query request) { - return request.getContractGetInfo().getHeader(); - } - - @Override - ContractInfo mapResponse(Response response, AccountId nodeId, com.hedera.hashgraph.sdk.proto.Query request) { - return ContractInfo.fromProtobuf(response.getContractGetInfo().getContractInfo()); - } - - @Override - MethodDescriptor getMethodDescriptor() { - return SmartContractServiceGrpc.getGetContractInfoMethod(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractLogInfo.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractLogInfo.java deleted file mode 100644 index aff32be966..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractLogInfo.java +++ /dev/null @@ -1,139 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.ContractLoginfo; -import org.bouncycastle.util.encoders.Hex; - -import java.util.ArrayList; -import java.util.List; - -/** - * The log information for an event returned by a smart contract function call. - * One function call may return several such events. - */ -public final class ContractLogInfo { - /** - * Address of a contract that emitted the event. - */ - public final ContractId contractId; - - /** - * Bloom filter for a particular log. - */ - public final ByteString bloom; - - /** - * Topics of a particular event. - */ - public final List topics; - - /** - * The event data. - */ - public final ByteString data; - - /** - * Constructor. - * - * @param contractId the contract id - * @param bloom the bloom filter - * @param topics list of topics - * @param data the event data - */ - private ContractLogInfo(ContractId contractId, ByteString bloom, List topics, ByteString data) { - this.contractId = contractId; - this.bloom = bloom; - this.topics = topics; - this.data = data; - } - - /** - * Convert to a protobuf. - * - * @param logInfo the log info object - * @return the protobuf - */ - static ContractLogInfo fromProtobuf(com.hedera.hashgraph.sdk.proto.ContractLoginfo logInfo) { - return new ContractLogInfo( - ContractId.fromProtobuf(logInfo.getContractID()), - logInfo.getBloom(), - logInfo.getTopicList(), - logInfo.getData() - ); - } - - /** - * Create the contract log info from a byte array. - * - * @param bytes the byte array - * @return the contract log info object - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - public static ContractLogInfo fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return fromProtobuf(ContractLoginfo.parseFrom(bytes)); - } - - /** - * Create the protobuf. - * - * @return the protobuf representation - */ - com.hedera.hashgraph.sdk.proto.ContractLoginfo toProtobuf() { - var contractLogInfo = com.hedera.hashgraph.sdk.proto.ContractLoginfo.newBuilder() - .setContractID(contractId.toProtobuf()) - .setBloom(bloom); - - for (ByteString topic : topics) { - contractLogInfo.addTopic(topic); - } - - return contractLogInfo.build(); - } - - /** - * Create the byte array. - * - * @return the byte array representation - */ - public byte[] toBytes() { - return toProtobuf().toByteArray(); - } - - @Override - public String toString() { - var stringHelper = MoreObjects.toStringHelper(this) - .add("contractId", contractId) - .add("bloom", Hex.toHexString(bloom.toByteArray())); - - var topicList = new ArrayList<>(); - - for (var topic : topics) { - topicList.add(Hex.toHexString(topic.toByteArray())); - } - - return stringHelper - .add("topics", topicList) - .toString(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractNonceInfo.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractNonceInfo.java deleted file mode 100644 index 2dc56b54f5..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractNonceInfo.java +++ /dev/null @@ -1,120 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.google.protobuf.InvalidProtocolBufferException; - -import java.util.Objects; - -/** - * Info about a contract account's nonce value. - * A nonce of a contract is only incremented when that contract creates another contract. - */ -public final class ContractNonceInfo { - /** - * Id of the contract - */ - public final ContractId contractId; - - /** - * The current value of the contract account's nonce property - */ - public final Long nonce; - - public ContractNonceInfo( - ContractId contractId, - Long nonce - ) { - this.contractId = contractId; - this.nonce = nonce; - } - - /** - * Extract the contractNonce from the protobuf. - * - * @param contractNonceInfo the protobuf - * @return the contract object - */ - static ContractNonceInfo fromProtobuf(com.hedera.hashgraph.sdk.proto.ContractNonceInfo contractNonceInfo) { - return new ContractNonceInfo( - ContractId.fromProtobuf(contractNonceInfo.getContractId()), - contractNonceInfo.getNonce() - ); - } - - /** - * Extract the contractNonce from a byte array. - * - * @param bytes the byte array - * @return the extracted contract - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - public static ContractNonceInfo fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return fromProtobuf(com.hedera.hashgraph.sdk.proto.ContractNonceInfo.parseFrom(bytes).toBuilder().build()); - } - - /** - * Build the protobuf. - * - * @return the protobuf representation - */ - com.hedera.hashgraph.sdk.proto.ContractNonceInfo toProtobuf() { - return com.hedera.hashgraph.sdk.proto.ContractNonceInfo.newBuilder() - .setContractId(contractId.toProtobuf()) - .setNonce(nonce) - .build(); - } - - @Override - public int hashCode() { - return Objects.hash(contractId, nonce); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof ContractNonceInfo otherInfo)) { - return false; - } - - return contractId.equals(otherInfo.contractId) && nonce.equals(otherInfo.nonce); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("contractId", contractId) - .add("nonce", nonce) - .toString(); - } - - /** - * Create a byte array representation. - * - * @return the byte array representation - */ - public byte[] toBytes() { - return toProtobuf().toByteArray(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractStateChange.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractStateChange.java deleted file mode 100644 index 140404528a..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractStateChange.java +++ /dev/null @@ -1,103 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; - -import java.util.ArrayList; -import java.util.List; - -/** - * @deprecated - User mirror nodes for contract traceability instead - * - * The storage changes to a smart contract's storage as a side effect of the function call. - * See Hedera Documentation - */ -@Deprecated -public class ContractStateChange { - /** - * The contract to which the storage changes apply to - */ - public final ContractId contractId; - - /** - * The list of storage changes - */ - public final List storageChanges; - - /** - * Constructor. - * - * @param contractId the contract id - * @param storageChanges the list of storage change objects - */ - ContractStateChange(ContractId contractId, List storageChanges) { - this.contractId = contractId; - this.storageChanges = storageChanges; - } - - // /** - // * Create contract stage change object from protobuf. - // * - // * @param stateChangeProto the protobuf - // * @return the contract stage change object - // */ - // static ContractStateChange fromProtobuf(com.hedera.hashgraph.sdk.proto.ContractStateChange stateChangeProto) { - // List storageChanges = new ArrayList<>(stateChangeProto.getStorageChangesCount()); - // for (var storageChangeProto : stateChangeProto.getStorageChangesList()) { - // storageChanges.add(StorageChange.fromProtobuf(storageChangeProto)); - // } - // return new ContractStateChange(ContractId.fromProtobuf(stateChangeProto.getContractID()), storageChanges); - // } - // - // /** - // * Create contract stage change object from byte array. - // * - // * @param bytes the byte array - // * @return the contract stage change object - // * @throws InvalidProtocolBufferException when there is an issue with the protobuf - // */ - // public static ContractStateChange fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - // return fromProtobuf(com.hedera.hashgraph.sdk.proto.ContractStateChange.parseFrom(bytes)); - // } - // - // /** - // * Create the protobuf. - // * - // * @return the protobuf representation - // */ - // com.hedera.hashgraph.sdk.proto.ContractStateChange toProtobuf() { - // var builder = com.hedera.hashgraph.sdk.proto.ContractStateChange.newBuilder() - // .setContractID(contractId.toProtobuf()); - // for (var storageChange : storageChanges) { - // builder.addStorageChanges(storageChange.toProtobuf()); - // } - // return builder.build(); - // } - // - // /** - // * Create the byte array. - // * - // * @return the byte array representation - // */ - // public byte[] toBytes() { - // return toProtobuf().toByteArray(); - // } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/CustomFee.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/CustomFee.java deleted file mode 100644 index a659e92878..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/CustomFee.java +++ /dev/null @@ -1,185 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.google.protobuf.InvalidProtocolBufferException; - -import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -/** - * Base class for custom fees. - */ -abstract public class CustomFee { - /** - * The account to receive the custom fee - */ - @Nullable - protected AccountId feeCollectorAccountId = null; - - /** - * If true, exempts all the token's fee collection accounts from this fee - */ - protected boolean allCollectorsAreExempt = false; - - /** - * Constructor. - */ - CustomFee() { - } - - /** - * Convert the protobuf object to a custom fee object. - * - * @param customFee protobuf response object - * @return the converted custom fee object - */ - static CustomFee fromProtobufInner(com.hedera.hashgraph.sdk.proto.CustomFee customFee) { - switch (customFee.getFeeCase()) { - case FIXED_FEE: - return CustomFixedFee.fromProtobuf(customFee.getFixedFee()); - - case FRACTIONAL_FEE: - return CustomFractionalFee.fromProtobuf(customFee.getFractionalFee()); - - case ROYALTY_FEE: - return CustomRoyaltyFee.fromProtobuf(customFee.getRoyaltyFee()); - - default: - throw new IllegalStateException("CustomFee#fromProtobuf: unhandled fee case: " + customFee.getFeeCase()); - } - } - - static CustomFee fromProtobuf(com.hedera.hashgraph.sdk.proto.CustomFee customFee) { - var outFee = fromProtobufInner(customFee); - if (customFee.hasFeeCollectorAccountId()) { - outFee.feeCollectorAccountId = AccountId.fromProtobuf(customFee.getFeeCollectorAccountId()); - } - outFee.allCollectorsAreExempt = customFee.getAllCollectorsAreExempt(); - - return outFee; - } - - /** - * Convert byte array to a custom fee object. - * - * @param bytes the byte array - * @return the converted custom fee object - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - public static CustomFee fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return fromProtobuf(com.hedera.hashgraph.sdk.proto.CustomFee.parseFrom(bytes).toBuilder().build()); - } - - /** - * Create a new copy of a custom fee list. - * - * @param customFees existing custom fee list - * @return new custom fee list - */ - public static List deepCloneList(List customFees) { - var returnCustomFees = new ArrayList(customFees.size()); - for (var fee : customFees) { - returnCustomFees.add(fee.deepClone()); - } - return returnCustomFees; - } - - /** - * Extract the fee collector account id. - * - * @return the fee collector account id - */ - @Nullable - public AccountId getFeeCollectorAccountId() { - return feeCollectorAccountId; - } - - /** - * - * @return whether all fee collectors are exempt from fees - */ - public boolean getAllCollectorsAreExempt() { - return allCollectorsAreExempt; - } - - /** - * Create a deep clone. - * - * @return the correct cloned fee type - */ - abstract CustomFee deepClone(); - - /** - * Verify the validity of the client object. - * - * @param client the configured client - * @throws BadEntityIdException if entity ID is formatted poorly - */ - void validateChecksums(Client client) throws BadEntityIdException { - if (feeCollectorAccountId != null) { - feeCollectorAccountId.validateChecksum(client); - } - } - - /** - * Finalize the builder into the protobuf. - * - * @param customFeeBuilder the builder object - * @return the protobuf - */ - protected com.hedera.hashgraph.sdk.proto.CustomFee finishToProtobuf(com.hedera.hashgraph.sdk.proto.CustomFee.Builder customFeeBuilder) { - if (feeCollectorAccountId != null) { - customFeeBuilder.setFeeCollectorAccountId(feeCollectorAccountId.toProtobuf()); - } - customFeeBuilder.setAllCollectorsAreExempt(allCollectorsAreExempt); - return customFeeBuilder.build(); - } - - /** - * Create the protobuf. - * - * @return the protobuf for the custom fee object - */ - abstract com.hedera.hashgraph.sdk.proto.CustomFee toProtobuf(); - - /** - * Create the byte array. - * - * @return the byte array representing the protobuf - */ - public byte[] toBytes() { - return toProtobuf().toByteArray(); - } - - /** - * Serializes the class to ToStringHelper - * - * @return the {@link com.google.common.base.MoreObjects.ToStringHelper} - */ - protected MoreObjects.ToStringHelper toStringHelper() { - return MoreObjects.toStringHelper(this) - .add("feeCollectorAccountId", feeCollectorAccountId) - .add("allCollectorsAreExempt", allCollectorsAreExempt); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/CustomFeeBase.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/CustomFeeBase.java deleted file mode 100644 index 5f5c195b2c..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/CustomFeeBase.java +++ /dev/null @@ -1,73 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import java.util.Objects; - -abstract class CustomFeeBase > extends CustomFee { - - /** - * Assign the fee collector account id. - * - * @param feeCollectorAccountId the account id of the fee collector - * @return {@code this} - */ - public F setFeeCollectorAccountId(AccountId feeCollectorAccountId) { - this.feeCollectorAccountId = Objects.requireNonNull(feeCollectorAccountId); - // noinspection unchecked - return (F) this; - } - - /** - * If true, exempts all the token's fee collection accounts from this fee. - * (The token's treasury and the above fee_collector_account_id will always - * be exempt. Please see HIP-573 - * for details.) - * - * @param allCollectorsAreExempt whether all fee collectors are exempt from fees - * @return {@code this} - */ - public F setAllCollectorsAreExempt(boolean allCollectorsAreExempt) { - this.allCollectorsAreExempt = allCollectorsAreExempt; - // noinspection unchecked - return (F) this; - } - - abstract F deepCloneSubclass(); - - /** - * Finishes the deep clone by setting the fields of the {@link CustomFeeBase} class - * - * @param source the source object - * @return the cloned object - */ - protected F finishDeepClone(CustomFeeBase source) { - feeCollectorAccountId = source.feeCollectorAccountId; - allCollectorsAreExempt = source.getAllCollectorsAreExempt(); - - // noinspection unchecked - return (F) this; - } - - @Override - CustomFee deepClone() { - return deepCloneSubclass(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/CustomFixedFee.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/CustomFixedFee.java deleted file mode 100644 index e06c955129..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/CustomFixedFee.java +++ /dev/null @@ -1,176 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.hedera.hashgraph.sdk.proto.FixedFee; - -import javax.annotation.Nullable; - -/** - * Custom fixed fee utility class. - * See Hedera Documentation - */ -public class CustomFixedFee extends CustomFeeBase { - private long amount = 0; - /** - * The shard, realm, number of the tokens. - */ - @Nullable - private TokenId denominatingTokenId = null; - - /** - * Constructor. - */ - public CustomFixedFee() { - } - - /** - * Create a custom fixed fee from a fixed fee protobuf. - * - * @param fixedFee the fixed fee protobuf - * @return the new custom fixed fee object - */ - static CustomFixedFee fromProtobuf(FixedFee fixedFee) { - var returnFee = new CustomFixedFee() - .setAmount(fixedFee.getAmount()); - if (fixedFee.hasDenominatingTokenId()) { - returnFee.setDenominatingTokenId(TokenId.fromProtobuf(fixedFee.getDenominatingTokenId())); - } - return returnFee; - } - - @Override - CustomFixedFee deepCloneSubclass() { - return new CustomFixedFee() - .setAmount(amount) - .setDenominatingTokenId(denominatingTokenId) - .finishDeepClone(this); - } - - /** - * Extract the amount. - * - * @return the amount of the fee in tiny bar - */ - public long getAmount() { - return amount; - } - - /** - * Assign the fee amount in tiny bar. - * - * @param amount the amount of the fee in tiny bar - * @return {@code this} - */ - public CustomFixedFee setAmount(long amount) { - this.amount = amount; - return this; - } - - /** - * Extract the fee amount. - * - * @return the fee amount in hbar - */ - public Hbar getHbarAmount() { - return Hbar.fromTinybars(amount); - } - - /** - * Assign the fee amount in hbar. - * - * @param amount the fee amount in hbar - * @return {@code this} - */ - public CustomFixedFee setHbarAmount(Hbar amount) { - denominatingTokenId = null; - this.amount = amount.toTinybars(); - return this; - } - - /** - * Extract the token id. - * - * @return the token id object - */ - @Nullable - public TokenId getDenominatingTokenId() { - return denominatingTokenId; - } - - /** - * Assign the desired token id. - * - * @param tokenId the token id - * @return {@code this} - */ - public CustomFixedFee setDenominatingTokenId(@Nullable TokenId tokenId) { - denominatingTokenId = tokenId; - return this; - } - - /** - * Assign the default token 0.0.0. - * - * @return {@code this} - */ - public CustomFixedFee setDenominatingTokenToSameToken() { - denominatingTokenId = new TokenId(0, 0, 0); - return this; - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - super.validateChecksums(client); - if (denominatingTokenId != null) { - denominatingTokenId.validateChecksum(client); - } - } - - @Override - public String toString() { - return toStringHelper() - .add("amount", getAmount()) - .add("demoninatingTokenId", getDenominatingTokenId()) - .toString(); - } - - /** - * Convert to a protobuf. - * - * @return the protobuf converted object - */ - FixedFee toFixedFeeProtobuf() { - var fixedFeeBuilder = FixedFee.newBuilder() - .setAmount(getAmount()); - if (getDenominatingTokenId() != null) { - fixedFeeBuilder.setDenominatingTokenId(getDenominatingTokenId().toProtobuf()); - } - return fixedFeeBuilder.build(); - } - - @Override - com.hedera.hashgraph.sdk.proto.CustomFee toProtobuf() { - var customFeeBuilder = com.hedera.hashgraph.sdk.proto.CustomFee.newBuilder() - .setFixedFee(toFixedFeeProtobuf()); - return finishToProtobuf(customFeeBuilder); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/CustomFractionalFee.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/CustomFractionalFee.java deleted file mode 100644 index c6d876a477..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/CustomFractionalFee.java +++ /dev/null @@ -1,215 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.Fraction; -import com.hedera.hashgraph.sdk.proto.FractionalFee; - -import java.util.Objects; - -/** - * Custom fractional fee utility class. - * See Hedera Documentation - */ -public class CustomFractionalFee extends CustomFeeBase { - private long numerator = 0; - private long denominator = 1; - private long min = 0; - private long max = 0; - private FeeAssessmentMethod assessmentMethod = FeeAssessmentMethod.INCLUSIVE; - - /** - * Constructor. - */ - public CustomFractionalFee() { - } - - /** - * Create a custom fractional fee from a fee protobuf. - * - * @param fractionalFee the fractional fee protobuf - * @return the new custom fractional fee object - */ - static CustomFractionalFee fromProtobuf(FractionalFee fractionalFee) { - var fraction = fractionalFee.getFractionalAmount(); - return new CustomFractionalFee() - .setNumerator(fraction.getNumerator()) - .setDenominator(fraction.getDenominator()) - .setMin(fractionalFee.getMinimumAmount()) - .setMax(fractionalFee.getMaximumAmount()) - .setAssessmentMethod(FeeAssessmentMethod.valueOf(fractionalFee.getNetOfTransfers())); - } - - @Override - CustomFractionalFee deepCloneSubclass() { - return new CustomFractionalFee() - .setNumerator(numerator) - .setDenominator(denominator) - .setMin(min) - .setMax(max) - .setAssessmentMethod(assessmentMethod) - .finishDeepClone(this); - } - - /** - * Extract the numerator. - * - * @return the numerator - */ - public long getNumerator() { - return numerator; - } - - /** - * Assign the numerator. - * - * @param numerator the numerator - * @return {@code this} - */ - public CustomFractionalFee setNumerator(long numerator) { - this.numerator = numerator; - return this; - } - - /** - * Extract the denominator. - * - * @return the denominator - */ - public long getDenominator() { - return denominator; - } - - /** - * Assign the denominator can not be zero (0). - * - * @param denominator the denominator - * @return {@code this} - */ - public CustomFractionalFee setDenominator(long denominator) { - this.denominator = denominator; - return this; - } - - /** - * Extract the minimum fee amount. - * - * @return the minimum fee amount - */ - public long getMin() { - return min; - } - - /** - * Assign the minimum fee amount. - * - * @param min the fee amount - * @return {@code this} - */ - public CustomFractionalFee setMin(long min) { - this.min = min; - return this; - } - - /** - * Extract the fee amount. - * - * @return the fee amount - */ - public long getMax() { - return max; - } - - /** - * Assign the maximum fee amount. - * - * @param max the fee amount - * @return {@code this} - */ - public CustomFractionalFee setMax(long max) { - this.max = max; - return this; - } - - /** - * Extract the assessment method inclusive / exclusive. - * - * @return the assessment method inclusive / exclusive - */ - public FeeAssessmentMethod getAssessmentMethod() { - return assessmentMethod; - } - - /** - * Assign the assessment method inclusive / exclusive. - *

- * If the assessment method field is set, the token's custom fee is charged - * to the sending account and the receiving account receives the full token - * transfer amount. If this field is set to false, the receiver pays for - * the token custom fees and gets the remaining token balance. - * INCLUSIVE(false) - * EXCLUSIVE(true) - * See Hedera Documentation - * - * @param assessmentMethod inclusive / exclusive - * @return {@code this} - */ - public CustomFractionalFee setAssessmentMethod(FeeAssessmentMethod assessmentMethod) { - Objects.requireNonNull(assessmentMethod); - this.assessmentMethod = assessmentMethod; - return this; - } - - @Override - public String toString() { - return toStringHelper() - .add("numerator", getNumerator()) - .add("denominator", getDenominator()) - .add("min", getMin()) - .add("max", getMax()) - .add("assessmentMethod", getAssessmentMethod()) - .toString(); - } - - /** - * Convert the fractional fee object to a protobuf. - * - * @return the protobuf object - */ - FractionalFee toFractionalFeeProtobuf() { - return FractionalFee.newBuilder() - .setMinimumAmount(getMin()) - .setMaximumAmount(getMax()) - .setFractionalAmount( - Fraction.newBuilder() - .setNumerator(getNumerator()) - .setDenominator(getDenominator()) - ) - .setNetOfTransfers(assessmentMethod.code) - .build(); - } - - @Override - com.hedera.hashgraph.sdk.proto.CustomFee toProtobuf() { - var customFeeBuilder = com.hedera.hashgraph.sdk.proto.CustomFee.newBuilder() - .setFractionalFee(toFractionalFeeProtobuf()); - return finishToProtobuf(customFeeBuilder); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/CustomRoyaltyFee.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/CustomRoyaltyFee.java deleted file mode 100644 index 09e27e67e3..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/CustomRoyaltyFee.java +++ /dev/null @@ -1,170 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.hedera.hashgraph.sdk.proto.Fraction; -import com.hedera.hashgraph.sdk.proto.RoyaltyFee; - -import javax.annotation.Nullable; -import java.util.Objects; - -/** - * Custom royalty fee utility class. - * See Hedera Documentation - */ -public class CustomRoyaltyFee extends CustomFeeBase { - private long numerator = 0; - private long denominator = 1; - @Nullable - private CustomFixedFee fallbackFee = null; - - /** - * Constructor. - */ - public CustomRoyaltyFee() { - } - - /** - * Create a custom royalty fee from a royalty fee protobuf. - * - * @param royaltyFee the royalty fee protobuf - * @return the new royalty fee object - */ - static CustomRoyaltyFee fromProtobuf(RoyaltyFee royaltyFee) { - var fraction = royaltyFee.getExchangeValueFraction(); - var returnFee = new CustomRoyaltyFee() - .setNumerator(fraction.getNumerator()) - .setDenominator(fraction.getDenominator()); - if (royaltyFee.hasFallbackFee()) { - returnFee.fallbackFee = CustomFixedFee.fromProtobuf(royaltyFee.getFallbackFee()); - } - return returnFee; - } - - @Override - CustomRoyaltyFee deepCloneSubclass() { - var returnFee = new CustomRoyaltyFee(); - returnFee.numerator = numerator; - returnFee.denominator = denominator; - returnFee.fallbackFee = fallbackFee != null ? fallbackFee.deepCloneSubclass() : null; - returnFee.feeCollectorAccountId = feeCollectorAccountId; - returnFee.allCollectorsAreExempt = allCollectorsAreExempt; - return returnFee; - - } - - /** - * Extract the numerator. - * - * @return the numerator - */ - public long getNumerator() { - return numerator; - } - - /** - * Assign the numerator. - * - * @param numerator the numerator - * @return {@code this} - */ - public CustomRoyaltyFee setNumerator(long numerator) { - this.numerator = numerator; - return this; - } - - /** - * Extract the denominator. - * - * @return the denominator - */ - public long getDenominator() { - return denominator; - } - - /** - * Assign the denominator can not be zero (0). - * - * @param denominator the denominator - * @return {@code this} - */ - public CustomRoyaltyFee setDenominator(long denominator) { - this.denominator = denominator; - return this; - } - - /** - * The fallback fee is a fixed fee that is charged to the NFT receiver - * when there is no fungible value exchanged with the sender of the NFT. - * - * @param fallbackFee the fallback fee amount - * @return {@code this} - */ - public CustomRoyaltyFee setFallbackFee(CustomFixedFee fallbackFee) { - Objects.requireNonNull(fallbackFee); - this.fallbackFee = fallbackFee.deepCloneSubclass(); - return this; - } - - /** - * Get the fallback fixed fee. - * - * @return the fallback fixed fee - */ - @Nullable - public CustomFixedFee getFallbackFee() { - return fallbackFee != null ? fallbackFee.deepCloneSubclass() : null; - } - - /** - * Convert the royalty fee object to a protobuf. - * - * @return the protobuf object - */ - RoyaltyFee toRoyaltyFeeProtobuf() { - var royaltyFeeBuilder = RoyaltyFee.newBuilder() - .setExchangeValueFraction( - Fraction.newBuilder() - .setNumerator(numerator) - .setDenominator(denominator) - ); - if (fallbackFee != null) { - royaltyFeeBuilder.setFallbackFee(fallbackFee.toFixedFeeProtobuf()); - } - return royaltyFeeBuilder.build(); - } - - @Override - com.hedera.hashgraph.sdk.proto.CustomFee toProtobuf() { - var customFeeBuilder = com.hedera.hashgraph.sdk.proto.CustomFee.newBuilder() - .setRoyaltyFee(toRoyaltyFeeProtobuf()); - return finishToProtobuf(customFeeBuilder); - } - - @Override - public String toString() { - return toStringHelper() - .add("numerator", numerator) - .add("denominator", denominator) - .add("fallbackFee", fallbackFee) - .toString(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/Delayer.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/Delayer.java deleted file mode 100644 index b672c577d7..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/Delayer.java +++ /dev/null @@ -1,83 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import java.time.Duration; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.TimeUnit; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -/** - * Utility class used internally by the sdk. - */ -final class Delayer { - private static final Logger logger = LoggerFactory.getLogger(Delayer.class); - - private static final ScheduledExecutorService SCHEDULER = Executors.newSingleThreadScheduledExecutor(r -> { - Thread t = new Thread(r); - t.setDaemon(true); - return t; - }); - - private static final Duration MIN_DELAY = Duration.ofMillis(500); - - /** - * Constructor. - */ - private Delayer() { - } - - /** - * Set the delay backoff attempts. - * - * @param attempt the attempts - * @param executor the executor - * @return the updated future - */ - static CompletableFuture delayBackOff(int attempt, Executor executor) { - var interval = MIN_DELAY.multipliedBy(ThreadLocalRandom.current().nextLong(1L << attempt)); - - return delayFor(interval.toMillis(), executor); - } - - /** - * Set the delay backoff milliseconds. - * - * @param milliseconds the milliseconds - * @param executor the executor - * @return the updated future - */ - static CompletableFuture delayFor(long milliseconds, Executor executor) { - logger.trace("waiting for {} seconds before trying again", (double) milliseconds / 1000.0); - - return CompletableFuture.runAsync( - () -> { - }, - delayedExecutor(milliseconds, TimeUnit.MILLISECONDS, executor)); - } - - private static Executor delayedExecutor(long delay, TimeUnit unit, Executor executor) { - return r -> SCHEDULER.schedule(() -> executor.execute(r), delay, unit); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/DurationConverter.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/DurationConverter.java deleted file mode 100644 index 9e3b7e1d23..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/DurationConverter.java +++ /dev/null @@ -1,52 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import java.time.Duration; - -/** - * Utility class used internally by the sdk. - */ -final class DurationConverter { - private DurationConverter() { - } - - /** - * Create a duration object from a protobuf. - * - * @param duration the duration protobuf - * @return the duration object - */ - static Duration fromProtobuf(com.hedera.hashgraph.sdk.proto.Duration duration) { - return Duration.ofSeconds(duration.getSeconds()); - } - - /** - * Convert the duration object into a protobuf. - * - * @param duration the duration object - * @return the protobuf - */ - static com.hedera.hashgraph.sdk.proto.Duration toProtobuf(Duration duration) { - return com.hedera.hashgraph.sdk.proto.Duration.newBuilder() - .setSeconds(duration.getSeconds()) - .build(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/Endpoint.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/Endpoint.java deleted file mode 100644 index fc542450e0..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/Endpoint.java +++ /dev/null @@ -1,161 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import com.hedera.hashgraph.sdk.proto.ServiceEndpoint; - -import java.util.Arrays; -import javax.annotation.Nullable; -import java.util.Objects; - -/** - * Utility class used internally by the sdk. - */ -public class Endpoint implements Cloneable { - - @Nullable - byte[] address = null; - - int port; - - String domainName = ""; - - /** - * Constructor. - */ - public Endpoint() { - } - - /** - * Create an endpoint object from a service endpoint protobuf. - * - * @param serviceEndpoint the service endpoint protobuf - * @return the endpoint object - */ - static Endpoint fromProtobuf(ServiceEndpoint serviceEndpoint) { - var port = (int) (serviceEndpoint.getPort() & 0x00000000ffffffffL); - - return new Endpoint() - .setAddress(serviceEndpoint.getIpAddressV4().toByteArray()) - .setPort(port) - .setDomainName(serviceEndpoint.getDomainName()); - } - - /** - * Extract the ipv4 address. - * - * @return the ipv4 address - */ - @Nullable - public byte[] getAddress() { - return address; - } - - /** - * Assign the ipv4 address. - * - * @param address the desired ipv4 address - * @return {@code this} - */ - public Endpoint setAddress(byte[] address) { - this.address = address; - return this; - } - - /** - * Extract the port number. - * - * @return the port number - */ - public int getPort() { - return port; - } - - /** - * Assign the desired port number. - * - * @param port the desired port number - * @return {@code this} - */ - public Endpoint setPort(int port) { - this.port = port; - return this; - } - - /** - * Extract the domain name. - * - * @return the domain name - */ - public String getDomainName() { - return domainName; - } - - /** - * Assign the desired domain name. - * - * @param domainName the desired domain name - * @return {@code this} - */ - public Endpoint setDomainName(String domainName) { - this.domainName = domainName; - return this; - } - - /** - * Create the protobuf. - * - * @return the protobuf representation - */ - ServiceEndpoint toProtobuf() { - var builder = ServiceEndpoint.newBuilder(); - - if (address != null) { - builder.setIpAddressV4(ByteString.copyFrom(address)); - } - - builder.setDomainName(domainName); - - return builder.setPort(port).build(); - } - - @Override - public String toString() { - if (this.domainName != null && !this.domainName.isEmpty()) { - return domainName + ":" + port; - } else { - return ((int) address[0] & 0x000000FF) + "." + ((int) address[1] & 0x000000FF) + "." + - ((int) address[2] & 0x000000FF) + "." + ((int) address[3] & 0x000000FF) + - ":" + port; - } - } - - @Override - public Endpoint clone() { - try { - Endpoint clone = (Endpoint) super.clone(); - clone.address = address != null ? address.clone() : null; - return clone; - } catch (CloneNotSupportedException e) { - throw new AssertionError(); - } - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/EntityIdHelper.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/EntityIdHelper.java deleted file mode 100644 index 7258c7f44e..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/EntityIdHelper.java +++ /dev/null @@ -1,402 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import java.io.IOException; -import java.net.URI; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; -import java.net.http.HttpTimeoutException; -import java.nio.ByteBuffer; -import java.time.Duration; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; -import java.util.regex.Pattern; -import javax.annotation.Nullable; -import org.bouncycastle.util.encoders.DecoderException; -import org.bouncycastle.util.encoders.Hex; - -/** - * Utility class used internally by the sdk. - */ -public class EntityIdHelper { - /** - * The length of a Solidity address in bytes. - */ - static final int SOLIDITY_ADDRESS_LEN = 20; - - /** - * The length of a hexadecimal-encoded Solidity address, in ASCII characters (bytes). - */ - static final int SOLIDITY_ADDRESS_LEN_HEX = SOLIDITY_ADDRESS_LEN * 2; - - private static final Pattern ENTITY_ID_REGEX = Pattern.compile( - "(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-([a-z]{5}))?$"); - - static final Duration MIRROR_NODE_CONNECTION_TIMEOUT = Duration.ofSeconds(30); - - /** - * Constructor. - */ - private EntityIdHelper() { - } - - /** - * Generate an R object from a string. - * - * @param idString the id string - * @param constructObjectWithIdNums the R object generator - * @param - * @return the R type object - */ - static R fromString(String idString, WithIdNums constructObjectWithIdNums) { - var match = ENTITY_ID_REGEX.matcher(idString); - if (!match.find()) { - throw new IllegalArgumentException( - "Invalid ID \"" + idString + "\": format should look like 0.0.123 or 0.0.123-vfmkw" - ); - } - return constructObjectWithIdNums.apply( - Long.parseLong(match.group(1)), - Long.parseLong(match.group(2)), - Long.parseLong(match.group(3)), - match.group(4)); - } - - /** - * Generate an R object from a solidity address. - * - * @param address the string representation - * @param withAddress the R object generator - * @param - * @return the R type object - */ - static R fromSolidityAddress(String address, WithIdNums withAddress) { - return fromSolidityAddress(decodeSolidityAddress(address), withAddress); - } - - private static R fromSolidityAddress(byte[] address, WithIdNums withAddress) { - if (address.length != SOLIDITY_ADDRESS_LEN) { - throw new IllegalArgumentException( - "Solidity addresses must be 20 bytes or 40 hex chars"); - } - - var buf = ByteBuffer.wrap(address); - return withAddress.apply(buf.getInt(), buf.getLong(), buf.getLong(), null); - } - - /** - * Decode the solidity address from a string. - * - * @param address the string representation - * @return the decoded address - */ - static byte[] decodeSolidityAddress(String address) { - address = address.startsWith("0x") ? address.substring(2) : address; - - if (address.length() != SOLIDITY_ADDRESS_LEN_HEX) { - throw new IllegalArgumentException( - "Solidity addresses must be 20 bytes or 40 hex chars"); - } - - try { - return Hex.decode(address); - } catch (DecoderException e) { - throw new IllegalArgumentException("failed to decode Solidity address as hex", e); - } - } - - /** - * Generate a solidity address. - * - * @param shard the shard part - * @param realm the realm part - * @param num the num part - * @return the solidity address - */ - static String toSolidityAddress(long shard, long realm, long num) { - if (Long.highestOneBit(shard) > 32) { - throw new IllegalStateException("shard out of 32-bit range " + shard); - } - - return Hex.toHexString( - ByteBuffer.allocate(20) - .putInt((int) shard) - .putLong(realm) - .putLong(num) - .array()); - } - - /** - * Generate a checksum. - * - * @param ledgerId the ledger id - * @param addr the address - * @return the checksum - */ - static String checksum(LedgerId ledgerId, String addr) { - StringBuilder answer = new StringBuilder(); - List d = new ArrayList<>(); // Digits with 10 for ".", so if addr == "0.0.123" then d == [0, 10, 0, 10, 1, 2, 3] - long s0 = 0; // Sum of even positions (mod 11) - long s1 = 0; // Sum of odd positions (mod 11) - long s = 0; // Weighted sum of all positions (mod p3) - long sh = 0; // Hash of the ledger ID - @SuppressWarnings("UnusedVariable") - long c = 0; // The checksum, as a single number - long p3 = 26 * 26 * 26; // 3 digits in base 26 - long p5 = 26 * 26 * 26 * 26 * 26; // 5 digits in base 26 - long asciiA = Character.codePointAt("a", 0); // 97 - long m = 1_000_003; //min prime greater than a million. Used for the final permutation. - long w = 31; // Sum s of digit values weights them by powers of w. Should be coprime to p5. - - List h = new ArrayList<>(ledgerId.toBytes().length + 6); - for (byte b : ledgerId.toBytes()) { - h.add(b); - } - for (int i = 0; i < 6; i++) { - h.add((byte) 0); - } - for (var i = 0; i < addr.length(); i++) { - d.add(addr.charAt(i) == '.' ? 10 : Integer.parseInt(String.valueOf(addr.charAt(i)), 10)); - } - for (var i = 0; i < d.size(); i++) { - s = (w * s + d.get(i)) % p3; - if (i % 2 == 0) { - s0 = (s0 + d.get(i)) % 11; - } else { - s1 = (s1 + d.get(i)) % 11; - } - } - for (byte b : h) { - // byte is signed in java, have to fake it to make bytes act like they're unsigned - sh = (w * sh + (b < 0 ? 256 + b : b)) % p5; - } - c = ((((addr.length() % 5) * 11 + s0) * 11 + s1) * p3 + s + sh) % p5; - c = (c * m) % p5; - - for (var i = 0; i < 5; i++) { - answer.append((char) (asciiA + (c % 26))); - c /= 26; - } - - return answer.reverse().toString(); - } - - /** - * Validate the configured client. - * - * @param shard the shard part - * @param realm the realm part - * @param num the num part - * @param client the configured client - * @param checksum the checksum - * @throws BadEntityIdException - */ - static void validate(long shard, long realm, long num, Client client, @Nullable String checksum) - throws BadEntityIdException { - if (client.getNetworkName() == null) { - throw new IllegalStateException( - "Can't validate checksum without knowing which network the ID is for. Ensure client's network name is set."); - } - if (checksum != null) { - String expectedChecksum = EntityIdHelper.checksum( - client.getLedgerId(), - EntityIdHelper.toString(shard, realm, num) - ); - if (!checksum.equals(expectedChecksum)) { - throw new BadEntityIdException(shard, realm, num, checksum, expectedChecksum); - } - } - } - - /** - * Generate a string representation. - * - * @param shard the shard part - * @param realm the realm part - * @param num the num part - * @return the string representation - */ - static String toString(long shard, long realm, long num) { - return "" + shard + "." + realm + "." + num; - } - - /** - * Generate a string representation with a checksum. - * - * @param shard the shard part - * @param realm the realm part - * @param num the num part - * @param client the configured client - * @param checksum the checksum - * @return the string representation with checksum - */ - static String toStringWithChecksum(long shard, long realm, long num, Client client, @Nullable String checksum) { - if (client.getLedgerId() != null) { - return "" + shard + "." + realm + "." + num + "-" + checksum(client.getLedgerId(), - EntityIdHelper.toString(shard, realm, num)); - } else { - throw new IllegalStateException( - "Can't derive checksum for ID without knowing which network the ID is for. Ensure client's ledgerId is set."); - } - } - - /** - * Takes an address as `byte[]` and returns whether this is a long-zero address - * @param address - * @return - */ - static boolean isLongZeroAddress(byte[] address) { - for (int i = 0; i < 12; i++) { - if (address[i] != 0) { - return false; - } - } - return true; - } - - /** - * Get AccountId num from mirror node using evm address. - * - *

Note: This method requires API level 33 or higher. It will not work on devices running API versions below 33 - * because it uses features introduced in API level 33 (Android 13).

* - * - * @param client - * @param evmAddress - */ - static CompletableFuture getAccountNumFromMirrorNodeAsync(Client client, String evmAddress) { - String apiEndpoint = "/accounts/" + evmAddress; - return performQueryToMirrorNodeAsync(client, apiEndpoint, null, false) - .thenApply(response -> - parseNumFromMirrorNodeResponse(response, "account")); - } - - /** - * Get EvmAddress from mirror node using account num. - * - *

Note: This method requires API level 33 or higher. It will not work on devices running API versions below 33 - * because it uses features introduced in API level 33 (Android 13).

* - * - * @param client - * @param num - */ - public static CompletableFuture getEvmAddressFromMirrorNodeAsync(Client client, long num) { - String apiEndpoint = "/accounts/" + num; - return performQueryToMirrorNodeAsync(client, apiEndpoint, null, false) - .thenApply(response -> - EvmAddress.fromString(parseStringMirrorNodeResponse(response, "evm_address"))); - } - - /** - * Get ContractId num from mirror node using evm address. - * - *

Note: This method requires API level 33 or higher. It will not work on devices running API versions below 33 - * because it uses features introduced in API level 33 (Android 13).

* - * - * @param client - * @param evmAddress - */ - public static CompletableFuture getContractNumFromMirrorNodeAsync(Client client, String evmAddress) { - String apiEndpoint = "/contracts/" + evmAddress; - - CompletableFuture responseFuture = performQueryToMirrorNodeAsync(client, apiEndpoint, null, false); - - return responseFuture.thenApply(response -> - parseNumFromMirrorNodeResponse(response, "contract_id")); - } - - - public static CompletableFuture getContractAddressFromMirrorNodeAsync(Client client, String id) { - String apiEndpoint = "/contracts/" + id; - CompletableFuture responseFuture = performQueryToMirrorNodeAsync(client, apiEndpoint, null, false); - - return responseFuture.thenApply(response -> - parseStringMirrorNodeResponse(response, "evm_address")); - } - - static CompletableFuture performQueryToMirrorNodeAsync(Client client, String apiEndpoint, String jsonBody, boolean isContractCall) { - Optional mirrorUrl = client.getMirrorNetwork().stream() - .map(url -> url.substring(0, url.indexOf(":"))) - .findFirst(); - - if (mirrorUrl.isEmpty()) { - return CompletableFuture.failedFuture(new IllegalArgumentException("Mirror URL not found")); - } - - String apiUrl = "https://" + mirrorUrl.get() + "/api/v1" + apiEndpoint; - - if (client.getLedgerId() == null) { - if (isContractCall) { - apiUrl = "http://" + mirrorUrl.get() + ":8545/api/v1" + apiEndpoint; - } else { - apiUrl = "http://" + mirrorUrl.get() + ":5551/api/v1" + apiEndpoint; - } - } - - HttpClient httpClient = HttpClient.newHttpClient(); - var httpBuilder = HttpRequest.newBuilder() - .timeout(MIRROR_NODE_CONNECTION_TIMEOUT) - .uri(URI.create(apiUrl)); - - if (jsonBody != null) { - httpBuilder.header("Content-Type", "application/json") - .POST(HttpRequest.BodyPublishers.ofString(jsonBody)); - } - var httpRequest = httpBuilder.build(); - - return httpClient.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofString()) - .handle((response, ex) -> { - if (ex != null) { - if (ex instanceof HttpTimeoutException) { - throw new CompletionException(new RuntimeException("Request to Mirror Node timed out", ex)); - } else { - throw new CompletionException(new RuntimeException("Failed to send request to Mirror Node", ex)); - } - } - - int statusCode = response.statusCode(); - if (statusCode != 200) { - throw new CompletionException(new RuntimeException("Received non-200 response from Mirror Node: " + response.body())); - } - return response.body(); - }); - } - - private static String parseStringMirrorNodeResponse(String responseBody, String memberName) { - JsonObject jsonObject = JsonParser.parseString(responseBody).getAsJsonObject(); - String evmAddress = jsonObject.get(memberName).getAsString(); - return evmAddress.substring(evmAddress.lastIndexOf(".") + 1); - } - - private static long parseNumFromMirrorNodeResponse(String responseBody, String memberName) { - return Long.parseLong(parseStringMirrorNodeResponse(responseBody, memberName)); - } - - @FunctionalInterface - interface WithIdNums { - R apply(long shard, long realm, long num, @Nullable String checksum); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/EthereumFlow.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/EthereumFlow.java deleted file mode 100644 index 8bbc116b10..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/EthereumFlow.java +++ /dev/null @@ -1,284 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import java.time.Duration; -import java.util.Arrays; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeoutException; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import javax.annotation.Nullable; - -/** - * Execute an Ethereum transaction on Hedera - */ -public class EthereumFlow { - /** - * 5KiB in Bytes - * Indicates when we should splice out the call data from an ethereum transaction data - */ - static int MAX_ETHEREUM_DATA_SIZE = 5120; - @Nullable - private EthereumTransactionData ethereumData; - - @Nullable - private FileId callDataFileId; - - @Nullable - private Hbar maxGasAllowance; - - - /** - * Constructor - */ - public EthereumFlow() { - } - - private static FileId createFile(byte[] callData, Client client, Duration timeoutPerTransaction) throws PrecheckStatusException, TimeoutException { - try { - var transaction = new FileCreateTransaction() - .setContents(Arrays.copyOfRange(callData, 0, Math.min(FileAppendTransaction.DEFAULT_CHUNK_SIZE, callData.length))) - .execute(client, timeoutPerTransaction); - var fileId = transaction.getReceipt(client, timeoutPerTransaction).fileId; - - if (callData.length > FileAppendTransaction.DEFAULT_CHUNK_SIZE) { - new FileAppendTransaction() - .setFileId(fileId) - .setContents(Arrays.copyOfRange(callData, FileAppendTransaction.DEFAULT_CHUNK_SIZE, callData.length)) - .execute(client, timeoutPerTransaction); - } - return fileId; - } catch (ReceiptStatusException e) { - throw new RuntimeException(e); - } - - } - - private static CompletableFuture createFileAsync(byte[] callData, Client client, Duration timeoutPerTransaction) { - return new FileCreateTransaction() - .setContents(Arrays.copyOfRange(callData, 0, Math.min(FileAppendTransaction.DEFAULT_CHUNK_SIZE, callData.length))) - .executeAsync(client, timeoutPerTransaction) - .thenCompose((response) -> response.getReceiptAsync(client, timeoutPerTransaction)) - .thenCompose((receipt) -> { - if (callData.length > FileAppendTransaction.DEFAULT_CHUNK_SIZE) { - return new FileAppendTransaction() - .setFileId(receipt.fileId) - .setContents(Arrays.copyOfRange(callData, FileAppendTransaction.DEFAULT_CHUNK_SIZE, callData.length)) - .executeAsync(client, timeoutPerTransaction) - .thenApply((r) -> receipt.fileId); - } else { - return CompletableFuture.completedFuture(receipt.fileId); - } - }); - } - - /** - * Gets the data of the Ethereum transaction - * - * @return the data of the Ethereum transaction - */ - @Nullable - public EthereumTransactionData getEthereumData() { - return ethereumData; - } - - /** - * Sets the raw Ethereum transaction (RLP encoded type 0, 1, and 2). Complete - * unless the callDataFileId is set. - * - * @param ethereumData raw ethereum transaction bytes - * @return {@code this} - */ - public EthereumFlow setEthereumData(byte[] ethereumData) { - this.ethereumData = EthereumTransactionData.fromBytes(ethereumData); - return this; - } - - /** - * Gets the maximum amount that the payer of the hedera transaction - * is willing to pay to complete the transaction. - * - * @return the max gas allowance - */ - @Nullable - public Hbar getMaxGasAllowance() { - return maxGasAllowance; - } - - /** - * Sets the maximum amount that the payer of the hedera transaction - * is willing to pay to complete the transaction. - *
- * Ordinarily the account with the ECDSA alias corresponding to the public - * key that is extracted from the ethereum_data signature is responsible for - * fees that result from the execution of the transaction. If that amount of - * authorized fees is not sufficient then the payer of the transaction can be - * charged, up to but not exceeding this amount. If the ethereum_data - * transaction authorized an amount that was insufficient then the payer will - * only be charged the amount needed to make up the difference. If the gas - * price in the transaction was set to zero then the payer will be assessed - * the entire fee. - * - * @param maxGasAllowance the maximum gas allowance - * @return {@code this} - */ - public EthereumFlow setMaxGasAllowance(Hbar maxGasAllowance) { - this.maxGasAllowance = maxGasAllowance; - return this; - } - - /** - * Execute the transactions in the flow with the passed in client. - * - * @param client the client with the transaction to execute - * @return the response - * @throws PrecheckStatusException when the precheck fails - * @throws TimeoutException when the transaction times out - */ - public TransactionResponse execute(Client client) throws PrecheckStatusException, TimeoutException { - return execute(client, client.getRequestTimeout()); - } - - /** - * Execute the transactions in the flow with the passed in client. - * - * @param client the client with the transaction to execute - * @param timeoutPerTransaction The timeout after which each transaction's execution attempt will be cancelled. - * @return the response - * @throws PrecheckStatusException when the precheck fails - * @throws TimeoutException when the transaction times out - */ - public TransactionResponse execute(Client client, Duration timeoutPerTransaction) throws PrecheckStatusException, TimeoutException { - if (ethereumData == null) { - throw new IllegalStateException("Cannot execute a ethereum flow when ethereum data was not provided"); - } - - var ethereumTransaction = new EthereumTransaction(); - var ethereumDataBytes = ethereumData.toBytes(); - - if (maxGasAllowance != null) { - ethereumTransaction.setMaxGasAllowanceHbar(maxGasAllowance); - } - - if (ethereumDataBytes.length <= MAX_ETHEREUM_DATA_SIZE) { - ethereumTransaction.setEthereumData(ethereumDataBytes); - } else { - var callDataFileId = createFile(ethereumData.callData, client, timeoutPerTransaction); - ethereumData.callData = new byte[]{}; - ethereumTransaction.setEthereumData(ethereumData.toBytes()).setCallDataFileId(callDataFileId); - } - - return ethereumTransaction.execute(client, timeoutPerTransaction); - } - - /** - * Execute the transactions in the flow with the passed in client asynchronously. - * - *

Note: This method requires API level 33 or higher. It will not work on devices running API versions below 31 - * because it uses features introduced in API level 31 (Android 12).

* - * - * @param client the client with the transaction to execute - * @return the response - */ - public CompletableFuture executeAsync(Client client) { - return executeAsync(client, client.getRequestTimeout()); - } - - /** - * Execute the transactions in the flow with the passed in client asynchronously. - * - *

Note: This method requires API level 33 or higher. It will not work on devices running API versions below 31 - * because it uses features introduced in API level 31 (Android 12).

* - * - * @param client the client with the transaction to execute - * @param timeoutPerTransaction The timeout after which each transaction's execution attempt will be cancelled. - * @return the response - */ - public CompletableFuture executeAsync(Client client, Duration timeoutPerTransaction) { - if (ethereumData == null) { - return CompletableFuture.failedFuture(new IllegalStateException("Cannot execute a ethereum flow when ethereum data was not provided")); - } - - var ethereumTransaction = new EthereumTransaction(); - var ethereumDataBytes = ethereumData.toBytes(); - - if (maxGasAllowance != null) { - ethereumTransaction.setMaxGasAllowanceHbar(maxGasAllowance); - } - - if (ethereumDataBytes.length <= MAX_ETHEREUM_DATA_SIZE) { - return ethereumTransaction.setEthereumData(ethereumDataBytes).executeAsync(client); - } else { - return createFileAsync(ethereumData.callData, client, timeoutPerTransaction) - .thenCompose((callDataFileId) -> { - ethereumData.callData = new byte[]{}; - return ethereumTransaction - .setEthereumData(ethereumData.toBytes()) - .setCallDataFileId(callDataFileId) - .executeAsync(client, timeoutPerTransaction); - }); - } - } - - /** - * Execute the transactions in the flow with the passed in client asynchronously. - * - * @param client the client with the transaction to execute - * @param callback a BiConsumer which handles the result or error. - */ - public void executeAsync(Client client, BiConsumer callback) { - ConsumerHelper.biConsumer(executeAsync(client), callback); - } - - /** - * Execute the transactions in the flow with the passed in client asynchronously. - * - * @param client the client with the transaction to execute - * @param timeoutPerTransaction The timeout after which each transaction's execution attempt will be cancelled. - * @param callback a BiConsumer which handles the result or error. - */ - public void executeAsync(Client client, Duration timeoutPerTransaction, BiConsumer callback) { - ConsumerHelper.biConsumer(executeAsync(client, timeoutPerTransaction), callback); - } - - /** - * Execute the transactions in the flow with the passed in client asynchronously. - * - * @param client the client with the transaction to execute - * @param onSuccess a Consumer which consumes the result on success. - * @param onFailure a Consumer which consumes the error on failure. - */ - public void executeAsync(Client client, Consumer onSuccess, Consumer onFailure) { - ConsumerHelper.twoConsumers(executeAsync(client), onSuccess, onFailure); - } - - /** - * Execute the transactions in the flow with the passed in client asynchronously. - * - * @param client the client with the transaction to execute - * @param timeoutPerTransaction The timeout after which each transaction's execution attempt will be cancelled. - * @param onSuccess a Consumer which consumes the result on success. - * @param onFailure a Consumer which consumes the error on failure. - */ - public void executeAsync(Client client, Duration timeoutPerTransaction, Consumer onSuccess, Consumer onFailure) { - ConsumerHelper.twoConsumers(executeAsync(client, timeoutPerTransaction), onSuccess, onFailure); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/EthereumTransactionData.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/EthereumTransactionData.java deleted file mode 100644 index 7b459abec1..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/EthereumTransactionData.java +++ /dev/null @@ -1,62 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.esaulpaugh.headlong.rlp.RLPDecoder; - -/** - * This class represents the data of an Ethereum transaction. - *

- * It may be of subclass {@link EthereumTransactionDataLegacy} or of subclass {@link EthereumTransactionDataEip1559} - */ -public abstract class EthereumTransactionData { - /** - * The raw call data. - */ - public byte[] callData; - - EthereumTransactionData(byte[] callData) { - this.callData = callData; - } - - static EthereumTransactionData fromBytes(byte[] bytes) { - var decoder = RLPDecoder.RLP_STRICT.sequenceIterator(bytes); - var rlpItem = decoder.next(); - if (rlpItem.isList()) { - return EthereumTransactionDataLegacy.fromBytes(bytes); - } else { - return EthereumTransactionDataEip1559.fromBytes(bytes); - } - } - - /** - * Serialize the ethereum transaction data into bytes using RLP - * - * @return the serialized transaction as a byte array - */ - public abstract byte[] toBytes(); - - /** - * Serialize the ethereum transaction data into a string - * - * @return the serialized transaction as a string - */ - public abstract String toString(); -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/EthereumTransactionDataEip1559.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/EthereumTransactionDataEip1559.java deleted file mode 100644 index 15890ced04..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/EthereumTransactionDataEip1559.java +++ /dev/null @@ -1,185 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.esaulpaugh.headlong.rlp.RLPDecoder; -import com.esaulpaugh.headlong.rlp.RLPEncoder; -import com.esaulpaugh.headlong.rlp.RLPItem; -import com.esaulpaugh.headlong.util.Integers; -import com.google.common.base.MoreObjects; -import java.util.ArrayList; -import java.util.List; -import org.bouncycastle.util.encoders.Hex; - -/** - * The ethereum transaction data, in the format defined in EIP-1559 - */ -public class EthereumTransactionDataEip1559 extends EthereumTransactionData { - - /** - * ID of the chain - */ - public byte[] chainId; - - /** - * Transaction's nonce - */ - public byte[] nonce; - - /** - * An 'optional' additional fee in Ethereum that is paid directly to miners in order to incentivize them to include - * your transaction in a block. Not used in Hedera - */ - public byte[] maxPriorityGas; - - /** - * The maximum amount, in tinybars, that the payer of the hedera transaction is willing to pay to complete the - * transaction - */ - public byte[] maxGas; - - /** - * The amount of gas available for the transaction - */ - public byte[] gasLimit; - - /** - * The receiver of the transaction - */ - public byte[] to; - - /** - * The transaction value - */ - public byte[] value; - - /** - * specifies an array of addresses and storage keys that the transaction plans to access - */ - public byte[] accessList; - - /** - * recovery parameter used to ease the signature verification - */ - public byte[] recoveryId; - - /** - * The R value of the signature - */ - public byte[] r; - - /** - * The S value of the signature - */ - public byte[] s; - - EthereumTransactionDataEip1559( - byte[] chainId, - byte[] nonce, - byte[] maxPriorityGas, - byte[] maxGas, - byte[] gasLimit, - byte[] to, - byte[] value, - byte[] callData, - byte[] accessList, - byte[] recoveryId, - byte[] r, - byte[] s - ) { - super(callData); - - this.chainId = chainId; - this.nonce = nonce; - this.maxPriorityGas = maxPriorityGas; - this.maxGas = maxGas; - this.gasLimit = gasLimit; - this.to = to; - this.value = value; - this.accessList = accessList; - this.recoveryId = recoveryId; - this.r = r; - this.s = s; - } - - /** - * Convert a byte array to an ethereum transaction data. - * - * @param bytes the byte array - * @return the ethereum transaction data - */ - public static EthereumTransactionDataEip1559 fromBytes(byte[] bytes) { - var decoder = RLPDecoder.RLP_STRICT.sequenceIterator(bytes); - var rlpItem = decoder.next(); - - // typed transaction? - byte typeByte = rlpItem.asByte(); - if (typeByte != 2) { - throw new IllegalArgumentException("rlp type byte " + typeByte + "is not supported"); - } - rlpItem = decoder.next(); - if (!rlpItem.isList()) { - throw new IllegalArgumentException("expected RLP element list"); - } - List rlpList = rlpItem.asRLPList().elements(); - if (rlpList.size() != 12) { - throw new IllegalArgumentException("expected 12 RLP encoded elements, found " + rlpList.size()); - } - - return new EthereumTransactionDataEip1559( - rlpList.get(0).data(), - rlpList.get(1).data(), - rlpList.get(2).data(), - rlpList.get(3).data(), - rlpList.get(4).data(), - rlpList.get(5).data(), - rlpList.get(6).data(), - rlpList.get(7).data(), - rlpList.get(8).data(), - rlpList.get(9).data(), - rlpList.get(10).data(), - rlpList.get(11).data() - ); - } - - public byte[] toBytes() { - return RLPEncoder.sequence(Integers.toBytes(0x02), List.of( - chainId, nonce, maxPriorityGas, maxGas, gasLimit, to, - value, callData, new ArrayList(), recoveryId, r, s - )); - } - - public String toString() { - return MoreObjects.toStringHelper(this) - .add("chainId", Hex.toHexString(chainId)) - .add("nonce", Hex.toHexString(nonce)) - .add("maxPriorityGas", Hex.toHexString(maxPriorityGas)) - .add("maxGas", Hex.toHexString(maxGas)) - .add("gasLimit", Hex.toHexString(gasLimit)) - .add("to", Hex.toHexString(to)) - .add("value", Hex.toHexString(value)) - .add("accessList", Hex.toHexString(accessList)) - .add("recoveryId", Hex.toHexString(recoveryId)) - .add("r", Hex.toHexString(r)) - .add("s", Hex.toHexString(s)) - .toString(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/EthereumTransactionDataLegacy.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/EthereumTransactionDataLegacy.java deleted file mode 100644 index 4e30727130..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/EthereumTransactionDataLegacy.java +++ /dev/null @@ -1,162 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.esaulpaugh.headlong.rlp.RLPDecoder; -import com.esaulpaugh.headlong.rlp.RLPEncoder; -import com.esaulpaugh.headlong.rlp.RLPItem; -import com.google.common.base.MoreObjects; -import org.bouncycastle.util.encoders.Hex; - -import java.math.BigInteger; -import java.util.List; - -/** - * The ethereum transaction data, in the legacy format - */ -public class EthereumTransactionDataLegacy extends EthereumTransactionData { - - /** - * ID of the chain - */ - public byte[] chainId = new byte[]{}; - - /** - * Transaction's nonce - */ - public byte[] nonce; - - /** - * The price for 1 gas - */ - public byte[] gasPrice; - - /** - * The amount of gas available for the transaction - */ - public byte[] gasLimit; - - /** - * The receiver of the transaction - */ - public byte[] to; - - /** - * The transaction value - */ - public byte[] value; - - /** - * The V value of the signature - */ - public byte[] v; - - /** - * recovery parameter used to ease the signature verification - */ - public int recoveryId; - - /** - * The R value of the signature - */ - public byte[] r; - - /** - * The S value of the signature - */ - public byte[] s; - - EthereumTransactionDataLegacy( - byte[] nonce, - byte[] gasPrice, - byte[] gasLimit, - byte[] to, - byte[] value, - byte[] callData, - byte[] v, - byte[] r, - byte[] s - ) { - super(callData); - - this.nonce = nonce; - this.gasPrice = gasPrice; - this.gasLimit = gasLimit; - this.to = to; - this.value = value; - this.v = v; - this.r = r; - this.s = s; - - var vBI = new BigInteger(1, this.v); - this.recoveryId = vBI.testBit(0) ? 0 : 1; - - if (vBI.compareTo(BigInteger.valueOf(34)) > 0) { - this.chainId = vBI.subtract(BigInteger.valueOf(35)).shiftRight(1).toByteArray(); - } - } - - /** - * Convert a byte array to an ethereum transaction data. - * - * @param bytes the byte array - * @return the ethereum transaction data - */ - public static EthereumTransactionDataLegacy fromBytes(byte[] bytes) { - var decoder = RLPDecoder.RLP_STRICT.sequenceIterator(bytes); - var rlpItem = decoder.next(); - - List rlpList = rlpItem.asRLPList().elements(); - if (rlpList.size() != 9) { - throw new IllegalArgumentException("expected 9 RLP encoded elements, found " + rlpList.size()); - } - - return new EthereumTransactionDataLegacy( - rlpList.get(0).data(), - rlpList.get(1).asBytes(), - rlpList.get(2).data(), - rlpList.get(3).data(), - rlpList.get(4).data(), - rlpList.get(5).data(), - rlpList.get(6).asBytes(), - rlpList.get(7).data(), - rlpList.get(8).data() - ); - } - - public byte[] toBytes() { - return RLPEncoder.list(nonce, gasPrice, gasLimit, to, value, callData, v, r, s); - } - - public String toString() { - return MoreObjects.toStringHelper(this) - .add("chainId", Hex.toHexString(this.chainId)) - .add("nonce", Hex.toHexString(this.nonce)) - .add("gasPrice", Hex.toHexString(this.gasPrice)) - .add("gasLimit", Hex.toHexString(this.gasLimit)) - .add("to", Hex.toHexString(this.to)) - .add("value", Hex.toHexString(this.value)) - .add("recoveryId", this.recoveryId) - .add("v", Hex.toHexString(this.v)) - .add("r", Hex.toHexString(this.r)) - .add("s", Hex.toHexString(this.s)) - .toString(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/EvmAddress.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/EvmAddress.java deleted file mode 100644 index 7606a938b1..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/EvmAddress.java +++ /dev/null @@ -1,112 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.AccountID; -import org.bouncycastle.util.encoders.Hex; - -import javax.annotation.Nonnegative; -import javax.annotation.Nullable; -import java.util.Arrays; -import java.util.Objects; -import java.util.regex.Pattern; - -/** - * The ID for a cryptocurrency account on Hedera. - */ -public final class EvmAddress extends Key { - private final byte[] bytes; - - /** - * Constructor - * - * @param bytes the byte array representation of the address - */ - public EvmAddress(byte[] bytes) { - this.bytes = Arrays.copyOf(bytes, bytes.length); - } - - /** - * Convert a string to an ethereum address. - * - * @param text the string - * @return the ethereum address - */ - public static EvmAddress fromString(String text) { - String address = text.startsWith("0x") ? text.substring(2) : text; - if (address.length() == 40) { - return new EvmAddress(Hex.decode(address)); - } - return null; - } - - @Nullable - static EvmAddress fromAliasBytes(ByteString aliasBytes) { - if (!aliasBytes.isEmpty() && aliasBytes.size() == 20) { - return new EvmAddress(aliasBytes.toByteArray()); - } - return null; - } - - /** - * Convert a byte array to an ethereum address. - * - * @param bytes the byte array - * @return the ethereum address - */ - public static EvmAddress fromBytes(byte[] bytes) { - return new EvmAddress(bytes); - } - - @Override - com.hedera.hashgraph.sdk.proto.Key toProtobufKey() { - throw new UnsupportedOperationException("toProtobufKey() not implemented for EvmAddress"); - } - - public byte[] toBytes() { - return Arrays.copyOf(bytes, bytes.length); - } - - @Override - public String toString() { - return Hex.toHexString(bytes); - } - - @Override - public int hashCode() { - return Arrays.hashCode(bytes); - } - - @Override - public boolean equals( Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof EvmAddress)) { - return false; - } - - EvmAddress other = (EvmAddress) o; - return Arrays.equals(bytes, other.bytes); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/ExchangeRate.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/ExchangeRate.java deleted file mode 100644 index 8f17c1c2e9..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/ExchangeRate.java +++ /dev/null @@ -1,79 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import java.time.Instant; - -/** - * Denotes a conversion between Hbars and cents (USD). - */ -public final class ExchangeRate { - /** - * Denotes Hbar equivalent to cents (USD) - */ - public final int hbars; - - /** - * Denotes cents (USD) equivalent to Hbar - */ - public final int cents; - - /** - * Expiration time of this exchange rate - */ - public final Instant expirationTime; - - /** - * Calculated exchange rate - */ - public final double exchangeRateInCents; - - ExchangeRate(int hbars, int cents, Instant expirationTime) { - this.hbars = hbars; - this.cents = cents; - this.expirationTime = expirationTime; - this.exchangeRateInCents = (double) cents / (double) hbars; - } - - /** - * Create an Exchange Rate from a protobuf. - * - * @param pb the protobuf - * @return the new exchange rate - */ - static ExchangeRate fromProtobuf(com.hedera.hashgraph.sdk.proto.ExchangeRate pb) { - return new ExchangeRate( - pb.getHbarEquiv(), - pb.getCentEquiv(), - InstantConverter.fromProtobuf(pb.getExpirationTime()) - ); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("hbars", hbars) - .add("cents", cents) - .add("expirationTime", expirationTime) - .add("exchangeRateInCents", exchangeRateInCents) - .toString(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/ExchangeRates.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/ExchangeRates.java deleted file mode 100644 index 6e83ec5461..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/ExchangeRates.java +++ /dev/null @@ -1,77 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.google.protobuf.InvalidProtocolBufferException; - -/** - * Contains a set of Exchange Rates (current and next). - */ -public final class ExchangeRates { - /** - * Current Exchange Rate - */ - public final ExchangeRate currentRate; - - /** - * Next Exchange Rate - */ - public final ExchangeRate nextRate; - - private ExchangeRates(ExchangeRate currentRate, ExchangeRate nextRate) { - this.currentRate = currentRate; - this.nextRate = nextRate; - } - - /** - * Create an Exchange Rates from a protobuf. - * - * @param pb the protobuf - * @return the new exchange rates - */ - static ExchangeRates fromProtobuf(com.hedera.hashgraph.sdk.proto.ExchangeRateSet pb) { - return new ExchangeRates( - ExchangeRate.fromProtobuf(pb.getCurrentRate()), - ExchangeRate.fromProtobuf(pb.getNextRate()) - ); - } - - /** - * Create an Exchange Rates from a byte array. - * - * @param bytes the byte array - * @return the new exchange rates - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - public static ExchangeRates fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return fromProtobuf(com.hedera.hashgraph.sdk.proto.ExchangeRateSet.parseFrom(bytes).toBuilder().build()); - } - - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("currentRate", currentRate.toString()) - .add("nextRate", nextRate.toString()) - .toString(); - } - -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/ExecutionState.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/ExecutionState.java deleted file mode 100644 index 7eabf572d5..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/ExecutionState.java +++ /dev/null @@ -1,42 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -/** - * Enum for the execution states. - */ -public enum ExecutionState { - /** - * Indicates that the execution was successful - */ - SUCCESS, - /** - * Indicates that the call was successful but the operation did not complete. Retry with same/new node - */ - RETRY, - /** - * Indicates that the receiver was bad node. Retry with new node - */ - SERVER_ERROR, - /** - * Indicates that the request was incorrect - */ - REQUEST_ERROR -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/FeeAssessmentMethod.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/FeeAssessmentMethod.java deleted file mode 100644 index ef6319ca75..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/FeeAssessmentMethod.java +++ /dev/null @@ -1,58 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -/** - * Enum for the fee assessment method. - *

- * The terminology here (exclusive vs inclusive) is borrowed from tax assessment. - */ -public enum FeeAssessmentMethod { - - /** - * If Alice is paying Bob, and an inclusive fractional fee is collected to be sent to Charlie, - * the amount Alice declares she will pay in the transfer transaction includes the fee amount. - *

- * In other words, Bob receives the amount that Alice intended to send, minus the fee. - */ - INCLUSIVE(false), - /** - * If Alice is paying Bob, and an exclusive fractional fee is collected to be sent to Charlie, - * the amount Alice declares she will pay in the transfer transaction does not include the fee amount. - *

- * In other words, Alice is charged the fee in addition to the amount she intended to send to Bob. - */ - EXCLUSIVE(true); - - final boolean code; - - FeeAssessmentMethod(boolean code) { - this.code = code; - } - - static FeeAssessmentMethod valueOf(boolean code) { - return code ? EXCLUSIVE : INCLUSIVE; - } - - @Override - public String toString() { - return code ? "EXCLUSIVE" : "INCLUSIVE"; - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/FeeData.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/FeeData.java deleted file mode 100644 index 633b65a857..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/FeeData.java +++ /dev/null @@ -1,205 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.google.protobuf.InvalidProtocolBufferException; - -import javax.annotation.Nullable; - -/** - * The total fees charged for a transaction. It contains three parts namely - * node data, network data and service data. - */ -public class FeeData implements Cloneable { - @Nullable - private FeeComponents nodeData = null; - @Nullable - private FeeComponents networkData = null; - @Nullable - private FeeComponents serviceData = null; - private FeeDataType type = FeeDataType.DEFAULT; - - /** - * Constructor. - */ - public FeeData() { - } - - /** - * Initialize fee data object from a protobuf. - * - * @param feeData the protobuf - * @return the fee data object - */ - static FeeData fromProtobuf(com.hedera.hashgraph.sdk.proto.FeeData feeData) { - return new FeeData() - .setNodeData(feeData.hasNodedata() ? FeeComponents.fromProtobuf(feeData.getNodedata()) : null) - .setNetworkData(feeData.hasNetworkdata() ? FeeComponents.fromProtobuf(feeData.getNetworkdata()) : null) - .setServiceData(feeData.hasNodedata() ? FeeComponents.fromProtobuf(feeData.getServicedata()) : null) - .setType(FeeDataType.valueOf(feeData.getSubType())); - } - - /** - * Initialize fee data object from byte array. - * - * @param bytes the byte array - * @return the fee data object - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - public static FeeData fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return fromProtobuf(com.hedera.hashgraph.sdk.proto.FeeData.parseFrom(bytes).toBuilder().build()); - } - - /** - * Extract the node data. - * - * @return the node data fee components object - */ - @Nullable - FeeComponents getNodeData() { - return nodeData; - } - - /** - * Assign the node data fee component object. - * - * @param nodeData the node data fee component object - * @return {@code this} - */ - FeeData setNodeData(@Nullable FeeComponents nodeData) { - this.nodeData = nodeData; - return this; - } - - /** - * Extract the network data. - * - * @return the network data fee component object - */ - @Nullable - FeeComponents getNetworkData() { - return networkData; - } - - /** - * Assign the network data fee component object. - * - * @param networkData the network data fee component object - * @return {@code this} - */ - FeeData setNetworkData(@Nullable FeeComponents networkData) { - this.networkData = networkData; - return this; - } - - /** - * Extract the service data. - * - * @return the service data fee component object - */ - @Nullable - FeeComponents getServiceData() { - return serviceData; - } - - /** - * Assign the service data fee component object. - * - * @param serviceData the service data fee component object - * @return {@code this} - */ - FeeData setServiceData(@Nullable FeeComponents serviceData) { - this.serviceData = serviceData; - return this; - } - - /** - * Extract the fee data type. - * - * @return the fee data type - */ - FeeDataType getType() { - return type; - } - - /** - * Assign the fee data type. - * {@link com.hedera.hashgraph.sdk.FeeDataType} - * - * @param type the fee data type - * @return {@code this} - */ - FeeData setType(FeeDataType type) { - this.type = type; - return this; - } - - /** - * Convert the fee data type into a protobuf. - * - * @return the protobuf - */ - com.hedera.hashgraph.sdk.proto.FeeData toProtobuf() { - var builder = com.hedera.hashgraph.sdk.proto.FeeData.newBuilder().setSubType(type.code); - if (nodeData != null) { - builder.setNodedata(nodeData.toProtobuf()); - } - if (networkData != null) { - builder.setNetworkdata(networkData.toProtobuf()); - } - if (serviceData != null) { - builder.setServicedata(serviceData.toProtobuf()); - } - return builder.build(); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("nodeData", getNodeData()) - .add("networkData", getNetworkData()) - .add("serviceData", getServiceData()) - .add("type", getType()) - .toString(); - } - - /** - * Create the byte array. - * - * @return a byte array representation - */ - public byte[] toBytes() { - return toProtobuf().toByteArray(); - } - - @Override - public FeeData clone() { - try { - FeeData clone = (FeeData) super.clone(); - clone.nodeData = nodeData != null ? nodeData.clone() : null; - clone.networkData = networkData != null ? networkData.clone() : null; - clone.serviceData = serviceData != null ? serviceData.clone() : null; - return clone; - } catch (CloneNotSupportedException e) { - throw new AssertionError(); - } - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/FeeSchedules.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/FeeSchedules.java deleted file mode 100644 index cab329ca5e..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/FeeSchedules.java +++ /dev/null @@ -1,143 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.CurrentAndNextFeeSchedule; - -import javax.annotation.Nullable; - -/** - * This contains two Fee Schedules with expiry timestamp. - * - * See Hedera Documentation - */ -public class FeeSchedules { - @Nullable - private FeeSchedule current; - @Nullable - private FeeSchedule next; - - /** - * Constructor. - */ - public FeeSchedules() { - current = next = null; - } - - /** - * Create a fee schedules object from a protobuf. - * - * @param feeSchedules the protobuf - * @return the fee schedules object - */ - static FeeSchedules fromProtobuf(CurrentAndNextFeeSchedule feeSchedules) { - return new FeeSchedules() - .setCurrent(feeSchedules.hasCurrentFeeSchedule() ? FeeSchedule.fromProtobuf(feeSchedules.getCurrentFeeSchedule()) : null) - .setNext(feeSchedules.hasNextFeeSchedule() ? FeeSchedule.fromProtobuf(feeSchedules.getNextFeeSchedule()) : null); - } - - /** - * Create a fee schedules object from a byte array. - * - * @param bytes the byte array - * @return the fee schedules object - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - public static FeeSchedules fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return fromProtobuf(CurrentAndNextFeeSchedule.parseFrom(bytes).toBuilder().build()); - } - - /** - * Extract the current fee schedule. - * - * @return the current fee schedule - */ - @Nullable - public FeeSchedule getCurrent() { - return current != null ? current.clone() : null; - } - - /** - * Assign the current fee schedule. - * - * @param current the fee schedule - * @return {@code this} - */ - public FeeSchedules setCurrent(@Nullable FeeSchedule current) { - this.current = current != null ? current.clone() : null; - return this; - } - - /** - * Extract the next fee schedule. - * - * @return the next fee schedule - */ - @Nullable - public FeeSchedule getNext() { - return next != null ? next.clone() : null; - } - - /** - * Assign the next fee schedule. - * - * @param next the fee schedule - * @return {@code this} - */ - public FeeSchedules setNext(@Nullable FeeSchedule next) { - this.next = next != null ? next.clone() : null; - return this; - } - - /** - * Create the protobuf. - * - * @return protobuf representation - */ - CurrentAndNextFeeSchedule toProtobuf() { - var returnBuilder = CurrentAndNextFeeSchedule.newBuilder(); - if (current != null) { - returnBuilder.setCurrentFeeSchedule(current.toProtobuf()); - } - if (next != null) { - returnBuilder.setNextFeeSchedule(next.toProtobuf()); - } - return returnBuilder.build(); - } - - /** - * Create the byte array. - * - * @return byte array representation - */ - public byte[] toBytes() { - return toProtobuf().toByteArray(); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("current", getCurrent()) - .add("next", getNext()) - .toString(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/FileContentsQuery.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/FileContentsQuery.java deleted file mode 100644 index ad01e485df..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/FileContentsQuery.java +++ /dev/null @@ -1,120 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import com.hedera.hashgraph.sdk.proto.FileGetContentsQuery; -import com.hedera.hashgraph.sdk.proto.FileServiceGrpc; -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import com.hedera.hashgraph.sdk.proto.Response; -import com.hedera.hashgraph.sdk.proto.ResponseHeader; -import io.grpc.MethodDescriptor; -import java.util.Objects; -import java.util.concurrent.CompletableFuture; -import javax.annotation.Nullable; - -/** - * Get the contents of a file. The content field is empty (no bytes) if the - * file is empty. - * - * A query to get the contents of a file. Queries do not change the state of - * the file or require network consensus. The information is returned from a - * single node processing the query. - * - * See Hedera Documentation - */ -public final class FileContentsQuery extends Query { - - @Nullable - private FileId fileId = null; - - /** - * Constructor. - */ - public FileContentsQuery() { - } - - /** - * Extract the file id. - * - * @return the file id - */ - @Nullable - public FileId getFileId() { - return fileId; - } - - /** - * Sets the file ID of the file whose contents are requested. - * - * @param fileId The FileId to be set - * @return {@code this} - */ - public FileContentsQuery setFileId(FileId fileId) { - Objects.requireNonNull(fileId); - this.fileId = fileId; - return this; - } - - @Override - public CompletableFuture getCostAsync(Client client) { - // deleted accounts return a COST_ANSWER of zero which triggers `INSUFFICIENT_TX_FEE` - // if you set that as the query payment; 25 tinybar seems to be enough to get - // `FILE_DELETED` back instead. - return super.getCostAsync(client).thenApply((cost) -> Hbar.fromTinybars(Math.max(cost.toTinybars(), 25))); - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (fileId != null) { - fileId.validateChecksum(client); - } - } - - @Override - void onMakeRequest(com.hedera.hashgraph.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { - var builder = FileGetContentsQuery.newBuilder(); - if (fileId != null) { - builder.setFileID(fileId.toProtobuf()); - } - - queryBuilder.setFileGetContents(builder.setHeader(header)); - } - - @Override - ResponseHeader mapResponseHeader(Response response) { - return response.getFileGetContents().getHeader(); - } - - @Override - QueryHeader mapRequestHeader(com.hedera.hashgraph.sdk.proto.Query request) { - return request.getFileGetContents().getHeader(); - } - - @Override - ByteString mapResponse(Response response, AccountId nodeId, com.hedera.hashgraph.sdk.proto.Query request) { - return response.getFileGetContents().getFileContents().getContents(); - } - - @Override - MethodDescriptor getMethodDescriptor() { - return FileServiceGrpc.getGetFileContentMethod(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/FileDeleteTransaction.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/FileDeleteTransaction.java deleted file mode 100644 index 556f1c101e..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/FileDeleteTransaction.java +++ /dev/null @@ -1,145 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.FileDeleteTransactionBody; -import com.hedera.hashgraph.sdk.proto.FileServiceGrpc; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; -import java.util.LinkedHashMap; -import java.util.Objects; - -/** - *

A transaction to delete a file on the Hedera network. - * - *

When deleted, a file's contents are truncated to zero length and it can no longer be updated - * or appended to, or its expiration time extended. {@link FileContentsQuery} and {@link FileInfoQuery} - * will throw {@link PrecheckStatusException} with a status of {@link Status#FILE_DELETED}. - * - *

Only one of the file's keys needs to sign to delete the file, unless the key you have is part - * of a {@link com.hedera.hashgraph.sdk.KeyList}. - */ -public final class FileDeleteTransaction extends Transaction { - - @Nullable - private FileId fileId = null; - - /** - * Constructor. - */ - public FileDeleteTransaction() { - } - - /** - * Constructor. - * - * @param txs Compound list of transaction id's list of (AccountId, Transaction) - * records - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - FileDeleteTransaction(LinkedHashMap> txs) throws InvalidProtocolBufferException { - super(txs); - initFromTransactionBody(); - } - - /** - * Constructor. - * - * @param txBody protobuf TransactionBody - */ - FileDeleteTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { - super(txBody); - initFromTransactionBody(); - } - - /** - * Extract the file id. - * - * @return the file id - */ - @Nullable - public FileId getFileId() { - return fileId; - } - - /** - *

Set the ID of the file to delete. Required. - * - * @param fileId the ID of the file to delete. - * @return {@code this} - */ - public FileDeleteTransaction setFileId(FileId fileId) { - Objects.requireNonNull(fileId); - requireNotFrozen(); - this.fileId = fileId; - return this; - } - - /** - * Initialize from the transaction body. - */ - void initFromTransactionBody() { - var body = sourceTransactionBody.getFileDelete(); - if (body.hasFileID()) { - fileId = FileId.fromProtobuf(body.getFileID()); - } - } - - /** - * Build the transaction body. - * - * @return {@link com.hedera.hashgraph.sdk.proto.FileDeleteTransactionBody builder} - */ - FileDeleteTransactionBody.Builder build() { - var builder = FileDeleteTransactionBody.newBuilder(); - if (fileId != null) { - builder.setFileID(fileId.toProtobuf()); - } - - return builder; - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (fileId != null) { - fileId.validateChecksum(client); - } - } - - @Override - MethodDescriptor getMethodDescriptor() { - return FileServiceGrpc.getDeleteFileMethod(); - } - - @Override - void onFreeze(TransactionBody.Builder bodyBuilder) { - bodyBuilder.setFileDelete(build()); - } - - @Override - void onScheduled(SchedulableTransactionBody.Builder scheduled) { - scheduled.setFileDelete(build()); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/FileInfo.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/FileInfo.java deleted file mode 100644 index 10daa194e2..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/FileInfo.java +++ /dev/null @@ -1,171 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.FileGetInfoResponse; -import java.time.Instant; - -import javax.annotation.Nullable; - -/** - * Current information for a file, including its size. - * - * See Hedera Documentation - */ -public final class FileInfo { - /** - * The ID of the file for which information is requested. - */ - public final FileId fileId; - - /** - * Number of bytes in contents. - */ - public final long size; - - /** - * The current time at which this account is set to expire. - */ - public final Instant expirationTime; - - /** - * True if deleted but not yet expired. - */ - public final boolean isDeleted; - - /** - * One of these keys must sign in order to delete the file. - * All of these keys must sign in order to update the file. - */ - @Nullable - public final KeyList keys; - - /** - * The memo associated with the file - */ - public final String fileMemo; - - /** - * The ledger ID the response was returned from; please see HIP-198 for the network-specific IDs. - */ - public final LedgerId ledgerId; - - private FileInfo( - FileId fileId, - long size, - Instant expirationTime, - boolean isDeleted, - @Nullable KeyList keys, - String fileMemo, - LedgerId ledgerId - ) { - this.fileId = fileId; - this.size = size; - this.expirationTime = expirationTime; - this.isDeleted = isDeleted; - this.keys = keys; - this.fileMemo = fileMemo; - this.ledgerId = ledgerId; - } - - /** - * Create a file info object from a ptotobuf. - * - * @param fileInfo the protobuf - * @return the new file info object - */ - static FileInfo fromProtobuf(FileGetInfoResponse.FileInfo fileInfo) { - @Nullable KeyList keys = fileInfo.hasKeys() ? - KeyList.fromProtobuf(fileInfo.getKeys(), null) : - null; - - return new FileInfo( - FileId.fromProtobuf(fileInfo.getFileID()), - fileInfo.getSize(), - InstantConverter.fromProtobuf(fileInfo.getExpirationTime()), - fileInfo.getDeleted(), - keys, - fileInfo.getMemo(), - LedgerId.fromByteString(fileInfo.getLedgerId()) - ); - } - - /** - * Create a file info object from a byte array. - * - * @param bytes the byte array - * @return the new file info object - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - public static FileInfo fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return fromProtobuf(FileGetInfoResponse.FileInfo.parseFrom(bytes).toBuilder().build()); - } - - /** - * Create the protobuf. - * - * @return the protobuf representation - */ - FileGetInfoResponse.FileInfo toProtobuf() { - var fileInfoBuilder = FileGetInfoResponse.FileInfo.newBuilder() - .setFileID(fileId.toProtobuf()) - .setSize(size) - .setExpirationTime(InstantConverter.toProtobuf(expirationTime)) - .setDeleted(isDeleted) - .setMemo(fileMemo) - .setLedgerId(ledgerId.toByteString()); - - if (keys != null) { - var keyList = com.hedera.hashgraph.sdk.proto.KeyList.newBuilder(); - - for (Key key : keys) { - keyList.addKeys(key.toProtobufKey()); - } - - fileInfoBuilder.setKeys(keyList); - } - - return fileInfoBuilder.build(); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("fileId", fileId) - .add("size", size) - .add("expirationTime", expirationTime) - .add("isDeleted", isDeleted) - .add("keys", keys) - .add("fileMemo", fileMemo) - .add("ledgerId", ledgerId) - .toString(); - } - - /** - * Create the byte array. - * - * @return the byte array representation - */ - public byte[] toBytes() { - return toProtobuf().toByteArray(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/FileInfoQuery.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/FileInfoQuery.java deleted file mode 100644 index 4d12d8a58b..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/FileInfoQuery.java +++ /dev/null @@ -1,119 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.FileGetInfoQuery; -import com.hedera.hashgraph.sdk.proto.FileServiceGrpc; -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import com.hedera.hashgraph.sdk.proto.Response; -import com.hedera.hashgraph.sdk.proto.ResponseHeader; -import io.grpc.MethodDescriptor; -import java.util.Objects; -import java.util.concurrent.CompletableFuture; -import javax.annotation.Nullable; - -/** - * Get all of the information about a file, except for its contents. - *

- * When a file expires, it no longer exists, and there will be no info about it, and the fileInfo field - * will be blank. - *

- * If a transaction or smart contract deletes the file, but it has not yet expired, then the - * fileInfo field will be non-empty, the deleted field will be true, its size will be 0, - * and its contents will be empty. Note that each file has a FileID, but does not have a filename. - */ -public final class FileInfoQuery extends Query { - - @Nullable - private FileId fileId = null; - - /** - * Constructor. - */ - public FileInfoQuery() { - } - - /** - * Extract the file id. - * - * @return the file id - */ - @Nullable - public FileId getFileId() { - return fileId; - } - - /** - * Sets the file ID for which information is requested. - * - * @param fileId The FileId to be set - * @return {@code this} - */ - public FileInfoQuery setFileId(FileId fileId) { - Objects.requireNonNull(fileId); - this.fileId = fileId; - return this; - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (fileId != null) { - fileId.validateChecksum(client); - } - } - - @Override - void onMakeRequest(com.hedera.hashgraph.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { - var builder = FileGetInfoQuery.newBuilder(); - if (fileId != null) { - builder.setFileID(fileId.toProtobuf()); - } - - queryBuilder.setFileGetInfo(builder.setHeader(header)); - } - - @Override - ResponseHeader mapResponseHeader(Response response) { - return response.getFileGetInfo().getHeader(); - } - - @Override - QueryHeader mapRequestHeader(com.hedera.hashgraph.sdk.proto.Query request) { - return request.getFileGetInfo().getHeader(); - } - - @Override - FileInfo mapResponse(Response response, AccountId nodeId, com.hedera.hashgraph.sdk.proto.Query request) { - return FileInfo.fromProtobuf(response.getFileGetInfo().getFileInfo()); - } - - @Override - MethodDescriptor getMethodDescriptor() { - return FileServiceGrpc.getGetFileInfoMethod(); - } - - @Override - public CompletableFuture getCostAsync(Client client) { - // deleted accounts return a COST_ANSWER of zero which triggers `INSUFFICIENT_TX_FEE` - // if you set that as the query payment; 25 tinybar seems to be enough to get - // `FILE_DELETED` back instead. - return super.getCostAsync(client).thenApply((cost) -> Hbar.fromTinybars(Math.max(cost.toTinybars(), 25))); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/FreezeType.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/FreezeType.java deleted file mode 100644 index 3921e0639e..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/FreezeType.java +++ /dev/null @@ -1,89 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -/** - * Enum for the freeze types. - */ -public enum FreezeType { - /** - * An (invalid) default value for this enum, to ensure the client explicitly sets - * the intended type of freeze transaction. - */ - UNKNOWN_FREEZE_TYPE(com.hedera.hashgraph.sdk.proto.FreezeType.UNKNOWN_FREEZE_TYPE), - - /** - * Freezes the network at the specified time. The start_time field must be provided and - * must reference a future time. Any values specified for the update_file and file_hash - * fields will be ignored. This transaction does not perform any network changes or - * upgrades and requires manual intervention to restart the network. - */ - FREEZE_ONLY(com.hedera.hashgraph.sdk.proto.FreezeType.FREEZE_ONLY), - - /** - * A non-freezing operation that initiates network wide preparation in advance of a - * scheduled freeze upgrade. The update_file and file_hash fields must be provided and - * valid. The start_time field may be omitted and any value present will be ignored. - */ - PREPARE_UPGRADE(com.hedera.hashgraph.sdk.proto.FreezeType.PREPARE_UPGRADE), - - /** - * Freezes the network at the specified time and performs the previously prepared - * automatic upgrade across the entire network. - */ - FREEZE_UPGRADE(com.hedera.hashgraph.sdk.proto.FreezeType.FREEZE_UPGRADE), - - /** - * Aborts a pending network freeze operation. - */ - FREEZE_ABORT(com.hedera.hashgraph.sdk.proto.FreezeType.FREEZE_ABORT), - - /** - * Performs an immediate upgrade on auxilary services and containers providing - * telemetry/metrics. Does not impact network operations. - */ - TELEMETRY_UPGRADE(com.hedera.hashgraph.sdk.proto.FreezeType.TELEMETRY_UPGRADE); - - final com.hedera.hashgraph.sdk.proto.FreezeType code; - - FreezeType(com.hedera.hashgraph.sdk.proto.FreezeType code) { - this.code = code; - } - - static FreezeType valueOf(com.hedera.hashgraph.sdk.proto.FreezeType code) { - return switch (code) { - case UNKNOWN_FREEZE_TYPE -> UNKNOWN_FREEZE_TYPE; - case FREEZE_ONLY -> FREEZE_ONLY; - case PREPARE_UPGRADE -> PREPARE_UPGRADE; - case FREEZE_UPGRADE -> FREEZE_UPGRADE; - case FREEZE_ABORT -> FREEZE_ABORT; - case TELEMETRY_UPGRADE -> TELEMETRY_UPGRADE; - case UNRECOGNIZED -> - // NOTE: Protobuf deserialization will not give us the code on the wire - throw new IllegalArgumentException( - "network returned unrecognized response code; your SDK may be out of date"); - }; - } - - @Override - public String toString() { - return code.name(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/FutureConverter.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/FutureConverter.java deleted file mode 100644 index f4e037e31c..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/FutureConverter.java +++ /dev/null @@ -1,227 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; -import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; - -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executor; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -// Converts between ListenableFuture (Guava) and CompletableFuture (StreamSupport). -// https://github.com/lukas-krecan/future-converter/blob/master/java8-guava/src/main/java/net/javacrumbs/futureconverter/java8guava/FutureConverter.java#L28 -final class FutureConverter { - private FutureConverter() { - } - - /** - * Generate a T object from a listenable future. - * - * @param listenableFuture the T object generator - * @return the T type object - */ - static CompletableFuture toCompletableFuture(ListenableFuture listenableFuture) { - return Java8FutureUtils.createCompletableFuture( - GuavaFutureUtils.createValueSourceFuture(listenableFuture)); - } - - // https://github.com/lukas-krecan/future-converter/blob/master/common/src/main/java/net/javacrumbs/futureconverter/common/internal/ValueSource.java - private interface ValueSource { - void addCallbacks(Consumer successCallback, Consumer failureCallback); - - boolean cancel(boolean mayInterruptIfRunning); - } - - // https://github.com/lukas-krecan/future-converter/blob/master/common/src/main/java/net/javacrumbs/futureconverter/common/internal/ValueSourceFuture.java - private abstract static class ValueSourceFuture extends FutureWrapper - implements ValueSource { - ValueSourceFuture(Future wrappedFuture) { - super(wrappedFuture); - } - } - - // https://github.com/lukas-krecan/future-converter/blob/652b845824de90b075cf5ddbbda6fdf440f3ed0a/common/src/main/java/net/javacrumbs/futureconverter/common/internal/FutureWrapper.java - private static class FutureWrapper implements Future { - private final Future wrappedFuture; - - FutureWrapper(Future wrappedFuture) { - this.wrappedFuture = wrappedFuture; - } - - @Override - public boolean cancel(boolean mayInterruptIfRunning) { - return wrappedFuture.cancel(mayInterruptIfRunning); - } - - @Override - public boolean isCancelled() { - return wrappedFuture.isCancelled(); - } - - @Override - public boolean isDone() { - return wrappedFuture.isDone(); - } - - @Override - public T get() throws InterruptedException, ExecutionException { - return wrappedFuture.get(); - } - - @Override - public T get(long timeout, TimeUnit unit) - throws InterruptedException, ExecutionException, TimeoutException { - return wrappedFuture.get(timeout, unit); - } - - Future getWrappedFuture() { - return wrappedFuture; - } - } - - // https://github.com/lukas-krecan/future-converter/blob/master/guava-common/src/main/java/net/javacrumbs/futureconverter/guavacommon/GuavaFutureUtils.java - private static class GuavaFutureUtils { - public static ValueSourceFuture createValueSourceFuture( - ListenableFuture listenableFuture) { - if (listenableFuture instanceof ValueSourceFutureBackedListenableFuture) { - return ((ValueSourceFutureBackedListenableFuture) listenableFuture) - .getWrappedFuture(); - } else { - return new ListenableFutureBackedValueSourceFuture<>(listenableFuture); - } - } - - private static class ValueSourceFutureBackedListenableFuture extends FutureWrapper - implements ListenableFuture { - ValueSourceFutureBackedListenableFuture(ValueSourceFuture valueSourceFuture) { - super(valueSourceFuture); - } - - @Override - ValueSourceFuture getWrappedFuture() { - return (ValueSourceFuture) super.getWrappedFuture(); - } - - @Override - public void addListener(Runnable listener, Executor executor) { - getWrappedFuture() - .addCallbacks( - value -> executor.execute(listener), - ex -> executor.execute(listener)); - } - } - - private static class ListenableFutureBackedValueSourceFuture - extends ValueSourceFuture { - private ListenableFutureBackedValueSourceFuture(ListenableFuture wrappedFuture) { - super(wrappedFuture); - } - - @Override - public void addCallbacks( - Consumer successCallback, Consumer failureCallback) { - Futures.addCallback( - getWrappedFuture(), - new FutureCallback() { - @Override - public void onSuccess(T result) { - successCallback.accept(result); - } - - @Override - public void onFailure(Throwable t) { - failureCallback.accept(t); - } - }, - MoreExecutors.directExecutor()); - } - - @Override - ListenableFuture getWrappedFuture() { - return (ListenableFuture) super.getWrappedFuture(); - } - } - } - - // https://github.com/lukas-krecan/future-converter/blob/master/java8-common/src/main/java/net/javacrumbs/futureconverter/java8common/Java8FutureUtils.java - private static class Java8FutureUtils { - public static CompletableFuture createCompletableFuture(ValueSource valueSource) { - if (valueSource instanceof CompletableFutureBackedValueSource) { - return ((CompletableFutureBackedValueSource) valueSource).getWrappedFuture(); - } else { - return new ValueSourceBackedCompletableFuture(valueSource); - } - } - - private static final class ValueSourceBackedCompletableFuture - extends CompletableFuture { - private final ValueSource valueSource; - - @SuppressWarnings("ConstructorLeaksThis") - private ValueSourceBackedCompletableFuture(ValueSource valueSource) { - this.valueSource = valueSource; - valueSource.addCallbacks(this::complete, this::completeExceptionally); - } - - @Override - public boolean cancel(boolean mayInterruptIfRunning) { - if (isDone()) { - return false; - } - boolean result = valueSource.cancel(mayInterruptIfRunning); - super.cancel(mayInterruptIfRunning); - return result; - } - } - - private static final class CompletableFutureBackedValueSource - extends ValueSourceFuture { - private CompletableFutureBackedValueSource(CompletableFuture completableFuture) { - super(completableFuture); - } - - @Override - public void addCallbacks( - Consumer successCallback, Consumer failureCallback) { - getWrappedFuture() - .whenComplete( - (v, t) -> { - if (t == null) { - successCallback.accept(v); - } else { - failureCallback.accept(t); - } - }); - } - - @Override - CompletableFuture getWrappedFuture() { - return (CompletableFuture) super.getWrappedFuture(); - } - } - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/HbarAllowance.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/HbarAllowance.java deleted file mode 100644 index 832f97d279..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/HbarAllowance.java +++ /dev/null @@ -1,169 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.CryptoAllowance; -import com.hedera.hashgraph.sdk.proto.GrantedCryptoAllowance; - -import javax.annotation.Nullable; -import java.util.Objects; - -/** - * An approved allowance of hbar transfers for a spender. - * - * See Hedera Documentation - */ -public class HbarAllowance { - - /** - * The account ID of the hbar owner (ie. the grantor of the allowance) - */ - @Nullable - public final AccountId ownerAccountId; - - /** - * The account ID of the spender of the hbar allowance - */ - @Nullable - public final AccountId spenderAccountId; - - /** - * The amount of the spender's allowance in tinybars - */ - @Nullable - public final Hbar amount; - - /** - * Constructor. - * @param ownerAccountId the owner granting the allowance - * @param spenderAccountId the spender - * @param amount the amount of hbar - */ - HbarAllowance(@Nullable AccountId ownerAccountId, @Nullable AccountId spenderAccountId, @Nullable Hbar amount) { - this.ownerAccountId = ownerAccountId; - this.spenderAccountId = spenderAccountId; - this.amount = amount; - } - - /** - * Create a hbar allowance from a crypto allowance protobuf. - * - * @param allowanceProto the crypto allowance protobuf - * @return the new hbar allowance - */ - static HbarAllowance fromProtobuf(CryptoAllowance allowanceProto) { - return new HbarAllowance( - allowanceProto.hasOwner() ? AccountId.fromProtobuf(allowanceProto.getOwner()) : null, - allowanceProto.hasSpender() ? AccountId.fromProtobuf(allowanceProto.getSpender()) : null, - Hbar.fromTinybars(allowanceProto.getAmount()) - ); - } - - /** - * Create a hbar allowance from a granted crypto allowance protobuf. - * - * @param allowanceProto the granted crypto allowance protobuf - * @return the new hbar allowance - */ - static HbarAllowance fromProtobuf(GrantedCryptoAllowance allowanceProto) { - return new HbarAllowance( - null, - allowanceProto.hasSpender() ? AccountId.fromProtobuf(allowanceProto.getSpender()) : null, - Hbar.fromTinybars(allowanceProto.getAmount()) - ); - } - - /** - * Create a hbar allowance from a byte array. - * - * @param bytes the byte array - * @return the new hbar allowance - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - public static HbarAllowance fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return fromProtobuf(CryptoAllowance.parseFrom(Objects.requireNonNull(bytes))); - } - - /** - * Validate that the client is configured correctly. - * - * @param client the client to verify - * @throws BadEntityIdException if entity ID is formatted poorly - */ - void validateChecksums(Client client) throws BadEntityIdException { - if (ownerAccountId != null) { - ownerAccountId.validateChecksum(client); - } - if (spenderAccountId != null) { - spenderAccountId.validateChecksum(client); - } - } - - /** - * Convert a crypto allowance into a protobuf. - * - * @return the protobuf - */ - CryptoAllowance toProtobuf() { - var builder = CryptoAllowance.newBuilder() - .setAmount(amount.toTinybars()); - if (ownerAccountId != null) { - builder.setOwner(ownerAccountId.toProtobuf()); - } - if (spenderAccountId != null) { - builder.setSpender(spenderAccountId.toProtobuf()); - } - return builder.build(); - } - - /** - * Convert a crypto allowance into a granted crypto allowance protobuf. - * - * @return the granted crypto allowance - */ - GrantedCryptoAllowance toGrantedProtobuf() { - var builder = GrantedCryptoAllowance.newBuilder() - .setAmount(amount.toTinybars()); - if (spenderAccountId != null) { - builder.setSpender(spenderAccountId.toProtobuf()); - } - return builder.build(); - } - - /** - * Create the byte array. - * - * @return a byte array representation - */ - public byte[] toBytes() { - return toProtobuf().toByteArray(); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("ownerAccountId", ownerAccountId) - .add("spenderAccountId", spenderAccountId) - .add("amount", amount) - .toString(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/HbarUnit.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/HbarUnit.java deleted file mode 100644 index e604b93bfe..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/HbarUnit.java +++ /dev/null @@ -1,95 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -/** - * Common units of hbar; for the most part they follow SI prefix conventions. - * - * See Hedera Documentation - */ -public enum HbarUnit { - /** - * The atomic (smallest) unit of hbar, used natively by the Hedera network. - *

- * It is equivalent to 1100,000,000 hbar. - */ - TINYBAR("tℏ", 1), - - /** - * Equivalent to 100 tinybar or 11,000,000 hbar. - */ - MICROBAR("μℏ", 100), - - /** - * Equivalent to 100,000 tinybar or 11,000 hbar. - */ - MILLIBAR("mℏ", 100_000), - - /** - * The base unit of hbar, equivalent to 100 million tinybar. - */ - HBAR("ℏ", 100_000_000), - - /** - * Equivalent to 1 thousand hbar or 100 billion tinybar. - */ - KILOBAR("kℏ", 1000 * 100_000_000L), - - /** - * Equivalent to 1 million hbar or 100 trillion tinybar. - */ - MEGABAR("Mℏ", 1_000_000 * 100_000_000L), - - /** - * Equivalent to 1 billion hbar or 100 quadillion tinybar. - *

- * The maximum hbar amount supported by Hedera in any context is ~92 gigabar - * (263 tinybar); use this unit sparingly. - */ - GIGABAR("Gℏ", 1_000_000_000 * 100_000_000L); - - final long tinybar; - - private final String symbol; - - HbarUnit(String symbol, long tinybar) { - this.symbol = symbol; - this.tinybar = tinybar; - } - - /** - * Get the preferred symbol of the current unit. - *

- * E.g. {@link #TINYBAR}.getSymbol() returns "tℏ". - * - * @return the symbol - */ - public String getSymbol() { - return symbol; - } - - /** - * Get the name of this unit. - */ - @Override - public String toString() { - return name().toLowerCase(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/HederaPreCheckStatusException.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/HederaPreCheckStatusException.java deleted file mode 100644 index 73a5975327..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/HederaPreCheckStatusException.java +++ /dev/null @@ -1,35 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import javax.annotation.Nullable; - -/** - * Signals that a transaction has failed the pre-check. - *

- * Before a node submits a transaction to the rest of the network, - * it attempts some cheap assertions. This process is called the "pre-check". - */ -@Deprecated -public final class HederaPreCheckStatusException extends PrecheckStatusException { - HederaPreCheckStatusException(Status status, @Nullable TransactionId transactionId) { - super(status, transactionId); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/HederaReceiptStatusException.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/HederaReceiptStatusException.java deleted file mode 100644 index 0ab1306152..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/HederaReceiptStatusException.java +++ /dev/null @@ -1,33 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -/** - * An Exception thrown on error status by {@link TransactionId#getReceipt(Client)}. - *

- * The receipt is included, though only the {@link TransactionReceipt#status} field will be - * initialized; all the getters should throw. - */ -@Deprecated -public class HederaReceiptStatusException extends ReceiptStatusException { - HederaReceiptStatusException(TransactionId transactionId, TransactionReceipt receipt) { - super(transactionId, receipt); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/HederaTrustManager.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/HederaTrustManager.java deleted file mode 100644 index dae46e2da9..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/HederaTrustManager.java +++ /dev/null @@ -1,118 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import org.bouncycastle.util.encoders.Hex; -import org.bouncycastle.util.io.pem.PemObject; -import org.bouncycastle.util.io.pem.PemWriter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.Nullable; -import javax.net.ssl.X509TrustManager; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; - -/** - * Internal class used by node. - */ -class HederaTrustManager implements X509TrustManager { - private static final String CERTIFICATE = "CERTIFICATE"; - private static final String PEM_HEADER = "-----BEGIN CERTIFICATE-----\n"; - private static final String PEM_FOOTER = "-----END CERTIFICATE-----\n"; - protected final Logger logger = LoggerFactory.getLogger(getClass()); - - @Nullable - private final String certHash; - - /** - * Constructor. - * - * @param certHash a byte string of the certificate hash - * @param verifyCertificate should be verified - */ - HederaTrustManager(@Nullable ByteString certHash, boolean verifyCertificate) { - if (certHash == null || certHash.isEmpty()) { - if (verifyCertificate) { - throw new IllegalStateException("transport security and certificate verification are enabled, but no applicable address book was found"); - } - - logger.warn("skipping certificate check since no cert hash was found"); - this.certHash = null; - } else { - this.certHash = new String(certHash.toByteArray(), StandardCharsets.UTF_8); - } - } - - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) { - throw new UnsupportedOperationException("Attempted to use HederaTrustManager to verify a client, but this trust manager is for verifying server only"); - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { - if (certHash == null) { - return; - } - - for (var cert : chain) { - byte[] pem; - - try ( - var outputStream = new ByteArrayOutputStream(); - var pemWriter = new PemWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8)) - ) { - pemWriter.writeObject(new PemObject(CERTIFICATE, cert.getEncoded())); - pemWriter.flush(); - - pem = outputStream.toByteArray(); - } catch (IOException e) { - logger.warn("Failed to write PEM to byte array: ", e); - continue; - } - - var certHashBytes = new byte[0]; - - try { - certHashBytes = MessageDigest.getInstance("SHA-384").digest(pem); - } catch (NoSuchAlgorithmException e) { - throw new IllegalStateException("Failed to find SHA-384 digest for certificate hashing", e); - } - - if (this.certHash.equals(Hex.toHexString(certHashBytes))) { - return; - } - } - - throw new CertificateException("Failed to confirm the server's certificate from a known address book"); - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - return new X509Certificate[0]; - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/InstantConverter.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/InstantConverter.java deleted file mode 100644 index 0380f114d7..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/InstantConverter.java +++ /dev/null @@ -1,88 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.Timestamp; -import com.hedera.hashgraph.sdk.proto.TimestampSeconds; -import java.time.Duration; -import java.time.Instant; - -/** - * Instance in time utilities. - */ -final class InstantConverter { - /** - * Constructor. - */ - private InstantConverter() { - } - - /** - * Create an instance from a timestamp protobuf. - * - * @param timestamp the protobuf - * @return the instance - */ - static Instant fromProtobuf(Timestamp timestamp) { - return Instant.ofEpochSecond(timestamp.getSeconds(), timestamp.getNanos()); - } - - /** - * Create an instance from a timestamp in seconds protobuf. - * - * @param timestampSeconds the protobuf - * @return the instance - */ - static Instant fromProtobuf(TimestampSeconds timestampSeconds) { - return Instant.ofEpochSecond(timestampSeconds.getSeconds()); - } - - /** - * Convert an instance into a timestamp. - * - * @param instant the instance - * @return the timestamp - */ - static Timestamp toProtobuf(Instant instant) { - return Timestamp.newBuilder() - .setSeconds(instant.getEpochSecond()) - .setNanos(instant.getNano()) - .build(); - } - - static Timestamp toProtobuf(Duration duration) { - return Timestamp.newBuilder() - .setSeconds(duration.getSeconds()) - .setNanos(duration.getNano()) - .build(); - } - - /** - * Convert an instance into a timestamp in seconds. - * - * @param instant the instance - * @return the timestamp in seconds - */ - static TimestampSeconds toSecondsProtobuf(Instant instant) { - return TimestampSeconds.newBuilder() - .setSeconds(instant.getEpochSecond()) - .build(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/Key.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/Key.java deleted file mode 100644 index dbcabbffc4..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/Key.java +++ /dev/null @@ -1,110 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import org.bouncycastle.asn1.ASN1ObjectIdentifier; -import org.bouncycastle.asn1.sec.SECNamedCurves; -import org.bouncycastle.asn1.x9.X9ECParameters; -import org.bouncycastle.crypto.params.ECDomainParameters; - -/** - * A common base for the signing authority or key that entities in Hedera may have. - * - * See Hedera Documentation - * @see KeyList - * @see PublicKey - */ -public abstract class Key { - static final ASN1ObjectIdentifier ID_ED25519 = new ASN1ObjectIdentifier("1.3.101.112"); - static final ASN1ObjectIdentifier ID_ECDSA_SECP256K1 = new ASN1ObjectIdentifier("1.3.132.0.10"); - static final ASN1ObjectIdentifier ID_EC_PUBLIC_KEY = new ASN1ObjectIdentifier("1.2.840.10045.2.1"); - - static final X9ECParameters ECDSA_SECP256K1_CURVE = SECNamedCurves.getByName("secp256k1"); - static final ECDomainParameters ECDSA_SECP256K1_DOMAIN = - new ECDomainParameters( - ECDSA_SECP256K1_CURVE.getCurve(), - ECDSA_SECP256K1_CURVE.getG(), - ECDSA_SECP256K1_CURVE.getN(), - ECDSA_SECP256K1_CURVE.getH() - ); - - /** - * Create a specific key type from the protobuf. - * - * @param key the protobuf key of unknown type - * @return the differentiated key - */ - static Key fromProtobufKey(com.hedera.hashgraph.sdk.proto.Key key) { - switch (key.getKeyCase()) { - case ED25519 -> { - return PublicKeyED25519.fromBytesInternal(key.getEd25519().toByteArray()); - } - case ECDSA_SECP256K1 -> { - if (key.getECDSASecp256K1().size() == 20) { - return new EvmAddress(key.getECDSASecp256K1().toByteArray()); - } else { - return PublicKeyECDSA.fromBytesInternal(key.getECDSASecp256K1().toByteArray()); - } - } - case KEYLIST -> { - return KeyList.fromProtobuf(key.getKeyList(), null); - } - case THRESHOLDKEY -> { - return KeyList.fromProtobuf(key.getThresholdKey().getKeys(), key.getThresholdKey().getThreshold()); - } - case CONTRACTID -> { - return ContractId.fromProtobuf(key.getContractID()); - } - case DELEGATABLE_CONTRACT_ID -> { - return DelegateContractId.fromProtobuf(key.getDelegatableContractId()); - } - case KEY_NOT_SET -> { - return null; - } - default -> throw new IllegalStateException("Key#fromProtobuf: unhandled key case: " + key.getKeyCase()); - } - } - - /** - * Serialize this key as a protobuf object - */ - abstract com.hedera.hashgraph.sdk.proto.Key toProtobufKey(); - - /** - * Create the byte array. - * - * @return the byte array representation - */ - public byte[] toBytes() { - return toProtobufKey().toByteArray(); - } - - /** - * Create Key from proto.Key byte array - * - * @param bytes - * @return Key representation - * @throws InvalidProtocolBufferException - */ - public static Key fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return fromProtobufKey(com.hedera.hashgraph.sdk.proto.Key.parseFrom(bytes)); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/LiveHash.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/LiveHash.java deleted file mode 100644 index ac0e1a9c2b..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/LiveHash.java +++ /dev/null @@ -1,136 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; -import java.time.Duration; - -/** - *A hash (presumably of some kind of credential or certificate), along with a - * list of keys (each of which is either a primitive or a threshold key). Each - * of them must reach its threshold when signing the transaction, to attach - * this livehash to this account. At least one of them must reach its - * threshold to delete this livehash from this account. - * - * See Hedera Documentation - */ -public class LiveHash { - - /** - * The account to which the livehash is attached - */ - public final AccountId accountId; - - /** - * The SHA-384 hash of a credential or certificate - */ - public final ByteString hash; - - /** - * A list of keys (primitive or threshold), all of which must sign to attach the livehash to an account, and any one of which can later delete it. - */ - public final KeyList keys; - - /** - * The duration for which the livehash will remain valid - */ - public final Duration duration; - - /** - * Constructor. - * - * @param accountId the account id - * @param hash the hash - * @param keys the key list - * @param duration the duration - */ - private LiveHash(AccountId accountId, ByteString hash, KeyList keys, Duration duration) { - this.accountId = accountId; - this.hash = hash; - this.keys = keys; - this.duration = duration; - } - - /** - * Create a live hash from a protobuf. - * - * @param liveHash the protobuf - * @return the new live hash - */ - protected static LiveHash fromProtobuf(com.hedera.hashgraph.sdk.proto.LiveHash liveHash) { - return new LiveHash( - AccountId.fromProtobuf(liveHash.getAccountId()), - liveHash.getHash(), - KeyList.fromProtobuf(liveHash.getKeys(), null), - DurationConverter.fromProtobuf(liveHash.getDuration()) - ); - } - - /** - * Create a live hash from a byte array. - * - * @param bytes the byte array - * @return the new live hash - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - public static LiveHash fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return fromProtobuf(com.hedera.hashgraph.sdk.proto.LiveHash.parseFrom(bytes).toBuilder().build()); - } - - /** - * Convert the live hash into a protobuf. - * - * @return the protobuf - */ - protected com.hedera.hashgraph.sdk.proto.LiveHash toProtobuf() { - var keyList = com.hedera.hashgraph.sdk.proto.KeyList.newBuilder(); - for (Key key : keys) { - keyList.addKeys(key.toProtobufKey()); - } - - return com.hedera.hashgraph.sdk.proto.LiveHash.newBuilder() - .setAccountId(accountId.toProtobuf()) - .setHash(hash) - .setKeys(keyList) - .setDuration(DurationConverter.toProtobuf(duration)) - .build(); - } - - /** - * Extract the byte array. - * - * @return the byte array representation - */ - public ByteString toBytes() { - return toProtobuf().toByteString(); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("accountId", accountId) - .add("hash", hash.toByteArray()) - .add("keys", keys) - .add("duration", duration) - .toString(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/LiveHashDeleteTransaction.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/LiveHashDeleteTransaction.java deleted file mode 100644 index ebbfc1a4e2..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/LiveHashDeleteTransaction.java +++ /dev/null @@ -1,166 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.CryptoDeleteLiveHashTransactionBody; -import com.hedera.hashgraph.sdk.proto.CryptoServiceGrpc; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.Objects; - -/** - * At consensus, deletes a livehash associated to the given account. The transaction must be signed - * by either the key of the owning account, or at least one of the keys associated to the livehash. - */ -public final class LiveHashDeleteTransaction extends Transaction { - @Nullable - private AccountId accountId = null; - private byte[] hash = {}; - - /** - * Constructor. - */ - public LiveHashDeleteTransaction() { - } - - /** - * Constructor. - * - * @param txs Compound list of transaction id's list of (AccountId, Transaction) - * records - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - LiveHashDeleteTransaction(LinkedHashMap> txs) throws InvalidProtocolBufferException { - super(txs); - initFromTransactionBody(); - } - - /** - * Extract the account id. - * - * @return the account id - */ - @Nullable - public AccountId getAccountId() { - return accountId; - } - - /** - * The account owning the livehash - * - * @param accountId The AccountId to be set - * @return {@code this} - */ - public LiveHashDeleteTransaction setAccountId(AccountId accountId) { - Objects.requireNonNull(accountId); - requireNotFrozen(); - this.accountId = accountId; - return this; - } - - /** - * Extract the hash. - * - * @return the hash - */ - public ByteString getHash() { - return ByteString.copyFrom(hash); - } - - /** - * The SHA-384 livehash to delete from the account - * - * @param hash The array of bytes to be set as hash - * @return {@code this} - */ - public LiveHashDeleteTransaction setHash(byte[] hash) { - requireNotFrozen(); - Objects.requireNonNull(hash); - this.hash = Arrays.copyOf(hash, hash.length); - return this; - } - - /** - * The SHA-384 livehash to delete from the account - * - * @param hash The array of bytes to be set as hash - * @return {@code this} - */ - public LiveHashDeleteTransaction setHash(ByteString hash) { - Objects.requireNonNull(hash); - return setHash(hash.toByteArray()); - } - - /** - * Initialize from the transaction body. - */ - void initFromTransactionBody() { - var body = sourceTransactionBody.getCryptoDeleteLiveHash(); - if (body.hasAccountOfLiveHash()) { - accountId = AccountId.fromProtobuf(body.getAccountOfLiveHash()); - } - hash = body.getLiveHashToDelete().toByteArray(); - } - - /** - * Build the correct transaction body. - * - * @return {@link com.hedera.hashgraph.sdk.proto.CryptoAddLiveHashTransactionBody} - */ - CryptoDeleteLiveHashTransactionBody.Builder build() { - var builder = CryptoDeleteLiveHashTransactionBody.newBuilder(); - if (accountId != null) { - builder.setAccountOfLiveHash(accountId.toProtobuf()); - } - builder.setLiveHashToDelete(ByteString.copyFrom(hash)); - - return builder; - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (accountId != null) { - accountId.validateChecksum(client); - } - } - - @Override - MethodDescriptor getMethodDescriptor() { - return CryptoServiceGrpc.getDeleteLiveHashMethod(); - } - - @Override - void onFreeze(TransactionBody.Builder bodyBuilder) { - bodyBuilder.setCryptoDeleteLiveHash(build()); - } - - @Override - void onScheduled(SchedulableTransactionBody.Builder scheduled) { - throw new UnsupportedOperationException("Cannot schedule LiveHashDeleteTransaction"); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/LiveHashQuery.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/LiveHashQuery.java deleted file mode 100644 index 6d08e1425c..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/LiveHashQuery.java +++ /dev/null @@ -1,127 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import com.hedera.hashgraph.sdk.proto.CryptoGetLiveHashQuery; -import com.hedera.hashgraph.sdk.proto.CryptoServiceGrpc; -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import com.hedera.hashgraph.sdk.proto.Response; -import com.hedera.hashgraph.sdk.proto.ResponseHeader; -import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; -import java.util.Arrays; -import java.util.Objects; - -/** - * Requests a livehash associated to an account. - */ -public final class LiveHashQuery extends Query { - @Nullable - private AccountId accountId = null; - private byte[] hash = {}; - - /** - * Constructor. - */ - public LiveHashQuery() { - } - - /** - * Extract the account id. - * - * @return the account id - */ - @Nullable - public AccountId getAccountId() { - return accountId; - } - - /** - * The account to which the livehash is associated - * - * @param accountId The AccountId to be set - * @return {@code this} - */ - public LiveHashQuery setAccountId(AccountId accountId) { - Objects.requireNonNull(accountId); - this.accountId = accountId; - return this; - } - - /** - * Extract the hash. - * - * @return the hash - */ - public ByteString getHash() { - return ByteString.copyFrom(hash); - } - - /** - * The SHA-384 data in the livehash - * - * @param hash The array of bytes to be set as hash - * @return {@code this} - */ - public LiveHashQuery setHash(byte[] hash) { - this.hash = Arrays.copyOf(hash, hash.length); - return this; - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (accountId != null) { - accountId.validateChecksum(client); - } - } - - @Override - void onMakeRequest(com.hedera.hashgraph.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { - var builder = CryptoGetLiveHashQuery.newBuilder(); - if (accountId != null) { - builder.setAccountID(accountId.toProtobuf()); - } - builder.setHash(ByteString.copyFrom(hash)); - - queryBuilder.setCryptoGetLiveHash(builder.setHeader(header)); - } - - @Override - LiveHash mapResponse(Response response, AccountId nodeId, com.hedera.hashgraph.sdk.proto.Query request) { - return LiveHash.fromProtobuf(response.getCryptoGetLiveHash().getLiveHash()); - } - - @Override - ResponseHeader mapResponseHeader(Response response) { - return response.getCryptoGetLiveHash().getHeader(); - } - - @Override - QueryHeader mapRequestHeader(com.hedera.hashgraph.sdk.proto.Query request) { - return request.getCryptoGetLiveHash().getHeader(); - } - - @Override - MethodDescriptor getMethodDescriptor() { - return CryptoServiceGrpc.getCryptoGetBalanceMethod(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/MaxAttemptsExceededException.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/MaxAttemptsExceededException.java deleted file mode 100644 index c2478f7925..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/MaxAttemptsExceededException.java +++ /dev/null @@ -1,31 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import javax.annotation.Nullable; - -/** - * Utility exception class. - */ -public class MaxAttemptsExceededException extends IllegalStateException { - MaxAttemptsExceededException(@Nullable Throwable e) { - super("exceeded maximum attempts for request with last exception being", e); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/MaxQueryPaymentExceededException.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/MaxQueryPaymentExceededException.java deleted file mode 100644 index ea78f3d1d8..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/MaxQueryPaymentExceededException.java +++ /dev/null @@ -1,55 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -/** - * Signals that a query will cost more than a pre-configured maximum payment amount. - */ -public final class MaxQueryPaymentExceededException extends RuntimeException { - /** - * The cost of the query that was attempted as returned by {@link Query#getCost(Client)}. - */ - public final Hbar queryCost; - - /** - * The limit for a single automatic query payment, set by - * {@link Client#setDefaultMaxQueryPayment(Hbar)} (Hbar)} or {@link Query#setMaxQueryPayment(Hbar)}. - */ - public final Hbar maxQueryPayment; - - /** - * Constructor. - * - * @param builder the query builder object - * @param cost the query cost - * @param maxQueryPayment the maximum query payment - */ - MaxQueryPaymentExceededException(Query builder, Hbar cost, Hbar maxQueryPayment) { - super(String.format( - "cost for %s, of %s, without explicit payment is greater than " - + "the maximum allowed payment of %s", - builder.getClass().getSimpleName(), - cost, - maxQueryPayment)); - - this.queryCost = cost; - this.maxQueryPayment = maxQueryPayment; - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/MirrorNode.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/MirrorNode.java deleted file mode 100644 index 9ceec34db2..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/MirrorNode.java +++ /dev/null @@ -1,57 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import java.util.concurrent.ExecutorService; - -/** - * An individual mirror node. - */ -class MirrorNode extends BaseNode { - /** - * Constructor. - * - * @param address the node address as a managed node address - * @param executor the executor service - */ - MirrorNode(BaseNodeAddress address, ExecutorService executor) { - super(address, executor); - } - - /** - * Constructor. - * - * @param address the node address as a string - * @param executor the executor service - */ - MirrorNode(String address, ExecutorService executor) { - this(BaseNodeAddress.fromString(address), executor); - } - - @Override - protected String getAuthority() { - return null; - } - - @Override - BaseNodeAddress getKey() { - return address; - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/MirrorNodeContractCallQuery.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/MirrorNodeContractCallQuery.java deleted file mode 100644 index 7601f683cc..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/MirrorNodeContractCallQuery.java +++ /dev/null @@ -1,42 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import java.util.concurrent.ExecutionException; - -public class MirrorNodeContractCallQuery extends MirrorNodeContractQuery { - /** - * Does transient simulation of read-write operations and returns the result in hexadecimal string format. - * - * @param client The Client instance to perform the operation with - * @return The result of the contract call - * @throws ExecutionException - * @throws InterruptedException - */ - public String execute(Client client) throws ExecutionException, InterruptedException { - return call(client); - } - - @Override - public String toString() { - return "MirrorNodeContractCallQuery" + super.toString(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/MirrorNodeContractEstimateGasQuery.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/MirrorNodeContractEstimateGasQuery.java deleted file mode 100644 index 2c9562c952..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/MirrorNodeContractEstimateGasQuery.java +++ /dev/null @@ -1,43 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import java.util.concurrent.ExecutionException; - -public class MirrorNodeContractEstimateGasQuery extends MirrorNodeContractQuery { - - /** - * Returns gas estimation for the EVM execution. - * - * @param client The Client instance to perform the operation with - * @return The estimated gas cost - * @throws ExecutionException - * @throws InterruptedException - */ - public long execute(Client client) throws ExecutionException, InterruptedException { - return estimate(client); - } - - @Override - public String toString() { - return "MirrorNodeContractEstimateGasQuery" + super.toString(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/NetworkName.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/NetworkName.java deleted file mode 100644 index 6759c55b45..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/NetworkName.java +++ /dev/null @@ -1,86 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -/** - * Enum for the network names. - */ -@Deprecated -public enum NetworkName { - /** - * The mainnet network - */ - @Deprecated - MAINNET(0), - /** - * The testnet network - */ - @Deprecated - TESTNET(1), - /** - * The previewnet network - */ - @Deprecated - PREVIEWNET(2), - /** - * Other network - */ - @Deprecated - OTHER(Integer.MAX_VALUE); - - final int id; - - NetworkName(int id) { - this.id = id; - } - - /** - * Assign the network name via a string name. - * - * @param networkName the string containing the network name - * @return the ledger id - */ - public static NetworkName fromString(String networkName) { - switch (networkName) { - case "mainnet": - return NetworkName.MAINNET; - case "testnet": - return NetworkName.TESTNET; - case "previewnet": - return NetworkName.PREVIEWNET; - default: - throw new IllegalArgumentException("The only supported network names are 'mainnet', 'testnet', and 'previewnet'"); - } - } - - @Override - public String toString() { - switch (this) { - case MAINNET: - return "mainnet"; - case TESTNET: - return "testnet"; - case PREVIEWNET: - return "previewnet"; - default: - throw new IllegalStateException("(BUG) `NetworkName.toString()` switch is non-exhaustive"); - } - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/NetworkVersionInfo.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/NetworkVersionInfo.java deleted file mode 100644 index 784033ae77..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/NetworkVersionInfo.java +++ /dev/null @@ -1,94 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.NetworkGetVersionInfoResponse; - -/** - * Internal utility class. - */ -public class NetworkVersionInfo { - /** - * Version of the protobuf schema in use by the network - */ - public final SemanticVersion protobufVersion; - - /** - * Version of the Hedera services in use by the network - */ - public final SemanticVersion servicesVersion; - - /** - * Constructor. - * - * @param hapi the protobuf version - * @param hedera the hedera version - */ - NetworkVersionInfo(SemanticVersion hapi, SemanticVersion hedera) { - this.protobufVersion = hapi; - this.servicesVersion = hedera; - } - - /** - * Create a network version info object from a protobuf. - * - * @param proto the protobuf - * @return the new network version object - */ - protected static NetworkVersionInfo fromProtobuf(NetworkGetVersionInfoResponse proto) { - return new NetworkVersionInfo( - SemanticVersion.fromProtobuf(proto.getHapiProtoVersion()), - SemanticVersion.fromProtobuf(proto.getHederaServicesVersion()) - ); - } - - /** - * Create a network version info object from a byte array. - * - * @param bytes the byte array - * @return the new network version object - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - public static NetworkVersionInfo fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return fromProtobuf(NetworkGetVersionInfoResponse.parseFrom(bytes)); - } - - /** - * Create the protobuf. - * - * @return the protobuf representation - */ - protected NetworkGetVersionInfoResponse toProtobuf() { - return NetworkGetVersionInfoResponse.newBuilder() - .setHapiProtoVersion(protobufVersion.toProtobuf()) - .setHederaServicesVersion(servicesVersion.toProtobuf()) - .build(); - } - - /** - * Create the byte array. - * - * @return the byte array representation - */ - public byte[] toBytes() { - return toProtobuf().toByteArray(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/NetworkVersionInfoQuery.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/NetworkVersionInfoQuery.java deleted file mode 100644 index a457a02b6a..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/NetworkVersionInfoQuery.java +++ /dev/null @@ -1,68 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.NetworkGetVersionInfoQuery; -import com.hedera.hashgraph.sdk.proto.NetworkServiceGrpc; -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import com.hedera.hashgraph.sdk.proto.Response; -import com.hedera.hashgraph.sdk.proto.ResponseHeader; -import io.grpc.MethodDescriptor; - -/** - * Information about the versions of protobuf and hedera. - */ -public class NetworkVersionInfoQuery extends Query { - /** - * Constructor. - */ - public NetworkVersionInfoQuery() { - } - - @Override - void onMakeRequest(com.hedera.hashgraph.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { - queryBuilder.setNetworkGetVersionInfo(NetworkGetVersionInfoQuery.newBuilder().setHeader(header)); - } - - @Override - ResponseHeader mapResponseHeader(Response response) { - return response.getNetworkGetVersionInfo().getHeader(); - } - - @Override - QueryHeader mapRequestHeader(com.hedera.hashgraph.sdk.proto.Query request) { - return request.getNetworkGetVersionInfo().getHeader(); - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - // do nothing - } - - @Override - NetworkVersionInfo mapResponse(Response response, AccountId nodeId, com.hedera.hashgraph.sdk.proto.Query request) { - return NetworkVersionInfo.fromProtobuf(response.getNetworkGetVersionInfo()); - } - - @Override - MethodDescriptor getMethodDescriptor() { - return NetworkServiceGrpc.getGetVersionInfoMethod(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/NodeAddressBook.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/NodeAddressBook.java deleted file mode 100644 index 2b53efdd2f..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/NodeAddressBook.java +++ /dev/null @@ -1,129 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * A list of nodes and their metadata. - * - * See Hedera Documentation - */ -public class NodeAddressBook { - List nodeAddresses = Collections.emptyList(); - - /** - * Constructor. - */ - NodeAddressBook() { - } - - /** - * Extract the of node addresses. - * - * @return list of node addresses - */ - public List getNodeAddresses() { - return cloneNodeAddresses(nodeAddresses); - } - - /** - * Assign the list of node addresses. - * - * @param nodeAddresses list of node addresses - * @return {@code this} - */ - public NodeAddressBook setNodeAddresses(List nodeAddresses) { - this.nodeAddresses = cloneNodeAddresses(nodeAddresses); - return this; - } - - static List cloneNodeAddresses(List addresses) { - List cloneAddresses = new ArrayList<>(addresses.size()); - for (var address : addresses) { - cloneAddresses.add(address.clone()); - } - return cloneAddresses; - } - - /** - * Create a node address book from a protobuf. - * - * @param book the protobuf - * @return the new node address book - */ - static NodeAddressBook fromProtobuf(com.hedera.hashgraph.sdk.proto.NodeAddressBook book) { - var addresses = new ArrayList(book.getNodeAddressCount()); - - for (var address : book.getNodeAddressList()) { - addresses.add(NodeAddress.fromProtobuf(address)); - } - - return new NodeAddressBook().setNodeAddresses(addresses); - } - - /** - * Create a node address book from a byte string. - * - * @param bytes the byte string - * @return the new node address book - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - public static NodeAddressBook fromBytes(ByteString bytes) throws InvalidProtocolBufferException { - return fromProtobuf(com.hedera.hashgraph.sdk.proto.NodeAddressBook.parseFrom(bytes)); - } - - /** - * Create the protobuf. - * - * @return the protobuf representation - */ - com.hedera.hashgraph.sdk.proto.NodeAddressBook toProtobuf() { - var builder = com.hedera.hashgraph.sdk.proto.NodeAddressBook.newBuilder(); - - for (var nodeAdress : nodeAddresses) { - builder.addNodeAddress(nodeAdress.toProtobuf()); - } - - return builder.build(); - } - - /** - * Create the byte string. - * - * @return the byte string representation - */ - public ByteString toBytes() { - return toProtobuf().toByteString(); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("nodeAddresses", nodeAddresses) - .toString(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/NodeDeleteTransaction.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/NodeDeleteTransaction.java deleted file mode 100644 index 37c594f3c2..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/NodeDeleteTransaction.java +++ /dev/null @@ -1,145 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.AddressBookServiceGrpc; -import com.hedera.hashgraph.sdk.proto.NodeDeleteTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import io.grpc.MethodDescriptor; -import java.util.LinkedHashMap; - -/** - * A transaction to delete a node from the network address book. - * - * This transaction body SHALL be considered a "privileged transaction". - * - * - A transaction MUST be signed by the governing council. - * - Upon success, the address book entry SHALL enter a "pending delete" - * state. - * - All address book entries pending deletion SHALL be removed from the - * active network configuration during the next `freeze` transaction with - * the field `freeze_type` set to `PREPARE_UPGRADE`.
- * - A deleted address book node SHALL be removed entirely from network state. - * - A deleted address book node identifier SHALL NOT be reused. - * - * ### Record Stream Effects - * Upon completion the "deleted" `node_id` SHALL be in the transaction - * receipt. - */ -public class NodeDeleteTransaction extends Transaction { - - /** - * A consensus node identifier in the network state. - *

- * The node identified MUST exist in the network address book.
- * The node identified MUST NOT be deleted.
- * This value is REQUIRED. - */ - private long nodeId = 0; - - /** - * Constructor. - */ - public NodeDeleteTransaction() {} - - /** - * Constructor. - * - * @param txs Compound list of transaction id's list of (AccountId, Transaction) records - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - NodeDeleteTransaction( - LinkedHashMap> txs) - throws InvalidProtocolBufferException { - super(txs); - initFromTransactionBody(); - } - - /** - * Constructor. - * - * @param txBody protobuf TransactionBody - */ - NodeDeleteTransaction(TransactionBody txBody) { - super(txBody); - initFromTransactionBody(); - } - - /** - * Extract the consensus node identifier in the network state. - * @return the consensus node identifier in the network state. - */ - public long getNodeId() { - return nodeId; - } - - /** - * Assign the consensus node identifier in the network state. - * @param nodeId the consensus node identifier in the network state. - * @return {@code this} - */ - public NodeDeleteTransaction setNodeId(long nodeId) { - requireNotFrozen(); - this.nodeId = nodeId; - return this; - } - - /** - * Build the transaction body. - * - * @return {@link com.hedera.hashgraph.sdk.proto.NodeDeleteTransactionBody} - */ - NodeDeleteTransactionBody.Builder build() { - var builder = NodeDeleteTransactionBody.newBuilder(); - builder.setNodeId(nodeId); - return builder; - } - - /** - * Initialize from the transaction body. - */ - void initFromTransactionBody() { - var body = sourceTransactionBody.getNodeDelete(); - nodeId = body.getNodeId(); - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - // no-op - } - - @Override - MethodDescriptor getMethodDescriptor() { - return AddressBookServiceGrpc.getDeleteNodeMethod(); - } - - @Override - void onFreeze(TransactionBody.Builder bodyBuilder) { - bodyBuilder.setNodeDelete(build()); - } - - @Override - void onScheduled(SchedulableTransactionBody.Builder scheduled) { - scheduled.setNodeDelete(build()); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/Pem.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/Pem.java deleted file mode 100644 index f8ec830f86..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/Pem.java +++ /dev/null @@ -1,153 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import org.bouncycastle.asn1.ASN1Primitive; -import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; -import org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo; -import org.bouncycastle.asn1.pkcs.EncryptionScheme; -import org.bouncycastle.asn1.pkcs.KeyDerivationFunc; -import org.bouncycastle.asn1.pkcs.PBES2Parameters; -import org.bouncycastle.asn1.pkcs.PBKDF2Params; -import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; -import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; -import org.bouncycastle.asn1.x509.AlgorithmIdentifier; -import org.bouncycastle.crypto.params.KeyParameter; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.openssl.PEMEncryptedKeyPair; -import org.bouncycastle.openssl.PEMKeyPair; -import org.bouncycastle.openssl.PEMParser; -import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder; -import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder; -import org.bouncycastle.operator.OperatorCreationException; -import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo; -import org.bouncycastle.pkcs.PKCSException; -import org.bouncycastle.util.io.pem.PemObject; -import org.bouncycastle.util.io.pem.PemWriter; - -import javax.annotation.Nullable; -import javax.crypto.Cipher; -import java.io.IOException; -import java.io.Reader; -import java.io.Writer; - -/** - * Internal utility class for handling PEM objects. - *
- * Privacy-Enhanced Mail (PEM) is a de facto file format for storing and - * sending cryptographic keys, certificates, and other data, based on a set of - * 1993 IETF standards defining "privacy-enhanced mail." - */ -final class Pem { - private static final String TYPE_PRIVATE_KEY = "PRIVATE KEY"; - private static final String TYPE_ENCRYPTED_PRIVATE_KEY = "ENCRYPTED PRIVATE KEY"; - - /** - * Constructor. - */ - private Pem() { - } - - /** - * For some reason, this generates PEM encodings that we ourselves can import, but OpenSSL - * doesn't like. We decided to punt on generating encrypted PEMs for now but saving - * the code for when we get back to it and/or any demand arises. - */ - @SuppressWarnings("unused") - static void writeEncryptedPrivateKey(PrivateKeyInfo pkInfo, Writer out, String passphrase) throws IOException { - byte[] salt = Crypto.randomBytes(Crypto.SALT_LEN); - - KeyParameter derivedKey = Crypto.deriveKeySha256( - passphrase, salt, Crypto.ITERATIONS, Crypto.CBC_DK_LEN); - - byte[] iv = Crypto.randomBytes(Crypto.IV_LEN); - - Cipher cipher = Crypto.initAesCbc128Encrypt(derivedKey, iv); - - byte[] encryptedKey = Crypto.runCipher(cipher, pkInfo.getEncoded()); - - // I wanted to just do this with BC's PKCS8Generator and KcePKCSPBEOutputEncryptorBuilder - // but it tries to init AES instance of `Cipher` with a `PBKDF2Key` and the former complains - - // So this is basically a reimplementation of that minus the excess OO - PBES2Parameters parameters = new PBES2Parameters( - new KeyDerivationFunc( - PKCSObjectIdentifiers.id_PBKDF2, - new PBKDF2Params( - salt, - Crypto.ITERATIONS, - Crypto.CBC_DK_LEN, - new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA256))), - new EncryptionScheme(NISTObjectIdentifiers.id_aes128_CBC, - ASN1Primitive.fromByteArray(cipher.getParameters().getEncoded()))); - - EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo( - new AlgorithmIdentifier(PKCSObjectIdentifiers.id_PBES2, parameters), - encryptedKey); - - PemWriter writer = new PemWriter(out); - writer.writeObject(new PemObject(TYPE_ENCRYPTED_PRIVATE_KEY, encryptedPrivateKeyInfo.getEncoded())); - writer.flush(); - } - - /** - * Create a private key info object from a reader. - * - * @param input reader object - * @param passphrase passphrase - * @return private key info object - * @throws IOException if IO operations fail - */ - static PrivateKeyInfo readPrivateKey(Reader input, @Nullable String passphrase) throws IOException { - try (final var parser = new PEMParser(input)){ - Object parsedObject = parser.readObject(); - var password = (passphrase != null) ? passphrase.toCharArray() : "".toCharArray(); - if (parsedObject == null) { - throw new BadKeyException("PEM file did not contain a private key"); - } else if (parsedObject instanceof PKCS8EncryptedPrivateKeyInfo) { - var decryptProvider = new JceOpenSSLPKCS8DecryptorProviderBuilder() - .setProvider(new BouncyCastleProvider()) - .build(password); - var encryptedPrivateKeyInfo = (PKCS8EncryptedPrivateKeyInfo) parsedObject; - return encryptedPrivateKeyInfo.decryptPrivateKeyInfo(decryptProvider); - } else if (parsedObject instanceof PrivateKeyInfo){ - return (PrivateKeyInfo) parsedObject; - } else if (parsedObject instanceof PEMEncryptedKeyPair) { - var decryptProvider = new JcePEMDecryptorProviderBuilder() - .setProvider(new BouncyCastleProvider()) - .build(password); - var encryptedKeyPair = (PEMEncryptedKeyPair) parsedObject; - return encryptedKeyPair.decryptKeyPair(decryptProvider).getPrivateKeyInfo(); - } else if (parsedObject instanceof PEMKeyPair) { - var keyPair = (PEMKeyPair) parsedObject; - return keyPair.getPrivateKeyInfo(); - } else { - throw new BadKeyException("PEM file contained something the SDK didn't know what to do with: " + parsedObject.getClass().getName()); - } - } catch (PKCSException e) { - if (e.getMessage().contains("password empty")) { - throw new BadKeyException("PEM file contained an encrypted private key but no passphrase was given"); - } - throw new RuntimeException(e); - } catch (OperatorCreationException e) { - throw new RuntimeException(e); - } - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/PendingAirdropId.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/PendingAirdropId.java deleted file mode 100644 index 2a826289d1..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/PendingAirdropId.java +++ /dev/null @@ -1,127 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -/** - * A unique, composite, identifier for a pending airdrop. - * - * Each pending airdrop SHALL be uniquely identified by a PendingAirdropId. - * A PendingAirdropId SHALL be recorded when created and MUST be provided in any transaction - * that would modify that pending airdrop (such as a `claimAirdrop` or `cancelAirdrop`). - */ -public class PendingAirdropId { - private AccountId sender; - private AccountId receiver; - @Nullable - private TokenId tokenId; - @Nullable - private NftId nftId; - - public PendingAirdropId() {} - - PendingAirdropId(AccountId sender, AccountId receiver, TokenId tokenId) { - this.sender = sender; - this.receiver = receiver; - this.tokenId = tokenId; - this.nftId = null; - } - - PendingAirdropId(AccountId sender, AccountId receiver, NftId nftId) { - this.sender = sender; - this.receiver = receiver; - this.nftId = nftId; - this.tokenId = null; - } - - public AccountId getSender() { - return sender; - } - - public PendingAirdropId setSender(@Nonnull AccountId sender) { - this.sender = sender; - return this; - } - - public AccountId getReceiver() { - return receiver; - } - - public PendingAirdropId setReceiver(@Nonnull AccountId receiver) { - this.receiver = receiver; - return this; - } - - public TokenId getTokenId() { - return tokenId; - } - - public PendingAirdropId setTokenId(@Nullable TokenId tokenId) { - this.tokenId = tokenId; - return this; - } - - public NftId getNftId() { - return nftId; - } - - public PendingAirdropId setNftId(@Nullable NftId nftId) { - this.nftId = nftId; - return this; - } - - static PendingAirdropId fromProtobuf(com.hedera.hashgraph.sdk.proto.PendingAirdropId pendingAirdropId) { - if (pendingAirdropId.hasFungibleTokenType()) { - return new PendingAirdropId(AccountId.fromProtobuf(pendingAirdropId.getSenderId()), - AccountId.fromProtobuf(pendingAirdropId.getReceiverId()), - TokenId.fromProtobuf(pendingAirdropId.getFungibleTokenType())); - } else { - return new PendingAirdropId(AccountId.fromProtobuf(pendingAirdropId.getSenderId()), - AccountId.fromProtobuf(pendingAirdropId.getReceiverId()), - NftId.fromProtobuf(pendingAirdropId.getNonFungibleToken())); - } - } - - com.hedera.hashgraph.sdk.proto.PendingAirdropId toProtobuf() { - var builder = com.hedera.hashgraph.sdk.proto.PendingAirdropId.newBuilder() - .setSenderId(sender.toProtobuf()) - .setReceiverId(receiver.toProtobuf()); - - if (tokenId != null) { - builder.setFungibleTokenType(tokenId.toProtobuf()); - } else if (nftId != null) { - builder.setNonFungibleToken(nftId.toProtobuf()); - } - return builder.build(); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("sender", sender) - .add("receiver", receiver) - .add("tokenId", tokenId) - .add("nftId", nftId) - .toString(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/PendingAirdropLogic.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/PendingAirdropLogic.java deleted file mode 100644 index c8fafb043c..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/PendingAirdropLogic.java +++ /dev/null @@ -1,125 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Objects; - -abstract class PendingAirdropLogic> extends Transaction { - - protected List pendingAirdropIds = new ArrayList<>(); - - - protected PendingAirdropLogic() {} - - /** - * Constructor. - * - * @param txs Compound list of transaction id's list of (AccountId, Transaction) records - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - PendingAirdropLogic( - LinkedHashMap> txs) - throws InvalidProtocolBufferException { - super(txs); - } - - /** - * Constructor. - * - * @param txBody protobuf TransactionBody - */ - PendingAirdropLogic(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { - super(txBody); - } - - /** - * Extract the pending airdrop ids - * - * @return the pending airdrop ids - */ - public List getPendingAirdropIds() { - return this.pendingAirdropIds; - } - - /** - * Set the pending airdrop ids - * - * @param pendingAirdropIds - * @return {@code this} - */ - public T setPendingAirdropIds(List pendingAirdropIds) { - Objects.requireNonNull(pendingAirdropIds); - requireNotFrozen(); - this.pendingAirdropIds = pendingAirdropIds; - // noinspection unchecked - return (T) this; - } - - /** - * clear the pending airdrop ids - * - * @return {@code this} - */ - public T clearPendingAirdropIds() { - requireNotFrozen(); - this.pendingAirdropIds = new ArrayList<>(); - // noinspection unchecked - return (T) this; - } - - /** - * Add pendingAirdropId - * - * @param pendingAirdropId - * @return {@code this} - */ - public T addPendingAirdrop(PendingAirdropId pendingAirdropId) { - Objects.requireNonNull(pendingAirdropId); - requireNotFrozen(); - this.pendingAirdropIds.add(pendingAirdropId); - // noinspection unchecked - return (T) this; - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - for (var pendingAirdropId : pendingAirdropIds) { - if (pendingAirdropId.getTokenId() != null) { - pendingAirdropId.getTokenId().validateChecksum(client); - } - - if (pendingAirdropId.getReceiver() != null) { - pendingAirdropId.getReceiver().validateChecksum(client); - } - - if (pendingAirdropId.getSender() != null) { - pendingAirdropId.getSender().validateChecksum(client); - } - - if (pendingAirdropId.getNftId() != null) { - pendingAirdropId.getNftId().tokenId.validateChecksum(client); - } - } - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/PendingAirdropRecord.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/PendingAirdropRecord.java deleted file mode 100644 index ebff20ab96..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/PendingAirdropRecord.java +++ /dev/null @@ -1,62 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.hedera.hashgraph.sdk.proto.PendingAirdropValue; - -public class PendingAirdropRecord { - private final PendingAirdropId pendingAirdropId; - private final long pendingAirdropAmount; - - PendingAirdropRecord(PendingAirdropId pendingAirdropId, long pendingAirdropAmount) { - this.pendingAirdropId = pendingAirdropId; - this.pendingAirdropAmount = pendingAirdropAmount; - } - - public PendingAirdropId getPendingAirdropId() { - return pendingAirdropId; - } - - public long getPendingAirdropAmount() { - return pendingAirdropAmount; - } - - com.hedera.hashgraph.sdk.proto.PendingAirdropRecord toProtobuf() { - return com.hedera.hashgraph.sdk.proto.PendingAirdropRecord.newBuilder() - .setPendingAirdropId(this.pendingAirdropId.toProtobuf()) - .setPendingAirdropValue(PendingAirdropValue.newBuilder().setAmount(pendingAirdropAmount)) - .build(); - } - - static PendingAirdropRecord fromProtobuf(com.hedera.hashgraph.sdk.proto.PendingAirdropRecord pendingAirdropRecord) { - return new PendingAirdropRecord( - PendingAirdropId.fromProtobuf(pendingAirdropRecord.getPendingAirdropId()), - pendingAirdropRecord.getPendingAirdropValue().getAmount()); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("pendingAirdropId", pendingAirdropId) - .add("pendingAirdropAmount", pendingAirdropAmount) - .toString(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/PrecheckStatusException.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/PrecheckStatusException.java deleted file mode 100644 index 10ac1ace55..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/PrecheckStatusException.java +++ /dev/null @@ -1,68 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import javax.annotation.Nullable; - -/** - * Signals that a transaction has failed the pre-check. - *

- * Before a node submits a transaction to the rest of the network, - * it attempts some cheap assertions. This process is called the "pre-check". - */ -public class PrecheckStatusException extends Exception { - /** - * The status of the failing transaction - */ - public final Status status; - - /** - * The ID of the transaction that failed. - *

- * This can be `null` if a query fails pre-check without an - * associated payment transaction. - */ - @Nullable - public final TransactionId transactionId; - - /** - * Constructor. - * - * @param status the status - * @param transactionId the transaction id - */ - PrecheckStatusException(Status status, @Nullable TransactionId transactionId) { - this.status = status; - this.transactionId = transactionId; - } - - @Override - public String getMessage() { - var stringBuilder = new StringBuilder(); - - if (transactionId != null) { - stringBuilder.append("Hedera transaction `").append(transactionId).append("` "); - } - - stringBuilder.append("failed pre-check with the status `").append(status).append("`"); - - return stringBuilder.toString(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/PrngTransaction.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/PrngTransaction.java deleted file mode 100644 index ba30ba3b45..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/PrngTransaction.java +++ /dev/null @@ -1,94 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.*; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; - -/** - * Random Number Generator Transaction. - */ -public class PrngTransaction extends Transaction { - - /** - * If provided and is positive, returns a 32-bit pseudorandom number from the given range in the transaction record. - * If not set or set to zero, will return a 384-bit pseudorandom data in the record. - */ - @Nullable - private Integer range = null; - - /** - * Constructor. - */ - public PrngTransaction() { - } - - /** - * Assign the range. - * - * @param range if > 0 32 bit else 384 bit - * @return {@code this} - */ - public PrngTransaction setRange(Integer range) { - this.range = range; - return this; - } - - /** - * Retrieve the range. - * - * @return the range - */ - public Integer getRange() { - return range; - } - - UtilPrngTransactionBody.Builder build() { - var builder = UtilPrngTransactionBody.newBuilder(); - - if (range != null) { - builder.setRange(range); - } - - return builder; - } - - @Override - void onFreeze(TransactionBody.Builder bodyBuilder) { - bodyBuilder.setUtilPrng(build()); - } - - @Override - void onScheduled(SchedulableTransactionBody.Builder scheduled) { - throw new UnsupportedOperationException("cannot schedule RngTransaction"); - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - } - - @Override - MethodDescriptor getMethodDescriptor() { - return UtilServiceGrpc.getPrngMethod(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/ProxyStaker.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/ProxyStaker.java deleted file mode 100644 index 8285607561..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/ProxyStaker.java +++ /dev/null @@ -1,64 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -/** - * Information about a single account that is proxy staking. - */ -public final class ProxyStaker { - /** - * The Account ID that is proxy staking. - */ - public final AccountId accountId; - - /** - * The number of hbars that are currently proxy staked. - */ - public final Hbar amount; - - /** - * Constructor. - * - * @param accountId the account id - * @param amount the amount - */ - private ProxyStaker(AccountId accountId, long amount) { - this.accountId = accountId; - this.amount = Hbar.fromTinybars(amount); - } - - /** - * Create a proxy staker object from a protobuf. - * - * @param proxyStaker the protobuf - * @return the new proxy staker object - */ - static ProxyStaker fromProtobuf(com.hedera.hashgraph.sdk.proto.ProxyStaker proxyStaker) { - return new ProxyStaker(AccountId.fromProtobuf(proxyStaker.getAccountID()), proxyStaker.getAmount()); - } - - @Override - public String toString() { - return "ProxyStaker{" + - "accountId=" + accountId + - ", amount=" + amount + - '}'; - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/PublicKeyECDSA.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/PublicKeyECDSA.java deleted file mode 100644 index 986a2c1426..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/PublicKeyECDSA.java +++ /dev/null @@ -1,178 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import static com.hedera.hashgraph.sdk.Crypto.calcKeccak256; - -import com.google.protobuf.ByteString; -import com.hedera.hashgraph.sdk.proto.SignaturePair; -import java.io.IOException; -import java.math.BigInteger; -import java.util.Arrays; -import org.bouncycastle.asn1.x509.AlgorithmIdentifier; -import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; -import org.bouncycastle.crypto.params.ECPublicKeyParameters; -import org.bouncycastle.crypto.signers.ECDSASigner; - -/** - * Encapsulate the ECDSA public key. - */ -class PublicKeyECDSA extends PublicKey { - // Compressed 33 byte form - private byte[] keyData; - - /** - * Constructor. - * - * @param keyData the byte array representing the key - */ - private PublicKeyECDSA(byte[] keyData) { - this.keyData = keyData; - } - - /** - * Create a key from a byte array representation. - * - * @param publicKey the byte array representing the key - * @return the new key - */ - static PublicKeyECDSA fromBytesInternal(byte[] publicKey) { - // Validate the key if it's not all zero public key, see HIP-540 - if (Arrays.equals(publicKey, new byte[33])) { - return new PublicKeyECDSA(publicKey); - } - if (publicKey.length == 33 || publicKey.length == 65) { - return new PublicKeyECDSA( - // compress and validate the key - Key.ECDSA_SECP256K1_CURVE.getCurve().decodePoint(publicKey).getEncoded(true)); - } - - // Assume a DER-encoded public key descriptor - return fromSubjectKeyInfoInternal(SubjectPublicKeyInfo.getInstance(publicKey)); - } - - /** - * Create a key from a subject public key info object. - * - * @param subjectPublicKeyInfo the subject public key info object - * @return the new public key - */ - static PublicKeyECDSA fromSubjectKeyInfoInternal(SubjectPublicKeyInfo subjectPublicKeyInfo) { - return fromBytesInternal(subjectPublicKeyInfo.getPublicKeyData().getBytes()); - } - - @Override - ByteString extractSignatureFromProtobuf(SignaturePair pair) { - return pair.getECDSA384(); - } - - @Override - public boolean verify(byte[] message, byte[] signature) { - var hash = calcKeccak256(message); - - ECDSASigner signer = new ECDSASigner(); - signer.init(false, new ECPublicKeyParameters( - Key.ECDSA_SECP256K1_CURVE.getCurve().decodePoint(keyData), - Key.ECDSA_SECP256K1_DOMAIN - )); - - BigInteger r = new BigInteger(1, Arrays.copyOf(signature, 32)); - BigInteger s = new BigInteger(1, Arrays.copyOfRange(signature, 32, 64)); - - return signer.verifySignature(hash, r, s); - } - - @Override - com.hedera.hashgraph.sdk.proto.Key toProtobufKey() { - return com.hedera.hashgraph.sdk.proto.Key.newBuilder() - .setECDSASecp256K1(ByteString.copyFrom(keyData)) - .build(); - } - - @Override - SignaturePair toSignaturePairProtobuf(byte[] signature) { - return SignaturePair.newBuilder() - .setPubKeyPrefix(ByteString.copyFrom(keyData)) - .setECDSASecp256K1(ByteString.copyFrom(signature)) - .build(); - } - - @Override - public byte[] toBytesDER() { - try { - return new SubjectPublicKeyInfo( - new AlgorithmIdentifier(ID_EC_PUBLIC_KEY, ID_ECDSA_SECP256K1), - keyData - ).getEncoded("DER"); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - @Override - public byte[] toBytes() { - return toBytesDER(); - } - - @Override - public byte[] toBytesRaw() { - return Arrays.copyOf(keyData, keyData.length); - } - - @Override - public EvmAddress toEvmAddress() { - // Calculate the Keccak-256 hash of the uncompressed key without "04" prefix - byte[] uncompressed = Key.ECDSA_SECP256K1_CURVE - .getCurve().decodePoint(toBytesRaw()).getEncoded(false); - byte[] keccakBytes = calcKeccak256(Arrays.copyOfRange(uncompressed, 1, uncompressed.length)); - - // Return the last 20 bytes - return EvmAddress.fromBytes(Arrays.copyOfRange(keccakBytes, 12, 32)); - } - - @Override - public boolean equals( Object o) { - if (this == o) { - return true; - } - - if (o == null || getClass() != o.getClass()) { - return false; - } - - PublicKeyECDSA publicKey = (PublicKeyECDSA) o; - return Arrays.equals(keyData, publicKey.keyData); - } - - @Override - public int hashCode() { - return Arrays.hashCode(keyData); - } - - @Override - public boolean isED25519() { - return false; - } - - @Override - public boolean isECDSA() { - return true; - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/PublicKeyED25519.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/PublicKeyED25519.java deleted file mode 100644 index ca7928aa62..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/PublicKeyED25519.java +++ /dev/null @@ -1,159 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import com.hedera.hashgraph.sdk.proto.SignaturePair; -import org.bouncycastle.asn1.x509.AlgorithmIdentifier; -import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; -import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters; -import org.bouncycastle.math.ec.rfc8032.Ed25519; - -import javax.annotation.Nullable; -import java.io.IOException; -import java.util.Arrays; - -/** - * Encapsulate the ED25519 public key. - */ -class PublicKeyED25519 extends PublicKey { - private final byte[] keyData; - - /** - * Constructor. - * - * @param keyData the byte array representing the key - */ - private PublicKeyED25519(byte[] keyData) { - this.keyData = keyData; - } - - /** - * Create a key from a byte array representation. - * - * @param publicKey the byte array representing the key - * @return the new key - */ - static PublicKeyED25519 fromBytesInternal(byte[] publicKey) { - if (publicKey.length == Ed25519.PUBLIC_KEY_SIZE) { - // Validate the key if it's not all zero public key, see HIP-540 - if (!Arrays.equals(publicKey, new byte[32])) { - // Will throw if the key is invalid - new Ed25519PublicKeyParameters(publicKey, 0); - } - // If this is a 32 byte string, assume an Ed25519 public key - return new PublicKeyED25519(publicKey); - } - - // Assume a DER-encoded public key descriptor - return fromSubjectKeyInfoInternal(SubjectPublicKeyInfo.getInstance(publicKey)); - } - - /** - * Create a key from a subject public key info object. - * - * @param subjectPublicKeyInfo the subject public key info object - * @return the new public key - */ - static PublicKeyED25519 fromSubjectKeyInfoInternal(SubjectPublicKeyInfo subjectPublicKeyInfo) { - return new PublicKeyED25519(subjectPublicKeyInfo.getPublicKeyData().getBytes()); - } - - @Override - ByteString extractSignatureFromProtobuf(SignaturePair pair) { - return pair.getEd25519(); - } - - @Override - public boolean verify(byte[] message, byte[] signature) { - return Ed25519.verify(signature, 0, keyData, 0, message, 0, message.length); - } - - @Override - com.hedera.hashgraph.sdk.proto.Key toProtobufKey() { - return com.hedera.hashgraph.sdk.proto.Key.newBuilder() - .setEd25519(ByteString.copyFrom(keyData)) - .build(); - } - - @Override - SignaturePair toSignaturePairProtobuf(byte[] signature) { - return SignaturePair.newBuilder() - .setPubKeyPrefix(ByteString.copyFrom(keyData)) - .setEd25519(ByteString.copyFrom(signature)) - .build(); - } - - @Override - public byte[] toBytesDER() { - try { - return new SubjectPublicKeyInfo( - new AlgorithmIdentifier(ID_ED25519), - keyData - ).getEncoded("DER"); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - @Override - public byte[] toBytes() { - return toBytesRaw(); - } - - @Override - public byte[] toBytesRaw() { - return keyData; - } - - @Override - public EvmAddress toEvmAddress() { - throw new IllegalStateException("unsupported operation on Ed25519PublicKey"); - } - - @Override - public boolean equals( Object o) { - if (this == o) { - return true; - } - - if (o == null || getClass() != o.getClass()) { - return false; - } - - PublicKeyED25519 publicKey = (PublicKeyED25519) o; - return Arrays.equals(keyData, publicKey.keyData); - } - - @Override - public int hashCode() { - return Arrays.hashCode(keyData); - } - - @Override - public boolean isED25519() { - return true; - } - - @Override - public boolean isECDSA() { - return false; - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/ReceiptStatusException.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/ReceiptStatusException.java deleted file mode 100644 index 2792f5e205..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/ReceiptStatusException.java +++ /dev/null @@ -1,59 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import javax.annotation.Nullable; - -/** - * An Exception thrown on error status by {@link TransactionId#getReceipt(Client)}. - *

- * The receipt is included, though only the {@link TransactionReceipt#status} field will be - * initialized; all the getters should throw. - */ -public class ReceiptStatusException extends Exception { - /** - * The ID of the transaction that failed, in case that context is no longer available - * (e.g. the exception was bubbled up). - */ - @Nullable - public final TransactionId transactionId; - - /** - * The receipt of the transaction that failed; the only initialized field is - * {@link TransactionReceipt#status}. - */ - public final TransactionReceipt receipt; - - /** - * Constructor. - * - * @param transactionId the transaction id - * @param receipt the receipt - */ - ReceiptStatusException(@Nullable TransactionId transactionId, TransactionReceipt receipt) { - this.transactionId = transactionId; - this.receipt = receipt; - } - - @Override - public String getMessage() { - return "receipt for transaction " + transactionId + " raised status " + receipt.status; - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/ScheduleDeleteTransaction.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/ScheduleDeleteTransaction.java deleted file mode 100644 index c4d39ad114..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/ScheduleDeleteTransaction.java +++ /dev/null @@ -1,141 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.ScheduleDeleteTransactionBody; -import com.hedera.hashgraph.sdk.proto.ScheduleServiceGrpc; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; -import java.util.LinkedHashMap; -import java.util.Objects; - -/** - * Delete a scheduled transaction. - *

- * See Hedera Documentation - */ -public final class ScheduleDeleteTransaction extends Transaction { - - @Nullable - private ScheduleId scheduleId = null; - - /** - * Constructor. - */ - public ScheduleDeleteTransaction() { - defaultMaxTransactionFee = new Hbar(5); - } - - /** - * Constructor. - * - * @param txs Compound list of transaction id's list of (AccountId, Transaction) - * records - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - ScheduleDeleteTransaction(LinkedHashMap> txs) throws InvalidProtocolBufferException { - super(txs); - initFromTransactionBody(); - } - - /** - * Constructor. - * - * @param txBody protobuf TransactionBody - */ - ScheduleDeleteTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { - super(txBody); - initFromTransactionBody(); - } - - /** - * Extract the schedule id. - * - * @return the schedule id - */ - @Nullable - public ScheduleId getScheduleId() { - return scheduleId; - } - - /** - * Assign the scheduled id. - * - * @param scheduleId the schedule id - * @return {@code this} - */ - public ScheduleDeleteTransaction setScheduleId(ScheduleId scheduleId) { - Objects.requireNonNull(scheduleId); - requireNotFrozen(); - this.scheduleId = scheduleId; - return this; - } - - /** - * Initialize from the transaction body. - */ - void initFromTransactionBody() { - var body = sourceTransactionBody.getScheduleDelete(); - if (body.hasScheduleID()) { - scheduleId = ScheduleId.fromProtobuf(body.getScheduleID()); - } - } - - /** - * Build the correct transaction body. - * - * @return {@link com.hedera.hashgraph.sdk.proto.ScheduleDeleteTransactionBody builder } - */ - ScheduleDeleteTransactionBody.Builder build() { - var builder = ScheduleDeleteTransactionBody.newBuilder(); - if (scheduleId != null) { - builder.setScheduleID(scheduleId.toProtobuf()); - } - - return builder; - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (scheduleId != null) { - scheduleId.validateChecksum(client); - } - } - - @Override - MethodDescriptor getMethodDescriptor() { - return ScheduleServiceGrpc.getDeleteScheduleMethod(); - } - - @Override - void onFreeze(TransactionBody.Builder bodyBuilder) { - bodyBuilder.setScheduleDelete(build()); - } - - @Override - void onScheduled(SchedulableTransactionBody.Builder scheduled) { - scheduled.setScheduleDelete(build()); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/ScheduleInfo.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/ScheduleInfo.java deleted file mode 100644 index 2262b051f3..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/ScheduleInfo.java +++ /dev/null @@ -1,280 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.ScheduleGetInfoResponse; -import java.time.Instant; - -import javax.annotation.Nullable; - -/** - * A query that returns information about the current state of a scheduled - * transaction on a Hedera network. - *

- * See Hedera Documentation - */ -public final class ScheduleInfo { - /** - * The ID of the schedule transaction - */ - public final ScheduleId scheduleId; - /** - * The Hedera account that created the schedule transaction in x.y.z format - */ - public final AccountId creatorAccountId; - /** - * The Hedera account paying for the execution of the schedule transaction - * in x.y.z format - */ - public final AccountId payerAccountId; - /** - * The signatories that have provided signatures so far for the schedule - * transaction - */ - public final KeyList signatories; - - /** - * The Key which is able to delete the schedule transaction if set - */ - @Nullable - public final Key adminKey; - - /** - * The scheduled transaction - */ - @Nullable - public final TransactionId scheduledTransactionId; - - /** - * Publicly visible information about the Schedule entity, up to - * 100 bytes. No guarantee of uniqueness. - */ - public final String memo; - - /** - * The date and time the schedule transaction will expire - */ - @Nullable - public final Instant expirationTime; - - /** - * The time the schedule transaction was executed. If the schedule - * transaction has not executed this field will be left null. - */ - @Nullable - public final Instant executedAt; - - /** - * The consensus time the schedule transaction was deleted. If the - * schedule transaction was not deleted, this field will be left null. - */ - @Nullable - public final Instant deletedAt; - - /** - * The scheduled transaction (inner transaction). - */ - final SchedulableTransactionBody transactionBody; - - /** - * The ledger ID the response was returned from; please see HIP-198 for the network-specific IDs. - */ - @Nullable - public final LedgerId ledgerId; - - /** - * When set to true, the transaction will be evaluated for execution at expiration_time instead - * of when all required signatures are received. - * When set to false, the transaction will execute immediately after sufficient signatures are received - * to sign the contained transaction. During the initial ScheduleCreate transaction or via ScheduleSign transactions. - * - * Note: this field is unused until Long Term Scheduled Transactions are enabled. - */ - public final boolean waitForExpiry; - - /** - * Constructor. - * - * @param scheduleId the schedule id - * @param creatorAccountId the creator account id - * @param payerAccountId the payer account id - * @param transactionBody the transaction body - * @param signers the signers key list - * @param adminKey the admin key - * @param scheduledTransactionId the transaction id - * @param memo the memo 100 bytes max - * @param expirationTime the expiration time - * @param executed the time transaction was executed - * @param deleted the time it was deleted - * @param ledgerId the ledger id - * @param waitForExpiry the wait for expiry field - */ - ScheduleInfo( - ScheduleId scheduleId, - AccountId creatorAccountId, - AccountId payerAccountId, - SchedulableTransactionBody transactionBody, - KeyList signers, - @Nullable Key adminKey, - @Nullable TransactionId scheduledTransactionId, - String memo, - @Nullable Instant expirationTime, - @Nullable Instant executed, - @Nullable Instant deleted, - LedgerId ledgerId, - boolean waitForExpiry - ) { - this.scheduleId = scheduleId; - this.creatorAccountId = creatorAccountId; - this.payerAccountId = payerAccountId; - this.signatories = signers; - this.adminKey = adminKey; - this.transactionBody = transactionBody; - this.scheduledTransactionId = scheduledTransactionId; - this.memo = memo; - this.expirationTime = expirationTime; - this.executedAt = executed; - this.deletedAt = deleted; - this.ledgerId = ledgerId; - this.waitForExpiry = waitForExpiry; - } - - /** - * Create a schedule info object from a protobuf. - * - * @param info the protobuf - * @return the new schedule info object - */ - static ScheduleInfo fromProtobuf(com.hedera.hashgraph.sdk.proto.ScheduleInfo info) { - var scheduleId = ScheduleId.fromProtobuf(info.getScheduleID()); - var creatorAccountId = AccountId.fromProtobuf(info.getCreatorAccountID()); - var payerAccountId = AccountId.fromProtobuf(info.getPayerAccountID()); - var adminKey = info.hasAdminKey() ? Key.fromProtobufKey(info.getAdminKey()) : null; - var scheduledTransactionId = info.hasScheduledTransactionID() ? - TransactionId.fromProtobuf(info.getScheduledTransactionID()) : - null; - - return new ScheduleInfo( - scheduleId, - creatorAccountId, - payerAccountId, - info.getScheduledTransactionBody(), - KeyList.fromProtobuf(info.getSigners(), null), - adminKey, - scheduledTransactionId, - info.getMemo(), - info.hasExpirationTime() ? InstantConverter.fromProtobuf(info.getExpirationTime()) : null, - info.hasExecutionTime() ? InstantConverter.fromProtobuf(info.getExecutionTime()) : null, - info.hasDeletionTime() ? InstantConverter.fromProtobuf(info.getDeletionTime()) : null, - LedgerId.fromByteString(info.getLedgerId()), - info.getWaitForExpiry() - ); - } - - /** - * Create a schedule info object from a byte array. - * - * @param bytes the byte array - * @return the new schedule info object - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - public static ScheduleInfo fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return fromProtobuf(com.hedera.hashgraph.sdk.proto.ScheduleInfo.parseFrom(bytes).toBuilder().build()); - } - - /** - * Create the protobuf. - * - * @return the protobuf representation - */ - com.hedera.hashgraph.sdk.proto.ScheduleInfo toProtobuf() { - var info = com.hedera.hashgraph.sdk.proto.ScheduleInfo.newBuilder(); - - if (adminKey != null) { - info.setAdminKey(adminKey.toProtobufKey()); - } - - if (scheduledTransactionId != null) { - info.setScheduledTransactionID(scheduledTransactionId.toProtobuf()); - } - - if (expirationTime != null) { - info.setExpirationTime(InstantConverter.toProtobuf(expirationTime)); - } - - if (executedAt != null) { - info.setExecutionTime(InstantConverter.toProtobuf(executedAt)); - } - - if (deletedAt != null) { - info.setDeletionTime(InstantConverter.toProtobuf(deletedAt)); - } - - return info - .setScheduleID(scheduleId.toProtobuf()) - .setCreatorAccountID(creatorAccountId.toProtobuf()) - .setScheduledTransactionBody(transactionBody) - .setPayerAccountID(payerAccountId.toProtobuf()) - .setSigners(signatories.toProtobuf()) - .setMemo(memo) - .setLedgerId(ledgerId.toByteString()) - .setWaitForExpiry(waitForExpiry) - .build(); - } - - /** - * Extract the transaction. - * - * @return the transaction - */ - public Transaction getScheduledTransaction() { - return Transaction.fromScheduledTransaction(transactionBody); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("scheduleId", scheduleId) - .add("scheduledTransactionId", scheduledTransactionId) - .add("creatorAccountId", creatorAccountId) - .add("payerAccountId", payerAccountId) - .add("signatories", signatories) - .add("adminKey", adminKey) - .add("expirationTime", expirationTime) - .add("memo", memo) - .add("executedAt", executedAt) - .add("deletedAt", deletedAt) - .add("ledgerId", ledgerId) - .add("waitForExpiry", waitForExpiry) - .toString(); - } - - /** - * Create the byte array. - * - * @return the byte array representation - */ - public byte[] toBytes() { - return toProtobuf().toByteArray(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/ScheduleInfoQuery.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/ScheduleInfoQuery.java deleted file mode 100644 index e5a7b0cdc9..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/ScheduleInfoQuery.java +++ /dev/null @@ -1,115 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.Query; -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import com.hedera.hashgraph.sdk.proto.Response; -import com.hedera.hashgraph.sdk.proto.ResponseHeader; -import com.hedera.hashgraph.sdk.proto.ScheduleGetInfoQuery; -import com.hedera.hashgraph.sdk.proto.ScheduleServiceGrpc; -import io.grpc.MethodDescriptor; -import java.util.Objects; -import java.util.concurrent.CompletableFuture; -import javax.annotation.Nullable; - -/** - * A query that returns information about the current state of a schedule - * transaction on a Hedera network. - * - * See Hedera Documentation - */ -public class ScheduleInfoQuery extends com.hedera.hashgraph.sdk.Query { - @Nullable - private ScheduleId scheduleId = null; - - /** - * Constructor. - */ - public ScheduleInfoQuery() { - } - - /** - * Extract the schedule id. - * - * @return the schedule id - */ - @Nullable - public ScheduleId getScheduleId() { - return scheduleId; - } - - /** - * Assign the schedule id. - * - * @param scheduleId the schedule id - * @return {@code this} - */ - public ScheduleInfoQuery setScheduleId(ScheduleId scheduleId) { - Objects.requireNonNull(scheduleId); - this.scheduleId = scheduleId; - return this; - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (scheduleId != null) { - scheduleId.validateChecksum(client); - } - } - - @Override - void onMakeRequest(com.hedera.hashgraph.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { - var builder = ScheduleGetInfoQuery.newBuilder(); - if (scheduleId != null) { - builder.setScheduleID(scheduleId.toProtobuf()); - } - - queryBuilder.setScheduleGetInfo(builder.setHeader(header)); - } - - @Override - ResponseHeader mapResponseHeader(Response response) { - return response.getScheduleGetInfo().getHeader(); - } - - @Override - QueryHeader mapRequestHeader(com.hedera.hashgraph.sdk.proto.Query request) { - return request.getScheduleGetInfo().getHeader(); - } - - @Override - ScheduleInfo mapResponse(Response response, AccountId nodeId, Query request) { - return ScheduleInfo.fromProtobuf(response.getScheduleGetInfo().getScheduleInfo()); - } - - @Override - MethodDescriptor getMethodDescriptor() { - return ScheduleServiceGrpc.getGetScheduleInfoMethod(); - } - - @Override - public CompletableFuture getCostAsync(Client client) { - // deleted accounts return a COST_ANSWER of zero which triggers `INSUFFICIENT_TX_FEE` - // if you set that as the query payment; 25 tinybar seems to be enough to get - // `Token_DELETED` back instead. - return super.getCostAsync(client).thenApply((cost) -> Hbar.fromTinybars(Math.max(cost.toTinybars(), 25))); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/ScheduleSignTransaction.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/ScheduleSignTransaction.java deleted file mode 100644 index a1f04041b6..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/ScheduleSignTransaction.java +++ /dev/null @@ -1,153 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.ScheduleServiceGrpc; -import com.hedera.hashgraph.sdk.proto.ScheduleSignTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; -import java.util.LinkedHashMap; -import java.util.Objects; - -/** - * A transaction that appends signatures to a schedule transaction. - * You will need to know the schedule ID to reference the schedule - * transaction to submit signatures to. A record will be generated - * for each ScheduleSign transaction that is successful and the schedule - * entity will subsequently update with the public keys that have signed - * the schedule transaction. To view the keys that have signed the - * schedule transaction, you can query the network for the schedule info. - * Once a schedule transaction receives the last required signature, the - * schedule transaction executes. - * - * See Hedera Documentation - */ -public final class ScheduleSignTransaction extends Transaction { - - @Nullable - private ScheduleId scheduleId = null; - - /** - * Constructor. - */ - public ScheduleSignTransaction() { - defaultMaxTransactionFee = new Hbar(5); - } - - /** - * Constructor. - * - * @param txs Compound list of transaction id's list of (AccountId, Transaction) - * records - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - ScheduleSignTransaction(LinkedHashMap> txs) throws InvalidProtocolBufferException { - super(txs); - initFromTransactionBody(); - } - - /** - * Extract the schedule id. - * - * @return the schedule id - */ - @Nullable - public ScheduleId getScheduleId() { - return scheduleId; - } - - /** - * Assign the schedule id. - * - * @param scheduleId the schedule id - * @return {@code this} - */ - public ScheduleSignTransaction setScheduleId(ScheduleId scheduleId) { - Objects.requireNonNull(scheduleId); - requireNotFrozen(); - this.scheduleId = scheduleId; - return this; - } - - /** - * Clears the schedule id - * - * @return {@code this} - */ - @Deprecated - public ScheduleSignTransaction clearScheduleId() { - requireNotFrozen(); - this.scheduleId = null; - return this; - } - - /** - * Build the correct transaction body. - * - * @return {@link - * com.hedera.hashgraph.sdk.proto.ScheduleSignTransactionBody - * builder } - */ - ScheduleSignTransactionBody.Builder build() { - var builder = ScheduleSignTransactionBody.newBuilder(); - if (scheduleId != null) { - builder.setScheduleID(scheduleId.toProtobuf()); - } - - return builder; - } - - /** - * Initialize from the transaction body. - */ - void initFromTransactionBody() { - var body = sourceTransactionBody.getScheduleSign(); - if (body.hasScheduleID()) { - scheduleId = ScheduleId.fromProtobuf(body.getScheduleID()); - } - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (scheduleId != null) { - scheduleId.validateChecksum(client); - } - } - - @Override - MethodDescriptor getMethodDescriptor() { - return ScheduleServiceGrpc.getSignScheduleMethod(); - } - - @Override - void onFreeze(TransactionBody.Builder bodyBuilder) { - bodyBuilder.setScheduleSign(build()); - } - - @Override - void onScheduled(SchedulableTransactionBody.Builder scheduled) { - throw new UnsupportedOperationException("cannot schedule ScheduleSignTransaction"); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/SemanticVersion.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/SemanticVersion.java deleted file mode 100644 index 3a27eb46f3..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/SemanticVersion.java +++ /dev/null @@ -1,110 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; - -/** - * Hedera follows semantic versioning () for both the HAPI protobufs and - * the Services software. This type allows the getVersionInfo query in the - * NetworkService to return the deployed versions of both protobufs and - * software on the node answering the query. - * - * See Hedera Documentation - */ -public class SemanticVersion { - /** - * Increases with incompatible API changes - */ - public int major; - /** - * Increases with backwards-compatible new functionality - */ - public int minor; - /** - * Increases with backwards-compatible bug fixes - */ - public int patch; - - /** - * Constructor. - * - * @param major the major part - * @param minor the minor part - * @param patch the patch part - */ - SemanticVersion(int major, int minor, int patch) { - this.major = major; - this.minor = minor; - this.patch = patch; - } - - /** - * Create a semantic version object from a protobuf. - * - * @param version the protobuf - * @return the new semantic version - */ - protected static SemanticVersion fromProtobuf(com.hedera.hashgraph.sdk.proto.SemanticVersion version) { - return new SemanticVersion( - version.getMajor(), - version.getMinor(), - version.getPatch() - ); - } - - /** - * Create a semantic version from a byte array. - * - * @param bytes the byte array - * @return the new semantic version - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - public static SemanticVersion fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return fromProtobuf(com.hedera.hashgraph.sdk.proto.SemanticVersion.parseFrom(bytes)); - } - - /** - * Create the protobuf. - * - * @return the protobuf representation - */ - protected com.hedera.hashgraph.sdk.proto.SemanticVersion toProtobuf() { - return com.hedera.hashgraph.sdk.proto.SemanticVersion.newBuilder() - .setMajor(major) - .setMinor(minor) - .setPatch(patch) - .build(); - } - - /** - * Create the byte array. - * - * @return the byte array representation - */ - public byte[] toBytes() { - return toProtobuf().toByteArray(); - } - - @Override - public String toString() { - return String.format("%d.%d.%d", major, minor, patch); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/StakingInfo.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/StakingInfo.java deleted file mode 100644 index f1eef7cb85..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/StakingInfo.java +++ /dev/null @@ -1,141 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.google.protobuf.InvalidProtocolBufferException; -import java.time.Instant; - -import javax.annotation.Nullable; - -/** - * Staking metadata for an account or a contract returned in CryptoGetInfo or ContractGetInfo queries - */ -public class StakingInfo { - /** - * If true, the contract declines receiving a staking reward. The default value is false. - */ - public final boolean declineStakingReward; - /** - * The staking period during which either the staking settings for this account or contract changed (such as starting - * staking or changing staked_node_id) or the most recent reward was earned, whichever is later. If this account or contract - * is not currently staked to a node, then this field is not set. - */ - public final Instant stakePeriodStart; - /** - * The amount in Hbar that will be received in the next reward situation. - */ - public final Hbar pendingReward; - /** - * The total of balance of all accounts staked to this account or contract. - */ - public final Hbar stakedToMe; - - /** - * The account to which this account or contract is staking. - */ - @Nullable - public final AccountId stakedAccountId; - - /** - * The ID of the node this account or contract is staked to. - */ - @Nullable - public final Long stakedNodeId; - - /** - * Constructor - * - * @param declineStakingReward the declineStakingReward - * @param stakePeriodStart the stakePeriodStart - * @param pendingReward the amount in Hbar that will be received in the next reward situation - * @param stakedToMe the total of balance of all accounts staked to this account or contract - * @param stakedAccountId the account to which this account or contract is staking - * @param stakedNodeId the ID of the node this account or contract is staked to - */ - public StakingInfo(boolean declineStakingReward, Instant stakePeriodStart, Hbar pendingReward, Hbar stakedToMe, @Nullable AccountId stakedAccountId, @Nullable Long stakedNodeId) { - this.declineStakingReward = declineStakingReward; - this.stakePeriodStart = stakePeriodStart; - this.pendingReward = pendingReward; - this.stakedToMe = stakedToMe; - this.stakedAccountId = stakedAccountId; - this.stakedNodeId = stakedNodeId; - } - - static StakingInfo fromProtobuf(com.hedera.hashgraph.sdk.proto.StakingInfo info) { - return new StakingInfo( - info.getDeclineReward(), - InstantConverter.fromProtobuf(info.getStakePeriodStart()), - Hbar.fromTinybars(info.getPendingReward()), - Hbar.fromTinybars(info.getStakedToMe()), - info.hasStakedAccountId() ? AccountId.fromProtobuf(info.getStakedAccountId()) : null, - info.hasStakedNodeId() ? info.getStakedNodeId() : null - ); - } - - /** - * Convert a byte array to a staking info object. - * - * @param bytes the byte array - * @return the converted staking info object - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - public static StakingInfo fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return fromProtobuf(com.hedera.hashgraph.sdk.proto.StakingInfo.parseFrom(bytes)); - } - - com.hedera.hashgraph.sdk.proto.StakingInfo toProtobuf() { - var builder = com.hedera.hashgraph.sdk.proto.StakingInfo.newBuilder() - .setDeclineReward(declineStakingReward) - .setStakePeriodStart(InstantConverter.toProtobuf(stakePeriodStart)) - .setPendingReward(pendingReward.toTinybars()) - .setStakedToMe(stakedToMe.toTinybars()); - - if (stakedAccountId != null) { - builder.setStakedAccountId(stakedAccountId.toProtobuf()); - } - - if (stakedNodeId != null) { - builder.setStakedNodeId(stakedNodeId); - } - - return builder.build(); - } - - /** - * Convert the staking info object to a byte array. - * - * @return the converted staking info object - */ - public byte[] toBytes() { - return toProtobuf().toByteArray(); - } - - public String toString() { - return MoreObjects.toStringHelper(this) - .add("declineStakingReward", declineStakingReward) - .add("stakePeriodStart", stakePeriodStart) - .add("pendingReward", pendingReward) - .add("stakedToMe", stakedToMe) - .add("stakedAccountId", stakedAccountId) - .add("stakedNodeId", stakedNodeId) - .toString(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/StorageChange.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/StorageChange.java deleted file mode 100644 index ddd0245ca1..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/StorageChange.java +++ /dev/null @@ -1,125 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import com.google.protobuf.BytesValue; -import com.google.protobuf.InvalidProtocolBufferException; - -import javax.annotation.Nullable; -import java.math.BigInteger; - -/** - * @deprecated - User mirror nodes for contract traceability instead - * - * A storage slot change description. - * See Hedera Documentation - */ -@Deprecated -public class StorageChange { - /** - * The storage slot changed. Up to 32 bytes, big-endian, zero bytes left trimmed - */ - public final BigInteger slot; - /** - * The value read from the storage slot. Up to 32 bytes, big-endian, zero - * bytes left trimmed. Because of the way SSTORE operations are charged - * the slot is always read before being written to - */ - public final BigInteger valueRead; - /** - * The new value written to the slot. Up to 32 bytes, big-endian, zero - * bytes left trimmed. If a value of zero is written the valueWritten - * will be present but the inner value will be absent. If a value was - * read and not written this value will not be present. - */ - @Nullable - public final BigInteger valueWritten; - - /** - * Constructor. - * - * @param slot the storage slot charged - * @param valueRead the value read - * @param valueWritten the value written - */ - StorageChange(BigInteger slot, BigInteger valueRead, @Nullable BigInteger valueWritten) { - this.slot = slot; - this.valueRead = valueRead; - this.valueWritten = valueWritten; - } - - // /** - // * Create a storage charge from a protobuf. - // * - // * @param storageChangeProto the protobuf - // * @return the new storage charge object - // */ - // static StorageChange fromProtobuf(com.hedera.hashgraph.sdk.proto.StorageChange storageChangeProto) { - // return new StorageChange( - // new BigInteger(storageChangeProto.getSlot().toByteArray()), - // new BigInteger(storageChangeProto.getValueRead().toByteArray()), - // storageChangeProto.hasValueWritten() ? ( - // storageChangeProto.getValueWritten().getValue().size() == 0 ? - // BigInteger.ZERO : - // new BigInteger(storageChangeProto.getValueWritten().getValue().toByteArray()) - // ) : null - // ); - // } - // - // /** - // * Create a storage charge from a byte array. - // * - // * @param bytes the byte array - // * @return the new storage charge object - // * @throws InvalidProtocolBufferException when there is an issue with the protobuf - // */ - // public static StorageChange fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - // return fromProtobuf(com.hedera.hashgraph.sdk.proto.StorageChange.parseFrom(bytes)); - // } - // - // /** - // * Create the byte array. - // * - // * @return the byte array representation - // */ - // com.hedera.hashgraph.sdk.proto.StorageChange toProtobuf() { - // var builder = com.hedera.hashgraph.sdk.proto.StorageChange.newBuilder() - // .setSlot(ByteString.copyFrom(slot.toByteArray())) - // .setValueRead(ByteString.copyFrom(valueRead.toByteArray())); - // if (valueWritten != null) { - // if (valueWritten.equals(BigInteger.ZERO)) { - // builder.setValueWritten(BytesValue.newBuilder().setValue(ByteString.EMPTY).build()); - // } else { - // builder.setValueWritten(BytesValue.newBuilder().setValue(ByteString.copyFrom(valueWritten.toByteArray())).build()); - // } - // } - // return builder.build(); - // } - // - // /** - // * Create the byte array. - // * - // * @return the byte array representation - // */ - // public byte[] toBytes() { - // return toProtobuf().toByteArray(); - // } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/SubscriptionHandle.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/SubscriptionHandle.java deleted file mode 100644 index 9abc694ba4..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/SubscriptionHandle.java +++ /dev/null @@ -1,63 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import javax.annotation.Nullable; - -/** - * Subscribe to a topic ID's messages from a mirror node. You will receive - * all messages for the specified topic or within the defined start and end - * time. - * - * See Hedera Documentation - */ -public final class SubscriptionHandle { - @Nullable - private Runnable onUnsubscribe; - - /** - * Constructor. - */ - SubscriptionHandle() { - } - - /** - * Assign the callback method. - * - * @param onUnsubscribe the callback method - */ - void setOnUnsubscribe(Runnable onUnsubscribe) { - this.onUnsubscribe = onUnsubscribe; - } - - /** - * Call the callback. - */ - public void unsubscribe() { - var unsubscribe = this.onUnsubscribe; - - // Set onUnsubscribe back to null to make sure it is run just once. - this.onUnsubscribe = null; - - if (unsubscribe != null) { - unsubscribe.run(); - } - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/SystemUndeleteTransaction.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/SystemUndeleteTransaction.java deleted file mode 100644 index fb8f4478ec..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/SystemUndeleteTransaction.java +++ /dev/null @@ -1,193 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.FileServiceGrpc; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.SmartContractServiceGrpc; -import com.hedera.hashgraph.sdk.proto.SystemUndeleteTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import io.grpc.MethodDescriptor; -import java.util.LinkedHashMap; -import java.util.Objects; -import java.util.concurrent.CompletableFuture; -import javax.annotation.Nullable; - -/** - * Undelete a file or smart contract that was deleted by AdminDelete. - *

- * Can only be done with a Hedera admin. - */ -public final class SystemUndeleteTransaction extends Transaction { - @Nullable - private FileId fileId; - @Nullable - private ContractId contractId; - - /** - * Constructor. - */ - public SystemUndeleteTransaction() { - } - - /** - * Constructor. - * - * @param txs Compound list of transaction id's list of (AccountId, Transaction) - * records - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - SystemUndeleteTransaction(LinkedHashMap> txs) throws InvalidProtocolBufferException { - super(txs); - initFromTransactionBody(); - } - - /** - * Constructor. - * - * @param txBody protobuf TransactionBody - */ - SystemUndeleteTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { - super(txBody); - initFromTransactionBody(); - } - - /** - * Extract the file id. - * - * @return the file id - */ - @Nullable - public final FileId getFileId() { - return fileId; - } - - /** - * Sets the file ID to undelete. - *

- * Mutually exclusive with {@link #setContractId(ContractId)}. - * - * @param fileId The FileId to be set - * @return {@code this} - */ - public final SystemUndeleteTransaction setFileId(FileId fileId) { - Objects.requireNonNull(fileId); - requireNotFrozen(); - this.fileId = fileId; - return this; - } - - /** - * The contract ID instance to undelete, in the format used in transactions - * - * @return the contractId - */ - @Nullable - public ContractId getContractId() { - return contractId; - } - - /** - * Sets the contract ID to undelete. - *

- * Mutually exclusive with {@link #setFileId(FileId)}. - * - * @param contractId The ContractId to be set - * @return {@code this} - */ - public final SystemUndeleteTransaction setContractId(ContractId contractId) { - Objects.requireNonNull(contractId); - requireNotFrozen(); - this.contractId = contractId; - return this; - } - - /** - * Initialize from the transaction body. - */ - void initFromTransactionBody() { - var body = sourceTransactionBody.getSystemUndelete(); - if (body.hasFileID()) { - fileId = FileId.fromProtobuf(body.getFileID()); - } - if (body.hasContractID()) { - contractId = ContractId.fromProtobuf(body.getContractID()); - } - } - - /** - * Build the transaction body. - * - * @return {@link com.hedera.hashgraph.sdk.proto.SystemUndeleteTransactionBody} - */ - SystemUndeleteTransactionBody.Builder build() { - var builder = SystemUndeleteTransactionBody.newBuilder(); - if (fileId != null) { - builder.setFileID(fileId.toProtobuf()); - } - if (contractId != null) { - builder.setContractID(contractId.toProtobuf()); - } - - return builder; - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (fileId != null) { - fileId.validateChecksum(client); - } - - if (contractId != null) { - contractId.validateChecksum(client); - } - } - - @Override - CompletableFuture onExecuteAsync(Client client) { - int modesEnabled = (fileId != null ? 1 : 0) + (contractId != null ? 1 : 0); - if (modesEnabled != 1) { - throw new IllegalStateException("SystemDeleteTransaction must have exactly 1 of the following fields set: contractId, fileId"); - } - return super.onExecuteAsync(client); - } - - - @Override - MethodDescriptor getMethodDescriptor() { - if (fileId != null) { - return FileServiceGrpc.getSystemUndeleteMethod(); - } else { - return SmartContractServiceGrpc.getSystemUndeleteMethod(); - } - } - - @Override - void onFreeze(TransactionBody.Builder bodyBuilder) { - bodyBuilder.setSystemUndelete(build()); - } - - @Override - void onScheduled(SchedulableTransactionBody.Builder scheduled) { - scheduled.setSystemUndelete(build()); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/ThreadLocalSecureRandom.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/ThreadLocalSecureRandom.java deleted file mode 100644 index 0d272f3e17..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/ThreadLocalSecureRandom.java +++ /dev/null @@ -1,51 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import java.security.SecureRandom; - -/** - * Internal utility class. - */ -final class ThreadLocalSecureRandom { - @SuppressWarnings("AnonymousHasLambdaAlternative") - private static final ThreadLocal secureRandom = - new ThreadLocal() { - @Override - protected SecureRandom initialValue() { - return new SecureRandom(); - } - }; - - /** - * Constructor. - */ - private ThreadLocalSecureRandom() { - } - - /** - * Extract seme randomness. - * - * @return some randomness - */ - static SecureRandom current() { - return secureRandom.get(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenAirdropTransaction.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenAirdropTransaction.java deleted file mode 100644 index 0fe56f3238..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenAirdropTransaction.java +++ /dev/null @@ -1,130 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenAirdropTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenServiceGrpc; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import io.grpc.MethodDescriptor; -import java.util.LinkedHashMap; - -/** - * Token Airdrop - * An "airdrop" is a distribution of tokens from a funding account - * to one or more recipient accounts, ideally with no action required - * by the recipient account(s). - */ -public class TokenAirdropTransaction extends AbstractTokenTransferTransaction { - /** - * Constructor. - */ - public TokenAirdropTransaction() { - super(); - defaultMaxTransactionFee = new Hbar(1); - } - - /** - * Constructor. - * - * @param txs Compound list of transaction id's list of (AccountId, Transaction) - * records - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - TokenAirdropTransaction(LinkedHashMap> txs) throws InvalidProtocolBufferException { - super(txs); - initFromTransactionBody(); - } - - /** - * Constructor. - * - * @param txBody protobuf TransactionBody - */ - TokenAirdropTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { - super(txBody); - initFromTransactionBody(); - } - - /** - * Build the transaction body. - * - * @return {@link - * com.hedera.hashgraph.sdk.proto.TokenAirdropTransactionBody} - */ - TokenAirdropTransactionBody.Builder build() { - var transfers = sortTransfersAndBuild(); - var builder = TokenAirdropTransactionBody.newBuilder(); - - for (var transfer : transfers) { - builder.addTokenTransfers(transfer.toProtobuf()); - } - - return builder; - } - - @Override - MethodDescriptor getMethodDescriptor() { - return TokenServiceGrpc.getAirdropTokensMethod(); - } - - @Override - void onFreeze(TransactionBody.Builder bodyBuilder) { - bodyBuilder.setTokenAirdrop(build()); - } - - @Override - void onScheduled(SchedulableTransactionBody.Builder scheduled) { - scheduled.setTokenAirdrop(build()); - } - - /** - * Initialize from the transaction body. - */ - void initFromTransactionBody() { - var body = sourceTransactionBody.getTokenAirdrop(); - - for (var tokenTransferList : body.getTokenTransfersList()) { - var token = TokenId.fromProtobuf(tokenTransferList.getToken()); - - for (var transfer : tokenTransferList.getTransfersList()) { - tokenTransfers.add(new TokenTransfer( - token, - AccountId.fromProtobuf(transfer.getAccountID()), - transfer.getAmount(), - tokenTransferList.hasExpectedDecimals() ? tokenTransferList.getExpectedDecimals().getValue() : null, - transfer.getIsApproval() - )); - } - - for (var transfer : tokenTransferList.getNftTransfersList()) { - nftTransfers.add(new TokenNftTransfer( - token, - AccountId.fromProtobuf(transfer.getSenderAccountID()), - AccountId.fromProtobuf(transfer.getReceiverAccountID()), - transfer.getSerialNumber(), - transfer.getIsApproval() - )); - } - } - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenAllowance.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenAllowance.java deleted file mode 100644 index 4dd4661ddf..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenAllowance.java +++ /dev/null @@ -1,189 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.GrantedTokenAllowance; - -import javax.annotation.Nullable; -import java.util.Objects; - -/** - * An approved allowance of token transfers for a spender. - * - * See Hedera Documentation - */ -public class TokenAllowance { - /** - * The token that the allowance pertains to - */ - @Nullable - public final TokenId tokenId; - /** - * The account ID of the hbar owner (ie. the grantor of the allowance) - */ - @Nullable - public final AccountId ownerAccountId; - /** - * The account ID of the spender of the hbar allowance - */ - @Nullable - public final AccountId spenderAccountId; - /** - * The amount of the spender's token allowance - */ - public final long amount; - - /** - * Constructor. - * - * @param tokenId the token id - * @param ownerAccountId the grantor account id - * @param spenderAccountId the spender account id - * @param amount the token allowance - */ - TokenAllowance( - @Nullable TokenId tokenId, - @Nullable AccountId ownerAccountId, - @Nullable AccountId spenderAccountId, - long amount - ) { - this.tokenId = tokenId; - this.ownerAccountId = ownerAccountId; - this.spenderAccountId = spenderAccountId; - this.amount = amount; - } - - /** - * Create a token allowance from a protobuf. - * - * @param allowanceProto the protobuf - * @return the new token allowance - */ - static TokenAllowance fromProtobuf(com.hedera.hashgraph.sdk.proto.TokenAllowance allowanceProto) { - return new TokenAllowance( - allowanceProto.hasTokenId() ? TokenId.fromProtobuf(allowanceProto.getTokenId()) : null, - allowanceProto.hasOwner() ? AccountId.fromProtobuf(allowanceProto.getOwner()) : null, - allowanceProto.hasSpender() ? AccountId.fromProtobuf(allowanceProto.getSpender()) : null, - allowanceProto.getAmount() - ); - } - - /** - * Create a token allowance from a protobuf. - * - * @param allowanceProto the protobuf - * @return the new token allowance - */ - static TokenAllowance fromProtobuf(GrantedTokenAllowance allowanceProto) { - return new TokenAllowance( - allowanceProto.hasTokenId() ? TokenId.fromProtobuf(allowanceProto.getTokenId()) : null, - null, - allowanceProto.hasSpender() ? AccountId.fromProtobuf(allowanceProto.getSpender()) : null, - allowanceProto.getAmount() - ); - } - - /** - * Create a token allowance from a byte array. - * - * @param bytes the byte array - * @return the new token allowance - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - public static TokenAllowance fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return fromProtobuf(com.hedera.hashgraph.sdk.proto.TokenAllowance.parseFrom(Objects.requireNonNull(bytes))); - } - - /** - * Validate the configured client. - * - * @param client the configured client - * @throws BadEntityIdException if entity ID is formatted poorly - */ - void validateChecksums(Client client) throws BadEntityIdException { - if (tokenId != null) { - tokenId.validateChecksum(client); - } - if (ownerAccountId != null) { - ownerAccountId.validateChecksum(client); - } - if (spenderAccountId != null) { - spenderAccountId.validateChecksum(client); - } - } - - /** - * Create the protobuf. - * - * @return the protobuf representation - */ - com.hedera.hashgraph.sdk.proto.TokenAllowance toProtobuf() { - var builder = com.hedera.hashgraph.sdk.proto.TokenAllowance.newBuilder() - .setAmount(amount); - if (tokenId != null) { - builder.setTokenId(tokenId.toProtobuf()); - } - if (ownerAccountId != null) { - builder.setOwner(ownerAccountId.toProtobuf()); - } - if (spenderAccountId != null) { - builder.setSpender(spenderAccountId.toProtobuf()); - } - return builder.build(); - } - - /** - * Create the byte array. - * - * @return the protobuf representation - */ - GrantedTokenAllowance toGrantedProtobuf() { - var builder = GrantedTokenAllowance.newBuilder() - .setAmount(amount); - if (tokenId != null) { - builder.setTokenId(tokenId.toProtobuf()); - } - if (spenderAccountId != null) { - builder.setSpender(spenderAccountId.toProtobuf()); - } - return builder.build(); - } - - /** - * Create the byte array. - * - * @return the byte array representation - */ - public byte[] toBytes() { - return toProtobuf().toByteArray(); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("tokenId", tokenId) - .add("ownerAccountId", ownerAccountId) - .add("spenderAccountId", spenderAccountId) - .add("amount", amount) - .toString(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenAssociateTransaction.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenAssociateTransaction.java deleted file mode 100644 index a91b1b5463..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenAssociateTransaction.java +++ /dev/null @@ -1,180 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenAssociateTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenServiceGrpc; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Objects; - -/** - * The transaction that will associate accounts to a token id. - */ -public class TokenAssociateTransaction extends Transaction { - @Nullable - private AccountId accountId = null; - private List tokenIds = new ArrayList<>(); - - /** - * Constructor. - */ - public TokenAssociateTransaction() { - defaultMaxTransactionFee = new Hbar(5); - } - - /** - * Constructor. - * - * @param txs Compound list of transaction id's list of (AccountId, Transaction) - * records - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - TokenAssociateTransaction(LinkedHashMap> txs) throws InvalidProtocolBufferException { - super(txs); - initFromTransactionBody(); - } - - /** - * Constructor. - * - * @param txBody protobuf TransactionBody - */ - TokenAssociateTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { - super(txBody); - initFromTransactionBody(); - } - - /** - * Extract the account id. - * - * @return the account id - */ - @Nullable - public AccountId getAccountId() { - return accountId; - } - - /** - * Assign the account id. - * - * @param accountId the account id - * @return {@code this} - */ - public TokenAssociateTransaction setAccountId(AccountId accountId) { - Objects.requireNonNull(accountId); - requireNotFrozen(); - this.accountId = accountId; - return this; - } - - /** - * Extract the list of token id's. - * - * @return the list of token id's - */ - public List getTokenIds() { - return new ArrayList<>(tokenIds); - } - - /** - * Assign a new list of token id's. - * - * @param tokens the list of token id's - * @return {@code this} - */ - public TokenAssociateTransaction setTokenIds(List tokens) { - Objects.requireNonNull(tokens); - requireNotFrozen(); - this.tokenIds = new ArrayList<>(tokens); - return this; - } - - /** - * Build the transaction body. - * - * @return {@link com.hedera.hashgraph.sdk.proto.TokenAssociateTransactionBody} - */ - TokenAssociateTransactionBody.Builder build() { - var builder = TokenAssociateTransactionBody.newBuilder(); - if (accountId != null) { - builder.setAccount(accountId.toProtobuf()); - } - - for (var token : tokenIds) { - if (token != null) { - builder.addTokens(token.toProtobuf()); - } - } - - return builder; - } - - /** - * Initialize from the transaction body. - */ - void initFromTransactionBody() { - var body = sourceTransactionBody.getTokenAssociate(); - if (body.hasAccount()) { - accountId = AccountId.fromProtobuf(body.getAccount()); - } - - for (var token : body.getTokensList()) { - tokenIds.add(TokenId.fromProtobuf(token)); - } - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - Objects.requireNonNull(client); - if (accountId != null) { - accountId.validateChecksum(client); - } - - for (var token : tokenIds) { - if (token != null) { - token.validateChecksum(client); - } - } - } - - @Override - MethodDescriptor getMethodDescriptor() { - return TokenServiceGrpc.getAssociateTokensMethod(); - } - - @Override - void onFreeze(TransactionBody.Builder bodyBuilder) { - bodyBuilder.setTokenAssociate(build()); - } - - @Override - void onScheduled(SchedulableTransactionBody.Builder scheduled) { - scheduled.setTokenAssociate(build()); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenAssociation.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenAssociation.java deleted file mode 100644 index 0604dc3662..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenAssociation.java +++ /dev/null @@ -1,110 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.google.protobuf.InvalidProtocolBufferException; - -/** - * Associates the provided Hedera account with the provided Hedera token(s). - * Hedera accounts must be associated with a fungible or non-fungible token - * first before you can transfer tokens to that account. In the case of - * NON_FUNGIBLE Type, once an account is associated, it can hold any number - * of NFTs (serial numbers) of that token type. The Hedera account that is - * being associated with a token is required to sign the transaction. - * - * See Hedera Documentation - */ -public class TokenAssociation { - - /** - * The token involved in the association - */ - public final TokenId tokenId; - - /** - * The account involved in the association - */ - public final AccountId accountId; - - /** - * Constructor. - * - * @param tokenId the token id - * @param accountId the account id - */ - TokenAssociation(TokenId tokenId, AccountId accountId) { - this.tokenId = tokenId; - this.accountId = accountId; - } - - /** - * Create a token association from a protobuf. - * - * @param tokenAssociation the protobuf - * @return the new token association - */ - static TokenAssociation fromProtobuf(com.hedera.hashgraph.sdk.proto.TokenAssociation tokenAssociation) { - return new TokenAssociation( - tokenAssociation.hasTokenId() ? TokenId.fromProtobuf(tokenAssociation.getTokenId()) : new TokenId(0, 0, 0), - tokenAssociation.hasAccountId() ? AccountId.fromProtobuf(tokenAssociation.getAccountId()) : new AccountId(0, 0, 0) - ); - } - - /** - * Create a token association from a byte array. - * - * @param bytes the byte array - * @return the new token association - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - public static TokenAssociation fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return fromProtobuf(com.hedera.hashgraph.sdk.proto.TokenAssociation.parseFrom(bytes)); - } - - /** - * Create the protobuf. - * - * @return the protobuf representation - */ - com.hedera.hashgraph.sdk.proto.TokenAssociation toProtobuf() { - return com.hedera.hashgraph.sdk.proto.TokenAssociation.newBuilder() - .setTokenId(tokenId.toProtobuf()) - .setAccountId(accountId.toProtobuf()) - .build(); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("tokenId", tokenId) - .add("accountId", accountId) - .toString(); - } - - /** - * Create the byte array. - * - * @return the byte array representation - */ - public byte[] toBytes() { - return toProtobuf().toByteArray(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenCancelAirdropTransaction.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenCancelAirdropTransaction.java deleted file mode 100644 index 9e2e7c9db5..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenCancelAirdropTransaction.java +++ /dev/null @@ -1,103 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenCancelAirdropTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenServiceGrpc; -import com.hedera.hashgraph.sdk.proto.TransactionBody.Builder; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import io.grpc.MethodDescriptor; -import java.util.LinkedHashMap; - -public class TokenCancelAirdropTransaction extends PendingAirdropLogic { - - /** - * Constructor. - */ - public TokenCancelAirdropTransaction() { - defaultMaxTransactionFee = Hbar.from(1); - } - - /** - * Constructor. - * - * @param txs Compound list of transaction id's list of (AccountId, Transaction) records - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - TokenCancelAirdropTransaction( - LinkedHashMap> txs) - throws InvalidProtocolBufferException { - super(txs); - initFromTransactionBody(); - } - - /** - * Constructor. - * - * @param txBody protobuf TransactionBody - */ - TokenCancelAirdropTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { - super(txBody); - initFromTransactionBody(); - } - - - /** - * Build the transaction body. - * - * @return {@link com.hedera.hashgraph.sdk.proto.TokenCancelAirdropTransactionBody} - */ - TokenCancelAirdropTransactionBody.Builder build() { - var builder = TokenCancelAirdropTransactionBody.newBuilder(); - - for (var pendingAirdropId : pendingAirdropIds) { - builder.addPendingAirdrops(pendingAirdropId.toProtobuf()); - } - - return builder; - } - - /** - * Initialize from the transaction body. - */ - void initFromTransactionBody() { - var body = sourceTransactionBody.getTokenCancelAirdrop(); - for (var pendingAirdropId : body.getPendingAirdropsList()) { - this.pendingAirdropIds.add(PendingAirdropId.fromProtobuf(pendingAirdropId)); - } - } - - @Override - MethodDescriptor getMethodDescriptor() { - return TokenServiceGrpc.getCancelAirdropMethod(); - } - - @Override - void onFreeze(Builder bodyBuilder) { - bodyBuilder.setTokenCancelAirdrop(build()); - } - - @Override - void onScheduled(SchedulableTransactionBody.Builder scheduled) { - scheduled.setTokenCancelAirdrop(build()); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenClaimAirdropTransaction.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenClaimAirdropTransaction.java deleted file mode 100644 index cafcaa7b6c..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenClaimAirdropTransaction.java +++ /dev/null @@ -1,102 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenClaimAirdropTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenServiceGrpc; -import com.hedera.hashgraph.sdk.proto.TransactionBody.Builder; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import io.grpc.MethodDescriptor; -import java.util.LinkedHashMap; - -public class TokenClaimAirdropTransaction extends PendingAirdropLogic { - - /** - * Constructor. - */ - public TokenClaimAirdropTransaction() { - defaultMaxTransactionFee = Hbar.from(1); - } - - /** - * Constructor. - * - * @param txs Compound list of transaction id's list of (AccountId, Transaction) records - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - TokenClaimAirdropTransaction( - LinkedHashMap> txs) - throws InvalidProtocolBufferException { - super(txs); - initFromTransactionBody(); - } - - /** - * Constructor. - * - * @param txBody protobuf TransactionBody - */ - TokenClaimAirdropTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { - super(txBody); - initFromTransactionBody(); - } - - /** - * Build the transaction body. - * - * @return {@link com.hedera.hashgraph.sdk.proto.TokenClaimAirdropTransactionBody} - */ - TokenClaimAirdropTransactionBody.Builder build() { - var builder = TokenClaimAirdropTransactionBody.newBuilder(); - - for (var pendingAirdropId : pendingAirdropIds) { - builder.addPendingAirdrops(pendingAirdropId.toProtobuf()); - } - - return builder; - } - - /** - * Initialize from the transaction body. - */ - void initFromTransactionBody() { - var body = sourceTransactionBody.getTokenClaimAirdrop(); - for (var pendingAirdropId : body.getPendingAirdropsList()) { - this.pendingAirdropIds.add(PendingAirdropId.fromProtobuf(pendingAirdropId)); - } - } - - @Override - MethodDescriptor getMethodDescriptor() { - return TokenServiceGrpc.getClaimAirdropMethod(); - } - - @Override - void onFreeze(Builder bodyBuilder) { - bodyBuilder.setTokenClaimAirdrop(build()); - } - - @Override - void onScheduled(SchedulableTransactionBody.Builder scheduled) { - scheduled.setTokenClaimAirdrop(build()); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenDeleteTransaction.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenDeleteTransaction.java deleted file mode 100644 index c909005f9b..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenDeleteTransaction.java +++ /dev/null @@ -1,146 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenDeleteTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenServiceGrpc; -import com.hedera.hashgraph.sdk.proto.Transaction; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; -import java.util.LinkedHashMap; -import java.util.Objects; - -/** - * Deleting a token marks a token as deleted, though it will remain in the - * ledger. The operation must be signed by the specified Admin Key of the - * Token. If the Admin Key is not set, Transaction will result in - * TOKEN_IS_IMMUTABlE. Once deleted update, mint, burn, wipe, freeze, - * unfreeze, grant kyc, revoke kyc and token transfer transactions will - * resolve to TOKEN_WAS_DELETED. - * - * See Hedera Documentation - */ -public class TokenDeleteTransaction extends com.hedera.hashgraph.sdk.Transaction { - @Nullable - private TokenId tokenId = null; - - /** - * Constructor. - */ - public TokenDeleteTransaction() { - } - - /** - * Constructor. - * - * @param txs Compound list of transaction id's list of (AccountId, Transaction) - * records - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - TokenDeleteTransaction(LinkedHashMap> txs) throws InvalidProtocolBufferException { - super(txs); - initFromTransactionBody(); - } - - /** - * Constructor. - * - * @param txBody protobuf TransactionBody - */ - TokenDeleteTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { - super(txBody); - initFromTransactionBody(); - } - - /** - * Extract the token id. - * - * @return the token id - */ - @Nullable - public TokenId getTokenId() { - return tokenId; - } - - /** - * Assign the token id. - * - * @param tokenId the token id - * @return {@code this} - */ - public TokenDeleteTransaction setTokenId(TokenId tokenId) { - Objects.requireNonNull(tokenId); - requireNotFrozen(); - this.tokenId = tokenId; - return this; - } - - /** - * Initialize from the transaction body. - */ - void initFromTransactionBody() { - var body = sourceTransactionBody.getTokenDeletion(); - if (body.hasToken()) { - tokenId = TokenId.fromProtobuf(body.getToken()); - } - } - - /** - * Build the transaction body. - * - * @return {@link - * com.hedera.hashgraph.sdk.proto.TokenDeleteTransactionBody} - */ - TokenDeleteTransactionBody.Builder build() { - var builder = TokenDeleteTransactionBody.newBuilder(); - if (tokenId != null) { - builder.setToken(tokenId.toProtobuf()); - } - - return builder; - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (tokenId != null) { - tokenId.validateChecksum(client); - } - } - - @Override - MethodDescriptor getMethodDescriptor() { - return TokenServiceGrpc.getDeleteTokenMethod(); - } - - @Override - void onFreeze(TransactionBody.Builder bodyBuilder) { - bodyBuilder.setTokenDeletion(build()); - } - - @Override - void onScheduled(SchedulableTransactionBody.Builder scheduled) { - scheduled.setTokenDeletion(build()); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenDissociateTransaction.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenDissociateTransaction.java deleted file mode 100644 index bc9e66e9f4..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenDissociateTransaction.java +++ /dev/null @@ -1,185 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenDissociateTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenServiceGrpc; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Objects; - -/** - * Disassociates the provided Hedera account from the provided Hedera tokens. - * This transaction must be signed by the provided account's key. Once the - * association is removed, no token related operation can be performed to that - * account. AccountBalanceQuery and AccountInfoQuery will not return anything - * related to the token that was disassociated. - * - * See Hedera Documentation - */ -public class TokenDissociateTransaction extends com.hedera.hashgraph.sdk.Transaction { - @Nullable - private AccountId accountId = null; - private List tokenIds = new ArrayList<>(); - - /** - * Constructor. - */ - public TokenDissociateTransaction() { - defaultMaxTransactionFee = new Hbar(5); - } - - /** - * Constructor. - * - * @param txs Compound list of transaction id's list of (AccountId, Transaction) - * records - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - TokenDissociateTransaction(LinkedHashMap> txs) throws InvalidProtocolBufferException { - super(txs); - initFromTransactionBody(); - } - - /** - * Constructor. - * - * @param txBody protobuf TransactionBody - */ - TokenDissociateTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { - super(txBody); - initFromTransactionBody(); - } - - /** - * Extract the account id. - * - * @return the account id - */ - @Nullable - public AccountId getAccountId() { - return accountId; - } - - /** - * Assign the account id. - * - * @param accountId the account id - * @return {@code this} - */ - public TokenDissociateTransaction setAccountId(AccountId accountId) { - Objects.requireNonNull(accountId); - requireNotFrozen(); - this.accountId = accountId; - return this; - } - - /** - * Extract the list of token id's. - * - * @return the list of token id's - */ - public List getTokenIds() { - return new ArrayList<>(tokenIds); - } - - /** - * Assign the list of token id's. - * - * @param tokens the list of token id's. - * @return {@code this} - */ - public TokenDissociateTransaction setTokenIds(List tokens) { - requireNotFrozen(); - this.tokenIds = new ArrayList<>(tokens); - return this; - } - - /** - * Initialize from the transaction body. - */ - void initFromTransactionBody() { - var body = sourceTransactionBody.getTokenDissociate(); - if (body.hasAccount()) { - accountId = AccountId.fromProtobuf(body.getAccount()); - } - - for (var token : body.getTokensList()) { - tokenIds.add(TokenId.fromProtobuf(token)); - } - } - - /** - * Build the transaction body. - * - * @return {@link - * com.hedera.hashgraph.sdk.proto.TokenDissociateTransactionBody} - */ - TokenDissociateTransactionBody.Builder build() { - var builder = TokenDissociateTransactionBody.newBuilder(); - if (accountId != null) { - builder.setAccount(accountId.toProtobuf()); - } - - for (var token : tokenIds) { - if (token != null) { - builder.addTokens(token.toProtobuf()); - } - } - - return builder; - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (accountId != null) { - accountId.validateChecksum(client); - } - - for (var token : tokenIds) { - if (token != null) { - token.validateChecksum(client); - } - } - } - - @Override - MethodDescriptor getMethodDescriptor() { - return TokenServiceGrpc.getDissociateTokensMethod(); - } - - @Override - void onFreeze(TransactionBody.Builder bodyBuilder) { - bodyBuilder.setTokenDissociate(build()); - } - - @Override - void onScheduled(SchedulableTransactionBody.Builder scheduled) { - scheduled.setTokenDissociate(build()); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenFeeScheduleUpdateTransaction.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenFeeScheduleUpdateTransaction.java deleted file mode 100644 index 30699d7cc6..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenFeeScheduleUpdateTransaction.java +++ /dev/null @@ -1,183 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenFeeScheduleUpdateTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenServiceGrpc; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Objects; - -/** - * Update the custom fees for a given token. If the token does not have a - * fee schedule, the network response returned will be - * CUSTOM_SCHEDULE_ALREADY_HAS_NO_FEES. You will need to sign the transaction - * with the fee schedule key to update the fee schedule for the token. If you - * do not have a fee schedule key set for the token, you will not be able to - * update the fee schedule. - * - * See Hedera Documentation - */ -public class TokenFeeScheduleUpdateTransaction extends Transaction { - @Nullable - private TokenId tokenId = null; - private List customFees = new ArrayList<>(); - - /** - * Constructor. - */ - public TokenFeeScheduleUpdateTransaction() { - } - - /** - * Constructor. - * - * @param txs Compound list of transaction id's list of (AccountId, Transaction) - * records - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - TokenFeeScheduleUpdateTransaction(LinkedHashMap> txs) throws InvalidProtocolBufferException { - super(txs); - initFromTransactionBody(); - } - - /** - * Constructor. - * - * @param txBody protobuf TransactionBody - */ - TokenFeeScheduleUpdateTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { - super(txBody); - initFromTransactionBody(); - } - - /** - * Extract the token id. - * - * @return the token id - */ - @Nullable - public TokenId getTokenId() { - return tokenId; - } - - /** - * Assign the token id. - * - * @param tokenId the token id - * @return {@code this} - */ - public TokenFeeScheduleUpdateTransaction setTokenId(TokenId tokenId) { - Objects.requireNonNull(tokenId); - requireNotFrozen(); - this.tokenId = tokenId; - return this; - } - - /** - * Extract the list of custom fees. - * - * @return the list of custom fees - */ - public List getCustomFees() { - return CustomFee.deepCloneList(customFees); - } - - /** - * Assign the list of custom fees. - * - * @param customFees the list of custom fees - * @return {@code this} - */ - public TokenFeeScheduleUpdateTransaction setCustomFees(List customFees) { - Objects.requireNonNull(customFees); - requireNotFrozen(); - this.customFees = CustomFee.deepCloneList(customFees); - return this; - } - - /** - * Initialize from the transaction body. - */ - void initFromTransactionBody() { - var body = sourceTransactionBody.getTokenFeeScheduleUpdate(); - if (body.hasTokenId()) { - tokenId = TokenId.fromProtobuf(body.getTokenId()); - } - - for (var fee : body.getCustomFeesList()) { - customFees.add(CustomFee.fromProtobuf(fee)); - } - } - - /** - * Build the transaction body. - * - * @return {@link - * com.hedera.hashgraph.sdk.proto.TokenFeeScheduleUpdateTransactionBody} - */ - TokenFeeScheduleUpdateTransactionBody.Builder build() { - var builder = TokenFeeScheduleUpdateTransactionBody.newBuilder(); - if (tokenId != null) { - builder.setTokenId(tokenId.toProtobuf()); - } - - builder.clearCustomFees(); - for (var fee : customFees) { - builder.addCustomFees(fee.toProtobuf()); - } - - return builder; - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (tokenId != null) { - tokenId.validateChecksum(client); - } - - for (CustomFee fee : customFees) { - fee.validateChecksums(client); - } - } - - @Override - MethodDescriptor getMethodDescriptor() { - return TokenServiceGrpc.getUpdateTokenFeeScheduleMethod(); - } - - @Override - void onFreeze(TransactionBody.Builder bodyBuilder) { - bodyBuilder.setTokenFeeScheduleUpdate(build()); - } - - @Override - void onScheduled(SchedulableTransactionBody.Builder scheduled) { - throw new UnsupportedOperationException("Cannot schedule TokenFeeScheduleUpdateTransaction"); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenFreezeTransaction.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenFreezeTransaction.java deleted file mode 100644 index e3ba72042a..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenFreezeTransaction.java +++ /dev/null @@ -1,179 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenFreezeAccountTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenServiceGrpc; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; -import java.util.LinkedHashMap; -import java.util.Objects; - -/** - * Freezes transfers of the specified token for the account. - * - * The transaction must be signed by the token's Freeze Key. - * - * See Hedera Documentation - */ -public class TokenFreezeTransaction extends com.hedera.hashgraph.sdk.Transaction { - @Nullable - private TokenId tokenId = null; - @Nullable - private AccountId accountId = null; - - /** - * Constructor. - */ - public TokenFreezeTransaction() { - } - - /** - * Constructor. - * - * @param txs Compound list of transaction id's list of (AccountId, Transaction) - * records - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - TokenFreezeTransaction(LinkedHashMap> txs) throws InvalidProtocolBufferException { - super(txs); - initFromTransactionBody(); - } - - /** - * Constructor. - * - * @param txBody protobuf TransactionBody - */ - TokenFreezeTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { - super(txBody); - initFromTransactionBody(); - } - - /** - * Extract the token id. - * - * @return the token id - */ - @Nullable - public TokenId getTokenId() { - return tokenId; - } - - /** - * Assign the token id. - * - * @param tokenId the token id - * @return {@code this} - */ - public TokenFreezeTransaction setTokenId(TokenId tokenId) { - Objects.requireNonNull(tokenId); - requireNotFrozen(); - this.tokenId = tokenId; - return this; - } - - /** - * Extract the account id. - * - * @return the account id - */ - @Nullable - public AccountId getAccountId() { - return accountId; - } - - /** - * Assign the account id. - * - * @param accountId the account id - * @return {@code this} - */ - public TokenFreezeTransaction setAccountId(AccountId accountId) { - Objects.requireNonNull(accountId); - requireNotFrozen(); - this.accountId = accountId; - return this; - } - - /** - * Initialize from the transaction body. - */ - void initFromTransactionBody() { - var body = sourceTransactionBody.getTokenFreeze(); - if (body.hasToken()) { - tokenId = TokenId.fromProtobuf(body.getToken()); - } - - if (body.hasAccount()) { - accountId = AccountId.fromProtobuf(body.getAccount()); - } - } - - /** - * Build the transaction body. - * - * @return {@link - * com.hedera.hashgraph.sdk.proto.TokenFreezeAccountTransactionBody} - */ - TokenFreezeAccountTransactionBody.Builder build() { - var builder = TokenFreezeAccountTransactionBody.newBuilder(); - if (tokenId != null) { - builder.setToken(tokenId.toProtobuf()); - } - - if (accountId != null) { - builder.setAccount(accountId.toProtobuf()); - } - - return builder; - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (tokenId != null) { - tokenId.validateChecksum(client); - } - - if (accountId != null) { - accountId.validateChecksum(client); - } - } - - @Override - MethodDescriptor getMethodDescriptor() { - return TokenServiceGrpc.getFreezeTokenAccountMethod(); - } - - @Override - void onFreeze(TransactionBody.Builder bodyBuilder) { - bodyBuilder.setTokenFreeze(build()); - } - - @Override - void onScheduled(SchedulableTransactionBody.Builder scheduled) { - scheduled.setTokenFreeze(build()); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenGrantKycTransaction.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenGrantKycTransaction.java deleted file mode 100644 index 0fc96e5e4b..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenGrantKycTransaction.java +++ /dev/null @@ -1,179 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenGrantKycTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenServiceGrpc; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; -import java.util.LinkedHashMap; -import java.util.Objects; - -/** - * Grants KYC to the Hedera accounts for the given Hedera token. - * - * This transaction must be signed by the token's KYC Key. - * - * See Hedera Documentation - */ -public class TokenGrantKycTransaction extends com.hedera.hashgraph.sdk.Transaction { - @Nullable - private TokenId tokenId = null; - @Nullable - private AccountId accountId = null; - - /** - * Configure. - */ - public TokenGrantKycTransaction() { - } - - /** - * Constructor. - * - * @param txs Compound list of transaction id's list of (AccountId, Transaction) - * records - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - TokenGrantKycTransaction(LinkedHashMap> txs) throws InvalidProtocolBufferException { - super(txs); - initFromTransactionBody(); - } - - /** - * Constructor. - * - * @param txBody protobuf TransactionBody - */ - TokenGrantKycTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { - super(txBody); - initFromTransactionBody(); - } - - /** - * Extract the token id. - * - * @return the token id - */ - @Nullable - public TokenId getTokenId() { - return tokenId; - } - - /** - * Assign the token id. - * - * @param tokenId the token id - * @return {@code this} - */ - public TokenGrantKycTransaction setTokenId(TokenId tokenId) { - Objects.requireNonNull(tokenId); - requireNotFrozen(); - this.tokenId = tokenId; - return this; - } - - /** - * Extract the account id. - * - * @return the account id - */ - @Nullable - public AccountId getAccountId() { - return accountId; - } - - /** - * Assign the account id. - * - * @param accountId the account id - * @return {@code this} - */ - public TokenGrantKycTransaction setAccountId(AccountId accountId) { - Objects.requireNonNull(accountId); - requireNotFrozen(); - this.accountId = accountId; - return this; - } - - /** - * Initialize from the transaction body. - */ - void initFromTransactionBody() { - var body = sourceTransactionBody.getTokenGrantKyc(); - if (body.hasToken()) { - tokenId = TokenId.fromProtobuf(body.getToken()); - } - - if (body.hasAccount()) { - accountId = AccountId.fromProtobuf(body.getAccount()); - } - } - - /** - * Build the transaction body. - * - * @return {@link - * com.hedera.hashgraph.sdk.proto.TokenGrantKycTransactionBody} - */ - TokenGrantKycTransactionBody.Builder build() { - var builder = TokenGrantKycTransactionBody.newBuilder(); - if (tokenId != null) { - builder.setToken(tokenId.toProtobuf()); - } - - if (accountId != null) { - builder.setAccount(accountId.toProtobuf()); - } - - return builder; - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (tokenId != null) { - tokenId.validateChecksum(client); - } - - if (accountId != null) { - accountId.validateChecksum(client); - } - } - - @Override - MethodDescriptor getMethodDescriptor() { - return TokenServiceGrpc.getFreezeTokenAccountMethod(); - } - - @Override - void onFreeze(TransactionBody.Builder bodyBuilder) { - bodyBuilder.setTokenGrantKyc(build()); - } - - @Override - void onScheduled(SchedulableTransactionBody.Builder scheduled) { - scheduled.setTokenGrantKyc(build()); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenInfo.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenInfo.java deleted file mode 100644 index cde44756a9..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenInfo.java +++ /dev/null @@ -1,494 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.TokenFreezeStatus; -import com.hedera.hashgraph.sdk.proto.TokenGetInfoResponse; -import com.hedera.hashgraph.sdk.proto.TokenKycStatus; -import com.hedera.hashgraph.sdk.proto.TokenPauseStatus; -import java.time.Duration; -import java.time.Instant; - -import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.List; - -/** - * Gets information about a fungible or non-fungible token instance. - * - * See Hedera Documentation - */ -public class TokenInfo { - /** - * The ID of the token for which information is requested. - */ - public final TokenId tokenId; - - /** - * Name of token. - */ - public final String name; - - /** - * Symbol of token. - */ - public final String symbol; - - /** - * The amount of decimal places that this token supports. - */ - public final int decimals; - - /** - * Total Supply of token. - */ - public final long totalSupply; - - /** - * The ID of the account which is set as Treasury - */ - public final AccountId treasuryAccountId; - - /** - * The key which can perform update/delete operations on the token. If empty, the token can be perceived as immutable (not being able to be updated/deleted) - */ - @Nullable - public final Key adminKey; - - /** - * The key which can grant or revoke KYC of an account for the token's transactions. If empty, KYC is not required, and KYC grant or revoke operations are not possible. - */ - @Nullable - public final Key kycKey; - - /** - * The key which can freeze or unfreeze an account for token transactions. If empty, freezing is not possible - */ - @Nullable - public final Key freezeKey; - - /** - * The key which can wipe token balance of an account. If empty, wipe is not possible - */ - @Nullable - public final Key wipeKey; - - /** - * The key which can change the supply of a token. The key is used to sign Token Mint/Burn operations - */ - @Nullable - public final Key supplyKey; - - /** - * The key which can change the custom fees of the token; if not set, the fees are immutable - */ - @Nullable - public final Key feeScheduleKey; - - /** - * The default Freeze status (not applicable, frozen or unfrozen) of Hedera accounts relative to this token. FreezeNotApplicable is returned if Token Freeze Key is empty. Frozen is returned if Token Freeze Key is set and defaultFreeze is set to true. Unfrozen is returned if Token Freeze Key is set and defaultFreeze is set to false - */ - @Nullable - public final Boolean defaultFreezeStatus; - - /** - * The default KYC status (KycNotApplicable or Revoked) of Hedera accounts relative to this token. KycNotApplicable is returned if KYC key is not set, otherwise Revoked - */ - @Nullable - public final Boolean defaultKycStatus; - - /** - * Specifies whether the token was deleted or not - */ - public final boolean isDeleted; - - /** - * An account which will be automatically charged to renew the token's expiration, at autoRenewPeriod interval - */ - @Nullable - public final AccountId autoRenewAccount; - - /** - * The interval at which the auto-renew account will be charged to extend the token's expiry - */ - @Nullable - public final Duration autoRenewPeriod; - - /** - * The epoch second at which the token will expire - */ - @Nullable - public final Instant expirationTime; - - /** - * The memo associated with the token - */ - public final String tokenMemo; - - /** - * The custom fees to be assessed during a CryptoTransfer that transfers units of this token - */ - public final List customFees; - - /** - * The token type - */ - public final TokenType tokenType; - - /** - * The token supply type - */ - public final TokenSupplyType supplyType; - - /** - * For tokens of type FUNGIBLE_COMMON - The Maximum number of fungible tokens that can be in - * circulation. For tokens of type NON_FUNGIBLE_UNIQUE - the maximum number of NFTs (serial - * numbers) that can be in circulation - */ - public final long maxSupply; - - /** - * The Key which can pause and unpause the Token. - */ - @Nullable - public final Key pauseKey; - - /** - * Specifies whether the token is paused or not. Null if pauseKey is not set. - */ - @Nullable - public final Boolean pauseStatus; - - /** - * Represents the metadata of the token definition. - */ - public byte[] metadata = {}; - - /** - * The key which can change the metadata of a token - * (token definition and individual NFTs). - */ - @Nullable - public final Key metadataKey; - - /** - * The ledger ID the response was returned from; please see HIP-198 for the network-specific IDs. - */ - public final LedgerId ledgerId; - - TokenInfo( - TokenId tokenId, - String name, - String symbol, - int decimals, - long totalSupply, - AccountId treasuryAccountId, - @Nullable Key adminKey, - @Nullable Key kycKey, - @Nullable Key freezeKey, - @Nullable Key wipeKey, - @Nullable Key supplyKey, - @Nullable Key feeScheduleKey, - @Nullable Boolean defaultFreezeStatus, - @Nullable Boolean defaultKycStatus, - boolean isDeleted, - @Nullable AccountId autoRenewAccount, - @Nullable Duration autoRenewPeriod, - @Nullable Instant expirationTime, - String tokenMemo, - List customFees, - TokenType tokenType, - TokenSupplyType supplyType, - long maxSupply, - @Nullable Key pauseKey, - @Nullable Boolean pauseStatus, - byte[] metadata, - @Nullable Key metadataKey, - LedgerId ledgerId - ) { - this.tokenId = tokenId; - this.name = name; - this.symbol = symbol; - this.decimals = decimals; - this.totalSupply = totalSupply; - this.treasuryAccountId = treasuryAccountId; - this.adminKey = adminKey; - this.kycKey = kycKey; - this.freezeKey = freezeKey; - this.wipeKey = wipeKey; - this.supplyKey = supplyKey; - this.feeScheduleKey = feeScheduleKey; - this.defaultFreezeStatus = defaultFreezeStatus; - this.defaultKycStatus = defaultKycStatus; - this.isDeleted = isDeleted; - this.autoRenewAccount = autoRenewAccount; - this.autoRenewPeriod = autoRenewPeriod; - this.expirationTime = expirationTime; - this.tokenMemo = tokenMemo; - this.customFees = customFees; - this.tokenType = tokenType; - this.supplyType = supplyType; - this.maxSupply = maxSupply; - this.pauseKey = pauseKey; - this.pauseStatus = pauseStatus; - this.metadata = metadata; - this.metadataKey = metadataKey; - this.ledgerId = ledgerId; - } - - /** - * Are we frozen? - * - * @param freezeStatus the freeze status - * @return true / false / null - */ - @Nullable - static Boolean freezeStatusFromProtobuf(TokenFreezeStatus freezeStatus) { - return freezeStatus == TokenFreezeStatus.FreezeNotApplicable ? null : freezeStatus == TokenFreezeStatus.Frozen; - } - - /** - * Is kyc required? - * - * @param kycStatus the kyc status - * @return true / false / null - */ - @Nullable - static Boolean kycStatusFromProtobuf(TokenKycStatus kycStatus) { - return kycStatus == TokenKycStatus.KycNotApplicable ? null : kycStatus == TokenKycStatus.Granted; - } - - /** - * Are we paused? - * - * @param pauseStatus the paused status - * @return true / false / null - */ - @Nullable - static Boolean pauseStatusFromProtobuf(TokenPauseStatus pauseStatus) { - return pauseStatus == TokenPauseStatus.PauseNotApplicable ? null : pauseStatus == TokenPauseStatus.Paused; - } - - /** - * Create a token info object from a protobuf. - * - * @param response the protobuf - * @return new token info object - */ - static TokenInfo fromProtobuf(TokenGetInfoResponse response) { - var info = response.getTokenInfo(); - - return new TokenInfo( - TokenId.fromProtobuf(info.getTokenId()), - info.getName(), - info.getSymbol(), - info.getDecimals(), - info.getTotalSupply(), - AccountId.fromProtobuf(info.getTreasury()), - info.hasAdminKey() ? Key.fromProtobufKey(info.getAdminKey()) : null, - info.hasKycKey() ? Key.fromProtobufKey(info.getKycKey()) : null, - info.hasFreezeKey() ? Key.fromProtobufKey(info.getFreezeKey()) : null, - info.hasWipeKey() ? Key.fromProtobufKey(info.getWipeKey()) : null, - info.hasSupplyKey() ? Key.fromProtobufKey(info.getSupplyKey()) : null, - info.hasFeeScheduleKey() ? Key.fromProtobufKey(info.getFeeScheduleKey()) : null, - freezeStatusFromProtobuf(info.getDefaultFreezeStatus()), - kycStatusFromProtobuf(info.getDefaultKycStatus()), - info.getDeleted(), - info.hasAutoRenewAccount() ? AccountId.fromProtobuf(info.getAutoRenewAccount()) : null, - info.hasAutoRenewPeriod() ? DurationConverter.fromProtobuf(info.getAutoRenewPeriod()) : null, - info.hasExpiry() ? InstantConverter.fromProtobuf(info.getExpiry()) : null, - info.getMemo(), - customFeesFromProto(info), - TokenType.valueOf(info.getTokenType()), - TokenSupplyType.valueOf(info.getSupplyType()), - info.getMaxSupply(), - info.hasPauseKey() ? Key.fromProtobufKey(info.getPauseKey()) : null, - pauseStatusFromProtobuf(info.getPauseStatus()), - info.getMetadata().toByteArray(), - info.hasMetadataKey() ? Key.fromProtobufKey(info.getMetadataKey()) : null, - LedgerId.fromByteString(info.getLedgerId()) - ); - } - - /** - * Create a token info object from a byte array. - * - * @param bytes the byte array - * @return the new token info object - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - public static TokenInfo fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return fromProtobuf(TokenGetInfoResponse.parseFrom(bytes).toBuilder().build()); - } - - /** - * Create custom fee list from protobuf. - * - * @param info the protobuf - * @return the list of custom fee's - */ - private static List customFeesFromProto(com.hedera.hashgraph.sdk.proto.TokenInfo info) { - var returnCustomFees = new ArrayList(info.getCustomFeesCount()); - for (var feeProto : info.getCustomFeesList()) { - returnCustomFees.add(CustomFee.fromProtobuf(feeProto)); - } - return returnCustomFees; - } - - /** - * Create a token freeze status protobuf. - * - * @param freezeStatus the freeze status - * @return the protobuf - */ - static TokenFreezeStatus freezeStatusToProtobuf(@Nullable Boolean freezeStatus) { - return freezeStatus == null ? TokenFreezeStatus.FreezeNotApplicable : freezeStatus ? TokenFreezeStatus.Frozen : TokenFreezeStatus.Unfrozen; - } - - /** - * Create a kyc status protobuf. - * - * @param kycStatus the kyc status - * @return the protobuf - */ - static TokenKycStatus kycStatusToProtobuf(@Nullable Boolean kycStatus) { - return kycStatus == null ? TokenKycStatus.KycNotApplicable : kycStatus ? TokenKycStatus.Granted : TokenKycStatus.Revoked; - } - - /** - * Create a pause status protobuf. - * - * @param pauseStatus the pause status - * @return the protobuf - */ - static TokenPauseStatus pauseStatusToProtobuf(@Nullable Boolean pauseStatus) { - return pauseStatus == null ? TokenPauseStatus.PauseNotApplicable : pauseStatus ? TokenPauseStatus.Paused : TokenPauseStatus.Unpaused; - } - - /** - * Create the protobuf. - * - * @return the protobuf representation - */ - TokenGetInfoResponse toProtobuf() { - var tokenInfoBuilder = com.hedera.hashgraph.sdk.proto.TokenInfo.newBuilder() - .setTokenId(tokenId.toProtobuf()) - .setName(name) - .setSymbol(symbol) - .setDecimals(decimals) - .setTotalSupply(totalSupply) - .setTreasury(treasuryAccountId.toProtobuf()) - .setDefaultFreezeStatus(freezeStatusToProtobuf(defaultFreezeStatus)) - .setDefaultKycStatus(kycStatusToProtobuf(defaultKycStatus)) - .setDeleted(isDeleted) - .setMemo(tokenMemo) - .setTokenType(tokenType.code) - .setSupplyType(supplyType.code) - .setMaxSupply(maxSupply) - .setPauseStatus(pauseStatusToProtobuf(pauseStatus)) - .setLedgerId(ledgerId.toByteString()); - if (adminKey != null) { - tokenInfoBuilder.setAdminKey(adminKey.toProtobufKey()); - } - if (kycKey != null) { - tokenInfoBuilder.setKycKey(kycKey.toProtobufKey()); - } - if (freezeKey != null) { - tokenInfoBuilder.setFreezeKey(freezeKey.toProtobufKey()); - } - if (wipeKey != null) { - tokenInfoBuilder.setWipeKey(wipeKey.toProtobufKey()); - } - if (supplyKey != null) { - tokenInfoBuilder.setSupplyKey(supplyKey.toProtobufKey()); - } - if (feeScheduleKey != null) { - tokenInfoBuilder.setFeeScheduleKey(feeScheduleKey.toProtobufKey()); - } - if (pauseKey != null) { - tokenInfoBuilder.setPauseKey(pauseKey.toProtobufKey()); - } - if (metadata != null) { - tokenInfoBuilder.setMetadata(ByteString.copyFrom(metadata)); - } - if (metadataKey != null) { - tokenInfoBuilder.setMetadataKey(metadataKey.toProtobufKey()); - } - if (autoRenewAccount != null) { - tokenInfoBuilder.setAutoRenewAccount(autoRenewAccount.toProtobuf()); - } - if (autoRenewPeriod != null) { - tokenInfoBuilder.setAutoRenewPeriod(DurationConverter.toProtobuf(autoRenewPeriod)); - } - if (expirationTime != null) { - tokenInfoBuilder.setExpiry(InstantConverter.toProtobuf(expirationTime)); - } - for (var fee : customFees) { - tokenInfoBuilder.addCustomFees(fee.toProtobuf()); - } - return TokenGetInfoResponse.newBuilder().setTokenInfo(tokenInfoBuilder).build(); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("tokenId", tokenId) - .add("name", name) - .add("symbol", symbol) - .add("decimals", decimals) - .add("totalSupply", totalSupply) - .add("treasuryAccountId", treasuryAccountId) - .add("adminKey", adminKey) - .add("kycKey", kycKey) - .add("freezeKey", freezeKey) - .add("wipeKey", wipeKey) - .add("supplyKey", supplyKey) - .add("feeScheduleKey", feeScheduleKey) - .add("defaultFreezeStatus", defaultFreezeStatus) - .add("defaultKycStatus", defaultKycStatus) - .add("isDeleted", isDeleted) - .add("autoRenewAccount", autoRenewAccount) - .add("autoRenewPeriod", autoRenewPeriod) - .add("expirationTime", expirationTime) - .add("tokenMemo", tokenMemo) - .add("customFees", customFees) - .add("tokenType", tokenType) - .add("supplyType", supplyType) - .add("maxSupply", maxSupply) - .add("pauseKey", pauseKey) - .add("pauseStatus", pauseStatus) - .add("metadata", metadata) - .add("metadataKey", metadataKey) - .add("ledgerId", ledgerId) - .toString(); - } - - /** - * Create the byte array. - * - * @return the byte array representation - */ - public byte[] toBytes() { - return toProtobuf().toByteArray(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenInfoQuery.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenInfoQuery.java deleted file mode 100644 index fd0fe60aeb..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenInfoQuery.java +++ /dev/null @@ -1,112 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.Query; -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import com.hedera.hashgraph.sdk.proto.Response; -import com.hedera.hashgraph.sdk.proto.ResponseHeader; -import com.hedera.hashgraph.sdk.proto.TokenGetInfoQuery; -import com.hedera.hashgraph.sdk.proto.TokenServiceGrpc; -import io.grpc.MethodDescriptor; -import java.util.Objects; -import java.util.concurrent.CompletableFuture; -import javax.annotation.Nullable; - -/** - * Initializes the TokenInfoQuery object. - */ -public class TokenInfoQuery extends com.hedera.hashgraph.sdk.Query { - @Nullable - TokenId tokenId = null; - - /** - * Constructor. - */ - public TokenInfoQuery() { - } - - /** - * Extract the token id. - * - * @return the token id - */ - @Nullable - public TokenId getTokenId() { - return tokenId; - } - - /** - * Sets the Token ID for which information is requested. - * - * @param tokenId The TokenId to be set - * @return {@code this} - */ - public TokenInfoQuery setTokenId(TokenId tokenId) { - Objects.requireNonNull(tokenId); - this.tokenId = tokenId; - return this; - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (tokenId != null) { - tokenId.validateChecksum(client); - } - } - - @Override - void onMakeRequest(com.hedera.hashgraph.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { - var builder = TokenGetInfoQuery.newBuilder(); - if (tokenId != null) { - builder.setToken(tokenId.toProtobuf()); - } - - queryBuilder.setTokenGetInfo(builder.setHeader(header)); - } - - @Override - ResponseHeader mapResponseHeader(Response response) { - return response.getTokenGetInfo().getHeader(); - } - - @Override - QueryHeader mapRequestHeader(com.hedera.hashgraph.sdk.proto.Query request) { - return request.getTokenGetInfo().getHeader(); - } - - @Override - TokenInfo mapResponse(Response response, AccountId nodeId, Query request) { - return TokenInfo.fromProtobuf(response.getTokenGetInfo()); - } - - @Override - MethodDescriptor getMethodDescriptor() { - return TokenServiceGrpc.getGetTokenInfoMethod(); - } - - @Override - public CompletableFuture getCostAsync(Client client) { - // deleted accounts return a COST_ANSWER of zero which triggers `INSUFFICIENT_TX_FEE` - // if you set that as the query payment; 25 tinybar seems to be enough to get - // `Token_DELETED` back instead. - return super.getCostAsync(client).thenApply((cost) -> Hbar.fromTinybars(Math.max(cost.toTinybars(), 25))); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenKeyValidation.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenKeyValidation.java deleted file mode 100644 index b19a61786a..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenKeyValidation.java +++ /dev/null @@ -1,67 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -/** - * Types of validation strategies for token keys. - * - */ -public enum TokenKeyValidation { - /** - * Currently the default behaviour. It will perform all token key validations. - */ - FULL_VALIDATION(com.hedera.hashgraph.sdk.proto.TokenKeyValidation.FULL_VALIDATION), - - /** - * Perform no validations at all for all passed token keys. - */ - NO_VALIDATION(com.hedera.hashgraph.sdk.proto.TokenKeyValidation.NO_VALIDATION); - - final com.hedera.hashgraph.sdk.proto.TokenKeyValidation code; - - /** - * Constructor. - * - * @param code the token key validation - */ - TokenKeyValidation(com.hedera.hashgraph.sdk.proto.TokenKeyValidation code) { - this.code = code; - } - - static TokenKeyValidation valueOf(com.hedera.hashgraph.sdk.proto.TokenKeyValidation code) { - return switch (code) { - case FULL_VALIDATION -> FULL_VALIDATION; - case NO_VALIDATION -> NO_VALIDATION; - default -> throw new IllegalStateException("(BUG) unhandled TokenKeyValidation"); - }; - } - - @Override - public String toString() { - return switch (this) { - case FULL_VALIDATION -> "FULL_VALIDATION"; - case NO_VALIDATION -> "NO_VALIDATION"; - }; - } - - public com.hedera.hashgraph.sdk.proto.TokenKeyValidation toProtobuf() { - return this.code; - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenNftAllowance.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenNftAllowance.java deleted file mode 100644 index 4e1760437a..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenNftAllowance.java +++ /dev/null @@ -1,235 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.google.protobuf.BoolValue; -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.GrantedNftAllowance; -import com.hedera.hashgraph.sdk.proto.NftAllowance; -import com.hedera.hashgraph.sdk.proto.NftRemoveAllowance; - -import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Objects; - -/** - * Class to encapsulate the nft methods for token allowance's. - */ -public class TokenNftAllowance { - - /** - * The NFT token type that the allowance pertains to - */ - @Nullable - public final TokenId tokenId; - - /** - * The account ID of the token owner (ie. the grantor of the allowance) - */ - @Nullable - public final AccountId ownerAccountId; - - /** - * The account ID of the token allowance spender - */ - @Nullable - public final AccountId spenderAccountId; - - /** - * The account ID of the spender who is granted approvedForAll allowance and granting - * approval on an NFT serial to another spender. - */ - @Nullable AccountId delegatingSpender; - - /** - * The list of serial numbers that the spender is permitted to transfer. - */ - public final List serialNumbers; - - /** - * If true, the spender has access to all of the owner's NFT units of type tokenId (currently - * owned and any in the future). - */ - @Nullable - public final Boolean allSerials; - - /** - * Constructor. - * - * @param tokenId the token id - * @param ownerAccountId the grantor's account id - * @param spenderAccountId the spender's account id - * @param delegatingSpender the delegating spender's account id - * @param serialNumbers the list of serial numbers - * @param allSerials grant for all serial's - */ - TokenNftAllowance( - @Nullable TokenId tokenId, - @Nullable AccountId ownerAccountId, - @Nullable AccountId spenderAccountId, - @Nullable AccountId delegatingSpender, - Collection serialNumbers, - @Nullable Boolean allSerials - ) { - this.tokenId = tokenId; - this.ownerAccountId = ownerAccountId; - this.spenderAccountId = spenderAccountId; - this.delegatingSpender = delegatingSpender; - this.serialNumbers = new ArrayList<>(serialNumbers); - this.allSerials = allSerials; - } - - /** - * Create a copy of a nft token allowance object. - * - * @param allowance the nft token allowance to copj - * @return a new copy - */ - static TokenNftAllowance copyFrom(TokenNftAllowance allowance) { - return new TokenNftAllowance( - allowance.tokenId, - allowance.ownerAccountId, - allowance.spenderAccountId, - allowance.delegatingSpender, - allowance.serialNumbers, - allowance.allSerials - ); - } - - /** - * Create a nft token allowance from a protobuf. - * - * @param allowanceProto the protobuf - * @return the nft token allowance - */ - static TokenNftAllowance fromProtobuf(NftAllowance allowanceProto) { - return new TokenNftAllowance( - allowanceProto.hasTokenId() ? TokenId.fromProtobuf(allowanceProto.getTokenId()) : null, - allowanceProto.hasOwner() ? AccountId.fromProtobuf(allowanceProto.getOwner()) : null, - allowanceProto.hasSpender() ? AccountId.fromProtobuf(allowanceProto.getSpender()) : null, - allowanceProto.hasDelegatingSpender() ? AccountId.fromProtobuf(allowanceProto.getDelegatingSpender()) : null, - allowanceProto.getSerialNumbersList(), - allowanceProto.hasApprovedForAll() ? allowanceProto.getApprovedForAll().getValue() : null - ); - } - - /** - * Create a nft token allowance from a byte array. - * - * @param bytes the byte array - * @return the nft token allowance - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - public static TokenNftAllowance fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return fromProtobuf(NftAllowance.parseFrom(Objects.requireNonNull(bytes))); - } - - /** - * Validate the configured client. - * - * @param client the configured client - * @throws BadEntityIdException if entity ID is formatted poorly - */ - void validateChecksums(Client client) throws BadEntityIdException { - if (tokenId != null) { - tokenId.validateChecksum(client); - } - if (ownerAccountId != null) { - ownerAccountId.validateChecksum(client); - } - if (spenderAccountId != null) { - spenderAccountId.validateChecksum(client); - } - if (delegatingSpender != null) { - delegatingSpender.validateChecksum(client); - } - } - - /** - * Create the protobuf. - * - * @return the protobuf representation - */ - NftAllowance toProtobuf() { - var builder = NftAllowance.newBuilder(); - if (tokenId != null) { - builder.setTokenId(tokenId.toProtobuf()); - } - if (ownerAccountId != null) { - builder.setOwner(ownerAccountId.toProtobuf()); - } - if (spenderAccountId != null) { - builder.setSpender(spenderAccountId.toProtobuf()); - } - if (delegatingSpender != null) { - builder.setDelegatingSpender(delegatingSpender.toProtobuf()); - } - builder.addAllSerialNumbers(serialNumbers); - if (allSerials != null) { - builder.setApprovedForAll(BoolValue.newBuilder().setValue(allSerials).build()); - } - return builder.build(); - } - - /** - * Create the protobuf. - * - * @return the remove protobuf - */ - NftRemoveAllowance toRemoveProtobuf() { - var builder = NftRemoveAllowance.newBuilder(); - if (tokenId != null) { - builder.setTokenId(tokenId.toProtobuf()); - } - if (ownerAccountId != null) { - builder.setOwner(ownerAccountId.toProtobuf()); - } - builder.addAllSerialNumbers(serialNumbers); - return builder.build(); - } - - /** - * Create the byte array. - * - * @return the byte array representation - */ - public byte[] toBytes() { - return toProtobuf().toByteArray(); - } - - @Override - public String toString() { - var stringHelper = MoreObjects.toStringHelper(this) - .add("tokenId", tokenId) - .add("ownerAccountId", ownerAccountId) - .add("spenderAccountId", spenderAccountId) - .add("delegatingSpender", delegatingSpender); - if (allSerials != null) { - stringHelper.add("allSerials", allSerials); - } else { - stringHelper.add("serials", serialNumbers); - } - return stringHelper.toString(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenNftInfo.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenNftInfo.java deleted file mode 100644 index 160890e777..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenNftInfo.java +++ /dev/null @@ -1,159 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; -import java.time.Instant; - -import javax.annotation.Nullable; -import java.util.Objects; - -/** - * - * - * See Hedera Documentation - */ -public class TokenNftInfo { - /** - * The ID of the NFT - */ - public final NftId nftId; - - /** - * The current owner of the NFT - */ - public final AccountId accountId; - - /** - * The effective consensus timestamp at which the NFT was minted - */ - public final Instant creationTime; - - /** - * Represents the unique metadata of the NFT - */ - public final byte[] metadata; - - /** - * The ledger ID the response was returned from; please see HIP-198 for the network-specific IDs. - */ - public final LedgerId ledgerId; - - /** - * If an allowance is granted for the NFT, its corresponding spender account - */ - @Nullable - public final AccountId spenderId; - - /** - * Constructor. - * - * @param nftId the id of the nft - * @param accountId the current owner of the nft - * @param creationTime the effective consensus time - * @param metadata the unique metadata - * @param ledgerId the ledger id of the response - * @param spenderId the spender of the allowance (null if not an allowance) - */ - TokenNftInfo( - NftId nftId, - AccountId accountId, - Instant creationTime, - byte[] metadata, - LedgerId ledgerId, - @Nullable AccountId spenderId - ) { - this.nftId = nftId; - this.accountId = accountId; - this.creationTime = Objects.requireNonNull(creationTime); - this.metadata = metadata; - this.ledgerId = ledgerId; - this.spenderId = spenderId; - } - - /** - * Create token nft info from a protobuf. - * - * @param info the protobuf - * @return the new token nft info - */ - static TokenNftInfo fromProtobuf(com.hedera.hashgraph.sdk.proto.TokenNftInfo info) { - return new TokenNftInfo( - NftId.fromProtobuf(info.getNftID()), - AccountId.fromProtobuf(info.getAccountID()), - InstantConverter.fromProtobuf(info.getCreationTime()), - info.getMetadata().toByteArray(), - LedgerId.fromByteString(info.getLedgerId()), - info.hasSpenderId() ? AccountId.fromProtobuf(info.getSpenderId()) : null - ); - } - - /** - * Create token nft info from byte array. - * - * @param bytes the byte array - * @return the new token nft info - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - public static TokenNftInfo fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return fromProtobuf(com.hedera.hashgraph.sdk.proto.TokenNftInfo.parseFrom(bytes)); - } - - /** - * Create the protobuf. - * - * @return the protobuf representation - */ - com.hedera.hashgraph.sdk.proto.TokenNftInfo toProtobuf() { - var builder = com.hedera.hashgraph.sdk.proto.TokenNftInfo.newBuilder() - .setNftID(nftId.toProtobuf()) - .setAccountID(accountId.toProtobuf()) - .setCreationTime(InstantConverter.toProtobuf(creationTime)) - .setMetadata(ByteString.copyFrom(metadata)) - .setLedgerId(ledgerId.toByteString()); - if (spenderId != null) { - builder.setSpenderId(spenderId.toProtobuf()); - } - return builder.build(); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("nftId", nftId) - .add("accountId", accountId) - .add("creationTime", creationTime) - .add("metadata", metadata) - .add("ledgerId", ledgerId) - .add("spenderId", spenderId) - .toString(); - } - - /** - * Create the byte array. - * - * @return the byte array representation - */ - public byte[] toBytes() { - return toProtobuf().toByteArray(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenNftTransfer.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenNftTransfer.java deleted file mode 100644 index 7852df662d..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenNftTransfer.java +++ /dev/null @@ -1,185 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.NftTransfer; -import com.hedera.hashgraph.sdk.proto.TokenID; -import com.hedera.hashgraph.sdk.proto.TokenTransferList; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -/** - * Internal utility class. - */ -public class TokenNftTransfer implements Comparable { - /** - * The ID of the token - */ - public final TokenId tokenId; - /** - * The accountID of the sender - */ - public final AccountId sender; - /** - * The accountID of the receiver - */ - public final AccountId receiver; - /** - * The serial number of the NFT - */ - public final long serial; - /** - * If true then the transfer is expected to be an approved allowance and the sender is expected to be the owner. The - * default is false. - */ - public boolean isApproved; - - /** - * Constructor. - * - * @param tokenId the token id - * @param sender the sender account id - * @param receiver the receiver account id - * @param serial the serial number - * @param isApproved is it approved - */ - TokenNftTransfer(TokenId tokenId, AccountId sender, AccountId receiver, long serial, boolean isApproved) { - this.tokenId = tokenId; - this.sender = sender; - this.receiver = receiver; - this.serial = serial; - this.isApproved = isApproved; - } - - /** - * Create a list of token nft transfer records from a protobuf. - * - * @param tokenTransferList the protobuf - * @return the new list - */ - static ArrayList fromProtobuf(List tokenTransferList) { - var transfers = new ArrayList(); - - for (var tokenTransfer : tokenTransferList) { - var tokenId = TokenId.fromProtobuf(tokenTransfer.getToken()); - - for (var transfer : tokenTransfer.getNftTransfersList()) { - transfers.add(new TokenNftTransfer( - tokenId, - AccountId.fromProtobuf(transfer.getSenderAccountID()), - AccountId.fromProtobuf(transfer.getReceiverAccountID()), - transfer.getSerialNumber(), - transfer.getIsApproval() - )); - } - } - - return transfers; - } - - /** - * Convert a byte array to a token NFT transfer object. - * - * @param bytes the byte array - * @return the converted token nft transfer object - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - @Deprecated - public static TokenNftTransfer fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return fromProtobuf( - List.of( - TokenTransferList.newBuilder() - .setToken(TokenID.newBuilder().build()) - .addNftTransfers(NftTransfer.parseFrom(bytes)) - .build() - ) - ).get(0); - } - - /** - * Create the protobuf. - * - * @return the protobuf representation - */ - NftTransfer toProtobuf() { - return NftTransfer.newBuilder() - .setSenderAccountID(sender.toProtobuf()) - .setReceiverAccountID(receiver.toProtobuf()) - .setSerialNumber(serial) - .setIsApproval(isApproved) - .build(); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("tokenId", tokenId) - .add("sender", sender) - .add("receiver", receiver) - .add("serial", serial) - .add("isApproved", isApproved) - .toString(); - } - - /** - * Convert the token NFT transfer object to a byte array. - * - * @return the converted token NFT transfer object - */ - @Deprecated - public byte[] toBytes() { - return toProtobuf().toByteArray(); - } - - @Override - public int compareTo(TokenNftTransfer o) { - int senderComparison = sender.compareTo(o.sender); - if (senderComparison != 0) { - return senderComparison; - } - int receiverComparison = receiver.compareTo(o.receiver); - if (receiverComparison != 0) { - return receiverComparison; - } - return Long.compare(serial, o.serial); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - TokenNftTransfer that = (TokenNftTransfer) o; - return serial == that.serial && isApproved == that.isApproved && tokenId.equals(that.tokenId) && sender.equals( - that.sender) && receiver.equals(that.receiver); - } - - @Override - public int hashCode() { - return Objects.hash(tokenId, sender, receiver, serial, isApproved); - } - -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenPauseTransaction.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenPauseTransaction.java deleted file mode 100644 index c751855e03..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenPauseTransaction.java +++ /dev/null @@ -1,142 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenPauseTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenServiceGrpc; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; -import java.util.LinkedHashMap; -import java.util.Objects; - -/** - * A token pause transaction prevents the token from being involved in any - * kind of operation. The token's pause key is required to sign the - * transaction. This is a key that is specified during the creation of a - * token. If a token has no pause key, you will not be able to pause the - * token. If the pause key was not set during the creation of a token, you - * will not be able to update the token to add this key. - * - * See Hedera Documentation - */ -public class TokenPauseTransaction extends Transaction{ - @Nullable - private TokenId tokenId = null; - - /** - * Constructor. - */ - public TokenPauseTransaction() { - } - - /** - * Constructor. - * - * @param txs Compound list of transaction id's list of (AccountId, Transaction) - * records - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - TokenPauseTransaction(LinkedHashMap> txs) throws InvalidProtocolBufferException { - super(txs); - initFromTransactionBody(); - } - - /** - * Constructor. - * - * @param txBody protobuf TransactionBody - */ - TokenPauseTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { - super(txBody); - initFromTransactionBody(); - } - - /** - * Extract the token id. - * - * @return the token id - */ - @Nullable - public TokenId getTokenId() { - return tokenId; - } - - /** - * Assign the token id. - * - * @param tokenId the token id - * @return {@code this} - */ - public TokenPauseTransaction setTokenId(TokenId tokenId) { - Objects.requireNonNull(tokenId); - requireNotFrozen(); - this.tokenId = tokenId; - return this; - } - - void initFromTransactionBody() { - var body = sourceTransactionBody.getTokenPause(); - if (body.hasToken()) { - tokenId = TokenId.fromProtobuf(body.getToken()); - } - } - - /** - * Build the transaction body. - * - * @return {@link - * com.hedera.hashgraph.sdk.proto.TokenPauseTransactionBody} - */ - TokenPauseTransactionBody.Builder build() { - var builder = TokenPauseTransactionBody.newBuilder(); - if (tokenId != null) { - builder.setToken(tokenId.toProtobuf()); - } - - return builder; - } - - @Override - MethodDescriptor getMethodDescriptor() { - return TokenServiceGrpc.getPauseTokenMethod(); - } - - @Override - void onFreeze(TransactionBody.Builder bodyBuilder) { - bodyBuilder.setTokenPause(build()); - } - - @Override - void onScheduled(SchedulableTransactionBody.Builder scheduled) { - scheduled.setTokenPause(build()); - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (tokenId != null) { - tokenId.validateChecksum(client); - } - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenRejectTransaction.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenRejectTransaction.java deleted file mode 100644 index 6e45d54f71..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenRejectTransaction.java +++ /dev/null @@ -1,242 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenReference; -import com.hedera.hashgraph.sdk.proto.TokenRejectTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenServiceGrpc; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import io.grpc.MethodDescriptor; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Objects; -import javax.annotation.Nullable; - -/** - * Reject undesired token(s).
- * Transfer one or more token balances held by the requesting account to the treasury for each - * token type.
- */ -public class TokenRejectTransaction extends Transaction { - - /** - * An account holding the tokens to be rejected. - */ - @Nullable - private AccountId ownerId = null; - - /** - * A list of one or more token rejections (a fungible/common token type). - */ - private List tokenIds = new ArrayList<>(); - - /** - * A list of one or more token rejections (a single specific serialized non-fungible/unique token). - */ - private List nftIds = new ArrayList<>(); - - /** - * Constructor - */ - public TokenRejectTransaction() {} - - /** - * Constructor. - * - * @param txs Compound list of transaction id's list of (AccountId, Transaction) records - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - TokenRejectTransaction( - LinkedHashMap> txs) - throws InvalidProtocolBufferException { - super(txs); - initFromTransactionBody(); - } - - /** - * Constructor. - * - * @param txBody protobuf TransactionBody - */ - TokenRejectTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { - super(txBody); - initFromTransactionBody(); - } - - /** - * Extract the Account ID of the Owner. - * @return the Account ID of the Owner. - */ - @Nullable - public AccountId getOwnerId() { - return ownerId; - } - - /** - * Assign the Account ID of the Owner. - * @param ownerId the Account ID of the Owner. - * @return {@code this} - */ - public TokenRejectTransaction setOwnerId(AccountId ownerId) { - Objects.requireNonNull(ownerId); - requireNotFrozen(); - this.ownerId = ownerId; - return this; - } - - /** - * Extract the list of tokenIds. - * @return the list of tokenIds. - */ - public List getTokenIds() { - return tokenIds; - } - - /** - * Assign the list of tokenIds. - * @param tokenIds the list of tokenIds. - * @return {@code this} - */ - public TokenRejectTransaction setTokenIds(List tokenIds) { - requireNotFrozen(); - Objects.requireNonNull(tokenIds); - this.tokenIds = new ArrayList<>(tokenIds); - return this; - } - - /** - * Add a token to the list of tokens. - * @param tokenId token to add. - * @return {@code this} - */ - public TokenRejectTransaction addTokenId(TokenId tokenId) { - requireNotFrozen(); - tokenIds.add(tokenId); - return this; - } - - /** - * Extract the list of nftIds. - * @return the list of nftIds. - */ - public List getNftIds() { - return nftIds; - } - - /** - * Assign the list of nftIds. - * @param nftIds the list of nftIds. - * @return {@code this} - */ - public TokenRejectTransaction setNftIds(List nftIds) { - requireNotFrozen(); - Objects.requireNonNull(nftIds); - this.nftIds = new ArrayList<>(nftIds); - return this; - } - - /** - * Add a nft to the list of nfts. - * @param nftId nft to add. - * @return {@code this} - */ - public TokenRejectTransaction addNftId(NftId nftId) { - requireNotFrozen(); - nftIds.add(nftId); - return this; - } - - /** - * Build the transaction body. - * - * @return {@link com.hedera.hashgraph.sdk.proto.TokenRejectTransactionBody} - */ - TokenRejectTransactionBody.Builder build() { - var builder = TokenRejectTransactionBody.newBuilder(); - - if (ownerId != null) { - builder.setOwner(ownerId.toProtobuf()); - } - - for (TokenId tokenId : tokenIds) { - builder.addRejections(TokenReference.newBuilder().setFungibleToken(tokenId.toProtobuf()).build()); - } - - for (NftId nftId : nftIds) { - builder.addRejections(TokenReference.newBuilder().setNft(nftId.toProtobuf()).build()); - } - - return builder; - } - - /** - * Initialize from the transaction body. - */ - void initFromTransactionBody() { - var body = sourceTransactionBody.getTokenReject(); - if (body.hasOwner()) { - ownerId = AccountId.fromProtobuf(body.getOwner()); - } - - for (TokenReference tokenReference : body.getRejectionsList()) { - if (tokenReference.hasFungibleToken()) { - tokenIds.add(TokenId.fromProtobuf(tokenReference.getFungibleToken())); - } else if (tokenReference.hasNft()) { - nftIds.add(NftId.fromProtobuf(tokenReference.getNft())); - } - } - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (ownerId != null) { - ownerId.validateChecksum(client); - } - - for (var token : tokenIds) { - if (token != null) { - token.validateChecksum(client); - } - } - - for (var nftId : nftIds) { - nftId.tokenId.validateChecksum(client); - } - } - - @Override - MethodDescriptor getMethodDescriptor() { - return TokenServiceGrpc.getRejectTokenMethod(); - } - - @Override - void onFreeze(TransactionBody.Builder bodyBuilder) { - bodyBuilder.setTokenReject(build()); - } - - @Override - void onScheduled(SchedulableTransactionBody.Builder scheduled) { - scheduled.setTokenReject(build()); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenRelationship.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenRelationship.java deleted file mode 100644 index 6d7bce9051..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenRelationship.java +++ /dev/null @@ -1,203 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.TokenFreezeStatus; -import com.hedera.hashgraph.sdk.proto.TokenKycStatus; - -import javax.annotation.Nullable; - -/** - * Token's information related to the given Account. - * - * See Hedera Documentation - */ -public class TokenRelationship { - /** - * A unique token id - */ - public final TokenId tokenId; - /** - * The Symbol of the token - */ - public final String symbol; - /** - * For token of type FUNGIBLE_COMMON - the balance that the Account holds - * in the smallest denomination. - * - * For token of type NON_FUNGIBLE_UNIQUE - the number of NFTs held by the - * account - */ - public final long balance; - /** - * The KYC status of the account (KycNotApplicable, Granted or Revoked). - * - * If the token does not have KYC key, KycNotApplicable is returned - */ - @Nullable - public final Boolean kycStatus; - /** - * The Freeze status of the account (FreezeNotApplicable, Frozen or - * Unfrozen). If the token does not have Freeze key, - * FreezeNotApplicable is returned - */ - @Nullable - public final Boolean freezeStatus; - /** - * The amount of decimal places that this token supports. - */ - public final int decimals; - /** - * Specifies if the relationship is created implicitly. - * False : explicitly associated, - * True : implicitly associated. - */ - public final boolean automaticAssociation; - - TokenRelationship( - TokenId tokenId, - String symbol, - long balance, - @Nullable Boolean kycStatus, - @Nullable Boolean freezeStatus, - int decimals, - boolean automaticAssociation - ) { - this.tokenId = tokenId; - this.symbol = symbol; - this.balance = balance; - this.kycStatus = kycStatus; - this.freezeStatus = freezeStatus; - this.decimals = decimals; - this.automaticAssociation = automaticAssociation; - } - - /** - * Retrieve freeze status from a protobuf. - * - * @param freezeStatus the protobuf - * @return the freeze status - */ - @Nullable - static Boolean freezeStatusFromProtobuf(TokenFreezeStatus freezeStatus) { - return freezeStatus == TokenFreezeStatus.FreezeNotApplicable ? null : freezeStatus == TokenFreezeStatus.Frozen; - } - - /** - * Retrieve the kyc status from a protobuf. - * - * @param kycStatus the protobuf - * @return the kyc status - */ - @Nullable - static Boolean kycStatusFromProtobuf(TokenKycStatus kycStatus) { - return kycStatus == TokenKycStatus.KycNotApplicable ? null : kycStatus == TokenKycStatus.Granted; - } - - /** - * Create a token relationship object from a protobuf. - * - * @param tokenRelationship the protobuf - * @return the new token relationship - */ - static TokenRelationship fromProtobuf(com.hedera.hashgraph.sdk.proto.TokenRelationship tokenRelationship) { - return new TokenRelationship( - TokenId.fromProtobuf(tokenRelationship.getTokenId()), - tokenRelationship.getSymbol(), - tokenRelationship.getBalance(), - kycStatusFromProtobuf(tokenRelationship.getKycStatus()), - freezeStatusFromProtobuf(tokenRelationship.getFreezeStatus()), - tokenRelationship.getDecimals(), - tokenRelationship.getAutomaticAssociation() - ); - } - - /** - * Create a token relationship from a byte array. - * - * @param bytes the byte array - * @return the new token relationship - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - public static TokenRelationship fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return fromProtobuf(com.hedera.hashgraph.sdk.proto.TokenRelationship.parseFrom(bytes).toBuilder().build()); - } - - /** - * Retrieve the freeze status from a protobuf. - * - * @param freezeStatus the protobuf - * @return the freeze status - */ - static TokenFreezeStatus freezeStatusToProtobuf(@Nullable Boolean freezeStatus) { - return freezeStatus == null ? TokenFreezeStatus.FreezeNotApplicable : freezeStatus ? TokenFreezeStatus.Frozen : TokenFreezeStatus.Unfrozen; - } - - /** - * Retrieve the kyc status from a protobuf. - * - * @param kycStatus the protobuf - * @return the kyc status - */ - static TokenKycStatus kycStatusToProtobuf(@Nullable Boolean kycStatus) { - return kycStatus == null ? TokenKycStatus.KycNotApplicable : kycStatus ? TokenKycStatus.Granted : TokenKycStatus.Revoked; - } - - /** - * Create the protobuf. - * - * @return the protobuf representation - */ - com.hedera.hashgraph.sdk.proto.TokenRelationship toProtobuf() { - return com.hedera.hashgraph.sdk.proto.TokenRelationship.newBuilder() - .setTokenId(tokenId.toProtobuf()) - .setSymbol(symbol) - .setBalance(balance) - .setKycStatus(kycStatusToProtobuf(kycStatus)) - .setFreezeStatus(freezeStatusToProtobuf(freezeStatus)) - .setDecimals(decimals) - .setAutomaticAssociation(automaticAssociation) - .build(); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("tokenId", tokenId) - .add("symbol", symbol) - .add("balance", balance) - .add("kycStatus", kycStatus) - .add("freezeStatus", freezeStatus) - .add("decimals", decimals) - .add("automaticAssociation", automaticAssociation) - .toString(); - } - - /** - * Create the byte array. - * - * @return the byte array representation - */ - public byte[] toBytes() { - return toProtobuf().toByteArray(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenRevokeKycTransaction.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenRevokeKycTransaction.java deleted file mode 100644 index 15f60d6eb1..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenRevokeKycTransaction.java +++ /dev/null @@ -1,183 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenRevokeKycTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenServiceGrpc; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; -import java.util.LinkedHashMap; -import java.util.Objects; - -/** - * Revokes the KYC flag to the Hedera account for the given Hedera token. - * This transaction must be signed by the token's KYC Key. If this key is - * not set, you can submit a TokenUpdateTransaction to provide the token - * with this key. - * - * See Hedera Documentation - */ -public class TokenRevokeKycTransaction extends com.hedera.hashgraph.sdk.Transaction { - /** - * The token ID that is associated with the account to remove the KYC flag for - */ - @Nullable - private TokenId tokenId = null; - /** - * The account ID that is associated with the account to remove the KYC flag - */ - @Nullable - private AccountId accountId = null; - - /** - * Constructor. - */ - public TokenRevokeKycTransaction() { - } - - /** - * Constructor. - * - * @param txs Compound list of transaction id's list of (AccountId, Transaction) - * records - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - TokenRevokeKycTransaction(LinkedHashMap> txs) throws InvalidProtocolBufferException { - super(txs); - initFromTransactionBody(); - } - - /** - * Constructor. - * - * @param txBody protobuf TransactionBody - */ - TokenRevokeKycTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { - super(txBody); - initFromTransactionBody(); - } - - /** - * Extract the token id. - * - * @return the token id - */ - @Nullable - public TokenId getTokenId() { - return tokenId; - } - - /** - * Assign the token id. - * - * @param tokenId the token id - * @return {@code this} - */ - public TokenRevokeKycTransaction setTokenId(TokenId tokenId) { - Objects.requireNonNull(tokenId); - requireNotFrozen(); - this.tokenId = tokenId; - return this; - } - - /** - * Extract the account id. - * - * @return the account id - */ - @Nullable - public AccountId getAccountId() { - return accountId; - } - - /** - * Assign the account id. - * - * @param accountId the account id - * @return {@code this} - */ - public TokenRevokeKycTransaction setAccountId(AccountId accountId) { - Objects.requireNonNull(accountId); - requireNotFrozen(); - this.accountId = accountId; - return this; - } - - void initFromTransactionBody() { - var body = sourceTransactionBody.getTokenRevokeKyc(); - if (body.hasToken()) { - tokenId = TokenId.fromProtobuf(body.getToken()); - } - - if (body.hasAccount()) { - accountId = AccountId.fromProtobuf(body.getAccount()); - } - } - - /** - * Build the transaction body. - * - * @return {@link - * com.hedera.hashgraph.sdk.proto.TokenRevokeKycTransactionBody} - */ - TokenRevokeKycTransactionBody.Builder build() { - var builder = TokenRevokeKycTransactionBody.newBuilder(); - if (tokenId != null) { - builder.setToken(tokenId.toProtobuf()); - } - - if (accountId != null) { - builder.setAccount(accountId.toProtobuf()); - } - - return builder; - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (tokenId != null) { - tokenId.validateChecksum(client); - } - - if (accountId != null) { - accountId.validateChecksum(client); - } - } - - @Override - MethodDescriptor getMethodDescriptor() { - return TokenServiceGrpc.getFreezeTokenAccountMethod(); - } - - @Override - void onFreeze(TransactionBody.Builder bodyBuilder) { - bodyBuilder.setTokenRevokeKyc(build()); - } - - @Override - void onScheduled(SchedulableTransactionBody.Builder scheduled) { - scheduled.setTokenRevokeKyc(build()); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenSupplyType.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenSupplyType.java deleted file mode 100644 index dc973b2a3b..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenSupplyType.java +++ /dev/null @@ -1,75 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -/** - * Possible Token Supply Types (IWA Compatibility). - *

- * Indicates how many tokens can have during its lifetime. - *

- * See Hedera Documentation - */ -public enum TokenSupplyType { - /** - * Indicates that tokens of that type have an upper bound of Long.MAX_VALUE. - */ - INFINITE(com.hedera.hashgraph.sdk.proto.TokenSupplyType.INFINITE), - /** - * Indicates that tokens of that type have an upper bound of maxSupply, provided on token creation. - */ - FINITE(com.hedera.hashgraph.sdk.proto.TokenSupplyType.FINITE); - - final com.hedera.hashgraph.sdk.proto.TokenSupplyType code; - - /** - * Constructor. - * - * @param code the token supply type - */ - TokenSupplyType(com.hedera.hashgraph.sdk.proto.TokenSupplyType code) { - this.code = code; - } - - /** - * What type are we. - * - * @param code the token supply type in question - * @return the token supply type - */ - static TokenSupplyType valueOf(com.hedera.hashgraph.sdk.proto.TokenSupplyType code) { - return switch (code) { - case INFINITE -> INFINITE; - case FINITE -> FINITE; - default -> throw new IllegalStateException("(BUG) unhandled TokenSupplyType"); - }; - } - - @Override - public String toString() { - return switch (this) { - case INFINITE -> "INFINITE"; - case FINITE -> "FINITE"; - }; - } - - public com.hedera.hashgraph.sdk.proto.TokenSupplyType toProtobuf() { - return this.code; - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenTransfer.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenTransfer.java deleted file mode 100644 index 3261d6f344..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenTransfer.java +++ /dev/null @@ -1,122 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.hedera.hashgraph.sdk.proto.AccountAmount; - -import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.List; - -/** - * A token transfer record. - * - * Internal utility class. - */ -public class TokenTransfer { - final TokenId tokenId; - final AccountId accountId; - - @Nullable - Integer expectedDecimals; - long amount; - - boolean isApproved; - - /** - * Constructor. - * - * @param tokenId the token id - * @param accountId the account id - * @param amount the amount - * @param isApproved is it approved - */ - TokenTransfer(TokenId tokenId, AccountId accountId, long amount, boolean isApproved) { - this(tokenId, accountId, amount, null, isApproved); - } - - /** - * Constructor. - * - * @param tokenId the token id - * @param accountId the account id - * @param amount the amount - * @param expectedDecimals the expected decimals - * @param isApproved is it approved - */ - TokenTransfer(TokenId tokenId, AccountId accountId, long amount, @Nullable Integer expectedDecimals, boolean isApproved) { - this.tokenId = tokenId; - this.accountId = accountId; - this.amount = amount; - this.expectedDecimals = expectedDecimals; - this.isApproved = isApproved; - } - - /** - * Create a list of token transfer records from a protobuf. - * - * @param tokenTransferLists the protobuf - * @return the list of token transfer records - */ - static List fromProtobuf(List tokenTransferLists) { - var transfers = new ArrayList(); - - for (var tokenTransferList : tokenTransferLists) { - var tokenId = TokenId.fromProtobuf(tokenTransferList.getToken()); - - for (var transfer : tokenTransferList.getTransfersList()) { - transfers.add(new TokenTransfer( - tokenId, - AccountId.fromProtobuf(transfer.getAccountID()), - transfer.getAmount(), - tokenTransferList.hasExpectedDecimals() ? tokenTransferList.getExpectedDecimals().getValue() : null, - transfer.getIsApproval() - )); - } - } - - return transfers; - } - - /** - * Create the protobuf. - * - * @return an account amount protobuf - */ - AccountAmount toProtobuf() { - return AccountAmount.newBuilder() - .setAccountID(accountId.toProtobuf()) - .setAmount(amount) - .setIsApproval(isApproved) - .build(); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("tokenId", tokenId) - .add("accountId", accountId) - .add("amount", amount) - .add("expectedDecimals", expectedDecimals) - .add("isApproved", isApproved) - .toString(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenTransferList.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenTransferList.java deleted file mode 100644 index 3d45b41acf..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenTransferList.java +++ /dev/null @@ -1,73 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.UInt32Value; -import com.hedera.hashgraph.sdk.proto.AccountAmount; -import com.hedera.hashgraph.sdk.proto.NftTransfer; -import java.util.ArrayList; -import java.util.List; -import javax.annotation.Nullable; - -class TokenTransferList { - final TokenId tokenId; - @Nullable - final Integer expectDecimals; - List transfers = new ArrayList<>(); - List nftTransfers = new ArrayList<>(); - - TokenTransferList(TokenId tokenId, @Nullable Integer expectDecimals, @Nullable TokenTransfer transfer, - @Nullable TokenNftTransfer nftTransfer) { - this.tokenId = tokenId; - this.expectDecimals = expectDecimals; - - if (transfer != null) { - this.transfers.add(transfer); - } - - if (nftTransfer != null) { - this.nftTransfers.add(nftTransfer); - } - } - - com.hedera.hashgraph.sdk.proto.TokenTransferList toProtobuf() { - var transfers = new ArrayList(); - var nftTransfers = new ArrayList(); - - for (var transfer : this.transfers) { - transfers.add(transfer.toProtobuf()); - } - - for (var transfer : this.nftTransfers) { - nftTransfers.add(transfer.toProtobuf()); - } - - var builder = com.hedera.hashgraph.sdk.proto.TokenTransferList.newBuilder() - .setToken(tokenId.toProtobuf()) - .addAllTransfers(transfers) - .addAllNftTransfers(nftTransfers); - - if (expectDecimals != null) { - builder.setExpectedDecimals(UInt32Value.newBuilder().setValue(expectDecimals).build()); - } - - return builder.build(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenType.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenType.java deleted file mode 100644 index 38a9e7511a..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenType.java +++ /dev/null @@ -1,80 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -/** - * Possible Token Types (IWA Compatibility). - *

- * Apart from fungible and non-fungible, Tokens can have either a common or - * unique representation. This distinction might seem subtle, but it is - * important when considering how tokens can be traced and if they can have - * isolated and unique properties. - *

- * See Hedera Documentation - */ -public enum TokenType { - /** - * Interchangeable value with one another, where any quantity of them has the same value as another equal quantity if they are in the same class. - * Share a single set of properties, not distinct from one another. Simply represented as a balance or quantity to a given Hedera account. - */ - FUNGIBLE_COMMON(com.hedera.hashgraph.sdk.proto.TokenType.FUNGIBLE_COMMON), - /** - * Unique, not interchangeable with other tokens of the same type as they typically have different values. - * Individually traced and can carry unique properties (e.g. serial number). - */ - NON_FUNGIBLE_UNIQUE(com.hedera.hashgraph.sdk.proto.TokenType.NON_FUNGIBLE_UNIQUE); - - final com.hedera.hashgraph.sdk.proto.TokenType code; - - /** - * Constructor. - * - * @param code the token type - */ - TokenType(com.hedera.hashgraph.sdk.proto.TokenType code) { - this.code = code; - } - - /** - * What type are we. - * - * @param code the token type in question - * @return the token type - */ - static TokenType valueOf(com.hedera.hashgraph.sdk.proto.TokenType code) { - return switch (code) { - case FUNGIBLE_COMMON -> FUNGIBLE_COMMON; - case NON_FUNGIBLE_UNIQUE -> NON_FUNGIBLE_UNIQUE; - default -> throw new IllegalStateException("(BUG) unhandled TokenType"); - }; - } - - @Override - public String toString() { - return switch (this) { - case FUNGIBLE_COMMON -> "FUNGIBLE_COMMON"; - case NON_FUNGIBLE_UNIQUE -> "NON_FUNGIBLE_UNIQUE"; - }; - } - - public com.hedera.hashgraph.sdk.proto.TokenType toProtobuf() { - return this.code; - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenUnfreezeTransaction.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenUnfreezeTransaction.java deleted file mode 100644 index fc7daa2b7c..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenUnfreezeTransaction.java +++ /dev/null @@ -1,179 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenServiceGrpc; -import com.hedera.hashgraph.sdk.proto.TokenUnfreezeAccountTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; -import java.util.LinkedHashMap; -import java.util.Objects; - -/** - * Unfreezes transfers of the specified token for the account. - * - * The transaction must be signed by the token's Freeze Key. - * - * See Hedera Documentation - */ -public class TokenUnfreezeTransaction extends com.hedera.hashgraph.sdk.Transaction { - @Nullable - private TokenId tokenId = null; - @Nullable - private AccountId accountId = null; - - /** - * Constructor. - */ - public TokenUnfreezeTransaction() { - } - - /** - * Constructor. - * - * @param txs Compound list of transaction id's list of (AccountId, Transaction) - * records - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - TokenUnfreezeTransaction(LinkedHashMap> txs) throws InvalidProtocolBufferException { - super(txs); - initFromTransactionBody(); - } - - /** - * Constructor. - * - * @param txBody protobuf TransactionBody - */ - TokenUnfreezeTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { - super(txBody); - initFromTransactionBody(); - } - - /** - * Extract the token id. - * - * @return the token id - */ - @Nullable - public TokenId getTokenId() { - return tokenId; - } - - /** - * Assign the token id. - * - * @param tokenId the token id - * @return {@code this} - */ - public TokenUnfreezeTransaction setTokenId(TokenId tokenId) { - Objects.requireNonNull(tokenId); - requireNotFrozen(); - this.tokenId = tokenId; - return this; - } - - /** - * Extract the account id. - * - * @return the account id - */ - @Nullable - public AccountId getAccountId() { - return accountId; - } - - /** - * Assign the account id. - * - * @param accountId the account id - * @return {@code this} - */ - public TokenUnfreezeTransaction setAccountId(AccountId accountId) { - Objects.requireNonNull(accountId); - requireNotFrozen(); - this.accountId = accountId; - return this; - } - - /** - * Initialize from the transaction body. - */ - void initFromTransactionBody() { - var body = sourceTransactionBody.getTokenUnfreeze(); - if (body.hasToken()) { - tokenId = TokenId.fromProtobuf(body.getToken()); - } - - if (body.hasAccount()) { - accountId = AccountId.fromProtobuf(body.getAccount()); - } - } - - /** - * Build the transaction body. - * - * @return {@code @link - * com.hedera.hashgraph.sdk.proto.TokenUnfreezeAccountTransactionBody} - */ - TokenUnfreezeAccountTransactionBody.Builder build() { - var builder = TokenUnfreezeAccountTransactionBody.newBuilder(); - if (tokenId != null) { - builder.setToken(tokenId.toProtobuf()); - } - - if (accountId != null) { - builder.setAccount(accountId.toProtobuf()); - } - - return builder; - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (tokenId != null) { - tokenId.validateChecksum(client); - } - - if (accountId != null) { - accountId.validateChecksum(client); - } - } - - @Override - MethodDescriptor getMethodDescriptor() { - return TokenServiceGrpc.getFreezeTokenAccountMethod(); - } - - @Override - void onFreeze(TransactionBody.Builder bodyBuilder) { - bodyBuilder.setTokenUnfreeze(build()); - } - - @Override - void onScheduled(SchedulableTransactionBody.Builder scheduled) { - scheduled.setTokenUnfreeze(build()); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenUnpauseTransaction.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenUnpauseTransaction.java deleted file mode 100644 index e71e980460..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenUnpauseTransaction.java +++ /dev/null @@ -1,143 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenServiceGrpc; -import com.hedera.hashgraph.sdk.proto.TokenUnpauseTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; -import java.util.LinkedHashMap; -import java.util.Objects; - -/** - * A token unpause transaction is a transaction that unpauses the token - * that was previously disabled from participating in transactions. The - * token's pause key is required to sign the transaction. Once the unpause - * transaction is submitted the token pause status is updated to unpause. - * - * See Hedera Documentation - */ -public class TokenUnpauseTransaction extends Transaction{ - @Nullable - private TokenId tokenId = null; - - /** - * Constructor - */ - public TokenUnpauseTransaction() { - } - - /** - * Constructor. - * - * @param txs Compound list of transaction id's list of (AccountId, Transaction) - * records - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - TokenUnpauseTransaction(LinkedHashMap> txs) throws InvalidProtocolBufferException { - super(txs); - initFromTransactionBody(); - } - - /** - * Constructor. - * - * @param txBody protobuf TransactionBody - */ - TokenUnpauseTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { - super(txBody); - initFromTransactionBody(); - } - - /** - * Extract the token id. - * - * @return the token id - */ - @Nullable - public TokenId getTokenId() { - return tokenId; - } - - /** - * Assign the token id. - * - * @param tokenId the token id - * @return {@code this} - */ - public TokenUnpauseTransaction setTokenId(TokenId tokenId) { - Objects.requireNonNull(tokenId); - requireNotFrozen(); - this.tokenId = tokenId; - return this; - } - - /** - * Initialize from the transaction body. - */ - void initFromTransactionBody() { - var body = sourceTransactionBody.getTokenUnpause(); - if (body.hasToken()) { - tokenId = TokenId.fromProtobuf(body.getToken()); - } - } - - /** - * Build the transaction body. - * - * @return {@link - * com.hedera.hashgraph.sdk.proto.TokenUnpauseTransactionBody} - */ - TokenUnpauseTransactionBody.Builder build() { - var builder = TokenUnpauseTransactionBody.newBuilder(); - if (tokenId != null) { - builder.setToken(tokenId.toProtobuf()); - } - - return builder; - } - - @Override - MethodDescriptor getMethodDescriptor() { - return TokenServiceGrpc.getUnpauseTokenMethod(); - } - - @Override - void onFreeze(TransactionBody.Builder bodyBuilder) { - bodyBuilder.setTokenUnpause(build()); - } - - @Override - void onScheduled(SchedulableTransactionBody.Builder scheduled) { - scheduled.setTokenUnpause(build()); - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (tokenId != null) { - tokenId.validateChecksum(client); - } - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TopicDeleteTransaction.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TopicDeleteTransaction.java deleted file mode 100644 index 2f5ad1e6c2..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TopicDeleteTransaction.java +++ /dev/null @@ -1,143 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.ConsensusDeleteTopicTransactionBody; -import com.hedera.hashgraph.sdk.proto.ConsensusServiceGrpc; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; -import java.util.LinkedHashMap; -import java.util.Objects; - -/** - * Delete a topic. - *

- * No more transactions or queries on the topic will succeed. - *

- * If an {@code adminKey} is set, this transaction must be signed by that key. - * If there is no {@code adminKey}, this transaction will fail with {@link Status#UNAUTHORIZED}. - */ -public final class TopicDeleteTransaction extends Transaction { - @Nullable - private TopicId topicId = null; - - /** - * Constructor. - */ - public TopicDeleteTransaction() { - } - - /** - * Constructor. - * - * @param txs Compound list of transaction id's list of (AccountId, Transaction) - * records - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - TopicDeleteTransaction(LinkedHashMap> txs) throws InvalidProtocolBufferException { - super(txs); - initFromTransactionBody(); - } - - /** - * Constructor. - * - * @param txBody protobuf TransactionBody - */ - TopicDeleteTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { - super(txBody); - initFromTransactionBody(); - } - - /** - * Extract the topic id. - * - * @return the topic id - */ - @Nullable - public TopicId getTopicId() { - return topicId; - } - - /** - * Set the topic ID to delete. - * - * @param topicId The TopicId to be set - * @return {@code this} - */ - public TopicDeleteTransaction setTopicId(TopicId topicId) { - Objects.requireNonNull(topicId); - requireNotFrozen(); - this.topicId = topicId; - return this; - } - - /** - * Initialize from the transaction body. - */ - void initFromTransactionBody() { - var body = sourceTransactionBody.getConsensusDeleteTopic(); - if (body.hasTopicID()) { - topicId = TopicId.fromProtobuf(body.getTopicID()); - } - } - - /** - * Build the transaction body. - * - * @return {@link - * com.hedera.hashgraph.sdk.proto.ConsensusDeleteTopicTransactionBody} - */ - ConsensusDeleteTopicTransactionBody.Builder build() { - var builder = ConsensusDeleteTopicTransactionBody.newBuilder(); - if (topicId != null) { - builder.setTopicID(topicId.toProtobuf()); - } - - return builder; - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (topicId != null) { - topicId.validateChecksum(client); - } - } - - @Override - MethodDescriptor getMethodDescriptor() { - return ConsensusServiceGrpc.getDeleteTopicMethod(); - } - - @Override - void onFreeze(TransactionBody.Builder bodyBuilder) { - bodyBuilder.setConsensusDeleteTopic(build()); - } - - @Override - void onScheduled(SchedulableTransactionBody.Builder scheduled) { - scheduled.setConsensusDeleteTopic(build()); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TopicInfo.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TopicInfo.java deleted file mode 100644 index 70cdbd5943..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TopicInfo.java +++ /dev/null @@ -1,217 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.ConsensusGetTopicInfoResponse; -import com.hedera.hashgraph.sdk.proto.ConsensusTopicInfo; -import java.time.Duration; -import java.time.Instant; - -import javax.annotation.Nullable; - -/** - * Current state of a topic. - */ -public final class TopicInfo { - /** - * The ID of the topic for which information is requested. - */ - public final TopicId topicId; - - /** - * Short publicly visible memo about the topic. No guarantee of uniqueness. - */ - public final String topicMemo; - - /** - * SHA-384 running hash of (previousRunningHash, topicId, consensusTimestamp, sequenceNumber, message). - */ - public final ByteString runningHash; - - /** - * Sequence number (starting at 1 for the first submitMessage) of messages on the topic. - */ - public final long sequenceNumber; - - /** - * Effective consensus timestamp at (and after) which submitMessage calls will no longer succeed on the topic. - */ - public final Instant expirationTime; - - /** - * Access control for update/delete of the topic. Null if there is no key. - */ - @Nullable - public final Key adminKey; - - /** - * Access control for ConsensusService.submitMessage. Null if there is no key. - */ - @Nullable - public final Key submitKey; - - /** - * If an auto-renew account is specified, when the topic expires, its lifetime will be extended - * by up to this duration (depending on the solvency of the auto-renew account). If the - * auto-renew account has no funds at all, the topic will be deleted instead. - */ - public final Duration autoRenewPeriod; - - /** - * The account, if any, to charge for automatic renewal of the topic's lifetime upon expiry. - */ - @Nullable - public final AccountId autoRenewAccountId; - - /** - * The ledger ID the response was returned from; please see HIP-198 for the network-specific IDs. - */ - public final LedgerId ledgerId; - - private TopicInfo( - TopicId topicId, - String topicMemo, - ByteString runningHash, - long sequenceNumber, - Instant expirationTime, - @Nullable Key adminKey, - @Nullable Key submitKey, - Duration autoRenewPeriod, - @Nullable AccountId autoRenewAccountId, - LedgerId ledgerId - ) { - this.topicId = topicId; - this.topicMemo = topicMemo; - this.runningHash = runningHash; - this.sequenceNumber = sequenceNumber; - this.expirationTime = expirationTime; - this.adminKey = adminKey; - this.submitKey = submitKey; - this.autoRenewPeriod = autoRenewPeriod; - this.autoRenewAccountId = autoRenewAccountId; - this.ledgerId = ledgerId; - } - - /** - * Create a topic info object from a protobuf. - * - * @param topicInfoResponse the protobuf - * @return the new topic info object - */ - static TopicInfo fromProtobuf(ConsensusGetTopicInfoResponse topicInfoResponse) { - var topicInfo = topicInfoResponse.getTopicInfo(); - - var adminKey = topicInfo.hasAdminKey() - ? Key.fromProtobufKey(topicInfo.getAdminKey()) - : null; - - var submitKey = topicInfo.hasSubmitKey() - ? Key.fromProtobufKey(topicInfo.getSubmitKey()) - : null; - - var autoRenewAccountId = topicInfo.hasAutoRenewAccount() - ? AccountId.fromProtobuf(topicInfo.getAutoRenewAccount()) - : null; - - return new TopicInfo( - TopicId.fromProtobuf(topicInfoResponse.getTopicID()), - topicInfo.getMemo(), - topicInfo.getRunningHash(), - topicInfo.getSequenceNumber(), - InstantConverter.fromProtobuf(topicInfo.getExpirationTime()), - adminKey, - submitKey, - DurationConverter.fromProtobuf(topicInfo.getAutoRenewPeriod()), - autoRenewAccountId, - LedgerId.fromByteString(topicInfo.getLedgerId()) - ); - } - - /** - * Create a topic info object from a byte array. - * - * @param bytes the byte array - * @return the new topic info object - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - public static TopicInfo fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return fromProtobuf(ConsensusGetTopicInfoResponse.parseFrom(bytes).toBuilder().build()); - } - - /** - * Create the protobuf. - * - * @return the protobuf representation - */ - ConsensusGetTopicInfoResponse toProtobuf() { - var topicInfoResponseBuilder = ConsensusGetTopicInfoResponse.newBuilder() - .setTopicID(topicId.toProtobuf()); - - var topicInfoBuilder = ConsensusTopicInfo.newBuilder() - .setMemo(topicMemo) - .setRunningHash(runningHash) - .setSequenceNumber(sequenceNumber) - .setExpirationTime(InstantConverter.toProtobuf(expirationTime)) - .setAutoRenewPeriod(DurationConverter.toProtobuf(autoRenewPeriod)) - .setLedgerId(ledgerId.toByteString()); - - if (adminKey != null) { - topicInfoBuilder.setAdminKey(adminKey.toProtobufKey()); - } - - if (submitKey != null) { - topicInfoBuilder.setSubmitKey(submitKey.toProtobufKey()); - } - - if (autoRenewAccountId != null) { - topicInfoBuilder.setAutoRenewAccount(autoRenewAccountId.toProtobuf()); - } - - return topicInfoResponseBuilder.setTopicInfo(topicInfoBuilder).build(); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("topicId", topicId) - .add("topicMemo", topicMemo) - .add("runningHash", runningHash.toByteArray()) - .add("sequenceNumber", sequenceNumber) - .add("expirationTime", expirationTime) - .add("adminKey", adminKey) - .add("submitKey", submitKey) - .add("autoRenewPeriod", autoRenewPeriod) - .add("autoRenewAccountId", autoRenewAccountId) - .add("ledgerId", ledgerId) - .toString(); - } - - /** - * Create the byte array. - * - * @return the byte array representation - */ - public byte[] toBytes() { - return toProtobuf().toByteArray(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TopicInfoQuery.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TopicInfoQuery.java deleted file mode 100644 index 65136b8fff..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TopicInfoQuery.java +++ /dev/null @@ -1,105 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.ConsensusGetTopicInfoQuery; -import com.hedera.hashgraph.sdk.proto.ConsensusServiceGrpc; -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import com.hedera.hashgraph.sdk.proto.Response; -import com.hedera.hashgraph.sdk.proto.ResponseHeader; -import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; -import java.util.Objects; - -/** - * Retrieve the latest state of a topic. - *

- * This method is unrestricted and allowed on any topic by any payer account. - */ -public final class TopicInfoQuery extends Query { - @Nullable - TopicId topicId = null; - - /** - * Constructor. - */ - public TopicInfoQuery() { - } - - /** - * Extract the topic id. - * - * @return the topic id - */ - @Nullable - public TopicId getTopicId() { - return topicId; - } - - /** - * Set the topic to retrieve info about (the parameters and running state of). - * - * @param topicId The TopicId to be set - * @return {@code this} - */ - public TopicInfoQuery setTopicId(TopicId topicId) { - Objects.requireNonNull(topicId); - this.topicId = topicId; - return this; - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (topicId != null) { - topicId.validateChecksum(client); - } - } - - @Override - void onMakeRequest(com.hedera.hashgraph.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { - var builder = ConsensusGetTopicInfoQuery.newBuilder(); - if (topicId != null) { - builder.setTopicID(topicId.toProtobuf()); - } - - queryBuilder.setConsensusGetTopicInfo(builder.setHeader(header)); - } - - @Override - ResponseHeader mapResponseHeader(Response response) { - return response.getConsensusGetTopicInfo().getHeader(); - } - - @Override - QueryHeader mapRequestHeader(com.hedera.hashgraph.sdk.proto.Query request) { - return request.getConsensusGetTopicInfo().getHeader(); - } - - @Override - TopicInfo mapResponse(Response response, AccountId nodeId, com.hedera.hashgraph.sdk.proto.Query request) { - return TopicInfo.fromProtobuf(response.getConsensusGetTopicInfo()); - } - - @Override - MethodDescriptor getMethodDescriptor() { - return ConsensusServiceGrpc.getGetTopicInfoMethod(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TopicMessage.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TopicMessage.java deleted file mode 100644 index 69bb8f2efa..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TopicMessage.java +++ /dev/null @@ -1,161 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.google.protobuf.ByteString; -import com.hedera.hashgraph.sdk.proto.mirror.ConsensusTopicResponse; -import java.time.Instant; - -import javax.annotation.Nullable; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.List; - -/** - * Topic message records. - */ -public final class TopicMessage { - /** - * The consensus timestamp of the message in seconds.nanoseconds - */ - public final Instant consensusTimestamp; - /** - * The content of the message - */ - public final byte[] contents; - /** - * The new running hash of the topic that received the message - */ - public final byte[] runningHash; - /** - * The sequence number of the message relative to all other messages - * for the same topic - */ - public final long sequenceNumber; - /** - * Array of topic message chunks. - */ - @Nullable - public final TopicMessageChunk[] chunks; - /** - * The transaction id - */ - @Nullable - public final TransactionId transactionId; - - /** - * Constructor. - * - * @param lastConsensusTimestamp the last consensus time - * @param message the message - * @param lastRunningHash the last running hash - * @param lastSequenceNumber the last sequence number - * @param chunks the array of chunks - * @param transactionId the transaction id - */ - TopicMessage( - Instant lastConsensusTimestamp, - byte[] message, - byte[] lastRunningHash, - long lastSequenceNumber, - @Nullable TopicMessageChunk[] chunks, - @Nullable TransactionId transactionId - ) { - this.consensusTimestamp = lastConsensusTimestamp; - this.contents = message; - this.runningHash = lastRunningHash; - this.sequenceNumber = lastSequenceNumber; - this.chunks = chunks; - this.transactionId = transactionId; - } - - /** - * Create a new topic message from a response protobuf. - * - * @param response the protobuf response - * @return the new topic message - */ - static TopicMessage ofSingle(ConsensusTopicResponse response) { - return new TopicMessage( - InstantConverter.fromProtobuf(response.getConsensusTimestamp()), - response.getMessage().toByteArray(), - response.getRunningHash().toByteArray(), - response.getSequenceNumber(), - new TopicMessageChunk[]{new TopicMessageChunk(response)}, - response.hasChunkInfo() && response.getChunkInfo().hasInitialTransactionID() ? - TransactionId.fromProtobuf(response.getChunkInfo().getInitialTransactionID()) : - null - ); - } - - /** - * Create a new topic message from a list of response's protobuf. - * - * @param responses the protobuf response - * @return the new topic message - */ - static TopicMessage ofMany(List responses) { - // response should be in the order of oldest to newest (not chunk order) - var chunks = new TopicMessageChunk[responses.size()]; - TransactionId transactionId = null; - var contents = new ByteString[responses.size()]; - long totalSize = 0; - - for (ConsensusTopicResponse r : responses) { - if (transactionId == null && r.getChunkInfo().hasInitialTransactionID()) { - transactionId = TransactionId.fromProtobuf(r.getChunkInfo().getInitialTransactionID()); - } - - int index = r.getChunkInfo().getNumber() - 1; - - chunks[index] = new TopicMessageChunk(r); - contents[index] = r.getMessage(); - totalSize += r.getMessage().size(); - } - - var wholeMessage = ByteBuffer.allocate((int) totalSize); - - for (var content : contents) { - wholeMessage.put(content.asReadOnlyByteBuffer()); - } - - var lastReceived = responses.get(responses.size() - 1); - - return new TopicMessage( - InstantConverter.fromProtobuf(lastReceived.getConsensusTimestamp()), - wholeMessage.array(), - lastReceived.getRunningHash().toByteArray(), - lastReceived.getSequenceNumber(), - chunks, - transactionId - ); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("consensusTimestamp", consensusTimestamp) - .add("contents", new String(contents, StandardCharsets.UTF_8)) - .add("runningHash", runningHash) - .add("sequenceNumber", sequenceNumber) - .toString(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TopicMessageChunk.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TopicMessageChunk.java deleted file mode 100644 index 3c1dd4302b..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TopicMessageChunk.java +++ /dev/null @@ -1,46 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.mirror.ConsensusTopicResponse; -import java.time.Instant; - -/** - * A chunk of the topic message. - */ -final class TopicMessageChunk { - public final Instant consensusTimestamp; - public final long contentSize; - public final byte[] runningHash; - public final long sequenceNumber; - - /** - * Create a topic message chunk from a protobuf. - * - * @param response the protobuf - */ - TopicMessageChunk(ConsensusTopicResponse response) { - consensusTimestamp = InstantConverter.fromProtobuf(response.getConsensusTimestamp()); - contentSize = response.getMessage().size(); - runningHash = response.getRunningHash().toByteArray(); - sequenceNumber = response.getSequenceNumber(); - } -} - diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TopicMessageSubmitTransaction.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TopicMessageSubmitTransaction.java deleted file mode 100644 index 01df81950c..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TopicMessageSubmitTransaction.java +++ /dev/null @@ -1,221 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.ConsensusMessageChunkInfo; -import com.hedera.hashgraph.sdk.proto.ConsensusServiceGrpc; -import com.hedera.hashgraph.sdk.proto.ConsensusSubmitMessageTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionID; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; -import java.util.LinkedHashMap; -import java.util.Objects; - -/** - * Submit a message for consensus. - *

- * Valid and authorized messages on valid topics will be ordered by the consensus service, gossipped to the - * mirror net, and published (in order) to all subscribers (from the mirror net) on this topic. - *

- * The submitKey (if any) must sign this transaction. - *

- * On success, the resulting TransactionReceipt contains the topic's updated topicSequenceNumber and - * topicRunningHash. - */ -public final class TopicMessageSubmitTransaction extends ChunkedTransaction { - @Nullable - private TopicId topicId = null; - - /** - * Constructor. - */ - public TopicMessageSubmitTransaction() { - } - - /** - * Constructor. - * - * @param txs Compound list of transaction id's list of (AccountId, Transaction) - * records - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - TopicMessageSubmitTransaction(LinkedHashMap> txs) throws InvalidProtocolBufferException { - super(txs); - initFromTransactionBody(); - } - - /** - * Constructor. - * - * @param txBody protobuf TransactionBody - */ - TopicMessageSubmitTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { - super(txBody); - initFromTransactionBody(); - } - - /** - * Extract the topic id. - * - * @return the topic id - */ - @Nullable - public TopicId getTopicId() { - return topicId; - } - - /** - * Assign the topic id. - * - * @param topicId the topic id - * @return {@code this} - */ - public TopicMessageSubmitTransaction setTopicId(TopicId topicId) { - Objects.requireNonNull(topicId); - requireNotFrozen(); - this.topicId = topicId; - return this; - } - - /** - * Extract the message. - * - * @return the message - */ - public ByteString getMessage() { - return getData(); - } - - /** - * Assign the message from a byte string. - * - * @param message the byte string - * @return the message - */ - public TopicMessageSubmitTransaction setMessage(ByteString message) { - return setData(message); - } - - /** - * Assign the message from a byte array. - * - * @param message the byte array - * @return the message - */ - public TopicMessageSubmitTransaction setMessage(byte[] message) { - return setData(message); - } - - /** - * Assign the message from a string. - * - * @param message the string - * @return the message - */ - public TopicMessageSubmitTransaction setMessage(String message) { - return setData(message); - } - - /** - * Initialize from the transaction body. - */ - void initFromTransactionBody() { - var body = sourceTransactionBody.getConsensusSubmitMessage(); - if (body.hasTopicID()) { - topicId = TopicId.fromProtobuf(body.getTopicID()); - } - - if (!innerSignedTransactions.isEmpty()) { - try { - for (var i = 0; i < innerSignedTransactions.size(); - i += nodeAccountIds.isEmpty() ? 1 : nodeAccountIds.size()) { - data = data.concat( - TransactionBody.parseFrom(innerSignedTransactions.get(i).getBodyBytes()) - .getConsensusSubmitMessage().getMessage() - ); - } - } catch (InvalidProtocolBufferException exc) { - throw new IllegalArgumentException(exc.getMessage()); - } - } else { - data = body.getMessage(); - } - } - - /** - * Build the transaction body. - * - * @return {@link - * com.hedera.hashgraph.sdk.proto.ConsensusSubmitMessageTransactionBody} - */ - ConsensusSubmitMessageTransactionBody.Builder build() { - var builder = ConsensusSubmitMessageTransactionBody.newBuilder(); - if (topicId != null) { - builder.setTopicID(topicId.toProtobuf()); - } - builder.setMessage(data); - - return builder; - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (topicId != null) { - topicId.validateChecksum(client); - } - } - - @Override - MethodDescriptor getMethodDescriptor() { - return ConsensusServiceGrpc.getSubmitMessageMethod(); - } - - @Override - void onFreeze(TransactionBody.Builder bodyBuilder) { - bodyBuilder.setConsensusSubmitMessage(build()); - } - - @Override - void onFreezeChunk(TransactionBody.Builder body, @Nullable TransactionID initialTransactionId, int startIndex, int endIndex, int chunk, int total) { - if (total == 1) { - body.setConsensusSubmitMessage(build().setMessage(data.substring(startIndex, endIndex))); - } else { - body.setConsensusSubmitMessage(build().setMessage(data.substring(startIndex, endIndex)) - .setChunkInfo(ConsensusMessageChunkInfo.newBuilder() - .setInitialTransactionID(Objects.requireNonNull(initialTransactionId)) - .setNumber(chunk + 1) - .setTotal(total) - ) - ); - } - - } - - @Override - void onScheduled(SchedulableTransactionBody.Builder scheduled) { - scheduled.setConsensusSubmitMessage(build().setMessage(data)); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/Transaction.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/Transaction.java deleted file mode 100644 index 488ba06f4c..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/Transaction.java +++ /dev/null @@ -1,1370 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.SignatureMap; -import com.hedera.hashgraph.sdk.proto.SignaturePair; -import com.hedera.hashgraph.sdk.proto.SignedTransaction; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionList; -import java.lang.reflect.Modifier; -import java.time.Duration; -import java.time.Instant; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.function.Function; -import java.util.function.UnaryOperator; -import javax.annotation.Nullable; -import org.bouncycastle.crypto.digests.SHA384Digest; - -/** - * Base class for all transactions that may be built and submitted to Hedera. - * - * @param The type of the transaction. Used to enable chaining. - */ -public abstract class Transaction> - extends - Executable { - - /** - * Default auto renew duration for accounts, contracts, topics, and files (entities) - */ - static final Duration DEFAULT_AUTO_RENEW_PERIOD = Duration.ofDays(90); - - /** - * Dummy account ID used to assist in deserializing incomplete Transactions. - */ - protected static final AccountId DUMMY_ACCOUNT_ID = new AccountId(0L, 0L, 0L); - - /** - * Dummy transaction ID used to assist in deserializing incomplete Transactions. - */ - protected static final TransactionId DUMMY_TRANSACTION_ID = TransactionId.withValidStart(DUMMY_ACCOUNT_ID, Instant.EPOCH); - - /** - * Default transaction duration - */ - private static final Duration DEFAULT_TRANSACTION_VALID_DURATION = Duration.ofSeconds(120); - - /** - * Transaction constructors end their work by setting sourceTransactionBody. The expectation is that the Transaction - * subclass constructor will pick up where the Transaction superclass constructor left off, and will unpack the data - * in the transaction body. - */ - protected final TransactionBody sourceTransactionBody; - /** - * The builder that gets re-used to build each outer transaction. freezeWith() will create the frozenBodyBuilder. - * The presence of frozenBodyBuilder indicates that this transaction is frozen. - */ - @Nullable - protected TransactionBody.Builder frozenBodyBuilder = null; - - /** - * An SDK [Transaction] is composed of multiple, raw protobuf transactions. These should be functionally identical, - * except pointing to different nodes. When retrying a transaction after a network error or retry-able status - * response, we try a different transaction and thus a different node. - */ - protected List outerTransactions = Collections.emptyList(); - - /** - * An SDK [Transaction] is composed of multiple, raw protobuf transactions. These should be functionally identical, - * except pointing to different nodes. When retrying a transaction after a network error or retry-able status - * response, we try a different transaction and thus a different node. - */ - protected List innerSignedTransactions = Collections.emptyList(); - - /** - * A set of signatures corresponding to every unique public key used to sign the transaction. - */ - protected List sigPairLists = Collections.emptyList(); - - /** - * List of IDs for the transaction based on the operator because the transaction ID includes the operator's account - */ - protected LockableList transactionIds = new LockableList<>(); - - /** - * publicKeys and signers are parallel arrays. If the signer associated with a public key is null, that means that - * the private key associated with that public key has already contributed a signature to sigPairListBuilders, but - * the signer is not available (likely because this came from fromBytes()) - */ - protected List publicKeys = new ArrayList<>(); - - /** - * publicKeys and signers are parallel arrays. If the signer associated with a public key is null, that means that - * the private key associated with that public key has already contributed a signature to sigPairListBuilders, but - * the signer is not available (likely because this came from fromBytes()) - */ - protected List> signers = new ArrayList<>(); - - /** - * The maximum transaction fee the client is willing to pay - */ - protected Hbar defaultMaxTransactionFee = new Hbar(2); - /** - * Should the transaction id be regenerated - */ - protected Boolean regenerateTransactionId = null; - private Duration transactionValidDuration; - @Nullable - private Hbar maxTransactionFee = null; - private String memo = ""; - - /** - * Constructor. - */ - Transaction() { - setTransactionValidDuration(DEFAULT_TRANSACTION_VALID_DURATION); - - sourceTransactionBody = TransactionBody.getDefaultInstance(); - } - - // This constructor is used to construct from a scheduled transaction body - - /** - * Constructor. - * - * @param txBody protobuf TransactionBody - */ - Transaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { - setTransactionValidDuration(DEFAULT_TRANSACTION_VALID_DURATION); - setMaxTransactionFee(Hbar.fromTinybars(txBody.getTransactionFee())); - setTransactionMemo(txBody.getMemo()); - - sourceTransactionBody = txBody; - } - - // This constructor is used to construct via fromBytes - - /** - * Constructor. - * - * @param txs Compound list of transaction id's list of (AccountId, Transaction) records - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - Transaction(LinkedHashMap> txs) - throws InvalidProtocolBufferException { - LinkedHashMap transactionMap = txs.values().iterator().next(); - if (!transactionMap.isEmpty() && transactionMap.keySet().iterator().next().equals(DUMMY_ACCOUNT_ID)) { - // If the first account ID is a dummy account ID, then only the source TransactionBody needs to be copied. - var signedTransaction = SignedTransaction.parseFrom(transactionMap.values().iterator().next().getSignedTransactionBytes()); - sourceTransactionBody = TransactionBody.parseFrom(signedTransaction.getBodyBytes()); - } else { - var txCount = txs.keySet().size(); - var nodeCount = txs.values().iterator().next().size(); - - nodeAccountIds.ensureCapacity(nodeCount); - sigPairLists = new ArrayList<>(nodeCount * txCount); - outerTransactions = new ArrayList<>(nodeCount * txCount); - innerSignedTransactions = new ArrayList<>(nodeCount * txCount); - transactionIds.ensureCapacity(txCount); - - for (var transactionEntry : txs.entrySet()) { - if (!transactionEntry.getKey().equals(DUMMY_TRANSACTION_ID)) { - transactionIds.add(transactionEntry.getKey()); - } - for (var nodeEntry : transactionEntry.getValue().entrySet()) { - if (nodeAccountIds.size() != nodeCount) { - nodeAccountIds.add(nodeEntry.getKey()); - } - - var transaction = SignedTransaction.parseFrom(nodeEntry.getValue().getSignedTransactionBytes()); - outerTransactions.add(nodeEntry.getValue()); - sigPairLists.add(transaction.getSigMap().toBuilder()); - innerSignedTransactions.add(transaction.toBuilder()); - - if (publicKeys.isEmpty()) { - for (var sigPair : transaction.getSigMap().getSigPairList()) { - publicKeys.add(PublicKey.fromBytes(sigPair.getPubKeyPrefix().toByteArray())); - signers.add(null); - } - } - } - } - - nodeAccountIds.remove(new AccountId(0)); - - // Verify that transaction bodies match - for (int i = 0; i < txCount; i++) { - TransactionBody firstTxBody = null; - for (int j = 0; j < nodeCount; j++) { - int k = i * nodeCount + j; - var txBody = TransactionBody.parseFrom(innerSignedTransactions.get(k).getBodyBytes()); - if (firstTxBody == null) { - firstTxBody = txBody; - } else { - requireProtoMatches( - firstTxBody, - txBody, - new HashSet<>(List.of("NodeAccountID")), - "TransactionBody" - ); - } - } - } - sourceTransactionBody = TransactionBody.parseFrom(innerSignedTransactions.get(0).getBodyBytes()); - } - - setTransactionValidDuration( - DurationConverter.fromProtobuf(sourceTransactionBody.getTransactionValidDuration())); - setMaxTransactionFee(Hbar.fromTinybars(sourceTransactionBody.getTransactionFee())); - setTransactionMemo(sourceTransactionBody.getMemo()); - - // The presence of signatures implies the Transaction should be frozen. - if (!publicKeys.isEmpty()) { - frozenBodyBuilder = sourceTransactionBody.toBuilder(); - } - } - - /** - * Create the correct transaction from a byte array. - * - * @param bytes the byte array - * @return the new transaction - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - public static Transaction fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - var txs = new LinkedHashMap>(); - TransactionBody.DataCase dataCase = TransactionBody.DataCase.DATA_NOT_SET; - - var list = TransactionList.parseFrom(bytes); - - if (list.getTransactionListList().isEmpty()) { - var transaction = com.hedera.hashgraph.sdk.proto.Transaction.parseFrom(bytes).toBuilder(); - - TransactionBody txBody; - if (transaction.getSignedTransactionBytes().isEmpty()) { - txBody = TransactionBody.parseFrom(transaction.getBodyBytes()); - - transaction.setSignedTransactionBytes(SignedTransaction.newBuilder() - .setBodyBytes(transaction.getBodyBytes()) - .setSigMap(transaction.getSigMap()) - .build() - .toByteString()) - .clearBodyBytes() - .clearSigMap(); - } else { - var signedTransaction = SignedTransaction.parseFrom(transaction.getSignedTransactionBytes()); - txBody = TransactionBody.parseFrom(signedTransaction.getBodyBytes()); - } - - dataCase = txBody.getDataCase(); - - var account = txBody.hasNodeAccountID() ? AccountId.fromProtobuf(txBody.getNodeAccountID()) - : DUMMY_ACCOUNT_ID; - var transactionId = txBody.hasTransactionID() ? TransactionId.fromProtobuf(txBody.getTransactionID()) - : DUMMY_TRANSACTION_ID; - - var linked = new LinkedHashMap(); - linked.put(account, transaction.build()); - txs.put(transactionId, linked); - } else { - for (var transaction : list.getTransactionListList()) { - var signedTransaction = SignedTransaction.parseFrom(transaction.getSignedTransactionBytes()); - var txBody = TransactionBody.parseFrom(signedTransaction.getBodyBytes()); - - if (dataCase.getNumber() == TransactionBody.DataCase.DATA_NOT_SET.getNumber()) { - dataCase = txBody.getDataCase(); - } - - var account = txBody.hasNodeAccountID() ? AccountId.fromProtobuf(txBody.getNodeAccountID()) - : DUMMY_ACCOUNT_ID; - var transactionId = txBody.hasTransactionID() ? TransactionId.fromProtobuf(txBody.getTransactionID()) - : DUMMY_TRANSACTION_ID; - - var linked = txs.containsKey(transactionId) ? - Objects.requireNonNull(txs.get(transactionId)) : - new LinkedHashMap(); - - linked.put(account, transaction); - - txs.put(transactionId, linked); - } - } - - return switch (dataCase) { - case CONTRACTCALL -> new ContractExecuteTransaction(txs); - case CONTRACTCREATEINSTANCE -> new ContractCreateTransaction(txs); - case CONTRACTUPDATEINSTANCE -> new ContractUpdateTransaction(txs); - case CONTRACTDELETEINSTANCE -> new ContractDeleteTransaction(txs); - case ETHEREUMTRANSACTION -> new EthereumTransaction(txs); - case CRYPTOADDLIVEHASH -> new LiveHashAddTransaction(txs); - case CRYPTOCREATEACCOUNT -> new AccountCreateTransaction(txs); - case CRYPTODELETE -> new AccountDeleteTransaction(txs); - case CRYPTODELETELIVEHASH -> new LiveHashDeleteTransaction(txs); - case CRYPTOTRANSFER -> new TransferTransaction(txs); - case CRYPTOUPDATEACCOUNT -> new AccountUpdateTransaction(txs); - case FILEAPPEND -> new FileAppendTransaction(txs); - case FILECREATE -> new FileCreateTransaction(txs); - case FILEDELETE -> new FileDeleteTransaction(txs); - case FILEUPDATE -> new FileUpdateTransaction(txs); - case NODECREATE -> new NodeCreateTransaction(txs); - case NODEUPDATE -> new NodeUpdateTransaction(txs); - case NODEDELETE -> new NodeDeleteTransaction(txs); - case SYSTEMDELETE -> new SystemDeleteTransaction(txs); - case SYSTEMUNDELETE -> new SystemUndeleteTransaction(txs); - case FREEZE -> new FreezeTransaction(txs); - case CONSENSUSCREATETOPIC -> new TopicCreateTransaction(txs); - case CONSENSUSUPDATETOPIC -> new TopicUpdateTransaction(txs); - case CONSENSUSDELETETOPIC -> new TopicDeleteTransaction(txs); - case CONSENSUSSUBMITMESSAGE -> new TopicMessageSubmitTransaction(txs); - case TOKENASSOCIATE -> new TokenAssociateTransaction(txs); - case TOKENBURN -> new TokenBurnTransaction(txs); - case TOKENCREATION -> new TokenCreateTransaction(txs); - case TOKENDELETION -> new TokenDeleteTransaction(txs); - case TOKENDISSOCIATE -> new TokenDissociateTransaction(txs); - case TOKENFREEZE -> new TokenFreezeTransaction(txs); - case TOKENGRANTKYC -> new TokenGrantKycTransaction(txs); - case TOKENMINT -> new TokenMintTransaction(txs); - case TOKENREVOKEKYC -> new TokenRevokeKycTransaction(txs); - case TOKENUNFREEZE -> new TokenUnfreezeTransaction(txs); - case TOKENUPDATE -> new TokenUpdateTransaction(txs); - case TOKEN_UPDATE_NFTS -> new TokenUpdateNftsTransaction(txs); - case TOKENWIPE -> new TokenWipeTransaction(txs); - case TOKEN_FEE_SCHEDULE_UPDATE -> new TokenFeeScheduleUpdateTransaction(txs); - case SCHEDULECREATE -> new ScheduleCreateTransaction(txs); - case SCHEDULEDELETE -> new ScheduleDeleteTransaction(txs); - case SCHEDULESIGN -> new ScheduleSignTransaction(txs); - case TOKEN_PAUSE -> new TokenPauseTransaction(txs); - case TOKEN_UNPAUSE -> new TokenUnpauseTransaction(txs); - case TOKENREJECT -> new TokenRejectTransaction(txs); - case TOKENAIRDROP -> new TokenAirdropTransaction(txs); - case TOKENCANCELAIRDROP -> new TokenCancelAirdropTransaction(txs); - case TOKENCLAIMAIRDROP -> new TokenClaimAirdropTransaction(txs); - case CRYPTOAPPROVEALLOWANCE -> new AccountAllowanceApproveTransaction(txs); - case CRYPTODELETEALLOWANCE -> new AccountAllowanceDeleteTransaction(txs); - default -> throw new IllegalArgumentException("parsed transaction body has no data"); - }; - } - - /** - * Create the correct transaction from a scheduled transaction. - * - * @param scheduled the scheduled transaction - * @return the new transaction - */ - public static Transaction fromScheduledTransaction( - com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody scheduled) { - var body = TransactionBody.newBuilder() - .setMemo(scheduled.getMemo()) - .setTransactionFee(scheduled.getTransactionFee()); - - return switch (scheduled.getDataCase()) { - case CONTRACTCALL -> - new ContractExecuteTransaction(body.setContractCall(scheduled.getContractCall()).build()); - case CONTRACTCREATEINSTANCE -> new ContractCreateTransaction( - body.setContractCreateInstance(scheduled.getContractCreateInstance()).build()); - case CONTRACTUPDATEINSTANCE -> new ContractUpdateTransaction( - body.setContractUpdateInstance(scheduled.getContractUpdateInstance()).build()); - case CONTRACTDELETEINSTANCE -> new ContractDeleteTransaction( - body.setContractDeleteInstance(scheduled.getContractDeleteInstance()).build()); - case CRYPTOAPPROVEALLOWANCE -> new AccountAllowanceApproveTransaction( - body.setCryptoApproveAllowance(scheduled.getCryptoApproveAllowance()).build()); - case CRYPTODELETEALLOWANCE -> new AccountAllowanceDeleteTransaction( - body.setCryptoDeleteAllowance(scheduled.getCryptoDeleteAllowance()).build()); - case CRYPTOCREATEACCOUNT -> new AccountCreateTransaction( - body.setCryptoCreateAccount(scheduled.getCryptoCreateAccount()).build()); - case CRYPTODELETE -> - new AccountDeleteTransaction(body.setCryptoDelete(scheduled.getCryptoDelete()).build()); - case CRYPTOTRANSFER -> - new TransferTransaction(body.setCryptoTransfer(scheduled.getCryptoTransfer()).build()); - case CRYPTOUPDATEACCOUNT -> new AccountUpdateTransaction( - body.setCryptoUpdateAccount(scheduled.getCryptoUpdateAccount()).build()); - case FILEAPPEND -> new FileAppendTransaction(body.setFileAppend(scheduled.getFileAppend()).build()); - case FILECREATE -> new FileCreateTransaction(body.setFileCreate(scheduled.getFileCreate()).build()); - case FILEDELETE -> new FileDeleteTransaction(body.setFileDelete(scheduled.getFileDelete()).build()); - case FILEUPDATE -> new FileUpdateTransaction(body.setFileUpdate(scheduled.getFileUpdate()).build()); - case NODECREATE -> new NodeCreateTransaction(body.setNodeCreate(scheduled.getNodeCreate()).build()); - case NODEUPDATE -> new NodeUpdateTransaction(body.setNodeUpdate(scheduled.getNodeUpdate()).build()); - case NODEDELETE -> new NodeDeleteTransaction(body.setNodeDelete(scheduled.getNodeDelete()).build()); - case SYSTEMDELETE -> new SystemDeleteTransaction(body.setSystemDelete(scheduled.getSystemDelete()).build()); - case SYSTEMUNDELETE -> - new SystemUndeleteTransaction(body.setSystemUndelete(scheduled.getSystemUndelete()).build()); - case FREEZE -> new FreezeTransaction(body.setFreeze(scheduled.getFreeze()).build()); - case CONSENSUSCREATETOPIC -> new TopicCreateTransaction( - body.setConsensusCreateTopic(scheduled.getConsensusCreateTopic()).build()); - case CONSENSUSUPDATETOPIC -> new TopicUpdateTransaction( - body.setConsensusUpdateTopic(scheduled.getConsensusUpdateTopic()).build()); - case CONSENSUSDELETETOPIC -> new TopicDeleteTransaction( - body.setConsensusDeleteTopic(scheduled.getConsensusDeleteTopic()).build()); - case CONSENSUSSUBMITMESSAGE -> new TopicMessageSubmitTransaction( - body.setConsensusSubmitMessage(scheduled.getConsensusSubmitMessage()).build()); - case TOKENCREATION -> - new TokenCreateTransaction(body.setTokenCreation(scheduled.getTokenCreation()).build()); - case TOKENFREEZE -> new TokenFreezeTransaction(body.setTokenFreeze(scheduled.getTokenFreeze()).build()); - case TOKENUNFREEZE -> - new TokenUnfreezeTransaction(body.setTokenUnfreeze(scheduled.getTokenUnfreeze()).build()); - case TOKENGRANTKYC -> - new TokenGrantKycTransaction(body.setTokenGrantKyc(scheduled.getTokenGrantKyc()).build()); - case TOKENREVOKEKYC -> - new TokenRevokeKycTransaction(body.setTokenRevokeKyc(scheduled.getTokenRevokeKyc()).build()); - case TOKENDELETION -> - new TokenDeleteTransaction(body.setTokenDeletion(scheduled.getTokenDeletion()).build()); - case TOKENUPDATE -> new TokenUpdateTransaction(body.setTokenUpdate(scheduled.getTokenUpdate()).build()); - case TOKEN_UPDATE_NFTS -> new TokenUpdateNftsTransaction(body.setTokenUpdateNfts(scheduled.getTokenUpdateNfts()).build()); - case TOKENMINT -> new TokenMintTransaction(body.setTokenMint(scheduled.getTokenMint()).build()); - case TOKENBURN -> new TokenBurnTransaction(body.setTokenBurn(scheduled.getTokenBurn()).build()); - case TOKENWIPE -> new TokenWipeTransaction(body.setTokenWipe(scheduled.getTokenWipe()).build()); - case TOKENASSOCIATE -> - new TokenAssociateTransaction(body.setTokenAssociate(scheduled.getTokenAssociate()).build()); - case TOKENDISSOCIATE -> - new TokenDissociateTransaction(body.setTokenDissociate(scheduled.getTokenDissociate()).build()); - case TOKEN_FEE_SCHEDULE_UPDATE -> new TokenFeeScheduleUpdateTransaction( - body.setTokenFeeScheduleUpdate(scheduled.getTokenFeeScheduleUpdate()).build()); - case TOKEN_PAUSE -> new TokenPauseTransaction(body.setTokenPause(scheduled.getTokenPause()).build()); - case TOKEN_UNPAUSE -> - new TokenUnpauseTransaction(body.setTokenUnpause(scheduled.getTokenUnpause()).build()); - case TOKENREJECT -> - new TokenRejectTransaction(body.setTokenReject(scheduled.getTokenReject()).build()); - case TOKENAIRDROP -> new TokenAirdropTransaction(body.setTokenAirdrop(scheduled.getTokenAirdrop()).build()); - case TOKENCANCELAIRDROP -> new TokenCancelAirdropTransaction(body.setTokenCancelAirdrop(scheduled.getTokenCancelAirdrop()).build()); - case TOKENCLAIMAIRDROP -> new TokenClaimAirdropTransaction(body.setTokenCancelAirdrop(scheduled.getTokenCancelAirdrop()).build()); - case SCHEDULEDELETE -> - new ScheduleDeleteTransaction(body.setScheduleDelete(scheduled.getScheduleDelete()).build()); - default -> throw new IllegalStateException("schedulable transaction did not have a transaction set"); - }; - } - - private static void throwProtoMatchException(String fieldName, String aWas, String bWas) { - throw new IllegalArgumentException( - "fromBytes() failed because " + fieldName + - " fields in TransactionBody protobuf messages in the TransactionList did not match: A was " + - aWas + ", B was " + bWas - ); - } - - private static void requireProtoMatches(Object protoA, Object protoB, Set ignoreSet, String thisFieldName) { - var aIsNull = protoA == null; - var bIsNull = protoB == null; - if (aIsNull != bIsNull) { - throwProtoMatchException(thisFieldName, aIsNull ? "null" : "not null", bIsNull ? "null" : "not null"); - } - if (aIsNull) { - return; - } - var protoAClass = protoA.getClass(); - var protoBClass = protoB.getClass(); - if (!protoAClass.equals(protoBClass)) { - throwProtoMatchException(thisFieldName, "of class " + protoAClass, "of class " + protoBClass); - } - if (protoA instanceof Boolean || - protoA instanceof Integer || - protoA instanceof Long || - protoA instanceof Float || - protoA instanceof Double || - protoA instanceof String || - protoA instanceof ByteString - ) { - // System.out.println("values A = " + protoA.toString() + ", B = " + protoB.toString()); - if (!protoA.equals(protoB)) { - throwProtoMatchException(thisFieldName, protoA.toString(), protoB.toString()); - } - } - for (var method : protoAClass.getDeclaredMethods()) { - if (method.getParameterCount() != 0) { - continue; - } - int methodModifiers = method.getModifiers(); - if ((!Modifier.isPublic(methodModifiers)) || Modifier.isStatic(methodModifiers)) { - continue; - } - var methodName = method.getName(); - if (!methodName.startsWith("get")) { - continue; - } - var isList = methodName.endsWith("List") && List.class.isAssignableFrom(method.getReturnType()); - var methodFieldName = methodName.substring(3, methodName.length() - (isList ? 4 : 0)); - if (ignoreSet.contains(methodFieldName) || methodFieldName.equals("DefaultInstance")) { - continue; - } - if (!isList) { - try { - var hasMethod = protoAClass.getMethod("has" + methodFieldName); - var hasA = (Boolean) hasMethod.invoke(protoA); - var hasB = (Boolean) hasMethod.invoke(protoB); - if (!hasA.equals(hasB)) { - throwProtoMatchException(methodFieldName, hasA ? "present" : "not present", - hasB ? "present" : "not present"); - } - if (!hasA) { - continue; - } - } catch (NoSuchMethodException ignored) { - // pass if there is no has method - } catch (IllegalArgumentException error) { - throw error; - } catch (Throwable error) { - throw new IllegalArgumentException("fromBytes() failed due to error", error); - } - } - try { - var retvalA = method.invoke(protoA); - var retvalB = method.invoke(protoB); - if (isList) { - var listA = (List) retvalA; - var listB = (List) retvalB; - if (listA.size() != listB.size()) { - throwProtoMatchException(methodFieldName, "of size " + listA.size(), "of size " + listB.size()); - } - for (int i = 0; i < listA.size(); i++) { - // System.out.println("comparing " + thisFieldName + "." + methodFieldName + "[" + i + "]"); - requireProtoMatches(listA.get(i), listB.get(i), ignoreSet, methodFieldName + "[" + i + "]"); - } - } else { - // System.out.println("comparing " + thisFieldName + "." + methodFieldName); - requireProtoMatches(retvalA, retvalB, ignoreSet, methodFieldName); - } - } catch (IllegalArgumentException error) { - throw error; - } catch (Throwable error) { - throw new IllegalArgumentException("fromBytes() failed due to error", error); - } - } - } - - /** - * Generate a hash from a byte array. - * - * @param bytes the byte array - * @return the hash - */ - static byte[] hash(byte[] bytes) { - var digest = new SHA384Digest(); - var hash = new byte[digest.getDigestSize()]; - - digest.update(bytes, 0, bytes.length); - digest.doFinal(hash, 0); - - return hash; - } - - private static boolean publicKeyIsInSigPairList(ByteString publicKeyBytes, List sigPairList) { - for (var pair : sigPairList) { - if (pair.getPubKeyPrefix().equals(publicKeyBytes)) { - return true; - } - } - return false; - } - - /** - * Converts transaction into a scheduled version - * - * @param bodyBuilder the transaction's body builder - * @return the scheduled transaction - */ - protected ScheduleCreateTransaction doSchedule(TransactionBody.Builder bodyBuilder) { - var schedulable = SchedulableTransactionBody.newBuilder() - .setTransactionFee(bodyBuilder.getTransactionFee()) - .setMemo(bodyBuilder.getMemo()); - - onScheduled(schedulable); - - var scheduled = new ScheduleCreateTransaction() - .setScheduledTransactionBody(schedulable.build()); - - if (!transactionIds.isEmpty()) { - scheduled.setTransactionId(transactionIds.get(0)); - } - - return scheduled; - } - - /** - * Extract the scheduled transaction. - * - * @return the scheduled transaction - */ - public ScheduleCreateTransaction schedule() { - requireNotFrozen(); - if (!nodeAccountIds.isEmpty()) { - throw new IllegalStateException( - "The underlying transaction for a scheduled transaction cannot have node account IDs set" - ); - } - - var bodyBuilder = spawnBodyBuilder(null); - - onFreeze(bodyBuilder); - - return doSchedule(bodyBuilder); - } - - /** - * Set the account IDs of the nodes that this transaction will be submitted to. - *

- * Providing an explicit node account ID interferes with client-side load balancing of the network. By default, the - * SDK will pre-generate a transaction for 1/3 of the nodes on the network. If a node is down, busy, or otherwise - * reports a fatal error, the SDK will try again with a different node. - * - * @param nodeAccountIds The list of node AccountIds to be set - * @return {@code this} - */ - @Override - public final T setNodeAccountIds(List nodeAccountIds) { - requireNotFrozen(); - Objects.requireNonNull(nodeAccountIds); - return super.setNodeAccountIds(nodeAccountIds); - } - - /** - * Extract the valid transaction duration. - * - * @return the transaction valid duration - */ - @Nullable - public final Duration getTransactionValidDuration() { - return transactionValidDuration; - } - - /** - * Sets the duration that this transaction is valid for. - *

- * This is defaulted by the SDK to 120 seconds (or two minutes). - * - * @param validDuration The duration to be set - * @return {@code this} - */ - public final T setTransactionValidDuration(Duration validDuration) { - requireNotFrozen(); - Objects.requireNonNull(validDuration); - transactionValidDuration = validDuration; - // noinspection unchecked - return (T) this; - } - - /** - * Extract the maximum transaction fee. - * - * @return the maximum transaction fee - */ - @Nullable - public final Hbar getMaxTransactionFee() { - return maxTransactionFee; - } - - /** - * Set the maximum transaction fee the operator (paying account) is willing to pay. - * - * @param maxTransactionFee the maximum transaction fee, in tinybars. - * @return {@code this} - */ - public final T setMaxTransactionFee(Hbar maxTransactionFee) { - requireNotFrozen(); - Objects.requireNonNull(maxTransactionFee); - this.maxTransactionFee = maxTransactionFee; - // noinspection unchecked - return (T) this; - } - - /** - * Extract the default maximum transaction fee. - * - * @return the default maximum transaction fee - */ - public final Hbar getDefaultMaxTransactionFee() { - return defaultMaxTransactionFee; - } - - /** - * Extract the memo for the transaction. - * - * @return the memo for the transaction - */ - public final String getTransactionMemo() { - return memo; - } - - /** - * Set a note or description that should be recorded in the transaction record (maximum length of 100 characters). - * - * @param memo any notes or descriptions for this transaction. - * @return {@code this} - */ - public final T setTransactionMemo(String memo) { - requireNotFrozen(); - Objects.requireNonNull(memo); - this.memo = memo; - // noinspection unchecked - return (T) this; - } - - /** - * Extract a byte array representation. - * - * @return the byte array representation - */ - public byte[] toBytes() { - var list = TransactionList.newBuilder(); - - // If no nodes have been selected yet, - // the new TransactionBody can be used to build a Transaction protobuf object. - if (nodeAccountIds.isEmpty()) { - var bodyBuilder = spawnBodyBuilder(null); - if (!transactionIds.isEmpty()) { - bodyBuilder.setTransactionID(transactionIds.get(0).toProtobuf()); - } - onFreeze(bodyBuilder); - - var signedTransaction = SignedTransaction.newBuilder() - .setBodyBytes(bodyBuilder.build().toByteString()) - .build(); - - var transaction = com.hedera.hashgraph.sdk.proto.Transaction.newBuilder() - .setSignedTransactionBytes(signedTransaction.toByteString()) - .build(); - - list.addTransactionList(transaction); - } else { - // Generate the SignedTransaction protobuf objects if the Transaction's not frozen. - if (!this.isFrozen()) { - frozenBodyBuilder = spawnBodyBuilder(null); - if (!transactionIds.isEmpty()) { - frozenBodyBuilder.setTransactionID(transactionIds.get(0).toProtobuf()); - } - onFreeze(frozenBodyBuilder); - - int requiredChunks = getRequiredChunks(); - if (!transactionIds.isEmpty()){ - generateTransactionIds(transactionIds.get(0), requiredChunks); - } - wipeTransactionLists(requiredChunks); - } - - // Build all the Transaction protobuf objects and add them to the TransactionList protobuf object. - buildAllTransactions(); - for (var transaction : outerTransactions) { - list.addTransactionList(transaction); - } - } - - return list.build().toByteArray(); - } - - /** - * Extract a byte array of the transaction hash. - * - * @return the transaction hash - */ - public byte[] getTransactionHash() { - if (!this.isFrozen()) { - throw new IllegalStateException( - "transaction must have been frozen before calculating the hash will be stable, try calling `freeze`"); - } - - transactionIds.setLocked(true); - nodeAccountIds.setLocked(true); - - var index = transactionIds.getIndex() * nodeAccountIds.size() + nodeAccountIds.getIndex(); - - buildTransaction(index); - - return hash(outerTransactions.get(index).getSignedTransactionBytes().toByteArray()); - } - - /** - * Extract the list of account id and hash records. - * - * @return the list of account id and hash records - */ - public Map getTransactionHashPerNode() { - if (!this.isFrozen()) { - throw new IllegalStateException( - "transaction must have been frozen before calculating the hash will be stable, try calling `freeze`"); - } - - buildAllTransactions(); - - var hashes = new HashMap(); - - for (var i = 0; i < outerTransactions.size(); i++) { - hashes.put(nodeAccountIds.get(i), hash(outerTransactions.get(i).getSignedTransactionBytes().toByteArray())); - } - - return hashes; - } - - @Override - final TransactionId getTransactionIdInternal() { - return transactionIds.getCurrent(); - } - - /** - * Extract the transaction id. - * - * @return the transaction id - */ - public final TransactionId getTransactionId() { - if (transactionIds.isEmpty() || !this.isFrozen()) { - throw new IllegalStateException("No transaction ID generated yet. Try freezing the transaction or manually setting the transaction ID."); - } - - return transactionIds.setLocked(true).getCurrent(); - } - - /** - * Set the ID for this transaction. - *

- * The transaction ID includes the operator's account ( the account paying the transaction fee). If two transactions - * have the same transaction ID, they won't both have an effect. One will complete normally and the other will fail - * with a duplicate transaction status. - *

- * Normally, you should not use this method. Just before a transaction is executed, a transaction ID will be - * generated from the operator on the client. - * - * @param transactionId The TransactionId to be set - * @return {@code this} - * @see TransactionId - */ - public final T setTransactionId(TransactionId transactionId) { - requireNotFrozen(); - - transactionIds.setList(Collections.singletonList(transactionId)).setLocked(true); - - // noinspection unchecked - return (T) this; - } - - /** - * Should the transaction id be regenerated. - * - * @return should the transaction id be regenerated - */ - public final Boolean getRegenerateTransactionId() { - return regenerateTransactionId; - } - - /** - * Regenerate the transaction id. - * - * @param regenerateTransactionId should the transaction id be regenerated - * @return {@code this} - */ - public final T setRegenerateTransactionId(boolean regenerateTransactionId) { - this.regenerateTransactionId = regenerateTransactionId; - - // noinspection unchecked - return (T) this; - } - - /** - * Sign the transaction. - * - * @param privateKey the private key - * @return the signed transaction - */ - public final T sign(PrivateKey privateKey) { - return signWith(privateKey.getPublicKey(), privateKey::sign); - } - - /** - * Sign the transaction. - * - * @param publicKey the public key - * @param transactionSigner the key list - * @return {@code this} - */ - public T signWith(PublicKey publicKey, UnaryOperator transactionSigner) { - if (!isFrozen()) { - throw new IllegalStateException("Signing requires transaction to be frozen"); - } - - if (keyAlreadySigned(publicKey)) { - // noinspection unchecked - return (T) this; - } - - for (int i = 0; i < outerTransactions.size(); i++) { - outerTransactions.set(i, null); - } - publicKeys.add(publicKey); - signers.add(transactionSigner); - - // noinspection unchecked - return (T) this; - } - - /** - * Sign the transaction with the configured client. - * - * @param client the configured client - * @return the signed transaction - */ - public T signWithOperator(Client client) { - var operator = client.getOperator(); - - if (operator == null) { - throw new IllegalStateException( - "`client` must have an `operator` to sign with the operator"); - } - - if (!isFrozen()) { - freezeWith(client); - } - - return signWith(operator.publicKey, operator.transactionSigner); - } - - /** - * Checks if a public key is already added to the transaction - * - * @param key the public key - * @return if the public key is already added - */ - protected boolean keyAlreadySigned(PublicKey key) { - return publicKeys.contains(key); - } - - /** - * Add a signature to the transaction. - * - * @param publicKey the public key - * @param signature the signature - * @return {@code this} - */ - public T addSignature(PublicKey publicKey, byte[] signature) { - requireOneNodeAccountId(); - if (!isFrozen()) { - freeze(); - } - - if (keyAlreadySigned(publicKey)) { - // noinspection unchecked - return (T) this; - } - - transactionIds.setLocked(true); - nodeAccountIds.setLocked(true); - - for (int i = 0; i < outerTransactions.size(); i++) { - outerTransactions.set(i, null); - } - publicKeys.add(publicKey); - signers.add(null); - sigPairLists.get(0).addSigPair(publicKey.toSignaturePairProtobuf(signature)); - - // noinspection unchecked - return (T) this; - } - - protected Map> getSignaturesAtOffset(int offset) { - var map = new HashMap>(nodeAccountIds.size()); - - for (int i = 0; i < nodeAccountIds.size(); i++) { - var sigMap = sigPairLists.get(i + offset); - var nodeAccountId = nodeAccountIds.get(i); - - var keyMap = map.containsKey(nodeAccountId) ? - Objects.requireNonNull(map.get(nodeAccountId)) : - new HashMap(sigMap.getSigPairCount()); - map.put(nodeAccountId, keyMap); - - for (var sigPair : sigMap.getSigPairList()) { - keyMap.put( - PublicKey.fromBytes(sigPair.getPubKeyPrefix().toByteArray()), - sigPair.getEd25519().toByteArray() - ); - } - } - - return map; - } - - /** - * Extract list of account id and public keys. - * - * @return the list of account id and public keys - */ - public Map> getSignatures() { - if (!isFrozen()) { - throw new IllegalStateException("Transaction must be frozen in order to have signatures."); - } - - if (publicKeys.isEmpty()) { - return Collections.emptyMap(); - } - - buildAllTransactions(); - - return getSignaturesAtOffset(0); - } - - /** - * Check if transaction is frozen. - * - * @return is the transaction frozen - */ - protected boolean isFrozen() { - return frozenBodyBuilder != null; - } - - /** - * Throw an exception if the transaction is frozen. - */ - protected void requireNotFrozen() { - if (isFrozen()) { - throw new IllegalStateException( - "transaction is immutable; it has at least one signature or has been explicitly frozen"); - } - } - - /** - * Throw an exception if there is not exactly one node id set. - */ - protected void requireOneNodeAccountId() { - if (nodeAccountIds.size() != 1) { - throw new IllegalStateException("transaction did not have exactly one node ID set"); - } - } - - protected TransactionBody.Builder spawnBodyBuilder(@Nullable Client client) { - var clientDefaultFee = client != null ? client.getDefaultMaxTransactionFee() : null; - - var defaultFee = clientDefaultFee != null ? clientDefaultFee : defaultMaxTransactionFee; - - var feeHbars = maxTransactionFee != null ? maxTransactionFee : defaultFee; - - return TransactionBody.newBuilder() - .setTransactionFee(feeHbars.toTinybars()) - .setTransactionValidDuration(DurationConverter.toProtobuf(transactionValidDuration).toBuilder()) - .setMemo(memo); - } - - /** - * Freeze this transaction from further modification to prepare for signing or serialization. - * - * @return {@code this} - */ - public T freeze() { - return freezeWith(null); - } - - /** - * Freeze this transaction from further modification to prepare for signing or serialization. - *

- * Will use the `Client`, if available, to generate a default Transaction ID and select 1/3 nodes to prepare this - * transaction for. - * - * @param client the configured client - * @return {@code this} - */ - public T freezeWith(@Nullable Client client) { - if (isFrozen()) { - // noinspection unchecked - return (T) this; - } - - if (transactionIds.isEmpty()) { - if (client != null) { - var operator = client.getOperator(); - - if (operator != null) { - // Set a default transaction ID, generated from the operator account ID - - transactionIds.setList(Collections.singletonList(TransactionId.generate(operator.accountId))); - } else { - // no client means there must be an explicitly set node ID and transaction ID - throw new IllegalStateException( - "`client` must have an `operator` or `transactionId` must be set"); - } - } else { - throw new IllegalStateException( - "Transaction ID must be set, or operator must be provided via freezeWith()"); - } - } - - if (nodeAccountIds.isEmpty()) { - if (client == null) { - throw new IllegalStateException( - "`client` must be provided or both `nodeId` and `transactionId` must be set"); - } - - try { - nodeAccountIds.setList(client.network.getNodeAccountIdsForExecute()); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - - frozenBodyBuilder = spawnBodyBuilder(client).setTransactionID(transactionIds.get(0).toProtobuf()); - onFreeze(frozenBodyBuilder); - - int requiredChunks = getRequiredChunks(); - generateTransactionIds(transactionIds.get(0), requiredChunks); - wipeTransactionLists(requiredChunks); - - var clientDefaultRegenerateTransactionId = client != null ? client.getDefaultRegenerateTransactionId() : null; - regenerateTransactionId = - regenerateTransactionId != null ? regenerateTransactionId : clientDefaultRegenerateTransactionId; - - // noinspection unchecked - return (T) this; - } - - /** - * There must be at least one chunk. - * - * @return there is 1 required chunk - */ - int getRequiredChunks() { - return 1; - } - - /** - * Generate transaction id's. - * - * @param initialTransactionId the initial transaction id - * @param count the number of id's to generate. - */ - void generateTransactionIds(TransactionId initialTransactionId, int count) { - var locked = transactionIds.isLocked(); - transactionIds.setLocked(false); - - if (count == 1) { - transactionIds.setList(Collections.singletonList(initialTransactionId)); - return; - } - - var nextTransactionId = initialTransactionId.toProtobuf().toBuilder(); - transactionIds.ensureCapacity(count); - transactionIds.clear(); - for (int i = 0; i < count; i++) { - transactionIds.add(TransactionId.fromProtobuf(nextTransactionId.build())); - - // add 1 ns to the validStart to make cascading transaction IDs - var nextValidStart = nextTransactionId.getTransactionValidStart().toBuilder(); - nextValidStart.setNanos(nextValidStart.getNanos() + 1); - - nextTransactionId.setTransactionValidStart(nextValidStart); - } - - transactionIds.setLocked(locked); - } - - /** - * Wipe / reset the transaction list. - * - * @param requiredChunks the number of required chunks - */ - void wipeTransactionLists(int requiredChunks) { - if (!transactionIds.isEmpty()) { - Objects.requireNonNull(frozenBodyBuilder).setTransactionID(getTransactionIdInternal().toProtobuf()); - } - - outerTransactions = new ArrayList<>(nodeAccountIds.size()); - sigPairLists = new ArrayList<>(nodeAccountIds.size()); - innerSignedTransactions = new ArrayList<>(nodeAccountIds.size()); - - for (AccountId nodeId : nodeAccountIds) { - sigPairLists.add(SignatureMap.newBuilder()); - innerSignedTransactions.add(SignedTransaction.newBuilder() - .setBodyBytes(Objects.requireNonNull(frozenBodyBuilder) - .setNodeAccountID(nodeId.toProtobuf()) - .build() - .toByteString() - )); - outerTransactions.add(null); - } - } - - /** - * Build all the transactions. - */ - void buildAllTransactions() { - transactionIds.setLocked(true); - nodeAccountIds.setLocked(true); - - for (var i = 0; i < innerSignedTransactions.size(); ++i) { - buildTransaction(i); - } - } - - /** - * Will build the specific transaction at {@code index} This function is only ever called after the transaction is - * frozen. - * - * @param index the index of the transaction to be built - */ - void buildTransaction(int index) { - // Check if transaction is already built. - // Every time a signer is added via sign() or signWith(), all outerTransactions are nullified. - if ( - outerTransactions.get(index) != null && - !outerTransactions.get(index).getSignedTransactionBytes().isEmpty() - ) { - return; - } - - signTransaction(index); - - outerTransactions.set(index, com.hedera.hashgraph.sdk.proto.Transaction.newBuilder() - .setSignedTransactionBytes( - innerSignedTransactions.get(index) - .setSigMap(sigPairLists.get(index)) - .build() - .toByteString() - ).build()); - } - - /** - * Will sign the specific transaction at {@code index} This function is only ever called after the transaction is - * frozen. - * - * @param index the index of the transaction to sign - */ - void signTransaction(int index) { - var bodyBytes = innerSignedTransactions.get(index).getBodyBytes().toByteArray(); - var thisSigPairList = sigPairLists.get(index).getSigPairList(); - - for (var i = 0; i < publicKeys.size(); i++) { - if (signers.get(i) == null) { - continue; - } - if (publicKeyIsInSigPairList(ByteString.copyFrom(publicKeys.get(i).toBytesRaw()), thisSigPairList)) { - continue; - } - - var signatureBytes = signers.get(i).apply(bodyBytes); - - sigPairLists - .get(index) - .addSigPair(publicKeys.get(i).toSignaturePairProtobuf(signatureBytes)); - } - } - - /** - * Called in {@link #freezeWith(Client)} just before the transaction body is built. The intent is for the derived - * class to assign their data variant to the transaction body. - */ - abstract void onFreeze(TransactionBody.Builder bodyBuilder); - - /** - * Called in {@link #schedule()} when converting transaction into a scheduled version. - */ - abstract void onScheduled(SchedulableTransactionBody.Builder scheduled); - - @Override - final com.hedera.hashgraph.sdk.proto.Transaction makeRequest() { - var index = nodeAccountIds.getIndex() + (transactionIds.getIndex() * nodeAccountIds.size()); - - buildTransaction(index); - - return outerTransactions.get(index); - } - - @Override - TransactionResponse mapResponse( - com.hedera.hashgraph.sdk.proto.TransactionResponse transactionResponse, - AccountId nodeId, - com.hedera.hashgraph.sdk.proto.Transaction request - ) { - var transactionId = Objects.requireNonNull(getTransactionIdInternal()); - var hash = hash(request.getSignedTransactionBytes().toByteArray()); - transactionIds.advance(); - return new TransactionResponse(nodeId, transactionId, hash, null); - } - - @Override - final Status mapResponseStatus(com.hedera.hashgraph.sdk.proto.TransactionResponse transactionResponse) { - return Status.valueOf(transactionResponse.getNodeTransactionPrecheckCode()); - } - - abstract void validateChecksums(Client client) throws BadEntityIdException; - - /** - * Prepare the transactions to be executed. - * - * @param client the configured client - */ - void onExecute(Client client) { - if (!isFrozen()) { - freezeWith(client); - } - - var accountId = Objects.requireNonNull(Objects.requireNonNull(transactionIds.get(0)).accountId); - - if (client.isAutoValidateChecksumsEnabled()) { - try { - accountId.validateChecksum(client); - validateChecksums(client); - } catch (BadEntityIdException exc) { - throw new IllegalArgumentException(exc.getMessage()); - } - } - - var operatorId = client.getOperatorAccountId(); - if (operatorId != null && operatorId.equals(accountId)) { - // on execute, sign each transaction with the operator, if present - // and we are signing a transaction that used the default transaction ID - signWithOperator(client); - } - } - - @Override - CompletableFuture onExecuteAsync(Client client) { - onExecute(client); - return CompletableFuture.completedFuture(null); - } - - @Override - ExecutionState getExecutionState(Status status, com.hedera.hashgraph.sdk.proto.TransactionResponse response) { - if (status == Status.TRANSACTION_EXPIRED) { - if ((regenerateTransactionId != null && !regenerateTransactionId) || transactionIds.isLocked()) { - return ExecutionState.REQUEST_ERROR; - } else { - var firstTransactionId = Objects.requireNonNull(transactionIds.get(0)); - var accountId = Objects.requireNonNull(firstTransactionId.accountId); - generateTransactionIds(TransactionId.generate(accountId), transactionIds.size()); - wipeTransactionLists(transactionIds.size()); - return ExecutionState.RETRY; - } - } - return super.getExecutionState(status, response); - } - - @Override - @SuppressWarnings("LiteProtoToString") - public String toString() { - // NOTE: regex is for removing the instance address from the default debug output - TransactionBody.Builder body = spawnBodyBuilder(null); - - if (!transactionIds.isEmpty()) { - body.setTransactionID(transactionIds.get(0).toProtobuf()); - } - if (!nodeAccountIds.isEmpty()) { - body.setNodeAccountID(nodeAccountIds.get(0).toProtobuf()); - } - - onFreeze(body); - - return body.buildPartial().toString().replaceAll("@[A-Za-z0-9]+", ""); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TransactionFeeSchedule.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TransactionFeeSchedule.java deleted file mode 100644 index a5b893ee8d..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TransactionFeeSchedule.java +++ /dev/null @@ -1,196 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.google.protobuf.InvalidProtocolBufferException; - -import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; - -/** - * The fees for a specific transaction or query based on the fee data. - * - * See Hedera Documentation - */ -public class TransactionFeeSchedule implements Cloneable { - private RequestType requestType; - @Nullable - private FeeData feeData; - private List fees; - - /** - * Constructor. - */ - public TransactionFeeSchedule() { - requestType = RequestType.NONE; - feeData = null; - fees = new ArrayList<>(); - } - - /** - * Create a transaction fee schedule object from a protobuf. - * - * @param transactionFeeSchedule the protobuf - * @return the new transaction fee schedule - */ - static TransactionFeeSchedule fromProtobuf(com.hedera.hashgraph.sdk.proto.TransactionFeeSchedule transactionFeeSchedule) { - var returnFeeSchedule = new TransactionFeeSchedule() - .setRequestType(RequestType.valueOf(transactionFeeSchedule.getHederaFunctionality())) - .setFeeData(transactionFeeSchedule.hasFeeData() ? FeeData.fromProtobuf(transactionFeeSchedule.getFeeData()) : null); - for (var feeData : transactionFeeSchedule.getFeesList()) { - returnFeeSchedule.addFee(FeeData.fromProtobuf(feeData)); - } - return returnFeeSchedule; - } - - /** - * Create a transaction fee schedule object from a byte array. - * - * @param bytes the byte array - * @return the new transaction fee schedule - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - public static TransactionFeeSchedule fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return fromProtobuf(com.hedera.hashgraph.sdk.proto.TransactionFeeSchedule.parseFrom(bytes).toBuilder().build()); - } - - /** - * Extract the request type. - * - * @return the request type - */ - public RequestType getRequestType() { - return requestType; - } - - /** - * Assign the request type. - * - * @param requestType the request type - * @return {@code this} - */ - public TransactionFeeSchedule setRequestType(RequestType requestType) { - this.requestType = requestType; - return this; - } - - /** - * Get the total fee charged for a transaction - * - * @return the feeData - */ - @Deprecated - @Nullable - public FeeData getFeeData() { - return feeData; - } - - /** - * Set the total fee charged for a transaction - * - * @param feeData the feeData to set - * @return {@code this} - */ - @Deprecated - public TransactionFeeSchedule setFeeData(@Nullable FeeData feeData) { - this.feeData = feeData; - return this; - } - - /** - * Extract the list of fee's. - * - * @return the list of fee's - */ - public List getFees() { - return Collections.unmodifiableList(fees); - } - - /** - * Add a fee to the schedule. - * - * @param fee the fee to add - * @return {@code this} - */ - public TransactionFeeSchedule addFee(FeeData fee) { - fees.add(Objects.requireNonNull(fee)); - return this; - } - - /** - * Build the transaction body. - * - * @return {@link - * com.hedera.hashgraph.sdk.proto.TransactionFeeSchedule} - */ - com.hedera.hashgraph.sdk.proto.TransactionFeeSchedule toProtobuf() { - var returnBuilder = com.hedera.hashgraph.sdk.proto.TransactionFeeSchedule.newBuilder() - .setHederaFunctionality(getRequestType().code); - if (feeData != null) { - returnBuilder.setFeeData(feeData.toProtobuf()); - } - for (var fee : fees) { - returnBuilder.addFees(fee.toProtobuf()); - } - return returnBuilder.build(); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("requestType", getRequestType()) - .add("feeData", getFeeData()) - .add("fees", getFees()) - .toString(); - } - - /** - * Create the byte array. - * - * @return the byte array representation - */ - public byte[] toBytes() { - return toProtobuf().toByteArray(); - } - - List cloneFees() { - List cloneFees = new ArrayList<>(fees.size()); - for (var fee : fees) { - cloneFees.add(fee.clone()); - } - return cloneFees; - } - - @Override - public TransactionFeeSchedule clone() { - try { - TransactionFeeSchedule clone = (TransactionFeeSchedule) super.clone(); - clone.feeData = feeData != null ? feeData.clone() : null; - clone.fees = fees != null ? cloneFees() : null; - return clone; - } catch (CloneNotSupportedException e) { - throw new AssertionError(); - } - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TransactionReceipt.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TransactionReceipt.java deleted file mode 100644 index 3410ad2e26..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TransactionReceipt.java +++ /dev/null @@ -1,412 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.ExchangeRateSet; -import com.hedera.hashgraph.sdk.proto.TimestampSeconds; -import org.bouncycastle.util.encoders.Hex; - -import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.List; - -/** - * The consensus result for a transaction, which might not be currently - * known, or may succeed or fail. - */ -public final class TransactionReceipt { - - /** - * The transaction's ID - */ - @Nullable - public final TransactionId transactionId; - /** - * Whether the transaction succeeded or failed (or is unknown). - */ - public final Status status; - - /** - * The exchange rate of Hbars to cents (USD). - */ - public final ExchangeRate exchangeRate; - - /** - * The account ID, if a new account was created. - */ - @Nullable - public final AccountId accountId; - - /** - * The file ID, if a new file was created. - */ - @Nullable - public final FileId fileId; - - /** - * The contract ID, if a new contract was created. - */ - @Nullable - public final ContractId contractId; - - /** - * The topic ID, if a new topic was created. - */ - @Nullable - public final TopicId topicId; - - /** - * The token ID, if a new token was created. - */ - @Nullable - public final TokenId tokenId; - - /** - * Updated sequence number for a consensus service topic. - * Set for {@link TopicMessageSubmitTransaction}. - */ - @Nullable - public final Long topicSequenceNumber; - - /** - * Updated running hash for a consensus service topic. - * Set for {@link TopicMessageSubmitTransaction}. - */ - @Nullable - public final ByteString topicRunningHash; - - /** - * In the receipt of TokenMint, TokenWipe, TokenBurn, For fungible tokens - the current total - * supply of this token. For non fungible tokens - the total number of NFTs issued for a given - * tokenID - */ - public final Long totalSupply; - - /** - * In the receipt of a ScheduleCreate, the id of the newly created Scheduled Entity - */ - @Nullable - public final ScheduleId scheduleId; - - /** - * In the receipt of a ScheduleCreate or ScheduleSign that resolves to SUCCESS, the - * TransactionID that should be used to query for the receipt or record of the relevant - * scheduled transaction - */ - @Nullable - public final TransactionId scheduledTransactionId; - - /** - * In the receipt of a TokenMint for tokens of type NON_FUNGIBLE_UNIQUE, the serial numbers of - * the newly created NFTs - */ - public final List serials; - - /** - * In the receipt of a NodeCreate, NodeUpdate, NodeDelete, the id of the newly created node. - * An affected node identifier.
- * This value SHALL be set following a `createNode` transaction.
- * This value SHALL be set following a `updateNode` transaction.
- * This value SHALL be set following a `deleteNode` transaction.
- * This value SHALL NOT be set following any other transaction. - */ - public final long nodeId; - - /** - * The receipts of processing all transactions with the given id, in consensus time order. - */ - public final List duplicates; - - /** - * The receipts (if any) of all child transactions spawned by the transaction with the - * given top-level id, in consensus order. Always empty if the top-level status is UNKNOWN. - */ - public final List children; - - TransactionReceipt( - @Nullable TransactionId transactionId, - Status status, - ExchangeRate exchangeRate, - @Nullable AccountId accountId, - @Nullable FileId fileId, - @Nullable ContractId contractId, - @Nullable TopicId topicId, - @Nullable TokenId tokenId, - @Nullable Long topicSequenceNumber, - @Nullable ByteString topicRunningHash, - Long totalSupply, - @Nullable ScheduleId scheduleId, - @Nullable TransactionId scheduledTransactionId, - List serials, - long nodeId, - List duplicates, - List children - ) { - this.transactionId = transactionId; - this.status = status; - this.exchangeRate = exchangeRate; - this.accountId = accountId; - this.fileId = fileId; - this.contractId = contractId; - this.topicId = topicId; - this.tokenId = tokenId; - this.topicSequenceNumber = topicSequenceNumber; - this.topicRunningHash = topicRunningHash; - this.totalSupply = totalSupply; - this.scheduleId = scheduleId; - this.scheduledTransactionId = scheduledTransactionId; - this.serials = serials; - this.nodeId = nodeId; - this.duplicates = duplicates; - this.children = children; - } - - /** - * Create transaction receipt from protobuf. - * - * @param transactionReceipt the protobuf - * @param duplicates list of duplicates - * @param children list of children - * @return the new transaction receipt - */ - static TransactionReceipt fromProtobuf( - com.hedera.hashgraph.sdk.proto.TransactionReceipt transactionReceipt, - List duplicates, - List children, - @Nullable TransactionId transactionId - ) { - var status = Status.valueOf(transactionReceipt.getStatus()); - - var rate = transactionReceipt.getExchangeRate(); - var exchangeRate = ExchangeRate.fromProtobuf(rate.getCurrentRate()); - - var accountId = - transactionReceipt.hasAccountID() - ? AccountId.fromProtobuf(transactionReceipt.getAccountID()) - : null; - - var fileId = - transactionReceipt.hasFileID() - ? FileId.fromProtobuf(transactionReceipt.getFileID()) - : null; - - var contractId = - transactionReceipt.hasContractID() - ? ContractId.fromProtobuf(transactionReceipt.getContractID()) - : null; - - var topicId = - transactionReceipt.hasTopicID() - ? TopicId.fromProtobuf(transactionReceipt.getTopicID()) - : null; - - var tokenId = - transactionReceipt.hasTokenID() - ? TokenId.fromProtobuf(transactionReceipt.getTokenID()) - : null; - - var topicSequenceNumber = - transactionReceipt.getTopicSequenceNumber() == 0 - ? null - : transactionReceipt.getTopicSequenceNumber(); - - var topicRunningHash = - transactionReceipt.getTopicRunningHash().isEmpty() - ? null - : transactionReceipt.getTopicRunningHash(); - - var totalSupply = transactionReceipt.getNewTotalSupply(); - - var scheduleId = - transactionReceipt.hasScheduleID() - ? ScheduleId.fromProtobuf(transactionReceipt.getScheduleID()) - : null; - - var scheduledTransactionId = - transactionReceipt.hasScheduledTransactionID() - ? TransactionId.fromProtobuf(transactionReceipt.getScheduledTransactionID()) - : null; - - var serials = transactionReceipt.getSerialNumbersList(); - - var nodeId = transactionReceipt.getNodeId(); - - return new TransactionReceipt( - transactionId, - status, - exchangeRate, - accountId, - fileId, - contractId, - topicId, - tokenId, - topicSequenceNumber, - topicRunningHash, - totalSupply, - scheduleId, - scheduledTransactionId, - serials, - nodeId, - duplicates, - children - ); - } - - /** - * Create a transaction receipt from a protobuf. - * - * @param transactionReceipt the protobuf - * @return the new transaction receipt - */ - public static TransactionReceipt fromProtobuf(com.hedera.hashgraph.sdk.proto.TransactionReceipt transactionReceipt) { - return fromProtobuf(transactionReceipt, new ArrayList<>(), new ArrayList<>(), null); - } - - static TransactionReceipt fromProtobuf( - com.hedera.hashgraph.sdk.proto.TransactionReceipt transactionReceipt, - @Nullable TransactionId transactionId - ) { - return fromProtobuf(transactionReceipt, new ArrayList<>(), new ArrayList<>(), transactionId); - } - - /** - * Create a transaction receipt from a byte array. - * - * @param bytes the byte array - * @return the new transaction receipt - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - public static TransactionReceipt fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return fromProtobuf(com.hedera.hashgraph.sdk.proto.TransactionReceipt.parseFrom(bytes).toBuilder().build()); - } - - /** - * Validate the transaction status in the receipt. - * - * @param shouldValidate Whether to perform transaction status validation - * @return {@code this} - * @throws ReceiptStatusException when shouldValidate is true and the transaction status is not SUCCESS - */ - public TransactionReceipt validateStatus(boolean shouldValidate) throws ReceiptStatusException { - if (shouldValidate && status != Status.SUCCESS && status != Status.FEE_SCHEDULE_FILE_PART_UPLOADED) { - throw new ReceiptStatusException(transactionId, this); - } - return this; - } - - /** - * Create the protobuf. - * - * @return the protobuf representation - */ - com.hedera.hashgraph.sdk.proto.TransactionReceipt toProtobuf() { - var transactionReceiptBuilder = com.hedera.hashgraph.sdk.proto.TransactionReceipt.newBuilder() - .setStatus(status.code) - .setExchangeRate(ExchangeRateSet.newBuilder() - .setCurrentRate(com.hedera.hashgraph.sdk.proto.ExchangeRate.newBuilder() - .setHbarEquiv(exchangeRate.hbars) - .setCentEquiv(exchangeRate.cents) - .setExpirationTime(TimestampSeconds.newBuilder() - .setSeconds(exchangeRate.expirationTime.getEpochSecond()) - ) - ) - ) - .setNewTotalSupply(totalSupply); - - if (accountId != null) { - transactionReceiptBuilder.setAccountID(accountId.toProtobuf()); - } - - if (fileId != null) { - transactionReceiptBuilder.setFileID(fileId.toProtobuf()); - } - - if (contractId != null) { - transactionReceiptBuilder.setContractID(contractId.toProtobuf()); - } - - if (topicId != null) { - transactionReceiptBuilder.setTopicID(topicId.toProtobuf()); - } - - if (tokenId != null) { - transactionReceiptBuilder.setTokenID(tokenId.toProtobuf()); - } - - if (topicSequenceNumber != null) { - transactionReceiptBuilder.setTopicSequenceNumber(topicSequenceNumber); - } - - if (topicRunningHash != null) { - transactionReceiptBuilder.setTopicRunningHash(topicRunningHash); - } - - if (scheduleId != null) { - transactionReceiptBuilder.setScheduleID(scheduleId.toProtobuf()); - } - - if (scheduledTransactionId != null) { - transactionReceiptBuilder.setScheduledTransactionID(scheduledTransactionId.toProtobuf()); - } - - for (var serial : serials) { - transactionReceiptBuilder.addSerialNumbers(serial); - } - - transactionReceiptBuilder.setNodeId(nodeId); - - return transactionReceiptBuilder.build(); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("transactionId", transactionId) - .add("status", status) - .add("exchangeRate", exchangeRate) - .add("accountId", accountId) - .add("fileId", fileId) - .add("contractId", contractId) - .add("topicId", topicId) - .add("tokenId", tokenId) - .add("topicSequenceNumber", topicSequenceNumber) - .add("topicRunningHash", topicRunningHash != null ? Hex.encode(topicRunningHash.toByteArray()) : null) - .add("totalSupply", totalSupply) - .add("scheduleId", scheduleId) - .add("scheduledTransactionId", scheduledTransactionId) - .add("serials", serials) - .add("nodeId", nodeId) - .add("duplicates", duplicates) - .add("children", children) - .toString(); - } - - /** - * Create the byte array. - * - * @return the byte array representation - */ - public byte[] toBytes() { - return toProtobuf().toByteArray(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TransactionReceiptQuery.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TransactionReceiptQuery.java deleted file mode 100644 index 903ca74ae5..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TransactionReceiptQuery.java +++ /dev/null @@ -1,227 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.CryptoServiceGrpc; -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import com.hedera.hashgraph.sdk.proto.Response; -import com.hedera.hashgraph.sdk.proto.ResponseHeader; -import com.hedera.hashgraph.sdk.proto.TransactionGetReceiptQuery; -import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -/** - * Get the receipt of a transaction, given its transaction ID. - * - *

Once a transaction reaches consensus, then information about whether it succeeded or failed - * will be available until the end of the receipt period. - * - *

This query is free. - */ -public final class TransactionReceiptQuery - extends Query { - - @Nullable - private TransactionId transactionId = null; - private boolean includeChildren = false; - private boolean includeDuplicates = false; - - /** - * Constructor. - */ - public TransactionReceiptQuery() { - } - - /** - * Extract the transaction id. - * - * @return the transaction id - */ - @Override - @Nullable - public TransactionId getTransactionIdInternal() { - return transactionId; - } - - /** - * Set the ID of the transaction for which the receipt is being requested. - * - * @param transactionId The TransactionId to be set - * @return {@code this} - */ - public TransactionReceiptQuery setTransactionId(TransactionId transactionId) { - Objects.requireNonNull(transactionId); - this.transactionId = transactionId; - return this; - } - - /** - * Should the children be included? - * - * @return should children be included - */ - public boolean getIncludeChildren() { - return includeChildren; - } - - /** - * Whether the response should include the records of any child transactions spawned by the - * top-level transaction with the given transactionID. - * - * @param value The value that includeChildren should be set to; true to include children, false to exclude - * @return {@code this} - */ - public TransactionReceiptQuery setIncludeChildren(boolean value) { - includeChildren = value; - return this; - } - - /** - * Should duplicates be included? - * - * @return should duplicates be included - */ - public boolean getIncludeDuplicates() { - return includeDuplicates; - } - - /** - * Whether records of processing duplicate transactions should be returned along with the record - * of processing the first consensus transaction with the given id whose status was neither - * INVALID_NODE_ACCOUNT nor INVALID_PAYER_SIGNATURE or, if no such - * record exists, the record of processing the first transaction to reach consensus with the - * given transaction id. - * - * @param value The value that includeDuplicates should be set to; true to include duplicates, false to exclude - * @return {@code this} - */ - public TransactionReceiptQuery setIncludeDuplicates(boolean value) { - includeDuplicates = value; - return this; - } - - @Override - boolean isPaymentRequired() { - return false; - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - if (transactionId != null) { - Objects.requireNonNull(transactionId.accountId).validateChecksum(client); - } - } - - @Override - void onMakeRequest(com.hedera.hashgraph.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { - var builder = TransactionGetReceiptQuery.newBuilder() - .setIncludeChildReceipts(includeChildren) - .setIncludeDuplicates(includeDuplicates); - if (transactionId != null) { - builder.setTransactionID(transactionId.toProtobuf()); - } - - queryBuilder.setTransactionGetReceipt(builder.setHeader(header)); - } - - @Override - Status mapResponseStatus(Response response) { - var preCheckCode = response.getTransactionGetReceipt().getHeader().getNodeTransactionPrecheckCode(); - - return Status.valueOf(preCheckCode); - } - - @Override - TransactionReceipt mapResponse(Response response, AccountId nodeId, com.hedera.hashgraph.sdk.proto.Query request) { - var receiptResponse = response.getTransactionGetReceipt(); - var duplicates = mapReceiptList(receiptResponse.getDuplicateTransactionReceiptsList()); - var children = mapReceiptList(receiptResponse.getChildTransactionReceiptsList()); - return TransactionReceipt.fromProtobuf(response.getTransactionGetReceipt().getReceipt(), duplicates, children, transactionId); - } - - /** - * Create a list of transaction receipts from a protobuf. - * - * @param protoReceiptList the protobuf - * @return the list of transaction receipts - */ - private static List mapReceiptList( - List protoReceiptList - ) { - List outList = new ArrayList<>(protoReceiptList.size()); - for (var protoReceipt : protoReceiptList) { - outList.add(TransactionReceipt.fromProtobuf(protoReceipt)); - } - return outList; - } - - @Override - QueryHeader mapRequestHeader(com.hedera.hashgraph.sdk.proto.Query request) { - return request.getTransactionGetReceipt().getHeader(); - } - - @Override - ResponseHeader mapResponseHeader(Response response) { - return response.getTransactionGetReceipt().getHeader(); - } - - @Override - MethodDescriptor getMethodDescriptor() { - return CryptoServiceGrpc.getGetTransactionReceiptsMethod(); - } - - @Override - ExecutionState getExecutionState(Status status, Response response) { - switch (status) { - case BUSY: - case UNKNOWN: - case RECEIPT_NOT_FOUND: - case RECORD_NOT_FOUND: - case PLATFORM_NOT_ACTIVE: - return ExecutionState.RETRY; - - case OK: - break; - - default: - return ExecutionState.REQUEST_ERROR; - } - - var receiptStatus = - Status.valueOf(response.getTransactionGetReceipt().getReceipt().getStatus()); - - switch (receiptStatus) { - case BUSY: - case UNKNOWN: - case OK: - case RECEIPT_NOT_FOUND: - case RECORD_NOT_FOUND: - case PLATFORM_NOT_ACTIVE: - return ExecutionState.RETRY; - - default: - return ExecutionState.SUCCESS; - } - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TransactionRecord.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TransactionRecord.java deleted file mode 100644 index 5b6559d477..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TransactionRecord.java +++ /dev/null @@ -1,530 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.AccountAmount; -import com.hedera.hashgraph.sdk.proto.NftTransfer; -import com.hedera.hashgraph.sdk.proto.TokenTransferList; -import com.hedera.hashgraph.sdk.proto.TransferList; -import org.bouncycastle.util.encoders.Hex; -import java.time.Instant; - -import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * The complete record for a transaction on Hedera that has reached consensus. - *

- * This is not-free to request and is available for 1 hour after a transaction reaches consensus. - *

- * A {@link TransactionReceipt} can be thought of as a light-weight record which is free to ask for if you just - * need what it contains. A receipt however lasts for only 180 seconds. - */ -public final class TransactionRecord { - /** - * The status (reach consensus, or failed, or is unknown) and the ID of - * any new account/file/instance created. - */ - public final TransactionReceipt receipt; - - /** - * The hash of the Transaction that executed (not the hash of any Transaction that failed for - * having a duplicate TransactionID). - */ - public final ByteString transactionHash; - - /** - * The consensus timestamp (or null if didn't reach consensus yet). - */ - public final Instant consensusTimestamp; - - /** - * The ID of the transaction this record represents. - */ - public final TransactionId transactionId; - - /** - * The memo that was submitted as part of the transaction (max 100 bytes). - */ - public final String transactionMemo; - - /** - * The actual transaction fee charged, not the original - * transactionFee value from TransactionBody. - */ - public final Hbar transactionFee; - - /** - * Record of the value returned by the smart contract - * function or constructor. - */ - @Nullable - public final ContractFunctionResult contractFunctionResult; - - /** - * All hbar transfers as a result of this transaction, such as fees, or - * transfers performed by the transaction, or by a smart contract it calls, - * or by the creation of threshold records that it triggers. - */ - public final List transfers; - - /** - * All fungible token transfers as a result of this transaction as a map - */ - public final Map> tokenTransfers; - - /** - * All fungible token transfers as a result of this transaction as a list - */ - public final List tokenTransferList; - - /** - * All NFT Token transfers as a result of this transaction - */ - public final Map> tokenNftTransfers; - - /** - * Reference to the scheduled transaction ID that this transaction record represents - */ - @Nullable - public final ScheduleId scheduleRef; - - /** - * All custom fees that were assessed during a CryptoTransfer, and must be paid if the - * transaction status resolved to SUCCESS - */ - public final List assessedCustomFees; - - /** - * All token associations implicitly created while handling this transaction - */ - public final List automaticTokenAssociations; - - /** - * In the record of an internal CryptoCreate transaction triggered by a user - * transaction with a (previously unused) alias, the new account's alias. - */ - @Nullable - public final PublicKey aliasKey; - - /** - * The records of processing all child transaction spawned by the transaction with the given - * top-level id, in consensus order. Always empty if the top-level status is UNKNOWN. - */ - public final List children; - - /** - * The records of processing all consensus transaction with the same id as the distinguished - * record above, in chronological order. - */ - public final List duplicates; - - /** - * In the record of an internal transaction, the consensus timestamp of the user - * transaction that spawned it. - */ - @Nullable - public final Instant parentConsensusTimestamp; - - /** - * The keccak256 hash of the ethereumData. This field will only be populated for - * EthereumTransaction. - */ - public final ByteString ethereumHash; - - /** - * An approved allowance of hbar transfers for a spender - */ - @Deprecated - public final List hbarAllowanceAdjustments; - - /** - * An approved allowance of token transfers for a spender - */ - @Deprecated - public final List tokenAllowanceAdjustments; - - /** - * An approved allowance of NFT transfers for a spender - */ - @Deprecated - public final List tokenNftAllowanceAdjustments; - - /** - * List of accounts with the corresponding staking rewards paid as a result of a transaction. - */ - public final List paidStakingRewards; - - /** - * In the record of a UtilPrng transaction with no output range, a pseudorandom 384-bit string. - */ - @Nullable - public final ByteString prngBytes; - - /** - * In the record of a PRNG transaction with an output range, the output of a PRNG whose input was a 384-bit string. - */ - @Nullable - public final Integer prngNumber; - - /** - * The new default EVM address of the account created by this transaction. - * This field is populated only when the EVM address is not specified in the related transaction body. - */ - public final ByteString evmAddress; - - /** - * A list of pending token airdrops. - * Each pending airdrop represents a single requested transfer from a - * sending account to a recipient account. These pending transfers are - * issued unilaterally by the sending account, and MUST be claimed by the - * recipient account before the transfer MAY complete. - * A sender MAY cancel a pending airdrop before it is claimed. - * An airdrop transaction SHALL emit a pending airdrop when the recipient has no - * available automatic association slots available or when the recipient - * has set `receiver_sig_required`. - */ - public final List pendingAirdropRecords; - - TransactionRecord( - TransactionReceipt transactionReceipt, - ByteString transactionHash, - Instant consensusTimestamp, - TransactionId transactionId, - String transactionMemo, - long transactionFee, - @Nullable ContractFunctionResult contractFunctionResult, - List transfers, - Map> tokenTransfers, - List tokenTransferList, - Map> tokenNftTransfers, - @Nullable ScheduleId scheduleRef, - List assessedCustomFees, - List automaticTokenAssociations, - @Nullable PublicKey aliasKey, - List children, - List duplicates, - @Nullable Instant parentConsensusTimestamp, - ByteString ethereumHash, - List paidStakingRewards, - @Nullable ByteString prngBytes, - @Nullable Integer prngNumber, - ByteString evmAddress, - List pendingAirdropRecords - ) { - this.receipt = transactionReceipt; - this.transactionHash = transactionHash; - this.consensusTimestamp = consensusTimestamp; - this.transactionMemo = transactionMemo; - this.transactionId = transactionId; - this.transfers = transfers; - this.contractFunctionResult = contractFunctionResult; - this.transactionFee = Hbar.fromTinybars(transactionFee); - this.tokenTransfers = tokenTransfers; - this.tokenTransferList = tokenTransferList; - this.tokenNftTransfers = tokenNftTransfers; - this.scheduleRef = scheduleRef; - this.assessedCustomFees = assessedCustomFees; - this.automaticTokenAssociations = automaticTokenAssociations; - this.aliasKey = aliasKey; - this.children = children; - this.duplicates = duplicates; - this.parentConsensusTimestamp = parentConsensusTimestamp; - this.ethereumHash = ethereumHash; - this.pendingAirdropRecords = pendingAirdropRecords; - this.hbarAllowanceAdjustments = Collections.emptyList(); - this.tokenAllowanceAdjustments = Collections.emptyList(); - this.tokenNftAllowanceAdjustments = Collections.emptyList(); - this.paidStakingRewards = paidStakingRewards; - this.prngBytes = prngBytes; - this.prngNumber = prngNumber; - this.evmAddress = evmAddress; - } - - /** - * Create a transaction record from a protobuf. - * - * @param transactionRecord the protobuf - * @param children the list of children - * @param duplicates the list of duplicates - * @return the new transaction record - */ - static TransactionRecord fromProtobuf( - com.hedera.hashgraph.sdk.proto.TransactionRecord transactionRecord, - List children, - List duplicates, - @Nullable TransactionId transactionId - ) { - var transfers = new ArrayList(transactionRecord.getTransferList().getAccountAmountsCount()); - for (var accountAmount : transactionRecord.getTransferList().getAccountAmountsList()) { - transfers.add(Transfer.fromProtobuf(accountAmount)); - } - - var tokenTransfers = new HashMap>(); - var tokenNftTransfers = new HashMap>(); - - var tokenTransfersList = TokenTransfer.fromProtobuf(transactionRecord.getTokenTransferListsList()); - var nftTransfersList = TokenNftTransfer.fromProtobuf(transactionRecord.getTokenTransferListsList()); - - for (var transfer : tokenTransfersList) { - var current = tokenTransfers.containsKey(transfer.tokenId) ? tokenTransfers.get(transfer.tokenId) : new HashMap(); - current.put(transfer.accountId, transfer.amount); - tokenTransfers.put(transfer.tokenId, current); - } - - for (var transfer : nftTransfersList) { - var current = tokenNftTransfers.containsKey(transfer.tokenId) ? tokenNftTransfers.get(transfer.tokenId) : new ArrayList(); - current.add(transfer); - tokenNftTransfers.put(transfer.tokenId, current); - } - - var fees = new ArrayList(transactionRecord.getAssessedCustomFeesCount()); - for (var fee : transactionRecord.getAssessedCustomFeesList()) { - fees.add(AssessedCustomFee.fromProtobuf(fee)); - } - - // HACK: This is a bit bad, any takers to clean this up - var contractFunctionResult = transactionRecord.hasContractCallResult() ? - new ContractFunctionResult(transactionRecord.getContractCallResult()) : - transactionRecord.hasContractCreateResult() ? - new ContractFunctionResult(transactionRecord.getContractCreateResult()) : - null; - - var automaticTokenAssociations = new ArrayList(transactionRecord.getAutomaticTokenAssociationsCount()); - for (var tokenAssociation : transactionRecord.getAutomaticTokenAssociationsList()) { - automaticTokenAssociations.add(TokenAssociation.fromProtobuf(tokenAssociation)); - } - - var aliasKey = PublicKey.fromAliasBytes(transactionRecord.getAlias()); - - var paidStakingRewards = new ArrayList(transactionRecord.getPaidStakingRewardsCount()); - for (var reward : transactionRecord.getPaidStakingRewardsList()) { - paidStakingRewards.add(Transfer.fromProtobuf(reward)); - } - - List pendingAirdropRecords = transactionRecord.getNewPendingAirdropsList() - .stream().map(PendingAirdropRecord::fromProtobuf) - .collect(Collectors.toList()); - - return new TransactionRecord( - TransactionReceipt.fromProtobuf(transactionRecord.getReceipt(), transactionId), - transactionRecord.getTransactionHash(), - InstantConverter.fromProtobuf(transactionRecord.getConsensusTimestamp()), - TransactionId.fromProtobuf(transactionRecord.getTransactionID()), - transactionRecord.getMemo(), - transactionRecord.getTransactionFee(), - contractFunctionResult, - transfers, - tokenTransfers, - tokenTransfersList, - tokenNftTransfers, - transactionRecord.hasScheduleRef() ? ScheduleId.fromProtobuf(transactionRecord.getScheduleRef()) : null, - fees, - automaticTokenAssociations, - aliasKey, - children, - duplicates, - transactionRecord.hasParentConsensusTimestamp() ? - InstantConverter.fromProtobuf(transactionRecord.getParentConsensusTimestamp()) : null, - transactionRecord.getEthereumHash(), - paidStakingRewards, - transactionRecord.hasPrngBytes() ? transactionRecord.getPrngBytes() : null, - transactionRecord.hasPrngNumber() ? transactionRecord.getPrngNumber() : null, - transactionRecord.getEvmAddress(), - pendingAirdropRecords - ); - } - - /** - * Create a transaction record from a protobuf. - * - * @param transactionRecord the protobuf - * @return the new transaction record - */ - static TransactionRecord fromProtobuf(com.hedera.hashgraph.sdk.proto.TransactionRecord transactionRecord) { - return fromProtobuf(transactionRecord, new ArrayList<>(), new ArrayList<>(), null); - } - - /** - * Create a transaction record from a byte array. - * - * @param bytes the byte array - * @return the new transaction record - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - public static TransactionRecord fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return fromProtobuf(com.hedera.hashgraph.sdk.proto.TransactionRecord.parseFrom(bytes).toBuilder().build()); - } - - /** - * Validate the transaction status in the receipt. - * - * @param shouldValidate Whether to perform transaction status validation - * @return {@code this} - * @throws ReceiptStatusException when shouldValidate is true and the transaction status is not SUCCESS - */ - public TransactionRecord validateReceiptStatus(boolean shouldValidate) throws ReceiptStatusException { - receipt.validateStatus(shouldValidate); - return this; - } - - /** - * Create the protobuf. - * - * @return the protobuf representation - */ - com.hedera.hashgraph.sdk.proto.TransactionRecord toProtobuf() { - var transferList = TransferList.newBuilder(); - for (Transfer transfer : transfers) { - transferList.addAccountAmounts(transfer.toProtobuf()); - } - - var transactionRecord = com.hedera.hashgraph.sdk.proto.TransactionRecord.newBuilder() - .setReceipt(receipt.toProtobuf()) - .setTransactionHash(transactionHash) - .setConsensusTimestamp(InstantConverter.toProtobuf(consensusTimestamp)) - .setTransactionID(transactionId.toProtobuf()) - .setMemo(transactionMemo) - .setTransactionFee(transactionFee.toTinybars()) - .setTransferList(transferList) - .setEthereumHash(ethereumHash) - .setEvmAddress(evmAddress); - - for (var tokenEntry : tokenTransfers.entrySet()) { - var tokenTransfersList = TokenTransferList.newBuilder() - .setToken(tokenEntry.getKey().toProtobuf()); - for (var aaEntry : tokenEntry.getValue().entrySet()) { - tokenTransfersList.addTransfers(AccountAmount.newBuilder() - .setAccountID(aaEntry.getKey().toProtobuf()) - .setAmount(aaEntry.getValue()).build() - ); - } - - transactionRecord.addTokenTransferLists(tokenTransfersList); - } - - for (var nftEntry : tokenNftTransfers.entrySet()) { - var nftTransferList = TokenTransferList.newBuilder() - .setToken(nftEntry.getKey().toProtobuf()); - for (var aaEntry : nftEntry.getValue()) { - nftTransferList.addNftTransfers( - NftTransfer.newBuilder() - .setSenderAccountID(aaEntry.sender.toProtobuf()) - .setReceiverAccountID(aaEntry.receiver.toProtobuf()) - .setSerialNumber(aaEntry.serial) - .setIsApproval(aaEntry.isApproved) - .build() - ); - } - - transactionRecord.addTokenTransferLists(nftTransferList); - } - - if (contractFunctionResult != null) { - transactionRecord.setContractCallResult(contractFunctionResult.toProtobuf()); - } - - if (scheduleRef != null) { - transactionRecord.setScheduleRef(scheduleRef.toProtobuf()); - } - - for (var fee : assessedCustomFees) { - transactionRecord.addAssessedCustomFees(fee.toProtobuf()); - } - - for (var tokenAssociation : automaticTokenAssociations) { - transactionRecord.addAutomaticTokenAssociations(tokenAssociation.toProtobuf()); - } - - if (aliasKey != null) { - transactionRecord.setAlias(aliasKey.toProtobufKey().toByteString()); - } - - if (parentConsensusTimestamp != null) { - transactionRecord.setParentConsensusTimestamp(InstantConverter.toProtobuf(parentConsensusTimestamp)); - } - - for (Transfer reward : paidStakingRewards) { - transactionRecord.addPaidStakingRewards(reward.toProtobuf()); - } - - if (prngBytes != null) { - transactionRecord.setPrngBytes(prngBytes); - } - - if (prngNumber != null) { - transactionRecord.setPrngNumber(prngNumber); - } - - if (pendingAirdropRecords != null) { - for (PendingAirdropRecord pendingAirdropRecord : pendingAirdropRecords) { - transactionRecord.addNewPendingAirdrops(pendingAirdropRecords.indexOf(pendingAirdropRecord), pendingAirdropRecord.toProtobuf()); - } - } - - return transactionRecord.build(); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("receipt", receipt) - .add("transactionHash", Hex.toHexString(transactionHash.toByteArray())) - .add("consensusTimestamp", consensusTimestamp) - .add("transactionId", transactionId) - .add("transactionMemo", transactionMemo) - .add("transactionFee", transactionFee) - .add("contractFunctionResult", contractFunctionResult) - .add("transfers", transfers) - .add("tokenTransfers", tokenTransfers) - .add("tokenNftTransfers", tokenNftTransfers) - .add("scheduleRef", scheduleRef) - .add("assessedCustomFees", assessedCustomFees) - .add("automaticTokenAssociations", automaticTokenAssociations) - .add("aliasKey", aliasKey) - .add("children", children) - .add("duplicates", duplicates) - .add("parentConsensusTimestamp", parentConsensusTimestamp) - .add("ethereumHash", Hex.toHexString(ethereumHash.toByteArray())) - .add("paidStakingRewards", paidStakingRewards) - .add("prngBytes", prngBytes != null ? Hex.toHexString(prngBytes.toByteArray()) : null) - .add("prngNumber", prngNumber) - .add("evmAddress", Hex.toHexString(evmAddress.toByteArray())) - .add("pendingAirdropRecords", pendingAirdropRecords.toString()) - .toString(); - } - - /** - * Create the byte array. - * - * @return the byte array representation - */ - public byte[] toBytes() { - return toProtobuf().toByteArray(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/Transfer.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/Transfer.java deleted file mode 100644 index c76d32f8ff..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/Transfer.java +++ /dev/null @@ -1,75 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.hedera.hashgraph.sdk.proto.AccountAmount; - -/** - * A transfer of Hbar that occurred within a transaction. - *

- * Returned with a {@link TransactionRecord}. - */ -public final class Transfer { - /** - * The Account ID that sends or receives crypto-currency. - */ - public final AccountId accountId; - - /** - * The amount that the account sends (negative) or receives (positive). - */ - public final Hbar amount; - - Transfer(AccountId accountId, Hbar amount) { - this.accountId = accountId; - this.amount = amount; - } - - /** - * Create a transfer from a protobuf. - * - * @param accountAmount the protobuf - * @return the new transfer - */ - static Transfer fromProtobuf(AccountAmount accountAmount) { - return new Transfer(AccountId.fromProtobuf(accountAmount.getAccountID()), Hbar.fromTinybars(accountAmount.getAmount())); - } - - /** - * Create the protobuf. - * - * @return the protobuf representation - */ - AccountAmount toProtobuf() { - return AccountAmount.newBuilder() - .setAccountID(accountId.toProtobuf()) - .setAmount(amount.toTinybars()) - .build(); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("accountId", accountId) - .add("amount", amount) - .toString(); - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TransferTransaction.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TransferTransaction.java deleted file mode 100644 index 25bac22f29..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TransferTransaction.java +++ /dev/null @@ -1,275 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.MoreObjects; -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.AccountAmount; -import com.hedera.hashgraph.sdk.proto.CryptoServiceGrpc; -import com.hedera.hashgraph.sdk.proto.CryptoTransferTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import com.hedera.hashgraph.sdk.proto.TransferList; -import io.grpc.MethodDescriptor; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * A transaction that transfers hbars and tokens between Hedera accounts. You can enter multiple transfers in a single - * transaction. The net value of hbars between the sending accounts and receiving accounts must equal zero. - *

- * See Hedera - * Documentation - */ -public class TransferTransaction extends AbstractTokenTransferTransaction { - private final ArrayList hbarTransfers = new ArrayList<>(); - - private static class HbarTransfer { - final AccountId accountId; - Hbar amount; - boolean isApproved; - - HbarTransfer(AccountId accountId, Hbar amount, boolean isApproved) { - this.accountId = accountId; - this.amount = amount; - this.isApproved = isApproved; - } - - AccountAmount toProtobuf() { - return AccountAmount.newBuilder() - .setAccountID(accountId.toProtobuf()) - .setAmount(amount.toTinybars()) - .setIsApproval(isApproved) - .build(); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("accountId", accountId) - .add("amount", amount) - .add("isApproved", isApproved) - .toString(); - } - } - - /** - * Constructor. - */ - public TransferTransaction() { - defaultMaxTransactionFee = new Hbar(1); - } - - /** - * Constructor. - * - * @param txs Compound list of transaction id's list of (AccountId, Transaction) records - * @throws InvalidProtocolBufferException when there is an issue with the protobuf - */ - TransferTransaction( - LinkedHashMap> txs) - throws InvalidProtocolBufferException { - super(txs); - initFromTransactionBody(); - } - - /** - * Constructor. - * - * @param txBody protobuf TransactionBody - */ - TransferTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { - super(txBody); - initFromTransactionBody(); - } - - /** - * Extract the of hbar transfers. - * - * @return list of hbar transfers - */ - public Map getHbarTransfers() { - Map transfers = new HashMap<>(); - - for (var transfer : hbarTransfers) { - transfers.put(transfer.accountId, transfer.amount); - } - - return transfers; - } - - private TransferTransaction doAddHbarTransfer(AccountId accountId, Hbar value, boolean isApproved) { - requireNotFrozen(); - - for (var transfer : hbarTransfers) { - if (transfer.accountId.equals(accountId) && transfer.isApproved == isApproved) { - transfer.amount = Hbar.fromTinybars(transfer.amount.toTinybars() + value.toTinybars()); - return this; - } - } - - hbarTransfers.add(new HbarTransfer(accountId, value, isApproved)); - return this; - } - - /** - * Add a non approved hbar transfer to an EVM address. - * - * @param evmAddress the EVM address - * @param value the value - * @return the updated transaction - */ - public TransferTransaction addHbarTransfer(EvmAddress evmAddress, Hbar value) { - AccountId accountId = AccountId.fromEvmAddress(evmAddress); - return doAddHbarTransfer(accountId, value, false); - } - - /** - * Add a non approved hbar transfer. - * - * @param accountId the account id - * @param value the value - * @return the updated transaction - */ - public TransferTransaction addHbarTransfer(AccountId accountId, Hbar value) { - return doAddHbarTransfer(accountId, value, false); - } - - /** - * Add an approved hbar transfer. - * - * @param accountId the account id - * @param value the value - * @return the updated transaction - */ - public TransferTransaction addApprovedHbarTransfer(AccountId accountId, Hbar value) { - return doAddHbarTransfer(accountId, value, true); - } - - /** - * @param accountId the account id - * @param isApproved whether the transfer is approved - * @return {@code this} - * @deprecated - Use {@link #addApprovedHbarTransfer(AccountId, Hbar)} instead - */ - @Deprecated - public TransferTransaction setHbarTransferApproval(AccountId accountId, boolean isApproved) { - requireNotFrozen(); - - for (var transfer : hbarTransfers) { - if (transfer.accountId.equals(accountId)) { - transfer.isApproved = isApproved; - return this; - } - } - - return this; - } - - /** - * Build the transaction body. - * - * @return {@link com.hedera.hashgraph.sdk.proto.CryptoTransferTransactionBody} - */ - CryptoTransferTransactionBody.Builder build() { - var transfers = sortTransfersAndBuild(); - - var builder = CryptoTransferTransactionBody.newBuilder(); - - this.hbarTransfers.sort(Comparator.comparing((HbarTransfer a) -> a.accountId).thenComparing(a -> a.isApproved)); - var hbarTransfersList = TransferList.newBuilder(); - for (var transfer : hbarTransfers) { - hbarTransfersList.addAccountAmounts(transfer.toProtobuf()); - } - builder.setTransfers(hbarTransfersList); - - for (var transfer : transfers) { - builder.addTokenTransfers(transfer.toProtobuf()); - } - - return builder; - } - - @Override - void validateChecksums(Client client) throws BadEntityIdException { - super.validateChecksums(client); - for (var transfer : hbarTransfers) { - transfer.accountId.validateChecksum(client); - } - } - - @Override - MethodDescriptor getMethodDescriptor() { - return CryptoServiceGrpc.getCryptoTransferMethod(); - } - - @Override - void onFreeze(TransactionBody.Builder bodyBuilder) { - bodyBuilder.setCryptoTransfer(build()); - } - - @Override - void onScheduled(SchedulableTransactionBody.Builder scheduled) { - scheduled.setCryptoTransfer(build()); - } - - /** - * Initialize from the transaction body. - */ - void initFromTransactionBody() { - var body = sourceTransactionBody.getCryptoTransfer(); - - for (var transfer : body.getTransfers().getAccountAmountsList()) { - hbarTransfers.add(new HbarTransfer( - AccountId.fromProtobuf(transfer.getAccountID()), - Hbar.fromTinybars(transfer.getAmount()), - transfer.getIsApproval() - )); - } - - for (var tokenTransferList : body.getTokenTransfersList()) { - var token = TokenId.fromProtobuf(tokenTransferList.getToken()); - - for (var transfer : tokenTransferList.getTransfersList()) { - tokenTransfers.add(new TokenTransfer( - token, - AccountId.fromProtobuf(transfer.getAccountID()), - transfer.getAmount(), - tokenTransferList.hasExpectedDecimals() ? tokenTransferList.getExpectedDecimals().getValue() : null, - transfer.getIsApproval() - )); - } - - for (var transfer : tokenTransferList.getNftTransfersList()) { - nftTransfers.add(new TokenNftTransfer( - token, - AccountId.fromProtobuf(transfer.getSenderAccountID()), - AccountId.fromProtobuf(transfer.getReceiverAccountID()), - transfer.getSerialNumber(), - transfer.getIsApproval() - )); - } - } - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/logger/LogLevel.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/logger/LogLevel.java deleted file mode 100644 index da4ef430d7..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/logger/LogLevel.java +++ /dev/null @@ -1,39 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.logger; - -public enum LogLevel { - TRACE(0), - DEBUG(1), - INFO(2), - WARN(3), - ERROR(4), - SILENT(5); - - private final int levelInt; - - LogLevel(int i) { - this.levelInt = i; - } - - public int toInt() { - return levelInt; - } -} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/utils/Bip32Utils.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/utils/Bip32Utils.java deleted file mode 100644 index 7c24adba55..0000000000 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/utils/Bip32Utils.java +++ /dev/null @@ -1,55 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.utils; - -/** - * Utility class for BIP32 functionalities - */ -public class Bip32Utils { - - /** - * Indicates if the index is hardened - */ - public static final int HARDENED_BIT = 0x80000000; - - private Bip32Utils() { - throw new IllegalStateException("Utility class"); - } - - /** - * Harden the index - * - * @param index the derivation index - * @return the hardened index - */ - public static int toHardenedIndex(int index) { - return index | HARDENED_BIT; - } - - /** - * Check if the index is hardened - * - * @param index the derivation index - * @return true if the index is hardened - */ - public static boolean isHardenedIndex(int index) { - return (index & HARDENED_BIT) != 0; - } -} diff --git a/sdk/src/main/java/module-info.java b/sdk/src/main/java/module-info.java index 36ca7aaf7a..6c62636fb0 100644 --- a/sdk/src/main/java/module-info.java +++ b/sdk/src/main/java/module-info.java @@ -1,24 +1,6 @@ -/*- - * - * Hedera Java SDK - * - * 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. - * - */ +// SPDX-License-Identifier: Apache-2.0 -module com.hedera.hashgraph.sdk { +module org.hiero.sdk { requires transitive com.google.protobuf; requires com.esaulpaugh.headlong; requires com.google.common; @@ -33,9 +15,10 @@ requires org.slf4j; requires static transitive java.annotation; - exports com.hedera.hashgraph.sdk; - exports com.hedera.hashgraph.sdk.logger; - exports com.hedera.hashgraph.sdk.proto; + exports org.hiero.sdk; + exports org.hiero.sdk.proto; - opens com.hedera.hashgraph.sdk; + opens org.hiero.sdk; + + exports org.hiero.sdk.logger; } diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/AbstractTokenTransferTransaction.java b/sdk/src/main/java/org/hiero/sdk/AbstractTokenTransferTransaction.java similarity index 76% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/AbstractTokenTransferTransaction.java rename to sdk/src/main/java/org/hiero/sdk/AbstractTokenTransferTransaction.java index 3b090d477e..7ddd9d9170 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/AbstractTokenTransferTransaction.java +++ b/sdk/src/main/java/org/hiero/sdk/AbstractTokenTransferTransaction.java @@ -1,23 +1,5 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.InvalidProtocolBufferException; import java.util.ArrayList; @@ -41,8 +23,8 @@ protected AbstractTokenTransferTransaction() {} * @throws InvalidProtocolBufferException when there is an issue with the protobuf */ AbstractTokenTransferTransaction( - LinkedHashMap> txs) - throws InvalidProtocolBufferException { + LinkedHashMap> txs) + throws InvalidProtocolBufferException { super(txs); } @@ -51,11 +33,10 @@ protected AbstractTokenTransferTransaction() {} * * @param txBody protobuf TransactionBody */ - AbstractTokenTransferTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { + AbstractTokenTransferTransaction(org.hiero.sdk.proto.TransactionBody txBody) { super(txBody); } - /** * Extract the list of token id decimals. * @@ -81,7 +62,8 @@ public Map> getTokenTransfers() { for (var transfer : tokenTransfers) { var current = transfers.get(transfer.tokenId) != null - ? transfers.get(transfer.tokenId) : new HashMap(); + ? transfers.get(transfer.tokenId) + : new HashMap(); current.put(transfer.accountId, transfer.amount); transfers.put(transfer.tokenId, current); } @@ -89,13 +71,13 @@ public Map> getTokenTransfers() { return transfers; } - private T doAddTokenTransfer(TokenId tokenId, AccountId accountId, long value, - boolean isApproved) { + private T doAddTokenTransfer(TokenId tokenId, AccountId accountId, long value, boolean isApproved) { requireNotFrozen(); for (var transfer : tokenTransfers) { - if (transfer.tokenId.equals(tokenId) && transfer.accountId.equals(accountId) - && transfer.isApproved == isApproved) { + if (transfer.tokenId.equals(tokenId) + && transfer.accountId.equals(accountId) + && transfer.isApproved == isApproved) { transfer.amount = transfer.amount + value; // noinspection unchecked return (T) this; @@ -132,12 +114,7 @@ public T addApprovedTokenTransfer(TokenId tokenId, AccountId accountId, long val } private T doAddTokenTransferWithDecimals( - TokenId tokenId, - AccountId accountId, - long value, - int decimals, - boolean isApproved - ) { + TokenId tokenId, AccountId accountId, long value, int decimals, boolean isApproved) { requireNotFrozen(); var found = false; @@ -146,7 +123,7 @@ private T doAddTokenTransferWithDecimals( if (transfer.tokenId.equals(tokenId)) { if (transfer.expectedDecimals != null && transfer.expectedDecimals != decimals) { throw new IllegalArgumentException( - "expected decimals for a token in a token transfer cannot be changed after being set"); + "expected decimals for a token in a token transfer cannot be changed after being set"); } transfer.expectedDecimals = decimals; @@ -155,7 +132,6 @@ private T doAddTokenTransferWithDecimals( transfer.amount = transfer.amount + value; found = true; } - } } @@ -178,12 +154,7 @@ private T doAddTokenTransferWithDecimals( * @param decimals the decimals * @return the updated transaction */ - public T addTokenTransferWithDecimals( - TokenId tokenId, - AccountId accountId, - long value, - int decimals - ) { + public T addTokenTransferWithDecimals(TokenId tokenId, AccountId accountId, long value, int decimals) { return doAddTokenTransferWithDecimals(tokenId, accountId, value, decimals, false); } @@ -196,12 +167,7 @@ public T addTokenTransferWithDecimals( * @param decimals the decimals * @return the updated transaction */ - public T addApprovedTokenTransferWithDecimals( - TokenId tokenId, - AccountId accountId, - long value, - int decimals - ) { + public T addApprovedTokenTransferWithDecimals(TokenId tokenId, AccountId accountId, long value, int decimals) { return doAddTokenTransferWithDecimals(tokenId, accountId, value, decimals, true); } @@ -238,7 +204,8 @@ public Map> getTokenNftTransfers() { for (var transfer : nftTransfers) { var current = transfers.get(transfer.tokenId) != null - ? transfers.get(transfer.tokenId) : new ArrayList(); + ? transfers.get(transfer.tokenId) + : new ArrayList(); current.add(transfer); transfers.put(transfer.tokenId, current); } @@ -246,8 +213,7 @@ public Map> getTokenNftTransfers() { return transfers; } - private T doAddNftTransfer(NftId nftId, AccountId sender, AccountId receiver, - boolean isApproved) { + private T doAddNftTransfer(NftId nftId, AccountId sender, AccountId receiver, boolean isApproved) { requireNotFrozen(); nftTransfers.add(new TokenNftTransfer(nftId.tokenId, sender, receiver, nftId.serial, isApproved)); return (T) this; @@ -299,13 +265,16 @@ public T setNftTransferApproval(NftId nftId, boolean isApproved) { return (T) this; } - protected ArrayList sortTransfersAndBuild() { - var transferLists = new ArrayList(); + protected ArrayList sortTransfersAndBuild() { + var transferLists = new ArrayList(); - this.tokenTransfers.sort(Comparator.comparing((TokenTransfer a) -> a.tokenId).thenComparing(a -> a.accountId) - .thenComparing(a -> a.isApproved)); - this.nftTransfers.sort(Comparator.comparing((TokenNftTransfer a) -> a.tokenId).thenComparing(a -> a.sender) - .thenComparing(a -> a.receiver).thenComparing(a -> a.serial)); + this.tokenTransfers.sort(Comparator.comparing((TokenTransfer a) -> a.tokenId) + .thenComparing(a -> a.accountId) + .thenComparing(a -> a.isApproved)); + this.nftTransfers.sort(Comparator.comparing((TokenNftTransfer a) -> a.tokenId) + .thenComparing(a -> a.sender) + .thenComparing(a -> a.receiver) + .thenComparing(a -> a.serial)); var i = 0; var j = 0; @@ -331,15 +300,17 @@ protected ArrayList sortTransfersAnd var result = iTokenId.compareTo(jTokenId); if (result == 0) { - transferLists.add(new com.hedera.hashgraph.sdk.TokenTransferList(iTokenId, - this.tokenTransfers.get(i).expectedDecimals, this.tokenTransfers.get(i++), - this.nftTransfers.get(j++))); + transferLists.add(new org.hiero.sdk.TokenTransferList( + iTokenId, + this.tokenTransfers.get(i).expectedDecimals, + this.tokenTransfers.get(i++), + this.nftTransfers.get(j++))); } else if (result < 0) { - transferLists.add(new com.hedera.hashgraph.sdk.TokenTransferList(iTokenId, - this.tokenTransfers.get(i).expectedDecimals, this.tokenTransfers.get(i++), null)); + transferLists.add(new org.hiero.sdk.TokenTransferList( + iTokenId, this.tokenTransfers.get(i).expectedDecimals, this.tokenTransfers.get(i++), null)); } else { - transferLists.add(new com.hedera.hashgraph.sdk.TokenTransferList(jTokenId, null, null, - this.nftTransfers.get(j++))); + transferLists.add( + new org.hiero.sdk.TokenTransferList(jTokenId, null, null, this.nftTransfers.get(j++))); } } else if (i < this.tokenTransfers.size()) { var iTokenId = this.tokenTransfers.get(i).tokenId; @@ -351,8 +322,8 @@ protected ArrayList sortTransfersAnd continue; } - transferLists.add(new com.hedera.hashgraph.sdk.TokenTransferList(iTokenId, - this.tokenTransfers.get(i).expectedDecimals, this.tokenTransfers.get(i++), null)); + transferLists.add(new org.hiero.sdk.TokenTransferList( + iTokenId, this.tokenTransfers.get(i).expectedDecimals, this.tokenTransfers.get(i++), null)); } else { var jTokenId = this.nftTransfers.get(j).tokenId; var last = !transferLists.isEmpty() ? transferLists.get(transferLists.size() - 1) : null; @@ -364,7 +335,7 @@ protected ArrayList sortTransfersAnd } transferLists.add( - new com.hedera.hashgraph.sdk.TokenTransferList(jTokenId, null, null, this.nftTransfers.get(j++))); + new org.hiero.sdk.TokenTransferList(jTokenId, null, null, this.nftTransfers.get(j++))); } } return transferLists; diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountAllowanceAdjustTransaction.java b/sdk/src/main/java/org/hiero/sdk/AccountAllowanceAdjustTransaction.java similarity index 76% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/AccountAllowanceAdjustTransaction.java rename to sdk/src/main/java/org/hiero/sdk/AccountAllowanceAdjustTransaction.java index a3f867db2e..fa42fa9662 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountAllowanceAdjustTransaction.java +++ b/sdk/src/main/java/org/hiero/sdk/AccountAllowanceAdjustTransaction.java @@ -1,33 +1,8 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.CryptoServiceGrpc; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; import io.grpc.MethodDescriptor; - -import javax.annotation.Nonnegative; -import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -35,15 +10,19 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import javax.annotation.Nonnegative; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; /** * @deprecated with no replacement */ - @Deprecated public class AccountAllowanceAdjustTransaction extends Transaction { private final List hbarAllowances = new ArrayList<>(); - private final List tokenAllowances = new ArrayList<>(); + private final List tokenAllowances = new ArrayList<>(); private final List nftAllowances = new ArrayList<>(); // key is "{ownerId}:{spenderId}". OwnerId may be "FEE_PAYER" private final Map> nftMap = new HashMap<>(); @@ -51,17 +30,16 @@ public class AccountAllowanceAdjustTransaction extends Transaction> txs - ) throws InvalidProtocolBufferException { + LinkedHashMap> txs) + throws InvalidProtocolBufferException { super(txs); initFromTransactionBody(); } - AccountAllowanceAdjustTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { + AccountAllowanceAdjustTransaction(org.hiero.sdk.proto.TransactionBody txBody) { super(txBody); initFromTransactionBody(); } @@ -71,10 +49,7 @@ private void initFromTransactionBody() { } private AccountAllowanceAdjustTransaction adjustHbarAllowance( - @Nullable AccountId ownerAccountId, - AccountId spenderAccountId, - Hbar amount - ) { + @Nullable AccountId ownerAccountId, AccountId spenderAccountId, Hbar amount) { requireNotFrozen(); hbarAllowances.add(new HbarAllowance(ownerAccountId, Objects.requireNonNull(spenderAccountId), amount)); return this; @@ -102,10 +77,7 @@ public AccountAllowanceAdjustTransaction addHbarAllowance(AccountId spenderAccou * @return {@code this} */ public AccountAllowanceAdjustTransaction grantHbarAllowance( - AccountId ownerAccountId, - AccountId spenderAccountId, - Hbar amount - ) { + AccountId ownerAccountId, AccountId spenderAccountId, Hbar amount) { Objects.requireNonNull(amount); if (amount.compareTo(Hbar.ZERO) < 0) { throw new IllegalArgumentException("amount passed to grantHbarAllowance must be positive"); @@ -122,10 +94,7 @@ public AccountAllowanceAdjustTransaction grantHbarAllowance( * @return {@code this} */ public AccountAllowanceAdjustTransaction revokeHbarAllowance( - AccountId ownerAccountId, - AccountId spenderAccountId, - Hbar amount - ) { + AccountId ownerAccountId, AccountId spenderAccountId, Hbar amount) { Objects.requireNonNull(amount); if (amount.compareTo(Hbar.ZERO) < 0) { throw new IllegalArgumentException("amount passed to revokeHbarAllowance must be positive"); @@ -143,18 +112,10 @@ public List getHbarAllowances() { } private AccountAllowanceAdjustTransaction adjustTokenAllowance( - TokenId tokenId, - @Nullable AccountId ownerAccountId, - AccountId spenderAccountId, - long amount - ) { + TokenId tokenId, @Nullable AccountId ownerAccountId, AccountId spenderAccountId, long amount) { requireNotFrozen(); tokenAllowances.add(new TokenAllowance( - Objects.requireNonNull(tokenId), - ownerAccountId, - Objects.requireNonNull(spenderAccountId), - amount - )); + Objects.requireNonNull(tokenId), ownerAccountId, Objects.requireNonNull(spenderAccountId), amount)); return this; } @@ -168,7 +129,8 @@ private AccountAllowanceAdjustTransaction adjustTokenAllowance( * @return an account allowance adjust transaction */ @Deprecated - public AccountAllowanceAdjustTransaction addTokenAllowance(TokenId tokenId, AccountId spenderAccountId, long amount) { + public AccountAllowanceAdjustTransaction addTokenAllowance( + TokenId tokenId, AccountId spenderAccountId, long amount) { return adjustTokenAllowance(tokenId, null, spenderAccountId, amount); } @@ -182,11 +144,7 @@ public AccountAllowanceAdjustTransaction addTokenAllowance(TokenId tokenId, Acco * @return {@code this} */ public AccountAllowanceAdjustTransaction grantTokenAllowance( - TokenId tokenId, - AccountId ownerAccountId, - AccountId spenderAccountId, - @Nonnegative long amount - ) { + TokenId tokenId, AccountId ownerAccountId, AccountId spenderAccountId, @Nonnegative long amount) { return adjustTokenAllowance(tokenId, Objects.requireNonNull(ownerAccountId), spenderAccountId, amount); } @@ -200,11 +158,7 @@ public AccountAllowanceAdjustTransaction grantTokenAllowance( * @return {@code this} */ public AccountAllowanceAdjustTransaction revokeTokenAllowance( - TokenId tokenId, - AccountId ownerAccountId, - AccountId spenderAccountId, - @Nonnegative long amount - ) { + TokenId tokenId, AccountId ownerAccountId, AccountId spenderAccountId, @Nonnegative long amount) { return adjustTokenAllowance(tokenId, Objects.requireNonNull(ownerAccountId), spenderAccountId, -amount); } @@ -238,43 +192,35 @@ private List getNftSerials(@Nullable AccountId ownerAccountId, AccountId s } private List newNftSerials( - @Nullable AccountId ownerAccountId, - AccountId spenderAccountId, - TokenId tokenId, - Map innerMap - ) { + @Nullable AccountId ownerAccountId, + AccountId spenderAccountId, + TokenId tokenId, + Map innerMap) { innerMap.put(tokenId, nftAllowances.size()); - TokenNftAllowance newAllowance = new TokenNftAllowance(tokenId, ownerAccountId, spenderAccountId, null, new ArrayList<>(), null); + TokenNftAllowance newAllowance = + new TokenNftAllowance(tokenId, ownerAccountId, spenderAccountId, null, new ArrayList<>(), null); nftAllowances.add(newAllowance); return newAllowance.serialNumbers; } private AccountAllowanceAdjustTransaction adjustNftAllowance( - TokenId tokenId, - long serial, - @Nullable AccountId ownerAccountId, - AccountId spenderAccountId - ) { + TokenId tokenId, long serial, @Nullable AccountId ownerAccountId, AccountId spenderAccountId) { requireNotFrozen(); - getNftSerials(ownerAccountId, Objects.requireNonNull(spenderAccountId), tokenId).add(serial); + getNftSerials(ownerAccountId, Objects.requireNonNull(spenderAccountId), tokenId) + .add(serial); return this; } private AccountAllowanceAdjustTransaction adjustNftAllowanceAllSerials( - TokenId tokenId, - boolean allSerials, - @Nullable AccountId ownerAccountId, - AccountId spenderAccountId - ) { + TokenId tokenId, boolean allSerials, @Nullable AccountId ownerAccountId, AccountId spenderAccountId) { requireNotFrozen(); nftAllowances.add(new TokenNftAllowance( - Objects.requireNonNull(tokenId), - ownerAccountId, - Objects.requireNonNull(spenderAccountId), - null, - Collections.emptyList(), - allSerials - )); + Objects.requireNonNull(tokenId), + ownerAccountId, + Objects.requireNonNull(spenderAccountId), + null, + Collections.emptyList(), + allSerials)); return this; } @@ -313,10 +259,7 @@ public AccountAllowanceAdjustTransaction addAllTokenNftAllowance(TokenId tokenId * @return {@code this} */ public AccountAllowanceAdjustTransaction grantTokenNftAllowance( - NftId nftId, - AccountId ownerAccountId, - AccountId spenderAccountId - ) { + NftId nftId, AccountId ownerAccountId, AccountId spenderAccountId) { Objects.requireNonNull(nftId); Objects.requireNonNull(ownerAccountId); return adjustNftAllowance(nftId.tokenId, nftId.serial, ownerAccountId, spenderAccountId); @@ -331,10 +274,7 @@ public AccountAllowanceAdjustTransaction grantTokenNftAllowance( * @return an account allowance adjust transaction */ public AccountAllowanceAdjustTransaction grantTokenNftAllowanceAllSerials( - TokenId tokenId, - AccountId ownerAccountId, - AccountId spenderAccountId - ) { + TokenId tokenId, AccountId ownerAccountId, AccountId spenderAccountId) { Objects.requireNonNull(ownerAccountId); return adjustNftAllowanceAllSerials(tokenId, true, ownerAccountId, spenderAccountId); } @@ -348,10 +288,7 @@ public AccountAllowanceAdjustTransaction grantTokenNftAllowanceAllSerials( */ @Deprecated public AccountAllowanceAdjustTransaction revokeTokenNftAllowance( - NftId nftId, - AccountId ownerAccountId, - AccountId spenderAccountId - ) { + NftId nftId, AccountId ownerAccountId, AccountId spenderAccountId) { Objects.requireNonNull(nftId); Objects.requireNonNull(ownerAccountId); return adjustNftAllowance(nftId.tokenId, -nftId.serial, ownerAccountId, spenderAccountId); @@ -366,10 +303,7 @@ public AccountAllowanceAdjustTransaction revokeTokenNftAllowance( * @return an account allowance adjust transaction */ public AccountAllowanceAdjustTransaction revokeTokenNftAllowanceAllSerials( - TokenId tokenId, - AccountId ownerAccountId, - AccountId spenderAccountId - ) { + TokenId tokenId, AccountId ownerAccountId, AccountId spenderAccountId) { Objects.requireNonNull(ownerAccountId); return adjustNftAllowanceAllSerials(tokenId, false, ownerAccountId, spenderAccountId); } @@ -388,7 +322,7 @@ public List getTokenNftAllowances() { } @Override - MethodDescriptor getMethodDescriptor() { + MethodDescriptor getMethodDescriptor() { throw new UnsupportedOperationException("Cannot get method descriptor for AccountAllowanceAdjustTransaction"); } diff --git a/sdk/src/main/java/org/hiero/sdk/AccountAllowanceApproveTransaction.java b/sdk/src/main/java/org/hiero/sdk/AccountAllowanceApproveTransaction.java new file mode 100644 index 0000000000..b5653b19fa --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/AccountAllowanceApproveTransaction.java @@ -0,0 +1,427 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.grpc.MethodDescriptor; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.CryptoApproveAllowanceTransactionBody; +import org.hiero.sdk.proto.CryptoServiceGrpc; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; + +/** + * This transaction type is for approving account allowance. + */ +public class AccountAllowanceApproveTransaction extends Transaction { + private final List hbarAllowances = new ArrayList<>(); + private final List tokenAllowances = new ArrayList<>(); + private final List nftAllowances = new ArrayList<>(); + // key is "{ownerId}:{spenderId}". OwnerId may be "FEE_PAYER" + // > + private final Map> nftMap = new HashMap<>(); + + /** + * Constructor. + */ + public AccountAllowanceApproveTransaction() {} + + /** + * Constructor. + * + * @param txs Compound list of transaction id's list of (AccountId, Transaction) records + */ + AccountAllowanceApproveTransaction( + LinkedHashMap> txs) + throws InvalidProtocolBufferException { + super(txs); + initFromTransactionBody(); + } + + /** + * Constructor. + * + * @param txBody protobuf TransactionBody + */ + AccountAllowanceApproveTransaction(org.hiero.sdk.proto.TransactionBody txBody) { + super(txBody); + initFromTransactionBody(); + } + + private void initFromTransactionBody() { + var body = sourceTransactionBody.getCryptoApproveAllowance(); + for (var allowanceProto : body.getCryptoAllowancesList()) { + hbarAllowances.add(HbarAllowance.fromProtobuf(allowanceProto)); + } + for (var allowanceProto : body.getTokenAllowancesList()) { + tokenAllowances.add(TokenAllowance.fromProtobuf(allowanceProto)); + } + for (var allowanceProto : body.getNftAllowancesList()) { + if (allowanceProto.hasApprovedForAll() + && allowanceProto.getApprovedForAll().getValue()) { + nftAllowances.add(TokenNftAllowance.fromProtobuf(allowanceProto)); + } else { + getNftSerials( + allowanceProto.hasOwner() ? AccountId.fromProtobuf(allowanceProto.getOwner()) : null, + AccountId.fromProtobuf(allowanceProto.getSpender()), + allowanceProto.hasDelegatingSpender() + ? AccountId.fromProtobuf(allowanceProto.getDelegatingSpender()) + : null, + TokenId.fromProtobuf(allowanceProto.getTokenId())) + .addAll(allowanceProto.getSerialNumbersList()); + } + } + } + + /** + * @deprecated - Use {@link #approveHbarAllowance(AccountId, AccountId, Hbar)} instead + * + * @param spenderAccountId the spender account id + * @param amount the amount of hbar + * @return an account allowance approve transaction + */ + @Deprecated + public AccountAllowanceApproveTransaction addHbarAllowance(AccountId spenderAccountId, Hbar amount) { + return approveHbarAllowance(null, spenderAccountId, amount); + } + + /** + * Approves the Hbar allowance. + * + * @param ownerAccountId owner's account id + * @param spenderAccountId spender's account id + * @param amount amount of hbar add + * @return {@code this} + */ + public AccountAllowanceApproveTransaction approveHbarAllowance( + @Nullable AccountId ownerAccountId, AccountId spenderAccountId, Hbar amount) { + requireNotFrozen(); + Objects.requireNonNull(amount); + if (amount.compareTo(Hbar.ZERO) < 0) { + throw new IllegalArgumentException("amount passed to approveHbarAllowance must be positive"); + } + hbarAllowances.add(new HbarAllowance(ownerAccountId, Objects.requireNonNull(spenderAccountId), amount)); + return this; + } + + /** + * @deprecated - Use {@link #getHbarApprovals()} instead + * + * @return list of hbar allowance records + */ + @Deprecated + public List getHbarAllowances() { + return getHbarApprovals(); + } + + /** + * Extract the list of hbar allowances. + * + * @return array list of hbar allowances + */ + public List getHbarApprovals() { + return new ArrayList<>(hbarAllowances); + } + + /** + * @deprecated - Use {@link #approveTokenAllowance(TokenId, AccountId, AccountId, long)} instead + * + * @param tokenId the token id + * @param spenderAccountId the spenders account id + * @param amount the hbar amount + * @return an account allowance approve transaction + */ + @Deprecated + public AccountAllowanceApproveTransaction addTokenAllowance( + TokenId tokenId, AccountId spenderAccountId, long amount) { + return approveTokenAllowance(tokenId, null, spenderAccountId, amount); + } + + /** + * Approves the Token allowance. + * + * @param tokenId the token's id + * @param ownerAccountId owner's account id + * @param spenderAccountId spender's account id + * @param amount amount of tokens + * @return {@code this} + */ + public AccountAllowanceApproveTransaction approveTokenAllowance( + TokenId tokenId, @Nullable AccountId ownerAccountId, AccountId spenderAccountId, long amount) { + requireNotFrozen(); + if (amount < 0) { + throw new IllegalArgumentException("amount given to approveTokenAllowance must be positive"); + } + tokenAllowances.add(new TokenAllowance( + Objects.requireNonNull(tokenId), ownerAccountId, Objects.requireNonNull(spenderAccountId), amount)); + return this; + } + + /** + * @deprecated - Use {@link #getTokenApprovals()} instead + * + * @return a list of token allowances + */ + @Deprecated + public List getTokenAllowances() { + return getTokenApprovals(); + } + + /** + * Extract a list of token allowance approvals. + * + * @return array list of token approvals. + */ + public List getTokenApprovals() { + return new ArrayList<>(tokenAllowances); + } + + /** + * Extract the owner as a string. + * + * @param ownerAccountId owner's account id + * @return a string representation of the account id + * or FEE_PAYER + */ + private static String ownerToString(@Nullable AccountId ownerAccountId) { + return ownerAccountId != null ? ownerAccountId.toString() : "FEE_PAYER"; + } + + /** + * Return a list of NFT serial numbers. + * + * @param ownerAccountId owner's account id + * @param spenderAccountId spender's account id + * @param delegatingSpender delegating spender's account id + * @param tokenId the token's id + * @return list of NFT serial numbers + */ + private List getNftSerials( + @Nullable AccountId ownerAccountId, + AccountId spenderAccountId, + @Nullable AccountId delegatingSpender, + TokenId tokenId) { + var key = ownerToString(ownerAccountId) + ":" + spenderAccountId; + if (nftMap.containsKey(key)) { + var innerMap = nftMap.get(key); + if (innerMap.containsKey(tokenId)) { + return Objects.requireNonNull(nftAllowances.get(innerMap.get(tokenId)).serialNumbers); + } else { + return newNftSerials(ownerAccountId, spenderAccountId, delegatingSpender, tokenId, innerMap); + } + } else { + Map innerMap = new HashMap<>(); + nftMap.put(key, innerMap); + return newNftSerials(ownerAccountId, spenderAccountId, delegatingSpender, tokenId, innerMap); + } + } + + /** + * Add NFT serials. + * + * @param ownerAccountId owner's account id + * @param spenderAccountId spender's account id + * @param delegatingSpender delegating spender's account id + * @param tokenId the token's id + * @param innerMap list of token id's and serial number records + * @return list of NFT serial numbers + */ + private List newNftSerials( + @Nullable AccountId ownerAccountId, + AccountId spenderAccountId, + @Nullable AccountId delegatingSpender, + TokenId tokenId, + Map innerMap) { + innerMap.put(tokenId, nftAllowances.size()); + TokenNftAllowance newAllowance = new TokenNftAllowance( + tokenId, ownerAccountId, spenderAccountId, delegatingSpender, new ArrayList<>(), null); + nftAllowances.add(newAllowance); + return newAllowance.serialNumbers; + } + + /** + * @deprecated - Use {@link #approveTokenNftAllowance(NftId, AccountId, AccountId, AccountId)} instead + * + * @param nftId the nft id + * @param spenderAccountId the spender's account id + * @return {@code this} + */ + @Deprecated + public AccountAllowanceApproveTransaction addTokenNftAllowance(NftId nftId, AccountId spenderAccountId) { + requireNotFrozen(); + getNftSerials(null, spenderAccountId, null, nftId.tokenId).add(nftId.serial); + return this; + } + + /** + * @deprecated - Use {@link #approveTokenNftAllowanceAllSerials(TokenId, AccountId, AccountId)} instead + * + * @param tokenId the token id + * @param spenderAccountId the spender's account id + * @return {@code this} + */ + @Deprecated + public AccountAllowanceApproveTransaction addAllTokenNftAllowance(TokenId tokenId, AccountId spenderAccountId) { + requireNotFrozen(); + nftAllowances.add(new TokenNftAllowance(tokenId, null, spenderAccountId, null, Collections.emptyList(), true)); + return this; + } + + /** + * Approve the NFT allowance. + * + * @param nftId nft's id + * @param ownerAccountId owner's account id + * @param spenderAccountId spender's account id + * @param delegatingSpender delegating spender's account id + * @return {@code this} + */ + public AccountAllowanceApproveTransaction approveTokenNftAllowance( + NftId nftId, + @Nullable AccountId ownerAccountId, + AccountId spenderAccountId, + @Nullable AccountId delegatingSpender) { + requireNotFrozen(); + Objects.requireNonNull(nftId); + getNftSerials(ownerAccountId, Objects.requireNonNull(spenderAccountId), delegatingSpender, nftId.tokenId) + .add(nftId.serial); + return this; + } + + /** + * Approve the NFT allowance. + * + * @param nftId nft's id + * @param ownerAccountId owner's account id + * @param spenderAccountId spender's account id + * @return {@code this} + */ + public AccountAllowanceApproveTransaction approveTokenNftAllowance( + NftId nftId, @Nullable AccountId ownerAccountId, AccountId spenderAccountId) { + requireNotFrozen(); + Objects.requireNonNull(nftId); + getNftSerials(ownerAccountId, Objects.requireNonNull(spenderAccountId), null, nftId.tokenId) + .add(nftId.serial); + return this; + } + + /** + * Approve the token nft allowance on all serials. + * + * @param tokenId the token's id + * @param ownerAccountId owner's account id + * @param spenderAccountId spender's account id + * @return {@code this} + */ + public AccountAllowanceApproveTransaction approveTokenNftAllowanceAllSerials( + TokenId tokenId, @Nullable AccountId ownerAccountId, AccountId spenderAccountId) { + requireNotFrozen(); + nftAllowances.add(new TokenNftAllowance( + Objects.requireNonNull(tokenId), + ownerAccountId, + Objects.requireNonNull(spenderAccountId), + null, + Collections.emptyList(), + true)); + return this; + } + + /** + * Delete the token nft allowance on all serials. + * + * @param tokenId the token's id + * @param ownerAccountId owner's account id + * @param spenderAccountId spender's account id + * @return {@code this} + */ + public AccountAllowanceApproveTransaction deleteTokenNftAllowanceAllSerials( + TokenId tokenId, @Nullable AccountId ownerAccountId, AccountId spenderAccountId) { + requireNotFrozen(); + nftAllowances.add(new TokenNftAllowance( + Objects.requireNonNull(tokenId), + ownerAccountId, + Objects.requireNonNull(spenderAccountId), + null, + Collections.emptyList(), + false)); + return this; + } + + /** + * @deprecated - Use {@link #getTokenNftApprovals()} instead + * + * @return {@code this} + */ + @Deprecated + public List getTokenNftAllowances() { + return getTokenNftApprovals(); + } + + /** + * Returns the list of token nft allowances. + * + * @return list of token nft allowances. + */ + public List getTokenNftApprovals() { + List retval = new ArrayList<>(nftAllowances.size()); + for (var allowance : nftAllowances) { + retval.add(TokenNftAllowance.copyFrom(allowance)); + } + return retval; + } + + @Override + MethodDescriptor getMethodDescriptor() { + return CryptoServiceGrpc.getApproveAllowancesMethod(); + } + + /** + * Build the correct transaction body. + * + * @return {@link org.hiero.sdk.proto.CryptoApproveAllowanceTransactionBody builder } + */ + CryptoApproveAllowanceTransactionBody.Builder build() { + var builder = CryptoApproveAllowanceTransactionBody.newBuilder(); + + for (var allowance : hbarAllowances) { + builder.addCryptoAllowances(allowance.toProtobuf()); + } + for (var allowance : tokenAllowances) { + builder.addTokenAllowances(allowance.toProtobuf()); + } + for (var allowance : nftAllowances) { + builder.addNftAllowances(allowance.toProtobuf()); + } + return builder; + } + + @Override + void onFreeze(TransactionBody.Builder bodyBuilder) { + bodyBuilder.setCryptoApproveAllowance(build()); + } + + @Override + void onScheduled(SchedulableTransactionBody.Builder scheduled) { + scheduled.setCryptoApproveAllowance(build()); + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + for (var allowance : hbarAllowances) { + allowance.validateChecksums(client); + } + for (var allowance : tokenAllowances) { + allowance.validateChecksums(client); + } + for (var allowance : nftAllowances) { + allowance.validateChecksums(client); + } + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/AccountAllowanceDeleteTransaction.java b/sdk/src/main/java/org/hiero/sdk/AccountAllowanceDeleteTransaction.java new file mode 100644 index 0000000000..13fe306667 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/AccountAllowanceDeleteTransaction.java @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.grpc.MethodDescriptor; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.CryptoDeleteAllowanceTransactionBody; +import org.hiero.sdk.proto.CryptoServiceGrpc; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.Transaction; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; + +/** + * This transaction type is for deleting an account allowance. + */ +public class AccountAllowanceDeleteTransaction extends org.hiero.sdk.Transaction { + private final List hbarAllowances = new ArrayList<>(); + private final List tokenAllowances = new ArrayList<>(); + private final List nftAllowances = new ArrayList<>(); + // > + private final Map> nftMap = new HashMap<>(); + + /** + * Constructor. + */ + public AccountAllowanceDeleteTransaction() {} + + /** + * Constructor. + * + * @param txs Compound list of transaction id's list of (AccountId, Transaction) records + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + AccountAllowanceDeleteTransaction( + LinkedHashMap> txs) + throws InvalidProtocolBufferException { + super(txs); + initFromTransactionBody(); + } + + /** + * Constructor. + * + * @param txBody protobuf TransactionBody + */ + AccountAllowanceDeleteTransaction(org.hiero.sdk.proto.TransactionBody txBody) { + super(txBody); + initFromTransactionBody(); + } + + private void initFromTransactionBody() { + var body = sourceTransactionBody.getCryptoDeleteAllowance(); + for (var allowanceProto : body.getNftAllowancesList()) { + getNftSerials( + AccountId.fromProtobuf(allowanceProto.getOwner()), + TokenId.fromProtobuf(allowanceProto.getTokenId())) + .addAll(allowanceProto.getSerialNumbersList()); + } + } + + /** + * @deprecated with no replacement + * + * @param ownerAccountId the owner's account id + * @return {@code this} + */ + @Deprecated + public AccountAllowanceDeleteTransaction deleteAllHbarAllowances(AccountId ownerAccountId) { + requireNotFrozen(); + hbarAllowances.add(new HbarAllowance(Objects.requireNonNull(ownerAccountId), null, null)); + return this; + } + + /** + * @deprecated with no replacement + * + * @return a list of hbar allowance records + */ + @Deprecated + public List getHbarAllowanceDeletions() { + return new ArrayList<>(hbarAllowances); + } + + /** + * @deprecated with no replacement + * + * @param tokenId the token id + * @param ownerAccountId the owner's account id + * @return {@code this} + */ + @Deprecated + public AccountAllowanceDeleteTransaction deleteAllTokenAllowances(TokenId tokenId, AccountId ownerAccountId) { + requireNotFrozen(); + tokenAllowances.add( + new TokenAllowance(Objects.requireNonNull(tokenId), Objects.requireNonNull(ownerAccountId), null, 0)); + return this; + } + + /** + * @deprecated with no replacement + * + * @return a list of token allowance records + */ + @Deprecated + public List getTokenAllowanceDeletions() { + return new ArrayList<>(tokenAllowances); + } + + /** + * Remove all nft token allowances. + * + * @param nftId nft's id + * @param ownerAccountId owner's account id + * @return {@code this} + */ + public AccountAllowanceDeleteTransaction deleteAllTokenNftAllowances(NftId nftId, AccountId ownerAccountId) { + requireNotFrozen(); + Objects.requireNonNull(nftId); + getNftSerials(Objects.requireNonNull(ownerAccountId), nftId.tokenId).add(nftId.serial); + return this; + } + + /** + * Return list of nft tokens to be deleted. + * + * @return list of token nft allowances + */ + public List getTokenNftAllowanceDeletions() { + List retval = new ArrayList<>(nftAllowances.size()); + for (var allowance : nftAllowances) { + retval.add(TokenNftAllowance.copyFrom(allowance)); + } + return retval; + } + + /** + * Return list of nft serial numbers. + * + * @param ownerAccountId owner's account id + * @param tokenId the token's id + * @return list of nft serial numbers + */ + private List getNftSerials(@Nullable AccountId ownerAccountId, TokenId tokenId) { + var key = ownerAccountId; + if (nftMap.containsKey(key)) { + var innerMap = nftMap.get(key); + if (innerMap.containsKey(tokenId)) { + return Objects.requireNonNull(nftAllowances.get(innerMap.get(tokenId)).serialNumbers); + } else { + return newNftSerials(ownerAccountId, tokenId, innerMap); + } + } else { + Map innerMap = new HashMap<>(); + nftMap.put(key, innerMap); + return newNftSerials(ownerAccountId, tokenId, innerMap); + } + } + + /** + * Return serial numbers of new nft's. + * + * @param ownerAccountId owner's account id + * @param tokenId the token's id + * @param innerMap list of token id's and serial number records + * @return list of nft serial numbers + */ + private List newNftSerials( + @Nullable AccountId ownerAccountId, TokenId tokenId, Map innerMap) { + innerMap.put(tokenId, nftAllowances.size()); + TokenNftAllowance newAllowance = + new TokenNftAllowance(tokenId, ownerAccountId, null, null, new ArrayList<>(), null); + nftAllowances.add(newAllowance); + return newAllowance.serialNumbers; + } + + @Override + MethodDescriptor getMethodDescriptor() { + return CryptoServiceGrpc.getDeleteAllowancesMethod(); + } + + /** + * Build the transaction body. + * + * @return {@link CryptoDeleteAllowanceTransactionBody} + */ + CryptoDeleteAllowanceTransactionBody.Builder build() { + var builder = CryptoDeleteAllowanceTransactionBody.newBuilder(); + for (var allowance : nftAllowances) { + builder.addNftAllowances(allowance.toRemoveProtobuf()); + } + return builder; + } + + @Override + void onFreeze(TransactionBody.Builder bodyBuilder) { + bodyBuilder.setCryptoDeleteAllowance(build()); + } + + @Override + void onScheduled(SchedulableTransactionBody.Builder scheduled) { + scheduled.setCryptoDeleteAllowance(build()); + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + for (var allowance : nftAllowances) { + allowance.validateChecksums(client); + } + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/AccountBalance.java b/sdk/src/main/java/org/hiero/sdk/AccountBalance.java new file mode 100644 index 0000000000..be92c66a1c --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/AccountBalance.java @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import javax.annotation.Nonnegative; +import org.hiero.sdk.proto.CryptoGetAccountBalanceResponse; +import org.hiero.sdk.proto.TokenBalance; + +/** + * This class represents the account balance object + */ +public class AccountBalance { + /** + * The Hbar balance of the account + */ + @Nonnegative + public final Hbar hbars; + + /** + * @deprecated - Use `tokens` instead + */ + @Deprecated + @Nonnegative + public final Map token = new HashMap<>(); + + public final Map tokens; + + @Nonnegative + public final Map tokenDecimals; + + AccountBalance(Hbar hbars, Map token, Map decimal) { + this.hbars = hbars; + this.tokens = token; + this.tokenDecimals = decimal; + } + + /** + * Convert the protobuf object to an account balance object. + * + * @param protobuf protobuf response object + * @return the converted account balance object + */ + static AccountBalance fromProtobuf(CryptoGetAccountBalanceResponse protobuf) { + var balanceList = protobuf.getTokenBalancesList(); + Map map = new HashMap<>(); + Map decimalMap = new HashMap<>(); + for (int i = 0; i < protobuf.getTokenBalancesCount(); i++) { + map.put( + TokenId.fromProtobuf(balanceList.get(i).getTokenId()), + balanceList.get(i).getBalance()); + decimalMap.put( + TokenId.fromProtobuf(balanceList.get(i).getTokenId()), + balanceList.get(i).getDecimals()); + } + + return new AccountBalance(Hbar.fromTinybars(protobuf.getBalance()), map, decimalMap); + } + + /** + * Convert a byte array to an account balance object. + * + * @param data the byte array + * @return the converted account balance object + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + public static AccountBalance fromBytes(byte[] data) throws InvalidProtocolBufferException { + return fromProtobuf(CryptoGetAccountBalanceResponse.parseFrom(data)); + } + + /** + * Convert an account balance object into a protobuf. + * + * @return the protobuf object + */ + CryptoGetAccountBalanceResponse toProtobuf() { + var protobuf = CryptoGetAccountBalanceResponse.newBuilder().setBalance(hbars.toTinybars()); + + for (var entry : tokens.entrySet()) { + protobuf.addTokenBalances(TokenBalance.newBuilder() + .setTokenId(entry.getKey().toProtobuf()) + .setBalance(entry.getValue()) + .setDecimals(Objects.requireNonNull(tokenDecimals.get(entry.getKey())))); + } + + return protobuf.build(); + } + + /** + * Convert the account balance object to a byte array. + * + * @return the converted account balance object + */ + public ByteString toBytes() { + return toProtobuf().toByteString(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("hbars", hbars) + .add("tokens", tokens) + .toString(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/AccountBalanceQuery.java b/sdk/src/main/java/org/hiero/sdk/AccountBalanceQuery.java new file mode 100644 index 0000000000..bd3f9fc263 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/AccountBalanceQuery.java @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.grpc.MethodDescriptor; +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.CryptoGetAccountBalanceQuery; +import org.hiero.sdk.proto.CryptoServiceGrpc; +import org.hiero.sdk.proto.QueryHeader; +import org.hiero.sdk.proto.Response; +import org.hiero.sdk.proto.ResponseHeader; + +/** + * Get the balance of a Hedera™ crypto-currency account. This returns only the balance, so it is a + * smaller and faster reply than {@link AccountInfoQuery}. + * + *

This query is free. + */ +public final class AccountBalanceQuery extends Query { + @Nullable + private AccountId accountId = null; + + @Nullable + private ContractId contractId = null; + + /** + * Constructor. + */ + public AccountBalanceQuery() {} + + /** + * Return the account's id. + * + * @return {@code accountId} + */ + @Nullable + public AccountId getAccountId() { + return accountId; + } + + /** + * The account ID for which the balance is being requested. + *

+ * This is mutually exclusive with {@link #setContractId(ContractId)}. + * + * @param accountId The AccountId to set + * @return {@code this} + */ + public AccountBalanceQuery setAccountId(AccountId accountId) { + Objects.requireNonNull(accountId); + this.accountId = accountId; + return this; + } + + /** + * Extract the contract id. + * + * @return the contract id + */ + @Nullable + public ContractId getContractId() { + return contractId; + } + + /** + * The contract ID for which the balance is being requested. + *

+ * This is mutually exclusive with {@link #setAccountId(AccountId)}. + * + * @param contractId The ContractId to set + * @return {@code this} + */ + public AccountBalanceQuery setContractId(ContractId contractId) { + Objects.requireNonNull(contractId); + this.contractId = contractId; + return this; + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (accountId != null) { + accountId.validateChecksum(client); + } + + if (contractId != null) { + contractId.validateChecksum(client); + } + } + + @Override + boolean isPaymentRequired() { + return false; + } + + @Override + void onMakeRequest(org.hiero.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { + var builder = CryptoGetAccountBalanceQuery.newBuilder(); + if (accountId != null) { + builder.setAccountID(accountId.toProtobuf()); + } + + if (contractId != null) { + builder.setContractID(contractId.toProtobuf()); + } + + queryBuilder.setCryptogetAccountBalance(builder.setHeader(header)); + } + + @Override + AccountBalance mapResponse(Response response, AccountId nodeId, org.hiero.sdk.proto.Query request) { + return AccountBalance.fromProtobuf(response.getCryptogetAccountBalance()); + } + + @Override + ResponseHeader mapResponseHeader(Response response) { + return response.getCryptogetAccountBalance().getHeader(); + } + + @Override + QueryHeader mapRequestHeader(org.hiero.sdk.proto.Query request) { + return request.getCryptogetAccountBalance().getHeader(); + } + + @Override + MethodDescriptor getMethodDescriptor() { + return CryptoServiceGrpc.getCryptoGetBalanceMethod(); + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountCreateTransaction.java b/sdk/src/main/java/org/hiero/sdk/AccountCreateTransaction.java similarity index 88% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/AccountCreateTransaction.java rename to sdk/src/main/java/org/hiero/sdk/AccountCreateTransaction.java index 86c5af66c9..bb324e992f 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountCreateTransaction.java +++ b/sdk/src/main/java/org/hiero/sdk/AccountCreateTransaction.java @@ -1,37 +1,18 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.CryptoCreateTransactionBody; -import com.hedera.hashgraph.sdk.proto.CryptoServiceGrpc; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; import io.grpc.MethodDescriptor; import java.time.Duration; - -import javax.annotation.Nullable; import java.util.LinkedHashMap; import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.CryptoCreateTransactionBody; +import org.hiero.sdk.proto.CryptoServiceGrpc; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; /** * Create a new Hedera™ account. @@ -39,8 +20,10 @@ public final class AccountCreateTransaction extends Transaction { @Nullable private AccountId proxyAccountId = null; + @Nullable private Key key = null; + private String accountMemo = ""; private Hbar initialBalance = new Hbar(0); private boolean receiverSigRequired = false; @@ -71,7 +54,9 @@ public AccountCreateTransaction() { * @param txs Compound list of transaction id's list of (AccountId, Transaction) records * @throws InvalidProtocolBufferException when there is an issue with the protobuf */ - AccountCreateTransaction(LinkedHashMap> txs) throws InvalidProtocolBufferException { + AccountCreateTransaction( + LinkedHashMap> txs) + throws InvalidProtocolBufferException { super(txs); initFromTransactionBody(); } @@ -81,7 +66,7 @@ public AccountCreateTransaction() { * * @param txBody protobuf TransactionBody */ - AccountCreateTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { + AccountCreateTransaction(org.hiero.sdk.proto.TransactionBody txBody) { super(txBody); initFromTransactionBody(); } @@ -388,16 +373,16 @@ public AccountCreateTransaction setAlias(String aliasEvmAddress) { /** * Build the transaction body. * - * @return {@link com.hedera.hashgraph.sdk.proto.CryptoCreateTransactionBody} + * @return {@link org.hiero.sdk.proto.CryptoCreateTransactionBody} */ CryptoCreateTransactionBody.Builder build() { var builder = CryptoCreateTransactionBody.newBuilder() - .setInitialBalance(initialBalance.toTinybars()) - .setReceiverSigRequired(receiverSigRequired) - .setAutoRenewPeriod(DurationConverter.toProtobuf(autoRenewPeriod)) - .setMemo(accountMemo) - .setMaxAutomaticTokenAssociations(maxAutomaticTokenAssociations) - .setDeclineReward(declineStakingReward); + .setInitialBalance(initialBalance.toTinybars()) + .setReceiverSigRequired(receiverSigRequired) + .setAutoRenewPeriod(DurationConverter.toProtobuf(autoRenewPeriod)) + .setMemo(accountMemo) + .setMaxAutomaticTokenAssociations(maxAutomaticTokenAssociations) + .setDeclineReward(declineStakingReward); if (proxyAccountId != null) { builder.setProxyAccountID(proxyAccountId.toProtobuf()); @@ -464,7 +449,7 @@ void initFromTransactionBody() { } @Override - MethodDescriptor getMethodDescriptor() { + MethodDescriptor getMethodDescriptor() { return CryptoServiceGrpc.getCreateAccountMethod(); } diff --git a/sdk/src/main/java/org/hiero/sdk/AccountDeleteTransaction.java b/sdk/src/main/java/org/hiero/sdk/AccountDeleteTransaction.java new file mode 100644 index 0000000000..1890d9d043 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/AccountDeleteTransaction.java @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.grpc.MethodDescriptor; +import java.util.LinkedHashMap; +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.CryptoDeleteTransactionBody; +import org.hiero.sdk.proto.CryptoServiceGrpc; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; + +/** + * Marks an account as deleted, moving all its current hbars to another account. + *

+ * It will remain in the ledger, marked as deleted, until it expires. + * Transfers into it a deleted account fail. But a deleted account can still have its + * expiration extended in the normal way. + */ +public final class AccountDeleteTransaction extends Transaction { + @Nullable + private AccountId accountId = null; + + @Nullable + private AccountId transferAccountId = null; + + /** + * Constructor. + */ + public AccountDeleteTransaction() {} + + /** + * Constructor. + * + * @param txs Compound list of transaction id's list of (AccountId, Transaction) records + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + AccountDeleteTransaction( + LinkedHashMap> txs) + throws InvalidProtocolBufferException { + super(txs); + initFromTransactionBody(); + } + + /** + * Constructor. + * + * @param txBody protobuf TransactionBody + */ + AccountDeleteTransaction(org.hiero.sdk.proto.TransactionBody txBody) { + super(txBody); + initFromTransactionBody(); + } + + /** + * Extract the account id. + * + * @return the account id + */ + @Nullable + public AccountId getAccountId() { + return accountId; + } + + /** + * Sets the account ID which should be deleted. + * + * @param deleteAccountId The AccountId to be set + * @return {@code this} + */ + public AccountDeleteTransaction setAccountId(AccountId deleteAccountId) { + Objects.requireNonNull(deleteAccountId); + requireNotFrozen(); + this.accountId = deleteAccountId; + return this; + } + + /** + * Extract the receiving account id. + * + * @return the account id that receives the hbar + */ + @Nullable + public AccountId getTransferAccountId() { + return transferAccountId; + } + + /** + * Sets the account ID which will receive all remaining hbars. + * + * @param transferAccountId The AccountId to be set + * @return {@code this} + */ + public AccountDeleteTransaction setTransferAccountId(AccountId transferAccountId) { + requireNotFrozen(); + Objects.requireNonNull(transferAccountId); + this.transferAccountId = transferAccountId; + return this; + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (accountId != null) { + accountId.validateChecksum(client); + } + + if (transferAccountId != null) { + transferAccountId.validateChecksum(client); + } + } + + @Override + MethodDescriptor getMethodDescriptor() { + return CryptoServiceGrpc.getCryptoDeleteMethod(); + } + + /** + * Build the transaction body. + * + * @return {@link CryptoDeleteTransactionBody} + */ + CryptoDeleteTransactionBody.Builder build() { + var builder = CryptoDeleteTransactionBody.newBuilder(); + + if (accountId != null) { + builder.setDeleteAccountID(accountId.toProtobuf()); + } + + if (transferAccountId != null) { + builder.setTransferAccountID(transferAccountId.toProtobuf()); + } + + return builder; + } + + /** + * Initialize from the transaction body. + */ + void initFromTransactionBody() { + var body = sourceTransactionBody.getCryptoDelete(); + if (body.hasDeleteAccountID()) { + accountId = AccountId.fromProtobuf(body.getDeleteAccountID()); + } + + if (body.hasTransferAccountID()) { + transferAccountId = AccountId.fromProtobuf(body.getTransferAccountID()); + } + } + + @Override + void onFreeze(TransactionBody.Builder bodyBuilder) { + bodyBuilder.setCryptoDelete(build()); + } + + @Override + void onScheduled(SchedulableTransactionBody.Builder scheduled) { + scheduled.setCryptoDelete(build()); + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountId.java b/sdk/src/main/java/org/hiero/sdk/AccountId.java similarity index 80% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/AccountId.java rename to sdk/src/main/java/org/hiero/sdk/AccountId.java index 3faa33990f..cc35b48a01 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountId.java +++ b/sdk/src/main/java/org/hiero/sdk/AccountId.java @@ -1,27 +1,8 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.AccountID; import java.util.Arrays; import java.util.Objects; import java.util.concurrent.CompletableFuture; @@ -30,12 +11,14 @@ import javax.annotation.Nonnegative; import javax.annotation.Nullable; import org.bouncycastle.util.encoders.Hex; +import org.hiero.sdk.proto.AccountID; /** * The ID for a cryptocurrency account on Hedera. */ public final class AccountId implements Comparable { - private static final Pattern ALIAS_ID_REGEX = Pattern.compile("(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.((?:[0-9a-fA-F][0-9a-fA-F])+)$"); + private static final Pattern ALIAS_ID_REGEX = + Pattern.compile("(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.((?:[0-9a-fA-F][0-9a-fA-F])+)$"); /** * The shard number @@ -117,13 +100,12 @@ public AccountId(@Nonnegative long shard, @Nonnegative long realm, @Nonnegative */ @SuppressWarnings("InconsistentOverloads") AccountId( - @Nonnegative long shard, - @Nonnegative long realm, - @Nonnegative long num, - @Nullable String checksum, - @Nullable PublicKey aliasKey, - @Nullable EvmAddress evmAddress - ) { + @Nonnegative long shard, + @Nonnegative long realm, + @Nonnegative long num, + @Nullable String checksum, + @Nullable PublicKey aliasKey, + @Nullable EvmAddress evmAddress) { this.shard = shard; this.realm = realm; this.num = num; @@ -140,8 +122,7 @@ public AccountId(@Nonnegative long shard, @Nonnegative long realm, @Nonnegative * @throws IllegalArgumentException when the account id and checksum are invalid */ public static AccountId fromString(String id) { - if ((id.startsWith("0x") && id.length() == 42) || id.length() == 40) - return fromEvmAddress(id); + if ((id.startsWith("0x") && id.length() == 42) || id.length() == 40) return fromEvmAddress(id); try { return EntityIdHelper.fromString(id, AccountId::new); @@ -149,19 +130,18 @@ public static AccountId fromString(String id) { var match = ALIAS_ID_REGEX.matcher(id); if (!match.find()) { throw new IllegalArgumentException( - "Invalid Account ID \"" + id + "\": format should look like 0.0.123 or 0.0.123-vfmkw or 0.0.1337BEEF (where 1337BEEF is a hex-encoded, DER-format public key)" - ); + "Invalid Account ID \"" + id + + "\": format should look like 0.0.123 or 0.0.123-vfmkw or 0.0.1337BEEF (where 1337BEEF is a hex-encoded, DER-format public key)"); } else { byte[] aliasBytes = Hex.decode(match.group(3)); boolean isEvmAddress = aliasBytes.length == 20; return new AccountId( - Long.parseLong(match.group(1)), - Long.parseLong(match.group(2)), - 0, - null, - isEvmAddress ? null : PublicKey.fromBytesDER(aliasBytes), - isEvmAddress ? EvmAddress.fromBytes(aliasBytes) : null - ); + Long.parseLong(match.group(1)), + Long.parseLong(match.group(2)), + 0, + null, + isEvmAddress ? null : PublicKey.fromBytesDER(aliasBytes), + isEvmAddress ? EvmAddress.fromBytes(aliasBytes) : null); } } } @@ -207,14 +187,7 @@ public static AccountId fromEvmAddress(EvmAddress evmAddress) { * @return the account id object */ public static AccountId fromEvmAddress(EvmAddress evmAddress, @Nonnegative long shard, @Nonnegative long realm) { - return new AccountId( - shard, - realm, - 0, - null, - null, - evmAddress - ); + return new AccountId(shard, realm, 0, null, null, evmAddress); } /** @@ -245,18 +218,17 @@ static AccountId fromProtobuf(AccountID accountId) { if (accountId.getAlias().size() == 20) { evmAddress = EvmAddress.fromAliasBytes(accountId.getAlias()); } else { - aliasKey = PublicKey.fromAliasBytes(accountId.getAlias()); + aliasKey = PublicKey.fromAliasBytes(accountId.getAlias()); } } Objects.requireNonNull(accountId); return new AccountId( - accountId.getShardNum(), - accountId.getRealmNum(), - accountId.getAccountNum(), - null, - aliasKey, - evmAddress - ); + accountId.getShardNum(), + accountId.getRealmNum(), + accountId.getAccountNum(), + null, + aliasKey, + evmAddress); } /** @@ -285,14 +257,12 @@ public String toSolidityAddress() { * @return the account id builder */ AccountID toProtobuf() { - var accountIdBuilder = AccountID.newBuilder() - .setShardNum(shard) - .setRealmNum(realm); + var accountIdBuilder = AccountID.newBuilder().setShardNum(shard).setRealmNum(realm); if (aliasKey != null) { accountIdBuilder.setAlias(aliasKey.toProtobufKey().toByteString()); } else if (evmAddress != null) { accountIdBuilder.setAlias(ByteString.copyFrom(evmAddress.toBytes())); - }else { + } else { accountIdBuilder.setAccountNum(num); } return accountIdBuilder.build(); @@ -324,14 +294,13 @@ public AccountId populateAccountNum(Client client) throws InterruptedException, @Deprecated public CompletableFuture populateAccountNumAsync(Client client) { return EntityIdHelper.getAccountNumFromMirrorNodeAsync(client, evmAddress.toString()) - .thenApply(accountNumFromMirrorNode -> - new AccountId( - this.shard, - this.realm, - accountNumFromMirrorNode, - this.checksum, - this.aliasKey, - this.evmAddress)); + .thenApply(accountNumFromMirrorNode -> new AccountId( + this.shard, + this.realm, + accountNumFromMirrorNode, + this.checksum, + this.aliasKey, + this.evmAddress)); } /** @@ -356,14 +325,8 @@ public AccountId populateAccountEvmAddress(Client client) throws ExecutionExcept @Deprecated public CompletableFuture populateAccountEvmAddressAsync(Client client) { return EntityIdHelper.getEvmAddressFromMirrorNodeAsync(client, num) - .thenApply(evmAddressFromMirrorNode -> - new AccountId( - this.shard, - this.realm, - this.num, - this.checksum, - this.aliasKey, - evmAddressFromMirrorNode)); + .thenApply(evmAddressFromMirrorNode -> new AccountId( + this.shard, this.realm, this.num, this.checksum, this.aliasKey, evmAddressFromMirrorNode)); } /** @@ -426,7 +389,8 @@ public String toString() { */ public String toStringWithChecksum(Client client) { if (aliasKey != null || evmAddress != null) { - throw new IllegalStateException("toStringWithChecksum cannot be applied to AccountId with aliasKey or evmAddress"); + throw new IllegalStateException( + "toStringWithChecksum cannot be applied to AccountId with aliasKey or evmAddress"); } else { return EntityIdHelper.toStringWithChecksum(shard, realm, num, client, checksum); } @@ -442,9 +406,7 @@ public int hashCode() { aliasBytes = evmAddress.toBytes(); } - return Objects.hash( - shard, realm, num, Arrays.hashCode(aliasBytes) - ); + return Objects.hash(shard, realm, num, Arrays.hashCode(aliasBytes)); } @Override @@ -464,9 +426,11 @@ public boolean equals(Object o) { if ((evmAddress == null) != (otherId.evmAddress == null)) { return false; } - return shard == otherId.shard && realm == otherId.realm && num == otherId.num && - (aliasKey == null || aliasKey.equals(otherId.aliasKey)) && - (evmAddress == null || evmAddress.equals(otherId.evmAddress)); + return shard == otherId.shard + && realm == otherId.realm + && num == otherId.num + && (aliasKey == null || aliasKey.equals(otherId.aliasKey)) + && (evmAddress == null || evmAddress.equals(otherId.evmAddress)); } @Override diff --git a/sdk/src/main/java/org/hiero/sdk/AccountInfo.java b/sdk/src/main/java/org/hiero/sdk/AccountInfo.java new file mode 100644 index 0000000000..e4c416fd39 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/AccountInfo.java @@ -0,0 +1,372 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static java.util.stream.Collectors.toList; + +import com.google.common.base.MoreObjects; +import com.google.protobuf.InvalidProtocolBufferException; +import java.time.Duration; +import java.time.Instant; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.CryptoGetInfoResponse; + +/** + * Current information about an account, including the balance. + */ +public final class AccountInfo { + /** + * The account ID for which this information applies. + */ + public final AccountId accountId; + + /** + * The Contract Account ID comprising both the contract instance and the cryptocurrency account owned by the + * contract instance, in the format used by Solidity. + */ + public final String contractAccountId; + + /** + * If true, then this account has been deleted, it will disappear when it expires, and all transactions for it will + * fail except the transaction to extend its expiration date. + */ + public final boolean isDeleted; + + /** + * The Account ID of the account to which this is proxy staked. If proxyAccountID is null, or is an invalid account, + * or is an account that isn't a node, then this account is automatically proxy staked to a node chosen by the + * network, but without earning payments. If the proxyAccountID account refuses to accept proxy staking , or if it + * is not currently running a node, then it will behave as if proxyAccountID was null. + */ + @Nullable + public final AccountId proxyAccountId; + + /** + * The total proxy staked to this account. + */ + public final Hbar proxyReceived; + + /** + * The key for the account, which must sign in order to transfer out, or to modify the account in any way other than + * extending its expiration date. + */ + public final Key key; + + /** + * The current balance of account. + */ + public final Hbar balance; + + /** + * The threshold amount for which an account record is created (and this account charged for them) for any + * send/withdraw transaction. + */ + public final Hbar sendRecordThreshold; + + /** + * The threshold amount for which an account record is created (and this account charged for them) for any + * transaction above this amount. + */ + public final Hbar receiveRecordThreshold; + + /** + * If true, no transaction can transfer to this account unless signed by this account's key. + */ + public final boolean isReceiverSignatureRequired; + + /** + * The time at which this account is set to expire. + */ + public final Instant expirationTime; + + /** + * The duration for expiration time will extend every this many seconds. If there are insufficient funds, then it + * extends as long as possible. If it is empty when it expires, then it is deleted. + */ + public final Duration autoRenewPeriod; + + /** + * All the livehashes attached to the account (each of which is a hash along with the keys that authorized it and + * can delete it) + */ + public final List liveHashes; + + public final Map tokenRelationships; + + /** + * The memo associated with the account + */ + public final String accountMemo; + + /** + * The number of NFTs owned by this account + */ + public final long ownedNfts; + + /** + * The maximum number of tokens that an Account can be implicitly associated with. + */ + public final int maxAutomaticTokenAssociations; + + /** + * The public key which aliases to this account. + */ + @Nullable + public final PublicKey aliasKey; + + /** + * The ledger ID the response was returned from; please see HIP-198 for the + * network-specific IDs. + */ + public final LedgerId ledgerId; + + /** + * The ethereum transaction nonce associated with this account. + */ + public final long ethereumNonce; + + /** + * List of Hbar allowances + */ + @Deprecated + public final List hbarAllowances; + + /** + * List of token allowances + */ + @Deprecated + public final List tokenAllowances; + + /** + * List of NFT allowances + */ + @Deprecated + public final List tokenNftAllowances; + + /** + * Staking metadata for this account. + */ + @Nullable + public final StakingInfo stakingInfo; + + /** + * Constructor. + * + * @param accountId the account id + * @param contractAccountId the contracts account id + * @param isDeleted is it deleted + * @param proxyAccountId the proxy account's id + * @param proxyReceived amount of proxy received + * @param key signing key + * @param balance account balance + * @param sendRecordThreshold @depreciated no replacement + * @param receiveRecordThreshold @depreciated no replacement + * @param receiverSignatureRequired is the receiver's signature required + * @param expirationTime the expiration time + * @param autoRenewPeriod the auto renew period + * @param liveHashes the live hashes + * @param tokenRelationships list of token id and token relationship records + * @param accountMemo the account memo + * @param ownedNfts number of nft's + * @param maxAutomaticTokenAssociations max number of token associations + * @param aliasKey public alias key + * @param ledgerId the ledger id + */ + private AccountInfo( + AccountId accountId, + String contractAccountId, + boolean isDeleted, + @Nullable AccountId proxyAccountId, + long proxyReceived, + Key key, + long balance, + long sendRecordThreshold, + long receiveRecordThreshold, + boolean receiverSignatureRequired, + Instant expirationTime, + Duration autoRenewPeriod, + List liveHashes, + Map tokenRelationships, + String accountMemo, + long ownedNfts, + int maxAutomaticTokenAssociations, + @Nullable PublicKey aliasKey, + LedgerId ledgerId, + long ethereumNonce, + @Nullable StakingInfo stakingInfo) { + this.accountId = accountId; + this.contractAccountId = contractAccountId; + this.isDeleted = isDeleted; + this.proxyAccountId = proxyAccountId; + this.proxyReceived = Hbar.fromTinybars(proxyReceived); + this.key = key; + this.balance = Hbar.fromTinybars(balance); + this.sendRecordThreshold = Hbar.fromTinybars(sendRecordThreshold); + this.receiveRecordThreshold = Hbar.fromTinybars(receiveRecordThreshold); + this.isReceiverSignatureRequired = receiverSignatureRequired; + this.expirationTime = expirationTime; + this.autoRenewPeriod = autoRenewPeriod; + this.liveHashes = liveHashes; + this.tokenRelationships = Collections.unmodifiableMap(tokenRelationships); + this.accountMemo = accountMemo; + this.ownedNfts = ownedNfts; + this.maxAutomaticTokenAssociations = maxAutomaticTokenAssociations; + this.aliasKey = aliasKey; + this.ledgerId = ledgerId; + this.ethereumNonce = ethereumNonce; + this.hbarAllowances = Collections.emptyList(); + this.tokenAllowances = Collections.emptyList(); + this.tokenNftAllowances = Collections.emptyList(); + this.stakingInfo = stakingInfo; + } + + /** + * Retrieve the account info from a protobuf. + * + * @param accountInfo the account info protobuf + * @return the account info object + */ + static AccountInfo fromProtobuf(CryptoGetInfoResponse.AccountInfo accountInfo) { + var accountId = AccountId.fromProtobuf(accountInfo.getAccountID()); + + var proxyAccountId = accountInfo.getProxyAccountID().getAccountNum() > 0 + ? AccountId.fromProtobuf(accountInfo.getProxyAccountID()) + : null; + + var liveHashes = Arrays.stream(accountInfo.getLiveHashesList().toArray()) + .map((liveHash) -> LiveHash.fromProtobuf((org.hiero.sdk.proto.LiveHash) liveHash)) + .collect(toList()); + + Map relationships = new HashMap<>(); + + for (org.hiero.sdk.proto.TokenRelationship relationship : accountInfo.getTokenRelationshipsList()) { + TokenId tokenId = TokenId.fromProtobuf(relationship.getTokenId()); + relationships.put(tokenId, TokenRelationship.fromProtobuf(relationship)); + } + + @Nullable var aliasKey = PublicKey.fromAliasBytes(accountInfo.getAlias()); + + return new AccountInfo( + accountId, + accountInfo.getContractAccountID(), + accountInfo.getDeleted(), + proxyAccountId, + accountInfo.getProxyReceived(), + Key.fromProtobufKey(accountInfo.getKey()), + accountInfo.getBalance(), + accountInfo.getGenerateSendRecordThreshold(), + accountInfo.getGenerateReceiveRecordThreshold(), + accountInfo.getReceiverSigRequired(), + InstantConverter.fromProtobuf(accountInfo.getExpirationTime()), + DurationConverter.fromProtobuf(accountInfo.getAutoRenewPeriod()), + liveHashes, + relationships, + accountInfo.getMemo(), + accountInfo.getOwnedNfts(), + accountInfo.getMaxAutomaticTokenAssociations(), + aliasKey, + LedgerId.fromByteString(accountInfo.getLedgerId()), + accountInfo.getEthereumNonce(), + accountInfo.hasStakingInfo() ? StakingInfo.fromProtobuf(accountInfo.getStakingInfo()) : null); + } + + /** + * Retrieve the account info from a protobuf byte array. + * + * @param bytes a byte array representing the protobuf + * @return the account info object + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + public static AccountInfo fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + return fromProtobuf( + CryptoGetInfoResponse.AccountInfo.parseFrom(bytes).toBuilder().build()); + } + + /** + * Convert an account info object into a protobuf. + * + * @return the protobuf object + */ + CryptoGetInfoResponse.AccountInfo toProtobuf() { + var hashes = Arrays.stream(liveHashes.toArray()) + .map((liveHash) -> ((LiveHash) liveHash).toProtobuf()) + .collect(toList()); + + var accountInfoBuilder = CryptoGetInfoResponse.AccountInfo.newBuilder() + .setAccountID(accountId.toProtobuf()) + .setDeleted(isDeleted) + .setProxyReceived(proxyReceived.toTinybars()) + .setKey(key.toProtobufKey()) + .setBalance(balance.toTinybars()) + .setGenerateSendRecordThreshold(sendRecordThreshold.toTinybars()) + .setGenerateReceiveRecordThreshold(receiveRecordThreshold.toTinybars()) + .setReceiverSigRequired(isReceiverSignatureRequired) + .setExpirationTime(InstantConverter.toProtobuf(expirationTime)) + .setAutoRenewPeriod(DurationConverter.toProtobuf(autoRenewPeriod)) + .addAllLiveHashes(hashes) + .setMemo(accountMemo) + .setOwnedNfts(ownedNfts) + .setMaxAutomaticTokenAssociations(maxAutomaticTokenAssociations) + .setLedgerId(ledgerId.toByteString()) + .setEthereumNonce(ethereumNonce); + + if (contractAccountId != null) { + accountInfoBuilder.setContractAccountID(contractAccountId); + } + + if (proxyAccountId != null) { + accountInfoBuilder.setProxyAccountID(proxyAccountId.toProtobuf()); + } + + if (aliasKey != null) { + accountInfoBuilder.setAlias(aliasKey.toProtobufKey().toByteString()); + } + + if (stakingInfo != null) { + accountInfoBuilder.setStakingInfo(stakingInfo.toProtobuf()); + } + + return accountInfoBuilder.build(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("accountId", accountId) + .add("contractAccountId", contractAccountId) + .add("deleted", isDeleted) + .add("proxyAccountId", proxyAccountId) + .add("proxyReceived", proxyReceived) + .add("key", key) + .add("balance", balance) + .add("sendRecordThreshold", sendRecordThreshold) + .add("receiveRecordThreshold", receiveRecordThreshold) + .add("receiverSignatureRequired", isReceiverSignatureRequired) + .add("expirationTime", expirationTime) + .add("autoRenewPeriod", autoRenewPeriod) + .add("liveHashes", liveHashes) + .add("tokenRelationships", tokenRelationships) + .add("accountMemo", accountMemo) + .add("ownedNfts", ownedNfts) + .add("maxAutomaticTokenAssociations", maxAutomaticTokenAssociations) + .add("aliasKey", aliasKey) + .add("ledgerId", ledgerId) + .add("ethereumNonce", ethereumNonce) + .add("stakingInfo", stakingInfo) + .toString(); + } + + /** + * Extract a byte array representation. + * + * @return a byte array representation + */ + public byte[] toBytes() { + return toProtobuf().toByteArray(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/AccountInfoFlow.java b/sdk/src/main/java/org/hiero/sdk/AccountInfoFlow.java new file mode 100644 index 0000000000..11ebc1d315 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/AccountInfoFlow.java @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeoutException; + +/** + * Account Info Flow object. + */ +public class AccountInfoFlow { + + private static PublicKey getAccountPublicKey(Client client, AccountId accountId) + throws PrecheckStatusException, TimeoutException { + return requirePublicKey( + accountId, new AccountInfoQuery().setAccountId(accountId).execute(client).key); + } + + private static CompletableFuture getAccountPublicKeyAsync(Client client, AccountId accountId) { + return new AccountInfoQuery() + .setAccountId(accountId) + .executeAsync(client) + .thenApply(accountInfo -> { + return requirePublicKey(accountId, accountInfo.key); + }); + } + + private static PublicKey requirePublicKey(AccountId accountId, Key key) { + if (key instanceof PublicKey k) { + return k; + } + throw new UnsupportedOperationException("Account " + accountId + " has a KeyList key, which is not supported"); + } + + /** + * Is the signature valid. + * + * @param client the client + * @param accountId the account id + * @param message the message + * @param signature the signature + * @return is the signature valid + * @throws PrecheckStatusException when the precheck fails + * @throws TimeoutException when the transaction times out + */ + public static boolean verifySignature(Client client, AccountId accountId, byte[] message, byte[] signature) + throws PrecheckStatusException, TimeoutException { + return getAccountPublicKey(client, accountId).verify(message, signature); + } + + /** + * Is the transaction signature valid. + * + * @param client the client + * @param accountId the account id + * @param transaction the signed transaction + * @return is the transaction signature valid + * @throws PrecheckStatusException when the precheck fails + * @throws TimeoutException when the transaction times out + */ + public static boolean verifyTransactionSignature(Client client, AccountId accountId, Transaction transaction) + throws PrecheckStatusException, TimeoutException { + return getAccountPublicKey(client, accountId).verifyTransaction(transaction); + } + + /** + * Asynchronously determine if the signature is valid. + * + * @param client the client + * @param accountId the account id + * @param message the message + * @param signature the signature + * @return is the signature valid + */ + public static CompletableFuture verifySignatureAsync( + Client client, AccountId accountId, byte[] message, byte[] signature) { + return getAccountPublicKeyAsync(client, accountId).thenApply(pubKey -> pubKey.verify(message, signature)); + } + + /** + * Asynchronously determine if the signature is valid. + * + * @param client the client + * @param accountId the account id + * @param transaction the signed transaction + * @return is the signature valid + */ + public static CompletableFuture verifyTransactionSignatureAsync( + Client client, AccountId accountId, Transaction transaction) { + return getAccountPublicKeyAsync(client, accountId).thenApply(pubKey -> pubKey.verifyTransaction(transaction)); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/AccountInfoQuery.java b/sdk/src/main/java/org/hiero/sdk/AccountInfoQuery.java new file mode 100644 index 0000000000..28db512ed8 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/AccountInfoQuery.java @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.grpc.MethodDescriptor; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.CryptoGetInfoQuery; +import org.hiero.sdk.proto.CryptoServiceGrpc; +import org.hiero.sdk.proto.QueryHeader; +import org.hiero.sdk.proto.Response; +import org.hiero.sdk.proto.ResponseHeader; + +/** + * Get all the information about an account, including the balance. + * This does not get the list of account records. + */ +public final class AccountInfoQuery extends Query { + + @Nullable + private AccountId accountId = null; + + /** + * Constructor. + */ + public AccountInfoQuery() {} + + /** + * Extract the account id. + * + * @return the account id + */ + @Nullable + public AccountId getAccountId() { + return accountId; + } + + /** + * Sets the account ID for which information is requested. + * + * @param accountId The AccountId to be set + * @return {@code this} + */ + public AccountInfoQuery setAccountId(AccountId accountId) { + Objects.requireNonNull(accountId); + this.accountId = accountId; + return this; + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (accountId != null) { + accountId.validateChecksum(client); + } + } + + @Override + void onMakeRequest(org.hiero.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { + var builder = CryptoGetInfoQuery.newBuilder(); + + if (accountId != null) { + builder.setAccountID(accountId.toProtobuf()); + } + + queryBuilder.setCryptoGetInfo(builder.setHeader(header)); + } + + @Override + ResponseHeader mapResponseHeader(Response response) { + return response.getCryptoGetInfo().getHeader(); + } + + @Override + QueryHeader mapRequestHeader(org.hiero.sdk.proto.Query request) { + return request.getCryptoGetInfo().getHeader(); + } + + @Override + AccountInfo mapResponse(Response response, AccountId nodeId, org.hiero.sdk.proto.Query request) { + return AccountInfo.fromProtobuf(response.getCryptoGetInfo().getAccountInfo()); + } + + @Override + MethodDescriptor getMethodDescriptor() { + return CryptoServiceGrpc.getGetAccountInfoMethod(); + } + + @Override + public CompletableFuture getCostAsync(Client client) { + // deleted accounts return a COST_ANSWER of zero which triggers `INSUFFICIENT_TX_FEE` + // if you set that as the query payment; 25 tinybar seems to be enough to get + // `ACCOUNT_DELETED` back instead. + return super.getCostAsync(client).thenApply((cost) -> Hbar.fromTinybars(Math.max(cost.toTinybars(), 25))); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/AccountRecordsQuery.java b/sdk/src/main/java/org/hiero/sdk/AccountRecordsQuery.java new file mode 100644 index 0000000000..f14815cc92 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/AccountRecordsQuery.java @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.grpc.MethodDescriptor; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.CryptoGetAccountRecordsQuery; +import org.hiero.sdk.proto.CryptoServiceGrpc; +import org.hiero.sdk.proto.QueryHeader; +import org.hiero.sdk.proto.Response; +import org.hiero.sdk.proto.ResponseHeader; + +/** + * Get all the records for an account for any transfers into it and out of it, + * that were above the threshold, during the last 25 hours. + */ +public final class AccountRecordsQuery extends Query, AccountRecordsQuery> { + @Nullable + private AccountId accountId = null; + + /** + * Constructor. + */ + public AccountRecordsQuery() {} + + /** + * Extract the account id. + * + * @return the account id + */ + @Nullable + public AccountId getAccountId() { + return accountId; + } + + /** + * Sets the account ID for which the records should be retrieved. + * + * @param accountId The AccountId to be set + * @return {@code this} + */ + public AccountRecordsQuery setAccountId(AccountId accountId) { + Objects.requireNonNull(accountId); + this.accountId = accountId; + return this; + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (accountId != null) { + accountId.validateChecksum(client); + } + } + + @Override + void onMakeRequest(org.hiero.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { + var builder = CryptoGetAccountRecordsQuery.newBuilder(); + + if (accountId != null) { + builder.setAccountID(accountId.toProtobuf()); + } + + queryBuilder.setCryptoGetAccountRecords(builder.setHeader(header)); + } + + @Override + ResponseHeader mapResponseHeader(Response response) { + return response.getCryptoGetAccountRecords().getHeader(); + } + + @Override + QueryHeader mapRequestHeader(org.hiero.sdk.proto.Query request) { + return request.getCryptoGetAccountRecords().getHeader(); + } + + @Override + List mapResponse(Response response, AccountId nodeId, org.hiero.sdk.proto.Query request) { + var rawTransactionRecords = response.getCryptoGetAccountRecords().getRecordsList(); + var transactionRecords = new ArrayList(rawTransactionRecords.size()); + + for (var record : rawTransactionRecords) { + transactionRecords.add(TransactionRecord.fromProtobuf(record)); + } + + return transactionRecords; + } + + @Override + MethodDescriptor getMethodDescriptor() { + return CryptoServiceGrpc.getGetAccountRecordsMethod(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/AccountStakersQuery.java b/sdk/src/main/java/org/hiero/sdk/AccountStakersQuery.java new file mode 100644 index 0000000000..1d013f4e2f --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/AccountStakersQuery.java @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.grpc.MethodDescriptor; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.CryptoGetStakersQuery; +import org.hiero.sdk.proto.CryptoServiceGrpc; +import org.hiero.sdk.proto.QueryHeader; +import org.hiero.sdk.proto.Response; +import org.hiero.sdk.proto.ResponseHeader; + +/** + * Get all the accounts that are proxy staking to this account. + * For each of them, give the amount currently staked. + *

+ * This is not yet implemented, but will be in a future version of the API. + */ +public final class AccountStakersQuery extends Query, AccountStakersQuery> { + @Nullable + private AccountId accountId = null; + + /** + * Constructor. + */ + public AccountStakersQuery() {} + + /** + * Extract the account id. + * + * @return the account id + */ + @Nullable + public AccountId getAccountId() { + return accountId; + } + + /** + * Sets the Account ID for which the records should be retrieved. + * + * @param accountId The AccountId to be set + * @return {@code this} + */ + public AccountStakersQuery setAccountId(AccountId accountId) { + Objects.requireNonNull(accountId); + this.accountId = accountId; + return this; + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (accountId != null) { + accountId.validateChecksum(client); + } + } + + @Override + void onMakeRequest(org.hiero.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { + var builder = CryptoGetStakersQuery.newBuilder(); + + if (accountId != null) { + builder.setAccountID(accountId.toProtobuf()); + } + + queryBuilder.setCryptoGetProxyStakers(builder.setHeader(header)); + } + + @Override + ResponseHeader mapResponseHeader(Response response) { + return response.getCryptoGetProxyStakers().getHeader(); + } + + @Override + QueryHeader mapRequestHeader(org.hiero.sdk.proto.Query request) { + return request.getCryptoGetProxyStakers().getHeader(); + } + + @Override + List mapResponse(Response response, AccountId nodeId, org.hiero.sdk.proto.Query request) { + var rawStakers = response.getCryptoGetProxyStakers().getStakers(); + var stakers = new ArrayList(rawStakers.getProxyStakerCount()); + + for (var i = 0; i < rawStakers.getProxyStakerCount(); ++i) { + stakers.add(ProxyStaker.fromProtobuf(rawStakers.getProxyStaker(i))); + } + + return stakers; + } + + @Override + MethodDescriptor getMethodDescriptor() { + return CryptoServiceGrpc.getGetStakersByAccountIDMethod(); + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountUpdateTransaction.java b/sdk/src/main/java/org/hiero/sdk/AccountUpdateTransaction.java similarity index 90% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/AccountUpdateTransaction.java rename to sdk/src/main/java/org/hiero/sdk/AccountUpdateTransaction.java index 3b7fa00b93..35415f8d89 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/AccountUpdateTransaction.java +++ b/sdk/src/main/java/org/hiero/sdk/AccountUpdateTransaction.java @@ -1,40 +1,21 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.BoolValue; import com.google.protobuf.Int32Value; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.StringValue; -import com.hedera.hashgraph.sdk.proto.CryptoServiceGrpc; -import com.hedera.hashgraph.sdk.proto.CryptoUpdateTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; import io.grpc.MethodDescriptor; import java.time.Duration; import java.time.Instant; - -import javax.annotation.Nullable; import java.util.LinkedHashMap; import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.CryptoServiceGrpc; +import org.hiero.sdk.proto.CryptoUpdateTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; /** * Change properties for the given account. @@ -55,20 +36,28 @@ public final class AccountUpdateTransaction extends Transaction { @Nullable private AccountId accountId = null; + @Nullable private AccountId proxyAccountId = null; + @Nullable private Key key = null; + @Nullable private Instant expirationTime = null; + @Nullable private Duration autoRenewPeriod = null; + @Nullable private Boolean receiverSigRequired = null; + @Nullable private String accountMemo = null; + @Nullable private Integer maxAutomaticTokenAssociations = null; + @Nullable private Key aliasKey; @@ -84,8 +73,7 @@ public final class AccountUpdateTransaction extends Transaction> txs) throws InvalidProtocolBufferException { + AccountUpdateTransaction( + LinkedHashMap> txs) + throws InvalidProtocolBufferException { super(txs); initFromTransactionBody(); } @@ -103,7 +93,7 @@ public AccountUpdateTransaction() { * * @param txBody protobuf TransactionBody */ - AccountUpdateTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { + AccountUpdateTransaction(org.hiero.sdk.proto.TransactionBody txBody) { super(txBody); initFromTransactionBody(); } @@ -485,7 +475,8 @@ void initFromTransactionBody() { accountMemo = body.getMemo().getValue(); } if (body.hasMaxAutomaticTokenAssociations()) { - maxAutomaticTokenAssociations = body.getMaxAutomaticTokenAssociations().getValue(); + maxAutomaticTokenAssociations = + body.getMaxAutomaticTokenAssociations().getValue(); } if (body.hasDeclineReward()) { @@ -502,7 +493,7 @@ void initFromTransactionBody() { } @Override - MethodDescriptor getMethodDescriptor() { + MethodDescriptor getMethodDescriptor() { return CryptoServiceGrpc.getUpdateAccountMethod(); } @@ -545,7 +536,8 @@ CryptoUpdateTransactionBody.Builder build() { } if (declineStakingReward != null) { - builder.setDeclineReward(BoolValue.newBuilder().setValue(declineStakingReward).build()); + builder.setDeclineReward( + BoolValue.newBuilder().setValue(declineStakingReward).build()); } return builder; diff --git a/sdk/src/main/java/org/hiero/sdk/AddressBookQuery.java b/sdk/src/main/java/org/hiero/sdk/AddressBookQuery.java new file mode 100644 index 0000000000..fb7bf1ec5b --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/AddressBookQuery.java @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.grpc.CallOptions; +import io.grpc.ClientCall; +import io.grpc.Deadline; +import io.grpc.Status; +import io.grpc.StatusRuntimeException; +import io.grpc.stub.ClientCalls; +import io.grpc.stub.StreamObserver; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import javax.annotation.Nonnegative; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.mirror.NetworkServiceGrpc; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Query the mirror node for the address book. + */ +public class AddressBookQuery { + private static final Logger LOGGER = LoggerFactory.getLogger(AddressBookQuery.class); + + @Nullable + private FileId fileId = null; + + @Nullable + private Integer limit = null; + + private int maxAttempts = 10; + private Duration maxBackoff = Duration.ofSeconds(8L); + + /** + * Constructor. + */ + public AddressBookQuery() {} + + private static boolean shouldRetry(Throwable throwable) { + if (throwable instanceof StatusRuntimeException statusRuntimeException) { + var code = statusRuntimeException.getStatus().getCode(); + var description = statusRuntimeException.getStatus().getDescription(); + + return (code == io.grpc.Status.Code.UNAVAILABLE) + || (code == io.grpc.Status.Code.RESOURCE_EXHAUSTED) + || (code == Status.Code.INTERNAL + && description != null + && Executable.RST_STREAM.matcher(description).matches()); + } + + return false; + } + + /** + * Extract the file id. + * + * @return the file id that was assigned + */ + @Nullable + public FileId getFileId() { + return fileId; + } + + /** + * Assign the file id of address book to retrieve. + * + * @param fileId the file id of the address book + * @return {@code this} + */ + public AddressBookQuery setFileId(FileId fileId) { + this.fileId = fileId; + return this; + } + + /** + * Extract the limit number. + * + * @return the limit number that was assigned + */ + @Nullable + public Integer getLimit() { + return limit; + } + + /** + * Assign the number of node addresses to retrieve or all nodes set to 0. + * + * @param limit number of node addresses to get + * @return {@code this} + */ + public AddressBookQuery setLimit(@Nullable @Nonnegative Integer limit) { + this.limit = limit; + return this; + } + + /** + * Extract the maximum number of attempts. + * + * @return the maximum number of attempts + */ + public int getMaxAttempts() { + return maxAttempts; + } + + /** + * Assign the maximum number of attempts. + * + * @param maxAttempts the maximum number of attempts + * @return {@code this} + */ + public AddressBookQuery setMaxAttempts(@Nonnegative int maxAttempts) { + this.maxAttempts = maxAttempts; + return this; + } + + /** + * Assign the maximum backoff duration. + * + * @param maxBackoff the maximum backoff duration + * @return {@code this} + */ + public AddressBookQuery setMaxBackoff(Duration maxBackoff) { + Objects.requireNonNull(maxBackoff); + if (maxBackoff.toMillis() < 500L) { + throw new IllegalArgumentException("maxBackoff must be at least 500 ms"); + } + this.maxBackoff = maxBackoff; + return this; + } + + /** + * Execute the query with preset timeout. + * + * @param client the client object + * @return the node address book + */ + public NodeAddressBook execute(Client client) { + return execute(client, client.getRequestTimeout()); + } + + /** + * Execute the query with user supplied timeout. + * + * @param client the client object + * @param timeout the user supplied timeout + * @return the node address book + */ + public NodeAddressBook execute(Client client, Duration timeout) { + var deadline = Deadline.after(timeout.toMillis(), TimeUnit.MILLISECONDS); + for (int attempt = 1; true; attempt++) { + try { + var addressProtoIter = + ClientCalls.blockingServerStreamingCall(buildCall(client, deadline), buildQuery()); + List addresses = new ArrayList<>(); + while (addressProtoIter.hasNext()) { + addresses.add(NodeAddress.fromProtobuf(addressProtoIter.next())); + } + return new NodeAddressBook().setNodeAddresses(addresses); + } catch (Throwable error) { + if (!shouldRetry(error) || attempt >= maxAttempts) { + LOGGER.error("Error attempting to get address book at FileId {}", fileId, error); + throw error; + } + warnAndDelay(attempt, error); + } + } + } + + /** + * Execute the query with preset timeout asynchronously. + * + * @param client the client object + * @return the node address book + */ + public CompletableFuture executeAsync(Client client) { + return executeAsync(client, client.getRequestTimeout()); + } + + /** + * Execute the query with user supplied timeout. + * + * @param client the client object + * @param timeout the user supplied timeout + * @return the node address book + */ + public CompletableFuture executeAsync(Client client, Duration timeout) { + var deadline = Deadline.after(timeout.toMillis(), TimeUnit.MILLISECONDS); + CompletableFuture returnFuture = new CompletableFuture<>(); + executeAsync(client, deadline, returnFuture, 1); + return returnFuture; + } + + /** + * Execute the query. + * + * @param client the client object + * @param deadline the user supplied timeout + * @param returnFuture returned promise callback + * @param attempt maximum number of attempts + */ + void executeAsync(Client client, Deadline deadline, CompletableFuture returnFuture, int attempt) { + List addresses = new ArrayList<>(); + ClientCalls.asyncServerStreamingCall( + buildCall(client, deadline), buildQuery(), new StreamObserver() { + @Override + public void onNext(org.hiero.sdk.proto.NodeAddress addressProto) { + addresses.add(NodeAddress.fromProtobuf(addressProto)); + } + + @Override + public void onError(Throwable error) { + if (attempt >= maxAttempts || !shouldRetry(error)) { + LOGGER.error("Error attempting to get address book at FileId {}", fileId, error); + returnFuture.completeExceptionally(error); + return; + } + warnAndDelay(attempt, error); + addresses.clear(); + executeAsync(client, deadline, returnFuture, attempt + 1); + } + + @Override + public void onCompleted() { + returnFuture.complete(new NodeAddressBook().setNodeAddresses(addresses)); + } + }); + } + + /** + * Build the address book query. + * + * @return {@link org.hiero.sdk.proto.mirror.AddressBookQuery buildQuery } + */ + org.hiero.sdk.proto.mirror.AddressBookQuery buildQuery() { + var builder = org.hiero.sdk.proto.mirror.AddressBookQuery.newBuilder(); + if (fileId != null) { + builder.setFileId(fileId.toProtobuf()); + } + if (limit != null) { + builder.setLimit(limit); + } + return builder.build(); + } + + private ClientCall buildCall( + Client client, Deadline deadline) { + try { + return client.mirrorNetwork + .getNextMirrorNode() + .getChannel() + .newCall(NetworkServiceGrpc.getGetNodesMethod(), CallOptions.DEFAULT.withDeadline(deadline)); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + private void warnAndDelay(int attempt, Throwable error) { + var delay = Math.min(500 * (long) Math.pow(2, attempt), maxBackoff.toMillis()); + LOGGER.warn( + "Error fetching address book at FileId {} during attempt #{}. Waiting {} ms before next attempt: {}", + fileId, + attempt, + delay, + error.getMessage()); + + try { + Thread.sleep(delay); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/AssessedCustomFee.java b/sdk/src/main/java/org/hiero/sdk/AssessedCustomFee.java new file mode 100644 index 0000000000..62ca4ee656 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/AssessedCustomFee.java @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import com.google.protobuf.InvalidProtocolBufferException; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nullable; + +/** + * A custom transfer fee that was assessed during the handling of a CryptoTransfer. + */ +public class AssessedCustomFee { + /** + * The number of units assessed for the fee + */ + public final long amount; + + /** + * The denomination of the fee; taken as hbar if left unset + */ + @Nullable + public final TokenId tokenId; + + /** + * The account to receive the assessed fee + */ + @Nullable + public final AccountId feeCollectorAccountId; + + /** + * The account(s) whose final balances would have been higher in the absence of this assessed fee + */ + public final List payerAccountIdList; + + AssessedCustomFee( + long amount, + @Nullable TokenId tokenId, + @Nullable AccountId feeCollectorAccountId, + List payerAccountIdList) { + this.amount = amount; + this.tokenId = tokenId; + this.feeCollectorAccountId = feeCollectorAccountId; + this.payerAccountIdList = payerAccountIdList; + } + + /** + * Convert the protobuf object to an assessed custom fee object. + * + * @param assessedCustomFee protobuf response object + * @return the converted assessed custom fee object + */ + static AssessedCustomFee fromProtobuf(org.hiero.sdk.proto.AssessedCustomFee assessedCustomFee) { + var payerList = new ArrayList(assessedCustomFee.getEffectivePayerAccountIdCount()); + for (var payerId : assessedCustomFee.getEffectivePayerAccountIdList()) { + payerList.add(AccountId.fromProtobuf(payerId)); + } + return new AssessedCustomFee( + assessedCustomFee.getAmount(), + assessedCustomFee.hasTokenId() ? TokenId.fromProtobuf(assessedCustomFee.getTokenId()) : null, + assessedCustomFee.hasFeeCollectorAccountId() + ? AccountId.fromProtobuf(assessedCustomFee.getFeeCollectorAccountId()) + : null, + payerList); + } + + /** + * Convert a byte array into an assessed custom fee object. + * + * @param bytes the byte array + * @return the converted assessed custom fee object + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + public static AssessedCustomFee fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + return fromProtobuf(org.hiero.sdk.proto.AssessedCustomFee.parseFrom(bytes).toBuilder() + .build()); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("amount", amount) + .add("tokenId", tokenId) + .add("feeCollectorAccountId", feeCollectorAccountId) + .add("payerAccountIdList", payerAccountIdList) + .toString(); + } + + /** + * Create the protobuf representation. + * + * @return {@link org.hiero.sdk.proto.AssessedCustomFee} + */ + org.hiero.sdk.proto.AssessedCustomFee toProtobuf() { + var builder = org.hiero.sdk.proto.AssessedCustomFee.newBuilder().setAmount(amount); + if (tokenId != null) { + builder.setTokenId(tokenId.toProtobuf()); + } + if (feeCollectorAccountId != null) { + builder.setFeeCollectorAccountId(feeCollectorAccountId.toProtobuf()); + } + for (var payerId : payerAccountIdList) { + builder.addEffectivePayerAccountId(payerId.toProtobuf()); + } + return builder.build(); + } + + /** + * Create a byte array representation. + * + * @return the converted assessed custom fees + */ + public byte[] toBytes() { + return toProtobuf().toByteArray(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/BadEntityIdException.java b/sdk/src/main/java/org/hiero/sdk/BadEntityIdException.java new file mode 100644 index 0000000000..c78ae02ee8 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/BadEntityIdException.java @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +/** + * Custom exception thrown by the entity helper validate method when the account id and checksum are invalid. + */ +public class BadEntityIdException extends Exception { + /** + * the shard portion of the account id + */ + public final long shard; + /** + * the realm portion of the account id + */ + public final long realm; + /** + * the num portion of the account id + */ + public final long num; + /** + * the user supplied checksum + */ + public final String presentChecksum; + /** + * the calculated checksum + */ + public final String expectedChecksum; + + /** + * Constructor. + * + * @param shard the shard portion of the account id + * @param realm the realm portion of the account id + * @param num the num portion of the account id + * @param presentChecksum the user supplied checksum + * @param expectedChecksum the calculated checksum + */ + BadEntityIdException(long shard, long realm, long num, String presentChecksum, String expectedChecksum) { + super(String.format("Entity ID %d.%d.%d-%s was incorrect.", shard, realm, num, presentChecksum)); + this.shard = shard; + this.realm = realm; + this.num = num; + this.presentChecksum = presentChecksum; + this.expectedChecksum = expectedChecksum; + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/BadKeyException.java b/sdk/src/main/java/org/hiero/sdk/BadKeyException.java new file mode 100644 index 0000000000..24782389c3 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/BadKeyException.java @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +/** + * Signals that a key could not be realized from the given input. + *

+ * This exception can be raised by any of the {@code from} methods + * on {@link PrivateKey} or {@link PublicKey}. + */ +public final class BadKeyException extends IllegalArgumentException { + /** + * @param message the message + */ + BadKeyException(String message) { + super(message); + } + + /** + * @param cause the cause + */ + BadKeyException(Throwable cause) { + super(cause); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/BadMnemonicException.java b/sdk/src/main/java/org/hiero/sdk/BadMnemonicException.java new file mode 100644 index 0000000000..3cc6080d78 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/BadMnemonicException.java @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import java.util.List; +import javax.annotation.Nullable; + +/** + * Custom exception for when there are issues with the mnemonic. + */ +public class BadMnemonicException extends Exception { + /** + * The mnemonic that failed validation. + */ + public final Mnemonic mnemonic; + + /** + * The reason for which the mnemonic failed validation. + */ + public final BadMnemonicReason reason; + + /** + * If not null, these are the indices in the mnemonic that were not found in the + * BIP-39 standard English word list. + *

+ * If {@code reason == BadMnemonicReason.UnknownWords} then this will be not null. + */ + @Nullable + public final List unknownWordIndices; + + /** + * Constructor. + * + * @param mnemonic the mnemonic + * @param reason the reason + * @param unknownWordIndices the indices + */ + BadMnemonicException(Mnemonic mnemonic, BadMnemonicReason reason, List unknownWordIndices) { + this.mnemonic = mnemonic; + this.reason = reason; + this.unknownWordIndices = unknownWordIndices; + } + + /** + * Constructor. + * + * @param mnemonic the mnemonic + * @param reason the reason + */ + BadMnemonicException(Mnemonic mnemonic, BadMnemonicReason reason) { + this.mnemonic = mnemonic; + this.reason = reason; + this.unknownWordIndices = null; + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/BadMnemonicReason.java b/sdk/src/main/java/org/hiero/sdk/BadMnemonicReason.java new file mode 100644 index 0000000000..b448ce31a9 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/BadMnemonicReason.java @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +/** + * Possible reason why a {@link Mnemonic} failed validation. + */ +public enum BadMnemonicReason { + /** + * The mnemonic did not contain exactly 24 words. + */ + BadLength, + + /** + * The mnemonic contained words which were not found in the BIP-39 standard English word list. + *

+ * {@link BadMnemonicException#unknownWordIndices} will be set with the list of word indices + * in {@link Mnemonic#words} which were not found in the standard word list. + * + * @see BIP-39 + * English word list. + */ + UnknownWords, + + /** + * The checksum encoded in the mnemonic did not match the checksum we just calculated for + * that mnemonic. + *

+ * 24-word mnemonics have an 8-bit checksum that is appended to the 32 bytes of source entropy + * after being calculated from it, before being encoded into words. This status is returned if + * {@link Mnemonic#validate()} calculated a different checksum for the mnemonic than that which + * was encoded into it. + *

+ * This could happen if two or more of the words were entered out of the original order or + * replaced with another from the standard word list (as this is only returned if all the words + * exist in the word list). + */ + ChecksumMismatch, + /** + * The given mnemonic doesn't contain 22 words required to be a legacy mnemonic, or the words are + * not in the legacy list. + */ + NotLegacy +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/BaseNetwork.java b/sdk/src/main/java/org/hiero/sdk/BaseNetwork.java similarity index 92% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/BaseNetwork.java rename to sdk/src/main/java/org/hiero/sdk/BaseNetwork.java index 0897a0bfe0..d883e4dc90 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/BaseNetwork.java +++ b/sdk/src/main/java/org/hiero/sdk/BaseNetwork.java @@ -1,34 +1,15 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.common.annotations.VisibleForTesting; import java.time.Duration; import java.time.Instant; - -import javax.annotation.Nullable; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import javax.annotation.Nullable; /** * Abstracts away most of the similar functionality between {@link Network} and {@link MirrorNetwork} @@ -38,9 +19,9 @@ * @param - The specific node type for this network. */ abstract class BaseNetwork< - BaseNetworkT extends BaseNetwork, - KeyT, - BaseNodeT extends BaseNode> { + BaseNetworkT extends BaseNetwork, + KeyT, + BaseNodeT extends BaseNode> { protected static final Integer DEFAULT_MAX_NODE_ATTEMPTS = -1; protected static final Random random = new Random(); @@ -220,7 +201,7 @@ synchronized BaseNetworkT setMaxNodeBackoff(Duration maxNodeBackoff) { * * @return the minimum node readmit time */ - synchronized public Duration getMinNodeReadmitTime() { + public synchronized Duration getMinNodeReadmitTime() { return minNodeReadmitTime; } @@ -229,7 +210,7 @@ synchronized public Duration getMinNodeReadmitTime() { * * @param minNodeReadmitTime the minimum node readmit time */ - synchronized public void setMinNodeReadmitTime(Duration minNodeReadmitTime) { + public synchronized void setMinNodeReadmitTime(Duration minNodeReadmitTime) { this.minNodeReadmitTime = minNodeReadmitTime; for (var node : nodes) { @@ -310,12 +291,10 @@ protected List getNodesToRemove(Map network) { return nodes; } - private boolean nodeIsInGivenNetwork(BaseNodeT node, Map network) { + private boolean nodeIsInGivenNetwork(BaseNodeT node, Map network) { for (var entry : network.entrySet()) { - if ( - node.getKey().equals(entry.getValue()) && - node.address.equals(BaseNodeAddress.fromString(entry.getKey())) - ) { + if (node.getKey().equals(entry.getValue()) + && node.address.equals(BaseNodeAddress.fromString(entry.getKey()))) { return true; } } @@ -367,7 +346,8 @@ synchronized BaseNetworkT setNetwork(Map network) throws TimeoutEx for (var entry : network.entrySet()) { var node = createNodeFromNetworkEntry(entry); - if (newNodeKeys.contains(node.getKey()) && newNodeAddresses.contains(node.getAddress().toString())) { + if (newNodeKeys.contains(node.getKey()) + && newNodeAddresses.contains(node.getAddress().toString())) { continue; } @@ -459,13 +439,13 @@ synchronized void readmitNodes() { } } - this.earliestReadmitTime = nextEarliestReadmitTime; if (this.earliestReadmitTime.isBefore(now.plus(minNodeReadmitTime))) { this.earliestReadmitTime = now.plus(minNodeReadmitTime); } - outer: for (var i = 0; i < this.nodes.size(); i++) { + outer: + for (var i = 0; i < this.nodes.size(); i++) { // Check if `healthyNodes` already contains this node for (var j = 0; j < this.healthyNodes.size(); j++) { if (this.nodes.get(i) == this.healthyNodes.get(j)) { @@ -478,7 +458,6 @@ synchronized void readmitNodes() { this.healthyNodes.add(this.nodes.get(i)); } } - } } @@ -531,7 +510,7 @@ protected synchronized List getNumberOfMostHealthyNodes(int count) th var returnNodes = new HashMap(count); - for (var i = 0; i < count; i++ ) { + for (var i = 0; i < count; i++) { var node = getRandomNode(); if (!returnNodes.containsKey(node.getKey())) { @@ -544,7 +523,6 @@ protected synchronized List getNumberOfMostHealthyNodes(int count) th return returnList; } - synchronized void beginClose() { for (var node : nodes) { if (node.channel != null) { @@ -563,7 +541,8 @@ synchronized Throwable awaitClose(Instant deadline, @Nullable Throwable previous for (var node : nodes) { if (node.channel != null) { - var timeoutMillis = Duration.between(Instant.now(), deadline).toMillis(); + var timeoutMillis = + Duration.between(Instant.now(), deadline).toMillis(); if (timeoutMillis <= 0 || !node.channel.awaitTermination(timeoutMillis, TimeUnit.MILLISECONDS)) { throw new TimeoutException("Failed to properly shutdown all channels"); } else { diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/BaseNode.java b/sdk/src/main/java/org/hiero/sdk/BaseNode.java similarity index 91% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/BaseNode.java rename to sdk/src/main/java/org/hiero/sdk/BaseNode.java index 1b6ae177ef..4c06a07795 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/BaseNode.java +++ b/sdk/src/main/java/org/hiero/sdk/BaseNode.java @@ -1,23 +1,5 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import io.grpc.ChannelCredentials; import io.grpc.ConnectivityState; @@ -26,8 +8,6 @@ import io.grpc.ManagedChannelBuilder; import io.grpc.TlsChannelCredentials; import io.grpc.inprocess.InProcessChannelBuilder; - -import java.lang.module.ModuleDescriptor; import java.time.Duration; import java.time.Instant; import java.util.Objects; @@ -281,12 +261,12 @@ synchronized ManagedChannel getChannel() { } channel = channelBuilder - .keepAliveTimeout(10, TimeUnit.SECONDS) - .keepAliveWithoutCalls(true) - .disableRetry() - .userAgent(getUserAgent()) - .executor(executor) - .build(); + .keepAliveTimeout(10, TimeUnit.SECONDS) + .keepAliveWithoutCalls(true) + .disableRetry() + .userAgent(getUserAgent()) + .executor(executor) + .build(); return channel; } @@ -320,7 +300,6 @@ boolean channelFailedToConnect(Instant timeoutTime) { return !hasConnected; } - private CompletableFuture channelFailedToConnectAsync(int i, ConnectivityState state) { hasConnected = (state == ConnectivityState.READY); if (i >= GET_STATE_MAX_ATTEMPTS || hasConnected) { diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/BaseNodeAddress.java b/sdk/src/main/java/org/hiero/sdk/BaseNodeAddress.java similarity index 84% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/BaseNodeAddress.java rename to sdk/src/main/java/org/hiero/sdk/BaseNodeAddress.java index b3b9299695..e7600a3143 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/BaseNodeAddress.java +++ b/sdk/src/main/java/org/hiero/sdk/BaseNodeAddress.java @@ -1,27 +1,9 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; -import javax.annotation.Nullable; import java.util.Objects; import java.util.regex.Pattern; +import javax.annotation.Nullable; /** * Internal utility class. @@ -39,6 +21,7 @@ class BaseNodeAddress { @Nullable private final String address; + private final int port; private final boolean secure; @@ -169,7 +152,9 @@ public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; BaseNodeAddress that = (BaseNodeAddress) o; - return Objects.equals(getName(), that.getName()) && Objects.equals(getAddress(), that.getAddress()) && port == that.port; + return Objects.equals(getName(), that.getName()) + && Objects.equals(getAddress(), that.getAddress()) + && port == that.port; } @Override diff --git a/sdk/src/main/java/org/hiero/sdk/ChunkedTransaction.java b/sdk/src/main/java/org/hiero/sdk/ChunkedTransaction.java new file mode 100644 index 0000000000..9e549eb9b8 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/ChunkedTransaction.java @@ -0,0 +1,526 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.TimeoutException; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.SignatureMap; +import org.hiero.sdk.proto.SignedTransaction; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionID; + +/** + * A common base for file and topic message transactions. + */ +abstract class ChunkedTransaction> extends Transaction { + private int chunkSize = 1024; + + /** + * The transaction data + */ + protected ByteString data = ByteString.EMPTY; + + /** + * Maximum number of chunks this message will get broken up into when + * it's frozen. + */ + private int maxChunks = 20; + + /** + * Constructor. + * + * @param txs Compound list of transaction id's list of (AccountId, Transaction) records + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + ChunkedTransaction(LinkedHashMap> txs) + throws InvalidProtocolBufferException { + super(txs); + } + + /** + * Constructor. + * + * @param txBody protobuf TransactionBody + */ + ChunkedTransaction(org.hiero.sdk.proto.TransactionBody txBody) { + super(txBody); + } + + /** + * Constructor. + */ + ChunkedTransaction() { + super(); + } + + /** + * Extract the data. + * + * @return the data + */ + ByteString getData() { + return data; + } + + /** + * Assign the data via a byte array. + * + * @param data the byte array + * @return {@code this} + */ + T setData(byte[] data) { + requireNotFrozen(); + this.data = ByteString.copyFrom(data); + + // noinspection unchecked + return (T) this; + } + + /** + * Assign the data via a byte string. + * + * @param data the byte string + * @return {@code this} + */ + T setData(ByteString data) { + requireNotFrozen(); + this.data = data; + + // noinspection unchecked + return (T) this; + } + + /** + * Assign the data via a string. + * + * @param text the byte array + * @return {@code this} + */ + T setData(String text) { + requireNotFrozen(); + this.data = ByteString.copyFromUtf8(text); + + // noinspection unchecked + return (T) this; + } + + /** + * Retrieve the maximum number of chunks. + * + * @return the number of chunks + */ + public int getMaxChunks() { + return maxChunks; + } + + /** + * Assign the max number of chunks. + * + * @param maxChunks the number of chunks + * @return {@code this} + */ + public T setMaxChunks(int maxChunks) { + requireNotFrozen(); + this.maxChunks = maxChunks; + + // noinspection unchecked + return (T) this; + } + + /** + * Retrieve the chunk size. + * + * @return the chunk size + */ + public int getChunkSize() { + return chunkSize; + } + + /** + * Assign the chunk size. + * + * @param chunkSize the chunk size + * @return {@code this} + */ + public T setChunkSize(int chunkSize) { + requireNotFrozen(); + this.chunkSize = chunkSize; + + // noinspection unchecked + return (T) this; + } + + @Override + public byte[] getTransactionHash() { + if (outerTransactions.size() > nodeAccountIds.size()) { + throw new IllegalStateException( + "a single transaction hash can not be calculated for a chunked transaction, try calling `getAllTransactionHashesPerNode`"); + } + + return super.getTransactionHash(); + } + + @Override + public Map getTransactionHashPerNode() { + if (outerTransactions.size() > nodeAccountIds.size()) { + throw new IllegalStateException( + "a single transaction hash can not be calculated for a chunked transaction, try calling `getAllTransactionHashesPerNode`"); + } + + return super.getTransactionHashPerNode(); + } + + /** + * Extract the list of transaction hashes. + * + * @return the list of transaction hashes + */ + public final List> getAllTransactionHashesPerNode() { + if (!this.isFrozen()) { + throw new IllegalStateException( + "transaction must have been frozen before calculating the hash will be stable, try calling `freeze`"); + } + + transactionIds.setLocked(true); + nodeAccountIds.setLocked(true); + + buildAllTransactions(); + + var txCount = transactionIds.size(); + var nodeCount = nodeAccountIds.size(); + var transactionHashes = new ArrayList>(txCount); + + for (var txIndex = 0; txIndex < txCount; ++txIndex) { + var hashes = new HashMap(); + var offset = txIndex * nodeCount; + + for (var nodeIndex = 0; nodeIndex < nodeCount; ++nodeIndex) { + hashes.put( + nodeAccountIds.get(nodeIndex), + hash(outerTransactions + .get(offset + nodeIndex) + .getSignedTransactionBytes() + .toByteArray())); + } + + transactionHashes.add(hashes); + } + + return transactionHashes; + } + + @Override + public T addSignature(PublicKey publicKey, byte[] signature) { + if (data.size() > chunkSize) { + throw new IllegalStateException( + "Cannot manually add signature to chunked transaction with length greater than " + chunkSize); + } + return super.addSignature(publicKey, signature); + } + + @Override + public Map> getSignatures() { + if (data.size() > chunkSize) { + throw new IllegalStateException( + "Cannot call getSignatures() on a chunked transaction with length greater than " + chunkSize); + } + return super.getSignatures(); + } + + /** + * Extract the list of all signers. + * + * @return the list of all signatures + */ + public List>> getAllSignatures() { + if (publicKeys.isEmpty()) { + return new ArrayList<>(); + } + + buildAllTransactions(); + + var txCount = transactionIds.size(); + var nodeCount = nodeAccountIds.size(); + + var retval = new ArrayList>>(txCount); + + for (int i = 0; i < txCount; i++) { + retval.add(getSignaturesAtOffset(i * nodeCount)); + } + + return retval; + } + + private void freezeAndSign(Client client) { + if (!isFrozen()) { + freezeWith(client); + } + + var operatorId = client.getOperatorAccountId(); + + if (operatorId != null && operatorId.equals(Objects.requireNonNull(getTransactionIdInternal().accountId))) { + // on execute, sign each transaction with the operator, if present + // and we are signing a transaction that used the default transaction ID + signWithOperator(client); + } + } + + @Override + public TransactionResponse execute(Client client, Duration timeoutPerChunk) + throws TimeoutException, PrecheckStatusException { + return executeAll(client, timeoutPerChunk).get(0); + } + + /** + * Execute this transaction or query + * + * @param client The client with which this will be executed. + * @return Result of execution for each chunk + * @throws TimeoutException when the transaction times out + * @throws PrecheckStatusException when the precheck fails + */ + public List executeAll(Client client) throws PrecheckStatusException, TimeoutException { + return executeAll(client, client.getRequestTimeout()); + } + + /** + * Execute this transaction or query + * + * @param client The client with which this will be executed. + * @param timeoutPerChunk The timeout after which the execution attempt will be cancelled. + * @return Result of execution for each chunk + * @throws TimeoutException when the transaction times out + * @throws PrecheckStatusException when the precheck fails + */ + public List executeAll(Client client, Duration timeoutPerChunk) + throws PrecheckStatusException, TimeoutException { + freezeAndSign(client); + + var responses = new ArrayList(transactionIds.size()); + + for (var i = 0; i < transactionIds.size(); i++) { + var response = super.execute(client, timeoutPerChunk); + + if (shouldGetReceipt()) { + new TransactionReceiptQuery() + .setNodeAccountIds(Collections.singletonList(response.nodeId)) + .setTransactionId(response.transactionId) + .execute(client, timeoutPerChunk); + } + + responses.add(response); + } + + return responses; + } + + /** + * Execute this transaction or query asynchronously. + * + * @param client The client with which this will be executed. + * @return Future result of execution for each chunk + */ + public CompletableFuture> executeAllAsync(Client client) { + return executeAllAsync(client, client.getRequestTimeout()); + } + + /** + * Execute this transaction or query asynchronously. + * + * @param client The client with which this will be executed. + * @param timeoutPerChunk The timeout after which the execution attempt will be cancelled. + * @return Future result of execution for each chunk + */ + public CompletableFuture> executeAllAsync(Client client, Duration timeoutPerChunk) { + freezeAndSign(client); + + CompletableFuture> future = + CompletableFuture.supplyAsync(() -> new ArrayList<>(transactionIds.size())); + + for (var i = 0; i < transactionIds.size(); i++) { + future = future.thenCompose(list -> { + var responseFuture = super.executeAsync(client, timeoutPerChunk); + + Function> receiptFuture = + (TransactionResponse response) -> response.getReceiptAsync(client, timeoutPerChunk) + .thenApply(receipt -> response); + + Function> addToList = (response) -> { + list.add(response); + return list; + }; + + if (shouldGetReceipt()) { + return responseFuture.thenCompose(receiptFuture).thenApply(addToList); + } else { + return responseFuture.thenApply(addToList); + } + }); + } + + return future; + } + + /** + * Execute this transaction or query asynchronously. + * + * @param client The client with which this will be executed. + * @param callback a BiConsumer which handles the result or error. + */ + public void executeAllAsync(Client client, BiConsumer, Throwable> callback) { + ConsumerHelper.biConsumer(executeAllAsync(client), callback); + } + + /** + * Execute this transaction or query asynchronously. + * + * @param client The client with which this will be executed. + * @param timeout The timeout after which the execution attempt will be cancelled. + * @param callback a BiConsumer which handles the result or error. + */ + public void executeAllAsync( + Client client, Duration timeout, BiConsumer, Throwable> callback) { + ConsumerHelper.biConsumer(executeAllAsync(client, timeout), callback); + } + + /** + * Execute this transaction or query asynchronously. + * + * @param client The client with which this will be executed. + * @param onSuccess a Consumer which consumes the result on success. + * @param onFailure a Consumer which consumes the error on failure. + */ + public void executeAllAsync( + Client client, Consumer> onSuccess, Consumer onFailure) { + ConsumerHelper.twoConsumers(executeAllAsync(client), onSuccess, onFailure); + } + + /** + * Execute this transaction or query asynchronously. + * + * @param client The client with which this will be executed. + * @param timeout The timeout after which the execution attempt will be cancelled. + * @param onSuccess a Consumer which consumes the result on success. + * @param onFailure a Consumer which consumes the error on failure. + */ + public void executeAllAsync( + Client client, + Duration timeout, + Consumer> onSuccess, + Consumer onFailure) { + ConsumerHelper.twoConsumers(executeAllAsync(client, timeout), onSuccess, onFailure); + } + + @Override + public CompletableFuture executeAsync(Client client, Duration timeoutPerChunk) { + return executeAllAsync(client, timeoutPerChunk).thenApply(responses -> responses.get(0)); + } + + @Override + public ScheduleCreateTransaction schedule() { + requireNotFrozen(); + if (!nodeAccountIds.isEmpty()) { + throw new IllegalStateException( + "The underlying transaction for a scheduled transaction cannot have node account IDs set"); + } + if (data.size() > chunkSize) { + throw new IllegalStateException( + "Cannot schedule a chunked transaction with length greater than " + chunkSize); + } + + var bodyBuilder = spawnBodyBuilder(null); + + onFreeze(bodyBuilder); + + onFreezeChunk(bodyBuilder, null, 0, data.size(), 1, 1); + + return doSchedule(bodyBuilder); + } + + @Override + int getRequiredChunks() { + var requiredChunks = (this.data.size() + (chunkSize - 1)) / chunkSize; + + if (requiredChunks == 0) { + requiredChunks = 1; + } + + if (requiredChunks > maxChunks) { + throw new IllegalArgumentException("message of " + this.data.size() + " bytes requires " + requiredChunks + + " chunks but the maximum allowed chunks is " + maxChunks + ", try using setMaxChunks"); + } + return requiredChunks; + } + + @Override + void wipeTransactionLists(int requiredChunks) { + sigPairLists = new ArrayList<>(requiredChunks * nodeAccountIds.size()); + outerTransactions = new ArrayList<>(requiredChunks * nodeAccountIds.size()); + innerSignedTransactions = new ArrayList<>(requiredChunks * nodeAccountIds.size()); + + for (int i = 0; i < requiredChunks; i++) { + if (!transactionIds.isEmpty()) { + var startIndex = i * chunkSize; + var endIndex = startIndex + chunkSize; + + if (endIndex > this.data.size()) { + endIndex = this.data.size(); + } + + onFreezeChunk( + Objects.requireNonNull(frozenBodyBuilder) + .setTransactionID(transactionIds.get(i).toProtobuf()), + transactionIds.get(0).toProtobuf(), + startIndex, + endIndex, + i, + requiredChunks); + } + + // For each node we add a transaction with that node + for (var nodeId : nodeAccountIds) { + sigPairLists.add(SignatureMap.newBuilder()); + innerSignedTransactions.add(SignedTransaction.newBuilder() + .setBodyBytes(frozenBodyBuilder + .setNodeAccountID(nodeId.toProtobuf()) + .build() + .toByteString())); + outerTransactions.add(null); + } + } + } + + /** + * A common base for file and topic message transactions. + */ + abstract void onFreezeChunk( + TransactionBody.Builder body, + @Nullable TransactionID initialTransactionId, + int startIndex, + int endIndex, + int chunk, + int total); + + /** + * Should the receipt be retrieved? + * + * @return by default do not get a receipt + */ + boolean shouldGetReceipt() { + return false; + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/Client.java b/sdk/src/main/java/org/hiero/sdk/Client.java similarity index 90% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/Client.java rename to sdk/src/main/java/org/hiero/sdk/Client.java index 957b14ba57..16d86222ee 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/Client.java +++ b/sdk/src/main/java/org/hiero/sdk/Client.java @@ -1,23 +1,5 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.common.annotations.VisibleForTesting; import com.google.common.util.concurrent.ThreadFactoryBuilder; @@ -26,8 +8,6 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; -import com.hedera.hashgraph.sdk.logger.LogLevel; -import com.hedera.hashgraph.sdk.logger.Logger; import java.io.File; import java.io.IOException; import java.io.Reader; @@ -57,6 +37,8 @@ import java.util.function.Supplier; import java.util.function.UnaryOperator; import javax.annotation.Nullable; +import org.hiero.sdk.logger.LogLevel; +import org.hiero.sdk.logger.Logger; /** * Managed client for use on the Hedera Hashgraph network. @@ -81,13 +63,17 @@ public final class Client implements AutoCloseable { final ExecutorService executor; private final AtomicReference grpcDeadline = new AtomicReference(DEFAULT_GRPC_DEADLINE); private final Set subscriptions = ConcurrentHashMap.newKeySet(); + @Nullable Hbar defaultMaxTransactionFee = null; + Hbar defaultMaxQueryPayment = DEFAULT_MAX_QUERY_PAYMENT; Network network; MirrorNetwork mirrorNetwork; + @Nullable private Operator operator; + private Duration requestTimeout = DEFAULT_REQUEST_TIMEOUT; private Duration closeTimeout = DEFAULT_CLOSE_TIMEOUT; private int maxAttempts = DEFAULT_MAX_ATTEMPTS; @@ -99,8 +85,10 @@ public final class Client implements AutoCloseable { // If networkUpdatePeriod is null, any network updates in progress will not complete @Nullable private Duration networkUpdatePeriod; + @Nullable private CompletableFuture networkUpdateFuture; + private Logger logger = new Logger(LogLevel.SILENT); /** @@ -113,12 +101,12 @@ public final class Client implements AutoCloseable { */ @VisibleForTesting Client( - ExecutorService executor, - Network network, - MirrorNetwork mirrorNetwork, - @Nullable Duration networkUpdateInitialDelay, boolean shouldShutdownExecutor, - @Nullable Duration networkUpdatePeriod - ) { + ExecutorService executor, + Network network, + MirrorNetwork mirrorNetwork, + @Nullable Duration networkUpdateInitialDelay, + boolean shouldShutdownExecutor, + @Nullable Duration networkUpdatePeriod) { this.executor = executor; this.network = network; this.mirrorNetwork = mirrorNetwork; @@ -134,15 +122,19 @@ public final class Client implements AutoCloseable { */ static ExecutorService createExecutor() { var threadFactory = new ThreadFactoryBuilder() - .setNameFormat("hedera-sdk-%d") - .setDaemon(true) - .build(); + .setNameFormat("hedera-sdk-%d") + .setDaemon(true) + .build(); int nThreads = Runtime.getRuntime().availableProcessors(); - return new ThreadPoolExecutor(nThreads, nThreads, - 0L, TimeUnit.MILLISECONDS, - new LinkedBlockingQueue<>(), - threadFactory, new ThreadPoolExecutor.CallerRunsPolicy()); + return new ThreadPoolExecutor( + nThreads, + nThreads, + 0L, + TimeUnit.MILLISECONDS, + new LinkedBlockingQueue<>(), + threadFactory, + new ThreadPoolExecutor.CallerRunsPolicy()); } /** @@ -155,7 +147,7 @@ static ExecutorService createExecutor() { * * @param networkMap the map of node IDs to node addresses that make up the network. * @param executor runs the grpc requests asynchronously. - * @return {@link com.hedera.hashgraph.sdk.Client} + * @return {@link org.hiero.sdk.Client} */ public static Client forNetwork(Map networkMap, ExecutorService executor) { var network = Network.forNetwork(executor, networkMap); @@ -164,7 +156,6 @@ public static Client forNetwork(Map networkMap, ExecutorServi return new Client(executor, network, mirrorNetwork, null, false, null); } - /** * Construct a client given a set of nodes. * @@ -175,7 +166,7 @@ public static Client forNetwork(Map networkMap, ExecutorServi * chose nodes to send transactions to. For one transaction, at most 1/3 of the nodes will be tried. * * @param networkMap the map of node IDs to node addresses that make up the network. - * @return {@link com.hedera.hashgraph.sdk.Client} + * @return {@link org.hiero.sdk.Client} */ public static Client forNetwork(Map networkMap) { var executor = createExecutor(); @@ -191,14 +182,13 @@ public static Client forNetwork(Map networkMap) { * @param mirrorNetworkList * @return */ - public static Client forMirrorNetwork(List mirrorNetworkList) throws InterruptedException, TimeoutException { + public static Client forMirrorNetwork(List mirrorNetworkList) + throws InterruptedException, TimeoutException { var executor = createExecutor(); var network = Network.forNetwork(executor, new HashMap<>()); var mirrorNetwork = MirrorNetwork.forNetwork(executor, mirrorNetworkList); var client = new Client(executor, network, mirrorNetwork, null, true, null); - var addressBook = new AddressBookQuery() - .setFileId(FileId.ADDRESS_BOOK) - .execute(client); + var addressBook = new AddressBookQuery().setFileId(FileId.ADDRESS_BOOK).execute(client); client.setNetworkFromAddressBook(addressBook); return client; } @@ -223,14 +213,14 @@ public static Client forName(String name) { * href="https://docs.hedera.com/guides/mainnet/address-book#mainnet-address-book">Mainnet access. * * @param executor runs the grpc requests asynchronously. - * @return {@link com.hedera.hashgraph.sdk.Client} + * @return {@link org.hiero.sdk.Client} */ public static Client forMainnet(ExecutorService executor) { var network = Network.forMainnet(executor); var mirrorNetwork = MirrorNetwork.forMainnet(executor); - return new Client(executor, network, mirrorNetwork, NETWORK_UPDATE_INITIAL_DELAY, - false, DEFAULT_NETWORK_UPDATE_PERIOD); + return new Client( + executor, network, mirrorNetwork, NETWORK_UPDATE_INITIAL_DELAY, false, DEFAULT_NETWORK_UPDATE_PERIOD); } /** @@ -238,14 +228,14 @@ public static Client forMainnet(ExecutorService executor) { * access. * * @param executor runs the grpc requests asynchronously. - * @return {@link com.hedera.hashgraph.sdk.Client} + * @return {@link org.hiero.sdk.Client} */ public static Client forTestnet(ExecutorService executor) { var network = Network.forTestnet(executor); var mirrorNetwork = MirrorNetwork.forTestnet(executor); - return new Client(executor, network, mirrorNetwork, NETWORK_UPDATE_INITIAL_DELAY, - false, DEFAULT_NETWORK_UPDATE_PERIOD); + return new Client( + executor, network, mirrorNetwork, NETWORK_UPDATE_INITIAL_DELAY, false, DEFAULT_NETWORK_UPDATE_PERIOD); } /** @@ -254,45 +244,44 @@ public static Client forTestnet(ExecutorService executor) { * nodes. * * @param executor runs the grpc requests asynchronously. - * @return {@link com.hedera.hashgraph.sdk.Client} + * @return {@link org.hiero.sdk.Client} */ public static Client forPreviewnet(ExecutorService executor) { var network = Network.forPreviewnet(executor); var mirrorNetwork = MirrorNetwork.forPreviewnet(executor); - return new Client(executor, network, mirrorNetwork, NETWORK_UPDATE_INITIAL_DELAY, - false, DEFAULT_NETWORK_UPDATE_PERIOD); + return new Client( + executor, network, mirrorNetwork, NETWORK_UPDATE_INITIAL_DELAY, false, DEFAULT_NETWORK_UPDATE_PERIOD); } - /** * Construct a Hedera client pre-configured for Mainnet access. * - * @return {@link com.hedera.hashgraph.sdk.Client} + * @return {@link org.hiero.sdk.Client} */ public static Client forMainnet() { var executor = createExecutor(); var network = Network.forMainnet(executor); var mirrorNetwork = MirrorNetwork.forMainnet(executor); - return new Client(executor, network, mirrorNetwork, NETWORK_UPDATE_INITIAL_DELAY, - true, DEFAULT_NETWORK_UPDATE_PERIOD); + return new Client( + executor, network, mirrorNetwork, NETWORK_UPDATE_INITIAL_DELAY, true, DEFAULT_NETWORK_UPDATE_PERIOD); } /** * Construct a Hedera client pre-configured for Testnet * access. * - * @return {@link com.hedera.hashgraph.sdk.Client} + * @return {@link org.hiero.sdk.Client} */ public static Client forTestnet() { var executor = createExecutor(); var network = Network.forTestnet(executor); var mirrorNetwork = MirrorNetwork.forTestnet(executor); - return new Client(executor, network, mirrorNetwork, NETWORK_UPDATE_INITIAL_DELAY, - true, DEFAULT_NETWORK_UPDATE_PERIOD); + return new Client( + executor, network, mirrorNetwork, NETWORK_UPDATE_INITIAL_DELAY, true, DEFAULT_NETWORK_UPDATE_PERIOD); } /** @@ -300,22 +289,22 @@ public static Client forTestnet() { * href="https://docs.hedera.com/guides/testnet/testnet-nodes#previewnet-node-public-keys">Preview Testnet * nodes. * - * @return {@link com.hedera.hashgraph.sdk.Client} + * @return {@link org.hiero.sdk.Client} */ public static Client forPreviewnet() { var executor = createExecutor(); var network = Network.forPreviewnet(executor); var mirrorNetwork = MirrorNetwork.forPreviewnet(executor); - return new Client(executor, network, mirrorNetwork, NETWORK_UPDATE_INITIAL_DELAY, - true, DEFAULT_NETWORK_UPDATE_PERIOD); + return new Client( + executor, network, mirrorNetwork, NETWORK_UPDATE_INITIAL_DELAY, true, DEFAULT_NETWORK_UPDATE_PERIOD); } /** * Configure a client based off the given JSON string. * * @param json The json string containing the client configuration - * @return {@link com.hedera.hashgraph.sdk.Client} + * @return {@link org.hiero.sdk.Client} * @throws Exception if the config is incorrect */ public static Client fromConfig(String json) throws Exception { @@ -326,7 +315,7 @@ public static Client fromConfig(String json) throws Exception { * Configure a client based off the given JSON reader. * * @param json The Reader containing the client configuration - * @return {@link com.hedera.hashgraph.sdk.Client} + * @return {@link org.hiero.sdk.Client} * @throws Exception if the config is incorrect */ public static Client fromConfig(Reader json) throws Exception { @@ -336,8 +325,9 @@ public static Client fromConfig(Reader json) throws Exception { private static Map getNetworkNodes(JsonObject networks) { Map nodes = new HashMap<>(networks.size()); for (Map.Entry entry : networks.entrySet()) { - nodes.put(entry.getValue().toString().replace("\"", ""), - AccountId.fromString(entry.getKey().replace("\"", ""))); + nodes.put( + entry.getValue().toString().replace("\"", ""), + AccountId.fromString(entry.getKey().replace("\"", ""))); } return nodes; } @@ -346,7 +336,7 @@ private static Map getNetworkNodes(JsonObject networks) { * Configure a client based on a JSON file at the given path. * * @param fileName The string containing the file path - * @return {@link com.hedera.hashgraph.sdk.Client} + * @return {@link org.hiero.sdk.Client} * @throws IOException if IO operations fail */ public static Client fromConfigFile(String fileName) throws Exception { @@ -357,7 +347,7 @@ public static Client fromConfigFile(String fileName) throws Exception { * Configure a client based on a JSON file. * * @param file The file containing the client configuration - * @return {@link com.hedera.hashgraph.sdk.Client} + * @return {@link org.hiero.sdk.Client} * @throws IOException if IO operations fail */ public static Client fromConfigFile(File file) throws Exception { @@ -369,7 +359,7 @@ public static Client fromConfigFile(File file) throws Exception { * * @return the list of mirror nodes */ - synchronized public List getMirrorNetwork() { + public synchronized List getMirrorNetwork() { return mirrorNetwork.getNetwork(); } @@ -399,18 +389,21 @@ private synchronized void scheduleNetworkUpdate(@Nullable Duration delay) { networkUpdateFuture.thenRun(() -> { // Checking networkUpdatePeriod != null must be synchronized, so I've put it in a synchronized method. requireNetworkUpdatePeriodNotNull(() -> { - new AddressBookQuery().setFileId(FileId.ADDRESS_BOOK).executeAsync(this) - .thenCompose(addressBook -> requireNetworkUpdatePeriodNotNull(() -> { - try { - this.setNetworkFromAddressBook(addressBook); - } catch (Throwable error) { - return CompletableFuture.failedFuture(error); - } - return CompletableFuture.completedFuture(null); - })).exceptionally(error -> { - logger.warn("Failed to update address book via mirror node query ", error); - return null; - }); + new AddressBookQuery() + .setFileId(FileId.ADDRESS_BOOK) + .executeAsync(this) + .thenCompose(addressBook -> requireNetworkUpdatePeriodNotNull(() -> { + try { + this.setNetworkFromAddressBook(addressBook); + } catch (Throwable error) { + return CompletableFuture.failedFuture(error); + } + return CompletableFuture.completedFuture(null); + })) + .exceptionally(error -> { + logger.warn("Failed to update address book via mirror node query ", error); + return null; + }); scheduleNetworkUpdate(networkUpdatePeriod); return null; }); @@ -447,7 +440,7 @@ void untrackSubscription(SubscriptionHandle subscriptionHandle) { * @return {@code this} */ public synchronized Client setNetworkFromAddressBook(NodeAddressBook addressBook) - throws InterruptedException, TimeoutException { + throws InterruptedException, TimeoutException { network.setNetwork(Network.addressBookToNetwork(addressBook.nodeAddresses)); network.setAddressBook(addressBook); return this; @@ -458,7 +451,7 @@ public synchronized Client setNetworkFromAddressBook(NodeAddressBook addressBook * * @return the client's network */ - synchronized public Map getNetwork() { + public synchronized Map getNetwork() { return network.getNetwork(); } @@ -471,7 +464,7 @@ synchronized public Map getNetwork() { * @throws InterruptedException when a thread is interrupted while it's waiting, sleeping, or otherwise occupied */ public synchronized Client setNetwork(Map network) - throws InterruptedException, TimeoutException { + throws InterruptedException, TimeoutException { this.network.setNetwork(network); return this; } @@ -567,9 +560,9 @@ public Void ping(AccountId nodeAccountId) throws PrecheckStatusException, Timeou */ public Void ping(AccountId nodeAccountId, Duration timeout) throws PrecheckStatusException, TimeoutException { new AccountBalanceQuery() - .setAccountId(nodeAccountId) - .setNodeAccountIds(Collections.singletonList(nodeAccountId)) - .execute(this, timeout); + .setAccountId(nodeAccountId) + .setNodeAccountIds(Collections.singletonList(nodeAccountId)) + .execute(this, timeout); return null; } @@ -594,16 +587,16 @@ public CompletableFuture pingAsync(AccountId nodeAccountId) { public CompletableFuture pingAsync(AccountId nodeAccountId, Duration timeout) { var result = new CompletableFuture(); new AccountBalanceQuery() - .setAccountId(nodeAccountId) - .setNodeAccountIds(Collections.singletonList(nodeAccountId)) - .executeAsync(this, timeout) - .whenComplete((balance, error) -> { - if (error == null) { - result.complete(null); - } else { - result.completeExceptionally(error); - } - }); + .setAccountId(nodeAccountId) + .setNodeAccountIds(Collections.singletonList(nodeAccountId)) + .executeAsync(this, timeout) + .whenComplete((balance, error) -> { + if (error == null) { + result.complete(null); + } else { + result.completeExceptionally(error); + } + }); return result; } @@ -647,8 +640,8 @@ public void pingAsync(AccountId nodeAccountId, Consumer onSuccess, Consume * @param onSuccess a Consumer which consumes the result on success. * @param onFailure a Consumer which consumes the error on failure. */ - public void pingAsync(AccountId nodeAccountId, Duration timeout, Consumer onSuccess, - Consumer onFailure) { + public void pingAsync( + AccountId nodeAccountId, Duration timeout, Consumer onSuccess, Consumer onFailure) { ConsumerHelper.twoConsumers(pingAsync(nodeAccountId, timeout), onSuccess, onFailure); } @@ -704,7 +697,8 @@ public synchronized CompletableFuture pingAllAsync(Duration timeoutPerPing list.add(pingAsync(nodeAccountId, timeoutPerPing)); } - return CompletableFuture.allOf(list.toArray(new CompletableFuture[0])).thenApply((v) -> null); + return CompletableFuture.allOf(list.toArray(new CompletableFuture[0])) + .thenApply((v) -> null); } /** @@ -780,16 +774,15 @@ public synchronized Client setOperator(AccountId accountId, PrivateKey privateKe * @param transactionSigner The signer for the operator * @return {@code this} */ - public synchronized Client setOperatorWith(AccountId accountId, PublicKey publicKey, - UnaryOperator transactionSigner) { + public synchronized Client setOperatorWith( + AccountId accountId, PublicKey publicKey, UnaryOperator transactionSigner) { if (getNetworkName() != null) { try { accountId.validateChecksum(this); } catch (BadEntityIdException exc) { throw new IllegalArgumentException( - "Tried to set the client operator account ID to an account ID with an invalid checksum: " - + exc.getMessage() - ); + "Tried to set the client operator account ID to an account ID with an invalid checksum: " + + exc.getMessage()); } } @@ -1441,7 +1434,8 @@ private Client initializeWithNetwork() throws Exception { Client client; if (network.isJsonObject()) { client = clientFromNetworkJson(); - } else {; + } else { + ; client = clientFromNetworkString(); } return client; @@ -1463,7 +1457,7 @@ private void setNetworkNameOn(Client client) { client.setNetworkName(NetworkName.fromString(networkNameString)); } catch (Exception ignored) { throw new IllegalArgumentException("networkName in config was \"" + networkNameString - + "\", expected either \"mainnet\", \"testnet\" or \"previewnet\""); + + "\", expected either \"mainnet\", \"testnet\" or \"previewnet\""); } } } diff --git a/sdk/src/main/java/org/hiero/sdk/ConsumerHelper.java b/sdk/src/main/java/org/hiero/sdk/ConsumerHelper.java new file mode 100644 index 0000000000..8e586a9b8d --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/ConsumerHelper.java @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import java.util.concurrent.CompletableFuture; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +class ConsumerHelper { + static void biConsumer(CompletableFuture future, BiConsumer consumer) { + future.whenComplete(consumer); + } + + static void twoConsumers(CompletableFuture future, Consumer onSuccess, Consumer onFailure) { + future.whenComplete((output, error) -> { + if (error != null) { + onFailure.accept(error); + } else { + onSuccess.accept(output); + } + }); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/ContractByteCodeQuery.java b/sdk/src/main/java/org/hiero/sdk/ContractByteCodeQuery.java new file mode 100644 index 0000000000..a37b1b35ec --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/ContractByteCodeQuery.java @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.ByteString; +import io.grpc.MethodDescriptor; +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.ContractGetBytecodeQuery; +import org.hiero.sdk.proto.QueryHeader; +import org.hiero.sdk.proto.Response; +import org.hiero.sdk.proto.ResponseHeader; +import org.hiero.sdk.proto.SmartContractServiceGrpc; + +/** + * Get the bytecode for a smart contract instance. + */ +public final class ContractByteCodeQuery extends Query { + @Nullable + private ContractId contractId = null; + + /** + * Constructor. + */ + public ContractByteCodeQuery() {} + + /** + * Extract the contract id. + * + * @return the contract id + */ + @Nullable + public ContractId getContractId() { + return contractId; + } + + /** + * Sets the contract ID for which information is requested. + * + * @param contractId The ContractId to be set + * @return {@code this} + */ + public ContractByteCodeQuery setContractId(ContractId contractId) { + Objects.requireNonNull(contractId); + this.contractId = contractId; + return this; + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (contractId != null) { + contractId.validateChecksum(client); + } + } + + @Override + void onMakeRequest(org.hiero.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { + var builder = ContractGetBytecodeQuery.newBuilder(); + if (contractId != null) { + builder.setContractID(contractId.toProtobuf()); + } + + queryBuilder.setContractGetBytecode(builder.setHeader(header)); + } + + @Override + ResponseHeader mapResponseHeader(Response response) { + return response.getContractGetBytecodeResponse().getHeader(); + } + + @Override + QueryHeader mapRequestHeader(org.hiero.sdk.proto.Query request) { + return request.getContractGetBytecode().getHeader(); + } + + @Override + ByteString mapResponse(Response response, AccountId nodeId, org.hiero.sdk.proto.Query request) { + return response.getContractGetBytecodeResponse().getBytecode(); + } + + @Override + MethodDescriptor getMethodDescriptor() { + return SmartContractServiceGrpc.getContractGetBytecodeMethod(); + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractCallQuery.java b/sdk/src/main/java/org/hiero/sdk/ContractCallQuery.java similarity index 83% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/ContractCallQuery.java rename to sdk/src/main/java/org/hiero/sdk/ContractCallQuery.java index 16b0d080e3..eafbcab7b2 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractCallQuery.java +++ b/sdk/src/main/java/org/hiero/sdk/ContractCallQuery.java @@ -1,35 +1,17 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.ByteString; -import com.hedera.hashgraph.sdk.proto.ContractCallLocalQuery; -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import com.hedera.hashgraph.sdk.proto.Response; -import com.hedera.hashgraph.sdk.proto.ResponseHeader; -import com.hedera.hashgraph.sdk.proto.SmartContractServiceGrpc; import io.grpc.MethodDescriptor; import java.util.Arrays; import java.util.Objects; import java.util.concurrent.CompletableFuture; import javax.annotation.Nullable; +import org.hiero.sdk.proto.ContractCallLocalQuery; +import org.hiero.sdk.proto.QueryHeader; +import org.hiero.sdk.proto.Response; +import org.hiero.sdk.proto.ResponseHeader; +import org.hiero.sdk.proto.SmartContractServiceGrpc; /** * Call a function of the given smart contract instance, giving it functionParameters as its inputs. @@ -47,17 +29,18 @@ public final class ContractCallQuery extends Query { @Nullable private ContractId contractId = null; + private long gas = 0; private byte[] functionParameters = {}; private long maxResultSize = 0; + @Nullable private AccountId senderAccountId = null; /** * Constructor. */ - public ContractCallQuery() { - } + public ContractCallQuery() {} /** * Extract the contract id. @@ -201,7 +184,6 @@ public AccountId getSenderAccountId() { * @param senderAccountId the account that is the "sender" * @return {@code this} */ - public ContractCallQuery setSenderAccountId(AccountId senderAccountId) { Objects.requireNonNull(senderAccountId); this.senderAccountId = senderAccountId; @@ -216,7 +198,7 @@ void validateChecksums(Client client) throws BadEntityIdException { } @Override - void onMakeRequest(com.hedera.hashgraph.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { + void onMakeRequest(org.hiero.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { var builder = ContractCallLocalQuery.newBuilder(); if (contractId != null) { builder.setContractID(contractId.toProtobuf()); @@ -236,17 +218,17 @@ ResponseHeader mapResponseHeader(Response response) { } @Override - QueryHeader mapRequestHeader(com.hedera.hashgraph.sdk.proto.Query request) { + QueryHeader mapRequestHeader(org.hiero.sdk.proto.Query request) { return request.getContractCallLocal().getHeader(); } @Override - ContractFunctionResult mapResponse(Response response, AccountId nodeId, com.hedera.hashgraph.sdk.proto.Query request) { + ContractFunctionResult mapResponse(Response response, AccountId nodeId, org.hiero.sdk.proto.Query request) { return new ContractFunctionResult(response.getContractCallLocal().getFunctionResult()); } @Override - MethodDescriptor getMethodDescriptor() { + MethodDescriptor getMethodDescriptor() { return SmartContractServiceGrpc.getContractCallLocalMethodMethod(); } } diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractCreateFlow.java b/sdk/src/main/java/org/hiero/sdk/ContractCreateFlow.java similarity index 88% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/ContractCreateFlow.java rename to sdk/src/main/java/org/hiero/sdk/ContractCreateFlow.java index c5cba04d4e..6a88833f4a 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractCreateFlow.java +++ b/sdk/src/main/java/org/hiero/sdk/ContractCreateFlow.java @@ -1,23 +1,5 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.ByteString; import java.time.Duration; @@ -80,24 +62,35 @@ public class ContractCreateFlow { static final int FILE_CREATE_MAX_BYTES = 2048; private String bytecode = ""; + @Nullable private Integer maxChunks = null; + @Nullable private Key adminKey = null; + private long gas = 0; private Hbar initialBalance = Hbar.ZERO; + @Nullable private AccountId proxyAccountId = null; + private int maxAutomaticTokenAssociations = 0; + @Nullable private Duration autoRenewPeriod = null; + @Nullable private AccountId autoRenewAccountId = null; + private byte[] constructorParameters = {}; + @Nullable private String contractMemo = null; + @Nullable private List nodeAccountIds = null; + private String createBytecode = ""; private String appendBytecode = ""; @@ -124,8 +117,7 @@ public class ContractCreateFlow { /** * Constructor */ - public ContractCreateFlow() { - } + public ContractCreateFlow() {} /** * Extract the hex-encoded bytecode of the contract. @@ -568,8 +560,8 @@ private void splitBytecode() { private FileCreateTransaction createFileCreateTransaction(Client client) { var fileCreateTx = new FileCreateTransaction() - .setKeys(Objects.requireNonNull(client.getOperatorPublicKey())) - .setContents(createBytecode); + .setKeys(Objects.requireNonNull(client.getOperatorPublicKey())) + .setContents(createBytecode); if (nodeAccountIds != null) { fileCreateTx.setNodeAccountIds(nodeAccountIds); } @@ -577,9 +569,7 @@ private FileCreateTransaction createFileCreateTransaction(Client client) { } private FileAppendTransaction createFileAppendTransaction(FileId fileId) { - var fileAppendTx = new FileAppendTransaction() - .setFileId(fileId) - .setContents(appendBytecode); + var fileAppendTx = new FileAppendTransaction().setFileId(fileId).setContents(appendBytecode); if (maxChunks != null) { fileAppendTx.setMaxChunks(maxChunks); } @@ -591,12 +581,12 @@ private FileAppendTransaction createFileAppendTransaction(FileId fileId) { private ContractCreateTransaction createContractCreateTransaction(FileId fileId) { var contractCreateTx = new ContractCreateTransaction() - .setBytecodeFileId(fileId) - .setConstructorParameters(constructorParameters) - .setGas(gas) - .setInitialBalance(initialBalance) - .setMaxAutomaticTokenAssociations(maxAutomaticTokenAssociations) - .setDeclineStakingReward(declineStakingReward); + .setBytecodeFileId(fileId) + .setConstructorParameters(constructorParameters) + .setGas(gas) + .setInitialBalance(initialBalance) + .setMaxAutomaticTokenAssociations(maxAutomaticTokenAssociations) + .setDeclineStakingReward(declineStakingReward); if (adminKey != null) { contractCreateTx.setAdminKey(adminKey); } @@ -639,8 +629,8 @@ private ContractCreateTransaction createContractCreateTransaction(FileId fileId) */ TransactionReceiptQuery createTransactionReceiptQuery(TransactionResponse response) { return new TransactionReceiptQuery() - .setNodeAccountIds(Collections.singletonList(response.nodeId)) - .setTransactionId(response.transactionId); + .setNodeAccountIds(Collections.singletonList(response.nodeId)) + .setTransactionId(response.transactionId); } /** @@ -665,23 +655,20 @@ public TransactionResponse execute(Client client) throws PrecheckStatusException * @throws TimeoutException when the transaction times out */ public TransactionResponse execute(Client client, Duration timeoutPerTransaction) - throws PrecheckStatusException, TimeoutException { + throws PrecheckStatusException, TimeoutException { try { splitBytecode(); var fileId = createFileCreateTransaction(client) - .execute(client, timeoutPerTransaction) - .getReceipt(client, timeoutPerTransaction) - .fileId; + .execute(client, timeoutPerTransaction) + .getReceipt(client, timeoutPerTransaction) + .fileId; Objects.requireNonNull(fileId); if (!appendBytecode.isEmpty()) { - createFileAppendTransaction(fileId) - .execute(client, timeoutPerTransaction); + createFileAppendTransaction(fileId).execute(client, timeoutPerTransaction); } var response = createContractCreateTransaction(fileId).execute(client, timeoutPerTransaction); response.getReceipt(client, timeoutPerTransaction); - new FileDeleteTransaction() - .setFileId(fileId) - .execute(client, timeoutPerTransaction); + new FileDeleteTransaction().setFileId(fileId).execute(client, timeoutPerTransaction); return response; } catch (ReceiptStatusException e) { throw new RuntimeException(e); @@ -707,26 +694,30 @@ public CompletableFuture executeAsync(Client client) { */ public CompletableFuture executeAsync(Client client, Duration timeoutPerTransaction) { splitBytecode(); - return createFileCreateTransaction(client).executeAsync(client, timeoutPerTransaction) - .thenCompose(fileCreateResponse -> createTransactionReceiptQuery(fileCreateResponse) + return createFileCreateTransaction(client) .executeAsync(client, timeoutPerTransaction) - .thenApply(receipt -> receipt.fileId)).thenCompose(fileId -> { - CompletableFuture appendFuture = - appendBytecode.isEmpty() ? CompletableFuture.completedFuture(null) : - createFileAppendTransaction(fileId).executeAsync(client, timeoutPerTransaction) - .thenApply(ignored -> null); - return appendFuture.thenCompose( - ignored -> createContractCreateTransaction(fileId).executeAsync(client, timeoutPerTransaction) - .thenApply(contractCreateResponse -> { - createTransactionReceiptQuery(contractCreateResponse).executeAsync(client, - timeoutPerTransaction).thenRun(() -> { - new FileDeleteTransaction() - .setFileId(fileId) - .executeAsync(client, timeoutPerTransaction); - }); - return contractCreateResponse; - })); - }); + .thenCompose(fileCreateResponse -> createTransactionReceiptQuery(fileCreateResponse) + .executeAsync(client, timeoutPerTransaction) + .thenApply(receipt -> receipt.fileId)) + .thenCompose(fileId -> { + CompletableFuture appendFuture = appendBytecode.isEmpty() + ? CompletableFuture.completedFuture(null) + : createFileAppendTransaction(fileId) + .executeAsync(client, timeoutPerTransaction) + .thenApply(ignored -> null); + return appendFuture.thenCompose(ignored -> createContractCreateTransaction(fileId) + .executeAsync(client, timeoutPerTransaction) + .thenApply(contractCreateResponse -> { + createTransactionReceiptQuery(contractCreateResponse) + .executeAsync(client, timeoutPerTransaction) + .thenRun(() -> { + new FileDeleteTransaction() + .setFileId(fileId) + .executeAsync(client, timeoutPerTransaction); + }); + return contractCreateResponse; + })); + }); } /** @@ -746,8 +737,8 @@ public void executeAsync(Client client, BiConsumer callback) { + public void executeAsync( + Client client, Duration timeoutPerTransaction, BiConsumer callback) { ConsumerHelper.biConsumer(executeAsync(client, timeoutPerTransaction), callback); } @@ -770,8 +761,11 @@ public void executeAsync(Client client, Consumer onSuccess, * @param onSuccess a Consumer which consumes the result on success. * @param onFailure a Consumer which consumes the error on failure. */ - public void executeAsync(Client client, Duration timeoutPerTransaction, Consumer onSuccess, - Consumer onFailure) { + public void executeAsync( + Client client, + Duration timeoutPerTransaction, + Consumer onSuccess, + Consumer onFailure) { ConsumerHelper.twoConsumers(executeAsync(client, timeoutPerTransaction), onSuccess, onFailure); } } diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractCreateTransaction.java b/sdk/src/main/java/org/hiero/sdk/ContractCreateTransaction.java similarity index 93% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/ContractCreateTransaction.java rename to sdk/src/main/java/org/hiero/sdk/ContractCreateTransaction.java index a21f336ae9..c767450714 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractCreateTransaction.java +++ b/sdk/src/main/java/org/hiero/sdk/ContractCreateTransaction.java @@ -1,38 +1,19 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.ContractCreateTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.SmartContractServiceGrpc; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; import io.grpc.MethodDescriptor; -import org.bouncycastle.util.Arrays; import java.time.Duration; - -import javax.annotation.Nullable; import java.util.LinkedHashMap; import java.util.Objects; +import javax.annotation.Nullable; +import org.bouncycastle.util.Arrays; +import org.hiero.sdk.proto.ContractCreateTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.SmartContractServiceGrpc; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; /** * Start a new smart contract instance. @@ -82,6 +63,7 @@ public final class ContractCreateTransaction extends Transaction> txs) throws InvalidProtocolBufferException { + ContractCreateTransaction( + LinkedHashMap> txs) + throws InvalidProtocolBufferException { super(txs); initFromTransactionBody(); } @@ -138,7 +125,7 @@ public ContractCreateTransaction() { * * @param txBody protobuf TransactionBody */ - ContractCreateTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { + ContractCreateTransaction(org.hiero.sdk.proto.TransactionBody txBody) { super(txBody); initFromTransactionBody(); } @@ -191,7 +178,6 @@ public byte[] getBytecode() { * @param bytecode The bytecode * @return {@code this} */ - public ContractCreateTransaction setBytecode(byte[] bytecode) { Objects.requireNonNull(bytecode); requireNotFrozen(); @@ -604,7 +590,7 @@ void initFromTransactionBody() { } @Override - MethodDescriptor getMethodDescriptor() { + MethodDescriptor getMethodDescriptor() { return SmartContractServiceGrpc.getCreateContractMethod(); } diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractDeleteTransaction.java b/sdk/src/main/java/org/hiero/sdk/ContractDeleteTransaction.java similarity index 79% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/ContractDeleteTransaction.java rename to sdk/src/main/java/org/hiero/sdk/ContractDeleteTransaction.java index 571840484d..e7b7077d57 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractDeleteTransaction.java +++ b/sdk/src/main/java/org/hiero/sdk/ContractDeleteTransaction.java @@ -1,35 +1,16 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.ContractDeleteTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.SmartContractServiceGrpc; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; import java.util.LinkedHashMap; import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.ContractDeleteTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.SmartContractServiceGrpc; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; /** * Marks a contract as deleted, moving all its current hbars to another account. @@ -38,16 +19,17 @@ public final class ContractDeleteTransaction extends Transaction> txs) throws InvalidProtocolBufferException { + ContractDeleteTransaction( + LinkedHashMap> txs) + throws InvalidProtocolBufferException { super(txs); initFromTransactionBody(); } @@ -66,7 +50,7 @@ public ContractDeleteTransaction() { * * @param txBody protobuf TransactionBody */ - ContractDeleteTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { + ContractDeleteTransaction(org.hiero.sdk.proto.TransactionBody txBody) { super(txBody); initFromTransactionBody(); } @@ -165,9 +149,8 @@ void validateChecksums(Client client) throws BadEntityIdException { } } - @Override - MethodDescriptor getMethodDescriptor() { + MethodDescriptor getMethodDescriptor() { return SmartContractServiceGrpc.getDeleteContractMethod(); } diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractExecuteTransaction.java b/sdk/src/main/java/org/hiero/sdk/ContractExecuteTransaction.java similarity index 81% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/ContractExecuteTransaction.java rename to sdk/src/main/java/org/hiero/sdk/ContractExecuteTransaction.java index 5318948c45..68f0b90855 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractExecuteTransaction.java +++ b/sdk/src/main/java/org/hiero/sdk/ContractExecuteTransaction.java @@ -1,36 +1,17 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.ContractCallTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.SmartContractServiceGrpc; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; import java.util.LinkedHashMap; import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.ContractCallTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.SmartContractServiceGrpc; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; /** * Call a function of the given smart contract instance, giving it parameters as its inputs. @@ -45,6 +26,7 @@ public final class ContractExecuteTransaction extends Transaction> txs) throws InvalidProtocolBufferException { + ContractExecuteTransaction( + LinkedHashMap> txs) + throws InvalidProtocolBufferException { super(txs); initFromTransactionBody(); } @@ -72,7 +55,7 @@ public ContractExecuteTransaction() { * * @param txBody protobuf TransactionBody */ - ContractExecuteTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { + ContractExecuteTransaction(org.hiero.sdk.proto.TransactionBody txBody) { super(txBody); initFromTransactionBody(); } @@ -231,7 +214,7 @@ void validateChecksums(Client client) throws BadEntityIdException { } @Override - MethodDescriptor getMethodDescriptor() { + MethodDescriptor getMethodDescriptor() { return SmartContractServiceGrpc.getContractCallMethodMethod(); } diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractFunctionParameters.java b/sdk/src/main/java/org/hiero/sdk/ContractFunctionParameters.java similarity index 89% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/ContractFunctionParameters.java rename to sdk/src/main/java/org/hiero/sdk/ContractFunctionParameters.java index bbdca320f3..a9e45b0798 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractFunctionParameters.java +++ b/sdk/src/main/java/org/hiero/sdk/ContractFunctionParameters.java @@ -1,26 +1,7 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.ByteString; - import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; @@ -29,7 +10,6 @@ import java.util.stream.IntStream; import java.util.stream.Stream; import javax.annotation.Nullable; - import org.bouncycastle.util.encoders.DecoderException; import org.bouncycastle.util.encoders.Hex; @@ -78,13 +58,11 @@ public final class ContractFunctionParameters { private static ByteString encodeString(String string) { ByteString strBytes = ByteString.copyFromUtf8(string); // prepend the size of the string in UTF-8 bytes - return int256(strBytes.size(), 32) - .concat(rightPad32(strBytes)); + return int256(strBytes.size(), 32).concat(rightPad32(strBytes)); } private static ByteString encodeBytes(byte[] bytes) { - return int256(bytes.length, 32) - .concat(rightPad32(ByteString.copyFrom(bytes))); + return int256(bytes.length, 32).concat(rightPad32(ByteString.copyFrom(bytes))); } private static ByteString encodeBytes4(byte[] bytes) { @@ -94,7 +72,6 @@ private static ByteString encodeBytes4(byte[] bytes) { return rightPad32(ByteString.copyFrom(bytes)); } - private static ByteString encodeBytes32(byte[] bytes) { if (bytes.length > 32) { throw new IllegalArgumentException("byte32 encoding forbids byte array length greater than 32"); @@ -110,8 +87,7 @@ private static ByteString encodeBool(boolean bool) { private static ByteString encodeArray(Stream elements) { List list = elements.collect(Collectors.toList()); - return int256(list.size(), 32) - .concat(ByteString.copyFrom(list)); + return int256(list.size(), 32).concat(ByteString.copyFrom(list)); } private static ByteString encodeDynArr(List elements) { @@ -165,9 +141,9 @@ static ByteString int256(long val, int bitWidth, boolean signed) { static byte[] getTruncatedBytes(BigInteger bigInt, int bitWidth) { byte[] bytes = bigInt.toByteArray(); int expectedBytes = bitWidth / 8; - return bytes.length <= expectedBytes ? - bytes : - Arrays.copyOfRange(bytes, bytes.length - expectedBytes, bytes.length); + return bytes.length <= expectedBytes + ? bytes + : Arrays.copyOfRange(bytes, bytes.length - expectedBytes, bytes.length); } static ByteString int256(BigInteger bigInt, int bitWidth) { @@ -194,9 +170,8 @@ static ByteString leftPad32(ByteString input) { static ByteString leftPad32(ByteString input, boolean negative) { int rem = 32 - input.size() % 32; return rem == 32 - ? input - : (negative ? negativePadding : padding).substring(0, rem) - .concat(input); + ? input + : (negative ? negativePadding : padding).substring(0, rem).concat(input); } static ByteString leftPad32(byte[] input, boolean negative) { @@ -212,8 +187,7 @@ private static byte[] decodeAddress(String address) { address = address.startsWith("0x") ? address.substring(2) : address; if (address.length() != ADDRESS_LEN_HEX) { - throw new IllegalArgumentException( - "Solidity addresses must be 40 hex chars"); + throw new IllegalArgumentException("Solidity addresses must be 40 hex chars"); } try { @@ -246,8 +220,8 @@ public ContractFunctionParameters addString(String param) { */ public ContractFunctionParameters addStringArray(String[] strings) { List byteStrings = Arrays.stream(strings) - .map(ContractFunctionParameters::encodeString) - .collect(Collectors.toList()); + .map(ContractFunctionParameters::encodeString) + .collect(Collectors.toList()); ByteString argBytes = encodeDynArr(byteStrings); @@ -276,8 +250,8 @@ public ContractFunctionParameters addBytes(byte[] param) { */ public ContractFunctionParameters addBytesArray(byte[][] param) { List byteArrays = Arrays.stream(param) - .map(ContractFunctionParameters::encodeBytes) - .collect(Collectors.toList()); + .map(ContractFunctionParameters::encodeBytes) + .collect(Collectors.toList()); args.add(new Argument("bytes[]", encodeDynArr(byteArrays), true)); @@ -305,8 +279,7 @@ public ContractFunctionParameters addBytes4(byte[] param) { * @throws IllegalArgumentException if the length of any byte array is not 4. */ public ContractFunctionParameters addBytes4Array(byte[][] param) { - Stream byteArrays = Arrays.stream(param) - .map(ContractFunctionParameters::encodeBytes4); + Stream byteArrays = Arrays.stream(param).map(ContractFunctionParameters::encodeBytes4); args.add(new Argument("bytes4[]", encodeArray(byteArrays), true)); @@ -339,8 +312,7 @@ public ContractFunctionParameters addBytes32(byte[] param) { */ public ContractFunctionParameters addBytes32Array(byte[][] param) { // array of fixed-size elements - Stream byteArrays = Arrays.stream(param) - .map(ContractFunctionParameters::encodeBytes32); + Stream byteArrays = Arrays.stream(param).map(ContractFunctionParameters::encodeBytes32); args.add(new Argument("bytes32[]", encodeArray(byteArrays), true)); @@ -371,8 +343,7 @@ public ContractFunctionParameters addBoolArray(boolean[] param) { boolWrapperArray[i] = param[i]; } - Stream bools = Arrays.stream(boolWrapperArray) - .map(ContractFunctionParameters::encodeBool); + Stream bools = Arrays.stream(boolWrapperArray).map(ContractFunctionParameters::encodeBool); args.add(new Argument("bool[]", encodeArray(bools), true)); @@ -776,9 +747,8 @@ public ContractFunctionParameters addInt256(BigInteger value) { public ContractFunctionParameters addInt8Array(byte[] intArray) { IntStream intStream = IntStream.range(0, intArray.length).map(idx -> intArray[idx]); - ByteString arrayBytes = ByteString.copyFrom( - intStream.mapToObj(i -> int256(i, 8)) - .collect(Collectors.toList())); + ByteString arrayBytes = + ByteString.copyFrom(intStream.mapToObj(i -> int256(i, 8)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -795,8 +765,7 @@ public ContractFunctionParameters addInt8Array(byte[] intArray) { */ public ContractFunctionParameters addInt16Array(int[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).mapToObj(i -> int256(i, 16)) - .collect(Collectors.toList())); + Arrays.stream(intArray).mapToObj(i -> int256(i, 16)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -813,8 +782,7 @@ public ContractFunctionParameters addInt16Array(int[] intArray) { */ public ContractFunctionParameters addInt24Array(int[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).mapToObj(i -> int256(i, 24)) - .collect(Collectors.toList())); + Arrays.stream(intArray).mapToObj(i -> int256(i, 24)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -831,8 +799,7 @@ public ContractFunctionParameters addInt24Array(int[] intArray) { */ public ContractFunctionParameters addInt32Array(int[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).mapToObj(i -> int256(i, 32)) - .collect(Collectors.toList())); + Arrays.stream(intArray).mapToObj(i -> int256(i, 32)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -849,8 +816,7 @@ public ContractFunctionParameters addInt32Array(int[] intArray) { */ public ContractFunctionParameters addInt40Array(long[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).mapToObj(i -> int256(i, 40)) - .collect(Collectors.toList())); + Arrays.stream(intArray).mapToObj(i -> int256(i, 40)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -867,8 +833,7 @@ public ContractFunctionParameters addInt40Array(long[] intArray) { */ public ContractFunctionParameters addInt48Array(long[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).mapToObj(i -> int256(i, 48)) - .collect(Collectors.toList())); + Arrays.stream(intArray).mapToObj(i -> int256(i, 48)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -885,8 +850,7 @@ public ContractFunctionParameters addInt48Array(long[] intArray) { */ public ContractFunctionParameters addInt56Array(long[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).mapToObj(i -> int256(i, 56)) - .collect(Collectors.toList())); + Arrays.stream(intArray).mapToObj(i -> int256(i, 56)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -903,8 +867,7 @@ public ContractFunctionParameters addInt56Array(long[] intArray) { */ public ContractFunctionParameters addInt64Array(long[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).mapToObj(i -> int256(i, 64)) - .collect(Collectors.toList())); + Arrays.stream(intArray).mapToObj(i -> int256(i, 64)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -921,8 +884,7 @@ public ContractFunctionParameters addInt64Array(long[] intArray) { */ public ContractFunctionParameters addInt72Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> int256(i, 72)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> int256(i, 72)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -939,8 +901,7 @@ public ContractFunctionParameters addInt72Array(BigInteger[] intArray) { */ public ContractFunctionParameters addInt80Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> int256(i, 80)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> int256(i, 80)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -957,8 +918,7 @@ public ContractFunctionParameters addInt80Array(BigInteger[] intArray) { */ public ContractFunctionParameters addInt88Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> int256(i, 88)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> int256(i, 88)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -975,8 +935,7 @@ public ContractFunctionParameters addInt88Array(BigInteger[] intArray) { */ public ContractFunctionParameters addInt96Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> int256(i, 96)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> int256(i, 96)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -993,8 +952,7 @@ public ContractFunctionParameters addInt96Array(BigInteger[] intArray) { */ public ContractFunctionParameters addInt104Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> int256(i, 104)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> int256(i, 104)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -1011,8 +969,7 @@ public ContractFunctionParameters addInt104Array(BigInteger[] intArray) { */ public ContractFunctionParameters addInt112Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> int256(i, 112)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> int256(i, 112)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -1029,8 +986,7 @@ public ContractFunctionParameters addInt112Array(BigInteger[] intArray) { */ public ContractFunctionParameters addInt120Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> int256(i, 120)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> int256(i, 120)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -1047,8 +1003,7 @@ public ContractFunctionParameters addInt120Array(BigInteger[] intArray) { */ public ContractFunctionParameters addInt128Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> int256(i, 128)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> int256(i, 128)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -1065,8 +1020,7 @@ public ContractFunctionParameters addInt128Array(BigInteger[] intArray) { */ public ContractFunctionParameters addInt136Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> int256(i, 136)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> int256(i, 136)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -1083,8 +1037,7 @@ public ContractFunctionParameters addInt136Array(BigInteger[] intArray) { */ public ContractFunctionParameters addInt144Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> int256(i, 144)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> int256(i, 144)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -1101,8 +1054,7 @@ public ContractFunctionParameters addInt144Array(BigInteger[] intArray) { */ public ContractFunctionParameters addInt152Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> int256(i, 152)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> int256(i, 152)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -1119,8 +1071,7 @@ public ContractFunctionParameters addInt152Array(BigInteger[] intArray) { */ public ContractFunctionParameters addInt160Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> int256(i, 160)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> int256(i, 160)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -1137,8 +1088,7 @@ public ContractFunctionParameters addInt160Array(BigInteger[] intArray) { */ public ContractFunctionParameters addInt168Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> int256(i, 168)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> int256(i, 168)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -1155,8 +1105,7 @@ public ContractFunctionParameters addInt168Array(BigInteger[] intArray) { */ public ContractFunctionParameters addInt176Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> int256(i, 176)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> int256(i, 176)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -1173,8 +1122,7 @@ public ContractFunctionParameters addInt176Array(BigInteger[] intArray) { */ public ContractFunctionParameters addInt184Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> int256(i, 184)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> int256(i, 184)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -1191,8 +1139,7 @@ public ContractFunctionParameters addInt184Array(BigInteger[] intArray) { */ public ContractFunctionParameters addInt192Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> int256(i, 192)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> int256(i, 192)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -1209,8 +1156,7 @@ public ContractFunctionParameters addInt192Array(BigInteger[] intArray) { */ public ContractFunctionParameters addInt200Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> int256(i, 200)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> int256(i, 200)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -1227,8 +1173,7 @@ public ContractFunctionParameters addInt200Array(BigInteger[] intArray) { */ public ContractFunctionParameters addInt208Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> int256(i, 208)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> int256(i, 208)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -1245,8 +1190,7 @@ public ContractFunctionParameters addInt208Array(BigInteger[] intArray) { */ public ContractFunctionParameters addInt216Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> int256(i, 216)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> int256(i, 216)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -1263,8 +1207,7 @@ public ContractFunctionParameters addInt216Array(BigInteger[] intArray) { */ public ContractFunctionParameters addInt224Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> int256(i, 224)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> int256(i, 224)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -1281,8 +1224,7 @@ public ContractFunctionParameters addInt224Array(BigInteger[] intArray) { */ public ContractFunctionParameters addInt232Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> int256(i, 232)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> int256(i, 232)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -1299,8 +1241,7 @@ public ContractFunctionParameters addInt232Array(BigInteger[] intArray) { */ public ContractFunctionParameters addInt240Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> int256(i, 240)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> int256(i, 240)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -1317,8 +1258,7 @@ public ContractFunctionParameters addInt240Array(BigInteger[] intArray) { */ public ContractFunctionParameters addInt248Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> int256(i, 248)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> int256(i, 248)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -1335,8 +1275,7 @@ public ContractFunctionParameters addInt248Array(BigInteger[] intArray) { */ public ContractFunctionParameters addInt256Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> int256(i, 256)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> int256(i, 256)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -1859,9 +1798,8 @@ public ContractFunctionParameters addUint256(BigInteger value) { public ContractFunctionParameters addUint8Array(byte[] intArray) { IntStream intStream = IntStream.range(0, intArray.length).map(idx -> intArray[idx]); - ByteString arrayBytes = ByteString.copyFrom( - intStream.mapToObj(i -> uint256(i, 8)) - .collect(Collectors.toList())); + ByteString arrayBytes = + ByteString.copyFrom(intStream.mapToObj(i -> uint256(i, 8)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -1881,8 +1819,7 @@ public ContractFunctionParameters addUint8Array(byte[] intArray) { */ public ContractFunctionParameters addUint16Array(int[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).mapToObj(i -> uint256(i, 16)) - .collect(Collectors.toList())); + Arrays.stream(intArray).mapToObj(i -> uint256(i, 16)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -1902,8 +1839,7 @@ public ContractFunctionParameters addUint16Array(int[] intArray) { */ public ContractFunctionParameters addUint24Array(int[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).mapToObj(i -> uint256(i, 24)) - .collect(Collectors.toList())); + Arrays.stream(intArray).mapToObj(i -> uint256(i, 24)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -1923,8 +1859,7 @@ public ContractFunctionParameters addUint24Array(int[] intArray) { */ public ContractFunctionParameters addUint32Array(int[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).mapToObj(i -> uint256(i, 32)) - .collect(Collectors.toList())); + Arrays.stream(intArray).mapToObj(i -> uint256(i, 32)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -1944,8 +1879,7 @@ public ContractFunctionParameters addUint32Array(int[] intArray) { */ public ContractFunctionParameters addUint40Array(long[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).mapToObj(i -> uint256(i, 40)) - .collect(Collectors.toList())); + Arrays.stream(intArray).mapToObj(i -> uint256(i, 40)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -1965,8 +1899,7 @@ public ContractFunctionParameters addUint40Array(long[] intArray) { */ public ContractFunctionParameters addUint48Array(long[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).mapToObj(i -> uint256(i, 48)) - .collect(Collectors.toList())); + Arrays.stream(intArray).mapToObj(i -> uint256(i, 48)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -1986,8 +1919,7 @@ public ContractFunctionParameters addUint48Array(long[] intArray) { */ public ContractFunctionParameters addUint56Array(long[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).mapToObj(i -> uint256(i, 56)) - .collect(Collectors.toList())); + Arrays.stream(intArray).mapToObj(i -> uint256(i, 56)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -2007,8 +1939,7 @@ public ContractFunctionParameters addUint56Array(long[] intArray) { */ public ContractFunctionParameters addUint64Array(long[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).mapToObj(i -> uint256(i, 64)) - .collect(Collectors.toList())); + Arrays.stream(intArray).mapToObj(i -> uint256(i, 64)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -2029,8 +1960,7 @@ public ContractFunctionParameters addUint64Array(long[] intArray) { */ public ContractFunctionParameters addUint72Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> uint256(i, 72)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> uint256(i, 72)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -2051,8 +1981,7 @@ public ContractFunctionParameters addUint72Array(BigInteger[] intArray) { */ public ContractFunctionParameters addUint80Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> uint256(i, 80)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> uint256(i, 80)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -2073,8 +2002,7 @@ public ContractFunctionParameters addUint80Array(BigInteger[] intArray) { */ public ContractFunctionParameters addUint88Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> uint256(i, 88)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> uint256(i, 88)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -2095,8 +2023,7 @@ public ContractFunctionParameters addUint88Array(BigInteger[] intArray) { */ public ContractFunctionParameters addUint96Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> uint256(i, 96)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> uint256(i, 96)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -2117,8 +2044,7 @@ public ContractFunctionParameters addUint96Array(BigInteger[] intArray) { */ public ContractFunctionParameters addUint104Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> uint256(i, 104)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> uint256(i, 104)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -2139,8 +2065,7 @@ public ContractFunctionParameters addUint104Array(BigInteger[] intArray) { */ public ContractFunctionParameters addUint112Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> uint256(i, 112)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> uint256(i, 112)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -2161,8 +2086,7 @@ public ContractFunctionParameters addUint112Array(BigInteger[] intArray) { */ public ContractFunctionParameters addUint120Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> uint256(i, 120)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> uint256(i, 120)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -2183,8 +2107,7 @@ public ContractFunctionParameters addUint120Array(BigInteger[] intArray) { */ public ContractFunctionParameters addUint128Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> uint256(i, 128)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> uint256(i, 128)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -2205,8 +2128,7 @@ public ContractFunctionParameters addUint128Array(BigInteger[] intArray) { */ public ContractFunctionParameters addUint136Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> uint256(i, 136)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> uint256(i, 136)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -2227,8 +2149,7 @@ public ContractFunctionParameters addUint136Array(BigInteger[] intArray) { */ public ContractFunctionParameters addUint144Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> uint256(i, 144)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> uint256(i, 144)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -2249,8 +2170,7 @@ public ContractFunctionParameters addUint144Array(BigInteger[] intArray) { */ public ContractFunctionParameters addUint152Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> uint256(i, 152)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> uint256(i, 152)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -2271,8 +2191,7 @@ public ContractFunctionParameters addUint152Array(BigInteger[] intArray) { */ public ContractFunctionParameters addUint160Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> uint256(i, 160)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> uint256(i, 160)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -2293,8 +2212,7 @@ public ContractFunctionParameters addUint160Array(BigInteger[] intArray) { */ public ContractFunctionParameters addUint168Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> uint256(i, 168)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> uint256(i, 168)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -2315,8 +2233,7 @@ public ContractFunctionParameters addUint168Array(BigInteger[] intArray) { */ public ContractFunctionParameters addUint176Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> uint256(i, 176)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> uint256(i, 176)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -2337,8 +2254,7 @@ public ContractFunctionParameters addUint176Array(BigInteger[] intArray) { */ public ContractFunctionParameters addUint184Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> uint256(i, 184)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> uint256(i, 184)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -2359,8 +2275,7 @@ public ContractFunctionParameters addUint184Array(BigInteger[] intArray) { */ public ContractFunctionParameters addUint192Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> uint256(i, 192)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> uint256(i, 192)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -2381,8 +2296,7 @@ public ContractFunctionParameters addUint192Array(BigInteger[] intArray) { */ public ContractFunctionParameters addUint200Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> uint256(i, 200)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> uint256(i, 200)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -2403,8 +2317,7 @@ public ContractFunctionParameters addUint200Array(BigInteger[] intArray) { */ public ContractFunctionParameters addUint208Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> uint256(i, 208)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> uint256(i, 208)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -2425,8 +2338,7 @@ public ContractFunctionParameters addUint208Array(BigInteger[] intArray) { */ public ContractFunctionParameters addUint216Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> uint256(i, 216)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> uint256(i, 216)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -2447,8 +2359,7 @@ public ContractFunctionParameters addUint216Array(BigInteger[] intArray) { */ public ContractFunctionParameters addUint224Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> uint256(i, 224)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> uint256(i, 224)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -2469,8 +2380,7 @@ public ContractFunctionParameters addUint224Array(BigInteger[] intArray) { */ public ContractFunctionParameters addUint232Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> uint256(i, 232)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> uint256(i, 232)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -2491,8 +2401,7 @@ public ContractFunctionParameters addUint232Array(BigInteger[] intArray) { */ public ContractFunctionParameters addUint240Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> uint256(i, 240)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> uint256(i, 240)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -2513,8 +2422,7 @@ public ContractFunctionParameters addUint240Array(BigInteger[] intArray) { */ public ContractFunctionParameters addUint248Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> uint256(i, 248)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> uint256(i, 248)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -2535,8 +2443,7 @@ public ContractFunctionParameters addUint248Array(BigInteger[] intArray) { */ public ContractFunctionParameters addUint256Array(BigInteger[] intArray) { ByteString arrayBytes = ByteString.copyFrom( - Arrays.stream(intArray).map(i -> uint256(i, 256)) - .collect(Collectors.toList())); + Arrays.stream(intArray).map(i -> uint256(i, 256)).collect(Collectors.toList())); arrayBytes = uint256(intArray.length, 32).concat(arrayBytes); @@ -2574,11 +2481,10 @@ public ContractFunctionParameters addAddress(String address) { * @throws NullPointerException if any value in the array is null. */ public ContractFunctionParameters addAddressArray(String[] addresses) { - ByteString addressArray = encodeArray( - Arrays.stream(addresses).map(a -> { - byte[] address = decodeAddress(a); - return leftPad32(ByteString.copyFrom(address)); - })); + ByteString addressArray = encodeArray(Arrays.stream(addresses).map(a -> { + byte[] address = decodeAddress(a); + return leftPad32(ByteString.copyFrom(address)); + })); args.add(new Argument("address[]", addressArray, true)); @@ -2651,8 +2557,7 @@ ByteString toBytes(@Nullable String funcName) { var dynamicArgs = new ArrayList(); - ContractFunctionSelector functionSelector = funcName != null - ? new ContractFunctionSelector(funcName) : null; + ContractFunctionSelector functionSelector = funcName != null ? new ContractFunctionSelector(funcName) : null; // iterate the arguments and determine whether they are dynamic or not for (Argument arg : args) { @@ -2681,7 +2586,7 @@ ByteString toBytes(@Nullable String funcName) { return ByteString.copyFrom(paramsBytes); } - private final static class Argument { + private static final class Argument { private final String type; private final ByteString value; diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractFunctionResult.java b/sdk/src/main/java/org/hiero/sdk/ContractFunctionResult.java similarity index 84% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/ContractFunctionResult.java rename to sdk/src/main/java/org/hiero/sdk/ContractFunctionResult.java index 32fb5ede74..7304abbd24 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractFunctionResult.java +++ b/sdk/src/main/java/org/hiero/sdk/ContractFunctionResult.java @@ -1,23 +1,5 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.esaulpaugh.headlong.abi.Tuple; import com.esaulpaugh.headlong.abi.TupleType; @@ -25,16 +7,15 @@ import com.google.protobuf.ByteString; import com.google.protobuf.BytesValue; import com.google.protobuf.Int64Value; -import com.hedera.hashgraph.sdk.proto.ContractFunctionResultOrBuilder; import java.math.BigInteger; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; - import javax.annotation.Nullable; import org.bouncycastle.util.encoders.Hex; +import org.hiero.sdk.proto.ContractFunctionResultOrBuilder; /** * Result of invoking a contract via {@link ContractCallQuery}, or {@link ContractExecuteTransaction}, or the result of @@ -44,7 +25,7 @@ * this Github issue. */ public final class ContractFunctionResult { - private static final ByteString errorPrefix = ByteString.copyFrom(new byte[]{8, -61, 121, -96}); + private static final ByteString errorPrefix = ByteString.copyFrom(new byte[] {8, -61, 121, -96}); /** * The ID of the contract that was invoked. @@ -136,8 +117,12 @@ public final class ContractFunctionResult { ContractFunctionResult(ContractFunctionResultOrBuilder inner) { contractId = ContractId.fromProtobuf(inner.getContractID()); - evmAddress = inner.hasEvmAddress() ? - new ContractId(contractId.shard, contractId.realm, inner.getEvmAddress().getValue().toByteArray()) : null; + evmAddress = inner.hasEvmAddress() + ? new ContractId( + contractId.shard, + contractId.realm, + inner.getEvmAddress().getValue().toByteArray()) + : null; String errMsg = inner.getErrorMessage(); errorMessage = !errMsg.isEmpty() ? errMsg : null; @@ -158,9 +143,13 @@ public final class ContractFunctionResult { gasUsed = inner.getGasUsed(); - logs = inner.getLogInfoList().stream().map(ContractLogInfo::fromProtobuf).collect(Collectors.toList()); + logs = inner.getLogInfoList().stream() + .map(ContractLogInfo::fromProtobuf) + .collect(Collectors.toList()); - createdContractIds = inner.getCreatedContractIDsList().stream().map(ContractId::fromProtobuf).collect(Collectors.toList()); + createdContractIds = inner.getCreatedContractIDsList().stream() + .map(ContractId::fromProtobuf) + .collect(Collectors.toList()); stateChanges = new ArrayList(); // for (var stateChangeProto : inner.getStateChangesList()) { @@ -175,7 +164,9 @@ public final class ContractFunctionResult { senderAccountId = inner.hasSenderId() ? AccountId.fromProtobuf(inner.getSenderId()) : null; - contractNonces = inner.getContractNoncesList().stream().map(ContractNonceInfo::fromProtobuf).collect(Collectors.toList()); + contractNonces = inner.getContractNoncesList().stream() + .map(ContractNonceInfo::fromProtobuf) + .collect(Collectors.toList()); signerNonce = inner.getSignerNonce().getValue(); } @@ -213,7 +204,8 @@ public List getStringArray(int index) { for (int i = 0; i < count; i++) { var strOffset = getIntValueAt(offset + 32 + (i * 32)); var len = getIntValueAt(offset + strOffset + 32); - var str = getByteString(offset + strOffset + 32 + 32, offset + strOffset + 32 + 32 + len).toStringUtf8(); + var str = getByteString(offset + strOffset + 32 + 32, offset + strOffset + 32 + 32 + len) + .toStringUtf8(); strings.add(str); } @@ -424,21 +416,20 @@ private ByteString getByteString(int startIndex, int endIndex) { /** * Create the protobuf representation. * - * @return {@link com.hedera.hashgraph.sdk.proto.ContractFunctionResult} + * @return {@link org.hiero.sdk.proto.ContractFunctionResult} */ - com.hedera.hashgraph.sdk.proto.ContractFunctionResult toProtobuf() { - var contractFunctionResult = com.hedera.hashgraph.sdk.proto.ContractFunctionResult.newBuilder() - .setContractID(contractId.toProtobuf()) - .setContractCallResult(rawResult) - .setBloom(bloom) - .setGasUsed(gasUsed) - .setSignerNonce(Int64Value.of(this.signerNonce)); + org.hiero.sdk.proto.ContractFunctionResult toProtobuf() { + var contractFunctionResult = org.hiero.sdk.proto.ContractFunctionResult.newBuilder() + .setContractID(contractId.toProtobuf()) + .setContractCallResult(rawResult) + .setBloom(bloom) + .setGasUsed(gasUsed) + .setSignerNonce(Int64Value.of(this.signerNonce)); if (evmAddress != null) { contractFunctionResult.setEvmAddress(BytesValue.newBuilder() - .setValue(ByteString.copyFrom(Objects.requireNonNull(evmAddress.evmAddress))) - .build() - ); + .setValue(ByteString.copyFrom(Objects.requireNonNull(evmAddress.evmAddress))) + .build()); } if (errorMessage != null) { @@ -471,22 +462,22 @@ com.hedera.hashgraph.sdk.proto.ContractFunctionResult toProtobuf() { @Override public String toString() { return MoreObjects.toStringHelper(this) - .add("contractId", contractId) - .add("evmAddress", evmAddress) - .add("errorMessage", errorMessage) - .add("bloom", Hex.toHexString(bloom.toByteArray())) - .add("gasUsed", gasUsed) - .add("logs", logs) - .add("createdContractIds", createdContractIds) - .add("stateChanges", stateChanges) - .add("gas", gas) - .add("hbarAmount", hbarAmount) - .add("contractFunctionparametersBytes", Hex.toHexString(contractFunctionParametersBytes)) - .add("rawResult", Hex.toHexString(rawResult.toByteArray())) - .add("senderAccountId", senderAccountId) - .add("contractNonces", contractNonces) - .add("signerNonce", signerNonce) - .toString(); + .add("contractId", contractId) + .add("evmAddress", evmAddress) + .add("errorMessage", errorMessage) + .add("bloom", Hex.toHexString(bloom.toByteArray())) + .add("gasUsed", gasUsed) + .add("logs", logs) + .add("createdContractIds", createdContractIds) + .add("stateChanges", stateChanges) + .add("gas", gas) + .add("hbarAmount", hbarAmount) + .add("contractFunctionparametersBytes", Hex.toHexString(contractFunctionParametersBytes)) + .add("rawResult", Hex.toHexString(rawResult.toByteArray())) + .add("senderAccountId", senderAccountId) + .add("contractNonces", contractNonces) + .add("signerNonce", signerNonce) + .toString(); } public Tuple getResult(String types) { diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractFunctionSelector.java b/sdk/src/main/java/org/hiero/sdk/ContractFunctionSelector.java similarity index 91% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/ContractFunctionSelector.java rename to sdk/src/main/java/org/hiero/sdk/ContractFunctionSelector.java index b10944efeb..fb5f9a719b 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractFunctionSelector.java +++ b/sdk/src/main/java/org/hiero/sdk/ContractFunctionSelector.java @@ -1,31 +1,12 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; -import org.bouncycastle.jcajce.provider.digest.Keccak; +import static java.nio.charset.StandardCharsets.US_ASCII; -import javax.annotation.Nullable; import java.util.Arrays; import java.util.Objects; - -import static java.nio.charset.StandardCharsets.US_ASCII; +import javax.annotation.Nullable; +import org.bouncycastle.jcajce.provider.digest.Keccak; /** * Builder class for Solidity function selectors. diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractId.java b/sdk/src/main/java/org/hiero/sdk/ContractId.java similarity index 86% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/ContractId.java rename to sdk/src/main/java/org/hiero/sdk/ContractId.java index ff486069b0..816e7e4421 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractId.java +++ b/sdk/src/main/java/org/hiero/sdk/ContractId.java @@ -1,27 +1,8 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.ContractID; import java.util.Arrays; import java.util.Objects; import java.util.concurrent.CompletableFuture; @@ -30,6 +11,7 @@ import javax.annotation.Nonnegative; import javax.annotation.Nullable; import org.bouncycastle.util.encoders.Hex; +import org.hiero.sdk.proto.ContractID; /** * The ID for a smart contract instance on Hedera. @@ -118,10 +100,7 @@ public static ContractId fromString(String id) { var match = EVM_ADDRESS_REGEX.matcher(id); if (match.find()) { return new ContractId( - Long.parseLong(match.group(1)), - Long.parseLong(match.group(2)), - Hex.decode(match.group(3)) - ); + Long.parseLong(match.group(1)), Long.parseLong(match.group(2)), Hex.decode(match.group(3))); } else { return EntityIdHelper.fromString(id, ContractId::new); } @@ -151,10 +130,7 @@ public static ContractId fromSolidityAddress(String address) { */ public static ContractId fromEvmAddress(@Nonnegative long shard, @Nonnegative long realm, String evmAddress) { return new ContractId( - shard, - realm, - Hex.decode(evmAddress.startsWith("0x") ? evmAddress.substring(2) : evmAddress) - ); + shard, realm, Hex.decode(evmAddress.startsWith("0x") ? evmAddress.substring(2) : evmAddress)); } /** @@ -167,10 +143,9 @@ static ContractId fromProtobuf(ContractID contractId) { Objects.requireNonNull(contractId); if (contractId.hasEvmAddress()) { return new ContractId( - contractId.getShardNum(), - contractId.getRealmNum(), - contractId.getEvmAddress().toByteArray() - ); + contractId.getShardNum(), + contractId.getRealmNum(), + contractId.getEvmAddress().toByteArray()); } else { return new ContractId(contractId.getShardNum(), contractId.getRealmNum(), contractId.getContractNum()); } @@ -206,9 +181,7 @@ public String toSolidityAddress() { * @return the protobuf object */ ContractID toProtobuf() { - var builder = ContractID.newBuilder() - .setShardNum(shard) - .setRealmNum(realm); + var builder = ContractID.newBuilder().setShardNum(shard).setRealmNum(realm); if (evmAddress != null) { builder.setEvmAddress(ByteString.copyFrom(evmAddress)); } else { @@ -245,8 +218,8 @@ public CompletableFuture populateContractNumAsync(Client client) { EvmAddress address = new EvmAddress(this.evmAddress); return EntityIdHelper.getContractNumFromMirrorNodeAsync(client, address.toString()) - .thenApply(contractNumFromMirrorNode -> - new ContractId(this.shard, this.realm, contractNumFromMirrorNode, checksum)); + .thenApply(contractNumFromMirrorNode -> + new ContractId(this.shard, this.realm, contractNumFromMirrorNode, checksum)); } /** @@ -280,10 +253,8 @@ public String getChecksum() { } @Override - com.hedera.hashgraph.sdk.proto.Key toProtobufKey() { - return com.hedera.hashgraph.sdk.proto.Key.newBuilder() - .setContractID(toProtobuf()) - .build(); + org.hiero.sdk.proto.Key toProtobufKey() { + return org.hiero.sdk.proto.Key.newBuilder().setContractID(toProtobuf()).build(); } @Override @@ -320,7 +291,7 @@ public int hashCode() { } @Override - public boolean equals( Object o) { + public boolean equals(Object o) { if (this == o) { return true; } diff --git a/sdk/src/main/java/org/hiero/sdk/ContractInfo.java b/sdk/src/main/java/org/hiero/sdk/ContractInfo.java new file mode 100644 index 0000000000..b28f5d301d --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/ContractInfo.java @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import com.google.protobuf.InvalidProtocolBufferException; +import java.time.Duration; +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.ContractGetInfoResponse; + +/** + * Current information on the smart contract instance, including its balance. + */ +public final class ContractInfo { + /** + * ID of the contract instance, in the format used in transactions. + */ + public final ContractId contractId; + + /** + * ID of the cryptocurrency account owned by the contract instance, + * in the format used in transactions. + */ + public final AccountId accountId; + + /** + * ID of both the contract instance and the cryptocurrency account owned by the contract + * instance, in the format used by Solidity. + */ + public final String contractAccountId; + + /** + * The state of the instance and its fields can be modified arbitrarily if this key signs a + * transaction to modify it. If this is null, then such modifications are not possible, + * and there is no administrator that can override the normal operation of this smart + * contract instance. Note that if it is created with no admin keys, then there is no + * administrator to authorize changing the admin keys, so there can never be any admin keys + * for that instance. + */ + @Nullable + public final Key adminKey; + + /** + * The current time at which this contract instance (and its account) is set to expire. + */ + public final Instant expirationTime; + + /** + * The expiration time will extend every this many seconds. If there are insufficient funds, + * then it extends as long as possible. If the account is empty when it expires, + * then it is deleted. + */ + public final Duration autoRenewPeriod; + + /** + * ID of the an account to charge for auto-renewal of this contract. If not set, or set to + * an account with zero hbar balance, the contract's own hbar balance will be used to cover + * auto-renewal fees. + */ + @Nullable + public final AccountId autoRenewAccountId; + + /** + * Number of bytes of storage being used by this instance (which affects the cost to + * extend the expiration time). + */ + public final long storage; + + /** + * The memo associated with the contract (max 100 bytes). + */ + public final String contractMemo; + + /** + * The current balance of the contract. + */ + public final Hbar balance; + + /** + * Whether the contract has been deleted + */ + public final boolean isDeleted; + + /** + * The tokens associated to the contract + */ + public final Map tokenRelationships; + + /** + * The ledger ID the response was returned from; please see HIP-198 for the network-specific IDs. + */ + public final LedgerId ledgerId; + + /** + * Staking metadata for this account. + */ + @Nullable + public final StakingInfo stakingInfo; + + /** + * Constructor. + * + * @param contractId the contract id + * @param accountId the account id + * @param contractAccountId the account id of the owner + * @param adminKey the key that can modify the contract + * @param expirationTime the time that contract will expire + * @param autoRenewPeriod seconds before contract is renewed (funds must be available) + * @param autoRenewAccountId account ID which will be charged for renewing this account + * @param storage number of bytes used by this contract + * @param contractMemo the memo field 100 bytes + * @param balance current balance + * @param isDeleted does it still exist + * @param tokenRelationships list of compound token id and relationship records + * @param ledgerId the ledger id + */ + private ContractInfo( + ContractId contractId, + AccountId accountId, + String contractAccountId, + @Nullable Key adminKey, + Instant expirationTime, + Duration autoRenewPeriod, + @Nullable AccountId autoRenewAccountId, + long storage, + String contractMemo, + Hbar balance, + boolean isDeleted, + Map tokenRelationships, + LedgerId ledgerId, + @Nullable StakingInfo stakingInfo) { + this.contractId = contractId; + this.accountId = accountId; + this.contractAccountId = contractAccountId; + this.adminKey = adminKey; + this.expirationTime = expirationTime; + this.autoRenewPeriod = autoRenewPeriod; + this.autoRenewAccountId = autoRenewAccountId; + this.storage = storage; + this.contractMemo = contractMemo; + this.balance = balance; + this.isDeleted = isDeleted; + this.tokenRelationships = tokenRelationships; + this.ledgerId = ledgerId; + this.stakingInfo = stakingInfo; + } + + /** + * Extract the contract from the protobuf. + * + * @param contractInfo the protobuf + * @return the contract object + */ + static ContractInfo fromProtobuf(ContractGetInfoResponse.ContractInfo contractInfo) { + var adminKey = contractInfo.hasAdminKey() ? Key.fromProtobufKey(contractInfo.getAdminKey()) : null; + + var tokenRelationships = new HashMap(contractInfo.getTokenRelationshipsCount()); + + for (var relationship : contractInfo.getTokenRelationshipsList()) { + tokenRelationships.put( + TokenId.fromProtobuf(relationship.getTokenId()), TokenRelationship.fromProtobuf(relationship)); + } + + return new ContractInfo( + ContractId.fromProtobuf(contractInfo.getContractID()), + AccountId.fromProtobuf(contractInfo.getAccountID()), + contractInfo.getContractAccountID(), + adminKey, + InstantConverter.fromProtobuf(contractInfo.getExpirationTime()), + DurationConverter.fromProtobuf(contractInfo.getAutoRenewPeriod()), + contractInfo.hasAutoRenewAccountId() + ? AccountId.fromProtobuf(contractInfo.getAutoRenewAccountId()) + : null, + contractInfo.getStorage(), + contractInfo.getMemo(), + Hbar.fromTinybars(contractInfo.getBalance()), + contractInfo.getDeleted(), + tokenRelationships, + LedgerId.fromByteString(contractInfo.getLedgerId()), + contractInfo.hasStakingInfo() ? StakingInfo.fromProtobuf(contractInfo.getStakingInfo()) : null); + } + + /** + * Extract the contract from a byte array. + * + * @param bytes the byte array + * @return the extracted contract + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + public static ContractInfo fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + return fromProtobuf(ContractGetInfoResponse.ContractInfo.parseFrom(bytes).toBuilder() + .build()); + } + + /** + * Build the protobuf. + * + * @return the protobuf representation + */ + ContractGetInfoResponse.ContractInfo toProtobuf() { + var contractInfoBuilder = ContractGetInfoResponse.ContractInfo.newBuilder() + .setContractID(contractId.toProtobuf()) + .setAccountID(accountId.toProtobuf()) + .setContractAccountID(contractAccountId) + .setExpirationTime(InstantConverter.toProtobuf(expirationTime)) + .setAutoRenewPeriod(DurationConverter.toProtobuf(autoRenewPeriod)) + .setStorage(storage) + .setMemo(contractMemo) + .setBalance(balance.toTinybars()) + .setLedgerId(ledgerId.toByteString()); + + if (adminKey != null) { + contractInfoBuilder.setAdminKey(adminKey.toProtobufKey()); + } + + if (stakingInfo != null) { + contractInfoBuilder.setStakingInfo(stakingInfo.toProtobuf()); + } + + if (autoRenewAccountId != null) { + contractInfoBuilder.setAutoRenewAccountId(autoRenewAccountId.toProtobuf()); + } + + return contractInfoBuilder.build(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("contractId", contractId) + .add("accountId", accountId) + .add("contractAccountId", contractAccountId) + .add("adminKey", adminKey) + .add("expirationTime", expirationTime) + .add("autoRenewPeriod", autoRenewPeriod) + .add("autoRenewAccountId", autoRenewAccountId) + .add("storage", storage) + .add("contractMemo", contractMemo) + .add("balance", balance) + .add("isDeleted", isDeleted) + .add("tokenRelationships", tokenRelationships) + .add("ledgerId", ledgerId) + .add("stakingInfo", stakingInfo) + .toString(); + } + + /** + * Create a byte array representation. + * + * @return the byte array representation + */ + public byte[] toBytes() { + return toProtobuf().toByteArray(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/ContractInfoQuery.java b/sdk/src/main/java/org/hiero/sdk/ContractInfoQuery.java new file mode 100644 index 0000000000..3507d251c7 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/ContractInfoQuery.java @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.grpc.MethodDescriptor; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.ContractGetInfoQuery; +import org.hiero.sdk.proto.QueryHeader; +import org.hiero.sdk.proto.Response; +import org.hiero.sdk.proto.ResponseHeader; +import org.hiero.sdk.proto.SmartContractServiceGrpc; + +/** + * Get information about a smart contract instance. + *

+ * This includes the account that it uses, the file containing its bytecode, + * and the time when it will expire. + */ +public final class ContractInfoQuery extends Query { + @Nullable + private ContractId contractId = null; + + /** + * Constructor. + */ + public ContractInfoQuery() {} + + /** + * Extract the contract id. + * + * @return the contract id + */ + @Nullable + public ContractId getContractId() { + return contractId; + } + + /** + * Sets the contract ID for which information is requested. + * + * @param contractId The ContractId to be set + * @return {@code this} + */ + public ContractInfoQuery setContractId(ContractId contractId) { + Objects.requireNonNull(contractId); + this.contractId = contractId; + return this; + } + + @Override + public CompletableFuture getCostAsync(Client client) { + // deleted accounts return a COST_ANSWER of zero which triggers `INSUFFICIENT_TX_FEE` + // if you set that as the query payment; 25 tinybar seems to be enough to get + // `CONTRACT_DELETED` back instead. + return super.getCostAsync(client).thenApply((cost) -> Hbar.fromTinybars(Math.max(cost.toTinybars(), 25))); + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (contractId != null) { + contractId.validateChecksum(client); + } + } + + @Override + void onMakeRequest(org.hiero.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { + var builder = ContractGetInfoQuery.newBuilder(); + if (contractId != null) { + builder.setContractID(contractId.toProtobuf()); + } + + queryBuilder.setContractGetInfo(builder.setHeader(header)); + } + + @Override + ResponseHeader mapResponseHeader(Response response) { + return response.getContractGetInfo().getHeader(); + } + + @Override + QueryHeader mapRequestHeader(org.hiero.sdk.proto.Query request) { + return request.getContractGetInfo().getHeader(); + } + + @Override + ContractInfo mapResponse(Response response, AccountId nodeId, org.hiero.sdk.proto.Query request) { + return ContractInfo.fromProtobuf(response.getContractGetInfo().getContractInfo()); + } + + @Override + MethodDescriptor getMethodDescriptor() { + return SmartContractServiceGrpc.getGetContractInfoMethod(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/ContractLogInfo.java b/sdk/src/main/java/org/hiero/sdk/ContractLogInfo.java new file mode 100644 index 0000000000..8ee06e0150 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/ContractLogInfo.java @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import java.util.ArrayList; +import java.util.List; +import org.bouncycastle.util.encoders.Hex; +import org.hiero.sdk.proto.ContractLoginfo; + +/** + * The log information for an event returned by a smart contract function call. + * One function call may return several such events. + */ +public final class ContractLogInfo { + /** + * Address of a contract that emitted the event. + */ + public final ContractId contractId; + + /** + * Bloom filter for a particular log. + */ + public final ByteString bloom; + + /** + * Topics of a particular event. + */ + public final List topics; + + /** + * The event data. + */ + public final ByteString data; + + /** + * Constructor. + * + * @param contractId the contract id + * @param bloom the bloom filter + * @param topics list of topics + * @param data the event data + */ + private ContractLogInfo(ContractId contractId, ByteString bloom, List topics, ByteString data) { + this.contractId = contractId; + this.bloom = bloom; + this.topics = topics; + this.data = data; + } + + /** + * Convert to a protobuf. + * + * @param logInfo the log info object + * @return the protobuf + */ + static ContractLogInfo fromProtobuf(org.hiero.sdk.proto.ContractLoginfo logInfo) { + return new ContractLogInfo( + ContractId.fromProtobuf(logInfo.getContractID()), + logInfo.getBloom(), + logInfo.getTopicList(), + logInfo.getData()); + } + + /** + * Create the contract log info from a byte array. + * + * @param bytes the byte array + * @return the contract log info object + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + public static ContractLogInfo fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + return fromProtobuf(ContractLoginfo.parseFrom(bytes)); + } + + /** + * Create the protobuf. + * + * @return the protobuf representation + */ + org.hiero.sdk.proto.ContractLoginfo toProtobuf() { + var contractLogInfo = org.hiero.sdk.proto.ContractLoginfo.newBuilder() + .setContractID(contractId.toProtobuf()) + .setBloom(bloom); + + for (ByteString topic : topics) { + contractLogInfo.addTopic(topic); + } + + return contractLogInfo.build(); + } + + /** + * Create the byte array. + * + * @return the byte array representation + */ + public byte[] toBytes() { + return toProtobuf().toByteArray(); + } + + @Override + public String toString() { + var stringHelper = MoreObjects.toStringHelper(this) + .add("contractId", contractId) + .add("bloom", Hex.toHexString(bloom.toByteArray())); + + var topicList = new ArrayList<>(); + + for (var topic : topics) { + topicList.add(Hex.toHexString(topic.toByteArray())); + } + + return stringHelper.add("topics", topicList).toString(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/ContractNonceInfo.java b/sdk/src/main/java/org/hiero/sdk/ContractNonceInfo.java new file mode 100644 index 0000000000..03761d6287 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/ContractNonceInfo.java @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import com.google.protobuf.InvalidProtocolBufferException; +import java.util.Objects; + +/** + * Info about a contract account's nonce value. + * A nonce of a contract is only incremented when that contract creates another contract. + */ +public final class ContractNonceInfo { + /** + * Id of the contract + */ + public final ContractId contractId; + + /** + * The current value of the contract account's nonce property + */ + public final Long nonce; + + public ContractNonceInfo(ContractId contractId, Long nonce) { + this.contractId = contractId; + this.nonce = nonce; + } + + /** + * Extract the contractNonce from the protobuf. + * + * @param contractNonceInfo the protobuf + * @return the contract object + */ + static ContractNonceInfo fromProtobuf(org.hiero.sdk.proto.ContractNonceInfo contractNonceInfo) { + return new ContractNonceInfo( + ContractId.fromProtobuf(contractNonceInfo.getContractId()), contractNonceInfo.getNonce()); + } + + /** + * Extract the contractNonce from a byte array. + * + * @param bytes the byte array + * @return the extracted contract + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + public static ContractNonceInfo fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + return fromProtobuf(org.hiero.sdk.proto.ContractNonceInfo.parseFrom(bytes).toBuilder() + .build()); + } + + /** + * Build the protobuf. + * + * @return the protobuf representation + */ + org.hiero.sdk.proto.ContractNonceInfo toProtobuf() { + return org.hiero.sdk.proto.ContractNonceInfo.newBuilder() + .setContractId(contractId.toProtobuf()) + .setNonce(nonce) + .build(); + } + + @Override + public int hashCode() { + return Objects.hash(contractId, nonce); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof ContractNonceInfo otherInfo)) { + return false; + } + + return contractId.equals(otherInfo.contractId) && nonce.equals(otherInfo.nonce); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("contractId", contractId) + .add("nonce", nonce) + .toString(); + } + + /** + * Create a byte array representation. + * + * @return the byte array representation + */ + public byte[] toBytes() { + return toProtobuf().toByteArray(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/ContractStateChange.java b/sdk/src/main/java/org/hiero/sdk/ContractStateChange.java new file mode 100644 index 0000000000..fa88004400 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/ContractStateChange.java @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import java.util.List; + +/** + * @deprecated - User mirror nodes for contract traceability instead + * + * The storage changes to a smart contract's storage as a side effect of the function call. + * See Hedera Documentation + */ +@Deprecated +public class ContractStateChange { + /** + * The contract to which the storage changes apply to + */ + public final ContractId contractId; + + /** + * The list of storage changes + */ + public final List storageChanges; + + /** + * Constructor. + * + * @param contractId the contract id + * @param storageChanges the list of storage change objects + */ + ContractStateChange(ContractId contractId, List storageChanges) { + this.contractId = contractId; + this.storageChanges = storageChanges; + } + + // /** + // * Create contract stage change object from protobuf. + // * + // * @param stateChangeProto the protobuf + // * @return the contract stage change object + // */ + // static ContractStateChange fromProtobuf(org.hiero.sdk.proto.ContractStateChange stateChangeProto) { + // List storageChanges = new ArrayList<>(stateChangeProto.getStorageChangesCount()); + // for (var storageChangeProto : stateChangeProto.getStorageChangesList()) { + // storageChanges.add(StorageChange.fromProtobuf(storageChangeProto)); + // } + // return new ContractStateChange(ContractId.fromProtobuf(stateChangeProto.getContractID()), storageChanges); + // } + // + // /** + // * Create contract stage change object from byte array. + // * + // * @param bytes the byte array + // * @return the contract stage change object + // * @throws InvalidProtocolBufferException when there is an issue with the protobuf + // */ + // public static ContractStateChange fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + // return fromProtobuf(org.hiero.sdk.proto.ContractStateChange.parseFrom(bytes)); + // } + // + // /** + // * Create the protobuf. + // * + // * @return the protobuf representation + // */ + // org.hiero.sdk.proto.ContractStateChange toProtobuf() { + // var builder = org.hiero.sdk.proto.ContractStateChange.newBuilder() + // .setContractID(contractId.toProtobuf()); + // for (var storageChange : storageChanges) { + // builder.addStorageChanges(storageChange.toProtobuf()); + // } + // return builder.build(); + // } + // + // /** + // * Create the byte array. + // * + // * @return the byte array representation + // */ + // public byte[] toBytes() { + // return toProtobuf().toByteArray(); + // } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractUpdateTransaction.java b/sdk/src/main/java/org/hiero/sdk/ContractUpdateTransaction.java similarity index 90% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/ContractUpdateTransaction.java rename to sdk/src/main/java/org/hiero/sdk/ContractUpdateTransaction.java index bcc748da41..8c57a0a6be 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/ContractUpdateTransaction.java +++ b/sdk/src/main/java/org/hiero/sdk/ContractUpdateTransaction.java @@ -1,40 +1,21 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.BoolValue; import com.google.protobuf.Int32Value; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.StringValue; -import com.hedera.hashgraph.sdk.proto.ContractUpdateTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.SmartContractServiceGrpc; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; import io.grpc.MethodDescriptor; import java.time.Duration; import java.time.Instant; - -import javax.annotation.Nullable; import java.util.LinkedHashMap; import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.ContractUpdateTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.SmartContractServiceGrpc; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; /** * Modify a smart contract instance to have the given parameter values. @@ -62,18 +43,25 @@ public final class ContractUpdateTransaction extends Transaction { @Nullable private ContractId contractId = null; + @Nullable private AccountId proxyAccountId = null; + @Nullable private FileId bytecodeFileId = null; + @Nullable private Instant expirationTime = null; + @Nullable private Key adminKey = null; + @Nullable private Integer maxAutomaticTokenAssociations = null; + @Nullable private Duration autoRenewPeriod = null; + @Nullable private String contractMemo = null; @@ -92,8 +80,7 @@ public final class ContractUpdateTransaction extends Transaction> txs) throws InvalidProtocolBufferException { + ContractUpdateTransaction( + LinkedHashMap> txs) + throws InvalidProtocolBufferException { super(txs); initFromTransactionBody(); } @@ -110,7 +99,7 @@ public ContractUpdateTransaction() { * * @param txBody protobuf TransactionBody */ - ContractUpdateTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { + ContractUpdateTransaction(org.hiero.sdk.proto.TransactionBody txBody) { super(txBody); initFromTransactionBody(); } @@ -480,7 +469,8 @@ void initFromTransactionBody() { adminKey = Key.fromProtobufKey(body.getAdminKey()); } if (body.hasMaxAutomaticTokenAssociations()) { - maxAutomaticTokenAssociations = body.getMaxAutomaticTokenAssociations().getValue(); + maxAutomaticTokenAssociations = + body.getMaxAutomaticTokenAssociations().getValue(); } if (body.hasAutoRenewPeriod()) { autoRenewPeriod = DurationConverter.fromProtobuf(body.getAutoRenewPeriod()); @@ -509,7 +499,7 @@ void initFromTransactionBody() { /** * Build the correct transaction body. * - * @return {@link com.hedera.hashgraph.sdk.proto.ContractUpdateTransactionBody builder } + * @return {@link org.hiero.sdk.proto.ContractUpdateTransactionBody builder } */ ContractUpdateTransactionBody.Builder build() { var builder = ContractUpdateTransactionBody.newBuilder(); @@ -544,7 +534,8 @@ ContractUpdateTransactionBody.Builder build() { } if (declineStakingReward != null) { - builder.setDeclineReward(BoolValue.newBuilder().setValue(declineStakingReward).build()); + builder.setDeclineReward( + BoolValue.newBuilder().setValue(declineStakingReward).build()); } if (autoRenewAccountId != null) { @@ -574,7 +565,7 @@ void validateChecksums(Client client) throws BadEntityIdException { } @Override - MethodDescriptor getMethodDescriptor() { + MethodDescriptor getMethodDescriptor() { return SmartContractServiceGrpc.getUpdateContractMethod(); } diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/Crypto.java b/sdk/src/main/java/org/hiero/sdk/Crypto.java similarity index 92% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/Crypto.java rename to sdk/src/main/java/org/hiero/sdk/Crypto.java index c2d2f831b9..6bc6de0861 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/Crypto.java +++ b/sdk/src/main/java/org/hiero/sdk/Crypto.java @@ -1,24 +1,20 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import javax.annotation.Nullable; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; import org.bouncycastle.asn1.sec.SECNamedCurves; import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.asn1.x9.X9IntegerConverter; @@ -32,21 +28,6 @@ import org.bouncycastle.math.ec.ECAlgorithms; import org.bouncycastle.math.ec.ECPoint; -import javax.annotation.Nullable; -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.ShortBufferException; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; -import java.math.BigInteger; -import java.nio.charset.StandardCharsets; -import java.security.AlgorithmParameters; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; - /** * Utility class used internally by the sdk. */ @@ -61,16 +42,15 @@ final class Crypto { static final X9ECParameters ECDSA_SECP256K1_CURVE = SECNamedCurves.getByName("secp256k1"); static final ECDomainParameters ECDSA_SECP256K1_DOMAIN = new ECDomainParameters( - ECDSA_SECP256K1_CURVE.getCurve(), - ECDSA_SECP256K1_CURVE.getG(), - ECDSA_SECP256K1_CURVE.getN(), - ECDSA_SECP256K1_CURVE.getH()); + ECDSA_SECP256K1_CURVE.getCurve(), + ECDSA_SECP256K1_CURVE.getG(), + ECDSA_SECP256K1_CURVE.getN(), + ECDSA_SECP256K1_CURVE.getH()); /** * Constructor. */ - private Crypto() { - } + private Crypto() {} /** * Derive a sha 256 key. @@ -169,8 +149,7 @@ private static Cipher initAesCipher(Cipher aesCipher, KeyParameter cipherKey, by int mode = forDecrypt ? Cipher.DECRYPT_MODE : Cipher.ENCRYPT_MODE; try { - aesCipher.init(mode, new SecretKeySpec(cipherKey.getKey(), 0, 16, "AES"), - new IvParameterSpec(iv)); + aesCipher.init(mode, new SecretKeySpec(cipherKey.getKey(), 0, 16, "AES"), new IvParameterSpec(iv)); } catch (InvalidKeyException e) { throw new Error("platform does not support AES-128 ciphers", e); } catch (InvalidAlgorithmParameterException e) { @@ -328,7 +307,7 @@ static byte[] recoverPublicKeyECDSAFromSignature(int recId, BigInteger r, BigInt private static ECPoint decompressKey(BigInteger xBN, boolean yBit) { var X_9_INTEGER_CONVERTER = new X9IntegerConverter(); byte[] compEnc = X_9_INTEGER_CONVERTER.integerToBytes( - xBN, 1 + X_9_INTEGER_CONVERTER.getByteLength(ECDSA_SECP256K1_DOMAIN.getCurve())); + xBN, 1 + X_9_INTEGER_CONVERTER.getByteLength(ECDSA_SECP256K1_DOMAIN.getCurve())); compEnc[0] = (byte) (yBit ? 0x03 : 0x02); try { return ECDSA_SECP256K1_DOMAIN.getCurve().decodePoint(compEnc); diff --git a/sdk/src/main/java/org/hiero/sdk/CustomFee.java b/sdk/src/main/java/org/hiero/sdk/CustomFee.java new file mode 100644 index 0000000000..68c0fd51e7 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/CustomFee.java @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import com.google.protobuf.InvalidProtocolBufferException; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nullable; + +/** + * Base class for custom fees. + */ +public abstract class CustomFee { + /** + * The account to receive the custom fee + */ + @Nullable + protected AccountId feeCollectorAccountId = null; + + /** + * If true, exempts all the token's fee collection accounts from this fee + */ + protected boolean allCollectorsAreExempt = false; + + /** + * Constructor. + */ + CustomFee() {} + + /** + * Convert the protobuf object to a custom fee object. + * + * @param customFee protobuf response object + * @return the converted custom fee object + */ + static CustomFee fromProtobufInner(org.hiero.sdk.proto.CustomFee customFee) { + switch (customFee.getFeeCase()) { + case FIXED_FEE: + return CustomFixedFee.fromProtobuf(customFee.getFixedFee()); + + case FRACTIONAL_FEE: + return CustomFractionalFee.fromProtobuf(customFee.getFractionalFee()); + + case ROYALTY_FEE: + return CustomRoyaltyFee.fromProtobuf(customFee.getRoyaltyFee()); + + default: + throw new IllegalStateException( + "CustomFee#fromProtobuf: unhandled fee case: " + customFee.getFeeCase()); + } + } + + static CustomFee fromProtobuf(org.hiero.sdk.proto.CustomFee customFee) { + var outFee = fromProtobufInner(customFee); + if (customFee.hasFeeCollectorAccountId()) { + outFee.feeCollectorAccountId = AccountId.fromProtobuf(customFee.getFeeCollectorAccountId()); + } + outFee.allCollectorsAreExempt = customFee.getAllCollectorsAreExempt(); + + return outFee; + } + + /** + * Convert byte array to a custom fee object. + * + * @param bytes the byte array + * @return the converted custom fee object + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + public static CustomFee fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + return fromProtobuf( + org.hiero.sdk.proto.CustomFee.parseFrom(bytes).toBuilder().build()); + } + + /** + * Create a new copy of a custom fee list. + * + * @param customFees existing custom fee list + * @return new custom fee list + */ + public static List deepCloneList(List customFees) { + var returnCustomFees = new ArrayList(customFees.size()); + for (var fee : customFees) { + returnCustomFees.add(fee.deepClone()); + } + return returnCustomFees; + } + + /** + * Extract the fee collector account id. + * + * @return the fee collector account id + */ + @Nullable + public AccountId getFeeCollectorAccountId() { + return feeCollectorAccountId; + } + + /** + * + * @return whether all fee collectors are exempt from fees + */ + public boolean getAllCollectorsAreExempt() { + return allCollectorsAreExempt; + } + + /** + * Create a deep clone. + * + * @return the correct cloned fee type + */ + abstract CustomFee deepClone(); + + /** + * Verify the validity of the client object. + * + * @param client the configured client + * @throws BadEntityIdException if entity ID is formatted poorly + */ + void validateChecksums(Client client) throws BadEntityIdException { + if (feeCollectorAccountId != null) { + feeCollectorAccountId.validateChecksum(client); + } + } + + /** + * Finalize the builder into the protobuf. + * + * @param customFeeBuilder the builder object + * @return the protobuf + */ + protected org.hiero.sdk.proto.CustomFee finishToProtobuf(org.hiero.sdk.proto.CustomFee.Builder customFeeBuilder) { + if (feeCollectorAccountId != null) { + customFeeBuilder.setFeeCollectorAccountId(feeCollectorAccountId.toProtobuf()); + } + customFeeBuilder.setAllCollectorsAreExempt(allCollectorsAreExempt); + return customFeeBuilder.build(); + } + + /** + * Create the protobuf. + * + * @return the protobuf for the custom fee object + */ + abstract org.hiero.sdk.proto.CustomFee toProtobuf(); + + /** + * Create the byte array. + * + * @return the byte array representing the protobuf + */ + public byte[] toBytes() { + return toProtobuf().toByteArray(); + } + + /** + * Serializes the class to ToStringHelper + * + * @return the {@link com.google.common.base.MoreObjects.ToStringHelper} + */ + protected MoreObjects.ToStringHelper toStringHelper() { + return MoreObjects.toStringHelper(this) + .add("feeCollectorAccountId", feeCollectorAccountId) + .add("allCollectorsAreExempt", allCollectorsAreExempt); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/CustomFeeBase.java b/sdk/src/main/java/org/hiero/sdk/CustomFeeBase.java new file mode 100644 index 0000000000..54b5ac627e --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/CustomFeeBase.java @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import java.util.Objects; + +abstract class CustomFeeBase> extends CustomFee { + + /** + * Assign the fee collector account id. + * + * @param feeCollectorAccountId the account id of the fee collector + * @return {@code this} + */ + public F setFeeCollectorAccountId(AccountId feeCollectorAccountId) { + this.feeCollectorAccountId = Objects.requireNonNull(feeCollectorAccountId); + // noinspection unchecked + return (F) this; + } + + /** + * If true, exempts all the token's fee collection accounts from this fee. + * (The token's treasury and the above fee_collector_account_id will always + * be exempt. Please see HIP-573 + * for details.) + * + * @param allCollectorsAreExempt whether all fee collectors are exempt from fees + * @return {@code this} + */ + public F setAllCollectorsAreExempt(boolean allCollectorsAreExempt) { + this.allCollectorsAreExempt = allCollectorsAreExempt; + // noinspection unchecked + return (F) this; + } + + abstract F deepCloneSubclass(); + + /** + * Finishes the deep clone by setting the fields of the {@link CustomFeeBase} class + * + * @param source the source object + * @return the cloned object + */ + protected F finishDeepClone(CustomFeeBase source) { + feeCollectorAccountId = source.feeCollectorAccountId; + allCollectorsAreExempt = source.getAllCollectorsAreExempt(); + + // noinspection unchecked + return (F) this; + } + + @Override + CustomFee deepClone() { + return deepCloneSubclass(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/CustomFixedFee.java b/sdk/src/main/java/org/hiero/sdk/CustomFixedFee.java new file mode 100644 index 0000000000..05bb6fb65e --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/CustomFixedFee.java @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import javax.annotation.Nullable; +import org.hiero.sdk.proto.FixedFee; + +/** + * Custom fixed fee utility class. + * See Hedera Documentation + */ +public class CustomFixedFee extends CustomFeeBase { + private long amount = 0; + /** + * The shard, realm, number of the tokens. + */ + @Nullable + private TokenId denominatingTokenId = null; + + /** + * Constructor. + */ + public CustomFixedFee() {} + + /** + * Create a custom fixed fee from a fixed fee protobuf. + * + * @param fixedFee the fixed fee protobuf + * @return the new custom fixed fee object + */ + static CustomFixedFee fromProtobuf(FixedFee fixedFee) { + var returnFee = new CustomFixedFee().setAmount(fixedFee.getAmount()); + if (fixedFee.hasDenominatingTokenId()) { + returnFee.setDenominatingTokenId(TokenId.fromProtobuf(fixedFee.getDenominatingTokenId())); + } + return returnFee; + } + + @Override + CustomFixedFee deepCloneSubclass() { + return new CustomFixedFee() + .setAmount(amount) + .setDenominatingTokenId(denominatingTokenId) + .finishDeepClone(this); + } + + /** + * Extract the amount. + * + * @return the amount of the fee in tiny bar + */ + public long getAmount() { + return amount; + } + + /** + * Assign the fee amount in tiny bar. + * + * @param amount the amount of the fee in tiny bar + * @return {@code this} + */ + public CustomFixedFee setAmount(long amount) { + this.amount = amount; + return this; + } + + /** + * Extract the fee amount. + * + * @return the fee amount in hbar + */ + public Hbar getHbarAmount() { + return Hbar.fromTinybars(amount); + } + + /** + * Assign the fee amount in hbar. + * + * @param amount the fee amount in hbar + * @return {@code this} + */ + public CustomFixedFee setHbarAmount(Hbar amount) { + denominatingTokenId = null; + this.amount = amount.toTinybars(); + return this; + } + + /** + * Extract the token id. + * + * @return the token id object + */ + @Nullable + public TokenId getDenominatingTokenId() { + return denominatingTokenId; + } + + /** + * Assign the desired token id. + * + * @param tokenId the token id + * @return {@code this} + */ + public CustomFixedFee setDenominatingTokenId(@Nullable TokenId tokenId) { + denominatingTokenId = tokenId; + return this; + } + + /** + * Assign the default token 0.0.0. + * + * @return {@code this} + */ + public CustomFixedFee setDenominatingTokenToSameToken() { + denominatingTokenId = new TokenId(0, 0, 0); + return this; + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + super.validateChecksums(client); + if (denominatingTokenId != null) { + denominatingTokenId.validateChecksum(client); + } + } + + @Override + public String toString() { + return toStringHelper() + .add("amount", getAmount()) + .add("demoninatingTokenId", getDenominatingTokenId()) + .toString(); + } + + /** + * Convert to a protobuf. + * + * @return the protobuf converted object + */ + FixedFee toFixedFeeProtobuf() { + var fixedFeeBuilder = FixedFee.newBuilder().setAmount(getAmount()); + if (getDenominatingTokenId() != null) { + fixedFeeBuilder.setDenominatingTokenId(getDenominatingTokenId().toProtobuf()); + } + return fixedFeeBuilder.build(); + } + + @Override + org.hiero.sdk.proto.CustomFee toProtobuf() { + var customFeeBuilder = org.hiero.sdk.proto.CustomFee.newBuilder().setFixedFee(toFixedFeeProtobuf()); + return finishToProtobuf(customFeeBuilder); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/CustomFractionalFee.java b/sdk/src/main/java/org/hiero/sdk/CustomFractionalFee.java new file mode 100644 index 0000000000..73ec348e3a --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/CustomFractionalFee.java @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import java.util.Objects; +import org.hiero.sdk.proto.Fraction; +import org.hiero.sdk.proto.FractionalFee; + +/** + * Custom fractional fee utility class. + * See Hedera Documentation + */ +public class CustomFractionalFee extends CustomFeeBase { + private long numerator = 0; + private long denominator = 1; + private long min = 0; + private long max = 0; + private FeeAssessmentMethod assessmentMethod = FeeAssessmentMethod.INCLUSIVE; + + /** + * Constructor. + */ + public CustomFractionalFee() {} + + /** + * Create a custom fractional fee from a fee protobuf. + * + * @param fractionalFee the fractional fee protobuf + * @return the new custom fractional fee object + */ + static CustomFractionalFee fromProtobuf(FractionalFee fractionalFee) { + var fraction = fractionalFee.getFractionalAmount(); + return new CustomFractionalFee() + .setNumerator(fraction.getNumerator()) + .setDenominator(fraction.getDenominator()) + .setMin(fractionalFee.getMinimumAmount()) + .setMax(fractionalFee.getMaximumAmount()) + .setAssessmentMethod(FeeAssessmentMethod.valueOf(fractionalFee.getNetOfTransfers())); + } + + @Override + CustomFractionalFee deepCloneSubclass() { + return new CustomFractionalFee() + .setNumerator(numerator) + .setDenominator(denominator) + .setMin(min) + .setMax(max) + .setAssessmentMethod(assessmentMethod) + .finishDeepClone(this); + } + + /** + * Extract the numerator. + * + * @return the numerator + */ + public long getNumerator() { + return numerator; + } + + /** + * Assign the numerator. + * + * @param numerator the numerator + * @return {@code this} + */ + public CustomFractionalFee setNumerator(long numerator) { + this.numerator = numerator; + return this; + } + + /** + * Extract the denominator. + * + * @return the denominator + */ + public long getDenominator() { + return denominator; + } + + /** + * Assign the denominator can not be zero (0). + * + * @param denominator the denominator + * @return {@code this} + */ + public CustomFractionalFee setDenominator(long denominator) { + this.denominator = denominator; + return this; + } + + /** + * Extract the minimum fee amount. + * + * @return the minimum fee amount + */ + public long getMin() { + return min; + } + + /** + * Assign the minimum fee amount. + * + * @param min the fee amount + * @return {@code this} + */ + public CustomFractionalFee setMin(long min) { + this.min = min; + return this; + } + + /** + * Extract the fee amount. + * + * @return the fee amount + */ + public long getMax() { + return max; + } + + /** + * Assign the maximum fee amount. + * + * @param max the fee amount + * @return {@code this} + */ + public CustomFractionalFee setMax(long max) { + this.max = max; + return this; + } + + /** + * Extract the assessment method inclusive / exclusive. + * + * @return the assessment method inclusive / exclusive + */ + public FeeAssessmentMethod getAssessmentMethod() { + return assessmentMethod; + } + + /** + * Assign the assessment method inclusive / exclusive. + *

+ * If the assessment method field is set, the token's custom fee is charged + * to the sending account and the receiving account receives the full token + * transfer amount. If this field is set to false, the receiver pays for + * the token custom fees and gets the remaining token balance. + * INCLUSIVE(false) + * EXCLUSIVE(true) + * See Hedera Documentation + * + * @param assessmentMethod inclusive / exclusive + * @return {@code this} + */ + public CustomFractionalFee setAssessmentMethod(FeeAssessmentMethod assessmentMethod) { + Objects.requireNonNull(assessmentMethod); + this.assessmentMethod = assessmentMethod; + return this; + } + + @Override + public String toString() { + return toStringHelper() + .add("numerator", getNumerator()) + .add("denominator", getDenominator()) + .add("min", getMin()) + .add("max", getMax()) + .add("assessmentMethod", getAssessmentMethod()) + .toString(); + } + + /** + * Convert the fractional fee object to a protobuf. + * + * @return the protobuf object + */ + FractionalFee toFractionalFeeProtobuf() { + return FractionalFee.newBuilder() + .setMinimumAmount(getMin()) + .setMaximumAmount(getMax()) + .setFractionalAmount( + Fraction.newBuilder().setNumerator(getNumerator()).setDenominator(getDenominator())) + .setNetOfTransfers(assessmentMethod.code) + .build(); + } + + @Override + org.hiero.sdk.proto.CustomFee toProtobuf() { + var customFeeBuilder = org.hiero.sdk.proto.CustomFee.newBuilder().setFractionalFee(toFractionalFeeProtobuf()); + return finishToProtobuf(customFeeBuilder); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/CustomRoyaltyFee.java b/sdk/src/main/java/org/hiero/sdk/CustomRoyaltyFee.java new file mode 100644 index 0000000000..98a26e1a50 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/CustomRoyaltyFee.java @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.Fraction; +import org.hiero.sdk.proto.RoyaltyFee; + +/** + * Custom royalty fee utility class. + * See Hedera Documentation + */ +public class CustomRoyaltyFee extends CustomFeeBase { + private long numerator = 0; + private long denominator = 1; + + @Nullable + private CustomFixedFee fallbackFee = null; + + /** + * Constructor. + */ + public CustomRoyaltyFee() {} + + /** + * Create a custom royalty fee from a royalty fee protobuf. + * + * @param royaltyFee the royalty fee protobuf + * @return the new royalty fee object + */ + static CustomRoyaltyFee fromProtobuf(RoyaltyFee royaltyFee) { + var fraction = royaltyFee.getExchangeValueFraction(); + var returnFee = + new CustomRoyaltyFee().setNumerator(fraction.getNumerator()).setDenominator(fraction.getDenominator()); + if (royaltyFee.hasFallbackFee()) { + returnFee.fallbackFee = CustomFixedFee.fromProtobuf(royaltyFee.getFallbackFee()); + } + return returnFee; + } + + @Override + CustomRoyaltyFee deepCloneSubclass() { + var returnFee = new CustomRoyaltyFee(); + returnFee.numerator = numerator; + returnFee.denominator = denominator; + returnFee.fallbackFee = fallbackFee != null ? fallbackFee.deepCloneSubclass() : null; + returnFee.feeCollectorAccountId = feeCollectorAccountId; + returnFee.allCollectorsAreExempt = allCollectorsAreExempt; + return returnFee; + } + + /** + * Extract the numerator. + * + * @return the numerator + */ + public long getNumerator() { + return numerator; + } + + /** + * Assign the numerator. + * + * @param numerator the numerator + * @return {@code this} + */ + public CustomRoyaltyFee setNumerator(long numerator) { + this.numerator = numerator; + return this; + } + + /** + * Extract the denominator. + * + * @return the denominator + */ + public long getDenominator() { + return denominator; + } + + /** + * Assign the denominator can not be zero (0). + * + * @param denominator the denominator + * @return {@code this} + */ + public CustomRoyaltyFee setDenominator(long denominator) { + this.denominator = denominator; + return this; + } + + /** + * The fallback fee is a fixed fee that is charged to the NFT receiver + * when there is no fungible value exchanged with the sender of the NFT. + * + * @param fallbackFee the fallback fee amount + * @return {@code this} + */ + public CustomRoyaltyFee setFallbackFee(CustomFixedFee fallbackFee) { + Objects.requireNonNull(fallbackFee); + this.fallbackFee = fallbackFee.deepCloneSubclass(); + return this; + } + + /** + * Get the fallback fixed fee. + * + * @return the fallback fixed fee + */ + @Nullable + public CustomFixedFee getFallbackFee() { + return fallbackFee != null ? fallbackFee.deepCloneSubclass() : null; + } + + /** + * Convert the royalty fee object to a protobuf. + * + * @return the protobuf object + */ + RoyaltyFee toRoyaltyFeeProtobuf() { + var royaltyFeeBuilder = RoyaltyFee.newBuilder() + .setExchangeValueFraction( + Fraction.newBuilder().setNumerator(numerator).setDenominator(denominator)); + if (fallbackFee != null) { + royaltyFeeBuilder.setFallbackFee(fallbackFee.toFixedFeeProtobuf()); + } + return royaltyFeeBuilder.build(); + } + + @Override + org.hiero.sdk.proto.CustomFee toProtobuf() { + var customFeeBuilder = org.hiero.sdk.proto.CustomFee.newBuilder().setRoyaltyFee(toRoyaltyFeeProtobuf()); + return finishToProtobuf(customFeeBuilder); + } + + @Override + public String toString() { + return toStringHelper() + .add("numerator", numerator) + .add("denominator", denominator) + .add("fallbackFee", fallbackFee) + .toString(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/Delayer.java b/sdk/src/main/java/org/hiero/sdk/Delayer.java new file mode 100644 index 0000000000..722ec569eb --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/Delayer.java @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Utility class used internally by the sdk. + */ +final class Delayer { + private static final Logger logger = LoggerFactory.getLogger(Delayer.class); + + private static final ScheduledExecutorService SCHEDULER = Executors.newSingleThreadScheduledExecutor(r -> { + Thread t = new Thread(r); + t.setDaemon(true); + return t; + }); + + private static final Duration MIN_DELAY = Duration.ofMillis(500); + + /** + * Constructor. + */ + private Delayer() {} + + /** + * Set the delay backoff attempts. + * + * @param attempt the attempts + * @param executor the executor + * @return the updated future + */ + static CompletableFuture delayBackOff(int attempt, Executor executor) { + var interval = MIN_DELAY.multipliedBy(ThreadLocalRandom.current().nextLong(1L << attempt)); + + return delayFor(interval.toMillis(), executor); + } + + /** + * Set the delay backoff milliseconds. + * + * @param milliseconds the milliseconds + * @param executor the executor + * @return the updated future + */ + static CompletableFuture delayFor(long milliseconds, Executor executor) { + logger.trace("waiting for {} seconds before trying again", (double) milliseconds / 1000.0); + + return CompletableFuture.runAsync(() -> {}, delayedExecutor(milliseconds, TimeUnit.MILLISECONDS, executor)); + } + + private static Executor delayedExecutor(long delay, TimeUnit unit, Executor executor) { + return r -> SCHEDULER.schedule(() -> executor.execute(r), delay, unit); + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/DelegateContractId.java b/sdk/src/main/java/org/hiero/sdk/DelegateContractId.java similarity index 78% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/DelegateContractId.java rename to sdk/src/main/java/org/hiero/sdk/DelegateContractId.java index 30cf09a2b8..7817b2d2e0 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/DelegateContractId.java +++ b/sdk/src/main/java/org/hiero/sdk/DelegateContractId.java @@ -1,29 +1,10 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.ContractID; - -import javax.annotation.Nullable; import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.ContractID; /** * The ID for a smart contract instance on Hedera. @@ -104,14 +85,14 @@ public static DelegateContractId fromBytes(byte[] bytes) throws InvalidProtocolB } @Override - com.hedera.hashgraph.sdk.proto.Key toProtobufKey() { - return com.hedera.hashgraph.sdk.proto.Key.newBuilder() - .setDelegatableContractId(toProtobuf()) - .build(); + org.hiero.sdk.proto.Key toProtobufKey() { + return org.hiero.sdk.proto.Key.newBuilder() + .setDelegatableContractId(toProtobuf()) + .build(); } @Override - public boolean equals( Object o) { + public boolean equals(Object o) { if (this == o) { return true; } @@ -125,7 +106,5 @@ public boolean equals( Object o) { } else { return false; } - } } - diff --git a/sdk/src/main/java/org/hiero/sdk/DurationConverter.java b/sdk/src/main/java/org/hiero/sdk/DurationConverter.java new file mode 100644 index 0000000000..6006c25ea9 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/DurationConverter.java @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import java.time.Duration; + +/** + * Utility class used internally by the sdk. + */ +final class DurationConverter { + private DurationConverter() {} + + /** + * Create a duration object from a protobuf. + * + * @param duration the duration protobuf + * @return the duration object + */ + static Duration fromProtobuf(org.hiero.sdk.proto.Duration duration) { + return Duration.ofSeconds(duration.getSeconds()); + } + + /** + * Convert the duration object into a protobuf. + * + * @param duration the duration object + * @return the protobuf + */ + static org.hiero.sdk.proto.Duration toProtobuf(Duration duration) { + return org.hiero.sdk.proto.Duration.newBuilder() + .setSeconds(duration.getSeconds()) + .build(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/Endpoint.java b/sdk/src/main/java/org/hiero/sdk/Endpoint.java new file mode 100644 index 0000000000..fcf5b04dfe --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/Endpoint.java @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.ByteString; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.ServiceEndpoint; + +/** + * Utility class used internally by the sdk. + */ +public class Endpoint implements Cloneable { + + @Nullable + byte[] address = null; + + int port; + + String domainName = ""; + + /** + * Constructor. + */ + public Endpoint() {} + + /** + * Create an endpoint object from a service endpoint protobuf. + * + * @param serviceEndpoint the service endpoint protobuf + * @return the endpoint object + */ + static Endpoint fromProtobuf(ServiceEndpoint serviceEndpoint) { + var port = (int) (serviceEndpoint.getPort() & 0x00000000ffffffffL); + + return new Endpoint() + .setAddress(serviceEndpoint.getIpAddressV4().toByteArray()) + .setPort(port) + .setDomainName(serviceEndpoint.getDomainName()); + } + + /** + * Extract the ipv4 address. + * + * @return the ipv4 address + */ + @Nullable + public byte[] getAddress() { + return address; + } + + /** + * Assign the ipv4 address. + * + * @param address the desired ipv4 address + * @return {@code this} + */ + public Endpoint setAddress(byte[] address) { + this.address = address; + return this; + } + + /** + * Extract the port number. + * + * @return the port number + */ + public int getPort() { + return port; + } + + /** + * Assign the desired port number. + * + * @param port the desired port number + * @return {@code this} + */ + public Endpoint setPort(int port) { + this.port = port; + return this; + } + + /** + * Extract the domain name. + * + * @return the domain name + */ + public String getDomainName() { + return domainName; + } + + /** + * Assign the desired domain name. + * + * @param domainName the desired domain name + * @return {@code this} + */ + public Endpoint setDomainName(String domainName) { + this.domainName = domainName; + return this; + } + + /** + * Create the protobuf. + * + * @return the protobuf representation + */ + ServiceEndpoint toProtobuf() { + var builder = ServiceEndpoint.newBuilder(); + + if (address != null) { + builder.setIpAddressV4(ByteString.copyFrom(address)); + } + + builder.setDomainName(domainName); + + return builder.setPort(port).build(); + } + + @Override + public String toString() { + if (this.domainName != null && !this.domainName.isEmpty()) { + return domainName + ":" + port; + } else { + return ((int) address[0] & 0x000000FF) + "." + ((int) address[1] & 0x000000FF) + "." + + ((int) address[2] & 0x000000FF) + + "." + ((int) address[3] & 0x000000FF) + ":" + + port; + } + } + + @Override + public Endpoint clone() { + try { + Endpoint clone = (Endpoint) super.clone(); + clone.address = address != null ? address.clone() : null; + return clone; + } catch (CloneNotSupportedException e) { + throw new AssertionError(); + } + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/EntityIdHelper.java b/sdk/src/main/java/org/hiero/sdk/EntityIdHelper.java new file mode 100644 index 0000000000..c3d9837f37 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/EntityIdHelper.java @@ -0,0 +1,374 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.net.http.HttpTimeoutException; +import java.nio.ByteBuffer; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.regex.Pattern; +import javax.annotation.Nullable; +import org.bouncycastle.util.encoders.DecoderException; +import org.bouncycastle.util.encoders.Hex; + +/** + * Utility class used internally by the sdk. + */ +public class EntityIdHelper { + /** + * The length of a Solidity address in bytes. + */ + static final int SOLIDITY_ADDRESS_LEN = 20; + + /** + * The length of a hexadecimal-encoded Solidity address, in ASCII characters (bytes). + */ + static final int SOLIDITY_ADDRESS_LEN_HEX = SOLIDITY_ADDRESS_LEN * 2; + + private static final Pattern ENTITY_ID_REGEX = + Pattern.compile("(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-([a-z]{5}))?$"); + + static final Duration MIRROR_NODE_CONNECTION_TIMEOUT = Duration.ofSeconds(30); + + /** + * Constructor. + */ + private EntityIdHelper() {} + + /** + * Generate an R object from a string. + * + * @param idString the id string + * @param constructObjectWithIdNums the R object generator + * @param + * @return the R type object + */ + static R fromString(String idString, WithIdNums constructObjectWithIdNums) { + var match = ENTITY_ID_REGEX.matcher(idString); + if (!match.find()) { + throw new IllegalArgumentException( + "Invalid ID \"" + idString + "\": format should look like 0.0.123 or 0.0.123-vfmkw"); + } + return constructObjectWithIdNums.apply( + Long.parseLong(match.group(1)), + Long.parseLong(match.group(2)), + Long.parseLong(match.group(3)), + match.group(4)); + } + + /** + * Generate an R object from a solidity address. + * + * @param address the string representation + * @param withAddress the R object generator + * @param + * @return the R type object + */ + static R fromSolidityAddress(String address, WithIdNums withAddress) { + return fromSolidityAddress(decodeSolidityAddress(address), withAddress); + } + + private static R fromSolidityAddress(byte[] address, WithIdNums withAddress) { + if (address.length != SOLIDITY_ADDRESS_LEN) { + throw new IllegalArgumentException("Solidity addresses must be 20 bytes or 40 hex chars"); + } + + var buf = ByteBuffer.wrap(address); + return withAddress.apply(buf.getInt(), buf.getLong(), buf.getLong(), null); + } + + /** + * Decode the solidity address from a string. + * + * @param address the string representation + * @return the decoded address + */ + static byte[] decodeSolidityAddress(String address) { + address = address.startsWith("0x") ? address.substring(2) : address; + + if (address.length() != SOLIDITY_ADDRESS_LEN_HEX) { + throw new IllegalArgumentException("Solidity addresses must be 20 bytes or 40 hex chars"); + } + + try { + return Hex.decode(address); + } catch (DecoderException e) { + throw new IllegalArgumentException("failed to decode Solidity address as hex", e); + } + } + + /** + * Generate a solidity address. + * + * @param shard the shard part + * @param realm the realm part + * @param num the num part + * @return the solidity address + */ + static String toSolidityAddress(long shard, long realm, long num) { + if (Long.highestOneBit(shard) > 32) { + throw new IllegalStateException("shard out of 32-bit range " + shard); + } + + return Hex.toHexString(ByteBuffer.allocate(20) + .putInt((int) shard) + .putLong(realm) + .putLong(num) + .array()); + } + + /** + * Generate a checksum. + * + * @param ledgerId the ledger id + * @param addr the address + * @return the checksum + */ + static String checksum(LedgerId ledgerId, String addr) { + StringBuilder answer = new StringBuilder(); + List d = + new ArrayList<>(); // Digits with 10 for ".", so if addr == "0.0.123" then d == [0, 10, 0, 10, 1, 2, 3] + long s0 = 0; // Sum of even positions (mod 11) + long s1 = 0; // Sum of odd positions (mod 11) + long s = 0; // Weighted sum of all positions (mod p3) + long sh = 0; // Hash of the ledger ID + @SuppressWarnings("UnusedVariable") + long c = 0; // The checksum, as a single number + long p3 = 26 * 26 * 26; // 3 digits in base 26 + long p5 = 26 * 26 * 26 * 26 * 26; // 5 digits in base 26 + long asciiA = Character.codePointAt("a", 0); // 97 + long m = 1_000_003; // min prime greater than a million. Used for the final permutation. + long w = 31; // Sum s of digit values weights them by powers of w. Should be coprime to p5. + + List h = new ArrayList<>(ledgerId.toBytes().length + 6); + for (byte b : ledgerId.toBytes()) { + h.add(b); + } + for (int i = 0; i < 6; i++) { + h.add((byte) 0); + } + for (var i = 0; i < addr.length(); i++) { + d.add(addr.charAt(i) == '.' ? 10 : Integer.parseInt(String.valueOf(addr.charAt(i)), 10)); + } + for (var i = 0; i < d.size(); i++) { + s = (w * s + d.get(i)) % p3; + if (i % 2 == 0) { + s0 = (s0 + d.get(i)) % 11; + } else { + s1 = (s1 + d.get(i)) % 11; + } + } + for (byte b : h) { + // byte is signed in java, have to fake it to make bytes act like they're unsigned + sh = (w * sh + (b < 0 ? 256 + b : b)) % p5; + } + c = ((((addr.length() % 5) * 11 + s0) * 11 + s1) * p3 + s + sh) % p5; + c = (c * m) % p5; + + for (var i = 0; i < 5; i++) { + answer.append((char) (asciiA + (c % 26))); + c /= 26; + } + + return answer.reverse().toString(); + } + + /** + * Validate the configured client. + * + * @param shard the shard part + * @param realm the realm part + * @param num the num part + * @param client the configured client + * @param checksum the checksum + * @throws BadEntityIdException + */ + static void validate(long shard, long realm, long num, Client client, @Nullable String checksum) + throws BadEntityIdException { + if (client.getNetworkName() == null) { + throw new IllegalStateException( + "Can't validate checksum without knowing which network the ID is for. Ensure client's network name is set."); + } + if (checksum != null) { + String expectedChecksum = + EntityIdHelper.checksum(client.getLedgerId(), EntityIdHelper.toString(shard, realm, num)); + if (!checksum.equals(expectedChecksum)) { + throw new BadEntityIdException(shard, realm, num, checksum, expectedChecksum); + } + } + } + + /** + * Generate a string representation. + * + * @param shard the shard part + * @param realm the realm part + * @param num the num part + * @return the string representation + */ + static String toString(long shard, long realm, long num) { + return "" + shard + "." + realm + "." + num; + } + + /** + * Generate a string representation with a checksum. + * + * @param shard the shard part + * @param realm the realm part + * @param num the num part + * @param client the configured client + * @param checksum the checksum + * @return the string representation with checksum + */ + static String toStringWithChecksum(long shard, long realm, long num, Client client, @Nullable String checksum) { + if (client.getLedgerId() != null) { + return "" + shard + "." + realm + "." + num + "-" + + checksum(client.getLedgerId(), EntityIdHelper.toString(shard, realm, num)); + } else { + throw new IllegalStateException( + "Can't derive checksum for ID without knowing which network the ID is for. Ensure client's ledgerId is set."); + } + } + + /** + * Takes an address as `byte[]` and returns whether this is a long-zero address + * @param address + * @return + */ + static boolean isLongZeroAddress(byte[] address) { + for (int i = 0; i < 12; i++) { + if (address[i] != 0) { + return false; + } + } + return true; + } + + /** + * Get AccountId num from mirror node using evm address. + * + *

Note: This method requires API level 33 or higher. It will not work on devices running API versions below 33 + * because it uses features introduced in API level 33 (Android 13).

* + * + * @param client + * @param evmAddress + */ + static CompletableFuture getAccountNumFromMirrorNodeAsync(Client client, String evmAddress) { + String apiEndpoint = "/accounts/" + evmAddress; + return performQueryToMirrorNodeAsync(client, apiEndpoint, null, false) + .thenApply(response -> parseNumFromMirrorNodeResponse(response, "account")); + } + + /** + * Get EvmAddress from mirror node using account num. + * + *

Note: This method requires API level 33 or higher. It will not work on devices running API versions below 33 + * because it uses features introduced in API level 33 (Android 13).

* + * + * @param client + * @param num + */ + public static CompletableFuture getEvmAddressFromMirrorNodeAsync(Client client, long num) { + String apiEndpoint = "/accounts/" + num; + return performQueryToMirrorNodeAsync(client, apiEndpoint, null, false) + .thenApply(response -> EvmAddress.fromString(parseStringMirrorNodeResponse(response, "evm_address"))); + } + + /** + * Get ContractId num from mirror node using evm address. + * + *

Note: This method requires API level 33 or higher. It will not work on devices running API versions below 33 + * because it uses features introduced in API level 33 (Android 13).

* + * + * @param client + * @param evmAddress + */ + public static CompletableFuture getContractNumFromMirrorNodeAsync(Client client, String evmAddress) { + String apiEndpoint = "/contracts/" + evmAddress; + + CompletableFuture responseFuture = performQueryToMirrorNodeAsync(client, apiEndpoint, null, false); + + return responseFuture.thenApply(response -> parseNumFromMirrorNodeResponse(response, "contract_id")); + } + + public static CompletableFuture getContractAddressFromMirrorNodeAsync(Client client, String id) { + String apiEndpoint = "/contracts/" + id; + CompletableFuture responseFuture = performQueryToMirrorNodeAsync(client, apiEndpoint, null, false); + + return responseFuture.thenApply(response -> parseStringMirrorNodeResponse(response, "evm_address")); + } + + static CompletableFuture performQueryToMirrorNodeAsync( + Client client, String apiEndpoint, String jsonBody, boolean isContractCall) { + Optional mirrorUrl = client.getMirrorNetwork().stream() + .map(url -> url.substring(0, url.indexOf(":"))) + .findFirst(); + + if (mirrorUrl.isEmpty()) { + return CompletableFuture.failedFuture(new IllegalArgumentException("Mirror URL not found")); + } + + String apiUrl = "https://" + mirrorUrl.get() + "/api/v1" + apiEndpoint; + + if (client.getLedgerId() == null) { + if (isContractCall) { + apiUrl = "http://" + mirrorUrl.get() + ":8545/api/v1" + apiEndpoint; + } else { + apiUrl = "http://" + mirrorUrl.get() + ":5551/api/v1" + apiEndpoint; + } + } + + HttpClient httpClient = HttpClient.newHttpClient(); + var httpBuilder = + HttpRequest.newBuilder().timeout(MIRROR_NODE_CONNECTION_TIMEOUT).uri(URI.create(apiUrl)); + + if (jsonBody != null) { + httpBuilder.header("Content-Type", "application/json").POST(HttpRequest.BodyPublishers.ofString(jsonBody)); + } + var httpRequest = httpBuilder.build(); + + return httpClient + .sendAsync(httpRequest, HttpResponse.BodyHandlers.ofString()) + .handle((response, ex) -> { + if (ex != null) { + if (ex instanceof HttpTimeoutException) { + throw new CompletionException(new RuntimeException("Request to Mirror Node timed out", ex)); + } else { + throw new CompletionException( + new RuntimeException("Failed to send request to Mirror Node", ex)); + } + } + + int statusCode = response.statusCode(); + if (statusCode != 200) { + throw new CompletionException( + new RuntimeException("Received non-200 response from Mirror Node: " + response.body())); + } + return response.body(); + }); + } + + private static String parseStringMirrorNodeResponse(String responseBody, String memberName) { + JsonObject jsonObject = JsonParser.parseString(responseBody).getAsJsonObject(); + String evmAddress = jsonObject.get(memberName).getAsString(); + return evmAddress.substring(evmAddress.lastIndexOf(".") + 1); + } + + private static long parseNumFromMirrorNodeResponse(String responseBody, String memberName) { + return Long.parseLong(parseStringMirrorNodeResponse(responseBody, memberName)); + } + + @FunctionalInterface + interface WithIdNums { + R apply(long shard, long realm, long num, @Nullable String checksum); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/EthereumFlow.java b/sdk/src/main/java/org/hiero/sdk/EthereumFlow.java new file mode 100644 index 0000000000..304f470774 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/EthereumFlow.java @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import java.time.Duration; +import java.util.Arrays; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeoutException; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import javax.annotation.Nullable; + +/** + * Execute an Ethereum transaction on Hedera + */ +public class EthereumFlow { + /** + * 5KiB in Bytes + * Indicates when we should splice out the call data from an ethereum transaction data + */ + static int MAX_ETHEREUM_DATA_SIZE = 5120; + + @Nullable + private EthereumTransactionData ethereumData; + + @Nullable + private FileId callDataFileId; + + @Nullable + private Hbar maxGasAllowance; + + /** + * Constructor + */ + public EthereumFlow() {} + + private static FileId createFile(byte[] callData, Client client, Duration timeoutPerTransaction) + throws PrecheckStatusException, TimeoutException { + try { + var transaction = new FileCreateTransaction() + .setContents(Arrays.copyOfRange( + callData, 0, Math.min(FileAppendTransaction.DEFAULT_CHUNK_SIZE, callData.length))) + .execute(client, timeoutPerTransaction); + var fileId = transaction.getReceipt(client, timeoutPerTransaction).fileId; + + if (callData.length > FileAppendTransaction.DEFAULT_CHUNK_SIZE) { + new FileAppendTransaction() + .setFileId(fileId) + .setContents( + Arrays.copyOfRange(callData, FileAppendTransaction.DEFAULT_CHUNK_SIZE, callData.length)) + .execute(client, timeoutPerTransaction); + } + return fileId; + } catch (ReceiptStatusException e) { + throw new RuntimeException(e); + } + } + + private static CompletableFuture createFileAsync( + byte[] callData, Client client, Duration timeoutPerTransaction) { + return new FileCreateTransaction() + .setContents(Arrays.copyOfRange( + callData, 0, Math.min(FileAppendTransaction.DEFAULT_CHUNK_SIZE, callData.length))) + .executeAsync(client, timeoutPerTransaction) + .thenCompose((response) -> response.getReceiptAsync(client, timeoutPerTransaction)) + .thenCompose((receipt) -> { + if (callData.length > FileAppendTransaction.DEFAULT_CHUNK_SIZE) { + return new FileAppendTransaction() + .setFileId(receipt.fileId) + .setContents(Arrays.copyOfRange( + callData, FileAppendTransaction.DEFAULT_CHUNK_SIZE, callData.length)) + .executeAsync(client, timeoutPerTransaction) + .thenApply((r) -> receipt.fileId); + } else { + return CompletableFuture.completedFuture(receipt.fileId); + } + }); + } + + /** + * Gets the data of the Ethereum transaction + * + * @return the data of the Ethereum transaction + */ + @Nullable + public EthereumTransactionData getEthereumData() { + return ethereumData; + } + + /** + * Sets the raw Ethereum transaction (RLP encoded type 0, 1, and 2). Complete + * unless the callDataFileId is set. + * + * @param ethereumData raw ethereum transaction bytes + * @return {@code this} + */ + public EthereumFlow setEthereumData(byte[] ethereumData) { + this.ethereumData = EthereumTransactionData.fromBytes(ethereumData); + return this; + } + + /** + * Gets the maximum amount that the payer of the hedera transaction + * is willing to pay to complete the transaction. + * + * @return the max gas allowance + */ + @Nullable + public Hbar getMaxGasAllowance() { + return maxGasAllowance; + } + + /** + * Sets the maximum amount that the payer of the hedera transaction + * is willing to pay to complete the transaction. + *
+ * Ordinarily the account with the ECDSA alias corresponding to the public + * key that is extracted from the ethereum_data signature is responsible for + * fees that result from the execution of the transaction. If that amount of + * authorized fees is not sufficient then the payer of the transaction can be + * charged, up to but not exceeding this amount. If the ethereum_data + * transaction authorized an amount that was insufficient then the payer will + * only be charged the amount needed to make up the difference. If the gas + * price in the transaction was set to zero then the payer will be assessed + * the entire fee. + * + * @param maxGasAllowance the maximum gas allowance + * @return {@code this} + */ + public EthereumFlow setMaxGasAllowance(Hbar maxGasAllowance) { + this.maxGasAllowance = maxGasAllowance; + return this; + } + + /** + * Execute the transactions in the flow with the passed in client. + * + * @param client the client with the transaction to execute + * @return the response + * @throws PrecheckStatusException when the precheck fails + * @throws TimeoutException when the transaction times out + */ + public TransactionResponse execute(Client client) throws PrecheckStatusException, TimeoutException { + return execute(client, client.getRequestTimeout()); + } + + /** + * Execute the transactions in the flow with the passed in client. + * + * @param client the client with the transaction to execute + * @param timeoutPerTransaction The timeout after which each transaction's execution attempt will be cancelled. + * @return the response + * @throws PrecheckStatusException when the precheck fails + * @throws TimeoutException when the transaction times out + */ + public TransactionResponse execute(Client client, Duration timeoutPerTransaction) + throws PrecheckStatusException, TimeoutException { + if (ethereumData == null) { + throw new IllegalStateException("Cannot execute a ethereum flow when ethereum data was not provided"); + } + + var ethereumTransaction = new EthereumTransaction(); + var ethereumDataBytes = ethereumData.toBytes(); + + if (maxGasAllowance != null) { + ethereumTransaction.setMaxGasAllowanceHbar(maxGasAllowance); + } + + if (ethereumDataBytes.length <= MAX_ETHEREUM_DATA_SIZE) { + ethereumTransaction.setEthereumData(ethereumDataBytes); + } else { + var callDataFileId = createFile(ethereumData.callData, client, timeoutPerTransaction); + ethereumData.callData = new byte[] {}; + ethereumTransaction.setEthereumData(ethereumData.toBytes()).setCallDataFileId(callDataFileId); + } + + return ethereumTransaction.execute(client, timeoutPerTransaction); + } + + /** + * Execute the transactions in the flow with the passed in client asynchronously. + * + *

Note: This method requires API level 33 or higher. It will not work on devices running API versions below 31 + * because it uses features introduced in API level 31 (Android 12).

* + * + * @param client the client with the transaction to execute + * @return the response + */ + public CompletableFuture executeAsync(Client client) { + return executeAsync(client, client.getRequestTimeout()); + } + + /** + * Execute the transactions in the flow with the passed in client asynchronously. + * + *

Note: This method requires API level 33 or higher. It will not work on devices running API versions below 31 + * because it uses features introduced in API level 31 (Android 12).

* + * + * @param client the client with the transaction to execute + * @param timeoutPerTransaction The timeout after which each transaction's execution attempt will be cancelled. + * @return the response + */ + public CompletableFuture executeAsync(Client client, Duration timeoutPerTransaction) { + if (ethereumData == null) { + return CompletableFuture.failedFuture( + new IllegalStateException("Cannot execute a ethereum flow when ethereum data was not provided")); + } + + var ethereumTransaction = new EthereumTransaction(); + var ethereumDataBytes = ethereumData.toBytes(); + + if (maxGasAllowance != null) { + ethereumTransaction.setMaxGasAllowanceHbar(maxGasAllowance); + } + + if (ethereumDataBytes.length <= MAX_ETHEREUM_DATA_SIZE) { + return ethereumTransaction.setEthereumData(ethereumDataBytes).executeAsync(client); + } else { + return createFileAsync(ethereumData.callData, client, timeoutPerTransaction) + .thenCompose((callDataFileId) -> { + ethereumData.callData = new byte[] {}; + return ethereumTransaction + .setEthereumData(ethereumData.toBytes()) + .setCallDataFileId(callDataFileId) + .executeAsync(client, timeoutPerTransaction); + }); + } + } + + /** + * Execute the transactions in the flow with the passed in client asynchronously. + * + * @param client the client with the transaction to execute + * @param callback a BiConsumer which handles the result or error. + */ + public void executeAsync(Client client, BiConsumer callback) { + ConsumerHelper.biConsumer(executeAsync(client), callback); + } + + /** + * Execute the transactions in the flow with the passed in client asynchronously. + * + * @param client the client with the transaction to execute + * @param timeoutPerTransaction The timeout after which each transaction's execution attempt will be cancelled. + * @param callback a BiConsumer which handles the result or error. + */ + public void executeAsync( + Client client, Duration timeoutPerTransaction, BiConsumer callback) { + ConsumerHelper.biConsumer(executeAsync(client, timeoutPerTransaction), callback); + } + + /** + * Execute the transactions in the flow with the passed in client asynchronously. + * + * @param client the client with the transaction to execute + * @param onSuccess a Consumer which consumes the result on success. + * @param onFailure a Consumer which consumes the error on failure. + */ + public void executeAsync(Client client, Consumer onSuccess, Consumer onFailure) { + ConsumerHelper.twoConsumers(executeAsync(client), onSuccess, onFailure); + } + + /** + * Execute the transactions in the flow with the passed in client asynchronously. + * + * @param client the client with the transaction to execute + * @param timeoutPerTransaction The timeout after which each transaction's execution attempt will be cancelled. + * @param onSuccess a Consumer which consumes the result on success. + * @param onFailure a Consumer which consumes the error on failure. + */ + public void executeAsync( + Client client, + Duration timeoutPerTransaction, + Consumer onSuccess, + Consumer onFailure) { + ConsumerHelper.twoConsumers(executeAsync(client, timeoutPerTransaction), onSuccess, onFailure); + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/EthereumTransaction.java b/sdk/src/main/java/org/hiero/sdk/EthereumTransaction.java similarity index 77% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/EthereumTransaction.java rename to sdk/src/main/java/org/hiero/sdk/EthereumTransaction.java index 37e2f08e75..8196fdda34 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/EthereumTransaction.java +++ b/sdk/src/main/java/org/hiero/sdk/EthereumTransaction.java @@ -1,38 +1,18 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.EthereumTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.SmartContractServiceGrpc; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; import java.util.Arrays; -import java.util.Collections; import java.util.LinkedHashMap; import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.EthereumTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.SmartContractServiceGrpc; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; /** * Execute an Ethereum transaction on Hedera @@ -45,15 +25,15 @@ public class EthereumTransaction extends Transaction { /** * Constructor */ - public EthereumTransaction() { - } + public EthereumTransaction() {} - EthereumTransaction(LinkedHashMap> txs) throws InvalidProtocolBufferException { + EthereumTransaction(LinkedHashMap> txs) + throws InvalidProtocolBufferException { super(txs); initFromTransactionBody(); } - EthereumTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { + EthereumTransaction(org.hiero.sdk.proto.TransactionBody txBody) { super(txBody); initFromTransactionBody(); } @@ -74,7 +54,6 @@ public byte[] getEthereumData() { * @param ethereumData raw ethereum transaction bytes * @return {@code this} */ - public EthereumTransaction setEthereumData(byte[] ethereumData) { Objects.requireNonNull(ethereumData); requireNotFrozen(); @@ -103,7 +82,6 @@ public FileId getCallDataFileId() { * @param fileId File ID of an HFS file containing the callData * @return {@code this} */ - public EthereumTransaction setCallDataFileId(FileId fileId) { Objects.requireNonNull(fileId); requireNotFrozen(); @@ -157,8 +135,8 @@ private void initFromTransactionBody() { private EthereumTransactionBody.Builder build() { var builder = EthereumTransactionBody.newBuilder() - .setEthereumData(ByteString.copyFrom(ethereumData)) - .setMaxGasAllowance(maxGasAllowanceHbar.toTinybars()); + .setEthereumData(ByteString.copyFrom(ethereumData)) + .setMaxGasAllowance(maxGasAllowanceHbar.toTinybars()); if (callDataFileId != null) { builder.setCallData(callDataFileId.toProtobuf()); } @@ -166,7 +144,7 @@ private EthereumTransactionBody.Builder build() { } @Override - MethodDescriptor getMethodDescriptor() { + MethodDescriptor getMethodDescriptor() { return SmartContractServiceGrpc.getCallEthereumMethod(); } diff --git a/sdk/src/main/java/org/hiero/sdk/EthereumTransactionData.java b/sdk/src/main/java/org/hiero/sdk/EthereumTransactionData.java new file mode 100644 index 0000000000..4059766e11 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/EthereumTransactionData.java @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.esaulpaugh.headlong.rlp.RLPDecoder; + +/** + * This class represents the data of an Ethereum transaction. + *

+ * It may be of subclass {@link EthereumTransactionDataLegacy} or of subclass {@link EthereumTransactionDataEip1559} + */ +public abstract class EthereumTransactionData { + /** + * The raw call data. + */ + public byte[] callData; + + EthereumTransactionData(byte[] callData) { + this.callData = callData; + } + + static EthereumTransactionData fromBytes(byte[] bytes) { + var decoder = RLPDecoder.RLP_STRICT.sequenceIterator(bytes); + var rlpItem = decoder.next(); + if (rlpItem.isList()) { + return EthereumTransactionDataLegacy.fromBytes(bytes); + } else { + return EthereumTransactionDataEip1559.fromBytes(bytes); + } + } + + /** + * Serialize the ethereum transaction data into bytes using RLP + * + * @return the serialized transaction as a byte array + */ + public abstract byte[] toBytes(); + + /** + * Serialize the ethereum transaction data into a string + * + * @return the serialized transaction as a string + */ + public abstract String toString(); +} diff --git a/sdk/src/main/java/org/hiero/sdk/EthereumTransactionDataEip1559.java b/sdk/src/main/java/org/hiero/sdk/EthereumTransactionDataEip1559.java new file mode 100644 index 0000000000..5d26c33b1c --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/EthereumTransactionDataEip1559.java @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.esaulpaugh.headlong.rlp.RLPDecoder; +import com.esaulpaugh.headlong.rlp.RLPEncoder; +import com.esaulpaugh.headlong.rlp.RLPItem; +import com.esaulpaugh.headlong.util.Integers; +import com.google.common.base.MoreObjects; +import java.util.ArrayList; +import java.util.List; +import org.bouncycastle.util.encoders.Hex; + +/** + * The ethereum transaction data, in the format defined in EIP-1559 + */ +public class EthereumTransactionDataEip1559 extends EthereumTransactionData { + + /** + * ID of the chain + */ + public byte[] chainId; + + /** + * Transaction's nonce + */ + public byte[] nonce; + + /** + * An 'optional' additional fee in Ethereum that is paid directly to miners in order to incentivize them to include + * your transaction in a block. Not used in Hedera + */ + public byte[] maxPriorityGas; + + /** + * The maximum amount, in tinybars, that the payer of the hedera transaction is willing to pay to complete the + * transaction + */ + public byte[] maxGas; + + /** + * The amount of gas available for the transaction + */ + public byte[] gasLimit; + + /** + * The receiver of the transaction + */ + public byte[] to; + + /** + * The transaction value + */ + public byte[] value; + + /** + * specifies an array of addresses and storage keys that the transaction plans to access + */ + public byte[] accessList; + + /** + * recovery parameter used to ease the signature verification + */ + public byte[] recoveryId; + + /** + * The R value of the signature + */ + public byte[] r; + + /** + * The S value of the signature + */ + public byte[] s; + + EthereumTransactionDataEip1559( + byte[] chainId, + byte[] nonce, + byte[] maxPriorityGas, + byte[] maxGas, + byte[] gasLimit, + byte[] to, + byte[] value, + byte[] callData, + byte[] accessList, + byte[] recoveryId, + byte[] r, + byte[] s) { + super(callData); + + this.chainId = chainId; + this.nonce = nonce; + this.maxPriorityGas = maxPriorityGas; + this.maxGas = maxGas; + this.gasLimit = gasLimit; + this.to = to; + this.value = value; + this.accessList = accessList; + this.recoveryId = recoveryId; + this.r = r; + this.s = s; + } + + /** + * Convert a byte array to an ethereum transaction data. + * + * @param bytes the byte array + * @return the ethereum transaction data + */ + public static EthereumTransactionDataEip1559 fromBytes(byte[] bytes) { + var decoder = RLPDecoder.RLP_STRICT.sequenceIterator(bytes); + var rlpItem = decoder.next(); + + // typed transaction? + byte typeByte = rlpItem.asByte(); + if (typeByte != 2) { + throw new IllegalArgumentException("rlp type byte " + typeByte + "is not supported"); + } + rlpItem = decoder.next(); + if (!rlpItem.isList()) { + throw new IllegalArgumentException("expected RLP element list"); + } + List rlpList = rlpItem.asRLPList().elements(); + if (rlpList.size() != 12) { + throw new IllegalArgumentException("expected 12 RLP encoded elements, found " + rlpList.size()); + } + + return new EthereumTransactionDataEip1559( + rlpList.get(0).data(), + rlpList.get(1).data(), + rlpList.get(2).data(), + rlpList.get(3).data(), + rlpList.get(4).data(), + rlpList.get(5).data(), + rlpList.get(6).data(), + rlpList.get(7).data(), + rlpList.get(8).data(), + rlpList.get(9).data(), + rlpList.get(10).data(), + rlpList.get(11).data()); + } + + public byte[] toBytes() { + return RLPEncoder.sequence( + Integers.toBytes(0x02), + List.of( + chainId, + nonce, + maxPriorityGas, + maxGas, + gasLimit, + to, + value, + callData, + new ArrayList(), + recoveryId, + r, + s)); + } + + public String toString() { + return MoreObjects.toStringHelper(this) + .add("chainId", Hex.toHexString(chainId)) + .add("nonce", Hex.toHexString(nonce)) + .add("maxPriorityGas", Hex.toHexString(maxPriorityGas)) + .add("maxGas", Hex.toHexString(maxGas)) + .add("gasLimit", Hex.toHexString(gasLimit)) + .add("to", Hex.toHexString(to)) + .add("value", Hex.toHexString(value)) + .add("accessList", Hex.toHexString(accessList)) + .add("recoveryId", Hex.toHexString(recoveryId)) + .add("r", Hex.toHexString(r)) + .add("s", Hex.toHexString(s)) + .toString(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/EthereumTransactionDataLegacy.java b/sdk/src/main/java/org/hiero/sdk/EthereumTransactionDataLegacy.java new file mode 100644 index 0000000000..183e0e7e30 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/EthereumTransactionDataLegacy.java @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.esaulpaugh.headlong.rlp.RLPDecoder; +import com.esaulpaugh.headlong.rlp.RLPEncoder; +import com.esaulpaugh.headlong.rlp.RLPItem; +import com.google.common.base.MoreObjects; +import java.math.BigInteger; +import java.util.List; +import org.bouncycastle.util.encoders.Hex; + +/** + * The ethereum transaction data, in the legacy format + */ +public class EthereumTransactionDataLegacy extends EthereumTransactionData { + + /** + * ID of the chain + */ + public byte[] chainId = new byte[] {}; + + /** + * Transaction's nonce + */ + public byte[] nonce; + + /** + * The price for 1 gas + */ + public byte[] gasPrice; + + /** + * The amount of gas available for the transaction + */ + public byte[] gasLimit; + + /** + * The receiver of the transaction + */ + public byte[] to; + + /** + * The transaction value + */ + public byte[] value; + + /** + * The V value of the signature + */ + public byte[] v; + + /** + * recovery parameter used to ease the signature verification + */ + public int recoveryId; + + /** + * The R value of the signature + */ + public byte[] r; + + /** + * The S value of the signature + */ + public byte[] s; + + EthereumTransactionDataLegacy( + byte[] nonce, + byte[] gasPrice, + byte[] gasLimit, + byte[] to, + byte[] value, + byte[] callData, + byte[] v, + byte[] r, + byte[] s) { + super(callData); + + this.nonce = nonce; + this.gasPrice = gasPrice; + this.gasLimit = gasLimit; + this.to = to; + this.value = value; + this.v = v; + this.r = r; + this.s = s; + + var vBI = new BigInteger(1, this.v); + this.recoveryId = vBI.testBit(0) ? 0 : 1; + + if (vBI.compareTo(BigInteger.valueOf(34)) > 0) { + this.chainId = vBI.subtract(BigInteger.valueOf(35)).shiftRight(1).toByteArray(); + } + } + + /** + * Convert a byte array to an ethereum transaction data. + * + * @param bytes the byte array + * @return the ethereum transaction data + */ + public static EthereumTransactionDataLegacy fromBytes(byte[] bytes) { + var decoder = RLPDecoder.RLP_STRICT.sequenceIterator(bytes); + var rlpItem = decoder.next(); + + List rlpList = rlpItem.asRLPList().elements(); + if (rlpList.size() != 9) { + throw new IllegalArgumentException("expected 9 RLP encoded elements, found " + rlpList.size()); + } + + return new EthereumTransactionDataLegacy( + rlpList.get(0).data(), + rlpList.get(1).asBytes(), + rlpList.get(2).data(), + rlpList.get(3).data(), + rlpList.get(4).data(), + rlpList.get(5).data(), + rlpList.get(6).asBytes(), + rlpList.get(7).data(), + rlpList.get(8).data()); + } + + public byte[] toBytes() { + return RLPEncoder.list(nonce, gasPrice, gasLimit, to, value, callData, v, r, s); + } + + public String toString() { + return MoreObjects.toStringHelper(this) + .add("chainId", Hex.toHexString(this.chainId)) + .add("nonce", Hex.toHexString(this.nonce)) + .add("gasPrice", Hex.toHexString(this.gasPrice)) + .add("gasLimit", Hex.toHexString(this.gasLimit)) + .add("to", Hex.toHexString(this.to)) + .add("value", Hex.toHexString(this.value)) + .add("recoveryId", this.recoveryId) + .add("v", Hex.toHexString(this.v)) + .add("r", Hex.toHexString(this.r)) + .add("s", Hex.toHexString(this.s)) + .toString(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/EvmAddress.java b/sdk/src/main/java/org/hiero/sdk/EvmAddress.java new file mode 100644 index 0000000000..90ead27816 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/EvmAddress.java @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.ByteString; +import java.util.Arrays; +import javax.annotation.Nullable; +import org.bouncycastle.util.encoders.Hex; + +/** + * The ID for a cryptocurrency account on Hedera. + */ +public final class EvmAddress extends Key { + private final byte[] bytes; + + /** + * Constructor + * + * @param bytes the byte array representation of the address + */ + public EvmAddress(byte[] bytes) { + this.bytes = Arrays.copyOf(bytes, bytes.length); + } + + /** + * Convert a string to an ethereum address. + * + * @param text the string + * @return the ethereum address + */ + public static EvmAddress fromString(String text) { + String address = text.startsWith("0x") ? text.substring(2) : text; + if (address.length() == 40) { + return new EvmAddress(Hex.decode(address)); + } + return null; + } + + @Nullable + static EvmAddress fromAliasBytes(ByteString aliasBytes) { + if (!aliasBytes.isEmpty() && aliasBytes.size() == 20) { + return new EvmAddress(aliasBytes.toByteArray()); + } + return null; + } + + /** + * Convert a byte array to an ethereum address. + * + * @param bytes the byte array + * @return the ethereum address + */ + public static EvmAddress fromBytes(byte[] bytes) { + return new EvmAddress(bytes); + } + + @Override + org.hiero.sdk.proto.Key toProtobufKey() { + throw new UnsupportedOperationException("toProtobufKey() not implemented for EvmAddress"); + } + + public byte[] toBytes() { + return Arrays.copyOf(bytes, bytes.length); + } + + @Override + public String toString() { + return Hex.toHexString(bytes); + } + + @Override + public int hashCode() { + return Arrays.hashCode(bytes); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof EvmAddress)) { + return false; + } + + EvmAddress other = (EvmAddress) o; + return Arrays.equals(bytes, other.bytes); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/ExchangeRate.java b/sdk/src/main/java/org/hiero/sdk/ExchangeRate.java new file mode 100644 index 0000000000..e5ba9f7075 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/ExchangeRate.java @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import java.time.Instant; + +/** + * Denotes a conversion between Hbars and cents (USD). + */ +public final class ExchangeRate { + /** + * Denotes Hbar equivalent to cents (USD) + */ + public final int hbars; + + /** + * Denotes cents (USD) equivalent to Hbar + */ + public final int cents; + + /** + * Expiration time of this exchange rate + */ + public final Instant expirationTime; + + /** + * Calculated exchange rate + */ + public final double exchangeRateInCents; + + ExchangeRate(int hbars, int cents, Instant expirationTime) { + this.hbars = hbars; + this.cents = cents; + this.expirationTime = expirationTime; + this.exchangeRateInCents = (double) cents / (double) hbars; + } + + /** + * Create an Exchange Rate from a protobuf. + * + * @param pb the protobuf + * @return the new exchange rate + */ + static ExchangeRate fromProtobuf(org.hiero.sdk.proto.ExchangeRate pb) { + return new ExchangeRate( + pb.getHbarEquiv(), pb.getCentEquiv(), InstantConverter.fromProtobuf(pb.getExpirationTime())); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("hbars", hbars) + .add("cents", cents) + .add("expirationTime", expirationTime) + .add("exchangeRateInCents", exchangeRateInCents) + .toString(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/ExchangeRates.java b/sdk/src/main/java/org/hiero/sdk/ExchangeRates.java new file mode 100644 index 0000000000..b089496121 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/ExchangeRates.java @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import com.google.protobuf.InvalidProtocolBufferException; + +/** + * Contains a set of Exchange Rates (current and next). + */ +public final class ExchangeRates { + /** + * Current Exchange Rate + */ + public final ExchangeRate currentRate; + + /** + * Next Exchange Rate + */ + public final ExchangeRate nextRate; + + private ExchangeRates(ExchangeRate currentRate, ExchangeRate nextRate) { + this.currentRate = currentRate; + this.nextRate = nextRate; + } + + /** + * Create an Exchange Rates from a protobuf. + * + * @param pb the protobuf + * @return the new exchange rates + */ + static ExchangeRates fromProtobuf(org.hiero.sdk.proto.ExchangeRateSet pb) { + return new ExchangeRates( + ExchangeRate.fromProtobuf(pb.getCurrentRate()), ExchangeRate.fromProtobuf(pb.getNextRate())); + } + + /** + * Create an Exchange Rates from a byte array. + * + * @param bytes the byte array + * @return the new exchange rates + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + public static ExchangeRates fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + return fromProtobuf( + org.hiero.sdk.proto.ExchangeRateSet.parseFrom(bytes).toBuilder().build()); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("currentRate", currentRate.toString()) + .add("nextRate", nextRate.toString()) + .toString(); + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/Executable.java b/sdk/src/main/java/org/hiero/sdk/Executable.java similarity index 75% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/Executable.java rename to sdk/src/main/java/org/hiero/sdk/Executable.java index 4bb6a01fa6..bc5b7f00de 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/Executable.java +++ b/sdk/src/main/java/org/hiero/sdk/Executable.java @@ -1,30 +1,10 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; -import static com.hedera.hashgraph.sdk.FutureConverter.toCompletableFuture; +import static org.hiero.sdk.FutureConverter.toCompletableFuture; import com.google.common.annotations.VisibleForTesting; import com.google.protobuf.MessageLite; -import com.hedera.hashgraph.sdk.logger.LogLevel; -import com.hedera.hashgraph.sdk.logger.Logger; import io.grpc.CallOptions; import io.grpc.ClientCall; import io.grpc.MethodDescriptor; @@ -49,6 +29,8 @@ import java.util.regex.Pattern; import javax.annotation.Nullable; import org.bouncycastle.util.encoders.Hex; +import org.hiero.sdk.logger.LogLevel; +import org.hiero.sdk.logger.Logger; /** * Abstract base utility class. @@ -61,8 +43,9 @@ abstract class Executable { @SuppressWarnings("java:S2245") protected static final Random random = new Random(); - static final Pattern RST_STREAM = Pattern - .compile(".*\\brst[^0-9a-zA-Z]stream\\b.*", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); + + static final Pattern RST_STREAM = + Pattern.compile(".*\\brst[^0-9a-zA-Z]stream\\b.*", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); /** * The maximum times execution will be attempted */ @@ -99,12 +82,14 @@ abstract class Executable requestListener; // Lambda responsible for executing synchronous gRPC requests. Pluggable for unit testing. @VisibleForTesting Function blockingUnaryCall = - (grpcRequest) -> ClientCalls.blockingUnaryCall(grpcRequest.createCall(), grpcRequest.getRequest()); + (grpcRequest) -> ClientCalls.blockingUnaryCall(grpcRequest.createCall(), grpcRequest.getRequest()); + private java.util.function.Function responseListener; Executable() { @@ -346,7 +331,8 @@ private void delay(long delay) { try { if (delay > 0) { if (logger.isEnabledForLevel(LogLevel.DEBUG)) { - logger.debug("Sleeping for: " + delay + " | Thread name: " + Thread.currentThread().getName()); + logger.debug("Sleeping for: " + delay + " | Thread name: " + + Thread.currentThread().getName()); } Thread.sleep(delay); } @@ -496,15 +482,17 @@ public CompletableFuture executeAsync(Client client, Duration timeout) { mergeFromClient(client); - onExecuteAsync(client).thenRun(() -> { - checkNodeAccountIds(); - setNodesFromNodeAccountIds(client); + onExecuteAsync(client) + .thenRun(() -> { + checkNodeAccountIds(); + setNodesFromNodeAccountIds(client); - executeAsyncInternal(client, 1, null, retval, timeout); - }).exceptionally(error -> { - retval.completeExceptionally(error); - return null; - }); + executeAsyncInternal(client, 1, null, retval, timeout); + }) + .exceptionally(error -> { + retval.completeExceptionally(error); + return null; + }); return retval; } @@ -564,24 +552,25 @@ public void executeAsync(Client client, Duration timeout, Consumer onSuccess, * @param error the error if the transaction was not successful */ protected void logTransaction( - TransactionId transactionId, - Client client, - Node node, - boolean isAsync, - int attempt, - @Nullable ResponseT response, - @Nullable Throwable error) { + TransactionId transactionId, + Client client, + Node node, + boolean isAsync, + int attempt, + @Nullable ResponseT response, + @Nullable Throwable error) { if (!logger.isEnabledForLevel(LogLevel.TRACE)) { return; } - logger.trace("Execute{} Transaction ID: {}, submit to {}, node: {}, attempt: {}", - isAsync ? "Async" : "", - transactionId, - client.network, - node.getAccountId(), - attempt); + logger.trace( + "Execute{} Transaction ID: {}, submit to {}, node: {}, attempt: {}", + isAsync ? "Async" : "", + transactionId, + client.network, + node.getAccountId(), + attempt); if (response != null) { logger.trace(" - Response: {}", response); @@ -613,8 +602,7 @@ void setNodesFromNodeAccountIds(Client client) { // When multiple nodes are available the system retries with different node on each attempt // instead of different proxy of the same node for (var accountId : nodeAccountIds) { - @Nullable - var nodeProxies = client.network.getNodeProxies(accountId); + @Nullable var nodeProxies = client.network.getNodeProxies(accountId); if (nodeProxies == null || nodeProxies.isEmpty()) { continue; } @@ -624,8 +612,7 @@ void setNodesFromNodeAccountIds(Client client) { nodes.add(Objects.requireNonNull(node)); } if (nodes.isEmpty()) { - throw new IllegalStateException( - "All node account IDs did not map to valid nodes in the client's network"); + throw new IllegalStateException("All node account IDs did not map to valid nodes in the client's network"); } } @@ -692,12 +679,11 @@ private ProtoRequestT getRequestForExecute() { } private void executeAsyncInternal( - Client client, - int attempt, - @Nullable Throwable lastException, - CompletableFuture returnFuture, - Duration timeout - ) { + Client client, + int attempt, + @Nullable Throwable lastException, + CompletableFuture returnFuture, + Duration timeout) { // If the logger on the request is not set, use the logger in client // (if set, otherwise do not use logger) if (this.logger == null && client.getLogger() != null) { @@ -710,81 +696,109 @@ private void executeAsyncInternal( if (attempt > maxAttempts) { returnFuture.completeExceptionally( - new CompletionException(new MaxAttemptsExceededException(lastException))); + new CompletionException(new MaxAttemptsExceededException(lastException))); return; } var timeoutTime = Instant.now().plus(timeout); - GrpcRequest grpcRequest = new GrpcRequest(client.network, attempt, - Duration.between(Instant.now(), timeoutTime)); + GrpcRequest grpcRequest = + new GrpcRequest(client.network, attempt, Duration.between(Instant.now(), timeoutTime)); Supplier> afterUnhealthyDelay = () -> { - return grpcRequest.getNode().isHealthy() ? - CompletableFuture.completedFuture((Void) null) : - Delayer.delayFor(grpcRequest.getNode().getRemainingTimeForBackoff(), client.executor); + return grpcRequest.getNode().isHealthy() + ? CompletableFuture.completedFuture((Void) null) + : Delayer.delayFor(grpcRequest.getNode().getRemainingTimeForBackoff(), client.executor); }; afterUnhealthyDelay.get().thenRun(() -> { - grpcRequest.getNode().channelFailedToConnectAsync().thenAccept(connectionFailed -> { - if (connectionFailed) { - var connectionException = grpcRequest.reactToConnectionFailure(); - executeAsyncInternal(client, attempt + 1, connectionException, returnFuture, - Duration.between(Instant.now(), timeoutTime)); - return; - } - - toCompletableFuture( - ClientCalls.futureUnaryCall(grpcRequest.createCall(), grpcRequest.getRequest())).handle( - (response, error) -> { - logTransaction(this.getTransactionIdInternal(), client, grpcRequest.getNode(), true, attempt, - response, error); - - if (grpcRequest.shouldRetryExceptionally(error)) { - // the transaction had a network failure reaching Hedera - executeAsyncInternal(client, attempt + 1, error, returnFuture, - Duration.between(Instant.now(), timeoutTime)); - return null; - } - - if (error != null) { - // not a network failure, some other weirdness going on; just fail fast - returnFuture.completeExceptionally(new CompletionException(error)); - return null; + grpcRequest + .getNode() + .channelFailedToConnectAsync() + .thenAccept(connectionFailed -> { + if (connectionFailed) { + var connectionException = grpcRequest.reactToConnectionFailure(); + executeAsyncInternal( + client, + attempt + 1, + connectionException, + returnFuture, + Duration.between(Instant.now(), timeoutTime)); + return; } - var status = mapResponseStatus(response); - var executionState = getExecutionState(status, response); - grpcRequest.handleResponse(response, status, executionState); - - switch (executionState) { - case SERVER_ERROR: - executeAsyncInternal(client, attempt + 1, grpcRequest.mapStatusException(), - returnFuture, Duration.between(Instant.now(), timeoutTime)); - break; - case RETRY: - Delayer.delayFor((attempt < maxAttempts) ? grpcRequest.getDelay() : 0, client.executor) - .thenRun(() -> executeAsyncInternal(client, attempt + 1, - grpcRequest.mapStatusException(), - returnFuture, Duration.between(Instant.now(), timeoutTime))); - break; - case REQUEST_ERROR: - returnFuture.completeExceptionally( - new CompletionException(grpcRequest.mapStatusException())); - break; - case SUCCESS: - default: - returnFuture.complete(grpcRequest.mapResponse()); - } + toCompletableFuture( + ClientCalls.futureUnaryCall(grpcRequest.createCall(), grpcRequest.getRequest())) + .handle((response, error) -> { + logTransaction( + this.getTransactionIdInternal(), + client, + grpcRequest.getNode(), + true, + attempt, + response, + error); + + if (grpcRequest.shouldRetryExceptionally(error)) { + // the transaction had a network failure reaching Hedera + executeAsyncInternal( + client, + attempt + 1, + error, + returnFuture, + Duration.between(Instant.now(), timeoutTime)); + return null; + } + + if (error != null) { + // not a network failure, some other weirdness going on; just fail fast + returnFuture.completeExceptionally(new CompletionException(error)); + return null; + } + + var status = mapResponseStatus(response); + var executionState = getExecutionState(status, response); + grpcRequest.handleResponse(response, status, executionState); + + switch (executionState) { + case SERVER_ERROR: + executeAsyncInternal( + client, + attempt + 1, + grpcRequest.mapStatusException(), + returnFuture, + Duration.between(Instant.now(), timeoutTime)); + break; + case RETRY: + Delayer.delayFor( + (attempt < maxAttempts) ? grpcRequest.getDelay() : 0, + client.executor) + .thenRun(() -> executeAsyncInternal( + client, + attempt + 1, + grpcRequest.mapStatusException(), + returnFuture, + Duration.between(Instant.now(), timeoutTime))); + break; + case REQUEST_ERROR: + returnFuture.completeExceptionally( + new CompletionException(grpcRequest.mapStatusException())); + break; + case SUCCESS: + default: + returnFuture.complete(grpcRequest.mapResponse()); + } + return null; + }) + .exceptionally(error -> { + returnFuture.completeExceptionally(error); + return null; + }); + }) + .exceptionally(error -> { + returnFuture.completeExceptionally(error); return null; - }).exceptionally(error -> { - returnFuture.completeExceptionally(error); - return null; - }); - }).exceptionally(error -> { - returnFuture.completeExceptionally(error); - return null; - }); + }); }); } @@ -825,9 +839,11 @@ boolean shouldRetryExceptionally(@Nullable Throwable error) { var status = statusException.getStatus().getCode(); var description = statusException.getStatus().getDescription(); - return (status == Code.UNAVAILABLE) || - (status == Code.RESOURCE_EXHAUSTED) || - (status == Code.INTERNAL && description != null && RST_STREAM.matcher(description).matches()); + return (status == Code.UNAVAILABLE) + || (status == Code.RESOURCE_EXHAUSTED) + || (status == Code.INTERNAL + && description != null + && RST_STREAM.matcher(description).matches()); } return false; @@ -847,7 +863,7 @@ ExecutionState getExecutionState(Status status, ResponseT response) { case OK: return ExecutionState.SUCCESS; default: - return ExecutionState.REQUEST_ERROR; // user error + return ExecutionState.REQUEST_ERROR; // user error } } @@ -855,9 +871,10 @@ ExecutionState getExecutionState(Status status, ResponseT response) { class GrpcRequest { @Nullable private final Network network; + private final Node node; private final int attempt; - //private final ClientCall call; + // private final ClientCall call; private final ProtoRequestT request; private final long startAt; private final long delay; @@ -875,14 +892,13 @@ class GrpcRequest { this.startAt = System.nanoTime(); // Exponential back-off for Delayer: 250ms, 500ms, 1s, 2s, 4s, 8s, ... 8s - delay = (long) Math.min(Objects.requireNonNull(minBackoff).toMillis() * Math.pow(2, attempt - 1.0), - Objects.requireNonNull(maxBackoff).toMillis()); + delay = (long) Math.min( + Objects.requireNonNull(minBackoff).toMillis() * Math.pow(2, attempt - 1.0), + Objects.requireNonNull(maxBackoff).toMillis()); } public CallOptions getCallOptions() { - long deadline = Math.min( - this.grpcDeadline.toMillis(), - Executable.this.grpcDeadline.toMillis()); + long deadline = Math.min(this.grpcDeadline.toMillis(), Executable.this.grpcDeadline.toMillis()); return CallOptions.DEFAULT.withDeadlineAfter(deadline, TimeUnit.MILLISECONDS); } @@ -910,8 +926,11 @@ public long getDelay() { Throwable reactToConnectionFailure() { Objects.requireNonNull(network).increaseBackoff(node); - logger.warn("Retrying in {} ms after channel connection failure with node {} during attempt #{}", - node.getRemainingTimeForBackoff(), node.getAccountId(), attempt); + logger.warn( + "Retrying in {} ms after channel connection failure with node {} during attempt #{}", + node.getRemainingTimeForBackoff(), + node.getAccountId(), + attempt); verboseLog(node); return new IllegalStateException("Failed to connect to node " + node.getAccountId()); } @@ -923,9 +942,12 @@ boolean shouldRetryExceptionally(@Nullable Throwable e) { if (retry) { Objects.requireNonNull(network).increaseBackoff(node); - logger.warn("Retrying in {} ms after failure with node {} during attempt #{}: {}", - node.getRemainingTimeForBackoff(), node.getAccountId(), attempt, - e != null ? e.getMessage() : "NULL"); + logger.warn( + "Retrying in {} ms after failure with node {} during attempt #{}: {}", + node.getRemainingTimeForBackoff(), + node.getAccountId(), + attempt, + e != null ? e.getMessage() : "NULL"); verboseLog(node); } @@ -948,8 +970,13 @@ void handleResponse(ResponseT response, Status status, ExecutionState executionS this.response = Executable.this.responseListener.apply(response); this.responseStatus = status; - logger.trace("Received {} response in {} s from node {} during attempt #{}: {}", - responseStatus, latency, node.getAccountId(), attempt, response); + logger.trace( + "Received {} response in {} s from node {} during attempt #{}: {}", + responseStatus, + latency, + node.getAccountId(), + attempt, + response); if (executionState == ExecutionState.SERVER_ERROR && attemptedAllNodes) { executionState = ExecutionState.RETRY; @@ -957,15 +984,20 @@ void handleResponse(ResponseT response, Status status, ExecutionState executionS } switch (executionState) { case RETRY -> { - logger.warn("Retrying in {} ms after failure with node {} during attempt #{}: {}", - delay, node.getAccountId(), attempt, responseStatus); + logger.warn( + "Retrying in {} ms after failure with node {} during attempt #{}: {}", + delay, + node.getAccountId(), + attempt, + responseStatus); verboseLog(node); } - case SERVER_ERROR -> - logger.warn("Problem submitting request to node {} for attempt #{}, retry with new node: {}", - node.getAccountId(), attempt, responseStatus); - default -> { - } + case SERVER_ERROR -> logger.warn( + "Problem submitting request to node {} for attempt #{}, retry with new node: {}", + node.getAccountId(), + attempt, + responseStatus); + default -> {} } } @@ -978,11 +1010,11 @@ void verboseLog(Node node) { } else { ipAddress = node.address.getAddress(); } - logger.trace("Node IP {} Timestamp {} Transaction Type {}", - ipAddress, - System.currentTimeMillis(), - this.getClass().getSimpleName() - ); + logger.trace( + "Node IP {} Timestamp {} Transaction Type {}", + ipAddress, + System.currentTimeMillis(), + this.getClass().getSimpleName()); } } } diff --git a/sdk/src/main/java/org/hiero/sdk/ExecutionState.java b/sdk/src/main/java/org/hiero/sdk/ExecutionState.java new file mode 100644 index 0000000000..7674ebda06 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/ExecutionState.java @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +/** + * Enum for the execution states. + */ +public enum ExecutionState { + /** + * Indicates that the execution was successful + */ + SUCCESS, + /** + * Indicates that the call was successful but the operation did not complete. Retry with same/new node + */ + RETRY, + /** + * Indicates that the receiver was bad node. Retry with new node + */ + SERVER_ERROR, + /** + * Indicates that the request was incorrect + */ + REQUEST_ERROR +} diff --git a/sdk/src/main/java/org/hiero/sdk/FeeAssessmentMethod.java b/sdk/src/main/java/org/hiero/sdk/FeeAssessmentMethod.java new file mode 100644 index 0000000000..40c668b37d --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/FeeAssessmentMethod.java @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +/** + * Enum for the fee assessment method. + *

+ * The terminology here (exclusive vs inclusive) is borrowed from tax assessment. + */ +public enum FeeAssessmentMethod { + + /** + * If Alice is paying Bob, and an inclusive fractional fee is collected to be sent to Charlie, + * the amount Alice declares she will pay in the transfer transaction includes the fee amount. + *

+ * In other words, Bob receives the amount that Alice intended to send, minus the fee. + */ + INCLUSIVE(false), + /** + * If Alice is paying Bob, and an exclusive fractional fee is collected to be sent to Charlie, + * the amount Alice declares she will pay in the transfer transaction does not include the fee amount. + *

+ * In other words, Alice is charged the fee in addition to the amount she intended to send to Bob. + */ + EXCLUSIVE(true); + + final boolean code; + + FeeAssessmentMethod(boolean code) { + this.code = code; + } + + static FeeAssessmentMethod valueOf(boolean code) { + return code ? EXCLUSIVE : INCLUSIVE; + } + + @Override + public String toString() { + return code ? "EXCLUSIVE" : "INCLUSIVE"; + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/FeeComponents.java b/sdk/src/main/java/org/hiero/sdk/FeeComponents.java similarity index 75% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/FeeComponents.java rename to sdk/src/main/java/org/hiero/sdk/FeeComponents.java index 10b7583c90..5911a74733 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/FeeComponents.java +++ b/sdk/src/main/java/org/hiero/sdk/FeeComponents.java @@ -1,23 +1,5 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.common.base.MoreObjects; import com.google.protobuf.InvalidProtocolBufferException; @@ -74,8 +56,7 @@ public class FeeComponents implements Cloneable { /** * Constructor. */ - public FeeComponents() { - } + public FeeComponents() {} /** * Create a fee components object from a protobuf. @@ -83,19 +64,19 @@ public FeeComponents() { * @param feeComponents the protobuf * @return the fee component object */ - static FeeComponents fromProtobuf(com.hedera.hashgraph.sdk.proto.FeeComponents feeComponents) { + static FeeComponents fromProtobuf(org.hiero.sdk.proto.FeeComponents feeComponents) { return new FeeComponents() - .setMin(feeComponents.getMin()) - .setMax(feeComponents.getMax()) - .setConstant(feeComponents.getConstant()) - .setTransactionBandwidthByte(feeComponents.getBpt()) - .setTransactionVerification(feeComponents.getVpt()) - .setTransactionRamByteHour(feeComponents.getRbh()) - .setTransactionStorageByteHour(feeComponents.getSbh()) - .setContractTransactionGas(feeComponents.getGas()) - .setTransferVolumeHbar(feeComponents.getTv()) - .setResponseMemoryByte(feeComponents.getBpr()) - .setResponseDiskByte(feeComponents.getSbpr()); + .setMin(feeComponents.getMin()) + .setMax(feeComponents.getMax()) + .setConstant(feeComponents.getConstant()) + .setTransactionBandwidthByte(feeComponents.getBpt()) + .setTransactionVerification(feeComponents.getVpt()) + .setTransactionRamByteHour(feeComponents.getRbh()) + .setTransactionStorageByteHour(feeComponents.getSbh()) + .setContractTransactionGas(feeComponents.getGas()) + .setTransferVolumeHbar(feeComponents.getTv()) + .setResponseMemoryByte(feeComponents.getBpr()) + .setResponseDiskByte(feeComponents.getSbpr()); } /** @@ -106,7 +87,8 @@ static FeeComponents fromProtobuf(com.hedera.hashgraph.sdk.proto.FeeComponents f * @throws InvalidProtocolBufferException when there is an issue with the protobuf */ public static FeeComponents fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return fromProtobuf(com.hedera.hashgraph.sdk.proto.FeeComponents.parseFrom(bytes).toBuilder().build()); + return fromProtobuf( + org.hiero.sdk.proto.FeeComponents.parseFrom(bytes).toBuilder().build()); } /** @@ -334,37 +316,37 @@ public FeeComponents setResponseDiskByte(long responseDiskByte) { * * @return the protobuf */ - com.hedera.hashgraph.sdk.proto.FeeComponents toProtobuf() { - return com.hedera.hashgraph.sdk.proto.FeeComponents.newBuilder() - .setMin(getMin()) - .setMax(getMax()) - .setConstant(getConstant()) - .setBpt(getTransactionBandwidthByte()) - .setVpt(getTransactionVerification()) - .setRbh(getTransactionRamByteHour()) - .setSbh(getTransactionStorageByteHour()) - .setGas(getContractTransactionGas()) - .setTv(getTransferVolumeHbar()) - .setBpr(getResponseMemoryByte()) - .setSbpr(getResponseDiskByte()) - .build(); + org.hiero.sdk.proto.FeeComponents toProtobuf() { + return org.hiero.sdk.proto.FeeComponents.newBuilder() + .setMin(getMin()) + .setMax(getMax()) + .setConstant(getConstant()) + .setBpt(getTransactionBandwidthByte()) + .setVpt(getTransactionVerification()) + .setRbh(getTransactionRamByteHour()) + .setSbh(getTransactionStorageByteHour()) + .setGas(getContractTransactionGas()) + .setTv(getTransferVolumeHbar()) + .setBpr(getResponseMemoryByte()) + .setSbpr(getResponseDiskByte()) + .build(); } @Override public String toString() { return MoreObjects.toStringHelper(this) - .add("min", getMin()) - .add("max", getMax()) - .add("constant", getConstant()) - .add("transactionBandwidthByte", getTransactionBandwidthByte()) - .add("transactionVerification", getTransactionVerification()) - .add("transactionRamByteHour", getTransactionRamByteHour()) - .add("transactionStorageByteHour", getTransactionStorageByteHour()) - .add("contractTransactionGas", getContractTransactionGas()) - .add("transferVolumeHbar", getTransferVolumeHbar()) - .add("responseMemoryByte", getResponseMemoryByte()) - .add("responseDiskByte", getResponseDiskByte()) - .toString(); + .add("min", getMin()) + .add("max", getMax()) + .add("constant", getConstant()) + .add("transactionBandwidthByte", getTransactionBandwidthByte()) + .add("transactionVerification", getTransactionVerification()) + .add("transactionRamByteHour", getTransactionRamByteHour()) + .add("transactionStorageByteHour", getTransactionStorageByteHour()) + .add("contractTransactionGas", getContractTransactionGas()) + .add("transferVolumeHbar", getTransferVolumeHbar()) + .add("responseMemoryByte", getResponseMemoryByte()) + .add("responseDiskByte", getResponseDiskByte()) + .toString(); } /** diff --git a/sdk/src/main/java/org/hiero/sdk/FeeData.java b/sdk/src/main/java/org/hiero/sdk/FeeData.java new file mode 100644 index 0000000000..e1b26f46b8 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/FeeData.java @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import com.google.protobuf.InvalidProtocolBufferException; +import javax.annotation.Nullable; + +/** + * The total fees charged for a transaction. It contains three parts namely + * node data, network data and service data. + */ +public class FeeData implements Cloneable { + @Nullable + private FeeComponents nodeData = null; + + @Nullable + private FeeComponents networkData = null; + + @Nullable + private FeeComponents serviceData = null; + + private FeeDataType type = FeeDataType.DEFAULT; + + /** + * Constructor. + */ + public FeeData() {} + + /** + * Initialize fee data object from a protobuf. + * + * @param feeData the protobuf + * @return the fee data object + */ + static FeeData fromProtobuf(org.hiero.sdk.proto.FeeData feeData) { + return new FeeData() + .setNodeData(feeData.hasNodedata() ? FeeComponents.fromProtobuf(feeData.getNodedata()) : null) + .setNetworkData(feeData.hasNetworkdata() ? FeeComponents.fromProtobuf(feeData.getNetworkdata()) : null) + .setServiceData(feeData.hasNodedata() ? FeeComponents.fromProtobuf(feeData.getServicedata()) : null) + .setType(FeeDataType.valueOf(feeData.getSubType())); + } + + /** + * Initialize fee data object from byte array. + * + * @param bytes the byte array + * @return the fee data object + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + public static FeeData fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + return fromProtobuf( + org.hiero.sdk.proto.FeeData.parseFrom(bytes).toBuilder().build()); + } + + /** + * Extract the node data. + * + * @return the node data fee components object + */ + @Nullable + FeeComponents getNodeData() { + return nodeData; + } + + /** + * Assign the node data fee component object. + * + * @param nodeData the node data fee component object + * @return {@code this} + */ + FeeData setNodeData(@Nullable FeeComponents nodeData) { + this.nodeData = nodeData; + return this; + } + + /** + * Extract the network data. + * + * @return the network data fee component object + */ + @Nullable + FeeComponents getNetworkData() { + return networkData; + } + + /** + * Assign the network data fee component object. + * + * @param networkData the network data fee component object + * @return {@code this} + */ + FeeData setNetworkData(@Nullable FeeComponents networkData) { + this.networkData = networkData; + return this; + } + + /** + * Extract the service data. + * + * @return the service data fee component object + */ + @Nullable + FeeComponents getServiceData() { + return serviceData; + } + + /** + * Assign the service data fee component object. + * + * @param serviceData the service data fee component object + * @return {@code this} + */ + FeeData setServiceData(@Nullable FeeComponents serviceData) { + this.serviceData = serviceData; + return this; + } + + /** + * Extract the fee data type. + * + * @return the fee data type + */ + FeeDataType getType() { + return type; + } + + /** + * Assign the fee data type. + * {@link org.hiero.sdk.FeeDataType} + * + * @param type the fee data type + * @return {@code this} + */ + FeeData setType(FeeDataType type) { + this.type = type; + return this; + } + + /** + * Convert the fee data type into a protobuf. + * + * @return the protobuf + */ + org.hiero.sdk.proto.FeeData toProtobuf() { + var builder = org.hiero.sdk.proto.FeeData.newBuilder().setSubType(type.code); + if (nodeData != null) { + builder.setNodedata(nodeData.toProtobuf()); + } + if (networkData != null) { + builder.setNetworkdata(networkData.toProtobuf()); + } + if (serviceData != null) { + builder.setServicedata(serviceData.toProtobuf()); + } + return builder.build(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("nodeData", getNodeData()) + .add("networkData", getNetworkData()) + .add("serviceData", getServiceData()) + .add("type", getType()) + .toString(); + } + + /** + * Create the byte array. + * + * @return a byte array representation + */ + public byte[] toBytes() { + return toProtobuf().toByteArray(); + } + + @Override + public FeeData clone() { + try { + FeeData clone = (FeeData) super.clone(); + clone.nodeData = nodeData != null ? nodeData.clone() : null; + clone.networkData = networkData != null ? networkData.clone() : null; + clone.serviceData = serviceData != null ? serviceData.clone() : null; + return clone; + } catch (CloneNotSupportedException e) { + throw new AssertionError(); + } + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/FeeDataType.java b/sdk/src/main/java/org/hiero/sdk/FeeDataType.java similarity index 77% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/FeeDataType.java rename to sdk/src/main/java/org/hiero/sdk/FeeDataType.java index 1632770ae4..ccc295b0c7 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/FeeDataType.java +++ b/sdk/src/main/java/org/hiero/sdk/FeeDataType.java @@ -1,25 +1,7 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; -import com.hedera.hashgraph.sdk.proto.SubType; +import org.hiero.sdk.proto.SubType; /** * Enum for the fee data types. @@ -57,7 +39,6 @@ public enum FeeDataType { */ SCHEDULE_CREATE_CONTRACT_CALL(SubType.SCHEDULE_CREATE_CONTRACT_CALL); - final SubType code; FeeDataType(SubType code) { diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/FeeSchedule.java b/sdk/src/main/java/org/hiero/sdk/FeeSchedule.java similarity index 75% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/FeeSchedule.java rename to sdk/src/main/java/org/hiero/sdk/FeeSchedule.java index f5afd5acd8..25988a8461 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/FeeSchedule.java +++ b/sdk/src/main/java/org/hiero/sdk/FeeSchedule.java @@ -1,33 +1,14 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.common.base.MoreObjects; import com.google.protobuf.InvalidProtocolBufferException; import java.time.Instant; - -import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; +import javax.annotation.Nullable; /** * The fee schedule for a specific hedera functionality and the time period this fee schedule will expire. @@ -36,14 +17,14 @@ */ public class FeeSchedule implements Cloneable { private List transactionFeeSchedules = new ArrayList<>(); + @Nullable private Instant expirationTime; /** * Constructor. */ - public FeeSchedule() { - } + public FeeSchedule() {} /** * Create a fee schedule from a protobuf. @@ -51,12 +32,14 @@ public FeeSchedule() { * @param feeSchedule the protobuf * @return the fee schedule */ - static FeeSchedule fromProtobuf(com.hedera.hashgraph.sdk.proto.FeeSchedule feeSchedule) { + static FeeSchedule fromProtobuf(org.hiero.sdk.proto.FeeSchedule feeSchedule) { FeeSchedule returnFeeSchedule = new FeeSchedule() - .setExpirationTime(feeSchedule.hasExpiryTime() ? InstantConverter.fromProtobuf(feeSchedule.getExpiryTime()) : null); + .setExpirationTime( + feeSchedule.hasExpiryTime() + ? InstantConverter.fromProtobuf(feeSchedule.getExpiryTime()) + : null); for (var transactionFeeSchedule : feeSchedule.getTransactionFeeScheduleList()) { - returnFeeSchedule - .addTransactionFeeSchedule(TransactionFeeSchedule.fromProtobuf(transactionFeeSchedule)); + returnFeeSchedule.addTransactionFeeSchedule(TransactionFeeSchedule.fromProtobuf(transactionFeeSchedule)); } return returnFeeSchedule; } @@ -69,7 +52,8 @@ static FeeSchedule fromProtobuf(com.hedera.hashgraph.sdk.proto.FeeSchedule feeSc * @throws InvalidProtocolBufferException when there is an issue with the protobuf */ public static FeeSchedule fromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return fromProtobuf(com.hedera.hashgraph.sdk.proto.FeeSchedule.parseFrom(bytes).toBuilder().build()); + return fromProtobuf( + org.hiero.sdk.proto.FeeSchedule.parseFrom(bytes).toBuilder().build()); } /** @@ -137,8 +121,8 @@ public FeeSchedule setExpirationTime(@Nullable Instant expirationTime) { * * @return the protobuf */ - com.hedera.hashgraph.sdk.proto.FeeSchedule toProtobuf() { - var returnBuilder = com.hedera.hashgraph.sdk.proto.FeeSchedule.newBuilder(); + org.hiero.sdk.proto.FeeSchedule toProtobuf() { + var returnBuilder = org.hiero.sdk.proto.FeeSchedule.newBuilder(); if (expirationTime != null) { returnBuilder.setExpiryTime(InstantConverter.toSecondsProtobuf(expirationTime)); } @@ -151,9 +135,9 @@ com.hedera.hashgraph.sdk.proto.FeeSchedule toProtobuf() { @Override public String toString() { return MoreObjects.toStringHelper(this) - .add("transactionFeeSchedules", getTransactionFeeSchedules()) - .add("expirationTime", getExpirationTime()) - .toString(); + .add("transactionFeeSchedules", getTransactionFeeSchedules()) + .add("expirationTime", getExpirationTime()) + .toString(); } /** diff --git a/sdk/src/main/java/org/hiero/sdk/FeeSchedules.java b/sdk/src/main/java/org/hiero/sdk/FeeSchedules.java new file mode 100644 index 0000000000..cb0a8043ad --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/FeeSchedules.java @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import com.google.protobuf.InvalidProtocolBufferException; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.CurrentAndNextFeeSchedule; + +/** + * This contains two Fee Schedules with expiry timestamp. + * + * See Hedera Documentation + */ +public class FeeSchedules { + @Nullable + private FeeSchedule current; + + @Nullable + private FeeSchedule next; + + /** + * Constructor. + */ + public FeeSchedules() { + current = next = null; + } + + /** + * Create a fee schedules object from a protobuf. + * + * @param feeSchedules the protobuf + * @return the fee schedules object + */ + static FeeSchedules fromProtobuf(CurrentAndNextFeeSchedule feeSchedules) { + return new FeeSchedules() + .setCurrent( + feeSchedules.hasCurrentFeeSchedule() + ? FeeSchedule.fromProtobuf(feeSchedules.getCurrentFeeSchedule()) + : null) + .setNext( + feeSchedules.hasNextFeeSchedule() + ? FeeSchedule.fromProtobuf(feeSchedules.getNextFeeSchedule()) + : null); + } + + /** + * Create a fee schedules object from a byte array. + * + * @param bytes the byte array + * @return the fee schedules object + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + public static FeeSchedules fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + return fromProtobuf( + CurrentAndNextFeeSchedule.parseFrom(bytes).toBuilder().build()); + } + + /** + * Extract the current fee schedule. + * + * @return the current fee schedule + */ + @Nullable + public FeeSchedule getCurrent() { + return current != null ? current.clone() : null; + } + + /** + * Assign the current fee schedule. + * + * @param current the fee schedule + * @return {@code this} + */ + public FeeSchedules setCurrent(@Nullable FeeSchedule current) { + this.current = current != null ? current.clone() : null; + return this; + } + + /** + * Extract the next fee schedule. + * + * @return the next fee schedule + */ + @Nullable + public FeeSchedule getNext() { + return next != null ? next.clone() : null; + } + + /** + * Assign the next fee schedule. + * + * @param next the fee schedule + * @return {@code this} + */ + public FeeSchedules setNext(@Nullable FeeSchedule next) { + this.next = next != null ? next.clone() : null; + return this; + } + + /** + * Create the protobuf. + * + * @return protobuf representation + */ + CurrentAndNextFeeSchedule toProtobuf() { + var returnBuilder = CurrentAndNextFeeSchedule.newBuilder(); + if (current != null) { + returnBuilder.setCurrentFeeSchedule(current.toProtobuf()); + } + if (next != null) { + returnBuilder.setNextFeeSchedule(next.toProtobuf()); + } + return returnBuilder.build(); + } + + /** + * Create the byte array. + * + * @return byte array representation + */ + public byte[] toBytes() { + return toProtobuf().toByteArray(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("current", getCurrent()) + .add("next", getNext()) + .toString(); + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/FileAppendTransaction.java b/sdk/src/main/java/org/hiero/sdk/FileAppendTransaction.java similarity index 87% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/FileAppendTransaction.java rename to sdk/src/main/java/org/hiero/sdk/FileAppendTransaction.java index d33a1a66cf..8191beec2b 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/FileAppendTransaction.java +++ b/sdk/src/main/java/org/hiero/sdk/FileAppendTransaction.java @@ -1,18 +1,18 @@ // SPDX-License-Identifier: Apache-2.0 -package com.hedera.hashgraph.sdk; +package org.hiero.sdk; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.FileAppendTransactionBody; -import com.hedera.hashgraph.sdk.proto.FileServiceGrpc; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionID; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; import io.grpc.MethodDescriptor; import java.util.LinkedHashMap; import java.util.Objects; import javax.annotation.Nullable; +import org.hiero.sdk.proto.FileAppendTransactionBody; +import org.hiero.sdk.proto.FileServiceGrpc; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionID; +import org.hiero.sdk.proto.TransactionResponse; /** *

A transaction specifically to append data to a file on the network. @@ -43,8 +43,7 @@ public FileAppendTransaction() { * records * @throws InvalidProtocolBufferException when there is an issue with the protobuf */ - FileAppendTransaction( - LinkedHashMap> txs) + FileAppendTransaction(LinkedHashMap> txs) throws InvalidProtocolBufferException { super(txs); @@ -56,7 +55,7 @@ public FileAppendTransaction() { * * @param txBody protobuf TransactionBody */ - FileAppendTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { + FileAppendTransaction(org.hiero.sdk.proto.TransactionBody txBody) { super(txBody); initFromTransactionBody(); } @@ -141,7 +140,7 @@ void validateChecksums(Client client) throws BadEntityIdException { } @Override - MethodDescriptor getMethodDescriptor() { + MethodDescriptor getMethodDescriptor() { return FileServiceGrpc.getAppendContentMethod(); } @@ -175,7 +174,7 @@ void initFromTransactionBody() { /** * Build the transaction body. * - * @return {@link com.hedera.hashgraph.sdk.proto.FileAppendTransactionBody builder} + * @return {@link org.hiero.sdk.proto.FileAppendTransactionBody builder} */ FileAppendTransactionBody.Builder build() { var builder = FileAppendTransactionBody.newBuilder(); diff --git a/sdk/src/main/java/org/hiero/sdk/FileContentsQuery.java b/sdk/src/main/java/org/hiero/sdk/FileContentsQuery.java new file mode 100644 index 0000000000..31f0d3483d --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/FileContentsQuery.java @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.ByteString; +import io.grpc.MethodDescriptor; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.FileGetContentsQuery; +import org.hiero.sdk.proto.FileServiceGrpc; +import org.hiero.sdk.proto.QueryHeader; +import org.hiero.sdk.proto.Response; +import org.hiero.sdk.proto.ResponseHeader; + +/** + * Get the contents of a file. The content field is empty (no bytes) if the + * file is empty. + * + * A query to get the contents of a file. Queries do not change the state of + * the file or require network consensus. The information is returned from a + * single node processing the query. + * + * See Hedera Documentation + */ +public final class FileContentsQuery extends Query { + + @Nullable + private FileId fileId = null; + + /** + * Constructor. + */ + public FileContentsQuery() {} + + /** + * Extract the file id. + * + * @return the file id + */ + @Nullable + public FileId getFileId() { + return fileId; + } + + /** + * Sets the file ID of the file whose contents are requested. + * + * @param fileId The FileId to be set + * @return {@code this} + */ + public FileContentsQuery setFileId(FileId fileId) { + Objects.requireNonNull(fileId); + this.fileId = fileId; + return this; + } + + @Override + public CompletableFuture getCostAsync(Client client) { + // deleted accounts return a COST_ANSWER of zero which triggers `INSUFFICIENT_TX_FEE` + // if you set that as the query payment; 25 tinybar seems to be enough to get + // `FILE_DELETED` back instead. + return super.getCostAsync(client).thenApply((cost) -> Hbar.fromTinybars(Math.max(cost.toTinybars(), 25))); + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (fileId != null) { + fileId.validateChecksum(client); + } + } + + @Override + void onMakeRequest(org.hiero.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { + var builder = FileGetContentsQuery.newBuilder(); + if (fileId != null) { + builder.setFileID(fileId.toProtobuf()); + } + + queryBuilder.setFileGetContents(builder.setHeader(header)); + } + + @Override + ResponseHeader mapResponseHeader(Response response) { + return response.getFileGetContents().getHeader(); + } + + @Override + QueryHeader mapRequestHeader(org.hiero.sdk.proto.Query request) { + return request.getFileGetContents().getHeader(); + } + + @Override + ByteString mapResponse(Response response, AccountId nodeId, org.hiero.sdk.proto.Query request) { + return response.getFileGetContents().getFileContents().getContents(); + } + + @Override + MethodDescriptor getMethodDescriptor() { + return FileServiceGrpc.getGetFileContentMethod(); + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/FileCreateTransaction.java b/sdk/src/main/java/org/hiero/sdk/FileCreateTransaction.java similarity index 82% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/FileCreateTransaction.java rename to sdk/src/main/java/org/hiero/sdk/FileCreateTransaction.java index 39dc787be2..e509aa9856 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/FileCreateTransaction.java +++ b/sdk/src/main/java/org/hiero/sdk/FileCreateTransaction.java @@ -1,41 +1,22 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.FileCreateTransactionBody; -import com.hedera.hashgraph.sdk.proto.FileServiceGrpc; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; import io.grpc.MethodDescriptor; -import java.time.Instant; - -import javax.annotation.Nullable; import java.nio.charset.StandardCharsets; +import java.time.Instant; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.FileCreateTransactionBody; +import org.hiero.sdk.proto.FileServiceGrpc; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; /** * Creates a file with the content by submitting the transaction. @@ -46,8 +27,10 @@ public final class FileCreateTransaction extends Transaction> txs) throws InvalidProtocolBufferException { + FileCreateTransaction(LinkedHashMap> txs) + throws InvalidProtocolBufferException { super(txs); initFromTransactionBody(); } @@ -76,7 +60,7 @@ public FileCreateTransaction() { * * @param txBody protobuf TransactionBody */ - FileCreateTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { + FileCreateTransaction(org.hiero.sdk.proto.TransactionBody txBody) { super(txBody); initFromTransactionBody(); } @@ -128,7 +112,7 @@ public Collection getKeys() { * paying for the transaction). Only one key must sign to delete the file, however. * *

To require more than one key to sign to delete a file, add them to a - * {@link com.hedera.hashgraph.sdk.KeyList} and pass that here. + * {@link org.hiero.sdk.KeyList} and pass that here. * *

The network currently requires a file to have at least one key (or key list or threshold key) * but this requirement may be lifted in the future. @@ -158,7 +142,7 @@ public ByteString getContents() { * *

Note that total size for a given transaction is limited to 6KiB (as of March 2020) by the * network; if you exceed this you may receive a {@link PrecheckStatusException} - * with {@link com.hedera.hashgraph.sdk.Status#TRANSACTION_OVERSIZE}. + * with {@link org.hiero.sdk.Status#TRANSACTION_OVERSIZE}. * *

In this case, you can use {@link FileAppendTransaction}, which automatically breaks the contents * into chunks for you, to append contents of arbitrary size. @@ -184,7 +168,7 @@ public FileCreateTransaction setContents(byte[] bytes) { * *

Note that total size for a given transaction is limited to 6KiB (as of March 2020) by the * network; if you exceed this you may receive a {@link PrecheckStatusException} - * with {@link com.hedera.hashgraph.sdk.Status#TRANSACTION_OVERSIZE}. + * with {@link org.hiero.sdk.Status#TRANSACTION_OVERSIZE}. * *

In this case, you can use {@link FileAppendTransaction}, which automatically breaks the contents * into chunks for you, to append contents of arbitrary size. @@ -222,7 +206,7 @@ public FileCreateTransaction setFileMemo(String memo) { } @Override - MethodDescriptor getMethodDescriptor() { + MethodDescriptor getMethodDescriptor() { return FileServiceGrpc.getCreateFileMethod(); } @@ -249,7 +233,7 @@ void initFromTransactionBody() { /** * Build the transaction body. * - * @return {@link com.hedera.hashgraph.sdk.proto.FileCreateTransactionBody builder} + * @return {@link org.hiero.sdk.proto.FileCreateTransactionBody builder} */ FileCreateTransactionBody.Builder build() { var builder = FileCreateTransactionBody.newBuilder(); diff --git a/sdk/src/main/java/org/hiero/sdk/FileDeleteTransaction.java b/sdk/src/main/java/org/hiero/sdk/FileDeleteTransaction.java new file mode 100644 index 0000000000..1371dde176 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/FileDeleteTransaction.java @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.grpc.MethodDescriptor; +import java.util.LinkedHashMap; +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.FileDeleteTransactionBody; +import org.hiero.sdk.proto.FileServiceGrpc; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; + +/** + *

A transaction to delete a file on the Hedera network. + * + *

When deleted, a file's contents are truncated to zero length and it can no longer be updated + * or appended to, or its expiration time extended. {@link FileContentsQuery} and {@link FileInfoQuery} + * will throw {@link PrecheckStatusException} with a status of {@link Status#FILE_DELETED}. + * + *

Only one of the file's keys needs to sign to delete the file, unless the key you have is part + * of a {@link org.hiero.sdk.KeyList}. + */ +public final class FileDeleteTransaction extends Transaction { + + @Nullable + private FileId fileId = null; + + /** + * Constructor. + */ + public FileDeleteTransaction() {} + + /** + * Constructor. + * + * @param txs Compound list of transaction id's list of (AccountId, Transaction) + * records + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + FileDeleteTransaction(LinkedHashMap> txs) + throws InvalidProtocolBufferException { + super(txs); + initFromTransactionBody(); + } + + /** + * Constructor. + * + * @param txBody protobuf TransactionBody + */ + FileDeleteTransaction(org.hiero.sdk.proto.TransactionBody txBody) { + super(txBody); + initFromTransactionBody(); + } + + /** + * Extract the file id. + * + * @return the file id + */ + @Nullable + public FileId getFileId() { + return fileId; + } + + /** + *

Set the ID of the file to delete. Required. + * + * @param fileId the ID of the file to delete. + * @return {@code this} + */ + public FileDeleteTransaction setFileId(FileId fileId) { + Objects.requireNonNull(fileId); + requireNotFrozen(); + this.fileId = fileId; + return this; + } + + /** + * Initialize from the transaction body. + */ + void initFromTransactionBody() { + var body = sourceTransactionBody.getFileDelete(); + if (body.hasFileID()) { + fileId = FileId.fromProtobuf(body.getFileID()); + } + } + + /** + * Build the transaction body. + * + * @return {@link org.hiero.sdk.proto.FileDeleteTransactionBody builder} + */ + FileDeleteTransactionBody.Builder build() { + var builder = FileDeleteTransactionBody.newBuilder(); + if (fileId != null) { + builder.setFileID(fileId.toProtobuf()); + } + + return builder; + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (fileId != null) { + fileId.validateChecksum(client); + } + } + + @Override + MethodDescriptor getMethodDescriptor() { + return FileServiceGrpc.getDeleteFileMethod(); + } + + @Override + void onFreeze(TransactionBody.Builder bodyBuilder) { + bodyBuilder.setFileDelete(build()); + } + + @Override + void onScheduled(SchedulableTransactionBody.Builder scheduled) { + scheduled.setFileDelete(build()); + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/FileId.java b/sdk/src/main/java/org/hiero/sdk/FileId.java similarity index 87% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/FileId.java rename to sdk/src/main/java/org/hiero/sdk/FileId.java index a740b58e1c..7fc22889aa 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/FileId.java +++ b/sdk/src/main/java/org/hiero/sdk/FileId.java @@ -1,30 +1,11 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.FileID; - +import java.util.Objects; import javax.annotation.Nonnegative; import javax.annotation.Nullable; -import java.util.Objects; +import org.hiero.sdk.proto.FileID; /** * The ID for a file on Hedera. @@ -57,6 +38,7 @@ public final class FileId implements Comparable { */ @Nonnegative public final long num; + @Nullable private final String checksum; @@ -149,14 +131,14 @@ public String toSolidityAddress() { } /** - * @return protobuf representing the file id + * @return protobuf representing the file id */ FileID toProtobuf() { return FileID.newBuilder() - .setShardNum(shard) - .setRealmNum(realm) - .setFileNum(num) - .build(); + .setShardNum(shard) + .setRealmNum(realm) + .setFileNum(num) + .build(); } /** @@ -219,7 +201,7 @@ public int hashCode() { } @Override - public boolean equals( Object o) { + public boolean equals(Object o) { if (this == o) { return true; } diff --git a/sdk/src/main/java/org/hiero/sdk/FileInfo.java b/sdk/src/main/java/org/hiero/sdk/FileInfo.java new file mode 100644 index 0000000000..b502046517 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/FileInfo.java @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import com.google.protobuf.InvalidProtocolBufferException; +import java.time.Instant; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.FileGetInfoResponse; + +/** + * Current information for a file, including its size. + * + * See Hedera Documentation + */ +public final class FileInfo { + /** + * The ID of the file for which information is requested. + */ + public final FileId fileId; + + /** + * Number of bytes in contents. + */ + public final long size; + + /** + * The current time at which this account is set to expire. + */ + public final Instant expirationTime; + + /** + * True if deleted but not yet expired. + */ + public final boolean isDeleted; + + /** + * One of these keys must sign in order to delete the file. + * All of these keys must sign in order to update the file. + */ + @Nullable + public final KeyList keys; + + /** + * The memo associated with the file + */ + public final String fileMemo; + + /** + * The ledger ID the response was returned from; please see HIP-198 for the network-specific IDs. + */ + public final LedgerId ledgerId; + + private FileInfo( + FileId fileId, + long size, + Instant expirationTime, + boolean isDeleted, + @Nullable KeyList keys, + String fileMemo, + LedgerId ledgerId) { + this.fileId = fileId; + this.size = size; + this.expirationTime = expirationTime; + this.isDeleted = isDeleted; + this.keys = keys; + this.fileMemo = fileMemo; + this.ledgerId = ledgerId; + } + + /** + * Create a file info object from a ptotobuf. + * + * @param fileInfo the protobuf + * @return the new file info object + */ + static FileInfo fromProtobuf(FileGetInfoResponse.FileInfo fileInfo) { + @Nullable KeyList keys = fileInfo.hasKeys() ? KeyList.fromProtobuf(fileInfo.getKeys(), null) : null; + + return new FileInfo( + FileId.fromProtobuf(fileInfo.getFileID()), + fileInfo.getSize(), + InstantConverter.fromProtobuf(fileInfo.getExpirationTime()), + fileInfo.getDeleted(), + keys, + fileInfo.getMemo(), + LedgerId.fromByteString(fileInfo.getLedgerId())); + } + + /** + * Create a file info object from a byte array. + * + * @param bytes the byte array + * @return the new file info object + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + public static FileInfo fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + return fromProtobuf( + FileGetInfoResponse.FileInfo.parseFrom(bytes).toBuilder().build()); + } + + /** + * Create the protobuf. + * + * @return the protobuf representation + */ + FileGetInfoResponse.FileInfo toProtobuf() { + var fileInfoBuilder = FileGetInfoResponse.FileInfo.newBuilder() + .setFileID(fileId.toProtobuf()) + .setSize(size) + .setExpirationTime(InstantConverter.toProtobuf(expirationTime)) + .setDeleted(isDeleted) + .setMemo(fileMemo) + .setLedgerId(ledgerId.toByteString()); + + if (keys != null) { + var keyList = org.hiero.sdk.proto.KeyList.newBuilder(); + + for (Key key : keys) { + keyList.addKeys(key.toProtobufKey()); + } + + fileInfoBuilder.setKeys(keyList); + } + + return fileInfoBuilder.build(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("fileId", fileId) + .add("size", size) + .add("expirationTime", expirationTime) + .add("isDeleted", isDeleted) + .add("keys", keys) + .add("fileMemo", fileMemo) + .add("ledgerId", ledgerId) + .toString(); + } + + /** + * Create the byte array. + * + * @return the byte array representation + */ + public byte[] toBytes() { + return toProtobuf().toByteArray(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/FileInfoQuery.java b/sdk/src/main/java/org/hiero/sdk/FileInfoQuery.java new file mode 100644 index 0000000000..8942890132 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/FileInfoQuery.java @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.grpc.MethodDescriptor; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.FileGetInfoQuery; +import org.hiero.sdk.proto.FileServiceGrpc; +import org.hiero.sdk.proto.QueryHeader; +import org.hiero.sdk.proto.Response; +import org.hiero.sdk.proto.ResponseHeader; + +/** + * Get all of the information about a file, except for its contents. + *

+ * When a file expires, it no longer exists, and there will be no info about it, and the fileInfo field + * will be blank. + *

+ * If a transaction or smart contract deletes the file, but it has not yet expired, then the + * fileInfo field will be non-empty, the deleted field will be true, its size will be 0, + * and its contents will be empty. Note that each file has a FileID, but does not have a filename. + */ +public final class FileInfoQuery extends Query { + + @Nullable + private FileId fileId = null; + + /** + * Constructor. + */ + public FileInfoQuery() {} + + /** + * Extract the file id. + * + * @return the file id + */ + @Nullable + public FileId getFileId() { + return fileId; + } + + /** + * Sets the file ID for which information is requested. + * + * @param fileId The FileId to be set + * @return {@code this} + */ + public FileInfoQuery setFileId(FileId fileId) { + Objects.requireNonNull(fileId); + this.fileId = fileId; + return this; + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (fileId != null) { + fileId.validateChecksum(client); + } + } + + @Override + void onMakeRequest(org.hiero.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { + var builder = FileGetInfoQuery.newBuilder(); + if (fileId != null) { + builder.setFileID(fileId.toProtobuf()); + } + + queryBuilder.setFileGetInfo(builder.setHeader(header)); + } + + @Override + ResponseHeader mapResponseHeader(Response response) { + return response.getFileGetInfo().getHeader(); + } + + @Override + QueryHeader mapRequestHeader(org.hiero.sdk.proto.Query request) { + return request.getFileGetInfo().getHeader(); + } + + @Override + FileInfo mapResponse(Response response, AccountId nodeId, org.hiero.sdk.proto.Query request) { + return FileInfo.fromProtobuf(response.getFileGetInfo().getFileInfo()); + } + + @Override + MethodDescriptor getMethodDescriptor() { + return FileServiceGrpc.getGetFileInfoMethod(); + } + + @Override + public CompletableFuture getCostAsync(Client client) { + // deleted accounts return a COST_ANSWER of zero which triggers `INSUFFICIENT_TX_FEE` + // if you set that as the query payment; 25 tinybar seems to be enough to get + // `FILE_DELETED` back instead. + return super.getCostAsync(client).thenApply((cost) -> Hbar.fromTinybars(Math.max(cost.toTinybars(), 25))); + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/FileUpdateTransaction.java b/sdk/src/main/java/org/hiero/sdk/FileUpdateTransaction.java similarity index 85% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/FileUpdateTransaction.java rename to sdk/src/main/java/org/hiero/sdk/FileUpdateTransaction.java index 053a19634e..cf51ff1650 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/FileUpdateTransaction.java +++ b/sdk/src/main/java/org/hiero/sdk/FileUpdateTransaction.java @@ -1,42 +1,23 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.StringValue; -import com.hedera.hashgraph.sdk.proto.FileServiceGrpc; -import com.hedera.hashgraph.sdk.proto.FileUpdateTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; import io.grpc.MethodDescriptor; -import java.time.Instant; - -import javax.annotation.Nullable; import java.nio.charset.StandardCharsets; +import java.time.Instant; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.FileServiceGrpc; +import org.hiero.sdk.proto.FileUpdateTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; /** * Updates a file by submitting the transaction. @@ -47,19 +28,22 @@ public final class FileUpdateTransaction extends Transaction> txs) throws InvalidProtocolBufferException { + FileUpdateTransaction(LinkedHashMap> txs) + throws InvalidProtocolBufferException { super(txs); initFromTransactionBody(); } @@ -78,7 +63,7 @@ public FileUpdateTransaction() { * * @param txBody protobuf TransactionBody */ - FileUpdateTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { + FileUpdateTransaction(org.hiero.sdk.proto.TransactionBody txBody) { super(txBody); initFromTransactionBody(); } @@ -109,7 +94,7 @@ public FileUpdateTransaction setFileId(FileId fileId) { /** * Get the keys which must sign any transactions modifying this file. * - * @return the list of keys + * @return the list of keys */ @Nullable public Collection getKeys() { @@ -181,7 +166,7 @@ public ByteString getContents() { * * @param bytes the bytes to replace the contents of the file with. * @return {@code this} - * @see #setContents(String) for an overload which takes a {@link String}. + * @see #setContents(String) for an overload which takes a String. * @see FileAppendTransaction if you merely want to add data to a file's existing contents. */ public FileUpdateTransaction setContents(byte[] bytes) { @@ -278,7 +263,7 @@ void initFromTransactionBody() { /** * Build the correct transaction body. * - * @return {@link com.hedera.hashgraph.sdk.proto.FileUpdateTransactionBody builder } + * @return {@link org.hiero.sdk.proto.FileUpdateTransactionBody builder } */ FileUpdateTransactionBody.Builder build() { var builder = FileUpdateTransactionBody.newBuilder(); @@ -307,7 +292,7 @@ void validateChecksums(Client client) throws BadEntityIdException { } @Override - MethodDescriptor getMethodDescriptor() { + MethodDescriptor getMethodDescriptor() { return FileServiceGrpc.getUpdateFileMethod(); } diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/FreezeTransaction.java b/sdk/src/main/java/org/hiero/sdk/FreezeTransaction.java similarity index 80% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/FreezeTransaction.java rename to sdk/src/main/java/org/hiero/sdk/FreezeTransaction.java index b78ca0e6df..1dfe4654dd 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/FreezeTransaction.java +++ b/sdk/src/main/java/org/hiero/sdk/FreezeTransaction.java @@ -1,40 +1,21 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.FreezeServiceGrpc; -import com.hedera.hashgraph.sdk.proto.FreezeTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; import io.grpc.MethodDescriptor; import java.time.Instant; import java.time.OffsetTime; import java.time.ZoneOffset; - -import javax.annotation.Nullable; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.FreezeServiceGrpc; +import org.hiero.sdk.proto.FreezeTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; /** * Set the freezing period in which the platform will stop creating events and accepting transactions. @@ -43,18 +24,20 @@ public final class FreezeTransaction extends Transaction { private int endHour = 0; private int endMinute = 0; + @Nullable private Instant startTime = null; + @Nullable private FileId fileId = null; + private byte[] fileHash = {}; private FreezeType freezeType = FreezeType.UNKNOWN_FREEZE_TYPE; /** * Constructor. */ - public FreezeTransaction() { - } + public FreezeTransaction() {} /** * Constructor. @@ -63,7 +46,8 @@ public FreezeTransaction() { * records * @throws InvalidProtocolBufferException when there is an issue with the protobuf */ - FreezeTransaction(LinkedHashMap> txs) throws InvalidProtocolBufferException { + FreezeTransaction(LinkedHashMap> txs) + throws InvalidProtocolBufferException { super(txs); initFromTransactionBody(); } @@ -73,7 +57,7 @@ public FreezeTransaction() { * * @param txBody protobuf TransactionBody */ - FreezeTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { + FreezeTransaction(org.hiero.sdk.proto.TransactionBody txBody) { super(txBody); initFromTransactionBody(); } @@ -108,7 +92,7 @@ public FreezeTransaction setStartTime(Instant startTime) { */ @Deprecated public FreezeTransaction setStartTime(int hour, int minute) { - return setStartTime(Instant.ofEpochMilli(((long)hour * 60 * 60 + (long)minute * 60) * 1000)); + return setStartTime(Instant.ofEpochMilli(((long) hour * 60 * 60 + (long) minute * 60) * 1000)); } /** @@ -191,7 +175,7 @@ public FileId getFileId() { /** * Assign the file id. * - * @param fileId the file id + * @param fileId the file id * @return {@code this} */ public FreezeTransaction setFileId(FileId fileId) { @@ -234,7 +218,7 @@ public FreezeType getFreezeType() { /** * Assign the freeze type. - * {@link com.hedera.hashgraph.sdk.FreezeTransaction} + * {@link org.hiero.sdk.FreezeTransaction} * * @param freezeType the freeze type * @return {@code this} @@ -247,11 +231,10 @@ public FreezeTransaction setFreezeType(FreezeType freezeType) { } @Override - void validateChecksums(Client client) { - } + void validateChecksums(Client client) {} @Override - MethodDescriptor getMethodDescriptor() { + MethodDescriptor getMethodDescriptor() { return FreezeServiceGrpc.getFreezeMethod(); } @@ -273,7 +256,7 @@ void initFromTransactionBody() { /** * Build the correct transaction body. * - * @return {@link com.hedera.hashgraph.sdk.proto.FreezeTransactionBody builder } + * @return {@link org.hiero.sdk.proto.FreezeTransactionBody builder } */ FreezeTransactionBody.Builder build() { var builder = FreezeTransactionBody.newBuilder(); diff --git a/sdk/src/main/java/org/hiero/sdk/FreezeType.java b/sdk/src/main/java/org/hiero/sdk/FreezeType.java new file mode 100644 index 0000000000..fe05df3c1a --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/FreezeType.java @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +/** + * Enum for the freeze types. + */ +public enum FreezeType { + /** + * An (invalid) default value for this enum, to ensure the client explicitly sets + * the intended type of freeze transaction. + */ + UNKNOWN_FREEZE_TYPE(org.hiero.sdk.proto.FreezeType.UNKNOWN_FREEZE_TYPE), + + /** + * Freezes the network at the specified time. The start_time field must be provided and + * must reference a future time. Any values specified for the update_file and file_hash + * fields will be ignored. This transaction does not perform any network changes or + * upgrades and requires manual intervention to restart the network. + */ + FREEZE_ONLY(org.hiero.sdk.proto.FreezeType.FREEZE_ONLY), + + /** + * A non-freezing operation that initiates network wide preparation in advance of a + * scheduled freeze upgrade. The update_file and file_hash fields must be provided and + * valid. The start_time field may be omitted and any value present will be ignored. + */ + PREPARE_UPGRADE(org.hiero.sdk.proto.FreezeType.PREPARE_UPGRADE), + + /** + * Freezes the network at the specified time and performs the previously prepared + * automatic upgrade across the entire network. + */ + FREEZE_UPGRADE(org.hiero.sdk.proto.FreezeType.FREEZE_UPGRADE), + + /** + * Aborts a pending network freeze operation. + */ + FREEZE_ABORT(org.hiero.sdk.proto.FreezeType.FREEZE_ABORT), + + /** + * Performs an immediate upgrade on auxilary services and containers providing + * telemetry/metrics. Does not impact network operations. + */ + TELEMETRY_UPGRADE(org.hiero.sdk.proto.FreezeType.TELEMETRY_UPGRADE); + + final org.hiero.sdk.proto.FreezeType code; + + FreezeType(org.hiero.sdk.proto.FreezeType code) { + this.code = code; + } + + static FreezeType valueOf(org.hiero.sdk.proto.FreezeType code) { + return switch (code) { + case UNKNOWN_FREEZE_TYPE -> UNKNOWN_FREEZE_TYPE; + case FREEZE_ONLY -> FREEZE_ONLY; + case PREPARE_UPGRADE -> PREPARE_UPGRADE; + case FREEZE_UPGRADE -> FREEZE_UPGRADE; + case FREEZE_ABORT -> FREEZE_ABORT; + case TELEMETRY_UPGRADE -> TELEMETRY_UPGRADE; + case UNRECOGNIZED -> + // NOTE: Protobuf deserialization will not give us the code on the wire + throw new IllegalArgumentException( + "network returned unrecognized response code; your SDK may be out of date"); + }; + } + + @Override + public String toString() { + return code.name(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/FutureConverter.java b/sdk/src/main/java/org/hiero/sdk/FutureConverter.java new file mode 100644 index 0000000000..eff4a8f4b9 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/FutureConverter.java @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Consumer; + +// Converts between ListenableFuture (Guava) and CompletableFuture (StreamSupport). +// https://github.com/lukas-krecan/future-converter/blob/master/java8-guava/src/main/java/net/javacrumbs/futureconverter/java8guava/FutureConverter.java#L28 +final class FutureConverter { + private FutureConverter() {} + + /** + * Generate a T object from a listenable future. + * + * @param listenableFuture the T object generator + * @return the T type object + */ + static CompletableFuture toCompletableFuture(ListenableFuture listenableFuture) { + return Java8FutureUtils.createCompletableFuture(GuavaFutureUtils.createValueSourceFuture(listenableFuture)); + } + + // https://github.com/lukas-krecan/future-converter/blob/master/common/src/main/java/net/javacrumbs/futureconverter/common/internal/ValueSource.java + private interface ValueSource { + void addCallbacks(Consumer successCallback, Consumer failureCallback); + + boolean cancel(boolean mayInterruptIfRunning); + } + + // https://github.com/lukas-krecan/future-converter/blob/master/common/src/main/java/net/javacrumbs/futureconverter/common/internal/ValueSourceFuture.java + private abstract static class ValueSourceFuture extends FutureWrapper implements ValueSource { + ValueSourceFuture(Future wrappedFuture) { + super(wrappedFuture); + } + } + + // https://github.com/lukas-krecan/future-converter/blob/652b845824de90b075cf5ddbbda6fdf440f3ed0a/common/src/main/java/net/javacrumbs/futureconverter/common/internal/FutureWrapper.java + private static class FutureWrapper implements Future { + private final Future wrappedFuture; + + FutureWrapper(Future wrappedFuture) { + this.wrappedFuture = wrappedFuture; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return wrappedFuture.cancel(mayInterruptIfRunning); + } + + @Override + public boolean isCancelled() { + return wrappedFuture.isCancelled(); + } + + @Override + public boolean isDone() { + return wrappedFuture.isDone(); + } + + @Override + public T get() throws InterruptedException, ExecutionException { + return wrappedFuture.get(); + } + + @Override + public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + return wrappedFuture.get(timeout, unit); + } + + Future getWrappedFuture() { + return wrappedFuture; + } + } + + // https://github.com/lukas-krecan/future-converter/blob/master/guava-common/src/main/java/net/javacrumbs/futureconverter/guavacommon/GuavaFutureUtils.java + private static class GuavaFutureUtils { + public static ValueSourceFuture createValueSourceFuture(ListenableFuture listenableFuture) { + if (listenableFuture instanceof ValueSourceFutureBackedListenableFuture) { + return ((ValueSourceFutureBackedListenableFuture) listenableFuture).getWrappedFuture(); + } else { + return new ListenableFutureBackedValueSourceFuture<>(listenableFuture); + } + } + + private static class ValueSourceFutureBackedListenableFuture extends FutureWrapper + implements ListenableFuture { + ValueSourceFutureBackedListenableFuture(ValueSourceFuture valueSourceFuture) { + super(valueSourceFuture); + } + + @Override + ValueSourceFuture getWrappedFuture() { + return (ValueSourceFuture) super.getWrappedFuture(); + } + + @Override + public void addListener(Runnable listener, Executor executor) { + getWrappedFuture().addCallbacks(value -> executor.execute(listener), ex -> executor.execute(listener)); + } + } + + private static class ListenableFutureBackedValueSourceFuture extends ValueSourceFuture { + private ListenableFutureBackedValueSourceFuture(ListenableFuture wrappedFuture) { + super(wrappedFuture); + } + + @Override + public void addCallbacks(Consumer successCallback, Consumer failureCallback) { + Futures.addCallback( + getWrappedFuture(), + new FutureCallback() { + @Override + public void onSuccess(T result) { + successCallback.accept(result); + } + + @Override + public void onFailure(Throwable t) { + failureCallback.accept(t); + } + }, + MoreExecutors.directExecutor()); + } + + @Override + ListenableFuture getWrappedFuture() { + return (ListenableFuture) super.getWrappedFuture(); + } + } + } + + // https://github.com/lukas-krecan/future-converter/blob/master/java8-common/src/main/java/net/javacrumbs/futureconverter/java8common/Java8FutureUtils.java + private static class Java8FutureUtils { + public static CompletableFuture createCompletableFuture(ValueSource valueSource) { + if (valueSource instanceof CompletableFutureBackedValueSource) { + return ((CompletableFutureBackedValueSource) valueSource).getWrappedFuture(); + } else { + return new ValueSourceBackedCompletableFuture(valueSource); + } + } + + private static final class ValueSourceBackedCompletableFuture extends CompletableFuture { + private final ValueSource valueSource; + + @SuppressWarnings("ConstructorLeaksThis") + private ValueSourceBackedCompletableFuture(ValueSource valueSource) { + this.valueSource = valueSource; + valueSource.addCallbacks(this::complete, this::completeExceptionally); + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + if (isDone()) { + return false; + } + boolean result = valueSource.cancel(mayInterruptIfRunning); + super.cancel(mayInterruptIfRunning); + return result; + } + } + + private static final class CompletableFutureBackedValueSource extends ValueSourceFuture { + private CompletableFutureBackedValueSource(CompletableFuture completableFuture) { + super(completableFuture); + } + + @Override + public void addCallbacks(Consumer successCallback, Consumer failureCallback) { + getWrappedFuture().whenComplete((v, t) -> { + if (t == null) { + successCallback.accept(v); + } else { + failureCallback.accept(t); + } + }); + } + + @Override + CompletableFuture getWrappedFuture() { + return (CompletableFuture) super.getWrappedFuture(); + } + } + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/Hbar.java b/sdk/src/main/java/org/hiero/sdk/Hbar.java similarity index 78% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/Hbar.java rename to sdk/src/main/java/org/hiero/sdk/Hbar.java index 5ccfc404fb..a00ebf284c 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/Hbar.java +++ b/sdk/src/main/java/org/hiero/sdk/Hbar.java @@ -1,27 +1,7 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.common.base.Splitter; - -import javax.annotation.Nullable; import java.math.BigDecimal; import java.math.MathContext; import java.util.Objects; @@ -46,7 +26,9 @@ public final class Hbar implements Comparable { * A constant value of the minimum number of hbars. */ public static final Hbar MIN = Hbar.from(-50_000_000_000L); - private static final Pattern FROM_STRING_PATTERN = Pattern.compile("^((?:\\+|\\-)?\\d+(?:\\.\\d+)?)(\\ (tℏ|μℏ|mℏ|ℏ|kℏ|Mℏ|Gℏ))?$"); + + private static final Pattern FROM_STRING_PATTERN = + Pattern.compile("^((?:\\+|\\-)?\\d+(?:\\.\\d+)?)(\\ (tℏ|μℏ|mℏ|ℏ|kℏ|Mℏ|Gℏ))?$"); private final long valueInTinybar; /** @@ -60,7 +42,7 @@ public Hbar(long amount) { /** * Constructs a new hbar of the specified value in the specified unit. - * {@link com.hedera.hashgraph.sdk.HbarUnit} + * {@link org.hiero.sdk.HbarUnit} * * @param amount the amount * @param unit the unit for amount @@ -84,7 +66,7 @@ public Hbar(BigDecimal amount) { /** * Constructs a new hbar of the specified value in the specified unit. - * {@link com.hedera.hashgraph.sdk.HbarUnit} + * {@link org.hiero.sdk.HbarUnit} * * @param amount the amount * @param unit the unit for amount @@ -93,7 +75,8 @@ public Hbar(BigDecimal amount) { var tinybars = amount.multiply(BigDecimal.valueOf(unit.tinybar)); if (tinybars.doubleValue() % 1 != 0) { - throw new IllegalArgumentException("Amount and Unit combination results in a fractional value for tinybar. Ensure tinybar value is a whole number."); + throw new IllegalArgumentException( + "Amount and Unit combination results in a fractional value for tinybar. Ensure tinybar value is a whole number."); } valueInTinybar = tinybars.longValue(); @@ -105,19 +88,21 @@ private static HbarUnit getUnit(String symbolString) { return unit; } } - throw new IllegalArgumentException("Attempted to convert string to Hbar, but unit symbol \"" + symbolString + "\" was not recognized"); + throw new IllegalArgumentException( + "Attempted to convert string to Hbar, but unit symbol \"" + symbolString + "\" was not recognized"); } /** * Converts the provided string into an amount of hbars. * * @param text The string representing the amount of Hbar - * @return {@link com.hedera.hashgraph.sdk.Hbar} + * @return {@link org.hiero.sdk.Hbar} */ public static Hbar fromString(CharSequence text) { var matcher = FROM_STRING_PATTERN.matcher(text); if (!matcher.matches()) { - throw new IllegalArgumentException("Attempted to convert string to Hbar, but \"" + text + "\" was not correctly formatted"); + throw new IllegalArgumentException( + "Attempted to convert string to Hbar, but \"" + text + "\" was not correctly formatted"); } var parts = Splitter.on(' ').splitToList(text.toString()); return new Hbar(new BigDecimal(parts.get(0)), parts.size() == 2 ? getUnit(parts.get(1)) : HbarUnit.HBAR); @@ -128,7 +113,7 @@ public static Hbar fromString(CharSequence text) { * * @param text The string representing the amount of set units * @param unit The unit to convert from to Hbar - * @return {@link com.hedera.hashgraph.sdk.Hbar} + * @return {@link org.hiero.sdk.Hbar} */ public static Hbar fromString(CharSequence text, HbarUnit unit) { return new Hbar(new BigDecimal(text.toString()), unit); @@ -138,7 +123,7 @@ public static Hbar fromString(CharSequence text, HbarUnit unit) { * Returns an Hbar whose value is equal to the specified long. * * @param hbars The value of Hbar - * @return {@link com.hedera.hashgraph.sdk.Hbar} + * @return {@link org.hiero.sdk.Hbar} */ public static Hbar from(long hbars) { return new Hbar(hbars, HbarUnit.HBAR); @@ -149,7 +134,7 @@ public static Hbar from(long hbars) { * * @param amount The long representing the amount of set units * @param unit The unit to convert from to Hbar - * @return {@link com.hedera.hashgraph.sdk.Hbar} + * @return {@link org.hiero.sdk.Hbar} */ public static Hbar from(long amount, HbarUnit unit) { return new Hbar(amount, unit); @@ -159,7 +144,7 @@ public static Hbar from(long amount, HbarUnit unit) { * Returns an Hbar whose value is equal to the specified long. * * @param hbars The BigDecimal representing the amount of Hbar - * @return {@link com.hedera.hashgraph.sdk.Hbar} + * @return {@link org.hiero.sdk.Hbar} */ public static Hbar from(BigDecimal hbars) { return new Hbar(hbars, HbarUnit.HBAR); @@ -170,7 +155,7 @@ public static Hbar from(BigDecimal hbars) { * * @param amount The BigDecimal representing the amount of set units * @param unit The unit to convert from to Hbar - * @return {@link com.hedera.hashgraph.sdk.Hbar} + * @return {@link org.hiero.sdk.Hbar} */ public static Hbar from(BigDecimal amount, HbarUnit unit) { return new Hbar(amount, unit); @@ -180,7 +165,7 @@ public static Hbar from(BigDecimal amount, HbarUnit unit) { * Returns an Hbar converted from the specified number of tinybars. * * @param tinybars The long representing the amount of tinybar - * @return {@link com.hedera.hashgraph.sdk.Hbar} + * @return {@link org.hiero.sdk.Hbar} */ public static Hbar fromTinybars(long tinybars) { return new Hbar(tinybars, HbarUnit.TINYBAR); @@ -243,7 +228,7 @@ public String toString(HbarUnit unit) { } @Override - public boolean equals( Object o) { + public boolean equals(Object o) { if (this == o) { return true; } diff --git a/sdk/src/main/java/org/hiero/sdk/HbarAllowance.java b/sdk/src/main/java/org/hiero/sdk/HbarAllowance.java new file mode 100644 index 0000000000..97644a6afc --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/HbarAllowance.java @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import com.google.protobuf.InvalidProtocolBufferException; +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.CryptoAllowance; +import org.hiero.sdk.proto.GrantedCryptoAllowance; + +/** + * An approved allowance of hbar transfers for a spender. + * + * See Hedera Documentation + */ +public class HbarAllowance { + + /** + * The account ID of the hbar owner (ie. the grantor of the allowance) + */ + @Nullable + public final AccountId ownerAccountId; + + /** + * The account ID of the spender of the hbar allowance + */ + @Nullable + public final AccountId spenderAccountId; + + /** + * The amount of the spender's allowance in tinybars + */ + @Nullable + public final Hbar amount; + + /** + * Constructor. + * @param ownerAccountId the owner granting the allowance + * @param spenderAccountId the spender + * @param amount the amount of hbar + */ + HbarAllowance(@Nullable AccountId ownerAccountId, @Nullable AccountId spenderAccountId, @Nullable Hbar amount) { + this.ownerAccountId = ownerAccountId; + this.spenderAccountId = spenderAccountId; + this.amount = amount; + } + + /** + * Create a hbar allowance from a crypto allowance protobuf. + * + * @param allowanceProto the crypto allowance protobuf + * @return the new hbar allowance + */ + static HbarAllowance fromProtobuf(CryptoAllowance allowanceProto) { + return new HbarAllowance( + allowanceProto.hasOwner() ? AccountId.fromProtobuf(allowanceProto.getOwner()) : null, + allowanceProto.hasSpender() ? AccountId.fromProtobuf(allowanceProto.getSpender()) : null, + Hbar.fromTinybars(allowanceProto.getAmount())); + } + + /** + * Create a hbar allowance from a granted crypto allowance protobuf. + * + * @param allowanceProto the granted crypto allowance protobuf + * @return the new hbar allowance + */ + static HbarAllowance fromProtobuf(GrantedCryptoAllowance allowanceProto) { + return new HbarAllowance( + null, + allowanceProto.hasSpender() ? AccountId.fromProtobuf(allowanceProto.getSpender()) : null, + Hbar.fromTinybars(allowanceProto.getAmount())); + } + + /** + * Create a hbar allowance from a byte array. + * + * @param bytes the byte array + * @return the new hbar allowance + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + public static HbarAllowance fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + return fromProtobuf(CryptoAllowance.parseFrom(Objects.requireNonNull(bytes))); + } + + /** + * Validate that the client is configured correctly. + * + * @param client the client to verify + * @throws BadEntityIdException if entity ID is formatted poorly + */ + void validateChecksums(Client client) throws BadEntityIdException { + if (ownerAccountId != null) { + ownerAccountId.validateChecksum(client); + } + if (spenderAccountId != null) { + spenderAccountId.validateChecksum(client); + } + } + + /** + * Convert a crypto allowance into a protobuf. + * + * @return the protobuf + */ + CryptoAllowance toProtobuf() { + var builder = CryptoAllowance.newBuilder().setAmount(amount.toTinybars()); + if (ownerAccountId != null) { + builder.setOwner(ownerAccountId.toProtobuf()); + } + if (spenderAccountId != null) { + builder.setSpender(spenderAccountId.toProtobuf()); + } + return builder.build(); + } + + /** + * Convert a crypto allowance into a granted crypto allowance protobuf. + * + * @return the granted crypto allowance + */ + GrantedCryptoAllowance toGrantedProtobuf() { + var builder = GrantedCryptoAllowance.newBuilder().setAmount(amount.toTinybars()); + if (spenderAccountId != null) { + builder.setSpender(spenderAccountId.toProtobuf()); + } + return builder.build(); + } + + /** + * Create the byte array. + * + * @return a byte array representation + */ + public byte[] toBytes() { + return toProtobuf().toByteArray(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("ownerAccountId", ownerAccountId) + .add("spenderAccountId", spenderAccountId) + .add("amount", amount) + .toString(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/HbarUnit.java b/sdk/src/main/java/org/hiero/sdk/HbarUnit.java new file mode 100644 index 0000000000..97bd8603c7 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/HbarUnit.java @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +/** + * Common units of hbar; for the most part they follow SI prefix conventions. + * + * See Hedera Documentation + */ +public enum HbarUnit { + /** + * The atomic (smallest) unit of hbar, used natively by the Hedera network. + *

+ * It is equivalent to 1100,000,000 hbar. + */ + TINYBAR("tℏ", 1), + + /** + * Equivalent to 100 tinybar or 11,000,000 hbar. + */ + MICROBAR("μℏ", 100), + + /** + * Equivalent to 100,000 tinybar or 11,000 hbar. + */ + MILLIBAR("mℏ", 100_000), + + /** + * The base unit of hbar, equivalent to 100 million tinybar. + */ + HBAR("ℏ", 100_000_000), + + /** + * Equivalent to 1 thousand hbar or 100 billion tinybar. + */ + KILOBAR("kℏ", 1000 * 100_000_000L), + + /** + * Equivalent to 1 million hbar or 100 trillion tinybar. + */ + MEGABAR("Mℏ", 1_000_000 * 100_000_000L), + + /** + * Equivalent to 1 billion hbar or 100 quadillion tinybar. + *

+ * The maximum hbar amount supported by Hedera in any context is ~92 gigabar + * (263 tinybar); use this unit sparingly. + */ + GIGABAR("Gℏ", 1_000_000_000 * 100_000_000L); + + final long tinybar; + + private final String symbol; + + HbarUnit(String symbol, long tinybar) { + this.symbol = symbol; + this.tinybar = tinybar; + } + + /** + * Get the preferred symbol of the current unit. + *

+ * E.g. {@link #TINYBAR}.getSymbol() returns "tℏ". + * + * @return the symbol + */ + public String getSymbol() { + return symbol; + } + + /** + * Get the name of this unit. + */ + @Override + public String toString() { + return name().toLowerCase(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/HederaPreCheckStatusException.java b/sdk/src/main/java/org/hiero/sdk/HederaPreCheckStatusException.java new file mode 100644 index 0000000000..6f2bb85952 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/HederaPreCheckStatusException.java @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import javax.annotation.Nullable; + +/** + * Signals that a transaction has failed the pre-check. + *

+ * Before a node submits a transaction to the rest of the network, + * it attempts some cheap assertions. This process is called the "pre-check". + */ +@Deprecated +public final class HederaPreCheckStatusException extends PrecheckStatusException { + HederaPreCheckStatusException(Status status, @Nullable TransactionId transactionId) { + super(status, transactionId); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/HederaReceiptStatusException.java b/sdk/src/main/java/org/hiero/sdk/HederaReceiptStatusException.java new file mode 100644 index 0000000000..9641814a04 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/HederaReceiptStatusException.java @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +/** + * An Exception thrown on error status by {@link TransactionId#getReceipt(Client)}. + *

+ * The receipt is included, though only the {@link TransactionReceipt#status} field will be + * initialized; all the getters should throw. + */ +@Deprecated +public class HederaReceiptStatusException extends ReceiptStatusException { + HederaReceiptStatusException(TransactionId transactionId, TransactionReceipt receipt) { + super(transactionId, receipt); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/HederaTrustManager.java b/sdk/src/main/java/org/hiero/sdk/HederaTrustManager.java new file mode 100644 index 0000000000..759e313ebe --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/HederaTrustManager.java @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.ByteString; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import javax.annotation.Nullable; +import javax.net.ssl.X509TrustManager; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.io.pem.PemObject; +import org.bouncycastle.util.io.pem.PemWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Internal class used by node. + */ +class HederaTrustManager implements X509TrustManager { + private static final String CERTIFICATE = "CERTIFICATE"; + private static final String PEM_HEADER = "-----BEGIN CERTIFICATE-----\n"; + private static final String PEM_FOOTER = "-----END CERTIFICATE-----\n"; + protected final Logger logger = LoggerFactory.getLogger(getClass()); + + @Nullable + private final String certHash; + + /** + * Constructor. + * + * @param certHash a byte string of the certificate hash + * @param verifyCertificate should be verified + */ + HederaTrustManager(@Nullable ByteString certHash, boolean verifyCertificate) { + if (certHash == null || certHash.isEmpty()) { + if (verifyCertificate) { + throw new IllegalStateException( + "transport security and certificate verification are enabled, but no applicable address book was found"); + } + + logger.warn("skipping certificate check since no cert hash was found"); + this.certHash = null; + } else { + this.certHash = new String(certHash.toByteArray(), StandardCharsets.UTF_8); + } + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) { + throw new UnsupportedOperationException( + "Attempted to use HederaTrustManager to verify a client, but this trust manager is for verifying server only"); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + if (certHash == null) { + return; + } + + for (var cert : chain) { + byte[] pem; + + try (var outputStream = new ByteArrayOutputStream(); + var pemWriter = new PemWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8))) { + pemWriter.writeObject(new PemObject(CERTIFICATE, cert.getEncoded())); + pemWriter.flush(); + + pem = outputStream.toByteArray(); + } catch (IOException e) { + logger.warn("Failed to write PEM to byte array: ", e); + continue; + } + + var certHashBytes = new byte[0]; + + try { + certHashBytes = MessageDigest.getInstance("SHA-384").digest(pem); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException("Failed to find SHA-384 digest for certificate hashing", e); + } + + if (this.certHash.equals(Hex.toHexString(certHashBytes))) { + return; + } + } + + throw new CertificateException("Failed to confirm the server's certificate from a known address book"); + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/InstantConverter.java b/sdk/src/main/java/org/hiero/sdk/InstantConverter.java new file mode 100644 index 0000000000..830e46355e --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/InstantConverter.java @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import java.time.Duration; +import java.time.Instant; +import org.hiero.sdk.proto.Timestamp; +import org.hiero.sdk.proto.TimestampSeconds; + +/** + * Instance in time utilities. + */ +final class InstantConverter { + /** + * Constructor. + */ + private InstantConverter() {} + + /** + * Create an instance from a timestamp protobuf. + * + * @param timestamp the protobuf + * @return the instance + */ + static Instant fromProtobuf(Timestamp timestamp) { + return Instant.ofEpochSecond(timestamp.getSeconds(), timestamp.getNanos()); + } + + /** + * Create an instance from a timestamp in seconds protobuf. + * + * @param timestampSeconds the protobuf + * @return the instance + */ + static Instant fromProtobuf(TimestampSeconds timestampSeconds) { + return Instant.ofEpochSecond(timestampSeconds.getSeconds()); + } + + /** + * Convert an instance into a timestamp. + * + * @param instant the instance + * @return the timestamp + */ + static Timestamp toProtobuf(Instant instant) { + return Timestamp.newBuilder() + .setSeconds(instant.getEpochSecond()) + .setNanos(instant.getNano()) + .build(); + } + + static Timestamp toProtobuf(Duration duration) { + return Timestamp.newBuilder() + .setSeconds(duration.getSeconds()) + .setNanos(duration.getNano()) + .build(); + } + + /** + * Convert an instance into a timestamp in seconds. + * + * @param instant the instance + * @return the timestamp in seconds + */ + static TimestampSeconds toSecondsProtobuf(Instant instant) { + return TimestampSeconds.newBuilder() + .setSeconds(instant.getEpochSecond()) + .build(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/Key.java b/sdk/src/main/java/org/hiero/sdk/Key.java new file mode 100644 index 0000000000..8abb8e66a8 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/Key.java @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.sec.SECNamedCurves; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.crypto.params.ECDomainParameters; + +/** + * A common base for the signing authority or key that entities in Hedera may have. + * + * See Hedera Documentation + * @see KeyList + * @see PublicKey + */ +public abstract class Key { + static final ASN1ObjectIdentifier ID_ED25519 = new ASN1ObjectIdentifier("1.3.101.112"); + static final ASN1ObjectIdentifier ID_ECDSA_SECP256K1 = new ASN1ObjectIdentifier("1.3.132.0.10"); + static final ASN1ObjectIdentifier ID_EC_PUBLIC_KEY = new ASN1ObjectIdentifier("1.2.840.10045.2.1"); + + static final X9ECParameters ECDSA_SECP256K1_CURVE = SECNamedCurves.getByName("secp256k1"); + static final ECDomainParameters ECDSA_SECP256K1_DOMAIN = new ECDomainParameters( + ECDSA_SECP256K1_CURVE.getCurve(), + ECDSA_SECP256K1_CURVE.getG(), + ECDSA_SECP256K1_CURVE.getN(), + ECDSA_SECP256K1_CURVE.getH()); + + /** + * Create a specific key type from the protobuf. + * + * @param key the protobuf key of unknown type + * @return the differentiated key + */ + static Key fromProtobufKey(org.hiero.sdk.proto.Key key) { + switch (key.getKeyCase()) { + case ED25519 -> { + return PublicKeyED25519.fromBytesInternal(key.getEd25519().toByteArray()); + } + case ECDSA_SECP256K1 -> { + if (key.getECDSASecp256K1().size() == 20) { + return new EvmAddress(key.getECDSASecp256K1().toByteArray()); + } else { + return PublicKeyECDSA.fromBytesInternal( + key.getECDSASecp256K1().toByteArray()); + } + } + case KEYLIST -> { + return KeyList.fromProtobuf(key.getKeyList(), null); + } + case THRESHOLDKEY -> { + return KeyList.fromProtobuf( + key.getThresholdKey().getKeys(), key.getThresholdKey().getThreshold()); + } + case CONTRACTID -> { + return ContractId.fromProtobuf(key.getContractID()); + } + case DELEGATABLE_CONTRACT_ID -> { + return DelegateContractId.fromProtobuf(key.getDelegatableContractId()); + } + case KEY_NOT_SET -> { + return null; + } + default -> throw new IllegalStateException("Key#fromProtobuf: unhandled key case: " + key.getKeyCase()); + } + } + + /** + * Serialize this key as a protobuf object + */ + abstract org.hiero.sdk.proto.Key toProtobufKey(); + + /** + * Create the byte array. + * + * @return the byte array representation + */ + public byte[] toBytes() { + return toProtobufKey().toByteArray(); + } + + /** + * Create Key from proto.Key byte array + * + * @param bytes + * @return Key representation + * @throws InvalidProtocolBufferException + */ + public static Key fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + return fromProtobufKey(org.hiero.sdk.proto.Key.parseFrom(bytes)); + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/KeyList.java b/sdk/src/main/java/org/hiero/sdk/KeyList.java similarity index 76% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/KeyList.java rename to sdk/src/main/java/org/hiero/sdk/KeyList.java index a0abd79e53..b5d6f193af 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/KeyList.java +++ b/sdk/src/main/java/org/hiero/sdk/KeyList.java @@ -1,34 +1,15 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.common.base.MoreObjects; -import com.hedera.hashgraph.sdk.proto.ThresholdKey; - -import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.ThresholdKey; /** * A list of keys that are required to sign in unison, with an optional threshold controlling how many keys of @@ -97,7 +78,7 @@ public static KeyList withThreshold(int threshold) { * @param threshold the minimum number of keys that must sign * @return the key list */ - static KeyList fromProtobuf(com.hedera.hashgraph.sdk.proto.KeyList keyList, @Nullable Integer threshold) { + static KeyList fromProtobuf(org.hiero.sdk.proto.KeyList keyList, @Nullable Integer threshold) { var keys = (threshold != null ? new KeyList(threshold) : new KeyList()); for (var i = 0; i < keyList.getKeysCount(); ++i) { keys.add(Key.fromProtobufKey(keyList.getKeys(i))); @@ -194,23 +175,20 @@ public void clear() { } @Override - com.hedera.hashgraph.sdk.proto.Key toProtobufKey() { - var protoKeyList = com.hedera.hashgraph.sdk.proto.KeyList.newBuilder(); + org.hiero.sdk.proto.Key toProtobufKey() { + var protoKeyList = org.hiero.sdk.proto.KeyList.newBuilder(); for (var key : keys) { protoKeyList.addKeys(key.toProtobufKey()); } if (threshold != null) { - return com.hedera.hashgraph.sdk.proto.Key.newBuilder() - .setThresholdKey(ThresholdKey.newBuilder() - .setThreshold(threshold) - .setKeys(protoKeyList)) - .build(); + return org.hiero.sdk.proto.Key.newBuilder() + .setThresholdKey( + ThresholdKey.newBuilder().setThreshold(threshold).setKeys(protoKeyList)) + .build(); } - return com.hedera.hashgraph.sdk.proto.Key.newBuilder() - .setKeyList(protoKeyList) - .build(); + return org.hiero.sdk.proto.Key.newBuilder().setKeyList(protoKeyList).build(); } /** @@ -218,8 +196,8 @@ com.hedera.hashgraph.sdk.proto.Key toProtobufKey() { * * @return the protobuf representation */ - com.hedera.hashgraph.sdk.proto.KeyList toProtobuf() { - var keyList = com.hedera.hashgraph.sdk.proto.KeyList.newBuilder(); + org.hiero.sdk.proto.KeyList toProtobuf() { + var keyList = org.hiero.sdk.proto.KeyList.newBuilder(); for (Key key : keys) { keyList.addKeys(key.toProtobufKey()); @@ -231,13 +209,13 @@ com.hedera.hashgraph.sdk.proto.KeyList toProtobuf() { @Override public String toString() { return MoreObjects.toStringHelper(this) - .add("threshold", threshold) - .add("keys", keys) - .toString(); + .add("threshold", threshold) + .add("keys", keys) + .toString(); } @Override - public boolean equals( Object o) { + public boolean equals(Object o) { if (this == o) { return true; } diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/Keystore.java b/sdk/src/main/java/org/hiero/sdk/Keystore.java similarity index 91% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/Keystore.java rename to sdk/src/main/java/org/hiero/sdk/Keystore.java index 6e94cb54e3..04af6246f6 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/Keystore.java +++ b/sdk/src/main/java/org/hiero/sdk/Keystore.java @@ -1,23 +1,5 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.gson.Gson; import com.google.gson.JsonIOException; @@ -25,10 +7,6 @@ import com.google.gson.JsonParser; import com.google.gson.JsonSyntaxException; import com.google.gson.stream.JsonWriter; -import java.util.Optional; -import org.bouncycastle.crypto.params.KeyParameter; -import org.bouncycastle.util.encoders.Hex; - import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -37,6 +15,9 @@ import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.Objects; +import java.util.Optional; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.util.encoders.Hex; /** * Internal utility class to serialize / deserialize between java object / json representation. @@ -57,8 +38,9 @@ public Keystore(PrivateKey privateKey) { public static Keystore fromStream(InputStream stream, String passphrase) throws IOException { try { - JsonObject jsonObject = jsonParser.parse(new InputStreamReader(stream, StandardCharsets.UTF_8)) - .getAsJsonObject(); + JsonObject jsonObject = jsonParser + .parse(new InputStreamReader(stream, StandardCharsets.UTF_8)) + .getAsJsonObject(); return fromJson(jsonObject, passphrase); } catch (IllegalStateException e) { throw new BadKeyException(Optional.ofNullable(e.getMessage()).orElse("failed to parse Keystore")); diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/LedgerId.java b/sdk/src/main/java/org/hiero/sdk/LedgerId.java similarity index 82% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/LedgerId.java rename to sdk/src/main/java/org/hiero/sdk/LedgerId.java index fad6abfbd6..9cda165959 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/LedgerId.java +++ b/sdk/src/main/java/org/hiero/sdk/LedgerId.java @@ -1,50 +1,30 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.ByteString; -import org.bouncycastle.util.encoders.Hex; - -import javax.annotation.Nullable; import java.util.Arrays; +import org.bouncycastle.util.encoders.Hex; /** * Internal utility class for ledger id manipulation. */ public class LedgerId { - final private byte[] idBytes; + private final byte[] idBytes; /** * The mainnet ledger id */ - public final static LedgerId MAINNET = new LedgerId(new byte[]{0}); + public static final LedgerId MAINNET = new LedgerId(new byte[] {0}); /** * The testnet ledger id */ - public final static LedgerId TESTNET = new LedgerId(new byte[]{1}); + public static final LedgerId TESTNET = new LedgerId(new byte[] {1}); /** * The previewnet ledger id */ - public final static LedgerId PREVIEWNET = new LedgerId(new byte[]{2}); + public static final LedgerId PREVIEWNET = new LedgerId(new byte[] {2}); /** * Constructor. @@ -204,7 +184,7 @@ public NetworkName toNetworkName() { } @Override - public boolean equals( Object o) { + public boolean equals(Object o) { if (this == o) { return true; } diff --git a/sdk/src/main/java/org/hiero/sdk/LiveHash.java b/sdk/src/main/java/org/hiero/sdk/LiveHash.java new file mode 100644 index 0000000000..f7412f5bf4 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/LiveHash.java @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import java.time.Duration; + +/** + *A hash (presumably of some kind of credential or certificate), along with a + * list of keys (each of which is either a primitive or a threshold key). Each + * of them must reach its threshold when signing the transaction, to attach + * this livehash to this account. At least one of them must reach its + * threshold to delete this livehash from this account. + * + * See Hedera Documentation + */ +public class LiveHash { + + /** + * The account to which the livehash is attached + */ + public final AccountId accountId; + + /** + * The SHA-384 hash of a credential or certificate + */ + public final ByteString hash; + + /** + * A list of keys (primitive or threshold), all of which must sign to attach the livehash to an account, and any one of which can later delete it. + */ + public final KeyList keys; + + /** + * The duration for which the livehash will remain valid + */ + public final Duration duration; + + /** + * Constructor. + * + * @param accountId the account id + * @param hash the hash + * @param keys the key list + * @param duration the duration + */ + private LiveHash(AccountId accountId, ByteString hash, KeyList keys, Duration duration) { + this.accountId = accountId; + this.hash = hash; + this.keys = keys; + this.duration = duration; + } + + /** + * Create a live hash from a protobuf. + * + * @param liveHash the protobuf + * @return the new live hash + */ + protected static LiveHash fromProtobuf(org.hiero.sdk.proto.LiveHash liveHash) { + return new LiveHash( + AccountId.fromProtobuf(liveHash.getAccountId()), + liveHash.getHash(), + KeyList.fromProtobuf(liveHash.getKeys(), null), + DurationConverter.fromProtobuf(liveHash.getDuration())); + } + + /** + * Create a live hash from a byte array. + * + * @param bytes the byte array + * @return the new live hash + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + public static LiveHash fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + return fromProtobuf( + org.hiero.sdk.proto.LiveHash.parseFrom(bytes).toBuilder().build()); + } + + /** + * Convert the live hash into a protobuf. + * + * @return the protobuf + */ + protected org.hiero.sdk.proto.LiveHash toProtobuf() { + var keyList = org.hiero.sdk.proto.KeyList.newBuilder(); + for (Key key : keys) { + keyList.addKeys(key.toProtobufKey()); + } + + return org.hiero.sdk.proto.LiveHash.newBuilder() + .setAccountId(accountId.toProtobuf()) + .setHash(hash) + .setKeys(keyList) + .setDuration(DurationConverter.toProtobuf(duration)) + .build(); + } + + /** + * Extract the byte array. + * + * @return the byte array representation + */ + public ByteString toBytes() { + return toProtobuf().toByteString(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("accountId", accountId) + .add("hash", hash.toByteArray()) + .add("keys", keys) + .add("duration", duration) + .toString(); + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/LiveHashAddTransaction.java b/sdk/src/main/java/org/hiero/sdk/LiveHashAddTransaction.java similarity index 80% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/LiveHashAddTransaction.java rename to sdk/src/main/java/org/hiero/sdk/LiveHashAddTransaction.java index 97d52d5027..823c678552 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/LiveHashAddTransaction.java +++ b/sdk/src/main/java/org/hiero/sdk/LiveHashAddTransaction.java @@ -1,41 +1,22 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.CryptoAddLiveHashTransactionBody; -import com.hedera.hashgraph.sdk.proto.CryptoServiceGrpc; -import com.hedera.hashgraph.sdk.proto.LiveHash; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; import io.grpc.MethodDescriptor; import java.time.Duration; - -import javax.annotation.Nullable; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.CryptoAddLiveHashTransactionBody; +import org.hiero.sdk.proto.CryptoServiceGrpc; +import org.hiero.sdk.proto.LiveHash; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; /** * A hash---presumably of some kind of credential or certificate---along with a list of keys, @@ -44,17 +25,19 @@ public final class LiveHashAddTransaction extends Transaction { @Nullable private AccountId accountId = null; + private byte[] hash = {}; + @Nullable private KeyList keys = null; + @Nullable private Duration duration = null; /** * Constructor. */ - public LiveHashAddTransaction() { - } + public LiveHashAddTransaction() {} /** * Constructor. @@ -63,7 +46,8 @@ public LiveHashAddTransaction() { * records * @throws InvalidProtocolBufferException when there is an issue with the protobuf */ - LiveHashAddTransaction(LinkedHashMap> txs) throws InvalidProtocolBufferException { + LiveHashAddTransaction(LinkedHashMap> txs) + throws InvalidProtocolBufferException { super(txs); initFromTransactionBody(); } @@ -194,7 +178,7 @@ void initFromTransactionBody() { /** * Build the correct transaction body. * - * @return {@link com.hedera.hashgraph.sdk.proto.CryptoAddLiveHashTransactionBody} + * @return {@link org.hiero.sdk.proto.CryptoAddLiveHashTransactionBody} */ CryptoAddLiveHashTransactionBody.Builder build() { var builder = CryptoAddLiveHashTransactionBody.newBuilder(); @@ -221,7 +205,7 @@ void validateChecksums(Client client) throws BadEntityIdException { } @Override - MethodDescriptor getMethodDescriptor() { + MethodDescriptor getMethodDescriptor() { return CryptoServiceGrpc.getAddLiveHashMethod(); } diff --git a/sdk/src/main/java/org/hiero/sdk/LiveHashDeleteTransaction.java b/sdk/src/main/java/org/hiero/sdk/LiveHashDeleteTransaction.java new file mode 100644 index 0000000000..17ad457f49 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/LiveHashDeleteTransaction.java @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import io.grpc.MethodDescriptor; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.CryptoDeleteLiveHashTransactionBody; +import org.hiero.sdk.proto.CryptoServiceGrpc; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; + +/** + * At consensus, deletes a livehash associated to the given account. The transaction must be signed + * by either the key of the owning account, or at least one of the keys associated to the livehash. + */ +public final class LiveHashDeleteTransaction extends Transaction { + @Nullable + private AccountId accountId = null; + + private byte[] hash = {}; + + /** + * Constructor. + */ + public LiveHashDeleteTransaction() {} + + /** + * Constructor. + * + * @param txs Compound list of transaction id's list of (AccountId, Transaction) + * records + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + LiveHashDeleteTransaction( + LinkedHashMap> txs) + throws InvalidProtocolBufferException { + super(txs); + initFromTransactionBody(); + } + + /** + * Extract the account id. + * + * @return the account id + */ + @Nullable + public AccountId getAccountId() { + return accountId; + } + + /** + * The account owning the livehash + * + * @param accountId The AccountId to be set + * @return {@code this} + */ + public LiveHashDeleteTransaction setAccountId(AccountId accountId) { + Objects.requireNonNull(accountId); + requireNotFrozen(); + this.accountId = accountId; + return this; + } + + /** + * Extract the hash. + * + * @return the hash + */ + public ByteString getHash() { + return ByteString.copyFrom(hash); + } + + /** + * The SHA-384 livehash to delete from the account + * + * @param hash The array of bytes to be set as hash + * @return {@code this} + */ + public LiveHashDeleteTransaction setHash(byte[] hash) { + requireNotFrozen(); + Objects.requireNonNull(hash); + this.hash = Arrays.copyOf(hash, hash.length); + return this; + } + + /** + * The SHA-384 livehash to delete from the account + * + * @param hash The array of bytes to be set as hash + * @return {@code this} + */ + public LiveHashDeleteTransaction setHash(ByteString hash) { + Objects.requireNonNull(hash); + return setHash(hash.toByteArray()); + } + + /** + * Initialize from the transaction body. + */ + void initFromTransactionBody() { + var body = sourceTransactionBody.getCryptoDeleteLiveHash(); + if (body.hasAccountOfLiveHash()) { + accountId = AccountId.fromProtobuf(body.getAccountOfLiveHash()); + } + hash = body.getLiveHashToDelete().toByteArray(); + } + + /** + * Build the correct transaction body. + * + * @return {@link org.hiero.sdk.proto.CryptoAddLiveHashTransactionBody} + */ + CryptoDeleteLiveHashTransactionBody.Builder build() { + var builder = CryptoDeleteLiveHashTransactionBody.newBuilder(); + if (accountId != null) { + builder.setAccountOfLiveHash(accountId.toProtobuf()); + } + builder.setLiveHashToDelete(ByteString.copyFrom(hash)); + + return builder; + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (accountId != null) { + accountId.validateChecksum(client); + } + } + + @Override + MethodDescriptor getMethodDescriptor() { + return CryptoServiceGrpc.getDeleteLiveHashMethod(); + } + + @Override + void onFreeze(TransactionBody.Builder bodyBuilder) { + bodyBuilder.setCryptoDeleteLiveHash(build()); + } + + @Override + void onScheduled(SchedulableTransactionBody.Builder scheduled) { + throw new UnsupportedOperationException("Cannot schedule LiveHashDeleteTransaction"); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/LiveHashQuery.java b/sdk/src/main/java/org/hiero/sdk/LiveHashQuery.java new file mode 100644 index 0000000000..1db89fe2c3 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/LiveHashQuery.java @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.ByteString; +import io.grpc.MethodDescriptor; +import java.util.Arrays; +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.CryptoGetLiveHashQuery; +import org.hiero.sdk.proto.CryptoServiceGrpc; +import org.hiero.sdk.proto.QueryHeader; +import org.hiero.sdk.proto.Response; +import org.hiero.sdk.proto.ResponseHeader; + +/** + * Requests a livehash associated to an account. + */ +public final class LiveHashQuery extends Query { + @Nullable + private AccountId accountId = null; + + private byte[] hash = {}; + + /** + * Constructor. + */ + public LiveHashQuery() {} + + /** + * Extract the account id. + * + * @return the account id + */ + @Nullable + public AccountId getAccountId() { + return accountId; + } + + /** + * The account to which the livehash is associated + * + * @param accountId The AccountId to be set + * @return {@code this} + */ + public LiveHashQuery setAccountId(AccountId accountId) { + Objects.requireNonNull(accountId); + this.accountId = accountId; + return this; + } + + /** + * Extract the hash. + * + * @return the hash + */ + public ByteString getHash() { + return ByteString.copyFrom(hash); + } + + /** + * The SHA-384 data in the livehash + * + * @param hash The array of bytes to be set as hash + * @return {@code this} + */ + public LiveHashQuery setHash(byte[] hash) { + this.hash = Arrays.copyOf(hash, hash.length); + return this; + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (accountId != null) { + accountId.validateChecksum(client); + } + } + + @Override + void onMakeRequest(org.hiero.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { + var builder = CryptoGetLiveHashQuery.newBuilder(); + if (accountId != null) { + builder.setAccountID(accountId.toProtobuf()); + } + builder.setHash(ByteString.copyFrom(hash)); + + queryBuilder.setCryptoGetLiveHash(builder.setHeader(header)); + } + + @Override + LiveHash mapResponse(Response response, AccountId nodeId, org.hiero.sdk.proto.Query request) { + return LiveHash.fromProtobuf(response.getCryptoGetLiveHash().getLiveHash()); + } + + @Override + ResponseHeader mapResponseHeader(Response response) { + return response.getCryptoGetLiveHash().getHeader(); + } + + @Override + QueryHeader mapRequestHeader(org.hiero.sdk.proto.Query request) { + return request.getCryptoGetLiveHash().getHeader(); + } + + @Override + MethodDescriptor getMethodDescriptor() { + return CryptoServiceGrpc.getCryptoGetBalanceMethod(); + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/LockableList.java b/sdk/src/main/java/org/hiero/sdk/LockableList.java similarity index 89% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/LockableList.java rename to sdk/src/main/java/org/hiero/sdk/LockableList.java index a61ac8efc4..dd0522b3e5 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/LockableList.java +++ b/sdk/src/main/java/org/hiero/sdk/LockableList.java @@ -1,23 +1,5 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import java.util.*; @@ -34,8 +16,7 @@ class LockableList implements Iterable { /** * Constructor. */ - LockableList() { - } + LockableList() {} /** * Assign a list of items to this list instance. @@ -94,7 +75,7 @@ ArrayList getList() { * @param elements the items to add * @return the updated list */ - LockableList add(T ...elements) { + LockableList add(T... elements) { requireNotLocked(); for (var e : elements) { diff --git a/sdk/src/main/java/org/hiero/sdk/MaxAttemptsExceededException.java b/sdk/src/main/java/org/hiero/sdk/MaxAttemptsExceededException.java new file mode 100644 index 0000000000..15aaf00005 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/MaxAttemptsExceededException.java @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import javax.annotation.Nullable; + +/** + * Utility exception class. + */ +public class MaxAttemptsExceededException extends IllegalStateException { + MaxAttemptsExceededException(@Nullable Throwable e) { + super("exceeded maximum attempts for request with last exception being", e); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/MaxQueryPaymentExceededException.java b/sdk/src/main/java/org/hiero/sdk/MaxQueryPaymentExceededException.java new file mode 100644 index 0000000000..0295313620 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/MaxQueryPaymentExceededException.java @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +/** + * Signals that a query will cost more than a pre-configured maximum payment amount. + */ +public final class MaxQueryPaymentExceededException extends RuntimeException { + /** + * The cost of the query that was attempted as returned by {@link Query#getCost(Client)}. + */ + public final Hbar queryCost; + + /** + * The limit for a single automatic query payment, set by + * {@link Client#setDefaultMaxQueryPayment(Hbar)} (Hbar)} or {@link Query#setMaxQueryPayment(Hbar)}. + */ + public final Hbar maxQueryPayment; + + /** + * Constructor. + * + * @param builder the query builder object + * @param cost the query cost + * @param maxQueryPayment the maximum query payment + */ + MaxQueryPaymentExceededException(Query builder, Hbar cost, Hbar maxQueryPayment) { + super(String.format( + "cost for %s, of %s, without explicit payment is greater than " + "the maximum allowed payment of %s", + builder.getClass().getSimpleName(), cost, maxQueryPayment)); + + this.queryCost = cost; + this.maxQueryPayment = maxQueryPayment; + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/MirrorNetwork.java b/sdk/src/main/java/org/hiero/sdk/MirrorNetwork.java similarity index 83% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/MirrorNetwork.java rename to sdk/src/main/java/org/hiero/sdk/MirrorNetwork.java index 75df2b0337..43f3a864fa 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/MirrorNetwork.java +++ b/sdk/src/main/java/org/hiero/sdk/MirrorNetwork.java @@ -1,23 +1,5 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import java.util.ArrayList; import java.util.HashMap; diff --git a/sdk/src/main/java/org/hiero/sdk/MirrorNode.java b/sdk/src/main/java/org/hiero/sdk/MirrorNode.java new file mode 100644 index 0000000000..328b33bafa --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/MirrorNode.java @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import java.util.concurrent.ExecutorService; + +/** + * An individual mirror node. + */ +class MirrorNode extends BaseNode { + /** + * Constructor. + * + * @param address the node address as a managed node address + * @param executor the executor service + */ + MirrorNode(BaseNodeAddress address, ExecutorService executor) { + super(address, executor); + } + + /** + * Constructor. + * + * @param address the node address as a string + * @param executor the executor service + */ + MirrorNode(String address, ExecutorService executor) { + this(BaseNodeAddress.fromString(address), executor); + } + + @Override + protected String getAuthority() { + return null; + } + + @Override + BaseNodeAddress getKey() { + return address; + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/MirrorNodeContractCallQuery.java b/sdk/src/main/java/org/hiero/sdk/MirrorNodeContractCallQuery.java new file mode 100644 index 0000000000..6b390ce974 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/MirrorNodeContractCallQuery.java @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import java.util.concurrent.ExecutionException; + +public class MirrorNodeContractCallQuery extends MirrorNodeContractQuery { + /** + * Does transient simulation of read-write operations and returns the result in hexadecimal string format. + * + * @param client The Client instance to perform the operation with + * @return The result of the contract call + * @throws ExecutionException + * @throws InterruptedException + */ + public String execute(Client client) throws ExecutionException, InterruptedException { + return call(client); + } + + @Override + public String toString() { + return "MirrorNodeContractCallQuery" + super.toString(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/MirrorNodeContractEstimateGasQuery.java b/sdk/src/main/java/org/hiero/sdk/MirrorNodeContractEstimateGasQuery.java new file mode 100644 index 0000000000..c941e53ecb --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/MirrorNodeContractEstimateGasQuery.java @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import java.util.concurrent.ExecutionException; + +public class MirrorNodeContractEstimateGasQuery extends MirrorNodeContractQuery { + + /** + * Returns gas estimation for the EVM execution. + * + * @param client The Client instance to perform the operation with + * @return The estimated gas cost + * @throws ExecutionException + * @throws InterruptedException + */ + public long execute(Client client) throws ExecutionException, InterruptedException { + return estimate(client); + } + + @Override + public String toString() { + return "MirrorNodeContractEstimateGasQuery" + super.toString(); + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/MirrorNodeContractQuery.java b/sdk/src/main/java/org/hiero/sdk/MirrorNodeContractQuery.java similarity index 80% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/MirrorNodeContractQuery.java rename to sdk/src/main/java/org/hiero/sdk/MirrorNodeContractQuery.java index 6047ea64ce..5569275b51 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/MirrorNodeContractQuery.java +++ b/sdk/src/main/java/org/hiero/sdk/MirrorNodeContractQuery.java @@ -1,28 +1,9 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; -import static com.hedera.hashgraph.sdk.EntityIdHelper.getContractAddressFromMirrorNodeAsync; -import static com.hedera.hashgraph.sdk.EntityIdHelper.getEvmAddressFromMirrorNodeAsync; -import static com.hedera.hashgraph.sdk.EntityIdHelper.performQueryToMirrorNodeAsync; +import static org.hiero.sdk.EntityIdHelper.getContractAddressFromMirrorNodeAsync; +import static org.hiero.sdk.EntityIdHelper.getEvmAddressFromMirrorNodeAsync; +import static org.hiero.sdk.EntityIdHelper.performQueryToMirrorNodeAsync; import com.google.gson.JsonObject; import com.google.gson.JsonParser; @@ -268,38 +249,55 @@ protected String call(Client client) throws ExecutionException, InterruptedExcep private void fillEvmAddresses(Client client) throws ExecutionException, InterruptedException { if (this.contractEvmAddress == null) { Objects.requireNonNull(this.contractId); - this.contractEvmAddress = getContractAddressFromMirrorNodeAsync(client, this.contractId.toString()).get(); + this.contractEvmAddress = getContractAddressFromMirrorNodeAsync(client, this.contractId.toString()) + .get(); } if (this.senderEvmAddress == null && this.sender != null) { - this.senderEvmAddress = getEvmAddressFromMirrorNodeAsync(client, this.sender.num).get().toString(); + this.senderEvmAddress = getEvmAddressFromMirrorNodeAsync(client, this.sender.num) + .get() + .toString(); } } private CompletableFuture getContractCallResultFromMirrorNodeAsync(Client client, String blockNumber) { return executeMirrorNodeRequest(client, blockNumber, false) - .thenApply(MirrorNodeContractQuery::parseContractCallResult); + .thenApply(MirrorNodeContractQuery::parseContractCallResult); } private CompletableFuture getEstimateGasFromMirrorNodeAsync(Client client) { return executeMirrorNodeRequest(client, "latest", true) - .thenApply(MirrorNodeContractQuery::parseHexEstimateToLong); + .thenApply(MirrorNodeContractQuery::parseHexEstimateToLong); } private CompletableFuture executeMirrorNodeRequest(Client client, String blockNumber, boolean estimate) { String apiEndpoint = "/contracts/call"; - String jsonPayload = createJsonPayload(this.callData, this.senderEvmAddress, this.contractEvmAddress, - this.gasLimit, this.gasPrice, this.value, blockNumber, estimate); + String jsonPayload = createJsonPayload( + this.callData, + this.senderEvmAddress, + this.contractEvmAddress, + this.gasLimit, + this.gasPrice, + this.value, + blockNumber, + estimate); return performQueryToMirrorNodeAsync(client, apiEndpoint, jsonPayload, true) - .exceptionally(ex -> { - client.getLogger().error("Error while performing post request to Mirror Node: " + ex.getMessage()); - throw new CompletionException(ex); - }); + .exceptionally(ex -> { + client.getLogger().error("Error while performing post request to Mirror Node: " + ex.getMessage()); + throw new CompletionException(ex); + }); } - static String createJsonPayload(byte[] data, String senderAddress, String contractAddress, long gas, long gasPrice, - long value, String blockNumber, boolean estimate) { + static String createJsonPayload( + byte[] data, + String senderAddress, + String contractAddress, + long gas, + long gasPrice, + long value, + String blockNumber, + boolean estimate) { String hexData = Hex.toHexString(data); JsonObject jsonObject = new JsonObject(); @@ -336,16 +334,15 @@ static long parseHexEstimateToLong(String responseBody) { @Override public String toString() { - return "{" + - "contractId=" + contractId + - ", contractEvmAddress='" + contractEvmAddress + '\'' + - ", sender=" + sender + - ", senderEvmAddress='" + senderEvmAddress + '\'' + - ", callData=" + Arrays.toString(callData) + - ", value=" + value + - ", gasLimit=" + gasLimit + - ", gasPrice=" + gasPrice + - ", blockNumber=" + blockNumber + - '}'; + return "{" + "contractId=" + + contractId + ", contractEvmAddress='" + + contractEvmAddress + '\'' + ", sender=" + + sender + ", senderEvmAddress='" + + senderEvmAddress + '\'' + ", callData=" + + Arrays.toString(callData) + ", value=" + + value + ", gasLimit=" + + gasLimit + ", gasPrice=" + + gasPrice + ", blockNumber=" + + blockNumber + '}'; } } diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/Mnemonic.java b/sdk/src/main/java/org/hiero/sdk/Mnemonic.java similarity index 91% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/Mnemonic.java rename to sdk/src/main/java/org/hiero/sdk/Mnemonic.java index 3a5004a0db..1375f5e98d 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/Mnemonic.java +++ b/sdk/src/main/java/org/hiero/sdk/Mnemonic.java @@ -1,32 +1,9 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; -import com.google.common.base.Joiner; -import com.hedera.hashgraph.sdk.utils.Bip32Utils; -import org.bouncycastle.crypto.digests.SHA256Digest; -import org.bouncycastle.crypto.digests.SHA512Digest; -import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator; -import org.bouncycastle.crypto.params.KeyParameter; +import static java.nio.charset.StandardCharsets.UTF_8; -import javax.annotation.Nullable; +import com.google.common.base.Joiner; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -41,12 +18,16 @@ import java.util.Collections; import java.util.List; import java.util.Objects; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.function.Consumer; import java.util.function.Supplier; - -import static java.nio.charset.StandardCharsets.UTF_8; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.annotation.Nullable; +import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.crypto.digests.SHA512Digest; +import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator; +import org.bouncycastle.crypto.params.KeyParameter; +import org.hiero.sdk.utils.Bip32Utils; /** * BIP-39 24-word mnemonic phrases compatible with the Android and iOS mobile wallets. @@ -56,6 +37,7 @@ public final class Mnemonic { // but the implementation is meant to wait until free space is needed @Nullable private static SoftReference> bip39WordList = null; + @Nullable private static SoftReference> legacyWordList = null; @@ -205,24 +187,17 @@ private static int getWordIndex(CharSequence word, boolean isLegacy) { private static List getWordList(boolean isLegacy) { if (isLegacy) { return getSpecificWordList( - () -> legacyWordList, - () -> readWordList(true), - (newWordList) -> legacyWordList = newWordList - ); + () -> legacyWordList, () -> readWordList(true), (newWordList) -> legacyWordList = newWordList); } else { return getSpecificWordList( - () -> bip39WordList, - () -> readWordList(false), - (newWordList) -> bip39WordList = newWordList - ); + () -> bip39WordList, () -> readWordList(false), (newWordList) -> bip39WordList = newWordList); } } private static synchronized List getSpecificWordList( - Supplier>> getCurrentWordList, - Supplier> getNewWordList, - Consumer>> setCurrentWordList - ) { + Supplier>> getCurrentWordList, + Supplier> getNewWordList, + Consumer>> setCurrentWordList) { var localWordList = getCurrentWordList.get(); if (localWordList == null || localWordList.get() == null) { List words = getNewWordList.get(); @@ -237,7 +212,8 @@ private static synchronized List getSpecificWordList( private static List readWordList(boolean isLegacy) { if (isLegacy) { InputStream wordStream = Mnemonic.class.getClassLoader().getResourceAsStream("legacy-english.txt"); - try (BufferedReader reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(wordStream), UTF_8))) { + try (BufferedReader reader = + new BufferedReader(new InputStreamReader(Objects.requireNonNull(wordStream), UTF_8))) { ArrayList words = new ArrayList<>(4096); for (String word = reader.readLine(); word != null; word = reader.readLine()) { @@ -249,7 +225,8 @@ private static List readWordList(boolean isLegacy) { } } else { InputStream wordStream = Mnemonic.class.getClassLoader().getResourceAsStream("bip39-english.txt"); - try (BufferedReader reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(wordStream), UTF_8))) { + try (BufferedReader reader = + new BufferedReader(new InputStreamReader(Objects.requireNonNull(wordStream), UTF_8))) { ArrayList words = new ArrayList<>(2048); for (String word = reader.readLine(); word != null; word = reader.readLine()) { @@ -410,10 +387,7 @@ byte[] toSeed(String passphrase) { // BIP-39 seed generation PKCS5S2ParametersGenerator pbkdf2 = new PKCS5S2ParametersGenerator(new SHA512Digest()); - pbkdf2.init( - toString().getBytes(UTF_8), - salt.getBytes(UTF_8), - 2048); + pbkdf2.init(toString().getBytes(UTF_8), salt.getBytes(UTF_8), 2048); KeyParameter key = (KeyParameter) pbkdf2.generateDerivedParameters(512); return key.getKey(); @@ -422,8 +396,7 @@ byte[] toSeed(String passphrase) { private byte[] wordsToEntropyAndChecksum() { if (words.size() != 24 && words.size() != 12) { // should be checked in `validate()` - throw new IllegalStateException( - "(BUG) expected 24-word mnemonic, got " + words.size() + " words"); + throw new IllegalStateException("(BUG) expected 24-word mnemonic, got " + words.size() + " words"); } ByteBuffer buffer; if (words.size() == 12) { @@ -473,7 +446,7 @@ private byte[] wordsToLegacyEntropy() throws BadMnemonicException { for (var i = 0; i < data.length - 1; i += 1) { result[i] = data[i] ^ crc; } - //int to byte conversion + // int to byte conversion ByteBuffer byteBuffer = ByteBuffer.allocate(result.length * 4); IntBuffer intBuffer = byteBuffer.asIntBuffer(); intBuffer.put(result); @@ -487,7 +460,7 @@ private byte[] wordsToLegacyEntropy() throws BadMnemonicException { var i = 0; var j = 3; byte[] array2 = new byte[data.length - 1]; - //remove all the fill 0s + // remove all the fill 0s while (j < array.length) { array2[i] = array[j]; i++; @@ -550,7 +523,7 @@ public PrivateKey toStandardEd25519PrivateKey(String passphrase, int index) { var seed = this.toSeed(passphrase); PrivateKey derivedKey = PrivateKey.fromSeedED25519(seed); - for (int i : new int[]{44, 3030, 0, 0, index}) { + for (int i : new int[] {44, 3030, 0, 0, index}) { derivedKey = derivedKey.derive(i); } @@ -569,9 +542,7 @@ public PrivateKey toStandardEd25519PrivateKey(String passphrase, int index) { * e.g. "m/44'/60'/0'/0/0" * @return an array of integers designed to be used with PrivateKey#derive */ - private int[] calculateDerivationPathValues(String derivationPath) - throws IllegalArgumentException - { + private int[] calculateDerivationPathValues(String derivationPath) throws IllegalArgumentException { if (derivationPath == null || derivationPath.isEmpty()) { throw new IllegalArgumentException("Derivation path cannot be null or empty"); } @@ -642,12 +613,8 @@ private PrivateKey toStandardECDSAsecp256k1PrivateKeyImpl(String passphrase, int */ public PrivateKey toStandardECDSAsecp256k1PrivateKey(String passphrase, int index) { // Harden the first 3 indexes - final int[] derivationPathValues = new int[]{ - Bip32Utils.toHardenedIndex(44), - Bip32Utils.toHardenedIndex(3030), - Bip32Utils.toHardenedIndex(0), - 0, - index + final int[] derivationPathValues = new int[] { + Bip32Utils.toHardenedIndex(44), Bip32Utils.toHardenedIndex(3030), Bip32Utils.toHardenedIndex(0), 0, index }; return toStandardECDSAsecp256k1PrivateKeyImpl(passphrase, derivationPathValues); } diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/Network.java b/sdk/src/main/java/org/hiero/sdk/Network.java similarity index 76% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/Network.java rename to sdk/src/main/java/org/hiero/sdk/Network.java index 350dc0a6d9..f807aef58b 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/Network.java +++ b/sdk/src/main/java/org/hiero/sdk/Network.java @@ -1,23 +1,5 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.common.io.ByteStreams; import com.google.protobuf.ByteString; @@ -80,8 +62,8 @@ static Network forNetwork(ExecutorService executor, Map netwo */ static Network forMainnet(ExecutorService executor) { var addressBook = getAddressBookForLedger(LedgerId.MAINNET); - HashMap network = addressBookToNetwork( - Objects.requireNonNull(addressBook).values()); + HashMap network = + addressBookToNetwork(Objects.requireNonNull(addressBook).values()); return new Network(executor, network).setLedgerIdInternal(LedgerId.MAINNET, addressBook); } @@ -93,8 +75,8 @@ static Network forMainnet(ExecutorService executor) { */ static Network forTestnet(ExecutorService executor) { var addressBook = getAddressBookForLedger(LedgerId.TESTNET); - HashMap network = addressBookToNetwork( - Objects.requireNonNull(addressBook).values()); + HashMap network = + addressBookToNetwork(Objects.requireNonNull(addressBook).values()); return new Network(executor, network).setLedgerIdInternal(LedgerId.TESTNET, addressBook); } @@ -106,8 +88,8 @@ static Network forTestnet(ExecutorService executor) { */ static Network forPreviewnet(ExecutorService executor) { var addressBook = getAddressBookForLedger(LedgerId.PREVIEWNET); - HashMap network = addressBookToNetwork( - Objects.requireNonNull(addressBook).values()); + HashMap network = + addressBookToNetwork(Objects.requireNonNull(addressBook).values()); return new Network(executor, network).setLedgerIdInternal(LedgerId.PREVIEWNET, addressBook); } @@ -141,7 +123,8 @@ synchronized Network setLedgerId(@Nullable LedgerId ledgerId) { return setLedgerIdInternal(ledgerId, getAddressBookForLedger(ledgerId)); } - private Network setLedgerIdInternal(@Nullable LedgerId ledgerId, @Nullable Map addressBook) { + private Network setLedgerIdInternal( + @Nullable LedgerId ledgerId, @Nullable Map addressBook) { super.setLedgerId(ledgerId); this.addressBook = addressBook; @@ -154,25 +137,27 @@ private Network setLedgerIdInternal(@Nullable LedgerId ledgerId, @Nullable Map newAddressBook = addressBook.getNodeAddresses().stream() - .filter(nodeAddress -> Objects.nonNull(nodeAddress.getAccountId())) - .collect(Collectors.toMap(NodeAddress::getAccountId, Function.identity(), - /* - * Here we index by AccountId ignoring any subsequent entries with the same AccountId. - * - * Currently, this seems to be needed when reloading predefined address book for testnet which contains - * multiple entries with the same AccountId. - * - * If it becomes necessary to better handle such cases, either the one-to-one mapping from AccountId to - * single NodeAddress should be abandoned or NodeAddresses with the same AccountId may need to be merged. - * */ - (a, b) -> a)); + .filter(nodeAddress -> Objects.nonNull(nodeAddress.getAccountId())) + .collect(Collectors.toMap( + NodeAddress::getAccountId, + Function.identity(), + /* + * Here we index by AccountId ignoring any subsequent entries with the same AccountId. + * + * Currently, this seems to be needed when reloading predefined address book for testnet which contains + * multiple entries with the same AccountId. + * + * If it becomes necessary to better handle such cases, either the one-to-one mapping from AccountId to + * single NodeAddress should be abandoned or NodeAddresses with the same AccountId may need to be merged. + * */ + (a, b) -> a)); /* - * Here we preserve the certificate hash in the case where one is previously defined and no new one is provided. - * - * Currently, this seems to be needed since the downloaded address book lacks the certificate hash. However, - * it is expected the certificate hash will be provided in the future in which case this workaround will no - * longer be necessary. - * */ + * Here we preserve the certificate hash in the case where one is previously defined and no new one is provided. + * + * Currently, this seems to be needed since the downloaded address book lacks the certificate hash. However, + * it is expected the certificate hash will be provided in the future in which case this workaround will no + * longer be necessary. + * */ if (null != this.addressBook) { for (Map.Entry entry : newAddressBook.entrySet()) { NodeAddress previous = this.addressBook.get(entry.getKey()); @@ -192,16 +177,16 @@ void setAddressBook(NodeAddressBook addressBook) { @Nullable private static Map getAddressBookForLedger(@Nullable LedgerId ledgerId) { - return (ledgerId == null || !ledgerId.isKnownNetwork()) ? - null : - readAddressBookResource("addressbook/" + ledgerId + ".pb"); + return (ledgerId == null || !ledgerId.isKnownNetwork()) + ? null + : readAddressBookResource("addressbook/" + ledgerId + ".pb"); } static HashMap addressBookToNetwork(Collection addressBook) { var network = new HashMap(); for (var nodeAddress : addressBook) { for (var endpoint : nodeAddress.addresses) { - network.put(endpoint.toString(), nodeAddress.accountId); + network.put(endpoint.toString(), nodeAddress.accountId); } } return network; @@ -214,7 +199,8 @@ static HashMap addressBookToNetwork(Collection a * @return the list of address book records */ static Map readAddressBookResource(String fileName) { - try (var inputStream = Objects.requireNonNull(Network.class.getResource("/" + fileName)).openStream()) { + try (var inputStream = Objects.requireNonNull(Network.class.getResource("/" + fileName)) + .openStream()) { var contents = ByteStreams.toByteArray(inputStream); var nodeAddressBook = NodeAddressBook.fromBytes(ByteString.copyFrom(contents)); var map = new HashMap(); @@ -248,15 +234,14 @@ synchronized Map getNetwork() { @Override protected Node createNodeFromNetworkEntry(Map.Entry entry) { - return new Node(entry.getValue(), entry.getKey(), executor) - .setVerifyCertificates(verifyCertificates); + return new Node(entry.getValue(), entry.getKey(), executor).setVerifyCertificates(verifyCertificates); } /** * Pick 1/3 of the nodes sorted by health and expected delay from the network. * This is used by Query and Transaction for selecting node AccountId's. * - * @return {@link java.util.List} + * @return {@link java.util.List} */ synchronized List getNodeAccountIdsForExecute() throws InterruptedException { var nodes = getNumberOfMostHealthyNodes(getNumberOfNodesForRequest()); diff --git a/sdk/src/main/java/org/hiero/sdk/NetworkName.java b/sdk/src/main/java/org/hiero/sdk/NetworkName.java new file mode 100644 index 0000000000..84353f3d6a --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/NetworkName.java @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +/** + * Enum for the network names. + */ +@Deprecated +public enum NetworkName { + /** + * The mainnet network + */ + @Deprecated + MAINNET(0), + /** + * The testnet network + */ + @Deprecated + TESTNET(1), + /** + * The previewnet network + */ + @Deprecated + PREVIEWNET(2), + /** + * Other network + */ + @Deprecated + OTHER(Integer.MAX_VALUE); + + final int id; + + NetworkName(int id) { + this.id = id; + } + + /** + * Assign the network name via a string name. + * + * @param networkName the string containing the network name + * @return the ledger id + */ + public static NetworkName fromString(String networkName) { + switch (networkName) { + case "mainnet": + return NetworkName.MAINNET; + case "testnet": + return NetworkName.TESTNET; + case "previewnet": + return NetworkName.PREVIEWNET; + default: + throw new IllegalArgumentException( + "The only supported network names are 'mainnet', 'testnet', and 'previewnet'"); + } + } + + @Override + public String toString() { + switch (this) { + case MAINNET: + return "mainnet"; + case TESTNET: + return "testnet"; + case PREVIEWNET: + return "previewnet"; + default: + throw new IllegalStateException("(BUG) `NetworkName.toString()` switch is non-exhaustive"); + } + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/NetworkVersionInfo.java b/sdk/src/main/java/org/hiero/sdk/NetworkVersionInfo.java new file mode 100644 index 0000000000..24735ea175 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/NetworkVersionInfo.java @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import org.hiero.sdk.proto.NetworkGetVersionInfoResponse; + +/** + * Internal utility class. + */ +public class NetworkVersionInfo { + /** + * Version of the protobuf schema in use by the network + */ + public final SemanticVersion protobufVersion; + + /** + * Version of the Hedera services in use by the network + */ + public final SemanticVersion servicesVersion; + + /** + * Constructor. + * + * @param hapi the protobuf version + * @param hedera the hedera version + */ + NetworkVersionInfo(SemanticVersion hapi, SemanticVersion hedera) { + this.protobufVersion = hapi; + this.servicesVersion = hedera; + } + + /** + * Create a network version info object from a protobuf. + * + * @param proto the protobuf + * @return the new network version object + */ + protected static NetworkVersionInfo fromProtobuf(NetworkGetVersionInfoResponse proto) { + return new NetworkVersionInfo( + SemanticVersion.fromProtobuf(proto.getHapiProtoVersion()), + SemanticVersion.fromProtobuf(proto.getHederaServicesVersion())); + } + + /** + * Create a network version info object from a byte array. + * + * @param bytes the byte array + * @return the new network version object + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + public static NetworkVersionInfo fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + return fromProtobuf(NetworkGetVersionInfoResponse.parseFrom(bytes)); + } + + /** + * Create the protobuf. + * + * @return the protobuf representation + */ + protected NetworkGetVersionInfoResponse toProtobuf() { + return NetworkGetVersionInfoResponse.newBuilder() + .setHapiProtoVersion(protobufVersion.toProtobuf()) + .setHederaServicesVersion(servicesVersion.toProtobuf()) + .build(); + } + + /** + * Create the byte array. + * + * @return the byte array representation + */ + public byte[] toBytes() { + return toProtobuf().toByteArray(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/NetworkVersionInfoQuery.java b/sdk/src/main/java/org/hiero/sdk/NetworkVersionInfoQuery.java new file mode 100644 index 0000000000..84b1899cfc --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/NetworkVersionInfoQuery.java @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.grpc.MethodDescriptor; +import org.hiero.sdk.proto.NetworkGetVersionInfoQuery; +import org.hiero.sdk.proto.NetworkServiceGrpc; +import org.hiero.sdk.proto.QueryHeader; +import org.hiero.sdk.proto.Response; +import org.hiero.sdk.proto.ResponseHeader; + +/** + * Information about the versions of protobuf and hedera. + */ +public class NetworkVersionInfoQuery extends Query { + /** + * Constructor. + */ + public NetworkVersionInfoQuery() {} + + @Override + void onMakeRequest(org.hiero.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { + queryBuilder.setNetworkGetVersionInfo( + NetworkGetVersionInfoQuery.newBuilder().setHeader(header)); + } + + @Override + ResponseHeader mapResponseHeader(Response response) { + return response.getNetworkGetVersionInfo().getHeader(); + } + + @Override + QueryHeader mapRequestHeader(org.hiero.sdk.proto.Query request) { + return request.getNetworkGetVersionInfo().getHeader(); + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + // do nothing + } + + @Override + NetworkVersionInfo mapResponse(Response response, AccountId nodeId, org.hiero.sdk.proto.Query request) { + return NetworkVersionInfo.fromProtobuf(response.getNetworkGetVersionInfo()); + } + + @Override + MethodDescriptor getMethodDescriptor() { + return NetworkServiceGrpc.getGetVersionInfoMethod(); + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/NftId.java b/sdk/src/main/java/org/hiero/sdk/NftId.java similarity index 80% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/NftId.java rename to sdk/src/main/java/org/hiero/sdk/NftId.java index 33e0ca25b0..f0043d4e94 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/NftId.java +++ b/sdk/src/main/java/org/hiero/sdk/NftId.java @@ -1,30 +1,10 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.NftID; - -import javax.annotation.Nonnegative; -import javax.annotation.Nullable; import java.util.Objects; +import javax.annotation.Nonnegative; +import org.hiero.sdk.proto.NftID; /** * The (non-fungible) token of which this NFT is an instance @@ -97,9 +77,9 @@ public static NftId fromBytes(byte[] bytes) throws InvalidProtocolBufferExceptio */ NftID toProtobuf() { return NftID.newBuilder() - .setTokenID(tokenId.toProtobuf()) - .setSerialNumber(serial) - .build(); + .setTokenID(tokenId.toProtobuf()) + .setSerialNumber(serial) + .build(); } /** @@ -132,7 +112,7 @@ public int hashCode() { } @Override - public boolean equals( Object o) { + public boolean equals(Object o) { if (this == o) { return true; } diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/Node.java b/sdk/src/main/java/org/hiero/sdk/Node.java similarity index 81% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/Node.java rename to sdk/src/main/java/org/hiero/sdk/Node.java index 14406e94fa..779f329ae7 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/Node.java +++ b/sdk/src/main/java/org/hiero/sdk/Node.java @@ -1,29 +1,10 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import io.grpc.ChannelCredentials; import io.grpc.TlsChannelCredentials; - -import javax.annotation.Nullable; import java.util.concurrent.ExecutorService; +import javax.annotation.Nullable; /** * Internal utility class. @@ -150,8 +131,9 @@ Node setVerifyCertificates(boolean verifyCertificates) { @Override ChannelCredentials getChannelCredentials() { return TlsChannelCredentials.newBuilder() - .trustManager(new HederaTrustManager(addressBookEntry == null ? null : addressBookEntry.certHash, verifyCertificates)) - .build(); + .trustManager(new HederaTrustManager( + addressBookEntry == null ? null : addressBookEntry.certHash, verifyCertificates)) + .build(); } @Override diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/NodeAddress.java b/sdk/src/main/java/org/hiero/sdk/NodeAddress.java similarity index 77% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/NodeAddress.java rename to sdk/src/main/java/org/hiero/sdk/NodeAddress.java index 350455a03f..e996d1ba73 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/NodeAddress.java +++ b/sdk/src/main/java/org/hiero/sdk/NodeAddress.java @@ -1,33 +1,14 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.common.base.MoreObjects; import com.google.protobuf.ByteString; -import com.hedera.hashgraph.sdk.proto.ServiceEndpoint; - -import javax.annotation.Nullable; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.ServiceEndpoint; /** * The metadata for a Node – including IP Address, and the crypto account associated with the Node. @@ -71,8 +52,7 @@ public class NodeAddress implements Cloneable { /** * Constructor. */ - NodeAddress() { - } + NodeAddress() {} /** * Create a node from a protobuf. @@ -80,16 +60,14 @@ public class NodeAddress implements Cloneable { * @param nodeAddress the protobuf * @return the new node */ - static NodeAddress fromProtobuf(com.hedera.hashgraph.sdk.proto.NodeAddress nodeAddress) { + static NodeAddress fromProtobuf(org.hiero.sdk.proto.NodeAddress nodeAddress) { var address = new ArrayList(nodeAddress.getServiceEndpointCount()); if (!nodeAddress.getIpAddress().isEmpty()) { - address.add( - Endpoint.fromProtobuf(ServiceEndpoint.newBuilder() + address.add(Endpoint.fromProtobuf(ServiceEndpoint.newBuilder() .setIpAddressV4(nodeAddress.getIpAddress()) .setPort(nodeAddress.getPortno()) - .build()) - ); + .build())); } for (var endpoint : nodeAddress.getServiceEndpointList()) { @@ -97,12 +75,12 @@ static NodeAddress fromProtobuf(com.hedera.hashgraph.sdk.proto.NodeAddress nodeA } var node = new NodeAddress() - .setPublicKey(nodeAddress.getRSAPubKey()) - .setNodeId(nodeAddress.getNodeId()) - .setCertHash(nodeAddress.getNodeCertHash()) - .setAddresses(address) - .setDescription(nodeAddress.getDescription()) - .setStake(nodeAddress.getStake()); + .setPublicKey(nodeAddress.getRSAPubKey()) + .setNodeId(nodeAddress.getNodeId()) + .setCertHash(nodeAddress.getNodeCertHash()) + .setAddresses(address) + .setDescription(nodeAddress.getDescription()) + .setStake(nodeAddress.getStake()); if (nodeAddress.hasNodeAccountId()) { node.setAccountId(AccountId.fromProtobuf(nodeAddress.getNodeAccountId())); @@ -124,7 +102,7 @@ public String getPublicKey() { /** * Assign the public key. * - * @param publicKey the public key + * @param publicKey the public key * @return {@code this} */ public NodeAddress setPublicKey(String publicKey) { @@ -156,7 +134,7 @@ public NodeAddress setAccountId(AccountId accountId) { /** * Extract the node id. * - * @return the node id + * @return the node id */ public long getNodeId() { return nodeId; @@ -269,9 +247,8 @@ public NodeAddress setStake(long stake) { * * @return the protobuf representation. */ - com.hedera.hashgraph.sdk.proto.NodeAddress toProtobuf() { - var builder = com.hedera.hashgraph.sdk.proto.NodeAddress.newBuilder() - .setNodeId(nodeId); + org.hiero.sdk.proto.NodeAddress toProtobuf() { + var builder = org.hiero.sdk.proto.NodeAddress.newBuilder().setNodeId(nodeId); if (certHash != null) { builder.setNodeCertHash(certHash); @@ -299,14 +276,14 @@ com.hedera.hashgraph.sdk.proto.NodeAddress toProtobuf() { @Override public String toString() { return MoreObjects.toStringHelper(this) - .add("publicKey", publicKey) - .add("accountId", accountId) - .add("nodeId", nodeId) - .add("certHash", certHash != null ? new String(certHash.toByteArray(), StandardCharsets.UTF_8) : null) - .add("addresses", addresses) - .add("description", description) - .add("stake", stake) - .toString(); + .add("publicKey", publicKey) + .add("accountId", accountId) + .add("nodeId", nodeId) + .add("certHash", certHash != null ? new String(certHash.toByteArray(), StandardCharsets.UTF_8) : null) + .add("addresses", addresses) + .add("description", description) + .add("stake", stake) + .toString(); } @Override diff --git a/sdk/src/main/java/org/hiero/sdk/NodeAddressBook.java b/sdk/src/main/java/org/hiero/sdk/NodeAddressBook.java new file mode 100644 index 0000000000..c481173a0d --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/NodeAddressBook.java @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * A list of nodes and their metadata. + * + * See Hedera Documentation + */ +public class NodeAddressBook { + List nodeAddresses = Collections.emptyList(); + + /** + * Constructor. + */ + NodeAddressBook() {} + + /** + * Extract the of node addresses. + * + * @return list of node addresses + */ + public List getNodeAddresses() { + return cloneNodeAddresses(nodeAddresses); + } + + /** + * Assign the list of node addresses. + * + * @param nodeAddresses list of node addresses + * @return {@code this} + */ + public NodeAddressBook setNodeAddresses(List nodeAddresses) { + this.nodeAddresses = cloneNodeAddresses(nodeAddresses); + return this; + } + + static List cloneNodeAddresses(List addresses) { + List cloneAddresses = new ArrayList<>(addresses.size()); + for (var address : addresses) { + cloneAddresses.add(address.clone()); + } + return cloneAddresses; + } + + /** + * Create a node address book from a protobuf. + * + * @param book the protobuf + * @return the new node address book + */ + static NodeAddressBook fromProtobuf(org.hiero.sdk.proto.NodeAddressBook book) { + var addresses = new ArrayList(book.getNodeAddressCount()); + + for (var address : book.getNodeAddressList()) { + addresses.add(NodeAddress.fromProtobuf(address)); + } + + return new NodeAddressBook().setNodeAddresses(addresses); + } + + /** + * Create a node address book from a byte string. + * + * @param bytes the byte string + * @return the new node address book + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + public static NodeAddressBook fromBytes(ByteString bytes) throws InvalidProtocolBufferException { + return fromProtobuf(org.hiero.sdk.proto.NodeAddressBook.parseFrom(bytes)); + } + + /** + * Create the protobuf. + * + * @return the protobuf representation + */ + org.hiero.sdk.proto.NodeAddressBook toProtobuf() { + var builder = org.hiero.sdk.proto.NodeAddressBook.newBuilder(); + + for (var nodeAdress : nodeAddresses) { + builder.addNodeAddress(nodeAdress.toProtobuf()); + } + + return builder.build(); + } + + /** + * Create the byte string. + * + * @return the byte string representation + */ + public ByteString toBytes() { + return toProtobuf().toByteString(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("nodeAddresses", nodeAddresses) + .toString(); + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/NodeCreateTransaction.java b/sdk/src/main/java/org/hiero/sdk/NodeCreateTransaction.java similarity index 89% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/NodeCreateTransaction.java rename to sdk/src/main/java/org/hiero/sdk/NodeCreateTransaction.java index 5779d3fe0f..6cfb030759 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/NodeCreateTransaction.java +++ b/sdk/src/main/java/org/hiero/sdk/NodeCreateTransaction.java @@ -1,38 +1,19 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.AddressBookServiceGrpc; -import com.hedera.hashgraph.sdk.proto.NodeCreateTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; import io.grpc.MethodDescriptor; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Objects; import javax.annotation.Nullable; -import org.bouncycastle.util.Arrays; +import org.hiero.sdk.proto.AddressBookServiceGrpc; +import org.hiero.sdk.proto.NodeCreateTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; /** * A transaction to create a new node in the network address book. @@ -163,9 +144,8 @@ public NodeCreateTransaction() {} * @param txs Compound list of transaction id's list of (AccountId, Transaction) records * @throws InvalidProtocolBufferException when there is an issue with the protobuf */ - NodeCreateTransaction( - LinkedHashMap> txs) - throws InvalidProtocolBufferException { + NodeCreateTransaction(LinkedHashMap> txs) + throws InvalidProtocolBufferException { super(txs); initFromTransactionBody(); } @@ -175,7 +155,7 @@ public NodeCreateTransaction() {} * * @param txBody protobuf TransactionBody */ - NodeCreateTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { + NodeCreateTransaction(org.hiero.sdk.proto.TransactionBody txBody) { super(txBody); initFromTransactionBody(); } @@ -347,7 +327,7 @@ public NodeCreateTransaction setAdminKey(Key adminKey) { /** * Build the transaction body. * - * @return {@link com.hedera.hashgraph.sdk.proto.NodeCreateTransactionBody} + * @return {@link org.hiero.sdk.proto.NodeCreateTransactionBody} */ NodeCreateTransactionBody.Builder build() { var builder = NodeCreateTransactionBody.newBuilder(); @@ -420,7 +400,7 @@ void validateChecksums(Client client) throws BadEntityIdException { } @Override - MethodDescriptor getMethodDescriptor() { + MethodDescriptor getMethodDescriptor() { return AddressBookServiceGrpc.getCreateNodeMethod(); } diff --git a/sdk/src/main/java/org/hiero/sdk/NodeDeleteTransaction.java b/sdk/src/main/java/org/hiero/sdk/NodeDeleteTransaction.java new file mode 100644 index 0000000000..23b9e48303 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/NodeDeleteTransaction.java @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.grpc.MethodDescriptor; +import java.util.LinkedHashMap; +import org.hiero.sdk.proto.AddressBookServiceGrpc; +import org.hiero.sdk.proto.NodeDeleteTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; + +/** + * A transaction to delete a node from the network address book. + * + * This transaction body SHALL be considered a "privileged transaction". + * + * - A transaction MUST be signed by the governing council. + * - Upon success, the address book entry SHALL enter a "pending delete" + * state. + * - All address book entries pending deletion SHALL be removed from the + * active network configuration during the next `freeze` transaction with + * the field `freeze_type` set to `PREPARE_UPGRADE`.
+ * - A deleted address book node SHALL be removed entirely from network state. + * - A deleted address book node identifier SHALL NOT be reused. + * + * ### Record Stream Effects + * Upon completion the "deleted" `node_id` SHALL be in the transaction + * receipt. + */ +public class NodeDeleteTransaction extends Transaction { + + /** + * A consensus node identifier in the network state. + *

+ * The node identified MUST exist in the network address book.
+ * The node identified MUST NOT be deleted.
+ * This value is REQUIRED. + */ + private long nodeId = 0; + + /** + * Constructor. + */ + public NodeDeleteTransaction() {} + + /** + * Constructor. + * + * @param txs Compound list of transaction id's list of (AccountId, Transaction) records + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + NodeDeleteTransaction(LinkedHashMap> txs) + throws InvalidProtocolBufferException { + super(txs); + initFromTransactionBody(); + } + + /** + * Constructor. + * + * @param txBody protobuf TransactionBody + */ + NodeDeleteTransaction(TransactionBody txBody) { + super(txBody); + initFromTransactionBody(); + } + + /** + * Extract the consensus node identifier in the network state. + * @return the consensus node identifier in the network state. + */ + public long getNodeId() { + return nodeId; + } + + /** + * Assign the consensus node identifier in the network state. + * @param nodeId the consensus node identifier in the network state. + * @return {@code this} + */ + public NodeDeleteTransaction setNodeId(long nodeId) { + requireNotFrozen(); + this.nodeId = nodeId; + return this; + } + + /** + * Build the transaction body. + * + * @return {@link org.hiero.sdk.proto.NodeDeleteTransactionBody} + */ + NodeDeleteTransactionBody.Builder build() { + var builder = NodeDeleteTransactionBody.newBuilder(); + builder.setNodeId(nodeId); + return builder; + } + + /** + * Initialize from the transaction body. + */ + void initFromTransactionBody() { + var body = sourceTransactionBody.getNodeDelete(); + nodeId = body.getNodeId(); + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + // no-op + } + + @Override + MethodDescriptor getMethodDescriptor() { + return AddressBookServiceGrpc.getDeleteNodeMethod(); + } + + @Override + void onFreeze(TransactionBody.Builder bodyBuilder) { + bodyBuilder.setNodeDelete(build()); + } + + @Override + void onScheduled(SchedulableTransactionBody.Builder scheduled) { + scheduled.setNodeDelete(build()); + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/NodeUpdateTransaction.java b/sdk/src/main/java/org/hiero/sdk/NodeUpdateTransaction.java similarity index 91% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/NodeUpdateTransaction.java rename to sdk/src/main/java/org/hiero/sdk/NodeUpdateTransaction.java index de5a315758..08f1dc6a94 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/NodeUpdateTransaction.java +++ b/sdk/src/main/java/org/hiero/sdk/NodeUpdateTransaction.java @@ -1,40 +1,21 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.ByteString; import com.google.protobuf.BytesValue; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.StringValue; -import com.hedera.hashgraph.sdk.proto.AddressBookServiceGrpc; -import com.hedera.hashgraph.sdk.proto.NodeUpdateTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; import io.grpc.MethodDescriptor; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Objects; import javax.annotation.Nullable; -import org.bouncycastle.util.Arrays; +import org.hiero.sdk.proto.AddressBookServiceGrpc; +import org.hiero.sdk.proto.NodeUpdateTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; /** * A transaction to modify address book node attributes. @@ -190,9 +171,8 @@ public NodeUpdateTransaction() {} * @param txs Compound list of transaction id's list of (AccountId, Transaction) records * @throws InvalidProtocolBufferException when there is an issue with the protobuf */ - NodeUpdateTransaction( - LinkedHashMap> txs) - throws InvalidProtocolBufferException { + NodeUpdateTransaction(LinkedHashMap> txs) + throws InvalidProtocolBufferException { super(txs); initFromTransactionBody(); } @@ -202,7 +182,7 @@ public NodeUpdateTransaction() {} * * @param txBody protobuf TransactionBody */ - NodeUpdateTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { + NodeUpdateTransaction(org.hiero.sdk.proto.TransactionBody txBody) { super(txBody); initFromTransactionBody(); } @@ -404,7 +384,7 @@ public NodeUpdateTransaction setAdminKey(Key adminKey) { /** * Build the transaction body. * - * @return {@link com.hedera.hashgraph.sdk.proto.NodeUpdateTransactionBody} + * @return {@link org.hiero.sdk.proto.NodeUpdateTransactionBody} */ NodeUpdateTransactionBody.Builder build() { var builder = NodeUpdateTransactionBody.newBuilder(); @@ -487,7 +467,7 @@ void validateChecksums(Client client) throws BadEntityIdException { } @Override - MethodDescriptor getMethodDescriptor() { + MethodDescriptor getMethodDescriptor() { return AddressBookServiceGrpc.getUpdateNodeMethod(); } diff --git a/sdk/src/main/java/org/hiero/sdk/Pem.java b/sdk/src/main/java/org/hiero/sdk/Pem.java new file mode 100644 index 0000000000..7dd81d74e7 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/Pem.java @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import javax.annotation.Nullable; +import javax.crypto.Cipher; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; +import org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo; +import org.bouncycastle.asn1.pkcs.EncryptionScheme; +import org.bouncycastle.asn1.pkcs.KeyDerivationFunc; +import org.bouncycastle.asn1.pkcs.PBES2Parameters; +import org.bouncycastle.asn1.pkcs.PBKDF2Params; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openssl.PEMEncryptedKeyPair; +import org.bouncycastle.openssl.PEMKeyPair; +import org.bouncycastle.openssl.PEMParser; +import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder; +import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder; +import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo; +import org.bouncycastle.pkcs.PKCSException; +import org.bouncycastle.util.io.pem.PemObject; +import org.bouncycastle.util.io.pem.PemWriter; + +/** + * Internal utility class for handling PEM objects. + *
+ * Privacy-Enhanced Mail (PEM) is a de facto file format for storing and + * sending cryptographic keys, certificates, and other data, based on a set of + * 1993 IETF standards defining "privacy-enhanced mail." + */ +final class Pem { + private static final String TYPE_PRIVATE_KEY = "PRIVATE KEY"; + private static final String TYPE_ENCRYPTED_PRIVATE_KEY = "ENCRYPTED PRIVATE KEY"; + + /** + * Constructor. + */ + private Pem() {} + + /** + * For some reason, this generates PEM encodings that we ourselves can import, but OpenSSL + * doesn't like. We decided to punt on generating encrypted PEMs for now but saving + * the code for when we get back to it and/or any demand arises. + */ + @SuppressWarnings("unused") + static void writeEncryptedPrivateKey(PrivateKeyInfo pkInfo, Writer out, String passphrase) throws IOException { + byte[] salt = Crypto.randomBytes(Crypto.SALT_LEN); + + KeyParameter derivedKey = Crypto.deriveKeySha256(passphrase, salt, Crypto.ITERATIONS, Crypto.CBC_DK_LEN); + + byte[] iv = Crypto.randomBytes(Crypto.IV_LEN); + + Cipher cipher = Crypto.initAesCbc128Encrypt(derivedKey, iv); + + byte[] encryptedKey = Crypto.runCipher(cipher, pkInfo.getEncoded()); + + // I wanted to just do this with BC's PKCS8Generator and KcePKCSPBEOutputEncryptorBuilder + // but it tries to init AES instance of `Cipher` with a `PBKDF2Key` and the former complains + + // So this is basically a reimplementation of that minus the excess OO + PBES2Parameters parameters = new PBES2Parameters( + new KeyDerivationFunc( + PKCSObjectIdentifiers.id_PBKDF2, + new PBKDF2Params( + salt, + Crypto.ITERATIONS, + Crypto.CBC_DK_LEN, + new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA256))), + new EncryptionScheme( + NISTObjectIdentifiers.id_aes128_CBC, + ASN1Primitive.fromByteArray(cipher.getParameters().getEncoded()))); + + EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo( + new AlgorithmIdentifier(PKCSObjectIdentifiers.id_PBES2, parameters), encryptedKey); + + PemWriter writer = new PemWriter(out); + writer.writeObject(new PemObject(TYPE_ENCRYPTED_PRIVATE_KEY, encryptedPrivateKeyInfo.getEncoded())); + writer.flush(); + } + + /** + * Create a private key info object from a reader. + * + * @param input reader object + * @param passphrase passphrase + * @return private key info object + * @throws IOException if IO operations fail + */ + static PrivateKeyInfo readPrivateKey(Reader input, @Nullable String passphrase) throws IOException { + try (final var parser = new PEMParser(input)) { + Object parsedObject = parser.readObject(); + var password = (passphrase != null) ? passphrase.toCharArray() : "".toCharArray(); + if (parsedObject == null) { + throw new BadKeyException("PEM file did not contain a private key"); + } else if (parsedObject instanceof PKCS8EncryptedPrivateKeyInfo) { + var decryptProvider = new JceOpenSSLPKCS8DecryptorProviderBuilder() + .setProvider(new BouncyCastleProvider()) + .build(password); + var encryptedPrivateKeyInfo = (PKCS8EncryptedPrivateKeyInfo) parsedObject; + return encryptedPrivateKeyInfo.decryptPrivateKeyInfo(decryptProvider); + } else if (parsedObject instanceof PrivateKeyInfo) { + return (PrivateKeyInfo) parsedObject; + } else if (parsedObject instanceof PEMEncryptedKeyPair) { + var decryptProvider = new JcePEMDecryptorProviderBuilder() + .setProvider(new BouncyCastleProvider()) + .build(password); + var encryptedKeyPair = (PEMEncryptedKeyPair) parsedObject; + return encryptedKeyPair.decryptKeyPair(decryptProvider).getPrivateKeyInfo(); + } else if (parsedObject instanceof PEMKeyPair) { + var keyPair = (PEMKeyPair) parsedObject; + return keyPair.getPrivateKeyInfo(); + } else { + throw new BadKeyException("PEM file contained something the SDK didn't know what to do with: " + + parsedObject.getClass().getName()); + } + } catch (PKCSException e) { + if (e.getMessage().contains("password empty")) { + throw new BadKeyException("PEM file contained an encrypted private key but no passphrase was given"); + } + throw new RuntimeException(e); + } catch (OperatorCreationException e) { + throw new RuntimeException(e); + } + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/PendingAirdropId.java b/sdk/src/main/java/org/hiero/sdk/PendingAirdropId.java new file mode 100644 index 0000000000..735b84170b --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/PendingAirdropId.java @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * A unique, composite, identifier for a pending airdrop. + * + * Each pending airdrop SHALL be uniquely identified by a PendingAirdropId. + * A PendingAirdropId SHALL be recorded when created and MUST be provided in any transaction + * that would modify that pending airdrop (such as a `claimAirdrop` or `cancelAirdrop`). + */ +public class PendingAirdropId { + private AccountId sender; + private AccountId receiver; + + @Nullable + private TokenId tokenId; + + @Nullable + private NftId nftId; + + public PendingAirdropId() {} + + PendingAirdropId(AccountId sender, AccountId receiver, TokenId tokenId) { + this.sender = sender; + this.receiver = receiver; + this.tokenId = tokenId; + this.nftId = null; + } + + PendingAirdropId(AccountId sender, AccountId receiver, NftId nftId) { + this.sender = sender; + this.receiver = receiver; + this.nftId = nftId; + this.tokenId = null; + } + + public AccountId getSender() { + return sender; + } + + public PendingAirdropId setSender(@Nonnull AccountId sender) { + this.sender = sender; + return this; + } + + public AccountId getReceiver() { + return receiver; + } + + public PendingAirdropId setReceiver(@Nonnull AccountId receiver) { + this.receiver = receiver; + return this; + } + + public TokenId getTokenId() { + return tokenId; + } + + public PendingAirdropId setTokenId(@Nullable TokenId tokenId) { + this.tokenId = tokenId; + return this; + } + + public NftId getNftId() { + return nftId; + } + + public PendingAirdropId setNftId(@Nullable NftId nftId) { + this.nftId = nftId; + return this; + } + + static PendingAirdropId fromProtobuf(org.hiero.sdk.proto.PendingAirdropId pendingAirdropId) { + if (pendingAirdropId.hasFungibleTokenType()) { + return new PendingAirdropId( + AccountId.fromProtobuf(pendingAirdropId.getSenderId()), + AccountId.fromProtobuf(pendingAirdropId.getReceiverId()), + TokenId.fromProtobuf(pendingAirdropId.getFungibleTokenType())); + } else { + return new PendingAirdropId( + AccountId.fromProtobuf(pendingAirdropId.getSenderId()), + AccountId.fromProtobuf(pendingAirdropId.getReceiverId()), + NftId.fromProtobuf(pendingAirdropId.getNonFungibleToken())); + } + } + + org.hiero.sdk.proto.PendingAirdropId toProtobuf() { + var builder = org.hiero.sdk.proto.PendingAirdropId.newBuilder() + .setSenderId(sender.toProtobuf()) + .setReceiverId(receiver.toProtobuf()); + + if (tokenId != null) { + builder.setFungibleTokenType(tokenId.toProtobuf()); + } else if (nftId != null) { + builder.setNonFungibleToken(nftId.toProtobuf()); + } + return builder.build(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("sender", sender) + .add("receiver", receiver) + .add("tokenId", tokenId) + .add("nftId", nftId) + .toString(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/PendingAirdropLogic.java b/sdk/src/main/java/org/hiero/sdk/PendingAirdropLogic.java new file mode 100644 index 0000000000..48bfd80de0 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/PendingAirdropLogic.java @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Objects; + +abstract class PendingAirdropLogic> extends Transaction { + + protected List pendingAirdropIds = new ArrayList<>(); + + protected PendingAirdropLogic() {} + + /** + * Constructor. + * + * @param txs Compound list of transaction id's list of (AccountId, Transaction) records + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + PendingAirdropLogic(LinkedHashMap> txs) + throws InvalidProtocolBufferException { + super(txs); + } + + /** + * Constructor. + * + * @param txBody protobuf TransactionBody + */ + PendingAirdropLogic(org.hiero.sdk.proto.TransactionBody txBody) { + super(txBody); + } + + /** + * Extract the pending airdrop ids + * + * @return the pending airdrop ids + */ + public List getPendingAirdropIds() { + return this.pendingAirdropIds; + } + + /** + * Set the pending airdrop ids + * + * @param pendingAirdropIds + * @return {@code this} + */ + public T setPendingAirdropIds(List pendingAirdropIds) { + Objects.requireNonNull(pendingAirdropIds); + requireNotFrozen(); + this.pendingAirdropIds = pendingAirdropIds; + // noinspection unchecked + return (T) this; + } + + /** + * clear the pending airdrop ids + * + * @return {@code this} + */ + public T clearPendingAirdropIds() { + requireNotFrozen(); + this.pendingAirdropIds = new ArrayList<>(); + // noinspection unchecked + return (T) this; + } + + /** + * Add pendingAirdropId + * + * @param pendingAirdropId + * @return {@code this} + */ + public T addPendingAirdrop(PendingAirdropId pendingAirdropId) { + Objects.requireNonNull(pendingAirdropId); + requireNotFrozen(); + this.pendingAirdropIds.add(pendingAirdropId); + // noinspection unchecked + return (T) this; + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + for (var pendingAirdropId : pendingAirdropIds) { + if (pendingAirdropId.getTokenId() != null) { + pendingAirdropId.getTokenId().validateChecksum(client); + } + + if (pendingAirdropId.getReceiver() != null) { + pendingAirdropId.getReceiver().validateChecksum(client); + } + + if (pendingAirdropId.getSender() != null) { + pendingAirdropId.getSender().validateChecksum(client); + } + + if (pendingAirdropId.getNftId() != null) { + pendingAirdropId.getNftId().tokenId.validateChecksum(client); + } + } + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/PendingAirdropRecord.java b/sdk/src/main/java/org/hiero/sdk/PendingAirdropRecord.java new file mode 100644 index 0000000000..c356c5eece --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/PendingAirdropRecord.java @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import org.hiero.sdk.proto.PendingAirdropValue; + +public class PendingAirdropRecord { + private final PendingAirdropId pendingAirdropId; + private final long pendingAirdropAmount; + + PendingAirdropRecord(PendingAirdropId pendingAirdropId, long pendingAirdropAmount) { + this.pendingAirdropId = pendingAirdropId; + this.pendingAirdropAmount = pendingAirdropAmount; + } + + public PendingAirdropId getPendingAirdropId() { + return pendingAirdropId; + } + + public long getPendingAirdropAmount() { + return pendingAirdropAmount; + } + + org.hiero.sdk.proto.PendingAirdropRecord toProtobuf() { + return org.hiero.sdk.proto.PendingAirdropRecord.newBuilder() + .setPendingAirdropId(this.pendingAirdropId.toProtobuf()) + .setPendingAirdropValue(PendingAirdropValue.newBuilder().setAmount(pendingAirdropAmount)) + .build(); + } + + static PendingAirdropRecord fromProtobuf(org.hiero.sdk.proto.PendingAirdropRecord pendingAirdropRecord) { + return new PendingAirdropRecord( + PendingAirdropId.fromProtobuf(pendingAirdropRecord.getPendingAirdropId()), + pendingAirdropRecord.getPendingAirdropValue().getAmount()); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("pendingAirdropId", pendingAirdropId) + .add("pendingAirdropAmount", pendingAirdropAmount) + .toString(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/PrecheckStatusException.java b/sdk/src/main/java/org/hiero/sdk/PrecheckStatusException.java new file mode 100644 index 0000000000..7984da2a88 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/PrecheckStatusException.java @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import javax.annotation.Nullable; + +/** + * Signals that a transaction has failed the pre-check. + *

+ * Before a node submits a transaction to the rest of the network, + * it attempts some cheap assertions. This process is called the "pre-check". + */ +public class PrecheckStatusException extends Exception { + /** + * The status of the failing transaction + */ + public final Status status; + + /** + * The ID of the transaction that failed. + *

+ * This can be `null` if a query fails pre-check without an + * associated payment transaction. + */ + @Nullable + public final TransactionId transactionId; + + /** + * Constructor. + * + * @param status the status + * @param transactionId the transaction id + */ + PrecheckStatusException(Status status, @Nullable TransactionId transactionId) { + this.status = status; + this.transactionId = transactionId; + } + + @Override + public String getMessage() { + var stringBuilder = new StringBuilder(); + + if (transactionId != null) { + stringBuilder.append("Hedera transaction `").append(transactionId).append("` "); + } + + stringBuilder + .append("failed pre-check with the status `") + .append(status) + .append("`"); + + return stringBuilder.toString(); + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/PrivateKey.java b/sdk/src/main/java/org/hiero/sdk/PrivateKey.java similarity index 93% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/PrivateKey.java rename to sdk/src/main/java/org/hiero/sdk/PrivateKey.java index 7152727c35..11cdf12bde 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/PrivateKey.java +++ b/sdk/src/main/java/org/hiero/sdk/PrivateKey.java @@ -1,24 +1,12 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.util.Arrays; +import javax.annotation.Nonnegative; +import javax.annotation.Nullable; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.asn1.sec.ECPrivateKey; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; @@ -26,13 +14,6 @@ import org.bouncycastle.math.ec.rfc8032.Ed25519; import org.bouncycastle.util.encoders.Hex; -import javax.annotation.Nonnegative; -import javax.annotation.Nullable; -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; -import java.util.Arrays; - /** * A private key on the Hedera™ network. */ @@ -116,7 +97,7 @@ public static PrivateKey fromMnemonic(Mnemonic mnemonic, String passphrase) { // we pre-derive most of the path as the mobile wallets don't expose more than the index // https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki // https://github.com/satoshilabs/slips/blob/master/slip-0044.md - for (int index : new int[]{44, 3030, 0, 0}) { + for (int index : new int[] {44, 3030, 0, 0}) { derivedKey = derivedKey.derive(index); } @@ -187,7 +168,7 @@ public static PrivateKey fromStringECDSA(String privateKey) { */ public static PrivateKey fromBytes(byte[] privateKey) { if ((privateKey.length == Ed25519.SECRET_KEY_SIZE) - || (privateKey.length == Ed25519.SECRET_KEY_SIZE + Ed25519.PUBLIC_KEY_SIZE)) { + || (privateKey.length == Ed25519.SECRET_KEY_SIZE + Ed25519.PUBLIC_KEY_SIZE)) { // If this is a 32 or 64 byte string, assume an Ed25519 private key return new PrivateKeyED25519(Arrays.copyOfRange(privateKey, 0, Ed25519.SECRET_KEY_SIZE), null); } @@ -251,7 +232,7 @@ private static PrivateKey fromPrivateKeyInfo(PrivateKeyInfo privateKeyInfo) { * This will read the first "PRIVATE KEY" section in the stream as an Ed25519 private key. * * @param pemFile The Reader containing the pem file - * @return {@link com.hedera.hashgraph.sdk.PrivateKey} + * @return {@link org.hiero.sdk.PrivateKey} * @throws IOException if one occurred while reading. * @throws BadKeyException if no "PRIVATE KEY" section was found or the key was not an Ed25519 * private key. @@ -281,7 +262,7 @@ public static PrivateKey readPem(Reader pemFile) throws IOException { * * @param pemFile the PEM encoded file * @param password the password to decrypt the PEM file; if null or empty, no decryption is performed. - * @return {@link com.hedera.hashgraph.sdk.PrivateKey} + * @return {@link org.hiero.sdk.PrivateKey} * @throws IOException if one occurred while reading the PEM file * @throws BadKeyException if no "ENCRYPTED PRIVATE KEY" or "PRIVATE KEY" section was found, * if the passphrase is wrong or the key was not an Ed25519 private key. @@ -294,7 +275,7 @@ public static PrivateKey readPem(Reader pemFile, @Nullable String password) thro * Parse a private key from a PEM encoded string. * * @param pemEncoded The String containing the pem - * @return {@link com.hedera.hashgraph.sdk.PrivateKey} + * @return {@link org.hiero.sdk.PrivateKey} * @throws IOException if the PEM string was improperly encoded * @throws BadKeyException if no "PRIVATE KEY" section was found or the key was not an Ed25519 * private key. @@ -311,7 +292,7 @@ public static PrivateKey fromPem(String pemEncoded) throws IOException { * * @param encodedPem the encoded PEM string * @param password the password to decrypt the PEM file; if null or empty, no decryption is performed. - * @return {@link com.hedera.hashgraph.sdk.PrivateKey} + * @return {@link org.hiero.sdk.PrivateKey} * @throws IOException if the PEM string was improperly encoded * @throws BadKeyException if no "ENCRYPTED PRIVATE KEY" or "PRIVATE KEY" section was found, * if the passphrase is wrong or the key was not an Ed25519 private key. @@ -451,7 +432,7 @@ public AccountId toAccountId(@Nonnegative long shard, @Nonnegative long realm) { } @Override - com.hedera.hashgraph.sdk.proto.Key toProtobufKey() { + org.hiero.sdk.proto.Key toProtobufKey() { // Forward to the corresponding public key. return getPublicKey().toProtobufKey(); } diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/PrivateKeyECDSA.java b/sdk/src/main/java/org/hiero/sdk/PrivateKeyECDSA.java similarity index 89% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/PrivateKeyECDSA.java rename to sdk/src/main/java/org/hiero/sdk/PrivateKeyECDSA.java index aa90d593d5..576f4ca4dd 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/PrivateKeyECDSA.java +++ b/sdk/src/main/java/org/hiero/sdk/PrivateKeyECDSA.java @@ -1,25 +1,11 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; -import com.hedera.hashgraph.sdk.utils.Bip32Utils; +import java.io.IOException; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import javax.annotation.Nullable; import org.bouncycastle.asn1.*; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.asn1.sec.ECPrivateKey; @@ -35,12 +21,7 @@ import org.bouncycastle.crypto.signers.ECDSASigner; import org.bouncycastle.crypto.signers.HMacDSAKCalculator; import org.bouncycastle.util.Arrays; - -import javax.annotation.Nullable; -import java.io.IOException; -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; +import org.hiero.sdk.utils.Bip32Utils; /** * Encapsulate the ECDSA private key. @@ -276,10 +257,11 @@ private static byte[] bigIntTo32Bytes(BigInteger n) { byte[] bytes = n.toByteArray(); byte[] bytes32 = new byte[32]; System.arraycopy( - bytes, Math.max(0, bytes.length - 32), - bytes32, Math.max(0, 32 - bytes.length), - Math.min(32, bytes.length) - ); + bytes, + Math.max(0, bytes.length - 32), + bytes32, + Math.max(0, 32 - bytes.length), + Math.min(32, bytes.length)); return bytes32; } @@ -292,11 +274,11 @@ public byte[] toBytesRaw() { public byte[] toBytesDER() { try { return new ECPrivateKey( - 256, - keyData, - new DERBitString(getPublicKey().toBytesRaw()), - new X962Parameters(ID_ECDSA_SECP256K1) - ).getEncoded("DER"); + 256, + keyData, + new DERBitString(getPublicKey().toBytesRaw()), + new X962Parameters(ID_ECDSA_SECP256K1)) + .getEncoded("DER"); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/PrivateKeyED25519.java b/sdk/src/main/java/org/hiero/sdk/PrivateKeyED25519.java similarity index 86% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/PrivateKeyED25519.java rename to sdk/src/main/java/org/hiero/sdk/PrivateKeyED25519.java index daa3feb20c..e1c015cab7 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/PrivateKeyED25519.java +++ b/sdk/src/main/java/org/hiero/sdk/PrivateKeyED25519.java @@ -1,25 +1,6 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; -import com.hedera.hashgraph.sdk.utils.Bip32Utils; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -35,6 +16,7 @@ import org.bouncycastle.crypto.macs.HMac; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.math.ec.rfc8032.Ed25519; +import org.hiero.sdk.utils.Bip32Utils; /** * Encapsulate the ED25519 private key. @@ -123,7 +105,7 @@ static PrivateKeyED25519 derivableKeyED25519(byte[] deriveData) { */ static PrivateKey fromBytesInternal(byte[] privateKey) { if ((privateKey.length == Ed25519.SECRET_KEY_SIZE) - || (privateKey.length == Ed25519.SECRET_KEY_SIZE + Ed25519.PUBLIC_KEY_SIZE)) { + || (privateKey.length == Ed25519.SECRET_KEY_SIZE + Ed25519.PUBLIC_KEY_SIZE)) { // If this is a 32 or 64 byte string, assume an Ed25519 private key return new PrivateKeyED25519(Arrays.copyOfRange(privateKey, 0, Ed25519.SECRET_KEY_SIZE), null); } @@ -151,17 +133,15 @@ static byte[] legacyDeriveChildKey(byte[] entropy, long index) { } else { Arrays.fill(seed, entropy.length, entropy.length + 4, (byte) 0); } - Arrays.fill(seed, entropy.length + 4, seed.length, Long.valueOf(index).byteValue()); + Arrays.fill( + seed, entropy.length + 4, seed.length, Long.valueOf(index).byteValue()); } System.arraycopy(entropy, 0, seed, 0, entropy.length); byte[] salt = new byte[1]; salt[0] = -1; PKCS5S2ParametersGenerator pbkdf2 = new PKCS5S2ParametersGenerator(new SHA512Digest()); - pbkdf2.init( - seed, - salt, - 2048); + pbkdf2.init(seed, salt, 2048); KeyParameter key = (KeyParameter) pbkdf2.generateDerivedParameters(256); return key.getKey(); @@ -249,10 +229,8 @@ public byte[] toBytesRaw() { @Override public byte[] toBytesDER() { try { - return new PrivateKeyInfo( - new AlgorithmIdentifier(ID_ED25519), - new DEROctetString(keyData) - ).getEncoded("DER"); + return new PrivateKeyInfo(new AlgorithmIdentifier(ID_ED25519), new DEROctetString(keyData)) + .getEncoded("DER"); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/sdk/src/main/java/org/hiero/sdk/PrngTransaction.java b/sdk/src/main/java/org/hiero/sdk/PrngTransaction.java new file mode 100644 index 0000000000..f08894162d --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/PrngTransaction.java @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.grpc.MethodDescriptor; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.*; +import org.hiero.sdk.proto.TransactionResponse; + +/** + * Random Number Generator Transaction. + */ +public class PrngTransaction extends Transaction { + + /** + * If provided and is positive, returns a 32-bit pseudorandom number from the given range in the transaction record. + * If not set or set to zero, will return a 384-bit pseudorandom data in the record. + */ + @Nullable + private Integer range = null; + + /** + * Constructor. + */ + public PrngTransaction() {} + + /** + * Assign the range. + * + * @param range if > 0 32 bit else 384 bit + * @return {@code this} + */ + public PrngTransaction setRange(Integer range) { + this.range = range; + return this; + } + + /** + * Retrieve the range. + * + * @return the range + */ + public Integer getRange() { + return range; + } + + UtilPrngTransactionBody.Builder build() { + var builder = UtilPrngTransactionBody.newBuilder(); + + if (range != null) { + builder.setRange(range); + } + + return builder; + } + + @Override + void onFreeze(TransactionBody.Builder bodyBuilder) { + bodyBuilder.setUtilPrng(build()); + } + + @Override + void onScheduled(SchedulableTransactionBody.Builder scheduled) { + throw new UnsupportedOperationException("cannot schedule RngTransaction"); + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException {} + + @Override + MethodDescriptor getMethodDescriptor() { + return UtilServiceGrpc.getPrngMethod(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/ProxyStaker.java b/sdk/src/main/java/org/hiero/sdk/ProxyStaker.java new file mode 100644 index 0000000000..c9641a59bb --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/ProxyStaker.java @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +/** + * Information about a single account that is proxy staking. + */ +public final class ProxyStaker { + /** + * The Account ID that is proxy staking. + */ + public final AccountId accountId; + + /** + * The number of hbars that are currently proxy staked. + */ + public final Hbar amount; + + /** + * Constructor. + * + * @param accountId the account id + * @param amount the amount + */ + private ProxyStaker(AccountId accountId, long amount) { + this.accountId = accountId; + this.amount = Hbar.fromTinybars(amount); + } + + /** + * Create a proxy staker object from a protobuf. + * + * @param proxyStaker the protobuf + * @return the new proxy staker object + */ + static ProxyStaker fromProtobuf(org.hiero.sdk.proto.ProxyStaker proxyStaker) { + return new ProxyStaker(AccountId.fromProtobuf(proxyStaker.getAccountID()), proxyStaker.getAmount()); + } + + @Override + public String toString() { + return "ProxyStaker{" + "accountId=" + accountId + ", amount=" + amount + '}'; + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/PublicKey.java b/sdk/src/main/java/org/hiero/sdk/PublicKey.java similarity index 87% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/PublicKey.java rename to sdk/src/main/java/org/hiero/sdk/PublicKey.java index d541d5ae03..9c25a644a6 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/PublicKey.java +++ b/sdk/src/main/java/org/hiero/sdk/PublicKey.java @@ -1,34 +1,15 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.SignaturePair; +import javax.annotation.Nonnegative; +import javax.annotation.Nullable; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.math.ec.rfc8032.Ed25519; import org.bouncycastle.util.encoders.Hex; - -import javax.annotation.Nonnegative; -import javax.annotation.Nullable; +import org.hiero.sdk.proto.SignaturePair; /** * A public key on the Hedera™ network. @@ -50,8 +31,7 @@ public static PublicKey fromBytes(byte[] publicKey) { } else if (publicKey.length == 65) { // compress the 65 byte form return PublicKeyECDSA.fromBytesInternal( - Key.ECDSA_SECP256K1_CURVE.getCurve().decodePoint(publicKey).getEncoded(true) - ); + Key.ECDSA_SECP256K1_CURVE.getCurve().decodePoint(publicKey).getEncoded(true)); } // Assume a DER-encoded private key descriptor @@ -135,7 +115,7 @@ public static PublicKey fromStringDER(String publicKey) { * @return the new key */ private static PublicKey fromSubjectKeyInfo(SubjectPublicKeyInfo subjectPublicKeyInfo) { - if(subjectPublicKeyInfo.getAlgorithm().equals(new AlgorithmIdentifier(ID_ED25519))) { + if (subjectPublicKeyInfo.getAlgorithm().equals(new AlgorithmIdentifier(ID_ED25519))) { return PublicKeyED25519.fromSubjectKeyInfoInternal(subjectPublicKeyInfo); } else { // assume ECDSA @@ -153,7 +133,7 @@ private static PublicKey fromSubjectKeyInfo(SubjectPublicKeyInfo subjectPublicKe static PublicKey fromAliasBytes(ByteString aliasBytes) { if (!aliasBytes.isEmpty()) { try { - var key = Key.fromProtobufKey(com.hedera.hashgraph.sdk.proto.Key.parseFrom(aliasBytes)); + var key = Key.fromProtobufKey(org.hiero.sdk.proto.Key.parseFrom(aliasBytes)); return (key instanceof PublicKey) ? ((PublicKey) key) : null; } catch (InvalidProtocolBufferException ignored) { } @@ -202,7 +182,9 @@ public boolean verifyTransaction(Transaction transaction) { if (sigPair.getPubKeyPrefix().equals(ByteString.copyFrom(toBytesRaw()))) { found = true; - if (!verify(signedTransaction.getBodyBytes().toByteArray(), extractSignatureFromProtobuf(sigPair).toByteArray())) { + if (!verify( + signedTransaction.getBodyBytes().toByteArray(), + extractSignatureFromProtobuf(sigPair).toByteArray())) { return false; } } diff --git a/sdk/src/main/java/org/hiero/sdk/PublicKeyECDSA.java b/sdk/src/main/java/org/hiero/sdk/PublicKeyECDSA.java new file mode 100644 index 0000000000..74488f6bb9 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/PublicKeyECDSA.java @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.hiero.sdk.Crypto.calcKeccak256; + +import com.google.protobuf.ByteString; +import java.io.IOException; +import java.math.BigInteger; +import java.util.Arrays; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; +import org.bouncycastle.crypto.signers.ECDSASigner; +import org.hiero.sdk.proto.SignaturePair; + +/** + * Encapsulate the ECDSA public key. + */ +class PublicKeyECDSA extends PublicKey { + // Compressed 33 byte form + private byte[] keyData; + + /** + * Constructor. + * + * @param keyData the byte array representing the key + */ + private PublicKeyECDSA(byte[] keyData) { + this.keyData = keyData; + } + + /** + * Create a key from a byte array representation. + * + * @param publicKey the byte array representing the key + * @return the new key + */ + static PublicKeyECDSA fromBytesInternal(byte[] publicKey) { + // Validate the key if it's not all zero public key, see HIP-540 + if (Arrays.equals(publicKey, new byte[33])) { + return new PublicKeyECDSA(publicKey); + } + if (publicKey.length == 33 || publicKey.length == 65) { + return new PublicKeyECDSA( + // compress and validate the key + Key.ECDSA_SECP256K1_CURVE.getCurve().decodePoint(publicKey).getEncoded(true)); + } + + // Assume a DER-encoded public key descriptor + return fromSubjectKeyInfoInternal(SubjectPublicKeyInfo.getInstance(publicKey)); + } + + /** + * Create a key from a subject public key info object. + * + * @param subjectPublicKeyInfo the subject public key info object + * @return the new public key + */ + static PublicKeyECDSA fromSubjectKeyInfoInternal(SubjectPublicKeyInfo subjectPublicKeyInfo) { + return fromBytesInternal(subjectPublicKeyInfo.getPublicKeyData().getBytes()); + } + + @Override + ByteString extractSignatureFromProtobuf(SignaturePair pair) { + return pair.getECDSA384(); + } + + @Override + public boolean verify(byte[] message, byte[] signature) { + var hash = calcKeccak256(message); + + ECDSASigner signer = new ECDSASigner(); + signer.init( + false, + new ECPublicKeyParameters( + Key.ECDSA_SECP256K1_CURVE.getCurve().decodePoint(keyData), Key.ECDSA_SECP256K1_DOMAIN)); + + BigInteger r = new BigInteger(1, Arrays.copyOf(signature, 32)); + BigInteger s = new BigInteger(1, Arrays.copyOfRange(signature, 32, 64)); + + return signer.verifySignature(hash, r, s); + } + + @Override + org.hiero.sdk.proto.Key toProtobufKey() { + return org.hiero.sdk.proto.Key.newBuilder() + .setECDSASecp256K1(ByteString.copyFrom(keyData)) + .build(); + } + + @Override + SignaturePair toSignaturePairProtobuf(byte[] signature) { + return SignaturePair.newBuilder() + .setPubKeyPrefix(ByteString.copyFrom(keyData)) + .setECDSASecp256K1(ByteString.copyFrom(signature)) + .build(); + } + + @Override + public byte[] toBytesDER() { + try { + return new SubjectPublicKeyInfo(new AlgorithmIdentifier(ID_EC_PUBLIC_KEY, ID_ECDSA_SECP256K1), keyData) + .getEncoded("DER"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public byte[] toBytes() { + return toBytesDER(); + } + + @Override + public byte[] toBytesRaw() { + return Arrays.copyOf(keyData, keyData.length); + } + + @Override + public EvmAddress toEvmAddress() { + // Calculate the Keccak-256 hash of the uncompressed key without "04" prefix + byte[] uncompressed = + Key.ECDSA_SECP256K1_CURVE.getCurve().decodePoint(toBytesRaw()).getEncoded(false); + byte[] keccakBytes = calcKeccak256(Arrays.copyOfRange(uncompressed, 1, uncompressed.length)); + + // Return the last 20 bytes + return EvmAddress.fromBytes(Arrays.copyOfRange(keccakBytes, 12, 32)); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass()) { + return false; + } + + PublicKeyECDSA publicKey = (PublicKeyECDSA) o; + return Arrays.equals(keyData, publicKey.keyData); + } + + @Override + public int hashCode() { + return Arrays.hashCode(keyData); + } + + @Override + public boolean isED25519() { + return false; + } + + @Override + public boolean isECDSA() { + return true; + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/PublicKeyED25519.java b/sdk/src/main/java/org/hiero/sdk/PublicKeyED25519.java new file mode 100644 index 0000000000..1f1e615286 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/PublicKeyED25519.java @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.ByteString; +import java.io.IOException; +import java.util.Arrays; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters; +import org.bouncycastle.math.ec.rfc8032.Ed25519; +import org.hiero.sdk.proto.SignaturePair; + +/** + * Encapsulate the ED25519 public key. + */ +class PublicKeyED25519 extends PublicKey { + private final byte[] keyData; + + /** + * Constructor. + * + * @param keyData the byte array representing the key + */ + private PublicKeyED25519(byte[] keyData) { + this.keyData = keyData; + } + + /** + * Create a key from a byte array representation. + * + * @param publicKey the byte array representing the key + * @return the new key + */ + static PublicKeyED25519 fromBytesInternal(byte[] publicKey) { + if (publicKey.length == Ed25519.PUBLIC_KEY_SIZE) { + // Validate the key if it's not all zero public key, see HIP-540 + if (!Arrays.equals(publicKey, new byte[32])) { + // Will throw if the key is invalid + new Ed25519PublicKeyParameters(publicKey, 0); + } + // If this is a 32 byte string, assume an Ed25519 public key + return new PublicKeyED25519(publicKey); + } + + // Assume a DER-encoded public key descriptor + return fromSubjectKeyInfoInternal(SubjectPublicKeyInfo.getInstance(publicKey)); + } + + /** + * Create a key from a subject public key info object. + * + * @param subjectPublicKeyInfo the subject public key info object + * @return the new public key + */ + static PublicKeyED25519 fromSubjectKeyInfoInternal(SubjectPublicKeyInfo subjectPublicKeyInfo) { + return new PublicKeyED25519(subjectPublicKeyInfo.getPublicKeyData().getBytes()); + } + + @Override + ByteString extractSignatureFromProtobuf(SignaturePair pair) { + return pair.getEd25519(); + } + + @Override + public boolean verify(byte[] message, byte[] signature) { + return Ed25519.verify(signature, 0, keyData, 0, message, 0, message.length); + } + + @Override + org.hiero.sdk.proto.Key toProtobufKey() { + return org.hiero.sdk.proto.Key.newBuilder() + .setEd25519(ByteString.copyFrom(keyData)) + .build(); + } + + @Override + SignaturePair toSignaturePairProtobuf(byte[] signature) { + return SignaturePair.newBuilder() + .setPubKeyPrefix(ByteString.copyFrom(keyData)) + .setEd25519(ByteString.copyFrom(signature)) + .build(); + } + + @Override + public byte[] toBytesDER() { + try { + return new SubjectPublicKeyInfo(new AlgorithmIdentifier(ID_ED25519), keyData).getEncoded("DER"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public byte[] toBytes() { + return toBytesRaw(); + } + + @Override + public byte[] toBytesRaw() { + return keyData; + } + + @Override + public EvmAddress toEvmAddress() { + throw new IllegalStateException("unsupported operation on Ed25519PublicKey"); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass()) { + return false; + } + + PublicKeyED25519 publicKey = (PublicKeyED25519) o; + return Arrays.equals(keyData, publicKey.keyData); + } + + @Override + public int hashCode() { + return Arrays.hashCode(keyData); + } + + @Override + public boolean isED25519() { + return true; + } + + @Override + public boolean isECDSA() { + return false; + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/Query.java b/sdk/src/main/java/org/hiero/sdk/Query.java similarity index 77% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/Query.java rename to sdk/src/main/java/org/hiero/sdk/Query.java index aa833af0e4..b11f43b1b4 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/Query.java +++ b/sdk/src/main/java/org/hiero/sdk/Query.java @@ -1,32 +1,8 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.common.base.MoreObjects; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import com.hedera.hashgraph.sdk.proto.Response; -import com.hedera.hashgraph.sdk.proto.ResponseHeader; -import com.hedera.hashgraph.sdk.proto.ResponseType; -import com.hedera.hashgraph.sdk.proto.Transaction; -import com.hedera.hashgraph.sdk.proto.TransactionBody; import io.grpc.MethodDescriptor; import java.time.Duration; import java.time.Instant; @@ -39,6 +15,12 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; import javax.annotation.Nullable; +import org.hiero.sdk.proto.QueryHeader; +import org.hiero.sdk.proto.Response; +import org.hiero.sdk.proto.ResponseHeader; +import org.hiero.sdk.proto.ResponseType; +import org.hiero.sdk.proto.Transaction; +import org.hiero.sdk.proto.TransactionBody; /** * Base class for all queries that can be submitted to Hedera. @@ -46,8 +28,8 @@ * @param The output type of the query. * @param The type of the query itself. Used to enable chaining. */ -public abstract class Query> extends Executable { - private final com.hedera.hashgraph.sdk.proto.Query.Builder builder; +public abstract class Query> extends Executable { + private final org.hiero.sdk.proto.Query.Builder builder; private final QueryHeader.Builder headerBuilder; @@ -62,8 +44,10 @@ public abstract class Query> extends Executable paymentTransactions = null; + @Nullable private Client.Operator paymentOperator = null; + @Nullable private Hbar queryPayment = null; @@ -77,7 +61,7 @@ public abstract class Query> extends Executable> extends Executable getCostAsync(Client client) { */ public CompletableFuture getCostAsync(Client client, Duration timeout) { initWithNodeIds(client); - return getCostExecutable().setNodeAccountIds(Objects.requireNonNull(getNodeAccountIds())).executeAsync(client, timeout); + return getCostExecutable() + .setNodeAccountIds(Objects.requireNonNull(getNodeAccountIds())) + .executeAsync(client, timeout); } /** @@ -254,7 +238,7 @@ boolean isPaymentRequired() { * Called in {@link #makeRequest} just before the query is built. The intent is for the derived * class to assign their data variant to the query. */ - abstract void onMakeRequest(com.hedera.hashgraph.sdk.proto.Query.Builder queryBuilder, QueryHeader header); + abstract void onMakeRequest(org.hiero.sdk.proto.Query.Builder queryBuilder, QueryHeader header); /** * The derived class should access its response header and return. @@ -264,7 +248,7 @@ boolean isPaymentRequired() { /** * The derived class should access its request header and return. */ - abstract QueryHeader mapRequestHeader(com.hedera.hashgraph.sdk.proto.Query request); + abstract QueryHeader mapRequestHeader(org.hiero.sdk.proto.Query request); /** * Crate the new Query. @@ -291,7 +275,7 @@ Client.Operator getOperatorFromClient(Client client) { if (operator == null) { throw new IllegalStateException( - "`client` must have an `operator` or an explicit payment transaction must be provided"); + "`client` must have an `operator` or an explicit payment transaction must be provided"); } return operator; @@ -328,27 +312,29 @@ CompletableFuture onExecuteAsync(Client client) { return CompletableFuture.completedFuture(null); } - return CompletableFuture.supplyAsync(() -> { - if (grpcCostQuery.getCost() == null) { - // No payment was specified so we need to go ask - // This is a query in its own right so we use a nested future here - return getCostAsync(client).thenCompose(cost -> { - grpcCostQuery.setCost(cost); - - if (grpcCostQuery.shouldError()) { - return CompletableFuture.failedFuture(grpcCostQuery.mapError()); - } - - return CompletableFuture.completedFuture(null); - }); - } - - return CompletableFuture.completedFuture(null); - }, client.executor) - .thenCompose(x -> x) - .thenAccept((paymentAmount) -> { - grpcCostQuery.finish(); - }); + return CompletableFuture.supplyAsync( + () -> { + if (grpcCostQuery.getCost() == null) { + // No payment was specified so we need to go ask + // This is a query in its own right so we use a nested future here + return getCostAsync(client).thenCompose(cost -> { + grpcCostQuery.setCost(cost); + + if (grpcCostQuery.shouldError()) { + return CompletableFuture.failedFuture(grpcCostQuery.mapError()); + } + + return CompletableFuture.completedFuture(null); + }); + } + + return CompletableFuture.completedFuture(null); + }, + client.executor) + .thenCompose(x -> x) + .thenAccept((paymentAmount) -> { + grpcCostQuery.finish(); + }); } private void initWithNodeIds(Client client) { @@ -386,18 +372,17 @@ Transaction getPaymentTransaction(int index) { } var newPaymentTx = makePaymentTransaction( - paymentTransactionId, - nodeAccountIds.get(index), - paymentOperator, - Objects.requireNonNull(chosenQueryPayment) - ); + paymentTransactionId, + nodeAccountIds.get(index), + paymentOperator, + Objects.requireNonNull(chosenQueryPayment)); paymentTransactions.set(index, newPaymentTx); return newPaymentTx; } } @Override - final com.hedera.hashgraph.sdk.proto.Query makeRequest() { + final org.hiero.sdk.proto.Query makeRequest() { // If payment is required, set the next payment transaction on the query if (isPaymentRequired() && paymentTransactions != null) { headerBuilder.setPayment(getPaymentTransaction(nodeAccountIds.getIndex())); @@ -405,7 +390,8 @@ final com.hedera.hashgraph.sdk.proto.Query makeRequest() { // Delegate to the derived class to apply the header because the common header struct is // within the nested type - onMakeRequest(builder, headerBuilder.setResponseType(ResponseType.ANSWER_ONLY).build()); + onMakeRequest( + builder, headerBuilder.setResponseType(ResponseType.ANSWER_ONLY).build()); return builder.build(); } @@ -454,7 +440,8 @@ public T setPaymentTransactionId(TransactionId paymentTransactionId) { public String toString() { var request = makeRequest(); - StringBuilder builder = new StringBuilder(request.toString().replaceAll("(?m)^# com.hedera.hashgraph.sdk.proto.Query.*", "")); + StringBuilder builder = + new StringBuilder(request.toString().replaceAll("(?m)^# org.hiero.sdk.proto.Query.*", "")); var queryHeader = mapRequestHeader(request); if (queryHeader.hasPayment()) { @@ -462,7 +449,10 @@ public String toString() { try { // the replaceAll() is for removing the class name from Transaction Body - builder.append(TransactionBody.parseFrom(queryHeader.getPayment().getBodyBytes()).toString().replaceAll("(?m)^# com.hedera.hashgraph.sdk.proto.TransactionBuilder.*", "")); + builder.append( + TransactionBody.parseFrom(queryHeader.getPayment().getBodyBytes()) + .toString() + .replaceAll("(?m)^# org.hiero.sdk.proto.TransactionBuilder.*", "")); } catch (InvalidProtocolBufferException e) { throw new RuntimeException(e); } @@ -530,11 +520,10 @@ void finish() { @SuppressWarnings("NullableDereference") private class QueryCostQuery extends Query { @Override - void validateChecksums(Client client) throws BadEntityIdException { - } + void validateChecksums(Client client) throws BadEntityIdException {} @Override - void onMakeRequest(com.hedera.hashgraph.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { + void onMakeRequest(org.hiero.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { headerBuilder.setResponseType(ResponseType.COST_ANSWER); // COST_ANSWER requires a payment to pass validation but doesn't actually process it @@ -543,10 +532,10 @@ void onMakeRequest(com.hedera.hashgraph.sdk.proto.Query.Builder queryBuilder, Qu // now go back to sleep // without this, an error of MISSING_QUERY_HEADER is returned headerBuilder.setPayment(new TransferTransaction() - .setNodeAccountIds(Collections.singletonList(new AccountId(0))) - .setTransactionId(TransactionId.withValidStart(new AccountId(0), Instant.ofEpochSecond(0))) - .freeze() - .makeRequest()); + .setNodeAccountIds(Collections.singletonList(new AccountId(0))) + .setTransactionId(TransactionId.withValidStart(new AccountId(0), Instant.ofEpochSecond(0))) + .freeze() + .makeRequest()); Query.this.onMakeRequest(queryBuilder, headerBuilder.build()); } @@ -557,17 +546,17 @@ ResponseHeader mapResponseHeader(Response response) { } @Override - QueryHeader mapRequestHeader(com.hedera.hashgraph.sdk.proto.Query request) { + QueryHeader mapRequestHeader(org.hiero.sdk.proto.Query request) { return Query.this.mapRequestHeader(request); } @Override - Hbar mapResponse(Response response, AccountId nodeId, com.hedera.hashgraph.sdk.proto.Query request) { + Hbar mapResponse(Response response, AccountId nodeId, org.hiero.sdk.proto.Query request) { return Hbar.fromTinybars(mapResponseHeader(response).getCost()); } @Override - MethodDescriptor getMethodDescriptor() { + MethodDescriptor getMethodDescriptor() { return Query.this.getMethodDescriptor(); } diff --git a/sdk/src/main/java/org/hiero/sdk/ReceiptStatusException.java b/sdk/src/main/java/org/hiero/sdk/ReceiptStatusException.java new file mode 100644 index 0000000000..5def0e1a6d --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/ReceiptStatusException.java @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import javax.annotation.Nullable; + +/** + * An Exception thrown on error status by {@link TransactionId#getReceipt(Client)}. + *

+ * The receipt is included, though only the {@link TransactionReceipt#status} field will be + * initialized; all the getters should throw. + */ +public class ReceiptStatusException extends Exception { + /** + * The ID of the transaction that failed, in case that context is no longer available + * (e.g. the exception was bubbled up). + */ + @Nullable + public final TransactionId transactionId; + + /** + * The receipt of the transaction that failed; the only initialized field is + * {@link TransactionReceipt#status}. + */ + public final TransactionReceipt receipt; + + /** + * Constructor. + * + * @param transactionId the transaction id + * @param receipt the receipt + */ + ReceiptStatusException(@Nullable TransactionId transactionId, TransactionReceipt receipt) { + this.transactionId = transactionId; + this.receipt = receipt; + } + + @Override + public String getMessage() { + return "receipt for transaction " + transactionId + " raised status " + receipt.status; + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/RequestType.java b/sdk/src/main/java/org/hiero/sdk/RequestType.java similarity index 96% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/RequestType.java rename to sdk/src/main/java/org/hiero/sdk/RequestType.java index d528b5d7d5..62f078855e 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/RequestType.java +++ b/sdk/src/main/java/org/hiero/sdk/RequestType.java @@ -1,25 +1,7 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; -import com.hedera.hashgraph.sdk.proto.HederaFunctionality; +import org.hiero.sdk.proto.HederaFunctionality; /** * Enum for the request types. diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/ScheduleCreateTransaction.java b/sdk/src/main/java/org/hiero/sdk/ScheduleCreateTransaction.java similarity index 85% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/ScheduleCreateTransaction.java rename to sdk/src/main/java/org/hiero/sdk/ScheduleCreateTransaction.java index 4cd57c7846..2cf14e5871 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/ScheduleCreateTransaction.java +++ b/sdk/src/main/java/org/hiero/sdk/ScheduleCreateTransaction.java @@ -1,36 +1,17 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.ScheduleCreateTransactionBody; -import com.hedera.hashgraph.sdk.proto.ScheduleServiceGrpc; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; import io.grpc.MethodDescriptor; import java.time.Instant; - -import javax.annotation.Nullable; import java.util.LinkedHashMap; import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.ScheduleCreateTransactionBody; +import org.hiero.sdk.proto.ScheduleServiceGrpc; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; /** * Create a scheduled transaction. @@ -40,10 +21,13 @@ public final class ScheduleCreateTransaction extends Transaction { @Nullable private AccountId payerAccountId = null; + @Nullable private SchedulableTransactionBody transactionToSchedule = null; + @Nullable private Key adminKey = null; + private String scheduleMemo = ""; @Nullable @@ -65,7 +49,9 @@ public ScheduleCreateTransaction() { * records * @throws InvalidProtocolBufferException when there is an issue with the protobuf */ - ScheduleCreateTransaction(LinkedHashMap> txs) throws InvalidProtocolBufferException { + ScheduleCreateTransaction( + LinkedHashMap> txs) + throws InvalidProtocolBufferException { super(txs); initFromTransactionBody(); } @@ -228,7 +214,7 @@ public ScheduleCreateTransaction setScheduleMemo(String memo) { /** * Build the correct transaction body. * - * @return {@link com.hedera.hashgraph.sdk.proto.ScheduleCreateTransactionBody builder } + * @return {@link org.hiero.sdk.proto.ScheduleCreateTransactionBody builder } */ ScheduleCreateTransactionBody.Builder build() { var builder = ScheduleCreateTransactionBody.newBuilder(); @@ -276,7 +262,7 @@ void validateChecksums(Client client) throws BadEntityIdException { } @Override - MethodDescriptor getMethodDescriptor() { + MethodDescriptor getMethodDescriptor() { return ScheduleServiceGrpc.getCreateScheduleMethod(); } diff --git a/sdk/src/main/java/org/hiero/sdk/ScheduleDeleteTransaction.java b/sdk/src/main/java/org/hiero/sdk/ScheduleDeleteTransaction.java new file mode 100644 index 0000000000..11b59902af --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/ScheduleDeleteTransaction.java @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.grpc.MethodDescriptor; +import java.util.LinkedHashMap; +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.ScheduleDeleteTransactionBody; +import org.hiero.sdk.proto.ScheduleServiceGrpc; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; + +/** + * Delete a scheduled transaction. + *

+ * See Hedera Documentation + */ +public final class ScheduleDeleteTransaction extends Transaction { + + @Nullable + private ScheduleId scheduleId = null; + + /** + * Constructor. + */ + public ScheduleDeleteTransaction() { + defaultMaxTransactionFee = new Hbar(5); + } + + /** + * Constructor. + * + * @param txs Compound list of transaction id's list of (AccountId, Transaction) + * records + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + ScheduleDeleteTransaction( + LinkedHashMap> txs) + throws InvalidProtocolBufferException { + super(txs); + initFromTransactionBody(); + } + + /** + * Constructor. + * + * @param txBody protobuf TransactionBody + */ + ScheduleDeleteTransaction(org.hiero.sdk.proto.TransactionBody txBody) { + super(txBody); + initFromTransactionBody(); + } + + /** + * Extract the schedule id. + * + * @return the schedule id + */ + @Nullable + public ScheduleId getScheduleId() { + return scheduleId; + } + + /** + * Assign the scheduled id. + * + * @param scheduleId the schedule id + * @return {@code this} + */ + public ScheduleDeleteTransaction setScheduleId(ScheduleId scheduleId) { + Objects.requireNonNull(scheduleId); + requireNotFrozen(); + this.scheduleId = scheduleId; + return this; + } + + /** + * Initialize from the transaction body. + */ + void initFromTransactionBody() { + var body = sourceTransactionBody.getScheduleDelete(); + if (body.hasScheduleID()) { + scheduleId = ScheduleId.fromProtobuf(body.getScheduleID()); + } + } + + /** + * Build the correct transaction body. + * + * @return {@link org.hiero.sdk.proto.ScheduleDeleteTransactionBody builder } + */ + ScheduleDeleteTransactionBody.Builder build() { + var builder = ScheduleDeleteTransactionBody.newBuilder(); + if (scheduleId != null) { + builder.setScheduleID(scheduleId.toProtobuf()); + } + + return builder; + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (scheduleId != null) { + scheduleId.validateChecksum(client); + } + } + + @Override + MethodDescriptor getMethodDescriptor() { + return ScheduleServiceGrpc.getDeleteScheduleMethod(); + } + + @Override + void onFreeze(TransactionBody.Builder bodyBuilder) { + bodyBuilder.setScheduleDelete(build()); + } + + @Override + void onScheduled(SchedulableTransactionBody.Builder scheduled) { + scheduled.setScheduleDelete(build()); + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/ScheduleId.java b/sdk/src/main/java/org/hiero/sdk/ScheduleId.java similarity index 86% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/ScheduleId.java rename to sdk/src/main/java/org/hiero/sdk/ScheduleId.java index 5f18dfba4e..270814dfba 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/ScheduleId.java +++ b/sdk/src/main/java/org/hiero/sdk/ScheduleId.java @@ -1,30 +1,11 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.ScheduleID; - +import java.util.Objects; import javax.annotation.Nonnegative; import javax.annotation.Nullable; -import java.util.Objects; +import org.hiero.sdk.proto.ScheduleID; /** * The entity ID of a schedule transaction. @@ -129,10 +110,10 @@ public static ScheduleId fromBytes(byte[] bytes) throws InvalidProtocolBufferExc */ ScheduleID toProtobuf() { return ScheduleID.newBuilder() - .setShardNum(shard) - .setRealmNum(realm) - .setScheduleNum(num) - .build(); + .setShardNum(shard) + .setRealmNum(realm) + .setScheduleNum(num) + .build(); } /** @@ -195,7 +176,7 @@ public int hashCode() { } @Override - public boolean equals( Object o) { + public boolean equals(Object o) { if (this == o) { return true; } diff --git a/sdk/src/main/java/org/hiero/sdk/ScheduleInfo.java b/sdk/src/main/java/org/hiero/sdk/ScheduleInfo.java new file mode 100644 index 0000000000..70514d5d49 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/ScheduleInfo.java @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import com.google.protobuf.InvalidProtocolBufferException; +import java.time.Instant; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.SchedulableTransactionBody; + +/** + * A query that returns information about the current state of a scheduled + * transaction on a Hedera network. + *

+ * See Hedera Documentation + */ +public final class ScheduleInfo { + /** + * The ID of the schedule transaction + */ + public final ScheduleId scheduleId; + /** + * The Hedera account that created the schedule transaction in x.y.z format + */ + public final AccountId creatorAccountId; + /** + * The Hedera account paying for the execution of the schedule transaction + * in x.y.z format + */ + public final AccountId payerAccountId; + /** + * The signatories that have provided signatures so far for the schedule + * transaction + */ + public final KeyList signatories; + + /** + * The Key which is able to delete the schedule transaction if set + */ + @Nullable + public final Key adminKey; + + /** + * The scheduled transaction + */ + @Nullable + public final TransactionId scheduledTransactionId; + + /** + * Publicly visible information about the Schedule entity, up to + * 100 bytes. No guarantee of uniqueness. + */ + public final String memo; + + /** + * The date and time the schedule transaction will expire + */ + @Nullable + public final Instant expirationTime; + + /** + * The time the schedule transaction was executed. If the schedule + * transaction has not executed this field will be left null. + */ + @Nullable + public final Instant executedAt; + + /** + * The consensus time the schedule transaction was deleted. If the + * schedule transaction was not deleted, this field will be left null. + */ + @Nullable + public final Instant deletedAt; + + /** + * The scheduled transaction (inner transaction). + */ + final SchedulableTransactionBody transactionBody; + + /** + * The ledger ID the response was returned from; please see HIP-198 for the network-specific IDs. + */ + @Nullable + public final LedgerId ledgerId; + + /** + * When set to true, the transaction will be evaluated for execution at expiration_time instead + * of when all required signatures are received. + * When set to false, the transaction will execute immediately after sufficient signatures are received + * to sign the contained transaction. During the initial ScheduleCreate transaction or via ScheduleSign transactions. + * + * Note: this field is unused until Long Term Scheduled Transactions are enabled. + */ + public final boolean waitForExpiry; + + /** + * Constructor. + * + * @param scheduleId the schedule id + * @param creatorAccountId the creator account id + * @param payerAccountId the payer account id + * @param transactionBody the transaction body + * @param signers the signers key list + * @param adminKey the admin key + * @param scheduledTransactionId the transaction id + * @param memo the memo 100 bytes max + * @param expirationTime the expiration time + * @param executed the time transaction was executed + * @param deleted the time it was deleted + * @param ledgerId the ledger id + * @param waitForExpiry the wait for expiry field + */ + ScheduleInfo( + ScheduleId scheduleId, + AccountId creatorAccountId, + AccountId payerAccountId, + SchedulableTransactionBody transactionBody, + KeyList signers, + @Nullable Key adminKey, + @Nullable TransactionId scheduledTransactionId, + String memo, + @Nullable Instant expirationTime, + @Nullable Instant executed, + @Nullable Instant deleted, + LedgerId ledgerId, + boolean waitForExpiry) { + this.scheduleId = scheduleId; + this.creatorAccountId = creatorAccountId; + this.payerAccountId = payerAccountId; + this.signatories = signers; + this.adminKey = adminKey; + this.transactionBody = transactionBody; + this.scheduledTransactionId = scheduledTransactionId; + this.memo = memo; + this.expirationTime = expirationTime; + this.executedAt = executed; + this.deletedAt = deleted; + this.ledgerId = ledgerId; + this.waitForExpiry = waitForExpiry; + } + + /** + * Create a schedule info object from a protobuf. + * + * @param info the protobuf + * @return the new schedule info object + */ + static ScheduleInfo fromProtobuf(org.hiero.sdk.proto.ScheduleInfo info) { + var scheduleId = ScheduleId.fromProtobuf(info.getScheduleID()); + var creatorAccountId = AccountId.fromProtobuf(info.getCreatorAccountID()); + var payerAccountId = AccountId.fromProtobuf(info.getPayerAccountID()); + var adminKey = info.hasAdminKey() ? Key.fromProtobufKey(info.getAdminKey()) : null; + var scheduledTransactionId = + info.hasScheduledTransactionID() ? TransactionId.fromProtobuf(info.getScheduledTransactionID()) : null; + + return new ScheduleInfo( + scheduleId, + creatorAccountId, + payerAccountId, + info.getScheduledTransactionBody(), + KeyList.fromProtobuf(info.getSigners(), null), + adminKey, + scheduledTransactionId, + info.getMemo(), + info.hasExpirationTime() ? InstantConverter.fromProtobuf(info.getExpirationTime()) : null, + info.hasExecutionTime() ? InstantConverter.fromProtobuf(info.getExecutionTime()) : null, + info.hasDeletionTime() ? InstantConverter.fromProtobuf(info.getDeletionTime()) : null, + LedgerId.fromByteString(info.getLedgerId()), + info.getWaitForExpiry()); + } + + /** + * Create a schedule info object from a byte array. + * + * @param bytes the byte array + * @return the new schedule info object + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + public static ScheduleInfo fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + return fromProtobuf( + org.hiero.sdk.proto.ScheduleInfo.parseFrom(bytes).toBuilder().build()); + } + + /** + * Create the protobuf. + * + * @return the protobuf representation + */ + org.hiero.sdk.proto.ScheduleInfo toProtobuf() { + var info = org.hiero.sdk.proto.ScheduleInfo.newBuilder(); + + if (adminKey != null) { + info.setAdminKey(adminKey.toProtobufKey()); + } + + if (scheduledTransactionId != null) { + info.setScheduledTransactionID(scheduledTransactionId.toProtobuf()); + } + + if (expirationTime != null) { + info.setExpirationTime(InstantConverter.toProtobuf(expirationTime)); + } + + if (executedAt != null) { + info.setExecutionTime(InstantConverter.toProtobuf(executedAt)); + } + + if (deletedAt != null) { + info.setDeletionTime(InstantConverter.toProtobuf(deletedAt)); + } + + return info.setScheduleID(scheduleId.toProtobuf()) + .setCreatorAccountID(creatorAccountId.toProtobuf()) + .setScheduledTransactionBody(transactionBody) + .setPayerAccountID(payerAccountId.toProtobuf()) + .setSigners(signatories.toProtobuf()) + .setMemo(memo) + .setLedgerId(ledgerId.toByteString()) + .setWaitForExpiry(waitForExpiry) + .build(); + } + + /** + * Extract the transaction. + * + * @return the transaction + */ + public Transaction getScheduledTransaction() { + return Transaction.fromScheduledTransaction(transactionBody); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("scheduleId", scheduleId) + .add("scheduledTransactionId", scheduledTransactionId) + .add("creatorAccountId", creatorAccountId) + .add("payerAccountId", payerAccountId) + .add("signatories", signatories) + .add("adminKey", adminKey) + .add("expirationTime", expirationTime) + .add("memo", memo) + .add("executedAt", executedAt) + .add("deletedAt", deletedAt) + .add("ledgerId", ledgerId) + .add("waitForExpiry", waitForExpiry) + .toString(); + } + + /** + * Create the byte array. + * + * @return the byte array representation + */ + public byte[] toBytes() { + return toProtobuf().toByteArray(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/ScheduleInfoQuery.java b/sdk/src/main/java/org/hiero/sdk/ScheduleInfoQuery.java new file mode 100644 index 0000000000..0e9e1fa037 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/ScheduleInfoQuery.java @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.grpc.MethodDescriptor; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.Query; +import org.hiero.sdk.proto.QueryHeader; +import org.hiero.sdk.proto.Response; +import org.hiero.sdk.proto.ResponseHeader; +import org.hiero.sdk.proto.ScheduleGetInfoQuery; +import org.hiero.sdk.proto.ScheduleServiceGrpc; + +/** + * A query that returns information about the current state of a schedule + * transaction on a Hedera network. + * + * See Hedera Documentation + */ +public class ScheduleInfoQuery extends org.hiero.sdk.Query { + @Nullable + private ScheduleId scheduleId = null; + + /** + * Constructor. + */ + public ScheduleInfoQuery() {} + + /** + * Extract the schedule id. + * + * @return the schedule id + */ + @Nullable + public ScheduleId getScheduleId() { + return scheduleId; + } + + /** + * Assign the schedule id. + * + * @param scheduleId the schedule id + * @return {@code this} + */ + public ScheduleInfoQuery setScheduleId(ScheduleId scheduleId) { + Objects.requireNonNull(scheduleId); + this.scheduleId = scheduleId; + return this; + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (scheduleId != null) { + scheduleId.validateChecksum(client); + } + } + + @Override + void onMakeRequest(org.hiero.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { + var builder = ScheduleGetInfoQuery.newBuilder(); + if (scheduleId != null) { + builder.setScheduleID(scheduleId.toProtobuf()); + } + + queryBuilder.setScheduleGetInfo(builder.setHeader(header)); + } + + @Override + ResponseHeader mapResponseHeader(Response response) { + return response.getScheduleGetInfo().getHeader(); + } + + @Override + QueryHeader mapRequestHeader(org.hiero.sdk.proto.Query request) { + return request.getScheduleGetInfo().getHeader(); + } + + @Override + ScheduleInfo mapResponse(Response response, AccountId nodeId, Query request) { + return ScheduleInfo.fromProtobuf(response.getScheduleGetInfo().getScheduleInfo()); + } + + @Override + MethodDescriptor getMethodDescriptor() { + return ScheduleServiceGrpc.getGetScheduleInfoMethod(); + } + + @Override + public CompletableFuture getCostAsync(Client client) { + // deleted accounts return a COST_ANSWER of zero which triggers `INSUFFICIENT_TX_FEE` + // if you set that as the query payment; 25 tinybar seems to be enough to get + // `Token_DELETED` back instead. + return super.getCostAsync(client).thenApply((cost) -> Hbar.fromTinybars(Math.max(cost.toTinybars(), 25))); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/ScheduleSignTransaction.java b/sdk/src/main/java/org/hiero/sdk/ScheduleSignTransaction.java new file mode 100644 index 0000000000..ff9f4d72df --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/ScheduleSignTransaction.java @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.grpc.MethodDescriptor; +import java.util.LinkedHashMap; +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.ScheduleServiceGrpc; +import org.hiero.sdk.proto.ScheduleSignTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; + +/** + * A transaction that appends signatures to a schedule transaction. + * You will need to know the schedule ID to reference the schedule + * transaction to submit signatures to. A record will be generated + * for each ScheduleSign transaction that is successful and the schedule + * entity will subsequently update with the public keys that have signed + * the schedule transaction. To view the keys that have signed the + * schedule transaction, you can query the network for the schedule info. + * Once a schedule transaction receives the last required signature, the + * schedule transaction executes. + * + * See Hedera Documentation + */ +public final class ScheduleSignTransaction extends Transaction { + + @Nullable + private ScheduleId scheduleId = null; + + /** + * Constructor. + */ + public ScheduleSignTransaction() { + defaultMaxTransactionFee = new Hbar(5); + } + + /** + * Constructor. + * + * @param txs Compound list of transaction id's list of (AccountId, Transaction) + * records + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + ScheduleSignTransaction(LinkedHashMap> txs) + throws InvalidProtocolBufferException { + super(txs); + initFromTransactionBody(); + } + + /** + * Extract the schedule id. + * + * @return the schedule id + */ + @Nullable + public ScheduleId getScheduleId() { + return scheduleId; + } + + /** + * Assign the schedule id. + * + * @param scheduleId the schedule id + * @return {@code this} + */ + public ScheduleSignTransaction setScheduleId(ScheduleId scheduleId) { + Objects.requireNonNull(scheduleId); + requireNotFrozen(); + this.scheduleId = scheduleId; + return this; + } + + /** + * Clears the schedule id + * + * @return {@code this} + */ + @Deprecated + public ScheduleSignTransaction clearScheduleId() { + requireNotFrozen(); + this.scheduleId = null; + return this; + } + + /** + * Build the correct transaction body. + * + * @return {@link + * org.hiero.sdk.proto.ScheduleSignTransactionBody + * builder } + */ + ScheduleSignTransactionBody.Builder build() { + var builder = ScheduleSignTransactionBody.newBuilder(); + if (scheduleId != null) { + builder.setScheduleID(scheduleId.toProtobuf()); + } + + return builder; + } + + /** + * Initialize from the transaction body. + */ + void initFromTransactionBody() { + var body = sourceTransactionBody.getScheduleSign(); + if (body.hasScheduleID()) { + scheduleId = ScheduleId.fromProtobuf(body.getScheduleID()); + } + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (scheduleId != null) { + scheduleId.validateChecksum(client); + } + } + + @Override + MethodDescriptor getMethodDescriptor() { + return ScheduleServiceGrpc.getSignScheduleMethod(); + } + + @Override + void onFreeze(TransactionBody.Builder bodyBuilder) { + bodyBuilder.setScheduleSign(build()); + } + + @Override + void onScheduled(SchedulableTransactionBody.Builder scheduled) { + throw new UnsupportedOperationException("cannot schedule ScheduleSignTransaction"); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/SemanticVersion.java b/sdk/src/main/java/org/hiero/sdk/SemanticVersion.java new file mode 100644 index 0000000000..eeacb7e9cd --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/SemanticVersion.java @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; + +/** + * Hedera follows semantic versioning () for both the HAPI protobufs and + * the Services software. This type allows the getVersionInfo query in the + * NetworkService to return the deployed versions of both protobufs and + * software on the node answering the query. + * + * See Hedera Documentation + */ +public class SemanticVersion { + /** + * Increases with incompatible API changes + */ + public int major; + /** + * Increases with backwards-compatible new functionality + */ + public int minor; + /** + * Increases with backwards-compatible bug fixes + */ + public int patch; + + /** + * Constructor. + * + * @param major the major part + * @param minor the minor part + * @param patch the patch part + */ + SemanticVersion(int major, int minor, int patch) { + this.major = major; + this.minor = minor; + this.patch = patch; + } + + /** + * Create a semantic version object from a protobuf. + * + * @param version the protobuf + * @return the new semantic version + */ + protected static SemanticVersion fromProtobuf(org.hiero.sdk.proto.SemanticVersion version) { + return new SemanticVersion(version.getMajor(), version.getMinor(), version.getPatch()); + } + + /** + * Create a semantic version from a byte array. + * + * @param bytes the byte array + * @return the new semantic version + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + public static SemanticVersion fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + return fromProtobuf(org.hiero.sdk.proto.SemanticVersion.parseFrom(bytes)); + } + + /** + * Create the protobuf. + * + * @return the protobuf representation + */ + protected org.hiero.sdk.proto.SemanticVersion toProtobuf() { + return org.hiero.sdk.proto.SemanticVersion.newBuilder() + .setMajor(major) + .setMinor(minor) + .setPatch(patch) + .build(); + } + + /** + * Create the byte array. + * + * @return the byte array representation + */ + public byte[] toBytes() { + return toProtobuf().toByteArray(); + } + + @Override + public String toString() { + return String.format("%d.%d.%d", major, minor, patch); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/StakingInfo.java b/sdk/src/main/java/org/hiero/sdk/StakingInfo.java new file mode 100644 index 0000000000..01db174895 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/StakingInfo.java @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import com.google.protobuf.InvalidProtocolBufferException; +import java.time.Instant; +import javax.annotation.Nullable; + +/** + * Staking metadata for an account or a contract returned in CryptoGetInfo or ContractGetInfo queries + */ +public class StakingInfo { + /** + * If true, the contract declines receiving a staking reward. The default value is false. + */ + public final boolean declineStakingReward; + /** + * The staking period during which either the staking settings for this account or contract changed (such as starting + * staking or changing staked_node_id) or the most recent reward was earned, whichever is later. If this account or contract + * is not currently staked to a node, then this field is not set. + */ + public final Instant stakePeriodStart; + /** + * The amount in Hbar that will be received in the next reward situation. + */ + public final Hbar pendingReward; + /** + * The total of balance of all accounts staked to this account or contract. + */ + public final Hbar stakedToMe; + + /** + * The account to which this account or contract is staking. + */ + @Nullable + public final AccountId stakedAccountId; + + /** + * The ID of the node this account or contract is staked to. + */ + @Nullable + public final Long stakedNodeId; + + /** + * Constructor + * + * @param declineStakingReward the declineStakingReward + * @param stakePeriodStart the stakePeriodStart + * @param pendingReward the amount in Hbar that will be received in the next reward situation + * @param stakedToMe the total of balance of all accounts staked to this account or contract + * @param stakedAccountId the account to which this account or contract is staking + * @param stakedNodeId the ID of the node this account or contract is staked to + */ + public StakingInfo( + boolean declineStakingReward, + Instant stakePeriodStart, + Hbar pendingReward, + Hbar stakedToMe, + @Nullable AccountId stakedAccountId, + @Nullable Long stakedNodeId) { + this.declineStakingReward = declineStakingReward; + this.stakePeriodStart = stakePeriodStart; + this.pendingReward = pendingReward; + this.stakedToMe = stakedToMe; + this.stakedAccountId = stakedAccountId; + this.stakedNodeId = stakedNodeId; + } + + static StakingInfo fromProtobuf(org.hiero.sdk.proto.StakingInfo info) { + return new StakingInfo( + info.getDeclineReward(), + InstantConverter.fromProtobuf(info.getStakePeriodStart()), + Hbar.fromTinybars(info.getPendingReward()), + Hbar.fromTinybars(info.getStakedToMe()), + info.hasStakedAccountId() ? AccountId.fromProtobuf(info.getStakedAccountId()) : null, + info.hasStakedNodeId() ? info.getStakedNodeId() : null); + } + + /** + * Convert a byte array to a staking info object. + * + * @param bytes the byte array + * @return the converted staking info object + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + public static StakingInfo fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + return fromProtobuf(org.hiero.sdk.proto.StakingInfo.parseFrom(bytes)); + } + + org.hiero.sdk.proto.StakingInfo toProtobuf() { + var builder = org.hiero.sdk.proto.StakingInfo.newBuilder() + .setDeclineReward(declineStakingReward) + .setStakePeriodStart(InstantConverter.toProtobuf(stakePeriodStart)) + .setPendingReward(pendingReward.toTinybars()) + .setStakedToMe(stakedToMe.toTinybars()); + + if (stakedAccountId != null) { + builder.setStakedAccountId(stakedAccountId.toProtobuf()); + } + + if (stakedNodeId != null) { + builder.setStakedNodeId(stakedNodeId); + } + + return builder.build(); + } + + /** + * Convert the staking info object to a byte array. + * + * @return the converted staking info object + */ + public byte[] toBytes() { + return toProtobuf().toByteArray(); + } + + public String toString() { + return MoreObjects.toStringHelper(this) + .add("declineStakingReward", declineStakingReward) + .add("stakePeriodStart", stakePeriodStart) + .add("pendingReward", pendingReward) + .add("stakedToMe", stakedToMe) + .add("stakedAccountId", stakedAccountId) + .add("stakedNodeId", stakedNodeId) + .toString(); + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/Status.java b/sdk/src/main/java/org/hiero/sdk/Status.java similarity index 98% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/Status.java rename to sdk/src/main/java/org/hiero/sdk/Status.java index 4290ab9bff..b981f24cf6 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/Status.java +++ b/sdk/src/main/java/org/hiero/sdk/Status.java @@ -1,27 +1,8 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.ResponseCodeEnum; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import java.util.Objects; +import org.hiero.sdk.proto.ResponseCodeEnum; /** * Returned in {@link TransactionReceipt}, {@link PrecheckStatusException} @@ -1072,7 +1053,8 @@ public enum Status { /** * Only tokens of type FUNGIBLE_COMMON can have fractional fees */ - CUSTOM_FRACTIONAL_FEE_ONLY_ALLOWED_FOR_FUNGIBLE_COMMON(ResponseCodeEnum.CUSTOM_FRACTIONAL_FEE_ONLY_ALLOWED_FOR_FUNGIBLE_COMMON), + CUSTOM_FRACTIONAL_FEE_ONLY_ALLOWED_FOR_FUNGIBLE_COMMON( + ResponseCodeEnum.CUSTOM_FRACTIONAL_FEE_ONLY_ALLOWED_FOR_FUNGIBLE_COMMON), /** * The provided custom fee schedule key was invalid @@ -1112,7 +1094,8 @@ public enum Status { /** * An AccountAmount token transfers list referenced a token type other than FUNGIBLE_COMMON */ - ACCOUNT_AMOUNT_TRANSFERS_ONLY_ALLOWED_FOR_FUNGIBLE_COMMON(ResponseCodeEnum.ACCOUNT_AMOUNT_TRANSFERS_ONLY_ALLOWED_FOR_FUNGIBLE_COMMON), + ACCOUNT_AMOUNT_TRANSFERS_ONLY_ALLOWED_FOR_FUNGIBLE_COMMON( + ResponseCodeEnum.ACCOUNT_AMOUNT_TRANSFERS_ONLY_ALLOWED_FOR_FUNGIBLE_COMMON), /** * All the NFTs allowed in the current price regime have already been minted @@ -1137,7 +1120,8 @@ public enum Status { /** * The sender account in the token transfer transaction could not afford a custom fee */ - INSUFFICIENT_SENDER_ACCOUNT_BALANCE_FOR_CUSTOM_FEE(ResponseCodeEnum.INSUFFICIENT_SENDER_ACCOUNT_BALANCE_FOR_CUSTOM_FEE), + INSUFFICIENT_SENDER_ACCOUNT_BALANCE_FOR_CUSTOM_FEE( + ResponseCodeEnum.INSUFFICIENT_SENDER_ACCOUNT_BALANCE_FOR_CUSTOM_FEE), /** * Currently no more than 4,294,967,295 NFTs may be minted for a given unique token type @@ -1147,7 +1131,8 @@ public enum Status { /** * Only tokens of type NON_FUNGIBLE_UNIQUE can have royalty fees */ - CUSTOM_ROYALTY_FEE_ONLY_ALLOWED_FOR_NON_FUNGIBLE_UNIQUE(ResponseCodeEnum.CUSTOM_ROYALTY_FEE_ONLY_ALLOWED_FOR_NON_FUNGIBLE_UNIQUE), + CUSTOM_ROYALTY_FEE_ONLY_ALLOWED_FOR_NON_FUNGIBLE_UNIQUE( + ResponseCodeEnum.CUSTOM_ROYALTY_FEE_ONLY_ALLOWED_FOR_NON_FUNGIBLE_UNIQUE), /** * The account has reached the limit on the automatic associations count. @@ -1157,13 +1142,15 @@ public enum Status { /** * Already existing automatic associations are more than the new maximum automatic associations. */ - EXISTING_AUTOMATIC_ASSOCIATIONS_EXCEED_GIVEN_LIMIT(ResponseCodeEnum.EXISTING_AUTOMATIC_ASSOCIATIONS_EXCEED_GIVEN_LIMIT), + EXISTING_AUTOMATIC_ASSOCIATIONS_EXCEED_GIVEN_LIMIT( + ResponseCodeEnum.EXISTING_AUTOMATIC_ASSOCIATIONS_EXCEED_GIVEN_LIMIT), /** * Cannot set the number of automatic associations for an account more than the maximum allowed * token associations tokens.maxPerAccount. */ - REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT(ResponseCodeEnum.REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT), + REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT( + ResponseCodeEnum.REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT), /** * Token is paused. This Token cannot be a part of any kind of Transaction until unpaused. @@ -1393,7 +1380,8 @@ public enum Status { /** * The scheduled transaction could not be created because it's expiration_time was less than or equal to the consensus time. */ - SCHEDULE_EXPIRATION_TIME_MUST_BE_HIGHER_THAN_CONSENSUS_TIME(ResponseCodeEnum.SCHEDULE_EXPIRATION_TIME_MUST_BE_HIGHER_THAN_CONSENSUS_TIME), + SCHEDULE_EXPIRATION_TIME_MUST_BE_HIGHER_THAN_CONSENSUS_TIME( + ResponseCodeEnum.SCHEDULE_EXPIRATION_TIME_MUST_BE_HIGHER_THAN_CONSENSUS_TIME), /** * The scheduled transaction could not be created because it would cause throttles to be violated on the specified expiration_time. @@ -2109,8 +2097,8 @@ static Status valueOf(ResponseCodeEnum code) { case INVALID_GRPC_CERTIFICATE_HASH -> INVALID_GRPC_CERTIFICATE_HASH; case MISSING_EXPIRY_TIME -> MISSING_EXPIRY_TIME; case UNRECOGNIZED -> - // NOTE: Protobuf deserialization will not give us the code on the wire - throw new IllegalArgumentException( + // NOTE: Protobuf deserialization will not give us the code on the wire + throw new IllegalArgumentException( "network returned unrecognized response code; your SDK may be out of date"); }; } diff --git a/sdk/src/main/java/org/hiero/sdk/StorageChange.java b/sdk/src/main/java/org/hiero/sdk/StorageChange.java new file mode 100644 index 0000000000..291b73e93d --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/StorageChange.java @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import java.math.BigInteger; +import javax.annotation.Nullable; + +/** + * @deprecated - User mirror nodes for contract traceability instead + * + * A storage slot change description. + * See Hedera Documentation + */ +@Deprecated +public class StorageChange { + /** + * The storage slot changed. Up to 32 bytes, big-endian, zero bytes left trimmed + */ + public final BigInteger slot; + /** + * The value read from the storage slot. Up to 32 bytes, big-endian, zero + * bytes left trimmed. Because of the way SSTORE operations are charged + * the slot is always read before being written to + */ + public final BigInteger valueRead; + /** + * The new value written to the slot. Up to 32 bytes, big-endian, zero + * bytes left trimmed. If a value of zero is written the valueWritten + * will be present but the inner value will be absent. If a value was + * read and not written this value will not be present. + */ + @Nullable + public final BigInteger valueWritten; + + /** + * Constructor. + * + * @param slot the storage slot charged + * @param valueRead the value read + * @param valueWritten the value written + */ + StorageChange(BigInteger slot, BigInteger valueRead, @Nullable BigInteger valueWritten) { + this.slot = slot; + this.valueRead = valueRead; + this.valueWritten = valueWritten; + } + + // /** + // * Create a storage charge from a protobuf. + // * + // * @param storageChangeProto the protobuf + // * @return the new storage charge object + // */ + // static StorageChange fromProtobuf(org.hiero.sdk.proto.StorageChange storageChangeProto) { + // return new StorageChange( + // new BigInteger(storageChangeProto.getSlot().toByteArray()), + // new BigInteger(storageChangeProto.getValueRead().toByteArray()), + // storageChangeProto.hasValueWritten() ? ( + // storageChangeProto.getValueWritten().getValue().size() == 0 ? + // BigInteger.ZERO : + // new BigInteger(storageChangeProto.getValueWritten().getValue().toByteArray()) + // ) : null + // ); + // } + // + // /** + // * Create a storage charge from a byte array. + // * + // * @param bytes the byte array + // * @return the new storage charge object + // * @throws InvalidProtocolBufferException when there is an issue with the protobuf + // */ + // public static StorageChange fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + // return fromProtobuf(org.hiero.sdk.proto.StorageChange.parseFrom(bytes)); + // } + // + // /** + // * Create the byte array. + // * + // * @return the byte array representation + // */ + // org.hiero.sdk.proto.StorageChange toProtobuf() { + // var builder = org.hiero.sdk.proto.StorageChange.newBuilder() + // .setSlot(ByteString.copyFrom(slot.toByteArray())) + // .setValueRead(ByteString.copyFrom(valueRead.toByteArray())); + // if (valueWritten != null) { + // if (valueWritten.equals(BigInteger.ZERO)) { + // builder.setValueWritten(BytesValue.newBuilder().setValue(ByteString.EMPTY).build()); + // } else { + // + // builder.setValueWritten(BytesValue.newBuilder().setValue(ByteString.copyFrom(valueWritten.toByteArray())).build()); + // } + // } + // return builder.build(); + // } + // + // /** + // * Create the byte array. + // * + // * @return the byte array representation + // */ + // public byte[] toBytes() { + // return toProtobuf().toByteArray(); + // } +} diff --git a/sdk/src/main/java/org/hiero/sdk/SubscriptionHandle.java b/sdk/src/main/java/org/hiero/sdk/SubscriptionHandle.java new file mode 100644 index 0000000000..51b4f9b120 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/SubscriptionHandle.java @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import javax.annotation.Nullable; + +/** + * Subscribe to a topic ID's messages from a mirror node. You will receive + * all messages for the specified topic or within the defined start and end + * time. + * + * See Hedera Documentation + */ +public final class SubscriptionHandle { + @Nullable + private Runnable onUnsubscribe; + + /** + * Constructor. + */ + SubscriptionHandle() {} + + /** + * Assign the callback method. + * + * @param onUnsubscribe the callback method + */ + void setOnUnsubscribe(Runnable onUnsubscribe) { + this.onUnsubscribe = onUnsubscribe; + } + + /** + * Call the callback. + */ + public void unsubscribe() { + var unsubscribe = this.onUnsubscribe; + + // Set onUnsubscribe back to null to make sure it is run just once. + this.onUnsubscribe = null; + + if (unsubscribe != null) { + unsubscribe.run(); + } + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/SystemDeleteTransaction.java b/sdk/src/main/java/org/hiero/sdk/SystemDeleteTransaction.java similarity index 77% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/SystemDeleteTransaction.java rename to sdk/src/main/java/org/hiero/sdk/SystemDeleteTransaction.java index 747c0d627c..33c4b94a69 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/SystemDeleteTransaction.java +++ b/sdk/src/main/java/org/hiero/sdk/SystemDeleteTransaction.java @@ -1,37 +1,19 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.FileServiceGrpc; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.SmartContractServiceGrpc; -import com.hedera.hashgraph.sdk.proto.SystemDeleteTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; import io.grpc.MethodDescriptor; import java.time.Instant; import java.util.LinkedHashMap; import java.util.Objects; import java.util.concurrent.CompletableFuture; import javax.annotation.Nullable; +import org.hiero.sdk.proto.FileServiceGrpc; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.SmartContractServiceGrpc; +import org.hiero.sdk.proto.SystemDeleteTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; /** * Delete a file or smart contract - can only be done with a Hedera admin. @@ -47,16 +29,17 @@ public final class SystemDeleteTransaction extends Transaction { @Nullable private FileId fileId = null; + @Nullable private ContractId contractId = null; + @Nullable private Instant expirationTime = null; /** * Constructor. */ - public SystemDeleteTransaction() { - } + public SystemDeleteTransaction() {} /** * Constructor. @@ -65,7 +48,8 @@ public SystemDeleteTransaction() { * records * @throws InvalidProtocolBufferException when there is an issue with the protobuf */ - SystemDeleteTransaction(LinkedHashMap> txs) throws InvalidProtocolBufferException { + SystemDeleteTransaction(LinkedHashMap> txs) + throws InvalidProtocolBufferException { super(txs); initFromTransactionBody(); } @@ -75,7 +59,7 @@ public SystemDeleteTransaction() { * * @param txBody protobuf TransactionBody */ - SystemDeleteTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { + SystemDeleteTransaction(org.hiero.sdk.proto.TransactionBody txBody) { super(txBody); initFromTransactionBody(); } @@ -161,7 +145,7 @@ public SystemDeleteTransaction setExpirationTime(Instant expirationTime) { /** * Build the transaction body. * - * @return {@link com.hedera.hashgraph.sdk.proto.SystemDeleteTransactionBody} + * @return {@link org.hiero.sdk.proto.SystemDeleteTransactionBody} */ SystemDeleteTransactionBody.Builder build() { var builder = SystemDeleteTransactionBody.newBuilder(); @@ -209,13 +193,14 @@ void validateChecksums(Client client) throws BadEntityIdException { CompletableFuture onExecuteAsync(Client client) { int modesEnabled = (fileId != null ? 1 : 0) + (contractId != null ? 1 : 0); if (modesEnabled != 1) { - throw new IllegalStateException("SystemDeleteTransaction must have exactly 1 of the following fields set: contractId, fileId"); + throw new IllegalStateException( + "SystemDeleteTransaction must have exactly 1 of the following fields set: contractId, fileId"); } return super.onExecuteAsync(client); } @Override - MethodDescriptor getMethodDescriptor() { + MethodDescriptor getMethodDescriptor() { if (fileId != null) { return FileServiceGrpc.getSystemDeleteMethod(); } else { diff --git a/sdk/src/main/java/org/hiero/sdk/SystemUndeleteTransaction.java b/sdk/src/main/java/org/hiero/sdk/SystemUndeleteTransaction.java new file mode 100644 index 0000000000..973cde3c41 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/SystemUndeleteTransaction.java @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.grpc.MethodDescriptor; +import java.util.LinkedHashMap; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.FileServiceGrpc; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.SmartContractServiceGrpc; +import org.hiero.sdk.proto.SystemUndeleteTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; + +/** + * Undelete a file or smart contract that was deleted by AdminDelete. + *

+ * Can only be done with a Hedera admin. + */ +public final class SystemUndeleteTransaction extends Transaction { + @Nullable + private FileId fileId; + + @Nullable + private ContractId contractId; + + /** + * Constructor. + */ + public SystemUndeleteTransaction() {} + + /** + * Constructor. + * + * @param txs Compound list of transaction id's list of (AccountId, Transaction) + * records + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + SystemUndeleteTransaction( + LinkedHashMap> txs) + throws InvalidProtocolBufferException { + super(txs); + initFromTransactionBody(); + } + + /** + * Constructor. + * + * @param txBody protobuf TransactionBody + */ + SystemUndeleteTransaction(org.hiero.sdk.proto.TransactionBody txBody) { + super(txBody); + initFromTransactionBody(); + } + + /** + * Extract the file id. + * + * @return the file id + */ + @Nullable + public final FileId getFileId() { + return fileId; + } + + /** + * Sets the file ID to undelete. + *

+ * Mutually exclusive with {@link #setContractId(ContractId)}. + * + * @param fileId The FileId to be set + * @return {@code this} + */ + public final SystemUndeleteTransaction setFileId(FileId fileId) { + Objects.requireNonNull(fileId); + requireNotFrozen(); + this.fileId = fileId; + return this; + } + + /** + * The contract ID instance to undelete, in the format used in transactions + * + * @return the contractId + */ + @Nullable + public ContractId getContractId() { + return contractId; + } + + /** + * Sets the contract ID to undelete. + *

+ * Mutually exclusive with {@link #setFileId(FileId)}. + * + * @param contractId The ContractId to be set + * @return {@code this} + */ + public final SystemUndeleteTransaction setContractId(ContractId contractId) { + Objects.requireNonNull(contractId); + requireNotFrozen(); + this.contractId = contractId; + return this; + } + + /** + * Initialize from the transaction body. + */ + void initFromTransactionBody() { + var body = sourceTransactionBody.getSystemUndelete(); + if (body.hasFileID()) { + fileId = FileId.fromProtobuf(body.getFileID()); + } + if (body.hasContractID()) { + contractId = ContractId.fromProtobuf(body.getContractID()); + } + } + + /** + * Build the transaction body. + * + * @return {@link org.hiero.sdk.proto.SystemUndeleteTransactionBody} + */ + SystemUndeleteTransactionBody.Builder build() { + var builder = SystemUndeleteTransactionBody.newBuilder(); + if (fileId != null) { + builder.setFileID(fileId.toProtobuf()); + } + if (contractId != null) { + builder.setContractID(contractId.toProtobuf()); + } + + return builder; + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (fileId != null) { + fileId.validateChecksum(client); + } + + if (contractId != null) { + contractId.validateChecksum(client); + } + } + + @Override + CompletableFuture onExecuteAsync(Client client) { + int modesEnabled = (fileId != null ? 1 : 0) + (contractId != null ? 1 : 0); + if (modesEnabled != 1) { + throw new IllegalStateException( + "SystemDeleteTransaction must have exactly 1 of the following fields set: contractId, fileId"); + } + return super.onExecuteAsync(client); + } + + @Override + MethodDescriptor getMethodDescriptor() { + if (fileId != null) { + return FileServiceGrpc.getSystemUndeleteMethod(); + } else { + return SmartContractServiceGrpc.getSystemUndeleteMethod(); + } + } + + @Override + void onFreeze(TransactionBody.Builder bodyBuilder) { + bodyBuilder.setSystemUndelete(build()); + } + + @Override + void onScheduled(SchedulableTransactionBody.Builder scheduled) { + scheduled.setSystemUndelete(build()); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/ThreadLocalSecureRandom.java b/sdk/src/main/java/org/hiero/sdk/ThreadLocalSecureRandom.java new file mode 100644 index 0000000000..0acc94dbfb --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/ThreadLocalSecureRandom.java @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import java.security.SecureRandom; + +/** + * Internal utility class. + */ +final class ThreadLocalSecureRandom { + @SuppressWarnings("AnonymousHasLambdaAlternative") + private static final ThreadLocal secureRandom = new ThreadLocal() { + @Override + protected SecureRandom initialValue() { + return new SecureRandom(); + } + }; + + /** + * Constructor. + */ + private ThreadLocalSecureRandom() {} + + /** + * Extract seme randomness. + * + * @return some randomness + */ + static SecureRandom current() { + return secureRandom.get(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/TokenAirdropTransaction.java b/sdk/src/main/java/org/hiero/sdk/TokenAirdropTransaction.java new file mode 100644 index 0000000000..2a023af712 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TokenAirdropTransaction.java @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.grpc.MethodDescriptor; +import java.util.LinkedHashMap; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenAirdropTransactionBody; +import org.hiero.sdk.proto.TokenServiceGrpc; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; + +/** + * Token Airdrop + * An "airdrop" is a distribution of tokens from a funding account + * to one or more recipient accounts, ideally with no action required + * by the recipient account(s). + */ +public class TokenAirdropTransaction extends AbstractTokenTransferTransaction { + /** + * Constructor. + */ + public TokenAirdropTransaction() { + super(); + defaultMaxTransactionFee = new Hbar(1); + } + + /** + * Constructor. + * + * @param txs Compound list of transaction id's list of (AccountId, Transaction) + * records + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + TokenAirdropTransaction(LinkedHashMap> txs) + throws InvalidProtocolBufferException { + super(txs); + initFromTransactionBody(); + } + + /** + * Constructor. + * + * @param txBody protobuf TransactionBody + */ + TokenAirdropTransaction(org.hiero.sdk.proto.TransactionBody txBody) { + super(txBody); + initFromTransactionBody(); + } + + /** + * Build the transaction body. + * + * @return {@link + * org.hiero.sdk.proto.TokenAirdropTransactionBody} + */ + TokenAirdropTransactionBody.Builder build() { + var transfers = sortTransfersAndBuild(); + var builder = TokenAirdropTransactionBody.newBuilder(); + + for (var transfer : transfers) { + builder.addTokenTransfers(transfer.toProtobuf()); + } + + return builder; + } + + @Override + MethodDescriptor getMethodDescriptor() { + return TokenServiceGrpc.getAirdropTokensMethod(); + } + + @Override + void onFreeze(TransactionBody.Builder bodyBuilder) { + bodyBuilder.setTokenAirdrop(build()); + } + + @Override + void onScheduled(SchedulableTransactionBody.Builder scheduled) { + scheduled.setTokenAirdrop(build()); + } + + /** + * Initialize from the transaction body. + */ + void initFromTransactionBody() { + var body = sourceTransactionBody.getTokenAirdrop(); + + for (var tokenTransferList : body.getTokenTransfersList()) { + var token = TokenId.fromProtobuf(tokenTransferList.getToken()); + + for (var transfer : tokenTransferList.getTransfersList()) { + tokenTransfers.add(new TokenTransfer( + token, + AccountId.fromProtobuf(transfer.getAccountID()), + transfer.getAmount(), + tokenTransferList.hasExpectedDecimals() + ? tokenTransferList.getExpectedDecimals().getValue() + : null, + transfer.getIsApproval())); + } + + for (var transfer : tokenTransferList.getNftTransfersList()) { + nftTransfers.add(new TokenNftTransfer( + token, + AccountId.fromProtobuf(transfer.getSenderAccountID()), + AccountId.fromProtobuf(transfer.getReceiverAccountID()), + transfer.getSerialNumber(), + transfer.getIsApproval())); + } + } + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/TokenAllowance.java b/sdk/src/main/java/org/hiero/sdk/TokenAllowance.java new file mode 100644 index 0000000000..23ed563cef --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TokenAllowance.java @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import com.google.protobuf.InvalidProtocolBufferException; +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.GrantedTokenAllowance; + +/** + * An approved allowance of token transfers for a spender. + * + * See Hedera Documentation + */ +public class TokenAllowance { + /** + * The token that the allowance pertains to + */ + @Nullable + public final TokenId tokenId; + /** + * The account ID of the hbar owner (ie. the grantor of the allowance) + */ + @Nullable + public final AccountId ownerAccountId; + /** + * The account ID of the spender of the hbar allowance + */ + @Nullable + public final AccountId spenderAccountId; + /** + * The amount of the spender's token allowance + */ + public final long amount; + + /** + * Constructor. + * + * @param tokenId the token id + * @param ownerAccountId the grantor account id + * @param spenderAccountId the spender account id + * @param amount the token allowance + */ + TokenAllowance( + @Nullable TokenId tokenId, + @Nullable AccountId ownerAccountId, + @Nullable AccountId spenderAccountId, + long amount) { + this.tokenId = tokenId; + this.ownerAccountId = ownerAccountId; + this.spenderAccountId = spenderAccountId; + this.amount = amount; + } + + /** + * Create a token allowance from a protobuf. + * + * @param allowanceProto the protobuf + * @return the new token allowance + */ + static TokenAllowance fromProtobuf(org.hiero.sdk.proto.TokenAllowance allowanceProto) { + return new TokenAllowance( + allowanceProto.hasTokenId() ? TokenId.fromProtobuf(allowanceProto.getTokenId()) : null, + allowanceProto.hasOwner() ? AccountId.fromProtobuf(allowanceProto.getOwner()) : null, + allowanceProto.hasSpender() ? AccountId.fromProtobuf(allowanceProto.getSpender()) : null, + allowanceProto.getAmount()); + } + + /** + * Create a token allowance from a protobuf. + * + * @param allowanceProto the protobuf + * @return the new token allowance + */ + static TokenAllowance fromProtobuf(GrantedTokenAllowance allowanceProto) { + return new TokenAllowance( + allowanceProto.hasTokenId() ? TokenId.fromProtobuf(allowanceProto.getTokenId()) : null, + null, + allowanceProto.hasSpender() ? AccountId.fromProtobuf(allowanceProto.getSpender()) : null, + allowanceProto.getAmount()); + } + + /** + * Create a token allowance from a byte array. + * + * @param bytes the byte array + * @return the new token allowance + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + public static TokenAllowance fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + return fromProtobuf(org.hiero.sdk.proto.TokenAllowance.parseFrom(Objects.requireNonNull(bytes))); + } + + /** + * Validate the configured client. + * + * @param client the configured client + * @throws BadEntityIdException if entity ID is formatted poorly + */ + void validateChecksums(Client client) throws BadEntityIdException { + if (tokenId != null) { + tokenId.validateChecksum(client); + } + if (ownerAccountId != null) { + ownerAccountId.validateChecksum(client); + } + if (spenderAccountId != null) { + spenderAccountId.validateChecksum(client); + } + } + + /** + * Create the protobuf. + * + * @return the protobuf representation + */ + org.hiero.sdk.proto.TokenAllowance toProtobuf() { + var builder = org.hiero.sdk.proto.TokenAllowance.newBuilder().setAmount(amount); + if (tokenId != null) { + builder.setTokenId(tokenId.toProtobuf()); + } + if (ownerAccountId != null) { + builder.setOwner(ownerAccountId.toProtobuf()); + } + if (spenderAccountId != null) { + builder.setSpender(spenderAccountId.toProtobuf()); + } + return builder.build(); + } + + /** + * Create the byte array. + * + * @return the protobuf representation + */ + GrantedTokenAllowance toGrantedProtobuf() { + var builder = GrantedTokenAllowance.newBuilder().setAmount(amount); + if (tokenId != null) { + builder.setTokenId(tokenId.toProtobuf()); + } + if (spenderAccountId != null) { + builder.setSpender(spenderAccountId.toProtobuf()); + } + return builder.build(); + } + + /** + * Create the byte array. + * + * @return the byte array representation + */ + public byte[] toBytes() { + return toProtobuf().toByteArray(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("tokenId", tokenId) + .add("ownerAccountId", ownerAccountId) + .add("spenderAccountId", spenderAccountId) + .add("amount", amount) + .toString(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/TokenAssociateTransaction.java b/sdk/src/main/java/org/hiero/sdk/TokenAssociateTransaction.java new file mode 100644 index 0000000000..3d1019eac3 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TokenAssociateTransaction.java @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.grpc.MethodDescriptor; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenAssociateTransactionBody; +import org.hiero.sdk.proto.TokenServiceGrpc; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; + +/** + * The transaction that will associate accounts to a token id. + */ +public class TokenAssociateTransaction extends Transaction { + @Nullable + private AccountId accountId = null; + + private List tokenIds = new ArrayList<>(); + + /** + * Constructor. + */ + public TokenAssociateTransaction() { + defaultMaxTransactionFee = new Hbar(5); + } + + /** + * Constructor. + * + * @param txs Compound list of transaction id's list of (AccountId, Transaction) + * records + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + TokenAssociateTransaction( + LinkedHashMap> txs) + throws InvalidProtocolBufferException { + super(txs); + initFromTransactionBody(); + } + + /** + * Constructor. + * + * @param txBody protobuf TransactionBody + */ + TokenAssociateTransaction(org.hiero.sdk.proto.TransactionBody txBody) { + super(txBody); + initFromTransactionBody(); + } + + /** + * Extract the account id. + * + * @return the account id + */ + @Nullable + public AccountId getAccountId() { + return accountId; + } + + /** + * Assign the account id. + * + * @param accountId the account id + * @return {@code this} + */ + public TokenAssociateTransaction setAccountId(AccountId accountId) { + Objects.requireNonNull(accountId); + requireNotFrozen(); + this.accountId = accountId; + return this; + } + + /** + * Extract the list of token id's. + * + * @return the list of token id's + */ + public List getTokenIds() { + return new ArrayList<>(tokenIds); + } + + /** + * Assign a new list of token id's. + * + * @param tokens the list of token id's + * @return {@code this} + */ + public TokenAssociateTransaction setTokenIds(List tokens) { + Objects.requireNonNull(tokens); + requireNotFrozen(); + this.tokenIds = new ArrayList<>(tokens); + return this; + } + + /** + * Build the transaction body. + * + * @return {@link org.hiero.sdk.proto.TokenAssociateTransactionBody} + */ + TokenAssociateTransactionBody.Builder build() { + var builder = TokenAssociateTransactionBody.newBuilder(); + if (accountId != null) { + builder.setAccount(accountId.toProtobuf()); + } + + for (var token : tokenIds) { + if (token != null) { + builder.addTokens(token.toProtobuf()); + } + } + + return builder; + } + + /** + * Initialize from the transaction body. + */ + void initFromTransactionBody() { + var body = sourceTransactionBody.getTokenAssociate(); + if (body.hasAccount()) { + accountId = AccountId.fromProtobuf(body.getAccount()); + } + + for (var token : body.getTokensList()) { + tokenIds.add(TokenId.fromProtobuf(token)); + } + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + Objects.requireNonNull(client); + if (accountId != null) { + accountId.validateChecksum(client); + } + + for (var token : tokenIds) { + if (token != null) { + token.validateChecksum(client); + } + } + } + + @Override + MethodDescriptor getMethodDescriptor() { + return TokenServiceGrpc.getAssociateTokensMethod(); + } + + @Override + void onFreeze(TransactionBody.Builder bodyBuilder) { + bodyBuilder.setTokenAssociate(build()); + } + + @Override + void onScheduled(SchedulableTransactionBody.Builder scheduled) { + scheduled.setTokenAssociate(build()); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/TokenAssociation.java b/sdk/src/main/java/org/hiero/sdk/TokenAssociation.java new file mode 100644 index 0000000000..2f986b081c --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TokenAssociation.java @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import com.google.protobuf.InvalidProtocolBufferException; + +/** + * Associates the provided Hedera account with the provided Hedera token(s). + * Hedera accounts must be associated with a fungible or non-fungible token + * first before you can transfer tokens to that account. In the case of + * NON_FUNGIBLE Type, once an account is associated, it can hold any number + * of NFTs (serial numbers) of that token type. The Hedera account that is + * being associated with a token is required to sign the transaction. + * + * See Hedera Documentation + */ +public class TokenAssociation { + + /** + * The token involved in the association + */ + public final TokenId tokenId; + + /** + * The account involved in the association + */ + public final AccountId accountId; + + /** + * Constructor. + * + * @param tokenId the token id + * @param accountId the account id + */ + TokenAssociation(TokenId tokenId, AccountId accountId) { + this.tokenId = tokenId; + this.accountId = accountId; + } + + /** + * Create a token association from a protobuf. + * + * @param tokenAssociation the protobuf + * @return the new token association + */ + static TokenAssociation fromProtobuf(org.hiero.sdk.proto.TokenAssociation tokenAssociation) { + return new TokenAssociation( + tokenAssociation.hasTokenId() + ? TokenId.fromProtobuf(tokenAssociation.getTokenId()) + : new TokenId(0, 0, 0), + tokenAssociation.hasAccountId() + ? AccountId.fromProtobuf(tokenAssociation.getAccountId()) + : new AccountId(0, 0, 0)); + } + + /** + * Create a token association from a byte array. + * + * @param bytes the byte array + * @return the new token association + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + public static TokenAssociation fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + return fromProtobuf(org.hiero.sdk.proto.TokenAssociation.parseFrom(bytes)); + } + + /** + * Create the protobuf. + * + * @return the protobuf representation + */ + org.hiero.sdk.proto.TokenAssociation toProtobuf() { + return org.hiero.sdk.proto.TokenAssociation.newBuilder() + .setTokenId(tokenId.toProtobuf()) + .setAccountId(accountId.toProtobuf()) + .build(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("tokenId", tokenId) + .add("accountId", accountId) + .toString(); + } + + /** + * Create the byte array. + * + * @return the byte array representation + */ + public byte[] toBytes() { + return toProtobuf().toByteArray(); + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenBurnTransaction.java b/sdk/src/main/java/org/hiero/sdk/TokenBurnTransaction.java similarity index 78% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/TokenBurnTransaction.java rename to sdk/src/main/java/org/hiero/sdk/TokenBurnTransaction.java index ce2c3c3192..8976ae3ea8 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenBurnTransaction.java +++ b/sdk/src/main/java/org/hiero/sdk/TokenBurnTransaction.java @@ -1,39 +1,20 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenBurnTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenServiceGrpc; -import com.hedera.hashgraph.sdk.proto.Transaction; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; import io.grpc.MethodDescriptor; - -import javax.annotation.Nonnegative; -import javax.annotation.Nullable; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Objects; +import javax.annotation.Nonnegative; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenBurnTransactionBody; +import org.hiero.sdk.proto.TokenServiceGrpc; +import org.hiero.sdk.proto.Transaction; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; /** * Burns fungible and non-fungible tokens owned by the Treasury Account. @@ -42,7 +23,7 @@ * * See Hedera Documentation */ -public class TokenBurnTransaction extends com.hedera.hashgraph.sdk.Transaction { +public class TokenBurnTransaction extends org.hiero.sdk.Transaction { /** * The ID of the token to burn supply */ @@ -60,8 +41,7 @@ public class TokenBurnTransaction extends com.hedera.hashgraph.sdk.Transaction> txs) throws InvalidProtocolBufferException { + TokenBurnTransaction(LinkedHashMap> txs) + throws InvalidProtocolBufferException { super(txs); initFromTransactionBody(); } @@ -80,7 +61,7 @@ public TokenBurnTransaction() { * * @param txBody protobuf TransactionBody */ - TokenBurnTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { + TokenBurnTransaction(org.hiero.sdk.proto.TransactionBody txBody) { super(txBody); initFromTransactionBody(); } @@ -174,7 +155,7 @@ public TokenBurnTransaction addSerial(@Nonnegative long serial) { /** * Build the transaction body. * - * @return {@link com.hedera.hashgraph.sdk.proto.TokenBurnTransactionBody} + * @return {@link org.hiero.sdk.proto.TokenBurnTransactionBody} */ TokenBurnTransactionBody.Builder build() { var builder = TokenBurnTransactionBody.newBuilder(); diff --git a/sdk/src/main/java/org/hiero/sdk/TokenCancelAirdropTransaction.java b/sdk/src/main/java/org/hiero/sdk/TokenCancelAirdropTransaction.java new file mode 100644 index 0000000000..3383adb7cd --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TokenCancelAirdropTransaction.java @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.grpc.MethodDescriptor; +import java.util.LinkedHashMap; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenCancelAirdropTransactionBody; +import org.hiero.sdk.proto.TokenServiceGrpc; +import org.hiero.sdk.proto.TransactionBody.Builder; +import org.hiero.sdk.proto.TransactionResponse; + +public class TokenCancelAirdropTransaction extends PendingAirdropLogic { + + /** + * Constructor. + */ + public TokenCancelAirdropTransaction() { + defaultMaxTransactionFee = Hbar.from(1); + } + + /** + * Constructor. + * + * @param txs Compound list of transaction id's list of (AccountId, Transaction) records + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + TokenCancelAirdropTransaction( + LinkedHashMap> txs) + throws InvalidProtocolBufferException { + super(txs); + initFromTransactionBody(); + } + + /** + * Constructor. + * + * @param txBody protobuf TransactionBody + */ + TokenCancelAirdropTransaction(org.hiero.sdk.proto.TransactionBody txBody) { + super(txBody); + initFromTransactionBody(); + } + + /** + * Build the transaction body. + * + * @return {@link org.hiero.sdk.proto.TokenCancelAirdropTransactionBody} + */ + TokenCancelAirdropTransactionBody.Builder build() { + var builder = TokenCancelAirdropTransactionBody.newBuilder(); + + for (var pendingAirdropId : pendingAirdropIds) { + builder.addPendingAirdrops(pendingAirdropId.toProtobuf()); + } + + return builder; + } + + /** + * Initialize from the transaction body. + */ + void initFromTransactionBody() { + var body = sourceTransactionBody.getTokenCancelAirdrop(); + for (var pendingAirdropId : body.getPendingAirdropsList()) { + this.pendingAirdropIds.add(PendingAirdropId.fromProtobuf(pendingAirdropId)); + } + } + + @Override + MethodDescriptor getMethodDescriptor() { + return TokenServiceGrpc.getCancelAirdropMethod(); + } + + @Override + void onFreeze(Builder bodyBuilder) { + bodyBuilder.setTokenCancelAirdrop(build()); + } + + @Override + void onScheduled(SchedulableTransactionBody.Builder scheduled) { + scheduled.setTokenCancelAirdrop(build()); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/TokenClaimAirdropTransaction.java b/sdk/src/main/java/org/hiero/sdk/TokenClaimAirdropTransaction.java new file mode 100644 index 0000000000..79ca0fcc8c --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TokenClaimAirdropTransaction.java @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.grpc.MethodDescriptor; +import java.util.LinkedHashMap; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenClaimAirdropTransactionBody; +import org.hiero.sdk.proto.TokenServiceGrpc; +import org.hiero.sdk.proto.TransactionBody.Builder; +import org.hiero.sdk.proto.TransactionResponse; + +public class TokenClaimAirdropTransaction extends PendingAirdropLogic { + + /** + * Constructor. + */ + public TokenClaimAirdropTransaction() { + defaultMaxTransactionFee = Hbar.from(1); + } + + /** + * Constructor. + * + * @param txs Compound list of transaction id's list of (AccountId, Transaction) records + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + TokenClaimAirdropTransaction( + LinkedHashMap> txs) + throws InvalidProtocolBufferException { + super(txs); + initFromTransactionBody(); + } + + /** + * Constructor. + * + * @param txBody protobuf TransactionBody + */ + TokenClaimAirdropTransaction(org.hiero.sdk.proto.TransactionBody txBody) { + super(txBody); + initFromTransactionBody(); + } + + /** + * Build the transaction body. + * + * @return {@link org.hiero.sdk.proto.TokenClaimAirdropTransactionBody} + */ + TokenClaimAirdropTransactionBody.Builder build() { + var builder = TokenClaimAirdropTransactionBody.newBuilder(); + + for (var pendingAirdropId : pendingAirdropIds) { + builder.addPendingAirdrops(pendingAirdropId.toProtobuf()); + } + + return builder; + } + + /** + * Initialize from the transaction body. + */ + void initFromTransactionBody() { + var body = sourceTransactionBody.getTokenClaimAirdrop(); + for (var pendingAirdropId : body.getPendingAirdropsList()) { + this.pendingAirdropIds.add(PendingAirdropId.fromProtobuf(pendingAirdropId)); + } + } + + @Override + MethodDescriptor getMethodDescriptor() { + return TokenServiceGrpc.getClaimAirdropMethod(); + } + + @Override + void onFreeze(Builder bodyBuilder) { + bodyBuilder.setTokenClaimAirdrop(build()); + } + + @Override + void onScheduled(SchedulableTransactionBody.Builder scheduled) { + scheduled.setTokenClaimAirdrop(build()); + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenCreateTransaction.java b/sdk/src/main/java/org/hiero/sdk/TokenCreateTransaction.java similarity index 93% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/TokenCreateTransaction.java rename to sdk/src/main/java/org/hiero/sdk/TokenCreateTransaction.java index 79f2f434d5..082289624a 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenCreateTransaction.java +++ b/sdk/src/main/java/org/hiero/sdk/TokenCreateTransaction.java @@ -1,41 +1,22 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenCreateTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenServiceGrpc; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; import io.grpc.MethodDescriptor; import java.time.Duration; import java.time.Instant; - -import javax.annotation.Nonnegative; -import javax.annotation.Nullable; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Objects; +import javax.annotation.Nonnegative; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenCreateTransactionBody; +import org.hiero.sdk.proto.TokenServiceGrpc; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; /** * Create a new fungible or non-fungible token (NFT) on the Hedera network. @@ -161,6 +142,7 @@ public class TokenCreateTransaction extends Transaction */ @Nullable private Instant expirationTime = null; + private Duration expirationTimeDuration = null; /** * The interval at which the auto-renew account will be charged to @@ -199,7 +181,7 @@ public class TokenCreateTransaction extends Transaction * Constructor. */ public TokenCreateTransaction() { - setAutoRenewPeriod(DEFAULT_AUTO_RENEW_PERIOD); + autoRenewPeriod = DEFAULT_AUTO_RENEW_PERIOD; defaultMaxTransactionFee = new Hbar(40); } @@ -210,7 +192,8 @@ public TokenCreateTransaction() { * records * @throws InvalidProtocolBufferException when there is an issue with the protobuf */ - TokenCreateTransaction(LinkedHashMap> txs) throws InvalidProtocolBufferException { + TokenCreateTransaction(LinkedHashMap> txs) + throws InvalidProtocolBufferException { super(txs); initFromTransactionBody(); } @@ -220,7 +203,7 @@ public TokenCreateTransaction() { * * @param txBody protobuf TransactionBody */ - TokenCreateTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { + TokenCreateTransaction(org.hiero.sdk.proto.TransactionBody txBody) { super(txBody); initFromTransactionBody(); } @@ -487,7 +470,7 @@ public Key getPauseKey() { /** * Assign the pause key. * - * @param key the pause key + * @param key the pause key * @return {@code this} */ public TokenCreateTransaction setPauseKey(Key key) { @@ -699,7 +682,7 @@ public TokenSupplyType getSupplyType() { /** * Assign the supply type. * - * @param supplyType the supply type + * @param supplyType the supply type * @return {@code this} */ public TokenCreateTransaction setSupplyType(TokenSupplyType supplyType) { @@ -753,12 +736,10 @@ public TokenCreateTransaction setTokenMetadata(byte[] tokenMetadata) { @Override public TokenCreateTransaction freezeWith(@Nullable Client client) { - if ( - autoRenewPeriod != null && - autoRenewAccountId == null && - client != null && - client.getOperatorAccountId() != null - ) { + if (autoRenewPeriod != null + && autoRenewAccountId == null + && client != null + && client.getOperatorAccountId() != null) { autoRenewAccountId = client.getOperatorAccountId(); } @@ -768,7 +749,7 @@ public TokenCreateTransaction freezeWith(@Nullable Client client) { /** * Build the transaction body. * - * @return {@link com.hedera.hashgraph.sdk.proto.TokenCreateTransactionBody} + * @return {@link org.hiero.sdk.proto.TokenCreateTransactionBody} */ TokenCreateTransactionBody.Builder build() { var builder = TokenCreateTransactionBody.newBuilder(); @@ -904,7 +885,7 @@ void validateChecksums(Client client) throws BadEntityIdException { } @Override - MethodDescriptor getMethodDescriptor() { + MethodDescriptor getMethodDescriptor() { return TokenServiceGrpc.getCreateTokenMethod(); } diff --git a/sdk/src/main/java/org/hiero/sdk/TokenDeleteTransaction.java b/sdk/src/main/java/org/hiero/sdk/TokenDeleteTransaction.java new file mode 100644 index 0000000000..7e5e20f61f --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TokenDeleteTransaction.java @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.grpc.MethodDescriptor; +import java.util.LinkedHashMap; +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenDeleteTransactionBody; +import org.hiero.sdk.proto.TokenServiceGrpc; +import org.hiero.sdk.proto.Transaction; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; + +/** + * Deleting a token marks a token as deleted, though it will remain in the + * ledger. The operation must be signed by the specified Admin Key of the + * Token. If the Admin Key is not set, Transaction will result in + * TOKEN_IS_IMMUTABlE. Once deleted update, mint, burn, wipe, freeze, + * unfreeze, grant kyc, revoke kyc and token transfer transactions will + * resolve to TOKEN_WAS_DELETED. + * + * See Hedera Documentation + */ +public class TokenDeleteTransaction extends org.hiero.sdk.Transaction { + @Nullable + private TokenId tokenId = null; + + /** + * Constructor. + */ + public TokenDeleteTransaction() {} + + /** + * Constructor. + * + * @param txs Compound list of transaction id's list of (AccountId, Transaction) + * records + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + TokenDeleteTransaction(LinkedHashMap> txs) + throws InvalidProtocolBufferException { + super(txs); + initFromTransactionBody(); + } + + /** + * Constructor. + * + * @param txBody protobuf TransactionBody + */ + TokenDeleteTransaction(org.hiero.sdk.proto.TransactionBody txBody) { + super(txBody); + initFromTransactionBody(); + } + + /** + * Extract the token id. + * + * @return the token id + */ + @Nullable + public TokenId getTokenId() { + return tokenId; + } + + /** + * Assign the token id. + * + * @param tokenId the token id + * @return {@code this} + */ + public TokenDeleteTransaction setTokenId(TokenId tokenId) { + Objects.requireNonNull(tokenId); + requireNotFrozen(); + this.tokenId = tokenId; + return this; + } + + /** + * Initialize from the transaction body. + */ + void initFromTransactionBody() { + var body = sourceTransactionBody.getTokenDeletion(); + if (body.hasToken()) { + tokenId = TokenId.fromProtobuf(body.getToken()); + } + } + + /** + * Build the transaction body. + * + * @return {@link + * org.hiero.sdk.proto.TokenDeleteTransactionBody} + */ + TokenDeleteTransactionBody.Builder build() { + var builder = TokenDeleteTransactionBody.newBuilder(); + if (tokenId != null) { + builder.setToken(tokenId.toProtobuf()); + } + + return builder; + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (tokenId != null) { + tokenId.validateChecksum(client); + } + } + + @Override + MethodDescriptor getMethodDescriptor() { + return TokenServiceGrpc.getDeleteTokenMethod(); + } + + @Override + void onFreeze(TransactionBody.Builder bodyBuilder) { + bodyBuilder.setTokenDeletion(build()); + } + + @Override + void onScheduled(SchedulableTransactionBody.Builder scheduled) { + scheduled.setTokenDeletion(build()); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/TokenDissociateTransaction.java b/sdk/src/main/java/org/hiero/sdk/TokenDissociateTransaction.java new file mode 100644 index 0000000000..3a01dd5798 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TokenDissociateTransaction.java @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.grpc.MethodDescriptor; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenDissociateTransactionBody; +import org.hiero.sdk.proto.TokenServiceGrpc; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; + +/** + * Disassociates the provided Hedera account from the provided Hedera tokens. + * This transaction must be signed by the provided account's key. Once the + * association is removed, no token related operation can be performed to that + * account. AccountBalanceQuery and AccountInfoQuery will not return anything + * related to the token that was disassociated. + * + * See Hedera Documentation + */ +public class TokenDissociateTransaction extends org.hiero.sdk.Transaction { + @Nullable + private AccountId accountId = null; + + private List tokenIds = new ArrayList<>(); + + /** + * Constructor. + */ + public TokenDissociateTransaction() { + defaultMaxTransactionFee = new Hbar(5); + } + + /** + * Constructor. + * + * @param txs Compound list of transaction id's list of (AccountId, Transaction) + * records + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + TokenDissociateTransaction( + LinkedHashMap> txs) + throws InvalidProtocolBufferException { + super(txs); + initFromTransactionBody(); + } + + /** + * Constructor. + * + * @param txBody protobuf TransactionBody + */ + TokenDissociateTransaction(org.hiero.sdk.proto.TransactionBody txBody) { + super(txBody); + initFromTransactionBody(); + } + + /** + * Extract the account id. + * + * @return the account id + */ + @Nullable + public AccountId getAccountId() { + return accountId; + } + + /** + * Assign the account id. + * + * @param accountId the account id + * @return {@code this} + */ + public TokenDissociateTransaction setAccountId(AccountId accountId) { + Objects.requireNonNull(accountId); + requireNotFrozen(); + this.accountId = accountId; + return this; + } + + /** + * Extract the list of token id's. + * + * @return the list of token id's + */ + public List getTokenIds() { + return new ArrayList<>(tokenIds); + } + + /** + * Assign the list of token id's. + * + * @param tokens the list of token id's. + * @return {@code this} + */ + public TokenDissociateTransaction setTokenIds(List tokens) { + requireNotFrozen(); + this.tokenIds = new ArrayList<>(tokens); + return this; + } + + /** + * Initialize from the transaction body. + */ + void initFromTransactionBody() { + var body = sourceTransactionBody.getTokenDissociate(); + if (body.hasAccount()) { + accountId = AccountId.fromProtobuf(body.getAccount()); + } + + for (var token : body.getTokensList()) { + tokenIds.add(TokenId.fromProtobuf(token)); + } + } + + /** + * Build the transaction body. + * + * @return {@link + * org.hiero.sdk.proto.TokenDissociateTransactionBody} + */ + TokenDissociateTransactionBody.Builder build() { + var builder = TokenDissociateTransactionBody.newBuilder(); + if (accountId != null) { + builder.setAccount(accountId.toProtobuf()); + } + + for (var token : tokenIds) { + if (token != null) { + builder.addTokens(token.toProtobuf()); + } + } + + return builder; + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (accountId != null) { + accountId.validateChecksum(client); + } + + for (var token : tokenIds) { + if (token != null) { + token.validateChecksum(client); + } + } + } + + @Override + MethodDescriptor getMethodDescriptor() { + return TokenServiceGrpc.getDissociateTokensMethod(); + } + + @Override + void onFreeze(TransactionBody.Builder bodyBuilder) { + bodyBuilder.setTokenDissociate(build()); + } + + @Override + void onScheduled(SchedulableTransactionBody.Builder scheduled) { + scheduled.setTokenDissociate(build()); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/TokenFeeScheduleUpdateTransaction.java b/sdk/src/main/java/org/hiero/sdk/TokenFeeScheduleUpdateTransaction.java new file mode 100644 index 0000000000..83ae65ec37 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TokenFeeScheduleUpdateTransaction.java @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.grpc.MethodDescriptor; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenFeeScheduleUpdateTransactionBody; +import org.hiero.sdk.proto.TokenServiceGrpc; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; + +/** + * Update the custom fees for a given token. If the token does not have a + * fee schedule, the network response returned will be + * CUSTOM_SCHEDULE_ALREADY_HAS_NO_FEES. You will need to sign the transaction + * with the fee schedule key to update the fee schedule for the token. If you + * do not have a fee schedule key set for the token, you will not be able to + * update the fee schedule. + * + * See Hedera Documentation + */ +public class TokenFeeScheduleUpdateTransaction extends Transaction { + @Nullable + private TokenId tokenId = null; + + private List customFees = new ArrayList<>(); + + /** + * Constructor. + */ + public TokenFeeScheduleUpdateTransaction() {} + + /** + * Constructor. + * + * @param txs Compound list of transaction id's list of (AccountId, Transaction) + * records + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + TokenFeeScheduleUpdateTransaction( + LinkedHashMap> txs) + throws InvalidProtocolBufferException { + super(txs); + initFromTransactionBody(); + } + + /** + * Constructor. + * + * @param txBody protobuf TransactionBody + */ + TokenFeeScheduleUpdateTransaction(org.hiero.sdk.proto.TransactionBody txBody) { + super(txBody); + initFromTransactionBody(); + } + + /** + * Extract the token id. + * + * @return the token id + */ + @Nullable + public TokenId getTokenId() { + return tokenId; + } + + /** + * Assign the token id. + * + * @param tokenId the token id + * @return {@code this} + */ + public TokenFeeScheduleUpdateTransaction setTokenId(TokenId tokenId) { + Objects.requireNonNull(tokenId); + requireNotFrozen(); + this.tokenId = tokenId; + return this; + } + + /** + * Extract the list of custom fees. + * + * @return the list of custom fees + */ + public List getCustomFees() { + return CustomFee.deepCloneList(customFees); + } + + /** + * Assign the list of custom fees. + * + * @param customFees the list of custom fees + * @return {@code this} + */ + public TokenFeeScheduleUpdateTransaction setCustomFees(List customFees) { + Objects.requireNonNull(customFees); + requireNotFrozen(); + this.customFees = CustomFee.deepCloneList(customFees); + return this; + } + + /** + * Initialize from the transaction body. + */ + void initFromTransactionBody() { + var body = sourceTransactionBody.getTokenFeeScheduleUpdate(); + if (body.hasTokenId()) { + tokenId = TokenId.fromProtobuf(body.getTokenId()); + } + + for (var fee : body.getCustomFeesList()) { + customFees.add(CustomFee.fromProtobuf(fee)); + } + } + + /** + * Build the transaction body. + * + * @return {@link + * org.hiero.sdk.proto.TokenFeeScheduleUpdateTransactionBody} + */ + TokenFeeScheduleUpdateTransactionBody.Builder build() { + var builder = TokenFeeScheduleUpdateTransactionBody.newBuilder(); + if (tokenId != null) { + builder.setTokenId(tokenId.toProtobuf()); + } + + builder.clearCustomFees(); + for (var fee : customFees) { + builder.addCustomFees(fee.toProtobuf()); + } + + return builder; + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (tokenId != null) { + tokenId.validateChecksum(client); + } + + for (CustomFee fee : customFees) { + fee.validateChecksums(client); + } + } + + @Override + MethodDescriptor getMethodDescriptor() { + return TokenServiceGrpc.getUpdateTokenFeeScheduleMethod(); + } + + @Override + void onFreeze(TransactionBody.Builder bodyBuilder) { + bodyBuilder.setTokenFeeScheduleUpdate(build()); + } + + @Override + void onScheduled(SchedulableTransactionBody.Builder scheduled) { + throw new UnsupportedOperationException("Cannot schedule TokenFeeScheduleUpdateTransaction"); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/TokenFreezeTransaction.java b/sdk/src/main/java/org/hiero/sdk/TokenFreezeTransaction.java new file mode 100644 index 0000000000..6d755dad17 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TokenFreezeTransaction.java @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.grpc.MethodDescriptor; +import java.util.LinkedHashMap; +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenFreezeAccountTransactionBody; +import org.hiero.sdk.proto.TokenServiceGrpc; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; + +/** + * Freezes transfers of the specified token for the account. + * + * The transaction must be signed by the token's Freeze Key. + * + * See Hedera Documentation + */ +public class TokenFreezeTransaction extends org.hiero.sdk.Transaction { + @Nullable + private TokenId tokenId = null; + + @Nullable + private AccountId accountId = null; + + /** + * Constructor. + */ + public TokenFreezeTransaction() {} + + /** + * Constructor. + * + * @param txs Compound list of transaction id's list of (AccountId, Transaction) + * records + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + TokenFreezeTransaction(LinkedHashMap> txs) + throws InvalidProtocolBufferException { + super(txs); + initFromTransactionBody(); + } + + /** + * Constructor. + * + * @param txBody protobuf TransactionBody + */ + TokenFreezeTransaction(org.hiero.sdk.proto.TransactionBody txBody) { + super(txBody); + initFromTransactionBody(); + } + + /** + * Extract the token id. + * + * @return the token id + */ + @Nullable + public TokenId getTokenId() { + return tokenId; + } + + /** + * Assign the token id. + * + * @param tokenId the token id + * @return {@code this} + */ + public TokenFreezeTransaction setTokenId(TokenId tokenId) { + Objects.requireNonNull(tokenId); + requireNotFrozen(); + this.tokenId = tokenId; + return this; + } + + /** + * Extract the account id. + * + * @return the account id + */ + @Nullable + public AccountId getAccountId() { + return accountId; + } + + /** + * Assign the account id. + * + * @param accountId the account id + * @return {@code this} + */ + public TokenFreezeTransaction setAccountId(AccountId accountId) { + Objects.requireNonNull(accountId); + requireNotFrozen(); + this.accountId = accountId; + return this; + } + + /** + * Initialize from the transaction body. + */ + void initFromTransactionBody() { + var body = sourceTransactionBody.getTokenFreeze(); + if (body.hasToken()) { + tokenId = TokenId.fromProtobuf(body.getToken()); + } + + if (body.hasAccount()) { + accountId = AccountId.fromProtobuf(body.getAccount()); + } + } + + /** + * Build the transaction body. + * + * @return {@link + * org.hiero.sdk.proto.TokenFreezeAccountTransactionBody} + */ + TokenFreezeAccountTransactionBody.Builder build() { + var builder = TokenFreezeAccountTransactionBody.newBuilder(); + if (tokenId != null) { + builder.setToken(tokenId.toProtobuf()); + } + + if (accountId != null) { + builder.setAccount(accountId.toProtobuf()); + } + + return builder; + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (tokenId != null) { + tokenId.validateChecksum(client); + } + + if (accountId != null) { + accountId.validateChecksum(client); + } + } + + @Override + MethodDescriptor getMethodDescriptor() { + return TokenServiceGrpc.getFreezeTokenAccountMethod(); + } + + @Override + void onFreeze(TransactionBody.Builder bodyBuilder) { + bodyBuilder.setTokenFreeze(build()); + } + + @Override + void onScheduled(SchedulableTransactionBody.Builder scheduled) { + scheduled.setTokenFreeze(build()); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/TokenGrantKycTransaction.java b/sdk/src/main/java/org/hiero/sdk/TokenGrantKycTransaction.java new file mode 100644 index 0000000000..21692fe2ee --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TokenGrantKycTransaction.java @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.grpc.MethodDescriptor; +import java.util.LinkedHashMap; +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenGrantKycTransactionBody; +import org.hiero.sdk.proto.TokenServiceGrpc; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; + +/** + * Grants KYC to the Hedera accounts for the given Hedera token. + * + * This transaction must be signed by the token's KYC Key. + * + * See Hedera Documentation + */ +public class TokenGrantKycTransaction extends org.hiero.sdk.Transaction { + @Nullable + private TokenId tokenId = null; + + @Nullable + private AccountId accountId = null; + + /** + * Configure. + */ + public TokenGrantKycTransaction() {} + + /** + * Constructor. + * + * @param txs Compound list of transaction id's list of (AccountId, Transaction) + * records + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + TokenGrantKycTransaction( + LinkedHashMap> txs) + throws InvalidProtocolBufferException { + super(txs); + initFromTransactionBody(); + } + + /** + * Constructor. + * + * @param txBody protobuf TransactionBody + */ + TokenGrantKycTransaction(org.hiero.sdk.proto.TransactionBody txBody) { + super(txBody); + initFromTransactionBody(); + } + + /** + * Extract the token id. + * + * @return the token id + */ + @Nullable + public TokenId getTokenId() { + return tokenId; + } + + /** + * Assign the token id. + * + * @param tokenId the token id + * @return {@code this} + */ + public TokenGrantKycTransaction setTokenId(TokenId tokenId) { + Objects.requireNonNull(tokenId); + requireNotFrozen(); + this.tokenId = tokenId; + return this; + } + + /** + * Extract the account id. + * + * @return the account id + */ + @Nullable + public AccountId getAccountId() { + return accountId; + } + + /** + * Assign the account id. + * + * @param accountId the account id + * @return {@code this} + */ + public TokenGrantKycTransaction setAccountId(AccountId accountId) { + Objects.requireNonNull(accountId); + requireNotFrozen(); + this.accountId = accountId; + return this; + } + + /** + * Initialize from the transaction body. + */ + void initFromTransactionBody() { + var body = sourceTransactionBody.getTokenGrantKyc(); + if (body.hasToken()) { + tokenId = TokenId.fromProtobuf(body.getToken()); + } + + if (body.hasAccount()) { + accountId = AccountId.fromProtobuf(body.getAccount()); + } + } + + /** + * Build the transaction body. + * + * @return {@link + * org.hiero.sdk.proto.TokenGrantKycTransactionBody} + */ + TokenGrantKycTransactionBody.Builder build() { + var builder = TokenGrantKycTransactionBody.newBuilder(); + if (tokenId != null) { + builder.setToken(tokenId.toProtobuf()); + } + + if (accountId != null) { + builder.setAccount(accountId.toProtobuf()); + } + + return builder; + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (tokenId != null) { + tokenId.validateChecksum(client); + } + + if (accountId != null) { + accountId.validateChecksum(client); + } + } + + @Override + MethodDescriptor getMethodDescriptor() { + return TokenServiceGrpc.getFreezeTokenAccountMethod(); + } + + @Override + void onFreeze(TransactionBody.Builder bodyBuilder) { + bodyBuilder.setTokenGrantKyc(build()); + } + + @Override + void onScheduled(SchedulableTransactionBody.Builder scheduled) { + scheduled.setTokenGrantKyc(build()); + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenId.java b/sdk/src/main/java/org/hiero/sdk/TokenId.java similarity index 86% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/TokenId.java rename to sdk/src/main/java/org/hiero/sdk/TokenId.java index 15da7034f9..465c91a431 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenId.java +++ b/sdk/src/main/java/org/hiero/sdk/TokenId.java @@ -1,30 +1,11 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.TokenID; - +import java.util.Objects; import javax.annotation.Nonnegative; import javax.annotation.Nullable; -import java.util.Objects; +import org.hiero.sdk.proto.TokenID; /** * Constructs a TokenId. @@ -145,7 +126,7 @@ public NftId nft(@Nonnegative long serial) { /** * Extract the solidity address as a string. * - * @return the solidity address as a string + * @return the solidity address as a string */ public String toSolidityAddress() { return EntityIdHelper.toSolidityAddress(shard, realm, num); @@ -158,10 +139,10 @@ public String toSolidityAddress() { */ TokenID toProtobuf() { return TokenID.newBuilder() - .setShardNum(shard) - .setRealmNum(realm) - .setTokenNum(num) - .build(); + .setShardNum(shard) + .setRealmNum(realm) + .setTokenNum(num) + .build(); } /** @@ -224,7 +205,7 @@ public int hashCode() { } @Override - public boolean equals( Object o) { + public boolean equals(Object o) { if (this == o) { return true; } diff --git a/sdk/src/main/java/org/hiero/sdk/TokenInfo.java b/sdk/src/main/java/org/hiero/sdk/TokenInfo.java new file mode 100644 index 0000000000..402315e2b3 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TokenInfo.java @@ -0,0 +1,479 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.TokenFreezeStatus; +import org.hiero.sdk.proto.TokenGetInfoResponse; +import org.hiero.sdk.proto.TokenKycStatus; +import org.hiero.sdk.proto.TokenPauseStatus; + +/** + * Gets information about a fungible or non-fungible token instance. + * + * See Hedera Documentation + */ +public class TokenInfo { + /** + * The ID of the token for which information is requested. + */ + public final TokenId tokenId; + + /** + * Name of token. + */ + public final String name; + + /** + * Symbol of token. + */ + public final String symbol; + + /** + * The amount of decimal places that this token supports. + */ + public final int decimals; + + /** + * Total Supply of token. + */ + public final long totalSupply; + + /** + * The ID of the account which is set as Treasury + */ + public final AccountId treasuryAccountId; + + /** + * The key which can perform update/delete operations on the token. If empty, the token can be perceived as immutable (not being able to be updated/deleted) + */ + @Nullable + public final Key adminKey; + + /** + * The key which can grant or revoke KYC of an account for the token's transactions. If empty, KYC is not required, and KYC grant or revoke operations are not possible. + */ + @Nullable + public final Key kycKey; + + /** + * The key which can freeze or unfreeze an account for token transactions. If empty, freezing is not possible + */ + @Nullable + public final Key freezeKey; + + /** + * The key which can wipe token balance of an account. If empty, wipe is not possible + */ + @Nullable + public final Key wipeKey; + + /** + * The key which can change the supply of a token. The key is used to sign Token Mint/Burn operations + */ + @Nullable + public final Key supplyKey; + + /** + * The key which can change the custom fees of the token; if not set, the fees are immutable + */ + @Nullable + public final Key feeScheduleKey; + + /** + * The default Freeze status (not applicable, frozen or unfrozen) of Hedera accounts relative to this token. FreezeNotApplicable is returned if Token Freeze Key is empty. Frozen is returned if Token Freeze Key is set and defaultFreeze is set to true. Unfrozen is returned if Token Freeze Key is set and defaultFreeze is set to false + */ + @Nullable + public final Boolean defaultFreezeStatus; + + /** + * The default KYC status (KycNotApplicable or Revoked) of Hedera accounts relative to this token. KycNotApplicable is returned if KYC key is not set, otherwise Revoked + */ + @Nullable + public final Boolean defaultKycStatus; + + /** + * Specifies whether the token was deleted or not + */ + public final boolean isDeleted; + + /** + * An account which will be automatically charged to renew the token's expiration, at autoRenewPeriod interval + */ + @Nullable + public final AccountId autoRenewAccount; + + /** + * The interval at which the auto-renew account will be charged to extend the token's expiry + */ + @Nullable + public final Duration autoRenewPeriod; + + /** + * The epoch second at which the token will expire + */ + @Nullable + public final Instant expirationTime; + + /** + * The memo associated with the token + */ + public final String tokenMemo; + + /** + * The custom fees to be assessed during a CryptoTransfer that transfers units of this token + */ + public final List customFees; + + /** + * The token type + */ + public final TokenType tokenType; + + /** + * The token supply type + */ + public final TokenSupplyType supplyType; + + /** + * For tokens of type FUNGIBLE_COMMON - The Maximum number of fungible tokens that can be in + * circulation. For tokens of type NON_FUNGIBLE_UNIQUE - the maximum number of NFTs (serial + * numbers) that can be in circulation + */ + public final long maxSupply; + + /** + * The Key which can pause and unpause the Token. + */ + @Nullable + public final Key pauseKey; + + /** + * Specifies whether the token is paused or not. Null if pauseKey is not set. + */ + @Nullable + public final Boolean pauseStatus; + + /** + * Represents the metadata of the token definition. + */ + public byte[] metadata = {}; + + /** + * The key which can change the metadata of a token + * (token definition and individual NFTs). + */ + @Nullable + public final Key metadataKey; + + /** + * The ledger ID the response was returned from; please see HIP-198 for the network-specific IDs. + */ + public final LedgerId ledgerId; + + TokenInfo( + TokenId tokenId, + String name, + String symbol, + int decimals, + long totalSupply, + AccountId treasuryAccountId, + @Nullable Key adminKey, + @Nullable Key kycKey, + @Nullable Key freezeKey, + @Nullable Key wipeKey, + @Nullable Key supplyKey, + @Nullable Key feeScheduleKey, + @Nullable Boolean defaultFreezeStatus, + @Nullable Boolean defaultKycStatus, + boolean isDeleted, + @Nullable AccountId autoRenewAccount, + @Nullable Duration autoRenewPeriod, + @Nullable Instant expirationTime, + String tokenMemo, + List customFees, + TokenType tokenType, + TokenSupplyType supplyType, + long maxSupply, + @Nullable Key pauseKey, + @Nullable Boolean pauseStatus, + byte[] metadata, + @Nullable Key metadataKey, + LedgerId ledgerId) { + this.tokenId = tokenId; + this.name = name; + this.symbol = symbol; + this.decimals = decimals; + this.totalSupply = totalSupply; + this.treasuryAccountId = treasuryAccountId; + this.adminKey = adminKey; + this.kycKey = kycKey; + this.freezeKey = freezeKey; + this.wipeKey = wipeKey; + this.supplyKey = supplyKey; + this.feeScheduleKey = feeScheduleKey; + this.defaultFreezeStatus = defaultFreezeStatus; + this.defaultKycStatus = defaultKycStatus; + this.isDeleted = isDeleted; + this.autoRenewAccount = autoRenewAccount; + this.autoRenewPeriod = autoRenewPeriod; + this.expirationTime = expirationTime; + this.tokenMemo = tokenMemo; + this.customFees = customFees; + this.tokenType = tokenType; + this.supplyType = supplyType; + this.maxSupply = maxSupply; + this.pauseKey = pauseKey; + this.pauseStatus = pauseStatus; + this.metadata = metadata; + this.metadataKey = metadataKey; + this.ledgerId = ledgerId; + } + + /** + * Are we frozen? + * + * @param freezeStatus the freeze status + * @return true / false / null + */ + @Nullable + static Boolean freezeStatusFromProtobuf(TokenFreezeStatus freezeStatus) { + return freezeStatus == TokenFreezeStatus.FreezeNotApplicable ? null : freezeStatus == TokenFreezeStatus.Frozen; + } + + /** + * Is kyc required? + * + * @param kycStatus the kyc status + * @return true / false / null + */ + @Nullable + static Boolean kycStatusFromProtobuf(TokenKycStatus kycStatus) { + return kycStatus == TokenKycStatus.KycNotApplicable ? null : kycStatus == TokenKycStatus.Granted; + } + + /** + * Are we paused? + * + * @param pauseStatus the paused status + * @return true / false / null + */ + @Nullable + static Boolean pauseStatusFromProtobuf(TokenPauseStatus pauseStatus) { + return pauseStatus == TokenPauseStatus.PauseNotApplicable ? null : pauseStatus == TokenPauseStatus.Paused; + } + + /** + * Create a token info object from a protobuf. + * + * @param response the protobuf + * @return new token info object + */ + static TokenInfo fromProtobuf(TokenGetInfoResponse response) { + var info = response.getTokenInfo(); + + return new TokenInfo( + TokenId.fromProtobuf(info.getTokenId()), + info.getName(), + info.getSymbol(), + info.getDecimals(), + info.getTotalSupply(), + AccountId.fromProtobuf(info.getTreasury()), + info.hasAdminKey() ? Key.fromProtobufKey(info.getAdminKey()) : null, + info.hasKycKey() ? Key.fromProtobufKey(info.getKycKey()) : null, + info.hasFreezeKey() ? Key.fromProtobufKey(info.getFreezeKey()) : null, + info.hasWipeKey() ? Key.fromProtobufKey(info.getWipeKey()) : null, + info.hasSupplyKey() ? Key.fromProtobufKey(info.getSupplyKey()) : null, + info.hasFeeScheduleKey() ? Key.fromProtobufKey(info.getFeeScheduleKey()) : null, + freezeStatusFromProtobuf(info.getDefaultFreezeStatus()), + kycStatusFromProtobuf(info.getDefaultKycStatus()), + info.getDeleted(), + info.hasAutoRenewAccount() ? AccountId.fromProtobuf(info.getAutoRenewAccount()) : null, + info.hasAutoRenewPeriod() ? DurationConverter.fromProtobuf(info.getAutoRenewPeriod()) : null, + info.hasExpiry() ? InstantConverter.fromProtobuf(info.getExpiry()) : null, + info.getMemo(), + customFeesFromProto(info), + TokenType.valueOf(info.getTokenType()), + TokenSupplyType.valueOf(info.getSupplyType()), + info.getMaxSupply(), + info.hasPauseKey() ? Key.fromProtobufKey(info.getPauseKey()) : null, + pauseStatusFromProtobuf(info.getPauseStatus()), + info.getMetadata().toByteArray(), + info.hasMetadataKey() ? Key.fromProtobufKey(info.getMetadataKey()) : null, + LedgerId.fromByteString(info.getLedgerId())); + } + + /** + * Create a token info object from a byte array. + * + * @param bytes the byte array + * @return the new token info object + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + public static TokenInfo fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + return fromProtobuf(TokenGetInfoResponse.parseFrom(bytes).toBuilder().build()); + } + + /** + * Create custom fee list from protobuf. + * + * @param info the protobuf + * @return the list of custom fee's + */ + private static List customFeesFromProto(org.hiero.sdk.proto.TokenInfo info) { + var returnCustomFees = new ArrayList(info.getCustomFeesCount()); + for (var feeProto : info.getCustomFeesList()) { + returnCustomFees.add(CustomFee.fromProtobuf(feeProto)); + } + return returnCustomFees; + } + + /** + * Create a token freeze status protobuf. + * + * @param freezeStatus the freeze status + * @return the protobuf + */ + static TokenFreezeStatus freezeStatusToProtobuf(@Nullable Boolean freezeStatus) { + return freezeStatus == null + ? TokenFreezeStatus.FreezeNotApplicable + : freezeStatus ? TokenFreezeStatus.Frozen : TokenFreezeStatus.Unfrozen; + } + + /** + * Create a kyc status protobuf. + * + * @param kycStatus the kyc status + * @return the protobuf + */ + static TokenKycStatus kycStatusToProtobuf(@Nullable Boolean kycStatus) { + return kycStatus == null + ? TokenKycStatus.KycNotApplicable + : kycStatus ? TokenKycStatus.Granted : TokenKycStatus.Revoked; + } + + /** + * Create a pause status protobuf. + * + * @param pauseStatus the pause status + * @return the protobuf + */ + static TokenPauseStatus pauseStatusToProtobuf(@Nullable Boolean pauseStatus) { + return pauseStatus == null + ? TokenPauseStatus.PauseNotApplicable + : pauseStatus ? TokenPauseStatus.Paused : TokenPauseStatus.Unpaused; + } + + /** + * Create the protobuf. + * + * @return the protobuf representation + */ + TokenGetInfoResponse toProtobuf() { + var tokenInfoBuilder = org.hiero.sdk.proto.TokenInfo.newBuilder() + .setTokenId(tokenId.toProtobuf()) + .setName(name) + .setSymbol(symbol) + .setDecimals(decimals) + .setTotalSupply(totalSupply) + .setTreasury(treasuryAccountId.toProtobuf()) + .setDefaultFreezeStatus(freezeStatusToProtobuf(defaultFreezeStatus)) + .setDefaultKycStatus(kycStatusToProtobuf(defaultKycStatus)) + .setDeleted(isDeleted) + .setMemo(tokenMemo) + .setTokenType(tokenType.code) + .setSupplyType(supplyType.code) + .setMaxSupply(maxSupply) + .setPauseStatus(pauseStatusToProtobuf(pauseStatus)) + .setLedgerId(ledgerId.toByteString()); + if (adminKey != null) { + tokenInfoBuilder.setAdminKey(adminKey.toProtobufKey()); + } + if (kycKey != null) { + tokenInfoBuilder.setKycKey(kycKey.toProtobufKey()); + } + if (freezeKey != null) { + tokenInfoBuilder.setFreezeKey(freezeKey.toProtobufKey()); + } + if (wipeKey != null) { + tokenInfoBuilder.setWipeKey(wipeKey.toProtobufKey()); + } + if (supplyKey != null) { + tokenInfoBuilder.setSupplyKey(supplyKey.toProtobufKey()); + } + if (feeScheduleKey != null) { + tokenInfoBuilder.setFeeScheduleKey(feeScheduleKey.toProtobufKey()); + } + if (pauseKey != null) { + tokenInfoBuilder.setPauseKey(pauseKey.toProtobufKey()); + } + if (metadata != null) { + tokenInfoBuilder.setMetadata(ByteString.copyFrom(metadata)); + } + if (metadataKey != null) { + tokenInfoBuilder.setMetadataKey(metadataKey.toProtobufKey()); + } + if (autoRenewAccount != null) { + tokenInfoBuilder.setAutoRenewAccount(autoRenewAccount.toProtobuf()); + } + if (autoRenewPeriod != null) { + tokenInfoBuilder.setAutoRenewPeriod(DurationConverter.toProtobuf(autoRenewPeriod)); + } + if (expirationTime != null) { + tokenInfoBuilder.setExpiry(InstantConverter.toProtobuf(expirationTime)); + } + for (var fee : customFees) { + tokenInfoBuilder.addCustomFees(fee.toProtobuf()); + } + return TokenGetInfoResponse.newBuilder().setTokenInfo(tokenInfoBuilder).build(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("tokenId", tokenId) + .add("name", name) + .add("symbol", symbol) + .add("decimals", decimals) + .add("totalSupply", totalSupply) + .add("treasuryAccountId", treasuryAccountId) + .add("adminKey", adminKey) + .add("kycKey", kycKey) + .add("freezeKey", freezeKey) + .add("wipeKey", wipeKey) + .add("supplyKey", supplyKey) + .add("feeScheduleKey", feeScheduleKey) + .add("defaultFreezeStatus", defaultFreezeStatus) + .add("defaultKycStatus", defaultKycStatus) + .add("isDeleted", isDeleted) + .add("autoRenewAccount", autoRenewAccount) + .add("autoRenewPeriod", autoRenewPeriod) + .add("expirationTime", expirationTime) + .add("tokenMemo", tokenMemo) + .add("customFees", customFees) + .add("tokenType", tokenType) + .add("supplyType", supplyType) + .add("maxSupply", maxSupply) + .add("pauseKey", pauseKey) + .add("pauseStatus", pauseStatus) + .add("metadata", metadata) + .add("metadataKey", metadataKey) + .add("ledgerId", ledgerId) + .toString(); + } + + /** + * Create the byte array. + * + * @return the byte array representation + */ + public byte[] toBytes() { + return toProtobuf().toByteArray(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/TokenInfoQuery.java b/sdk/src/main/java/org/hiero/sdk/TokenInfoQuery.java new file mode 100644 index 0000000000..05fddbe403 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TokenInfoQuery.java @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.grpc.MethodDescriptor; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.Query; +import org.hiero.sdk.proto.QueryHeader; +import org.hiero.sdk.proto.Response; +import org.hiero.sdk.proto.ResponseHeader; +import org.hiero.sdk.proto.TokenGetInfoQuery; +import org.hiero.sdk.proto.TokenServiceGrpc; + +/** + * Initializes the TokenInfoQuery object. + */ +public class TokenInfoQuery extends org.hiero.sdk.Query { + @Nullable + TokenId tokenId = null; + + /** + * Constructor. + */ + public TokenInfoQuery() {} + + /** + * Extract the token id. + * + * @return the token id + */ + @Nullable + public TokenId getTokenId() { + return tokenId; + } + + /** + * Sets the Token ID for which information is requested. + * + * @param tokenId The TokenId to be set + * @return {@code this} + */ + public TokenInfoQuery setTokenId(TokenId tokenId) { + Objects.requireNonNull(tokenId); + this.tokenId = tokenId; + return this; + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (tokenId != null) { + tokenId.validateChecksum(client); + } + } + + @Override + void onMakeRequest(org.hiero.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { + var builder = TokenGetInfoQuery.newBuilder(); + if (tokenId != null) { + builder.setToken(tokenId.toProtobuf()); + } + + queryBuilder.setTokenGetInfo(builder.setHeader(header)); + } + + @Override + ResponseHeader mapResponseHeader(Response response) { + return response.getTokenGetInfo().getHeader(); + } + + @Override + QueryHeader mapRequestHeader(org.hiero.sdk.proto.Query request) { + return request.getTokenGetInfo().getHeader(); + } + + @Override + TokenInfo mapResponse(Response response, AccountId nodeId, Query request) { + return TokenInfo.fromProtobuf(response.getTokenGetInfo()); + } + + @Override + MethodDescriptor getMethodDescriptor() { + return TokenServiceGrpc.getGetTokenInfoMethod(); + } + + @Override + public CompletableFuture getCostAsync(Client client) { + // deleted accounts return a COST_ANSWER of zero which triggers `INSUFFICIENT_TX_FEE` + // if you set that as the query payment; 25 tinybar seems to be enough to get + // `Token_DELETED` back instead. + return super.getCostAsync(client).thenApply((cost) -> Hbar.fromTinybars(Math.max(cost.toTinybars(), 25))); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/TokenKeyValidation.java b/sdk/src/main/java/org/hiero/sdk/TokenKeyValidation.java new file mode 100644 index 0000000000..5e2bd6d68f --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TokenKeyValidation.java @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +/** + * Types of validation strategies for token keys. + * + */ +public enum TokenKeyValidation { + /** + * Currently the default behaviour. It will perform all token key validations. + */ + FULL_VALIDATION(org.hiero.sdk.proto.TokenKeyValidation.FULL_VALIDATION), + + /** + * Perform no validations at all for all passed token keys. + */ + NO_VALIDATION(org.hiero.sdk.proto.TokenKeyValidation.NO_VALIDATION); + + final org.hiero.sdk.proto.TokenKeyValidation code; + + /** + * Constructor. + * + * @param code the token key validation + */ + TokenKeyValidation(org.hiero.sdk.proto.TokenKeyValidation code) { + this.code = code; + } + + static TokenKeyValidation valueOf(org.hiero.sdk.proto.TokenKeyValidation code) { + return switch (code) { + case FULL_VALIDATION -> FULL_VALIDATION; + case NO_VALIDATION -> NO_VALIDATION; + default -> throw new IllegalStateException("(BUG) unhandled TokenKeyValidation"); + }; + } + + @Override + public String toString() { + return switch (this) { + case FULL_VALIDATION -> "FULL_VALIDATION"; + case NO_VALIDATION -> "NO_VALIDATION"; + }; + } + + public org.hiero.sdk.proto.TokenKeyValidation toProtobuf() { + return this.code; + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenMintTransaction.java b/sdk/src/main/java/org/hiero/sdk/TokenMintTransaction.java similarity index 78% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/TokenMintTransaction.java rename to sdk/src/main/java/org/hiero/sdk/TokenMintTransaction.java index 256b7126cf..4b63bd0b7c 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenMintTransaction.java +++ b/sdk/src/main/java/org/hiero/sdk/TokenMintTransaction.java @@ -1,40 +1,21 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenMintTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenServiceGrpc; -import com.hedera.hashgraph.sdk.proto.Transaction; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; import io.grpc.MethodDescriptor; - -import javax.annotation.Nonnegative; -import javax.annotation.Nullable; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Objects; +import javax.annotation.Nonnegative; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenMintTransactionBody; +import org.hiero.sdk.proto.TokenServiceGrpc; +import org.hiero.sdk.proto.Transaction; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; /** * Minting fungible token allows you to increase the total supply of the @@ -44,7 +25,7 @@ * * See Hedera Documentation */ -public class TokenMintTransaction extends com.hedera.hashgraph.sdk.Transaction { +public class TokenMintTransaction extends org.hiero.sdk.Transaction { @Nullable private TokenId tokenId = null; /** @@ -72,8 +53,7 @@ public class TokenMintTransaction extends com.hedera.hashgraph.sdk.Transaction> txs) throws InvalidProtocolBufferException { + TokenMintTransaction(LinkedHashMap> txs) + throws InvalidProtocolBufferException { super(txs); initFromTransactionBody(); } @@ -92,7 +73,7 @@ public TokenMintTransaction() { * * @param txBody protobuf TransactionBody */ - TokenMintTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { + TokenMintTransaction(org.hiero.sdk.proto.TransactionBody txBody) { super(txBody); initFromTransactionBody(); } @@ -193,7 +174,7 @@ void initFromTransactionBody() { * Build the transaction body. * * @return {@link - * com.hedera.hashgraph.sdk.proto.TokenMintTransactionBody} + * org.hiero.sdk.proto.TokenMintTransactionBody} */ TokenMintTransactionBody.Builder build() { var builder = TokenMintTransactionBody.newBuilder(); diff --git a/sdk/src/main/java/org/hiero/sdk/TokenNftAllowance.java b/sdk/src/main/java/org/hiero/sdk/TokenNftAllowance.java new file mode 100644 index 0000000000..89e1575bae --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TokenNftAllowance.java @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import com.google.protobuf.BoolValue; +import com.google.protobuf.InvalidProtocolBufferException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.NftAllowance; +import org.hiero.sdk.proto.NftRemoveAllowance; + +/** + * Class to encapsulate the nft methods for token allowance's. + */ +public class TokenNftAllowance { + + /** + * The NFT token type that the allowance pertains to + */ + @Nullable + public final TokenId tokenId; + + /** + * The account ID of the token owner (ie. the grantor of the allowance) + */ + @Nullable + public final AccountId ownerAccountId; + + /** + * The account ID of the token allowance spender + */ + @Nullable + public final AccountId spenderAccountId; + + /** + * The account ID of the spender who is granted approvedForAll allowance and granting + * approval on an NFT serial to another spender. + */ + @Nullable + AccountId delegatingSpender; + + /** + * The list of serial numbers that the spender is permitted to transfer. + */ + public final List serialNumbers; + + /** + * If true, the spender has access to all of the owner's NFT units of type tokenId (currently + * owned and any in the future). + */ + @Nullable + public final Boolean allSerials; + + /** + * Constructor. + * + * @param tokenId the token id + * @param ownerAccountId the grantor's account id + * @param spenderAccountId the spender's account id + * @param delegatingSpender the delegating spender's account id + * @param serialNumbers the list of serial numbers + * @param allSerials grant for all serial's + */ + TokenNftAllowance( + @Nullable TokenId tokenId, + @Nullable AccountId ownerAccountId, + @Nullable AccountId spenderAccountId, + @Nullable AccountId delegatingSpender, + Collection serialNumbers, + @Nullable Boolean allSerials) { + this.tokenId = tokenId; + this.ownerAccountId = ownerAccountId; + this.spenderAccountId = spenderAccountId; + this.delegatingSpender = delegatingSpender; + this.serialNumbers = new ArrayList<>(serialNumbers); + this.allSerials = allSerials; + } + + /** + * Create a copy of a nft token allowance object. + * + * @param allowance the nft token allowance to copj + * @return a new copy + */ + static TokenNftAllowance copyFrom(TokenNftAllowance allowance) { + return new TokenNftAllowance( + allowance.tokenId, + allowance.ownerAccountId, + allowance.spenderAccountId, + allowance.delegatingSpender, + allowance.serialNumbers, + allowance.allSerials); + } + + /** + * Create a nft token allowance from a protobuf. + * + * @param allowanceProto the protobuf + * @return the nft token allowance + */ + static TokenNftAllowance fromProtobuf(NftAllowance allowanceProto) { + return new TokenNftAllowance( + allowanceProto.hasTokenId() ? TokenId.fromProtobuf(allowanceProto.getTokenId()) : null, + allowanceProto.hasOwner() ? AccountId.fromProtobuf(allowanceProto.getOwner()) : null, + allowanceProto.hasSpender() ? AccountId.fromProtobuf(allowanceProto.getSpender()) : null, + allowanceProto.hasDelegatingSpender() + ? AccountId.fromProtobuf(allowanceProto.getDelegatingSpender()) + : null, + allowanceProto.getSerialNumbersList(), + allowanceProto.hasApprovedForAll() + ? allowanceProto.getApprovedForAll().getValue() + : null); + } + + /** + * Create a nft token allowance from a byte array. + * + * @param bytes the byte array + * @return the nft token allowance + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + public static TokenNftAllowance fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + return fromProtobuf(NftAllowance.parseFrom(Objects.requireNonNull(bytes))); + } + + /** + * Validate the configured client. + * + * @param client the configured client + * @throws BadEntityIdException if entity ID is formatted poorly + */ + void validateChecksums(Client client) throws BadEntityIdException { + if (tokenId != null) { + tokenId.validateChecksum(client); + } + if (ownerAccountId != null) { + ownerAccountId.validateChecksum(client); + } + if (spenderAccountId != null) { + spenderAccountId.validateChecksum(client); + } + if (delegatingSpender != null) { + delegatingSpender.validateChecksum(client); + } + } + + /** + * Create the protobuf. + * + * @return the protobuf representation + */ + NftAllowance toProtobuf() { + var builder = NftAllowance.newBuilder(); + if (tokenId != null) { + builder.setTokenId(tokenId.toProtobuf()); + } + if (ownerAccountId != null) { + builder.setOwner(ownerAccountId.toProtobuf()); + } + if (spenderAccountId != null) { + builder.setSpender(spenderAccountId.toProtobuf()); + } + if (delegatingSpender != null) { + builder.setDelegatingSpender(delegatingSpender.toProtobuf()); + } + builder.addAllSerialNumbers(serialNumbers); + if (allSerials != null) { + builder.setApprovedForAll( + BoolValue.newBuilder().setValue(allSerials).build()); + } + return builder.build(); + } + + /** + * Create the protobuf. + * + * @return the remove protobuf + */ + NftRemoveAllowance toRemoveProtobuf() { + var builder = NftRemoveAllowance.newBuilder(); + if (tokenId != null) { + builder.setTokenId(tokenId.toProtobuf()); + } + if (ownerAccountId != null) { + builder.setOwner(ownerAccountId.toProtobuf()); + } + builder.addAllSerialNumbers(serialNumbers); + return builder.build(); + } + + /** + * Create the byte array. + * + * @return the byte array representation + */ + public byte[] toBytes() { + return toProtobuf().toByteArray(); + } + + @Override + public String toString() { + var stringHelper = MoreObjects.toStringHelper(this) + .add("tokenId", tokenId) + .add("ownerAccountId", ownerAccountId) + .add("spenderAccountId", spenderAccountId) + .add("delegatingSpender", delegatingSpender); + if (allSerials != null) { + stringHelper.add("allSerials", allSerials); + } else { + stringHelper.add("serials", serialNumbers); + } + return stringHelper.toString(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/TokenNftInfo.java b/sdk/src/main/java/org/hiero/sdk/TokenNftInfo.java new file mode 100644 index 0000000000..70d0ecf350 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TokenNftInfo.java @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import java.time.Instant; +import java.util.Objects; +import javax.annotation.Nullable; + +/** + * + * + * See Hedera Documentation + */ +public class TokenNftInfo { + /** + * The ID of the NFT + */ + public final NftId nftId; + + /** + * The current owner of the NFT + */ + public final AccountId accountId; + + /** + * The effective consensus timestamp at which the NFT was minted + */ + public final Instant creationTime; + + /** + * Represents the unique metadata of the NFT + */ + public final byte[] metadata; + + /** + * The ledger ID the response was returned from; please see HIP-198 for the network-specific IDs. + */ + public final LedgerId ledgerId; + + /** + * If an allowance is granted for the NFT, its corresponding spender account + */ + @Nullable + public final AccountId spenderId; + + /** + * Constructor. + * + * @param nftId the id of the nft + * @param accountId the current owner of the nft + * @param creationTime the effective consensus time + * @param metadata the unique metadata + * @param ledgerId the ledger id of the response + * @param spenderId the spender of the allowance (null if not an allowance) + */ + TokenNftInfo( + NftId nftId, + AccountId accountId, + Instant creationTime, + byte[] metadata, + LedgerId ledgerId, + @Nullable AccountId spenderId) { + this.nftId = nftId; + this.accountId = accountId; + this.creationTime = Objects.requireNonNull(creationTime); + this.metadata = metadata; + this.ledgerId = ledgerId; + this.spenderId = spenderId; + } + + /** + * Create token nft info from a protobuf. + * + * @param info the protobuf + * @return the new token nft info + */ + static TokenNftInfo fromProtobuf(org.hiero.sdk.proto.TokenNftInfo info) { + return new TokenNftInfo( + NftId.fromProtobuf(info.getNftID()), + AccountId.fromProtobuf(info.getAccountID()), + InstantConverter.fromProtobuf(info.getCreationTime()), + info.getMetadata().toByteArray(), + LedgerId.fromByteString(info.getLedgerId()), + info.hasSpenderId() ? AccountId.fromProtobuf(info.getSpenderId()) : null); + } + + /** + * Create token nft info from byte array. + * + * @param bytes the byte array + * @return the new token nft info + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + public static TokenNftInfo fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + return fromProtobuf(org.hiero.sdk.proto.TokenNftInfo.parseFrom(bytes)); + } + + /** + * Create the protobuf. + * + * @return the protobuf representation + */ + org.hiero.sdk.proto.TokenNftInfo toProtobuf() { + var builder = org.hiero.sdk.proto.TokenNftInfo.newBuilder() + .setNftID(nftId.toProtobuf()) + .setAccountID(accountId.toProtobuf()) + .setCreationTime(InstantConverter.toProtobuf(creationTime)) + .setMetadata(ByteString.copyFrom(metadata)) + .setLedgerId(ledgerId.toByteString()); + if (spenderId != null) { + builder.setSpenderId(spenderId.toProtobuf()); + } + return builder.build(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("nftId", nftId) + .add("accountId", accountId) + .add("creationTime", creationTime) + .add("metadata", metadata) + .add("ledgerId", ledgerId) + .add("spenderId", spenderId) + .toString(); + } + + /** + * Create the byte array. + * + * @return the byte array representation + */ + public byte[] toBytes() { + return toProtobuf().toByteArray(); + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenNftInfoQuery.java b/sdk/src/main/java/org/hiero/sdk/TokenNftInfoQuery.java similarity index 75% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/TokenNftInfoQuery.java rename to sdk/src/main/java/org/hiero/sdk/TokenNftInfoQuery.java index 2f26ad8f5b..72be36b23a 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenNftInfoQuery.java +++ b/sdk/src/main/java/org/hiero/sdk/TokenNftInfoQuery.java @@ -1,30 +1,6 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.Query; -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import com.hedera.hashgraph.sdk.proto.Response; -import com.hedera.hashgraph.sdk.proto.ResponseHeader; -import com.hedera.hashgraph.sdk.proto.TokenGetNftInfoQuery; -import com.hedera.hashgraph.sdk.proto.TokenServiceGrpc; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + import io.grpc.MethodDescriptor; import java.util.Collections; import java.util.List; @@ -32,6 +8,12 @@ import java.util.concurrent.CompletableFuture; import javax.annotation.Nonnegative; import javax.annotation.Nullable; +import org.hiero.sdk.proto.Query; +import org.hiero.sdk.proto.QueryHeader; +import org.hiero.sdk.proto.Response; +import org.hiero.sdk.proto.ResponseHeader; +import org.hiero.sdk.proto.TokenGetNftInfoQuery; +import org.hiero.sdk.proto.TokenServiceGrpc; /** * A query that returns information about a non-fungible token (NFT). @@ -40,12 +22,13 @@ * * See Hedera Documentation */ -public class TokenNftInfoQuery extends com.hedera.hashgraph.sdk.Query, TokenNftInfoQuery> { +public class TokenNftInfoQuery extends org.hiero.sdk.Query, TokenNftInfoQuery> { /** * The ID of the non-fungible token in x.y.z format. */ @Nullable private NftId nftId = null; + @Nullable private TokenId tokenId = null; /** @@ -53,14 +36,14 @@ public class TokenNftInfoQuery extends com.hedera.hashgraph.sdk.Query onExecuteAsync(Client client) { int modesEnabled = (nftId != null ? 1 : 0) + (tokenId != null ? 1 : 0) + (accountId != null ? 1 : 0); if (modesEnabled > 1) { - throw new IllegalStateException("TokenNftInfoQuery must be one of byNftId, byTokenId, or byAccountId, but multiple of these modes have been selected"); + throw new IllegalStateException( + "TokenNftInfoQuery must be one of byNftId, byTokenId, or byAccountId, but multiple of these modes have been selected"); } else if (modesEnabled == 0) { - throw new IllegalStateException("TokenNftInfoQuery must be one of byNftId, byTokenId, or byAccountId, but none of these modes have been selected"); + throw new IllegalStateException( + "TokenNftInfoQuery must be one of byNftId, byTokenId, or byAccountId, but none of these modes have been selected"); } return super.onExecuteAsync(client); } @Override - void onMakeRequest(com.hedera.hashgraph.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { + void onMakeRequest(org.hiero.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { var builder = TokenGetNftInfoQuery.newBuilder(); - if(nftId != null) { + if (nftId != null) { builder.setNftID(nftId.toProtobuf()); } queryBuilder.setTokenGetNftInfo(builder.setHeader(header)); @@ -225,13 +210,14 @@ ResponseHeader mapResponseHeader(Response response) { } @Override - QueryHeader mapRequestHeader(com.hedera.hashgraph.sdk.proto.Query request) { + QueryHeader mapRequestHeader(org.hiero.sdk.proto.Query request) { return request.getTokenGetInfo().getHeader(); } @Override - List mapResponse(Response response, AccountId nodeId, com.hedera.hashgraph.sdk.proto.Query request) { - return Collections.singletonList(TokenNftInfo.fromProtobuf(response.getTokenGetNftInfo().getNft())); + List mapResponse(Response response, AccountId nodeId, org.hiero.sdk.proto.Query request) { + return Collections.singletonList( + TokenNftInfo.fromProtobuf(response.getTokenGetNftInfo().getNft())); } @Override diff --git a/sdk/src/main/java/org/hiero/sdk/TokenNftTransfer.java b/sdk/src/main/java/org/hiero/sdk/TokenNftTransfer.java new file mode 100644 index 0000000000..2b9c7e4969 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TokenNftTransfer.java @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import com.google.protobuf.InvalidProtocolBufferException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import org.hiero.sdk.proto.NftTransfer; +import org.hiero.sdk.proto.TokenID; +import org.hiero.sdk.proto.TokenTransferList; + +/** + * Internal utility class. + */ +public class TokenNftTransfer implements Comparable { + /** + * The ID of the token + */ + public final TokenId tokenId; + /** + * The accountID of the sender + */ + public final AccountId sender; + /** + * The accountID of the receiver + */ + public final AccountId receiver; + /** + * The serial number of the NFT + */ + public final long serial; + /** + * If true then the transfer is expected to be an approved allowance and the sender is expected to be the owner. The + * default is false. + */ + public boolean isApproved; + + /** + * Constructor. + * + * @param tokenId the token id + * @param sender the sender account id + * @param receiver the receiver account id + * @param serial the serial number + * @param isApproved is it approved + */ + TokenNftTransfer(TokenId tokenId, AccountId sender, AccountId receiver, long serial, boolean isApproved) { + this.tokenId = tokenId; + this.sender = sender; + this.receiver = receiver; + this.serial = serial; + this.isApproved = isApproved; + } + + /** + * Create a list of token nft transfer records from a protobuf. + * + * @param tokenTransferList the protobuf + * @return the new list + */ + static ArrayList fromProtobuf(List tokenTransferList) { + var transfers = new ArrayList(); + + for (var tokenTransfer : tokenTransferList) { + var tokenId = TokenId.fromProtobuf(tokenTransfer.getToken()); + + for (var transfer : tokenTransfer.getNftTransfersList()) { + transfers.add(new TokenNftTransfer( + tokenId, + AccountId.fromProtobuf(transfer.getSenderAccountID()), + AccountId.fromProtobuf(transfer.getReceiverAccountID()), + transfer.getSerialNumber(), + transfer.getIsApproval())); + } + } + + return transfers; + } + + /** + * Convert a byte array to a token NFT transfer object. + * + * @param bytes the byte array + * @return the converted token nft transfer object + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + @Deprecated + public static TokenNftTransfer fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + return fromProtobuf(List.of(TokenTransferList.newBuilder() + .setToken(TokenID.newBuilder().build()) + .addNftTransfers(NftTransfer.parseFrom(bytes)) + .build())) + .get(0); + } + + /** + * Create the protobuf. + * + * @return the protobuf representation + */ + NftTransfer toProtobuf() { + return NftTransfer.newBuilder() + .setSenderAccountID(sender.toProtobuf()) + .setReceiverAccountID(receiver.toProtobuf()) + .setSerialNumber(serial) + .setIsApproval(isApproved) + .build(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("tokenId", tokenId) + .add("sender", sender) + .add("receiver", receiver) + .add("serial", serial) + .add("isApproved", isApproved) + .toString(); + } + + /** + * Convert the token NFT transfer object to a byte array. + * + * @return the converted token NFT transfer object + */ + @Deprecated + public byte[] toBytes() { + return toProtobuf().toByteArray(); + } + + @Override + public int compareTo(TokenNftTransfer o) { + int senderComparison = sender.compareTo(o.sender); + if (senderComparison != 0) { + return senderComparison; + } + int receiverComparison = receiver.compareTo(o.receiver); + if (receiverComparison != 0) { + return receiverComparison; + } + return Long.compare(serial, o.serial); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TokenNftTransfer that = (TokenNftTransfer) o; + return serial == that.serial + && isApproved == that.isApproved + && tokenId.equals(that.tokenId) + && sender.equals(that.sender) + && receiver.equals(that.receiver); + } + + @Override + public int hashCode() { + return Objects.hash(tokenId, sender, receiver, serial, isApproved); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/TokenPauseTransaction.java b/sdk/src/main/java/org/hiero/sdk/TokenPauseTransaction.java new file mode 100644 index 0000000000..a7cde94938 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TokenPauseTransaction.java @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.grpc.MethodDescriptor; +import java.util.LinkedHashMap; +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenPauseTransactionBody; +import org.hiero.sdk.proto.TokenServiceGrpc; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; + +/** + * A token pause transaction prevents the token from being involved in any + * kind of operation. The token's pause key is required to sign the + * transaction. This is a key that is specified during the creation of a + * token. If a token has no pause key, you will not be able to pause the + * token. If the pause key was not set during the creation of a token, you + * will not be able to update the token to add this key. + * + * See Hedera Documentation + */ +public class TokenPauseTransaction extends Transaction { + @Nullable + private TokenId tokenId = null; + + /** + * Constructor. + */ + public TokenPauseTransaction() {} + + /** + * Constructor. + * + * @param txs Compound list of transaction id's list of (AccountId, Transaction) + * records + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + TokenPauseTransaction(LinkedHashMap> txs) + throws InvalidProtocolBufferException { + super(txs); + initFromTransactionBody(); + } + + /** + * Constructor. + * + * @param txBody protobuf TransactionBody + */ + TokenPauseTransaction(org.hiero.sdk.proto.TransactionBody txBody) { + super(txBody); + initFromTransactionBody(); + } + + /** + * Extract the token id. + * + * @return the token id + */ + @Nullable + public TokenId getTokenId() { + return tokenId; + } + + /** + * Assign the token id. + * + * @param tokenId the token id + * @return {@code this} + */ + public TokenPauseTransaction setTokenId(TokenId tokenId) { + Objects.requireNonNull(tokenId); + requireNotFrozen(); + this.tokenId = tokenId; + return this; + } + + void initFromTransactionBody() { + var body = sourceTransactionBody.getTokenPause(); + if (body.hasToken()) { + tokenId = TokenId.fromProtobuf(body.getToken()); + } + } + + /** + * Build the transaction body. + * + * @return {@link + * org.hiero.sdk.proto.TokenPauseTransactionBody} + */ + TokenPauseTransactionBody.Builder build() { + var builder = TokenPauseTransactionBody.newBuilder(); + if (tokenId != null) { + builder.setToken(tokenId.toProtobuf()); + } + + return builder; + } + + @Override + MethodDescriptor getMethodDescriptor() { + return TokenServiceGrpc.getPauseTokenMethod(); + } + + @Override + void onFreeze(TransactionBody.Builder bodyBuilder) { + bodyBuilder.setTokenPause(build()); + } + + @Override + void onScheduled(SchedulableTransactionBody.Builder scheduled) { + scheduled.setTokenPause(build()); + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (tokenId != null) { + tokenId.validateChecksum(client); + } + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenRejectFlow.java b/sdk/src/main/java/org/hiero/sdk/TokenRejectFlow.java similarity index 84% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/TokenRejectFlow.java rename to sdk/src/main/java/org/hiero/sdk/TokenRejectFlow.java index 5015aeb2db..8259ec82fd 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenRejectFlow.java +++ b/sdk/src/main/java/org/hiero/sdk/TokenRejectFlow.java @@ -1,27 +1,8 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import java.time.Duration; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.concurrent.CompletableFuture; @@ -46,12 +27,12 @@ public class TokenRejectFlow { /** * A list of one or more token rejections (a fungible/common token type). */ - private List tokenIds = new ArrayList<>(); + private List tokenIds = new ArrayList<>(); /** * A list of one or more token rejections (a single specific serialized non-fungible/unique token). */ - private List nftIds = new ArrayList<>(); + private List nftIds = new ArrayList<>(); @Nullable private List nodeAccountIds = null; @@ -215,7 +196,6 @@ public TokenRejectFlow signWithOperator(Client client) { return this; } - private void fillOutTransaction(final Transaction transaction) { if (nodeAccountIds != null) { transaction.setNodeAccountIds(nodeAccountIds); @@ -232,9 +212,9 @@ private void fillOutTransaction(final Transaction transaction) { private TokenRejectTransaction createTokenRejectTransaction() { var tokenRejectTransaction = new TokenRejectTransaction() - .setOwnerId(ownerId) - .setTokenIds(tokenIds) - .setNftIds(nftIds); + .setOwnerId(ownerId) + .setTokenIds(tokenIds) + .setNftIds(nftIds); fillOutTransaction(tokenRejectTransaction); @@ -242,13 +222,13 @@ private TokenRejectTransaction createTokenRejectTransaction() { } private TokenDissociateTransaction createTokenDissociateTransaction() { - List tokenIdsToReject = Stream.concat(tokenIds.stream(), nftIds.stream().map(nftId -> nftId.tokenId)) - .distinct() - .collect(Collectors.toList()); + List tokenIdsToReject = Stream.concat( + tokenIds.stream(), nftIds.stream().map(nftId -> nftId.tokenId)) + .distinct() + .collect(Collectors.toList()); - var tokenDissociateTransaction = new TokenDissociateTransaction() - .setAccountId(ownerId) - .setTokenIds(tokenIdsToReject); + var tokenDissociateTransaction = + new TokenDissociateTransaction().setAccountId(ownerId).setTokenIds(tokenIdsToReject); fillOutTransaction(tokenDissociateTransaction); @@ -277,7 +257,7 @@ public TransactionResponse execute(Client client) throws PrecheckStatusException * @throws TimeoutException when the transaction times out */ public TransactionResponse execute(Client client, Duration timeoutPerTransaction) - throws PrecheckStatusException, TimeoutException { + throws PrecheckStatusException, TimeoutException { try { var tokenRejectTxResponse = createTokenRejectTransaction().execute(client, timeoutPerTransaction); tokenRejectTxResponse.getReceipt(client, timeoutPerTransaction); @@ -309,9 +289,11 @@ public CompletableFuture executeAsync(Client client) { * @return the response */ public CompletableFuture executeAsync(Client client, Duration timeoutPerTransaction) { - return createTokenRejectTransaction().executeAsync(client, timeoutPerTransaction) - .thenCompose(tokenRejectResponse -> tokenRejectResponse.getReceiptQuery().executeAsync(client, timeoutPerTransaction)) - .thenCompose(receipt -> createTokenDissociateTransaction().executeAsync(client, timeoutPerTransaction)); + return createTokenRejectTransaction() + .executeAsync(client, timeoutPerTransaction) + .thenCompose(tokenRejectResponse -> + tokenRejectResponse.getReceiptQuery().executeAsync(client, timeoutPerTransaction)) + .thenCompose(receipt -> createTokenDissociateTransaction().executeAsync(client, timeoutPerTransaction)); } /** @@ -331,8 +313,8 @@ public void executeAsync(Client client, BiConsumer callback) { + public void executeAsync( + Client client, Duration timeoutPerTransaction, BiConsumer callback) { ConsumerHelper.biConsumer(executeAsync(client, timeoutPerTransaction), callback); } @@ -355,8 +337,11 @@ public void executeAsync(Client client, Consumer onSuccess, * @param onSuccess a Consumer which consumes the result on success. * @param onFailure a Consumer which consumes the error on failure. */ - public void executeAsync(Client client, Duration timeoutPerTransaction, Consumer onSuccess, - Consumer onFailure) { + public void executeAsync( + Client client, + Duration timeoutPerTransaction, + Consumer onSuccess, + Consumer onFailure) { ConsumerHelper.twoConsumers(executeAsync(client, timeoutPerTransaction), onSuccess, onFailure); } } diff --git a/sdk/src/main/java/org/hiero/sdk/TokenRejectTransaction.java b/sdk/src/main/java/org/hiero/sdk/TokenRejectTransaction.java new file mode 100644 index 0000000000..aca81e0c18 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TokenRejectTransaction.java @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.grpc.MethodDescriptor; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenReference; +import org.hiero.sdk.proto.TokenRejectTransactionBody; +import org.hiero.sdk.proto.TokenServiceGrpc; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; + +/** + * Reject undesired token(s).
+ * Transfer one or more token balances held by the requesting account to the treasury for each + * token type.
+ */ +public class TokenRejectTransaction extends Transaction { + + /** + * An account holding the tokens to be rejected. + */ + @Nullable + private AccountId ownerId = null; + + /** + * A list of one or more token rejections (a fungible/common token type). + */ + private List tokenIds = new ArrayList<>(); + + /** + * A list of one or more token rejections (a single specific serialized non-fungible/unique token). + */ + private List nftIds = new ArrayList<>(); + + /** + * Constructor + */ + public TokenRejectTransaction() {} + + /** + * Constructor. + * + * @param txs Compound list of transaction id's list of (AccountId, Transaction) records + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + TokenRejectTransaction(LinkedHashMap> txs) + throws InvalidProtocolBufferException { + super(txs); + initFromTransactionBody(); + } + + /** + * Constructor. + * + * @param txBody protobuf TransactionBody + */ + TokenRejectTransaction(org.hiero.sdk.proto.TransactionBody txBody) { + super(txBody); + initFromTransactionBody(); + } + + /** + * Extract the Account ID of the Owner. + * @return the Account ID of the Owner. + */ + @Nullable + public AccountId getOwnerId() { + return ownerId; + } + + /** + * Assign the Account ID of the Owner. + * @param ownerId the Account ID of the Owner. + * @return {@code this} + */ + public TokenRejectTransaction setOwnerId(AccountId ownerId) { + Objects.requireNonNull(ownerId); + requireNotFrozen(); + this.ownerId = ownerId; + return this; + } + + /** + * Extract the list of tokenIds. + * @return the list of tokenIds. + */ + public List getTokenIds() { + return tokenIds; + } + + /** + * Assign the list of tokenIds. + * @param tokenIds the list of tokenIds. + * @return {@code this} + */ + public TokenRejectTransaction setTokenIds(List tokenIds) { + requireNotFrozen(); + Objects.requireNonNull(tokenIds); + this.tokenIds = new ArrayList<>(tokenIds); + return this; + } + + /** + * Add a token to the list of tokens. + * @param tokenId token to add. + * @return {@code this} + */ + public TokenRejectTransaction addTokenId(TokenId tokenId) { + requireNotFrozen(); + tokenIds.add(tokenId); + return this; + } + + /** + * Extract the list of nftIds. + * @return the list of nftIds. + */ + public List getNftIds() { + return nftIds; + } + + /** + * Assign the list of nftIds. + * @param nftIds the list of nftIds. + * @return {@code this} + */ + public TokenRejectTransaction setNftIds(List nftIds) { + requireNotFrozen(); + Objects.requireNonNull(nftIds); + this.nftIds = new ArrayList<>(nftIds); + return this; + } + + /** + * Add a nft to the list of nfts. + * @param nftId nft to add. + * @return {@code this} + */ + public TokenRejectTransaction addNftId(NftId nftId) { + requireNotFrozen(); + nftIds.add(nftId); + return this; + } + + /** + * Build the transaction body. + * + * @return {@link org.hiero.sdk.proto.TokenRejectTransactionBody} + */ + TokenRejectTransactionBody.Builder build() { + var builder = TokenRejectTransactionBody.newBuilder(); + + if (ownerId != null) { + builder.setOwner(ownerId.toProtobuf()); + } + + for (TokenId tokenId : tokenIds) { + builder.addRejections(TokenReference.newBuilder() + .setFungibleToken(tokenId.toProtobuf()) + .build()); + } + + for (NftId nftId : nftIds) { + builder.addRejections( + TokenReference.newBuilder().setNft(nftId.toProtobuf()).build()); + } + + return builder; + } + + /** + * Initialize from the transaction body. + */ + void initFromTransactionBody() { + var body = sourceTransactionBody.getTokenReject(); + if (body.hasOwner()) { + ownerId = AccountId.fromProtobuf(body.getOwner()); + } + + for (TokenReference tokenReference : body.getRejectionsList()) { + if (tokenReference.hasFungibleToken()) { + tokenIds.add(TokenId.fromProtobuf(tokenReference.getFungibleToken())); + } else if (tokenReference.hasNft()) { + nftIds.add(NftId.fromProtobuf(tokenReference.getNft())); + } + } + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (ownerId != null) { + ownerId.validateChecksum(client); + } + + for (var token : tokenIds) { + if (token != null) { + token.validateChecksum(client); + } + } + + for (var nftId : nftIds) { + nftId.tokenId.validateChecksum(client); + } + } + + @Override + MethodDescriptor getMethodDescriptor() { + return TokenServiceGrpc.getRejectTokenMethod(); + } + + @Override + void onFreeze(TransactionBody.Builder bodyBuilder) { + bodyBuilder.setTokenReject(build()); + } + + @Override + void onScheduled(SchedulableTransactionBody.Builder scheduled) { + scheduled.setTokenReject(build()); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/TokenRelationship.java b/sdk/src/main/java/org/hiero/sdk/TokenRelationship.java new file mode 100644 index 0000000000..0d3fc0d6ba --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TokenRelationship.java @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import com.google.protobuf.InvalidProtocolBufferException; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.TokenFreezeStatus; +import org.hiero.sdk.proto.TokenKycStatus; + +/** + * Token's information related to the given Account. + * + * See Hedera Documentation + */ +public class TokenRelationship { + /** + * A unique token id + */ + public final TokenId tokenId; + /** + * The Symbol of the token + */ + public final String symbol; + /** + * For token of type FUNGIBLE_COMMON - the balance that the Account holds + * in the smallest denomination. + * + * For token of type NON_FUNGIBLE_UNIQUE - the number of NFTs held by the + * account + */ + public final long balance; + /** + * The KYC status of the account (KycNotApplicable, Granted or Revoked). + * + * If the token does not have KYC key, KycNotApplicable is returned + */ + @Nullable + public final Boolean kycStatus; + /** + * The Freeze status of the account (FreezeNotApplicable, Frozen or + * Unfrozen). If the token does not have Freeze key, + * FreezeNotApplicable is returned + */ + @Nullable + public final Boolean freezeStatus; + /** + * The amount of decimal places that this token supports. + */ + public final int decimals; + /** + * Specifies if the relationship is created implicitly. + * False : explicitly associated, + * True : implicitly associated. + */ + public final boolean automaticAssociation; + + TokenRelationship( + TokenId tokenId, + String symbol, + long balance, + @Nullable Boolean kycStatus, + @Nullable Boolean freezeStatus, + int decimals, + boolean automaticAssociation) { + this.tokenId = tokenId; + this.symbol = symbol; + this.balance = balance; + this.kycStatus = kycStatus; + this.freezeStatus = freezeStatus; + this.decimals = decimals; + this.automaticAssociation = automaticAssociation; + } + + /** + * Retrieve freeze status from a protobuf. + * + * @param freezeStatus the protobuf + * @return the freeze status + */ + @Nullable + static Boolean freezeStatusFromProtobuf(TokenFreezeStatus freezeStatus) { + return freezeStatus == TokenFreezeStatus.FreezeNotApplicable ? null : freezeStatus == TokenFreezeStatus.Frozen; + } + + /** + * Retrieve the kyc status from a protobuf. + * + * @param kycStatus the protobuf + * @return the kyc status + */ + @Nullable + static Boolean kycStatusFromProtobuf(TokenKycStatus kycStatus) { + return kycStatus == TokenKycStatus.KycNotApplicable ? null : kycStatus == TokenKycStatus.Granted; + } + + /** + * Create a token relationship object from a protobuf. + * + * @param tokenRelationship the protobuf + * @return the new token relationship + */ + static TokenRelationship fromProtobuf(org.hiero.sdk.proto.TokenRelationship tokenRelationship) { + return new TokenRelationship( + TokenId.fromProtobuf(tokenRelationship.getTokenId()), + tokenRelationship.getSymbol(), + tokenRelationship.getBalance(), + kycStatusFromProtobuf(tokenRelationship.getKycStatus()), + freezeStatusFromProtobuf(tokenRelationship.getFreezeStatus()), + tokenRelationship.getDecimals(), + tokenRelationship.getAutomaticAssociation()); + } + + /** + * Create a token relationship from a byte array. + * + * @param bytes the byte array + * @return the new token relationship + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + public static TokenRelationship fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + return fromProtobuf(org.hiero.sdk.proto.TokenRelationship.parseFrom(bytes).toBuilder() + .build()); + } + + /** + * Retrieve the freeze status from a protobuf. + * + * @param freezeStatus the protobuf + * @return the freeze status + */ + static TokenFreezeStatus freezeStatusToProtobuf(@Nullable Boolean freezeStatus) { + return freezeStatus == null + ? TokenFreezeStatus.FreezeNotApplicable + : freezeStatus ? TokenFreezeStatus.Frozen : TokenFreezeStatus.Unfrozen; + } + + /** + * Retrieve the kyc status from a protobuf. + * + * @param kycStatus the protobuf + * @return the kyc status + */ + static TokenKycStatus kycStatusToProtobuf(@Nullable Boolean kycStatus) { + return kycStatus == null + ? TokenKycStatus.KycNotApplicable + : kycStatus ? TokenKycStatus.Granted : TokenKycStatus.Revoked; + } + + /** + * Create the protobuf. + * + * @return the protobuf representation + */ + org.hiero.sdk.proto.TokenRelationship toProtobuf() { + return org.hiero.sdk.proto.TokenRelationship.newBuilder() + .setTokenId(tokenId.toProtobuf()) + .setSymbol(symbol) + .setBalance(balance) + .setKycStatus(kycStatusToProtobuf(kycStatus)) + .setFreezeStatus(freezeStatusToProtobuf(freezeStatus)) + .setDecimals(decimals) + .setAutomaticAssociation(automaticAssociation) + .build(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("tokenId", tokenId) + .add("symbol", symbol) + .add("balance", balance) + .add("kycStatus", kycStatus) + .add("freezeStatus", freezeStatus) + .add("decimals", decimals) + .add("automaticAssociation", automaticAssociation) + .toString(); + } + + /** + * Create the byte array. + * + * @return the byte array representation + */ + public byte[] toBytes() { + return toProtobuf().toByteArray(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/TokenRevokeKycTransaction.java b/sdk/src/main/java/org/hiero/sdk/TokenRevokeKycTransaction.java new file mode 100644 index 0000000000..b834e20c59 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TokenRevokeKycTransaction.java @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.grpc.MethodDescriptor; +import java.util.LinkedHashMap; +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenRevokeKycTransactionBody; +import org.hiero.sdk.proto.TokenServiceGrpc; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; + +/** + * Revokes the KYC flag to the Hedera account for the given Hedera token. + * This transaction must be signed by the token's KYC Key. If this key is + * not set, you can submit a TokenUpdateTransaction to provide the token + * with this key. + * + * See Hedera Documentation + */ +public class TokenRevokeKycTransaction extends org.hiero.sdk.Transaction { + /** + * The token ID that is associated with the account to remove the KYC flag for + */ + @Nullable + private TokenId tokenId = null; + /** + * The account ID that is associated with the account to remove the KYC flag + */ + @Nullable + private AccountId accountId = null; + + /** + * Constructor. + */ + public TokenRevokeKycTransaction() {} + + /** + * Constructor. + * + * @param txs Compound list of transaction id's list of (AccountId, Transaction) + * records + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + TokenRevokeKycTransaction( + LinkedHashMap> txs) + throws InvalidProtocolBufferException { + super(txs); + initFromTransactionBody(); + } + + /** + * Constructor. + * + * @param txBody protobuf TransactionBody + */ + TokenRevokeKycTransaction(org.hiero.sdk.proto.TransactionBody txBody) { + super(txBody); + initFromTransactionBody(); + } + + /** + * Extract the token id. + * + * @return the token id + */ + @Nullable + public TokenId getTokenId() { + return tokenId; + } + + /** + * Assign the token id. + * + * @param tokenId the token id + * @return {@code this} + */ + public TokenRevokeKycTransaction setTokenId(TokenId tokenId) { + Objects.requireNonNull(tokenId); + requireNotFrozen(); + this.tokenId = tokenId; + return this; + } + + /** + * Extract the account id. + * + * @return the account id + */ + @Nullable + public AccountId getAccountId() { + return accountId; + } + + /** + * Assign the account id. + * + * @param accountId the account id + * @return {@code this} + */ + public TokenRevokeKycTransaction setAccountId(AccountId accountId) { + Objects.requireNonNull(accountId); + requireNotFrozen(); + this.accountId = accountId; + return this; + } + + void initFromTransactionBody() { + var body = sourceTransactionBody.getTokenRevokeKyc(); + if (body.hasToken()) { + tokenId = TokenId.fromProtobuf(body.getToken()); + } + + if (body.hasAccount()) { + accountId = AccountId.fromProtobuf(body.getAccount()); + } + } + + /** + * Build the transaction body. + * + * @return {@link + * org.hiero.sdk.proto.TokenRevokeKycTransactionBody} + */ + TokenRevokeKycTransactionBody.Builder build() { + var builder = TokenRevokeKycTransactionBody.newBuilder(); + if (tokenId != null) { + builder.setToken(tokenId.toProtobuf()); + } + + if (accountId != null) { + builder.setAccount(accountId.toProtobuf()); + } + + return builder; + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (tokenId != null) { + tokenId.validateChecksum(client); + } + + if (accountId != null) { + accountId.validateChecksum(client); + } + } + + @Override + MethodDescriptor getMethodDescriptor() { + return TokenServiceGrpc.getFreezeTokenAccountMethod(); + } + + @Override + void onFreeze(TransactionBody.Builder bodyBuilder) { + bodyBuilder.setTokenRevokeKyc(build()); + } + + @Override + void onScheduled(SchedulableTransactionBody.Builder scheduled) { + scheduled.setTokenRevokeKyc(build()); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/TokenSupplyType.java b/sdk/src/main/java/org/hiero/sdk/TokenSupplyType.java new file mode 100644 index 0000000000..fc2a4f8578 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TokenSupplyType.java @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +/** + * Possible Token Supply Types (IWA Compatibility). + *

+ * Indicates how many tokens can have during its lifetime. + *

+ * See Hedera Documentation + */ +public enum TokenSupplyType { + /** + * Indicates that tokens of that type have an upper bound of Long.MAX_VALUE. + */ + INFINITE(org.hiero.sdk.proto.TokenSupplyType.INFINITE), + /** + * Indicates that tokens of that type have an upper bound of maxSupply, provided on token creation. + */ + FINITE(org.hiero.sdk.proto.TokenSupplyType.FINITE); + + final org.hiero.sdk.proto.TokenSupplyType code; + + /** + * Constructor. + * + * @param code the token supply type + */ + TokenSupplyType(org.hiero.sdk.proto.TokenSupplyType code) { + this.code = code; + } + + /** + * What type are we. + * + * @param code the token supply type in question + * @return the token supply type + */ + static TokenSupplyType valueOf(org.hiero.sdk.proto.TokenSupplyType code) { + return switch (code) { + case INFINITE -> INFINITE; + case FINITE -> FINITE; + default -> throw new IllegalStateException("(BUG) unhandled TokenSupplyType"); + }; + } + + @Override + public String toString() { + return switch (this) { + case INFINITE -> "INFINITE"; + case FINITE -> "FINITE"; + }; + } + + public org.hiero.sdk.proto.TokenSupplyType toProtobuf() { + return this.code; + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/TokenTransfer.java b/sdk/src/main/java/org/hiero/sdk/TokenTransfer.java new file mode 100644 index 0000000000..6fda552f87 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TokenTransfer.java @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.AccountAmount; + +/** + * A token transfer record. + * + * Internal utility class. + */ +public class TokenTransfer { + final TokenId tokenId; + final AccountId accountId; + + @Nullable + Integer expectedDecimals; + + long amount; + + boolean isApproved; + + /** + * Constructor. + * + * @param tokenId the token id + * @param accountId the account id + * @param amount the amount + * @param isApproved is it approved + */ + TokenTransfer(TokenId tokenId, AccountId accountId, long amount, boolean isApproved) { + this(tokenId, accountId, amount, null, isApproved); + } + + /** + * Constructor. + * + * @param tokenId the token id + * @param accountId the account id + * @param amount the amount + * @param expectedDecimals the expected decimals + * @param isApproved is it approved + */ + TokenTransfer( + TokenId tokenId, AccountId accountId, long amount, @Nullable Integer expectedDecimals, boolean isApproved) { + this.tokenId = tokenId; + this.accountId = accountId; + this.amount = amount; + this.expectedDecimals = expectedDecimals; + this.isApproved = isApproved; + } + + /** + * Create a list of token transfer records from a protobuf. + * + * @param tokenTransferLists the protobuf + * @return the list of token transfer records + */ + static List fromProtobuf(List tokenTransferLists) { + var transfers = new ArrayList(); + + for (var tokenTransferList : tokenTransferLists) { + var tokenId = TokenId.fromProtobuf(tokenTransferList.getToken()); + + for (var transfer : tokenTransferList.getTransfersList()) { + transfers.add(new TokenTransfer( + tokenId, + AccountId.fromProtobuf(transfer.getAccountID()), + transfer.getAmount(), + tokenTransferList.hasExpectedDecimals() + ? tokenTransferList.getExpectedDecimals().getValue() + : null, + transfer.getIsApproval())); + } + } + + return transfers; + } + + /** + * Create the protobuf. + * + * @return an account amount protobuf + */ + AccountAmount toProtobuf() { + return AccountAmount.newBuilder() + .setAccountID(accountId.toProtobuf()) + .setAmount(amount) + .setIsApproval(isApproved) + .build(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("tokenId", tokenId) + .add("accountId", accountId) + .add("amount", amount) + .add("expectedDecimals", expectedDecimals) + .add("isApproved", isApproved) + .toString(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/TokenTransferList.java b/sdk/src/main/java/org/hiero/sdk/TokenTransferList.java new file mode 100644 index 0000000000..b4b55434b6 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TokenTransferList.java @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.UInt32Value; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.AccountAmount; +import org.hiero.sdk.proto.NftTransfer; + +class TokenTransferList { + final TokenId tokenId; + + @Nullable + final Integer expectDecimals; + + List transfers = new ArrayList<>(); + List nftTransfers = new ArrayList<>(); + + TokenTransferList( + TokenId tokenId, + @Nullable Integer expectDecimals, + @Nullable TokenTransfer transfer, + @Nullable TokenNftTransfer nftTransfer) { + this.tokenId = tokenId; + this.expectDecimals = expectDecimals; + + if (transfer != null) { + this.transfers.add(transfer); + } + + if (nftTransfer != null) { + this.nftTransfers.add(nftTransfer); + } + } + + org.hiero.sdk.proto.TokenTransferList toProtobuf() { + var transfers = new ArrayList(); + var nftTransfers = new ArrayList(); + + for (var transfer : this.transfers) { + transfers.add(transfer.toProtobuf()); + } + + for (var transfer : this.nftTransfers) { + nftTransfers.add(transfer.toProtobuf()); + } + + var builder = org.hiero.sdk.proto.TokenTransferList.newBuilder() + .setToken(tokenId.toProtobuf()) + .addAllTransfers(transfers) + .addAllNftTransfers(nftTransfers); + + if (expectDecimals != null) { + builder.setExpectedDecimals( + UInt32Value.newBuilder().setValue(expectDecimals).build()); + } + + return builder.build(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/TokenType.java b/sdk/src/main/java/org/hiero/sdk/TokenType.java new file mode 100644 index 0000000000..4f3a897ffd --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TokenType.java @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +/** + * Possible Token Types (IWA Compatibility). + *

+ * Apart from fungible and non-fungible, Tokens can have either a common or + * unique representation. This distinction might seem subtle, but it is + * important when considering how tokens can be traced and if they can have + * isolated and unique properties. + *

+ * See Hedera Documentation + */ +public enum TokenType { + /** + * Interchangeable value with one another, where any quantity of them has the same value as another equal quantity if they are in the same class. + * Share a single set of properties, not distinct from one another. Simply represented as a balance or quantity to a given Hedera account. + */ + FUNGIBLE_COMMON(org.hiero.sdk.proto.TokenType.FUNGIBLE_COMMON), + /** + * Unique, not interchangeable with other tokens of the same type as they typically have different values. + * Individually traced and can carry unique properties (e.g. serial number). + */ + NON_FUNGIBLE_UNIQUE(org.hiero.sdk.proto.TokenType.NON_FUNGIBLE_UNIQUE); + + final org.hiero.sdk.proto.TokenType code; + + /** + * Constructor. + * + * @param code the token type + */ + TokenType(org.hiero.sdk.proto.TokenType code) { + this.code = code; + } + + /** + * What type are we. + * + * @param code the token type in question + * @return the token type + */ + static TokenType valueOf(org.hiero.sdk.proto.TokenType code) { + return switch (code) { + case FUNGIBLE_COMMON -> FUNGIBLE_COMMON; + case NON_FUNGIBLE_UNIQUE -> NON_FUNGIBLE_UNIQUE; + default -> throw new IllegalStateException("(BUG) unhandled TokenType"); + }; + } + + @Override + public String toString() { + return switch (this) { + case FUNGIBLE_COMMON -> "FUNGIBLE_COMMON"; + case NON_FUNGIBLE_UNIQUE -> "NON_FUNGIBLE_UNIQUE"; + }; + } + + public org.hiero.sdk.proto.TokenType toProtobuf() { + return this.code; + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/TokenUnfreezeTransaction.java b/sdk/src/main/java/org/hiero/sdk/TokenUnfreezeTransaction.java new file mode 100644 index 0000000000..8e2fb0fa1a --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TokenUnfreezeTransaction.java @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.grpc.MethodDescriptor; +import java.util.LinkedHashMap; +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenServiceGrpc; +import org.hiero.sdk.proto.TokenUnfreezeAccountTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; + +/** + * Unfreezes transfers of the specified token for the account. + * + * The transaction must be signed by the token's Freeze Key. + * + * See Hedera Documentation + */ +public class TokenUnfreezeTransaction extends org.hiero.sdk.Transaction { + @Nullable + private TokenId tokenId = null; + + @Nullable + private AccountId accountId = null; + + /** + * Constructor. + */ + public TokenUnfreezeTransaction() {} + + /** + * Constructor. + * + * @param txs Compound list of transaction id's list of (AccountId, Transaction) + * records + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + TokenUnfreezeTransaction( + LinkedHashMap> txs) + throws InvalidProtocolBufferException { + super(txs); + initFromTransactionBody(); + } + + /** + * Constructor. + * + * @param txBody protobuf TransactionBody + */ + TokenUnfreezeTransaction(org.hiero.sdk.proto.TransactionBody txBody) { + super(txBody); + initFromTransactionBody(); + } + + /** + * Extract the token id. + * + * @return the token id + */ + @Nullable + public TokenId getTokenId() { + return tokenId; + } + + /** + * Assign the token id. + * + * @param tokenId the token id + * @return {@code this} + */ + public TokenUnfreezeTransaction setTokenId(TokenId tokenId) { + Objects.requireNonNull(tokenId); + requireNotFrozen(); + this.tokenId = tokenId; + return this; + } + + /** + * Extract the account id. + * + * @return the account id + */ + @Nullable + public AccountId getAccountId() { + return accountId; + } + + /** + * Assign the account id. + * + * @param accountId the account id + * @return {@code this} + */ + public TokenUnfreezeTransaction setAccountId(AccountId accountId) { + Objects.requireNonNull(accountId); + requireNotFrozen(); + this.accountId = accountId; + return this; + } + + /** + * Initialize from the transaction body. + */ + void initFromTransactionBody() { + var body = sourceTransactionBody.getTokenUnfreeze(); + if (body.hasToken()) { + tokenId = TokenId.fromProtobuf(body.getToken()); + } + + if (body.hasAccount()) { + accountId = AccountId.fromProtobuf(body.getAccount()); + } + } + + /** + * Build the transaction body. + * + * @return {@code @link + * org.hiero.sdk.proto.TokenUnfreezeAccountTransactionBody} + */ + TokenUnfreezeAccountTransactionBody.Builder build() { + var builder = TokenUnfreezeAccountTransactionBody.newBuilder(); + if (tokenId != null) { + builder.setToken(tokenId.toProtobuf()); + } + + if (accountId != null) { + builder.setAccount(accountId.toProtobuf()); + } + + return builder; + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (tokenId != null) { + tokenId.validateChecksum(client); + } + + if (accountId != null) { + accountId.validateChecksum(client); + } + } + + @Override + MethodDescriptor getMethodDescriptor() { + return TokenServiceGrpc.getFreezeTokenAccountMethod(); + } + + @Override + void onFreeze(TransactionBody.Builder bodyBuilder) { + bodyBuilder.setTokenUnfreeze(build()); + } + + @Override + void onScheduled(SchedulableTransactionBody.Builder scheduled) { + scheduled.setTokenUnfreeze(build()); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/TokenUnpauseTransaction.java b/sdk/src/main/java/org/hiero/sdk/TokenUnpauseTransaction.java new file mode 100644 index 0000000000..a421506615 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TokenUnpauseTransaction.java @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.grpc.MethodDescriptor; +import java.util.LinkedHashMap; +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenServiceGrpc; +import org.hiero.sdk.proto.TokenUnpauseTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; + +/** + * A token unpause transaction is a transaction that unpauses the token + * that was previously disabled from participating in transactions. The + * token's pause key is required to sign the transaction. Once the unpause + * transaction is submitted the token pause status is updated to unpause. + * + * See Hedera Documentation + */ +public class TokenUnpauseTransaction extends Transaction { + @Nullable + private TokenId tokenId = null; + + /** + * Constructor + */ + public TokenUnpauseTransaction() {} + + /** + * Constructor. + * + * @param txs Compound list of transaction id's list of (AccountId, Transaction) + * records + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + TokenUnpauseTransaction(LinkedHashMap> txs) + throws InvalidProtocolBufferException { + super(txs); + initFromTransactionBody(); + } + + /** + * Constructor. + * + * @param txBody protobuf TransactionBody + */ + TokenUnpauseTransaction(org.hiero.sdk.proto.TransactionBody txBody) { + super(txBody); + initFromTransactionBody(); + } + + /** + * Extract the token id. + * + * @return the token id + */ + @Nullable + public TokenId getTokenId() { + return tokenId; + } + + /** + * Assign the token id. + * + * @param tokenId the token id + * @return {@code this} + */ + public TokenUnpauseTransaction setTokenId(TokenId tokenId) { + Objects.requireNonNull(tokenId); + requireNotFrozen(); + this.tokenId = tokenId; + return this; + } + + /** + * Initialize from the transaction body. + */ + void initFromTransactionBody() { + var body = sourceTransactionBody.getTokenUnpause(); + if (body.hasToken()) { + tokenId = TokenId.fromProtobuf(body.getToken()); + } + } + + /** + * Build the transaction body. + * + * @return {@link + * org.hiero.sdk.proto.TokenUnpauseTransactionBody} + */ + TokenUnpauseTransactionBody.Builder build() { + var builder = TokenUnpauseTransactionBody.newBuilder(); + if (tokenId != null) { + builder.setToken(tokenId.toProtobuf()); + } + + return builder; + } + + @Override + MethodDescriptor getMethodDescriptor() { + return TokenServiceGrpc.getUnpauseTokenMethod(); + } + + @Override + void onFreeze(TransactionBody.Builder bodyBuilder) { + bodyBuilder.setTokenUnpause(build()); + } + + @Override + void onScheduled(SchedulableTransactionBody.Builder scheduled) { + scheduled.setTokenUnpause(build()); + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (tokenId != null) { + tokenId.validateChecksum(client); + } + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenUpdateNftsTransaction.java b/sdk/src/main/java/org/hiero/sdk/TokenUpdateNftsTransaction.java similarity index 75% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/TokenUpdateNftsTransaction.java rename to sdk/src/main/java/org/hiero/sdk/TokenUpdateNftsTransaction.java index 523d0365fe..7edd42ad2d 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenUpdateNftsTransaction.java +++ b/sdk/src/main/java/org/hiero/sdk/TokenUpdateNftsTransaction.java @@ -1,32 +1,9 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.ByteString; import com.google.protobuf.BytesValue; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenServiceGrpc; -import com.hedera.hashgraph.sdk.proto.TokenUpdateNftsTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; import io.grpc.MethodDescriptor; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -34,6 +11,11 @@ import java.util.Objects; import javax.annotation.Nonnegative; import javax.annotation.Nullable; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenServiceGrpc; +import org.hiero.sdk.proto.TokenUpdateNftsTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; public class TokenUpdateNftsTransaction extends Transaction { @@ -56,8 +38,7 @@ public class TokenUpdateNftsTransaction extends Transaction> txs) - throws InvalidProtocolBufferException { + LinkedHashMap> txs) + throws InvalidProtocolBufferException { super(txs); initFromTransactionBody(); } - TokenUpdateNftsTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { + TokenUpdateNftsTransaction(org.hiero.sdk.proto.TransactionBody txBody) { super(txBody); initFromTransactionBody(); } @@ -173,7 +154,7 @@ void initFromTransactionBody() { /** * Build the transaction body. * - * @return {@link com.hedera.hashgraph.sdk.proto.TokenUpdateNftsTransactionBody} + * @return {@link org.hiero.sdk.proto.TokenUpdateNftsTransactionBody} */ TokenUpdateNftsTransactionBody.Builder build() { var builder = TokenUpdateNftsTransactionBody.newBuilder(); @@ -201,7 +182,7 @@ void validateChecksums(Client client) throws BadEntityIdException { } @Override - MethodDescriptor getMethodDescriptor() { + MethodDescriptor getMethodDescriptor() { return TokenServiceGrpc.getUpdateNftsMethod(); } diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenUpdateTransaction.java b/sdk/src/main/java/org/hiero/sdk/TokenUpdateTransaction.java similarity index 93% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/TokenUpdateTransaction.java rename to sdk/src/main/java/org/hiero/sdk/TokenUpdateTransaction.java index ef130ff018..c0a9054c10 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenUpdateTransaction.java +++ b/sdk/src/main/java/org/hiero/sdk/TokenUpdateTransaction.java @@ -1,40 +1,21 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.ByteString; import com.google.protobuf.BytesValue; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.StringValue; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenServiceGrpc; -import com.hedera.hashgraph.sdk.proto.TokenUpdateTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; import io.grpc.MethodDescriptor; import java.time.Duration; import java.time.Instant; - -import javax.annotation.Nullable; import java.util.LinkedHashMap; import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenServiceGrpc; +import org.hiero.sdk.proto.TokenUpdateTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; /** * A transaction that updates the properties of an existing token. The admin @@ -142,6 +123,7 @@ public class TokenUpdateTransaction extends Transaction */ @Nullable private Instant expirationTime = null; + private Duration expirationTimeDuration = null; /** @@ -170,8 +152,7 @@ public class TokenUpdateTransaction extends Transaction /** * Constructor. */ - public TokenUpdateTransaction() { - } + public TokenUpdateTransaction() {} /** * Constructor. @@ -180,7 +161,8 @@ public TokenUpdateTransaction() { * records * @throws InvalidProtocolBufferException when there is an issue with the protobuf */ - TokenUpdateTransaction(LinkedHashMap> txs) throws InvalidProtocolBufferException { + TokenUpdateTransaction(LinkedHashMap> txs) + throws InvalidProtocolBufferException { super(txs); initFromTransactionBody(); } @@ -190,7 +172,7 @@ public TokenUpdateTransaction() { * * @param txBody protobuf TransactionBody */ - TokenUpdateTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { + TokenUpdateTransaction(org.hiero.sdk.proto.TransactionBody txBody) { super(txBody); initFromTransactionBody(); } @@ -686,7 +668,7 @@ void initFromTransactionBody() { * Build the transaction body. * * @return {@link - * com.hedera.hashgraph.sdk.proto.TokenUpdateTransactionBody} + * org.hiero.sdk.proto.TokenUpdateTransactionBody} */ TokenUpdateTransactionBody.Builder build() { var builder = TokenUpdateTransactionBody.newBuilder(); @@ -762,7 +744,7 @@ void validateChecksums(Client client) throws BadEntityIdException { } @Override - MethodDescriptor getMethodDescriptor() { + MethodDescriptor getMethodDescriptor() { return TokenServiceGrpc.getUpdateTokenMethod(); } diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenWipeTransaction.java b/sdk/src/main/java/org/hiero/sdk/TokenWipeTransaction.java similarity index 79% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/TokenWipeTransaction.java rename to sdk/src/main/java/org/hiero/sdk/TokenWipeTransaction.java index 905d784691..e75457e9e9 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TokenWipeTransaction.java +++ b/sdk/src/main/java/org/hiero/sdk/TokenWipeTransaction.java @@ -1,38 +1,19 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenServiceGrpc; -import com.hedera.hashgraph.sdk.proto.TokenWipeAccountTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; import io.grpc.MethodDescriptor; - -import javax.annotation.Nonnegative; -import javax.annotation.Nullable; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Objects; +import javax.annotation.Nonnegative; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenServiceGrpc; +import org.hiero.sdk.proto.TokenWipeAccountTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; /** * Wipes the provided amount of fungible or non-fungible tokens from the @@ -42,7 +23,7 @@ * * See Hedera Documentation */ -public class TokenWipeTransaction extends com.hedera.hashgraph.sdk.Transaction { +public class TokenWipeTransaction extends org.hiero.sdk.Transaction { /** * The ID of the token to wipe from the account */ @@ -70,8 +51,7 @@ public class TokenWipeTransaction extends com.hedera.hashgraph.sdk.Transaction> txs) throws InvalidProtocolBufferException { + TokenWipeTransaction(LinkedHashMap> txs) + throws InvalidProtocolBufferException { super(txs); initFromTransactionBody(); } @@ -90,7 +71,7 @@ public TokenWipeTransaction() { * * @param txBody protobuf TransactionBody */ - TokenWipeTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { + TokenWipeTransaction(org.hiero.sdk.proto.TransactionBody txBody) { super(txBody); initFromTransactionBody(); } @@ -216,7 +197,7 @@ void initFromTransactionBody() { * Build the transaction body. * * @return {@link - * com.hedera.hashgraph.sdk.proto.TokenWipeAccountTransactionBody} + * org.hiero.sdk.proto.TokenWipeAccountTransactionBody} */ TokenWipeAccountTransactionBody.Builder build() { var builder = TokenWipeAccountTransactionBody.newBuilder(); @@ -247,7 +228,7 @@ void validateChecksums(Client client) throws BadEntityIdException { } @Override - MethodDescriptor getMethodDescriptor() { + MethodDescriptor getMethodDescriptor() { return TokenServiceGrpc.getWipeTokenAccountMethod(); } @@ -261,4 +242,3 @@ void onScheduled(SchedulableTransactionBody.Builder scheduled) { scheduled.setTokenWipe(build()); } } - diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TopicCreateTransaction.java b/sdk/src/main/java/org/hiero/sdk/TopicCreateTransaction.java similarity index 87% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/TopicCreateTransaction.java rename to sdk/src/main/java/org/hiero/sdk/TopicCreateTransaction.java index 73bf7509d7..dd7f0e3ec7 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TopicCreateTransaction.java +++ b/sdk/src/main/java/org/hiero/sdk/TopicCreateTransaction.java @@ -1,36 +1,17 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.ConsensusCreateTopicTransactionBody; -import com.hedera.hashgraph.sdk.proto.ConsensusServiceGrpc; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; import io.grpc.MethodDescriptor; import java.time.Duration; - -import javax.annotation.Nullable; import java.util.LinkedHashMap; import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.ConsensusCreateTopicTransactionBody; +import org.hiero.sdk.proto.ConsensusServiceGrpc; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; /** * Create a topic to be used for consensus. @@ -107,7 +88,8 @@ public TopicCreateTransaction() { * records * @throws InvalidProtocolBufferException when there is an issue with the protobuf */ - TopicCreateTransaction(LinkedHashMap> txs) throws InvalidProtocolBufferException { + TopicCreateTransaction(LinkedHashMap> txs) + throws InvalidProtocolBufferException { super(txs); initFromTransactionBody(); } @@ -117,7 +99,7 @@ public TopicCreateTransaction() { * * @param txBody protobuf TransactionBody */ - TopicCreateTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { + TopicCreateTransaction(org.hiero.sdk.proto.TransactionBody txBody) { super(txBody); initFromTransactionBody(); } @@ -282,7 +264,7 @@ void initFromTransactionBody() { * Build the transaction body. * * @return {@link - * com.hedera.hashgraph.sdk.proto.ConsensusCreateTopicTransactionBody} + * org.hiero.sdk.proto.ConsensusCreateTopicTransactionBody} */ ConsensusCreateTopicTransactionBody.Builder build() { var builder = ConsensusCreateTopicTransactionBody.newBuilder(); @@ -311,7 +293,7 @@ void validateChecksums(Client client) throws BadEntityIdException { } @Override - MethodDescriptor getMethodDescriptor() { + MethodDescriptor getMethodDescriptor() { return ConsensusServiceGrpc.getCreateTopicMethod(); } diff --git a/sdk/src/main/java/org/hiero/sdk/TopicDeleteTransaction.java b/sdk/src/main/java/org/hiero/sdk/TopicDeleteTransaction.java new file mode 100644 index 0000000000..b6fda2f6c6 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TopicDeleteTransaction.java @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.grpc.MethodDescriptor; +import java.util.LinkedHashMap; +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.ConsensusDeleteTopicTransactionBody; +import org.hiero.sdk.proto.ConsensusServiceGrpc; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; + +/** + * Delete a topic. + *

+ * No more transactions or queries on the topic will succeed. + *

+ * If an {@code adminKey} is set, this transaction must be signed by that key. + * If there is no {@code adminKey}, this transaction will fail with {@link Status#UNAUTHORIZED}. + */ +public final class TopicDeleteTransaction extends Transaction { + @Nullable + private TopicId topicId = null; + + /** + * Constructor. + */ + public TopicDeleteTransaction() {} + + /** + * Constructor. + * + * @param txs Compound list of transaction id's list of (AccountId, Transaction) + * records + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + TopicDeleteTransaction(LinkedHashMap> txs) + throws InvalidProtocolBufferException { + super(txs); + initFromTransactionBody(); + } + + /** + * Constructor. + * + * @param txBody protobuf TransactionBody + */ + TopicDeleteTransaction(org.hiero.sdk.proto.TransactionBody txBody) { + super(txBody); + initFromTransactionBody(); + } + + /** + * Extract the topic id. + * + * @return the topic id + */ + @Nullable + public TopicId getTopicId() { + return topicId; + } + + /** + * Set the topic ID to delete. + * + * @param topicId The TopicId to be set + * @return {@code this} + */ + public TopicDeleteTransaction setTopicId(TopicId topicId) { + Objects.requireNonNull(topicId); + requireNotFrozen(); + this.topicId = topicId; + return this; + } + + /** + * Initialize from the transaction body. + */ + void initFromTransactionBody() { + var body = sourceTransactionBody.getConsensusDeleteTopic(); + if (body.hasTopicID()) { + topicId = TopicId.fromProtobuf(body.getTopicID()); + } + } + + /** + * Build the transaction body. + * + * @return {@link + * org.hiero.sdk.proto.ConsensusDeleteTopicTransactionBody} + */ + ConsensusDeleteTopicTransactionBody.Builder build() { + var builder = ConsensusDeleteTopicTransactionBody.newBuilder(); + if (topicId != null) { + builder.setTopicID(topicId.toProtobuf()); + } + + return builder; + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (topicId != null) { + topicId.validateChecksum(client); + } + } + + @Override + MethodDescriptor getMethodDescriptor() { + return ConsensusServiceGrpc.getDeleteTopicMethod(); + } + + @Override + void onFreeze(TransactionBody.Builder bodyBuilder) { + bodyBuilder.setConsensusDeleteTopic(build()); + } + + @Override + void onScheduled(SchedulableTransactionBody.Builder scheduled) { + scheduled.setConsensusDeleteTopic(build()); + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TopicId.java b/sdk/src/main/java/org/hiero/sdk/TopicId.java similarity index 87% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/TopicId.java rename to sdk/src/main/java/org/hiero/sdk/TopicId.java index 6961592b48..aa4139e2ba 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TopicId.java +++ b/sdk/src/main/java/org/hiero/sdk/TopicId.java @@ -1,30 +1,11 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.TopicID; - +import java.util.Objects; import javax.annotation.Nonnegative; import javax.annotation.Nullable; -import java.util.Objects; +import org.hiero.sdk.proto.TopicID; /** * Unique identifier for a topic (used by the consensus service). @@ -147,10 +128,10 @@ public String toSolidityAddress() { */ TopicID toProtobuf() { return TopicID.newBuilder() - .setShardNum(shard) - .setRealmNum(realm) - .setTopicNum(num) - .build(); + .setShardNum(shard) + .setRealmNum(realm) + .setTopicNum(num) + .build(); } /** @@ -213,7 +194,7 @@ public int hashCode() { } @Override - public boolean equals( Object o) { + public boolean equals(Object o) { if (this == o) { return true; } diff --git a/sdk/src/main/java/org/hiero/sdk/TopicInfo.java b/sdk/src/main/java/org/hiero/sdk/TopicInfo.java new file mode 100644 index 0000000000..fafa245f1d --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TopicInfo.java @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import java.time.Duration; +import java.time.Instant; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.ConsensusGetTopicInfoResponse; +import org.hiero.sdk.proto.ConsensusTopicInfo; + +/** + * Current state of a topic. + */ +public final class TopicInfo { + /** + * The ID of the topic for which information is requested. + */ + public final TopicId topicId; + + /** + * Short publicly visible memo about the topic. No guarantee of uniqueness. + */ + public final String topicMemo; + + /** + * SHA-384 running hash of (previousRunningHash, topicId, consensusTimestamp, sequenceNumber, message). + */ + public final ByteString runningHash; + + /** + * Sequence number (starting at 1 for the first submitMessage) of messages on the topic. + */ + public final long sequenceNumber; + + /** + * Effective consensus timestamp at (and after) which submitMessage calls will no longer succeed on the topic. + */ + public final Instant expirationTime; + + /** + * Access control for update/delete of the topic. Null if there is no key. + */ + @Nullable + public final Key adminKey; + + /** + * Access control for ConsensusService.submitMessage. Null if there is no key. + */ + @Nullable + public final Key submitKey; + + /** + * If an auto-renew account is specified, when the topic expires, its lifetime will be extended + * by up to this duration (depending on the solvency of the auto-renew account). If the + * auto-renew account has no funds at all, the topic will be deleted instead. + */ + public final Duration autoRenewPeriod; + + /** + * The account, if any, to charge for automatic renewal of the topic's lifetime upon expiry. + */ + @Nullable + public final AccountId autoRenewAccountId; + + /** + * The ledger ID the response was returned from; please see HIP-198 for the network-specific IDs. + */ + public final LedgerId ledgerId; + + private TopicInfo( + TopicId topicId, + String topicMemo, + ByteString runningHash, + long sequenceNumber, + Instant expirationTime, + @Nullable Key adminKey, + @Nullable Key submitKey, + Duration autoRenewPeriod, + @Nullable AccountId autoRenewAccountId, + LedgerId ledgerId) { + this.topicId = topicId; + this.topicMemo = topicMemo; + this.runningHash = runningHash; + this.sequenceNumber = sequenceNumber; + this.expirationTime = expirationTime; + this.adminKey = adminKey; + this.submitKey = submitKey; + this.autoRenewPeriod = autoRenewPeriod; + this.autoRenewAccountId = autoRenewAccountId; + this.ledgerId = ledgerId; + } + + /** + * Create a topic info object from a protobuf. + * + * @param topicInfoResponse the protobuf + * @return the new topic info object + */ + static TopicInfo fromProtobuf(ConsensusGetTopicInfoResponse topicInfoResponse) { + var topicInfo = topicInfoResponse.getTopicInfo(); + + var adminKey = topicInfo.hasAdminKey() ? Key.fromProtobufKey(topicInfo.getAdminKey()) : null; + + var submitKey = topicInfo.hasSubmitKey() ? Key.fromProtobufKey(topicInfo.getSubmitKey()) : null; + + var autoRenewAccountId = + topicInfo.hasAutoRenewAccount() ? AccountId.fromProtobuf(topicInfo.getAutoRenewAccount()) : null; + + return new TopicInfo( + TopicId.fromProtobuf(topicInfoResponse.getTopicID()), + topicInfo.getMemo(), + topicInfo.getRunningHash(), + topicInfo.getSequenceNumber(), + InstantConverter.fromProtobuf(topicInfo.getExpirationTime()), + adminKey, + submitKey, + DurationConverter.fromProtobuf(topicInfo.getAutoRenewPeriod()), + autoRenewAccountId, + LedgerId.fromByteString(topicInfo.getLedgerId())); + } + + /** + * Create a topic info object from a byte array. + * + * @param bytes the byte array + * @return the new topic info object + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + public static TopicInfo fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + return fromProtobuf( + ConsensusGetTopicInfoResponse.parseFrom(bytes).toBuilder().build()); + } + + /** + * Create the protobuf. + * + * @return the protobuf representation + */ + ConsensusGetTopicInfoResponse toProtobuf() { + var topicInfoResponseBuilder = + ConsensusGetTopicInfoResponse.newBuilder().setTopicID(topicId.toProtobuf()); + + var topicInfoBuilder = ConsensusTopicInfo.newBuilder() + .setMemo(topicMemo) + .setRunningHash(runningHash) + .setSequenceNumber(sequenceNumber) + .setExpirationTime(InstantConverter.toProtobuf(expirationTime)) + .setAutoRenewPeriod(DurationConverter.toProtobuf(autoRenewPeriod)) + .setLedgerId(ledgerId.toByteString()); + + if (adminKey != null) { + topicInfoBuilder.setAdminKey(adminKey.toProtobufKey()); + } + + if (submitKey != null) { + topicInfoBuilder.setSubmitKey(submitKey.toProtobufKey()); + } + + if (autoRenewAccountId != null) { + topicInfoBuilder.setAutoRenewAccount(autoRenewAccountId.toProtobuf()); + } + + return topicInfoResponseBuilder.setTopicInfo(topicInfoBuilder).build(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("topicId", topicId) + .add("topicMemo", topicMemo) + .add("runningHash", runningHash.toByteArray()) + .add("sequenceNumber", sequenceNumber) + .add("expirationTime", expirationTime) + .add("adminKey", adminKey) + .add("submitKey", submitKey) + .add("autoRenewPeriod", autoRenewPeriod) + .add("autoRenewAccountId", autoRenewAccountId) + .add("ledgerId", ledgerId) + .toString(); + } + + /** + * Create the byte array. + * + * @return the byte array representation + */ + public byte[] toBytes() { + return toProtobuf().toByteArray(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/TopicInfoQuery.java b/sdk/src/main/java/org/hiero/sdk/TopicInfoQuery.java new file mode 100644 index 0000000000..d729ec4372 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TopicInfoQuery.java @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.grpc.MethodDescriptor; +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.ConsensusGetTopicInfoQuery; +import org.hiero.sdk.proto.ConsensusServiceGrpc; +import org.hiero.sdk.proto.QueryHeader; +import org.hiero.sdk.proto.Response; +import org.hiero.sdk.proto.ResponseHeader; + +/** + * Retrieve the latest state of a topic. + *

+ * This method is unrestricted and allowed on any topic by any payer account. + */ +public final class TopicInfoQuery extends Query { + @Nullable + TopicId topicId = null; + + /** + * Constructor. + */ + public TopicInfoQuery() {} + + /** + * Extract the topic id. + * + * @return the topic id + */ + @Nullable + public TopicId getTopicId() { + return topicId; + } + + /** + * Set the topic to retrieve info about (the parameters and running state of). + * + * @param topicId The TopicId to be set + * @return {@code this} + */ + public TopicInfoQuery setTopicId(TopicId topicId) { + Objects.requireNonNull(topicId); + this.topicId = topicId; + return this; + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (topicId != null) { + topicId.validateChecksum(client); + } + } + + @Override + void onMakeRequest(org.hiero.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { + var builder = ConsensusGetTopicInfoQuery.newBuilder(); + if (topicId != null) { + builder.setTopicID(topicId.toProtobuf()); + } + + queryBuilder.setConsensusGetTopicInfo(builder.setHeader(header)); + } + + @Override + ResponseHeader mapResponseHeader(Response response) { + return response.getConsensusGetTopicInfo().getHeader(); + } + + @Override + QueryHeader mapRequestHeader(org.hiero.sdk.proto.Query request) { + return request.getConsensusGetTopicInfo().getHeader(); + } + + @Override + TopicInfo mapResponse(Response response, AccountId nodeId, org.hiero.sdk.proto.Query request) { + return TopicInfo.fromProtobuf(response.getConsensusGetTopicInfo()); + } + + @Override + MethodDescriptor getMethodDescriptor() { + return ConsensusServiceGrpc.getGetTopicInfoMethod(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/TopicMessage.java b/sdk/src/main/java/org/hiero/sdk/TopicMessage.java new file mode 100644 index 0000000000..f989e82de3 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TopicMessage.java @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import com.google.protobuf.ByteString; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.util.List; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.mirror.ConsensusTopicResponse; + +/** + * Topic message records. + */ +public final class TopicMessage { + /** + * The consensus timestamp of the message in seconds.nanoseconds + */ + public final Instant consensusTimestamp; + /** + * The content of the message + */ + public final byte[] contents; + /** + * The new running hash of the topic that received the message + */ + public final byte[] runningHash; + /** + * The sequence number of the message relative to all other messages + * for the same topic + */ + public final long sequenceNumber; + /** + * Array of topic message chunks. + */ + @Nullable + public final TopicMessageChunk[] chunks; + /** + * The transaction id + */ + @Nullable + public final TransactionId transactionId; + + /** + * Constructor. + * + * @param lastConsensusTimestamp the last consensus time + * @param message the message + * @param lastRunningHash the last running hash + * @param lastSequenceNumber the last sequence number + * @param chunks the array of chunks + * @param transactionId the transaction id + */ + TopicMessage( + Instant lastConsensusTimestamp, + byte[] message, + byte[] lastRunningHash, + long lastSequenceNumber, + @Nullable TopicMessageChunk[] chunks, + @Nullable TransactionId transactionId) { + this.consensusTimestamp = lastConsensusTimestamp; + this.contents = message; + this.runningHash = lastRunningHash; + this.sequenceNumber = lastSequenceNumber; + this.chunks = chunks; + this.transactionId = transactionId; + } + + /** + * Create a new topic message from a response protobuf. + * + * @param response the protobuf response + * @return the new topic message + */ + static TopicMessage ofSingle(ConsensusTopicResponse response) { + return new TopicMessage( + InstantConverter.fromProtobuf(response.getConsensusTimestamp()), + response.getMessage().toByteArray(), + response.getRunningHash().toByteArray(), + response.getSequenceNumber(), + new TopicMessageChunk[] {new TopicMessageChunk(response)}, + response.hasChunkInfo() && response.getChunkInfo().hasInitialTransactionID() + ? TransactionId.fromProtobuf(response.getChunkInfo().getInitialTransactionID()) + : null); + } + + /** + * Create a new topic message from a list of response's protobuf. + * + * @param responses the protobuf response + * @return the new topic message + */ + static TopicMessage ofMany(List responses) { + // response should be in the order of oldest to newest (not chunk order) + var chunks = new TopicMessageChunk[responses.size()]; + TransactionId transactionId = null; + var contents = new ByteString[responses.size()]; + long totalSize = 0; + + for (ConsensusTopicResponse r : responses) { + if (transactionId == null && r.getChunkInfo().hasInitialTransactionID()) { + transactionId = TransactionId.fromProtobuf(r.getChunkInfo().getInitialTransactionID()); + } + + int index = r.getChunkInfo().getNumber() - 1; + + chunks[index] = new TopicMessageChunk(r); + contents[index] = r.getMessage(); + totalSize += r.getMessage().size(); + } + + var wholeMessage = ByteBuffer.allocate((int) totalSize); + + for (var content : contents) { + wholeMessage.put(content.asReadOnlyByteBuffer()); + } + + var lastReceived = responses.get(responses.size() - 1); + + return new TopicMessage( + InstantConverter.fromProtobuf(lastReceived.getConsensusTimestamp()), + wholeMessage.array(), + lastReceived.getRunningHash().toByteArray(), + lastReceived.getSequenceNumber(), + chunks, + transactionId); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("consensusTimestamp", consensusTimestamp) + .add("contents", new String(contents, StandardCharsets.UTF_8)) + .add("runningHash", runningHash) + .add("sequenceNumber", sequenceNumber) + .toString(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/TopicMessageChunk.java b/sdk/src/main/java/org/hiero/sdk/TopicMessageChunk.java new file mode 100644 index 0000000000..803a6b85bf --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TopicMessageChunk.java @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import java.time.Instant; +import org.hiero.sdk.proto.mirror.ConsensusTopicResponse; + +/** + * A chunk of the topic message. + */ +final class TopicMessageChunk { + public final Instant consensusTimestamp; + public final long contentSize; + public final byte[] runningHash; + public final long sequenceNumber; + + /** + * Create a topic message chunk from a protobuf. + * + * @param response the protobuf + */ + TopicMessageChunk(ConsensusTopicResponse response) { + consensusTimestamp = InstantConverter.fromProtobuf(response.getConsensusTimestamp()); + contentSize = response.getMessage().size(); + runningHash = response.getRunningHash().toByteArray(); + sequenceNumber = response.getSequenceNumber(); + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TopicMessageQuery.java b/sdk/src/main/java/org/hiero/sdk/TopicMessageQuery.java similarity index 81% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/TopicMessageQuery.java rename to sdk/src/main/java/org/hiero/sdk/TopicMessageQuery.java index 6a9b151077..36d845b032 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TopicMessageQuery.java +++ b/sdk/src/main/java/org/hiero/sdk/TopicMessageQuery.java @@ -1,29 +1,6 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; -import com.hedera.hashgraph.sdk.proto.Timestamp; -import com.hedera.hashgraph.sdk.proto.TransactionID; -import com.hedera.hashgraph.sdk.proto.mirror.ConsensusServiceGrpc; -import com.hedera.hashgraph.sdk.proto.mirror.ConsensusTopicQuery; -import com.hedera.hashgraph.sdk.proto.mirror.ConsensusTopicResponse; import io.grpc.CallOptions; import io.grpc.ClientCall; import io.grpc.Status; @@ -40,6 +17,11 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Predicate; +import org.hiero.sdk.proto.Timestamp; +import org.hiero.sdk.proto.TransactionID; +import org.hiero.sdk.proto.mirror.ConsensusServiceGrpc; +import org.hiero.sdk.proto.mirror.ConsensusTopicQuery; +import org.hiero.sdk.proto.mirror.ConsensusTopicResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -184,8 +166,8 @@ private void onComplete() { private void onError(Throwable throwable, TopicMessage topicMessage) { var topicId = TopicId.fromProtobuf(builder.getTopicID()); - if (throwable instanceof StatusRuntimeException sre && sre.getStatus().getCode() - .equals(Status.Code.CANCELLED)) { + if (throwable instanceof StatusRuntimeException sre + && sre.getStatus().getCode().equals(Status.Code.CANCELLED)) { LOGGER.warn("Call is cancelled for topic {}.", topicId); } else { LOGGER.error("Error attempting to subscribe to topic {}:", topicId, throwable); @@ -215,11 +197,12 @@ private boolean shouldRetry(Throwable throwable) { var code = statusRuntimeException.getStatus().getCode(); var description = statusRuntimeException.getStatus().getDescription(); - return (code == Status.Code.NOT_FOUND) || - (code == Status.Code.UNAVAILABLE) || - (code == Status.Code.RESOURCE_EXHAUSTED) || - (code == Status.Code.INTERNAL && description != null && Executable.RST_STREAM.matcher(description) - .matches()); + return (code == Status.Code.NOT_FOUND) + || (code == Status.Code.UNAVAILABLE) + || (code == Status.Code.RESOURCE_EXHAUSTED) + || (code == Status.Code.INTERNAL + && description != null + && Executable.RST_STREAM.matcher(description).matches()); } return false; @@ -238,8 +221,8 @@ public SubscriptionHandle subscribe(Client client, Consumer onNext HashMap> pendingMessages = new HashMap<>(); try { - makeStreamingCall(client, subscriptionHandle, onNext, 0, new AtomicLong(), new AtomicReference<>(), - pendingMessages); + makeStreamingCall( + client, subscriptionHandle, onNext, 0, new AtomicLong(), new AtomicReference<>(), pendingMessages); } catch (InterruptedException e) { throw new RuntimeException(e); } @@ -248,17 +231,18 @@ public SubscriptionHandle subscribe(Client client, Consumer onNext } private void makeStreamingCall( - Client client, - SubscriptionHandle subscriptionHandle, - Consumer onNext, - int attempt, - AtomicLong counter, - AtomicReference lastMessage, - HashMap> pendingMessages - ) throws InterruptedException { + Client client, + SubscriptionHandle subscriptionHandle, + Consumer onNext, + int attempt, + AtomicLong counter, + AtomicReference lastMessage, + HashMap> pendingMessages) + throws InterruptedException { // TODO: check status of channel before using it? - ClientCall call = - client.mirrorNetwork.getNextMirrorNode().getChannel() + ClientCall call = client.mirrorNetwork + .getNextMirrorNode() + .getChannel() .newCall(ConsensusServiceGrpc.getSubscribeTopicMethod(), CallOptions.DEFAULT); subscriptionHandle.setOnUnsubscribe(() -> { @@ -291,7 +275,8 @@ public void onNext(ConsensusTopicResponse consensusTopicResponse) { lastMessage.set(consensusTopicResponse); // Short circuit for no chunks or 1/1 chunks - if (!consensusTopicResponse.hasChunkInfo() || consensusTopicResponse.getChunkInfo().getTotal() == 1) { + if (!consensusTopicResponse.hasChunkInfo() + || consensusTopicResponse.getChunkInfo().getTotal() == 1) { var message = TopicMessage.ofSingle(consensusTopicResponse); try { @@ -338,8 +323,12 @@ public void onError(Throwable t) { var delay = Math.min(500 * (long) Math.pow(2, attempt), maxBackoff.toMillis()); var topicId = TopicId.fromProtobuf(builder.getTopicID()); - LOGGER.warn("Error subscribing to topic {} during attempt #{}. Waiting {} ms before next attempt: {}", - topicId, attempt, delay, t.getMessage()); + LOGGER.warn( + "Error subscribing to topic {} during attempt #{}. Waiting {} ms before next attempt: {}", + topicId, + attempt, + delay, + t.getMessage()); call.cancel("unsubscribed", null); // Cannot use `CompletableFuture` here since this future is never polled @@ -350,8 +339,8 @@ public void onError(Throwable t) { } try { - makeStreamingCall(client, subscriptionHandle, onNext, attempt + 1, counter, lastMessage, - pendingMessages); + makeStreamingCall( + client, subscriptionHandle, onNext, attempt + 1, counter, lastMessage, pendingMessages); } catch (InterruptedException e) { throw new RuntimeException(e); } diff --git a/sdk/src/main/java/org/hiero/sdk/TopicMessageSubmitTransaction.java b/sdk/src/main/java/org/hiero/sdk/TopicMessageSubmitTransaction.java new file mode 100644 index 0000000000..191a88a494 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TopicMessageSubmitTransaction.java @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import io.grpc.MethodDescriptor; +import java.util.LinkedHashMap; +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.ConsensusMessageChunkInfo; +import org.hiero.sdk.proto.ConsensusServiceGrpc; +import org.hiero.sdk.proto.ConsensusSubmitMessageTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionID; +import org.hiero.sdk.proto.TransactionResponse; + +/** + * Submit a message for consensus. + *

+ * Valid and authorized messages on valid topics will be ordered by the consensus service, gossipped to the + * mirror net, and published (in order) to all subscribers (from the mirror net) on this topic. + *

+ * The submitKey (if any) must sign this transaction. + *

+ * On success, the resulting TransactionReceipt contains the topic's updated topicSequenceNumber and + * topicRunningHash. + */ +public final class TopicMessageSubmitTransaction extends ChunkedTransaction { + @Nullable + private TopicId topicId = null; + + /** + * Constructor. + */ + public TopicMessageSubmitTransaction() {} + + /** + * Constructor. + * + * @param txs Compound list of transaction id's list of (AccountId, Transaction) + * records + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + TopicMessageSubmitTransaction( + LinkedHashMap> txs) + throws InvalidProtocolBufferException { + super(txs); + initFromTransactionBody(); + } + + /** + * Constructor. + * + * @param txBody protobuf TransactionBody + */ + TopicMessageSubmitTransaction(org.hiero.sdk.proto.TransactionBody txBody) { + super(txBody); + initFromTransactionBody(); + } + + /** + * Extract the topic id. + * + * @return the topic id + */ + @Nullable + public TopicId getTopicId() { + return topicId; + } + + /** + * Assign the topic id. + * + * @param topicId the topic id + * @return {@code this} + */ + public TopicMessageSubmitTransaction setTopicId(TopicId topicId) { + Objects.requireNonNull(topicId); + requireNotFrozen(); + this.topicId = topicId; + return this; + } + + /** + * Extract the message. + * + * @return the message + */ + public ByteString getMessage() { + return getData(); + } + + /** + * Assign the message from a byte string. + * + * @param message the byte string + * @return the message + */ + public TopicMessageSubmitTransaction setMessage(ByteString message) { + return setData(message); + } + + /** + * Assign the message from a byte array. + * + * @param message the byte array + * @return the message + */ + public TopicMessageSubmitTransaction setMessage(byte[] message) { + return setData(message); + } + + /** + * Assign the message from a string. + * + * @param message the string + * @return the message + */ + public TopicMessageSubmitTransaction setMessage(String message) { + return setData(message); + } + + /** + * Initialize from the transaction body. + */ + void initFromTransactionBody() { + var body = sourceTransactionBody.getConsensusSubmitMessage(); + if (body.hasTopicID()) { + topicId = TopicId.fromProtobuf(body.getTopicID()); + } + + if (!innerSignedTransactions.isEmpty()) { + try { + for (var i = 0; + i < innerSignedTransactions.size(); + i += nodeAccountIds.isEmpty() ? 1 : nodeAccountIds.size()) { + data = data.concat(TransactionBody.parseFrom( + innerSignedTransactions.get(i).getBodyBytes()) + .getConsensusSubmitMessage() + .getMessage()); + } + } catch (InvalidProtocolBufferException exc) { + throw new IllegalArgumentException(exc.getMessage()); + } + } else { + data = body.getMessage(); + } + } + + /** + * Build the transaction body. + * + * @return {@link + * org.hiero.sdk.proto.ConsensusSubmitMessageTransactionBody} + */ + ConsensusSubmitMessageTransactionBody.Builder build() { + var builder = ConsensusSubmitMessageTransactionBody.newBuilder(); + if (topicId != null) { + builder.setTopicID(topicId.toProtobuf()); + } + builder.setMessage(data); + + return builder; + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (topicId != null) { + topicId.validateChecksum(client); + } + } + + @Override + MethodDescriptor getMethodDescriptor() { + return ConsensusServiceGrpc.getSubmitMessageMethod(); + } + + @Override + void onFreeze(TransactionBody.Builder bodyBuilder) { + bodyBuilder.setConsensusSubmitMessage(build()); + } + + @Override + void onFreezeChunk( + TransactionBody.Builder body, + @Nullable TransactionID initialTransactionId, + int startIndex, + int endIndex, + int chunk, + int total) { + if (total == 1) { + body.setConsensusSubmitMessage(build().setMessage(data.substring(startIndex, endIndex))); + } else { + body.setConsensusSubmitMessage(build().setMessage(data.substring(startIndex, endIndex)) + .setChunkInfo(ConsensusMessageChunkInfo.newBuilder() + .setInitialTransactionID(Objects.requireNonNull(initialTransactionId)) + .setNumber(chunk + 1) + .setTotal(total))); + } + } + + @Override + void onScheduled(SchedulableTransactionBody.Builder scheduled) { + scheduled.setConsensusSubmitMessage(build().setMessage(data)); + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TopicUpdateTransaction.java b/sdk/src/main/java/org/hiero/sdk/TopicUpdateTransaction.java similarity index 87% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/TopicUpdateTransaction.java rename to sdk/src/main/java/org/hiero/sdk/TopicUpdateTransaction.java index e8743dc533..ff984375f8 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TopicUpdateTransaction.java +++ b/sdk/src/main/java/org/hiero/sdk/TopicUpdateTransaction.java @@ -1,38 +1,19 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.StringValue; -import com.hedera.hashgraph.sdk.proto.ConsensusServiceGrpc; -import com.hedera.hashgraph.sdk.proto.ConsensusUpdateTopicTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; import io.grpc.MethodDescriptor; import java.time.Duration; import java.time.Instant; - -import javax.annotation.Nullable; import java.util.LinkedHashMap; import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.ConsensusServiceGrpc; +import org.hiero.sdk.proto.ConsensusUpdateTopicTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; /** * Update a topic. @@ -87,8 +68,7 @@ public final class TopicUpdateTransaction extends Transaction> txs) throws InvalidProtocolBufferException { + TopicUpdateTransaction(LinkedHashMap> txs) + throws InvalidProtocolBufferException { super(txs); initFromTransactionBody(); } @@ -107,7 +88,7 @@ public TopicUpdateTransaction() { * * @param txBody protobuf TransactionBody */ - TopicUpdateTransaction(com.hedera.hashgraph.sdk.proto.TransactionBody txBody) { + TopicUpdateTransaction(org.hiero.sdk.proto.TransactionBody txBody) { super(txBody); initFromTransactionBody(); } @@ -361,7 +342,7 @@ void initFromTransactionBody() { * Build the transaction body. * * @return {@link - * com.hedera.hashgraph.sdk.proto.ConsensusUpdateTopicTransactionBody} + * org.hiero.sdk.proto.ConsensusUpdateTopicTransactionBody} */ ConsensusUpdateTopicTransactionBody.Builder build() { var builder = ConsensusUpdateTopicTransactionBody.newBuilder(); @@ -395,15 +376,13 @@ void validateChecksums(Client client) throws BadEntityIdException { topicId.validateChecksum(client); } - if ((autoRenewAccountId != null) && - !autoRenewAccountId.equals(new AccountId(0)) - ) { + if ((autoRenewAccountId != null) && !autoRenewAccountId.equals(new AccountId(0))) { autoRenewAccountId.validateChecksum(client); } } @Override - MethodDescriptor getMethodDescriptor() { + MethodDescriptor getMethodDescriptor() { return ConsensusServiceGrpc.getUpdateTopicMethod(); } diff --git a/sdk/src/main/java/org/hiero/sdk/Transaction.java b/sdk/src/main/java/org/hiero/sdk/Transaction.java new file mode 100644 index 0000000000..7ce5299afd --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/Transaction.java @@ -0,0 +1,1389 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import java.lang.reflect.Modifier; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.function.Function; +import java.util.function.UnaryOperator; +import javax.annotation.Nullable; +import org.bouncycastle.crypto.digests.SHA384Digest; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.SignatureMap; +import org.hiero.sdk.proto.SignaturePair; +import org.hiero.sdk.proto.SignedTransaction; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionList; + +/** + * Base class for all transactions that may be built and submitted to Hedera. + * + * @param The type of the transaction. Used to enable chaining. + */ +public abstract class Transaction> + extends Executable< + T, org.hiero.sdk.proto.Transaction, org.hiero.sdk.proto.TransactionResponse, TransactionResponse> { + + /** + * Default auto renew duration for accounts, contracts, topics, and files (entities) + */ + static final Duration DEFAULT_AUTO_RENEW_PERIOD = Duration.ofDays(90); + + /** + * Dummy account ID used to assist in deserializing incomplete Transactions. + */ + protected static final AccountId DUMMY_ACCOUNT_ID = new AccountId(0L, 0L, 0L); + + /** + * Dummy transaction ID used to assist in deserializing incomplete Transactions. + */ + protected static final TransactionId DUMMY_TRANSACTION_ID = + TransactionId.withValidStart(DUMMY_ACCOUNT_ID, Instant.EPOCH); + + /** + * Default transaction duration + */ + private static final Duration DEFAULT_TRANSACTION_VALID_DURATION = Duration.ofSeconds(120); + + /** + * Transaction constructors end their work by setting sourceTransactionBody. The expectation is that the Transaction + * subclass constructor will pick up where the Transaction superclass constructor left off, and will unpack the data + * in the transaction body. + */ + protected final TransactionBody sourceTransactionBody; + /** + * The builder that gets re-used to build each outer transaction. freezeWith() will create the frozenBodyBuilder. + * The presence of frozenBodyBuilder indicates that this transaction is frozen. + */ + @Nullable + protected TransactionBody.Builder frozenBodyBuilder = null; + + /** + * An SDK [Transaction] is composed of multiple, raw protobuf transactions. These should be functionally identical, + * except pointing to different nodes. When retrying a transaction after a network error or retry-able status + * response, we try a different transaction and thus a different node. + */ + protected List outerTransactions = Collections.emptyList(); + + /** + * An SDK [Transaction] is composed of multiple, raw protobuf transactions. These should be functionally identical, + * except pointing to different nodes. When retrying a transaction after a network error or retry-able status + * response, we try a different transaction and thus a different node. + */ + protected List innerSignedTransactions = Collections.emptyList(); + + /** + * A set of signatures corresponding to every unique public key used to sign the transaction. + */ + protected List sigPairLists = Collections.emptyList(); + + /** + * List of IDs for the transaction based on the operator because the transaction ID includes the operator's account + */ + protected LockableList transactionIds = new LockableList<>(); + + /** + * publicKeys and signers are parallel arrays. If the signer associated with a public key is null, that means that + * the private key associated with that public key has already contributed a signature to sigPairListBuilders, but + * the signer is not available (likely because this came from fromBytes()) + */ + protected List publicKeys = new ArrayList<>(); + + /** + * publicKeys and signers are parallel arrays. If the signer associated with a public key is null, that means that + * the private key associated with that public key has already contributed a signature to sigPairListBuilders, but + * the signer is not available (likely because this came from fromBytes()) + */ + protected List> signers = new ArrayList<>(); + + /** + * The maximum transaction fee the client is willing to pay + */ + protected Hbar defaultMaxTransactionFee = new Hbar(2); + /** + * Should the transaction id be regenerated + */ + protected Boolean regenerateTransactionId = null; + + private Duration transactionValidDuration; + + @Nullable + private Hbar maxTransactionFee = null; + + private String memo = ""; + + /** + * Constructor. + */ + Transaction() { + setTransactionValidDuration(DEFAULT_TRANSACTION_VALID_DURATION); + + sourceTransactionBody = TransactionBody.getDefaultInstance(); + } + + // This constructor is used to construct from a scheduled transaction body + + /** + * Constructor. + * + * @param txBody protobuf TransactionBody + */ + Transaction(org.hiero.sdk.proto.TransactionBody txBody) { + setTransactionValidDuration(DEFAULT_TRANSACTION_VALID_DURATION); + setMaxTransactionFee(Hbar.fromTinybars(txBody.getTransactionFee())); + setTransactionMemo(txBody.getMemo()); + + sourceTransactionBody = txBody; + } + + // This constructor is used to construct via fromBytes + + /** + * Constructor. + * + * @param txs Compound list of transaction id's list of (AccountId, Transaction) records + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + Transaction(LinkedHashMap> txs) + throws InvalidProtocolBufferException { + LinkedHashMap transactionMap = + txs.values().iterator().next(); + if (!transactionMap.isEmpty() + && transactionMap.keySet().iterator().next().equals(DUMMY_ACCOUNT_ID)) { + // If the first account ID is a dummy account ID, then only the source TransactionBody needs to be copied. + var signedTransaction = SignedTransaction.parseFrom( + transactionMap.values().iterator().next().getSignedTransactionBytes()); + sourceTransactionBody = TransactionBody.parseFrom(signedTransaction.getBodyBytes()); + } else { + var txCount = txs.keySet().size(); + var nodeCount = txs.values().iterator().next().size(); + + nodeAccountIds.ensureCapacity(nodeCount); + sigPairLists = new ArrayList<>(nodeCount * txCount); + outerTransactions = new ArrayList<>(nodeCount * txCount); + innerSignedTransactions = new ArrayList<>(nodeCount * txCount); + transactionIds.ensureCapacity(txCount); + + for (var transactionEntry : txs.entrySet()) { + if (!transactionEntry.getKey().equals(DUMMY_TRANSACTION_ID)) { + transactionIds.add(transactionEntry.getKey()); + } + for (var nodeEntry : transactionEntry.getValue().entrySet()) { + if (nodeAccountIds.size() != nodeCount) { + nodeAccountIds.add(nodeEntry.getKey()); + } + + var transaction = + SignedTransaction.parseFrom(nodeEntry.getValue().getSignedTransactionBytes()); + outerTransactions.add(nodeEntry.getValue()); + sigPairLists.add(transaction.getSigMap().toBuilder()); + innerSignedTransactions.add(transaction.toBuilder()); + + if (publicKeys.isEmpty()) { + for (var sigPair : transaction.getSigMap().getSigPairList()) { + publicKeys.add(PublicKey.fromBytes( + sigPair.getPubKeyPrefix().toByteArray())); + signers.add(null); + } + } + } + } + + nodeAccountIds.remove(new AccountId(0)); + + // Verify that transaction bodies match + for (int i = 0; i < txCount; i++) { + TransactionBody firstTxBody = null; + for (int j = 0; j < nodeCount; j++) { + int k = i * nodeCount + j; + var txBody = TransactionBody.parseFrom( + innerSignedTransactions.get(k).getBodyBytes()); + if (firstTxBody == null) { + firstTxBody = txBody; + } else { + requireProtoMatches( + firstTxBody, txBody, new HashSet<>(List.of("NodeAccountID")), "TransactionBody"); + } + } + } + sourceTransactionBody = + TransactionBody.parseFrom(innerSignedTransactions.get(0).getBodyBytes()); + } + + setTransactionValidDuration( + DurationConverter.fromProtobuf(sourceTransactionBody.getTransactionValidDuration())); + setMaxTransactionFee(Hbar.fromTinybars(sourceTransactionBody.getTransactionFee())); + setTransactionMemo(sourceTransactionBody.getMemo()); + + // The presence of signatures implies the Transaction should be frozen. + if (!publicKeys.isEmpty()) { + frozenBodyBuilder = sourceTransactionBody.toBuilder(); + } + } + + /** + * Create the correct transaction from a byte array. + * + * @param bytes the byte array + * @return the new transaction + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + public static Transaction fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + var txs = new LinkedHashMap>(); + TransactionBody.DataCase dataCase = TransactionBody.DataCase.DATA_NOT_SET; + + var list = TransactionList.parseFrom(bytes); + + if (list.getTransactionListList().isEmpty()) { + var transaction = org.hiero.sdk.proto.Transaction.parseFrom(bytes).toBuilder(); + + TransactionBody txBody; + if (transaction.getSignedTransactionBytes().isEmpty()) { + txBody = TransactionBody.parseFrom(transaction.getBodyBytes()); + + transaction + .setSignedTransactionBytes(SignedTransaction.newBuilder() + .setBodyBytes(transaction.getBodyBytes()) + .setSigMap(transaction.getSigMap()) + .build() + .toByteString()) + .clearBodyBytes() + .clearSigMap(); + } else { + var signedTransaction = SignedTransaction.parseFrom(transaction.getSignedTransactionBytes()); + txBody = TransactionBody.parseFrom(signedTransaction.getBodyBytes()); + } + + dataCase = txBody.getDataCase(); + + var account = + txBody.hasNodeAccountID() ? AccountId.fromProtobuf(txBody.getNodeAccountID()) : DUMMY_ACCOUNT_ID; + var transactionId = txBody.hasTransactionID() + ? TransactionId.fromProtobuf(txBody.getTransactionID()) + : DUMMY_TRANSACTION_ID; + + var linked = new LinkedHashMap(); + linked.put(account, transaction.build()); + txs.put(transactionId, linked); + } else { + for (var transaction : list.getTransactionListList()) { + var signedTransaction = SignedTransaction.parseFrom(transaction.getSignedTransactionBytes()); + var txBody = TransactionBody.parseFrom(signedTransaction.getBodyBytes()); + + if (dataCase.getNumber() == TransactionBody.DataCase.DATA_NOT_SET.getNumber()) { + dataCase = txBody.getDataCase(); + } + + var account = txBody.hasNodeAccountID() + ? AccountId.fromProtobuf(txBody.getNodeAccountID()) + : DUMMY_ACCOUNT_ID; + var transactionId = txBody.hasTransactionID() + ? TransactionId.fromProtobuf(txBody.getTransactionID()) + : DUMMY_TRANSACTION_ID; + + var linked = txs.containsKey(transactionId) + ? Objects.requireNonNull(txs.get(transactionId)) + : new LinkedHashMap(); + + linked.put(account, transaction); + + txs.put(transactionId, linked); + } + } + + return switch (dataCase) { + case CONTRACTCALL -> new ContractExecuteTransaction(txs); + case CONTRACTCREATEINSTANCE -> new ContractCreateTransaction(txs); + case CONTRACTUPDATEINSTANCE -> new ContractUpdateTransaction(txs); + case CONTRACTDELETEINSTANCE -> new ContractDeleteTransaction(txs); + case ETHEREUMTRANSACTION -> new EthereumTransaction(txs); + case CRYPTOADDLIVEHASH -> new LiveHashAddTransaction(txs); + case CRYPTOCREATEACCOUNT -> new AccountCreateTransaction(txs); + case CRYPTODELETE -> new AccountDeleteTransaction(txs); + case CRYPTODELETELIVEHASH -> new LiveHashDeleteTransaction(txs); + case CRYPTOTRANSFER -> new TransferTransaction(txs); + case CRYPTOUPDATEACCOUNT -> new AccountUpdateTransaction(txs); + case FILEAPPEND -> new FileAppendTransaction(txs); + case FILECREATE -> new FileCreateTransaction(txs); + case FILEDELETE -> new FileDeleteTransaction(txs); + case FILEUPDATE -> new FileUpdateTransaction(txs); + case NODECREATE -> new NodeCreateTransaction(txs); + case NODEUPDATE -> new NodeUpdateTransaction(txs); + case NODEDELETE -> new NodeDeleteTransaction(txs); + case SYSTEMDELETE -> new SystemDeleteTransaction(txs); + case SYSTEMUNDELETE -> new SystemUndeleteTransaction(txs); + case FREEZE -> new FreezeTransaction(txs); + case CONSENSUSCREATETOPIC -> new TopicCreateTransaction(txs); + case CONSENSUSUPDATETOPIC -> new TopicUpdateTransaction(txs); + case CONSENSUSDELETETOPIC -> new TopicDeleteTransaction(txs); + case CONSENSUSSUBMITMESSAGE -> new TopicMessageSubmitTransaction(txs); + case TOKENASSOCIATE -> new TokenAssociateTransaction(txs); + case TOKENBURN -> new TokenBurnTransaction(txs); + case TOKENCREATION -> new TokenCreateTransaction(txs); + case TOKENDELETION -> new TokenDeleteTransaction(txs); + case TOKENDISSOCIATE -> new TokenDissociateTransaction(txs); + case TOKENFREEZE -> new TokenFreezeTransaction(txs); + case TOKENGRANTKYC -> new TokenGrantKycTransaction(txs); + case TOKENMINT -> new TokenMintTransaction(txs); + case TOKENREVOKEKYC -> new TokenRevokeKycTransaction(txs); + case TOKENUNFREEZE -> new TokenUnfreezeTransaction(txs); + case TOKENUPDATE -> new TokenUpdateTransaction(txs); + case TOKEN_UPDATE_NFTS -> new TokenUpdateNftsTransaction(txs); + case TOKENWIPE -> new TokenWipeTransaction(txs); + case TOKEN_FEE_SCHEDULE_UPDATE -> new TokenFeeScheduleUpdateTransaction(txs); + case SCHEDULECREATE -> new ScheduleCreateTransaction(txs); + case SCHEDULEDELETE -> new ScheduleDeleteTransaction(txs); + case SCHEDULESIGN -> new ScheduleSignTransaction(txs); + case TOKEN_PAUSE -> new TokenPauseTransaction(txs); + case TOKEN_UNPAUSE -> new TokenUnpauseTransaction(txs); + case TOKENREJECT -> new TokenRejectTransaction(txs); + case TOKENAIRDROP -> new TokenAirdropTransaction(txs); + case TOKENCANCELAIRDROP -> new TokenCancelAirdropTransaction(txs); + case TOKENCLAIMAIRDROP -> new TokenClaimAirdropTransaction(txs); + case CRYPTOAPPROVEALLOWANCE -> new AccountAllowanceApproveTransaction(txs); + case CRYPTODELETEALLOWANCE -> new AccountAllowanceDeleteTransaction(txs); + default -> throw new IllegalArgumentException("parsed transaction body has no data"); + }; + } + + /** + * Create the correct transaction from a scheduled transaction. + * + * @param scheduled the scheduled transaction + * @return the new transaction + */ + public static Transaction fromScheduledTransaction(org.hiero.sdk.proto.SchedulableTransactionBody scheduled) { + var body = TransactionBody.newBuilder() + .setMemo(scheduled.getMemo()) + .setTransactionFee(scheduled.getTransactionFee()); + + return switch (scheduled.getDataCase()) { + case CONTRACTCALL -> new ContractExecuteTransaction( + body.setContractCall(scheduled.getContractCall()).build()); + case CONTRACTCREATEINSTANCE -> new ContractCreateTransaction( + body.setContractCreateInstance(scheduled.getContractCreateInstance()) + .build()); + case CONTRACTUPDATEINSTANCE -> new ContractUpdateTransaction( + body.setContractUpdateInstance(scheduled.getContractUpdateInstance()) + .build()); + case CONTRACTDELETEINSTANCE -> new ContractDeleteTransaction( + body.setContractDeleteInstance(scheduled.getContractDeleteInstance()) + .build()); + case CRYPTOAPPROVEALLOWANCE -> new AccountAllowanceApproveTransaction( + body.setCryptoApproveAllowance(scheduled.getCryptoApproveAllowance()) + .build()); + case CRYPTODELETEALLOWANCE -> new AccountAllowanceDeleteTransaction( + body.setCryptoDeleteAllowance(scheduled.getCryptoDeleteAllowance()) + .build()); + case CRYPTOCREATEACCOUNT -> new AccountCreateTransaction( + body.setCryptoCreateAccount(scheduled.getCryptoCreateAccount()) + .build()); + case CRYPTODELETE -> new AccountDeleteTransaction( + body.setCryptoDelete(scheduled.getCryptoDelete()).build()); + case CRYPTOTRANSFER -> new TransferTransaction( + body.setCryptoTransfer(scheduled.getCryptoTransfer()).build()); + case CRYPTOUPDATEACCOUNT -> new AccountUpdateTransaction( + body.setCryptoUpdateAccount(scheduled.getCryptoUpdateAccount()) + .build()); + case FILEAPPEND -> new FileAppendTransaction( + body.setFileAppend(scheduled.getFileAppend()).build()); + case FILECREATE -> new FileCreateTransaction( + body.setFileCreate(scheduled.getFileCreate()).build()); + case FILEDELETE -> new FileDeleteTransaction( + body.setFileDelete(scheduled.getFileDelete()).build()); + case FILEUPDATE -> new FileUpdateTransaction( + body.setFileUpdate(scheduled.getFileUpdate()).build()); + case NODECREATE -> new NodeCreateTransaction( + body.setNodeCreate(scheduled.getNodeCreate()).build()); + case NODEUPDATE -> new NodeUpdateTransaction( + body.setNodeUpdate(scheduled.getNodeUpdate()).build()); + case NODEDELETE -> new NodeDeleteTransaction( + body.setNodeDelete(scheduled.getNodeDelete()).build()); + case SYSTEMDELETE -> new SystemDeleteTransaction( + body.setSystemDelete(scheduled.getSystemDelete()).build()); + case SYSTEMUNDELETE -> new SystemUndeleteTransaction( + body.setSystemUndelete(scheduled.getSystemUndelete()).build()); + case FREEZE -> new FreezeTransaction( + body.setFreeze(scheduled.getFreeze()).build()); + case CONSENSUSCREATETOPIC -> new TopicCreateTransaction( + body.setConsensusCreateTopic(scheduled.getConsensusCreateTopic()) + .build()); + case CONSENSUSUPDATETOPIC -> new TopicUpdateTransaction( + body.setConsensusUpdateTopic(scheduled.getConsensusUpdateTopic()) + .build()); + case CONSENSUSDELETETOPIC -> new TopicDeleteTransaction( + body.setConsensusDeleteTopic(scheduled.getConsensusDeleteTopic()) + .build()); + case CONSENSUSSUBMITMESSAGE -> new TopicMessageSubmitTransaction( + body.setConsensusSubmitMessage(scheduled.getConsensusSubmitMessage()) + .build()); + case TOKENCREATION -> new TokenCreateTransaction( + body.setTokenCreation(scheduled.getTokenCreation()).build()); + case TOKENFREEZE -> new TokenFreezeTransaction( + body.setTokenFreeze(scheduled.getTokenFreeze()).build()); + case TOKENUNFREEZE -> new TokenUnfreezeTransaction( + body.setTokenUnfreeze(scheduled.getTokenUnfreeze()).build()); + case TOKENGRANTKYC -> new TokenGrantKycTransaction( + body.setTokenGrantKyc(scheduled.getTokenGrantKyc()).build()); + case TOKENREVOKEKYC -> new TokenRevokeKycTransaction( + body.setTokenRevokeKyc(scheduled.getTokenRevokeKyc()).build()); + case TOKENDELETION -> new TokenDeleteTransaction( + body.setTokenDeletion(scheduled.getTokenDeletion()).build()); + case TOKENUPDATE -> new TokenUpdateTransaction( + body.setTokenUpdate(scheduled.getTokenUpdate()).build()); + case TOKEN_UPDATE_NFTS -> new TokenUpdateNftsTransaction( + body.setTokenUpdateNfts(scheduled.getTokenUpdateNfts()).build()); + case TOKENMINT -> new TokenMintTransaction( + body.setTokenMint(scheduled.getTokenMint()).build()); + case TOKENBURN -> new TokenBurnTransaction( + body.setTokenBurn(scheduled.getTokenBurn()).build()); + case TOKENWIPE -> new TokenWipeTransaction( + body.setTokenWipe(scheduled.getTokenWipe()).build()); + case TOKENASSOCIATE -> new TokenAssociateTransaction( + body.setTokenAssociate(scheduled.getTokenAssociate()).build()); + case TOKENDISSOCIATE -> new TokenDissociateTransaction( + body.setTokenDissociate(scheduled.getTokenDissociate()).build()); + case TOKEN_FEE_SCHEDULE_UPDATE -> new TokenFeeScheduleUpdateTransaction( + body.setTokenFeeScheduleUpdate(scheduled.getTokenFeeScheduleUpdate()) + .build()); + case TOKEN_PAUSE -> new TokenPauseTransaction( + body.setTokenPause(scheduled.getTokenPause()).build()); + case TOKEN_UNPAUSE -> new TokenUnpauseTransaction( + body.setTokenUnpause(scheduled.getTokenUnpause()).build()); + case TOKENREJECT -> new TokenRejectTransaction( + body.setTokenReject(scheduled.getTokenReject()).build()); + case TOKENAIRDROP -> new TokenAirdropTransaction( + body.setTokenAirdrop(scheduled.getTokenAirdrop()).build()); + case TOKENCANCELAIRDROP -> new TokenCancelAirdropTransaction( + body.setTokenCancelAirdrop(scheduled.getTokenCancelAirdrop()) + .build()); + case TOKENCLAIMAIRDROP -> new TokenClaimAirdropTransaction( + body.setTokenCancelAirdrop(scheduled.getTokenCancelAirdrop()) + .build()); + case SCHEDULEDELETE -> new ScheduleDeleteTransaction( + body.setScheduleDelete(scheduled.getScheduleDelete()).build()); + default -> throw new IllegalStateException("schedulable transaction did not have a transaction set"); + }; + } + + private static void throwProtoMatchException(String fieldName, String aWas, String bWas) { + throw new IllegalArgumentException("fromBytes() failed because " + fieldName + + " fields in TransactionBody protobuf messages in the TransactionList did not match: A was " + + aWas + + ", B was " + bWas); + } + + private static void requireProtoMatches(Object protoA, Object protoB, Set ignoreSet, String thisFieldName) { + var aIsNull = protoA == null; + var bIsNull = protoB == null; + if (aIsNull != bIsNull) { + throwProtoMatchException(thisFieldName, aIsNull ? "null" : "not null", bIsNull ? "null" : "not null"); + } + if (aIsNull) { + return; + } + var protoAClass = protoA.getClass(); + var protoBClass = protoB.getClass(); + if (!protoAClass.equals(protoBClass)) { + throwProtoMatchException(thisFieldName, "of class " + protoAClass, "of class " + protoBClass); + } + if (protoA instanceof Boolean + || protoA instanceof Integer + || protoA instanceof Long + || protoA instanceof Float + || protoA instanceof Double + || protoA instanceof String + || protoA instanceof ByteString) { + // System.out.println("values A = " + protoA.toString() + ", B = " + protoB.toString()); + if (!protoA.equals(protoB)) { + throwProtoMatchException(thisFieldName, protoA.toString(), protoB.toString()); + } + } + for (var method : protoAClass.getDeclaredMethods()) { + if (method.getParameterCount() != 0) { + continue; + } + int methodModifiers = method.getModifiers(); + if ((!Modifier.isPublic(methodModifiers)) || Modifier.isStatic(methodModifiers)) { + continue; + } + var methodName = method.getName(); + if (!methodName.startsWith("get")) { + continue; + } + var isList = methodName.endsWith("List") && List.class.isAssignableFrom(method.getReturnType()); + var methodFieldName = methodName.substring(3, methodName.length() - (isList ? 4 : 0)); + if (ignoreSet.contains(methodFieldName) || methodFieldName.equals("DefaultInstance")) { + continue; + } + if (!isList) { + try { + var hasMethod = protoAClass.getMethod("has" + methodFieldName); + var hasA = (Boolean) hasMethod.invoke(protoA); + var hasB = (Boolean) hasMethod.invoke(protoB); + if (!hasA.equals(hasB)) { + throwProtoMatchException( + methodFieldName, hasA ? "present" : "not present", hasB ? "present" : "not present"); + } + if (!hasA) { + continue; + } + } catch (NoSuchMethodException ignored) { + // pass if there is no has method + } catch (IllegalArgumentException error) { + throw error; + } catch (Throwable error) { + throw new IllegalArgumentException("fromBytes() failed due to error", error); + } + } + try { + var retvalA = method.invoke(protoA); + var retvalB = method.invoke(protoB); + if (isList) { + var listA = (List) retvalA; + var listB = (List) retvalB; + if (listA.size() != listB.size()) { + throwProtoMatchException(methodFieldName, "of size " + listA.size(), "of size " + listB.size()); + } + for (int i = 0; i < listA.size(); i++) { + // System.out.println("comparing " + thisFieldName + "." + methodFieldName + "[" + i + "]"); + requireProtoMatches(listA.get(i), listB.get(i), ignoreSet, methodFieldName + "[" + i + "]"); + } + } else { + // System.out.println("comparing " + thisFieldName + "." + methodFieldName); + requireProtoMatches(retvalA, retvalB, ignoreSet, methodFieldName); + } + } catch (IllegalArgumentException error) { + throw error; + } catch (Throwable error) { + throw new IllegalArgumentException("fromBytes() failed due to error", error); + } + } + } + + /** + * Generate a hash from a byte array. + * + * @param bytes the byte array + * @return the hash + */ + static byte[] hash(byte[] bytes) { + var digest = new SHA384Digest(); + var hash = new byte[digest.getDigestSize()]; + + digest.update(bytes, 0, bytes.length); + digest.doFinal(hash, 0); + + return hash; + } + + private static boolean publicKeyIsInSigPairList(ByteString publicKeyBytes, List sigPairList) { + for (var pair : sigPairList) { + if (pair.getPubKeyPrefix().equals(publicKeyBytes)) { + return true; + } + } + return false; + } + + /** + * Converts transaction into a scheduled version + * + * @param bodyBuilder the transaction's body builder + * @return the scheduled transaction + */ + protected ScheduleCreateTransaction doSchedule(TransactionBody.Builder bodyBuilder) { + var schedulable = SchedulableTransactionBody.newBuilder() + .setTransactionFee(bodyBuilder.getTransactionFee()) + .setMemo(bodyBuilder.getMemo()); + + onScheduled(schedulable); + + var scheduled = new ScheduleCreateTransaction().setScheduledTransactionBody(schedulable.build()); + + if (!transactionIds.isEmpty()) { + scheduled.setTransactionId(transactionIds.get(0)); + } + + return scheduled; + } + + /** + * Extract the scheduled transaction. + * + * @return the scheduled transaction + */ + public ScheduleCreateTransaction schedule() { + requireNotFrozen(); + if (!nodeAccountIds.isEmpty()) { + throw new IllegalStateException( + "The underlying transaction for a scheduled transaction cannot have node account IDs set"); + } + + var bodyBuilder = spawnBodyBuilder(null); + + onFreeze(bodyBuilder); + + return doSchedule(bodyBuilder); + } + + /** + * Set the account IDs of the nodes that this transaction will be submitted to. + *

+ * Providing an explicit node account ID interferes with client-side load balancing of the network. By default, the + * SDK will pre-generate a transaction for 1/3 of the nodes on the network. If a node is down, busy, or otherwise + * reports a fatal error, the SDK will try again with a different node. + * + * @param nodeAccountIds The list of node AccountIds to be set + * @return {@code this} + */ + @Override + public final T setNodeAccountIds(List nodeAccountIds) { + requireNotFrozen(); + Objects.requireNonNull(nodeAccountIds); + return super.setNodeAccountIds(nodeAccountIds); + } + + /** + * Extract the valid transaction duration. + * + * @return the transaction valid duration + */ + @Nullable + public final Duration getTransactionValidDuration() { + return transactionValidDuration; + } + + /** + * Sets the duration that this transaction is valid for. + *

+ * This is defaulted by the SDK to 120 seconds (or two minutes). + * + * @param validDuration The duration to be set + * @return {@code this} + */ + public final T setTransactionValidDuration(Duration validDuration) { + requireNotFrozen(); + Objects.requireNonNull(validDuration); + transactionValidDuration = validDuration; + // noinspection unchecked + return (T) this; + } + + /** + * Extract the maximum transaction fee. + * + * @return the maximum transaction fee + */ + @Nullable + public final Hbar getMaxTransactionFee() { + return maxTransactionFee; + } + + /** + * Set the maximum transaction fee the operator (paying account) is willing to pay. + * + * @param maxTransactionFee the maximum transaction fee, in tinybars. + * @return {@code this} + */ + public final T setMaxTransactionFee(Hbar maxTransactionFee) { + requireNotFrozen(); + Objects.requireNonNull(maxTransactionFee); + this.maxTransactionFee = maxTransactionFee; + // noinspection unchecked + return (T) this; + } + + /** + * Extract the default maximum transaction fee. + * + * @return the default maximum transaction fee + */ + public final Hbar getDefaultMaxTransactionFee() { + return defaultMaxTransactionFee; + } + + /** + * Extract the memo for the transaction. + * + * @return the memo for the transaction + */ + public final String getTransactionMemo() { + return memo; + } + + /** + * Set a note or description that should be recorded in the transaction record (maximum length of 100 characters). + * + * @param memo any notes or descriptions for this transaction. + * @return {@code this} + */ + public final T setTransactionMemo(String memo) { + requireNotFrozen(); + Objects.requireNonNull(memo); + this.memo = memo; + // noinspection unchecked + return (T) this; + } + + /** + * Extract a byte array representation. + * + * @return the byte array representation + */ + public byte[] toBytes() { + var list = TransactionList.newBuilder(); + + // If no nodes have been selected yet, + // the new TransactionBody can be used to build a Transaction protobuf object. + if (nodeAccountIds.isEmpty()) { + var bodyBuilder = spawnBodyBuilder(null); + if (!transactionIds.isEmpty()) { + bodyBuilder.setTransactionID(transactionIds.get(0).toProtobuf()); + } + onFreeze(bodyBuilder); + + var signedTransaction = SignedTransaction.newBuilder() + .setBodyBytes(bodyBuilder.build().toByteString()) + .build(); + + var transaction = org.hiero.sdk.proto.Transaction.newBuilder() + .setSignedTransactionBytes(signedTransaction.toByteString()) + .build(); + + list.addTransactionList(transaction); + } else { + // Generate the SignedTransaction protobuf objects if the Transaction's not frozen. + if (!this.isFrozen()) { + frozenBodyBuilder = spawnBodyBuilder(null); + if (!transactionIds.isEmpty()) { + frozenBodyBuilder.setTransactionID(transactionIds.get(0).toProtobuf()); + } + onFreeze(frozenBodyBuilder); + + int requiredChunks = getRequiredChunks(); + if (!transactionIds.isEmpty()) { + generateTransactionIds(transactionIds.get(0), requiredChunks); + } + wipeTransactionLists(requiredChunks); + } + + // Build all the Transaction protobuf objects and add them to the TransactionList protobuf object. + buildAllTransactions(); + for (var transaction : outerTransactions) { + list.addTransactionList(transaction); + } + } + + return list.build().toByteArray(); + } + + /** + * Extract a byte array of the transaction hash. + * + * @return the transaction hash + */ + public byte[] getTransactionHash() { + if (!this.isFrozen()) { + throw new IllegalStateException( + "transaction must have been frozen before calculating the hash will be stable, try calling `freeze`"); + } + + transactionIds.setLocked(true); + nodeAccountIds.setLocked(true); + + var index = transactionIds.getIndex() * nodeAccountIds.size() + nodeAccountIds.getIndex(); + + buildTransaction(index); + + return hash(outerTransactions.get(index).getSignedTransactionBytes().toByteArray()); + } + + /** + * Extract the list of account id and hash records. + * + * @return the list of account id and hash records + */ + public Map getTransactionHashPerNode() { + if (!this.isFrozen()) { + throw new IllegalStateException( + "transaction must have been frozen before calculating the hash will be stable, try calling `freeze`"); + } + + buildAllTransactions(); + + var hashes = new HashMap(); + + for (var i = 0; i < outerTransactions.size(); i++) { + hashes.put( + nodeAccountIds.get(i), + hash(outerTransactions.get(i).getSignedTransactionBytes().toByteArray())); + } + + return hashes; + } + + @Override + final TransactionId getTransactionIdInternal() { + return transactionIds.getCurrent(); + } + + /** + * Extract the transaction id. + * + * @return the transaction id + */ + public final TransactionId getTransactionId() { + if (transactionIds.isEmpty() || !this.isFrozen()) { + throw new IllegalStateException( + "No transaction ID generated yet. Try freezing the transaction or manually setting the transaction ID."); + } + + return transactionIds.setLocked(true).getCurrent(); + } + + /** + * Set the ID for this transaction. + *

+ * The transaction ID includes the operator's account ( the account paying the transaction fee). If two transactions + * have the same transaction ID, they won't both have an effect. One will complete normally and the other will fail + * with a duplicate transaction status. + *

+ * Normally, you should not use this method. Just before a transaction is executed, a transaction ID will be + * generated from the operator on the client. + * + * @param transactionId The TransactionId to be set + * @return {@code this} + * @see TransactionId + */ + public final T setTransactionId(TransactionId transactionId) { + requireNotFrozen(); + + transactionIds.setList(Collections.singletonList(transactionId)).setLocked(true); + + // noinspection unchecked + return (T) this; + } + + /** + * Should the transaction id be regenerated. + * + * @return should the transaction id be regenerated + */ + public final Boolean getRegenerateTransactionId() { + return regenerateTransactionId; + } + + /** + * Regenerate the transaction id. + * + * @param regenerateTransactionId should the transaction id be regenerated + * @return {@code this} + */ + public final T setRegenerateTransactionId(boolean regenerateTransactionId) { + this.regenerateTransactionId = regenerateTransactionId; + + // noinspection unchecked + return (T) this; + } + + /** + * Sign the transaction. + * + * @param privateKey the private key + * @return the signed transaction + */ + public final T sign(PrivateKey privateKey) { + return signWith(privateKey.getPublicKey(), privateKey::sign); + } + + /** + * Sign the transaction. + * + * @param publicKey the public key + * @param transactionSigner the key list + * @return {@code this} + */ + public T signWith(PublicKey publicKey, UnaryOperator transactionSigner) { + if (!isFrozen()) { + throw new IllegalStateException("Signing requires transaction to be frozen"); + } + + if (keyAlreadySigned(publicKey)) { + // noinspection unchecked + return (T) this; + } + + for (int i = 0; i < outerTransactions.size(); i++) { + outerTransactions.set(i, null); + } + publicKeys.add(publicKey); + signers.add(transactionSigner); + + // noinspection unchecked + return (T) this; + } + + /** + * Sign the transaction with the configured client. + * + * @param client the configured client + * @return the signed transaction + */ + public T signWithOperator(Client client) { + var operator = client.getOperator(); + + if (operator == null) { + throw new IllegalStateException("`client` must have an `operator` to sign with the operator"); + } + + if (!isFrozen()) { + freezeWith(client); + } + + return signWith(operator.publicKey, operator.transactionSigner); + } + + /** + * Checks if a public key is already added to the transaction + * + * @param key the public key + * @return if the public key is already added + */ + protected boolean keyAlreadySigned(PublicKey key) { + return publicKeys.contains(key); + } + + /** + * Add a signature to the transaction. + * + * @param publicKey the public key + * @param signature the signature + * @return {@code this} + */ + public T addSignature(PublicKey publicKey, byte[] signature) { + requireOneNodeAccountId(); + if (!isFrozen()) { + freeze(); + } + + if (keyAlreadySigned(publicKey)) { + // noinspection unchecked + return (T) this; + } + + transactionIds.setLocked(true); + nodeAccountIds.setLocked(true); + + for (int i = 0; i < outerTransactions.size(); i++) { + outerTransactions.set(i, null); + } + publicKeys.add(publicKey); + signers.add(null); + sigPairLists.get(0).addSigPair(publicKey.toSignaturePairProtobuf(signature)); + + // noinspection unchecked + return (T) this; + } + + protected Map> getSignaturesAtOffset(int offset) { + var map = new HashMap>(nodeAccountIds.size()); + + for (int i = 0; i < nodeAccountIds.size(); i++) { + var sigMap = sigPairLists.get(i + offset); + var nodeAccountId = nodeAccountIds.get(i); + + var keyMap = map.containsKey(nodeAccountId) + ? Objects.requireNonNull(map.get(nodeAccountId)) + : new HashMap(sigMap.getSigPairCount()); + map.put(nodeAccountId, keyMap); + + for (var sigPair : sigMap.getSigPairList()) { + keyMap.put( + PublicKey.fromBytes(sigPair.getPubKeyPrefix().toByteArray()), + sigPair.getEd25519().toByteArray()); + } + } + + return map; + } + + /** + * Extract list of account id and public keys. + * + * @return the list of account id and public keys + */ + public Map> getSignatures() { + if (!isFrozen()) { + throw new IllegalStateException("Transaction must be frozen in order to have signatures."); + } + + if (publicKeys.isEmpty()) { + return Collections.emptyMap(); + } + + buildAllTransactions(); + + return getSignaturesAtOffset(0); + } + + /** + * Check if transaction is frozen. + * + * @return is the transaction frozen + */ + protected boolean isFrozen() { + return frozenBodyBuilder != null; + } + + /** + * Throw an exception if the transaction is frozen. + */ + protected void requireNotFrozen() { + if (isFrozen()) { + throw new IllegalStateException( + "transaction is immutable; it has at least one signature or has been explicitly frozen"); + } + } + + /** + * Throw an exception if there is not exactly one node id set. + */ + protected void requireOneNodeAccountId() { + if (nodeAccountIds.size() != 1) { + throw new IllegalStateException("transaction did not have exactly one node ID set"); + } + } + + protected TransactionBody.Builder spawnBodyBuilder(@Nullable Client client) { + var clientDefaultFee = client != null ? client.getDefaultMaxTransactionFee() : null; + + var defaultFee = clientDefaultFee != null ? clientDefaultFee : defaultMaxTransactionFee; + + var feeHbars = maxTransactionFee != null ? maxTransactionFee : defaultFee; + + return TransactionBody.newBuilder() + .setTransactionFee(feeHbars.toTinybars()) + .setTransactionValidDuration(DurationConverter.toProtobuf(transactionValidDuration).toBuilder()) + .setMemo(memo); + } + + /** + * Freeze this transaction from further modification to prepare for signing or serialization. + * + * @return {@code this} + */ + public T freeze() { + return freezeWith(null); + } + + /** + * Freeze this transaction from further modification to prepare for signing or serialization. + *

+ * Will use the `Client`, if available, to generate a default Transaction ID and select 1/3 nodes to prepare this + * transaction for. + * + * @param client the configured client + * @return {@code this} + */ + public T freezeWith(@Nullable Client client) { + if (isFrozen()) { + // noinspection unchecked + return (T) this; + } + + if (transactionIds.isEmpty()) { + if (client != null) { + var operator = client.getOperator(); + + if (operator != null) { + // Set a default transaction ID, generated from the operator account ID + + transactionIds.setList(Collections.singletonList(TransactionId.generate(operator.accountId))); + } else { + // no client means there must be an explicitly set node ID and transaction ID + throw new IllegalStateException("`client` must have an `operator` or `transactionId` must be set"); + } + } else { + throw new IllegalStateException( + "Transaction ID must be set, or operator must be provided via freezeWith()"); + } + } + + if (nodeAccountIds.isEmpty()) { + if (client == null) { + throw new IllegalStateException( + "`client` must be provided or both `nodeId` and `transactionId` must be set"); + } + + try { + nodeAccountIds.setList(client.network.getNodeAccountIdsForExecute()); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + frozenBodyBuilder = + spawnBodyBuilder(client).setTransactionID(transactionIds.get(0).toProtobuf()); + onFreeze(frozenBodyBuilder); + + int requiredChunks = getRequiredChunks(); + generateTransactionIds(transactionIds.get(0), requiredChunks); + wipeTransactionLists(requiredChunks); + + var clientDefaultRegenerateTransactionId = client != null ? client.getDefaultRegenerateTransactionId() : null; + regenerateTransactionId = + regenerateTransactionId != null ? regenerateTransactionId : clientDefaultRegenerateTransactionId; + + // noinspection unchecked + return (T) this; + } + + /** + * There must be at least one chunk. + * + * @return there is 1 required chunk + */ + int getRequiredChunks() { + return 1; + } + + /** + * Generate transaction id's. + * + * @param initialTransactionId the initial transaction id + * @param count the number of id's to generate. + */ + void generateTransactionIds(TransactionId initialTransactionId, int count) { + var locked = transactionIds.isLocked(); + transactionIds.setLocked(false); + + if (count == 1) { + transactionIds.setList(Collections.singletonList(initialTransactionId)); + return; + } + + var nextTransactionId = initialTransactionId.toProtobuf().toBuilder(); + transactionIds.ensureCapacity(count); + transactionIds.clear(); + for (int i = 0; i < count; i++) { + transactionIds.add(TransactionId.fromProtobuf(nextTransactionId.build())); + + // add 1 ns to the validStart to make cascading transaction IDs + var nextValidStart = nextTransactionId.getTransactionValidStart().toBuilder(); + nextValidStart.setNanos(nextValidStart.getNanos() + 1); + + nextTransactionId.setTransactionValidStart(nextValidStart); + } + + transactionIds.setLocked(locked); + } + + /** + * Wipe / reset the transaction list. + * + * @param requiredChunks the number of required chunks + */ + void wipeTransactionLists(int requiredChunks) { + if (!transactionIds.isEmpty()) { + Objects.requireNonNull(frozenBodyBuilder) + .setTransactionID(getTransactionIdInternal().toProtobuf()); + } + + outerTransactions = new ArrayList<>(nodeAccountIds.size()); + sigPairLists = new ArrayList<>(nodeAccountIds.size()); + innerSignedTransactions = new ArrayList<>(nodeAccountIds.size()); + + for (AccountId nodeId : nodeAccountIds) { + sigPairLists.add(SignatureMap.newBuilder()); + innerSignedTransactions.add(SignedTransaction.newBuilder() + .setBodyBytes(Objects.requireNonNull(frozenBodyBuilder) + .setNodeAccountID(nodeId.toProtobuf()) + .build() + .toByteString())); + outerTransactions.add(null); + } + } + + /** + * Build all the transactions. + */ + void buildAllTransactions() { + transactionIds.setLocked(true); + nodeAccountIds.setLocked(true); + + for (var i = 0; i < innerSignedTransactions.size(); ++i) { + buildTransaction(i); + } + } + + /** + * Will build the specific transaction at {@code index} This function is only ever called after the transaction is + * frozen. + * + * @param index the index of the transaction to be built + */ + void buildTransaction(int index) { + // Check if transaction is already built. + // Every time a signer is added via sign() or signWith(), all outerTransactions are nullified. + if (outerTransactions.get(index) != null + && !outerTransactions.get(index).getSignedTransactionBytes().isEmpty()) { + return; + } + + signTransaction(index); + + outerTransactions.set( + index, + org.hiero.sdk.proto.Transaction.newBuilder() + .setSignedTransactionBytes(innerSignedTransactions + .get(index) + .setSigMap(sigPairLists.get(index)) + .build() + .toByteString()) + .build()); + } + + /** + * Will sign the specific transaction at {@code index} This function is only ever called after the transaction is + * frozen. + * + * @param index the index of the transaction to sign + */ + void signTransaction(int index) { + var bodyBytes = innerSignedTransactions.get(index).getBodyBytes().toByteArray(); + var thisSigPairList = sigPairLists.get(index).getSigPairList(); + + for (var i = 0; i < publicKeys.size(); i++) { + if (signers.get(i) == null) { + continue; + } + if (publicKeyIsInSigPairList(ByteString.copyFrom(publicKeys.get(i).toBytesRaw()), thisSigPairList)) { + continue; + } + + var signatureBytes = signers.get(i).apply(bodyBytes); + + sigPairLists.get(index).addSigPair(publicKeys.get(i).toSignaturePairProtobuf(signatureBytes)); + } + } + + /** + * Called in {@link #freezeWith(Client)} just before the transaction body is built. The intent is for the derived + * class to assign their data variant to the transaction body. + */ + abstract void onFreeze(TransactionBody.Builder bodyBuilder); + + /** + * Called in {@link #schedule()} when converting transaction into a scheduled version. + */ + abstract void onScheduled(SchedulableTransactionBody.Builder scheduled); + + @Override + final org.hiero.sdk.proto.Transaction makeRequest() { + var index = nodeAccountIds.getIndex() + (transactionIds.getIndex() * nodeAccountIds.size()); + + buildTransaction(index); + + return outerTransactions.get(index); + } + + @Override + TransactionResponse mapResponse( + org.hiero.sdk.proto.TransactionResponse transactionResponse, + AccountId nodeId, + org.hiero.sdk.proto.Transaction request) { + var transactionId = Objects.requireNonNull(getTransactionIdInternal()); + var hash = hash(request.getSignedTransactionBytes().toByteArray()); + transactionIds.advance(); + return new TransactionResponse(nodeId, transactionId, hash, null); + } + + @Override + final Status mapResponseStatus(org.hiero.sdk.proto.TransactionResponse transactionResponse) { + return Status.valueOf(transactionResponse.getNodeTransactionPrecheckCode()); + } + + abstract void validateChecksums(Client client) throws BadEntityIdException; + + /** + * Prepare the transactions to be executed. + * + * @param client the configured client + */ + void onExecute(Client client) { + if (!isFrozen()) { + freezeWith(client); + } + + var accountId = Objects.requireNonNull(Objects.requireNonNull(transactionIds.get(0)).accountId); + + if (client.isAutoValidateChecksumsEnabled()) { + try { + accountId.validateChecksum(client); + validateChecksums(client); + } catch (BadEntityIdException exc) { + throw new IllegalArgumentException(exc.getMessage()); + } + } + + var operatorId = client.getOperatorAccountId(); + if (operatorId != null && operatorId.equals(accountId)) { + // on execute, sign each transaction with the operator, if present + // and we are signing a transaction that used the default transaction ID + signWithOperator(client); + } + } + + @Override + CompletableFuture onExecuteAsync(Client client) { + onExecute(client); + return CompletableFuture.completedFuture(null); + } + + @Override + ExecutionState getExecutionState(Status status, org.hiero.sdk.proto.TransactionResponse response) { + if (status == Status.TRANSACTION_EXPIRED) { + if ((regenerateTransactionId != null && !regenerateTransactionId) || transactionIds.isLocked()) { + return ExecutionState.REQUEST_ERROR; + } else { + var firstTransactionId = Objects.requireNonNull(transactionIds.get(0)); + var accountId = Objects.requireNonNull(firstTransactionId.accountId); + generateTransactionIds(TransactionId.generate(accountId), transactionIds.size()); + wipeTransactionLists(transactionIds.size()); + return ExecutionState.RETRY; + } + } + return super.getExecutionState(status, response); + } + + @Override + @SuppressWarnings("LiteProtoToString") + public String toString() { + // NOTE: regex is for removing the instance address from the default debug output + TransactionBody.Builder body = spawnBodyBuilder(null); + + if (!transactionIds.isEmpty()) { + body.setTransactionID(transactionIds.get(0).toProtobuf()); + } + if (!nodeAccountIds.isEmpty()) { + body.setNodeAccountID(nodeAccountIds.get(0).toProtobuf()); + } + + onFreeze(body); + + return body.buildPartial().toString().replaceAll("@[A-Za-z0-9]+", ""); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/TransactionFeeSchedule.java b/sdk/src/main/java/org/hiero/sdk/TransactionFeeSchedule.java new file mode 100644 index 0000000000..e62d1fc76d --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TransactionFeeSchedule.java @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import com.google.protobuf.InvalidProtocolBufferException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import javax.annotation.Nullable; + +/** + * The fees for a specific transaction or query based on the fee data. + * + * See Hedera Documentation + */ +public class TransactionFeeSchedule implements Cloneable { + private RequestType requestType; + + @Nullable + private FeeData feeData; + + private List fees; + + /** + * Constructor. + */ + public TransactionFeeSchedule() { + requestType = RequestType.NONE; + feeData = null; + fees = new ArrayList<>(); + } + + /** + * Create a transaction fee schedule object from a protobuf. + * + * @param transactionFeeSchedule the protobuf + * @return the new transaction fee schedule + */ + static TransactionFeeSchedule fromProtobuf(org.hiero.sdk.proto.TransactionFeeSchedule transactionFeeSchedule) { + var returnFeeSchedule = new TransactionFeeSchedule() + .setRequestType(RequestType.valueOf(transactionFeeSchedule.getHederaFunctionality())) + .setFeeData( + transactionFeeSchedule.hasFeeData() + ? FeeData.fromProtobuf(transactionFeeSchedule.getFeeData()) + : null); + for (var feeData : transactionFeeSchedule.getFeesList()) { + returnFeeSchedule.addFee(FeeData.fromProtobuf(feeData)); + } + return returnFeeSchedule; + } + + /** + * Create a transaction fee schedule object from a byte array. + * + * @param bytes the byte array + * @return the new transaction fee schedule + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + public static TransactionFeeSchedule fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + return fromProtobuf(org.hiero.sdk.proto.TransactionFeeSchedule.parseFrom(bytes).toBuilder() + .build()); + } + + /** + * Extract the request type. + * + * @return the request type + */ + public RequestType getRequestType() { + return requestType; + } + + /** + * Assign the request type. + * + * @param requestType the request type + * @return {@code this} + */ + public TransactionFeeSchedule setRequestType(RequestType requestType) { + this.requestType = requestType; + return this; + } + + /** + * Get the total fee charged for a transaction + * + * @return the feeData + */ + @Deprecated + @Nullable + public FeeData getFeeData() { + return feeData; + } + + /** + * Set the total fee charged for a transaction + * + * @param feeData the feeData to set + * @return {@code this} + */ + @Deprecated + public TransactionFeeSchedule setFeeData(@Nullable FeeData feeData) { + this.feeData = feeData; + return this; + } + + /** + * Extract the list of fee's. + * + * @return the list of fee's + */ + public List getFees() { + return Collections.unmodifiableList(fees); + } + + /** + * Add a fee to the schedule. + * + * @param fee the fee to add + * @return {@code this} + */ + public TransactionFeeSchedule addFee(FeeData fee) { + fees.add(Objects.requireNonNull(fee)); + return this; + } + + /** + * Build the transaction body. + * + * @return {@link + * org.hiero.sdk.proto.TransactionFeeSchedule} + */ + org.hiero.sdk.proto.TransactionFeeSchedule toProtobuf() { + var returnBuilder = + org.hiero.sdk.proto.TransactionFeeSchedule.newBuilder().setHederaFunctionality(getRequestType().code); + if (feeData != null) { + returnBuilder.setFeeData(feeData.toProtobuf()); + } + for (var fee : fees) { + returnBuilder.addFees(fee.toProtobuf()); + } + return returnBuilder.build(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("requestType", getRequestType()) + .add("feeData", getFeeData()) + .add("fees", getFees()) + .toString(); + } + + /** + * Create the byte array. + * + * @return the byte array representation + */ + public byte[] toBytes() { + return toProtobuf().toByteArray(); + } + + List cloneFees() { + List cloneFees = new ArrayList<>(fees.size()); + for (var fee : fees) { + cloneFees.add(fee.clone()); + } + return cloneFees; + } + + @Override + public TransactionFeeSchedule clone() { + try { + TransactionFeeSchedule clone = (TransactionFeeSchedule) super.clone(); + clone.feeData = feeData != null ? feeData.clone() : null; + clone.fees = fees != null ? cloneFees() : null; + return clone; + } catch (CloneNotSupportedException e) { + throw new AssertionError(); + } + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TransactionId.java b/sdk/src/main/java/org/hiero/sdk/TransactionId.java similarity index 85% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/TransactionId.java rename to sdk/src/main/java/org/hiero/sdk/TransactionId.java index c4a7442ad2..95a4be7366 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TransactionId.java +++ b/sdk/src/main/java/org/hiero/sdk/TransactionId.java @@ -1,33 +1,13 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import static java.util.concurrent.CompletableFuture.completedFuture; import static java.util.concurrent.CompletableFuture.failedFuture; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.TransactionID; import java.time.Duration; import java.time.Instant; import java.util.Objects; -import java.util.Random; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeoutException; @@ -35,6 +15,7 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; import javax.annotation.Nullable; +import org.hiero.sdk.proto.TransactionID; /** * The client-generated ID for a transaction. @@ -72,7 +53,6 @@ public final class TransactionId implements Comparable { private static final AtomicLong monotonicTime = new AtomicLong(); - /** * No longer part of the public API. Use `Transaction.withValidStart()` instead. * @@ -103,7 +83,7 @@ public static TransactionId withValidStart(AccountId accountId, Instant validSta * that will be charged the transaction fees for the transaction. * * @param accountId the ID of the Hedera account that will be charge the transaction fees. - * @return {@link com.hedera.hashgraph.sdk.TransactionId} + * @return {@link org.hiero.sdk.TransactionId} */ public static TransactionId generate(AccountId accountId) { long currentTime; @@ -117,7 +97,6 @@ public static TransactionId generate(AccountId accountId) { // between the client and the receiving node and prevented spurious INVALID_TRANSACTION_START. currentTime = System.currentTimeMillis() * NANOSECONDS_PER_MILLISECOND - NANOSECONDS_TO_REMOVE; - // Get the last recorded timestamp. lastTime = monotonicTime.get(); @@ -129,7 +108,10 @@ public static TransactionId generate(AccountId accountId) { } while (!monotonicTime.compareAndSet(lastTime, currentTime)); // NOTE: using ThreadLocalRandom because it's compatible with Android SDK version 26 - return new TransactionId(accountId, Instant.ofEpochSecond(0, currentTime + ThreadLocalRandom.current().nextLong(1_000))); + return new TransactionId( + accountId, + Instant.ofEpochSecond( + 0, currentTime + ThreadLocalRandom.current().nextLong(1_000))); } /** @@ -140,11 +122,13 @@ public static TransactionId generate(AccountId accountId) { */ static TransactionId fromProtobuf(TransactionID transactionID) { var accountId = transactionID.hasAccountID() ? AccountId.fromProtobuf(transactionID.getAccountID()) : null; - var validStart = transactionID.hasTransactionValidStart() ? InstantConverter.fromProtobuf(transactionID.getTransactionValidStart()) : null; + var validStart = transactionID.hasTransactionValidStart() + ? InstantConverter.fromProtobuf(transactionID.getTransactionValidStart()) + : null; return new TransactionId(accountId, validStart) - .setScheduled(transactionID.getScheduled()) - .setNonce((transactionID.getNonce() != 0) ? transactionID.getNonce() : null); + .setScheduled(transactionID.getScheduled()) + .setNonce((transactionID.getNonce() != 0) ? transactionID.getNonce() : null); } /** @@ -176,9 +160,9 @@ public static TransactionId fromString(String s) { throw new IllegalArgumentException("expecting {account}@{seconds}.{nanos}"); } - @Nullable Instant validStart = Instant.ofEpochSecond( - Long.parseLong(validStartParts[0]), - Long.parseLong(validStartParts[1])); + @Nullable + Instant validStart = + Instant.ofEpochSecond(Long.parseLong(validStartParts[0]), Long.parseLong(validStartParts[1])); return new TransactionId(accountId, validStart).setScheduled(scheduled).setNonce(nonce); } @@ -244,7 +228,8 @@ public TransactionId setNonce(@Nullable Integer nonce) { * @throws PrecheckStatusException when the precheck fails * @throws ReceiptStatusException when there is an issue with the receipt */ - public TransactionReceipt getReceipt(Client client) throws TimeoutException, PrecheckStatusException, ReceiptStatusException { + public TransactionReceipt getReceipt(Client client) + throws TimeoutException, PrecheckStatusException, ReceiptStatusException { return getReceipt(client, client.getRequestTimeout()); } @@ -258,10 +243,9 @@ public TransactionReceipt getReceipt(Client client) throws TimeoutException, Pre * @throws PrecheckStatusException when the precheck fails * @throws ReceiptStatusException when there is an issue with the receipt */ - public TransactionReceipt getReceipt(Client client, Duration timeout) throws TimeoutException, PrecheckStatusException, ReceiptStatusException { - var receipt = new TransactionReceiptQuery() - .setTransactionId(this) - .execute(client, timeout); + public TransactionReceipt getReceipt(Client client, Duration timeout) + throws TimeoutException, PrecheckStatusException, ReceiptStatusException { + var receipt = new TransactionReceiptQuery().setTransactionId(this).execute(client, timeout); if (receipt.status != Status.SUCCESS) { throw new ReceiptStatusException(this, receipt); @@ -289,15 +273,15 @@ public CompletableFuture getReceiptAsync(Client client) { */ public CompletableFuture getReceiptAsync(Client client, Duration timeout) { return new TransactionReceiptQuery() - .setTransactionId(this) - .executeAsync(client, timeout) - .thenCompose(receipt -> { - if (receipt.status != Status.SUCCESS) { - return failedFuture(new ReceiptStatusException(this, receipt)); - } + .setTransactionId(this) + .executeAsync(client, timeout) + .thenCompose(receipt -> { + if (receipt.status != Status.SUCCESS) { + return failedFuture(new ReceiptStatusException(this, receipt)); + } - return completedFuture(receipt); - }); + return completedFuture(receipt); + }); } /** @@ -340,7 +324,8 @@ public void getReceiptAsync(Client client, Consumer onSucces * @param onSuccess a Consumer which consumes the result on success. * @param onFailure a Consumer which consumes the error on failure. */ - public void getReceiptAsync(Client client, Duration timeout, Consumer onSuccess, Consumer onFailure) { + public void getReceiptAsync( + Client client, Duration timeout, Consumer onSuccess, Consumer onFailure) { ConsumerHelper.twoConsumers(getReceiptAsync(client, timeout), onSuccess, onFailure); } @@ -353,7 +338,8 @@ public void getReceiptAsync(Client client, Duration timeout, Consumer getRecordAsync(Client client) { */ public CompletableFuture getRecordAsync(Client client, Duration timeout) { // note: we get the receipt first to ensure consensus has been reached - return getReceiptAsync(client, timeout).thenCompose(receipt -> new TransactionRecordQuery() - .setTransactionId(this) - .executeAsync(client, timeout)); + return getReceiptAsync(client, timeout) + .thenCompose(receipt -> + new TransactionRecordQuery().setTransactionId(this).executeAsync(client, timeout)); } /** @@ -439,7 +424,8 @@ public void getRecordAsync(Client client, Consumer onSuccess, * @param onSuccess a Consumer which consumes the result on success. * @param onFailure a Consumer which consumes the error on failure. */ - public void getRecordAsync(Client client, Duration timeout, Consumer onSuccess, Consumer onFailure) { + public void getRecordAsync( + Client client, Duration timeout, Consumer onSuccess, Consumer onFailure) { ConsumerHelper.twoConsumers(getRecordAsync(client, timeout), onSuccess, onFailure); } @@ -449,9 +435,7 @@ public void getRecordAsync(Client client, Duration timeout, Consumer serials; + + /** + * In the receipt of a NodeCreate, NodeUpdate, NodeDelete, the id of the newly created node. + * An affected node identifier.
+ * This value SHALL be set following a `createNode` transaction.
+ * This value SHALL be set following a `updateNode` transaction.
+ * This value SHALL be set following a `deleteNode` transaction.
+ * This value SHALL NOT be set following any other transaction. + */ + public final long nodeId; + + /** + * The receipts of processing all transactions with the given id, in consensus time order. + */ + public final List duplicates; + + /** + * The receipts (if any) of all child transactions spawned by the transaction with the + * given top-level id, in consensus order. Always empty if the top-level status is UNKNOWN. + */ + public final List children; + + TransactionReceipt( + @Nullable TransactionId transactionId, + Status status, + ExchangeRate exchangeRate, + @Nullable AccountId accountId, + @Nullable FileId fileId, + @Nullable ContractId contractId, + @Nullable TopicId topicId, + @Nullable TokenId tokenId, + @Nullable Long topicSequenceNumber, + @Nullable ByteString topicRunningHash, + Long totalSupply, + @Nullable ScheduleId scheduleId, + @Nullable TransactionId scheduledTransactionId, + List serials, + long nodeId, + List duplicates, + List children) { + this.transactionId = transactionId; + this.status = status; + this.exchangeRate = exchangeRate; + this.accountId = accountId; + this.fileId = fileId; + this.contractId = contractId; + this.topicId = topicId; + this.tokenId = tokenId; + this.topicSequenceNumber = topicSequenceNumber; + this.topicRunningHash = topicRunningHash; + this.totalSupply = totalSupply; + this.scheduleId = scheduleId; + this.scheduledTransactionId = scheduledTransactionId; + this.serials = serials; + this.nodeId = nodeId; + this.duplicates = duplicates; + this.children = children; + } + + /** + * Create transaction receipt from protobuf. + * + * @param transactionReceipt the protobuf + * @param duplicates list of duplicates + * @param children list of children + * @return the new transaction receipt + */ + static TransactionReceipt fromProtobuf( + org.hiero.sdk.proto.TransactionReceipt transactionReceipt, + List duplicates, + List children, + @Nullable TransactionId transactionId) { + var status = Status.valueOf(transactionReceipt.getStatus()); + + var rate = transactionReceipt.getExchangeRate(); + var exchangeRate = ExchangeRate.fromProtobuf(rate.getCurrentRate()); + + var accountId = + transactionReceipt.hasAccountID() ? AccountId.fromProtobuf(transactionReceipt.getAccountID()) : null; + + var fileId = transactionReceipt.hasFileID() ? FileId.fromProtobuf(transactionReceipt.getFileID()) : null; + + var contractId = + transactionReceipt.hasContractID() ? ContractId.fromProtobuf(transactionReceipt.getContractID()) : null; + + var topicId = transactionReceipt.hasTopicID() ? TopicId.fromProtobuf(transactionReceipt.getTopicID()) : null; + + var tokenId = transactionReceipt.hasTokenID() ? TokenId.fromProtobuf(transactionReceipt.getTokenID()) : null; + + var topicSequenceNumber = + transactionReceipt.getTopicSequenceNumber() == 0 ? null : transactionReceipt.getTopicSequenceNumber(); + + var topicRunningHash = + transactionReceipt.getTopicRunningHash().isEmpty() ? null : transactionReceipt.getTopicRunningHash(); + + var totalSupply = transactionReceipt.getNewTotalSupply(); + + var scheduleId = + transactionReceipt.hasScheduleID() ? ScheduleId.fromProtobuf(transactionReceipt.getScheduleID()) : null; + + var scheduledTransactionId = transactionReceipt.hasScheduledTransactionID() + ? TransactionId.fromProtobuf(transactionReceipt.getScheduledTransactionID()) + : null; + + var serials = transactionReceipt.getSerialNumbersList(); + + var nodeId = transactionReceipt.getNodeId(); + + return new TransactionReceipt( + transactionId, + status, + exchangeRate, + accountId, + fileId, + contractId, + topicId, + tokenId, + topicSequenceNumber, + topicRunningHash, + totalSupply, + scheduleId, + scheduledTransactionId, + serials, + nodeId, + duplicates, + children); + } + + /** + * Create a transaction receipt from a protobuf. + * + * @param transactionReceipt the protobuf + * @return the new transaction receipt + */ + public static TransactionReceipt fromProtobuf(org.hiero.sdk.proto.TransactionReceipt transactionReceipt) { + return fromProtobuf(transactionReceipt, new ArrayList<>(), new ArrayList<>(), null); + } + + static TransactionReceipt fromProtobuf( + org.hiero.sdk.proto.TransactionReceipt transactionReceipt, @Nullable TransactionId transactionId) { + return fromProtobuf(transactionReceipt, new ArrayList<>(), new ArrayList<>(), transactionId); + } + + /** + * Create a transaction receipt from a byte array. + * + * @param bytes the byte array + * @return the new transaction receipt + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + public static TransactionReceipt fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + return fromProtobuf(org.hiero.sdk.proto.TransactionReceipt.parseFrom(bytes).toBuilder() + .build()); + } + + /** + * Validate the transaction status in the receipt. + * + * @param shouldValidate Whether to perform transaction status validation + * @return {@code this} + * @throws ReceiptStatusException when shouldValidate is true and the transaction status is not SUCCESS + */ + public TransactionReceipt validateStatus(boolean shouldValidate) throws ReceiptStatusException { + if (shouldValidate && status != Status.SUCCESS && status != Status.FEE_SCHEDULE_FILE_PART_UPLOADED) { + throw new ReceiptStatusException(transactionId, this); + } + return this; + } + + /** + * Create the protobuf. + * + * @return the protobuf representation + */ + org.hiero.sdk.proto.TransactionReceipt toProtobuf() { + var transactionReceiptBuilder = org.hiero.sdk.proto.TransactionReceipt.newBuilder() + .setStatus(status.code) + .setExchangeRate(ExchangeRateSet.newBuilder() + .setCurrentRate(org.hiero.sdk.proto.ExchangeRate.newBuilder() + .setHbarEquiv(exchangeRate.hbars) + .setCentEquiv(exchangeRate.cents) + .setExpirationTime(TimestampSeconds.newBuilder() + .setSeconds(exchangeRate.expirationTime.getEpochSecond())))) + .setNewTotalSupply(totalSupply); + + if (accountId != null) { + transactionReceiptBuilder.setAccountID(accountId.toProtobuf()); + } + + if (fileId != null) { + transactionReceiptBuilder.setFileID(fileId.toProtobuf()); + } + + if (contractId != null) { + transactionReceiptBuilder.setContractID(contractId.toProtobuf()); + } + + if (topicId != null) { + transactionReceiptBuilder.setTopicID(topicId.toProtobuf()); + } + + if (tokenId != null) { + transactionReceiptBuilder.setTokenID(tokenId.toProtobuf()); + } + + if (topicSequenceNumber != null) { + transactionReceiptBuilder.setTopicSequenceNumber(topicSequenceNumber); + } + + if (topicRunningHash != null) { + transactionReceiptBuilder.setTopicRunningHash(topicRunningHash); + } + + if (scheduleId != null) { + transactionReceiptBuilder.setScheduleID(scheduleId.toProtobuf()); + } + + if (scheduledTransactionId != null) { + transactionReceiptBuilder.setScheduledTransactionID(scheduledTransactionId.toProtobuf()); + } + + for (var serial : serials) { + transactionReceiptBuilder.addSerialNumbers(serial); + } + + transactionReceiptBuilder.setNodeId(nodeId); + + return transactionReceiptBuilder.build(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("transactionId", transactionId) + .add("status", status) + .add("exchangeRate", exchangeRate) + .add("accountId", accountId) + .add("fileId", fileId) + .add("contractId", contractId) + .add("topicId", topicId) + .add("tokenId", tokenId) + .add("topicSequenceNumber", topicSequenceNumber) + .add("topicRunningHash", topicRunningHash != null ? Hex.encode(topicRunningHash.toByteArray()) : null) + .add("totalSupply", totalSupply) + .add("scheduleId", scheduleId) + .add("scheduledTransactionId", scheduledTransactionId) + .add("serials", serials) + .add("nodeId", nodeId) + .add("duplicates", duplicates) + .add("children", children) + .toString(); + } + + /** + * Create the byte array. + * + * @return the byte array representation + */ + public byte[] toBytes() { + return toProtobuf().toByteArray(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/TransactionReceiptQuery.java b/sdk/src/main/java/org/hiero/sdk/TransactionReceiptQuery.java new file mode 100644 index 0000000000..eb01de9368 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TransactionReceiptQuery.java @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.grpc.MethodDescriptor; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.CryptoServiceGrpc; +import org.hiero.sdk.proto.QueryHeader; +import org.hiero.sdk.proto.Response; +import org.hiero.sdk.proto.ResponseHeader; +import org.hiero.sdk.proto.TransactionGetReceiptQuery; + +/** + * Get the receipt of a transaction, given its transaction ID. + * + *

Once a transaction reaches consensus, then information about whether it succeeded or failed + * will be available until the end of the receipt period. + * + *

This query is free. + */ +public final class TransactionReceiptQuery extends Query { + + @Nullable + private TransactionId transactionId = null; + + private boolean includeChildren = false; + private boolean includeDuplicates = false; + + /** + * Constructor. + */ + public TransactionReceiptQuery() {} + + /** + * Extract the transaction id. + * + * @return the transaction id + */ + @Override + @Nullable + public TransactionId getTransactionIdInternal() { + return transactionId; + } + + /** + * Set the ID of the transaction for which the receipt is being requested. + * + * @param transactionId The TransactionId to be set + * @return {@code this} + */ + public TransactionReceiptQuery setTransactionId(TransactionId transactionId) { + Objects.requireNonNull(transactionId); + this.transactionId = transactionId; + return this; + } + + /** + * Should the children be included? + * + * @return should children be included + */ + public boolean getIncludeChildren() { + return includeChildren; + } + + /** + * Whether the response should include the records of any child transactions spawned by the + * top-level transaction with the given transactionID. + * + * @param value The value that includeChildren should be set to; true to include children, false to exclude + * @return {@code this} + */ + public TransactionReceiptQuery setIncludeChildren(boolean value) { + includeChildren = value; + return this; + } + + /** + * Should duplicates be included? + * + * @return should duplicates be included + */ + public boolean getIncludeDuplicates() { + return includeDuplicates; + } + + /** + * Whether records of processing duplicate transactions should be returned along with the record + * of processing the first consensus transaction with the given id whose status was neither + * INVALID_NODE_ACCOUNT nor INVALID_PAYER_SIGNATURE or, if no such + * record exists, the record of processing the first transaction to reach consensus with the + * given transaction id. + * + * @param value The value that includeDuplicates should be set to; true to include duplicates, false to exclude + * @return {@code this} + */ + public TransactionReceiptQuery setIncludeDuplicates(boolean value) { + includeDuplicates = value; + return this; + } + + @Override + boolean isPaymentRequired() { + return false; + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + if (transactionId != null) { + Objects.requireNonNull(transactionId.accountId).validateChecksum(client); + } + } + + @Override + void onMakeRequest(org.hiero.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { + var builder = TransactionGetReceiptQuery.newBuilder() + .setIncludeChildReceipts(includeChildren) + .setIncludeDuplicates(includeDuplicates); + if (transactionId != null) { + builder.setTransactionID(transactionId.toProtobuf()); + } + + queryBuilder.setTransactionGetReceipt(builder.setHeader(header)); + } + + @Override + Status mapResponseStatus(Response response) { + var preCheckCode = response.getTransactionGetReceipt().getHeader().getNodeTransactionPrecheckCode(); + + return Status.valueOf(preCheckCode); + } + + @Override + TransactionReceipt mapResponse(Response response, AccountId nodeId, org.hiero.sdk.proto.Query request) { + var receiptResponse = response.getTransactionGetReceipt(); + var duplicates = mapReceiptList(receiptResponse.getDuplicateTransactionReceiptsList()); + var children = mapReceiptList(receiptResponse.getChildTransactionReceiptsList()); + return TransactionReceipt.fromProtobuf( + response.getTransactionGetReceipt().getReceipt(), duplicates, children, transactionId); + } + + /** + * Create a list of transaction receipts from a protobuf. + * + * @param protoReceiptList the protobuf + * @return the list of transaction receipts + */ + private static List mapReceiptList( + List protoReceiptList) { + List outList = new ArrayList<>(protoReceiptList.size()); + for (var protoReceipt : protoReceiptList) { + outList.add(TransactionReceipt.fromProtobuf(protoReceipt)); + } + return outList; + } + + @Override + QueryHeader mapRequestHeader(org.hiero.sdk.proto.Query request) { + return request.getTransactionGetReceipt().getHeader(); + } + + @Override + ResponseHeader mapResponseHeader(Response response) { + return response.getTransactionGetReceipt().getHeader(); + } + + @Override + MethodDescriptor getMethodDescriptor() { + return CryptoServiceGrpc.getGetTransactionReceiptsMethod(); + } + + @Override + ExecutionState getExecutionState(Status status, Response response) { + switch (status) { + case BUSY: + case UNKNOWN: + case RECEIPT_NOT_FOUND: + case RECORD_NOT_FOUND: + case PLATFORM_NOT_ACTIVE: + return ExecutionState.RETRY; + + case OK: + break; + + default: + return ExecutionState.REQUEST_ERROR; + } + + var receiptStatus = + Status.valueOf(response.getTransactionGetReceipt().getReceipt().getStatus()); + + switch (receiptStatus) { + case BUSY: + case UNKNOWN: + case OK: + case RECEIPT_NOT_FOUND: + case RECORD_NOT_FOUND: + case PLATFORM_NOT_ACTIVE: + return ExecutionState.RETRY; + + default: + return ExecutionState.SUCCESS; + } + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/TransactionRecord.java b/sdk/src/main/java/org/hiero/sdk/TransactionRecord.java new file mode 100644 index 0000000000..1d719ab6a2 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TransactionRecord.java @@ -0,0 +1,515 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import javax.annotation.Nullable; +import org.bouncycastle.util.encoders.Hex; +import org.hiero.sdk.proto.AccountAmount; +import org.hiero.sdk.proto.NftTransfer; +import org.hiero.sdk.proto.TokenTransferList; +import org.hiero.sdk.proto.TransferList; + +/** + * The complete record for a transaction on Hedera that has reached consensus. + *

+ * This is not-free to request and is available for 1 hour after a transaction reaches consensus. + *

+ * A {@link TransactionReceipt} can be thought of as a light-weight record which is free to ask for if you just + * need what it contains. A receipt however lasts for only 180 seconds. + */ +public final class TransactionRecord { + /** + * The status (reach consensus, or failed, or is unknown) and the ID of + * any new account/file/instance created. + */ + public final TransactionReceipt receipt; + + /** + * The hash of the Transaction that executed (not the hash of any Transaction that failed for + * having a duplicate TransactionID). + */ + public final ByteString transactionHash; + + /** + * The consensus timestamp (or null if didn't reach consensus yet). + */ + public final Instant consensusTimestamp; + + /** + * The ID of the transaction this record represents. + */ + public final TransactionId transactionId; + + /** + * The memo that was submitted as part of the transaction (max 100 bytes). + */ + public final String transactionMemo; + + /** + * The actual transaction fee charged, not the original + * transactionFee value from TransactionBody. + */ + public final Hbar transactionFee; + + /** + * Record of the value returned by the smart contract + * function or constructor. + */ + @Nullable + public final ContractFunctionResult contractFunctionResult; + + /** + * All hbar transfers as a result of this transaction, such as fees, or + * transfers performed by the transaction, or by a smart contract it calls, + * or by the creation of threshold records that it triggers. + */ + public final List transfers; + + /** + * All fungible token transfers as a result of this transaction as a map + */ + public final Map> tokenTransfers; + + /** + * All fungible token transfers as a result of this transaction as a list + */ + public final List tokenTransferList; + + /** + * All NFT Token transfers as a result of this transaction + */ + public final Map> tokenNftTransfers; + + /** + * Reference to the scheduled transaction ID that this transaction record represents + */ + @Nullable + public final ScheduleId scheduleRef; + + /** + * All custom fees that were assessed during a CryptoTransfer, and must be paid if the + * transaction status resolved to SUCCESS + */ + public final List assessedCustomFees; + + /** + * All token associations implicitly created while handling this transaction + */ + public final List automaticTokenAssociations; + + /** + * In the record of an internal CryptoCreate transaction triggered by a user + * transaction with a (previously unused) alias, the new account's alias. + */ + @Nullable + public final PublicKey aliasKey; + + /** + * The records of processing all child transaction spawned by the transaction with the given + * top-level id, in consensus order. Always empty if the top-level status is UNKNOWN. + */ + public final List children; + + /** + * The records of processing all consensus transaction with the same id as the distinguished + * record above, in chronological order. + */ + public final List duplicates; + + /** + * In the record of an internal transaction, the consensus timestamp of the user + * transaction that spawned it. + */ + @Nullable + public final Instant parentConsensusTimestamp; + + /** + * The keccak256 hash of the ethereumData. This field will only be populated for + * EthereumTransaction. + */ + public final ByteString ethereumHash; + + /** + * An approved allowance of hbar transfers for a spender + */ + @Deprecated + public final List hbarAllowanceAdjustments; + + /** + * An approved allowance of token transfers for a spender + */ + @Deprecated + public final List tokenAllowanceAdjustments; + + /** + * An approved allowance of NFT transfers for a spender + */ + @Deprecated + public final List tokenNftAllowanceAdjustments; + + /** + * List of accounts with the corresponding staking rewards paid as a result of a transaction. + */ + public final List paidStakingRewards; + + /** + * In the record of a UtilPrng transaction with no output range, a pseudorandom 384-bit string. + */ + @Nullable + public final ByteString prngBytes; + + /** + * In the record of a PRNG transaction with an output range, the output of a PRNG whose input was a 384-bit string. + */ + @Nullable + public final Integer prngNumber; + + /** + * The new default EVM address of the account created by this transaction. + * This field is populated only when the EVM address is not specified in the related transaction body. + */ + public final ByteString evmAddress; + + /** + * A list of pending token airdrops. + * Each pending airdrop represents a single requested transfer from a + * sending account to a recipient account. These pending transfers are + * issued unilaterally by the sending account, and MUST be claimed by the + * recipient account before the transfer MAY complete. + * A sender MAY cancel a pending airdrop before it is claimed. + * An airdrop transaction SHALL emit a pending airdrop when the recipient has no + * available automatic association slots available or when the recipient + * has set `receiver_sig_required`. + */ + public final List pendingAirdropRecords; + + TransactionRecord( + TransactionReceipt transactionReceipt, + ByteString transactionHash, + Instant consensusTimestamp, + TransactionId transactionId, + String transactionMemo, + long transactionFee, + @Nullable ContractFunctionResult contractFunctionResult, + List transfers, + Map> tokenTransfers, + List tokenTransferList, + Map> tokenNftTransfers, + @Nullable ScheduleId scheduleRef, + List assessedCustomFees, + List automaticTokenAssociations, + @Nullable PublicKey aliasKey, + List children, + List duplicates, + @Nullable Instant parentConsensusTimestamp, + ByteString ethereumHash, + List paidStakingRewards, + @Nullable ByteString prngBytes, + @Nullable Integer prngNumber, + ByteString evmAddress, + List pendingAirdropRecords) { + this.receipt = transactionReceipt; + this.transactionHash = transactionHash; + this.consensusTimestamp = consensusTimestamp; + this.transactionMemo = transactionMemo; + this.transactionId = transactionId; + this.transfers = transfers; + this.contractFunctionResult = contractFunctionResult; + this.transactionFee = Hbar.fromTinybars(transactionFee); + this.tokenTransfers = tokenTransfers; + this.tokenTransferList = tokenTransferList; + this.tokenNftTransfers = tokenNftTransfers; + this.scheduleRef = scheduleRef; + this.assessedCustomFees = assessedCustomFees; + this.automaticTokenAssociations = automaticTokenAssociations; + this.aliasKey = aliasKey; + this.children = children; + this.duplicates = duplicates; + this.parentConsensusTimestamp = parentConsensusTimestamp; + this.ethereumHash = ethereumHash; + this.pendingAirdropRecords = pendingAirdropRecords; + this.hbarAllowanceAdjustments = Collections.emptyList(); + this.tokenAllowanceAdjustments = Collections.emptyList(); + this.tokenNftAllowanceAdjustments = Collections.emptyList(); + this.paidStakingRewards = paidStakingRewards; + this.prngBytes = prngBytes; + this.prngNumber = prngNumber; + this.evmAddress = evmAddress; + } + + /** + * Create a transaction record from a protobuf. + * + * @param transactionRecord the protobuf + * @param children the list of children + * @param duplicates the list of duplicates + * @return the new transaction record + */ + static TransactionRecord fromProtobuf( + org.hiero.sdk.proto.TransactionRecord transactionRecord, + List children, + List duplicates, + @Nullable TransactionId transactionId) { + var transfers = + new ArrayList(transactionRecord.getTransferList().getAccountAmountsCount()); + for (var accountAmount : transactionRecord.getTransferList().getAccountAmountsList()) { + transfers.add(Transfer.fromProtobuf(accountAmount)); + } + + var tokenTransfers = new HashMap>(); + var tokenNftTransfers = new HashMap>(); + + var tokenTransfersList = TokenTransfer.fromProtobuf(transactionRecord.getTokenTransferListsList()); + var nftTransfersList = TokenNftTransfer.fromProtobuf(transactionRecord.getTokenTransferListsList()); + + for (var transfer : tokenTransfersList) { + var current = tokenTransfers.containsKey(transfer.tokenId) + ? tokenTransfers.get(transfer.tokenId) + : new HashMap(); + current.put(transfer.accountId, transfer.amount); + tokenTransfers.put(transfer.tokenId, current); + } + + for (var transfer : nftTransfersList) { + var current = tokenNftTransfers.containsKey(transfer.tokenId) + ? tokenNftTransfers.get(transfer.tokenId) + : new ArrayList(); + current.add(transfer); + tokenNftTransfers.put(transfer.tokenId, current); + } + + var fees = new ArrayList(transactionRecord.getAssessedCustomFeesCount()); + for (var fee : transactionRecord.getAssessedCustomFeesList()) { + fees.add(AssessedCustomFee.fromProtobuf(fee)); + } + + // HACK: This is a bit bad, any takers to clean this up + var contractFunctionResult = transactionRecord.hasContractCallResult() + ? new ContractFunctionResult(transactionRecord.getContractCallResult()) + : transactionRecord.hasContractCreateResult() + ? new ContractFunctionResult(transactionRecord.getContractCreateResult()) + : null; + + var automaticTokenAssociations = + new ArrayList(transactionRecord.getAutomaticTokenAssociationsCount()); + for (var tokenAssociation : transactionRecord.getAutomaticTokenAssociationsList()) { + automaticTokenAssociations.add(TokenAssociation.fromProtobuf(tokenAssociation)); + } + + var aliasKey = PublicKey.fromAliasBytes(transactionRecord.getAlias()); + + var paidStakingRewards = new ArrayList(transactionRecord.getPaidStakingRewardsCount()); + for (var reward : transactionRecord.getPaidStakingRewardsList()) { + paidStakingRewards.add(Transfer.fromProtobuf(reward)); + } + + List pendingAirdropRecords = transactionRecord.getNewPendingAirdropsList().stream() + .map(PendingAirdropRecord::fromProtobuf) + .collect(Collectors.toList()); + + return new TransactionRecord( + TransactionReceipt.fromProtobuf(transactionRecord.getReceipt(), transactionId), + transactionRecord.getTransactionHash(), + InstantConverter.fromProtobuf(transactionRecord.getConsensusTimestamp()), + TransactionId.fromProtobuf(transactionRecord.getTransactionID()), + transactionRecord.getMemo(), + transactionRecord.getTransactionFee(), + contractFunctionResult, + transfers, + tokenTransfers, + tokenTransfersList, + tokenNftTransfers, + transactionRecord.hasScheduleRef() ? ScheduleId.fromProtobuf(transactionRecord.getScheduleRef()) : null, + fees, + automaticTokenAssociations, + aliasKey, + children, + duplicates, + transactionRecord.hasParentConsensusTimestamp() + ? InstantConverter.fromProtobuf(transactionRecord.getParentConsensusTimestamp()) + : null, + transactionRecord.getEthereumHash(), + paidStakingRewards, + transactionRecord.hasPrngBytes() ? transactionRecord.getPrngBytes() : null, + transactionRecord.hasPrngNumber() ? transactionRecord.getPrngNumber() : null, + transactionRecord.getEvmAddress(), + pendingAirdropRecords); + } + + /** + * Create a transaction record from a protobuf. + * + * @param transactionRecord the protobuf + * @return the new transaction record + */ + static TransactionRecord fromProtobuf(org.hiero.sdk.proto.TransactionRecord transactionRecord) { + return fromProtobuf(transactionRecord, new ArrayList<>(), new ArrayList<>(), null); + } + + /** + * Create a transaction record from a byte array. + * + * @param bytes the byte array + * @return the new transaction record + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + public static TransactionRecord fromBytes(byte[] bytes) throws InvalidProtocolBufferException { + return fromProtobuf(org.hiero.sdk.proto.TransactionRecord.parseFrom(bytes).toBuilder() + .build()); + } + + /** + * Validate the transaction status in the receipt. + * + * @param shouldValidate Whether to perform transaction status validation + * @return {@code this} + * @throws ReceiptStatusException when shouldValidate is true and the transaction status is not SUCCESS + */ + public TransactionRecord validateReceiptStatus(boolean shouldValidate) throws ReceiptStatusException { + receipt.validateStatus(shouldValidate); + return this; + } + + /** + * Create the protobuf. + * + * @return the protobuf representation + */ + org.hiero.sdk.proto.TransactionRecord toProtobuf() { + var transferList = TransferList.newBuilder(); + for (Transfer transfer : transfers) { + transferList.addAccountAmounts(transfer.toProtobuf()); + } + + var transactionRecord = org.hiero.sdk.proto.TransactionRecord.newBuilder() + .setReceipt(receipt.toProtobuf()) + .setTransactionHash(transactionHash) + .setConsensusTimestamp(InstantConverter.toProtobuf(consensusTimestamp)) + .setTransactionID(transactionId.toProtobuf()) + .setMemo(transactionMemo) + .setTransactionFee(transactionFee.toTinybars()) + .setTransferList(transferList) + .setEthereumHash(ethereumHash) + .setEvmAddress(evmAddress); + + for (var tokenEntry : tokenTransfers.entrySet()) { + var tokenTransfersList = + TokenTransferList.newBuilder().setToken(tokenEntry.getKey().toProtobuf()); + for (var aaEntry : tokenEntry.getValue().entrySet()) { + tokenTransfersList.addTransfers(AccountAmount.newBuilder() + .setAccountID(aaEntry.getKey().toProtobuf()) + .setAmount(aaEntry.getValue()) + .build()); + } + + transactionRecord.addTokenTransferLists(tokenTransfersList); + } + + for (var nftEntry : tokenNftTransfers.entrySet()) { + var nftTransferList = + TokenTransferList.newBuilder().setToken(nftEntry.getKey().toProtobuf()); + for (var aaEntry : nftEntry.getValue()) { + nftTransferList.addNftTransfers(NftTransfer.newBuilder() + .setSenderAccountID(aaEntry.sender.toProtobuf()) + .setReceiverAccountID(aaEntry.receiver.toProtobuf()) + .setSerialNumber(aaEntry.serial) + .setIsApproval(aaEntry.isApproved) + .build()); + } + + transactionRecord.addTokenTransferLists(nftTransferList); + } + + if (contractFunctionResult != null) { + transactionRecord.setContractCallResult(contractFunctionResult.toProtobuf()); + } + + if (scheduleRef != null) { + transactionRecord.setScheduleRef(scheduleRef.toProtobuf()); + } + + for (var fee : assessedCustomFees) { + transactionRecord.addAssessedCustomFees(fee.toProtobuf()); + } + + for (var tokenAssociation : automaticTokenAssociations) { + transactionRecord.addAutomaticTokenAssociations(tokenAssociation.toProtobuf()); + } + + if (aliasKey != null) { + transactionRecord.setAlias(aliasKey.toProtobufKey().toByteString()); + } + + if (parentConsensusTimestamp != null) { + transactionRecord.setParentConsensusTimestamp(InstantConverter.toProtobuf(parentConsensusTimestamp)); + } + + for (Transfer reward : paidStakingRewards) { + transactionRecord.addPaidStakingRewards(reward.toProtobuf()); + } + + if (prngBytes != null) { + transactionRecord.setPrngBytes(prngBytes); + } + + if (prngNumber != null) { + transactionRecord.setPrngNumber(prngNumber); + } + + if (pendingAirdropRecords != null) { + for (PendingAirdropRecord pendingAirdropRecord : pendingAirdropRecords) { + transactionRecord.addNewPendingAirdrops( + pendingAirdropRecords.indexOf(pendingAirdropRecord), pendingAirdropRecord.toProtobuf()); + } + } + + return transactionRecord.build(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("receipt", receipt) + .add("transactionHash", Hex.toHexString(transactionHash.toByteArray())) + .add("consensusTimestamp", consensusTimestamp) + .add("transactionId", transactionId) + .add("transactionMemo", transactionMemo) + .add("transactionFee", transactionFee) + .add("contractFunctionResult", contractFunctionResult) + .add("transfers", transfers) + .add("tokenTransfers", tokenTransfers) + .add("tokenNftTransfers", tokenNftTransfers) + .add("scheduleRef", scheduleRef) + .add("assessedCustomFees", assessedCustomFees) + .add("automaticTokenAssociations", automaticTokenAssociations) + .add("aliasKey", aliasKey) + .add("children", children) + .add("duplicates", duplicates) + .add("parentConsensusTimestamp", parentConsensusTimestamp) + .add("ethereumHash", Hex.toHexString(ethereumHash.toByteArray())) + .add("paidStakingRewards", paidStakingRewards) + .add("prngBytes", prngBytes != null ? Hex.toHexString(prngBytes.toByteArray()) : null) + .add("prngNumber", prngNumber) + .add("evmAddress", Hex.toHexString(evmAddress.toByteArray())) + .add("pendingAirdropRecords", pendingAirdropRecords.toString()) + .toString(); + } + + /** + * Create the byte array. + * + * @return the byte array representation + */ + public byte[] toBytes() { + return toProtobuf().toByteArray(); + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TransactionRecordQuery.java b/sdk/src/main/java/org/hiero/sdk/TransactionRecordQuery.java similarity index 76% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/TransactionRecordQuery.java rename to sdk/src/main/java/org/hiero/sdk/TransactionRecordQuery.java index 2ee0055881..beb15e1e86 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TransactionRecordQuery.java +++ b/sdk/src/main/java/org/hiero/sdk/TransactionRecordQuery.java @@ -1,35 +1,16 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; -import com.hedera.hashgraph.sdk.proto.CryptoServiceGrpc; -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import com.hedera.hashgraph.sdk.proto.Response; -import com.hedera.hashgraph.sdk.proto.ResponseHeader; -import com.hedera.hashgraph.sdk.proto.TransactionGetRecordQuery; import io.grpc.MethodDescriptor; - -import javax.annotation.Nullable; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.CryptoServiceGrpc; +import org.hiero.sdk.proto.QueryHeader; +import org.hiero.sdk.proto.Response; +import org.hiero.sdk.proto.ResponseHeader; +import org.hiero.sdk.proto.TransactionGetRecordQuery; /** * Get the record for a transaction. @@ -44,14 +25,14 @@ public final class TransactionRecordQuery extends Query { @Nullable private TransactionId transactionId = null; + private boolean includeChildren = false; private boolean includeDuplicates = false; /** * Constructor. */ - public TransactionRecordQuery() { - } + public TransactionRecordQuery() {} /** * Extract the transaction id. @@ -129,10 +110,10 @@ void validateChecksums(Client client) throws BadEntityIdException { } @Override - void onMakeRequest(com.hedera.hashgraph.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { + void onMakeRequest(org.hiero.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { var builder = TransactionGetRecordQuery.newBuilder() - .setIncludeChildRecords(includeChildren) - .setIncludeDuplicates(includeDuplicates); + .setIncludeChildRecords(includeChildren) + .setIncludeDuplicates(includeDuplicates); if (transactionId != null) { builder.setTransactionID(transactionId.toProtobuf()); } @@ -146,21 +127,20 @@ ResponseHeader mapResponseHeader(Response response) { } @Override - QueryHeader mapRequestHeader(com.hedera.hashgraph.sdk.proto.Query request) { + QueryHeader mapRequestHeader(org.hiero.sdk.proto.Query request) { return request.getTransactionGetRecord().getHeader(); } @Override - TransactionRecord mapResponse(Response response, AccountId nodeId, com.hedera.hashgraph.sdk.proto.Query request) { + TransactionRecord mapResponse(Response response, AccountId nodeId, org.hiero.sdk.proto.Query request) { var recordResponse = response.getTransactionGetRecord(); List children = mapRecordList(recordResponse.getChildTransactionRecordsList()); List duplicates = mapRecordList(recordResponse.getDuplicateTransactionRecordsList()); - return TransactionRecord.fromProtobuf(recordResponse.getTransactionRecord(), children, duplicates, transactionId); + return TransactionRecord.fromProtobuf( + recordResponse.getTransactionRecord(), children, duplicates, transactionId); } - private List mapRecordList( - List protoRecordList - ) { + private List mapRecordList(List protoRecordList) { List outList = new ArrayList<>(protoRecordList.size()); for (var protoRecord : protoRecordList) { outList.add(TransactionRecord.fromProtobuf(protoRecord)); @@ -169,7 +149,7 @@ private List mapRecordList( } @Override - MethodDescriptor getMethodDescriptor() { + MethodDescriptor getMethodDescriptor() { return CryptoServiceGrpc.getGetTxRecordByTxIDMethod(); } @@ -197,8 +177,10 @@ ExecutionState getExecutionState(Status status, Response response) { return ExecutionState.REQUEST_ERROR; } - var receiptStatus = - Status.valueOf(response.getTransactionGetRecord().getTransactionRecord().getReceipt().getStatus()); + var receiptStatus = Status.valueOf(response.getTransactionGetRecord() + .getTransactionRecord() + .getReceipt() + .getStatus()); switch (receiptStatus) { case BUSY: diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TransactionResponse.java b/sdk/src/main/java/org/hiero/sdk/TransactionResponse.java similarity index 80% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/TransactionResponse.java rename to sdk/src/main/java/org/hiero/sdk/TransactionResponse.java index 3b0bb1c815..85396e8f7b 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TransactionResponse.java +++ b/sdk/src/main/java/org/hiero/sdk/TransactionResponse.java @@ -1,23 +1,5 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.google.common.base.MoreObjects; import java.time.Duration; @@ -73,11 +55,10 @@ public final class TransactionResponse { * @param scheduledTransactionId the scheduled transaction id */ TransactionResponse( - AccountId nodeId, - TransactionId transactionId, - byte[] transactionHash, - @Nullable TransactionId scheduledTransactionId - ) { + AccountId nodeId, + TransactionId transactionId, + byte[] transactionHash, + @Nullable TransactionId scheduledTransactionId) { this.nodeId = nodeId; this.transactionId = transactionId; this.transactionHash = transactionHash; @@ -111,7 +92,8 @@ public TransactionResponse setValidateStatus(boolean validateStatus) { * @throws PrecheckStatusException when the precheck fails * @throws ReceiptStatusException when there is an issue with the receipt */ - public TransactionReceipt getReceipt(Client client) throws TimeoutException, PrecheckStatusException, ReceiptStatusException { + public TransactionReceipt getReceipt(Client client) + throws TimeoutException, PrecheckStatusException, ReceiptStatusException { return getReceipt(client, client.getRequestTimeout()); } @@ -125,10 +107,9 @@ public TransactionReceipt getReceipt(Client client) throws TimeoutException, Pre * @throws PrecheckStatusException when the precheck fails * @throws ReceiptStatusException when there is an issue with the receipt */ - public TransactionReceipt getReceipt(Client client, Duration timeout) throws TimeoutException, PrecheckStatusException, ReceiptStatusException { - var receipt = getReceiptQuery() - .execute(client, timeout) - .validateStatus(validateStatus); + public TransactionReceipt getReceipt(Client client, Duration timeout) + throws TimeoutException, PrecheckStatusException, ReceiptStatusException { + var receipt = getReceiptQuery().execute(client, timeout).validateStatus(validateStatus); return receipt; } @@ -136,12 +117,12 @@ public TransactionReceipt getReceipt(Client client, Duration timeout) throws Tim /** * Create receipt query from the {@link #transactionId} and {@link #transactionHash} * - * @return {@link com.hedera.hashgraph.sdk.TransactionReceiptQuery} + * @return {@link org.hiero.sdk.TransactionReceiptQuery} */ public TransactionReceiptQuery getReceiptQuery() { return new TransactionReceiptQuery() - .setTransactionId(transactionId) - .setNodeAccountIds(Collections.singletonList(nodeId)); + .setTransactionId(transactionId) + .setNodeAccountIds(Collections.singletonList(nodeId)); } /** @@ -162,15 +143,13 @@ public CompletableFuture getReceiptAsync(Client client) { * @return the transaction receipt */ public CompletableFuture getReceiptAsync(Client client, Duration timeout) { - return getReceiptQuery() - .executeAsync(client, timeout) - .thenCompose(receipt -> { - try { - return CompletableFuture.completedFuture(receipt.validateStatus(validateStatus)); - } catch (ReceiptStatusException e) { - return CompletableFuture.failedFuture(e); - } - }); + return getReceiptQuery().executeAsync(client, timeout).thenCompose(receipt -> { + try { + return CompletableFuture.completedFuture(receipt.validateStatus(validateStatus)); + } catch (ReceiptStatusException e) { + return CompletableFuture.failedFuture(e); + } + }); } /** @@ -213,7 +192,8 @@ public void getReceiptAsync(Client client, Consumer onSucces * @param onSuccess a Consumer which consumes the result on success. * @param onFailure a Consumer which consumes the error on failure. */ - public void getReceiptAsync(Client client, Duration timeout, Consumer onSuccess, Consumer onFailure) { + public void getReceiptAsync( + Client client, Duration timeout, Consumer onSuccess, Consumer onFailure) { ConsumerHelper.twoConsumers(getReceiptAsync(client, timeout), onSuccess, onFailure); } @@ -226,7 +206,8 @@ public void getReceiptAsync(Client client, Duration timeout, Consumer getRecordAsync(Client client) { * @return future result of the transaction record */ public CompletableFuture getRecordAsync(Client client, Duration timeout) { - return getReceiptAsync(client, timeout).thenCompose((receipt) -> getRecordQuery().executeAsync(client, timeout)); + return getReceiptAsync(client, timeout) + .thenCompose((receipt) -> getRecordQuery().executeAsync(client, timeout)); } /** @@ -317,16 +300,17 @@ public void getRecordAsync(Client client, Consumer onSuccess, * @param onSuccess a Consumer which consumes the result on success. * @param onFailure a Consumer which consumes the error on failure. */ - public void getRecordAsync(Client client, Duration timeout, Consumer onSuccess, Consumer onFailure) { + public void getRecordAsync( + Client client, Duration timeout, Consumer onSuccess, Consumer onFailure) { ConsumerHelper.twoConsumers(getRecordAsync(client, timeout), onSuccess, onFailure); } @Override public String toString() { return MoreObjects.toStringHelper(this) - .add("nodeId", nodeId) - .add("transactionHash", Hex.toHexString(transactionHash)) - .add("transactionId", transactionId) - .toString(); + .add("nodeId", nodeId) + .add("transactionHash", Hex.toHexString(transactionHash)) + .add("transactionId", transactionId) + .toString(); } } diff --git a/sdk/src/main/java/org/hiero/sdk/Transfer.java b/sdk/src/main/java/org/hiero/sdk/Transfer.java new file mode 100644 index 0000000000..62e32693ce --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/Transfer.java @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import org.hiero.sdk.proto.AccountAmount; + +/** + * A transfer of Hbar that occurred within a transaction. + *

+ * Returned with a {@link TransactionRecord}. + */ +public final class Transfer { + /** + * The Account ID that sends or receives crypto-currency. + */ + public final AccountId accountId; + + /** + * The amount that the account sends (negative) or receives (positive). + */ + public final Hbar amount; + + Transfer(AccountId accountId, Hbar amount) { + this.accountId = accountId; + this.amount = amount; + } + + /** + * Create a transfer from a protobuf. + * + * @param accountAmount the protobuf + * @return the new transfer + */ + static Transfer fromProtobuf(AccountAmount accountAmount) { + return new Transfer( + AccountId.fromProtobuf(accountAmount.getAccountID()), Hbar.fromTinybars(accountAmount.getAmount())); + } + + /** + * Create the protobuf. + * + * @return the protobuf representation + */ + AccountAmount toProtobuf() { + return AccountAmount.newBuilder() + .setAccountID(accountId.toProtobuf()) + .setAmount(amount.toTinybars()) + .build(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("accountId", accountId) + .add("amount", amount) + .toString(); + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/TransferTransaction.java b/sdk/src/main/java/org/hiero/sdk/TransferTransaction.java new file mode 100644 index 0000000000..1ee38bd956 --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/TransferTransaction.java @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.common.base.MoreObjects; +import com.google.protobuf.InvalidProtocolBufferException; +import io.grpc.MethodDescriptor; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import org.hiero.sdk.proto.AccountAmount; +import org.hiero.sdk.proto.CryptoServiceGrpc; +import org.hiero.sdk.proto.CryptoTransferTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; +import org.hiero.sdk.proto.TransferList; + +/** + * A transaction that transfers hbars and tokens between Hedera accounts. You can enter multiple transfers in a single + * transaction. The net value of hbars between the sending accounts and receiving accounts must equal zero. + *

+ * See Hedera + * Documentation + */ +public class TransferTransaction extends AbstractTokenTransferTransaction { + private final ArrayList hbarTransfers = new ArrayList<>(); + + private static class HbarTransfer { + final AccountId accountId; + Hbar amount; + boolean isApproved; + + HbarTransfer(AccountId accountId, Hbar amount, boolean isApproved) { + this.accountId = accountId; + this.amount = amount; + this.isApproved = isApproved; + } + + AccountAmount toProtobuf() { + return AccountAmount.newBuilder() + .setAccountID(accountId.toProtobuf()) + .setAmount(amount.toTinybars()) + .setIsApproval(isApproved) + .build(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("accountId", accountId) + .add("amount", amount) + .add("isApproved", isApproved) + .toString(); + } + } + + /** + * Constructor. + */ + public TransferTransaction() { + defaultMaxTransactionFee = new Hbar(1); + } + + /** + * Constructor. + * + * @param txs Compound list of transaction id's list of (AccountId, Transaction) records + * @throws InvalidProtocolBufferException when there is an issue with the protobuf + */ + TransferTransaction(LinkedHashMap> txs) + throws InvalidProtocolBufferException { + super(txs); + initFromTransactionBody(); + } + + /** + * Constructor. + * + * @param txBody protobuf TransactionBody + */ + TransferTransaction(org.hiero.sdk.proto.TransactionBody txBody) { + super(txBody); + initFromTransactionBody(); + } + + /** + * Extract the of hbar transfers. + * + * @return list of hbar transfers + */ + public Map getHbarTransfers() { + Map transfers = new HashMap<>(); + + for (var transfer : hbarTransfers) { + transfers.put(transfer.accountId, transfer.amount); + } + + return transfers; + } + + private TransferTransaction doAddHbarTransfer(AccountId accountId, Hbar value, boolean isApproved) { + requireNotFrozen(); + + for (var transfer : hbarTransfers) { + if (transfer.accountId.equals(accountId) && transfer.isApproved == isApproved) { + transfer.amount = Hbar.fromTinybars(transfer.amount.toTinybars() + value.toTinybars()); + return this; + } + } + + hbarTransfers.add(new HbarTransfer(accountId, value, isApproved)); + return this; + } + + /** + * Add a non approved hbar transfer to an EVM address. + * + * @param evmAddress the EVM address + * @param value the value + * @return the updated transaction + */ + public TransferTransaction addHbarTransfer(EvmAddress evmAddress, Hbar value) { + AccountId accountId = AccountId.fromEvmAddress(evmAddress); + return doAddHbarTransfer(accountId, value, false); + } + + /** + * Add a non approved hbar transfer. + * + * @param accountId the account id + * @param value the value + * @return the updated transaction + */ + public TransferTransaction addHbarTransfer(AccountId accountId, Hbar value) { + return doAddHbarTransfer(accountId, value, false); + } + + /** + * Add an approved hbar transfer. + * + * @param accountId the account id + * @param value the value + * @return the updated transaction + */ + public TransferTransaction addApprovedHbarTransfer(AccountId accountId, Hbar value) { + return doAddHbarTransfer(accountId, value, true); + } + + /** + * @param accountId the account id + * @param isApproved whether the transfer is approved + * @return {@code this} + * @deprecated - Use {@link #addApprovedHbarTransfer(AccountId, Hbar)} instead + */ + @Deprecated + public TransferTransaction setHbarTransferApproval(AccountId accountId, boolean isApproved) { + requireNotFrozen(); + + for (var transfer : hbarTransfers) { + if (transfer.accountId.equals(accountId)) { + transfer.isApproved = isApproved; + return this; + } + } + + return this; + } + + /** + * Build the transaction body. + * + * @return {@link org.hiero.sdk.proto.CryptoTransferTransactionBody} + */ + CryptoTransferTransactionBody.Builder build() { + var transfers = sortTransfersAndBuild(); + + var builder = CryptoTransferTransactionBody.newBuilder(); + + this.hbarTransfers.sort( + Comparator.comparing((HbarTransfer a) -> a.accountId).thenComparing(a -> a.isApproved)); + var hbarTransfersList = TransferList.newBuilder(); + for (var transfer : hbarTransfers) { + hbarTransfersList.addAccountAmounts(transfer.toProtobuf()); + } + builder.setTransfers(hbarTransfersList); + + for (var transfer : transfers) { + builder.addTokenTransfers(transfer.toProtobuf()); + } + + return builder; + } + + @Override + void validateChecksums(Client client) throws BadEntityIdException { + super.validateChecksums(client); + for (var transfer : hbarTransfers) { + transfer.accountId.validateChecksum(client); + } + } + + @Override + MethodDescriptor getMethodDescriptor() { + return CryptoServiceGrpc.getCryptoTransferMethod(); + } + + @Override + void onFreeze(TransactionBody.Builder bodyBuilder) { + bodyBuilder.setCryptoTransfer(build()); + } + + @Override + void onScheduled(SchedulableTransactionBody.Builder scheduled) { + scheduled.setCryptoTransfer(build()); + } + + /** + * Initialize from the transaction body. + */ + void initFromTransactionBody() { + var body = sourceTransactionBody.getCryptoTransfer(); + + for (var transfer : body.getTransfers().getAccountAmountsList()) { + hbarTransfers.add(new HbarTransfer( + AccountId.fromProtobuf(transfer.getAccountID()), + Hbar.fromTinybars(transfer.getAmount()), + transfer.getIsApproval())); + } + + for (var tokenTransferList : body.getTokenTransfersList()) { + var token = TokenId.fromProtobuf(tokenTransferList.getToken()); + + for (var transfer : tokenTransferList.getTransfersList()) { + tokenTransfers.add(new TokenTransfer( + token, + AccountId.fromProtobuf(transfer.getAccountID()), + transfer.getAmount(), + tokenTransferList.hasExpectedDecimals() + ? tokenTransferList.getExpectedDecimals().getValue() + : null, + transfer.getIsApproval())); + } + + for (var transfer : tokenTransferList.getNftTransfersList()) { + nftTransfers.add(new TokenNftTransfer( + token, + AccountId.fromProtobuf(transfer.getSenderAccountID()), + AccountId.fromProtobuf(transfer.getReceiverAccountID()), + transfer.getSerialNumber(), + transfer.getIsApproval())); + } + } + } +} diff --git a/sdk/src/main/java/org/hiero/sdk/logger/LogLevel.java b/sdk/src/main/java/org/hiero/sdk/logger/LogLevel.java new file mode 100644 index 0000000000..1c0819050c --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/logger/LogLevel.java @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.logger; + +public enum LogLevel { + TRACE(0), + DEBUG(1), + INFO(2), + WARN(3), + ERROR(4), + SILENT(5); + + private final int levelInt; + + LogLevel(int i) { + this.levelInt = i; + } + + public int toInt() { + return levelInt; + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/logger/Logger.java b/sdk/src/main/java/org/hiero/sdk/logger/Logger.java similarity index 83% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/logger/Logger.java rename to sdk/src/main/java/org/hiero/sdk/logger/Logger.java index e3a43a2f05..3255625127 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/logger/Logger.java +++ b/sdk/src/main/java/org/hiero/sdk/logger/Logger.java @@ -1,23 +1,5 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.logger; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.logger; import org.slf4j.LoggerFactory; diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/package-info.java b/sdk/src/main/java/org/hiero/sdk/package-info.java similarity index 96% rename from sdk/src/main/java/com/hedera/hashgraph/sdk/package-info.java rename to sdk/src/main/java/org/hiero/sdk/package-info.java index b5c2338bd0..b7076599cb 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/package-info.java +++ b/sdk/src/main/java/org/hiero/sdk/package-info.java @@ -19,6 +19,6 @@ * See Hedera Documentation */ @ParametersAreNonnullByDefault -package com.hedera.hashgraph.sdk; +package org.hiero.sdk; import javax.annotation.ParametersAreNonnullByDefault; diff --git a/sdk/src/main/java/org/hiero/sdk/utils/Bip32Utils.java b/sdk/src/main/java/org/hiero/sdk/utils/Bip32Utils.java new file mode 100644 index 0000000000..626b37feff --- /dev/null +++ b/sdk/src/main/java/org/hiero/sdk/utils/Bip32Utils.java @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.utils; + +/** + * Utility class for BIP32 functionalities + */ +public class Bip32Utils { + + /** + * Indicates if the index is hardened + */ + public static final int HARDENED_BIT = 0x80000000; + + private Bip32Utils() { + throw new IllegalStateException("Utility class"); + } + + /** + * Harden the index + * + * @param index the derivation index + * @return the hardened index + */ + public static int toHardenedIndex(int index) { + return index | HARDENED_BIT; + } + + /** + * Check if the index is hardened + * + * @param index the derivation index + * @return true if the index is hardened + */ + public static boolean isHardenedIndex(int index) { + return (index & HARDENED_BIT) != 0; + } +} diff --git a/sdk/src/main/proto/account.proto b/sdk/src/main/proto/account.proto index 3c7cfce552..77ff83462f 100644 --- a/sdk/src/main/proto/account.proto +++ b/sdk/src/main/proto/account.proto @@ -25,7 +25,7 @@ package proto; import "common.proto"; import "basic_types.proto"; -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/account_pending_airdrop.proto b/sdk/src/main/proto/account_pending_airdrop.proto index 6b2c7b47b9..b231dfc910 100644 --- a/sdk/src/main/proto/account_pending_airdrop.proto +++ b/sdk/src/main/proto/account_pending_airdrop.proto @@ -20,7 +20,7 @@ package proto; import "basic_types.proto"; -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/address_book_service.proto b/sdk/src/main/proto/address_book_service.proto index d3cde5733e..b84f1bb3e4 100644 --- a/sdk/src/main/proto/address_book_service.proto +++ b/sdk/src/main/proto/address_book_service.proto @@ -18,7 +18,7 @@ package proto; * limitations under the License. */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package import "query.proto"; diff --git a/sdk/src/main/proto/basic_types.proto b/sdk/src/main/proto/basic_types.proto index 282890f95c..2fb7723e63 100644 --- a/sdk/src/main/proto/basic_types.proto +++ b/sdk/src/main/proto/basic_types.proto @@ -25,7 +25,7 @@ package proto; import "timestamp.proto"; import "google/protobuf/wrappers.proto"; -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/block_info.proto b/sdk/src/main/proto/block_info.proto index cdd0508d67..79482ecd1a 100644 --- a/sdk/src/main/proto/block_info.proto +++ b/sdk/src/main/proto/block_info.proto @@ -24,7 +24,7 @@ package proto; import "timestamp.proto"; -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/block_stream_info.proto b/sdk/src/main/proto/block_stream_info.proto index cf3f36809b..889595f3f5 100644 --- a/sdk/src/main/proto/block_stream_info.proto +++ b/sdk/src/main/proto/block_stream_info.proto @@ -12,7 +12,7 @@ */ syntax = "proto3"; -package com.hedera.hapi.node.state.blockstream; +package org.hiero.hapi.node.state.blockstream; /* * Copyright (C) 2024 Hedera Hashgraph, LLC @@ -33,7 +33,7 @@ package com.hedera.hapi.node.state.blockstream; import "timestamp.proto"; import "basic_types.proto"; -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/bytecode.proto b/sdk/src/main/proto/bytecode.proto index 8652fce947..ead12b2e2d 100644 --- a/sdk/src/main/proto/bytecode.proto +++ b/sdk/src/main/proto/bytecode.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/common.proto b/sdk/src/main/proto/common.proto index 5eb76044a3..5d82e17012 100644 --- a/sdk/src/main/proto/common.proto +++ b/sdk/src/main/proto/common.proto @@ -23,7 +23,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/congestion_level_starts.proto b/sdk/src/main/proto/congestion_level_starts.proto index d9a3ac25a6..7f0ce23799 100644 --- a/sdk/src/main/proto/congestion_level_starts.proto +++ b/sdk/src/main/proto/congestion_level_starts.proto @@ -24,7 +24,7 @@ package proto; import "timestamp.proto"; -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/consensus_create_topic.proto b/sdk/src/main/proto/consensus_create_topic.proto index cd2e468777..00158b559e 100644 --- a/sdk/src/main/proto/consensus_create_topic.proto +++ b/sdk/src/main/proto/consensus_create_topic.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/consensus_delete_topic.proto b/sdk/src/main/proto/consensus_delete_topic.proto index 6b68113fd5..b48c6b9aeb 100644 --- a/sdk/src/main/proto/consensus_delete_topic.proto +++ b/sdk/src/main/proto/consensus_delete_topic.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/consensus_get_topic_info.proto b/sdk/src/main/proto/consensus_get_topic_info.proto index 408641f197..93ca19f138 100644 --- a/sdk/src/main/proto/consensus_get_topic_info.proto +++ b/sdk/src/main/proto/consensus_get_topic_info.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/consensus_service.proto b/sdk/src/main/proto/consensus_service.proto index da1bfbf9c9..27a1432404 100644 --- a/sdk/src/main/proto/consensus_service.proto +++ b/sdk/src/main/proto/consensus_service.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package import "query.proto"; diff --git a/sdk/src/main/proto/consensus_submit_message.proto b/sdk/src/main/proto/consensus_submit_message.proto index ece3155e98..abc408f1bc 100644 --- a/sdk/src/main/proto/consensus_submit_message.proto +++ b/sdk/src/main/proto/consensus_submit_message.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/consensus_topic_info.proto b/sdk/src/main/proto/consensus_topic_info.proto index c31b2d6255..eaa0436e09 100644 --- a/sdk/src/main/proto/consensus_topic_info.proto +++ b/sdk/src/main/proto/consensus_topic_info.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -44,7 +44,7 @@ message ConsensusTopicInfo { * When a topic is created, its running hash is initialized to 48 bytes of binary zeros. * For each submitted message, the topic's running hash is then updated to the output * of a particular SHA-384 digest whose input data include the previous running hash. - * + * * See the TransactionReceipt.proto documentation for an exact description of the * data included in the SHA-384 digest used for the update. */ @@ -84,7 +84,7 @@ message ConsensusTopicInfo { AccountID autoRenewAccount = 8; /** - * The ledger ID the response was returned from; please see HIP-198 for the network-specific IDs. + * The ledger ID the response was returned from; please see HIP-198 for the network-specific IDs. */ bytes ledger_id = 9; } diff --git a/sdk/src/main/proto/consensus_update_topic.proto b/sdk/src/main/proto/consensus_update_topic.proto index 7a1acf9c46..3479b279bd 100644 --- a/sdk/src/main/proto/consensus_update_topic.proto +++ b/sdk/src/main/proto/consensus_update_topic.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/contract_call.proto b/sdk/src/main/proto/contract_call.proto index 9000e339ca..c70e5ccabc 100644 --- a/sdk/src/main/proto/contract_call.proto +++ b/sdk/src/main/proto/contract_call.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/contract_call_local.proto b/sdk/src/main/proto/contract_call_local.proto index 2fd51a5b35..94197cfe58 100644 --- a/sdk/src/main/proto/contract_call_local.proto +++ b/sdk/src/main/proto/contract_call_local.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -98,11 +98,11 @@ message ContractFunctionResult { /** * [DEPRECATED] the list of smart contracts that were created by the function call. - * - * The created ids will now _also_ be externalized through internal transaction - * records, where each record has its alias field populated with the new contract's - * EVM address. (This is needed for contracts created with CREATE2, since - * there is no longer a simple relationship between the new contract's 0.0.X id + * + * The created ids will now _also_ be externalized through internal transaction + * records, where each record has its alias field populated with the new contract's + * EVM address. (This is needed for contracts created with CREATE2, since + * there is no longer a simple relationship between the new contract's 0.0.X id * and its Solidity address.) */ repeated ContractID createdContractIDs = 7 [deprecated=true]; @@ -110,33 +110,33 @@ message ContractFunctionResult { reserved 8; /** - * The new contract's 20-byte EVM address. Only populated after release 0.23, - * where each created contract will have its own record. (This is an important - * point--the field is not repeated because there will be a separate + * The new contract's 20-byte EVM address. Only populated after release 0.23, + * where each created contract will have its own record. (This is an important + * point--the field is not repeated because there will be a separate * child record for each created contract.) - * + * * Every contract has an EVM address determined by its shard.realm.num id. * This address is as follows: *

    *
  1. The first 4 bytes are the big-endian representation of the shard.
  2. *
  3. The next 8 bytes are the big-endian representation of the realm.
  4. *
  5. The final 8 bytes are the big-endian representation of the number.
  6. - *
- * - * Contracts created via CREATE2 have an additional, primary address that is - * derived from the EIP-1014 - * specification, and does not have a simple relation to a shard.realm.num id. - * - * (Please do note that CREATE2 contracts can also be referenced by the three-part + * + * + * Contracts created via CREATE2 have an additional, primary address that is + * derived from the EIP-1014 + * specification, and does not have a simple relation to a shard.realm.num id. + * + * (Please do note that CREATE2 contracts can also be referenced by the three-part * EVM address described above.) */ google.protobuf.BytesValue evm_address = 9; /** - * The amount of gas available for the call, aka the gasLimit. + * The amount of gas available for the call, aka the gasLimit. * * This field should only be populated when the paired TransactionBody in the record stream is not a - * ContractCreateTransactionBody or a ContractCallTransactionBody. + * ContractCreateTransactionBody or a ContractCallTransactionBody. */ int64 gas = 10; @@ -144,7 +144,7 @@ message ContractFunctionResult { * Number of tinybars sent (the function must be payable if this is nonzero). * * This field should only be populated when the paired TransactionBody in the record stream is not a - * ContractCreateTransactionBody or a ContractCallTransactionBody. + * ContractCreateTransactionBody or a ContractCallTransactionBody. */ int64 amount = 11; @@ -152,7 +152,7 @@ message ContractFunctionResult { * The parameters passed into the contract call. * * This field should only be populated when the paired TransactionBody in the record stream is not a - * ContractCreateTransactionBody or a ContractCallTransactionBody. + * ContractCreateTransactionBody or a ContractCallTransactionBody. */ bytes functionParameters = 12; @@ -160,7 +160,7 @@ message ContractFunctionResult { * The account that is the "sender." If not present it is the accountId from the transactionId. * * This field should only be populated when the paired TransactionBody in the record stream is not a - * ContractCreateTransactionBody or a ContractCallTransactionBody. + * ContractCreateTransactionBody or a ContractCallTransactionBody. */ AccountID sender_id = 13; @@ -184,7 +184,7 @@ message ContractFunctionResult { * It will not have a consensus timestamp. It cannot generate a record or a receipt. The response will contain the output * returned by the function call. This is useful for calling getter functions, which purely read the state and don't change it. * It is faster and cheaper than a normal call, because it is purely local to a single node. - * + * * Unlike a ContractCall transaction, the node will consume the entire amount of provided gas in determining * the fee for this query. */ @@ -217,7 +217,7 @@ message ContractCallLocalQuery { /** * The account that is the "sender." If not present it is the accountId from the transactionId. - * Typically a different value than specified in the transactionId requires a valid signature + * Typically a different value than specified in the transactionId requires a valid signature * over either the hedera transaction or foreign transaction data. */ AccountID sender_id = 6; diff --git a/sdk/src/main/proto/contract_create.proto b/sdk/src/main/proto/contract_create.proto index b2aea7fa92..fe55ab1e22 100644 --- a/sdk/src/main/proto/contract_create.proto +++ b/sdk/src/main/proto/contract_create.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -32,10 +32,10 @@ import "duration.proto"; /** * Start a new smart contract instance. After the instance is created, the ContractID for it is in * the receipt, and can be retrieved by the Record or with a GetByKey query. The instance will run - * the bytecode, either stored in a previously created file or in the transaction body itself for + * the bytecode, either stored in a previously created file or in the transaction body itself for * small contracts. - * - * + * + * * The constructor will be executed using the given amount of gas, and any unspent gas will be * refunded to the paying account. Constructor inputs come from the given constructorParameters. * - The instance will exist for autoRenewPeriod seconds. When that is reached, it will renew @@ -82,7 +82,7 @@ import "duration.proto"; * to the smart contract in the future. The memo field can only be changed using the admin keys. * If there are no admin keys, then it cannot be changed after the smart contract is created. * - * Signing requirements: If an admin key is set, it must sign the transaction. If an + * Signing requirements: If an admin key is set, it must sign the transaction. If an * auto-renew account is set, its key must sign the transaction. */ message ContractCreateTransactionBody { diff --git a/sdk/src/main/proto/contract_delete.proto b/sdk/src/main/proto/contract_delete.proto index 2260a55eb1..1ad28dc4fb 100644 --- a/sdk/src/main/proto/contract_delete.proto +++ b/sdk/src/main/proto/contract_delete.proto @@ -21,7 +21,7 @@ package proto; * limitations under the License. * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -30,10 +30,10 @@ import "basic_types.proto"; /** * At consensus, marks a contract as deleted and transfers its remaining hBars, if any, to a * designated receiver. After a contract is deleted, it can no longer be called. - * + * * If the target contract is immutable (that is, was created without an admin key), then this * transaction resolves to MODIFYING_IMMUTABLE_CONTRACT. - * + * * --- Signing Requirements --- * 1. The admin key of the target contract must sign. * 2. If the transfer account or contract has receiverSigRequired, its associated key must also sign @@ -57,7 +57,7 @@ message ContractDeleteTransactionBody { } /** - * If set to true, means this is a "synthetic" system transaction being used to + * If set to true, means this is a "synthetic" system transaction being used to * alert mirror nodes that the contract is being permanently removed from the ledger. * IMPORTANT: User transactions cannot set this field to true, as permanent * removal is always managed by the ledger itself. Any ContractDeleteTransactionBody diff --git a/sdk/src/main/proto/contract_get_bytecode.proto b/sdk/src/main/proto/contract_get_bytecode.proto index ba6224ecc2..19bd364c0f 100644 --- a/sdk/src/main/proto/contract_get_bytecode.proto +++ b/sdk/src/main/proto/contract_get_bytecode.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/contract_get_info.proto b/sdk/src/main/proto/contract_get_info.proto index d9c16b4052..7014f22184 100644 --- a/sdk/src/main/proto/contract_get_info.proto +++ b/sdk/src/main/proto/contract_get_info.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -121,15 +121,15 @@ message ContractGetInfoResponse { bool deleted = 10; /** - * [DEPRECATED] The metadata of the tokens associated to the contract. This field was - * deprecated by HIP-367, which allowed - * an account to be associated to an unlimited number of tokens. This scale makes it more + * [DEPRECATED] The metadata of the tokens associated to the contract. This field was + * deprecated by HIP-367, which allowed + * an account to be associated to an unlimited number of tokens. This scale makes it more * efficient for users to consult mirror nodes to review their token associations. */ repeated TokenRelationship tokenRelationships = 11 [deprecated = true]; /** - * The ledger ID the response was returned from; please see HIP-198 for the network-specific IDs. + * The ledger ID the response was returned from; please see HIP-198 for the network-specific IDs. */ bytes ledger_id = 12; diff --git a/sdk/src/main/proto/contract_get_records.proto b/sdk/src/main/proto/contract_get_records.proto index b4d68fab9f..e9a9e0250a 100644 --- a/sdk/src/main/proto/contract_get_records.proto +++ b/sdk/src/main/proto/contract_get_records.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/contract_types.proto b/sdk/src/main/proto/contract_types.proto index b345d239ea..a938d149f2 100644 --- a/sdk/src/main/proto/contract_types.proto +++ b/sdk/src/main/proto/contract_types.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -44,4 +44,4 @@ message ContractNonceInfo { * The current value of the contract account's nonce property */ int64 nonce = 2; -} \ No newline at end of file +} diff --git a/sdk/src/main/proto/contract_update.proto b/sdk/src/main/proto/contract_update.proto index d54fec5a96..9284336957 100644 --- a/sdk/src/main/proto/contract_update.proto +++ b/sdk/src/main/proto/contract_update.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -33,12 +33,12 @@ import "google/protobuf/wrappers.proto"; /** * At consensus, updates the fields of a smart contract to the given values. - * + * * If no value is given for a field, that field is left unchanged on the contract. For an immutable * smart contract (that is, a contract created without an adminKey), only the expirationTime may be * updated; setting any other field in this case will cause the transaction status to resolve to * MODIFYING_IMMUTABLE_CONTRACT. - * + * * --- Signing Requirements --- * 1. Whether or not a contract has an admin key, its expiry can be extended with only the * transaction payer's signature. @@ -109,7 +109,7 @@ message ContractUpdateTransactionBody { google.protobuf.Int32Value max_automatic_token_associations = 11; /** - * If set to the sentinel 0.0.0 AccountID, this field removes the contract's auto-renew + * If set to the sentinel 0.0.0 AccountID, this field removes the contract's auto-renew * account. Otherwise it updates the contract's auto-renew account to the referenced account. */ AccountID auto_renew_account_id = 12; diff --git a/sdk/src/main/proto/crypto_add_live_hash.proto b/sdk/src/main/proto/crypto_add_live_hash.proto index 826dd3d165..40f96c2cb5 100644 --- a/sdk/src/main/proto/crypto_add_live_hash.proto +++ b/sdk/src/main/proto/crypto_add_live_hash.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -32,7 +32,7 @@ import "duration.proto"; /** * A hash---presumably of some kind of credential or certificate---along with a list of keys, each - * of which may be either a primitive or a threshold key. + * of which may be either a primitive or a threshold key. */ message LiveHash { /** diff --git a/sdk/src/main/proto/crypto_approve_allowance.proto b/sdk/src/main/proto/crypto_approve_allowance.proto index 4177788179..310740974d 100644 --- a/sdk/src/main/proto/crypto_approve_allowance.proto +++ b/sdk/src/main/proto/crypto_approve_allowance.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -35,7 +35,7 @@ import "google/protobuf/wrappers.proto"; * hbar/token to any other account of the spender's choice. If the owner is not specified in any allowance, the payer * of transaction is considered to be the owner for that particular allowance. * Setting the amount to zero in CryptoAllowance or TokenAllowance will remove the respective allowance for the spender. - * + * * (So if account 0.0.X pays for this transaction and owner is not specified in the allowance, * then at consensus each spender account will have new allowances to spend hbar or tokens from 0.0.X). */ diff --git a/sdk/src/main/proto/crypto_create.proto b/sdk/src/main/proto/crypto_create.proto index 5281a65918..efcb9b4428 100644 --- a/sdk/src/main/proto/crypto_create.proto +++ b/sdk/src/main/proto/crypto_create.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -46,7 +46,7 @@ import "duration.proto"; * used to extend its expiration as long as possible. If it is has a zero balance when it expires, * then it is deleted. This transaction must be signed by the payer account. If receiverSigRequired * is false, then the transaction does not have to be signed by the keys in the keys field. If it is - * true, then it must be signed by them, in addition to the keys of the payer account. If the + * true, then it must be signed by them, in addition to the keys of the payer account. If the * auto_renew_account field is set, the key of the referenced account must sign. * * An entity (account, file, or smart contract instance) must be created in a particular realm. If @@ -169,7 +169,7 @@ message CryptoCreateTransactionBody { * A given alias can map to at most one account on the network at a time. This uniqueness will be enforced * relative to aliases currently on the network at alias assignment. * - * If a transaction creates an account using an alias, any further crypto transfers to that alias will + * If a transaction creates an account using an alias, any further crypto transfers to that alias will * simply be deposited in that account, without creating anything, and with no creation fee being charged. */ bytes alias = 18; diff --git a/sdk/src/main/proto/crypto_delete.proto b/sdk/src/main/proto/crypto_delete.proto index 30719afe42..a1a993e316 100644 --- a/sdk/src/main/proto/crypto_delete.proto +++ b/sdk/src/main/proto/crypto_delete.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/crypto_delete_allowance.proto b/sdk/src/main/proto/crypto_delete_allowance.proto index 475df5f869..d75eb2dd99 100644 --- a/sdk/src/main/proto/crypto_delete_allowance.proto +++ b/sdk/src/main/proto/crypto_delete_allowance.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/crypto_delete_live_hash.proto b/sdk/src/main/proto/crypto_delete_live_hash.proto index 41ba5dfbc3..e8d436c702 100644 --- a/sdk/src/main/proto/crypto_delete_live_hash.proto +++ b/sdk/src/main/proto/crypto_delete_live_hash.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/crypto_get_account_balance.proto b/sdk/src/main/proto/crypto_get_account_balance.proto index 7f72512ddf..084148fa43 100644 --- a/sdk/src/main/proto/crypto_get_account_balance.proto +++ b/sdk/src/main/proto/crypto_get_account_balance.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -76,9 +76,9 @@ message CryptoGetAccountBalanceResponse { uint64 balance = 3; /** - * [DEPRECATED] The balances of the tokens associated to the account. This field was - * deprecated by HIP-367, which allowed - * an account to be associated to an unlimited number of tokens. This scale makes it more + * [DEPRECATED] The balances of the tokens associated to the account. This field was + * deprecated by HIP-367, which allowed + * an account to be associated to an unlimited number of tokens. This scale makes it more * efficient for users to consult mirror nodes to review their token balances. */ repeated TokenBalance tokenBalances = 4 [deprecated = true]; diff --git a/sdk/src/main/proto/crypto_get_account_records.proto b/sdk/src/main/proto/crypto_get_account_records.proto index 1832972490..949fe2d434 100644 --- a/sdk/src/main/proto/crypto_get_account_records.proto +++ b/sdk/src/main/proto/crypto_get_account_records.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/crypto_get_info.proto b/sdk/src/main/proto/crypto_get_info.proto index d217398315..fb4e17104b 100644 --- a/sdk/src/main/proto/crypto_get_info.proto +++ b/sdk/src/main/proto/crypto_get_info.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -139,9 +139,9 @@ message CryptoGetInfoResponse { repeated LiveHash liveHashes = 14; /** - * [DEPRECATED] The metadata of the tokens associated to the account. This field was - * deprecated by HIP-367, which allowed - * an account to be associated to an unlimited number of tokens. This scale makes it more + * [DEPRECATED] The metadata of the tokens associated to the account. This field was + * deprecated by HIP-367, which allowed + * an account to be associated to an unlimited number of tokens. This scale makes it more * efficient for users to consult mirror nodes to review their token associations. */ repeated TokenRelationship tokenRelationships = 15 [deprecated = true]; @@ -167,7 +167,7 @@ message CryptoGetInfoResponse { bytes alias = 19; /** - * The ledger ID the response was returned from; please see HIP-198 for the network-specific IDs. + * The ledger ID the response was returned from; please see HIP-198 for the network-specific IDs. */ bytes ledger_id = 20; diff --git a/sdk/src/main/proto/crypto_get_live_hash.proto b/sdk/src/main/proto/crypto_get_live_hash.proto index 1bf80ef324..a56042f766 100644 --- a/sdk/src/main/proto/crypto_get_live_hash.proto +++ b/sdk/src/main/proto/crypto_get_live_hash.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/crypto_get_stakers.proto b/sdk/src/main/proto/crypto_get_stakers.proto index 44f52eb5a0..763a00a365 100644 --- a/sdk/src/main/proto/crypto_get_stakers.proto +++ b/sdk/src/main/proto/crypto_get_stakers.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/crypto_service.proto b/sdk/src/main/proto/crypto_service.proto index 226943555f..295477437b 100644 --- a/sdk/src/main/proto/crypto_service.proto +++ b/sdk/src/main/proto/crypto_service.proto @@ -21,7 +21,7 @@ package proto; * limitations under the License. * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package import "query.proto"; diff --git a/sdk/src/main/proto/crypto_transfer.proto b/sdk/src/main/proto/crypto_transfer.proto index 5c25747e3d..f8a1896250 100644 --- a/sdk/src/main/proto/crypto_transfer.proto +++ b/sdk/src/main/proto/crypto_transfer.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -37,7 +37,7 @@ import "basic_types.proto"; * hbars, then the entire transaction fails, and none of those transfers occur, though the * transaction fee is still charged. This transaction must be signed by the keys for all the sending * accounts, and for any receiving accounts that have receiverSigRequired == true. The signatures - * are in the same order as the accounts, skipping those accounts that don't need a signature. + * are in the same order as the accounts, skipping those accounts that don't need a signature. */ message CryptoTransferTransactionBody { /** diff --git a/sdk/src/main/proto/crypto_update.proto b/sdk/src/main/proto/crypto_update.proto index c83e8a80a1..d234328226 100644 --- a/sdk/src/main/proto/crypto_update.proto +++ b/sdk/src/main/proto/crypto_update.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -36,8 +36,8 @@ import "google/protobuf/wrappers.proto"; * transaction must be signed by the existing key for this account. If the transaction is changing * the key field, then the transaction must be signed by both the old key (from before the change) * and the new key. The old key must sign for security. The new key must sign as a safeguard to - * avoid accidentally changing to an invalid key, and then having no way to recover. - * If the update transaction sets the auto_renew_account field to anything other + * avoid accidentally changing to an invalid key, and then having no way to recover. + * If the update transaction sets the auto_renew_account field to anything other * than the sentinel 0.0.0, the key of the referenced account must sign. */ message CryptoUpdateTransactionBody { diff --git a/sdk/src/main/proto/custom_fees.proto b/sdk/src/main/proto/custom_fees.proto index bda20b4680..6f49ebded7 100644 --- a/sdk/src/main/proto/custom_fees.proto +++ b/sdk/src/main/proto/custom_fees.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -81,20 +81,20 @@ message FixedFee { * any fungible value, the ledger will assess the fallback fee, if present, to the new NFT owner. * Royalty fees can only be added to tokens of type type NON_FUNGIBLE_UNIQUE. * - * **IMPORTANT:** Users must understand that native royalty fees are _strictly_ a convenience feature, - * and that the network cannot enforce inescapable royalties on the exchange of a non-fractional NFT. - * For example, if the counterparties agree to split their value transfer and NFT exchange into separate - * transactions, the network cannot possibly intervene. (And note the counterparties could use a smart + * **IMPORTANT:** Users must understand that native royalty fees are _strictly_ a convenience feature, + * and that the network cannot enforce inescapable royalties on the exchange of a non-fractional NFT. + * For example, if the counterparties agree to split their value transfer and NFT exchange into separate + * transactions, the network cannot possibly intervene. (And note the counterparties could use a smart * contract to make this split transaction atomic if they do not trust each other.) - * - * Counterparties that _do_ wish to respect creator royalties should follow the pattern the network - * recognizes: The NFT sender and receiver should both sign a single `CryptoTransfer` that credits + * + * Counterparties that _do_ wish to respect creator royalties should follow the pattern the network + * recognizes: The NFT sender and receiver should both sign a single `CryptoTransfer` that credits * the sender with all the fungible value the receiver is exchanging for the NFT. - * - * Similarly, a marketplace using an approved spender account for an escrow transaction should credit - * the account selling the NFT in the same `CryptoTransfer` that deducts fungible value from the buying - * account. - * + * + * Similarly, a marketplace using an approved spender account for an escrow transaction should credit + * the account selling the NFT in the same `CryptoTransfer` that deducts fungible value from the buying + * account. + * * There is an [open HIP discussion](https://github.com/hashgraph/hedera-improvement-proposal/discussions/578) * that proposes to broaden the class of transactions for which the network automatically collects * royalties. If this interests or concerns you, please add your voice to that discussion. @@ -143,7 +143,7 @@ message CustomFee { /** * If true, exempts all the token's fee collection accounts from this fee. * (The token's treasury and the above fee_collector_account_id will always - * be exempt. Please see HIP-573 + * be exempt. Please see HIP-573 * for details.) */ bool all_collectors_are_exempt = 5; diff --git a/sdk/src/main/proto/duration.proto b/sdk/src/main/proto/duration.proto index 0883f7d595..1665380874 100644 --- a/sdk/src/main/proto/duration.proto +++ b/sdk/src/main/proto/duration.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/ethereum_transaction.proto b/sdk/src/main/proto/ethereum_transaction.proto index dad33e10bc..31065f7ae6 100644 --- a/sdk/src/main/proto/ethereum_transaction.proto +++ b/sdk/src/main/proto/ethereum_transaction.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -31,33 +31,33 @@ import "basic_types.proto"; message EthereumTransactionBody { /** - * The raw Ethereum transaction (RLP encoded type 0, 1, and 2). Complete + * The raw Ethereum transaction (RLP encoded type 0, 1, and 2). Complete * unless the callData field is set. */ bytes ethereum_data = 1; /** * For large transactions (for example contract create) this is the callData - * of the ethereumData. The data in the ethereumData will be re-written with - * the callData element as a zero length string with the original contents in - * the referenced file at time of execution. The ethereumData will need to be + * of the ethereumData. The data in the ethereumData will be re-written with + * the callData element as a zero length string with the original contents in + * the referenced file at time of execution. The ethereumData will need to be * "rehydrated" with the callData for signature validation to pass. */ FileID call_data = 2; /** - * The maximum amount, in tinybars, that the payer of the hedera transaction + * The maximum amount, in tinybars, that the payer of the hedera transaction * is willing to pay to complete the transaction. * - * Ordinarily the account with the ECDSA alias corresponding to the public + * Ordinarily the account with the ECDSA alias corresponding to the public * key that is extracted from the ethereum_data signature is responsible for * fees that result from the execution of the transaction. If that amount of * authorized fees is not sufficient then the payer of the transaction can be - * charged, up to but not exceeding this amount. If the ethereum_data + * charged, up to but not exceeding this amount. If the ethereum_data * transaction authorized an amount that was insufficient then the payer will - * only be charged the amount needed to make up the difference. If the gas - * price in the transaction was set to zero then the payer will be assessed + * only be charged the amount needed to make up the difference. If the gas + * price in the transaction was set to zero then the payer will be assessed * the entire fee. */ int64 max_gas_allowance = 3; -} \ No newline at end of file +} diff --git a/sdk/src/main/proto/exchange_rate.proto b/sdk/src/main/proto/exchange_rate.proto index 0a0c3f7b81..1d7c02b075 100644 --- a/sdk/src/main/proto/exchange_rate.proto +++ b/sdk/src/main/proto/exchange_rate.proto @@ -21,7 +21,7 @@ package proto; * limitations under the License. * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -30,7 +30,7 @@ import "timestamp.proto"; /** * An exchange rate between hbar and cents (USD) and the time at which the exchange rate will - * expire, and be superseded by a new exchange rate. + * expire, and be superseded by a new exchange rate. */ message ExchangeRate { /** diff --git a/sdk/src/main/proto/file.proto b/sdk/src/main/proto/file.proto index 657af7b4fa..4d7c61ef6c 100644 --- a/sdk/src/main/proto/file.proto +++ b/sdk/src/main/proto/file.proto @@ -24,7 +24,7 @@ package proto; import "basic_types.proto"; -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -64,4 +64,4 @@ message File { */ int64 pre_system_delete_expiration_second = 7; -} \ No newline at end of file +} diff --git a/sdk/src/main/proto/file_append.proto b/sdk/src/main/proto/file_append.proto index c70cd18f4e..60ff77560d 100644 --- a/sdk/src/main/proto/file_append.proto +++ b/sdk/src/main/proto/file_append.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -34,7 +34,7 @@ import "basic_types.proto"; * single FileCreateTransaction, then it can be created with the first part of its contents, and * then appended as many times as necessary to create the entire file. This transaction must be * signed by all initial M-of-M KeyList keys. If keys contains additional KeyList or ThresholdKey - * then M-of-M secondary KeyList or ThresholdKey signing requirements must be meet. + * then M-of-M secondary KeyList or ThresholdKey signing requirements must be meet. */ message FileAppendTransactionBody { /** diff --git a/sdk/src/main/proto/file_create.proto b/sdk/src/main/proto/file_create.proto index b3df3a5926..be6ed1eabe 100644 --- a/sdk/src/main/proto/file_create.proto +++ b/sdk/src/main/proto/file_create.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -38,14 +38,14 @@ import "timestamp.proto"; * at the expirationTime, unless its expiration is extended by another transaction before that time. * If the file is deleted, then its contents will become empty and it will be marked as deleted * until it expires, and then it will cease to exist. - * + * * The keys field is a list of keys. All keys within the top-level key list must sign (M-M) to * create or modify a file. However, to delete the file, only one key (1-M) is required to sign from * the top-level key list. Each of those "keys" may itself be threshold key containing other keys * (including other threshold keys). In other words, the behavior is an AND for create/modify, OR * for delete. This is useful for acting as a revocation server. If it is desired to have the * behavior be AND for all 3 operations (or OR for all 3), then the list should have only a single - * Key, which is a threshold key, with N=1 for OR, N=M for AND. If the auto_renew_account field + * Key, which is a threshold key, with N=1 for OR, N=M for AND. If the auto_renew_account field * is set, the key of the referenced account must sign. * * If a file is created without ANY keys in the keys field, the file is immutable and ONLY the diff --git a/sdk/src/main/proto/file_delete.proto b/sdk/src/main/proto/file_delete.proto index f4b23c988f..6f0ce2ef9e 100644 --- a/sdk/src/main/proto/file_delete.proto +++ b/sdk/src/main/proto/file_delete.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/file_get_contents.proto b/sdk/src/main/proto/file_get_contents.proto index ee888d6af0..21b4badfd0 100644 --- a/sdk/src/main/proto/file_get_contents.proto +++ b/sdk/src/main/proto/file_get_contents.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/file_get_info.proto b/sdk/src/main/proto/file_get_info.proto index a408ebd88d..fbd197f997 100644 --- a/sdk/src/main/proto/file_get_info.proto +++ b/sdk/src/main/proto/file_get_info.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -94,7 +94,7 @@ message FileGetInfoResponse { string memo = 6; /** - * The ledger ID the response was returned from; please see HIP-198 for the network-specific IDs. + * The ledger ID the response was returned from; please see HIP-198 for the network-specific IDs. */ bytes ledger_id = 7; } diff --git a/sdk/src/main/proto/file_service.proto b/sdk/src/main/proto/file_service.proto index 04087353fa..8fc9ab0ed1 100644 --- a/sdk/src/main/proto/file_service.proto +++ b/sdk/src/main/proto/file_service.proto @@ -21,7 +21,7 @@ package proto; * limitations under the License. * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package import "query.proto"; @@ -30,7 +30,7 @@ import "transaction_response.proto"; import "transaction.proto"; /** - * Transactions and queries for the file service. + * Transactions and queries for the file service. */ service FileService { /** diff --git a/sdk/src/main/proto/file_update.proto b/sdk/src/main/proto/file_update.proto index 74e0490ec0..bad7043509 100644 --- a/sdk/src/main/proto/file_update.proto +++ b/sdk/src/main/proto/file_update.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -36,7 +36,7 @@ import "google/protobuf/wrappers.proto"; * in the top level of a key list (M-of-M) of the file being updated. If the keys themselves are * being updated, then the transaction must also be signed by all the new keys. If the keys contain * additional KeyList or ThresholdKey then M-of-M secondary KeyList or ThresholdKey signing - * requirements must be meet If the update transaction sets the auto_renew_account_id field + * requirements must be meet If the update transaction sets the auto_renew_account_id field * to anything other than the sentinel 0.0.0, the key of the referenced account must sign. */ message FileUpdateTransactionBody { diff --git a/sdk/src/main/proto/freeze.proto b/sdk/src/main/proto/freeze.proto index a466b41816..e44ca78731 100644 --- a/sdk/src/main/proto/freeze.proto +++ b/sdk/src/main/proto/freeze.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/freeze_service.proto b/sdk/src/main/proto/freeze_service.proto index b43d1f8de6..f797711734 100644 --- a/sdk/src/main/proto/freeze_service.proto +++ b/sdk/src/main/proto/freeze_service.proto @@ -21,7 +21,7 @@ package proto; * limitations under the License. * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package import "transaction_response.proto"; diff --git a/sdk/src/main/proto/freeze_type.proto b/sdk/src/main/proto/freeze_type.proto index 7abcf47f66..d20807568f 100644 --- a/sdk/src/main/proto/freeze_type.proto +++ b/sdk/src/main/proto/freeze_type.proto @@ -22,50 +22,50 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; /** -* The type of network freeze or upgrade operation to be performed. This type dictates which -* fields are required. +* The type of network freeze or upgrade operation to be performed. This type dictates which +* fields are required. */ enum FreezeType { /** - * An (invalid) default value for this enum, to ensure the client explicitly sets + * An (invalid) default value for this enum, to ensure the client explicitly sets * the intended type of freeze transaction. */ UNKNOWN_FREEZE_TYPE = 0; /** - * Freezes the network at the specified time. The start_time field must be provided and - * must reference a future time. Any values specified for the update_file and file_hash - * fields will be ignored. This transaction does not perform any network changes or - * upgrades and requires manual intervention to restart the network. + * Freezes the network at the specified time. The start_time field must be provided and + * must reference a future time. Any values specified for the update_file and file_hash + * fields will be ignored. This transaction does not perform any network changes or + * upgrades and requires manual intervention to restart the network. */ - FREEZE_ONLY = 1; + FREEZE_ONLY = 1; /** - * A non-freezing operation that initiates network wide preparation in advance of a - * scheduled freeze upgrade. The update_file and file_hash fields must be provided and + * A non-freezing operation that initiates network wide preparation in advance of a + * scheduled freeze upgrade. The update_file and file_hash fields must be provided and * valid. The start_time field may be omitted and any value present will be ignored. */ - PREPARE_UPGRADE = 2; + PREPARE_UPGRADE = 2; /** - * Freezes the network at the specified time and performs the previously prepared - * automatic upgrade across the entire network. + * Freezes the network at the specified time and performs the previously prepared + * automatic upgrade across the entire network. */ - FREEZE_UPGRADE = 3; + FREEZE_UPGRADE = 3; /** * Aborts a pending network freeze operation. */ - FREEZE_ABORT = 4; + FREEZE_ABORT = 4; /** - * Performs an immediate upgrade on auxilary services and containers providing - * telemetry/metrics. Does not impact network operations. + * Performs an immediate upgrade on auxilary services and containers providing + * telemetry/metrics. Does not impact network operations. */ - TELEMETRY_UPGRADE = 5; + TELEMETRY_UPGRADE = 5; } diff --git a/sdk/src/main/proto/get_account_details.proto b/sdk/src/main/proto/get_account_details.proto index 98ebe7f271..b213c57b63 100644 --- a/sdk/src/main/proto/get_account_details.proto +++ b/sdk/src/main/proto/get_account_details.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/get_by_key.proto b/sdk/src/main/proto/get_by_key.proto index e166b3367a..2c06233eda 100644 --- a/sdk/src/main/proto/get_by_key.proto +++ b/sdk/src/main/proto/get_by_key.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/get_by_solidity_id.proto b/sdk/src/main/proto/get_by_solidity_id.proto index 1c9d111c3c..6921fb5be3 100644 --- a/sdk/src/main/proto/get_by_solidity_id.proto +++ b/sdk/src/main/proto/get_by_solidity_id.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/ledger_id.proto b/sdk/src/main/proto/ledger_id.proto index 98e5d42f43..cb633e9a2d 100644 --- a/sdk/src/main/proto/ledger_id.proto +++ b/sdk/src/main/proto/ledger_id.proto @@ -15,7 +15,7 @@ */ syntax = "proto3"; -package com.hedera.hapi.node.state.roster; +package org.hiero.hapi.node.state.roster; /* * Copyright (C) 2024 Hedera Hashgraph, LLC @@ -33,7 +33,7 @@ package com.hedera.hapi.node.state.roster; * limitations under the License. */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/mirror/consensus_service.proto b/sdk/src/main/proto/mirror/consensus_service.proto index 686f3e7ab1..a16bda881c 100644 --- a/sdk/src/main/proto/mirror/consensus_service.proto +++ b/sdk/src/main/proto/mirror/consensus_service.proto @@ -27,7 +27,7 @@ package com.hedera.mirror.api.proto; */ option java_multiple_files = true; -option java_package = "com.hedera.hashgraph.sdk.proto.mirror"; +option java_package = "org.hiero.sdk.proto.mirror"; import "basic_types.proto"; import "timestamp.proto"; diff --git a/sdk/src/main/proto/mirror/mirror_network_service.proto b/sdk/src/main/proto/mirror/mirror_network_service.proto index 460da29f30..f39b01db62 100644 --- a/sdk/src/main/proto/mirror/mirror_network_service.proto +++ b/sdk/src/main/proto/mirror/mirror_network_service.proto @@ -23,7 +23,7 @@ syntax = "proto3"; package com.hedera.mirror.api.proto; option java_multiple_files = true; // Required for the reactor-grpc generator to work correctly -option java_package = "com.hedera.hashgraph.sdk.proto.mirror"; +option java_package = "org.hiero.sdk.proto.mirror"; import "basic_types.proto"; import "timestamp.proto"; diff --git a/sdk/src/main/proto/network_get_execution_time.proto b/sdk/src/main/proto/network_get_execution_time.proto index 0c282f1a7b..5f360768f4 100644 --- a/sdk/src/main/proto/network_get_execution_time.proto +++ b/sdk/src/main/proto/network_get_execution_time.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -31,9 +31,9 @@ import "query_header.proto"; import "response_header.proto"; /** - * Gets the time in nanoseconds spent in handleTransaction for one or more - * TransactionIDs (assuming they have reached consensus "recently", since only a limited - * number of execution times are kept in-memory, depending on the value of the node-local + * Gets the time in nanoseconds spent in handleTransaction for one or more + * TransactionIDs (assuming they have reached consensus "recently", since only a limited + * number of execution times are kept in-memory, depending on the value of the node-local * property stats.executionTimesToTrack). */ message NetworkGetExecutionTimeQuery { diff --git a/sdk/src/main/proto/network_get_version_info.proto b/sdk/src/main/proto/network_get_version_info.proto index 64978788db..f144b9dff4 100644 --- a/sdk/src/main/proto/network_get_version_info.proto +++ b/sdk/src/main/proto/network_get_version_info.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/network_service.proto b/sdk/src/main/proto/network_service.proto index 49f06e5d5e..786f8b0baa 100644 --- a/sdk/src/main/proto/network_service.proto +++ b/sdk/src/main/proto/network_service.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package import "query.proto"; @@ -40,9 +40,9 @@ service NetworkService { rpc getVersionInfo (Query) returns (Response); /** - * Retrieves the time in nanoseconds spent in handleTransaction for one or more - * TransactionIDs (assuming they have reached consensus "recently", since only a limited - * number of execution times are kept in-memory, depending on the value of the node-local + * Retrieves the time in nanoseconds spent in handleTransaction for one or more + * TransactionIDs (assuming they have reached consensus "recently", since only a limited + * number of execution times are kept in-memory, depending on the value of the node-local * property stats.executionTimesToTrack). */ rpc getExecutionTime (Query) returns (Response); diff --git a/sdk/src/main/proto/network_staking_rewards.proto b/sdk/src/main/proto/network_staking_rewards.proto index 66d99530e3..6cb73eb8c0 100644 --- a/sdk/src/main/proto/network_staking_rewards.proto +++ b/sdk/src/main/proto/network_staking_rewards.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/nft.proto b/sdk/src/main/proto/nft.proto index bfb17fcebf..9e28c647fb 100644 --- a/sdk/src/main/proto/nft.proto +++ b/sdk/src/main/proto/nft.proto @@ -25,7 +25,7 @@ package proto; import "timestamp.proto"; import "basic_types.proto"; -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -64,13 +64,13 @@ message Nft { bytes metadata = 5; /** - * If the owner of this NFT is not its token treasury, the id of the previous NFT + * If the owner of this NFT is not its token treasury, the id of the previous NFT * in the owner's "doubly-linked list" of owned NFTs (if any). */ NftID owner_previous_nft_id = 6; /** - * If the owner of this NFT is not its token treasury, the id of the next NFT in + * If the owner of this NFT is not its token treasury, the id of the next NFT in * the owner's "doubly-linked list" of owned NFTs (if any). */ NftID owner_next_nft_id = 7; diff --git a/sdk/src/main/proto/node.proto b/sdk/src/main/proto/node.proto index 9b99cf1d06..1eedc792b4 100644 --- a/sdk/src/main/proto/node.proto +++ b/sdk/src/main/proto/node.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -package com.hedera.hapi.node.state.addressbook; +package org.hiero.hapi.node.state.addressbook; /* * Copyright (C) 2024 Hedera Hashgraph, LLC @@ -20,7 +20,7 @@ package com.hedera.hapi.node.state.addressbook; import "basic_types.proto"; -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/node_create.proto b/sdk/src/main/proto/node_create.proto index 8d828c724d..f3181dfaad 100644 --- a/sdk/src/main/proto/node_create.proto +++ b/sdk/src/main/proto/node_create.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -package com.hedera.hapi.node.addressbook; +package org.hiero.hapi.node.addressbook; /* * Copyright (C) 2024 Hedera Hashgraph, LLC @@ -18,7 +18,7 @@ package com.hedera.hapi.node.addressbook; * limitations under the License. */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/node_delete.proto b/sdk/src/main/proto/node_delete.proto index 48395312c4..08bd893265 100644 --- a/sdk/src/main/proto/node_delete.proto +++ b/sdk/src/main/proto/node_delete.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -package com.hedera.hapi.node.addressbook; +package org.hiero.hapi.node.addressbook; /* * Copyright (C) 2024 Hedera Hashgraph, LLC @@ -18,7 +18,7 @@ package com.hedera.hapi.node.addressbook; * limitations under the License. */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/node_stake_update.proto b/sdk/src/main/proto/node_stake_update.proto index a8afdb2c3b..c055e6d3d2 100644 --- a/sdk/src/main/proto/node_stake_update.proto +++ b/sdk/src/main/proto/node_stake_update.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -62,7 +62,7 @@ message NodeStakeUpdateTransactionBody { int64 staking_periods_stored = 5; /** - * The number of minutes in a staking period. Note for the special case of 1440 minutes, periods are + * The number of minutes in a staking period. Note for the special case of 1440 minutes, periods are * treated as UTC calendar days, rather than repeating 1440 minute periods left-aligned at the epoch. */ int64 staking_period = 6; @@ -78,40 +78,40 @@ message NodeStakeUpdateTransactionBody { int64 staking_start_threshold = 8; /** - * (DEPRECATED) The maximum total number of tinybars to be distributed as staking rewards in the + * (DEPRECATED) The maximum total number of tinybars to be distributed as staking rewards in the * ending period. Please consult the max_total_reward field instead. */ int64 staking_reward_rate = 9 [deprecated = true]; /** - * The amount of the staking reward funds (account 0.0.800) reserved to pay pending rewards that + * The amount of the staking reward funds (account 0.0.800) reserved to pay pending rewards that * have been earned but not collected. */ int64 reserved_staking_rewards = 10; /** - * The unreserved balance of account 0.0.800 at the close of the just-ending period; this value is + * The unreserved balance of account 0.0.800 at the close of the just-ending period; this value is * used to compute the HIP-782 balance ratio. */ int64 unreserved_staking_reward_balance = 11; /** - * The unreserved tinybar balance of account 0.0.800 required to achieve the maximum per-hbar reward + * The unreserved tinybar balance of account 0.0.800 required to achieve the maximum per-hbar reward * rate in any period; please see HIP-782 for details. */ int64 reward_balance_threshold = 12; /** - * The maximum amount of tinybar that can be staked for reward while still achieving the maximum + * The maximum amount of tinybar that can be staked for reward while still achieving the maximum * per-hbar reward rate in any period; please see HIP-782 for details. */ int64 max_stake_rewarded = 13; /** - * The maximum total tinybars that could be paid as staking rewards in the ending period, after + * The maximum total tinybars that could be paid as staking rewards in the ending period, after * applying the settings for the 0.0.800 balance threshold and the maximum stake rewarded. This - * field replaces the deprecated field staking_reward_rate. It is only for convenience, since a - * mirror node could also calculate its value by iterating the node_stake list and summing + * field replaces the deprecated field staking_reward_rate. It is only for convenience, since a + * mirror node could also calculate its value by iterating the node_stake list and summing * stake_rewarded fields; then multiplying this sum by the max_staking_reward_rate_per_hbar. */ int64 max_total_reward = 14; @@ -123,14 +123,14 @@ message NodeStakeUpdateTransactionBody { message NodeStake { /** * The maximum stake (rewarded or not rewarded) this node can have as consensus weight. If its stake to - * reward is above this maximum at the start of a period, then accounts staking to the node in that + * reward is above this maximum at the start of a period, then accounts staking to the node in that * period will be rewarded at a lower rate scaled by (maxStake / stakeRewardStart). */ int64 max_stake = 1; /** * The minimum stake (rewarded or not rewarded) this node must reach before having non-zero consensus weight. - * If its total stake is below this minimum at the start of a period, then accounts staking to the node in + * If its total stake is below this minimum at the start of a period, then accounts staking to the node in * that period will receive no rewards. */ int64 min_stake = 2; @@ -141,8 +141,8 @@ message NodeStake { int64 node_id = 3; /** - * The reward rate _per whole hbar_ that was staked to this node with declineReward=false from the start of - * the staking period that is ending. + * The reward rate _per whole hbar_ that was staked to this node with declineReward=false from the start of + * the staking period that is ending. */ int64 reward_rate = 4; @@ -152,13 +152,13 @@ message NodeStake { int64 stake = 5; /** - * Total of (balance + stakedToMe) for all accounts staked to this node with declineReward=true, at the + * Total of (balance + stakedToMe) for all accounts staked to this node with declineReward=true, at the * beginning of the new staking period. */ int64 stake_not_rewarded = 6; /** - * Total of (balance + stakedToMe) for all accounts staked to this node with declineReward=false, at the + * Total of (balance + stakedToMe) for all accounts staked to this node with declineReward=false, at the * beginning of the new staking period. */ int64 stake_rewarded = 7; diff --git a/sdk/src/main/proto/node_update.proto b/sdk/src/main/proto/node_update.proto index 25eed414c8..15a65cc562 100644 --- a/sdk/src/main/proto/node_update.proto +++ b/sdk/src/main/proto/node_update.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -package com.hedera.hapi.node.addressbook; +package org.hiero.hapi.node.addressbook; /* * Copyright (C) 2024 Hedera Hashgraph, LLC @@ -18,7 +18,7 @@ package com.hedera.hapi.node.addressbook; * limitations under the License. */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/primitives.proto b/sdk/src/main/proto/primitives.proto index 7917055e1d..614bdf80b5 100644 --- a/sdk/src/main/proto/primitives.proto +++ b/sdk/src/main/proto/primitives.proto @@ -24,7 +24,7 @@ package proto; * type instead. */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/query.proto b/sdk/src/main/proto/query.proto index 7bf38eb0c6..0e10710288 100644 --- a/sdk/src/main/proto/query.proto +++ b/sdk/src/main/proto/query.proto @@ -18,7 +18,7 @@ package proto; * limitations under the License. */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/query_header.proto b/sdk/src/main/proto/query_header.proto index c96aa250ad..869ea74de9 100644 --- a/sdk/src/main/proto/query_header.proto +++ b/sdk/src/main/proto/query_header.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -34,7 +34,7 @@ import "transaction.proto"; * (allowing it to tailor the payment transaction accordingly). If the payment in the query fails * the precheck, then the response may have some fields blank. The state proof is only available for * some types of information. It is available for a Record, but not a receipt. It is available for - * the information in each kind of *GetInfo request. + * the information in each kind of *GetInfo request. */ enum ResponseType { /** @@ -61,7 +61,7 @@ enum ResponseType { /** * Each query from the client to the node will contain the QueryHeader, which gives the requested * response type, and includes a payment transaction that will compensate the node for responding to - * the query. The payment can be blank if the query is free. + * the query. The payment can be blank if the query is free. */ message QueryHeader { /** diff --git a/sdk/src/main/proto/recordcache.proto b/sdk/src/main/proto/recordcache.proto index 5398675d5c..affffb6265 100644 --- a/sdk/src/main/proto/recordcache.proto +++ b/sdk/src/main/proto/recordcache.proto @@ -34,7 +34,7 @@ import "basic_types.proto"; import "transaction_record.proto"; import "response_code.proto"; -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/response.proto b/sdk/src/main/proto/response.proto index 62daa7fb63..7a74eaf149 100644 --- a/sdk/src/main/proto/response.proto +++ b/sdk/src/main/proto/response.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/response_code.proto b/sdk/src/main/proto/response_code.proto index 81a6bc4733..77ba49d99c 100644 --- a/sdk/src/main/proto/response_code.proto +++ b/sdk/src/main/proto/response_code.proto @@ -18,7 +18,7 @@ package proto; * limitations under the License. */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/response_header.proto b/sdk/src/main/proto/response_header.proto index 56f9c35710..acb20ed211 100644 --- a/sdk/src/main/proto/response_header.proto +++ b/sdk/src/main/proto/response_header.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/roster.proto b/sdk/src/main/proto/roster.proto index 853856c308..bc8b2fc0dc 100644 --- a/sdk/src/main/proto/roster.proto +++ b/sdk/src/main/proto/roster.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -package com.hedera.hapi.node.state.roster; +package org.hiero.hapi.node.state.roster; /* * Copyright (C) 2024 Hedera Hashgraph, LLC @@ -20,7 +20,7 @@ package com.hedera.hapi.node.state.roster; import "basic_types.proto"; -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/roster_state.proto b/sdk/src/main/proto/roster_state.proto index 6532694e13..40e9e83d8b 100644 --- a/sdk/src/main/proto/roster_state.proto +++ b/sdk/src/main/proto/roster_state.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -package com.hedera.hapi.node.state.roster; +package org.hiero.hapi.node.state.roster; /* * Copyright (C) 2024 Hedera Hashgraph, LLC @@ -20,7 +20,7 @@ package com.hedera.hapi.node.state.roster; import "basic_types.proto"; -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/running_hashes.proto b/sdk/src/main/proto/running_hashes.proto index 856de26b8d..2e5989b10c 100644 --- a/sdk/src/main/proto/running_hashes.proto +++ b/sdk/src/main/proto/running_hashes.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/schedulable_transaction_body.proto b/sdk/src/main/proto/schedulable_transaction_body.proto index 1cfa1cd286..7ab64b7807 100644 --- a/sdk/src/main/proto/schedulable_transaction_body.proto +++ b/sdk/src/main/proto/schedulable_transaction_body.proto @@ -18,7 +18,7 @@ package proto; * limitations under the License. */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -297,17 +297,17 @@ message SchedulableTransactionBody { /** * Transaction body for a scheduled transaction to create a new node. */ - com.hedera.hapi.node.addressbook.NodeCreateTransactionBody nodeCreate = 42; + org.hiero.hapi.node.addressbook.NodeCreateTransactionBody nodeCreate = 42; /** * Transaction body for a scheduled transaction to modify an existing node. */ - com.hedera.hapi.node.addressbook.NodeUpdateTransactionBody nodeUpdate = 43; + org.hiero.hapi.node.addressbook.NodeUpdateTransactionBody nodeUpdate = 43; /** * Transaction body for a scheduled transaction to remove a node. */ - com.hedera.hapi.node.addressbook.NodeDeleteTransactionBody nodeDelete = 44; + org.hiero.hapi.node.addressbook.NodeDeleteTransactionBody nodeDelete = 44; /** * A transaction body to "reject" undesired tokens.
diff --git a/sdk/src/main/proto/schedule.proto b/sdk/src/main/proto/schedule.proto index b8abb6c9e1..39dea43c11 100644 --- a/sdk/src/main/proto/schedule.proto +++ b/sdk/src/main/proto/schedule.proto @@ -27,7 +27,7 @@ import "timestamp.proto"; import "schedulable_transaction_body.proto"; import "transaction_body.proto"; -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/schedule_create.proto b/sdk/src/main/proto/schedule_create.proto index c7536aeee6..3dfcfb541b 100644 --- a/sdk/src/main/proto/schedule_create.proto +++ b/sdk/src/main/proto/schedule_create.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -44,40 +44,40 @@ import "schedulable_transaction_body.proto"; * or ScheduleSign. * * Upon `SUCCESS`, the receipt also includes the scheduledTransactionID to - * use to query for the record of the scheduled transaction's execution (if it occurs). - * + * use to query for the record of the scheduled transaction's execution (if it occurs). + * * The expiration time of a schedule is controlled by it's expiration_time. It remains in state and can be queried * using GetScheduleInfo until expiration, no matter if the scheduled transaction has * executed or marked deleted. If Long Term Scheduled Transactions are disabled, the expiration_time is always * 30 minutes in the future. - * + * * If the adminKey field is omitted, the resulting schedule is immutable. If the * adminKey is set, the ScheduleDelete transaction can be used to mark it as * deleted. The creator may also specify an optional memo whose UTF-8 encoding is at most * 100 bytes and does not include the zero byte is also supported. - * + * * When a scheduledTransactionBody is executed, the * network only charges its payer the service fee, and not the node and network fees. If the * optional payerAccountID is set, the network charges this account. Otherwise it charges - * the payer of the originating ScheduleCreate. - * + * the payer of the originating ScheduleCreate. + * * Two ScheduleCreate transactions are identical if they are equal in all their * fields other than payerAccountID. (For the scheduledTransactionBody field, * "equal" should be understood in the sense of * gRPC object equality in the network software runtime. In particular, a gRPC object with unknown fields is - * not equal to a gRPC object without unknown fields, even if they agree on all known fields.) - * + * not equal to a gRPC object without unknown fields, even if they agree on all known fields.) + * * A ScheduleCreate transaction that attempts to re-create an identical schedule already in * state will receive a receipt with status IDENTICAL_SCHEDULE_ALREADY_CREATED; the receipt * will include the ScheduleID of the extant schedule, which may be used in a subsequent * ScheduleSign transaction. (The receipt will also include the TransactionID to * use in querying for the receipt or record of the scheduled transaction.) - * + * * Other notable response codes include, INVALID_ACCOUNT_ID, * UNSCHEDULABLE_TRANSACTION, UNRESOLVABLE_REQUIRED_SIGNERS, * INVALID_SIGNATURE. For more information please see the section of this documentation on - * the ResponseCode enum. + * the ResponseCode enum. */ message ScheduleCreateTransactionBody { /** diff --git a/sdk/src/main/proto/schedule_delete.proto b/sdk/src/main/proto/schedule_delete.proto index 4f6de56473..ebd39943f7 100644 --- a/sdk/src/main/proto/schedule_delete.proto +++ b/sdk/src/main/proto/schedule_delete.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -36,7 +36,7 @@ import "basic_types.proto"; * Other notable response codes include, INVALID_SCHEDULE_ID, SCHEDULE_PENDING_EXPIRATION, * SCHEDULE_ALREADY_DELETED, SCHEDULE_ALREADY_EXECUTED, SCHEDULE_IS_IMMUTABLE. * For more information please see the section of this documentation on the ResponseCode - * enum. + * enum. */ message ScheduleDeleteTransactionBody { /** diff --git a/sdk/src/main/proto/schedule_get_info.proto b/sdk/src/main/proto/schedule_get_info.proto index e723764104..9964a15861 100644 --- a/sdk/src/main/proto/schedule_get_info.proto +++ b/sdk/src/main/proto/schedule_get_info.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -34,7 +34,7 @@ import "schedulable_transaction_body.proto"; /** * Gets information about a schedule in the network's action queue. - * + * * Responds with INVALID_SCHEDULE_ID if the requested schedule doesn't exist. */ message ScheduleGetInfoQuery { @@ -53,7 +53,7 @@ message ScheduleGetInfoQuery { /** * Information summarizing schedule state */ -message ScheduleInfo { +message ScheduleInfo { /** * The id of the schedule */ @@ -117,7 +117,7 @@ message ScheduleInfo { TransactionID scheduledTransactionID = 11; /** - * The ledger ID the response was returned from; please see HIP-198 for the network-specific IDs. + * The ledger ID the response was returned from; please see HIP-198 for the network-specific IDs. */ bytes ledger_id = 12; @@ -130,7 +130,7 @@ message ScheduleInfo { * Note: this field is unused until Long Term Scheduled Transactions are enabled. */ bool wait_for_expiry = 13; -} +} /** * Response wrapper for the ScheduleInfo diff --git a/sdk/src/main/proto/schedule_service.proto b/sdk/src/main/proto/schedule_service.proto index 2289f586de..15a2f29e83 100644 --- a/sdk/src/main/proto/schedule_service.proto +++ b/sdk/src/main/proto/schedule_service.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package import "query.proto"; diff --git a/sdk/src/main/proto/schedule_sign.proto b/sdk/src/main/proto/schedule_sign.proto index 784970377e..f5d78b9248 100644 --- a/sdk/src/main/proto/schedule_sign.proto +++ b/sdk/src/main/proto/schedule_sign.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -37,10 +37,10 @@ import "basic_types.proto"; * Otherwise, if the resulting set of signing keys satisfy the * scheduled transaction's signing requirements, it will be executed immediately after the * triggering ScheduleSign. - * + * * Upon SUCCESS, the receipt includes the scheduledTransactionID to use to query - * for the record of the scheduled transaction's execution (if it occurs). - * + * for the record of the scheduled transaction's execution (if it occurs). + * * Other notable response codes include INVALID_SCHEDULE_ID, SCHEDULE_ALREADY_DELETED, * SCHEDULE_PENDING_EXPIRATION, SCHEDULE_ALREADY_EXPIRED, * INVALID_ACCOUNT_ID, UNRESOLVABLE_REQUIRED_SIGNERS, diff --git a/sdk/src/main/proto/smart_contract_service.proto b/sdk/src/main/proto/smart_contract_service.proto index 05fc6c9bcc..2c7199a92d 100644 --- a/sdk/src/main/proto/smart_contract_service.proto +++ b/sdk/src/main/proto/smart_contract_service.proto @@ -21,7 +21,7 @@ package proto; * limitations under the License. * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package import "transaction_response.proto"; @@ -30,7 +30,7 @@ import "response.proto"; import "transaction.proto"; /** - * Transactions and queries for the file service. + * Transactions and queries for the file service. */ service SmartContractService { /** diff --git a/sdk/src/main/proto/staking_node_info.proto b/sdk/src/main/proto/staking_node_info.proto index 508acee943..6de150a697 100644 --- a/sdk/src/main/proto/staking_node_info.proto +++ b/sdk/src/main/proto/staking_node_info.proto @@ -25,7 +25,7 @@ package proto; import "common.proto"; import "basic_types.proto"; -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/storage_slot.proto b/sdk/src/main/proto/storage_slot.proto index 3df5527b28..6a2ffd8d59 100644 --- a/sdk/src/main/proto/storage_slot.proto +++ b/sdk/src/main/proto/storage_slot.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -30,8 +30,8 @@ import "basic_types.proto"; /** * The key of a storage slot. A slot is scoped to a specific contract number. - * - * For each contract, its EVM storage is a mapping of 256-bit keys (or "words") to 256-bit values. + * + * For each contract, its EVM storage is a mapping of 256-bit keys (or "words") to 256-bit values. */ message SlotKey { /** @@ -47,10 +47,10 @@ message SlotKey { /** * The value of a contract storage slot. For the EVM, this is a single word. - * + * * Because we iterate through all the storage slots for an expired contract * when purging it from state, our slot values also include the words - * of the previous and next keys in this contract's storage "list". + * of the previous and next keys in this contract's storage "list". */ message SlotValue { /** diff --git a/sdk/src/main/proto/system_delete.proto b/sdk/src/main/proto/system_delete.proto index a49b9ec810..12df065d9b 100644 --- a/sdk/src/main/proto/system_delete.proto +++ b/sdk/src/main/proto/system_delete.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -35,7 +35,7 @@ import "timestamp.proto"; * stored internally until the expiration time, at which time it is truly and permanently deleted. * Until that time, it can be undeleted by the Hedera administrative multisignature. When a smart * contract is deleted, the cryptocurrency account within it continues to exist, and is not affected - * by the expiration time here. + * by the expiration time here. */ message SystemDeleteTransactionBody { oneof id { diff --git a/sdk/src/main/proto/system_undelete.proto b/sdk/src/main/proto/system_undelete.proto index 4eab2349a3..d8c6d827f6 100644 --- a/sdk/src/main/proto/system_undelete.proto +++ b/sdk/src/main/proto/system_undelete.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -30,7 +30,7 @@ import "basic_types.proto"; /** * Undelete a file or smart contract that was deleted by SystemDelete; requires a Hedera - * administrative multisignature. + * administrative multisignature. */ message SystemUndeleteTransactionBody { oneof id { diff --git a/sdk/src/main/proto/throttle_definitions.proto b/sdk/src/main/proto/throttle_definitions.proto index 4be3aef644..1a345a6bbf 100644 --- a/sdk/src/main/proto/throttle_definitions.proto +++ b/sdk/src/main/proto/throttle_definitions.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -77,9 +77,9 @@ message ThrottleBucket { * A list of throttle buckets which, simultaneously enforced, define the system's throttling policy. *
    *
  1. When an operation appears in more than one throttling bucket, all its buckets must have room - * or it will be throttled.
  2. + * or it will be throttled. *
  3. An operation assigned to no buckets is always throttled.
  4. - *
+ * */ message ThrottleDefinitions { repeated ThrottleBucket throttleBuckets = 1; diff --git a/sdk/src/main/proto/throttle_usage_snapshots.proto b/sdk/src/main/proto/throttle_usage_snapshots.proto index b325b6b269..5d97bdc2f9 100644 --- a/sdk/src/main/proto/throttle_usage_snapshots.proto +++ b/sdk/src/main/proto/throttle_usage_snapshots.proto @@ -24,7 +24,7 @@ package proto; import "timestamp.proto"; -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/timestamp.proto b/sdk/src/main/proto/timestamp.proto index 0ca2798a7d..b2370c79ec 100644 --- a/sdk/src/main/proto/timestamp.proto +++ b/sdk/src/main/proto/timestamp.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/token.proto b/sdk/src/main/proto/token.proto index 78a6c60a98..9a22ef9c9f 100644 --- a/sdk/src/main/proto/token.proto +++ b/sdk/src/main/proto/token.proto @@ -25,7 +25,7 @@ package proto; import "basic_types.proto"; import "custom_fees.proto"; -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/token_airdrop.proto b/sdk/src/main/proto/token_airdrop.proto index cef39a2850..83f56b335d 100644 --- a/sdk/src/main/proto/token_airdrop.proto +++ b/sdk/src/main/proto/token_airdrop.proto @@ -30,7 +30,7 @@ package proto; * limitations under the License. */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/token_associate.proto b/sdk/src/main/proto/token_associate.proto index b1438b01a7..f310263667 100644 --- a/sdk/src/main/proto/token_associate.proto +++ b/sdk/src/main/proto/token_associate.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/token_burn.proto b/sdk/src/main/proto/token_burn.proto index fba56c492a..1ef5e098ba 100644 --- a/sdk/src/main/proto/token_burn.proto +++ b/sdk/src/main/proto/token_burn.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -36,7 +36,7 @@ import "basic_types.proto"; * Token A has 2 decimals. In order to burn 100 tokens, one must provide amount of 10000. In order * to burn 100.55 tokens, one must provide amount of 10055. * For non fungible tokens the transaction body accepts serialNumbers list of integers as a parameter. - * + * * If the serialNumbers don't get filled for non-fungible token type, a INVALID_TOKEN_BURN_AMOUNT response * code will be returned. * If a zero amount is provided for a fungible token type, it will be treated as a regular transaction. diff --git a/sdk/src/main/proto/token_cancel_airdrop.proto b/sdk/src/main/proto/token_cancel_airdrop.proto index 074aee7eee..d7f90d342b 100644 --- a/sdk/src/main/proto/token_cancel_airdrop.proto +++ b/sdk/src/main/proto/token_cancel_airdrop.proto @@ -28,7 +28,7 @@ package proto; * */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/token_claim_airdrop.proto b/sdk/src/main/proto/token_claim_airdrop.proto index 2d89789c4a..c5d4157f2a 100644 --- a/sdk/src/main/proto/token_claim_airdrop.proto +++ b/sdk/src/main/proto/token_claim_airdrop.proto @@ -28,7 +28,7 @@ package proto; * */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/token_create.proto b/sdk/src/main/proto/token_create.proto index e10be8c3b4..7e41f8a50f 100644 --- a/sdk/src/main/proto/token_create.proto +++ b/sdk/src/main/proto/token_create.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -36,51 +36,51 @@ import "timestamp.proto"; * The specified Treasury Account is receiving the initial supply of tokens as-well as the tokens * from the Token Mint operation once executed. The balance of the treasury account is decreased * when the Token Burn operation is executed. - * + * * The initialSupply is the initial supply of the smallest parts of a token (like a * tinybar, not an hbar). These are the smallest units of the token which may be transferred. - * + * * The supply can change over time. If the total supply at some moment is S parts of tokens, * and the token is using D decimals, then S must be less than or equal to * 263-1, which is 9,223,372,036,854,775,807. The number of whole tokens (not parts) will * be S / 10D. - * + * * If decimals is 8 or 11, then the number of whole tokens can be at most a few billions or * millions, respectively. For example, it could match Bitcoin (21 million whole tokens with 8 * decimals) or hbars (50 billion whole tokens with 8 decimals). It could even match Bitcoin with * milli-satoshis (21 million whole tokens with 11 decimals). - * + * * Note that a created token is immutable if the adminKey is omitted. No property of * an immutable token can ever change, with the sole exception of its expiry. Anyone can pay to * extend the expiry time of an immutable token. - * + * * A token can be either FUNGIBLE_COMMON or NON_FUNGIBLE_UNIQUE, based on its * TokenType. If it has been omitted, FUNGIBLE_COMMON type is used. - * + * * A token can have either INFINITE or FINITE supply type, based on its * TokenType. If it has been omitted, INFINITE type is used. - * + * * If a FUNGIBLE TokenType is used, initialSupply should explicitly be set to a * non-negative. If not, the transaction will resolve to INVALID_TOKEN_INITIAL_SUPPLY. - * + * * If a NON_FUNGIBLE_UNIQUE TokenType is used, initialSupply should explicitly be set * to 0. If not, the transaction will resolve to INVALID_TOKEN_INITIAL_SUPPLY. - * + * * If an INFINITE TokenSupplyType is used, maxSupply should explicitly be set to 0. If * it is not 0, the transaction will resolve to INVALID_TOKEN_MAX_SUPPLY. - * + * * If a FINITE TokenSupplyType is used, maxSupply should be explicitly set to a * non-negative value. If it is not, the transaction will resolve to INVALID_TOKEN_MAX_SUPPLY. */ message TokenCreateTransactionBody { /** - * The publicly visible name of the token. The token name is specified as a Unicode string. + * The publicly visible name of the token. The token name is specified as a Unicode string. * Its UTF-8 encoding cannot exceed 100 bytes, and cannot contain the 0 byte (NUL). */ string name = 1; /** - * The publicly visible token symbol. The token symbol is specified as a Unicode string. + * The publicly visible token symbol. The token symbol is specified as a Unicode string. * Its UTF-8 encoding cannot exceed 100 bytes, and cannot contain the 0 byte (NUL). */ string symbol = 2; diff --git a/sdk/src/main/proto/token_delete.proto b/sdk/src/main/proto/token_delete.proto index 2f6f443f3a..ba8b71de5b 100644 --- a/sdk/src/main/proto/token_delete.proto +++ b/sdk/src/main/proto/token_delete.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/token_dissociate.proto b/sdk/src/main/proto/token_dissociate.proto index d51043091e..aa7d2905e2 100644 --- a/sdk/src/main/proto/token_dissociate.proto +++ b/sdk/src/main/proto/token_dissociate.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/token_fee_schedule_update.proto b/sdk/src/main/proto/token_fee_schedule_update.proto index 64ed137398..1de22349fc 100644 --- a/sdk/src/main/proto/token_fee_schedule_update.proto +++ b/sdk/src/main/proto/token_fee_schedule_update.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -30,13 +30,13 @@ import "basic_types.proto"; import "custom_fees.proto"; /** - * At consensus, updates a token type's fee schedule to the given list of custom fees. - * + * At consensus, updates a token type's fee schedule to the given list of custom fees. + * * If the target token type has no fee_schedule_key, resolves to TOKEN_HAS_NO_FEE_SCHEDULE_KEY. - * Otherwise this transaction must be signed to the fee_schedule_key, or the transaction will + * Otherwise this transaction must be signed to the fee_schedule_key, or the transaction will * resolve to INVALID_SIGNATURE. - * - * If the custom_fees list is empty, clears the fee schedule or resolves to + * + * If the custom_fees list is empty, clears the fee schedule or resolves to * CUSTOM_SCHEDULE_ALREADY_HAS_NO_FEES if the fee schedule was already empty. */ message TokenFeeScheduleUpdateTransactionBody { diff --git a/sdk/src/main/proto/token_freeze_account.proto b/sdk/src/main/proto/token_freeze_account.proto index b25d64230a..0ba5ef1cd8 100644 --- a/sdk/src/main/proto/token_freeze_account.proto +++ b/sdk/src/main/proto/token_freeze_account.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/token_get_account_nft_infos.proto b/sdk/src/main/proto/token_get_account_nft_infos.proto index 3a22091bcc..7e5bbc4b5f 100644 --- a/sdk/src/main/proto/token_get_account_nft_infos.proto +++ b/sdk/src/main/proto/token_get_account_nft_infos.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/token_get_info.proto b/sdk/src/main/proto/token_get_info.proto index 9eb7bd7c9e..14b5c7ab11 100644 --- a/sdk/src/main/proto/token_get_info.proto +++ b/sdk/src/main/proto/token_get_info.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -195,7 +195,7 @@ message TokenInfo { TokenPauseStatus pause_status = 25; /** - * The ledger ID the response was returned from; please see HIP-198 for the network-specific IDs. + * The ledger ID the response was returned from; please see HIP-198 for the network-specific IDs. */ bytes ledger_id = 26; diff --git a/sdk/src/main/proto/token_get_nft_info.proto b/sdk/src/main/proto/token_get_nft_info.proto index b81c323a1d..b82a69fbb4 100644 --- a/sdk/src/main/proto/token_get_nft_info.proto +++ b/sdk/src/main/proto/token_get_nft_info.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -73,7 +73,7 @@ message TokenNftInfo { bytes metadata = 4; /** - * The ledger ID the response was returned from; please see HIP-198 for the network-specific IDs. + * The ledger ID the response was returned from; please see HIP-198 for the network-specific IDs. */ bytes ledger_id = 5; diff --git a/sdk/src/main/proto/token_get_nft_infos.proto b/sdk/src/main/proto/token_get_nft_infos.proto index 7c782abcd7..979c6eee1d 100644 --- a/sdk/src/main/proto/token_get_nft_infos.proto +++ b/sdk/src/main/proto/token_get_nft_infos.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/token_grant_kyc.proto b/sdk/src/main/proto/token_grant_kyc.proto index a45d4d109e..73f786e33a 100644 --- a/sdk/src/main/proto/token_grant_kyc.proto +++ b/sdk/src/main/proto/token_grant_kyc.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/token_mint.proto b/sdk/src/main/proto/token_mint.proto index bd2c499e7c..e899bca749 100644 --- a/sdk/src/main/proto/token_mint.proto +++ b/sdk/src/main/proto/token_mint.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/token_pause.proto b/sdk/src/main/proto/token_pause.proto index 261b913b65..b275d47145 100644 --- a/sdk/src/main/proto/token_pause.proto +++ b/sdk/src/main/proto/token_pause.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -42,4 +42,4 @@ message TokenPauseTransactionBody { * The token to be paused. */ TokenID token = 1; -} \ No newline at end of file +} diff --git a/sdk/src/main/proto/token_reject.proto b/sdk/src/main/proto/token_reject.proto index d90f0051c6..0be0640452 100644 --- a/sdk/src/main/proto/token_reject.proto +++ b/sdk/src/main/proto/token_reject.proto @@ -32,7 +32,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/token_relation.proto b/sdk/src/main/proto/token_relation.proto index aa594d92de..4462b5328d 100644 --- a/sdk/src/main/proto/token_relation.proto +++ b/sdk/src/main/proto/token_relation.proto @@ -24,7 +24,7 @@ package proto; import "basic_types.proto"; -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; /** diff --git a/sdk/src/main/proto/token_revoke_kyc.proto b/sdk/src/main/proto/token_revoke_kyc.proto index 5cb3bd0993..dfd489411e 100644 --- a/sdk/src/main/proto/token_revoke_kyc.proto +++ b/sdk/src/main/proto/token_revoke_kyc.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; option java_multiple_files = true; // <<>> This comment is special code for setting PBJ Compiler java package diff --git a/sdk/src/main/proto/token_service.proto b/sdk/src/main/proto/token_service.proto index 65e30c9eca..d6547d3458 100644 --- a/sdk/src/main/proto/token_service.proto +++ b/sdk/src/main/proto/token_service.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package import "query.proto"; diff --git a/sdk/src/main/proto/token_unfreeze_account.proto b/sdk/src/main/proto/token_unfreeze_account.proto index d490d128fd..216a84f440 100644 --- a/sdk/src/main/proto/token_unfreeze_account.proto +++ b/sdk/src/main/proto/token_unfreeze_account.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/token_unpause.proto b/sdk/src/main/proto/token_unpause.proto index e8107f82e8..ee3a928e04 100644 --- a/sdk/src/main/proto/token_unpause.proto +++ b/sdk/src/main/proto/token_unpause.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -41,4 +41,4 @@ message TokenUnpauseTransactionBody { * The token to be unpaused. */ TokenID token = 1; -} \ No newline at end of file +} diff --git a/sdk/src/main/proto/token_update.proto b/sdk/src/main/proto/token_update.proto index 9974b403ae..956a1e7623 100644 --- a/sdk/src/main/proto/token_update.proto +++ b/sdk/src/main/proto/token_update.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -33,11 +33,11 @@ import "google/protobuf/wrappers.proto"; /** * At consensus, updates an already created token to the given values. - * + * * If no value is given for a field, that field is left unchanged. For an immutable tokens (that is, * a token without an admin key), only the expiry may be updated. Setting any other field in that * case will cause the transaction status to resolve to TOKEN_IS_IMMUTABLE. - * + * * --- Signing Requirements --- * 1. Whether or not a token has an admin key, its expiry can be extended with only the transaction * payer's signature. @@ -47,7 +47,7 @@ import "google/protobuf/wrappers.proto"; * token to become immutable. (Other Key structures without a constituent * Ed25519 key will be rejected with INVALID_ADMIN_KEY.) * 4. If a new treasury is set, the new treasury account's key must sign the transaction. - * + * * --- Nft Requirements --- * 1. If a non fungible token has a positive treasury balance, the operation will abort with * CURRENT_TREASURY_STILL_OWNS_NFTS. @@ -59,13 +59,13 @@ message TokenUpdateTransactionBody { TokenID token = 1; /** - * The new publicly visible token symbol. The token symbol is specified as a Unicode string. + * The new publicly visible token symbol. The token symbol is specified as a Unicode string. * Its UTF-8 encoding cannot exceed 100 bytes, and cannot contain the 0 byte (NUL). */ string symbol = 2; /** - * The new publicly visible name of the token. The token name is specified as a Unicode string. + * The new publicly visible name of the token. The token name is specified as a Unicode string. * Its UTF-8 encoding cannot exceed 100 bytes, and cannot contain the 0 byte (NUL). */ string name = 3; diff --git a/sdk/src/main/proto/token_update_nfts.proto b/sdk/src/main/proto/token_update_nfts.proto index a5ede209c0..e7206a1e1e 100644 --- a/sdk/src/main/proto/token_update_nfts.proto +++ b/sdk/src/main/proto/token_update_nfts.proto @@ -18,7 +18,7 @@ package proto; * limitations under the License. */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/token_wipe_account.proto b/sdk/src/main/proto/token_wipe_account.proto index a46c1dab61..81a4c3bbeb 100644 --- a/sdk/src/main/proto/token_wipe_account.proto +++ b/sdk/src/main/proto/token_wipe_account.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -42,7 +42,7 @@ import "basic_types.proto"; * CANNOT_WIPE_TOKEN_TREASURY_ACCOUNT * On success, tokens are removed from the account and the total supply of the token is decreased by * the wiped amount. - * + * * If both amount and serialNumbers get filled, a INVALID_TRANSACTION_BODY response code will be * returned. * If the serialNumbers don't get filled for a non-fungible token type, a INVALID_WIPING_AMOUNT response @@ -52,7 +52,7 @@ import "basic_types.proto"; * response code will be returned. * If the serialNumbers' list count is greater than the batch size limit global dynamic property, a * BATCH_SIZE_LIMIT_EXCEEDED response code will be returned. - * + * * The amount provided is in the lowest denomination possible. Example: * Token A has 2 decimals. In order to wipe 100 tokens from account, one must provide amount of * 10000. In order to wipe 100.55 tokens, one must provide amount of 10055. diff --git a/sdk/src/main/proto/topic.proto b/sdk/src/main/proto/topic.proto index 8015ce7e4d..612881a373 100644 --- a/sdk/src/main/proto/topic.proto +++ b/sdk/src/main/proto/topic.proto @@ -24,25 +24,25 @@ package proto; import "basic_types.proto"; -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; /** * Representation of a Hedera Consensus Service topic in the network Merkle tree. - * - * As with all network entities, a topic has a unique entity number, which is usually given along + * + * As with all network entities, a topic has a unique entity number, which is usually given along * with the network's shard and realm in the form of a shard.realm.number id. - * + * * A topic consists of just two pieces of data: * 1. The total number of messages sent to the topic; and, * 2. The running hash of all those messages. * It also has several metadata elements: * 1. A consensus expiration time in seconds since the epoch. - * 2. (Optional) The number of an auto-renew account, in the same shard and realm as the topic, that - * has signed a transaction allowing the network to use its balance to automatically extend the topic's + * 2. (Optional) The number of an auto-renew account, in the same shard and realm as the topic, that + * has signed a transaction allowing the network to use its balance to automatically extend the topic's * expiration time when it passes. - * 3. The number of seconds the network should automatically extend the topic's expiration by, if the + * 3. The number of seconds the network should automatically extend the topic's expiration by, if the * topic has a valid auto-renew account, and is not deleted upon expiration. * 4. A boolean marking if the topic has been deleted. * 5. A memo string whose UTF-8 encoding is at most 100 bytes. @@ -63,7 +63,7 @@ message Topic { */ int64 expiration_second = 3; /** - * The number of seconds for which the topic will be automatically renewed + * The number of seconds for which the topic will be automatically renewed * upon expiring (if it has a valid auto-renew account). */ int64 auto_renew_period = 4; @@ -80,7 +80,7 @@ message Topic { * When a topic is created, its running hash is initialized to 48 bytes of binary zeros. * For each submitted message, the topic's running hash is then updated to the output * of a particular SHA-384 digest whose input data include the previous running hash. - * + * * See the TransactionReceipt.proto documentation for an exact description of the * data included in the SHA-384 digest used for the update. */ diff --git a/sdk/src/main/proto/transaction.proto b/sdk/src/main/proto/transaction.proto index 2f285f393c..a9c7ad0eed 100644 --- a/sdk/src/main/proto/transaction.proto +++ b/sdk/src/main/proto/transaction.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -35,7 +35,7 @@ import "transaction_body.proto"; * appear in the transaction. For example, a CryptoTransfer will first have a Signature * corresponding to the Key for the paying account, followed by a Signature corresponding to the Key * for each account that is sending or receiving cryptocurrency in the transfer. Each Transaction - * should not have more than 50 levels. + * should not have more than 50 levels. * The SignatureList field is deprecated and succeeded by SignatureMap. */ message Transaction { @@ -49,12 +49,12 @@ message Transaction { * SignatureMap field */ SignatureList sigs = 2 [deprecated = true]; - + /** * The signatures on the body with the new format, to authorize the transaction */ SignatureMap sigMap = 3 [deprecated = true]; - + /** * TransactionBody serialized into bytes, which needs to be signed */ diff --git a/sdk/src/main/proto/transaction_body.proto b/sdk/src/main/proto/transaction_body.proto index 24f4eb3e5e..736d7ef33f 100644 --- a/sdk/src/main/proto/transaction_body.proto +++ b/sdk/src/main/proto/transaction_body.proto @@ -18,7 +18,7 @@ package proto; * limitations under the License. */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -371,7 +371,7 @@ message TransactionBody { * This transaction SHALL create a new consensus node record and add * that record to the network address book. */ - com.hedera.hapi.node.addressbook.NodeCreateTransactionBody nodeCreate = 54; + org.hiero.hapi.node.addressbook.NodeCreateTransactionBody nodeCreate = 54; /** * A transaction body for an `updateNode` request. @@ -379,7 +379,7 @@ message TransactionBody { * This transaction SHALL update an existing consensus node record in * the network address book. */ - com.hedera.hapi.node.addressbook.NodeUpdateTransactionBody nodeUpdate = 55; + org.hiero.hapi.node.addressbook.NodeUpdateTransactionBody nodeUpdate = 55; /** * A transaction body for a `deleteNode` request. @@ -387,7 +387,7 @@ message TransactionBody { * This transaction SHALL remove an existing consensus node record from * the network address book. */ - com.hedera.hapi.node.addressbook.NodeDeleteTransactionBody nodeDelete = 56; + org.hiero.hapi.node.addressbook.NodeDeleteTransactionBody nodeDelete = 56; /** * A transaction body to "reject" undesired tokens.
@@ -426,16 +426,16 @@ message TransactionBody { /** * A transaction body for a `tssMessage` request. */ - com.hedera.hapi.services.auxiliary.tss.TssMessageTransactionBody tssMessage = 61; + org.hiero.hapi.services.auxiliary.tss.TssMessageTransactionBody tssMessage = 61; /** * A transaction body for a `tssVote` request. */ - com.hedera.hapi.services.auxiliary.tss.TssVoteTransactionBody tssVote = 62; + org.hiero.hapi.services.auxiliary.tss.TssVoteTransactionBody tssVote = 62; /** * A transaction body for a 'tssShareSignature` request */ - com.hedera.hapi.services.auxiliary.tss.TssShareSignatureTransactionBody tssShareSignature = 63; + org.hiero.hapi.services.auxiliary.tss.TssShareSignatureTransactionBody tssShareSignature = 63; } } diff --git a/sdk/src/main/proto/transaction_contents.proto b/sdk/src/main/proto/transaction_contents.proto index 01f2912216..7ac643322b 100644 --- a/sdk/src/main/proto/transaction_contents.proto +++ b/sdk/src/main/proto/transaction_contents.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/transaction_get_fast_record.proto b/sdk/src/main/proto/transaction_get_fast_record.proto index 49c0e49f83..10ac2d27ce 100644 --- a/sdk/src/main/proto/transaction_get_fast_record.proto +++ b/sdk/src/main/proto/transaction_get_fast_record.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/transaction_get_receipt.proto b/sdk/src/main/proto/transaction_get_receipt.proto index a38c5b74eb..5315456b98 100644 --- a/sdk/src/main/proto/transaction_get_receipt.proto +++ b/sdk/src/main/proto/transaction_get_receipt.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -60,8 +60,8 @@ message TransactionGetReceiptQuery { bool includeDuplicates = 3; /** - * Whether the response should include the receipts of any child transactions spawned by the - * top-level transaction with the given transactionID. + * Whether the response should include the receipts of any child transactions spawned by the + * top-level transaction with the given transactionID. */ bool include_child_receipts = 4; } @@ -94,7 +94,7 @@ message TransactionGetReceiptResponse { repeated TransactionReceipt duplicateTransactionReceipts = 4; /** - * The receipts (if any) of all child transactions spawned by the transaction with the + * The receipts (if any) of all child transactions spawned by the transaction with the * given top-level id, in consensus order. Always empty if the top-level status is UNKNOWN. */ repeated TransactionReceipt child_transaction_receipts = 5; diff --git a/sdk/src/main/proto/transaction_get_record.proto b/sdk/src/main/proto/transaction_get_record.proto index 8f17b99449..0321477456 100644 --- a/sdk/src/main/proto/transaction_get_record.proto +++ b/sdk/src/main/proto/transaction_get_record.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -62,8 +62,8 @@ message TransactionGetRecordQuery { bool includeDuplicates = 3; /** - * Whether the response should include the records of any child transactions spawned by the - * top-level transaction with the given transactionID. + * Whether the response should include the records of any child transactions spawned by the + * top-level transaction with the given transactionID. */ bool include_child_records = 4; } @@ -93,7 +93,7 @@ message TransactionGetRecordResponse { repeated TransactionRecord duplicateTransactionRecords = 4; /** - * The records of processing all child transaction spawned by the transaction with the given + * The records of processing all child transaction spawned by the transaction with the given * top-level id, in consensus order. Always empty if the top-level status is UNKNOWN. */ repeated TransactionRecord child_transaction_records = 5; diff --git a/sdk/src/main/proto/transaction_list.proto b/sdk/src/main/proto/transaction_list.proto index 91e1727407..a6a7b92418 100644 --- a/sdk/src/main/proto/transaction_list.proto +++ b/sdk/src/main/proto/transaction_list.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package proto; -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; option java_multiple_files = true; import "transaction.proto"; diff --git a/sdk/src/main/proto/transaction_receipt.proto b/sdk/src/main/proto/transaction_receipt.proto index 156ec6434d..f50f6c32ab 100644 --- a/sdk/src/main/proto/transaction_receipt.proto +++ b/sdk/src/main/proto/transaction_receipt.proto @@ -18,7 +18,7 @@ package proto; * limitations under the License. */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/transaction_record.proto b/sdk/src/main/proto/transaction_record.proto index 87b84c1171..3efeca6f66 100644 --- a/sdk/src/main/proto/transaction_record.proto +++ b/sdk/src/main/proto/transaction_record.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; @@ -120,12 +120,12 @@ message TransactionRecord { /** * In the record of a CryptoCreate transaction triggered by a user transaction with a - * (previously unused) alias, the new account's alias. + * (previously unused) alias, the new account's alias. */ bytes alias = 16; /** - * The keccak256 hash of the ethereumData. This field will only be populated for + * The keccak256 hash of the ethereumData. This field will only be populated for * EthereumTransaction. */ bytes ethereum_hash = 17; diff --git a/sdk/src/main/proto/transaction_response.proto b/sdk/src/main/proto/transaction_response.proto index 94b98db13b..c3136023ae 100644 --- a/sdk/src/main/proto/transaction_response.proto +++ b/sdk/src/main/proto/transaction_response.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/tss_message.proto b/sdk/src/main/proto/tss_message.proto index 73c495069e..dcfe317ad4 100644 --- a/sdk/src/main/proto/tss_message.proto +++ b/sdk/src/main/proto/tss_message.proto @@ -10,7 +10,7 @@ */ syntax = "proto3"; -package com.hedera.hapi.services.auxiliary.tss; +package org.hiero.hapi.services.auxiliary.tss; /* * Copyright (C) 2024 Hedera Hashgraph, LLC @@ -28,7 +28,7 @@ package com.hedera.hapi.services.auxiliary.tss; * limitations under the License. */ -option java_package = "com.hedera.hapi.services.auxiliary.tss.legacy"; +option java_package = "org.hiero.hapi.services.auxiliary.tss.legacy"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/tss_message_map_key.proto b/sdk/src/main/proto/tss_message_map_key.proto index bd33431563..31d202d427 100644 --- a/sdk/src/main/proto/tss_message_map_key.proto +++ b/sdk/src/main/proto/tss_message_map_key.proto @@ -10,7 +10,7 @@ */ syntax = "proto3"; -package com.hedera.hapi.node.state.tss; +package org.hiero.hapi.node.state.tss; /* * Copyright (C) 2024 Hedera Hashgraph, LLC @@ -28,7 +28,7 @@ package com.hedera.hapi.node.state.tss; * limitations under the License. */ -option java_package = "com.hedera.hapi.node.state.tss.legacy"; +option java_package = "org.hiero.hapi.node.state.tss.legacy"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/tss_share_signature.proto b/sdk/src/main/proto/tss_share_signature.proto index 8ff4b14f19..094a253bac 100644 --- a/sdk/src/main/proto/tss_share_signature.proto +++ b/sdk/src/main/proto/tss_share_signature.proto @@ -12,7 +12,7 @@ */ syntax = "proto3"; -package com.hedera.hapi.services.auxiliary.tss; +package org.hiero.hapi.services.auxiliary.tss; /* * Copyright (C) 2024 Hedera Hashgraph, LLC @@ -30,7 +30,7 @@ package com.hedera.hapi.services.auxiliary.tss; * limitations under the License. */ -option java_package = "com.hedera.hapi.services.auxiliary.tss.legacy"; +option java_package = "org.hiero.hapi.services.auxiliary.tss.legacy"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/tss_vote.proto b/sdk/src/main/proto/tss_vote.proto index 97eb559a43..38cc26bcd3 100644 --- a/sdk/src/main/proto/tss_vote.proto +++ b/sdk/src/main/proto/tss_vote.proto @@ -10,7 +10,7 @@ */ syntax = "proto3"; -package com.hedera.hapi.services.auxiliary.tss; +package org.hiero.hapi.services.auxiliary.tss; /* * Copyright (C) 2024 Hedera Hashgraph, LLC @@ -28,7 +28,7 @@ package com.hedera.hapi.services.auxiliary.tss; * limitations under the License. */ -option java_package = "com.hedera.hapi.services.auxiliary.tss.legacy"; +option java_package = "org.hiero.hapi.services.auxiliary.tss.legacy"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/tss_vote_map_key.proto b/sdk/src/main/proto/tss_vote_map_key.proto index d5a0d66926..570f3e3de4 100644 --- a/sdk/src/main/proto/tss_vote_map_key.proto +++ b/sdk/src/main/proto/tss_vote_map_key.proto @@ -10,7 +10,7 @@ */ syntax = "proto3"; -package com.hedera.hapi.node.state.tss; +package org.hiero.hapi.node.state.tss; /* * Copyright (C) 2024 Hedera Hashgraph, LLC @@ -28,7 +28,7 @@ package com.hedera.hapi.node.state.tss; * limitations under the License. */ -option java_package = "com.hedera.hapi.node.state.tss.legacy"; +option java_package = "org.hiero.hapi.node.state.tss.legacy"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/unchecked_submit.proto b/sdk/src/main/proto/unchecked_submit.proto index 86f3e192b7..7aff81bb62 100644 --- a/sdk/src/main/proto/unchecked_submit.proto +++ b/sdk/src/main/proto/unchecked_submit.proto @@ -22,13 +22,13 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; /** * Submit an arbitrary (serialized) Transaction to the network without prechecks. Requires superuser - * privileges. + * privileges. */ message UncheckedSubmitBody { /** diff --git a/sdk/src/main/proto/util_prng.proto b/sdk/src/main/proto/util_prng.proto index 11ca5d01c9..17af031603 100644 --- a/sdk/src/main/proto/util_prng.proto +++ b/sdk/src/main/proto/util_prng.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package option java_multiple_files = true; diff --git a/sdk/src/main/proto/util_service.proto b/sdk/src/main/proto/util_service.proto index c920c2869c..ac784ea24c 100644 --- a/sdk/src/main/proto/util_service.proto +++ b/sdk/src/main/proto/util_service.proto @@ -22,7 +22,7 @@ package proto; * ‍ */ -option java_package = "com.hedera.hashgraph.sdk.proto"; +option java_package = "org.hiero.sdk.proto"; // <<>> This comment is special code for setting PBJ Compiler java package import "transaction_response.proto"; diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountAllowanceApproveTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountAllowanceApproveTransactionTest.java deleted file mode 100644 index 5d325cb878..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountAllowanceApproveTransactionTest.java +++ /dev/null @@ -1,147 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.CryptoApproveAllowanceTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class AccountAllowanceApproveTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - private static final TokenId testTokenId = TokenId.fromString("1.2.3"); - private static final AccountId testOwnerAccountId = AccountId.fromString("4.5.7"); - private static final AccountId testSpenderAccountId = AccountId.fromString("8.9.0"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - AccountAllowanceApproveTransaction spawnTestTransaction() { - var ownerId = AccountId.fromString("5.6.7"); - return new AccountAllowanceApproveTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .addHbarAllowance(AccountId.fromString("1.1.1"), new Hbar(3)) - .addTokenAllowance(TokenId.fromString("2.2.2"), AccountId.fromString("3.3.3"), 6) - .addTokenNftAllowance(TokenId.fromString("4.4.4").nft(123), AccountId.fromString("5.5.5")) - .addTokenNftAllowance(TokenId.fromString("4.4.4").nft(456), AccountId.fromString("5.5.5")) - .addTokenNftAllowance(TokenId.fromString("8.8.8").nft(456), AccountId.fromString("5.5.5")) - .addTokenNftAllowance(TokenId.fromString("4.4.4").nft(789), AccountId.fromString("9.9.9")) - .addAllTokenNftAllowance(TokenId.fromString("6.6.6"), AccountId.fromString("7.7.7")) - .approveHbarAllowance(ownerId, AccountId.fromString("1.1.1"), new Hbar(3)) - .approveTokenAllowance(TokenId.fromString("2.2.2"), ownerId, AccountId.fromString("3.3.3"), 6) - .approveTokenNftAllowance(TokenId.fromString("4.4.4").nft(123), ownerId, AccountId.fromString("5.5.5")) - .approveTokenNftAllowance(TokenId.fromString("4.4.4").nft(456), ownerId, AccountId.fromString("5.5.5")) - .approveTokenNftAllowance(TokenId.fromString("8.8.8").nft(456), ownerId, AccountId.fromString("5.5.5")) - .approveTokenNftAllowance(TokenId.fromString("4.4.4").nft(789), ownerId, AccountId.fromString("9.9.9")) - .approveTokenNftAllowanceAllSerials(TokenId.fromString("6.6.6"), ownerId, AccountId.fromString("7.7.7")) - .setMaxTransactionFee(Hbar.fromTinybars(100_000)) - .freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = AccountAllowanceApproveTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void propertiesTest() { - var tx = spawnTestTransaction(); - - assertThat(tx.getHbarAllowances()).isNotEmpty(); - assertThat(tx.getHbarApprovals()).isNotEmpty(); - assertThat(tx.getTokenAllowances()).isNotEmpty(); - assertThat(tx.getTokenApprovals()).isNotEmpty(); - assertThat(tx.getTokenNftAllowances()).isNotEmpty(); - assertThat(tx.getTokenNftApprovals()).isNotEmpty(); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new AccountAllowanceApproveTransaction(); - var tx2 = AccountAllowanceApproveTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setCryptoApproveAllowance(CryptoApproveAllowanceTransactionBody.newBuilder().build()) - .build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(AccountAllowanceApproveTransaction.class); - } - - @Test - void deleteNftAllowanceAllSerials() { - var accountAllowanceApproveTransaction = new AccountAllowanceApproveTransaction() - .deleteTokenNftAllowanceAllSerials(testTokenId, testOwnerAccountId, testSpenderAccountId); - - assertThat(accountAllowanceApproveTransaction.getTokenNftApprovals().size()).isEqualTo(1); - assertThat(accountAllowanceApproveTransaction.getTokenNftApprovals().get(0).tokenId).isEqualTo(testTokenId); - assertThat(accountAllowanceApproveTransaction.getTokenNftApprovals().get(0).ownerAccountId).isEqualTo( - testOwnerAccountId); - assertThat(accountAllowanceApproveTransaction.getTokenNftApprovals().get(0).spenderAccountId).isEqualTo( - testSpenderAccountId); - assertTrue(accountAllowanceApproveTransaction.getTokenNftApprovals().get(0).serialNumbers.isEmpty()); - assertFalse(accountAllowanceApproveTransaction.getTokenNftApprovals().get(0).allSerials); - assertNull(accountAllowanceApproveTransaction.getTokenNftApprovals().get(0).delegatingSpender); - } - - @Test - void deleteNftAllowanceAllSerialsFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, - () -> tx.deleteTokenNftAllowanceAllSerials(testTokenId, testOwnerAccountId, testSpenderAccountId)); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountAllowanceApproveTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountAllowanceApproveTransactionTest.snap deleted file mode 100644 index 278c428dc9..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountAllowanceApproveTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.AccountAllowanceApproveTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\ncrypto_approve_allowance {\n crypto_allowances {\n amount: 300000000\n spender {\n account_num: 1\n realm_num: 1\n shard_num: 1\n }\n }\n crypto_allowances {\n amount: 300000000\n owner {\n account_num: 7\n realm_num: 6\n shard_num: 5\n }\n spender {\n account_num: 1\n realm_num: 1\n shard_num: 1\n }\n }\n nft_allowances {\n serial_numbers: 123\n serial_numbers: 456\n spender {\n account_num: 5\n realm_num: 5\n shard_num: 5\n }\n token_id {\n realm_num: 4\n shard_num: 4\n token_num: 4\n }\n }\n nft_allowances {\n serial_numbers: 456\n spender {\n account_num: 5\n realm_num: 5\n shard_num: 5\n }\n token_id {\n realm_num: 8\n shard_num: 8\n token_num: 8\n }\n }\n nft_allowances {\n serial_numbers: 789\n spender {\n account_num: 9\n realm_num: 9\n shard_num: 9\n }\n token_id {\n realm_num: 4\n shard_num: 4\n token_num: 4\n }\n }\n nft_allowances {\n approved_for_all {\n value: true\n }\n spender {\n account_num: 7\n realm_num: 7\n shard_num: 7\n }\n token_id {\n realm_num: 6\n shard_num: 6\n token_num: 6\n }\n }\n nft_allowances {\n owner {\n account_num: 7\n realm_num: 6\n shard_num: 5\n }\n serial_numbers: 123\n serial_numbers: 456\n spender {\n account_num: 5\n realm_num: 5\n shard_num: 5\n }\n token_id {\n realm_num: 4\n shard_num: 4\n token_num: 4\n }\n }\n nft_allowances {\n owner {\n account_num: 7\n realm_num: 6\n shard_num: 5\n }\n serial_numbers: 456\n spender {\n account_num: 5\n realm_num: 5\n shard_num: 5\n }\n token_id {\n realm_num: 8\n shard_num: 8\n token_num: 8\n }\n }\n nft_allowances {\n owner {\n account_num: 7\n realm_num: 6\n shard_num: 5\n }\n serial_numbers: 789\n spender {\n account_num: 9\n realm_num: 9\n shard_num: 9\n }\n token_id {\n realm_num: 4\n shard_num: 4\n token_num: 4\n }\n }\n nft_allowances {\n approved_for_all {\n value: true\n }\n owner {\n account_num: 7\n realm_num: 6\n shard_num: 5\n }\n spender {\n account_num: 7\n realm_num: 7\n shard_num: 7\n }\n token_id {\n realm_num: 6\n shard_num: 6\n token_num: 6\n }\n }\n token_allowances {\n amount: 6\n spender {\n account_num: 3\n realm_num: 3\n shard_num: 3\n }\n token_id {\n realm_num: 2\n shard_num: 2\n token_num: 2\n }\n }\n token_allowances {\n amount: 6\n owner {\n account_num: 7\n realm_num: 6\n shard_num: 5\n }\n spender {\n account_num: 3\n realm_num: 3\n shard_num: 3\n }\n token_id {\n realm_num: 2\n shard_num: 2\n token_num: 2\n }\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountAllowanceDeleteTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountAllowanceDeleteTransactionTest.java deleted file mode 100644 index 44bd40cb77..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountAllowanceDeleteTransactionTest.java +++ /dev/null @@ -1,95 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.CryptoDeleteAllowanceTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; - -public class AccountAllowanceDeleteTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - AccountAllowanceDeleteTransaction spawnTestTransaction() { - var ownerId = AccountId.fromString("5.6.7"); - return new AccountAllowanceDeleteTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .deleteAllHbarAllowances(ownerId) - .deleteAllTokenAllowances(TokenId.fromString("2.2.2"), ownerId) - .deleteAllTokenNftAllowances(TokenId.fromString("4.4.4").nft(123), ownerId) - .deleteAllTokenNftAllowances(TokenId.fromString("4.4.4").nft(456), ownerId) - .deleteAllTokenNftAllowances(TokenId.fromString("8.8.8").nft(456), ownerId) - .deleteAllTokenNftAllowances(TokenId.fromString("4.4.4").nft(789), ownerId) - .setMaxTransactionFee(Hbar.fromTinybars(100_000)) - .freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = AccountAllowanceDeleteTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new AccountAllowanceDeleteTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setCryptoDeleteAllowance(CryptoDeleteAllowanceTransactionBody.newBuilder().build()) - .build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(AccountAllowanceDeleteTransaction.class); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountAllowanceDeleteTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountAllowanceDeleteTransactionTest.snap deleted file mode 100644 index e4c35c14e5..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountAllowanceDeleteTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.AccountAllowanceDeleteTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\ncrypto_delete_allowance {\n nft_allowances {\n owner {\n account_num: 7\n realm_num: 6\n shard_num: 5\n }\n serial_numbers: 123\n serial_numbers: 456\n serial_numbers: 789\n token_id {\n realm_num: 4\n shard_num: 4\n token_num: 4\n }\n }\n nft_allowances {\n owner {\n account_num: 7\n realm_num: 6\n shard_num: 5\n }\n serial_numbers: 456\n token_id {\n realm_num: 8\n shard_num: 8\n token_num: 8\n }\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountBalanceQueryTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountBalanceQueryTest.java deleted file mode 100644 index b43763872d..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountBalanceQueryTest.java +++ /dev/null @@ -1,56 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -public class AccountBalanceQueryTest { - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerializeWithAccountId() { - var builder = com.hedera.hashgraph.sdk.proto.Query.newBuilder(); - new AccountBalanceQuery() - .setAccountId(AccountId.fromString("0.0.5005")) - .onMakeRequest(builder, QueryHeader.newBuilder().build()); - SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")).toMatchSnapshot(); - } - - @Test - void shouldSerializeWithContractId() { - var builder = com.hedera.hashgraph.sdk.proto.Query.newBuilder(); - new AccountBalanceQuery() - .setContractId(ContractId.fromString("0.0.5005")) - .onMakeRequest(builder, QueryHeader.newBuilder().build()); - SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")).toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountBalanceQueryTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountBalanceQueryTest.snap deleted file mode 100644 index c15c62c73a..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountBalanceQueryTest.snap +++ /dev/null @@ -1,8 +0,0 @@ -com.hedera.hashgraph.sdk.AccountBalanceQueryTest.shouldSerializeWithAccountId=[ - "# com.hedera.hashgraph.sdk.proto.Query\ncryptoget_account_balance {\n account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n }\n header {\n }\n}" -] - - -com.hedera.hashgraph.sdk.AccountBalanceQueryTest.shouldSerializeWithContractId=[ - "# com.hedera.hashgraph.sdk.proto.Query\ncryptoget_account_balance {\n contract_i_d {\n contract_num: 5005\n realm_num: 0\n shard_num: 0\n }\n header {\n }\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountCreateTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountCreateTransactionTest.java deleted file mode 100644 index 23f1dee89d..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountCreateTransactionTest.java +++ /dev/null @@ -1,145 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.CryptoCreateTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Duration; -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; - -public class AccountCreateTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - AccountCreateTransaction spawnTestTransaction() { - return new AccountCreateTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setKey(unusedPrivateKey) - .setInitialBalance(Hbar.fromTinybars(450)) - .setProxyAccountId(AccountId.fromString("0.0.1001")) - .setAccountMemo("some dumb memo") - .setReceiverSignatureRequired(true) - .setAutoRenewPeriod(Duration.ofHours(10)) - .setStakedAccountId(AccountId.fromString("0.0.3")) - .setAlias("0x5c562e90feaf0eebd33ea75d21024f249d451417") - .setMaxAutomaticTokenAssociations(100) - .setMaxTransactionFee(Hbar.fromTinybars(100_000)) - .freeze() - .sign(unusedPrivateKey); - } - - AccountCreateTransaction spawnTestTransaction2() { - return new AccountCreateTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setKey(unusedPrivateKey) - .setInitialBalance(Hbar.fromTinybars(450)) - .setProxyAccountId(AccountId.fromString("0.0.1001")) - .setAccountMemo("some dumb memo") - .setReceiverSignatureRequired(true) - .setAutoRenewPeriod(Duration.ofHours(10)) - .setStakedNodeId(4L) - .setMaxAutomaticTokenAssociations(100) - .setMaxTransactionFee(Hbar.fromTinybars(100_000)) - .freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = AccountCreateTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldSerialize2() { - SnapshotMatcher.expect(spawnTestTransaction2().toString()).toMatchSnapshot(); - } - - @Test - void shouldBytes2() throws Exception { - var tx = spawnTestTransaction2(); - var tx2 = AccountCreateTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new AccountCreateTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - - @Test - void propertiesTest() { - var tx = spawnTestTransaction(); - - assertThat(tx.getKey()).isEqualTo(unusedPrivateKey); - assertThat(tx.getInitialBalance()).isEqualTo(Hbar.fromTinybars(450)); - assertThat(tx.getReceiverSignatureRequired()).isTrue(); - assertThat(tx.getProxyAccountId()).hasToString("0.0.1001"); - assertThat(tx.getAutoRenewPeriod().toHours()).isEqualTo(10); - assertThat(tx.getMaxAutomaticTokenAssociations()).isEqualTo(100); - assertThat(tx.getAccountMemo()).isEqualTo("some dumb memo"); - assertThat(tx.getStakedAccountId()).hasToString("0.0.3"); - assertThat(tx.getStakedNodeId()).isNull(); - assertThat(tx.getDeclineStakingReward()).isFalse(); - assertThat(tx.getAlias()).isEqualTo(EvmAddress.fromString("0x5c562e90feaf0eebd33ea75d21024f249d451417")); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setCryptoCreateAccount(CryptoCreateTransactionBody.newBuilder().build()) - .build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(AccountCreateTransaction.class); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountCreateTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountCreateTransactionTest.snap deleted file mode 100644 index dfc3daf78d..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountCreateTransactionTest.snap +++ /dev/null @@ -1,8 +0,0 @@ -com.hedera.hashgraph.sdk.AccountCreateTransactionTest.shouldSerialize2=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\ncrypto_create_account {\n auto_renew_period {\n seconds: 36000\n }\n initial_balance: 450\n key {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n max_automatic_token_associations: 100\n memo: \"some dumb memo\"\n proxy_account_i_d {\n account_num: 1001\n realm_num: 0\n shard_num: 0\n }\n receive_record_threshold: 0\n receiver_sig_required: true\n send_record_threshold: 0\n staked_node_id: 4\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] - - -com.hedera.hashgraph.sdk.AccountCreateTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\ncrypto_create_account {\n alias: \"\\\\V.\\220\\376\\257\\016\\353\\323>\\247]!\\002O$\\235E\\024\\027\"\n auto_renew_period {\n seconds: 36000\n }\n initial_balance: 450\n key {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n max_automatic_token_associations: 100\n memo: \"some dumb memo\"\n proxy_account_i_d {\n account_num: 1001\n realm_num: 0\n shard_num: 0\n }\n receive_record_threshold: 0\n receiver_sig_required: true\n send_record_threshold: 0\n staked_account_id {\n account_num: 3\n realm_num: 0\n shard_num: 0\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountDeleteTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountDeleteTransactionTest.java deleted file mode 100644 index 12148ec8b2..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountDeleteTransactionTest.java +++ /dev/null @@ -1,94 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.CryptoDeleteTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; - -public class AccountDeleteTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction() - .toString() - ).toMatchSnapshot(); - } - - private AccountDeleteTransaction spawnTestTransaction() { - return new AccountDeleteTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setAccountId(AccountId.fromString("0.0.5007")) - .setTransferAccountId(AccountId.fromString("0.0.5008")) - .setMaxTransactionFee(Hbar.fromTinybars(100_000)) - .freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = AccountDeleteTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new AccountDeleteTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setCryptoDelete( - CryptoDeleteTransactionBody.newBuilder() - .setDeleteAccountID(AccountId.fromString("6.6.6").toProtobuf()).build()) - .build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(AccountDeleteTransaction.class); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountDeleteTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountDeleteTransactionTest.snap deleted file mode 100644 index a390abedf2..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountDeleteTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.AccountDeleteTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\ncrypto_delete {\n delete_account_i_d {\n account_num: 5007\n realm_num: 0\n shard_num: 0\n }\n transfer_account_i_d {\n account_num: 5008\n realm_num: 0\n shard_num: 0\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountIdTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountIdTest.java deleted file mode 100644 index 12e242ebb4..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountIdTest.java +++ /dev/null @@ -1,248 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.bouncycastle.util.encoders.Hex; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.util.concurrent.TimeoutException; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -class AccountIdTest { - - static Client mainnetClient; - static Client testnetClient; - static Client previewnetClient; - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - mainnetClient = Client.forMainnet(); - testnetClient = Client.forTestnet(); - previewnetClient = Client.forPreviewnet(); - } - - @AfterAll - public static void afterAll() throws TimeoutException { - mainnetClient.close(); - testnetClient.close(); - previewnetClient.close(); - SnapshotMatcher.validateSnapshots(); - } - - @Test - void fromString() { - SnapshotMatcher.expect(AccountId.fromString("0.0.5005").toString()).toMatchSnapshot(); - } - - @Test - void fromStringWithChecksumOnMainnet() { - SnapshotMatcher.expect(AccountId.fromString("0.0.123-vfmkw").toStringWithChecksum(mainnetClient)).toMatchSnapshot(); - } - - @Test - void fromStringWithChecksumOnTestnet() { - SnapshotMatcher.expect(AccountId.fromString("0.0.123-esxsf").toStringWithChecksum(testnetClient)).toMatchSnapshot(); - } - - @Test - void fromStringWithChecksumOnPreviewnet() { - SnapshotMatcher.expect(AccountId.fromString("0.0.123-ogizo").toStringWithChecksum(previewnetClient)).toMatchSnapshot(); - } - - @Test - void goodChecksumOnMainnet() throws BadEntityIdException { - AccountId.fromString("0.0.123-vfmkw").validateChecksum(mainnetClient); - } - - @Test - void goodChecksumOnTestnet() throws BadEntityIdException { - AccountId.fromString("0.0.123-esxsf").validateChecksum(testnetClient); - } - - @Test - void goodChecksumOnPreviewnet() throws BadEntityIdException { - AccountId.fromString("0.0.123-ogizo").validateChecksum(previewnetClient); - } - - @Test - void badChecksumOnPreviewnet() { - assertThatExceptionOfType(BadEntityIdException.class).isThrownBy(() -> { - AccountId.fromString("0.0.123-ntjli").validateChecksum(previewnetClient); - }); - } - - @Test - void malformedIdString() { - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { - AccountId.fromString("0.0."); - }); - } - - @Test - void malformedIdChecksum() { - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { - AccountId.fromString("0.0.123-ntjl"); - }); - } - - @Test - void malformedIdChecksum2() { - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { - AccountId.fromString("0.0.123-ntjl1"); - }); - } - - @Test - void malformedAliasKey() { - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { - AccountId.fromString("0.0.302a300506032b6570032100114e6abc371b82dab5c15ea149f02d34a012087b163516dd70f44acafabf777"); - }); - } - - @Test - void malformedAliasKey2() { - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { - AccountId.fromString("0.0.302a300506032b6570032100114e6abc371b82dab5c15ea149f02d34a012087b163516dd70f44acafabf777g"); - }); - } - - @Test - void malformedAliasKey3() { - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { - AccountId.fromString("0.0.303a300506032b6570032100114e6abc371b82dab5c15ea149f02d34a012087b163516dd70f44acafabf7777"); - }); - } - - @Test - void fromStringWithAliasKey() { - SnapshotMatcher.expect(AccountId.fromString("0.0.302a300506032b6570032100114e6abc371b82dab5c15ea149f02d34a012087b163516dd70f44acafabf7777").toString()).toMatchSnapshot(); - } - - @Test - void fromStringWithEvmAddress() { - SnapshotMatcher.expect(AccountId.fromString("0.0.302a300506032b6570032100114e6abc371b82da").toString()).toMatchSnapshot(); - } - - @Test - void fromSolidityAddress() { - SnapshotMatcher.expect(AccountId.fromSolidityAddress("000000000000000000000000000000000000138D").toString()).toMatchSnapshot(); - } - - @Test - void fromSolidityAddressWith0x() { - SnapshotMatcher.expect(AccountId.fromSolidityAddress("0x000000000000000000000000000000000000138D").toString()).toMatchSnapshot(); - } - - @Test - void toBytes() throws InvalidProtocolBufferException { - SnapshotMatcher.expect(Hex.toHexString(new AccountId(5005).toProtobuf().toByteArray())).toMatchSnapshot(); - } - - @Test - void toBytesAlias() { - SnapshotMatcher.expect(Hex.toHexString(AccountId.fromString("0.0.302a300506032b6570032100114e6abc371b82dab5c15ea149f02d34a012087b163516dd70f44acafabf7777").toBytes())).toMatchSnapshot(); - } - - @Test - void toBytesEvmAddress() { - SnapshotMatcher.expect(Hex.toHexString(AccountId.fromString("0.0.302a300506032b6570032100114e6abc371b82da").toBytes())).toMatchSnapshot(); - } - - @Test - void fromBytes() throws InvalidProtocolBufferException { - SnapshotMatcher.expect(AccountId.fromBytes(new AccountId(5005).toBytes()).toString()).toMatchSnapshot(); - } - - @Test - void toFromProtobuf() { - var id1 = new AccountId(5005); - var id2 = AccountId.fromProtobuf(id1.toProtobuf()); - assertThat(id2).isEqualTo(id1); - } - - @Test - void fromBytesAlias() throws InvalidProtocolBufferException { - SnapshotMatcher.expect(AccountId.fromBytes(AccountId.fromString("0.0.302a300506032b6570032100114e6abc371b82dab5c15ea149f02d34a012087b163516dd70f44acafabf7777").toBytes()).toString()).toMatchSnapshot(); - } - - @Test - void toFromProtobufAliasKey() { - var id1 = AccountId.fromString("0.0.302a300506032b6570032100114e6abc371b82dab5c15ea149f02d34a012087b163516dd70f44acafabf7777"); - var id2 = AccountId.fromProtobuf(id1.toProtobuf()); - assertThat(id2).isEqualTo(id1); - } - - @Test - void toFromProtobufEcdsaAliasKey() { - var id1 = AccountId.fromString("0.0.302d300706052b8104000a032200035d348292bbb8b511fdbe24e3217ec099944b4728999d337f9a025f4193324525"); - var id2 = AccountId.fromProtobuf(id1.toProtobuf()); - assertThat(id2).isEqualTo(id1); - } - - @Test - void fromBytesEvmAddress() throws InvalidProtocolBufferException { - SnapshotMatcher.expect(AccountId.fromBytes(AccountId.fromString("0.0.302a300506032b6570032100114e6abc371b82da").toBytes()).toString()).toMatchSnapshot(); - } - - @Test - void toFromProtobufEvmAddress() { - var id1 = AccountId.fromString("0.0.302a300506032b6570032100114e6abc371b82da"); - var id2 = AccountId.fromProtobuf(id1.toProtobuf()); - assertThat(id2).isEqualTo(id1); - } - - @Test - void toFromProtobufRawEvmAddress() { - var id1 = AccountId.fromString("302a300506032b6570032100114e6abc371b82da"); - var id2 = AccountId.fromProtobuf(id1.toProtobuf()); - assertThat(id2).isEqualTo(id1); - } - - @Test - void toSolidityAddress() { - SnapshotMatcher.expect(new AccountId(5005).toSolidityAddress()).toMatchSnapshot(); - } - - @Test - void fromEvmAddress() { - String evmAddress = "302a300506032b6570032100114e6abc371b82da"; - var id = AccountId.fromEvmAddress(evmAddress, 5, 9); - - assertThat(id.evmAddress).hasToString(evmAddress); - assertThat(id.shard).isEqualTo(5); - assertThat(id.realm).isEqualTo(9); - } - - @Test - void fromEvmAddressWithPrefix() { - String evmAddressString = "302a300506032b6570032100114e6abc371b82da"; - EvmAddress evmAddress = EvmAddress.fromString(evmAddressString); - var id1 = AccountId.fromEvmAddress(evmAddress); - var id2 = AccountId.fromEvmAddress("0x" + evmAddressString); - assertThat(id2).isEqualTo(id1); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountIdTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountIdTest.snap deleted file mode 100644 index d1bb2f2fc1..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountIdTest.snap +++ /dev/null @@ -1,88 +0,0 @@ -com.hedera.hashgraph.sdk.AccountIdTest.fromBytes=[ - "0.0.5005" -] - - -com.hedera.hashgraph.sdk.AccountIdTest.fromBytesAlias=[ - "0.0.302a300506032b6570032100114e6abc371b82dab5c15ea149f02d34a012087b163516dd70f44acafabf7777" -] - - -com.hedera.hashgraph.sdk.AccountIdTest.fromBytesAliasEvmAddress=[ - "0.0.302a300506032b6570032100114e6abc371b82da" -] - - -com.hedera.hashgraph.sdk.AccountIdTest.fromBytesEvmAddress=[ - "0.0.302a300506032b6570032100114e6abc371b82da" -] - - -com.hedera.hashgraph.sdk.AccountIdTest.fromSolidityAddress=[ - "0.0.5005" -] - - -com.hedera.hashgraph.sdk.AccountIdTest.fromSolidityAddressWith0x=[ - "0.0.5005" -] - - -com.hedera.hashgraph.sdk.AccountIdTest.fromString=[ - "0.0.5005" -] - - -com.hedera.hashgraph.sdk.AccountIdTest.fromStringWithAliasEvmAddress=[ - "0.0.302a300506032b6570032100114e6abc371b82da" -] - - -com.hedera.hashgraph.sdk.AccountIdTest.fromStringWithAliasKey=[ - "0.0.302a300506032b6570032100114e6abc371b82dab5c15ea149f02d34a012087b163516dd70f44acafabf7777" -] - - -com.hedera.hashgraph.sdk.AccountIdTest.fromStringWithChecksumOnMainnet=[ - "0.0.123-vfmkw" -] - - -com.hedera.hashgraph.sdk.AccountIdTest.fromStringWithChecksumOnPreviewnet=[ - "0.0.123-ogizo" -] - - -com.hedera.hashgraph.sdk.AccountIdTest.fromStringWithChecksumOnTestnet=[ - "0.0.123-esxsf" -] - - -com.hedera.hashgraph.sdk.AccountIdTest.fromStringWithEvmAddress=[ - "0.0.302a300506032b6570032100114e6abc371b82da" -] - - -com.hedera.hashgraph.sdk.AccountIdTest.toBytes=[ - "188d27" -] - - -com.hedera.hashgraph.sdk.AccountIdTest.toBytesAlias=[ - "22221220114e6abc371b82dab5c15ea149f02d34a012087b163516dd70f44acafabf7777" -] - - -com.hedera.hashgraph.sdk.AccountIdTest.toBytesAliasEvmAddress=[ - "2214302a300506032b6570032100114e6abc371b82da" -] - - -com.hedera.hashgraph.sdk.AccountIdTest.toBytesEvmAddress=[ - "2214302a300506032b6570032100114e6abc371b82da" -] - - -com.hedera.hashgraph.sdk.AccountIdTest.toSolidityAddress=[ - "000000000000000000000000000000000000138d" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountInfoQueryTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountInfoQueryTest.java deleted file mode 100644 index 4a74a35283..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountInfoQueryTest.java +++ /dev/null @@ -1,48 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -public class AccountInfoQueryTest { - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - var builder = com.hedera.hashgraph.sdk.proto.Query.newBuilder(); - new AccountInfoQuery() - .setAccountId(AccountId.fromString("0.0.5005")) - .setMaxQueryPayment(Hbar.fromTinybars(100_000)) - .onMakeRequest(builder, QueryHeader.newBuilder().build()); - SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")).toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountInfoQueryTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountInfoQueryTest.snap deleted file mode 100644 index bf3cb17f93..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountInfoQueryTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.AccountInfoQueryTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.Query\ncrypto_get_info {\n account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n }\n header {\n }\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountInfoTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountInfoTest.java deleted file mode 100644 index 2500af52a5..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountInfoTest.java +++ /dev/null @@ -1,94 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.CryptoGetInfoResponse; -import com.hedera.hashgraph.sdk.proto.KeyList; -import com.hedera.hashgraph.sdk.proto.LiveHash; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Duration; -import java.time.Instant; - -public class AccountInfoTest { - private static final PrivateKey privateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - private static final byte[] hash = {0, 1, 2}; - private static final LiveHash liveHash = LiveHash.newBuilder().setAccountId(new AccountId(10).toProtobuf()) - .setDuration(DurationConverter.toProtobuf(Duration.ofDays(11))) - .setHash(ByteString.copyFrom(hash)) - .setKeys(KeyList.newBuilder().addKeys(privateKey.getPublicKey().toProtobufKey())) - .build(); - private static final CryptoGetInfoResponse.AccountInfo info = CryptoGetInfoResponse.AccountInfo.newBuilder() - .setAccountID(new AccountId(1).toProtobuf()) - .setDeleted(true) - .setProxyReceived(2) - .setKey(privateKey.getPublicKey().toProtobufKey()) - .setBalance(3) - .setGenerateSendRecordThreshold(4) - .setGenerateReceiveRecordThreshold(5) - .setReceiverSigRequired(true) - .setExpirationTime(InstantConverter.toProtobuf(Instant.ofEpochMilli(6))) - .setAutoRenewPeriod(DurationConverter.toProtobuf(Duration.ofDays(7))) - .setProxyAccountID(new AccountId(8).toProtobuf()) - .addLiveHashes(liveHash) - .setLedgerId(LedgerId.PREVIEWNET.toByteString()) - .setEthereumNonce(1001) - .build(); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void fromProtobufWithOtherOptions() { - SnapshotMatcher.expect(AccountInfo.fromProtobuf(info).toString()) - .toMatchSnapshot(); - } - - @Test - void fromBytes() throws InvalidProtocolBufferException { - SnapshotMatcher.expect(AccountInfo.fromBytes(info.toByteArray()).toString()) - .toMatchSnapshot(); - } - - @Test - void toBytes() throws InvalidProtocolBufferException { - SnapshotMatcher.expect(AccountInfo.fromBytes(info.toByteArray()).toBytes()) - .toMatchSnapshot(); - } - - @Test - void toProtobuf() throws InvalidProtocolBufferException { - SnapshotMatcher.expect(AccountInfo.fromProtobuf(info).toProtobuf().toString()) - .toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountInfoTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountInfoTest.snap deleted file mode 100644 index 11e3c7785e..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountInfoTest.snap +++ /dev/null @@ -1,18 +0,0 @@ -com.hedera.hashgraph.sdk.AccountInfoTest.fromBytes=[ - "AccountInfo{accountId=0.0.1, contractAccountId=, deleted=true, proxyAccountId=0.0.8, proxyReceived=2 tℏ, key=302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7, balance=3 tℏ, sendRecordThreshold=4 tℏ, receiveRecordThreshold=5 tℏ, receiverSignatureRequired=true, expirationTime=1970-01-01T00:00:00.006Z, autoRenewPeriod=PT168H, liveHashes=[LiveHash{accountId=0.0.10, hash=[0, 1, 2], keys=KeyList{threshold=null, keys=[302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7]}, duration=PT264H}], tokenRelationships={}, accountMemo=, ownedNfts=0, maxAutomaticTokenAssociations=0, aliasKey=null, ledgerId=previewnet, ethereumNonce=1001, stakingInfo=null}" -] - - -com.hedera.hashgraph.sdk.AccountInfoTest.fromProtobufWithOtherOptions=[ - "AccountInfo{accountId=0.0.1, contractAccountId=, deleted=true, proxyAccountId=0.0.8, proxyReceived=2 tℏ, key=302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7, balance=3 tℏ, sendRecordThreshold=4 tℏ, receiveRecordThreshold=5 tℏ, receiverSignatureRequired=true, expirationTime=1970-01-01T00:00:00.006Z, autoRenewPeriod=PT168H, liveHashes=[LiveHash{accountId=0.0.10, hash=[0, 1, 2], keys=KeyList{threshold=null, keys=[302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7]}, duration=PT264H}], tokenRelationships={}, accountMemo=, ownedNfts=0, maxAutomaticTokenAssociations=0, aliasKey=null, ledgerId=previewnet, ethereumNonce=1001, stakingInfo=null}" -] - - -com.hedera.hashgraph.sdk.AccountInfoTest.toBytes=[ - "CgIYARgBIgIYCDACOiISIODI7CdYpYef+sImoTwMUWt5nnLjUUGg3YKPlNN5iKS3QANIBFAFWAFiBRCAm+4CagQIgPUkcjUKAhgKEgMAAQIaJAoiEiDgyOwnWKWHn/rCJqE8DFFreZ5y41FBoN2Cj5TTeYiktyoECICBOqIBAQKoAekH" -] - - -com.hedera.hashgraph.sdk.AccountInfoTest.toProtobuf=[ - "# com.hedera.hashgraph.sdk.proto.CryptoGetInfoResponse$AccountInfo@57c9441\naccount_i_d {\n account_num: 1\n realm_num: 0\n shard_num: 0\n}\nauto_renew_period {\n seconds: 604800\n}\nbalance: 3\ndeleted: true\nethereum_nonce: 1001\nexpiration_time {\n nanos: 6000000\n seconds: 0\n}\ngenerate_receive_record_threshold: 5\ngenerate_send_record_threshold: 4\nkey {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n}\nledger_id: \"\\002\"\nlive_hashes {\n account_id {\n account_num: 10\n realm_num: 0\n shard_num: 0\n }\n duration {\n seconds: 950400\n }\n hash: \"\\000\\001\\002\"\n keys {\n keys {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n }\n}\nowned_nfts: 0\nproxy_account_i_d {\n account_num: 8\n realm_num: 0\n shard_num: 0\n}\nproxy_received: 2\nreceiver_sig_required: true" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountRecordsQueryTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountRecordsQueryTest.java deleted file mode 100644 index 5e2d4366e3..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountRecordsQueryTest.java +++ /dev/null @@ -1,48 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -public class AccountRecordsQueryTest { - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - var builder = com.hedera.hashgraph.sdk.proto.Query.newBuilder(); - new AccountRecordsQuery() - .setAccountId(AccountId.fromString("0.0.5005")) - .setMaxQueryPayment(Hbar.fromTinybars(100_000)) - .onMakeRequest(builder, QueryHeader.newBuilder().build()); - SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")).toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountRecordsQueryTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountRecordsQueryTest.snap deleted file mode 100644 index 9785e76039..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountRecordsQueryTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.AccountRecordsQueryTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.Query\ncrypto_get_account_records {\n account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n }\n header {\n }\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountStakersQueryTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountStakersQueryTest.java deleted file mode 100644 index 2015b2e3cf..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountStakersQueryTest.java +++ /dev/null @@ -1,48 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -public class AccountStakersQueryTest { - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - var builder = com.hedera.hashgraph.sdk.proto.Query.newBuilder(); - new AccountStakersQuery() - .setAccountId(AccountId.fromString("0.0.5005")) - .setMaxQueryPayment(Hbar.fromTinybars(100_000)) - .onMakeRequest(builder, QueryHeader.newBuilder().build()); - SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")).toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountStakersQueryTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountStakersQueryTest.snap deleted file mode 100644 index d85d2964f2..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountStakersQueryTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.AccountStakersQueryTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.Query\ncrypto_get_proxy_stakers {\n account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n }\n header {\n }\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountUpdateTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountUpdateTransactionTest.java deleted file mode 100644 index 8e6f4655d8..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountUpdateTransactionTest.java +++ /dev/null @@ -1,128 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.CryptoUpdateTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Duration; -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; - -public class AccountUpdateTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - AccountUpdateTransaction spawnTestTransaction() { - return new AccountUpdateTransaction() - .setKey(unusedPrivateKey) - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setAccountId(AccountId.fromString("0.0.2002")) - .setProxyAccountId(AccountId.fromString("0.0.1001")) - .setAutoRenewPeriod(Duration.ofHours(10)) - .setExpirationTime(Instant.ofEpochSecond(1554158543)) - .setReceiverSignatureRequired(false) - .setMaxAutomaticTokenAssociations(100) - .setAccountMemo("Some memo") - .setMaxTransactionFee(Hbar.fromTinybars(100_000)) - .setStakedAccountId(AccountId.fromString("0.0.3")) - .freeze() - .sign(unusedPrivateKey); - } - - AccountUpdateTransaction spawnTestTransaction2() { - return new AccountUpdateTransaction() - .setKey(unusedPrivateKey) - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setAccountId(AccountId.fromString("0.0.2002")) - .setProxyAccountId(AccountId.fromString("0.0.1001")) - .setAutoRenewPeriod(Duration.ofHours(10)) - .setExpirationTime(Instant.ofEpochSecond(1554158543)) - .setReceiverSignatureRequired(false) - .setMaxAutomaticTokenAssociations(100) - .setAccountMemo("Some memo") - .setMaxTransactionFee(Hbar.fromTinybars(100_000)) - .setStakedNodeId(4L) - .freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = AccountUpdateTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new AccountUpdateTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldSerialize2() { - SnapshotMatcher.expect(spawnTestTransaction2().toString()).toMatchSnapshot(); - } - - @Test - void shouldBytes2() throws Exception { - var tx = spawnTestTransaction2(); - var tx2 = AccountUpdateTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setCryptoUpdateAccount(CryptoUpdateTransactionBody.newBuilder().build()) - .build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(AccountUpdateTransaction.class); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountUpdateTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountUpdateTransactionTest.snap deleted file mode 100644 index c0b99c66ed..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/AccountUpdateTransactionTest.snap +++ /dev/null @@ -1,8 +0,0 @@ -com.hedera.hashgraph.sdk.AccountUpdateTransactionTest.shouldSerialize2=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\ncrypto_update_account {\n account_i_d_to_update {\n account_num: 2002\n realm_num: 0\n shard_num: 0\n }\n auto_renew_period {\n seconds: 36000\n }\n expiration_time {\n seconds: 1554158543\n }\n key {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n max_automatic_token_associations {\n value: 100\n }\n memo {\n value: \"Some memo\"\n }\n proxy_account_i_d {\n account_num: 1001\n realm_num: 0\n shard_num: 0\n }\n receiver_sig_required_wrapper {\n }\n staked_node_id: 4\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] - - -com.hedera.hashgraph.sdk.AccountUpdateTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\ncrypto_update_account {\n account_i_d_to_update {\n account_num: 2002\n realm_num: 0\n shard_num: 0\n }\n auto_renew_period {\n seconds: 36000\n }\n expiration_time {\n seconds: 1554158543\n }\n key {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n max_automatic_token_associations {\n value: 100\n }\n memo {\n value: \"Some memo\"\n }\n proxy_account_i_d {\n account_num: 1001\n realm_num: 0\n shard_num: 0\n }\n receiver_sig_required_wrapper {\n }\n staked_account_id {\n account_num: 3\n realm_num: 0\n shard_num: 0\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/AddressBookQueryMockTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/AddressBookQueryMockTest.java deleted file mode 100644 index 15b12394e4..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/AddressBookQueryMockTest.java +++ /dev/null @@ -1,237 +0,0 @@ -package com.hedera.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.mirror.NetworkServiceGrpc; -import io.grpc.Server; -import io.grpc.Status; -import io.grpc.inprocess.InProcessServerBuilder; -import io.grpc.stub.StreamObserver; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; - -import java.time.Duration; -import java.util.ArrayDeque; -import java.util.Collections; -import java.util.List; -import java.util.Queue; - -import static com.hedera.hashgraph.sdk.BaseNodeAddress.PORT_NODE_PLAIN; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatException; - -class AddressBookQueryMockTest { - - private Client client; - final private AddressBookQueryStub addressBookServiceStub = new AddressBookQueryStub(); - private Server server; - private AddressBookQuery addressBookQuery; - - @BeforeEach - void setup() throws Exception { - client = Client.forNetwork(Collections.emptyMap()); - client.setMirrorNetwork(List.of("in-process:test")); - server = InProcessServerBuilder.forName("test") - .addService(addressBookServiceStub) - .directExecutor() - .build() - .start(); - addressBookQuery = new AddressBookQuery(); - addressBookQuery.setFileId(FileId.ADDRESS_BOOK); - } - - @AfterEach - void teardown() throws Exception { - addressBookServiceStub.verify(); - if (client != null) { - client.close(); - } - if (server != null) { - server.shutdown(); - server.awaitTermination(); - } - } - - @ParameterizedTest(name = "[{0}] AddressBookQuery works") - @CsvSource({"sync", "async"}) - void addressBookQueryWorks(String executeVersion) throws Throwable { - addressBookServiceStub.requests.add( - com.hedera.hashgraph.sdk.proto.mirror.AddressBookQuery.newBuilder() - .setFileId(FileId.ADDRESS_BOOK.toProtobuf()) - .setLimit(3) - .build() - ); - addressBookServiceStub.responses.add( - new com.hedera.hashgraph.sdk.NodeAddress() - .setAccountId(AccountId.fromString("0.0.3")) - .toProtobuf() - ); - - addressBookQuery.setLimit(3); - - var nodes = executeVersion.equals("sync") ? - addressBookQuery.execute(client) : - addressBookQuery.executeAsync(client).get(); - assertThat(nodes.nodeAddresses).hasSize(1); - assertThat(nodes.nodeAddresses.get(0).accountId).isEqualTo(AccountId.fromString("0.0.3")); - } - - Endpoint spawnEndpoint() { - return new Endpoint() - .setAddress(new byte[] {0x00, 0x01, 0x02, 0x03}) - .setDomainName("unit.test.com") - .setPort(PORT_NODE_PLAIN); - } - - @Test - void networkUpdatePeriodWorks() throws Throwable { - addressBookServiceStub.requests.add( - com.hedera.hashgraph.sdk.proto.mirror.AddressBookQuery.newBuilder() - .setFileId(FileId.ADDRESS_BOOK.toProtobuf()) - .build() - ); - addressBookServiceStub.responses.add( - new com.hedera.hashgraph.sdk.NodeAddress() - .setAccountId(AccountId.fromString("0.0.3")) - .setAddresses(Collections.singletonList(spawnEndpoint())) - .toProtobuf() - ); - - client.setNetworkUpdatePeriod(Duration.ofSeconds(1)); - Thread.sleep(1400); - - var clientNetwork = client.getNetwork(); - assertThat(clientNetwork).hasSize(1); - assertThat(clientNetwork.values()).contains(AccountId.fromString("0.0.3")); - } - - @ParameterizedTest(name = "[{0}] Retry recovers w/ status {1} and description {2}") - @CsvSource({ - "sync, INTERNAL, internal RST_STREAM error", - "sync, INTERNAL, rst stream", - "sync, RESOURCE_EXHAUSTED, ", - "sync, UNAVAILABLE, ", - "async, INTERNAL, internal RST_STREAM error", - "async, INTERNAL, rst stream", - "async, RESOURCE_EXHAUSTED, ", - "async, UNAVAILABLE, " - }) - void addressBookQueryRetries(String executeVersion, Status.Code code, String description) throws Throwable { - addressBookServiceStub.requests.add( - com.hedera.hashgraph.sdk.proto.mirror.AddressBookQuery.newBuilder() - .setFileId(FileId.ADDRESS_BOOK.toProtobuf()) - .build() - ); - addressBookServiceStub.requests.add( - com.hedera.hashgraph.sdk.proto.mirror.AddressBookQuery.newBuilder() - .setFileId(FileId.ADDRESS_BOOK.toProtobuf()) - .build() - ); - addressBookServiceStub.responses.add(code.toStatus().withDescription(description).asRuntimeException()); - addressBookServiceStub.responses.add( - new com.hedera.hashgraph.sdk.NodeAddress() - .setAccountId(AccountId.fromString("0.0.3")) - .toProtobuf() - ); - - var nodes = executeVersion.equals("sync") ? - addressBookQuery.execute(client) : - addressBookQuery.executeAsync(client).get(); - assertThat(nodes.nodeAddresses).hasSize(1); - assertThat(nodes.nodeAddresses.get(0).accountId).isEqualTo(AccountId.fromString("0.0.3")); - } - - @ParameterizedTest(name = "No retry w/ status {0} and description {1}") - @CsvSource({ - "sync, INTERNAL, internal first_stream error", - "sync, INTERNAL, internal error", - "sync, INTERNAL, ", - "sync, INVALID_ARGUMENT, ", - "async, INTERNAL, internal first_stream error", - "async, INTERNAL, internal error", - "async, INTERNAL, ", - "async, INVALID_ARGUMENT, " - }) - void addressBookQueryFails(String executeVersion, Status.Code code, String description) { - addressBookServiceStub.requests.add(com.hedera.hashgraph.sdk.proto.mirror.AddressBookQuery.newBuilder() - .setFileId(FileId.ADDRESS_BOOK.toProtobuf()) - .build() - ); - addressBookServiceStub.responses.add(code.toStatus().withDescription(description).asRuntimeException()); - - assertThatException().isThrownBy(() -> { - var result = executeVersion.equals("sync") ? - addressBookQuery.execute(client) : - addressBookQuery.executeAsync(client).get(); - }); - } - - @ParameterizedTest(name = "[{0}] address book query stops at max attempts w/ status {1} and description {2}") - @CsvSource({ - "sync, INTERNAL, internal RST_STREAM error", - "sync, INTERNAL, rst stream", - "sync, RESOURCE_EXHAUSTED, ", - "sync, UNAVAILABLE, ", - "async, INTERNAL, internal RST_STREAM error", - "async, INTERNAL, rst stream", - "async, RESOURCE_EXHAUSTED, ", - "async, UNAVAILABLE, " - }) - void addressBookQueryStopsAtMaxAttempts(String executeVersion, Status.Code code, String description) throws Throwable { - addressBookQuery.setMaxAttempts(2); - - addressBookServiceStub.requests.add( - com.hedera.hashgraph.sdk.proto.mirror.AddressBookQuery.newBuilder() - .setFileId(FileId.ADDRESS_BOOK.toProtobuf()) - .build() - ); - addressBookServiceStub.requests.add( - com.hedera.hashgraph.sdk.proto.mirror.AddressBookQuery.newBuilder() - .setFileId(FileId.ADDRESS_BOOK.toProtobuf()) - .build() - ); - addressBookServiceStub.responses.add(code.toStatus().withDescription(description).asRuntimeException()); - addressBookServiceStub.responses.add(code.toStatus().withDescription(description).asRuntimeException()); - - assertThatException().isThrownBy(() -> { - var result = executeVersion.equals("sync") ? - addressBookQuery.execute(client) : - addressBookQuery.executeAsync(client).get(); - }); - } - - private static class AddressBookQueryStub extends NetworkServiceGrpc.NetworkServiceImplBase { - - private final Queue requests = new ArrayDeque<>(); - private final Queue responses = new ArrayDeque<>(); - - @Override - public void getNodes( - com.hedera.hashgraph.sdk.proto.mirror.AddressBookQuery addressBookQuery, - StreamObserver streamObserver - ) { - var request = requests.poll(); - assertThat(request).isNotNull(); - assertThat(addressBookQuery).isEqualTo(request); - - while (!responses.isEmpty()) { - var response = responses.poll(); - assertThat(response).isNotNull(); - - if (response instanceof Throwable) { - streamObserver.onError((Throwable) response); - return; - } - - streamObserver.onNext((com.hedera.hashgraph.sdk.proto.NodeAddress) response); - } - streamObserver.onCompleted(); - } - - public void verify() { - assertThat(requests).isEmpty(); - assertThat(responses).isEmpty(); - } - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/AllowancesTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/AllowancesTest.java deleted file mode 100644 index 11b2353203..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/AllowancesTest.java +++ /dev/null @@ -1,108 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - -public class AllowancesTest { - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - TokenAllowance spawnTokenAllowance() { - return new TokenAllowance( - TokenId.fromString("1.2.3"), - AccountId.fromString("4.5.6"), - AccountId.fromString("5.5.5"), - 777 - ); - } - - TokenNftAllowance spawnNftAllowance() { - List serials = new ArrayList<>(); - serials.add(123L); - serials.add(456L); - return new TokenNftAllowance( - TokenId.fromString("1.1.1"), - AccountId.fromString("2.2.2"), - AccountId.fromString("3.3.3"), - null, - serials, - null - ); - } - - TokenNftAllowance spawnAllNftAllowance() { - return new TokenNftAllowance( - TokenId.fromString("1.1.1"), - AccountId.fromString("2.2.2"), - AccountId.fromString("3.3.3"), - null, - Collections.emptyList(), - true - ); - } - - HbarAllowance spawnHbarAllowance() { - return new HbarAllowance(AccountId.fromString("1.1.1"), AccountId.fromString("2.2.2"), new Hbar(3)); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect( - spawnHbarAllowance().toString(), - spawnTokenAllowance().toString(), - spawnNftAllowance().toString(), - spawnAllNftAllowance().toString() - ).toMatchSnapshot(); - } - - @Test - void shouldBytes() throws InvalidProtocolBufferException { - var hbar1 = spawnHbarAllowance(); - var token1 = spawnTokenAllowance(); - var nft1 = spawnNftAllowance(); - var allNft1 = spawnAllNftAllowance(); - var hbar2 = HbarAllowance.fromBytes(hbar1.toBytes()); - var token2 = TokenAllowance.fromBytes(token1.toBytes()); - var nft2 = TokenNftAllowance.fromBytes(nft1.toBytes()); - var allNft2 = TokenNftAllowance.fromBytes(allNft1.toBytes()); - assertThat(hbar2.toString()).isEqualTo(hbar1.toString()); - assertThat(token2.toString()).isEqualTo(token1.toString()); - assertThat(nft2.toString()).isEqualTo(nft1.toString()); - assertThat(allNft2.toString()).isEqualTo(allNft1.toString()); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/AssessedCustomFeeTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/AssessedCustomFeeTest.java deleted file mode 100644 index d111a21bd2..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/AssessedCustomFeeTest.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.hedera.hashgraph.sdk; - -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - -public class AssessedCustomFeeTest { - - private static final int amount = 1; - private static final TokenId tokenId = new TokenId(2, 3, 4); - private static final AccountId feeCollector = new AccountId(5, 6, 7); - private static final List payerAccountIds = List.of( - new AccountId(8, 9, 10), - new AccountId(11, 12, 13), - new AccountId(14, 15, 16) - ); - - private final com.hedera.hashgraph.sdk.proto.AssessedCustomFee fee = - com.hedera.hashgraph.sdk.proto.AssessedCustomFee.newBuilder() - .setAmount(amount) - .setTokenId(tokenId.toProtobuf()) - .setFeeCollectorAccountId(feeCollector.toProtobuf()) - .addAllEffectivePayerAccountId(payerAccountIds.stream().map(AccountId::toProtobuf).toList()) - .build(); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - AssessedCustomFee spawnAssessedCustomFeeExample() { - return new AssessedCustomFee( - 201, - TokenId.fromString("1.2.3"), - AccountId.fromString("4.5.6"), - List.of( - AccountId.fromString("0.0.1"), - AccountId.fromString("0.0.2"), - AccountId.fromString("0.0.3") - ) - ); - } - - @Test - void shouldSerialize() throws Exception { - var originalAssessedCustomFee = spawnAssessedCustomFeeExample(); - byte[] assessedCustomFeeBytes = originalAssessedCustomFee.toBytes(); - var copyAssessedCustomFee = AssessedCustomFee.fromBytes(assessedCustomFeeBytes); - assertThat(originalAssessedCustomFee.toString().replaceAll("@[A-Za-z0-9]+", "")) - .isEqualTo(copyAssessedCustomFee.toString().replaceAll("@[A-Za-z0-9]+", "")); - SnapshotMatcher.expect(originalAssessedCustomFee.toString().replaceAll("@[A-Za-z0-9]+", "")).toMatchSnapshot(); - } - - @Test - void fromProtobuf() { - SnapshotMatcher.expect(AssessedCustomFee.fromProtobuf(fee).toString()).toMatchSnapshot(); - } - - @Test - void toProtobuf() { - SnapshotMatcher.expect(AssessedCustomFee.fromProtobuf(fee).toProtobuf().toString()).toMatchSnapshot(); - } - - @Test - void shouldBytes() throws Exception { - var assessedCustomFee = spawnAssessedCustomFeeExample(); - var tx2 = AssessedCustomFee.fromBytes(assessedCustomFee.toBytes()); - assertThat(tx2).hasToString(assessedCustomFee.toString()); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/AssessedCustomFeeTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/AssessedCustomFeeTest.snap deleted file mode 100644 index f148579e50..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/AssessedCustomFeeTest.snap +++ /dev/null @@ -1,13 +0,0 @@ -com.hedera.hashgraph.sdk.AssessedCustomFeeTest.fromProtobuf=[ - "AssessedCustomFee{amount=1, tokenId=2.3.4, feeCollectorAccountId=5.6.7, payerAccountIdList=[8.9.10, 11.12.13, 14.15.16]}" -] - - -com.hedera.hashgraph.sdk.AssessedCustomFeeTest.shouldSerialize=[ - "AssessedCustomFee{amount=201, tokenId=1.2.3, feeCollectorAccountId=4.5.6, payerAccountIdList=[0.0.1, 0.0.2, 0.0.3]}" -] - - -com.hedera.hashgraph.sdk.AssessedCustomFeeTest.toProtobuf=[ - "# com.hedera.hashgraph.sdk.proto.AssessedCustomFee@55ba505b\namount: 1\neffective_payer_account_id {\n account_num: 10\n realm_num: 9\n shard_num: 8\n}\neffective_payer_account_id {\n account_num: 13\n realm_num: 12\n shard_num: 11\n}\neffective_payer_account_id {\n account_num: 16\n realm_num: 15\n shard_num: 14\n}\nfee_collector_account_id {\n account_num: 7\n realm_num: 6\n shard_num: 5\n}\ntoken_id {\n realm_num: 3\n shard_num: 2\n token_num: 4\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ClientTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/ClientTest.java deleted file mode 100644 index f46e34e706..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ClientTest.java +++ /dev/null @@ -1,459 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; -import org.junit.jupiter.params.provider.NullSource; -import org.junit.jupiter.params.provider.ValueSource; - -import com.google.protobuf.ByteString; -import javax.annotation.Nullable; -import java.io.File; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.time.Duration; -import java.time.Instant; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeoutException; -import java.util.function.Function; - -import static com.hedera.hashgraph.sdk.BaseNodeAddress.PORT_NODE_PLAIN; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -class ClientTest { - - @Test - @DisplayName("Can construct mainnet client") - void forMainnet() throws TimeoutException { - Client.forMainnet().close(); - } - - @Test - @DisplayName("Can construct mainnet client with executor") - void forMainnetWithExecutor() throws TimeoutException { - var executor = new ThreadPoolExecutor(2, 2, - 0L, TimeUnit.MILLISECONDS, - new LinkedBlockingQueue<>(), new ThreadPoolExecutor.CallerRunsPolicy()); - - Client.forMainnet(executor).close(); - } - - @Test - @DisplayName("Can construct testnet client with executor") - void forTestnetWithExecutor() throws TimeoutException { - var executor = new ThreadPoolExecutor(2, 2, - 0L, TimeUnit.MILLISECONDS, - new LinkedBlockingQueue<>(), new ThreadPoolExecutor.CallerRunsPolicy()); - - Client.forTestnet(executor).close(); - } - - @Test - @DisplayName("Can construct previewnet client with executor") - void forPreviewnetWithWithExecutor() throws TimeoutException { - var executor = new ThreadPoolExecutor(2, 2, - 0L, TimeUnit.MILLISECONDS, - new LinkedBlockingQueue<>(), new ThreadPoolExecutor.CallerRunsPolicy()); - - Client.forPreviewnet(executor).close(); - } - - @Test - @DisplayName("Client.setMaxQueryPayment() negative") - void setMaxQueryPaymentNegative() throws TimeoutException { - var client = Client.forTestnet(); - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { - client.setMaxQueryPayment(Hbar.MIN); - }); - client.close(); - } - - @ValueSource(ints = {-1, 0}) - @ParameterizedTest(name = "Invalid maxAttempts {0}") - void setMaxAttempts(int maxAttempts) throws TimeoutException { - var client = Client.forNetwork(Map.of()); - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { - client.setMaxAttempts(maxAttempts); - }); - client.close(); - } - - @NullSource - @ValueSource(longs = {-1, 0, 249}) - @ParameterizedTest(name = "Invalid maxBackoff {0}") - @SuppressWarnings("NullAway") - void setMaxBackoffInvalid(@Nullable Long maxBackoffMillis) throws TimeoutException { - @Nullable Duration maxBackoff = maxBackoffMillis != null ? Duration.ofMillis(maxBackoffMillis) : null; - var client = Client.forNetwork(Map.of()); - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { - client.setMaxBackoff(maxBackoff); - }); - client.close(); - } - - @ValueSource(longs = {250, 8000}) - @ParameterizedTest(name = "Valid maxBackoff {0}") - void setMaxBackoffValid(long maxBackoff) throws TimeoutException { - Client.forNetwork(Map.of()).setMaxBackoff(Duration.ofMillis(maxBackoff)).close(); - } - - @NullSource - @ValueSource(longs = {-1, 8001}) - @ParameterizedTest(name = "Invalid minBackoff {0}") - @SuppressWarnings("NullAway") - void setMinBackoffInvalid(@Nullable Long minBackoffMillis) throws TimeoutException { - @Nullable Duration minBackoff = minBackoffMillis != null ? Duration.ofMillis(minBackoffMillis) : null; - var client = Client.forNetwork(Map.of()); - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { - client.setMinBackoff(minBackoff); - }); - client.close(); - } - - @ValueSource(longs = {0, 250, 8000}) - @ParameterizedTest(name = "Valid minBackoff {0}") - void setMinBackoffValid(long minBackoff) throws TimeoutException { - Client.forNetwork(Map.of()).setMinBackoff(Duration.ofMillis(minBackoff)).close(); - } - - @Test - @DisplayName("Client.setMaxTransactionFee() negative") - void setMaxTransactionFeeNegative() throws TimeoutException { - var client = Client.forTestnet(); - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { - client.setDefaultMaxTransactionFee(Hbar.MIN); - }); - client.close(); - } - - @Test - @DisplayName("fromJsonFile() functions correctly") - void fromJsonFile() throws Exception { - Client.fromConfigFile(new File("./src/test/resources/client-config.json")).close(); - Client.fromConfigFile(new File("./src/test/resources/client-config-with-operator.json")).close(); - Client.fromConfigFile("./src/test/resources/client-config.json").close(); - Client.fromConfigFile("./src/test/resources/client-config-with-operator.json").close(); - } - - @Test - @DisplayName("fromJson() functions correctly") - void testFromJson() throws Exception { - // Copied content of `client-config-with-operator.json` - var client = Client.fromConfig("{\n" + - " \"network\":\"mainnet\",\n" + - " \"operator\": {\n" + - " \"accountId\": \"0.0.36\",\n" + - " \"privateKey\": \"302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10\"\n" + - " }\n" + - "}\n"); - - // put it in a file for nicer formatting - InputStream clientConfig = ClientTest.class.getClassLoader() - .getResourceAsStream("client-config.json"); - - assertThat(clientConfig).isNotNull(); - - Client.fromConfig(new InputStreamReader(clientConfig, StandardCharsets.UTF_8)).close(); - - // put it in a file for nicer formatting - InputStream clientConfigWithOperator = ClientTest.class.getClassLoader() - .getResourceAsStream("client-config-with-operator.json"); - - assertThat(clientConfigWithOperator).isNotNull(); - - client.close(); - } - - @Test - @DisplayName("setNetwork() functions correctly") - void setNetworkWorks() throws Exception { - var defaultNetwork = Map.of( - "0.testnet.hedera.com:50211", new AccountId(3), - "1.testnet.hedera.com:50211", new AccountId(4) - ); - - Client client = Client.forNetwork(defaultNetwork); - assertThat(client.getNetwork()).containsExactlyInAnyOrderEntriesOf(defaultNetwork); - - client.setNetwork(defaultNetwork); - assertThat(client.getNetwork()).containsExactlyInAnyOrderEntriesOf(defaultNetwork); - - var defaultNetworkWithExtraNode = Map.of( - "0.testnet.hedera.com:50211", new AccountId(3), - "1.testnet.hedera.com:50211", new AccountId(4), - "2.testnet.hedera.com:50211", new AccountId(5) - ); - - client.setNetwork(defaultNetworkWithExtraNode); - assertThat(client.getNetwork()).containsExactlyInAnyOrderEntriesOf(defaultNetworkWithExtraNode); - - var singleNodeNetwork = Map.of( - "2.testnet.hedera.com:50211", new AccountId(5) - ); - - client.setNetwork(singleNodeNetwork); - assertThat(client.getNetwork()).containsExactlyInAnyOrderEntriesOf(singleNodeNetwork); - - var singleNodeNetworkWithDifferentAccountId = Map.of( - "2.testnet.hedera.com:50211", new AccountId(6) - ); - - client.setNetwork(singleNodeNetworkWithDifferentAccountId); - assertThat(client.getNetwork()).containsExactlyInAnyOrderEntriesOf(singleNodeNetworkWithDifferentAccountId); - - var multiAddressNetwork = Map.of( - "0.testnet.hedera.com:50211", new AccountId(3), - "34.94.106.61:50211", new AccountId(3), - "50.18.132.211:50211", new AccountId(3), - "138.91.142.219:50211", new AccountId(3), - - "1.testnet.hedera.com:50211", new AccountId(4), - "35.237.119.55:50211", new AccountId(4), - "3.212.6.13:50211", new AccountId(4), - "52.168.76.241:50211", new AccountId(4) - ); - - client.setNetwork(multiAddressNetwork); - assertThat(client.getNetwork()).containsExactlyInAnyOrderEntriesOf(multiAddressNetwork); - - client.close(); - } - - @Test - @DisplayName("setMirrorNetwork() functions correctly") - void setMirrorNetworkWorks() throws Exception { - var defaultNetwork = List.of("testnet.mirrornode.hedera.com:443"); - - Client client = Client.forNetwork(new HashMap<>()).setMirrorNetwork(defaultNetwork); - assertThat(client.getMirrorNetwork()).containsExactlyInAnyOrderElementsOf(defaultNetwork); - - client.setMirrorNetwork(defaultNetwork); - assertThat(client.getMirrorNetwork()).containsExactlyInAnyOrderElementsOf(defaultNetwork); - - var defaultNetworkWithExtraNode = List.of( - "testnet.mirrornode.hedera.com:443", - "testnet1.mirrornode.hedera.com:443" - ); - - client.setMirrorNetwork(defaultNetworkWithExtraNode); - assertThat(client.getMirrorNetwork()).containsExactlyInAnyOrderElementsOf(defaultNetworkWithExtraNode); - - var singleNodeNetwork = List.of("testnet1.mirrornode.hedera.com:443"); - - client.setMirrorNetwork(singleNodeNetwork); - assertThat(client.getMirrorNetwork()).containsExactlyInAnyOrderElementsOf(singleNodeNetwork); - - var singleNodeNetworkWithDifferentNode = List.of("testnet.mirrornode.hedera.com:443"); - - client.setMirrorNetwork(singleNodeNetworkWithDifferentNode); - assertThat(client.getMirrorNetwork()).containsExactlyInAnyOrderElementsOf(singleNodeNetworkWithDifferentNode); - - client.close(); - } - - @Test - @DisplayName("setMirrorNetwork() throws exception if there is no time to remove the old nodes") - void setMirrorNetworkFails() throws Exception { - var defaultNetwork = List.of( - "testnet.mirrornode.hedera.com:443", - "testnet.mirrornode2.hedera.com:443" - ); - - Client client = Client.forNetwork(new HashMap<>()).setMirrorNetwork(defaultNetwork); - assertThat(client.getMirrorNetwork()).containsExactlyInAnyOrderElementsOf(defaultNetwork); - - client.setCloseTimeout(Duration.ZERO); - final List updatedNetwork = List.of("testnet.mirrornode.hedera.com:443"); - - assertThatThrownBy(() -> client.setMirrorNetwork(updatedNetwork)) - .hasMessageEndingWith("Failed to properly shutdown all channels"); - } - - @Test - @DisplayName("forName() sets the correct network") - void forNameReturnsCorrectNetwork() { - Client mainnetClient = Client.forName("mainnet"); - assertThat(mainnetClient.getLedgerId()).isEqualTo(LedgerId.MAINNET); - - Client testnetClient = Client.forName("testnet"); - assertThat(testnetClient.getLedgerId()).isEqualTo(LedgerId.TESTNET); - - Client previewnetClient = Client.forName("previewnet"); - assertThat(previewnetClient.getLedgerId()).isEqualTo(LedgerId.PREVIEWNET); - - assertThatThrownBy(() -> Client.forName("unknown")) - .hasMessageEndingWith("Name must be one-of `mainnet`, `testnet`, or `previewnet`"); - } - - @ParameterizedTest - @CsvSource({ - "onClient", - "onQuery" - }) - void testExecuteAsyncTimeout(String timeoutSite) throws Exception { - AccountId accountId = AccountId.fromString("0.0.1"); - Duration timeout = Duration.ofSeconds(5); - - Client client = Client.forNetwork(Map.of("1.1.1.1:50211", accountId)) - .setNodeMinBackoff(Duration.ofMillis(0)) - .setNodeMaxBackoff(Duration.ofMillis(0)) - .setMinNodeReadmitTime(Duration.ofMillis(0)) - .setMaxNodeReadmitTime(Duration.ofMillis(0)); - AccountBalanceQuery query = new AccountBalanceQuery() - .setAccountId(accountId) - .setMaxAttempts(3); - Instant start = Instant.now(); - - try { - if (timeoutSite.equals("onClient")) { - client.setRequestTimeout(timeout); - query.executeAsync(client).get(); - } else { - query.executeAsync(client, timeout).get(); - } - } catch (ExecutionException e) { - // fine... - } - long secondsTaken = java.time.Duration.between(start, Instant.now()).toSeconds(); - - // 20 seconds would indicate we tried 2 times to connect - assertThat(secondsTaken).isLessThan(7); - - client.close(); - } - - @ParameterizedTest - @CsvSource({ - "onClient", - "onQuery" - }) - void testExecuteSyncTimeout(String timeoutSite) throws Exception { - AccountId accountId = AccountId.fromString("0.0.1"); - // Executing requests in sync mode will require at most 10 seconds to connect - // to a gRPC node. If we're not able to connect to a gRPC node within 10 seconds - // we fail that request attempt. This means setting at timeout on a request - // which hits non-connecting gRPC nodes will fail within ~10s of the set timeout - // e.g. setting a timeout of 15 seconds, the request could fail within the range - // of [5 seconds, 25 seconds]. The 10 second timeout for connecting to gRPC nodes - // is not configurable. - Duration timeout = Duration.ofSeconds(5); - - Client client = Client.forNetwork(Map.of("1.1.1.1:50211", accountId)) - .setNodeMinBackoff(Duration.ofMillis(0)) - .setNodeMaxBackoff(Duration.ofMillis(0)) - .setMinNodeReadmitTime(Duration.ofMillis(0)) - .setMaxNodeReadmitTime(Duration.ofMillis(0)); - - AccountBalanceQuery query = new AccountBalanceQuery() - .setAccountId(accountId) - .setMaxAttempts(3) - .setGrpcDeadline(Duration.ofSeconds(5)); - Instant start = Instant.now(); - - try { - if (timeoutSite.equals("onClient")) { - client.setRequestTimeout(timeout); - query.execute(client); - } else { - query.execute(client, timeout); - } - } catch (TimeoutException e) { - // fine... - } - long secondsTaken = java.time.Duration.between(start, Instant.now()).toSeconds(); - - // 20 seconds would indicate we tried 2 times to connect - assertThat(secondsTaken).isLessThan(15); - - client.close(); - } - - com.hedera.hashgraph.sdk.proto.NodeAddress nodeAddress(long accountNum, String rsaPubKeyHex, byte[] certHash, byte[] ipv4) { - com.hedera.hashgraph.sdk.proto.NodeAddress.Builder builder = com.hedera.hashgraph.sdk.proto.NodeAddress.newBuilder() - .setNodeAccountId(com.hedera.hashgraph.sdk.proto.AccountID.newBuilder() - .setAccountNum(accountNum) - .build()) - .addServiceEndpoint(com.hedera.hashgraph.sdk.proto.ServiceEndpoint.newBuilder() - .setIpAddressV4(ByteString.copyFrom(ipv4)) - .setPort(PORT_NODE_PLAIN) - .build()) - .setRSAPubKey(rsaPubKeyHex); - if (certHash != null) { - builder.setNodeCertHash(ByteString.copyFrom(certHash)); - } - return builder.build(); - } - - @Test - @DisplayName("setNetworkFromAddressBook() updates security parameters in the client") - void setNetworkFromAddressBook() throws Exception { - try (Client client = Client.forNetwork(Map.of())) { - Function nodeAddress = accountNum -> client.network.network.get(new AccountId(accountNum)).get(0).getAddressBookEntry(); - - // reconfigure client network from addressbook (add new nodes) - client.setNetworkFromAddressBook(NodeAddressBook.fromBytes(com.hedera.hashgraph.sdk.proto.NodeAddressBook.newBuilder() - .addNodeAddress(nodeAddress(10001, "10001", new byte[] {1, 0, 1}, new byte[] {10, 0, 0, 1})) - .addNodeAddress(nodeAddress(10002, "10002", new byte[] {1, 0, 2}, new byte[] {10, 0, 0, 2})) - .build().toByteString())); - - // verify security parameters in client - assertThat(nodeAddress.apply(10001).certHash).isEqualTo(ByteString.copyFrom(new byte[]{1, 0, 1})); - assertThat(nodeAddress.apply(10001).publicKey).isEqualTo("10001"); - assertThat(nodeAddress.apply(10002).certHash).isEqualTo(ByteString.copyFrom(new byte[]{1, 0, 2})); - assertThat(nodeAddress.apply(10002).publicKey).isEqualTo("10002"); - - // reconfigure client network from addressbook without `certHash` - client.setNetworkFromAddressBook(NodeAddressBook.fromBytes(com.hedera.hashgraph.sdk.proto.NodeAddressBook.newBuilder() - .addNodeAddress(nodeAddress(10001, "10001", null, new byte[] {10, 0, 0, 1})) - .addNodeAddress(nodeAddress(10002, "10002", null, new byte[] {10, 0, 0, 2})) - .build().toByteString())); - - // verify security parameters in client (unchanged) - assertThat(nodeAddress.apply(10001).certHash).isEqualTo(ByteString.copyFrom(new byte[]{1, 0, 1})); - assertThat(nodeAddress.apply(10001).publicKey).isEqualTo("10001"); - assertThat(nodeAddress.apply(10002).certHash).isEqualTo(ByteString.copyFrom(new byte[]{1, 0, 2})); - assertThat(nodeAddress.apply(10002).publicKey).isEqualTo("10002"); - - // reconfigure client network from addressbook (update existing nodes) - client.setNetworkFromAddressBook(NodeAddressBook.fromBytes(com.hedera.hashgraph.sdk.proto.NodeAddressBook.newBuilder() - .addNodeAddress(nodeAddress(10001, "810001", new byte[] {8, 1, 0, 1}, new byte[] {10, 0, 0, 1})) - .addNodeAddress(nodeAddress(10002, "810002", new byte[] {8, 1, 0, 2}, new byte[] {10, 0, 0, 2})) - .build().toByteString())); - - // verify security parameters in client - assertThat(nodeAddress.apply(10001).certHash).isEqualTo(ByteString.copyFrom(new byte[]{8, 1, 0, 1})); - assertThat(nodeAddress.apply(10001).publicKey).isEqualTo("810001"); - assertThat(nodeAddress.apply(10002).certHash).isEqualTo(ByteString.copyFrom(new byte[]{8, 1, 0, 2})); - assertThat(nodeAddress.apply(10002).publicKey).isEqualTo("810002"); - } - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractByteCodeQueryTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractByteCodeQueryTest.java deleted file mode 100644 index 514cd92437..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractByteCodeQueryTest.java +++ /dev/null @@ -1,47 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -public class ContractByteCodeQueryTest { - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - var builder = com.hedera.hashgraph.sdk.proto.Query.newBuilder(); - new ContractByteCodeQuery() - .setContractId(ContractId.fromString("0.0.5005")) - .onMakeRequest(builder, QueryHeader.newBuilder().build()); - SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")).toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractByteCodeQueryTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractByteCodeQueryTest.snap deleted file mode 100644 index 3bcf495af4..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractByteCodeQueryTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.ContractByteCodeQueryTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.Query\ncontract_get_bytecode {\n contract_i_d {\n contract_num: 5005\n realm_num: 0\n shard_num: 0\n }\n header {\n }\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractCallQueryTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractCallQueryTest.java deleted file mode 100644 index 7e1e33cb07..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractCallQueryTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; - -public class ContractCallQueryTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - var builder = com.hedera.hashgraph.sdk.proto.Query.newBuilder(); - new ContractCallQuery() - .setContractId(ContractId.fromString("0.0.5005")) - .setGas(1541) - .setSenderAccountId(AccountId.fromString("1.2.3")) - .setFunction("foo", - new ContractFunctionParameters() - .addString("Hello") - .addString("world!")) - .onMakeRequest(builder, QueryHeader.newBuilder().build()); - SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")).toMatchSnapshot(); - } - - @Test - void setFunctionParameters() { - var builder = com.hedera.hashgraph.sdk.proto.Query.newBuilder(); - new ContractCallQuery() - .setContractId(ContractId.fromString("0.0.5005")) - .setGas(1541) - .setSenderAccountId(AccountId.fromString("1.2.3")) - .setFunctionParameters( - new ContractFunctionParameters() - .addString("Hello") - .addString("world!") - .toBytes(null) - .toByteArray()) - .onMakeRequest(builder, QueryHeader.newBuilder().build()); - SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")).toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractCallQueryTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractCallQueryTest.snap deleted file mode 100644 index c758a8b2a7..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractCallQueryTest.snap +++ /dev/null @@ -1,8 +0,0 @@ -com.hedera.hashgraph.sdk.ContractCallQueryTest.setFunctionParameters=[ - "# com.hedera.hashgraph.sdk.proto.Query\ncontract_call_local {\n contract_i_d {\n contract_num: 5005\n realm_num: 0\n shard_num: 0\n }\n function_parameters: \"\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000@\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\200\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\005Hello\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\006world!\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\"\n gas: 1541\n header {\n }\n max_result_size: 0\n sender_id {\n account_num: 3\n realm_num: 2\n shard_num: 1\n }\n}" -] - - -com.hedera.hashgraph.sdk.ContractCallQueryTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.Query\ncontract_call_local {\n contract_i_d {\n contract_num: 5005\n realm_num: 0\n shard_num: 0\n }\n function_parameters: \"\\022J\\203\\372\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000@\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\200\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\005Hello\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\006world!\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\"\n gas: 1541\n header {\n }\n max_result_size: 0\n sender_id {\n account_num: 3\n realm_num: 2\n shard_num: 1\n }\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractCreateTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractCreateTransactionTest.java deleted file mode 100644 index 363174ce1e..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractCreateTransactionTest.java +++ /dev/null @@ -1,133 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.ContractCreateTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.bouncycastle.util.encoders.Hex; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Duration; -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; - -public class ContractCreateTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction() - .toString() - ).toMatchSnapshot(); - } - - @Test - void shouldSerialize2() { - SnapshotMatcher.expect(spawnTestTransaction2() - .toString() - ).toMatchSnapshot(); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new ContractCreateTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - private ContractCreateTransaction spawnTestTransaction() { - return new ContractCreateTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setBytecodeFileId(FileId.fromString("0.0.3003")) - .setAdminKey(unusedPrivateKey) - .setGas(0) - .setInitialBalance(Hbar.fromTinybars(1000)) - .setStakedAccountId(AccountId.fromString("0.0.3")) - .setMaxAutomaticTokenAssociations(101) - .setAutoRenewPeriod(Duration.ofHours(10)) - .setConstructorParameters(new byte[]{10, 11, 12, 13, 25}) - .setMaxTransactionFee(Hbar.fromTinybars(100_000)) - .setAutoRenewAccountId(new AccountId(30)) - .freeze() - .sign(unusedPrivateKey); - } - - private ContractCreateTransaction spawnTestTransaction2() { - return new ContractCreateTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setBytecode(Hex.decode("deadbeef")) - .setAdminKey(unusedPrivateKey) - .setGas(0) - .setInitialBalance(Hbar.fromTinybars(1000)) - .setStakedNodeId(4L) - .setMaxAutomaticTokenAssociations(101) - .setAutoRenewPeriod(Duration.ofHours(10)) - .setConstructorParameters(new byte[]{10, 11, 12, 13, 25}) - .setMaxTransactionFee(Hbar.fromTinybars(100_000)) - .setAutoRenewAccountId(new AccountId(30)) - .freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = ContractCreateTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldBytes2() throws Exception { - var tx = spawnTestTransaction2(); - var tx2 = ContractCreateTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx2.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setContractCreateInstance(ContractCreateTransactionBody.newBuilder().build()) - .build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(ContractCreateTransaction.class); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractCreateTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractCreateTransactionTest.snap deleted file mode 100644 index fb650f2133..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractCreateTransactionTest.snap +++ /dev/null @@ -1,8 +0,0 @@ -com.hedera.hashgraph.sdk.ContractCreateTransactionTest.shouldSerialize2=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\ncontract_create_instance {\n admin_key {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n auto_renew_account_id {\n account_num: 30\n realm_num: 0\n shard_num: 0\n }\n auto_renew_period {\n seconds: 36000\n }\n constructor_parameters: \"\\n\\v\\f\\r\\031\"\n gas: 0\n initcode: \"\\336\\255\\276\\357\"\n initial_balance: 1000\n max_automatic_token_associations: 101\n staked_node_id: 4\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] - - -com.hedera.hashgraph.sdk.ContractCreateTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\ncontract_create_instance {\n admin_key {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n auto_renew_account_id {\n account_num: 30\n realm_num: 0\n shard_num: 0\n }\n auto_renew_period {\n seconds: 36000\n }\n constructor_parameters: \"\\n\\v\\f\\r\\031\"\n file_i_d {\n file_num: 3003\n realm_num: 0\n shard_num: 0\n }\n gas: 0\n initial_balance: 1000\n max_automatic_token_associations: 101\n staked_account_id {\n account_num: 3\n realm_num: 0\n shard_num: 0\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractDeleteTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractDeleteTransactionTest.java deleted file mode 100644 index f6f7d6f1f2..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractDeleteTransactionTest.java +++ /dev/null @@ -1,93 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.ContractDeleteTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; - -public class ContractDeleteTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction() - .toString() - ).toMatchSnapshot(); - } - - private ContractDeleteTransaction spawnTestTransaction() { - return new ContractDeleteTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setContractId(ContractId.fromString("0.0.5007")) - .setTransferAccountId(new AccountId(9)) - .setTransferContractId(ContractId.fromString("0.0.5008")) - .setMaxTransactionFee(Hbar.fromTinybars(100_000)) - .freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = ContractDeleteTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new ContractDeleteTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setContractDeleteInstance(ContractDeleteTransactionBody.newBuilder().build()) - .build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(ContractDeleteTransaction.class); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractDeleteTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractDeleteTransactionTest.snap deleted file mode 100644 index 82145335e9..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractDeleteTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.ContractDeleteTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\ncontract_delete_instance {\n contract_i_d {\n contract_num: 5007\n realm_num: 0\n shard_num: 0\n }\n transfer_contract_i_d {\n contract_num: 5008\n realm_num: 0\n shard_num: 0\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractExecuteTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractExecuteTransactionTest.java deleted file mode 100644 index 4dc337c482..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractExecuteTransactionTest.java +++ /dev/null @@ -1,95 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import com.hedera.hashgraph.sdk.proto.ContractCallTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; - -public class ContractExecuteTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction() - .toString() - ).toMatchSnapshot(); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new ContractExecuteTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - private ContractExecuteTransaction spawnTestTransaction() { - return new ContractExecuteTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setContractId(ContractId.fromString("0.0.5007")) - .setGas(10) - .setPayableAmount(Hbar.fromTinybars(1000)) - .setFunctionParameters(ByteString.copyFrom(new byte[]{24, 43, 11})) - .setMaxTransactionFee(Hbar.fromTinybars(100_000)) - .freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = ContractExecuteTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setContractCall(ContractCallTransactionBody.newBuilder().build()) - .build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(ContractExecuteTransaction.class); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractExecuteTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractExecuteTransactionTest.snap deleted file mode 100644 index b7dedc94a9..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractExecuteTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.ContractExecuteTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\ncontract_call {\n amount: 1000\n contract_i_d {\n contract_num: 5007\n realm_num: 0\n shard_num: 0\n }\n function_parameters: \"\\030+\\v\"\n gas: 10\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractFunctionParametersTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractFunctionParametersTest.java deleted file mode 100644 index dddfb5a66c..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractFunctionParametersTest.java +++ /dev/null @@ -1,651 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.bouncycastle.util.encoders.Hex; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -import java.lang.reflect.Array; -import java.math.BigInteger; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Supplier; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -public class ContractFunctionParametersTest { - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @SuppressWarnings("unused") - private static List int256Arguments() { - return List.of( - Arguments.of(0, "0000000000000000000000000000000000000000000000000000000000000000"), - Arguments.of(2, "0000000000000000000000000000000000000000000000000000000000000002"), - Arguments.of(255, "00000000000000000000000000000000000000000000000000000000000000ff"), - Arguments.of(4095, "0000000000000000000000000000000000000000000000000000000000000fff"), - Arguments.of(127 << 24, "000000000000000000000000000000000000000000000000000000007f000000"), - Arguments.of(2047 << 20, "000000000000000000000000000000000000000000000000000000007ff00000"), - // deadbeef as an integer literal is negative - Arguments.of(0xdeadbeefL, "00000000000000000000000000000000000000000000000000000000deadbeef"), - Arguments.of(-1, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), - Arguments.of(-2, "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"), - Arguments.of(-256, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"), - Arguments.of(-4096, "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000"), - Arguments.of(255 << 24, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000"), - Arguments.of(4095 << 20, "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000"), - Arguments.of(0xdeadbeef, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffdeadbeef") - ); - } - - @SuppressWarnings("unused") - private static List uInt256Arguments() { - return List.of( - Arguments.of(0, "0000000000000000000000000000000000000000000000000000000000000000", 8), - Arguments.of(2, "0000000000000000000000000000000000000000000000000000000000000002", 8), - Arguments.of(255, "00000000000000000000000000000000000000000000000000000000000000ff", 8), - Arguments.of(4095, "0000000000000000000000000000000000000000000000000000000000000fff", 32), - Arguments.of(127 << 24, "000000000000000000000000000000000000000000000000000000007f000000", 32), - Arguments.of(2047 << 20, "000000000000000000000000000000000000000000000000000000007ff00000", 32), - // deadbeef as an integer literal is negative - Arguments.of(0xdeadbeef, "00000000000000000000000000000000000000000000000000000000deadbeef", 32), - Arguments.of(-1, "000000000000000000000000000000000000000000000000ffffffffffffffff", 64), - Arguments.of(-2, "000000000000000000000000000000000000000000000000fffffffffffffffe", 64), - Arguments.of(-256, "000000000000000000000000000000000000000000000000ffffffffffffff00", 64), - Arguments.of(-4096, "000000000000000000000000000000000000000000000000fffffffffffff000", 64), - Arguments.of(255 << 24, "000000000000000000000000000000000000000000000000ffffffffff000000", 64), - Arguments.of(4095 << 20, "000000000000000000000000000000000000000000000000fffffffffff00000", 64), - Arguments.of(0xdeadbeefL, "00000000000000000000000000000000000000000000000000000000deadbeef", 64) - ); - } - - @Test - @DisplayName("encodes int types correctly") - void intTypes() { - ContractFunctionParameters params = new ContractFunctionParameters() - .addUint8((byte) 0x1) - .addInt8((byte) -0x2) - .addUint32(0x3) - .addInt32(-0x4) - .addUint64(0x4) - .addInt64(-0x5) - .addUint256(BigInteger.valueOf(0x6)) - .addInt256(BigInteger.valueOf(-0x7)) - .addUint8Array(new byte[]{(byte) 0x1, (byte) 0x2, (byte) 0x3, (byte) 0x4}) - .addInt8Array(new byte[]{(byte) -0x5, (byte) 0x6, (byte) 0x7, (byte) -0x8}) - .addUint32Array(new int[]{0x9, 0xA, 0xB, 0xC}) - .addInt32Array(new int[]{-0xD, 0xE, 0xF, -0x10}) - .addUint64Array(new long[]{0x11, 0x12, 0x13, 0x14}) - .addInt64Array(new long[]{-0x15, 0x16, 0x17, -0x18}) - .addUint256Array(new BigInteger[]{BigInteger.valueOf(0x19)}) - .addInt256Array(new BigInteger[]{BigInteger.valueOf(-0x1A)}); - - assertThat( - "11bcd903" + - "0000000000000000000000000000000000000000000000000000000000000001" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe" + - "0000000000000000000000000000000000000000000000000000000000000003" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc" + - "0000000000000000000000000000000000000000000000000000000000000004" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb" + - "0000000000000000000000000000000000000000000000000000000000000006" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9" + - "0000000000000000000000000000000000000000000000000000000000000200" + - "00000000000000000000000000000000000000000000000000000000000002a0" + - "0000000000000000000000000000000000000000000000000000000000000340" + - "00000000000000000000000000000000000000000000000000000000000003e0" + - "0000000000000000000000000000000000000000000000000000000000000480" + - "0000000000000000000000000000000000000000000000000000000000000520" + - "00000000000000000000000000000000000000000000000000000000000005c0" + - "0000000000000000000000000000000000000000000000000000000000000600" + - "0000000000000000000000000000000000000000000000000000000000000004" + - "0000000000000000000000000000000000000000000000000000000000000001" + - "0000000000000000000000000000000000000000000000000000000000000002" + - "0000000000000000000000000000000000000000000000000000000000000003" + - "0000000000000000000000000000000000000000000000000000000000000004" + - "0000000000000000000000000000000000000000000000000000000000000004" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb" + - "0000000000000000000000000000000000000000000000000000000000000006" + - "0000000000000000000000000000000000000000000000000000000000000007" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8" + - "0000000000000000000000000000000000000000000000000000000000000004" + - "0000000000000000000000000000000000000000000000000000000000000009" + - "000000000000000000000000000000000000000000000000000000000000000a" + - "000000000000000000000000000000000000000000000000000000000000000b" + - "000000000000000000000000000000000000000000000000000000000000000c" + - "0000000000000000000000000000000000000000000000000000000000000004" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3" + - "000000000000000000000000000000000000000000000000000000000000000e" + - "000000000000000000000000000000000000000000000000000000000000000f" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0" + - "0000000000000000000000000000000000000000000000000000000000000004" + - "0000000000000000000000000000000000000000000000000000000000000011" + - "0000000000000000000000000000000000000000000000000000000000000012" + - "0000000000000000000000000000000000000000000000000000000000000013" + - "0000000000000000000000000000000000000000000000000000000000000014" + - "0000000000000000000000000000000000000000000000000000000000000004" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeb" + - "0000000000000000000000000000000000000000000000000000000000000016" + - "0000000000000000000000000000000000000000000000000000000000000017" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe8" + - "0000000000000000000000000000000000000000000000000000000000000001" + - "0000000000000000000000000000000000000000000000000000000000000019" + - "0000000000000000000000000000000000000000000000000000000000000001" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe6" - ).isEqualTo(Hex.toHexString(params.toBytes("foo").toByteArray())); - } - - @Test - @DisplayName("uint256 chops off sign bit if length is 256 bits") - void uint256BitLength() { - var params = new ContractFunctionParameters() - .addUint256(BigInteger.valueOf(2).pow(255)); - - assertThat( - "2fbebd38" + - "8000000000000000000000000000000000000000000000000000000000000000" - ).isEqualTo(Hex.toHexString(params.toBytes("foo").toByteArray())); - } - - @Test - @DisplayName("uint256 errors if less than 0") - void uint256Errors() { - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { - new ContractFunctionParameters() - .addUint256(BigInteger.valueOf(-0x1)); - }); - - /* - assertThrows(IllegalArgumentException.class, () -> { - new ContractFunctionParameters() - .addUint256(BigInteger.valueOf(2).pow(256)); - }); - */ - } - - @Test - @DisplayName("encodes addresses correctly") - void addresses() { - var params = new ContractFunctionParameters() - .addAddress("1122334455667788990011223344556677889900") - .addAddress("0x1122334455667788990011223344556677889900") - .addAddressArray(new String[]{"1122334455667788990011223344556677889900", "1122334455667788990011223344556677889900"}); - - assertThat( - "7d48c86d" + - "0000000000000000000000001122334455667788990011223344556677889900" + - "0000000000000000000000001122334455667788990011223344556677889900" + - "0000000000000000000000000000000000000000000000000000000000000060" + - "0000000000000000000000000000000000000000000000000000000000000002" + - "0000000000000000000000001122334455667788990011223344556677889900" + - "0000000000000000000000001122334455667788990011223344556677889900" - ).isEqualTo(Hex.toHexString(params.toBytes("foo").toByteArray())); - } - - @Test - @DisplayName("encodes functions correctly") - void addressesError() { - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { - new ContractFunctionParameters() - .addAddress("112233445566778899001122334455667788990011"); - }); - } - - @Test - @DisplayName("encodes functions correctly") - void functions() { - var params = new ContractFunctionParameters() - .addFunction("1122334455667788990011223344556677889900", new byte[]{1, 2, 3, 4}) - .addFunction("0x1122334455667788990011223344556677889900", new ContractFunctionSelector("randomFunction").addBool()); - - assertThat( - "c99c40cd" + - "1122334455667788990011223344556677889900010203040000000000000000" + - "112233445566778899001122334455667788990063441d820000000000000000" - ).isEqualTo(Hex.toHexString(params.toBytes("foo").toByteArray())); - } - - @Test - @DisplayName("encodes functions correctly") - void functionsError() { - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { - new ContractFunctionParameters() - .addFunction("112233445566778899001122334455667788990011", new byte[]{1, 2, 3, 4}); - }); - - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { - new ContractFunctionParameters() - .addFunction("1122334455667788990011223344556677889900", new byte[]{1, 2, 3, 4, 5}); - }); - } - - @Test - @DisplayName("encodes bytes4 correctly") - void bytes4Encoding() { - var params = new ContractFunctionParameters() - .addBytes4(new byte[]{1, 2, 3, 4}); - assertThat( - "580526ee" + - "0102030400000000000000000000000000000000000000000000000000000000" - ).isEqualTo(Hex.toHexString(params.toBytes("foo").toByteArray())); - } - - @Test - @DisplayName("fails to encode bytes4 if length too long") - void bytes4EncodingError() { - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { - new ContractFunctionParameters().addBytes4(new byte[]{1, 2, 3, 4, 5}); - }); - } - - @Test - @DisplayName("encodes UTF-8 string as bytes4 correctly") - void bytes4UTF8Encoding() { - var params = new ContractFunctionParameters() - .addBytes4("ABCD".getBytes(StandardCharsets.UTF_8)); - assertThat( - "580526ee" + - "4142434400000000000000000000000000000000000000000000000000000000" - ).isEqualTo(Hex.toHexString(params.toBytes("foo").toByteArray())); - } - - @Test - @DisplayName("fails to encode UTF-8 string as bytes4 if length is bigger than 4 bytes") - void bytes4UTF8EncodingError() { - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { - new ContractFunctionParameters().addBytes4("ABCDE".getBytes(StandardCharsets.UTF_8)); - }); - } - - @Test - @DisplayName("encodes bytes32 correctly") - void bytes() { - var params = new ContractFunctionParameters() - .addBytes32(new byte[]{ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 - }); - - assertThat( - "11e814c1" + - "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20" - ).isEqualTo(Hex.toHexString(params.toBytes("foo").toByteArray())); - } - - @Test - @DisplayName("fails to encode bytes32 if length too long") - void bytesError() { - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { - new ContractFunctionParameters() - .addBytes32(new byte[]{ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, - 33 - }); - } - ); - } - - @Test - @DisplayName("encodes boolean correctly") - void bool() { - var params = new ContractFunctionParameters() - .addBool(true) - .addBool(false); - - assertThat( - "b3cedfcf" + - "0000000000000000000000000000000000000000000000000000000000000001" + - "0000000000000000000000000000000000000000000000000000000000000000" - ).isEqualTo(Hex.toHexString(params.toBytes("foo").toByteArray())); - } - - @Test - @DisplayName("encodes dynamic params correctly") - void dynamicParamsEncoding() { - ByteString paramsStringArg = new ContractFunctionParameters() - .addString("Hello, world!") - .toBytes("set_message"); - - ByteString paramsBytesArg = new ContractFunctionParameters() - .addBytes("Hello, world!".getBytes(StandardCharsets.UTF_8)) - .toBytes("set_message"); - - String paramsStringArgHex = Hex.toHexString(paramsStringArg.toByteArray()); - String paramsBytesArgHex = Hex.toHexString(paramsBytesArg.toByteArray()); - - assertThat( - "2e982602" - + "0000000000000000000000000000000000000000000000000000000000000020" - + "000000000000000000000000000000000000000000000000000000000000000d" - + "48656c6c6f2c20776f726c642100000000000000000000000000000000000000" - ).isEqualTo(paramsStringArgHex); - - // signature should encode differently but the contents are identical - assertThat( - "010473a7" - + "0000000000000000000000000000000000000000000000000000000000000020" - + "000000000000000000000000000000000000000000000000000000000000000d" - + "48656c6c6f2c20776f726c642100000000000000000000000000000000000000" - ).isEqualTo(paramsBytesArgHex); - } - - @Test - @DisplayName("encodes static params correctly") - void staticParamsEncoding() { - ContractFunctionParameters params = new ContractFunctionParameters() - .addInt32(0x11223344) - .addInt32(-65536) - .addUint64(-65536) - .addAddress("00112233445566778899aabbccddeeff00112233"); - - String paramsHex = Hex.toHexString(params.toBytes(null).toByteArray()); - - assertThat( - "0000000000000000000000000000000000000000000000000000000011223344" - // sign-extended - + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000" - // zero-padded - + "000000000000000000000000000000000000000000000000ffffffffffff0000" - + "00000000000000000000000000112233445566778899aabbccddeeff00112233" - ).isEqualTo(paramsHex); - } - - @Test - @DisplayName("encodes mixed static and dynamic params correctly") - void mixedParamsEncoding() { - ContractFunctionParameters params = new ContractFunctionParameters() - .addInt256(BigInteger.valueOf(0xdeadbeef).shiftLeft(8)) - .addString("Hello, world!") - .addBytes(new byte[]{-1, -18, 63, 127}) - .addBool(true) - .addUint8Array(new byte[]{-1, 127}); - - String paramsHex = Hex.toHexString(params.toBytes("foo").toByteArray()); - - assertThat( - "6a5bb8f2" - + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffdeadbeef00" - + "00000000000000000000000000000000000000000000000000000000000000a0" - + "00000000000000000000000000000000000000000000000000000000000000e0" - + "0000000000000000000000000000000000000000000000000000000000000001" - + "0000000000000000000000000000000000000000000000000000000000000120" - + "000000000000000000000000000000000000000000000000000000000000000d" - + "48656c6c6f2c20776f726c642100000000000000000000000000000000000000" - + "0000000000000000000000000000000000000000000000000000000000000004" - + "ffee3f7f00000000000000000000000000000000000000000000000000000000" - + "0000000000000000000000000000000000000000000000000000000000000002" - + "00000000000000000000000000000000000000000000000000000000000000ff" - + "000000000000000000000000000000000000000000000000000000000000007f" - ).isEqualTo(paramsHex); - } - - @Test - @DisplayName("encodes array types correctly") - void arrayTypesEncoding() { - ContractFunctionParameters params = new ContractFunctionParameters() - .addStringArray(new String[]{"hello", ",", "world!"}) - .addInt32Array(new int[]{0x88, 0x99, 0xAA, 0xBB}) - .addInt256Array(new BigInteger[]{BigInteger.valueOf(0x1111)}); - - assertThat( - "025838fc" + - "0000000000000000000000000000000000000000000000000000000000000060" + - "00000000000000000000000000000000000000000000000000000000000001a0" + - "0000000000000000000000000000000000000000000000000000000000000240" + - "0000000000000000000000000000000000000000000000000000000000000003" + - "0000000000000000000000000000000000000000000000000000000000000060" + - "00000000000000000000000000000000000000000000000000000000000000a0" + - "00000000000000000000000000000000000000000000000000000000000000e0" + - "0000000000000000000000000000000000000000000000000000000000000005" + - "68656c6c6f000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000001" + - "2c00000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000006" + - "776f726c64210000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000004" + - "0000000000000000000000000000000000000000000000000000000000000088" + - "0000000000000000000000000000000000000000000000000000000000000099" + - "00000000000000000000000000000000000000000000000000000000000000aa" + - "00000000000000000000000000000000000000000000000000000000000000bb" + - "0000000000000000000000000000000000000000000000000000000000000001" + - "0000000000000000000000000000000000000000000000000000000000001111" - ).isEqualTo(Hex.toHexString(params.toBytes("foo").toByteArray())); - } - - @Test - @DisplayName("bytes4[] encodes correctly") - void fixedBytes4ArrayEncoding() { - ContractFunctionParameters params = new ContractFunctionParameters() - .addBytes4Array(new byte[][]{ - {1, 2, 3, 4}, - {5, 6, 7, 8}, - {9, 10, 11, 12} - }); - assertThat( - "0000000000000000000000000000000000000000000000000000000000000020" + // offset of array - "0000000000000000000000000000000000000000000000000000000000000003" + // length of array - "0102030400000000000000000000000000000000000000000000000000000000" + // first bytes4 - "0506070800000000000000000000000000000000000000000000000000000000" + // second bytes4 - "090a0b0c00000000000000000000000000000000000000000000000000000000" // third bytes4 - ).isEqualTo(Hex.toHexString(params.toBytes(null).toByteArray())); - } - - @Test - @DisplayName("bytes32[] encodes correctly") - void fixedBytesArrayEncoding() { - // each string should be padded to 32 bytes and have no length prefix - - ContractFunctionParameters params = new ContractFunctionParameters() - .addBytes32Array(new byte[][]{ - "Hello".getBytes(StandardCharsets.UTF_8), - ",".getBytes(StandardCharsets.UTF_8), - "world!".getBytes(StandardCharsets.UTF_8) - }); - - assertThat( - "0000000000000000000000000000000000000000000000000000000000000020" + - "0000000000000000000000000000000000000000000000000000000000000003" + // length of array - "48656c6c6f000000000000000000000000000000000000000000000000000000" + // "Hello" UTF-8 encoded - "2c00000000000000000000000000000000000000000000000000000000000000" + // "," UTF-8 encoded - "776f726c64210000000000000000000000000000000000000000000000000000" // "world!" UTF-8 encoded - ).isEqualTo(Hex.toHexString(params.toBytes(null).toByteArray())); - } - - @Test - @DisplayName("bytes[] encodes correctly") - void dynBytesArrayEncoding() { - // result should be the exact same as the strings test below - ContractFunctionParameters params = new ContractFunctionParameters() - .addBytesArray(new byte[][]{ - "Hello".getBytes(StandardCharsets.UTF_8), - ",".getBytes(StandardCharsets.UTF_8), - "world!".getBytes(StandardCharsets.UTF_8) - }); - - assertThat( - "0000000000000000000000000000000000000000000000000000000000000020" + // offset to array - "0000000000000000000000000000000000000000000000000000000000000003" + // length of array - "0000000000000000000000000000000000000000000000000000000000000060" + // first element offset, relative to beginning of this list (after length) - "00000000000000000000000000000000000000000000000000000000000000a0" + // second element offset - "00000000000000000000000000000000000000000000000000000000000000e0" + // third element offset - "0000000000000000000000000000000000000000000000000000000000000005" + // "Hello".length - "48656c6c6f000000000000000000000000000000000000000000000000000000" + // "Hello" UTF-8 encoded - "0000000000000000000000000000000000000000000000000000000000000001" + // ",".length - "2c00000000000000000000000000000000000000000000000000000000000000" + // "," UTF-8 encoded - "0000000000000000000000000000000000000000000000000000000000000006" + // "world!".length - "776f726c64210000000000000000000000000000000000000000000000000000" // "world!" UTF-8 encoded - ).isEqualTo(Hex.toHexString(params.toBytes(null).toByteArray())); - } - - @Test - @DisplayName("issue 376: string[] encodes correctly") - void stringArrayEncoding() { - ContractFunctionParameters params = new ContractFunctionParameters() - .addStringArray(new String[]{"Hello", ",", "world!"}); - - assertThat( - "0000000000000000000000000000000000000000000000000000000000000020" + // offset to array - "0000000000000000000000000000000000000000000000000000000000000003" + // length of array - "0000000000000000000000000000000000000000000000000000000000000060" + // first element offset, relative to beginning of this list (after length) - "00000000000000000000000000000000000000000000000000000000000000a0" + // second element offset - "00000000000000000000000000000000000000000000000000000000000000e0" + // third element offset - "0000000000000000000000000000000000000000000000000000000000000005" + // "Hello".length - "48656c6c6f000000000000000000000000000000000000000000000000000000" + // "Hello" UTF-8 encoded - "0000000000000000000000000000000000000000000000000000000000000001" + // ",".length - "2c00000000000000000000000000000000000000000000000000000000000000" + // "," UTF-8 encoded - "0000000000000000000000000000000000000000000000000000000000000006" + // "world!".length - "776f726c64210000000000000000000000000000000000000000000000000000" // "world!" UTF-8 encoded - ).isEqualTo(Hex.toHexString(params.toBytes(null).toByteArray())); - } - - @Test - @Disabled - @DisplayName("BigInteger checks") - void bigIntChecks() { - ContractFunctionParameters params = new ContractFunctionParameters(); - - // allowed values for BigInteger - params.addInt256(BigInteger.ONE.shiftLeft(254)); - params.addInt256(BigInteger.ONE.negate().shiftLeft(255)); - - String rangeErr = "BigInteger out of range for Solidity integers"; - - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy( - () -> params.addInt256(BigInteger.ONE.shiftLeft(255)) - ).satisfies(error -> assertThat(error.getMessage()).isEqualTo(rangeErr)); - - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy( - () -> params.addInt256(BigInteger.ONE.negate().shiftLeft(256)) - ).satisfies(error -> assertThat(error.getMessage()).isEqualTo(rangeErr)); - } - - @Test - @DisplayName("address param checks") - void addressParamChecks() { - ContractFunctionParameters params = new ContractFunctionParameters(); - - String lenErr = "Solidity addresses must be 40 hex chars"; - - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy( - () -> params.addAddress("") - ).satisfies(error -> assertThat(error.getMessage()).isEqualTo(lenErr)); - - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy( - () -> params.addAddress("aabbccdd") - ).satisfies(error -> assertThat(error.getMessage()).isEqualTo(lenErr)); - - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy( - () -> params.addAddress("00112233445566778899aabbccddeeff0011223344") - ).satisfies(error -> assertThat(error.getMessage()).isEqualTo(lenErr)); - - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy( - () -> params.addAddress("gghhii--__zz66778899aabbccddeeff00112233") - ).satisfies(error -> assertThat(error.getMessage()).isEqualTo("failed to decode Solidity address as hex")); - } - - @ParameterizedTest - @DisplayName("int256() encodes correctly") - @MethodSource("int256Arguments") - void int256EncodesCorrectly(long val, String hexString) { - assertThat(hexString).isEqualTo( - Hex.toHexString(ContractFunctionParameters.int256(val, 64).toByteArray()) - ); - } - - @ParameterizedTest - @DisplayName("uint256() encodes correctly") - @MethodSource("uInt256Arguments") - void uInt256EncodesCorrectly(long val, String hexString, int bitWidth) { - assertThat(hexString).isEqualTo( - Hex.toHexString(ContractFunctionParameters.uint256(val, bitWidth).toByteArray()) - ); - } - - @Test - void intSizesEncodeCorrectly() throws Exception { - List snapshotStrings = new ArrayList<>(); - for (int n = 8; n <= 256; n += 8) { - var bitWidth = n; - - var argType = ((Supplier>) () -> { - if (bitWidth == 8) { - return byte.class; - } else if (bitWidth <= 32) { - return int.class; - } else if (bitWidth <= 64) { - return long.class; - } else { - return BigInteger.class; - } - }).get(); - - var argVal = ((Supplier) () -> { - if (bitWidth == 8) { - return (byte) (1 << (bitWidth - 1)); - } else if (bitWidth <= 32) { - return (1 << (bitWidth - 1)); - } else if (bitWidth <= 64) { - return (1L << (bitWidth - 1)); - } else { - return BigInteger.ONE.shiftLeft(bitWidth - 1); - } - }).get(); - - var argArrayVal = Array.newInstance(argType, 2); - Array.set(argArrayVal, 0, argVal); - Array.set(argArrayVal, 1, argVal); - var argArrayType = argArrayVal.getClass(); - - var cl = ContractFunctionParameters.class; - var addIntMethod = cl.getMethod("addInt" + n, argType); - var addUintMethod = cl.getMethod("addUint" + n, argType); - var addIntArrayMethod = cl.getMethod("addInt" + n + "Array", argArrayType); - var addUintArrayMethod = cl.getMethod("addUint" + n + "Array", argArrayType); - - var params = new ContractFunctionParameters(); - addIntMethod.invoke(params, argVal); - addUintMethod.invoke(params, argVal); - addIntArrayMethod.invoke(params, argArrayVal); - addUintArrayMethod.invoke(params, argArrayVal); - - snapshotStrings.add("bitWidth = " + bitWidth + ": " + Hex.toHexString(params.toBytes(null).toByteArray())); - } - SnapshotMatcher.expect(snapshotStrings.toArray()).toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractFunctionResultTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractFunctionResultTest.java deleted file mode 100644 index 1666542d4b..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractFunctionResultTest.java +++ /dev/null @@ -1,133 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import com.google.protobuf.BytesValue; -import org.bouncycastle.util.encoders.Hex; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.math.BigInteger; - -import static org.assertj.core.api.Assertions.assertThat; - -public class ContractFunctionResultTest { - static final String CALL_RESULT_HEX = "00000000000000000000000000000000000000000000000000000000ffffffff" - + "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - + "00000000000000000000000011223344556677889900aabbccddeeff00112233" - + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - + "00000000000000000000000000000000000000000000000000000000000000c0" - + "0000000000000000000000000000000000000000000000000000000000000100" - + "000000000000000000000000000000000000000000000000000000000000000d" - + "48656c6c6f2c20776f726c642100000000000000000000000000000000000000" - + "0000000000000000000000000000000000000000000000000000000000000014" - + "48656c6c6f2c20776f726c642c20616761696e21000000000000000000000000"; - - private static final String STRING_ARRAY_RESULT_HEX = "0000000000000000000000000000000000000000000000000000000000000020" - + "0000000000000000000000000000000000000000000000000000000000000002" - + "0000000000000000000000000000000000000000000000000000000000000040" - + "0000000000000000000000000000000000000000000000000000000000000080" - + "000000000000000000000000000000000000000000000000000000000000000C" - + "72616E646F6D2062797465730000000000000000000000000000000000000000" - + "000000000000000000000000000000000000000000000000000000000000000C" - + "72616E646F6D2062797465730000000000000000000000000000000000000000"; - - private static final byte[] callResult = Hex.decode(CALL_RESULT_HEX); - private static final byte[] stringArrayCallResult = Hex.decode(STRING_ARRAY_RESULT_HEX); - - @Test - @DisplayName("provides results correctly") - void providesResultsCorrectly() { - var result = new ContractFunctionResult( - com.hedera.hashgraph.sdk.proto.ContractFunctionResult.newBuilder() - .setContractID(ContractId.fromString("1.2.3").toProtobuf()) - .setContractCallResult(ByteString.copyFrom(callResult)) - .setEvmAddress(BytesValue.newBuilder().setValue(ByteString.copyFrom(Hex.decode("98329e006610472e6B372C080833f6D79ED833cf"))).build()) - // .addStateChanges( - // new ContractStateChange( - // ContractId.fromString("1.2.3"), - // Collections.singletonList( - // new StorageChange( - // BigInteger.valueOf(555), - // BigInteger.valueOf(666), - // BigInteger.valueOf(777) - // ) - // ) - // ).toProtobuf()) - .setSenderId(AccountId.fromString("1.2.3").toProtobuf()) - .addContractNonces( - new ContractNonceInfo(ContractId.fromString("1.2.3"), 10L) - .toProtobuf()) - ); - - // interpretation varies based on width - assertThat(result.getBool(0)).isTrue(); - assertThat(result.getInt32(0)).isEqualTo(-1); - assertThat(result.getInt64(0)).isEqualTo((1L << 32) - 1); - assertThat(result.getInt256(0)).isEqualTo(BigInteger.ONE.shiftLeft(32).subtract(BigInteger.ONE)); - - assertThat(result.getInt256(1)).isEqualTo(BigInteger.ONE.shiftLeft(255).subtract(BigInteger.ONE)); - - assertThat(result.getAddress(2)).isEqualTo("11223344556677889900aabbccddeeff00112233"); - - // unsigned integers (where applicable) - assertThat(result.getUint32(3)).isEqualTo(-1); - assertThat(result.getUint64(3)).isEqualTo(-1L); - // BigInteger can represent the full range and so should be 2^256 - 1 - assertThat(result.getUint256(3)).isEqualTo(BigInteger.ONE.shiftLeft(256).subtract(BigInteger.ONE)); - - assertThat(result.getString(4)).isEqualTo("Hello, world!"); - assertThat(result.getString(5)).isEqualTo("Hello, world, again!"); - - assertThat(result.senderAccountId).isEqualTo(AccountId.fromString("1.2.3")); - - assertThat(result.contractId).isEqualTo(ContractId.fromString("1.2.3")); - assertThat(result.evmAddress).isEqualTo(ContractId.fromEvmAddress(1, 2, "98329e006610472e6B372C080833f6D79ED833cf")); - // assertThat(result.stateChanges.size()).isEqualTo(1); - // ContractStateChange resultStateChange = result.stateChanges.get(0); - // assertThat(resultStateChange.contractId).isEqualTo(ContractId.fromString("1.2.3")); - // assertThat(resultStateChange.storageChanges.size()).isEqualTo(1); - // StorageChange resultStorageChange = resultStateChange.storageChanges.get(0); - // assertThat(resultStorageChange.slot).isEqualTo(BigInteger.valueOf(555)); - // assertThat(resultStorageChange.valueRead).isEqualTo(BigInteger.valueOf(666)); - // assertThat(resultStorageChange.valueWritten).isEqualTo(BigInteger.valueOf(777)); - assertThat(result.contractNonces).containsOnly(new ContractNonceInfo(ContractId.fromString("1.2.3"), 10L)); - } - - @Test - @DisplayName("can get string array result") - void canGetStringArrayResult() { - var result = new ContractFunctionResult( - com.hedera.hashgraph.sdk.proto.ContractFunctionResult.newBuilder() - .setContractCallResult(ByteString.copyFrom(stringArrayCallResult)) - ); - - var strings = result.getStringArray(0); - assertThat(strings.get(0)).isEqualTo("random bytes"); - assertThat(strings.get(1)).isEqualTo("random bytes"); - } - - @Test - @DisplayName("Can to/from bytes with state changes") - void canToFromBytesStateChanges() { - - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractFunctionSelectorTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractFunctionSelectorTest.java deleted file mode 100644 index 7ea8d4a5d8..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractFunctionSelectorTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import org.bouncycastle.util.encoders.Hex; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatNoException; - -public class ContractFunctionSelectorTest { - @Test - @DisplayName("Can add all types") - void selector() { - var signature = new ContractFunctionSelector("testFunction") - .addAddress() - .addAddressArray() - .addBool() - .addBytes() - .addBytes32() - .addBytes32Array() - .addBytesArray() - .addFunction() - .addInt8() - .addInt8Array() - .addInt32() - .addInt32Array() - .addInt64() - .addInt64Array() - .addInt256() - .addInt256Array() - .addUint8() - .addUint8Array() - .addUint32() - .addUint32Array() - .addUint64() - .addUint64Array() - .addUint256() - .addUint256Array() - .addString() - .addStringArray() - .finish(); - - assertThat(Hex.toHexString(signature)).isEqualTo("4438e4ce"); - } - - @Test - @DisplayName("Throws in adding after finished") - void selectorError() { - var signature = new ContractFunctionSelector("testFunction") - .addAddress(); - signature.finish(); - - assertThatExceptionOfType(IllegalStateException.class).isThrownBy(signature::addStringArray); - assertThatNoException().isThrownBy(signature::finish); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractIdTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractIdTest.java deleted file mode 100644 index f7522a5f49..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractIdTest.java +++ /dev/null @@ -1,100 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.bouncycastle.util.encoders.Hex; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - - -class ContractIdTest { - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void fromString() { - SnapshotMatcher.expect(ContractId.fromString("0.0.5005").toString()).toMatchSnapshot(); - } - - @Test - void fromSolidityAddress() { - SnapshotMatcher.expect(ContractId.fromSolidityAddress("000000000000000000000000000000000000138D").toString()).toMatchSnapshot(); - } - - @Test - void fromSolidityAddressWith0x() { - SnapshotMatcher.expect(ContractId.fromSolidityAddress("0x000000000000000000000000000000000000138D").toString()).toMatchSnapshot(); - } - - @Test - void fromEvmAddress() { - SnapshotMatcher.expect(ContractId.fromEvmAddress(1, 2, "98329e006610472e6B372C080833f6D79ED833cf").toString()).toMatchSnapshot(); - } - - @Test - void fromEvmAddressWith0x() { - SnapshotMatcher.expect(ContractId.fromEvmAddress(1, 2, "0x98329e006610472e6B372C080833f6D79ED833cf").toString()).toMatchSnapshot(); - } - - @Test - void fromStringWithEvmAddress() { - SnapshotMatcher.expect(ContractId.fromString("1.2.98329e006610472e6B372C080833f6D79ED833cf").toString()).toMatchSnapshot(); - } - - @Test - void toFromBytes() throws InvalidProtocolBufferException { - ContractId a = ContractId.fromString("1.2.3"); - assertThat(ContractId.fromBytes(a.toBytes())).isEqualTo(a); - ContractId b = ContractId.fromEvmAddress(1, 2, "0x98329e006610472e6B372C080833f6D79ED833cf"); - assertThat(ContractId.fromBytes(b.toBytes())).isEqualTo(b); - } - - @Test - void toBytes() throws InvalidProtocolBufferException { - SnapshotMatcher.expect(Hex.toHexString(new ContractId(5005).toBytes())).toMatchSnapshot(); - } - - @Test - void fromBytes() throws InvalidProtocolBufferException { - SnapshotMatcher.expect(ContractId.fromBytes(new ContractId(5005).toBytes()).toString()).toMatchSnapshot(); - } - - @Test - void toSolidityAddress() { - SnapshotMatcher.expect(new ContractId(5005).toSolidityAddress()).toMatchSnapshot(); - } - - @Test - void toSolidityAddress2() { - SnapshotMatcher.expect(ContractId.fromEvmAddress(1, 2, "0x98329e006610472e6B372C080833f6D79ED833cf").toSolidityAddress()).toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractIdTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractIdTest.snap deleted file mode 100644 index e1cba5d74c..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractIdTest.snap +++ /dev/null @@ -1,48 +0,0 @@ -com.hedera.hashgraph.sdk.ContractIdTest.fromBytes=[ - "0.0.5005" -] - - -com.hedera.hashgraph.sdk.ContractIdTest.fromEvmAddress=[ - "1.2.98329e006610472e6b372c080833f6d79ed833cf" -] - - -com.hedera.hashgraph.sdk.ContractIdTest.fromEvmAddressWith0x=[ - "1.2.98329e006610472e6b372c080833f6d79ed833cf" -] - - -com.hedera.hashgraph.sdk.ContractIdTest.fromSolidityAddress=[ - "0.0.5005" -] - - -com.hedera.hashgraph.sdk.ContractIdTest.fromSolidityAddressWith0x=[ - "0.0.5005" -] - - -com.hedera.hashgraph.sdk.ContractIdTest.fromString=[ - "0.0.5005" -] - - -com.hedera.hashgraph.sdk.ContractIdTest.fromStringWithEvmAddress=[ - "1.2.98329e006610472e6b372c080833f6d79ed833cf" -] - - -com.hedera.hashgraph.sdk.ContractIdTest.toBytes=[ - "188d27" -] - - -com.hedera.hashgraph.sdk.ContractIdTest.toSolidityAddress2=[ - "98329e006610472e6b372c080833f6d79ed833cf" -] - - -com.hedera.hashgraph.sdk.ContractIdTest.toSolidityAddress=[ - "000000000000000000000000000000000000138d" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractInfoQueryTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractInfoQueryTest.java deleted file mode 100644 index 6c16d2d193..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractInfoQueryTest.java +++ /dev/null @@ -1,47 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -public class ContractInfoQueryTest { - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - var builder = com.hedera.hashgraph.sdk.proto.Query.newBuilder(); - new ContractInfoQuery() - .setContractId(ContractId.fromString("0.0.5005")) - .onMakeRequest(builder, QueryHeader.newBuilder().build()); - SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")).toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractInfoQueryTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractInfoQueryTest.snap deleted file mode 100644 index 91b0f3a930..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractInfoQueryTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.ContractInfoQueryTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.Query\ncontract_get_info {\n contract_i_d {\n contract_num: 5005\n realm_num: 0\n shard_num: 0\n }\n header {\n }\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractInfoTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractInfoTest.java deleted file mode 100644 index 4188b1391f..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractInfoTest.java +++ /dev/null @@ -1,81 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.ContractGetInfoResponse; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.bouncycastle.util.encoders.Hex; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Duration; -import java.time.Instant; - - -public class ContractInfoTest { - private final ContractGetInfoResponse.ContractInfo info = - ContractGetInfoResponse.ContractInfo.newBuilder() - .setContractID(new ContractId(1).toProtobuf()) - .setAccountID(new AccountId(2).toProtobuf()) - .setContractAccountID("3") - .setExpirationTime(InstantConverter.toProtobuf(Instant.ofEpochMilli(4))) - .setAutoRenewPeriod(DurationConverter.toProtobuf(Duration.ofDays(5))) - .setStorage(6) - .setMemo("7") - .setBalance(8) - .setLedgerId(LedgerId.TESTNET.toByteString()) - .build(); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void fromProtobuf() { - SnapshotMatcher.expect(ContractInfo.fromProtobuf(info).toString()) - .toMatchSnapshot(); - } - - @Test - void toProtobuf() { - SnapshotMatcher.expect(ContractInfo.fromProtobuf(info).toProtobuf()) - .toMatchSnapshot(); - } - - @Test - void toBytes() { - SnapshotMatcher.expect(Hex.toHexString(ContractInfo.fromProtobuf(info).toBytes())) - .toMatchSnapshot(); - } - - @Test - void fromBytes() throws InvalidProtocolBufferException { - SnapshotMatcher.expect(ContractInfo.fromBytes(info.toByteArray()).toString()) - .toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractLogInfoTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractLogInfoTest.java deleted file mode 100644 index 358569620e..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractLogInfoTest.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.hedera.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.ContractLoginfo; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.bouncycastle.util.encoders.Hex; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; - -public class ContractLogInfoTest { - private static final ContractLoginfo info = ContractLoginfo.newBuilder() - .setContractID(new ContractId(10).toProtobuf()) - .setBloom(ByteString.copyFrom("bloom", StandardCharsets.UTF_8)) - .addTopic(ByteString.copyFrom("bloom", StandardCharsets.UTF_8)) - .setData(ByteString.copyFrom("data", StandardCharsets.UTF_8)) - .build(); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void fromProtobuf() { - SnapshotMatcher.expect(ContractLogInfo.fromProtobuf(info).toString()) - .toMatchSnapshot(); - } - - @Test - void toProtobuf() throws InvalidProtocolBufferException { - SnapshotMatcher.expect(ContractLogInfo.fromProtobuf(info).toProtobuf().toString()) - .toMatchSnapshot(); - } - - @Test - void fromBytes() throws InvalidProtocolBufferException { - SnapshotMatcher.expect(ContractLogInfo.fromBytes(info.toByteArray()).toString()) - .toMatchSnapshot(); - } - - @Test - void toBytes() { - SnapshotMatcher.expect(Hex.toHexString(ContractLogInfo.fromProtobuf(info).toBytes())) - .toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractLogInfoTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractLogInfoTest.snap deleted file mode 100644 index 678753ed9c..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractLogInfoTest.snap +++ /dev/null @@ -1,18 +0,0 @@ -com.hedera.hashgraph.sdk.ContractLogInfoTest.fromBytes=[ - "ContractLogInfo{contractId=0.0.10, bloom=626c6f6f6d, topics=[626c6f6f6d]}" -] - - -com.hedera.hashgraph.sdk.ContractLogInfoTest.fromProtobuf=[ - "ContractLogInfo{contractId=0.0.10, bloom=626c6f6f6d, topics=[626c6f6f6d]}" -] - - -com.hedera.hashgraph.sdk.ContractLogInfoTest.toBytes=[ - "0a02180a1205626c6f6f6d1a05626c6f6f6d" -] - - -com.hedera.hashgraph.sdk.ContractLogInfoTest.toProtobuf=[ - "# com.hedera.hashgraph.sdk.proto.ContractLoginfo@cb39e330\nbloom: \"bloom\"\ncontract_i_d {\n contract_num: 10\n realm_num: 0\n shard_num: 0\n}\ntopic: \"bloom\"" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractNonceInfoTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractNonceInfoTest.java deleted file mode 100644 index a9c3c6f5b7..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractNonceInfoTest.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.hedera.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.bouncycastle.util.encoders.Hex; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -public class ContractNonceInfoTest { - private final com.hedera.hashgraph.sdk.proto.ContractNonceInfo info = - com.hedera.hashgraph.sdk.proto.ContractNonceInfo.newBuilder() - .setContractId(new ContractId(1).toProtobuf()) - .setNonce(2) - .build(); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void fromProtobuf() { - SnapshotMatcher.expect(ContractNonceInfo.fromProtobuf(info).toString()) - .toMatchSnapshot(); - } - - @Test - void toProtobuf() { - SnapshotMatcher.expect(ContractNonceInfo.fromProtobuf(info).toProtobuf()) - .toMatchSnapshot(); - } - - @Test - void toBytes() { - SnapshotMatcher.expect(Hex.toHexString(ContractNonceInfo.fromProtobuf(info).toBytes())) - .toMatchSnapshot(); - } - - @Test - void fromBytes() throws InvalidProtocolBufferException { - SnapshotMatcher.expect(ContractNonceInfo.fromBytes(info.toByteArray()).toString()) - .toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractUpdateTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractUpdateTransactionTest.java deleted file mode 100644 index e74b165ab8..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractUpdateTransactionTest.java +++ /dev/null @@ -1,132 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.ContractUpdateTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Duration; -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; - -public class ContractUpdateTransactionTest { - private static final PrivateKey privateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction() - .toString() - ).toMatchSnapshot(); - } - - @Test - void shouldSerialize2() { - SnapshotMatcher.expect(spawnTestTransaction2() - .toString() - ).toMatchSnapshot(); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new ContractUpdateTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - private ContractUpdateTransaction spawnTestTransaction() { - return new ContractUpdateTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setContractId(ContractId.fromString("0.0.5007")) - .setAdminKey(privateKey) - .setMaxAutomaticTokenAssociations(101) - .setAutoRenewPeriod(Duration.ofDays(1)) - .setContractMemo("3") - .setStakedAccountId(AccountId.fromString("0.0.3")) - .setExpirationTime(Instant.ofEpochMilli(4)) - .setProxyAccountId(new AccountId(4)) - .setMaxTransactionFee(Hbar.fromTinybars(100_000)) - .setAutoRenewAccountId(new AccountId(30)) - .freeze() - .sign(privateKey); - } - - private ContractUpdateTransaction spawnTestTransaction2() { - return new ContractUpdateTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setContractId(ContractId.fromString("0.0.5007")) - .setAdminKey(privateKey) - .setMaxAutomaticTokenAssociations(101) - .setAutoRenewPeriod(Duration.ofDays(1)) - .setContractMemo("3") - .setStakedNodeId(4L) - .setExpirationTime(Instant.ofEpochMilli(4)) - .setProxyAccountId(new AccountId(4)) - .setMaxTransactionFee(Hbar.fromTinybars(100_000)) - .setAutoRenewAccountId(new AccountId(30)) - .freeze() - .sign(privateKey); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = ContractUpdateTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldBytes2() throws Exception { - var tx = spawnTestTransaction2(); - var tx2 = ContractUpdateTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setContractUpdateInstance(ContractUpdateTransactionBody.newBuilder().build()) - .build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(ContractUpdateTransaction.class); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractUpdateTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractUpdateTransactionTest.snap deleted file mode 100644 index 614b0ac21d..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractUpdateTransactionTest.snap +++ /dev/null @@ -1,8 +0,0 @@ -com.hedera.hashgraph.sdk.ContractUpdateTransactionTest.shouldSerialize2=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\ncontract_update_instance {\n admin_key {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n auto_renew_account_id {\n account_num: 30\n realm_num: 0\n shard_num: 0\n }\n auto_renew_period {\n seconds: 86400\n }\n contract_i_d {\n contract_num: 5007\n realm_num: 0\n shard_num: 0\n }\n expiration_time {\n nanos: 4000000\n seconds: 0\n }\n max_automatic_token_associations {\n value: 101\n }\n memo_wrapper {\n value: \"3\"\n }\n proxy_account_i_d {\n account_num: 4\n realm_num: 0\n shard_num: 0\n }\n staked_node_id: 4\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] - - -com.hedera.hashgraph.sdk.ContractUpdateTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\ncontract_update_instance {\n admin_key {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n auto_renew_account_id {\n account_num: 30\n realm_num: 0\n shard_num: 0\n }\n auto_renew_period {\n seconds: 86400\n }\n contract_i_d {\n contract_num: 5007\n realm_num: 0\n shard_num: 0\n }\n expiration_time {\n nanos: 4000000\n seconds: 0\n }\n max_automatic_token_associations {\n value: 101\n }\n memo_wrapper {\n value: \"3\"\n }\n proxy_account_i_d {\n account_num: 4\n realm_num: 0\n shard_num: 0\n }\n staked_account_id {\n account_num: 3\n realm_num: 0\n shard_num: 0\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/CryptoTransferTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/CryptoTransferTransactionTest.java deleted file mode 100644 index 784d71452a..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/CryptoTransferTransactionTest.java +++ /dev/null @@ -1,168 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.CryptoTransferTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionList; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -public class CryptoTransferTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction() - .toString() - ).toMatchSnapshot(); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new TransferTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - private TransferTransaction spawnTestTransaction() { - return new TransferTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .addHbarTransfer(AccountId.fromString("0.0.5008"), Hbar.fromTinybars(400)) - .addHbarTransfer(AccountId.fromString("0.0.5006"), Hbar.fromTinybars(800).negated()) - .addHbarTransfer(AccountId.fromString("0.0.5007"), Hbar.fromTinybars(400)) - .addTokenTransfer(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.5008"), 400) - .addTokenTransferWithDecimals(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.5006"), -800, 3) - .addTokenTransferWithDecimals(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.5007"), 400, 3) - .addTokenTransfer(TokenId.fromString("0.0.4"), AccountId.fromString("0.0.5008"), 1) - .addTokenTransfer(TokenId.fromString("0.0.4"), AccountId.fromString("0.0.5006"), -1) - .addNftTransfer(TokenId.fromString("0.0.3").nft(2), AccountId.fromString("0.0.5008"), AccountId.fromString("0.0.5007")) - .addNftTransfer(TokenId.fromString("0.0.3").nft(1), AccountId.fromString("0.0.5008"), AccountId.fromString("0.0.5007")) - .addNftTransfer(TokenId.fromString("0.0.3").nft(3), AccountId.fromString("0.0.5008"), AccountId.fromString("0.0.5006")) - .addNftTransfer(TokenId.fromString("0.0.3").nft(4), AccountId.fromString("0.0.5007"), AccountId.fromString("0.0.5006")) - .addNftTransfer(TokenId.fromString("0.0.2").nft(4), AccountId.fromString("0.0.5007"), AccountId.fromString("0.0.5006")) - .setHbarTransferApproval(AccountId.fromString("0.0.5007"), true) - .setTokenTransferApproval(TokenId.fromString("0.0.4"), AccountId.fromString("0.0.5006"), true) - .setNftTransferApproval(new NftId(TokenId.fromString("0.0.4"), 4), true) - .setMaxTransactionFee(Hbar.fromTinybars(100_000)) - .freeze() - .sign(unusedPrivateKey); - } - - private TransferTransaction spawnModifiedTestTransaction() { - return new TransferTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .addHbarTransfer(AccountId.fromString("0.0.5008"), Hbar.fromTinybars(400)) - .addHbarTransfer(AccountId.fromString("0.0.5006"), Hbar.fromTinybars(800).negated()) - .addHbarTransfer(AccountId.fromString("0.0.5007"), Hbar.fromTinybars(400)) - .addTokenTransfer(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.5008"), 400) - .addTokenTransferWithDecimals(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.5006"), -800, 3) - .addTokenTransferWithDecimals(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.5007"), 400, 3) - .addTokenTransfer(TokenId.fromString("0.0.4"), AccountId.fromString("0.0.5008"), 1) - .addTokenTransfer(TokenId.fromString("0.0.4"), AccountId.fromString("0.0.5006"), -1) - .addNftTransfer(TokenId.fromString("0.0.3").nft(2), AccountId.fromString("0.0.5008"), AccountId.fromString("0.0.5007")) - .addNftTransfer(TokenId.fromString("0.0.3").nft(1), AccountId.fromString("0.0.5008"), AccountId.fromString("0.0.5007")) - .addNftTransfer(TokenId.fromString("0.0.3").nft(3), AccountId.fromString("0.0.5008"), AccountId.fromString("0.0.5006")) - .addNftTransfer(TokenId.fromString("0.0.3").nft(4), AccountId.fromString("0.0.5007"), AccountId.fromString("0.0.5006")) - .addNftTransfer(TokenId.fromString("0.0.2").nft(4), AccountId.fromString("0.0.5007"), AccountId.fromString("0.0.5006")) - .setHbarTransferApproval(AccountId.fromString("0.0.5007"), true) - // !!! .setTokenTransferApproval(TokenId.fromString("0.0.4"), AccountId.fromString("0.0.5006"), true) !!! - .setNftTransferApproval(new NftId(TokenId.fromString("0.0.4"), 4), true) - .setMaxTransactionFee(Hbar.fromTinybars(100_000)) - .freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = TransferTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void decimalsMustBeConsistent() { - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { - new TransferTransaction() - .addTokenTransferWithDecimals(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.8"), 100, 2) - .addTokenTransferWithDecimals(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.7"), -100, 3); - }); - } - - @Test - void canGetDecimals() { - var tx = new TransferTransaction(); - assertThat(tx.getTokenIdDecimals().get(TokenId.fromString("0.0.5"))).isNull(); - tx.addTokenTransfer(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.8"), 100); - assertThat(tx.getTokenIdDecimals().get(TokenId.fromString("0.0.5"))).isNull(); - tx.addTokenTransferWithDecimals(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.7"), -100, 5); - assertThat(tx.getTokenIdDecimals().get(TokenId.fromString("0.0.5"))).isEqualTo(5); - } - - @Test - void transactionBodiesMustMatch() throws InvalidProtocolBufferException { - com.hedera.hashgraph.sdk.proto.Transaction tx1 = TransactionList.parseFrom(spawnTestTransaction().toBytes()) - .getTransactionList(0); - com.hedera.hashgraph.sdk.proto.Transaction tx2 = TransactionList.parseFrom(spawnModifiedTestTransaction().toBytes()) - .getTransactionList(1); - var brokenTxList = TransactionList.newBuilder() - .addTransactionList(tx1) - .addTransactionList(tx2); - var brokenTxBytes = brokenTxList.build().toByteArray(); - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { - Transaction.fromBytes(brokenTxBytes); - }); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setCryptoTransfer(CryptoTransferTransactionBody.newBuilder().build()) - .build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(TransferTransaction.class); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/CryptoTransferTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/CryptoTransferTransactionTest.snap deleted file mode 100644 index f627277a84..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/CryptoTransferTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.CryptoTransferTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\ncrypto_transfer {\n token_transfers {\n nft_transfers {\n receiver_account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n sender_account_i_d {\n account_num: 5007\n realm_num: 0\n shard_num: 0\n }\n serial_number: 4\n }\n token {\n realm_num: 0\n shard_num: 0\n token_num: 2\n }\n }\n token_transfers {\n nft_transfers {\n receiver_account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n sender_account_i_d {\n account_num: 5007\n realm_num: 0\n shard_num: 0\n }\n serial_number: 4\n }\n nft_transfers {\n receiver_account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n sender_account_i_d {\n account_num: 5008\n realm_num: 0\n shard_num: 0\n }\n serial_number: 3\n }\n nft_transfers {\n receiver_account_i_d {\n account_num: 5007\n realm_num: 0\n shard_num: 0\n }\n sender_account_i_d {\n account_num: 5008\n realm_num: 0\n shard_num: 0\n }\n serial_number: 1\n }\n nft_transfers {\n receiver_account_i_d {\n account_num: 5007\n realm_num: 0\n shard_num: 0\n }\n sender_account_i_d {\n account_num: 5008\n realm_num: 0\n shard_num: 0\n }\n serial_number: 2\n }\n token {\n realm_num: 0\n shard_num: 0\n token_num: 3\n }\n }\n token_transfers {\n token {\n realm_num: 0\n shard_num: 0\n token_num: 4\n }\n transfers {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n amount: -1\n is_approval: true\n }\n transfers {\n account_i_d {\n account_num: 5008\n realm_num: 0\n shard_num: 0\n }\n amount: 1\n }\n }\n token_transfers {\n expected_decimals {\n value: 3\n }\n token {\n realm_num: 0\n shard_num: 0\n token_num: 5\n }\n transfers {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n amount: -800\n }\n transfers {\n account_i_d {\n account_num: 5007\n realm_num: 0\n shard_num: 0\n }\n amount: 400\n }\n transfers {\n account_i_d {\n account_num: 5008\n realm_num: 0\n shard_num: 0\n }\n amount: 400\n }\n }\n transfers {\n account_amounts {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n amount: -800\n }\n account_amounts {\n account_i_d {\n account_num: 5007\n realm_num: 0\n shard_num: 0\n }\n amount: 400\n is_approval: true\n }\n account_amounts {\n account_i_d {\n account_num: 5008\n realm_num: 0\n shard_num: 0\n }\n amount: 400\n }\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/CustomFeeListTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/CustomFeeListTest.java deleted file mode 100644 index 10219974d9..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/CustomFeeListTest.java +++ /dev/null @@ -1,98 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - -public class CustomFeeListTest { - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - static private List spawnCustomFeeListExample() { - var returnList = new ArrayList(); - returnList.add(new CustomFixedFee() - .setFeeCollectorAccountId(new AccountId(4322)) - .setDenominatingTokenId(new TokenId(483902)) - .setAmount(10) - ); - returnList.add(new CustomFractionalFee() - .setFeeCollectorAccountId(new AccountId(389042)) - .setNumerator(3) - .setDenominator(7) - .setMin(3) - .setMax(100) - ); - returnList.add(new CustomRoyaltyFee() - .setFeeCollectorAccountId(new AccountId(23423)) - .setNumerator(5) - .setDenominator(8) - .setFallbackFee(new CustomFixedFee() - .setDenominatingTokenId(new TokenId(483902)) - .setAmount(10)) - ); - return returnList; - } - - @Test - void shouldSerialize() throws Exception { - var originalCustomFeeList = spawnCustomFeeListExample(); - byte[] customFee0Bytes = originalCustomFeeList.get(0).toBytes(); - byte[] customFee1Bytes = originalCustomFeeList.get(1).toBytes(); - byte[] customFee2Bytes = originalCustomFeeList.get(2).toBytes(); - var copyCustomFeeList = new ArrayList(); - copyCustomFeeList.add(CustomFee.fromBytes(customFee0Bytes)); - copyCustomFeeList.add(CustomFee.fromBytes(customFee1Bytes)); - copyCustomFeeList.add(CustomFee.fromBytes(customFee2Bytes)); - assertThat(originalCustomFeeList.toString()).isEqualTo(copyCustomFeeList.toString()); - SnapshotMatcher.expect(originalCustomFeeList.toString()).toMatchSnapshot(); - } - - @Test - void deepClone() throws Exception { - var originalCustomFeeList = spawnCustomFeeListExample(); - var copyCustomFeeList = new ArrayList(); - for (var fee : originalCustomFeeList) { - copyCustomFeeList.add(fee.deepClone()); - } - var originalCustomFeeListString = originalCustomFeeList.toString(); - assertThat(originalCustomFeeListString).isEqualTo(copyCustomFeeList.toString()); - - // modifying clone doesn't affect original - ((CustomFixedFee) copyCustomFeeList.get(0)).setDenominatingTokenId(new TokenId(89803)); - assertThat(originalCustomFeeListString).isEqualTo(originalCustomFeeList.toString()); - - SnapshotMatcher.expect(originalCustomFeeList.toString()).toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/CustomFixedFeeTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/CustomFixedFeeTest.java deleted file mode 100644 index 913709a2f9..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/CustomFixedFeeTest.java +++ /dev/null @@ -1,98 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.FixedFee; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class CustomFixedFeeTest { - private static final boolean allCollectorsAreExempt = true; - private static final AccountId feeCollectorAccountId = new AccountId(1, 2, 3); - private static final long amount = 4; - private static final TokenId tokenId = new TokenId(5, 6, 7); - - private final FixedFee fee = FixedFee.newBuilder() - .setAmount(amount) - .setDenominatingTokenId(tokenId.toProtobuf()) - .build(); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void fromProtobuf() { - SnapshotMatcher.expect(CustomFixedFee.fromProtobuf(fee).toString()).toMatchSnapshot(); - } - - @Test - void deepCloneSubclass() { - var customFixedFee = new CustomFixedFee() - .setFeeCollectorAccountId(feeCollectorAccountId) - .setAllCollectorsAreExempt(allCollectorsAreExempt); - var clonedCustomFixedFee = customFixedFee.deepCloneSubclass(); - - assertThat(clonedCustomFixedFee.getFeeCollectorAccountId()).isEqualTo(feeCollectorAccountId); - assertThat(clonedCustomFixedFee.getAllCollectorsAreExempt()).isEqualTo(allCollectorsAreExempt); - } - - @Test - void toProtobuf() { - SnapshotMatcher.expect(CustomFixedFee.fromProtobuf(fee).toProtobuf().toString()).toMatchSnapshot(); - } - - @Test - void toFixedFeeProtobuf() { - SnapshotMatcher.expect(CustomFixedFee.fromProtobuf(fee).toFixedFeeProtobuf().toString()).toMatchSnapshot(); - } - - @Test - void getSetAmount() { - final var customFixedFee1 = new CustomFixedFee().setAmount(amount); - final var customFixedFee2 = new CustomFixedFee().setHbarAmount(Hbar.fromTinybars(amount)); - - assertThat(customFixedFee1.getAmount()).isEqualTo(amount); - assertThat(customFixedFee2.getHbarAmount().toTinybars()).isEqualTo(amount); - assertThat(customFixedFee1.getHbarAmount().toTinybars()).isEqualTo(customFixedFee2.getAmount()); - } - - @Test - void getSetDenominatingToken() { - final var customFixedFee = new CustomFixedFee().setDenominatingTokenId(tokenId); - assertThat(customFixedFee.getDenominatingTokenId()).isEqualTo(tokenId); - } - - @Test - void setSentinelValueToken() { - final var customFixedFee = new CustomFixedFee().setDenominatingTokenToSameToken(); - assertThat(customFixedFee.getDenominatingTokenId()).isEqualTo(new TokenId(0, 0, 0)); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/CustomFixedFeeTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/CustomFixedFeeTest.snap deleted file mode 100644 index 01cd03c8e7..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/CustomFixedFeeTest.snap +++ /dev/null @@ -1,13 +0,0 @@ -com.hedera.hashgraph.sdk.CustomFixedFeeTest.fromProtobuf=[ - "CustomFixedFee{feeCollectorAccountId=null, allCollectorsAreExempt=false, amount=4, demoninatingTokenId=5.6.7}" -] - - -com.hedera.hashgraph.sdk.CustomFixedFeeTest.toFixedFeeProtobuf=[ - "# com.hedera.hashgraph.sdk.proto.FixedFee@409d860\namount: 4\ndenominating_token_id {\n realm_num: 6\n shard_num: 5\n token_num: 7\n}" -] - - -com.hedera.hashgraph.sdk.CustomFixedFeeTest.toProtobuf=[ - "# com.hedera.hashgraph.sdk.proto.CustomFee@91885f65\nfixed_fee {\n amount: 4\n denominating_token_id {\n realm_num: 6\n shard_num: 5\n token_num: 7\n }\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/CustomFractionalFeeTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/CustomFractionalFeeTest.snap deleted file mode 100644 index ab84b8b03f..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/CustomFractionalFeeTest.snap +++ /dev/null @@ -1,8 +0,0 @@ -com.hedera.hashgraph.sdk.CustomFractionalFeeTest.fromProtobuf=[ - "CustomFractionalFee{feeCollectorAccountId=null, allCollectorsAreExempt=false, numerator=4, denominator=5, min=6, max=7, assessmentMethod=EXCLUSIVE}" -] - - -com.hedera.hashgraph.sdk.CustomFractionalFeeTest.toProtobuf=[ - "# com.hedera.hashgraph.sdk.proto.CustomFee@b2abef2c\nfractional_fee {\n fractional_amount {\n denominator: 5\n numerator: 4\n }\n maximum_amount: 7\n minimum_amount: 6\n net_of_transfers: true\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/CustomRoyaltyFeeTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/CustomRoyaltyFeeTest.java deleted file mode 100644 index 5ab1dca143..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/CustomRoyaltyFeeTest.java +++ /dev/null @@ -1,93 +0,0 @@ -/*- - * - * Hedera C++ SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.FixedFee; -import com.hedera.hashgraph.sdk.proto.Fraction; -import com.hedera.hashgraph.sdk.proto.RoyaltyFee; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class CustomRoyaltyFeeTest { - private static final boolean allCollectorsAreExempt = true; - private static final AccountId feeCollectorAccountId = new AccountId(1, 2, 3); - private static final int numerator = 4; - private static final int denominator = 5; - private static final CustomFixedFee fallbackFee = new CustomFixedFee().setAmount(6); - - private final RoyaltyFee fee = RoyaltyFee.newBuilder() - .setExchangeValueFraction(Fraction.newBuilder().setNumerator(numerator).setDenominator(denominator)) - .setFallbackFee(FixedFee.newBuilder().setAmount(6).build()) - .build(); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void fromProtobuf() { - SnapshotMatcher.expect(CustomRoyaltyFee.fromProtobuf(fee).toString()).toMatchSnapshot(); - } - - @Test - void deepCloneSubclass() { - var customRoyaltyFee = new CustomRoyaltyFee() - .setFeeCollectorAccountId(feeCollectorAccountId) - .setAllCollectorsAreExempt(allCollectorsAreExempt); - var clonedCustomRoyaltyFee = customRoyaltyFee.deepCloneSubclass(); - - assertThat(clonedCustomRoyaltyFee.getFeeCollectorAccountId()).isEqualTo(feeCollectorAccountId); - assertThat(clonedCustomRoyaltyFee.getAllCollectorsAreExempt()).isEqualTo(allCollectorsAreExempt); - } - - @Test - void toProtobuf() { - SnapshotMatcher.expect(CustomRoyaltyFee.fromProtobuf(fee).toProtobuf().toString()).toMatchSnapshot(); - } - - @Test - void getSetNumerator() { - final var customRoyaltyFee = new CustomRoyaltyFee().setNumerator(numerator); - assertThat(customRoyaltyFee.getNumerator()).isEqualTo(numerator); - } - - @Test - void getSetDenominator() { - final var customRoyaltyFee = new CustomRoyaltyFee().setDenominator(denominator); - assertThat(customRoyaltyFee.getDenominator()).isEqualTo(denominator); - } - - @Test - void getSetFallbackFee() { - final var customRoyaltyFee = new CustomRoyaltyFee().setFallbackFee(fallbackFee); - assertThat(customRoyaltyFee.getFallbackFee()).isNotNull(); - assertThat(customRoyaltyFee.getFallbackFee().getAmount()).isEqualTo(fallbackFee.getAmount()); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/CustomRoyaltyFeeTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/CustomRoyaltyFeeTest.snap deleted file mode 100644 index d4dd2ec935..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/CustomRoyaltyFeeTest.snap +++ /dev/null @@ -1,8 +0,0 @@ -com.hedera.hashgraph.sdk.CustomRoyaltyFeeTest.fromProtobuf=[ - "CustomRoyaltyFee{feeCollectorAccountId=null, allCollectorsAreExempt=false, numerator=4, denominator=5, fallbackFee=CustomFixedFee{feeCollectorAccountId=null, allCollectorsAreExempt=false, amount=6, demoninatingTokenId=null}}" -] - - -com.hedera.hashgraph.sdk.CustomRoyaltyFeeTest.toProtobuf=[ - "# com.hedera.hashgraph.sdk.proto.CustomFee@1abe2492\nroyalty_fee {\n exchange_value_fraction {\n denominator: 5\n numerator: 4\n }\n fallback_fee {\n amount: 6\n }\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/DelegateContractIdTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/DelegateContractIdTest.java deleted file mode 100644 index 70d17abd11..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/DelegateContractIdTest.java +++ /dev/null @@ -1,69 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.bouncycastle.util.encoders.Hex; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -class DelegateContractIdTest { - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void fromString() { - SnapshotMatcher.expect(DelegateContractId.fromString("0.0.5005").toString()).toMatchSnapshot(); - } - - @Test - void fromSolidityAddress() { - SnapshotMatcher.expect(DelegateContractId.fromSolidityAddress("000000000000000000000000000000000000138D").toString()).toMatchSnapshot(); - } - - @Test - void fromSolidityAddressWith0x() { - SnapshotMatcher.expect(DelegateContractId.fromSolidityAddress("0x000000000000000000000000000000000000138D").toString()).toMatchSnapshot(); - } - - @Test - void toBytes() throws InvalidProtocolBufferException { - SnapshotMatcher.expect(Hex.toHexString(new DelegateContractId(5005).toBytes())).toMatchSnapshot(); - } - - @Test - void fromBytes() throws InvalidProtocolBufferException { - SnapshotMatcher.expect(DelegateContractId.fromBytes(new DelegateContractId(5005).toBytes()).toString()).toMatchSnapshot(); - } - - @Test - void toSolidityAddress() { - SnapshotMatcher.expect(new DelegateContractId(5005).toSolidityAddress()).toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/DelegateContractIdTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/DelegateContractIdTest.snap deleted file mode 100644 index e166fe4141..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/DelegateContractIdTest.snap +++ /dev/null @@ -1,28 +0,0 @@ -com.hedera.hashgraph.sdk.DelegateContractIdTest.fromBytes=[ - "0.0.5005" -] - - -com.hedera.hashgraph.sdk.DelegateContractIdTest.fromSolidityAddress=[ - "0.0.5005" -] - - -com.hedera.hashgraph.sdk.DelegateContractIdTest.fromSolidityAddressWith0x=[ - "0.0.5005" -] - - -com.hedera.hashgraph.sdk.DelegateContractIdTest.fromString=[ - "0.0.5005" -] - - -com.hedera.hashgraph.sdk.DelegateContractIdTest.toBytes=[ - "188d27" -] - - -com.hedera.hashgraph.sdk.DelegateContractIdTest.toSolidityAddress=[ - "000000000000000000000000000000000000138d" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ECDSAPublicKeyTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/ECDSAPublicKeyTest.java deleted file mode 100644 index 3f8ba0fe90..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ECDSAPublicKeyTest.java +++ /dev/null @@ -1,238 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.math.BigDecimal; -import java.util.Collections; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatNoException; -import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; - -public class ECDSAPublicKeyTest { - @Test - void verifyTransaction() { - var transaction = new TransferTransaction() - .setNodeAccountIds(Collections.singletonList(new AccountId(3))) - .setTransactionId(TransactionId.generate(new AccountId(4))) - .freeze(); - - var key = PrivateKey.fromStringECDSA("8776c6b831a1b61ac10dac0304a2843de4716f54b1919bb91a2685d0fe3f3048"); - key.signTransaction(transaction); - - assertThat(key.getPublicKey().verifyTransaction(transaction)).isTrue(); - } - - @Test - @DisplayName("public key can be recovered from bytes") - void keyByteSerialization() { - PublicKey key1 = PrivateKey.generateECDSA().getPublicKey(); - byte[] key1Bytes = key1.toBytes(); - PublicKey key2 = PublicKey.fromBytes(key1Bytes); - byte[] key2Bytes = key2.toBytes(); - - assertThat(key2Bytes).containsExactly(key1Bytes); - } - - @Test - @DisplayName("public key can be recovered from raw bytes") - void keyByteSerialization2() { - PublicKey key1 = PrivateKey.generateECDSA().getPublicKey(); - byte[] key1Bytes = key1.toBytesRaw(); - PublicKey key2 = PublicKey.fromBytesECDSA(key1Bytes); - byte[] key2Bytes = key2.toBytesRaw(); - // cannot use PrivateKey.fromBytes() to parse raw ECDSA bytes - // because they're indistinguishable from ED25519 raw bytes - - assertThat(key2Bytes).containsExactly(key1Bytes); - } - - @Test - void keyByteValidation() { - byte[] invalidKeyECDSA = new byte[33]; - assertDoesNotThrow(() -> PublicKey.fromBytes(invalidKeyECDSA)); - assertDoesNotThrow(() -> PublicKey.fromBytesECDSA(invalidKeyECDSA)); - - byte[] invalidCompressedKey = new byte[]{ - 0x00, - (byte) 0xca, (byte) 0x35, 0x4b, 0x7c, (byte) 0xf4, (byte) 0x87, (byte) 0xd1, (byte) 0xbc, 0x43, - 0x5a, 0x25, 0x66, 0x77, 0x09, (byte) 0xc1, (byte) 0xab, (byte) 0x98, 0x0c, 0x11, 0x4d, - 0x35, (byte) 0x94, (byte) 0xe6, 0x25, (byte) 0x9e, (byte) 0x81, 0x2e, 0x6a, 0x70, 0x3d, - 0x4f, 0x51 - }; - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> PublicKey.fromBytesECDSA(invalidCompressedKey)); - - byte[] malformedKey = new byte[]{0x00, 0x01, 0x02}; - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> PublicKey.fromBytesECDSA(malformedKey)); - - byte[] validCompressedKey = new byte[]{ - 0x02, // Prefix for compressed keys - (byte) 0xca, (byte) 0x35, 0x4b, 0x7c, (byte) 0xf4, (byte) 0x87, (byte) 0xd1, (byte) 0xbc, 0x43, - 0x5a, 0x25, 0x66, 0x77, 0x09, (byte) 0xc1, (byte) 0xab, (byte) 0x98, 0x0c, 0x1f, 0x4d, - 0x35, (byte) 0x94, (byte) 0xe6, 0x25, (byte) 0x9e, (byte) 0x81, 0x2e, 0x6a, 0x75, 0x3d, - 0x4f, 0x59 - }; - assertDoesNotThrow(() -> PublicKey.fromBytesECDSA(validCompressedKey)); - - byte[] validDERKey = PrivateKey.generateECDSA().getPublicKey().toBytesDER(); - assertDoesNotThrow(() -> PublicKey.fromBytesECDSA(validDERKey)); - } - - @Test - @DisplayName("public key can be recovered from DER bytes") - void keyByteSerialization3() { - PublicKey key1 = PrivateKey.generateECDSA().getPublicKey(); - byte[] key1Bytes = key1.toBytesDER(); - PublicKey key2 = PublicKey.fromBytesDER(key1Bytes); - byte[] key2Bytes = key2.toBytesDER(); - PublicKey key3 = PublicKey.fromBytes(key1Bytes); - byte[] key3Bytes = key3.toBytesDER(); - - assertThat(key2Bytes).containsExactly(key1Bytes); - assertThat(key3Bytes).isEqualTo(key1Bytes); - } - - @Test - @DisplayName("public key can be recovered after transaction serialization") - void keyByteSerializationThroughTransaction() { - var senderAccount = AccountId.fromString("0.0.1337"); - var receiverAccount = AccountId.fromString("0.0.3"); - var transferAmount = Hbar.from(new BigDecimal("0.0001"), HbarUnit.HBAR); - var privateKey = PrivateKey.generateECDSA(); - var client = Client.forTestnet() - .setOperator(senderAccount, privateKey); - var tx = new TransferTransaction() - .addHbarTransfer(senderAccount, transferAmount.negated()) - .addHbarTransfer(receiverAccount, transferAmount); - - tx.freezeWith(client); - tx.signWithOperator(client); - - var bytes = tx.toBytes(); - - assertThatNoException().isThrownBy(() -> Transaction.fromBytes(bytes)); - assertThat(tx.getSignatures()).isNotEmpty(); - } - - @Test - @DisplayName("public key can be recovered from string") - void keyStringSerialization() { - PublicKey key1 = PrivateKey.generateECDSA().getPublicKey(); - String key1Str = key1.toString(); - PublicKey key2 = PublicKey.fromString(key1Str); - String key2Str = key2.toString(); - PublicKey key3 = PublicKey.fromString(key1Str); - String key3Str = key3.toString(); - - assertThat(key3.getClass()).isEqualTo(PublicKeyECDSA.class); - assertThat(key2Str).isEqualTo(key1Str); - assertThat(key3Str).isEqualTo(key1Str); - } - - @Test - @DisplayName("public key can be recovered from raw string") - void keyStringSerialization2() { - PublicKey key1 = PrivateKey.generateECDSA().getPublicKey(); - String key1Str = key1.toStringRaw(); - PublicKey key2 = PublicKey.fromStringECDSA(key1Str); - String key2Str = key2.toStringRaw(); - PublicKey key3 = PublicKey.fromStringECDSA(key2Str); - String key3Str = key3.toStringRaw(); - // cannot use PublicKey.fromString() to parse raw ECDSA string - // because it's indistinguishable from ED25519 raw bytes - - assertThat(key3.getClass()).isEqualTo(PublicKeyECDSA.class); - assertThat(key2Str).isEqualTo(key1Str); - assertThat(key3Str).isEqualTo(key1Str); - } - - @Test - @DisplayName("public key can be recovered from DER string") - void keyStringSerialization3() { - PublicKey key1 = PrivateKey.generateECDSA().getPublicKey(); - String key1Str = key1.toStringDER(); - PublicKey key2 = PublicKey.fromStringDER(key1Str); - String key2Str = key2.toStringDER(); - PublicKey key3 = PublicKey.fromString(key1Str); - String key3Str = key3.toStringDER(); - - assertThat(key3.getClass()).isEqualTo(PublicKeyECDSA.class); - assertThat(key2Str).isEqualTo(key1Str); - assertThat(key3Str).isEqualTo(key1Str); - } - - @Test - @DisplayName("public key is is ECDSA") - void keyIsECDSA() { - PublicKey key = PrivateKey.generateECDSA().getPublicKey(); - - assertThat(key.isECDSA()).isTrue(); - } - - @Test - @DisplayName("public key is is not Ed25519") - void keyIsNotEd25519() { - PublicKey key = PrivateKey.generateECDSA().getPublicKey(); - - assertThat(key.isED25519()).isFalse(); - } - - @Test - @DisplayName("to EVM address") - void toEvmAddress() { - // Generated by https://www.rfctools.com/ethereum-address-test-tool/ - String privateKeyString = "DEBAE3CA62AB3157110DBA79C8DE26540DC320EE9BE73A77D70BA175643A3500"; - String expectedEvmAddress = "d8eb8db03c699faa3f47adcdcd2ae91773b10f8b"; - - PrivateKey privateKey = PrivateKey.fromStringECDSA(privateKeyString); - PublicKey key = privateKey.getPublicKey(); - - assertThat(key.toEvmAddress()).hasToString(expectedEvmAddress); - } - - @Test - @DisplayName("DER import test vectors") - void DERImportTestVectors() { - // https://github.com/hashgraph/hedera-sdk-reference/issues/93#issue-1665972122 - var PUBLIC_KEY_DER1 = "302d300706052b8104000a032200028173079d2e996ef6b2d064fc82d5fc7094367211e28422bec50a2f75c365f5fd"; - var PUBLIC_KEY1 = "028173079d2e996ef6b2d064fc82d5fc7094367211e28422bec50a2f75c365f5fd"; - - var PUBLIC_KEY_DER2 = "3036301006072a8648ce3d020106052b8104000a032200036843f5cb338bbb4cdb21b0da4ea739d910951d6e8a5f703d313efe31afe788f4"; - var PUBLIC_KEY2 = "036843f5cb338bbb4cdb21b0da4ea739d910951d6e8a5f703d313efe31afe788f4"; - - var PUBLIC_KEY_DER3 = "3056301006072a8648ce3d020106052b8104000a03420004aaac1c3ac1bea0245b8e00ce1e2018f9eab61b6331fbef7266f2287750a6597795f855ddcad2377e22259d1fcb4e0f1d35e8f2056300c15070bcbfce3759cc9d"; - var PUBLIC_KEY3 = "03aaac1c3ac1bea0245b8e00ce1e2018f9eab61b6331fbef7266f2287750a65977"; - - var ecdsaPublicKey1 = PublicKey.fromStringDER(PUBLIC_KEY_DER1); - assertThat(ecdsaPublicKey1.toStringRaw()).isEqualTo(PUBLIC_KEY1); - - var ecdsaPublicKey2 = PublicKey.fromStringDER(PUBLIC_KEY_DER2); - assertThat(ecdsaPublicKey2.toStringRaw()).isEqualTo(PUBLIC_KEY2); - - var ecdsaPublicKey3 = PublicKey.fromStringDER(PUBLIC_KEY_DER3); - assertThat(ecdsaPublicKey3.toStringRaw()).isEqualTo(PUBLIC_KEY3); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/EthereumFlowMockTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/EthereumFlowMockTest.java deleted file mode 100644 index 501474e14c..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/EthereumFlowMockTest.java +++ /dev/null @@ -1,134 +0,0 @@ -package com.hedera.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import com.hedera.hashgraph.sdk.proto.FileID; -import com.hedera.hashgraph.sdk.proto.Response; -import com.hedera.hashgraph.sdk.proto.ResponseCodeEnum; -import com.hedera.hashgraph.sdk.proto.SignedTransaction; -import com.hedera.hashgraph.sdk.proto.Transaction; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionGetReceiptResponse; -import com.hedera.hashgraph.sdk.proto.TransactionReceipt; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; - -import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeoutException; - -import static org.assertj.core.api.Assertions.assertThat; - -class EthereumFlowMockTest { - static ByteString ETHEREUM_DATA = ByteString.fromHex("f864012f83018000947e3a9eaf9bcc39e2ffa38eb30bf7a93feacbc18180827653820277a0f9fbff985d374be4a55f296915002eec11ac96f1ce2df183adf992baa9390b2fa00c1e867cc960d9c74ec2e6a662b7908ec4c8cc9f3091e886bcefbeb2290fb792"); - - static ByteString LONG_CALL_DATA = ByteString.fromHex("00".repeat(5121)); - - @Test - void dontTruncateEthereumDataUnnecessarily() throws PrecheckStatusException, TimeoutException, InterruptedException, ReceiptStatusException { - List responses1 = List.of( - (Function) o -> { - var signedTransaction = SignedTransaction.parseFrom(((Transaction) o).getSignedTransactionBytes()); - var transactionBody = TransactionBody.parseFrom(signedTransaction.getBodyBytes()); - assertThat(transactionBody.getDataCase()).isEqualByComparingTo(TransactionBody.DataCase.ETHEREUMTRANSACTION); - assertThat(transactionBody.hasEthereumTransaction()).isTrue(); - assertThat(transactionBody.getEthereumTransaction().getEthereumData()).isEqualTo(ETHEREUM_DATA); - return TransactionResponse.newBuilder().setNodeTransactionPrecheckCodeValue(0).build(); - }, - Response.newBuilder() - .setTransactionGetReceipt(TransactionGetReceiptResponse.newBuilder() - .setReceipt(TransactionReceipt.newBuilder().setStatusValue(ResponseCodeEnum.SUCCESS_VALUE)) - ).build() - ); - - var responses = List.of(responses1); - - try (var mocker = Mocker.withResponses(responses)) { - new EthereumFlow() - .setEthereumData(ETHEREUM_DATA.toByteArray()) - .execute(mocker.client) - .getReceipt(mocker.client); - } - } - - @ParameterizedTest(name = "[{0}] ContractCreateFlow functions") - @CsvSource({ - "sync", - "async" - }) - void extractsCallData(String versionToTest) throws PrecheckStatusException, TimeoutException, InterruptedException, ReceiptStatusException, ExecutionException { - List responses1 = List.of( - (Function) o -> { - var signedTransaction = SignedTransaction.parseFrom(((Transaction) o).getSignedTransactionBytes()); - var transactionBody = TransactionBody.parseFrom(signedTransaction.getBodyBytes()); - assertThat(transactionBody.getDataCase()).isEqualByComparingTo(TransactionBody.DataCase.FILECREATE); - assertThat(transactionBody.hasFileCreate()).isTrue(); - assertThat(transactionBody.getFileCreate().getContents().size()).isEqualTo(4096); - return TransactionResponse.newBuilder().setNodeTransactionPrecheckCodeValue(0).build(); - }, - Response.newBuilder() - .setTransactionGetReceipt(TransactionGetReceiptResponse.newBuilder() - .setReceipt(TransactionReceipt.newBuilder() - .setStatusValue(ResponseCodeEnum.SUCCESS_VALUE) - .setFileID(FileID.newBuilder().setFileNum(1) - ))).build(), - (Function) o -> { - var signedTransaction = SignedTransaction.parseFrom(((Transaction) o).getSignedTransactionBytes()); - var transactionBody = TransactionBody.parseFrom(signedTransaction.getBodyBytes()); - assertThat(transactionBody.getDataCase()).isEqualByComparingTo(TransactionBody.DataCase.FILEAPPEND); - assertThat(transactionBody.hasFileAppend()).isTrue(); - assertThat(transactionBody.getFileAppend().hasFileID()).isTrue(); - assertThat(transactionBody.getFileAppend().getFileID().getFileNum()).isEqualTo(1); - assertThat(transactionBody.getFileAppend().getContents()).isEqualTo(LONG_CALL_DATA.substring(4096)); - return TransactionResponse.newBuilder().setNodeTransactionPrecheckCodeValue(0).build(); - }, - Response.newBuilder() - .setTransactionGetReceipt(TransactionGetReceiptResponse.newBuilder() - .setReceipt(TransactionReceipt.newBuilder().setStatusValue(ResponseCodeEnum.SUCCESS_VALUE)) - ).build(), - (Function) o -> { - var signedTransaction = SignedTransaction.parseFrom(((Transaction) o).getSignedTransactionBytes()); - var transactionBody = TransactionBody.parseFrom(signedTransaction.getBodyBytes()); - assertThat(transactionBody.getDataCase()).isEqualByComparingTo(TransactionBody.DataCase.ETHEREUMTRANSACTION); - assertThat(transactionBody.hasEthereumTransaction()).isTrue(); - assertThat(EthereumTransactionData.fromBytes(transactionBody.getEthereumTransaction().getEthereumData().toByteArray()).callData).isEmpty(); - return TransactionResponse.newBuilder().setNodeTransactionPrecheckCodeValue(0).build(); - }, - Response.newBuilder() - .setTransactionGetReceipt(TransactionGetReceiptResponse.newBuilder() - .setReceipt(TransactionReceipt.newBuilder().setStatusValue(ResponseCodeEnum.SUCCESS_VALUE)) - ).build() - ); - - var responses = List.of(responses1); - - EthereumFlow ethereumFlow; - try (var mocker = Mocker.withResponses(responses)) { - var ethereumData = EthereumTransactionData.fromBytes(ETHEREUM_DATA.toByteArray()); - ethereumData.callData = LONG_CALL_DATA.toByteArray(); - - if (versionToTest.equals("sync")) { - ethereumFlow = new EthereumFlow() - .setMaxGasAllowance(Hbar.fromTinybars(25)) - .setEthereumData(ethereumData.toBytes()); - - ethereumFlow - .execute(mocker.client) - .getReceipt(mocker.client); - } else { - ethereumFlow = new EthereumFlow() - .setMaxGasAllowance(Hbar.fromTinybars(25)) - .setEthereumData(ethereumData.toBytes()); - - ethereumFlow - .executeAsync(mocker.client) - .thenCompose(response -> response.getReceiptAsync(mocker.client)) - .get(); - } - - assertThat(ethereumFlow.getEthereumData()).isNotNull(); - assertThat(ethereumFlow.getMaxGasAllowance().toTinybars()).isEqualTo(25); - } - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/EthereumTransactionDataLegacyTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/EthereumTransactionDataLegacyTest.java deleted file mode 100644 index e8c67b418e..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/EthereumTransactionDataLegacyTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.hedera.hashgraph.sdk; - -import org.bouncycastle.util.encoders.Hex; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class EthereumTransactionDataLegacyTest { - // https://github.com/hashgraph/hedera-services/blob/1e01d9c6b8923639b41359c55413640b589c4ec7/hapi-utils/src/test/java/com/hedera/services/ethereum/EthTxDataTest.java#L49 - static final String RAW_TX_TYPE_0 = - "f864012f83018000947e3a9eaf9bcc39e2ffa38eb30bf7a93feacbc18180827653820277a0f9fbff985d374be4a55f296915002eec11ac96f1ce2df183adf992baa9390b2fa00c1e867cc960d9c74ec2e6a662b7908ec4c8cc9f3091e886bcefbeb2290fb792"; - static final String RAW_TX_TYPE_0_TRIMMED_LAST_BYTES = - "f864012f83018000947e3a9eaf9bcc39e2ffa38eb30bf7a93feacbc18180827653820277a0f9fbff985d374be4a55f296915002eec11ac96f1ce2df183adf992baa9390b2fa00c1e867cc960d9c74ec2e6a662b7908ec4c8cc9f3091e886bcefbeb2290000"; - static final String RAW_TX_TYPE_2 = - "02f87082012a022f2f83018000947e3a9eaf9bcc39e2ffa38eb30bf7a93feacbc181880de0b6b3a764000083123456c001a0df48f2efd10421811de2bfb125ab75b2d3c44139c4642837fb1fccce911fd479a01aaf7ae92bee896651dfc9d99ae422a296bf5d9f1ca49b2d96d82b79eb112d66"; - - @Test - public void legacyToFromBytes() { - var data = (EthereumTransactionDataLegacy) EthereumTransactionData.fromBytes(Hex.decode(RAW_TX_TYPE_0)); - assertThat(RAW_TX_TYPE_0).isEqualTo(Hex.toHexString(data.toBytes())); - - // Chain ID is not part of the legacy ethereum transaction, so why are you calculating and checking it? - // assertEquals("012a", Hex.toHexString(data.chainId())); - - assertThat(Hex.toHexString(data.nonce)).isEqualTo("01"); - assertThat(Hex.toHexString(data.gasPrice)).isEqualTo("2f"); - assertThat(Hex.toHexString(data.gasLimit)).isEqualTo("018000"); - assertThat(Hex.toHexString(data.to)).isEqualTo("7e3a9eaf9bcc39e2ffa38eb30bf7a93feacbc181"); - assertThat(Hex.toHexString(data.value)).isEqualTo(""); - assertThat(Hex.toHexString(data.callData)).isEqualTo("7653"); - assertThat(Hex.toHexString(data.v)).isEqualTo("0277"); - assertThat(Hex.toHexString(data.r)).isEqualTo("f9fbff985d374be4a55f296915002eec11ac96f1ce2df183adf992baa9390b2f"); - assertThat(Hex.toHexString(data.s)).isEqualTo("0c1e867cc960d9c74ec2e6a662b7908ec4c8cc9f3091e886bcefbeb2290fb792"); - - // We don't currently support a way to get the ethereum has, but we probably should - // assertEquals("9ffbd69c44cf643ed8d1e756b505e545e3b5dd3a6b5ef9da1d8eca6679706594", - // Hex.toHexString(data.getEthereumHash())); - } - - @Test - public void eip1559ToFromBytes() { - var data = (EthereumTransactionDataEip1559) EthereumTransactionData.fromBytes(Hex.decode(RAW_TX_TYPE_2)); - assertThat(RAW_TX_TYPE_2).isEqualTo(Hex.toHexString(data.toBytes())); - - assertThat(Hex.toHexString(data.chainId)).isEqualTo("012a"); - assertThat(Hex.toHexString(data.nonce)).isEqualTo("02"); - assertThat(Hex.toHexString(data.maxPriorityGas)).isEqualTo("2f"); - assertThat(Hex.toHexString(data.maxGas)).isEqualTo("2f"); - assertThat(Hex.toHexString(data.gasLimit)).isEqualTo("018000"); - assertThat(Hex.toHexString(data.to)).isEqualTo("7e3a9eaf9bcc39e2ffa38eb30bf7a93feacbc181"); - assertThat(Hex.toHexString(data.value)).isEqualTo("0de0b6b3a7640000"); - assertThat(Hex.toHexString(data.callData)).isEqualTo("123456"); - assertThat(Hex.toHexString(data.accessList)).isEqualTo(""); - assertThat(Hex.toHexString(data.recoveryId)).isEqualTo("01"); - assertThat(Hex.toHexString(data.r)).isEqualTo("df48f2efd10421811de2bfb125ab75b2d3c44139c4642837fb1fccce911fd479"); - assertThat(Hex.toHexString(data.s)).isEqualTo("1aaf7ae92bee896651dfc9d99ae422a296bf5d9f1ca49b2d96d82b79eb112d66"); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/EthereumTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/EthereumTransactionTest.java deleted file mode 100644 index bc817b0ba3..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/EthereumTransactionTest.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.hedera.hashgraph.sdk; - -import io.github.jsonSnapshot.SnapshotMatcher; -import org.bouncycastle.util.encoders.Hex; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; - -public class EthereumTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - EthereumTransaction spawnTestTransaction() { - return new EthereumTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setEthereumData(Hex.decode("deadbeef")) - .setCallDataFileId(FileId.fromString("4.5.6")) - .setMaxGasAllowanceHbar(Hbar.fromString("3")) - .setMaxTransactionFee(new Hbar(1)) - .freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new EthereumTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction() - .toString() - ).toMatchSnapshot(); - } - - @Test - void shouldBytesNft() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = TokenCreateTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx2.toString()); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/EthereumTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/EthereumTransactionTest.snap deleted file mode 100644 index e1076942f7..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/EthereumTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.EthereumTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nethereum_transaction {\n call_data {\n file_num: 6\n realm_num: 5\n shard_num: 4\n }\n ethereum_data: \"\\336\\255\\276\\357\"\n max_gas_allowance: 300000000\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ExchangeRatesTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/ExchangeRatesTest.java deleted file mode 100644 index dd29ee090e..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ExchangeRatesTest.java +++ /dev/null @@ -1,51 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import org.bouncycastle.util.encoders.Hex; -import org.junit.jupiter.api.Test; - -import java.time.Instant; - -import static org.assertj.core.api.Assertions.assertThat; - -public class ExchangeRatesTest { - private static final String exchangeRateSetHex = "0a1008b0ea0110b6b4231a0608f0bade9006121008b0ea01108cef231a060880d7de9006"; - - @Test - void fromProtobuf() throws InvalidProtocolBufferException { - byte[] exchangeRatesBytes = Hex.decode(exchangeRateSetHex); - - ExchangeRates exchangeRates = ExchangeRates.fromBytes(exchangeRatesBytes); - - assertThat(exchangeRates.currentRate.cents).isEqualTo(580150); - assertThat(exchangeRates.currentRate.hbars).isEqualTo(30000); - Instant currentExpirationTime = Instant.ofEpochSecond(1645714800); - assertThat(exchangeRates.currentRate.expirationTime).isEqualTo(currentExpirationTime); - assertThat(exchangeRates.currentRate.exchangeRateInCents).isEqualTo(19.338333333333335); - - assertThat(exchangeRates.nextRate.cents).isEqualTo(587660); - assertThat(exchangeRates.nextRate.hbars).isEqualTo(30000); - Instant nextExpirationTime = Instant.ofEpochSecond(1645718400); - assertThat(exchangeRates.nextRate.expirationTime).isEqualTo(nextExpirationTime); - assertThat(exchangeRates.nextRate.exchangeRateInCents).isEqualTo(19.588666666666665); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ExecutableTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/ExecutableTest.java deleted file mode 100644 index fd85ccfa39..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ExecutableTest.java +++ /dev/null @@ -1,664 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.logger.LogLevel; -import com.hedera.hashgraph.sdk.logger.Logger; -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import com.hedera.hashgraph.sdk.proto.Response; -import com.hedera.hashgraph.sdk.proto.ResponseCodeEnum; -import com.hedera.hashgraph.sdk.proto.ResponseHeader; -import io.grpc.MethodDescriptor; -import io.grpc.StatusRuntimeException; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.stubbing.Answer; - -import javax.annotation.Nullable; -import java.time.Duration; -import java.time.Instant; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -class ExecutableTest { - Client client; - Network network; - Node node3, node4, node5; - List nodeAccountIds; - - @BeforeEach - void setup() { - client = Client.forMainnet(); - network = mock(Network.class); - client.network = network; - client.setLogger(new Logger(LogLevel.WARN)); - - node3 = mock(Node.class); - node4 = mock(Node.class); - node5 = mock(Node.class); - - when(node3.getAccountId()).thenReturn(new AccountId(3)); - when(node4.getAccountId()).thenReturn(new AccountId(4)); - when(node5.getAccountId()).thenReturn(new AccountId(5)); - when(network.getNodeProxies(new AccountId(3))).thenReturn(List.of(node3)); - when(network.getNodeProxies(new AccountId(4))).thenReturn(List.of(node4)); - when(network.getNodeProxies(new AccountId(5))).thenReturn(List.of(node5)); - - nodeAccountIds = Arrays.asList( - new AccountId(3), - new AccountId(4), - new AccountId(5) - ); - } - - @Test - void firstNodeHealthy() { - when(node3.isHealthy()).thenReturn(true); - - var tx = new DummyTransaction(); - tx.setNodeAccountIds(nodeAccountIds); - tx.setNodesFromNodeAccountIds(client); - tx.setMinBackoff(Duration.ofMillis(10)); - tx.setMaxBackoff(Duration.ofMillis(1000)); - - var node = tx.getNodeForExecute(1); - assertThat(node).isEqualTo(node3); - } - - - @Test - void calloptionsShouldRespectGrpcDeadline() { - when(node3.isHealthy()).thenReturn(true); - - var tx = new DummyTransaction(); - tx.setNodeAccountIds(nodeAccountIds); - tx.setNodesFromNodeAccountIds(client); - tx.setMinBackoff(Duration.ofMillis(10)); - tx.setMaxBackoff(Duration.ofMillis(1000)); - tx.setGrpcDeadline(Duration.ofSeconds(10)); - - var grpcRequest = tx.getGrpcRequest(1); - - var timeRemaining = grpcRequest.getCallOptions().getDeadline().timeRemaining(TimeUnit.MILLISECONDS); - assertThat(timeRemaining).isLessThan(10000); - assertThat(timeRemaining).isGreaterThan(9000); - } - - @Test - void executableShouldUseGrpcDeadline() throws InterruptedException, PrecheckStatusException, TimeoutException { - when(node3.isHealthy()).thenReturn(true); - - var tx = new DummyTransaction(); - tx.setNodeAccountIds(nodeAccountIds); - tx.setNodesFromNodeAccountIds(client); - tx.setMinBackoff(Duration.ofMillis(10)); - tx.setMaxBackoff(Duration.ofMillis(1000)); - tx.setMaxAttempts(10); - - var timeout = Duration.ofSeconds(5); - var currentTimeRemaining = new AtomicLong(timeout.toMillis()); - final long minimumRetryDelayMs = 100; - final long defaultDeadlineMs = timeout.toMillis() - (minimumRetryDelayMs * (tx.getMaxAttempts() / 2)); - - // later on when the transaction is executed its grpc deadline should not be modified... - tx.setGrpcDeadline(Duration.ofMillis(defaultDeadlineMs)); - - tx.blockingUnaryCall = (grpcRequest) -> { - var grpc = (Executable.GrpcRequest) grpcRequest; - - var grpcTimeRemaining = grpc.getCallOptions().getDeadline().timeRemaining(TimeUnit.MILLISECONDS); - - // the actual grpc deadline should be no larger than the smaller of the two values - - // the default transaction level grpc deadline and the remaining timeout - assertThat(grpcTimeRemaining).isLessThanOrEqualTo(defaultDeadlineMs); - assertThat(grpcTimeRemaining).isLessThanOrEqualTo(currentTimeRemaining.get()); - - assertThat(grpcTimeRemaining).isGreaterThan(0); - - // transaction's grpc deadline should keep its original value - assertThat(tx.grpcDeadline().toMillis()).isEqualTo(defaultDeadlineMs); - - currentTimeRemaining.set(currentTimeRemaining.get() - minimumRetryDelayMs); - - if (currentTimeRemaining.get() > 0) { - try { - Thread.sleep(minimumRetryDelayMs); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - - // Status.UNAVAILABLE tells the Executable to retry the request - throw new StatusRuntimeException(io.grpc.Status.UNAVAILABLE); - } - - throw new StatusRuntimeException(io.grpc.Status.ABORTED); - }; - - assertThatExceptionOfType(MaxAttemptsExceededException.class).isThrownBy(() -> { - tx.execute(client, timeout); - }); - } - - @Test - void multipleNodesUnhealthy() { - when(node3.isHealthy()).thenReturn(false); - when(node4.isHealthy()).thenReturn(true); - - when(node3.getRemainingTimeForBackoff()).thenReturn(1000L); - - var tx = new DummyTransaction(); - tx.setNodeAccountIds(nodeAccountIds); - tx.setNodesFromNodeAccountIds(client); - tx.setMinBackoff(Duration.ofMillis(10)); - tx.setMaxBackoff(Duration.ofMillis(1000)); - - var node = tx.getNodeForExecute(1); - assertThat(node).isEqualTo(node4); - } - - @Test - void allNodesUnhealthy() { - when(node3.isHealthy()).thenReturn(false); - when(node4.isHealthy()).thenReturn(false); - when(node5.isHealthy()).thenReturn(false); - - when(node3.getRemainingTimeForBackoff()).thenReturn(4000L); - when(node4.getRemainingTimeForBackoff()).thenReturn(3000L); - when(node5.getRemainingTimeForBackoff()).thenReturn(5000L); - - var tx = new DummyTransaction(); - tx.setNodeAccountIds(nodeAccountIds); - tx.setNodesFromNodeAccountIds(client); - tx.setMinBackoff(Duration.ofMillis(10)); - tx.setMaxBackoff(Duration.ofMillis(1000)); - tx.nodeAccountIds.setIndex(1); - - var node = tx.getNodeForExecute(1); - assertThat(node).isEqualTo(node4); - } - - @Test - void multipleRequestsWithSingleHealthyNode() { - when(node3.isHealthy()).thenReturn(true); - when(node4.isHealthy()).thenReturn(false); - when(node5.isHealthy()).thenReturn(false); - - when(node4.getRemainingTimeForBackoff()).thenReturn(4000L); - when(node5.getRemainingTimeForBackoff()).thenReturn(3000L); - - var tx = new DummyTransaction(); - tx.setNodeAccountIds(nodeAccountIds); - tx.setNodesFromNodeAccountIds(client); - tx.setMinBackoff(Duration.ofMillis(10)); - tx.setMaxBackoff(Duration.ofMillis(1000)); - - var node = tx.getNodeForExecute(1); - assertThat(node).isEqualTo(node3); - tx.nodeAccountIds.advance(); - tx.nodes.advance(); - - node = tx.getNodeForExecute(2); - assertThat(node).isEqualTo(node3); - verify(node4).getRemainingTimeForBackoff(); - verify(node5).getRemainingTimeForBackoff(); - } - - @Test - void multipleRequestsWithNoHealthyNodes() { - AtomicInteger i = new AtomicInteger(); - - when(node3.isHealthy()).thenReturn(false); - when(node4.isHealthy()).thenReturn(false); - when(node5.isHealthy()).thenReturn(false); - - long[] node3Times = {4000, 3000, 1000}; - long[] node4Times = {3000, 1000, 4000}; - long[] node5Times = {1000, 3000, 4000}; - - when(node3.getRemainingTimeForBackoff()).thenAnswer((Answer) invocation -> node3Times[i.get()]); - when(node4.getRemainingTimeForBackoff()).thenAnswer((Answer) invocation -> node4Times[i.get()]); - when(node5.getRemainingTimeForBackoff()).thenAnswer((Answer) invocation -> node5Times[i.get()]); - - var tx = new DummyTransaction(); - tx.setNodeAccountIds(nodeAccountIds); - tx.setNodesFromNodeAccountIds(client); - tx.setMinBackoff(Duration.ofMillis(10)); - tx.setMaxBackoff(Duration.ofMillis(1000)); - - var node = tx.getNodeForExecute(1); - assertThat(node).isEqualTo(node5); - i.incrementAndGet(); - - node = tx.getNodeForExecute(2); - assertThat(node).isEqualTo(node4); - i.incrementAndGet(); - - node = tx.getNodeForExecute(3); - assertThat(node).isEqualTo(node3); - } - - @Test - void successfulExecute() throws PrecheckStatusException, TimeoutException { - var now = java.time.Instant.now(); - var tx = new DummyTransaction() { - @Nullable - @Override - TransactionResponse mapResponse(com.hedera.hashgraph.sdk.proto.TransactionResponse response, - AccountId nodeId, com.hedera.hashgraph.sdk.proto.Transaction request) { - return new TransactionResponse( - new AccountId(3), - TransactionId.withValidStart(new AccountId(3), now), - new byte[]{1, 2, 3}, - null) - .setValidateStatus(true); - } - }; - - var nodeAccountIds = Arrays.asList( - new AccountId(3), - new AccountId(4), - new AccountId(5) - ); - tx.setNodeAccountIds(nodeAccountIds); - - var txResp = - com.hedera.hashgraph.sdk.proto.TransactionResponse - .newBuilder() - .setNodeTransactionPrecheckCode(ResponseCodeEnum.OK) - .build(); - - tx.blockingUnaryCall = (grpcRequest) -> txResp; - com.hedera.hashgraph.sdk.TransactionResponse resp = (com.hedera.hashgraph.sdk.TransactionResponse) tx.execute( - client); - - assertThat(resp.nodeId).isEqualTo(new AccountId(3)); - assertThat(resp.getValidateStatus()).isTrue(); - assertThat(resp.toString()).isNotNull(); - } - - @Test - void executeWithChannelFailure() throws PrecheckStatusException, TimeoutException { - when(node3.isHealthy()).thenReturn(true); - when(node4.isHealthy()).thenReturn(true); - - when(node3.channelFailedToConnect(any(Instant.class))).thenReturn(true); - when(node4.channelFailedToConnect(any(Instant.class))).thenReturn(false); - - var now = java.time.Instant.now(); - var tx = new DummyTransaction() { - @Nullable - @Override - TransactionResponse mapResponse(com.hedera.hashgraph.sdk.proto.TransactionResponse response, - AccountId nodeId, com.hedera.hashgraph.sdk.proto.Transaction request) { - return new TransactionResponse( - new AccountId(4), - TransactionId.withValidStart(new AccountId(4), now), - new byte[]{1, 2, 3}, - null); - } - }; - - var nodeAccountIds = Arrays.asList( - new AccountId(3), - new AccountId(4), - new AccountId(5) - ); - tx.setNodeAccountIds(nodeAccountIds); - - var txResp = - com.hedera.hashgraph.sdk.proto.TransactionResponse - .newBuilder() - .setNodeTransactionPrecheckCode(ResponseCodeEnum.OK) - .build(); - - tx.blockingUnaryCall = (grpcRequest) -> txResp; - com.hedera.hashgraph.sdk.TransactionResponse resp = (com.hedera.hashgraph.sdk.TransactionResponse) tx.execute( - client); - - verify(node3).channelFailedToConnect(any(Instant.class)); - verify(node4).channelFailedToConnect(any(Instant.class)); - assertThat(resp.nodeId).isEqualTo(new AccountId(4)); - } - - @Test - void executeWithAllUnhealthyNodes() throws PrecheckStatusException, TimeoutException { - AtomicInteger i = new AtomicInteger(); - - // 1st round, pick node3, fail channel connect - // 2nd round, pick node4, fail channel connect - // 3rd round, pick node5, fail channel connect - // 4th round, pick node 3, wait for delay, channel connect ok - when(node3.isHealthy()).thenAnswer((Answer) inv -> i.get() == 0); - when(node4.isHealthy()).thenAnswer((Answer) inv -> i.get() == 0); - when(node5.isHealthy()).thenAnswer((Answer) inv -> i.get() == 0); - - when(node3.channelFailedToConnect(any(Instant.class))).thenAnswer((Answer) inv -> i.get() == 0); - when(node4.channelFailedToConnect(any(Instant.class))).thenAnswer((Answer) inv -> i.get() == 0); - when(node5.channelFailedToConnect(any(Instant.class))).thenAnswer( - (Answer) inv -> i.getAndIncrement() == 0); - - when(node3.getRemainingTimeForBackoff()).thenReturn(500L); - when(node4.getRemainingTimeForBackoff()).thenReturn(600L); - when(node5.getRemainingTimeForBackoff()).thenReturn(700L); - - var now = java.time.Instant.now(); - var tx = new DummyTransaction() { - @Nullable - @Override - TransactionResponse mapResponse(com.hedera.hashgraph.sdk.proto.TransactionResponse response, - AccountId nodeId, com.hedera.hashgraph.sdk.proto.Transaction request) { - return new TransactionResponse( - new AccountId(3), - TransactionId.withValidStart(new AccountId(3), now), - new byte[]{1, 2, 3}, - null); - } - }; - - var nodeAccountIds = Arrays.asList( - new AccountId(3), - new AccountId(4), - new AccountId(5) - ); - tx.setNodeAccountIds(nodeAccountIds); - - var txResp = - com.hedera.hashgraph.sdk.proto.TransactionResponse - .newBuilder() - .setNodeTransactionPrecheckCode(ResponseCodeEnum.OK) - .build(); - - tx.blockingUnaryCall = (grpcRequest) -> txResp; - com.hedera.hashgraph.sdk.TransactionResponse resp = (com.hedera.hashgraph.sdk.TransactionResponse) tx.execute( - client); - - verify(node3, times(2)).channelFailedToConnect(any(Instant.class)); - verify(node4).channelFailedToConnect(any(Instant.class)); - verify(node5).channelFailedToConnect(any(Instant.class)); - assertThat(resp.nodeId).isEqualTo(new AccountId(3)); - } - - @Test - void executeExhaustRetries() { - when(node3.isHealthy()).thenReturn(true); - when(node4.isHealthy()).thenReturn(true); - when(node5.isHealthy()).thenReturn(true); - - when(node3.channelFailedToConnect(any(Instant.class))).thenReturn(true); - when(node4.channelFailedToConnect(any(Instant.class))).thenReturn(true); - when(node5.channelFailedToConnect(any(Instant.class))).thenReturn(true); - - var tx = new DummyTransaction(); - var nodeAccountIds = Arrays.asList( - new AccountId(3), - new AccountId(4), - new AccountId(5) - ); - tx.setNodeAccountIds(nodeAccountIds); - assertThatExceptionOfType(MaxAttemptsExceededException.class).isThrownBy(() -> tx.execute(client)); - } - - @Test - void executeRetriableErrorDuringCall() { - AtomicInteger i = new AtomicInteger(); - - when(node3.isHealthy()).thenReturn(true); - when(node4.isHealthy()).thenReturn(true); - - when(node3.channelFailedToConnect(any(Instant.class))).thenReturn(false); - when(node4.channelFailedToConnect(any(Instant.class))).thenReturn(false); - - var tx = new DummyTransaction(); - var nodeAccountIds = Arrays.asList( - new AccountId(3), - new AccountId(4), - new AccountId(5) - ); - tx.setNodeAccountIds(nodeAccountIds); - - tx.blockingUnaryCall = (grpcRequest) -> { - if (i.getAndIncrement() == 0) { - throw new StatusRuntimeException(io.grpc.Status.UNAVAILABLE); - } else { - throw new StatusRuntimeException(io.grpc.Status.ABORTED); - } - }; - - assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> tx.execute(client)); - - verify(node3).channelFailedToConnect(any(Instant.class)); - verify(node4).channelFailedToConnect(any(Instant.class)); - } - - @Test - void testChannelFailedToConnectTimeout() { - TransactionResponse transactionResponse = new TransactionResponse( - new AccountId(3), - TransactionId.withValidStart(new AccountId(3), java.time.Instant.now()), - new byte[]{1, 2, 3}, - null - ); - var tx = new DummyTransaction(); - - tx.blockingUnaryCall = (grpcRequest) -> { - throw new StatusRuntimeException(io.grpc.Status.UNAVAILABLE); - }; - - when(node3.isHealthy()).thenReturn(true); - when(node3.channelFailedToConnect(any(Instant.class))).thenReturn(true); - - assertThatExceptionOfType(MaxAttemptsExceededException.class).isThrownBy( - () -> transactionResponse.getReceipt(client, Duration.ofSeconds(2))); - } - - @Test - void executeQueryDelay() throws PrecheckStatusException, TimeoutException { - when(node3.isHealthy()).thenReturn(true); - when(node4.isHealthy()).thenReturn(true); - - when(node3.channelFailedToConnect()).thenReturn(false); - when(node4.channelFailedToConnect()).thenReturn(false); - - AtomicInteger i = new AtomicInteger(); - var tx = new DummyQuery() { - @Override - Status mapResponseStatus(com.hedera.hashgraph.sdk.proto.Response response) { - return Status.RECEIPT_NOT_FOUND; - } - - @Override - ExecutionState getExecutionState(Status status, Response response) { - return i.getAndIncrement() == 0 ? ExecutionState.RETRY : ExecutionState.SUCCESS; - } - }; - var nodeAccountIds = Arrays.asList( - new AccountId(3), - new AccountId(4), - new AccountId(5) - ); - tx.setNodeAccountIds(nodeAccountIds); - - var receipt = com.hedera.hashgraph.sdk.proto.TransactionReceipt.newBuilder() - .setStatus(ResponseCodeEnum.OK) - .build(); - var receiptResp = com.hedera.hashgraph.sdk.proto.TransactionGetReceiptResponse.newBuilder() - .setReceipt(receipt) - .build(); - - var resp = Response.newBuilder().setTransactionGetReceipt(receiptResp).build(); - tx.blockingUnaryCall = (grpcRequest) -> resp; - tx.execute(client); - - verify(node3).channelFailedToConnect(any(Instant.class)); - verify(node4).channelFailedToConnect(any(Instant.class)); - } - - - @Test - void executeUserError() throws PrecheckStatusException, TimeoutException { - when(node3.isHealthy()).thenReturn(true); - when(node3.channelFailedToConnect()).thenReturn(false); - - var tx = new DummyTransaction() { - @Override - Status mapResponseStatus(com.hedera.hashgraph.sdk.proto.TransactionResponse response) { - return Status.ACCOUNT_DELETED; - } - }; - var nodeAccountIds = Arrays.asList( - new AccountId(3), - new AccountId(4), - new AccountId(5) - ); - tx.setNodeAccountIds(nodeAccountIds); - - var txResp = - com.hedera.hashgraph.sdk.proto.TransactionResponse - .newBuilder() - .setNodeTransactionPrecheckCode(ResponseCodeEnum.ACCOUNT_DELETED) - .build(); - - tx.blockingUnaryCall = (grpcRequest) -> txResp; - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> tx.execute(client)); - - verify(node3).channelFailedToConnect(any(Instant.class)); - } - - @Test - void shouldRetryReturnsCorrectStates() { - var tx = new DummyTransaction(); - - assertThat(tx.getExecutionState(Status.PLATFORM_TRANSACTION_NOT_CREATED, null)).isEqualTo( - ExecutionState.SERVER_ERROR); - assertThat(tx.getExecutionState(Status.PLATFORM_NOT_ACTIVE, null)).isEqualTo(ExecutionState.SERVER_ERROR); - assertThat(tx.getExecutionState(Status.BUSY, null)).isEqualTo(ExecutionState.RETRY); - assertThat(tx.getExecutionState(Status.OK, null)).isEqualTo(ExecutionState.SUCCESS); - assertThat(tx.getExecutionState(Status.ACCOUNT_DELETED, null)).isEqualTo(ExecutionState.REQUEST_ERROR); - } - - @Test - void shouldSetMaxRetry() { - var tx = new DummyTransaction(); - - tx.setMaxRetry(1); - - assertThat(tx.getMaxRetry()).isEqualTo(1); - - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> tx.setMaxRetry(0)); - } - - static class DummyTransaction> - extends - Executable { - - @Override - void onExecute(Client client) { - } - - @Nullable - @Override - CompletableFuture onExecuteAsync(Client client) { - return null; - } - - @Nullable - @Override - com.hedera.hashgraph.sdk.proto.Transaction makeRequest() { - return null; - } - - @Nullable - @Override - TransactionResponse mapResponse(com.hedera.hashgraph.sdk.proto.TransactionResponse response, AccountId nodeId, - com.hedera.hashgraph.sdk.proto.Transaction request) { - return null; - } - - @Override - Status mapResponseStatus(com.hedera.hashgraph.sdk.proto.TransactionResponse response) { - return Status.OK; - } - - @Nullable - @Override - MethodDescriptor getMethodDescriptor() { - return null; - } - - @Nullable - @Override - TransactionId getTransactionIdInternal() { - return null; - } - } - - static class DummyQuery extends Query { - @Override - void onExecute(Client client) { - } - - @Override - TransactionReceipt mapResponse(Response response, AccountId nodeId, - com.hedera.hashgraph.sdk.proto.Query request) { - return null; - } - - @Override - Status mapResponseStatus(com.hedera.hashgraph.sdk.proto.Response response) { - return Status.OK; - } - - @Override - MethodDescriptor getMethodDescriptor() { - return null; - } - - @Override - void onMakeRequest(com.hedera.hashgraph.sdk.proto.Query.Builder queryBuilder, QueryHeader header) { - } - - @Override - ResponseHeader mapResponseHeader(Response response) { - return null; - } - - @Override - QueryHeader mapRequestHeader(com.hedera.hashgraph.sdk.proto.Query request) { - return null; - } - - @Override - void validateChecksums(Client client) { - } - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/FeeAssessmentMethodTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/FeeAssessmentMethodTest.java deleted file mode 100644 index 746788c8ca..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/FeeAssessmentMethodTest.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.hedera.hashgraph.sdk; - -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -class FeeAssessmentMethodTest { - @Test - void feeAssessmentMethodToString() { - assertThat(FeeAssessmentMethod.valueOf(true)) - .hasToString(FeeAssessmentMethod.EXCLUSIVE.toString()); - assertThat(FeeAssessmentMethod.valueOf(false)) - .hasToString(FeeAssessmentMethod.INCLUSIVE.toString()); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/FeeSchedulesTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/FeeSchedulesTest.java deleted file mode 100644 index fcf9dec412..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/FeeSchedulesTest.java +++ /dev/null @@ -1,89 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; - -import static org.assertj.core.api.Assertions.assertThat; - -public class FeeSchedulesTest { - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - FeeSchedules spawnFeeSchedulesExample() { - return new FeeSchedules() - .setCurrent(new FeeSchedule() - .setExpirationTime(Instant.ofEpochSecond(1554158542)) - .addTransactionFeeSchedule(new TransactionFeeSchedule() - .addFee(new FeeData() - .setNodeData(new FeeComponents()) - .setNetworkData(new FeeComponents() - .setMin(2) - .setMax(5) - ) - .setServiceData(new FeeComponents()) - ) - ) - ) - .setNext(new FeeSchedule() - .setExpirationTime(Instant.ofEpochSecond(1554158222)) - .addTransactionFeeSchedule(new TransactionFeeSchedule() - .addFee(new FeeData() - .setNodeData(new FeeComponents() - .setMin(1) - .setMax(2) - ) - .setNetworkData(new FeeComponents()) - .setServiceData(new FeeComponents()) - ) - ) - ); - } - - @Test - void shouldSerialize() throws Exception { - var originalFeeSchedules = spawnFeeSchedulesExample(); - byte[] feeSchedulesBytes = originalFeeSchedules.toBytes(); - var copyFeeSchedules = FeeSchedules.fromBytes(feeSchedulesBytes); - assertThat(copyFeeSchedules.toString().replaceAll("@[A-Za-z0-9]+", "")) - .isEqualTo(originalFeeSchedules.toString().replaceAll("@[A-Za-z0-9]+", "")); - SnapshotMatcher.expect(originalFeeSchedules.toString().replaceAll("@[A-Za-z0-9]+", "")).toMatchSnapshot(); - } - - @Test - void shouldSerializeNull() throws Exception { - var originalFeeSchedules = new FeeSchedules(); - byte[] feeSchedulesBytes = originalFeeSchedules.toBytes(); - var copyFeeSchedules = FeeSchedules.fromBytes(feeSchedulesBytes); - assertThat(copyFeeSchedules.toString()).isEqualTo(originalFeeSchedules.toString()); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileAppendTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/FileAppendTransactionTest.java deleted file mode 100644 index cec89ef1ef..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileAppendTransactionTest.java +++ /dev/null @@ -1,242 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.FileAppendTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.bouncycastle.util.encoders.Hex; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -public class FileAppendTransactionTest { - public static final String BIG_CONTENTS = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur aliquam augue sem, ut mattis dui laoreet a. Curabitur consequat est euismod, scelerisque metus et, tristique dui. Nulla commodo mauris ut faucibus ultricies. Quisque venenatis nisl nec augue tempus, at efficitur elit eleifend. Duis pharetra felis metus, sed dapibus urna vehicula id. Duis non venenatis turpis, sit amet ornare orci. Donec non interdum quam. Sed finibus nunc et risus finibus, non sagittis lorem cursus. Proin pellentesque tempor aliquam. Sed congue nisl in enim bibendum, condimentum vehicula nisi feugiat.\n" + - "\n" + - "Suspendisse non sodales arcu. Suspendisse sodales, lorem ac mollis blandit, ipsum neque porttitor nulla, et sodales arcu ante fermentum tellus. Integer sagittis dolor sed augue fringilla accumsan. Cras vitae finibus arcu, sit amet varius dolor. Etiam id finibus dolor, vitae luctus velit. Proin efficitur augue nec pharetra accumsan. Aliquam lobortis nisl diam, vel fermentum purus finibus id. Etiam at finibus orci, et tincidunt turpis. Aliquam imperdiet congue lacus vel facilisis. Phasellus id magna vitae enim dapibus vestibulum vitae quis augue. Morbi eu consequat enim. Maecenas neque nulla, pulvinar sit amet consequat sed, tempor sed magna. Mauris lacinia sem feugiat faucibus aliquet. Etiam congue non turpis at commodo. Nulla facilisi.\n" + - "\n" + - "Nunc velit turpis, cursus ornare fringilla eu, lacinia posuere leo. Mauris rutrum ultricies dui et suscipit. Curabitur in euismod ligula. Curabitur vitae faucibus orci. Phasellus volutpat vestibulum diam sit amet vestibulum. In vel purus leo. Nulla condimentum lectus vestibulum lectus faucibus, id lobortis eros consequat. Proin mollis libero elit, vel aliquet nisi imperdiet et. Morbi ornare est velit, in vehicula nunc malesuada quis. Donec vehicula convallis interdum.\n" + - "\n" + - "Integer pellentesque in nibh vitae aliquet. Ut at justo id libero dignissim hendrerit. Interdum et malesuada fames ac ante ipsum primis in faucibus. Praesent quis ornare lectus. Nam malesuada non diam quis cursus. Phasellus a libero ligula. Suspendisse ligula elit, congue et nisi sit amet, cursus euismod dolor. Morbi aliquam, nulla a posuere pellentesque, diam massa ornare risus, nec eleifend neque eros et elit.\n" + - "\n" + - "Pellentesque a sodales dui. Sed in efficitur ante. Duis eget volutpat elit, et ornare est. Nam felis dolor, placerat mattis diam id, maximus lobortis quam. Sed pellentesque lobortis sem sed placerat. Pellentesque augue odio, molestie sed lectus sit amet, congue volutpat massa. Quisque congue consequat nunc id fringilla. Duis semper nulla eget enim venenatis dapibus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Pellentesque varius turpis nibh, sit amet malesuada mauris malesuada quis. Vivamus dictum egestas placerat. Vivamus id augue elit.\n" + - "\n" + - "Cras fermentum volutpat eros, vitae euismod lorem viverra nec. Donec lectus augue, porta eleifend odio sit amet, condimentum rhoncus urna. Nunc sed odio velit. Morbi non cursus odio, eget imperdiet erat. Nunc rhoncus massa non neque volutpat, sit amet faucibus ante congue. Phasellus nec lorem vel leo accumsan lobortis. Maecenas id ligula bibendum purus suscipit posuere ac eget diam. Nam in quam pretium, semper erat auctor, iaculis odio. Maecenas placerat, nisi ac elementum tempor, felis nulla porttitor orci, ac rhoncus diam justo in elit. Etiam lobortis fermentum ligula in tincidunt. Donec quis vestibulum nunc. Sed eros diam, interdum in porta lobortis, gravida eu magna. Donec diam purus, finibus et sollicitudin quis, fringilla nec nisi. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Curabitur ultricies sagittis dapibus. Etiam ullamcorper aliquet libero, eu venenatis mauris suscipit id.\n" + - "\n" + - "Ut interdum eleifend sem, vel bibendum ipsum volutpat eget. Nunc ac dignissim augue. Aliquam ornare aliquet magna id dignissim. Vestibulum velit sem, lacinia eu rutrum in, rhoncus vitae mauris. Pellentesque scelerisque pulvinar mauris non cursus. Integer id dolor porta, bibendum sem vel, pretium tortor. Fusce a nisi convallis, interdum quam condimentum, suscipit dolor. Sed magna diam, efficitur non nunc in, tincidunt varius mi. Aliquam ullamcorper nulla eu fermentum bibendum. Vivamus a felis pretium, hendrerit enim vitae, hendrerit leo. Suspendisse lacinia lectus a diam consectetur suscipit. Aenean hendrerit nisl sed diam venenatis pellentesque. Nullam egestas lectus a consequat pharetra. Vivamus ornare tellus auctor, facilisis lacus id, feugiat dui. Nam id est ut est rhoncus varius.\n" + - "\n" + - "Aenean vel vehicula erat. Aenean gravida risus vitae purus sodales, quis dictum enim porta. Ut elit elit, fermentum sed posuere non, accumsan eu justo. Integer porta malesuada quam, et elementum massa suscipit nec. Donec in elit diam. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis suscipit vel ante volutpat vestibulum.\n" + - "\n" + - "Pellentesque ex arcu, euismod et sapien ut, vulputate suscipit enim. Donec mattis sagittis augue, et mattis lacus. Cras placerat consequat lorem sed luctus. Nam suscipit aliquam sem ac imperdiet. Mauris a semper augue, pulvinar fringilla magna. Integer pretium massa non risus commodo hendrerit. Sed dictum libero id erat sodales mattis. Etiam auctor dolor lectus, ut sagittis enim lobortis vitae. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur nec orci lobortis, cursus risus eget, sollicitudin massa. Integer vel tincidunt mi, id eleifend quam. Nullam facilisis nisl eu mauris congue, vitae euismod nisi malesuada. Vivamus sit amet urna et libero sagittis dictum.\n" + - "\n" + - "In hac habitasse platea dictumst. Aliquam erat volutpat. Ut dictum, mi a viverra venenatis, mi urna pulvinar nisi, nec gravida lectus diam eget urna. Ut dictum sit amet nisl ut dignissim. Sed sed mauris scelerisque, efficitur augue in, vulputate turpis. Proin dolor justo, bibendum et sollicitudin feugiat, imperdiet sed mi. Sed elementum vitae massa vel lobortis. Cras vitae massa sit amet libero dictum tempus.\n" + - "\n" + - "Vivamus ut mauris lectus. Curabitur placerat ornare scelerisque. Cras malesuada nunc quis tortor pretium bibendum vel sed dui. Cras lobortis nibh eu erat blandit, sit amet consequat neque fermentum. Phasellus imperdiet molestie tristique. Cras auctor purus erat, id mollis ligula porttitor eget. Mauris porta pharetra odio et finibus. Suspendisse eu est a ligula bibendum cursus. Mauris ac laoreet libero. Nullam volutpat sem quis rhoncus gravida.\n" + - "\n" + - "Donec malesuada lacus ac iaculis cursus. Sed sem orci, feugiat ac est ut, ultricies posuere nisi. Suspendisse potenti. Phasellus ut ultricies purus. Etiam sem tortor, fermentum quis aliquam eget, consequat ut nulla. Aliquam dictum metus in mi fringilla, vel gravida nulla accumsan. Cras aliquam eget leo vel posuere. Vivamus vitae malesuada nunc. Morbi placerat magna mi, id suscipit lacus auctor quis. Nam at lorem vel odio finibus fringilla ut ac velit. Donec laoreet at nibh quis vehicula.\n" + - "\n" + - "Fusce ac tristique nisi. Donec leo nisi, consectetur at tellus sit amet, consectetur ultrices dui. Quisque gravida mollis tempor. Maecenas semper, sapien ut dignissim feugiat, massa enim viverra dolor, non varius eros nulla nec felis. Nunc massa lacus, ornare et feugiat id, bibendum quis purus. Praesent viverra elit sit amet purus consectetur venenatis. Maecenas nibh risus, elementum sit amet enim sagittis, ultrices malesuada lectus. Vivamus non felis ante. Ut vulputate ex arcu. Aliquam porta gravida porta. Aliquam eros leo, malesuada quis eros non, maximus tristique neque. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam ligula orci, mollis vel luctus nec, venenatis vitae est. Fusce rutrum convallis nisi.\n" + - "\n" + - "Nunc laoreet eget nibh in feugiat. Pellentesque nec arcu cursus, gravida dolor a, pellentesque nisi. Praesent vel justo blandit, placerat risus eget, consectetur orci. Sed maximus metus mi, ut euismod augue ultricies in. Nunc id risus hendrerit, aliquet lorem nec, congue justo. Vestibulum vel nunc ac est euismod mattis ac vitae nulla. Donec blandit luctus mauris, sit amet bibendum dui egestas et. Aenean nec lorem nec elit ornare rutrum nec eget ligula. Fusce a ipsum vitae neque elementum pharetra. Pellentesque ullamcorper ullamcorper libero, vitae porta sem sagittis vel. Interdum et malesuada fames ac ante ipsum primis in faucibus.\n" + - "\n" + - "Duis at massa sit amet risus pellentesque varius sit amet vitae eros. Cras tempor aliquet sapien, vehicula varius neque volutpat et. Donec purus nibh, pellentesque ut lobortis nec, ultricies ultricies nisl. Sed accumsan ut dui sit amet vulputate. Suspendisse eu facilisis massa, a hendrerit mauris. Nulla elementum molestie tincidunt. Donec mi justo, ornare vel tempor id, gravida et orci. Nam molestie erat nec nisi bibendum accumsan. Duis vitae tempor ante. Morbi congue mauris vel sagittis facilisis. Vivamus vehicula odio orci, a tempor nibh euismod in. Proin mattis, nibh at feugiat porta, purus velit posuere felis, quis volutpat sapien dui vel odio. Nam fermentum sem nec euismod aliquet. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Aliquam erat volutpat.\n" + - "\n" + - "Mauris congue lacus tortor. Pellentesque arcu eros, accumsan imperdiet porttitor vitae, mattis nec justo. Nullam ac aliquam mauris. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Suspendisse potenti. Fusce accumsan tempus felis a sagittis. Maecenas et velit odio. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Aliquam eros lacus, volutpat non urna sed, tincidunt ullamcorper elit. Sed sit amet gravida libero. In varius mi vel diam sollicitudin mollis.\n" + - "\n" + - "Aenean varius, diam vitae hendrerit feugiat, libero augue ultrices odio, eget consequat sem tellus eu nisi. Nam dapibus enim et auctor sollicitudin. Nunc iaculis eros orci, ac accumsan eros malesuada ut. Ut semper augue felis, nec sodales lorem consectetur non. Cras gravida eleifend est, et sagittis eros imperdiet congue. Fusce id tellus dapibus nunc scelerisque tempus. Donec tempor placerat libero, in commodo nisi bibendum eu. Donec id eros non est sollicitudin luctus. Duis bibendum bibendum tellus nec viverra. Aliquam non faucibus ex, nec luctus dui. Curabitur efficitur varius urna non dignissim. Suspendisse elit elit, ultrices in elementum eu, tempor at magna.\n" + - "\n" + - "Nunc in purus sit amet mi laoreet pulvinar placerat eget sapien. Donec vel felis at dui ultricies euismod quis vel neque. Donec tincidunt urna non eros pretium blandit. Nullam congue tincidunt condimentum. Curabitur et libero nibh. Proin ultricies risus id imperdiet scelerisque. Suspendisse purus mi, viverra vitae risus ut, tempus tincidunt enim. Ut luctus lobortis nisl, eget venenatis tortor cursus non. Mauris finibus nisl quis gravida ultricies. Fusce elementum lacus sit amet nunc congue, in porta nulla tincidunt.\n" + - "\n" + - "Mauris ante risus, imperdiet blandit posuere in, blandit eu ipsum. Integer et auctor arcu. Integer quis elementum purus. Nunc in ultricies nisl, sed rutrum risus. Suspendisse venenatis eros nec lorem rhoncus, in scelerisque velit condimentum. Etiam condimentum quam felis, in elementum odio mattis et. In ut nibh porttitor, hendrerit tellus vel, luctus magna. Vestibulum et ligula et dolor pellentesque porta. Aenean efficitur porta massa et bibendum. Nulla vehicula sem in risus volutpat, a eleifend elit maximus.\n" + - "\n" + - "Proin orci lorem, auctor a felis eu, pretium lobortis nulla. Phasellus aliquam efficitur interdum. Sed sit amet velit rutrum est dictum facilisis. Duis cursus enim at nisl tincidunt, eu molestie elit vehicula. Cras pellentesque nisl id enim feugiat fringilla. In quis tincidunt neque. Nam eu consectetur dolor. Ut id interdum mauris. Mauris nunc tortor, placerat in tempor ut, vestibulum eu nisl. Integer vel dapibus ipsum. Nunc id erat pulvinar, tincidunt magna id, condimentum massa. Pellentesque consequat est eget odio placerat vehicula. Etiam augue neque, sagittis non leo eu, tristique scelerisque dui. Ut dui urna, blandit quis urna ac, tincidunt tristique turpis.\n" + - "\n" + - "Suspendisse venenatis rhoncus ligula ultrices condimentum. In id laoreet eros. Suspendisse suscipit fringilla leo id euismod. Sed in quam libero. Ut at ligula tellus. Sed tristique gravida dui, at egestas odio aliquam iaculis. Praesent imperdiet velit quis nibh consequat, quis pretium sem sagittis. Donec tortor ex, congue sit amet pulvinar ac, rutrum non est. Praesent ipsum magna, venenatis sit amet vulputate id, eleifend ac ipsum.\n" + - "\n" + - "In consequat, nisi iaculis laoreet elementum, massa mauris varius nisi, et porta nisi velit at urna. Maecenas sit amet aliquet eros, a rhoncus nisl. Maecenas convallis enim nunc. Morbi purus nisl, aliquam ac tincidunt sed, mattis in augue. Quisque et elementum quam, vitae maximus orci. Suspendisse hendrerit risus nec vehicula placerat. Nulla et lectus nunc. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.\n" + - "\n" + - "Etiam ut sodales ex. Nulla luctus, magna eu scelerisque sagittis, nibh quam consectetur neque, non rutrum dolor metus nec ex. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed egestas augue elit, sollicitudin accumsan massa lobortis ac. Curabitur placerat, dolor a aliquam maximus, velit ipsum laoreet ligula, id ullamcorper lacus nibh eget nisl. Donec eget lacus venenatis enim consequat auctor vel in.\n"; - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - private static final PrivateKey secondPrivateKey = PrivateKey.fromString( - "302e020100300506032b65700422042099b8587e5abccf6999b0d42b88c581c45284290450487ce90095561c85af11e4" - ); - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction(Collections.singletonList(AccountId.fromString("0.0.5005"))) - .toString() - ).toMatchSnapshot(); - } - - private FileAppendTransaction spawnTestTransaction(List accountIds) { - return new FileAppendTransaction() - .setNodeAccountIds(accountIds) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setFileId(FileId.fromString("0.0.6006")) - .setContents(new byte[]{1, 2, 3, 4}) - .setMaxTransactionFee(Hbar.fromTinybars(100_000)) - .freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new FileAppendTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldSerializeBigContents() { - var nodeAccountIds = new ArrayList(); - nodeAccountIds.add(AccountId.fromString("0.0.444")); - nodeAccountIds.add(AccountId.fromString("0.0.555")); - - SnapshotMatcher.expect(spawnTestTransactionBigContents(nodeAccountIds) - .toString() - ).toMatchSnapshot(); - } - - private FileAppendTransaction spawnTestTransactionBigContents(ArrayList nodeAccountIds) { - return new FileAppendTransaction() - .setNodeAccountIds(nodeAccountIds) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setFileId(FileId.fromString("0.0.6006")) - .setContents(BIG_CONTENTS) - .setMaxTransactionFee(Hbar.fromTinybars(100_000)) - .freeze() - .sign(unusedPrivateKey); - } - - String hashesToString(List> hashes) { - var outString = new StringBuilder(); - outString.append("["); - for (var tx : hashes) { - outString.append("{"); - for (var entry : tx.entrySet()) { - outString.append(entry.getKey().toString()).append("=").append(Hex.toHexString(entry.getValue())).append(", "); - } - outString.append("}, "); - } - return outString + "]"; - } - - @Test - void shouldHash() { - var nodeAccountIds = new ArrayList(); - nodeAccountIds.add(AccountId.fromString("0.0.444")); - nodeAccountIds.add(AccountId.fromString("0.0.555")); - - SnapshotMatcher.expect( - hashesToString(spawnTestTransactionBigContents(nodeAccountIds) - .getAllTransactionHashesPerNode() - ) - ).toMatchSnapshot(); - } - - String signaturesToString(Map> signatures) { - var outString = new StringBuilder(); - outString.append("{"); - for (var nodeEntry : signatures.entrySet()) { - outString.append(nodeEntry.getKey()).append("={"); - for (var sigEntry : nodeEntry.getValue().entrySet()) { - outString.append(sigEntry.getKey()).append("=").append(Hex.toHexString(sigEntry.getValue())).append(", "); - } - outString.append("}, "); - } - return outString + "}"; - } - - String allSignaturesToString(List>> allSignatures) { - var outString = new StringBuilder(); - outString.append("["); - for (var txEntry : allSignatures) { - outString.append(signaturesToString(txEntry)).append(", "); - } - return outString + "]"; - } - - @Test - void shouldGetSignatures() { - var nodeAccountIds = new ArrayList(); - nodeAccountIds.add(AccountId.fromString("0.0.444")); - nodeAccountIds.add(AccountId.fromString("0.0.555")); - var signatures = spawnTestTransaction(nodeAccountIds) - .sign(secondPrivateKey) - .getSignatures(); - SnapshotMatcher.expect(signaturesToString(signatures)).toMatchSnapshot(); - } - - @Test - void shouldGetAllSignatures() { - var nodeAccountIds = new ArrayList(); - nodeAccountIds.add(AccountId.fromString("0.0.444")); - nodeAccountIds.add(AccountId.fromString("0.0.555")); - var signatures = spawnTestTransactionBigContents(nodeAccountIds) - .sign(secondPrivateKey) - .getAllSignatures(); - SnapshotMatcher.expect(allSignaturesToString(signatures)).toMatchSnapshot(); - } - - @Test - void shouldBytes() throws Exception { - var nodeAccountIds = new ArrayList(); - nodeAccountIds.add(AccountId.fromString("0.0.444")); - nodeAccountIds.add(AccountId.fromString("0.0.555")); - var tx = spawnTestTransactionBigContents(nodeAccountIds); - var tx2 = FileAppendTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - - assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> tx2.getTransactionHash()); - assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> tx2.getTransactionHashPerNode()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setFileAppend(FileAppendTransactionBody.newBuilder().build()) - .build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(FileAppendTransaction.class); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileAppendTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/FileAppendTransactionTest.snap deleted file mode 100644 index 6708bb14bd..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileAppendTransactionTest.snap +++ /dev/null @@ -1,23 +0,0 @@ -com.hedera.hashgraph.sdk.FileAppendTransactionTest.shouldGetAllSignatures=[ - "[{0.0.555={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=40d079bcb4a9fbdf102051328ee5f3903e166c1dd7c1e748efc95a2fdd640e421c0a8e23709b61e4d8110451adecd71cd16d1293ce0e57ea974de5b6b2ef070e, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=0cbdeaac11d392081289fb67a93a7fd995e86f55764e45b3e5eefd2f64056a38b50e8478e64cb1af5c310c321478e09b60d641a6e898820625d2674dc54ce80e, }, 0.0.444={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=1490ad50f0fb5af40ee1d823e4f4b2603c135b3c7979b1b0ee6a323ba494e0b5cca833157c1106b976c876b060249e7ee2db30a8566d8682a42fac2f5f4a3406, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=23a657e362da4b3ea29673c26d8b17b967c53528449d99b3154e1b81676dc8d0aa229b824f9e613803b3262bc9cecc6dc6e2fc01a21830fb1641b5ed03722e07, }, }, {0.0.555={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=4b4ed404ef9a37752c070c5eb364b0df0fbe06afb0b64e0743d27767c26b1045e8532308288e2f1a40cced0782b87f20b6cfdcd8b9890c600c0e2146a48ace00, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=95444670cecb07fc452a4e3f350eca481e5462e3349a6741cd4e6746b21c66e67825c169f3b4cf82ab1853f1cf0acedc84b2f565346dc173a93f054c19966b08, }, 0.0.444={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=ce2b975f698ca802721c80a3792fca39d73609afaa8961bb91da569463c50eecb07d7e26e7e8febb5163474139e94533e8eb7016e0c149e0823d7102ed2b0502, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=ea31e49f64fd41773561f81459a2381e0194788853e28ffb683caded7a675d386b2a228c67ec83bfc448f14ee4fd2eae41569f06006c515621e1f26514c9bf0a, }, }, {0.0.555={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=0aedd8f92e68278823397cb0eddc1c07f1fd80c223781280a0ffb0eef21b516444e4521294ad380e8a46c7f22f1723eef8872ccf4d8aa415345e0a7fe1485c06, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=8cf7f0ae116557d5f4a5bbee82dee3cfe950c0ecedbce5d2a7b729dd7614d47c436e6951c401870f85dadd696160ecee1ab8e65aa607ca942051e78b8f09a406, }, 0.0.444={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=47c3e240259603f3e7fa41ced3eb9931bc4350367aa6529994c048fb0aca1853630bf8fbbdf9b2768c52b91b5f5d669a4adab06ef0851107f6e8012d948ba90b, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=d1a4d3349a1bc7cd0242e09ed9b1811ea99fb6123826b946651ee8c9dd593eb40591697fad4360e3941c9eea55cd2173189df349aa16b9a11db62ed28de8ad06, }, }, {0.0.555={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=aa009ad11594bb981504f8d4063c9d90bfb0bccc4bda8f9e3ffd326fc39e599fae94ba98110342a449437f7c848e2a9dd6de0d008e8d00c8387ff094c93c2700, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=5a2b40589f128f3bac99649d0aec7230e3c993130f36814c742ae37104e93d22e2b548d97e471f583d1c81bbcf6f916cc03f20375499e3a37b2c6b13a5a94507, }, 0.0.444={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=63800201fd378e81946c73104907b0b0aa70ff5913889b21b4eac730e535f8acbf08f63da5824419fc14155d6fc19f23a53bfd3324d8baa68dc5d00e8974de07, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=084bdc8f8580a2ebcc0a5140f766aca111b5488092a41871d104834dda27d6b99288423835bc7114860ce6c740300ebe9f8ab576e7646097eee7d6692b90d109, }, }, {0.0.555={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=645d9d66e90a104462741e2f49fe77d2edce5d49a033c9c51768a2fba6deda96a71ad95e00416bb5a8a07cfaf543a1fee8730c11af4a380d6f30ec88d2271c0d, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=2bc0f7e72aa2e953741815ac95135e50ece67543683e6da3a26bc95534d9ca538d802d567fcb154bf98001663dc46d8674ff4e9361021fbe2a55e83f9442f802, }, 0.0.444={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=e83759154409ce22146e31ff826a3ee4940916784e5953c4c0502ef5c2f9e7ca2d114223c3461a491dfebdcc11e511e91400007a1dc3827b7b98c7225239460f, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=595653b3f5cedf84e845f0dc2c503aad94b259eee4c10ad32cd86976b246cd81726ae8edc35e41e9ebae9e27a3ed96aff524b9b04481fad2722fdab8a4ee5a0f, }, }, {0.0.555={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=dea30ba3201c32f5c23afac9d5b013c4e41a30d08205d21a09722f5aebb0760b2452ba254eea5008de68411560081e94ccd78c5008353aa6bb64779e79078702, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=903762ba6159fcf940281d810a268980cdf2fa35683626d52e41ad18202492fef5f8de2002c49cb56ac9d9e78177823f1f97741634237943459c31540f801502, }, 0.0.444={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=c463b37297427a620f739b3a420512f897745a8451315de360391b2f6879e644ba3a8597e4f6e3a1a135bf354bb0911fe421e0174f48b650f54e3fe86247e507, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=342e41dbd4e3ef300192767ffeb9b062b1fae68027153a84f151fdb1d3ce43592f275d87a7b90a9d6db6a67d32c6ec9e585ce199ed199cdcac0b4ef4ac12200f, }, }, {0.0.555={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=9e4dcb75574c75c7e54cc76ff08bf9e6c28cb2e74f79eb4ed8e4f968e6829bd994d9fadff9fb265988de757b858e4921c7b4db119c31f6156a79eb62ffc4110c, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=195fdc0b4c9730139ede43d8e80befe7bef91ab8fa2f271173bab926cff2c797117a28716112b6d1ab3b6c669a4b923f5885b40f67885b1a64d0445ad75bbe0e, }, 0.0.444={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=0e3e02a08fd3a807c79c1ce2117612c2902557aa7c57da2494744f45b105f3f67b9977564f83baf2cd7dd4fe2cf0421f9367343a92d90199e97f22bc977f3d0f, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=a9ef610d944c989424477a2e31b970033bffcce8899116820f9eac06f2d06226cfe714e6a835135e2fb632dda80b439199e76870156acd6f81d4c3cf342a3706, }, }, ]" -] - - -com.hedera.hashgraph.sdk.FileAppendTransactionTest.shouldGetSignatures=[ - "{0.0.555={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=a762395ad7e3fa298ed990785699133ca64d11a28fb4dfc83d6d95a9a2639b688d5afc288fcdb8168f4a85876b2b785e3303ddec38666bbae4f5436d6334540b, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=8bea3d52ad5badc4ebf5e20f874cf2e5dec3189d3a471c39fd1062743ba7729de1c484f1e8ac2f15343494799e97b5ac220734a261994d91ffed9e113c09d700, }, 0.0.444={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=4b8fd70342b6c201f08177de53122be909d5d72f97d29fba69e7ac6ef04d96b19ac417c9b84fbb122bce9c8323bc38bd6165b5750ce16ee4a2f23e28d7123006, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=371b502056cbf34c1bd7f9891e82c7a53ab8d92ce1728f790acc9a09989196fbf4c1c111ca010a19f1af82f2480be92229b5a1aadfe4af9142bd212254c88b08, }, }" -] - - -com.hedera.hashgraph.sdk.FileAppendTransactionTest.shouldHash=[ - "[{0.0.555=d2f10044785bb7ecbfc024a171b28ec877a123a2b6f3b2a48db5595208ea8bd036e4c4babb682d87e06ce62d7c2b1bb3, 0.0.444=38603d6b2a449030ea49b7cbd7e19cd66b4e3f2891ca40df861b4840418cb08575b7ce25426b1192545d352a89f0f3da, }, {0.0.555=74ed3e44b14e41ad5964a52adde1c32932b9dd75f589d0985504bbc483dc778493866ae879769d693e2a6fa80c6a118c, 0.0.444=0939a5cbfb9bbbd545d3e9f961e7fc3bcbf49553ce7e3a00414b5b0964ca9aae0ea878e280efa217503aed65d1057880, }, {0.0.555=9ef69da0531b3828cfc5b14cb5df373ae46922ad192df9d488684b845ce4754b2a20c1e966d2835ea178890fdaac9a00, 0.0.444=1332b1dfb679ea2a91b21de2bed0d086c5139893fce55b0471e2b08ec2e0fbbcee48783eda0a1e876ee0a7086416c26a, }, {0.0.555=e62ca0554d6e40461dbecc035165d35716d61eae59a305414a91a6fb85f36ba3adc1f6b963c429366485fe514407d294, 0.0.444=2deb2d9674c3e238c53080ad4d139f172dc907ba4b898210ed9359f3db92f0880503cca4fec35cd6b47c71ba5dd46aa3, }, {0.0.555=8a7721e7a8d8eede13e0ab5adb5c9520af6099c909592f45f9dc89775f69e16a2bea4a392480f1b750c817787a94099f, 0.0.444=dbb52ed2eea79c5cc58bb0b898ce03f4ee7d4ba6ad94c0d718c0c6b46fd8cea9e2ab76c42221fce44933827e382df57f, }, {0.0.555=0292097e1f3bdd4390134f10b51b2c9928fcfbe4fd54de25d407ee8b93c6ede6dda4c4263786d983de47413f541834e0, 0.0.444=16c12a7acaa5754a1e1adfc40550e5d4b09195c2347dc4fda3882d7a2823dd212ea1682642f211290b8985d45be55a0b, }, {0.0.555=e4c53e598718933cc67491218c478c23be4637e9d89107a3c1699f04143c421fd6bf5f2ff11d31ea646103cd7cd00b57, 0.0.444=5571dfcd8a279d4c33c0e9199c865556ab3aec478ddbc1560dd9c41c53423decb95348fb54b63cbc744fd1e496b2a01f, }, ]" -] - - -com.hedera.hashgraph.sdk.FileAppendTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nfile_append {\n contents: \"\\001\\002\\003\\004\"\n file_i_d {\n file_num: 6006\n realm_num: 0\n shard_num: 0\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] - - -com.hedera.hashgraph.sdk.FileAppendTransactionTest.shouldSerializeBigContents=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nfile_append {\n contents: \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur aliquam augue sem, ut mattis dui laoreet a. Curabitur consequat est euismod, scelerisque metus et, tristique dui. Nulla commodo mauris ut faucibus ultricies. Quisque venenatis nisl nec augue tempus, at efficitur elit eleifend. Duis pharetra felis metus, sed dapibus urna vehicula id. Duis non venenatis turpis, sit amet ornare orci. Donec non interdum quam. Sed finibus nunc et risus finibus, non sagittis lorem cursus. Proin pellentesque tempor aliquam. Sed congue nisl in enim bibendum, condimentum vehicula nisi feugiat.\\n\\nSuspendisse non sodales arcu. Suspendisse sodales, lorem ac mollis blandit, ipsum neque porttitor nulla, et sodales arcu ante fermentum tellus. Integer sagittis dolor sed augue fringilla accumsan. Cras vitae finibus arcu, sit amet varius dolor. Etiam id finibus dolor, vitae luctus velit. Proin efficitur augue nec pharetra accumsan. Aliquam lobortis nisl diam, vel fermentum purus finibus id. Etiam at finibus orci, et tincidunt turpis. Aliquam imperdiet congue lacus vel facilisis. Phasellus id magna vitae enim dapibus vestibulum vitae quis augue. Morbi eu consequat enim. Maecenas neque nulla, pulvinar sit amet consequat sed, tempor sed magna. Mauris lacinia sem feugiat faucibus aliquet. Etiam congue non turpis at commodo. Nulla facilisi.\\n\\nNunc velit turpis, cursus ornare fringilla eu, lacinia posuere leo. Mauris rutrum ultricies dui et suscipit. Curabitur in euismod ligula. Curabitur vitae faucibus orci. Phasellus volutpat vestibulum diam sit amet vestibulum. In vel purus leo. Nulla condimentum lectus vestibulum lectus faucibus, id lobortis eros consequat. Proin mollis libero elit, vel aliquet nisi imperdiet et. Morbi ornare est velit, in vehicula nunc malesuada quis. Donec vehicula convallis interdum.\\n\\nInteger pellentesque in nibh vitae aliquet. Ut at justo id libero dignissim hendrerit. Interdum et malesuada fames ac ante ipsum primis in faucibus. Praesent quis ornare lectus. Nam malesuada non diam quis cursus. Phasellus a libero ligula. Suspendisse ligula elit, congue et nisi sit amet, cursus euismod dolor. Morbi aliquam, nulla a posuere pellentesque, diam massa ornare risus, nec eleifend neque eros et elit.\\n\\nPellentesque a sodales dui. Sed in efficitur ante. Duis eget volutpat elit, et ornare est. Nam felis dolor, placerat mattis diam id, maximus lobortis quam. Sed pellentesque lobortis sem sed placerat. Pellentesque augue odio, molestie sed lectus sit amet, congue volutpat massa. Quisque congue consequat nunc id fringilla. Duis semper nulla eget enim venenatis dapibus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Pellentesque varius turpis nibh, sit amet malesuada mauris malesuada quis. Vivamus dictum egestas placerat. Vivamus id augue elit.\\n\\nCras fermentum volutpat eros, vitae euismod lorem viverra nec. Donec lectus augue, porta eleifend odio sit amet, condimentum rhoncus urna. Nunc sed odio velit. Morbi non cursus odio, eget imperdiet erat. Nunc rhoncus massa non neque volutpat, sit amet faucibus ante congue. Phasellus nec lorem vel leo accumsan lobortis. Maecenas id ligula bibendum purus suscipit posuere ac eget diam. Nam in quam pretium, semper erat auctor, iaculis odio. Maecenas placerat, nisi ac elementum tempor, felis nulla porttitor orci, ac rhoncus diam justo in elit. Etiam lobortis fermentum ligula in tincidunt. Donec quis vestibulum nunc. Sed eros diam, interdum in porta lobortis, gravida eu magna. Donec diam purus, finibus et sollicitudin quis, fringilla nec nisi. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Curabitur ultricies sagittis dapibus. Etiam ullamcorper aliquet libero, eu venenatis mauris suscipit id.\\n\\nUt interdum eleifend sem, vel bibendum ipsum volutpat eget. Nunc ac dignissim augue. Aliquam ornare aliquet magna id dignissim. Vestibulum velit sem, lacinia eu rutrum in, rhoncus vitae mauris. Pellentesque scelerisque pulvinar mauris non cursus. Integer id dolor porta, bibendum sem vel, pretium tortor. Fusce a nisi convallis, interdum quam condimentum, suscipit dolor. Sed magna diam, efficitur non nunc in, tincidunt varius mi. Aliquam ullamcorper nulla eu fermentum bibendum. Vivamus a felis pretium, hendrerit enim vitae, hendrerit leo. Suspendisse lacinia lectus a diam consectetur suscipit. Aenean hendrerit nisl sed diam venenatis pellentesque. Nullam egestas lectus a consequat pharetra. Vivamus ornare tellus auctor, facilisis lacus id, feugiat dui. Nam id est ut est rhoncus varius.\\n\\nAenean vel vehicula erat. Aenean gravida risus vitae purus sodales, quis dictum enim porta. Ut elit elit, fermentum sed posuere non, accumsan eu justo. Integer porta malesuada quam, et elementum massa suscipit nec. Donec in elit diam. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis suscipit vel ante volutpat vestibulum.\\n\\nPellentesque ex arcu, euismod et sapien ut, vulputate suscipit enim. Donec mattis sagittis augue, et mattis lacus. Cras placerat consequat lorem sed luctus. Nam suscipit aliquam sem ac imperdiet. Mauris a semper augue, pulvinar fringilla magna. Integer pretium massa non risus commodo hendrerit. Sed dictum libero id erat sodales mattis. Etiam auctor dolor lectus, ut sagittis enim lobortis vitae. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur nec orci lobortis, cursus risus eget, sollicitudin massa. Integer vel tincidunt mi, id eleifend quam. Nullam facilisis nisl eu mauris congue, vitae euismod nisi malesuada. Vivamus sit amet urna et libero sagittis dictum.\\n\\nIn hac habitasse platea dictumst. Aliquam erat volutpat. Ut dictum, mi a viverra venenatis, mi urna pulvinar nisi, nec gravida lectus diam eget urna. Ut dictum sit amet nisl ut dignissim. Sed sed mauris scelerisque, efficitur augue in, vulputate turpis. Proin dolor justo, bibendum et sollicitudin feugiat, imperdiet sed mi. Sed elementum vitae massa vel lobortis. Cras vitae massa sit amet libero dictum tempus.\\n\\nVivamus ut mauris lectus. Curabitur placerat ornare scelerisque. Cras malesuada nunc quis tortor pretium bibendum vel sed dui. Cras lobortis nibh eu erat blandit, sit amet consequat neque fermentum. Phasellus imperdiet molestie tristique. Cras auctor purus erat, id mollis ligula porttitor eget. Mauris porta pharetra odio et finibus. Suspendisse eu est a ligula bibendum cursus. Mauris ac laoreet libero. Nullam volutpat sem quis rhoncus gravida.\\n\\nDonec malesuada lacus ac iaculis cursus. Sed sem orci, feugiat ac est ut, ultricies posuere nisi. Suspendisse potenti. Phasellus ut ultricies purus. Etiam sem tortor, fermentum quis aliquam eget, consequat ut nulla. Aliquam dictum metus in mi fringilla, vel gravida nulla accumsan. Cras aliquam eget leo vel posuere. Vivamus vitae malesuada nunc. Morbi placerat magna mi, id suscipit lacus auctor quis. Nam at lorem vel odio finibus fringilla ut ac velit. Donec laoreet at nibh quis vehicula.\\n\\nFusce ac tristique nisi. Donec leo nisi, consectetur at tellus sit amet, consectetur ultrices dui. Quisque gravida mollis tempor. Maecenas semper, sapien ut dignissim feugiat, massa enim viverra dolor, non varius eros nulla nec felis. Nunc massa lacus, ornare et feugiat id, bibendum quis purus. Praesent viverra elit sit amet purus consectetur venenatis. Maecenas nibh risus, elementum sit amet enim sagittis, ultrices malesuada lectus. Vivamus non felis ante. Ut vulputate ex arcu. Aliquam porta gravida porta. Aliquam eros leo, malesuada quis eros non, maximus tristique neque. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam ligula orci, mollis vel luctus nec, venenatis vitae est. Fusce rutrum convallis nisi.\\n\\nNunc laoreet eget nibh in feugiat. Pellentesque nec arcu cursus, gravida dolor a, pellentesque nisi. Praesent vel justo blandit, placerat risus eget, consectetur orci. Sed maximus metus mi, ut euismod augue ultricies in. Nunc id risus hendrerit, aliquet lorem nec, congue justo. Vestibulum vel nunc ac est euismod mattis ac vitae nulla. Donec blandit luctus mauris, sit amet bibendum dui egestas et. Aenean nec lorem nec elit ornare rutrum nec eget ligula. Fusce a ipsum vitae neque elementum pharetra. Pellentesque ullamcorper ullamcorper libero, vitae porta sem sagittis vel. Interdum et malesuada fames ac ante ipsum primis in faucibus.\\n\\nDuis at massa sit amet risus pellentesque varius sit amet vitae eros. Cras tempor aliquet sapien, vehicula varius neque volutpat et. Donec purus nibh, pellentesque ut lobortis nec, ultricies ultricies nisl. Sed accumsan ut dui sit amet vulputate. Suspendisse eu facilisis massa, a hendrerit mauris. Nulla elementum molestie tincidunt. Donec mi justo, ornare vel tempor id, gravida et orci. Nam molestie erat nec nisi bibendum accumsan. Duis vitae tempor ante. Morbi congue mauris vel sagittis facilisis. Vivamus vehicula odio orci, a tempor nibh euismod in. Proin mattis, nibh at feugiat porta, purus velit posuere felis, quis volutpat sapien dui vel odio. Nam fermentum sem nec euismod aliquet. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Aliquam erat volutpat.\\n\\nMauris congue lacus tortor. Pellentesque arcu eros, accumsan imperdiet porttitor vitae, mattis nec justo. Nullam ac aliquam mauris. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Suspendisse potenti. Fusce accumsan tempus felis a sagittis. Maecenas et velit odio. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Aliquam eros lacus, volutpat non urna sed, tincidunt ullamcorper elit. Sed sit amet gravida libero. In varius mi vel diam sollicitudin mollis.\\n\\nAenean varius, diam vitae hendrerit feugiat, libero augue ultrices odio, eget consequat sem tellus eu nisi. Nam dapibus enim et auctor sollicitudin. Nunc iaculis eros orci, ac accumsan eros malesuada ut. Ut semper augue felis, nec sodales lorem consectetur non. Cras gravida eleifend est, et sagittis eros imperdiet congue. Fusce id tellus dapibus nunc scelerisque tempus. Donec tempor placerat libero, in commodo nisi bibendum eu. Donec id eros non est sollicitudin luctus. Duis bibendum bibendum tellus nec viverra. Aliquam non faucibus ex, nec luctus dui. Curabitur efficitur varius urna non dignissim. Suspendisse elit elit, ultrices in elementum eu, tempor at magna.\\n\\nNunc in purus sit amet mi laoreet pulvinar placerat eget sapien. Donec vel felis at dui ultricies euismod quis vel neque. Donec tincidunt urna non eros pretium blandit. Nullam congue tincidunt condimentum. Curabitur et libero nibh. Proin ultricies risus id imperdiet scelerisque. Suspendisse purus mi, viverra vitae risus ut, tempus tincidunt enim. Ut luctus lobortis nisl, eget venenatis tortor cursus non. Mauris finibus nisl quis gravida ultricies. Fusce elementum lacus sit amet nunc congue, in porta nulla tincidunt.\\n\\nMauris ante risus, imperdiet blandit posuere in, blandit eu ipsum. Integer et auctor arcu. Integer quis elementum purus. Nunc in ultricies nisl, sed rutrum risus. Suspendisse venenatis eros nec lorem rhoncus, in scelerisque velit condimentum. Etiam condimentum quam felis, in elementum odio mattis et. In ut nibh porttitor, hendrerit tellus vel, luctus magna. Vestibulum et ligula et dolor pellentesque porta. Aenean efficitur porta massa et bibendum. Nulla vehicula sem in risus volutpat, a eleifend elit maximus.\\n\\nProin orci lorem, auctor a felis eu, pretium lobortis nulla. Phasellus aliquam efficitur interdum. Sed sit amet velit rutrum est dictum facilisis. Duis cursus enim at nisl tincidunt, eu molestie elit vehicula. Cras pellentesque nisl id enim feugiat fringilla. In quis tincidunt neque. Nam eu consectetur dolor. Ut id interdum mauris. Mauris nunc tortor, placerat in tempor ut, vestibulum eu nisl. Integer vel dapibus ipsum. Nunc id erat pulvinar, tincidunt magna id, condimentum massa. Pellentesque consequat est eget odio placerat vehicula. Etiam augue neque, sagittis non leo eu, tristique scelerisque dui. Ut dui urna, blandit quis urna ac, tincidunt tristique turpis.\\n\\nSuspendisse venenatis rhoncus ligula ultrices condimentum. In id laoreet eros. Suspendisse suscipit fringilla leo id euismod. Sed in quam libero. Ut at ligula tellus. Sed tristique gravida dui, at egestas odio aliquam iaculis. Praesent imperdiet velit quis nibh consequat, quis pretium sem sagittis. Donec tortor ex, congue sit amet pulvinar ac, rutrum non est. Praesent ipsum magna, venenatis sit amet vulputate id, eleifend ac ipsum.\\n\\nIn consequat, nisi iaculis laoreet elementum, massa mauris varius nisi, et porta nisi velit at urna. Maecenas sit amet aliquet eros, a rhoncus nisl. Maecenas convallis enim nunc. Morbi purus nisl, aliquam ac tincidunt sed, mattis in augue. Quisque et elementum quam, vitae maximus orci. Suspendisse hendrerit risus nec vehicula placerat. Nulla et lectus nunc. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.\\n\\nEtiam ut sodales ex. Nulla luctus, magna eu scelerisque sagittis, nibh quam consectetur neque, non rutrum dolor metus nec ex. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed egestas augue elit, sollicitudin accumsan massa lobortis ac. Curabitur placerat, dolor a aliquam maximus, velit ipsum laoreet ligula, id ullamcorper lacus nibh eget nisl. Donec eget lacus venenatis enim consequat auctor vel in.\\n\"\n file_i_d {\n file_num: 6006\n realm_num: 0\n shard_num: 0\n }\n}\nnode_account_i_d {\n account_num: 444\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileContentsQueryTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/FileContentsQueryTest.java deleted file mode 100644 index 9911fb2449..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileContentsQueryTest.java +++ /dev/null @@ -1,47 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -public class FileContentsQueryTest { - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - var builder = com.hedera.hashgraph.sdk.proto.Query.newBuilder(); - new FileContentsQuery() - .setFileId(FileId.fromString("0.0.5005")) - .onMakeRequest(builder, QueryHeader.newBuilder().build()); - SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")).toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileContentsQueryTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/FileContentsQueryTest.snap deleted file mode 100644 index 6f3eb2db24..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileContentsQueryTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.FileContentsQueryTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.Query\nfile_get_contents {\n file_i_d {\n file_num: 5005\n realm_num: 0\n shard_num: 0\n }\n header {\n }\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileCreateTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/FileCreateTransactionTest.java deleted file mode 100644 index 45b4c4ea10..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileCreateTransactionTest.java +++ /dev/null @@ -1,94 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.FileCreateTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; - -public class FileCreateTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction() - .toString() - ).toMatchSnapshot(); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new FileCreateTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - private FileCreateTransaction spawnTestTransaction() { - return new FileCreateTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setContents(new byte[]{1, 2, 3, 4}) - .setExpirationTime(Instant.ofEpochSecond(1554158728)) - .setKeys(unusedPrivateKey) - .setMaxTransactionFee(Hbar.fromTinybars(100_000)) - .setFileMemo("Hello memo") - .freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = FileCreateTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setFileCreate(FileCreateTransactionBody.newBuilder().build()) - .build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(FileCreateTransaction.class); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileCreateTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/FileCreateTransactionTest.snap deleted file mode 100644 index f9fb981009..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileCreateTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.FileCreateTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nfile_create {\n contents: \"\\001\\002\\003\\004\"\n expiration_time {\n seconds: 1554158728\n }\n keys {\n keys {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n }\n memo: \"Hello memo\"\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileDeleteTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/FileDeleteTransactionTest.java deleted file mode 100644 index 437ecb270d..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileDeleteTransactionTest.java +++ /dev/null @@ -1,91 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.FileDeleteTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; - -public class FileDeleteTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction() - .toString() - ).toMatchSnapshot(); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new FileDeleteTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - private FileDeleteTransaction spawnTestTransaction() { - return new FileDeleteTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setFileId(FileId.fromString("0.0.6006")) - .setMaxTransactionFee(Hbar.fromTinybars(100_000)) - .freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = FileDeleteTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setFileDelete(FileDeleteTransactionBody.newBuilder().build()) - .build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(FileDeleteTransaction.class); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileDeleteTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/FileDeleteTransactionTest.snap deleted file mode 100644 index ec04b923e1..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileDeleteTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.FileDeleteTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nfile_delete {\n file_i_d {\n file_num: 6006\n realm_num: 0\n shard_num: 0\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileIdTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/FileIdTest.java deleted file mode 100644 index cbcf26ab67..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileIdTest.java +++ /dev/null @@ -1,65 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.bouncycastle.util.encoders.Hex; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -class FileIdTest { - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerializeFromString() { - SnapshotMatcher.expect(FileId.fromString("0.0.5005").toString()).toMatchSnapshot(); - } - - @Test - void toBytes() throws InvalidProtocolBufferException { - SnapshotMatcher.expect(Hex.toHexString(new FileId(5005).toBytes())).toMatchSnapshot(); - } - - - @Test - void fromBytes() throws InvalidProtocolBufferException { - SnapshotMatcher.expect(FileId.fromBytes(new FileId(5005).toBytes()).toString()).toMatchSnapshot(); - } - - @Test - void fromSolidityAddress() { - SnapshotMatcher.expect(FileId.fromSolidityAddress("000000000000000000000000000000000000138D").toString()).toMatchSnapshot(); - } - - @Test - void toSolidityAddress() { - SnapshotMatcher.expect(new FileId(5005).toSolidityAddress()).toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileIdTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/FileIdTest.snap deleted file mode 100644 index d890f711e3..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileIdTest.snap +++ /dev/null @@ -1,23 +0,0 @@ -com.hedera.hashgraph.sdk.FileIdTest.fromBytes=[ - "0.0.5005" -] - - -com.hedera.hashgraph.sdk.FileIdTest.fromSolidityAddress=[ - "0.0.5005" -] - - -com.hedera.hashgraph.sdk.FileIdTest.shouldSerializeFromString=[ - "0.0.5005" -] - - -com.hedera.hashgraph.sdk.FileIdTest.toBytes=[ - "188d27" -] - - -com.hedera.hashgraph.sdk.FileIdTest.toSolidityAddress=[ - "000000000000000000000000000000000000138d" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileInfoQueryTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/FileInfoQueryTest.java deleted file mode 100644 index 2bb229aac8..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileInfoQueryTest.java +++ /dev/null @@ -1,47 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -public class FileInfoQueryTest { - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - var builder = com.hedera.hashgraph.sdk.proto.Query.newBuilder(); - new FileInfoQuery() - .setFileId(FileId.fromString("0.0.5005")) - .onMakeRequest(builder, QueryHeader.newBuilder().build()); - SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")).toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileInfoQueryTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/FileInfoQueryTest.snap deleted file mode 100644 index 43916ce930..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileInfoQueryTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.FileInfoQueryTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.Query\nfile_get_info {\n file_i_d {\n file_num: 5005\n realm_num: 0\n shard_num: 0\n }\n header {\n }\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileInfoTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/FileInfoTest.java deleted file mode 100644 index 3fbbb64b40..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileInfoTest.java +++ /dev/null @@ -1,81 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.FileGetInfoResponse; -import com.hedera.hashgraph.sdk.proto.KeyList; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.bouncycastle.util.encoders.Hex; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; - - -public class FileInfoTest { - private static final PrivateKey privateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - private static final FileGetInfoResponse.FileInfo info = FileGetInfoResponse.FileInfo.newBuilder() - .setFileID(new FileId(1).toProtobuf()) - .setSize(2) - .setExpirationTime(InstantConverter.toProtobuf(Instant.ofEpochMilli(3))) - .setDeleted(true) - .setKeys(KeyList.newBuilder() - .addKeys(privateKey.getPublicKey().toProtobufKey())) - .setLedgerId(LedgerId.MAINNET.toByteString()) - .build(); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void fromProtobuf() throws InvalidProtocolBufferException { - SnapshotMatcher.expect(FileInfo.fromProtobuf(info).toString()) - .toMatchSnapshot(); - } - - @Test - void toProtobuf() throws InvalidProtocolBufferException { - SnapshotMatcher.expect(FileInfo.fromProtobuf(info).toProtobuf().toString()) - .toMatchSnapshot(); - } - - @Test - void fromBytes() throws InvalidProtocolBufferException { - SnapshotMatcher.expect(FileInfo.fromBytes(info.toByteArray()).toString()) - .toMatchSnapshot(); - } - - @Test - void toBytes() { - SnapshotMatcher.expect(Hex.toHexString(FileInfo.fromProtobuf(info).toBytes())) - .toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileInfoTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/FileInfoTest.snap deleted file mode 100644 index f82cadf2ac..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileInfoTest.snap +++ /dev/null @@ -1,18 +0,0 @@ -com.hedera.hashgraph.sdk.FileInfoTest.fromBytes=[ - "FileInfo{fileId=0.0.1, size=2, expirationTime=1970-01-01T00:00:00.003Z, isDeleted=true, keys=KeyList{threshold=null, keys=[302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7]}, fileMemo=, ledgerId=mainnet}" -] - - -com.hedera.hashgraph.sdk.FileInfoTest.fromProtobuf=[ - "FileInfo{fileId=0.0.1, size=2, expirationTime=1970-01-01T00:00:00.003Z, isDeleted=true, keys=KeyList{threshold=null, keys=[302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7]}, fileMemo=, ledgerId=mainnet}" -] - - -com.hedera.hashgraph.sdk.FileInfoTest.toBytes=[ - "0a02180110021a0510c08db70120012a240a221220e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b73a0100" -] - - -com.hedera.hashgraph.sdk.FileInfoTest.toProtobuf=[ - "# com.hedera.hashgraph.sdk.proto.FileGetInfoResponse$FileInfo@e51665b9\ndeleted: true\nexpiration_time {\n nanos: 3000000\n seconds: 0\n}\nfile_i_d {\n file_num: 1\n realm_num: 0\n shard_num: 0\n}\nkeys {\n keys {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n}\nledger_id: \"\\000\"\nsize: 2" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileUpdateTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/FileUpdateTransactionTest.java deleted file mode 100644 index 31a32ae718..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileUpdateTransactionTest.java +++ /dev/null @@ -1,97 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.FileUpdateTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; - -public class FileUpdateTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction() - .toString() - ).toMatchSnapshot(); - } - - private FileUpdateTransaction spawnTestTransaction() { - return new FileUpdateTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setFileId(FileId.fromString("0.0.6006")) - .setExpirationTime(Instant.ofEpochSecond(1554158728)) - .setContents(new byte[]{1, 2, 3, 4, 5}) - .setMaxTransactionFee(Hbar.fromTinybars(100_000)) - .setKeys(unusedPrivateKey) - .setFileMemo("Hello memo") - .freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = FileUpdateTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new FileUpdateTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setFileUpdate(FileUpdateTransactionBody.newBuilder().build()) - .build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(FileUpdateTransaction.class); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileUpdateTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/FileUpdateTransactionTest.snap deleted file mode 100644 index 572ce64a7a..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/FileUpdateTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.FileUpdateTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nfile_update {\n contents: \"\\001\\002\\003\\004\\005\"\n expiration_time {\n seconds: 1554158728\n }\n file_i_d {\n file_num: 6006\n realm_num: 0\n shard_num: 0\n }\n keys {\n keys {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n }\n memo {\n value: \"Hello memo\"\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/FreezeTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/FreezeTransactionTest.java deleted file mode 100644 index ec14100840..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/FreezeTransactionTest.java +++ /dev/null @@ -1,165 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import com.hedera.hashgraph.sdk.proto.FreezeTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.Timestamp; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.bouncycastle.util.encoders.Hex; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class FreezeTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - private static final FileId testFileId = FileId.fromString("4.5.6"); - private static final byte[] testFileHash = Hex.decode("1723904587120938954702349857"); - private static final FreezeType testFreezeType = FreezeType.TELEMETRY_UPGRADE; - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); - } - - private FreezeTransaction spawnTestTransaction() { - return new FreezeTransaction().setNodeAccountIds( - Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setFileId(testFileId).setFileHash(testFileHash).setStartTime(validStart).setFreezeType(testFreezeType) - .setMaxTransactionFee(Hbar.fromTinybars(100_000)).freeze().sign(unusedPrivateKey); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = FreezeTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new FreezeTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setFreeze(FreezeTransactionBody.newBuilder().build()).build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(FreezeTransaction.class); - } - - @Test - void constructFreezeTransactionFromTransactionBodyProtobuf() { - var transactionBody = FreezeTransactionBody.newBuilder().setUpdateFile(testFileId.toProtobuf()) - .setFileHash(ByteString.copyFrom(testFileHash)) - .setStartTime(Timestamp.newBuilder().setSeconds(validStart.getEpochSecond())) - .setFreezeType(testFreezeType.code); - - var tx = TransactionBody.newBuilder().setFreeze(transactionBody).build(); - var freezeTransaction = new FreezeTransaction(tx); - - assertNotNull(freezeTransaction.getFileId()); - assertThat(freezeTransaction.getFileId()).isEqualTo(testFileId); - assertThat(freezeTransaction.getFileHash()).isEqualTo(testFileHash); - assertNotNull(freezeTransaction.getStartTime()); - assertThat(freezeTransaction.getStartTime().getEpochSecond()).isEqualTo(validStart.getEpochSecond()); - assertThat(freezeTransaction.getFreezeType()).isEqualTo(testFreezeType); - } - - @Test - void getSetFileId() { - var freezeTransaction = new FreezeTransaction().setFileId(testFileId); - assertNotNull(freezeTransaction.getFileId()); - assertThat(freezeTransaction.getFileId()).isEqualTo(testFileId); - } - - @Test - void getSetFileIdFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setFileId(testFileId)); - } - - @Test - void getSetFileHash() { - var freezeTransaction = new FreezeTransaction().setFileHash(testFileHash); - assertNotNull(freezeTransaction.getFileHash()); - assertThat(freezeTransaction.getFileHash()).isEqualTo(testFileHash); - } - - @Test - void getSetFileHashFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setFileHash(testFileHash)); - } - - @Test - void getSetStartTime() { - var freezeTransaction = new FreezeTransaction().setStartTime(validStart); - assertNotNull(freezeTransaction.getStartTime()); - assertThat(freezeTransaction.getStartTime().getEpochSecond()).isEqualTo(validStart.getEpochSecond()); - } - - @Test - void getSetStartTimeFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setStartTime(validStart)); - } - - @Test - void getSetFreezeType() { - var freezeTransaction = new FreezeTransaction().setFreezeType(testFreezeType); - assertThat(freezeTransaction.getFreezeType()).isEqualTo(testFreezeType); - } - - @Test - void getSetFreezeTypeFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setFreezeType(testFreezeType)); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/FreezeTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/FreezeTransactionTest.snap deleted file mode 100644 index ba3efc007f..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/FreezeTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.FreezeTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nfreeze {\n file_hash: \"\\027#\\220E\\207\\022\\t8\\225G\\0024\\230W\"\n freeze_type: TELEMETRY_UPGRADE\n freeze_type_value: 5\n start_time {\n seconds: 1554158542\n }\n update_file {\n file_num: 6\n realm_num: 5\n shard_num: 4\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/HederaTrustManagerTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/HederaTrustManagerTest.java deleted file mode 100644 index af41ae3bef..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/HederaTrustManagerTest.java +++ /dev/null @@ -1,114 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import org.junit.jupiter.api.Test; - -import java.io.ByteArrayInputStream; -import java.nio.charset.StandardCharsets; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; -import java.util.Map; -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -public class HederaTrustManagerTest { - public static final String PREVIEWNET_CERT_NODE_3_STRING = "-----BEGIN CERTIFICATE-----\n" + - "MIICnzCCAiWgAwIBAgIUenyqJ4UaFBbwokatcUqAwW3o3rswCgYIKoZIzj0EAwMw\n" + - "gYQxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJUWDETMBEGA1UEBwwKUmljaGFyZHNv\n" + - "bjEPMA0GA1UECgwGSGVkZXJhMQ8wDQYDVQQLDAZIZWRlcmExEDAOBgNVBAMMBzAw\n" + - "MDAwMDAxHzAdBgkqhkiG9w0BCQEWEGFkbWluQGhlZGVyYS5jb20wIBcNMjEwODIz\n" + - "MjIyMTU4WhgPMjI5NTA2MDcyMjIxNThaMIGEMQswCQYDVQQGEwJVUzELMAkGA1UE\n" + - "CAwCVFgxEzARBgNVBAcMClJpY2hhcmRzb24xDzANBgNVBAoMBkhlZGVyYTEPMA0G\n" + - "A1UECwwGSGVkZXJhMRAwDgYDVQQDDAcwMDAwMDAwMR8wHQYJKoZIhvcNAQkBFhBh\n" + - "ZG1pbkBoZWRlcmEuY29tMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEm5b1+oG9R0qt\n" + - "zM7UZnS5l/xxUNHIHq5+NAvtlviCpJL19jrW9+/UOy00Qqc6vS6tS1hS+dNJmpiZ\n" + - "FN0EHew4VDR7ACnL4LDJKmIHWjQ0iwvZo5kCpO0r9BtPN5FvaSxyo1QwUjAPBgNV\n" + - "HREECDAGhwR/AAABMAsGA1UdDwQEAwIEsDATBgNVHSUEDDAKBggrBgEFBQcDATAd\n" + - "BgNVHQ4EFgQUeciBviJtjeuue0GPf1xllNw7qvYwCgYIKoZIzj0EAwMDaAAwZQIw\n" + - "JeG0H2HdsI1VhOYmJmYlNeKCNgAk+LMorzPmsIInVBO2HK2IrKfpReWDS/m5j51V\n" + - "AjEAxKBxDezJDqAZHTkTXCg+X9Q9V6J6M5yDy5IS90aCWEo+W8C1Hc6hkn2/NrvT\n" + - "PhwK\n" + - "-----END CERTIFICATE-----\n"; - - public static final ByteArrayInputStream PREVIEWNET_CERT_NODE_3_BYTES = new ByteArrayInputStream(PREVIEWNET_CERT_NODE_3_STRING.getBytes(StandardCharsets.UTF_8)); - - public static final CertificateFactory CERTIFICATE_FACTORY; - - static { - try { - CERTIFICATE_FACTORY = CertificateFactory.getInstance("X.509"); - } catch (CertificateException e) { - throw new RuntimeException(e); - } - } - - public static final X509Certificate PREVIEWNET_CERT_NODE_3; - - static { - try { - PREVIEWNET_CERT_NODE_3 = (X509Certificate) CERTIFICATE_FACTORY.generateCertificate(PREVIEWNET_CERT_NODE_3_BYTES); - } catch (CertificateException e) { - throw new RuntimeException(e); - } - } - - static final X509Certificate[] CERTIFICATE_CHAIN = new X509Certificate[]{PREVIEWNET_CERT_NODE_3}; - - @Test - void skipsCheckIfVerificationIsDisabled() throws CertificateException { - new HederaTrustManager(ByteString.EMPTY, false).checkServerTrusted(CERTIFICATE_CHAIN, ""); - } - - @Test - void skipsCheckIfCertificateIsNotProvided() throws CertificateException { - new HederaTrustManager(null, false).checkServerTrusted(CERTIFICATE_CHAIN, ""); - } - - @Test - void throwsErrorIfCertificateIsNotProvidedButVerificationIsRequired() { - assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> new HederaTrustManager(null, true)); - } - - @Test - void properlyChecksCertificateAgainstCurrentNetworkAddressBook() throws InterruptedException, CertificateException { - var client = Client.forNetwork(Map.of("0.previewnet.hedera.com:50211", new AccountId(3))) - .setTransportSecurity(true) - .setVerifyCertificates(true) - .setLedgerId(LedgerId.PREVIEWNET); - - var nodeAddress = Objects.requireNonNull(Objects.requireNonNull(client.network.addressBook).get(new AccountId(3))); - new HederaTrustManager(nodeAddress.getCertHash(), client.isVerifyCertificates()).checkServerTrusted(CERTIFICATE_CHAIN, ""); - } - - @Test - void certificateCheckFailWhenHashMismatches() throws InterruptedException, CertificateException { - var client = Client.forNetwork(Map.of("0.previewnet.hedera.com:50211", new AccountId(3))) - .setTransportSecurity(true) - .setVerifyCertificates(true) - .setLedgerId(LedgerId.PREVIEWNET); - - var nodeAddress = Objects.requireNonNull(Objects.requireNonNull(client.network.addressBook).get(new AccountId(4))); - assertThatExceptionOfType(CertificateException.class).isThrownBy(() -> new HederaTrustManager(nodeAddress.getCertHash(), client.isVerifyCertificates()).checkServerTrusted(CERTIFICATE_CHAIN, "")); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/KeystoreTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/KeystoreTest.java deleted file mode 100644 index d4922eb8f8..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/KeystoreTest.java +++ /dev/null @@ -1,74 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; - -import static org.assertj.core.api.Assertions.assertThat; - -class KeystoreTest { - private static final String TEST_KEY_STR = "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"; - private static final String PASSPHRASE = "asdf1234"; - - @Test - @DisplayName("Keystore.fromStream returns correct key") - void keystoreFromStream() throws IOException { - // keystore file generated by hedera-sdk-js from `testKeyStr` and `passphrase` - // NOT USED ANYWHERE - InputStream inputStream = KeystoreTest.class.getResourceAsStream("/test-keystore.bin"); - Keystore keystore = Keystore.fromStream(inputStream, PASSPHRASE); - - PrivateKey privateKey = keystore.getEd25519(); - assertThat(privateKey.toString()).isEqualTo(TEST_KEY_STR); - } - - @Test - @DisplayName("Keystore.fromStream returns correct key v2") - void keystoreFromStreamV2() throws IOException { - // keystore file generated by hedera-sdk-js from `testKeyStr` and `passphrase` - // NOT USED ANYWHERE - InputStream inputStream = KeystoreTest.class.getResourceAsStream("/test-keystore2.bin"); - Keystore keystore = Keystore.fromStream(inputStream, PASSPHRASE); - - PrivateKey privateKey = keystore.getEd25519(); - assertThat(privateKey.toString()).isEqualTo(TEST_KEY_STR); - } - - @Test - @DisplayName("Keystore.toStream produces decodable value") - void keystoreToStream() throws IOException { - PrivateKey privateKey = PrivateKey.fromString(TEST_KEY_STR); - Keystore keystore = new Keystore(privateKey); - - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - keystore.export(outputStream, PASSPHRASE); - - Keystore keystore2 = Keystore.fromStream(new ByteArrayInputStream(outputStream.toByteArray()), PASSPHRASE); - PrivateKey privateKey2 = keystore2.getEd25519(); - - assertThat(privateKey2.toString()).isEqualTo(TEST_KEY_STR); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/LiveHashAddTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/LiveHashAddTransactionTest.java deleted file mode 100644 index 180cb18790..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/LiveHashAddTransactionTest.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.hedera.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.time.Duration; -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; - -class LiveHashAddTransactionTest { - private static final PrivateKey privateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction() - .toString() - ).toMatchSnapshot(); - } - - private LiveHashAddTransaction spawnTestTransaction() { - return new LiveHashAddTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setAccountId(AccountId.fromString("0.0.100")) - .setHash(ByteString.copyFrom("hash", StandardCharsets.UTF_8)) - .setKeys(privateKey) - .setDuration(Duration.ofDays(30)) - .freeze() - .sign(privateKey); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new LiveHashAddTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = LiveHashAddTransaction.fromBytes(tx.toBytes()); - assertThat(tx2).hasToString(tx.toString()); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/LiveHashAddTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/LiveHashAddTransactionTest.snap deleted file mode 100644 index c383adc489..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/LiveHashAddTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.LiveHashAddTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\ncrypto_add_live_hash {\n live_hash {\n account_id {\n account_num: 100\n realm_num: 0\n shard_num: 0\n }\n duration {\n seconds: 2592000\n }\n hash: \"hash\"\n keys {\n keys {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n }\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 200000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/LiveHashDeleteTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/LiveHashDeleteTransactionTest.java deleted file mode 100644 index 1fc0a7a86c..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/LiveHashDeleteTransactionTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.hedera.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; - -class LiveHashDeleteTransactionTest { - private static final PrivateKey privateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction() - .toString() - ).toMatchSnapshot(); - } - - private LiveHashDeleteTransaction spawnTestTransaction() { - return new LiveHashDeleteTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setAccountId(AccountId.fromString("0.0.100")) - .setHash(ByteString.copyFrom("hash", StandardCharsets.UTF_8)) - .freeze() - .sign(privateKey); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = LiveHashDeleteTransaction.fromBytes(tx.toBytes()); - assertThat(tx2).hasToString(tx.toString()); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new LiveHashDeleteTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/LiveHashDeleteTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/LiveHashDeleteTransactionTest.snap deleted file mode 100644 index 91661995c8..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/LiveHashDeleteTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.LiveHashDeleteTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\ncrypto_delete_live_hash {\n account_of_live_hash {\n account_num: 100\n realm_num: 0\n shard_num: 0\n }\n live_hash_to_delete: \"hash\"\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 200000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/LiveHashQueryTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/LiveHashQueryTest.java deleted file mode 100644 index 5bc859aab2..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/LiveHashQueryTest.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.hedera.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -public class LiveHashQueryTest { - private static final byte[] hash = {0, 1, 2}; - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - var builder = com.hedera.hashgraph.sdk.proto.Query.newBuilder(); - new LiveHashQuery() - .setAccountId(AccountId.fromString("0.0.100")) - .setHash(hash) - .onMakeRequest(builder, QueryHeader.newBuilder().build()); - SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")).toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/LiveHashQueryTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/LiveHashQueryTest.snap deleted file mode 100644 index 65fa054bb3..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/LiveHashQueryTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.LiveHashQueryTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.Query\ncrypto_get_live_hash {\n account_i_d {\n account_num: 100\n realm_num: 0\n shard_num: 0\n }\n hash: \"\\000\\001\\002\"\n header {\n }\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/MaxQueryPaymentExceededExceptionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/MaxQueryPaymentExceededExceptionTest.java deleted file mode 100644 index 96200bcd63..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/MaxQueryPaymentExceededExceptionTest.java +++ /dev/null @@ -1,39 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -class MaxQueryPaymentExceededExceptionTest { - @Test - void shouldHaveMessage() { - var e = new MaxQueryPaymentExceededException( - new AccountBalanceQuery(), - new Hbar(30), - new Hbar(15) - ); - - assertThat(e.getMessage()).isEqualTo( - "cost for AccountBalanceQuery, of 30 ℏ, without explicit payment is greater than the maximum allowed payment of 15 ℏ" - ); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/MessageSubmitTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/MessageSubmitTransactionTest.java deleted file mode 100644 index 390bffa9dd..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/MessageSubmitTransactionTest.java +++ /dev/null @@ -1,92 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.ConsensusSubmitMessageTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; - -public class MessageSubmitTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = FreezeTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new TopicMessageSubmitTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction() - .toString() - ).toMatchSnapshot(); - } - - private TopicMessageSubmitTransaction spawnTestTransaction() { - return new TopicMessageSubmitTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setTopicId(TopicId.fromString("0.0.5007")) - .setMessage("hello") - .setMaxTransactionFee(Hbar.fromTinybars(100_000)) - .freeze() - .sign(unusedPrivateKey); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setConsensusSubmitMessage(ConsensusSubmitMessageTransactionBody.newBuilder().build()) - .build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(TopicMessageSubmitTransaction.class); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/MessageSubmitTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/MessageSubmitTransactionTest.snap deleted file mode 100644 index 379f204011..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/MessageSubmitTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.MessageSubmitTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nconsensus_submit_message {\n message: \"hello\"\n topic_i_d {\n realm_num: 0\n shard_num: 0\n topic_num: 5007\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/MnemonicTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/MnemonicTest.java deleted file mode 100644 index 9cac6a7c63..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/MnemonicTest.java +++ /dev/null @@ -1,771 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.utils.Bip32Utils; -import org.bouncycastle.util.encoders.Hex; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; - -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertThrowsExactly; - -public class MnemonicTest { - private static final String MNEMONIC_LEGACY_V1_STRING = "jolly kidnap tom lawn drunk chick optic lust mutter mole bride galley dense member sage neural widow decide curb aboard margin manure"; - private static final String MNEMONIC_LEGACY_V2_STRING = "obvious favorite remain caution remove laptop base vacant increase video erase pass sniff sausage knock grid argue salt romance way alone fever slush dune"; - private static final String MNEMONIC_24_WORD_STRING = "inmate flip alley wear offer often piece magnet surge toddler submit right radio absent pear floor belt raven price stove replace reduce plate home"; - private static final String MNEMONIC_12_WORD_STRING = "finish furnace tomorrow wine mass goose festival air palm easy region guilt"; - @Test - @DisplayName("Mnemonic.generate() creates a valid mnemonic") - void generateValidMnemonic() { - Mnemonic.generate24(); - Mnemonic.generate12(); - } - - @ParameterizedTest - @DisplayName("Mnemonic.validate() passes on known-good mnemonics") - @ValueSource(strings = { - "inmate flip alley wear offer often piece magnet surge toddler submit right radio absent pear floor belt raven price stove replace reduce plate home", - "tiny denial casual grass skull spare awkward indoor ethics dash enough flavor good daughter early hard rug staff capable swallow raise flavor empty angle", - "ramp april job flavor surround pyramid fish sea good know blame gate village viable include mixed term draft among monitor swear swing novel track", - "evoke rich bicycle fire promote climb zero squeeze little spoil slight damage", - }) - void knownGoodMnemonics(String mnemonicStr) throws Exception { - Mnemonic.fromString(mnemonicStr); - } - - @Test - @DisplayName("Mnemonic.validate() throws on short word list") - void shortWordList() { - assertThatExceptionOfType(BadMnemonicException.class).isThrownBy( - () -> Mnemonic.fromWords(Arrays.asList("lorem", "ipsum", "dolor")) - ).satisfies( - error -> { - assertThat(error.reason).isEqualTo(BadMnemonicReason.BadLength); - assertThat(error.unknownWordIndices).isNull(); - } - ); - } - - @Test - @DisplayName("Mnemonic.validate() throws on long word list") - void longWordList() { - assertThatExceptionOfType(BadMnemonicException.class).isThrownBy( - () -> Mnemonic.fromWords(Arrays.asList( - "lorem", - "ipsum", - "dolor", - "ramp", - "april", - "job", - "flavor", - "surround", - "pyramid", - "fish", - "sea", - "good", - "know", - "blame", - "gate", - "village", - "viable", - "include", - "mixed", - "term", - "draft", - "among", - "monitor", - "swear", - "swing", - "novel", - "track" - )) - ).satisfies( - error -> { - assertThat(error.reason).isEqualTo(BadMnemonicReason.BadLength); - assertThat(error.unknownWordIndices).isNull(); - } - ); - } - - @Test - @DisplayName("Mnemonic.validate() throws on 12-24 words") - void betweenWordList() { - assertThatExceptionOfType(BadMnemonicException.class).isThrownBy( - () -> Mnemonic.fromWords(Arrays.asList("" + - "lorem", - "ipsum", - "dolor", - "ramp", - "april", - "job", - "flavor", - "surround", - "pyramid", - "fish", - "sea", - "good", - "know", - "blame" - )) - ).satisfies( - error -> { - assertThat(error.reason).isEqualTo(BadMnemonicReason.BadLength); - assertThat(error.unknownWordIndices).isNull(); - } - ); - } - - @Test - @DisplayName("Mnemonic.validate() throws on unknown words") - void unknownWords() { - assertThatExceptionOfType(BadMnemonicException.class).isThrownBy( - () -> Mnemonic.fromWords(Arrays.asList( - "abandon", - "ability", - "able", - "about", - "above", - "absent", - "adsorb", // typo from "absorb" - "abstract", - "absurd", - "abuse", - "access", - "accident", - "acount", // typo from "account" - "accuse", - "achieve", - "acid", - "acoustic", - "acquired", // typo from "acquire" - "across", - "act", - "action", - "actor", - "actress", - "actual" - )) - ).satisfies( - error -> { - assertThat(error.reason).isEqualTo(BadMnemonicReason.UnknownWords); - assertThat(error.unknownWordIndices).containsExactly(6, 12, 17); - } - ); - } - - @Test - @DisplayName("Mnemonic.validate() throws on checksum mismatch, 24 words") - void checksumMismatch() { - // this mnemonic was just made up, the checksum should definitely not match - assertThatExceptionOfType(BadMnemonicException.class).isThrownBy( - () -> Mnemonic.fromWords(Arrays.asList( - "abandon", - "ability", - "able", - "about", - "above", - "absent", - "absorb", - "abstract", - "absurd", - "abuse", - "access", - "accident", - "account", - "accuse", - "achieve", - "acid", - "acoustic", - "acquire", - "across", - "act", - "action", - "actor", - "actress", - "actual" - )) - ).satisfies( - error -> { - assertThat(error.reason).isEqualTo(BadMnemonicReason.ChecksumMismatch); - assertThat(error.unknownWordIndices).isNull(); - } - ); - } - - @Test - @DisplayName("Mnemonic.validate() throws on checksum mismatch, 12 words") - void checksumMismatch12() { - // this mnemonic was just made up, the checksum should definitely not match - assertThatExceptionOfType(BadMnemonicException.class).isThrownBy( - () -> Mnemonic.fromWords(Arrays.asList( - "abandon", - "ability", - "able", - "about", - "above", - "absent", - "absorb", - "abstract", - "absurd", - "abuse", - "access", - "accident" - )) - ).satisfies( - error -> { - assertThat(error.reason).isEqualTo(BadMnemonicReason.ChecksumMismatch); - assertThat(error.unknownWordIndices).isNull(); - } - ); - } - - @Test - @DisplayName("Invalid Mnemonic can still be used to generate a private key") - void invalidToPrivateKey() { - assertThatExceptionOfType(BadMnemonicException.class).isThrownBy(() -> Mnemonic.fromWords(Arrays.asList( - "abandon", - "ability", - "able", - "about", - "above", - "absent", - "absorb", - "abstract", - "absurd", - "abuse", - "access", - "accident", - "account", - "accuse", - "achieve", - "acid", - "acoustic", - "acquire", - "across", - "act", - "action", - "actor", - "actress", - "actual" - ))).satisfies(error -> assertThat(error.mnemonic).isNotNull()); - } - - @Test - @DisplayName("Legacy V1 mnemonic test") - void legacyV1MnemonicTest() throws Exception { - // TODO: add link to reference test vectors - final String PRIVATE_KEY1 = "00c2f59212cb3417f0ee0d38e7bd876810d04f2dd2cb5c2d8f26ff406573f2bd"; - final String PUBLIC_KEY1 = "0c5bb4624df6b64c2f07a8cb8753945dd42d4b9a2ed4c0bf98e87ef154f473e9"; - - final String PRIVATE_KEY2 = "fae0002d2716ea3a60c9cd05ee3c4bb88723b196341b68a02d20975f9d049dc6"; - final String PUBLIC_KEY2 = "f40f9fdb1f161c31ed656794ada7af8025e8b5c70e538f38a4dfb46a0a6b0392"; - - final String PRIVATE_KEY3 = "882a565ad8cb45643892b5366c1ee1c1ef4a730c5ce821a219ff49b6bf173ddf"; - final String PUBLIC_KEY3 = "53c6b451e695d6abc52168a269316a0d20deee2331f612d4fb8b2b379e5c6854"; - - final String PRIVATE_KEY4 = "6890dc311754ce9d3fc36bdf83301aa1c8f2556e035a6d0d13c2cccdbbab1242"; - final String PUBLIC_KEY4 = "45f3a673984a0b4ee404a1f4404ed058475ecd177729daa042e437702f7791e9"; - - Mnemonic mnemonic = Mnemonic.fromString(MNEMONIC_LEGACY_V1_STRING); - - // Chain m - PrivateKey key1 = mnemonic.toLegacyPrivateKey(); - assertThat(key1.toStringRaw()).isEqualTo(PRIVATE_KEY1); - assertThat(key1.getPublicKey().toStringRaw()).isEqualTo(PUBLIC_KEY1); - - // Chain m/0 - PrivateKey key2 = key1.legacyDerive(0); - assertThat(key2.toStringRaw()).isEqualTo(PRIVATE_KEY2); - assertThat(key2.getPublicKey().toStringRaw()).isEqualTo(PUBLIC_KEY2); - - // Chain m/-1 - PrivateKey key3 = key1.legacyDerive(-1); - assertThat(key3.toStringRaw()).isEqualTo(PRIVATE_KEY3); - assertThat(key3.getPublicKey().toStringRaw()).isEqualTo(PUBLIC_KEY3); - - // Chain m/1099511627775 - PrivateKey key4 = key1.legacyDerive(1099511627775L); - assertThat(key4.toStringRaw()).isEqualTo(PRIVATE_KEY4); - assertThat(key4.getPublicKey().toStringRaw()).isEqualTo(PUBLIC_KEY4); - } - - @Test - @DisplayName("Legacy V2 mnemonic test") - void legacyV2MnemonicTest() throws Exception { - // TODO: add link to reference test vectors - final String PRIVATE_KEY1 = "98aa82d6125b5efa04bf8372be7931d05cd77f5ef3330b97d6ee7c006eaaf312"; - final String PUBLIC_KEY1 = "e0ce688d614f22f96d9d213ca513d58a7d03d954fe45790006e6e86b25456465"; - - final String PRIVATE_KEY2 = "2b7345f302a10c2a6d55bf8b7af40f125ec41d780957826006d30776f0c441fb"; - final String PUBLIC_KEY2 = "0e19f99800b007cc7c82f9d85b73e0f6e48799469450caf43f253b48c4d0d91a"; - - final String PRIVATE_KEY3 = "caffc03fdb9853e6a91a5b3c57a5c0031d164ce1c464dea88f3114786b5199e5"; - final String PUBLIC_KEY3 = "9fe11da3fcfba5d28a6645ecb611a9a43dbe6014b102279ba1d34506ea86974b"; - - Mnemonic mnemonic = Mnemonic.fromString(MNEMONIC_LEGACY_V2_STRING); - - // Chain m - PrivateKey key1 = mnemonic.toLegacyPrivateKey(); - assertThat(key1.toStringRaw()).isEqualTo(PRIVATE_KEY1); - assertThat(key1.getPublicKey().toStringRaw()).isEqualTo(PUBLIC_KEY1); - - // Chain m/0 - PrivateKey key2 = key1.legacyDerive(0); - assertThat(key2.toStringRaw()).isEqualTo(PRIVATE_KEY2); - assertThat(key2.getPublicKey().toStringRaw()).isEqualTo(PUBLIC_KEY2); - - // Chain m/-1 - PrivateKey key3 = key1.legacyDerive(-1); - assertThat(key3.toStringRaw()).isEqualTo(PRIVATE_KEY3); - assertThat(key3.getPublicKey().toStringRaw()).isEqualTo(PUBLIC_KEY3); - } - - @Test - @DisplayName("Mnemonic test") - void mnemonicTest() throws Exception { - Mnemonic mnemonic = Mnemonic.fromString(MNEMONIC_24_WORD_STRING); - PrivateKey key = mnemonic.toPrivateKey(); - assertThat(key).hasToString( - "302e020100300506032b657004220420853f15aecd22706b105da1d709b4ac05b4906170c2b9c7495dff9af49e1391da" - ); - } - - @Test - @DisplayName("Mnemonic passphrase test") - void mnemonicPassphraseTest() throws Exception { - // Test if mnemonic passphrase is BIP-39 compliant which requires unicode phrases to be NFKD normalized. - // Use unicode string as a passphrase. If it is properly normalized to NFKD, - // it should generate the expectedPrivateKey bellow: - String passphrase = "\u03B4\u03BF\u03BA\u03B9\u03BC\u03AE"; - String expectedPrivateKey = "302e020100300506032b6570042204203fefe1000db9485372851d542453b07e7970de4e2ecede7187d733ac037f4d2c"; - - Mnemonic mnemonic = Mnemonic.fromString(MNEMONIC_24_WORD_STRING); - PrivateKey key = mnemonic.toPrivateKey(passphrase); - assertThat(key.toString()).isEqualTo(expectedPrivateKey); - } - - @Test - @DisplayName("BIP39 test vector") - void bip39() throws Exception { - final String passphrase = "TREZOR"; - - // The 18-word mnemonics are not supported by the SDK - final String[] MNEMONIC_STRINGS = { - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - "legal winner thank year wave sausage worth useful legal winner thank yellow", - "letter advice cage absurd amount doctor acoustic avoid letter advice cage above", - "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong", -// "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent", -// "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will", -// "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always", -// "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when", - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art", - "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth title", - "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic bless", - "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote", - "ozone drill grab fiber curtain grace pudding thank cruise elder eight picnic", -// "gravity machine north sort system female filter attitude volume fold club stay feature office ecology stable narrow fog", - "hamster diagram private dutch cause delay private meat slide toddler razor book happy fancy gospel tennis maple dilemma loan word shrug inflict delay length", - "scheme spot photo card baby mountain device kick cradle pact join borrow", -// "horn tenant knee talent sponsor spell gate clip pulse soap slush warm silver nephew swap uncle crack brave", - "panda eyebrow bullet gorilla call smoke muffin taste mesh discover soft ostrich alcohol speed nation flash devote level hobby quick inner drive ghost inside", - "cat swing flag economy stadium alone churn speed unique patch report train", -// "light rule cinnamon wrap drastic word pride squirrel upgrade then income fatal apart sustain crack supply proud access", - "all hour make first leader extend hole alien behind guard gospel lava path output census museum junior mass reopen famous sing advance salt reform", - "vessel ladder alter error federal sibling chat ability sun glass valve picture", -// "scissors invite lock maple supreme raw rapid void congress muscle digital elegant little brisk hair mango congress clump", - "void come effort suffer camp survey warrior heavy shoot primary clutch crush open amazing screen patrol group space point ten exist slush involve unfold", - }; - - final String[] EXPECTED_SEEDS = { - "c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04", - "2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6fa457fe1296106559a3c80937a1c1069be3a3a5bd381ee6260e8d9739fce1f607", - "d71de856f81a8acc65e6fc851a38d4d7ec216fd0796d0a6827a3ad6ed5511a30fa280f12eb2e47ed2ac03b5c462a0358d18d69fe4f985ec81778c1b370b652a8", - "ac27495480225222079d7be181583751e86f571027b0497b5b5d11218e0a8a13332572917f0f8e5a589620c6f15b11c61dee327651a14c34e18231052e48c069", -// "035895f2f481b1b0f01fcf8c289c794660b289981a78f8106447707fdd9666ca06da5a9a565181599b79f53b844d8a71dd9f439c52a3d7b3e8a79c906ac845fa", -// "035895f2f481b1b0f01fcf8c289c794660b289981a78f8106447707fdd9666ca06da5a9a565181599b79f53b844d8a71dd9f439c52a3d7b3e8a79c906ac845fa", -// "107d7c02a5aa6f38c58083ff74f04c607c2d2c0ecc55501dadd72d025b751bc27fe913ffb796f841c49b1d33b610cf0e91d3aa239027f5e99fe4ce9e5088cd65", -// "0cd6e5d827bb62eb8fc1e262254223817fd068a74b5b449cc2f667c3f1f985a76379b43348d952e2265b4cd129090758b3e3c2c49103b5051aac2eaeb890a528", - "bda85446c68413707090a52022edd26a1c9462295029f2e60cd7c4f2bbd3097170af7a4d73245cafa9c3cca8d561a7c3de6f5d4a10be8ed2a5e608d68f92fcc8", - "bc09fca1804f7e69da93c2f2028eb238c227f2e9dda30cd63699232578480a4021b146ad717fbb7e451ce9eb835f43620bf5c514db0f8add49f5d121449d3e87", - "c0c519bd0e91a2ed54357d9d1ebef6f5af218a153624cf4f2da911a0ed8f7a09e2ef61af0aca007096df430022f7a2b6fb91661a9589097069720d015e4e982f", - "dd48c104698c30cfe2b6142103248622fb7bb0ff692eebb00089b32d22484e1613912f0a5b694407be899ffd31ed3992c456cdf60f5d4564b8ba3f05a69890ad", - "274ddc525802f7c828d8ef7ddbcdc5304e87ac3535913611fbbfa986d0c9e5476c91689f9c8a54fd55bd38606aa6a8595ad213d4c9c9f9aca3fb217069a41028", -// "628c3827a8823298ee685db84f55caa34b5cc195a778e52d45f59bcf75aba68e4d7590e101dc414bc1bbd5737666fbbef35d1f1903953b66624f910feef245ac", - "64c87cde7e12ecf6704ab95bb1408bef047c22db4cc7491c4271d170a1b213d20b385bc1588d9c7b38f1b39d415665b8a9030c9ec653d75e65f847d8fc1fc440", - "ea725895aaae8d4c1cf682c1bfd2d358d52ed9f0f0591131b559e2724bb234fca05aa9c02c57407e04ee9dc3b454aa63fbff483a8b11de949624b9f1831a9612", -// "fd579828af3da1d32544ce4db5c73d53fc8acc4ddb1e3b251a31179cdb71e853c56d2fcb11aed39898ce6c34b10b5382772db8796e52837b54468aeb312cfc3d", - "72be8e052fc4919d2adf28d5306b5474b0069df35b02303de8c1729c9538dbb6fc2d731d5f832193cd9fb6aeecbc469594a70e3dd50811b5067f3b88b28c3e8d", - "deb5f45449e615feff5640f2e49f933ff51895de3b4381832b3139941c57b59205a42480c52175b6efcffaa58a2503887c1e8b363a707256bdd2b587b46541f5", -// "4cbdff1ca2db800fd61cae72a57475fdc6bab03e441fd63f96dabd1f183ef5b782925f00105f318309a7e9c3ea6967c7801e46c8a58082674c860a37b93eda02", - "26e975ec644423f4a4c4f4215ef09b4bd7ef924e85d1d17c4cf3f136c2863cf6df0a475045652c57eb5fb41513ca2a2d67722b77e954b4b3fc11f7590449191d", - "2aaa9242daafcee6aa9d7269f17d4efe271e1b9a529178d7dc139cd18747090bf9d60295d0ce74309a78852a9caadf0af48aae1c6253839624076224374bc63f", -// "7b4a10be9d98e6cba265566db7f136718e1398c71cb581e1b2f464cac1ceedf4f3e274dc270003c670ad8d02c4558b2f8e39edea2775c9e232c7cb798b069e88", - "01f5bced59dec48e362f2c45b5de68b9fd6c92c6634f44d6d40aab69056506f0e35524a518034ddc1192e1dacd32c1ed3eaa3c3b131c88ed8e7e54c49a5d0998", - }; - - for (int i = 0; i < MNEMONIC_STRINGS.length; i++) { - byte[] seed = Mnemonic.fromString(MNEMONIC_STRINGS[i]).toSeed(passphrase); - assertThat(Hex.toHexString(seed)).isEqualTo(EXPECTED_SEEDS[i]); - } - } - - @Test - @DisplayName("Mnemonic.toStandardED25519PrivateKey() test vector") - void toStandardED25519PrivateKey() throws BadMnemonicException { - // https://github.com/hashgraph/hedera-sdk-reference/issues/73#issuecomment-1422330626 - final String CHAIN_CODE1 = "404914563637c92d688deb9d41f3f25cbe8d6659d859cc743712fcfac72d7eda"; - final String PRIVATE_KEY1 = "f8dcc99a1ced1cc59bc2fee161c26ca6d6af657da9aa654da724441343ecd16f"; - final String PUBLIC_KEY1 = "2e42c9f5a5cdbde64afa65ce3dbaf013d5f9ff8d177f6ef4eb89fbe8c084ec0d"; - - final String CHAIN_CODE2 = "9c2b0073ac934696cd0b52c6c521b9bd1902aac134380a737282fdfe29014bf1"; - final String PRIVATE_KEY2 = "e978a6407b74a0730f7aeb722ad64ab449b308e56006c8bff9aad070b9b66ddf"; - final String PUBLIC_KEY2 = "c4b33dca1f83509f17b69b2686ee46b8556143f79f4b9df7fe7ed3864c0c64d0"; - - final String CHAIN_CODE3 = "699344acc5e07c77eb63b154b4c5c3d33cab8bf85ee21bea4cc29ab7f0502259"; - final String PRIVATE_KEY3 = "abeca64d2337db386e289482a252334c68c7536daaefff55dc169ddb77fbae28"; - final String PUBLIC_KEY3 = "fd311925a7a04b38f7508931c6ae6a93e5dc4394d83dafda49b051c0017d3380"; - - final String CHAIN_CODE4 = "e5af7c95043a912af57a6e031ddcad191677c265d75c39954152a2733c750a3b"; - final String PRIVATE_KEY4 = "9a601db3e24b199912cec6573e6a3d01ffd3600d50524f998b8169c105165ae5"; - final String PUBLIC_KEY4 = "cf525500706faa7752dca65a086c9381d30d72cc67f23bf334f330579074a890"; - - Mnemonic mnemonic = Mnemonic.fromString(MNEMONIC_24_WORD_STRING); - - // Chain m/44'/3030'/0'/0'/0' - PrivateKey key1 = mnemonic.toStandardEd25519PrivateKey("", 0); - assertThat(Hex.toHexString(key1.getChainCode().getKey())).isEqualTo(CHAIN_CODE1); - assertThat(key1.toStringRaw()).isEqualTo(PRIVATE_KEY1); - assertThat(key1.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY1); - - // Chain m/44'/3030'/0'/0'/2147483647' - PrivateKey key2 = mnemonic.toStandardEd25519PrivateKey("", 2147483647); - assertThat(Hex.toHexString(key2.getChainCode().getKey())).isEqualTo(CHAIN_CODE2); - assertThat(key2.toStringRaw()).isEqualTo(PRIVATE_KEY2); - assertThat(key2.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY2); - - // Chain m/44'/3030'/0'/0'/0'; Passphrase: "some pass" - PrivateKey key3 = mnemonic.toStandardEd25519PrivateKey("some pass", 0); - assertThat(Hex.toHexString(key3.getChainCode().getKey())).isEqualTo(CHAIN_CODE3); - assertThat(key3.toStringRaw()).isEqualTo(PRIVATE_KEY3); - assertThat(key3.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY3); - - // Chain m/44'/3030'/0'/0'/2147483647'; Passphrase: "some pass" - PrivateKey key4 = mnemonic.toStandardEd25519PrivateKey("some pass", 2147483647); - assertThat(Hex.toHexString(key4.getChainCode().getKey())).isEqualTo(CHAIN_CODE4); - assertThat(key4.toStringRaw()).isEqualTo(PRIVATE_KEY4); - assertThat(key4.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY4); - } - - @Test - @DisplayName("Mnemonic.toStandardED25519PrivateKey() test vector 2") - void toStandardED25519PrivateKey2() throws BadMnemonicException { - // https://github.com/hashgraph/hedera-sdk-reference/issues/73#issuecomment-1424761224 - final String CHAIN_CODE1 = "48c89d67e9920e443f09d2b14525213ff83b245c8b98d63747ea0801e6d0ff3f"; - final String PRIVATE_KEY1 = "020487611f3167a68482b0f4aacdeb02cc30c52e53852af7b73779f67eeca3c5"; - final String PUBLIC_KEY1 = "2d047ff02a2091f860633f849ea2024b23e7803cfd628c9bdd635010cbd782d3"; - - final String CHAIN_CODE2 = "c0bcdbd9df6d8a4f214f20f3e5c7856415b68be34a1f406398c04690818bea16"; - final String PRIVATE_KEY2 = "d0c4484480944db698dd51936b7ecc81b0b87e8eafc3d5563c76339338f9611a"; - final String PUBLIC_KEY2 = "a1a2573c2c45bd57b0fd054865b5b3d8f492a6e1572bf04b44471e07e2f589b2"; - - final String CHAIN_CODE3 = "998a156855ab5398afcde06164b63c5523ff2c8900db53962cc2af191df59e1c"; - final String PRIVATE_KEY3 = "d06630d6e4c17942155819bbbe0db8306cd989ba7baf3c29985c8455fbefc37f"; - final String PUBLIC_KEY3 = "6bd0a51e0ca6fcc8b13cf25efd0b4814978bcaca7d1cf7dbedf538eb02969acb"; - - final String CHAIN_CODE4 = "19d99506a5ce2dc0080092068d278fe29b85ffb8d9c26f8956bfca876307c79c"; - final String PRIVATE_KEY4 = "a095ef77ee88da28f373246e9ae143f76e5839f680746c3f921e90bf76c81b08"; - final String PUBLIC_KEY4 = "35be6a2a37ff6bbb142e9f4d9b558308f4f75d7c51d5632c6a084257455e1461"; - - Mnemonic mnemonic = Mnemonic.fromString(MNEMONIC_12_WORD_STRING); - - // Chain m/44'/3030'/0'/0'/0' - PrivateKey key1 = mnemonic.toStandardEd25519PrivateKey("", 0); - assertThat(Hex.toHexString(key1.getChainCode().getKey())).isEqualTo(CHAIN_CODE1); - assertThat(key1.toStringRaw()).isEqualTo(PRIVATE_KEY1); - assertThat(key1.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY1); - - // Chain m/44'/3030'/0'/0'/2147483647' - PrivateKey key2 = mnemonic.toStandardEd25519PrivateKey("", 2147483647); - assertThat(Hex.toHexString(key2.getChainCode().getKey())).isEqualTo(CHAIN_CODE2); - assertThat(key2.toStringRaw()).isEqualTo(PRIVATE_KEY2); - assertThat(key2.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY2); - - // Chain m/44'/3030'/0'/0'/0'; Passphrase: "some pass" - PrivateKey key3 = mnemonic.toStandardEd25519PrivateKey("some pass", 0); - assertThat(Hex.toHexString(key3.getChainCode().getKey())).isEqualTo(CHAIN_CODE3); - assertThat(key3.toStringRaw()).isEqualTo(PRIVATE_KEY3); - assertThat(key3.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY3); - - // Chain m/44'/3030'/0'/0'/2147483647'; Passphrase: "some pass" - PrivateKey key4 = mnemonic.toStandardEd25519PrivateKey("some pass", 2147483647); - assertThat(Hex.toHexString(key4.getChainCode().getKey())).isEqualTo(CHAIN_CODE4); - assertThat(key4.toStringRaw()).isEqualTo(PRIVATE_KEY4); - assertThat(key4.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY4); - } - - @Test - @DisplayName("Mnemonic.toStandardED25519PrivateKey() should fail when index is pre-hardened") - void toStandardED25519PrivateKeyShouldFailWhenIndexIsPreHardened() throws BadMnemonicException { - Mnemonic mnemonic = Mnemonic.fromString(MNEMONIC_24_WORD_STRING); - int hardenedIndex = Bip32Utils.toHardenedIndex(10); - - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy( - () -> mnemonic.toStandardEd25519PrivateKey("", hardenedIndex) - ).satisfies(error -> assertThat(error.getMessage()).isEqualTo("the index should not be pre-hardened")); - - } - - @Test - @DisplayName("Mnemonic.toStandardECDSAsecp256k1PrivateKey() test vector") - void toStandardECDSAsecp256k1PrivateKey() throws BadMnemonicException { - // https://github.com/hashgraph/hedera-sdk-reference/issues/73#issuecomment-1422330626 - final String CHAIN_CODE1 = "7717bc71194c257d4b233e16cf48c24adef630052f874a262d19aeb2b527620d"; - final String PRIVATE_KEY1 = "0fde7bfd57ae6ec310bdd8b95967d98e8762a2c02da6f694b152cf9860860ab8"; - final String PUBLIC_KEY1 = "03b1c064b4d04d52e51f6c8e8bb1bff75d62fa7b1446412d5901d424f6aedd6fd4"; - - final String CHAIN_CODE2 = "e333da4bd9e21b5dbd2b0f6d88bad02f0fa24cf4b70b2fb613368d0364cdf8af"; - final String PRIVATE_KEY2 = "aab7d720a32c2d1ea6123f58b074c865bb07f6c621f14cb012f66c08e64996bb"; - final String PUBLIC_KEY2 = "03a0ea31bb3562f8a309b1436bc4b2f537301778e8a5e12b68cec26052f567a235"; - - final String CHAIN_CODE3 = "0ff552587f6baef1f0818136bacac0bb37236473f6ecb5a8c1cc68a716726ed1"; - final String PRIVATE_KEY3 = "6df5ed217cf6d5586fdf9c69d39c843eb9d152ca19d3e41f7bab483e62f6ac25"; - final String PUBLIC_KEY3 = "0357d69bb36fee569838fe7b325c07ca511e8c1b222873cde93fc6bb541eb7ecea"; - - final String CHAIN_CODE4 = "3a5048e93aad88f1c42907163ba4dce914d3aaf2eea87b4dd247ca7da7530f0b"; - final String PRIVATE_KEY4 = "80df01f79ee1b1f4e9ab80491c592c0ef912194ccca1e58346c3d35cb5b7c098"; - final String PUBLIC_KEY4 = "039ebe79f85573baa065af5883d0509a5634245f7864ddead76a008c9e42aa758d"; - - final String CHAIN_CODE5 = "e54254940db58ef4913a377062ac6e411daebf435ad592d262d5a66d808a8b94"; - final String PRIVATE_KEY5 = "60cb2496a623e1201d4e0e7ce5da3833cd4ec7d6c2c06bce2bcbcbc9dfef22d6"; - final String PUBLIC_KEY5 = "02b59f348a6b69bd97afa80115e2d5331749b3c89c61297255430c487d6677f404"; - - final String CHAIN_CODE6 = "cb23165e9d2d798c85effddc901a248a1a273fab2a56fe7976df97b016e7bb77"; - final String PRIVATE_KEY6 = "100477c333028c8849250035be2a0a166a347a5074a8a727bce1db1c65181a50"; - final String PUBLIC_KEY6 = "03d10ebfa2d8ff2cd34aa96e5ef59ca2e69316b4c0996e6d5f54b6932fe51be560"; - - Mnemonic mnemonic = Mnemonic.fromString(MNEMONIC_24_WORD_STRING); - - // Chain m/44'/3030'/0'/0/0 - PrivateKey key1 = mnemonic.toStandardECDSAsecp256k1PrivateKey("", 0); - assertThat(Hex.toHexString(key1.getChainCode().getKey())).isEqualTo(CHAIN_CODE1); - assertThat(key1.toStringRaw()).isEqualTo(PRIVATE_KEY1); - assertThat(key1.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY1); - - // Chain m/44'/3030'/0'/0/0' - PrivateKey key2 = mnemonic.toStandardECDSAsecp256k1PrivateKey("", Bip32Utils.toHardenedIndex(0)); - assertThat(Hex.toHexString(key2.getChainCode().getKey())).isEqualTo(CHAIN_CODE2); - assertThat(key2.toStringRaw()).isEqualTo(PRIVATE_KEY2); - assertThat(key2.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY2); - - // Chain m/44'/3030'/0'/0/0; Passphrase "some pass" - PrivateKey key3 = mnemonic.toStandardECDSAsecp256k1PrivateKey("some pass", 0); - assertThat(Hex.toHexString(key3.getChainCode().getKey())).isEqualTo(CHAIN_CODE3); - assertThat(key3.toStringRaw()).isEqualTo(PRIVATE_KEY3); - assertThat(key3.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY3); - - // Chain m/44'/3030'/0'/0/0'; Passphrase "some pass" - PrivateKey key4 = mnemonic.toStandardECDSAsecp256k1PrivateKey("some pass", Bip32Utils.toHardenedIndex(0)); - assertThat(Hex.toHexString(key4.getChainCode().getKey())).isEqualTo(CHAIN_CODE4); - assertThat(key4.toStringRaw()).isEqualTo(PRIVATE_KEY4); - assertThat(key4.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY4); - - // Chain m/44'/3030'/0'/0/2147483647; Passphrase "some pass" - PrivateKey key5 = mnemonic.toStandardECDSAsecp256k1PrivateKey("some pass", 2147483647); - assertThat(Hex.toHexString(key5.getChainCode().getKey())).isEqualTo(CHAIN_CODE5); - assertThat(key5.toStringRaw()).isEqualTo(PRIVATE_KEY5); - assertThat(key5.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY5); - - // Chain m/44'/3030'/0'/0/2147483647'; Passphrase "some pass" - PrivateKey key6 = mnemonic.toStandardECDSAsecp256k1PrivateKey("some pass", Bip32Utils.toHardenedIndex(2147483647)); - assertThat(Hex.toHexString(key6.getChainCode().getKey())).isEqualTo(CHAIN_CODE6); - assertThat(key6.toStringRaw()).isEqualTo(PRIVATE_KEY6); - assertThat(key6.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY6); - } - - @Test - @DisplayName("Mnemonic.toStandardECDSAsecp256k1PrivateKey() test vector 2") - void toStandardECDSAsecp256k1PrivateKey2() throws BadMnemonicException { - // https://github.com/hashgraph/hedera-sdk-reference/issues/73#issuecomment-1424761224 - final String CHAIN_CODE1 = "e76e0480faf2790e62dc1a7bac9dce51db1b3571fd74d8e264abc0d240a55d09"; - final String PRIVATE_KEY1 = "f033824c20dd9949ad7a4440f67120ee02a826559ed5884077361d69b2ad51dd"; - final String PUBLIC_KEY1 = "0294bf84a54806989a74ca4b76291d386914610b40b610d303162b9e495bc06416"; - - final String CHAIN_CODE2 = "60c39c6a77bd68c0aaabfe2f4711dc9c2247214c4f4dae15ad4cb76905f5f544"; - final String PRIVATE_KEY2 = "962f549dafe2d9c8091ac918cb4fc348ab0767353f37501067897efbc84e7651"; - final String PUBLIC_KEY2 = "027123855357fd41d28130fbc59053192b771800d28ef47319ef277a1a032af78f"; - - final String CHAIN_CODE3 = "911a1095b64b01f7f3a06198df3d618654e5ed65862b211997c67515e3167892"; - final String PRIVATE_KEY3 = "c139ebb363d7f441ccbdd7f58883809ec0cc3ee7a122ef67974eec8534de65e8"; - final String PUBLIC_KEY3 = "0293bdb1507a26542ed9c1ec42afe959cf8b34f39daab4bf842cdac5fa36d50ef7"; - - final String CHAIN_CODE4 = "64173f2dcb1d65e15e787ef882fa15f54db00209e2dab16fa1661244cd98e95c"; - final String PRIVATE_KEY4 = "87c1d8d4bb0cebb4e230852f2a6d16f6847881294b14eb1d6058b729604afea0"; - final String PUBLIC_KEY4 = "03358e7761a422ca1c577f145fe845c77563f164b2c93b5b34516a8fa13c2c0888"; - - final String CHAIN_CODE5 = "a7250c2b07b368a054f5c91e6a3dbe6ca3bbe01eb0489fe8778304bd0a19c711"; - final String PRIVATE_KEY5 = "2583170ee745191d2bb83474b1de41a1621c47f6e23db3f2bf413a1acb5709e4"; - final String PUBLIC_KEY5 = "03f9eb27cc73f751e8e476dd1db79037a7df2c749fa75b6cc6951031370d2f95a5"; - - final String CHAIN_CODE6 = "66a1175e7690e3714d53ffce16ee6bb4eb02065516be2c2ad6bf6c9df81ec394"; - final String PRIVATE_KEY6 = "f2d008cd7349bdab19ed85b523ba218048f35ca141a3ecbc66377ad50819e961"; - final String PUBLIC_KEY6 = "027b653d04958d4bf83dd913a9379b4f9a1a1e64025a691830a67383bc3157c044"; - - Mnemonic mnemonic = Mnemonic.fromString(MNEMONIC_12_WORD_STRING); - - // Chain m/44'/3030'/0'/0/0 - PrivateKey key1 = mnemonic.toStandardECDSAsecp256k1PrivateKey("", 0); - assertThat(Hex.toHexString(key1.getChainCode().getKey())).isEqualTo(CHAIN_CODE1); - assertThat(key1.toStringRaw()).isEqualTo(PRIVATE_KEY1); - assertThat(key1.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY1); - - // Chain m/44'/3030'/0'/0/0' - PrivateKey key2 = mnemonic.toStandardECDSAsecp256k1PrivateKey("", Bip32Utils.toHardenedIndex(0)); - assertThat(Hex.toHexString(key2.getChainCode().getKey())).isEqualTo(CHAIN_CODE2); - assertThat(key2.toStringRaw()).isEqualTo(PRIVATE_KEY2); - assertThat(key2.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY2); - - // Chain m/44'/3030'/0'/0/0; Passphrase "some pass" - PrivateKey key3 = mnemonic.toStandardECDSAsecp256k1PrivateKey("some pass", 0); - assertThat(Hex.toHexString(key3.getChainCode().getKey())).isEqualTo(CHAIN_CODE3); - assertThat(key3.toStringRaw()).isEqualTo(PRIVATE_KEY3); - assertThat(key3.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY3); - - // Chain m/44'/3030'/0'/0/0'; Passphrase "some pass" - PrivateKey key4 = mnemonic.toStandardECDSAsecp256k1PrivateKey("some pass", Bip32Utils.toHardenedIndex(0)); - assertThat(Hex.toHexString(key4.getChainCode().getKey())).isEqualTo(CHAIN_CODE4); - assertThat(key4.toStringRaw()).isEqualTo(PRIVATE_KEY4); - assertThat(key4.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY4); - - // Chain m/44'/3030'/0'/0/2147483647; Passphrase "some pass" - PrivateKey key5 = mnemonic.toStandardECDSAsecp256k1PrivateKey("some pass", 2147483647); - assertThat(Hex.toHexString(key5.getChainCode().getKey())).isEqualTo(CHAIN_CODE5); - assertThat(key5.toStringRaw()).isEqualTo(PRIVATE_KEY5); - assertThat(key5.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY5); - - // Chain m/44'/3030'/0'/0/2147483647'; Passphrase "some pass" - PrivateKey key6 = mnemonic.toStandardECDSAsecp256k1PrivateKey("some pass", Bip32Utils.toHardenedIndex(2147483647)); - assertThat(Hex.toHexString(key6.getChainCode().getKey())).isEqualTo(CHAIN_CODE6); - assertThat(key6.toStringRaw()).isEqualTo(PRIVATE_KEY6); - assertThat(key6.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY6); - } - - @Test - @DisplayName("Mnemonic.toStandardECDSAsecp256k1PrivateKeyCustomDerivationPath() with custom derivation path invalid inputs vector") - void toStandardECDSAsecp256k1PrivateKeyCustomDpathInvalidInputs() throws BadMnemonicException { - final String DPATH_1 = "XYZ/44'/60'/0'/0/0"; // invalid derivation path - final String PASSPHRASE_1 = ""; - final String CHAIN_CODE_1 = "58a9ee31eaf7499abc01952b44dbf0a2a5d6447512367f09d99381c9605bf9e8"; - final String PRIVATE_KEY_1 = "78f9545e40025cf7da9126a4d6a861ae34031d1c74c3404df06110c9fde371ad"; - final String PUBLIC_KEY_1 = "02a8f4c22eea66617d4f119e3a951b93f584949bbfee90bd555305402da6c4e569"; - - final String DPATH_2 = ""; // null or empty derivation path - final String PASSPHRASE_2 = ""; - final String CHAIN_CODE_2 = "6dcfc7a4914bd0e75b94a2f38afee8c247b34810202a2c64fe599ee1b88afdc9"; - final String PRIVATE_KEY_2 = "77ca263661ebdd5a8b33c224aeff5e7bf67eedacee68a1699d97ee8929d7b130"; - final String PUBLIC_KEY_2 = "03e84c9be9be53ad722038cc1943e79df27e5c1d31088adb4f0e62444f4dece683"; - - final String DPATH_3 = "m/44'/60'/0'/0/6-7-8-9-0"; // invalid numeric value in derivation path - final String PASSPHRASE_3 = ""; - final String CHAIN_CODE_3 = "c8c798d2b3696be1e7a29d1cea205507eedc2057006b9ef1cde1b4e346089e17"; - final String PRIVATE_KEY_3 = "31c24292eac951279b659c335e44a2e812d0f1a228b1d4d87034874d376e605a"; - final String PUBLIC_KEY_3 = "0207ff3faf4055c1aa7a5ad94d6ff561fac35b9ae695ef486706243667d2b4d10e"; - - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() ->{ - Mnemonic mnemonic = Mnemonic.fromString(MNEMONIC_24_WORD_STRING); - mnemonic.toStandardECDSAsecp256k1PrivateKeyCustomDerivationPath( - PASSPHRASE_1, DPATH_1); - }).satisfies( - (iae) -> { - assertThat(iae.getMessage()).isEqualTo("Invalid derivation path format"); - } - ); - - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() ->{ - Mnemonic mnemonic = Mnemonic.fromString(MNEMONIC_24_WORD_STRING); - mnemonic.toStandardECDSAsecp256k1PrivateKeyCustomDerivationPath( - PASSPHRASE_2, DPATH_2); - }).satisfies( - (iae) -> { - assertThat(iae.getMessage()).isEqualTo("Derivation path cannot be null or empty"); - } - ); - - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() ->{ - Mnemonic mnemonic = Mnemonic.fromString(MNEMONIC_24_WORD_STRING); - mnemonic.toStandardECDSAsecp256k1PrivateKeyCustomDerivationPath( - PASSPHRASE_3, DPATH_3); - }).satisfies( - (iae) -> { - assertThat(iae.getMessage()).isEqualTo("Invalid derivation path format"); - } - ); - } - - @Test - @DisplayName("Mnemonic.toStandardECDSAsecp256k1PrivateKeyCustomDerivationPath() with custom derivation path test vector") - void toStandardECDSAsecp256k1PrivateKeyCustomDpath() throws BadMnemonicException { - final String DPATH_1 = "m/44'/60'/0'/0/0"; - final String PASSPHRASE_1 = ""; - final String CHAIN_CODE_1 = "58a9ee31eaf7499abc01952b44dbf0a2a5d6447512367f09d99381c9605bf9e8"; - final String PRIVATE_KEY_1 = "78f9545e40025cf7da9126a4d6a861ae34031d1c74c3404df06110c9fde371ad"; - final String PUBLIC_KEY_1 = "02a8f4c22eea66617d4f119e3a951b93f584949bbfee90bd555305402da6c4e569"; - - final String DPATH_2 = "m/44'/60'/0'/0/1"; - final String PASSPHRASE_2 = ""; - final String CHAIN_CODE_2 = "6dcfc7a4914bd0e75b94a2f38afee8c247b34810202a2c64fe599ee1b88afdc9"; - final String PRIVATE_KEY_2 = "77ca263661ebdd5a8b33c224aeff5e7bf67eedacee68a1699d97ee8929d7b130"; - final String PUBLIC_KEY_2 = "03e84c9be9be53ad722038cc1943e79df27e5c1d31088adb4f0e62444f4dece683"; - - final String DPATH_3 = "m/44'/60'/0'/0/2"; - final String PASSPHRASE_3 = ""; - final String CHAIN_CODE_3 = "c8c798d2b3696be1e7a29d1cea205507eedc2057006b9ef1cde1b4e346089e17"; - final String PRIVATE_KEY_3 = "31c24292eac951279b659c335e44a2e812d0f1a228b1d4d87034874d376e605a"; - final String PUBLIC_KEY_3 = "0207ff3faf4055c1aa7a5ad94d6ff561fac35b9ae695ef486706243667d2b4d10e"; - - Mnemonic mnemonic = Mnemonic.fromString(MNEMONIC_24_WORD_STRING); - - // m/44'/60'/0'/0/0 - PrivateKey key1 = mnemonic.toStandardECDSAsecp256k1PrivateKeyCustomDerivationPath(PASSPHRASE_1, DPATH_1); - assertThat(Hex.toHexString(key1.getChainCode().getKey())).isEqualTo(CHAIN_CODE_1); - assertThat(key1.toStringRaw()).isEqualTo(PRIVATE_KEY_1); - assertThat(key1.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY_1); - - // m/44'/60'/0'/0/1 - PrivateKey key2 = mnemonic.toStandardECDSAsecp256k1PrivateKeyCustomDerivationPath(PASSPHRASE_2, DPATH_2); - assertThat(Hex.toHexString(key2.getChainCode().getKey())).isEqualTo(CHAIN_CODE_2); - assertThat(key2.toStringRaw()).isEqualTo(PRIVATE_KEY_2); - assertThat(key2.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY_2); - - // m/44'/60'/0'/0/2 - PrivateKey key3 = mnemonic.toStandardECDSAsecp256k1PrivateKeyCustomDerivationPath(PASSPHRASE_3, DPATH_3); - assertThat(Hex.toHexString(key3.getChainCode().getKey())).isEqualTo(CHAIN_CODE_3); - assertThat(key3.toStringRaw()).isEqualTo(PRIVATE_KEY_3); - assertThat(key3.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY_3); - } - -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/Mocker.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/Mocker.java deleted file mode 100644 index 211b82926d..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/Mocker.java +++ /dev/null @@ -1,140 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.logger.LogLevel; -import com.hedera.hashgraph.sdk.logger.Logger; -import com.hedera.hashgraph.sdk.proto.ConsensusServiceGrpc; -import com.hedera.hashgraph.sdk.proto.CryptoServiceGrpc; -import com.hedera.hashgraph.sdk.proto.FileServiceGrpc; -import com.hedera.hashgraph.sdk.proto.SmartContractServiceGrpc; -import com.hedera.hashgraph.sdk.proto.TokenServiceGrpc; -import io.grpc.MethodDescriptor; -import io.grpc.Server; -import io.grpc.ServerMethodDefinition; -import io.grpc.ServerServiceDefinition; -import io.grpc.ServiceDescriptor; -import io.grpc.Status; -import io.grpc.inprocess.InProcessServerBuilder; -import io.grpc.stub.ServerCalls; - -import java.io.IOException; -import java.time.Duration; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicInteger; - -public class Mocker implements AutoCloseable { - private static final PrivateKey PRIVATE_KEY = PrivateKey.fromString("302e020100300506032b657004220420d45e1557156908c967804615af59a000be88c7aa7058bfcbe0f46b16c28f887d"); - public final Client client; - private final List services = List.of( - CryptoServiceGrpc.getServiceDescriptor(), - FileServiceGrpc.getServiceDescriptor(), - SmartContractServiceGrpc.getServiceDescriptor(), - ConsensusServiceGrpc.getServiceDescriptor(), - TokenServiceGrpc.getServiceDescriptor() - ); - private final List> responses; - private final List servers = new ArrayList<>(); - - Mocker(List> responses) { - this.responses = responses; - - var network = new HashMap(responses.size()); - - for (var i = 0; i < responses.size(); i++) { - var index = new AtomicInteger(); - var response = responses.get(i); - var name = InProcessServerBuilder.generateName(); - var nodeAccountId = new AccountId(3 + i); - var builder = InProcessServerBuilder.forName(name); - - network.put("in-process:" + name, nodeAccountId); - - for (var service : services) { - var descriptor = ServerServiceDefinition.builder(service); - - for (MethodDescriptor method : service.getMethods()) { - var methodDefinition = ServerMethodDefinition.create((MethodDescriptor) method, - ServerCalls.asyncUnaryCall((request, responseObserver) -> { - var responseIndex = index.getAndIncrement(); - - if (responseIndex >= response.size()) { - responseObserver.onError(Status.Code.ABORTED.toStatus().asRuntimeException()); - return; - } - - var r = response.get(responseIndex); - - if (r instanceof Function) { - try { - r = ((Function) r).apply(request); - } catch (Throwable e) { - r = Status.ABORTED.withDescription(e.getMessage()).asRuntimeException(); - } - } - - if (r instanceof Throwable) { - responseObserver.onError((Throwable) r); - } else { - responseObserver.onNext(r); - responseObserver.onCompleted(); - } - }) - ); - descriptor.addMethod(methodDefinition); - } - - builder.addService(descriptor.build()); - } - - try { - this.servers.add(builder.directExecutor().build().start()); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - this.client = Client.forNetwork(network) - .setOperator(new AccountId(1800), PRIVATE_KEY) - .setMinBackoff(Duration.ofMillis(0)) - .setMaxBackoff(Duration.ofMillis(0)) - .setNodeMinBackoff(Duration.ofMillis(0)) - .setNodeMaxBackoff(Duration.ofMillis(0)) - .setMinNodeReadmitTime(Duration.ofMillis(0)) - .setMaxNodeReadmitTime(Duration.ofMillis(0)) - .setLogger(new Logger(LogLevel.SILENT)); - } - - public static Mocker withResponses(List> responses) { - return new Mocker(responses); - } - - public void close() throws TimeoutException, InterruptedException { - client.close(); - - for (var server : servers) { - server.shutdown(); - server.awaitTermination(); - } - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/MockingTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/MockingTest.java deleted file mode 100644 index 7bd857deef..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/MockingTest.java +++ /dev/null @@ -1,798 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.AccountID; -import com.hedera.hashgraph.sdk.proto.CryptoGetAccountBalanceResponse; -import com.hedera.hashgraph.sdk.proto.CryptoGetInfoResponse; -import com.hedera.hashgraph.sdk.proto.CryptoServiceGrpc; -import com.hedera.hashgraph.sdk.proto.FileServiceGrpc; -import com.hedera.hashgraph.sdk.proto.Query; -import com.hedera.hashgraph.sdk.proto.Response; -import com.hedera.hashgraph.sdk.proto.ResponseCodeEnum; -import com.hedera.hashgraph.sdk.proto.ResponseHeader; -import com.hedera.hashgraph.sdk.proto.SignedTransaction; -import com.hedera.hashgraph.sdk.proto.SmartContractServiceGrpc; -import com.hedera.hashgraph.sdk.proto.Transaction; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionGetReceiptResponse; -import com.hedera.hashgraph.sdk.proto.TransactionGetRecordResponse; -import com.hedera.hashgraph.sdk.proto.TransactionReceipt; -import com.hedera.hashgraph.sdk.proto.TransactionRecord; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import io.grpc.Status; -import io.grpc.stub.StreamObserver; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; - -import java.nio.charset.StandardCharsets; -import java.time.Duration; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.TimeoutException; -import java.util.function.Supplier; - -public class MockingTest { - @Test - void testSucceedsWithCorrectHbars() throws PrecheckStatusException, TimeoutException, InterruptedException { - List responses1 = List.of( - Status.Code.UNAVAILABLE.toStatus().asRuntimeException(), - (Function) o -> Status.Code.UNAVAILABLE.toStatus().asRuntimeException(), - Response.newBuilder() - .setCryptogetAccountBalance( - CryptoGetAccountBalanceResponse.newBuilder() - .setHeader(ResponseHeader.newBuilder().setNodeTransactionPrecheckCode(ResponseCodeEnum.OK).build()) - .setAccountID(AccountID.newBuilder().setAccountNum(10).build()) - .setBalance(100) - .build() - ).build() - ); - - var responses = List.of(responses1); - - try (var mocker = Mocker.withResponses(responses)) { - var balance = new AccountBalanceQuery().setAccountId(new AccountId(10)).execute(mocker.client); - - Assertions.assertEquals(balance.hbars, Hbar.fromTinybars(100)); - } - } - - String makeBigString(int size) { - char[] chars = new char[size]; - Arrays.fill(chars, 'A'); - return new String(chars); - } - - @ParameterizedTest(name = "[{0}] Executable retries on gRPC error with PLATFORM_NOT_ACTIVE when getting receipt") - @CsvSource({"sync", "async"}) - void shouldRetryExceptionallyFunctionsCorrectlyForPlatformNotActiveGetReceipt(String sync) throws Exception { - var service = new TestCryptoService(); - var server = new TestServer("getReceiptRetry" + sync, service); - server.client.setMaxAttempts(3); - - service.buffer.enqueueResponse(TestResponse.transaction(com.hedera.hashgraph.sdk.Status.PLATFORM_NOT_ACTIVE)); - service.buffer.enqueueResponse(TestResponse.transactionOk()); - - com.hedera.hashgraph.sdk.TransactionResponse transactionResponse; - if (sync.equals("sync")) { - transactionResponse = new AccountCreateTransaction().execute(server.client); - } else { - transactionResponse = new AccountCreateTransaction().executeAsync(server.client).get(); - } - - service.buffer - .enqueueResponse(TestResponse.query( - Response.newBuilder().setTransactionGetReceipt( - TransactionGetReceiptResponse.newBuilder() - .setHeader(ResponseHeader.newBuilder().setNodeTransactionPrecheckCode(ResponseCodeEnum.PLATFORM_NOT_ACTIVE)) - .setReceipt(TransactionReceipt.newBuilder().setStatus(ResponseCodeEnum.SUCCESS).build()) - .build() - ).build() - )) - .enqueueResponse(TestResponse.receipt(com.hedera.hashgraph.sdk.Status.PLATFORM_NOT_ACTIVE)) - .enqueueResponse(TestResponse.successfulReceipt()); - - if (sync.equals("sync")) { - transactionResponse.getReceipt(server.client); - } else { - transactionResponse.getReceiptAsync(server.client).get(); - } - } - - @ParameterizedTest(name = "[{0}] Executable retries on gRPC error with PLATFORM_NOT_ACTIVE when getting record") - @CsvSource({"sync", "async"}) - void shouldRetryExceptionallyFunctionsCorrectlyForPlatformNotActiveGetRecord(String sync) throws Exception { - var service = new TestCryptoService(); - var server = new TestServer("getRecordRetry" + sync, service); - server.client.setMaxAttempts(3); - - service.buffer.enqueueResponse(TestResponse.transaction(com.hedera.hashgraph.sdk.Status.PLATFORM_NOT_ACTIVE)); - service.buffer.enqueueResponse(TestResponse.transactionOk()); - - com.hedera.hashgraph.sdk.TransactionResponse transactionResponse; - if (sync.equals("sync")) { - transactionResponse = new AccountCreateTransaction().execute(server.client); - } else { - transactionResponse = new AccountCreateTransaction().executeAsync(server.client).get(); - } - - service.buffer - .enqueueResponse(TestResponse.successfulReceipt()) // for inner getReceipt - .enqueueResponse(TestResponse.successfulReceipt()) // for inner getCost - .enqueueResponse(TestResponse.query( - Response.newBuilder().setTransactionGetRecord( - TransactionGetRecordResponse.newBuilder() - .setHeader(ResponseHeader.newBuilder().setNodeTransactionPrecheckCode(ResponseCodeEnum.PLATFORM_NOT_ACTIVE).build()) - .setTransactionRecord(TransactionRecord.newBuilder().setReceipt( - TransactionReceipt.newBuilder().setStatus(ResponseCodeEnum.SUCCESS).build() - ).build() - ).build() - ).build() - )) - .enqueueResponse(TestResponse.query( - Response.newBuilder().setTransactionGetRecord( - TransactionGetRecordResponse.newBuilder() - .setTransactionRecord(TransactionRecord.newBuilder().setReceipt( - TransactionReceipt.newBuilder().setStatus(ResponseCodeEnum.PLATFORM_NOT_ACTIVE).build() - ).build() - ).build() - ).build() - )) - .enqueueResponse(TestResponse.query( - Response.newBuilder().setTransactionGetRecord( - TransactionGetRecordResponse.newBuilder() - .setTransactionRecord(TransactionRecord.newBuilder().setReceipt( - TransactionReceipt.newBuilder().setStatus(ResponseCodeEnum.SUCCESS).build() - ).build() - ).build() - ).build() - )); - - if (sync.equals("sync")) { - transactionResponse.getRecord(server.client); - } else { - transactionResponse.getRecordAsync(server.client).get(); - } - } - - @ParameterizedTest(name = "[{0}, {1}] ContractCreateFlow functions") - @CsvSource({ - "sync, stakedNode", - "sync, stakedAccount", - "async, stakedNode", - "async, stakedAccount", - }) - void contractCreateFlowFunctions(String versionToTest, String stakeType) throws Throwable { - var BIG_BYTECODE = makeBigString(ContractCreateFlow.FILE_CREATE_MAX_BYTES + 1000); - var adminKey = PrivateKey.generateED25519().getPublicKey(); - - var cryptoService = new TestCryptoService(); - var fileService = new TestFileService(); - var contractService = new TestContractService(); - var server = new TestServer("contractCreateFlow" + versionToTest + stakeType, cryptoService, fileService, contractService); - - var fileId = FileId.fromString("1.2.3"); - var maxAutomaticTokenAssociations = 101; - var stakedAccountId = AccountId.fromString("4.3.2"); - var stakedNode = 13L; - var declineStakingReward = true; - - - cryptoService.buffer.enqueueResponse(TestResponse.query( - Response.newBuilder().setTransactionGetReceipt( - TransactionGetReceiptResponse.newBuilder().setReceipt( - TransactionReceipt.newBuilder().setFileID(fileId.toProtobuf()).setStatus(ResponseCodeEnum.SUCCESS).build() - ).build() - ).build() - )).enqueueResponse(TestResponse.successfulReceipt()).enqueueResponse(TestResponse.successfulReceipt()); - fileService.buffer - .enqueueResponse(TestResponse.transactionOk()) - .enqueueResponse(TestResponse.transactionOk()) - .enqueueResponse(TestResponse.transactionOk()); - - contractService.buffer.enqueueResponse(TestResponse.transactionOk()); - - var flow = new ContractCreateFlow() - .setBytecode(BIG_BYTECODE) - .setContractMemo("memo goes here") - .setConstructorParameters(new byte[]{1, 2, 3}) - .setAutoRenewPeriod(Duration.ofMinutes(1)) - .setAdminKey(adminKey) - .setGas(100) - .setInitialBalance(new Hbar(3)) - .setMaxAutomaticTokenAssociations(maxAutomaticTokenAssociations) - .setDeclineStakingReward(declineStakingReward); - - if (stakeType.equals("stakedAccount")) { - flow.setStakedAccountId(stakedAccountId); - } else { - flow.setStakedNodeId(stakedNode); - } - - if (versionToTest.equals("sync")) { - flow.execute(server.client); - } else { - flow.executeAsync(server.client).get(); - } - - Thread.sleep(1000); - - Assertions.assertEquals(3, cryptoService.buffer.queryRequestsReceived.size()); - Assertions.assertEquals(3, fileService.buffer.transactionRequestsReceived.size()); - Assertions.assertEquals(1, contractService.buffer.transactionRequestsReceived.size()); - var transactions = new ArrayList>(); - for (var request : fileService.buffer.transactionRequestsReceived) { - transactions.add(com.hedera.hashgraph.sdk.Transaction.fromBytes(request.toByteArray())); - } - transactions.add(com.hedera.hashgraph.sdk.Transaction.fromBytes( - contractService.buffer.transactionRequestsReceived.get(0).toByteArray() - )); - - Assertions.assertInstanceOf(FileCreateTransaction.class, transactions.get(0)); - Assertions.assertEquals( - ContractCreateFlow.FILE_CREATE_MAX_BYTES, - ((FileCreateTransaction) transactions.get(0)).getContents().size() - ); - - Assertions.assertTrue(cryptoService.buffer.queryRequestsReceived.get(0).hasTransactionGetReceipt()); - - Assertions.assertInstanceOf(FileAppendTransaction.class, transactions.get(1)); - var fileAppendTx = (FileAppendTransaction) transactions.get(1); - Assertions.assertEquals(fileId, fileAppendTx.getFileId()); - Assertions.assertEquals( - BIG_BYTECODE.length() - ContractCreateFlow.FILE_CREATE_MAX_BYTES, - fileAppendTx.getContents().size() - ); - - Assertions.assertInstanceOf(ContractCreateTransaction.class, transactions.get(3)); - var contractCreateTx = (ContractCreateTransaction) transactions.get(3); - Assertions.assertEquals("memo goes here", contractCreateTx.getContractMemo()); - Assertions.assertEquals(fileId, contractCreateTx.getBytecodeFileId()); - Assertions.assertEquals(ByteString.copyFrom(new byte[]{1, 2, 3}), contractCreateTx.getConstructorParameters()); - Assertions.assertEquals(Duration.ofMinutes(1), contractCreateTx.getAutoRenewPeriod()); - Assertions.assertEquals(adminKey, contractCreateTx.getAdminKey()); - Assertions.assertEquals(100, contractCreateTx.getGas()); - Assertions.assertEquals(new Hbar(3), contractCreateTx.getInitialBalance()); - Assertions.assertEquals(maxAutomaticTokenAssociations, contractCreateTx.getMaxAutomaticTokenAssociations()); - Assertions.assertEquals(declineStakingReward, contractCreateTx.getDeclineStakingReward()); - - if (stakeType.equals("stakedAccount")) { - Assertions.assertEquals(stakedAccountId, contractCreateTx.getStakedAccountId()); - } else { - Assertions.assertEquals(stakedNode, contractCreateTx.getStakedNodeId()); - } - - Assertions.assertInstanceOf(FileDeleteTransaction.class, transactions.get(2)); - - server.close(); - } - - - @Test - void accountInfoFlowFunctions() throws Throwable { - var BIG_BYTES = makeBigString(1000).getBytes(StandardCharsets.UTF_8); - var privateKey = PrivateKey.generateED25519(); - var otherPrivateKey = PrivateKey.generateED25519(); - var accountId = AccountId.fromString("1.2.3"); - var cost = Hbar.from(1); - - Supplier makeTx = () -> new TokenMintTransaction() - .setTokenId(TokenId.fromString("1.2.3")) - .setAmount(5) - .setTransactionId(TransactionId.generate(accountId)) - .setNodeAccountIds(List.of(AccountId.fromString("0.0.3"))) - .freeze(); - - var properlySignedTx = makeTx.get().sign(privateKey); - var improperlySignedTx = makeTx.get().sign(otherPrivateKey); - var properBigBytesSignature = privateKey.sign(BIG_BYTES); - var improperBigBytesSignature = otherPrivateKey.sign(BIG_BYTES); - - var cryptoService = new TestCryptoService(); - var server = new TestServer("accountInfoFlow", cryptoService); - - for (int i = 0; i < 8; i++) { - cryptoService.buffer.enqueueResponse( - TestResponse.query( - Response.newBuilder().setCryptoGetInfo( - CryptoGetInfoResponse.newBuilder() - .setHeader( - ResponseHeader.newBuilder() - .setCost(cost.toTinybars()) - .build() - ).build() - ).build() - ) - ); - cryptoService.buffer.enqueueResponse( - TestResponse.query( - Response.newBuilder().setCryptoGetInfo( - CryptoGetInfoResponse.newBuilder() - .setAccountInfo( - CryptoGetInfoResponse.AccountInfo.newBuilder() - .setKey(privateKey.getPublicKey().toProtobufKey()) - .build() - ).build() - ).build() - ) - ); - } - - Assertions.assertTrue( - AccountInfoFlow.verifyTransactionSignature(server.client, accountId, properlySignedTx) - ); - Assertions.assertFalse( - AccountInfoFlow.verifyTransactionSignature(server.client, accountId, improperlySignedTx) - ); - Assertions.assertTrue( - AccountInfoFlow.verifySignature(server.client, accountId, BIG_BYTES, properBigBytesSignature) - ); - Assertions.assertFalse( - AccountInfoFlow.verifySignature(server.client, accountId, BIG_BYTES, improperBigBytesSignature) - ); - Assertions.assertTrue( - AccountInfoFlow.verifyTransactionSignatureAsync(server.client, accountId, properlySignedTx).get() - ); - Assertions.assertFalse( - AccountInfoFlow.verifyTransactionSignatureAsync(server.client, accountId, improperlySignedTx).get() - ); - Assertions.assertTrue( - AccountInfoFlow.verifySignatureAsync(server.client, accountId, BIG_BYTES, properBigBytesSignature).get() - ); - Assertions.assertFalse( - AccountInfoFlow.verifySignatureAsync(server.client, accountId, BIG_BYTES, improperBigBytesSignature).get() - ); - - Assertions.assertEquals(16, cryptoService.buffer.queryRequestsReceived.size()); - for (int i = 0; i < 16; i += 2) { - var costQueryRequest = cryptoService.buffer.queryRequestsReceived.get(i); - var queryRequest = cryptoService.buffer.queryRequestsReceived.get(i + 1); - - Assertions.assertTrue(costQueryRequest.hasCryptoGetInfo()); - Assertions.assertTrue(costQueryRequest.getCryptoGetInfo().hasHeader()); - Assertions.assertTrue(costQueryRequest.getCryptoGetInfo().getHeader().hasPayment()); - - Assertions.assertTrue(queryRequest.hasCryptoGetInfo()); - Assertions.assertTrue(queryRequest.getCryptoGetInfo().hasAccountID()); - Assertions.assertEquals(accountId, AccountId.fromProtobuf(queryRequest.getCryptoGetInfo().getAccountID())); - } - server.close(); - } - - @Test - void exitOnAborted() throws PrecheckStatusException, TimeoutException, InterruptedException { - List responses1 = List.of(); - - var responses = List.of(responses1); - - try (var mocker = Mocker.withResponses(responses)) { - Assertions.assertThrows(RuntimeException.class, () -> new AccountBalanceQuery().setAccountId(new AccountId(10)).execute(mocker.client)); - } - } - - @ParameterizedTest(name = "[{2}] Executable retries on gRPC error with status {0} and description {1}") - @CsvSource({ - "INTERNAL, internal RST_STREAM error, sync", - "INTERNAL, rst stream, sync", - "RESOURCE_EXHAUSTED, , sync", - "UNAVAILABLE, , sync", - "INTERNAL, internal RST_STREAM error, async", - "INTERNAL, rst stream, async", - "RESOURCE_EXHAUSTED, , async", - "UNAVAILABLE, , async" - }) - void shouldRetryExceptionallyFunctionsCorrectly(Status.Code code, String description, String sync) throws Exception { - var service = new TestCryptoService(); - var server = new TestServer("executableRetry" + code + (description != null ? description.replace(" ", "") : "NULL") + sync, service); - - var exception = Status.fromCode(code) - .withDescription(description) - .asRuntimeException(); - - service.buffer - .enqueueResponse(TestResponse.error(exception)) - .enqueueResponse(TestResponse.transactionOk()); - - if (sync.equals("sync")) { - new AccountCreateTransaction() - .setNodeAccountIds(List.of(AccountId.fromString("1.1.1"), AccountId.fromString("2.2.2"))) - .execute(server.client); - } else { - new AccountCreateTransaction() - .setNodeAccountIds(List.of(AccountId.fromString("1.1.1"), AccountId.fromString("2.2.2"))) - .executeAsync(server.client) - .get(); - } - - Assertions.assertEquals(2, service.buffer.transactionRequestsReceived.size()); - assertFirstTwoRequestsNotDirectedAtSameNode(service); - - server.close(); - } - - - @ParameterizedTest(name = "[{2}] Executable should make max {1} attempts when there are {0} errors, and error") - @CsvSource({ - "2, 2, sync", - "2, 2, async" - }) - void hitsTxMaxAttemptsCorrectly(Integer numberOfErrors, Integer maxAttempts, String sync) throws Exception { - var service = new TestCryptoService(); - var server = new TestServer("executableMaxAttemptsSync" + numberOfErrors + maxAttempts + sync, service); - - var exception = Status.UNAVAILABLE.asRuntimeException(); - - for (var i = 0; i < numberOfErrors; i++) { - service.buffer.enqueueResponse(TestResponse.error(exception)); - } - - service.buffer.enqueueResponse(TestResponse.transactionOk()); - - if (sync.equals("sync")) { - Assertions.assertThrows(MaxAttemptsExceededException.class, () -> { - new AccountCreateTransaction() - .setMaxAttempts(maxAttempts) - .setNodeAccountIds(List.of(AccountId.fromString("1.1.1"), AccountId.fromString("2.2.2"))) - .execute(server.client); - }); - } else { - new AccountCreateTransaction() - .setMaxAttempts(maxAttempts) - .setNodeAccountIds(List.of(AccountId.fromString("1.1.1"), AccountId.fromString("2.2.2"))) - .executeAsync(server.client) - .handle((response, error) -> { - Assertions.assertNotNull(error); - System.out.println(error); - Assertions.assertTrue(error.getCause() instanceof MaxAttemptsExceededException); - - return null; - }) - .get(); - } - - Assertions.assertEquals(2, service.buffer.transactionRequestsReceived.size()); - - server.close(); - } - - @ParameterizedTest(name = "[{2}] Executable retries on {1} Hedera status error(s) {0}") - @CsvSource({ - "BUSY, 1, sync", - "PLATFORM_TRANSACTION_NOT_CREATED, 1, sync", - "TRANSACTION_EXPIRED, 1, sync", - "BUSY, 3, sync", - "PLATFORM_TRANSACTION_NOT_CREATED, 3, sync", - "TRANSACTION_EXPIRED, 3, sync", - "BUSY, 1, async", - "PLATFORM_TRANSACTION_NOT_CREATED, 1, async", - "TRANSACTION_EXPIRED, 1, async", - "BUSY, 3, async", - "PLATFORM_TRANSACTION_NOT_CREATED, 3, async", - "TRANSACTION_EXPIRED, 3, async" - }) - void shouldRetryFunctionsCorrectly(com.hedera.hashgraph.sdk.Status status, int numberOfErrors, String sync) throws Exception { - var service = new TestCryptoService(); - var server = new TestServer("shouldRetryFunctionsCorrectly" + status + numberOfErrors + sync, service); - - for (var i = 0; i < numberOfErrors; i++) { - service.buffer.enqueueResponse(TestResponse.transaction(status)); - } - - service.buffer.enqueueResponse(TestResponse.transactionOk()); - - server.client.setMaxAttempts(4); - - if (sync.equals("sync")) { - new AccountCreateTransaction() - .setNodeAccountIds(List.of(AccountId.fromString("1.1.1"), AccountId.fromString("2.2.2"))) - .execute(server.client); - } else { - new AccountCreateTransaction() - .setNodeAccountIds(List.of(AccountId.fromString("1.1.1"), AccountId.fromString("2.2.2"))) - .executeAsync(server.client) - .get(); - } - - Assertions.assertEquals(numberOfErrors + 1, service.buffer.transactionRequestsReceived.size()); - assertFirstTwoRequestsNotDirectedAtSameNode(service); - - server.close(); - } - - @ParameterizedTest(name = "[{2}] Executable retries on {1} Hedera status error(s) {0}") - @CsvSource({ - "BUSY, sync", - "PLATFORM_TRANSACTION_NOT_CREATED, sync", - "PLATFORM_NOT_ACTIVE, sync", - "BUSY, async", - "PLATFORM_TRANSACTION_NOT_CREATED, async", - "PLATFORM_NOT_ACTIVE, async", - }) - void hitsClientMaxAttemptsCorrectly(com.hedera.hashgraph.sdk.Status status, String sync) throws Exception { - var service = new TestCryptoService(); - var server = new TestServer("shouldRetryFunctionsCorrectly" + status + sync, service); - - for (var i = 0; i < 2; i++) { - service.buffer.enqueueResponse(TestResponse.transaction(status)); - } - - server.client.setMaxAttempts(2); - - if (sync.equals("sync")) { - Assertions.assertThrows(MaxAttemptsExceededException.class, () -> { - new AccountCreateTransaction() - .setNodeAccountIds(List.of(AccountId.fromString("1.1.1"), AccountId.fromString("2.2.2"))) - .execute(server.client); - }); - } else { - new AccountCreateTransaction() - .setNodeAccountIds(List.of(AccountId.fromString("1.1.1"), AccountId.fromString("2.2.2"))) - .executeAsync(server.client) - .handle((response, error) -> { - Assertions.assertNotNull(error); - Assertions.assertTrue(error.getCause() instanceof MaxAttemptsExceededException); - - return null; - }) - .get(); - } - Assertions.assertEquals(2, service.buffer.transactionRequestsReceived.size()); - assertFirstTwoRequestsNotDirectedAtSameNode(service); - - server.close(); - } - - private static void assertFirstTwoRequestsNotDirectedAtSameNode(TestCryptoService service) throws InvalidProtocolBufferException { - var requests = service.buffer.transactionRequestsReceived; - var signedTx0 = SignedTransaction.parseFrom(requests.get(0).getSignedTransactionBytes()); - var signedTx1 = SignedTransaction.parseFrom(requests.get(1).getSignedTransactionBytes()); - var txBody0 = TransactionBody.parseFrom(signedTx0.getBodyBytes()); - var txBody1 = TransactionBody.parseFrom(signedTx1.getBodyBytes()); - Assertions.assertNotEquals(txBody0.getNodeAccountID(), txBody1.getNodeAccountID()); - } - - @Test - @DisplayName("Client.setDefaultMaxTransactionFee() functions correctly") - void defaultMaxTransactionFeeTest() throws Exception { - var service = new TestCryptoService(); - var server = new TestServer("maxTransactionFee", service); - - service.buffer - .enqueueResponse(TestResponse.transactionOk()) - .enqueueResponse(TestResponse.transactionOk()) - .enqueueResponse(TestResponse.transactionOk()) - .enqueueResponse(TestResponse.transactionOk()); - - new AccountDeleteTransaction() - .execute(server.client); - - new AccountDeleteTransaction() - .setMaxTransactionFee(new Hbar(5)) - .execute(server.client); - - server.client.setDefaultMaxTransactionFee(new Hbar(1)); - - new AccountDeleteTransaction() - .execute(server.client); - - new AccountDeleteTransaction() - .setMaxTransactionFee(new Hbar(3)) - .execute(server.client); - - Assertions.assertEquals(4, service.buffer.transactionRequestsReceived.size()); - var transactions = new ArrayList>(); - for (var request : service.buffer.transactionRequestsReceived) { - transactions.add(com.hedera.hashgraph.sdk.Transaction.fromBytes(request.toByteArray())); - } - Assertions.assertEquals(new Hbar(2), transactions.get(0).getMaxTransactionFee()); - Assertions.assertEquals(new Hbar(5), transactions.get(1).getMaxTransactionFee()); - Assertions.assertEquals(new Hbar(1), transactions.get(2).getMaxTransactionFee()); - Assertions.assertEquals(new Hbar(3), transactions.get(3).getMaxTransactionFee()); - - server.close(); - } - - @Disabled - @Test - @DisplayName("Client.setDefaultMaxQueryPayment() functions correctly") - void defaultMaxQueryPaymentTest() throws Exception { - var service = new TestCryptoService(); - var server = new TestServer("queryPayment", service); - - var response = Response.newBuilder() - .setCryptogetAccountBalance( - new AccountBalance( - new Hbar(0), - new HashMap(), - new HashMap() - ).toProtobuf() - ).build(); - - service.buffer - .enqueueResponse(TestResponse.query(response)) - .enqueueResponse(TestResponse.query(response)) - .enqueueResponse(TestResponse.query(response)); - - // TODO: this will take some work, since I have to contend with Query's getCost behavior - // TODO: actually, because AccountBalanceQuery is free, I'll need some other query type to test this. - // Perhaps getAccountInfo? - - server.close(); - } - - @Test - @DisplayName("Signer is prevented from signing twice") - void signerDoesNotSignTwice() throws Exception { - var service = new TestCryptoService(); - var server = new TestServer("signerDoesNotSignTwice", service); - - service.buffer.enqueueResponse(TestResponse.transactionOk()); - - var aliceKey = PrivateKey.generateED25519(); - - var transaction = new AccountCreateTransaction() - .setTransactionId(TransactionId.generate(Objects.requireNonNull(server.client.getOperatorAccountId()))) - .setNodeAccountIds(server.client.network.getNodeAccountIdsForExecute()) - .freeze() - .sign(aliceKey); - - // This will cause the SDK Transaction to populate the sigPairLists list - transaction.getTransactionHashPerNode(); - - // This will clear the outerTransactions list while keeping the sigPairLists list - transaction.signWithOperator(server.client); - - // If Transaction.signTransaction() is not programmed correctly, it will add Alice's signature to the - // sigPairList a second time here. - transaction.execute(server.client); - - // Now we must go through the laborious process of digging info out of the response. =( - Assertions.assertEquals(1, service.buffer.transactionRequestsReceived.size()); - var request = service.buffer.transactionRequestsReceived.get(0); - var sigPairList = SignedTransaction.parseFrom(request.getSignedTransactionBytes()).getSigMap().getSigPairList(); - Assertions.assertEquals(2, sigPairList.size()); - Assertions.assertNotEquals( - sigPairList.get(0).getEd25519().toString(), - sigPairList.get(1).getEd25519().toString()); - - server.close(); - } - - @Test - @DisplayName("Can cancel executeAsync()") - void canCancelExecuteAsync() throws Exception { - var service = new TestCryptoService(); - var server = new TestServer("canCancelExecuteAsync", service); - - server.client.setMaxBackoff(Duration.ofSeconds(8)); - server.client.setMinBackoff(Duration.ofSeconds(1)); - - var noReceiptResponse = TestResponse.query( - Response.newBuilder() - .setTransactionGetReceipt( - TransactionGetReceiptResponse.newBuilder() - .setHeader( - ResponseHeader.newBuilder() - .setNodeTransactionPrecheckCode(com.hedera.hashgraph.sdk.Status.RECEIPT_NOT_FOUND.code) - ) - ).build() - ); - - service.buffer.enqueueResponse(noReceiptResponse); - service.buffer.enqueueResponse(noReceiptResponse); - service.buffer.enqueueResponse(noReceiptResponse); - - var future = new TransactionReceiptQuery().executeAsync(server.client); - Thread.sleep(1500); - future.cancel(true); - Thread.sleep(5000); - - Assertions.assertEquals(2, service.buffer.queryRequestsReceived.size()); - - server.close(); - } - - private static class TestCryptoService extends CryptoServiceGrpc.CryptoServiceImplBase implements TestService { - public Buffer buffer = new Buffer(); - - @Override - public Buffer getBuffer() { - return buffer; - } - - @Override - public void createAccount(Transaction request, StreamObserver responseObserver) { - respondToTransactionFromQueue(request, responseObserver); - } - - @Override - public void cryptoDelete(Transaction request, StreamObserver responseObserver) { - respondToTransactionFromQueue(request, responseObserver); - } - - @Override - public void cryptoGetBalance(Query request, StreamObserver responseObserver) { - respondToQueryFromQueue(request, responseObserver); - } - - @Override - public void getTransactionReceipts(Query request, StreamObserver responseObserver) { - respondToQueryFromQueue(request, responseObserver); - } - - @Override - public void getTxRecordByTxID(Query request, StreamObserver responseObserver) { - respondToQueryFromQueue(request, responseObserver); - } - - @Override - public void getAccountInfo(Query request, StreamObserver responseObserver) { - respondToQueryFromQueue(request, responseObserver); - } - } - - private static class TestFileService extends FileServiceGrpc.FileServiceImplBase implements TestService { - public Buffer buffer = new Buffer(); - - @Override - public Buffer getBuffer() { - return buffer; - } - - @Override - public void createFile(Transaction request, StreamObserver responseObserver) { - respondToTransactionFromQueue(request, responseObserver); - } - - @Override - public void appendContent(Transaction request, StreamObserver responseObserver) { - respondToTransactionFromQueue(request, responseObserver); - } - - @Override - public void deleteFile(Transaction request, StreamObserver responseObserver) { - respondToTransactionFromQueue(request, responseObserver); - } - } - - private static class TestContractService extends SmartContractServiceGrpc.SmartContractServiceImplBase implements TestService { - public Buffer buffer = new Buffer(); - - @Override - public Buffer getBuffer() { - return buffer; - } - - @Override - public void createContract(Transaction request, StreamObserver responseObserver) { - respondToTransactionFromQueue(request, responseObserver); - } - } -} - diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/NetworkVersionInfoQueryTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/NetworkVersionInfoQueryTest.java deleted file mode 100644 index d05f3fec6f..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/NetworkVersionInfoQueryTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.hedera.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import com.hedera.hashgraph.sdk.proto.Transaction; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -public class NetworkVersionInfoQueryTest { - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - var builder = com.hedera.hashgraph.sdk.proto.Query.newBuilder(); - new NetworkVersionInfoQuery() - .setMaxQueryPayment(Hbar.fromTinybars(100_000)) - .onMakeRequest( - builder, - QueryHeader - .newBuilder() - .setPayment( - Transaction.newBuilder() - .setSignedTransactionBytes( - ByteString.fromHex("deadbeef") - ).build() - ).build() - ); - SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")).toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/NetworkVersionInfoQueryTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/NetworkVersionInfoQueryTest.snap deleted file mode 100644 index e2b1e4faf0..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/NetworkVersionInfoQueryTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.NetworkVersionInfoQueryTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.Query\nnetwork_get_version_info {\n header {\n payment {\n signed_transaction_bytes: \"\\336\\255\\276\\357\"\n }\n }\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/NetworkVersionInfoTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/NetworkVersionInfoTest.snap deleted file mode 100644 index e3e16ccc11..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/NetworkVersionInfoTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.NetworkVersionInfoTest.shouldSerialize=[ - "com.hedera.hashgraph.sdk.NetworkVersionInfo" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/NftIdTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/NftIdTest.java deleted file mode 100644 index cb313d93f7..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/NftIdTest.java +++ /dev/null @@ -1,96 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.bouncycastle.util.encoders.Hex; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.util.concurrent.TimeoutException; - -import static org.assertj.core.api.Assertions.assertThat; - -class NftIdTest { - - static Client mainnetClient; - static Client testnetClient; - static Client previewnetClient; - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - mainnetClient = Client.forMainnet(); - testnetClient = Client.forTestnet(); - previewnetClient = Client.forPreviewnet(); - } - - @AfterAll - public static void afterAll() throws TimeoutException { - mainnetClient.close(); - testnetClient.close(); - previewnetClient.close(); - SnapshotMatcher.validateSnapshots(); - } - - @Test - void fromString() { - SnapshotMatcher.expect(NftId.fromString("0.0.5005@1234").toString()).toMatchSnapshot(); - } - - @Test - void fromString2() { - SnapshotMatcher.expect(NftId.fromString("0.0.5005/1234").toString()).toMatchSnapshot(); - } - - @Test - void toFromString() { - var id1 = NftId.fromString("0.0.5005@1234"); - var id2 = NftId.fromString(id1.toString()); - assertThat(id2.toString()).isEqualTo(id1.toString()); - } - - @Test - void fromStringWithChecksumOnMainnet() { - SnapshotMatcher.expect(NftId.fromString("0.0.123-vfmkw/7584").toStringWithChecksum(mainnetClient)).toMatchSnapshot(); - } - - @Test - void fromStringWithChecksumOnTestnet() { - SnapshotMatcher.expect(NftId.fromString("0.0.123-esxsf@584903").toStringWithChecksum(testnetClient)).toMatchSnapshot(); - } - - @Test - void fromStringWithChecksumOnPreviewnet() { - SnapshotMatcher.expect(NftId.fromString("0.0.123-ogizo/487302").toStringWithChecksum(previewnetClient)).toMatchSnapshot(); - } - - @Test - void toBytes() throws InvalidProtocolBufferException { - SnapshotMatcher.expect(Hex.toHexString(new TokenId(5005).nft(4920).toBytes())).toMatchSnapshot(); - } - - @Test - void fromBytes() throws InvalidProtocolBufferException { - SnapshotMatcher.expect(NftId.fromBytes(new TokenId(5005).nft(574489).toBytes()).toString()).toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/NftIdTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/NftIdTest.snap deleted file mode 100644 index 4a275b7e4e..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/NftIdTest.snap +++ /dev/null @@ -1,33 +0,0 @@ -com.hedera.hashgraph.sdk.NftIdTest.fromBytes=[ - "0.0.5005/574489" -] - - -com.hedera.hashgraph.sdk.NftIdTest.fromString2=[ - "0.0.5005/1234" -] - - -com.hedera.hashgraph.sdk.NftIdTest.fromString=[ - "0.0.5005/1234" -] - - -com.hedera.hashgraph.sdk.NftIdTest.fromStringWithChecksumOnMainnet=[ - "0.0.123-vfmkw/7584" -] - - -com.hedera.hashgraph.sdk.NftIdTest.fromStringWithChecksumOnPreviewnet=[ - "0.0.123-ogizo/487302" -] - - -com.hedera.hashgraph.sdk.NftIdTest.fromStringWithChecksumOnTestnet=[ - "0.0.123-esxsf/584903" -] - - -com.hedera.hashgraph.sdk.NftIdTest.toBytes=[ - "0a03188d2710b826" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/NodeCreateTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/NodeCreateTransactionTest.java deleted file mode 100644 index b08f4f90bd..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/NodeCreateTransactionTest.java +++ /dev/null @@ -1,269 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import com.google.protobuf.ByteString; -import com.hedera.hashgraph.sdk.proto.NodeCreateTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import java.time.Instant; -import java.util.Arrays; -import java.util.List; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -public class NodeCreateTransactionTest { - - private static final PrivateKey TEST_PRIVATE_KEY = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - private static final AccountId TEST_ACCOUNT_ID = AccountId.fromString("0.6.9"); - - private static final String TEST_DESCRIPTION = "Test description"; - - private static final List TEST_GOSSIP_ENDPOINTS = List.of( - spawnTestEndpoint((byte) 0), - spawnTestEndpoint((byte) 1), - spawnTestEndpoint((byte) 2) - ); - - private static final List TEST_SERVICE_ENDPOINTS = List.of( - spawnTestEndpoint((byte) 3), - spawnTestEndpoint((byte) 4), - spawnTestEndpoint((byte) 5), - spawnTestEndpoint((byte) 6) - ); - - private static final byte[] TEST_GOSSIP_CA_CERTIFICATE = new byte[]{0, 1, 2, 3, 4}; - - private static final byte[] TEST_GRPC_CERTIFICATE_HASH = new byte[]{5, 6, 7, 8, 9}; - - private static final PublicKey TEST_ADMIN_KEY = PrivateKey.fromString( - "302e020100300506032b65700422042062c4b69e9f45a554e5424fb5a6fe5e6ac1f19ead31dc7718c2d980fd1f998d4b") - .getPublicKey(); - - final Instant TEST_VALID_START = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); - } - - private static Endpoint spawnTestEndpoint(byte offset) { - return new Endpoint() - .setAddress(new byte[]{0x00, 0x01, 0x02, 0x03}) - .setDomainName(offset + "unit.test.com") - .setPort(42 + offset); - } - - private NodeCreateTransaction spawnTestTransaction() { - return new NodeCreateTransaction() - .setNodeAccountIds( - Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), TEST_VALID_START)) - .setAccountId(TEST_ACCOUNT_ID) - .setDescription(TEST_DESCRIPTION) - .setGossipEndpoints(TEST_GOSSIP_ENDPOINTS) - .setServiceEndpoints(TEST_SERVICE_ENDPOINTS) - .setGossipCaCertificate(TEST_GOSSIP_CA_CERTIFICATE) - .setGrpcCertificateHash(TEST_GRPC_CERTIFICATE_HASH) - .setAdminKey(TEST_ADMIN_KEY) - .setMaxTransactionFee(new Hbar(1)) - .freeze() - .sign(TEST_PRIVATE_KEY); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = NodeCreateTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setNodeCreate(NodeCreateTransactionBody.newBuilder().build()).build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(NodeCreateTransaction.class); - } - - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new NodeCreateTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void testUnrecognizedServicePort() throws Exception { - var tx = new NodeCreateTransaction() - .setServiceEndpoints( - List.of(new Endpoint() - .setAddress(new byte[]{0x00, 0x01, 0x02, 0x03}) - .setDomainName("unit.test.com") - .setPort(50111))); - var tx2 = NodeCreateTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void testSetNull() { - new NodeCreateTransaction() - .setDescription(null) - .setAccountId(null) - .setGossipCaCertificate(null) - .setGrpcCertificateHash(null) - .setAdminKey(null); - } - - @Test - void constructNodeCreateTransactionFromTransactionBodyProtobuf() { - var transactionBodyBuilder = NodeCreateTransactionBody.newBuilder(); - - transactionBodyBuilder.setAccountId(TEST_ACCOUNT_ID.toProtobuf()); - transactionBodyBuilder.setDescription(TEST_DESCRIPTION); - - for (Endpoint gossipEndpoint : TEST_GOSSIP_ENDPOINTS) { - transactionBodyBuilder.addGossipEndpoint(gossipEndpoint.toProtobuf()); - } - - for (Endpoint serviceEndpoint : TEST_SERVICE_ENDPOINTS) { - transactionBodyBuilder.addServiceEndpoint(serviceEndpoint.toProtobuf()); - } - - transactionBodyBuilder.setGossipCaCertificate(ByteString.copyFrom(TEST_GOSSIP_CA_CERTIFICATE)); - transactionBodyBuilder.setGrpcCertificateHash(ByteString.copyFrom(TEST_GRPC_CERTIFICATE_HASH)); - transactionBodyBuilder.setAdminKey(TEST_ADMIN_KEY.toProtobufKey()); - - var tx = TransactionBody.newBuilder().setNodeCreate(transactionBodyBuilder.build()).build(); - var nodeCreateTransaction = new NodeCreateTransaction(tx); - - assertThat(nodeCreateTransaction.getAccountId()).isEqualTo(TEST_ACCOUNT_ID); - assertThat(nodeCreateTransaction.getDescription()).isEqualTo(TEST_DESCRIPTION); - assertThat(nodeCreateTransaction.getGossipEndpoints()).hasSize(TEST_GOSSIP_ENDPOINTS.size()); - assertThat(nodeCreateTransaction.getServiceEndpoints()).hasSize(TEST_SERVICE_ENDPOINTS.size()); - assertThat(nodeCreateTransaction.getGossipCaCertificate()).isEqualTo(TEST_GOSSIP_CA_CERTIFICATE); - assertThat(nodeCreateTransaction.getGrpcCertificateHash()).isEqualTo(TEST_GRPC_CERTIFICATE_HASH); - assertThat(nodeCreateTransaction.getAdminKey()).isEqualTo(TEST_ADMIN_KEY); - } - - @Test - void getSetAccountId() { - var nodeCreateTransaction = new NodeCreateTransaction().setAccountId(TEST_ACCOUNT_ID); - assertThat(nodeCreateTransaction.getAccountId()).isEqualTo(TEST_ACCOUNT_ID); - } - - @Test - void getSetAccountIdFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setAccountId(TEST_ACCOUNT_ID)); - } - - @Test - void getSetDescription() { - var nodeCreateTransaction = new NodeCreateTransaction().setDescription(TEST_DESCRIPTION); - assertThat(nodeCreateTransaction.getDescription()).isEqualTo(TEST_DESCRIPTION); - } - - @Test - void getSetDescriptionFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setDescription(TEST_DESCRIPTION)); - } - - @Test - void getSetGossipEndpoints() { - var nodeCreateTransaction = new NodeCreateTransaction().setGossipEndpoints(TEST_GOSSIP_ENDPOINTS); - assertThat(nodeCreateTransaction.getGossipEndpoints()).isEqualTo(TEST_GOSSIP_ENDPOINTS); - } - - @Test - void setTestGossipEndpointsFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setGossipEndpoints(TEST_GOSSIP_ENDPOINTS)); - } - - @Test - void getSetServiceEndpoints() { - var nodeCreateTransaction = new NodeCreateTransaction().setServiceEndpoints(TEST_SERVICE_ENDPOINTS); - assertThat(nodeCreateTransaction.getServiceEndpoints()).isEqualTo(TEST_SERVICE_ENDPOINTS); - } - - @Test - void getSetServiceEndpointsFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setServiceEndpoints(TEST_SERVICE_ENDPOINTS)); - } - - @Test - void getSetGossipCaCertificate() { - var nodeCreateTransaction = new NodeCreateTransaction().setGossipCaCertificate(TEST_GOSSIP_CA_CERTIFICATE); - assertThat(nodeCreateTransaction.getGossipCaCertificate()).isEqualTo(TEST_GOSSIP_CA_CERTIFICATE); - } - - @Test - void getSetGossipCaCertificateFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setGossipCaCertificate(TEST_GOSSIP_CA_CERTIFICATE)); - } - - @Test - void getSetGrpcCertificateHash() { - var nodeCreateTransaction = new NodeCreateTransaction().setGrpcCertificateHash(TEST_GRPC_CERTIFICATE_HASH); - assertThat(nodeCreateTransaction.getGrpcCertificateHash()).isEqualTo(TEST_GRPC_CERTIFICATE_HASH); - } - - @Test - void getSetGrpcCertificateHashFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setGrpcCertificateHash(TEST_GRPC_CERTIFICATE_HASH)); - } - - @Test - void getSetAdminKey() { - var nodeCreateTransaction = new NodeCreateTransaction().setAdminKey(TEST_ADMIN_KEY); - assertThat(nodeCreateTransaction.getAdminKey()).isEqualTo(TEST_ADMIN_KEY); - } - - @Test - void getSetAdminKeyFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setAdminKey(TEST_ADMIN_KEY)); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/NodeCreateTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/NodeCreateTransactionTest.snap deleted file mode 100644 index d8436dd0b3..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/NodeCreateTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.NodeCreateTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\nnode_create {\n account_id {\n account_num: 9\n realm_num: 6\n shard_num: 0\n }\n admin_key {\n ed25519: \"\\030\\214\\252`\\231\\024#O\\b\\370\\342\\232\\321g\\274\\273\\346\\221}\\211m\\244R\\306\\017\\230\\017j,\\246\\206\\001\"\n }\n description: \"Test description\"\n gossip_ca_certificate: \"\\000\\001\\002\\003\\004\"\n gossip_endpoint {\n domain_name: \"0unit.test.com\"\n ip_address_v4: \"\\000\\001\\002\\003\"\n port: 42\n }\n gossip_endpoint {\n domain_name: \"1unit.test.com\"\n ip_address_v4: \"\\000\\001\\002\\003\"\n port: 43\n }\n gossip_endpoint {\n domain_name: \"2unit.test.com\"\n ip_address_v4: \"\\000\\001\\002\\003\"\n port: 44\n }\n grpc_certificate_hash: \"\\005\\006\\a\\b\\t\"\n service_endpoint {\n domain_name: \"3unit.test.com\"\n ip_address_v4: \"\\000\\001\\002\\003\"\n port: 45\n }\n service_endpoint {\n domain_name: \"4unit.test.com\"\n ip_address_v4: \"\\000\\001\\002\\003\"\n port: 46\n }\n service_endpoint {\n domain_name: \"5unit.test.com\"\n ip_address_v4: \"\\000\\001\\002\\003\"\n port: 47\n }\n service_endpoint {\n domain_name: \"6unit.test.com\"\n ip_address_v4: \"\\000\\001\\002\\003\"\n port: 48\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/NodeDeleteTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/NodeDeleteTransactionTest.java deleted file mode 100644 index 9aff4aaf79..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/NodeDeleteTransactionTest.java +++ /dev/null @@ -1,117 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import com.hedera.hashgraph.sdk.proto.NodeDeleteTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import java.time.Instant; -import java.util.Arrays; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -public class NodeDeleteTransactionTest { - - private static final PrivateKey TEST_PRIVATE_KEY = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - private static final long TEST_NODE_ID = 420; - - final Instant TEST_VALID_START = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); - } - - private NodeDeleteTransaction spawnTestTransaction() { - return new NodeDeleteTransaction() - .setNodeAccountIds( - Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), TEST_VALID_START)) - .setNodeId(TEST_NODE_ID) - .setMaxTransactionFee(new Hbar(1)) - .freeze() - .sign(TEST_PRIVATE_KEY); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = NodeDeleteTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new NodeDeleteTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setNodeDelete(NodeDeleteTransactionBody.newBuilder().build()).build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(NodeDeleteTransaction.class); - } - - @Test - void constructNodeDeleteTransactionFromTransactionBodyProtobuf() { - var transactionBodyBuilder = NodeDeleteTransactionBody.newBuilder(); - - transactionBodyBuilder.setNodeId(TEST_NODE_ID); - - var tx = TransactionBody.newBuilder().setNodeDelete(transactionBodyBuilder.build()).build(); - var nodeDeleteTransaction = new NodeDeleteTransaction(tx); - - assertThat(nodeDeleteTransaction.getNodeId()).isEqualTo(TEST_NODE_ID); - } - - @Test - void getSetNodeId() { - var nodeDeleteTransaction = new NodeDeleteTransaction().setNodeId(TEST_NODE_ID); - assertThat(nodeDeleteTransaction.getNodeId()).isEqualTo(TEST_NODE_ID); - } - - @Test - void getSetNodeIdFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setNodeId(TEST_NODE_ID)); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/NodeDeleteTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/NodeDeleteTransactionTest.snap deleted file mode 100644 index 456b6ad72c..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/NodeDeleteTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.NodeDeleteTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\nnode_delete {\n node_id: 420\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/NodeUpdateTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/NodeUpdateTransactionTest.java deleted file mode 100644 index 3ce37b29dc..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/NodeUpdateTransactionTest.java +++ /dev/null @@ -1,298 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import com.google.protobuf.ByteString; -import com.google.protobuf.BytesValue; -import com.google.protobuf.StringValue; -import com.hedera.hashgraph.sdk.proto.NodeUpdateTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import java.time.Instant; -import java.util.Arrays; -import java.util.List; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -public class NodeUpdateTransactionTest { - - private static final PrivateKey TEST_PRIVATE_KEY = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - private static final long TEST_NODE_ID = 420; - - private static final AccountId TEST_ACCOUNT_ID = AccountId.fromString("0.6.9"); - - private static final String TEST_DESCRIPTION = "Test description"; - - private static final List TEST_GOSSIP_ENDPOINTS = List.of( - spawnTestEndpoint((byte) 0), - spawnTestEndpoint((byte) 1), - spawnTestEndpoint((byte) 2) - ); - - private static final List TEST_SERVICE_ENDPOINTS = List.of( - spawnTestEndpoint((byte) 3), - spawnTestEndpoint((byte) 4), - spawnTestEndpoint((byte) 5), - spawnTestEndpoint((byte) 6) - ); - - private static final byte[] TEST_GOSSIP_CA_CERTIFICATE = new byte[]{0, 1, 2, 3, 4}; - - private static final byte[] TEST_GRPC_CERTIFICATE_HASH = new byte[]{5, 6, 7, 8, 9}; - - private static final PublicKey TEST_ADMIN_KEY = PrivateKey.fromString( - "302e020100300506032b65700422042062c4b69e9f45a554e5424fb5a6fe5e6ac1f19ead31dc7718c2d980fd1f998d4b") - .getPublicKey(); - - final Instant TEST_VALID_START = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); - } - - private static Endpoint spawnTestEndpoint(byte offset) { - return new Endpoint() - .setAddress(new byte[]{0x00, 0x01, 0x02, 0x03}) - .setDomainName(offset + "unit.test.com") - .setPort(42 + offset); - } - - private NodeUpdateTransaction spawnTestTransaction() { - return new NodeUpdateTransaction() - .setNodeAccountIds( - Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), TEST_VALID_START)) - .setNodeId(TEST_NODE_ID) - .setAccountId(TEST_ACCOUNT_ID) - .setDescription(TEST_DESCRIPTION) - .setGossipEndpoints(TEST_GOSSIP_ENDPOINTS) - .setServiceEndpoints(TEST_SERVICE_ENDPOINTS) - .setGossipCaCertificate(TEST_GOSSIP_CA_CERTIFICATE) - .setGrpcCertificateHash(TEST_GRPC_CERTIFICATE_HASH) - .setAdminKey(TEST_ADMIN_KEY) - .setMaxTransactionFee(new Hbar(1)) - .freeze() - .sign(TEST_PRIVATE_KEY); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = NodeUpdateTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new NodeUpdateTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void testUnrecognizedServicePort() throws Exception { - var tx = new NodeUpdateTransaction() - .setServiceEndpoints( - List.of(new Endpoint() - .setAddress(new byte[]{0x00, 0x01, 0x02, 0x03}) - .setDomainName("unit.test.com") - .setPort(50111))); - var tx2 = NodeUpdateTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void testEmptyCertificates() throws Exception { - var tx = new NodeUpdateTransaction() - .setGossipCaCertificate(new byte[]{}) - .setGrpcCertificateHash(new byte[]{}); - var tx2Bytes = tx.toBytes(); - NodeUpdateTransaction deserializedTx = (NodeUpdateTransaction) Transaction.fromBytes(tx2Bytes); - assertThat(deserializedTx.getGossipCaCertificate()).isEqualTo(new byte[]{}); - assertThat(deserializedTx.getGrpcCertificateHash()).isEqualTo(new byte[]{}); - } - - @Test - void testSetNull() { - new NodeUpdateTransaction() - .setDescription(null) - .setAccountId(null) - .setGossipCaCertificate(null) - .setGrpcCertificateHash(null) - .setAdminKey(null); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setNodeUpdate(NodeUpdateTransactionBody.newBuilder().build()).build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(NodeUpdateTransaction.class); - } - - @Test - void constructNodeUpdateTransactionFromTransactionBodyProtobuf() { - var transactionBodyBuilder = NodeUpdateTransactionBody.newBuilder(); - - transactionBodyBuilder.setNodeId(TEST_NODE_ID); - transactionBodyBuilder.setAccountId(TEST_ACCOUNT_ID.toProtobuf()); - transactionBodyBuilder.setDescription(StringValue.of(TEST_DESCRIPTION)); - - for (Endpoint gossipEndpoint : TEST_GOSSIP_ENDPOINTS) { - transactionBodyBuilder.addGossipEndpoint(gossipEndpoint.toProtobuf()); - } - - for (Endpoint serviceEndpoint : TEST_SERVICE_ENDPOINTS) { - transactionBodyBuilder.addServiceEndpoint(serviceEndpoint.toProtobuf()); - } - - transactionBodyBuilder.setGossipCaCertificate(BytesValue.of(ByteString.copyFrom(TEST_GOSSIP_CA_CERTIFICATE))); - transactionBodyBuilder.setGrpcCertificateHash(BytesValue.of(ByteString.copyFrom(TEST_GRPC_CERTIFICATE_HASH))); - transactionBodyBuilder.setAdminKey(TEST_ADMIN_KEY.toProtobufKey()); - - var tx = TransactionBody.newBuilder().setNodeUpdate(transactionBodyBuilder.build()).build(); - var nodeUpdateTransaction = new NodeUpdateTransaction(tx); - - assertThat(nodeUpdateTransaction.getNodeId()).isEqualTo(TEST_NODE_ID); - assertThat(nodeUpdateTransaction.getAccountId()).isEqualTo(TEST_ACCOUNT_ID); - assertThat(nodeUpdateTransaction.getDescription()).isEqualTo(TEST_DESCRIPTION); - assertThat(nodeUpdateTransaction.getGossipEndpoints()).hasSize(TEST_GOSSIP_ENDPOINTS.size()); - assertThat(nodeUpdateTransaction.getServiceEndpoints()).hasSize(TEST_SERVICE_ENDPOINTS.size()); - assertThat(nodeUpdateTransaction.getGossipCaCertificate()).isEqualTo(TEST_GOSSIP_CA_CERTIFICATE); - assertThat(nodeUpdateTransaction.getGrpcCertificateHash()).isEqualTo(TEST_GRPC_CERTIFICATE_HASH); - assertThat(nodeUpdateTransaction.getAdminKey()).isEqualTo(TEST_ADMIN_KEY); - } - - @Test - void getSetNodeId() { - var nodeUpdateTransaction = new NodeUpdateTransaction().setNodeId(TEST_NODE_ID); - assertThat(nodeUpdateTransaction.getNodeId()).isEqualTo(TEST_NODE_ID); - } - - @Test - void getSetNodeIdFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setNodeId(TEST_NODE_ID)); - } - - @Test - void getSetAccountId() { - var nodeUpdateTransaction = new NodeUpdateTransaction().setAccountId(TEST_ACCOUNT_ID); - assertThat(nodeUpdateTransaction.getAccountId()).isEqualTo(TEST_ACCOUNT_ID); - } - - @Test - void getSetAccountIdFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setAccountId(TEST_ACCOUNT_ID)); - } - - @Test - void getSetDescription() { - var nodeUpdateTransaction = new NodeUpdateTransaction().setDescription(TEST_DESCRIPTION); - assertThat(nodeUpdateTransaction.getDescription()).isEqualTo(TEST_DESCRIPTION); - } - - @Test - void getSetDescriptionFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setDescription(TEST_DESCRIPTION)); - } - - @Test - void getSetGossipEndpoints() { - var nodeUpdateTransaction = new NodeUpdateTransaction().setGossipEndpoints(TEST_GOSSIP_ENDPOINTS); - assertThat(nodeUpdateTransaction.getGossipEndpoints()).isEqualTo(TEST_GOSSIP_ENDPOINTS); - } - - @Test - void setTestGossipEndpointsFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setGossipEndpoints(TEST_GOSSIP_ENDPOINTS)); - } - - @Test - void getSetServiceEndpoints() { - var nodeUpdateTransaction = new NodeUpdateTransaction().setServiceEndpoints(TEST_SERVICE_ENDPOINTS); - assertThat(nodeUpdateTransaction.getServiceEndpoints()).isEqualTo(TEST_SERVICE_ENDPOINTS); - } - - @Test - void getSetServiceEndpointsFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setServiceEndpoints(TEST_SERVICE_ENDPOINTS)); - } - - @Test - void getSetGossipCaCertificate() { - var nodeUpdateTransaction = new NodeUpdateTransaction().setGossipCaCertificate(TEST_GOSSIP_CA_CERTIFICATE); - assertThat(nodeUpdateTransaction.getGossipCaCertificate()).isEqualTo(TEST_GOSSIP_CA_CERTIFICATE); - } - - @Test - void getSetGossipCaCertificateFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setGossipCaCertificate(TEST_GOSSIP_CA_CERTIFICATE)); - } - - @Test - void getSetGrpcCertificateHash() { - var nodeUpdateTransaction = new NodeUpdateTransaction().setGrpcCertificateHash(TEST_GRPC_CERTIFICATE_HASH); - assertThat(nodeUpdateTransaction.getGrpcCertificateHash()).isEqualTo(TEST_GRPC_CERTIFICATE_HASH); - } - - @Test - void getSetGrpcCertificateHashFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setGrpcCertificateHash(TEST_GRPC_CERTIFICATE_HASH)); - } - - @Test - void getSetAdminKey() { - var nodeUpdateTransaction = new NodeUpdateTransaction().setAdminKey(TEST_ADMIN_KEY); - assertThat(nodeUpdateTransaction.getAdminKey()).isEqualTo(TEST_ADMIN_KEY); - } - - @Test - void getSetAdminKeyFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setAdminKey(TEST_ADMIN_KEY)); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/NodeUpdateTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/NodeUpdateTransactionTest.snap deleted file mode 100644 index cb5159253f..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/NodeUpdateTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.NodeUpdateTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\nnode_update {\n account_id {\n account_num: 9\n realm_num: 6\n shard_num: 0\n }\n admin_key {\n ed25519: \"\\030\\214\\252`\\231\\024#O\\b\\370\\342\\232\\321g\\274\\273\\346\\221}\\211m\\244R\\306\\017\\230\\017j,\\246\\206\\001\"\n }\n description {\n value: \"Test description\"\n }\n gossip_ca_certificate {\n value: \"\\000\\001\\002\\003\\004\"\n }\n gossip_endpoint {\n domain_name: \"0unit.test.com\"\n ip_address_v4: \"\\000\\001\\002\\003\"\n port: 42\n }\n gossip_endpoint {\n domain_name: \"1unit.test.com\"\n ip_address_v4: \"\\000\\001\\002\\003\"\n port: 43\n }\n gossip_endpoint {\n domain_name: \"2unit.test.com\"\n ip_address_v4: \"\\000\\001\\002\\003\"\n port: 44\n }\n grpc_certificate_hash {\n value: \"\\005\\006\\a\\b\\t\"\n }\n node_id: 420\n service_endpoint {\n domain_name: \"3unit.test.com\"\n ip_address_v4: \"\\000\\001\\002\\003\"\n port: 45\n }\n service_endpoint {\n domain_name: \"4unit.test.com\"\n ip_address_v4: \"\\000\\001\\002\\003\"\n port: 46\n }\n service_endpoint {\n domain_name: \"5unit.test.com\"\n ip_address_v4: \"\\000\\001\\002\\003\"\n port: 47\n }\n service_endpoint {\n domain_name: \"6unit.test.com\"\n ip_address_v4: \"\\000\\001\\002\\003\"\n port: 48\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/PendingAirdropIdTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/PendingAirdropIdTest.java deleted file mode 100644 index 8ecbdc6f16..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/PendingAirdropIdTest.java +++ /dev/null @@ -1,163 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -class PendingAirdropIdTest { - private AccountId sender; - private AccountId receiver; - private TokenId tokenId; - private NftId nftId; - - @BeforeEach - void setUp() { - sender = new AccountId(1001); - receiver = new AccountId(1002); - tokenId = new TokenId(1003); - nftId = new NftId(new TokenId(1004), 1); - } - - @Test - void testConstructorWithTokenId() { - PendingAirdropId pendingAirdropId = new PendingAirdropId(sender, receiver, tokenId); - - assertEquals(sender, pendingAirdropId.getSender()); - assertEquals(receiver, pendingAirdropId.getReceiver()); - assertEquals(tokenId, pendingAirdropId.getTokenId()); - assertNull(pendingAirdropId.getNftId()); - } - - @Test - void testConstructorWithNftId() { - PendingAirdropId pendingAirdropId = new PendingAirdropId(sender, receiver, nftId); - - assertEquals(sender, pendingAirdropId.getSender()); - assertEquals(receiver, pendingAirdropId.getReceiver()); - assertEquals(nftId, pendingAirdropId.getNftId()); - assertNull(pendingAirdropId.getTokenId()); - } - - @Test - void testSetSender() { - PendingAirdropId pendingAirdropId = new PendingAirdropId(); - pendingAirdropId.setSender(sender); - - assertEquals(sender, pendingAirdropId.getSender()); - } - - @Test - void testSetReceiver() { - PendingAirdropId pendingAirdropId = new PendingAirdropId(); - pendingAirdropId.setReceiver(receiver); - - assertEquals(receiver, pendingAirdropId.getReceiver()); - } - - @Test - void testSetTokenId() { - PendingAirdropId pendingAirdropId = new PendingAirdropId(); - pendingAirdropId.setTokenId(tokenId); - - assertEquals(tokenId, pendingAirdropId.getTokenId()); - } - - @Test - void testSetNftId() { - PendingAirdropId pendingAirdropId = new PendingAirdropId(); - pendingAirdropId.setNftId(nftId); - - assertEquals(nftId, pendingAirdropId.getNftId()); - } - - @Test - void testToProtobufWithTokenId() { - PendingAirdropId pendingAirdropId = new PendingAirdropId(sender, receiver, tokenId); - com.hedera.hashgraph.sdk.proto.PendingAirdropId proto = pendingAirdropId.toProtobuf(); - - assertNotNull(proto); - assertEquals(sender.toProtobuf(), proto.getSenderId()); - assertEquals(receiver.toProtobuf(), proto.getReceiverId()); - assertEquals(tokenId.toProtobuf(), proto.getFungibleTokenType()); - } - - @Test - void testToProtobufWithNftId() { - PendingAirdropId pendingAirdropId = new PendingAirdropId(sender, receiver, nftId); - com.hedera.hashgraph.sdk.proto.PendingAirdropId proto = pendingAirdropId.toProtobuf(); - - assertNotNull(proto); - assertEquals(sender.toProtobuf(), proto.getSenderId()); - assertEquals(receiver.toProtobuf(), proto.getReceiverId()); - assertEquals(nftId.toProtobuf(), proto.getNonFungibleToken()); - } - - @Test - void testFromProtobufWithTokenId() { - com.hedera.hashgraph.sdk.proto.PendingAirdropId proto = com.hedera.hashgraph.sdk.proto.PendingAirdropId.newBuilder() - .setSenderId(sender.toProtobuf()) - .setReceiverId(receiver.toProtobuf()) - .setFungibleTokenType(tokenId.toProtobuf()) - .build(); - - PendingAirdropId pendingAirdropId = PendingAirdropId.fromProtobuf(proto); - - assertNotNull(pendingAirdropId); - assertEquals(sender, pendingAirdropId.getSender()); - assertEquals(receiver, pendingAirdropId.getReceiver()); - assertEquals(tokenId, pendingAirdropId.getTokenId()); - assertNull(pendingAirdropId.getNftId()); - } - - @Test - void testFromProtobufWithNftId() { - com.hedera.hashgraph.sdk.proto.PendingAirdropId proto = com.hedera.hashgraph.sdk.proto.PendingAirdropId.newBuilder() - .setSenderId(sender.toProtobuf()) - .setReceiverId(receiver.toProtobuf()) - .setNonFungibleToken(nftId.toProtobuf()) - .build(); - - PendingAirdropId pendingAirdropId = PendingAirdropId.fromProtobuf(proto); - - assertNotNull(pendingAirdropId); - assertEquals(sender, pendingAirdropId.getSender()); - assertEquals(receiver, pendingAirdropId.getReceiver()); - assertEquals(nftId, pendingAirdropId.getNftId()); - assertNull(pendingAirdropId.getTokenId()); - } - - @Test - void testToString() { - PendingAirdropId pendingAirdropId = new PendingAirdropId(sender, receiver, tokenId); - String result = pendingAirdropId.toString(); - - assertTrue(result.contains("sender")); - assertTrue(result.contains("receiver")); - assertTrue(result.contains("tokenId")); - assertTrue(result.contains("nftId")); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/PrivateKeyTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/PrivateKeyTest.java deleted file mode 100644 index d527c33362..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/PrivateKeyTest.java +++ /dev/null @@ -1,68 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import org.bouncycastle.util.encoders.Hex; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.time.Instant; -import java.util.Collections; - -import static org.assertj.core.api.Assertions.assertThat; - -public class PrivateKeyTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @Test - void signTransactionWorks() throws InvalidProtocolBufferException { - byte[] bytes = new AccountCreateTransaction() - .setNodeAccountIds(Collections.singletonList(AccountId.fromString("0.0.5005"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setKey(unusedPrivateKey) - .setInitialBalance(Hbar.fromTinybars(450)) - .setProxyAccountId(AccountId.fromString("0.0.1001")) - .setReceiverSignatureRequired(true) - .setMaxTransactionFee(Hbar.fromTinybars(100_000)) - .freeze() - .toBytes(); - - AccountCreateTransaction transaction = (AccountCreateTransaction) Transaction.fromBytes(bytes); - unusedPrivateKey.signTransaction(transaction); - } - - @Test - void ecdsa() { - var message = "hello world".getBytes(StandardCharsets.UTF_8); - var key = PrivateKey.fromStringECDSA("8776c6b831a1b61ac10dac0304a2843de4716f54b1919bb91a2685d0fe3f3048"); - var signature = key.sign(message); - - assertThat(Hex.toHexString(signature)).isEqualTo("f3a13a555f1f8cd6532716b8f388bd4e9d8ed0b252743e923114c0c6cbfe414cf791c8e859afd3c12009ecf2cb20dacf01636d80823bcdbd9ec1ce59afe008f0"); - } - - @Test - void supports0xPrefix() { - PrivateKey.fromString("0x8776c6b831a1b61ac10dac0304a2843de4716f54b1919bb91a2685d0fe3f3048"); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/PrngTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/PrngTransactionTest.java deleted file mode 100644 index 948076080a..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/PrngTransactionTest.java +++ /dev/null @@ -1,79 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; - -public class PrngTransactionTest { - - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - private PrngTransaction spawnTestTransaction() { - return new PrngTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setMaxTransactionFee(Hbar.fromTinybars(100_000)) - .freeze() - .sign(unusedPrivateKey); - } - - private PrngTransaction spawnTestTransaction2() { - return new PrngTransaction() - .setRange(100) - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setMaxTransactionFee(Hbar.fromTinybars(100_000)) - .freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction() - .toString() - ).toMatchSnapshot(); - } - - @Test - void shouldSerialize2() { - SnapshotMatcher.expect(spawnTestTransaction2() - .toString() - ).toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/PrngTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/PrngTransactionTest.snap deleted file mode 100644 index c5a7b90f52..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/PrngTransactionTest.snap +++ /dev/null @@ -1,8 +0,0 @@ -com.hedera.hashgraph.sdk.PrngTransactionTest.shouldSerialize2=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}\nutil_prng {\n range: 100\n}" -] - - -com.hedera.hashgraph.sdk.PrngTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}\nutil_prng {\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ProxyStakerTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/ProxyStakerTest.java deleted file mode 100644 index 0ab2f70eb9..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ProxyStakerTest.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.hedera.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.ProxyStaker; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -public class ProxyStakerTest { - private static final ProxyStaker proxyStaker = ProxyStaker.newBuilder() - .setAccountID(new AccountId(100).toProtobuf()) - .setAmount(10) - .build(); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void fromProtobuf() { - SnapshotMatcher.expect(com.hedera.hashgraph.sdk.ProxyStaker.fromProtobuf(proxyStaker).toString()) - .toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ProxyStakerTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/ProxyStakerTest.snap deleted file mode 100644 index 3b28ea5a55..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ProxyStakerTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.ProxyStakerTest.fromProtobuf=[ - "ProxyStaker{accountId=0.0.100, amount=10 tℏ}" -] diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ReceiptStatusExceptionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/ReceiptStatusExceptionTest.java deleted file mode 100644 index 650814f086..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ReceiptStatusExceptionTest.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.hedera.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.ResponseCodeEnum; -import org.junit.jupiter.api.Test; - -import java.time.Instant; - -import static org.assertj.core.api.Assertions.assertThat; - -class ReceiptStatusExceptionTest { - - @Test - void shouldHaveMessage() { - var validStart = Instant.ofEpochSecond(1554158542); - var txId = new TransactionId(new AccountId(0, 0, 100), validStart); - var txReceipt = TransactionReceipt.fromProtobuf( - com.hedera.hashgraph.sdk.proto.TransactionReceipt - .newBuilder() - .setStatusValue(ResponseCodeEnum.INSUFFICIENT_TX_FEE_VALUE) - .build()); - var e = new ReceiptStatusException( - txId, - txReceipt - ); - - assertThat(e.getMessage()).isEqualTo( - "receipt for transaction 0.0.100@1554158542.000000000 raised status INSUFFICIENT_TX_FEE" - ); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/RegenerateTransactionIdsTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/RegenerateTransactionIdsTest.java deleted file mode 100644 index 7bd7ccaa21..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/RegenerateTransactionIdsTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.ResponseCodeEnum; -import com.hedera.hashgraph.sdk.proto.SignedTransaction; -import com.hedera.hashgraph.sdk.proto.Transaction; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import io.grpc.Status; -import org.junit.jupiter.api.Test; - -import java.util.HashSet; -import java.util.List; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicInteger; - -public class RegenerateTransactionIdsTest { - @Test - void regeneratesTransactionIdsWhenTransactionExpiredIsReturned() throws PrecheckStatusException, TimeoutException, InterruptedException { - var transactionIds = new HashSet(); - AtomicInteger count = new AtomicInteger(0); - - var responses = List.of( - TransactionResponse.newBuilder().setNodeTransactionPrecheckCode(ResponseCodeEnum.TRANSACTION_EXPIRED).build(), - TransactionResponse.newBuilder().setNodeTransactionPrecheckCode(ResponseCodeEnum.TRANSACTION_EXPIRED).build(), - TransactionResponse.newBuilder().setNodeTransactionPrecheckCode(ResponseCodeEnum.TRANSACTION_EXPIRED).build(), - TransactionResponse.newBuilder().setNodeTransactionPrecheckCode(ResponseCodeEnum.OK).build() - ); - - var call = (Function) o -> { - try { - var transaction = (Transaction) o; - var signedTransaction = SignedTransaction.parseFrom(transaction.getSignedTransactionBytes()); - var transactionBody = TransactionBody.parseFrom(signedTransaction.getBodyBytes()); - var transactionId = TransactionId.fromProtobuf(transactionBody.getTransactionID()); - - if (transactionIds.contains(transactionId)) { - return Status.Code.ABORTED.toStatus().asRuntimeException(); - } - - transactionIds.add(transactionId); - - return responses.get(count.getAndIncrement()); - } catch (Throwable e) { - return new RuntimeException(e); - } - }; - - List responses1 = List.of( - call, call, call, call - ); - - try (var mocker = Mocker.withResponses(List.of(responses1))) { - new FileCreateTransaction().execute(mocker.client); - } - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/RequestTypeTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/RequestTypeTest.java deleted file mode 100644 index 13b2f92c0d..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/RequestTypeTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.hedera.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.HederaFunctionality; -import org.junit.jupiter.api.Test; - -import java.util.Map; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import static org.assertj.core.api.Assertions.assertThat; - -class RequestTypeTest { - - @Test - void valueOf() { - var codeValues = HederaFunctionality.values(); - var requestTypeValues = RequestType.values(); - var pair = IntStream.range(0, codeValues.length-1) - .mapToObj(i -> Map.entry(codeValues[i], requestTypeValues[i])) - .collect(Collectors.toList()); - - pair.forEach((a) -> { - var code = a.getKey(); - var requestType = a.getValue(); - assertThat(RequestType.valueOf(code)).hasToString(requestType.toString()); - }); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ScheduleCreateTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/ScheduleCreateTransactionTest.java deleted file mode 100644 index 2eae10a1f2..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ScheduleCreateTransactionTest.java +++ /dev/null @@ -1,85 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; - -public class ScheduleCreateTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction() - .toString() - ).toMatchSnapshot(); - } - - private ScheduleCreateTransaction spawnTestTransaction() { - var transferTransaction = new TransferTransaction() - .addHbarTransfer(AccountId.fromString("0.0.555"), new Hbar(-10)) - .addHbarTransfer(AccountId.fromString("0.0.333"), new Hbar(10)); - return transferTransaction.schedule() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setAdminKey(unusedPrivateKey) - .setPayerAccountId(AccountId.fromString("0.0.222")) - .setScheduleMemo("hi") - .setMaxTransactionFee(new Hbar(1)) - .setExpirationTime(validStart) - .freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = ScheduleCreateTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new ScheduleCreateTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ScheduleCreateTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/ScheduleCreateTransactionTest.snap deleted file mode 100644 index 7099d66c6c..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ScheduleCreateTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.ScheduleCreateTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\nschedule_create {\n admin_key {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n expiration_time {\n seconds: 1554158542\n }\n memo: \"hi\"\n payer_account_i_d {\n account_num: 222\n realm_num: 0\n shard_num: 0\n }\n scheduled_transaction_body {\n crypto_transfer {\n transfers {\n account_amounts {\n account_i_d {\n account_num: 333\n realm_num: 0\n shard_num: 0\n }\n amount: 1000000000\n }\n account_amounts {\n account_i_d {\n account_num: 555\n realm_num: 0\n shard_num: 0\n }\n amount: -1000000000\n }\n }\n }\n transaction_fee: 100000000\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ScheduleDeleteTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/ScheduleDeleteTransactionTest.java deleted file mode 100644 index 9d61c9c15f..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ScheduleDeleteTransactionTest.java +++ /dev/null @@ -1,91 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.ScheduleDeleteTransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; - -public class ScheduleDeleteTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction() - .toString() - ).toMatchSnapshot(); - } - - private ScheduleDeleteTransaction spawnTestTransaction() { - return new ScheduleDeleteTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setScheduleId(ScheduleId.fromString("0.0.444")) - .setMaxTransactionFee(new Hbar(1)) - .freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = ScheduleDeleteTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new ScheduleDeleteTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setScheduleDelete(ScheduleDeleteTransactionBody.newBuilder().build()) - .build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(ScheduleDeleteTransaction.class); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ScheduleDeleteTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/ScheduleDeleteTransactionTest.snap deleted file mode 100644 index c655b94f5b..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ScheduleDeleteTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.ScheduleDeleteTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\nschedule_delete {\n schedule_i_d {\n realm_num: 0\n schedule_num: 444\n shard_num: 0\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ScheduleInfoQueryTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/ScheduleInfoQueryTest.java deleted file mode 100644 index 31d584475e..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ScheduleInfoQueryTest.java +++ /dev/null @@ -1,48 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -public class ScheduleInfoQueryTest { - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - var builder = com.hedera.hashgraph.sdk.proto.Query.newBuilder(); - new ScheduleInfoQuery() - .setScheduleId(ScheduleId.fromString("0.0.5005")) - .setMaxQueryPayment(Hbar.fromTinybars(100_000)) - .onMakeRequest(builder, QueryHeader.newBuilder().build()); - SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")).toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ScheduleInfoQueryTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/ScheduleInfoQueryTest.snap deleted file mode 100644 index cb5c62c8f3..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ScheduleInfoQueryTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.ScheduleInfoQueryTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.Query\nschedule_get_info {\n header {\n }\n schedule_i_d {\n realm_num: 0\n schedule_num: 5005\n shard_num: 0\n }\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ScheduleInfoTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/ScheduleInfoTest.java deleted file mode 100644 index 4f15f7c45b..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ScheduleInfoTest.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.hedera.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.CryptoDeleteTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; - -import static org.assertj.core.api.Assertions.assertThat; - -public class ScheduleInfoTest { - private static final PublicKey unusedPublicKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10" - ).getPublicKey(); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - ScheduleInfo spawnScheduleInfoExample() { - return new ScheduleInfo( - ScheduleId.fromString("1.2.3"), - AccountId.fromString("4.5.6"), - AccountId.fromString("2.3.4"), - SchedulableTransactionBody.newBuilder() - .setCryptoDelete(CryptoDeleteTransactionBody.newBuilder() - .setDeleteAccountID(AccountId.fromString("6.6.6").toProtobuf()).build()).build(), - KeyList.of(unusedPublicKey), - unusedPublicKey, - TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart), - "memo", - validStart, - validStart, - null, - LedgerId.TESTNET, - true - ); - } - - ScheduleInfo spawnScheduleInfoDeletedExample() { - return new ScheduleInfo( - ScheduleId.fromString("1.2.3"), - AccountId.fromString("4.5.6"), - AccountId.fromString("2.3.4"), - SchedulableTransactionBody.newBuilder() - .setCryptoDelete(CryptoDeleteTransactionBody.newBuilder() - .setDeleteAccountID(AccountId.fromString("6.6.6").toProtobuf()).build()).build(), - KeyList.of(unusedPublicKey), - unusedPublicKey, - TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart), - "memo", - validStart, - null, - validStart, - LedgerId.TESTNET, - true - ); - } - - @Test - void shouldSerialize() throws Exception { - var originalScheduleInfo = spawnScheduleInfoExample(); - byte[] scheduleInfoBytes = originalScheduleInfo.toBytes(); - var copyScheduleInfo = ScheduleInfo.fromBytes(scheduleInfoBytes); - assertThat(copyScheduleInfo.toString().replaceAll("@[A-Za-z0-9]+", "")) - .isEqualTo(originalScheduleInfo.toString().replaceAll("@[A-Za-z0-9]+", "")); - SnapshotMatcher.expect(originalScheduleInfo.toString().replaceAll("@[A-Za-z0-9]+", "")).toMatchSnapshot(); - } - - @Test - void shouldSerializeDeleted() throws Exception { - var originalScheduleInfo = spawnScheduleInfoDeletedExample(); - byte[] scheduleInfoBytes = originalScheduleInfo.toBytes(); - var copyScheduleInfo = ScheduleInfo.fromBytes(scheduleInfoBytes); - assertThat(copyScheduleInfo.toString().replaceAll("@[A-Za-z0-9]+", "")) - .isEqualTo(originalScheduleInfo.toString().replaceAll("@[A-Za-z0-9]+", "")); - SnapshotMatcher.expect(originalScheduleInfo.toString().replaceAll("@[A-Za-z0-9]+", "")).toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ScheduleSignTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/ScheduleSignTransactionTest.java deleted file mode 100644 index 6a98e11a6c..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ScheduleSignTransactionTest.java +++ /dev/null @@ -1,78 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; - -public class ScheduleSignTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction() - .toString() - ).toMatchSnapshot(); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new ScheduleSignTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - private ScheduleSignTransaction spawnTestTransaction() { - return new ScheduleSignTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setScheduleId(ScheduleId.fromString("0.0.444")) - .setMaxTransactionFee(new Hbar(1)) - .freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = ScheduleSignTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ScheduleSignTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/ScheduleSignTransactionTest.snap deleted file mode 100644 index 95b7607475..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ScheduleSignTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.ScheduleSignTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\nschedule_sign {\n schedule_i_d {\n realm_num: 0\n schedule_num: 444\n shard_num: 0\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/StakingInfoTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/StakingInfoTest.java deleted file mode 100644 index e4af2fdcd4..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/StakingInfoTest.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.hedera.hashgraph.sdk; - -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; - -import static org.assertj.core.api.Assertions.assertThat; - -public class StakingInfoTest { - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - StakingInfo spawnStakingInfoAccountExample() { - return new StakingInfo( - true, - validStart, - Hbar.from(5), - Hbar.from(10), - AccountId.fromString("1.2.3"), - null - ); - } - - StakingInfo spawnStakingInfoNodeExample() { - return new StakingInfo( - true, - validStart, - Hbar.from(5), - Hbar.from(10), - null, - 3L - ); - } - - @Test - void shouldSerializeAccount() throws Exception { - var originalStakingInfo = spawnStakingInfoAccountExample(); - byte[] stakingInfoBytes = originalStakingInfo.toBytes(); - var copyStakingInfo = StakingInfo.fromBytes(stakingInfoBytes); - assertThat(copyStakingInfo.toString().replaceAll("@[A-Za-z0-9]+", "")) - .isEqualTo(originalStakingInfo.toString().replaceAll("@[A-Za-z0-9]+", "")); - SnapshotMatcher.expect(originalStakingInfo.toString().replaceAll("@[A-Za-z0-9]+", "")).toMatchSnapshot(); - } - - @Test - void shouldSerializeNode() throws Exception { - var originalStakingInfo = spawnStakingInfoNodeExample(); - byte[] stakingInfoBytes = originalStakingInfo.toBytes(); - var copyStakingInfo = StakingInfo.fromBytes(stakingInfoBytes); - assertThat(copyStakingInfo.toString().replaceAll("@[A-Za-z0-9]+", "")) - .isEqualTo(originalStakingInfo.toString().replaceAll("@[A-Za-z0-9]+", "")); - SnapshotMatcher.expect(originalStakingInfo.toString().replaceAll("@[A-Za-z0-9]+", "")).toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/StakingInfoTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/StakingInfoTest.snap deleted file mode 100644 index 144277bc1f..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/StakingInfoTest.snap +++ /dev/null @@ -1,8 +0,0 @@ -com.hedera.hashgraph.sdk.StakingInfoTest.shouldSerializeAccount=[ - "StakingInfo{declineStakingReward=true, stakePeriodStart=2019-04-01T22:42:22Z, pendingReward=5 ℏ, stakedToMe=10 ℏ, stakedAccountId=1.2.3, stakedNodeId=null}" -] - - -com.hedera.hashgraph.sdk.StakingInfoTest.shouldSerializeNode=[ - "StakingInfo{declineStakingReward=true, stakePeriodStart=2019-04-01T22:42:22Z, pendingReward=5 ℏ, stakedToMe=10 ℏ, stakedAccountId=null, stakedNodeId=3}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/StatusTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/StatusTest.java deleted file mode 100644 index cc44244e1b..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/StatusTest.java +++ /dev/null @@ -1,53 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.ResponseCodeEnum; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -class StatusTest { - @Test - @DisplayName("Status can be constructed from any ResponseCode") - void statusToResponseCode() { - for (ResponseCodeEnum code : ResponseCodeEnum.values()) { - // not an actual value we want to handle - // this is what we're given if an unexpected value was decoded - if (code == ResponseCodeEnum.UNRECOGNIZED) { - continue; - } - - Status status = Status.valueOf(code); - - assertThat(code.getNumber()).isEqualTo(status.code.getNumber()); - } - } - - @Test - @DisplayName("Status throws on Unrecognized") - void statusUnrecognized() { - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy( - () -> Status.valueOf(ResponseCodeEnum.UNRECOGNIZED) - ).withMessage("network returned unrecognized response code; your SDK may be out of date"); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/SystemDeleteTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/SystemDeleteTransactionTest.java deleted file mode 100644 index 8020fb58e8..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/SystemDeleteTransactionTest.java +++ /dev/null @@ -1,200 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.SystemDeleteTransactionBody; -import com.hedera.hashgraph.sdk.proto.TimestampSeconds; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class SystemDeleteTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - private static final FileId testFileId = FileId.fromString("4.2.0"); - private static final ContractId testContractId = ContractId.fromString("0.6.9"); - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerializeFile() { - SnapshotMatcher.expect(spawnTestTransactionFile().toString()).toMatchSnapshot(); - } - - private SystemDeleteTransaction spawnTestTransactionFile() { - return new SystemDeleteTransaction().setNodeAccountIds( - Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setFileId(FileId.fromString("0.0.444")).setExpirationTime(validStart).setMaxTransactionFee(new Hbar(1)) - .freeze().sign(unusedPrivateKey); - } - - @Test - void shouldSerializeContract() { - SnapshotMatcher.expect(spawnTestTransactionContract().toString()).toMatchSnapshot(); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new SystemDeleteTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - private SystemDeleteTransaction spawnTestTransactionContract() { - return new SystemDeleteTransaction().setNodeAccountIds( - Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setContractId(ContractId.fromString("0.0.444")).setExpirationTime(validStart) - .setMaxTransactionFee(new Hbar(1)).freeze().sign(unusedPrivateKey); - } - - @Test - void shouldBytesContract() throws Exception { - var tx = spawnTestTransactionContract(); - var tx2 = ScheduleDeleteTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldBytesFile() throws Exception { - var tx = spawnTestTransactionFile(); - var tx2 = SystemDeleteTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setSystemDelete(SystemDeleteTransactionBody.newBuilder().build()).build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(SystemDeleteTransaction.class); - } - - @Test - void constructSystemDeleteTransactionFromTransactionBodyProtobuf() { - var transactionBodyWithFileId = SystemDeleteTransactionBody.newBuilder().setFileID(testFileId.toProtobuf()) - .setExpirationTime(TimestampSeconds.newBuilder().setSeconds(validStart.getEpochSecond())); - - var transactionBodyWithContractId = SystemDeleteTransactionBody.newBuilder() - .setContractID(testContractId.toProtobuf()) - .setExpirationTime(TimestampSeconds.newBuilder().setSeconds(validStart.getEpochSecond())); - - var txWithFileId = TransactionBody.newBuilder().setSystemDelete(transactionBodyWithFileId).build(); - var systemDeleteTransactionWithFileId = new SystemDeleteTransaction(txWithFileId); - - var txWithContractId = TransactionBody.newBuilder().setSystemDelete(transactionBodyWithContractId).build(); - var systemDeleteTransactionWithContractId = new SystemDeleteTransaction(txWithContractId); - - assertNotNull(systemDeleteTransactionWithFileId.getFileId()); - assertThat(systemDeleteTransactionWithFileId.getFileId()).isEqualTo(testFileId); - assertNull(systemDeleteTransactionWithFileId.getContractId()); - assertThat(systemDeleteTransactionWithFileId.getExpirationTime().getEpochSecond()).isEqualTo( - validStart.getEpochSecond()); - - assertNull(systemDeleteTransactionWithContractId.getFileId()); - assertNotNull(systemDeleteTransactionWithContractId.getContractId()); - assertThat(systemDeleteTransactionWithContractId.getContractId()).isEqualTo(testContractId); - assertThat(systemDeleteTransactionWithContractId.getExpirationTime().getEpochSecond()).isEqualTo( - validStart.getEpochSecond()); - } - - @Test - void getSetFileId() { - var systemDeleteTransaction = new SystemDeleteTransaction().setFileId(testFileId); - assertNotNull(systemDeleteTransaction.getFileId()); - assertThat(systemDeleteTransaction.getFileId()).isEqualTo(testFileId); - } - - @Test - void getSetFileIdFrozen() { - var tx = spawnTestTransactionFile(); - assertThrows(IllegalStateException.class, () -> tx.setFileId(testFileId)); - } - - @Test - void getSetContractId() { - var systemDeleteTransaction = new SystemDeleteTransaction().setContractId(testContractId); - assertNotNull(systemDeleteTransaction.getContractId()); - assertThat(systemDeleteTransaction.getContractId()).isEqualTo(testContractId); - } - - @Test - void getSetContractIdFrozen() { - var tx = spawnTestTransactionContract(); - assertThrows(IllegalStateException.class, () -> tx.setContractId(testContractId)); - } - - @Test - void getSetExpirationTime() { - var systemDeleteTransaction = new SystemDeleteTransaction().setExpirationTime(validStart); - assertNotNull(systemDeleteTransaction.getExpirationTime()); - assertThat(systemDeleteTransaction.getExpirationTime().getEpochSecond()).isEqualTo(validStart.getEpochSecond()); - } - - @Test - void getSetExpirationTimeFrozen() { - var tx = spawnTestTransactionFile(); - assertThrows(IllegalStateException.class, () -> tx.setExpirationTime(validStart)); - } - - @Test - void resetFileId() { - var systemDeleteTransaction = new SystemDeleteTransaction(); - systemDeleteTransaction.setFileId(testFileId); - systemDeleteTransaction.setContractId(testContractId); - - assertNull(systemDeleteTransaction.getFileId()); - assertNotNull(systemDeleteTransaction.getContractId()); - } - - @Test - void resetContractId() { - var systemDeleteTransaction = new SystemDeleteTransaction(); - systemDeleteTransaction.setContractId(testContractId); - systemDeleteTransaction.setFileId(testFileId); - - assertNull(systemDeleteTransaction.getContractId()); - assertNotNull(systemDeleteTransaction.getFileId()); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/SystemDeleteTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/SystemDeleteTransactionTest.snap deleted file mode 100644 index 8d9ca7512f..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/SystemDeleteTransactionTest.snap +++ /dev/null @@ -1,8 +0,0 @@ -com.hedera.hashgraph.sdk.SystemDeleteTransactionTest.shouldSerializeContract=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\nsystem_delete {\n contract_i_d {\n contract_num: 444\n realm_num: 0\n shard_num: 0\n }\n expiration_time {\n seconds: 1554158542\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] - - -com.hedera.hashgraph.sdk.SystemDeleteTransactionTest.shouldSerializeFile=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\nsystem_delete {\n expiration_time {\n seconds: 1554158542\n }\n file_i_d {\n file_num: 444\n realm_num: 0\n shard_num: 0\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/SystemUndeleteTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/SystemUndeleteTransactionTest.java deleted file mode 100644 index 7f29d55d84..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/SystemUndeleteTransactionTest.java +++ /dev/null @@ -1,115 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.SystemUndeleteTransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; - -public class SystemUndeleteTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerializeFile() { - SnapshotMatcher.expect(spawnTestTransactionFile() - .toString() - ).toMatchSnapshot(); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new SystemUndeleteTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - private SystemUndeleteTransaction spawnTestTransactionFile() { - return new SystemUndeleteTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setFileId(FileId.fromString("0.0.444")) - .setMaxTransactionFee(new Hbar(1)) - .freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldSerializeContract() { - SnapshotMatcher.expect(spawnTestTransactionContract() - .toString() - ).toMatchSnapshot(); - } - - private SystemUndeleteTransaction spawnTestTransactionContract() { - return new SystemUndeleteTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setContractId(ContractId.fromString("0.0.444")) - .setMaxTransactionFee(new Hbar(1)) - .freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldBytesContract() throws Exception { - var tx = spawnTestTransactionContract(); - var tx2 = ScheduleDeleteTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldBytesFile() throws Exception { - var tx = spawnTestTransactionFile(); - var tx2 = SystemUndeleteTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setSystemUndelete(SystemUndeleteTransactionBody.newBuilder().build()) - .build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(SystemUndeleteTransaction.class); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/SystemUndeleteTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/SystemUndeleteTransactionTest.snap deleted file mode 100644 index 21fe849f74..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/SystemUndeleteTransactionTest.snap +++ /dev/null @@ -1,8 +0,0 @@ -com.hedera.hashgraph.sdk.SystemUndeleteTransactionTest.shouldSerializeContract=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\nsystem_undelete {\n contract_i_d {\n contract_num: 444\n realm_num: 0\n shard_num: 0\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] - - -com.hedera.hashgraph.sdk.SystemUndeleteTransactionTest.shouldSerializeFile=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\nsystem_undelete {\n file_i_d {\n file_num: 444\n realm_num: 0\n shard_num: 0\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TestResponse.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TestResponse.java deleted file mode 100644 index 7dadb11075..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TestResponse.java +++ /dev/null @@ -1,91 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.Response; -import com.hedera.hashgraph.sdk.proto.TransactionGetReceiptResponse; -import com.hedera.hashgraph.sdk.proto.TransactionReceipt; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import io.grpc.StatusRuntimeException; - -import javax.annotation.Nullable; - -public class TestResponse { - @Nullable - public final TransactionResponse transactionResponse; - @Nullable - public final Response queryResponse; - @Nullable - public final StatusRuntimeException errorResponse; - - private TestResponse( - @Nullable TransactionResponse transactionResponse, - @Nullable Response queryResponse, - @Nullable StatusRuntimeException errorResponse - ) { - this.transactionResponse = transactionResponse; - this.queryResponse = queryResponse; - this.errorResponse = errorResponse; - } - - public static TestResponse transaction(Status status, Hbar cost) { - return new TestResponse(buildTransactionResponse(status, cost), null, null); - } - - public static TestResponse transaction(Status status) { - return transaction(status, new Hbar(1)); - } - - public static TestResponse transactionOk(Hbar cost) { - return transaction(Status.OK, cost); - } - - public static TestResponse transactionOk() { - return transactionOk(new Hbar(1)); - } - - public static TestResponse query(Response queryResponse) { - return new TestResponse(null, queryResponse, null); - } - - public static TestResponse receipt(Status status) { - var response = Response.newBuilder().setTransactionGetReceipt( - TransactionGetReceiptResponse.newBuilder().setReceipt( - TransactionReceipt.newBuilder().setStatus(status.code).build() - ).build() - ).build(); - return new TestResponse(null, response, null); - } - - public static TestResponse successfulReceipt() { - return receipt(Status.SUCCESS); - } - - public static TestResponse error(StatusRuntimeException exception) { - return new TestResponse(null, null, exception); - } - - public static TransactionResponse buildTransactionResponse(Status status, Hbar cost) { - return TransactionResponse.newBuilder() - .setNodeTransactionPrecheckCode(status.code) - .setCost(cost.toTinybars()) - .build(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TestServer.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TestServer.java deleted file mode 100644 index 62bbaefe49..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TestServer.java +++ /dev/null @@ -1,64 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import io.grpc.BindableService; -import io.grpc.Server; -import io.grpc.inprocess.InProcessServerBuilder; - -import java.io.IOException; -import java.time.Duration; -import java.util.HashMap; -import java.util.concurrent.TimeoutException; - -// TODO: we may want to refactor to separate TestClient from TestServer. -// That way, we can have a client with a network of multiple test servers. -// Maybe we can test load-balancing? - -public class TestServer { - public final Client client; - private final Server[] grpcServers = new Server[2]; - - public TestServer(String name, BindableService... services) throws IOException { - for (int i = 0; i < 2; i++) { - var serverBuilder = InProcessServerBuilder.forName(name + "[" + i + "]"); - for (var service : services) { - serverBuilder.addService(service); - } - grpcServers[i] = serverBuilder.directExecutor().build().start(); - } - - var network = new HashMap(); - network.put("in-process:" + name + "[0]", AccountId.fromString("1.1.1")); - network.put("in-process:" + name + "[1]", AccountId.fromString("2.2.2")); - client = Client.forNetwork(network) - .setNodeMinBackoff(Duration.ofMillis(500)) - .setNodeMaxBackoff(Duration.ofMillis(500)) - .setOperator(AccountId.fromString("2.2.2"), PrivateKey.generate()); - } - - public void close() throws TimeoutException, InterruptedException { - client.close(); - for (var server : grpcServers) { - server.shutdown(); - server.awaitTermination(); - } - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TestService.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TestService.java deleted file mode 100644 index 7b12d5e66b..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TestService.java +++ /dev/null @@ -1,87 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.Query; -import com.hedera.hashgraph.sdk.proto.Response; -import com.hedera.hashgraph.sdk.proto.Transaction; -import com.hedera.hashgraph.sdk.proto.TransactionResponse; -import io.grpc.StatusRuntimeException; -import io.grpc.stub.StreamObserver; - -import javax.annotation.Nullable; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.List; -import java.util.Queue; - -public interface TestService { - - private static void respond( - StreamObserver streamObserver, - @Nullable ResponseTypeT normalResponse, - @Nullable StatusRuntimeException errorResponse, - String exceptionString - ) { - if (normalResponse != null) { - streamObserver.onNext(normalResponse); - streamObserver.onCompleted(); - } else if (errorResponse != null) { - streamObserver.onError(errorResponse); - } else { - throw new IllegalStateException(exceptionString); - } - } - - Buffer getBuffer(); - - default void respondToTransaction(Transaction request, StreamObserver streamObserver, TestResponse response) { - getBuffer().transactionRequestsReceived.add(request); - - var exceptionString = "TestService tried to respond to transaction with query response"; - respond(streamObserver, response.transactionResponse, response.errorResponse, exceptionString); - } - - default void respondToQuery(Query request, StreamObserver streamObserver, TestResponse response) { - getBuffer().queryRequestsReceived.add(request); - - var exceptionString = "TestService tried to respond to query with transaction response"; - respond(streamObserver, response.queryResponse, response.errorResponse, exceptionString); - } - - default void respondToTransactionFromQueue(Transaction request, StreamObserver streamObserver) { - respondToTransaction(request, streamObserver, getBuffer().responsesToSend.remove()); - } - - default void respondToQueryFromQueue(Query request, StreamObserver streamObserver) { - respondToQuery(request, streamObserver, getBuffer().responsesToSend.remove()); - } - - class Buffer { - public final List transactionRequestsReceived = new ArrayList<>(); - public final List queryRequestsReceived = new ArrayList<>(); - public final Queue responsesToSend = new ArrayDeque<>(); - - public Buffer enqueueResponse(TestResponse response) { - responsesToSend.add(response); - return this; - } - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenAirdropTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenAirdropTransactionTest.java deleted file mode 100644 index f0389ce68b..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenAirdropTransactionTest.java +++ /dev/null @@ -1,223 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenAirdropTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenServiceGrpc; -import io.github.jsonSnapshot.SnapshotMatcher; -import java.time.Instant; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -class TokenAirdropTransactionTest { - private static final PrivateKey privateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - private TokenAirdropTransaction transaction; - - @BeforeEach - public void setUp() { - transaction = new TokenAirdropTransaction(); - } - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction() - .toString() - ).toMatchSnapshot(); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new TokenAirdropTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - private TokenAirdropTransaction spawnTestTransaction() { - return new TokenAirdropTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .addTokenTransfer(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.5008"), 400) - .addTokenTransferWithDecimals(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.5006"), -800, 3) - .addTokenTransferWithDecimals(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.5007"), 400, 3) - .addTokenTransfer(TokenId.fromString("0.0.4"), AccountId.fromString("0.0.5008"), 1) - .addTokenTransfer(TokenId.fromString("0.0.4"), AccountId.fromString("0.0.5006"), -1) - .addNftTransfer(TokenId.fromString("0.0.3").nft(2), AccountId.fromString("0.0.5008"), AccountId.fromString("0.0.5007")) - .addNftTransfer(TokenId.fromString("0.0.3").nft(1), AccountId.fromString("0.0.5008"), AccountId.fromString("0.0.5007")) - .addNftTransfer(TokenId.fromString("0.0.3").nft(3), AccountId.fromString("0.0.5008"), AccountId.fromString("0.0.5006")) - .addNftTransfer(TokenId.fromString("0.0.3").nft(4), AccountId.fromString("0.0.5007"), AccountId.fromString("0.0.5006")) - .addNftTransfer(TokenId.fromString("0.0.2").nft(4), AccountId.fromString("0.0.5007"), AccountId.fromString("0.0.5006")) - .addApprovedTokenTransfer(TokenId.fromString("0.0.4"), AccountId.fromString("0.0.5006"), 123) - .addApprovedNftTransfer(new NftId(TokenId.fromString("0.0.4"), 4), AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006")) - .setMaxTransactionFee(Hbar.fromTinybars(100_000)) - .freeze() - .sign(privateKey); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = TokenAirdropTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void decimalsMustBeConsistent() { - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { - new TokenAirdropTransaction() - .addTokenTransferWithDecimals(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.8"), 100, 2) - .addTokenTransferWithDecimals(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.7"), -100, 3); - }); - } - - @Test - void canGetDecimals() { - var tx = new TokenAirdropTransaction(); - assertThat(tx.getTokenIdDecimals().get(TokenId.fromString("0.0.5"))).isNull(); - tx.addTokenTransfer(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.8"), 100); - assertThat(tx.getTokenIdDecimals().get(TokenId.fromString("0.0.5"))).isNull(); - tx.addTokenTransferWithDecimals(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.7"), -100, 5); - assertThat(tx.getTokenIdDecimals().get(TokenId.fromString("0.0.5"))).isEqualTo(5); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setTokenAirdrop(TokenAirdropTransactionBody.newBuilder().build()) - .build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(TokenAirdropTransaction.class); - } - - @Test - void testDefaultMaxTransactionFeeIsSet() { - assertEquals(new Hbar(1), transaction.getDefaultMaxTransactionFee(), "Default max transaction fee should be 1 Hbar"); - } - - @Test - void testAddTokenTransfer() { - TokenId tokenId = new TokenId(0, 0, 123); - AccountId accountId = new AccountId(0, 0, 456); - long value = 1000L; - - transaction.addTokenTransfer(tokenId, accountId, value); - - Map> tokenTransfers = transaction.getTokenTransfers(); - assertTrue(tokenTransfers.containsKey(tokenId)); - assertEquals(1, tokenTransfers.get(tokenId).size()); - assertEquals(value, tokenTransfers.get(tokenId).get(accountId)); - } - - @Test - void testAddApprovedTokenTransfer() { - TokenId tokenId = new TokenId(0, 0, 123); - AccountId accountId = new AccountId(0, 0, 456); - long value = 1000L; - - transaction.addApprovedTokenTransfer(tokenId, accountId, value); - - Map> tokenTransfers = transaction.getTokenTransfers(); - assertTrue(tokenTransfers.containsKey(tokenId)); - assertEquals(1, tokenTransfers.get(tokenId).size()); - assertEquals(value, tokenTransfers.get(tokenId).get(accountId)); - } - - @Test - void testAddNftTransfer() { - NftId nftId = new NftId(new TokenId(0, 0, 123), 1); - AccountId sender = new AccountId(0, 0, 456); - AccountId receiver = new AccountId(0, 0, 789); - - transaction.addNftTransfer(nftId, sender, receiver); - - Map> nftTransfers = transaction.getTokenNftTransfers(); - assertTrue(nftTransfers.containsKey(nftId.tokenId)); - assertEquals(1, nftTransfers.get(nftId.tokenId).size()); - assertEquals(sender, nftTransfers.get(nftId.tokenId).get(0).sender); - assertEquals(receiver, nftTransfers.get(nftId.tokenId).get(0).receiver); - } - - @Test - void testAddApprovedNftTransfer() { - NftId nftId = new NftId(new TokenId(0, 0, 123), 1); - AccountId sender = new AccountId(0, 0, 456); - AccountId receiver = new AccountId(0, 0, 789); - - transaction.addApprovedNftTransfer(nftId, sender, receiver); - - Map> nftTransfers = transaction.getTokenNftTransfers(); - assertTrue(nftTransfers.containsKey(nftId.tokenId)); - assertEquals(1, nftTransfers.get(nftId.tokenId).size()); - assertEquals(sender, nftTransfers.get(nftId.tokenId).get(0).sender); - assertEquals(receiver, nftTransfers.get(nftId.tokenId).get(0).receiver); - } - - @Test - void testGetTokenIdDecimals() { - TokenId tokenId = new TokenId(0, 0, 123); - AccountId accountId = new AccountId(0, 0, 456); - long value = 1000L; - int decimals = 8; - - transaction.addTokenTransferWithDecimals(tokenId, accountId, value, decimals); - - Map decimalsMap = transaction.getTokenIdDecimals(); - assertTrue(decimalsMap.containsKey(tokenId)); - assertEquals(decimals, decimalsMap.get(tokenId)); - } - - @Test - void testBuildTransactionBody() { - TokenAirdropTransactionBody.Builder builder = spawnTestTransaction().build(); - - assertNotNull(builder); - } - - @Test - void testGetMethodDescriptor() { - assertEquals(TokenServiceGrpc.getAirdropTokensMethod(), transaction.getMethodDescriptor()); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenAirdropTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenAirdropTransactionTest.snap deleted file mode 100644 index 87049b1a06..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenAirdropTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.TokenAirdropTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_airdrop {\n token_transfers {\n nft_transfers {\n receiver_account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n sender_account_i_d {\n account_num: 5007\n realm_num: 0\n shard_num: 0\n }\n serial_number: 4\n }\n token {\n realm_num: 0\n shard_num: 0\n token_num: 2\n }\n }\n token_transfers {\n nft_transfers {\n receiver_account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n sender_account_i_d {\n account_num: 5007\n realm_num: 0\n shard_num: 0\n }\n serial_number: 4\n }\n nft_transfers {\n receiver_account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n sender_account_i_d {\n account_num: 5008\n realm_num: 0\n shard_num: 0\n }\n serial_number: 3\n }\n nft_transfers {\n receiver_account_i_d {\n account_num: 5007\n realm_num: 0\n shard_num: 0\n }\n sender_account_i_d {\n account_num: 5008\n realm_num: 0\n shard_num: 0\n }\n serial_number: 1\n }\n nft_transfers {\n receiver_account_i_d {\n account_num: 5007\n realm_num: 0\n shard_num: 0\n }\n sender_account_i_d {\n account_num: 5008\n realm_num: 0\n shard_num: 0\n }\n serial_number: 2\n }\n token {\n realm_num: 0\n shard_num: 0\n token_num: 3\n }\n }\n token_transfers {\n nft_transfers {\n is_approval: true\n receiver_account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n sender_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n }\n serial_number: 4\n }\n token {\n realm_num: 0\n shard_num: 0\n token_num: 4\n }\n transfers {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n amount: -1\n }\n transfers {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n amount: 123\n is_approval: true\n }\n transfers {\n account_i_d {\n account_num: 5008\n realm_num: 0\n shard_num: 0\n }\n amount: 1\n }\n }\n token_transfers {\n expected_decimals {\n value: 3\n }\n token {\n realm_num: 0\n shard_num: 0\n token_num: 5\n }\n transfers {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n amount: -800\n }\n transfers {\n account_i_d {\n account_num: 5007\n realm_num: 0\n shard_num: 0\n }\n amount: 400\n }\n transfers {\n account_i_d {\n account_num: 5008\n realm_num: 0\n shard_num: 0\n }\n amount: 400\n }\n }\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenAssociateTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenAssociateTransactionTest.java deleted file mode 100644 index 6fd0295db7..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenAssociateTransactionTest.java +++ /dev/null @@ -1,135 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenAssociateTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class TokenAssociateTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - private static final AccountId accountId = AccountId.fromString("1.2.3"); - private static final List tokenIds = List.of(TokenId.fromString("4.5.6"), - TokenId.fromString("7.8.9"), - TokenId.fromString("10.11.12")); - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction() - .toString() - ).toMatchSnapshot(); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new TokenAssociateTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - private TokenAssociateTransaction spawnTestTransaction() { - return new TokenAssociateTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setAccountId(AccountId.fromString("0.0.222")) - .setTokenIds(Collections.singletonList(TokenId.fromString("0.0.666"))) - .setMaxTransactionFee(new Hbar(1)) - .freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = TokenAssociateTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setTokenAssociate(TokenAssociateTransactionBody.newBuilder().build()) - .build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(TokenAssociateTransaction.class); - } - - @Test - void constructTokenDeleteTransactionFromTransactionBodyProtobuf() { - var transactionBody = TokenAssociateTransactionBody.newBuilder() - .addAllTokens(tokenIds.stream().map(TokenId::toProtobuf).toList()) - .setAccount(accountId.toProtobuf()).build(); - var txBody = TransactionBody.newBuilder().setTokenAssociate(transactionBody).build(); - var tokenAssociateTransaction = new TokenAssociateTransaction(txBody); - - assertThat(tokenAssociateTransaction.getAccountId()).isEqualTo(accountId); - assertThat(tokenAssociateTransaction.getTokenIds()).hasSize(tokenIds.size()); - } - - @Test - void getSetAccountId() { - var transaction = new TokenAssociateTransaction().setAccountId(accountId); - assertThat(transaction.getAccountId()).isEqualTo(accountId); - } - - @Test - void getSetAccountIdFrozen() { - var transaction = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> transaction.setAccountId(accountId)); - } - - @Test - void getSetTokenIds() { - var transaction = new TokenAssociateTransaction().setTokenIds(tokenIds); - assertThat(transaction.getTokenIds()).isEqualTo(tokenIds); - } - - @Test - void getSetTokenIdFrozen() { - var transaction = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> transaction.setTokenIds(tokenIds)); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenAssociateTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenAssociateTransactionTest.snap deleted file mode 100644 index 109b1d30de..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenAssociateTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.TokenAssociateTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_associate {\n account {\n account_num: 222\n realm_num: 0\n shard_num: 0\n }\n tokens {\n realm_num: 0\n shard_num: 0\n token_num: 666\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenAssociationTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenAssociationTest.snap deleted file mode 100644 index f56199f654..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenAssociationTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.TokenAssociationTest.shouldSerializeAccount=[ - "TokenAssociation{tokenId=1.2.3, accountId=4.5.6}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenBurnTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenBurnTransactionTest.java deleted file mode 100644 index e62fce3893..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenBurnTransactionTest.java +++ /dev/null @@ -1,161 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenBurnTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class TokenBurnTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - private static final TokenId testTokenId = TokenId.fromString("4.2.0"); - private static final long testAmount = 69L; - private static final List testSerials = Collections.singletonList(420L); - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerializeFungible() { - SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); - } - - private TokenBurnTransaction spawnTestTransaction() { - return new TokenBurnTransaction().setNodeAccountIds( - Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setTokenId(testTokenId).setAmount(testAmount).setMaxTransactionFee(new Hbar(1)).freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new TokenBurnTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldSerializeNft() { - SnapshotMatcher.expect(spawnTestTransactionNft().toString()).toMatchSnapshot(); - } - - private TokenBurnTransaction spawnTestTransactionNft() { - return new TokenBurnTransaction().setNodeAccountIds( - Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setTokenId(testTokenId).setSerials(testSerials).setMaxTransactionFee(new Hbar(1)).freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldBytesFungible() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = TokenBurnTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldBytesNft() throws Exception { - var tx = spawnTestTransactionNft(); - var tx2 = TokenBurnTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setTokenBurn(TokenBurnTransactionBody.newBuilder().build()).build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(TokenBurnTransaction.class); - } - - @Test - void constructTokenBurnTransactionFromTransactionBodyProtobuf() { - var transactionBody = TokenBurnTransactionBody.newBuilder().setToken(testTokenId.toProtobuf()) - .setAmount(testAmount).addAllSerialNumbers(testSerials).build(); - - var tx = TransactionBody.newBuilder().setTokenBurn(transactionBody).build(); - var tokenBurnTransaction = new TokenBurnTransaction(tx); - - assertThat(tokenBurnTransaction.getTokenId()).isEqualTo(testTokenId); - assertThat(tokenBurnTransaction.getAmount()).isEqualTo(testAmount); - assertThat(tokenBurnTransaction.getSerials()).isEqualTo(testSerials); - } - - @Test - void getSetTokenId() { - var tokenBurnTransaction = new TokenBurnTransaction().setTokenId(testTokenId); - assertThat(tokenBurnTransaction.getTokenId()).isEqualTo(testTokenId); - } - - @Test - void getSetTokenIdFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setTokenId(testTokenId)); - } - - @Test - void getSetAmount() { - var tokenBurnTransaction = new TokenBurnTransaction().setAmount(testAmount); - assertThat(tokenBurnTransaction.getAmount()).isEqualTo(testAmount); - } - - @Test - void getSetAmountFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setAmount(testAmount)); - } - - @Test - void getSetSerials() { - var tokenBurnTransaction = new TokenBurnTransaction().setSerials(testSerials); - assertThat(tokenBurnTransaction.getSerials()).isEqualTo(testSerials); - } - - @Test - void getSetSerialsFrozen() { - var tx = spawnTestTransactionNft(); - assertThrows(IllegalStateException.class, () -> tx.setSerials(testSerials)); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenBurnTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenBurnTransactionTest.snap deleted file mode 100644 index 5ecd0f7114..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenBurnTransactionTest.snap +++ /dev/null @@ -1,8 +0,0 @@ -com.hedera.hashgraph.sdk.TokenBurnTransactionTest.shouldSerializeFungible=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_burn {\n amount: 69\n token {\n realm_num: 2\n shard_num: 4\n token_num: 0\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] - - -com.hedera.hashgraph.sdk.TokenBurnTransactionTest.shouldSerializeNft=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_burn {\n amount: 0\n serial_numbers: 420\n token {\n realm_num: 2\n shard_num: 4\n token_num: 0\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenCancelAirdropTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenCancelAirdropTransactionTest.java deleted file mode 100644 index be4cbcddb0..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenCancelAirdropTransactionTest.java +++ /dev/null @@ -1,177 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenCancelAirdropTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenServiceGrpc; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import java.time.Instant; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -class TokenCancelAirdropTransactionTest { - - private static final PrivateKey privateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - final Instant validStart = Instant.ofEpochSecond(1554158542); - private TokenCancelAirdropTransaction transaction; - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - private TokenCancelAirdropTransaction spawnTestTransaction() { - List pendingAirdropIds = new ArrayList<>(); - pendingAirdropIds.add(new PendingAirdropId(new AccountId(0, 0, 457), new AccountId(0, 0, 456), - new TokenId(0, 0, 123))); - pendingAirdropIds.add(new PendingAirdropId(new AccountId(0, 0, 457), new AccountId(0, 0, 456), - new NftId(new TokenId(0, 0, 1234), 123))); - - return new TokenCancelAirdropTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setMaxTransactionFee(Hbar.fromTinybars(100_000)) - .setPendingAirdropIds(pendingAirdropIds) - .freeze() - .sign(privateKey); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction() - .toString() - ).toMatchSnapshot(); - } - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new TokenCancelAirdropTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @BeforeEach - public void setUp() { - transaction = new TokenCancelAirdropTransaction(); - } - - @Test - void testConstructorSetsDefaultMaxTransactionFee() { - Assertions.assertEquals(Hbar.from(1), transaction.getDefaultMaxTransactionFee()); - } - - @Test - void testGetAndSetPendingAirdropIds() { - List pendingAirdropIds = new ArrayList<>(); - pendingAirdropIds.add(new PendingAirdropId(new AccountId(0, 0, 457), new AccountId(0, 0, 456), - new TokenId(0, 0, 123))); - pendingAirdropIds.add(new PendingAirdropId(new AccountId(0, 0, 457), new AccountId(0, 0, 456), - new NftId(new TokenId(0, 0, 1234), 123))); - - transaction.setPendingAirdropIds(pendingAirdropIds); - - Assertions.assertEquals(pendingAirdropIds, transaction.getPendingAirdropIds()); - } - - @Test - void testSetPendingAirdropIdsNullThrowsException() { - Assertions.assertThrows(NullPointerException.class, () -> transaction.setPendingAirdropIds(null)); - } - - @Test - void testClearPendingAirdropIds() { - List pendingAirdropIds = new ArrayList<>(); - PendingAirdropId pendingAirdropId = new PendingAirdropId(new AccountId(0, 0, 457), new AccountId(0, 0, 456), - new TokenId(0, 0, 123)); - pendingAirdropIds.add(pendingAirdropId); - - transaction.setPendingAirdropIds(pendingAirdropIds); - transaction.clearPendingAirdropIds(); - - Assertions.assertTrue(transaction.getPendingAirdropIds().isEmpty()); - } - - @Test - void testAddAllPendingAirdrops() { - PendingAirdropId pendingAirdropId1 = new PendingAirdropId(new AccountId(0, 0, 457), new AccountId(0, 0, 456), - new TokenId(0, 0, 123)); - PendingAirdropId pendingAirdropId2 = new PendingAirdropId(new AccountId(0, 0, 458), new AccountId(0, 0, 459), - new TokenId(0, 0, 123)); - - transaction.addPendingAirdrop(pendingAirdropId1); - transaction.addPendingAirdrop(pendingAirdropId2); - - Assertions.assertEquals(2, transaction.getPendingAirdropIds().size()); - Assertions.assertTrue(transaction.getPendingAirdropIds().contains(pendingAirdropId1)); - Assertions.assertTrue(transaction.getPendingAirdropIds().contains(pendingAirdropId2)); - } - - @Test - void testAddAllPendingAirdropsNullThrowsException() { - Assertions.assertThrows(NullPointerException.class, () -> transaction.addPendingAirdrop(null)); - } - - @Test - void testBuildTransactionBody() { - PendingAirdropId pendingAirdropId = new PendingAirdropId(new AccountId(0, 0, 457), new AccountId(0, 0, 456), - new NftId(new TokenId(0, 0, 1234), 123)); - transaction.addPendingAirdrop(pendingAirdropId); - - TokenCancelAirdropTransactionBody.Builder builder = transaction.build(); - Assertions.assertEquals(1, builder.getPendingAirdropsCount()); - Assertions.assertEquals(pendingAirdropId.toProtobuf(), builder.getPendingAirdrops(0)); - } - - @Test - void testGetMethodDescriptor() { - Assertions.assertEquals(TokenServiceGrpc.getCancelAirdropMethod(), transaction.getMethodDescriptor()); - } - - @Test - void testOnFreeze() { - var bodyBuilder = TransactionBody.newBuilder(); - transaction.onFreeze(bodyBuilder); - - Assertions.assertTrue(bodyBuilder.hasTokenCancelAirdrop()); - } - - @Test - void testOnScheduled() { - SchedulableTransactionBody.Builder scheduled = SchedulableTransactionBody.newBuilder(); - transaction.onScheduled(scheduled); - - Assertions.assertTrue(scheduled.hasTokenCancelAirdrop()); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenCancelAirdropTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenCancelAirdropTransactionTest.snap deleted file mode 100644 index 478020d764..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenCancelAirdropTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.TokenCancelAirdropTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_cancel_airdrop {\n pending_airdrops {\n fungible_token_type {\n realm_num: 0\n shard_num: 0\n token_num: 123\n }\n receiver_id {\n account_num: 456\n realm_num: 0\n shard_num: 0\n }\n sender_id {\n account_num: 457\n realm_num: 0\n shard_num: 0\n }\n }\n pending_airdrops {\n non_fungible_token {\n serial_number: 123\n token_i_d {\n realm_num: 0\n shard_num: 0\n token_num: 1234\n }\n }\n receiver_id {\n account_num: 456\n realm_num: 0\n shard_num: 0\n }\n sender_id {\n account_num: 457\n realm_num: 0\n shard_num: 0\n }\n }\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenClaimAirdropTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenClaimAirdropTransactionTest.java deleted file mode 100644 index 2a912ae733..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenClaimAirdropTransactionTest.java +++ /dev/null @@ -1,178 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenClaimAirdropTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenServiceGrpc; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import java.time.Instant; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -class TokenClaimAirdropTransactionTest { - - private static final PrivateKey privateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - final Instant validStart = Instant.ofEpochSecond(1554158542); - private TokenClaimAirdropTransaction transaction; - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - private TokenClaimAirdropTransaction spawnTestTransaction() { - List pendingAirdropIds = new ArrayList<>(); - pendingAirdropIds.add(new PendingAirdropId(new AccountId(0, 0, 457), new AccountId(0, 0, 456), - new TokenId(0, 0, 123))); - pendingAirdropIds.add(new PendingAirdropId(new AccountId(0, 0, 457), new AccountId(0, 0, 456), - new NftId(new TokenId(0, 0, 1234), 123))); - - return new TokenClaimAirdropTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setMaxTransactionFee(Hbar.fromTinybars(100_000)) - .setPendingAirdropIds(pendingAirdropIds) - .freeze() - .sign(privateKey); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction() - .toString() - ).toMatchSnapshot(); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new TokenClaimAirdropTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @BeforeEach - public void setUp() { - transaction = new TokenClaimAirdropTransaction(); - } - - @Test - void testConstructorSetsDefaultMaxTransactionFee() { - Assertions.assertEquals(Hbar.from(1), transaction.getDefaultMaxTransactionFee()); - } - - @Test - void testGetAndSetPendingAirdropIds() { - List pendingAirdropIds = new ArrayList<>(); - pendingAirdropIds.add(new PendingAirdropId(new AccountId(0, 0, 457), new AccountId(0, 0, 456), - new TokenId(0, 0, 123))); - pendingAirdropIds.add(new PendingAirdropId(new AccountId(0, 0, 457), new AccountId(0, 0, 456), - new NftId(new TokenId(0, 0, 1234), 123))); - - transaction.setPendingAirdropIds(pendingAirdropIds); - - Assertions.assertEquals(pendingAirdropIds, transaction.getPendingAirdropIds()); - } - - @Test - void testSetPendingAirdropIdsNullThrowsException() { - Assertions.assertThrows(NullPointerException.class, () -> transaction.setPendingAirdropIds(null)); - } - - @Test - void testClearPendingAirdropIds() { - List pendingAirdropIds = new ArrayList<>(); - PendingAirdropId pendingAirdropId = new PendingAirdropId(new AccountId(0, 0, 457), new AccountId(0, 0, 456), - new TokenId(0, 0, 123)); - pendingAirdropIds.add(pendingAirdropId); - - transaction.setPendingAirdropIds(pendingAirdropIds); - transaction.clearPendingAirdropIds(); - - Assertions.assertTrue(transaction.getPendingAirdropIds().isEmpty()); - } - - @Test - void testAddAllPendingAirdrops() { - PendingAirdropId pendingAirdropId1 = new PendingAirdropId(new AccountId(0, 0, 457), new AccountId(0, 0, 456), - new TokenId(0, 0, 123)); - PendingAirdropId pendingAirdropId2 = new PendingAirdropId(new AccountId(0, 0, 458), new AccountId(0, 0, 459), - new TokenId(0, 0, 123)); - - transaction.addPendingAirdrop(pendingAirdropId1); - transaction.addPendingAirdrop(pendingAirdropId2); - - Assertions.assertEquals(2, transaction.getPendingAirdropIds().size()); - Assertions.assertTrue(transaction.getPendingAirdropIds().contains(pendingAirdropId1)); - Assertions.assertTrue(transaction.getPendingAirdropIds().contains(pendingAirdropId2)); - } - - @Test - void testAddAllPendingAirdropsNullThrowsException() { - Assertions.assertThrows(NullPointerException.class, () -> transaction.addPendingAirdrop(null)); - } - - @Test - void testBuildTransactionBody() { - PendingAirdropId pendingAirdropId = new PendingAirdropId(new AccountId(0, 0, 457), new AccountId(0, 0, 456), - new NftId(new TokenId(0, 0, 1234), 123)); - transaction.addPendingAirdrop(pendingAirdropId); - - TokenClaimAirdropTransactionBody.Builder builder = transaction.build(); - Assertions.assertEquals(1, builder.getPendingAirdropsCount()); - Assertions.assertEquals(pendingAirdropId.toProtobuf(), builder.getPendingAirdrops(0)); - } - - @Test - void testGetMethodDescriptor() { - Assertions.assertEquals(TokenServiceGrpc.getClaimAirdropMethod(), transaction.getMethodDescriptor()); - } - - @Test - void testOnFreeze() { - var bodyBuilder = TransactionBody.newBuilder(); - transaction.onFreeze(bodyBuilder); - - Assertions.assertTrue(bodyBuilder.hasTokenClaimAirdrop()); - } - - @Test - void testOnScheduled() { - SchedulableTransactionBody.Builder scheduled = SchedulableTransactionBody.newBuilder(); - transaction.onScheduled(scheduled); - - Assertions.assertTrue(scheduled.hasTokenClaimAirdrop()); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenClaimAirdropTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenClaimAirdropTransactionTest.snap deleted file mode 100644 index 451ab57cd4..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenClaimAirdropTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.TokenClaimAirdropTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_claim_airdrop {\n pending_airdrops {\n fungible_token_type {\n realm_num: 0\n shard_num: 0\n token_num: 123\n }\n receiver_id {\n account_num: 456\n realm_num: 0\n shard_num: 0\n }\n sender_id {\n account_num: 457\n realm_num: 0\n shard_num: 0\n }\n }\n pending_airdrops {\n non_fungible_token {\n serial_number: 123\n token_i_d {\n realm_num: 0\n shard_num: 0\n token_num: 1234\n }\n }\n receiver_id {\n account_num: 456\n realm_num: 0\n shard_num: 0\n }\n sender_id {\n account_num: 457\n realm_num: 0\n shard_num: 0\n }\n }\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenCreateTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenCreateTransactionTest.java deleted file mode 100644 index e7c4640d5b..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenCreateTransactionTest.java +++ /dev/null @@ -1,500 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import com.google.common.collect.Iterables; -import com.google.protobuf.ByteString; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.Timestamp; -import com.hedera.hashgraph.sdk.proto.TokenCreateTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import java.time.Duration; -import java.time.Instant; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -public class TokenCreateTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - private static final PublicKey testAdminKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e11") - .getPublicKey(); - private static final PublicKey testKycKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e12") - .getPublicKey(); - private static final PublicKey testFreezeKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e13") - .getPublicKey(); - private static final PublicKey testWipeKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e14") - .getPublicKey(); - private static final PublicKey testSupplyKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e15") - .getPublicKey(); - private static final PublicKey testFeeScheduleKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e16") - .getPublicKey(); - private static final PublicKey testPauseKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e17") - .getPublicKey(); - private static final PublicKey testMetadataKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e18") - .getPublicKey(); - private static final AccountId testTreasuryAccountId = AccountId.fromString("7.7.7"); - private static final AccountId testAutoRenewAccountId = AccountId.fromString("8.8.8"); - private static final long testInitialSupply = 30; - private static final long testMaxSupply = 500; - private static final int testDecimals = 3; - private static final boolean testFreezeDefault = true; - private static final String testTokenName = "test name"; - private static final String testTokenSymbol = "test symbol"; - private static final String testTokenMemo = "test memo"; - private static final Duration testAutoRenewPeriod = Duration.ofHours(10); - private static final Instant testExpirationTime = Instant.now(); - private static final List testCustomFees = Collections.singletonList( - new CustomFixedFee().setFeeCollectorAccountId(AccountId.fromString("0.0.543")).setAmount(3) - .setDenominatingTokenId(TokenId.fromString("4.3.2"))); - private static final byte[] testMetadata = new byte[]{1, 2, 3, 4, 5}; - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerializeFungible() { - SnapshotMatcher.expect(spawnTestTransactionFungible().toString()).toMatchSnapshot(); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new TokenCreateTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldSerializeNft() { - SnapshotMatcher.expect(spawnTestTransactionNft().toString()).toMatchSnapshot(); - } - - private TokenCreateTransaction spawnTestTransactionFungible() { - return new TokenCreateTransaction().setNodeAccountIds( - Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setInitialSupply(testInitialSupply).setFeeScheduleKey(testFeeScheduleKey).setSupplyKey(testSupplyKey) - .setAdminKey(testAdminKey).setAutoRenewAccountId(testAutoRenewAccountId) - .setAutoRenewPeriod(testAutoRenewPeriod).setDecimals(testDecimals).setFreezeDefault(testFreezeDefault) - .setFreezeKey(testFreezeKey).setWipeKey(testWipeKey).setTokenSymbol(testTokenSymbol).setKycKey(testKycKey) - .setPauseKey(testPauseKey).setMetadataKey(testMetadataKey).setExpirationTime(validStart) - .setTreasuryAccountId(testTreasuryAccountId).setTokenName(testTokenName).setTokenMemo(testTokenMemo) - .setCustomFees(testCustomFees).setMaxTransactionFee(new Hbar(1)).setTokenMetadata(testMetadata).freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldBytesFungible() throws Exception { - var tx = spawnTestTransactionFungible(); - var tx2 = TokenCreateTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - private TokenCreateTransaction spawnTestTransactionNft() { - return new TokenCreateTransaction().setNodeAccountIds( - Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setFeeScheduleKey(testFeeScheduleKey).setSupplyKey(testSupplyKey).setMaxSupply(testMaxSupply) - .setAdminKey(testAdminKey).setAutoRenewAccountId(testAutoRenewAccountId) - .setAutoRenewPeriod(testAutoRenewPeriod).setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setSupplyType(TokenSupplyType.FINITE).setFreezeKey(testFreezeKey).setWipeKey(testWipeKey) - .setTokenSymbol(testTokenSymbol).setKycKey(testKycKey).setPauseKey(testPauseKey) - .setMetadataKey(testMetadataKey).setExpirationTime(validStart).setTreasuryAccountId(testTreasuryAccountId) - .setTokenName(testTokenName).setTokenMemo(testTokenMemo).setMaxTransactionFee(new Hbar(1)) - .setTokenMetadata(testMetadata).freeze().sign(unusedPrivateKey); - } - - @Test - void shouldBytesNft() throws Exception { - var tx = spawnTestTransactionNft(); - var tx2 = TokenCreateTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setTokenCreation(TokenCreateTransactionBody.newBuilder().build()).build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(TokenCreateTransaction.class); - } - - @Test - void constructTokenCreateTransactionFungibleFromTransactionBodyProtobuf() { - var transactionBody = TokenCreateTransactionBody.newBuilder().setInitialSupply(testInitialSupply) - .setFeeScheduleKey(testFeeScheduleKey.toProtobufKey()).setSupplyKey(testSupplyKey.toProtobufKey()) - .setAdminKey(testAdminKey.toProtobufKey()).setAutoRenewAccount(testAutoRenewAccountId.toProtobuf()) - .setAutoRenewPeriod( - com.hedera.hashgraph.sdk.proto.Duration.newBuilder().setSeconds(testAutoRenewPeriod.toSeconds()) - .build()).setExpiry(Timestamp.newBuilder().setSeconds(testExpirationTime.getEpochSecond()).build()) - .setDecimals(testDecimals).setFreezeDefault(testFreezeDefault).setFreezeKey(testFreezeKey.toProtobufKey()) - .setWipeKey(testWipeKey.toProtobufKey()).setSymbol(testTokenSymbol).setKycKey(testKycKey.toProtobufKey()) - .setPauseKey(testPauseKey.toProtobufKey()).setMetadataKey(testMetadataKey.toProtobufKey()) - .setExpiry(Timestamp.newBuilder().setSeconds(testExpirationTime.getEpochSecond())) - .setTreasury(testTreasuryAccountId.toProtobuf()).setName(testTokenName).setMemo(testTokenMemo) - .addCustomFees(Iterables.getLast(testCustomFees).toProtobuf()) - .setMetadata(ByteString.copyFrom(testMetadata)).build(); - - var tx = TransactionBody.newBuilder().setTokenCreation(transactionBody).build(); - var tokenCreateTransaction = new TokenCreateTransaction(tx); - - assertThat(tokenCreateTransaction.getFeeScheduleKey()).isEqualTo(testFeeScheduleKey); - assertThat(tokenCreateTransaction.getSupplyKey()).isEqualTo(testSupplyKey); - assertThat(tokenCreateTransaction.getAdminKey()).isEqualTo(testAdminKey); - assertThat(tokenCreateTransaction.getAutoRenewAccountId()).isEqualTo(testAutoRenewAccountId); - assertThat(tokenCreateTransaction.getAutoRenewPeriod().toSeconds()).isEqualTo(testAutoRenewPeriod.toSeconds()); - assertThat(tokenCreateTransaction.getDecimals()).isEqualTo(testDecimals); - assertThat(tokenCreateTransaction.getFreezeDefault()).isEqualTo(testFreezeDefault); - assertThat(tokenCreateTransaction.getFreezeKey()).isEqualTo(testFreezeKey); - assertThat(tokenCreateTransaction.getWipeKey()).isEqualTo(testWipeKey); - assertThat(tokenCreateTransaction.getTokenSymbol()).isEqualTo(testTokenSymbol); - assertThat(tokenCreateTransaction.getKycKey()).isEqualTo(testKycKey); - assertThat(tokenCreateTransaction.getPauseKey()).isEqualTo(testPauseKey); - assertThat(tokenCreateTransaction.getMetadataKey()).isEqualTo(testMetadataKey); - assertThat(tokenCreateTransaction.getExpirationTime().getEpochSecond()).isEqualTo( - testExpirationTime.getEpochSecond()); - assertThat(tokenCreateTransaction.getTreasuryAccountId()).isEqualTo(testTreasuryAccountId); - assertThat(tokenCreateTransaction.getTokenName()).isEqualTo(testTokenName); - assertThat(tokenCreateTransaction.getTokenMemo()).isEqualTo(testTokenMemo); - assertThat(tokenCreateTransaction.getTokenType()).isEqualTo(TokenType.FUNGIBLE_COMMON); - assertThat(Iterables.getLast(tokenCreateTransaction.getCustomFees()).toBytes()).isEqualTo( - Iterables.getLast(testCustomFees).toBytes()); - assertThat(tokenCreateTransaction.getTokenMetadata()).isEqualTo(testMetadata); - } - - @Test - void constructTokenCreateTransactionNftFromTransactionBodyProtobuf() { - var transactionBody = TokenCreateTransactionBody.newBuilder() - .setFeeScheduleKey(testFeeScheduleKey.toProtobufKey()).setSupplyKey(testSupplyKey.toProtobufKey()) - .setMaxSupply(testMaxSupply).setAdminKey(testAdminKey.toProtobufKey()) - .setAutoRenewAccount(testAutoRenewAccountId.toProtobuf()).setAutoRenewPeriod( - com.hedera.hashgraph.sdk.proto.Duration.newBuilder().setSeconds(testAutoRenewPeriod.toSeconds()) - .build()).setExpiry(Timestamp.newBuilder().setSeconds(testExpirationTime.getEpochSecond()).build()) - .setTokenType(com.hedera.hashgraph.sdk.proto.TokenType.NON_FUNGIBLE_UNIQUE) - .setSupplyType(com.hedera.hashgraph.sdk.proto.TokenSupplyType.FINITE) - .setFreezeKey(testFreezeKey.toProtobufKey()).setWipeKey(testWipeKey.toProtobufKey()) - .setSymbol(testTokenSymbol).setKycKey(testKycKey.toProtobufKey()).setPauseKey(testPauseKey.toProtobufKey()) - .setMetadataKey(testMetadataKey.toProtobufKey()) - .setExpiry(Timestamp.newBuilder().setSeconds(testExpirationTime.getEpochSecond())) - .setTreasury(testTreasuryAccountId.toProtobuf()).setName(testTokenName).setMemo(testTokenMemo).build(); - - var tx = TransactionBody.newBuilder().setTokenCreation(transactionBody).build(); - var tokenCreateTransaction = new TokenCreateTransaction(tx); - - assertThat(tokenCreateTransaction.getFeeScheduleKey()).isEqualTo(testFeeScheduleKey); - assertThat(tokenCreateTransaction.getSupplyKey()).isEqualTo(testSupplyKey); - assertThat(tokenCreateTransaction.getMaxSupply()).isEqualTo(testMaxSupply); - assertThat(tokenCreateTransaction.getAdminKey()).isEqualTo(testAdminKey); - assertThat(tokenCreateTransaction.getAutoRenewAccountId()).isEqualTo(testAutoRenewAccountId); - assertThat(tokenCreateTransaction.getAutoRenewPeriod().toSeconds()).isEqualTo(testAutoRenewPeriod.toSeconds()); - assertThat(tokenCreateTransaction.getTokenType()).isEqualTo(TokenType.NON_FUNGIBLE_UNIQUE); - assertThat(tokenCreateTransaction.getSupplyType()).isEqualTo(TokenSupplyType.FINITE); - assertThat(tokenCreateTransaction.getFreezeKey()).isEqualTo(testFreezeKey); - assertThat(tokenCreateTransaction.getWipeKey()).isEqualTo(testWipeKey); - assertThat(tokenCreateTransaction.getTokenSymbol()).isEqualTo(testTokenSymbol); - assertThat(tokenCreateTransaction.getKycKey()).isEqualTo(testKycKey); - assertThat(tokenCreateTransaction.getPauseKey()).isEqualTo(testPauseKey); - assertThat(tokenCreateTransaction.getMetadataKey()).isEqualTo(testMetadataKey); - assertThat(tokenCreateTransaction.getExpirationTime().getEpochSecond()).isEqualTo( - testExpirationTime.getEpochSecond()); - assertThat(tokenCreateTransaction.getTreasuryAccountId()).isEqualTo(testTreasuryAccountId); - assertThat(tokenCreateTransaction.getTokenName()).isEqualTo(testTokenName); - assertThat(tokenCreateTransaction.getTokenMemo()).isEqualTo(testTokenMemo); - } - - @Test - void getSetName() { - var tokenCreateTransaction = new TokenCreateTransaction().setTokenName(testTokenName); - assertThat(tokenCreateTransaction.getTokenName()).isEqualTo(testTokenName); - } - - @Test - void getSetNameFrozen() { - var tx = spawnTestTransactionFungible(); - assertThrows(IllegalStateException.class, () -> tx.setTokenName(testTokenName)); - } - - @Test - void getSetSymbol() { - var tokenCreateTransaction = new TokenCreateTransaction().setTokenSymbol(testTokenSymbol); - assertThat(tokenCreateTransaction.getTokenSymbol()).isEqualTo(testTokenSymbol); - } - - @Test - void getSetSymbolFrozen() { - var tx = spawnTestTransactionFungible(); - assertThrows(IllegalStateException.class, () -> tx.setTokenSymbol(testTokenSymbol)); - } - - @Test - void getSetDecimals() { - var tokenCreateTransaction = new TokenCreateTransaction().setDecimals(testDecimals); - assertThat(tokenCreateTransaction.getDecimals()).isEqualTo(testDecimals); - } - - @Test - void getSetDecimalsFrozen() { - var tx = spawnTestTransactionFungible(); - assertThrows(IllegalStateException.class, () -> tx.setDecimals(testDecimals)); - } - - @Test - void getSetInitialSupply() { - var tokenCreateTransaction = new TokenCreateTransaction().setInitialSupply(testInitialSupply); - assertThat(tokenCreateTransaction.getInitialSupply()).isEqualTo(testInitialSupply); - } - - @Test - void getSetInitialSupplyFrozen() { - var tx = spawnTestTransactionFungible(); - assertThrows(IllegalStateException.class, () -> tx.setInitialSupply(testInitialSupply)); - } - - @Test - void getSetTreasuryAccountId() { - var tokenCreateTransaction = new TokenCreateTransaction().setTreasuryAccountId(testTreasuryAccountId); - assertThat(tokenCreateTransaction.getTreasuryAccountId()).isEqualTo(testTreasuryAccountId); - } - - @Test - void getSetTreasuryAccountIdFrozen() { - var tx = spawnTestTransactionFungible(); - assertThrows(IllegalStateException.class, () -> tx.setTreasuryAccountId(testTreasuryAccountId)); - } - - @Test - void getSetAdminKey() { - var tokenCreateTransaction = new TokenCreateTransaction().setAdminKey(testAdminKey); - assertThat(tokenCreateTransaction.getAdminKey()).isEqualTo(testAdminKey); - } - - @Test - void getSetAdminKeyFrozen() { - var tx = spawnTestTransactionFungible(); - assertThrows(IllegalStateException.class, () -> tx.setAdminKey(testAdminKey)); - } - - @Test - void getSetKycKey() { - var tokenCreateTransaction = new TokenCreateTransaction().setKycKey(testKycKey); - assertThat(tokenCreateTransaction.getKycKey()).isEqualTo(testKycKey); - } - - @Test - void getSetKycKeyFrozen() { - var tx = spawnTestTransactionFungible(); - assertThrows(IllegalStateException.class, () -> tx.setKycKey(testKycKey)); - } - - @Test - void getSetFreezeKey() { - var tokenCreateTransaction = new TokenCreateTransaction().setFreezeKey(testFreezeKey); - assertThat(tokenCreateTransaction.getFreezeKey()).isEqualTo(testFreezeKey); - } - - @Test - void getSetFreezeKeyFrozen() { - var tx = spawnTestTransactionFungible(); - assertThrows(IllegalStateException.class, () -> tx.setFreezeKey(testFreezeKey)); - } - - @Test - void getSetWipeKey() { - var tokenCreateTransaction = new TokenCreateTransaction().setWipeKey(testWipeKey); - assertThat(tokenCreateTransaction.getWipeKey()).isEqualTo(testWipeKey); - } - - @Test - void getSetWipeKeyFrozen() { - var tx = spawnTestTransactionFungible(); - assertThrows(IllegalStateException.class, () -> tx.setWipeKey(testWipeKey)); - } - - @Test - void getSetSupplyKey() { - var tokenCreateTransaction = new TokenCreateTransaction().setSupplyKey(testSupplyKey); - assertThat(tokenCreateTransaction.getSupplyKey()).isEqualTo(testSupplyKey); - } - - @Test - void getSetSupplyKeyFrozen() { - var tx = spawnTestTransactionFungible(); - assertThrows(IllegalStateException.class, () -> tx.setSupplyKey(testSupplyKey)); - } - - @Test - void getSetFeeScheduleKey() { - var tokenCreateTransaction = new TokenCreateTransaction().setFeeScheduleKey(testFeeScheduleKey); - assertThat(tokenCreateTransaction.getFeeScheduleKey()).isEqualTo(testFeeScheduleKey); - } - - @Test - void getSetFeeScheduleKeyFrozen() { - var tx = spawnTestTransactionFungible(); - assertThrows(IllegalStateException.class, () -> tx.setFeeScheduleKey(testFeeScheduleKey)); - } - - @Test - void getSetPauseKey() { - var tokenCreateTransaction = new TokenCreateTransaction().setPauseKey(testPauseKey); - assertThat(tokenCreateTransaction.getPauseKey()).isEqualTo(testPauseKey); - } - - @Test - void getSetPauseKeyFrozen() { - var tx = spawnTestTransactionFungible(); - assertThrows(IllegalStateException.class, () -> tx.setPauseKey(testPauseKey)); - } - - @Test - void getSetMetadataKey() { - var tokenCreateTransaction = new TokenCreateTransaction().setMetadataKey(testMetadataKey); - assertThat(tokenCreateTransaction.getMetadataKey()).isEqualTo(testMetadataKey); - } - - @Test - void getSetMetadataKeyFrozen() { - var tx = spawnTestTransactionFungible(); - assertThrows(IllegalStateException.class, () -> tx.setMetadataKey(testMetadataKey)); - } - - @Test - void getSetExpirationTime() { - var tokenCreateTransaction = new TokenCreateTransaction().setExpirationTime(testExpirationTime); - assertThat(tokenCreateTransaction.getExpirationTime()).isEqualTo(testExpirationTime); - } - - @Test - void getSetExpirationTimeFrozen() { - var tx = spawnTestTransactionFungible(); - assertThrows(IllegalStateException.class, () -> tx.setExpirationTime(testExpirationTime)); - } - - @Test - void getSetAutoRenewAccountId() { - var tokenCreateTransaction = new TokenCreateTransaction().setAutoRenewAccountId(testAutoRenewAccountId); - assertThat(tokenCreateTransaction.getAutoRenewAccountId()).isEqualTo(testAutoRenewAccountId); - } - - @Test - void getSetAutoRenewAccountIdFrozen() { - var tx = spawnTestTransactionFungible(); - assertThrows(IllegalStateException.class, () -> tx.setAutoRenewAccountId(testAutoRenewAccountId)); - } - - @Test - void getSetAutoRenewPeriod() { - var tokenCreateTransaction = new TokenCreateTransaction().setAutoRenewPeriod(testAutoRenewPeriod); - assertThat(tokenCreateTransaction.getAutoRenewPeriod()).isEqualTo(testAutoRenewPeriod); - } - - @Test - void getSetAutoRenewPeriodFrozen() { - var tx = spawnTestTransactionFungible(); - assertThrows(IllegalStateException.class, () -> tx.setAutoRenewPeriod(testAutoRenewPeriod)); - } - - @Test - void getSetTokenMemo() { - var tokenCreateTransaction = new TokenCreateTransaction().setTokenMemo(testTokenMemo); - assertThat(tokenCreateTransaction.getTokenMemo()).isEqualTo(testTokenMemo); - } - - @Test - void getSetTokenMemoFrozen() { - var tx = spawnTestTransactionFungible(); - assertThrows(IllegalStateException.class, () -> tx.setTokenMemo(testTokenMemo)); - } - - @Test - void getSetTokenType() { - final TokenType testTokenType = TokenType.FUNGIBLE_COMMON; - var tokenCreateTransaction = new TokenCreateTransaction().setTokenType(testTokenType); - assertThat(tokenCreateTransaction.getTokenType()).isEqualTo(testTokenType); - } - - @Test - void getSetTokenTypeFrozen() { - var tx = spawnTestTransactionFungible(); - assertThrows(IllegalStateException.class, () -> tx.setTokenType(TokenType.FUNGIBLE_COMMON)); - } - - @Test - void getSetSupplyType() { - final TokenSupplyType testTokenType = TokenSupplyType.FINITE; - var tokenCreateTransaction = new TokenCreateTransaction().setSupplyType(testTokenType); - assertThat(tokenCreateTransaction.getSupplyType()).isEqualTo(testTokenType); - } - - @Test - void getSetSupplyTypeFrozen() { - var tx = spawnTestTransactionFungible(); - assertThrows(IllegalStateException.class, () -> tx.setSupplyType(TokenSupplyType.FINITE)); - } - - @Test - void getSetMaxSupply() { - var tokenCreateTransaction = new TokenCreateTransaction().setMaxSupply(testMaxSupply); - assertThat(tokenCreateTransaction.getMaxSupply()).isEqualTo(testMaxSupply); - } - - @Test - void getSetMaxSupplyFrozen() { - var tx = spawnTestTransactionFungible(); - assertThrows(IllegalStateException.class, () -> tx.setMaxSupply(testMaxSupply)); - } - - @Test - void getSetMetadata() { - var tx = spawnTestTransactionFungible(); - assertThat(tx.getTokenMetadata()).isEqualTo(testMetadata); - } - - @Test - void getSetMetadataFrozen() { - var tx = spawnTestTransactionFungible(); - assertThrows(IllegalStateException.class, () -> tx.setTokenMetadata(testMetadata)); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenCreateTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenCreateTransactionTest.snap deleted file mode 100644 index d5d7047fc6..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenCreateTransactionTest.snap +++ /dev/null @@ -1,8 +0,0 @@ -com.hedera.hashgraph.sdk.TokenCreateTransactionTest.shouldSerializeFungible=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_creation {\n admin_key {\n ed25519: \"\\332\\207p\\020\\227\\206ns\\360\\335\\224,\\273>\\227\\0063)\\371\\005X\\206!\\261x\\322\\027Yh\\215G\\374\"\n }\n auto_renew_account {\n account_num: 8\n realm_num: 8\n shard_num: 8\n }\n custom_fees {\n fee_collector_account_id {\n account_num: 543\n realm_num: 0\n shard_num: 0\n }\n fixed_fee {\n amount: 3\n denominating_token_id {\n realm_num: 3\n shard_num: 4\n token_num: 2\n }\n }\n }\n decimals: 3\n expiry {\n seconds: 1554158542\n }\n fee_schedule_key {\n ed25519: \"K\\276\\225\\250m$\\370\\371gs\\261(&\\374\\276\\000\\226\\210\\313\\r\\312\\210\\317\\361\\027\\243\\250\\257P\\303q\\023\"\n }\n freeze_default: true\n freeze_key {\n ed25519: \"=\\355S\\343\\\"3S/=\\204b2L\\321\\023\\253\\276Os!m\\360mT\\241\\034\\266\\221\\301[\\'\\315\"\n }\n initial_supply: 30\n kyc_key {\n ed25519: \"\\373\\210\\2637\\337\\327ea{\\3442*\\347\\357\\2053\\326\\036d\\203\\005\\016 \\354HE\\2453\\275\\334\\244\\261\"\n }\n max_supply: 0\n memo: \"test memo\"\n metadata: \"\\001\\002\\003\\004\\005\"\n metadata_key {\n ed25519: \"\\024m\\354\\2222\\nnF\\353\\032Cv{\\261\\354\\225\\242\\346\\300%\\032\\260\\335x\\017\\343tt\\324\\272\\304\\025\"\n }\n name: \"test name\"\n pause_key {\n ed25519: \"\\321he\\251\\214\\370\\260\\267\\370\\3727v\\262\\r\\257\\305\\276\\004\\377\\353\\232$\\227r\\a\\203\\316\\231\\036+\\031t\"\n }\n supply_key {\n ed25519: \";\\2218S\\257\\245\\233U\\253\\305\\201\\302\\254\\r6X\\n\\302\\354\\244\\275\\020\\034\\002\\027?\\357\\002\\346w\\335\\325\"\n }\n symbol: \"test symbol\"\n treasury {\n account_num: 7\n realm_num: 7\n shard_num: 7\n }\n wipe_key {\n ed25519: \"R[\\234\\025_\\220+\\221-\\275\\201\\276\\246\\324:\\a}zb\\335\\037\\357\\317\\307}\\351aD\\325\\372\\303\\356\"\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] - - -com.hedera.hashgraph.sdk.TokenCreateTransactionTest.shouldSerializeNft=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_creation {\n admin_key {\n ed25519: \"\\332\\207p\\020\\227\\206ns\\360\\335\\224,\\273>\\227\\0063)\\371\\005X\\206!\\261x\\322\\027Yh\\215G\\374\"\n }\n auto_renew_account {\n account_num: 8\n realm_num: 8\n shard_num: 8\n }\n expiry {\n seconds: 1554158542\n }\n fee_schedule_key {\n ed25519: \"K\\276\\225\\250m$\\370\\371gs\\261(&\\374\\276\\000\\226\\210\\313\\r\\312\\210\\317\\361\\027\\243\\250\\257P\\303q\\023\"\n }\n freeze_key {\n ed25519: \"=\\355S\\343\\\"3S/=\\204b2L\\321\\023\\253\\276Os!m\\360mT\\241\\034\\266\\221\\301[\\'\\315\"\n }\n initial_supply: 0\n kyc_key {\n ed25519: \"\\373\\210\\2637\\337\\327ea{\\3442*\\347\\357\\2053\\326\\036d\\203\\005\\016 \\354HE\\2453\\275\\334\\244\\261\"\n }\n max_supply: 500\n memo: \"test memo\"\n metadata: \"\\001\\002\\003\\004\\005\"\n metadata_key {\n ed25519: \"\\024m\\354\\2222\\nnF\\353\\032Cv{\\261\\354\\225\\242\\346\\300%\\032\\260\\335x\\017\\343tt\\324\\272\\304\\025\"\n }\n name: \"test name\"\n pause_key {\n ed25519: \"\\321he\\251\\214\\370\\260\\267\\370\\3727v\\262\\r\\257\\305\\276\\004\\377\\353\\232$\\227r\\a\\203\\316\\231\\036+\\031t\"\n }\n supply_key {\n ed25519: \";\\2218S\\257\\245\\233U\\253\\305\\201\\302\\254\\r6X\\n\\302\\354\\244\\275\\020\\034\\002\\027?\\357\\002\\346w\\335\\325\"\n }\n supply_type: FINITE\n supply_type_value: 1\n symbol: \"test symbol\"\n token_type: NON_FUNGIBLE_UNIQUE\n token_type_value: 1\n treasury {\n account_num: 7\n realm_num: 7\n shard_num: 7\n }\n wipe_key {\n ed25519: \"R[\\234\\025_\\220+\\221-\\275\\201\\276\\246\\324:\\a}zb\\335\\037\\357\\317\\307}\\351aD\\325\\372\\303\\356\"\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenDeleteTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenDeleteTransactionTest.java deleted file mode 100644 index c7f22095fd..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenDeleteTransactionTest.java +++ /dev/null @@ -1,124 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenDeleteTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class TokenDeleteTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new TokenDeleteTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - private TokenDeleteTransaction spawnTestTransaction() { - return new TokenDeleteTransaction().setNodeAccountIds( - Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setTokenId(TokenId.fromString("1.2.3")).setMaxTransactionFee(new Hbar(1)).freeze().sign(unusedPrivateKey); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = TokenDeleteTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setTokenDeletion(TokenDeleteTransactionBody.newBuilder().build()).build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(TokenDeleteTransaction.class); - } - - @Test - void constructTokenDeleteTransaction() { - var transaction = new TokenDeleteTransaction(); - - assertThat(transaction.getTokenId()).isNull(); - } - - @Test - void ConstructTokenDeleteTransactionFromTransactionBodyProtobuf() { - var tokenId = TokenId.fromString("1.2.3"); - - var transactionBody = TokenDeleteTransactionBody.newBuilder().setToken(tokenId.toProtobuf()).build(); - var txBody = TransactionBody.newBuilder().setTokenDeletion(transactionBody).build(); - var tokenDeleteTransaction = new TokenDeleteTransaction(txBody); - - assertThat(tokenDeleteTransaction.getTokenId()).isEqualTo(tokenId); - } - - @Test - void getSetTokenId() { - var tokenId = TokenId.fromString("1.2.3"); - - var transaction = new TokenDeleteTransaction().setTokenId(tokenId); - - assertThat(transaction.getTokenId()).isEqualTo(tokenId); - } - - @Test - void getSetTokenIdFrozen() { - var tokenId = TokenId.fromString("1.2.3"); - - var tx = spawnTestTransaction(); - - assertThrows(IllegalStateException.class, () -> tx.setTokenId(tokenId)); - } - -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenDeleteTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenDeleteTransactionTest.snap deleted file mode 100644 index 9408562cda..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenDeleteTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.TokenDeleteTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_deletion {\n token {\n realm_num: 2\n shard_num: 1\n token_num: 3\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenDissociateTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenDissociateTransactionTest.java deleted file mode 100644 index 16733ec733..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenDissociateTransactionTest.java +++ /dev/null @@ -1,129 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenDissociateTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class TokenDissociateTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - private static final AccountId testAccountId = AccountId.fromString("6.9.0"); - - private static final List testTokenIds = Arrays.asList(TokenId.fromString("4.2.0"), - TokenId.fromString("4.2.1"), TokenId.fromString("4.2.2")); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new TokenDissociateTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - private TokenDissociateTransaction spawnTestTransaction() { - return new TokenDissociateTransaction().setNodeAccountIds( - Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setAccountId(testAccountId).setTokenIds(testTokenIds).setMaxTransactionFee(new Hbar(1)).freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = TokenDissociateTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setTokenDissociate(TokenDissociateTransactionBody.newBuilder().build()).build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(TokenDissociateTransaction.class); - } - - @Test - void constructTokenDissociateTransactionFromTransactionBodyProtobuf() { - var transactionBody = TokenDissociateTransactionBody.newBuilder().setAccount(testAccountId.toProtobuf()) - .addAllTokens(testTokenIds.stream().map(TokenId::toProtobuf).toList()).build(); - - var tx = TransactionBody.newBuilder().setTokenDissociate(transactionBody).build(); - var tokenDissociateTransaction = new TokenDissociateTransaction(tx); - - assertThat(tokenDissociateTransaction.getAccountId()).isEqualTo(testAccountId); - assertThat(tokenDissociateTransaction.getTokenIds().size()).isEqualTo(testTokenIds.size()); - } - - @Test - void getSetAccountId() { - var tokenDissociateTransaction = new TokenDissociateTransaction().setAccountId(testAccountId); - assertThat(tokenDissociateTransaction.getAccountId()).isEqualTo(testAccountId); - } - - @Test - void getSetAccountIdFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setAccountId(testAccountId)); - } - - @Test - void getSetTokenIds() { - var tokenDissociateTransaction = new TokenDissociateTransaction().setTokenIds(testTokenIds); - assertThat(tokenDissociateTransaction.getTokenIds()).isEqualTo(testTokenIds); - } - - @Test - void getSetTokenIdsFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setTokenIds(testTokenIds)); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenDissociateTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenDissociateTransactionTest.snap deleted file mode 100644 index 8604fbddbd..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenDissociateTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.TokenDissociateTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_dissociate {\n account {\n account_num: 0\n realm_num: 9\n shard_num: 6\n }\n tokens {\n realm_num: 2\n shard_num: 4\n token_num: 0\n }\n tokens {\n realm_num: 2\n shard_num: 4\n token_num: 1\n }\n tokens {\n realm_num: 2\n shard_num: 4\n token_num: 2\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenFeeScheduleUpdateTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenFeeScheduleUpdateTransactionTest.java deleted file mode 100644 index 1ec0049d50..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenFeeScheduleUpdateTransactionTest.java +++ /dev/null @@ -1,99 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenFeeScheduleUpdateTransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.ArrayList; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; - -public class TokenFeeScheduleUpdateTransactionTest { - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - private TokenFeeScheduleUpdateTransaction spawnTestTransaction() { - var customFees = new ArrayList(); - customFees.add(new CustomFixedFee() - .setFeeCollectorAccountId(new AccountId(4322)) - .setDenominatingTokenId(new TokenId(483902)) - .setAmount(10) - ); - customFees.add(new CustomFractionalFee() - .setFeeCollectorAccountId(new AccountId(389042)) - .setNumerator(3) - .setDenominator(7) - .setMin(3) - .setMax(100) - .setAssessmentMethod(FeeAssessmentMethod.EXCLUSIVE) - ); - - return new TokenFeeScheduleUpdateTransaction() - .setTokenId(new TokenId(8798)) - .setCustomFees(customFees) - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .freeze(); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new TokenFeeScheduleUpdateTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldSerialize() throws InvalidProtocolBufferException { - var originalUpdate = spawnTestTransaction(); - byte[] updateBytes = originalUpdate.toBytes(); - var copyUpdate = TokenFeeScheduleUpdateTransaction.fromBytes(updateBytes); - assertThat(copyUpdate.toString()).isEqualTo(originalUpdate.toString()); - SnapshotMatcher.expect(originalUpdate.toString()).toMatchSnapshot(); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setTokenFeeScheduleUpdate(TokenFeeScheduleUpdateTransactionBody.newBuilder().build()) - .build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(TokenFeeScheduleUpdateTransaction.class); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenFeeScheduleUpdateTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenFeeScheduleUpdateTransactionTest.snap deleted file mode 100644 index efe9d1349f..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenFeeScheduleUpdateTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.TokenFeeScheduleUpdateTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_fee_schedule_update {\n custom_fees {\n fee_collector_account_id {\n account_num: 4322\n realm_num: 0\n shard_num: 0\n }\n fixed_fee {\n amount: 10\n denominating_token_id {\n realm_num: 0\n shard_num: 0\n token_num: 483902\n }\n }\n }\n custom_fees {\n fee_collector_account_id {\n account_num: 389042\n realm_num: 0\n shard_num: 0\n }\n fractional_fee {\n fractional_amount {\n denominator: 7\n numerator: 3\n }\n maximum_amount: 100\n minimum_amount: 3\n net_of_transfers: true\n }\n }\n token_id {\n realm_num: 0\n shard_num: 0\n token_num: 8798\n }\n}\ntransaction_fee: 200000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenFreezeTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenFreezeTransactionTest.java deleted file mode 100644 index 82a064723f..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenFreezeTransactionTest.java +++ /dev/null @@ -1,92 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenFreezeAccountTransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; - -public class TokenFreezeTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction() - .toString() - ).toMatchSnapshot(); - } - - private TokenFreezeTransaction spawnTestTransaction() { - return new TokenFreezeTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setAccountId(AccountId.fromString("0.0.222")) - .setTokenId(TokenId.fromString("6.5.4")) - .setMaxTransactionFee(new Hbar(1)) - .freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new TokenFreezeTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = TokenFreezeTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setTokenFreeze(TokenFreezeAccountTransactionBody.newBuilder().build()) - .build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(TokenFreezeTransaction.class); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenFreezeTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenFreezeTransactionTest.snap deleted file mode 100644 index c56916ede3..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenFreezeTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.TokenFreezeTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_freeze {\n account {\n account_num: 222\n realm_num: 0\n shard_num: 0\n }\n token {\n realm_num: 5\n shard_num: 6\n token_num: 4\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenGrantKycTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenGrantKycTransactionTest.java deleted file mode 100644 index 3131d761ce..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenGrantKycTransactionTest.java +++ /dev/null @@ -1,125 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenGrantKycTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class TokenGrantKycTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - private static final TokenId testTokenId = TokenId.fromString("4.2.0"); - private static final AccountId testAccountId = AccountId.fromString("6.9.0"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); - } - - private TokenGrantKycTransaction spawnTestTransaction() { - return new TokenGrantKycTransaction().setNodeAccountIds( - Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setAccountId(testAccountId).setTokenId(testTokenId).setMaxTransactionFee(new Hbar(1)).freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = TokenGrantKycTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new TokenGrantKycTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setTokenGrantKyc(TokenGrantKycTransactionBody.newBuilder().build()).build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(TokenGrantKycTransaction.class); - } - - @Test - void constructTokenGrantKycTransactionFromTransactionBodyProtobuf() { - var transactionBody = TokenGrantKycTransactionBody.newBuilder().setAccount(testAccountId.toProtobuf()) - .setToken(testTokenId.toProtobuf()).build(); - - var tx = TransactionBody.newBuilder().setTokenGrantKyc(transactionBody).build(); - var tokenGrantKycTransaction = new TokenGrantKycTransaction(tx); - - assertThat(tokenGrantKycTransaction.getTokenId()).isEqualTo(testTokenId); - } - - @Test - void getSetAccountId() { - var tokenGrantKycTransaction = new TokenGrantKycTransaction().setAccountId(testAccountId); - assertThat(tokenGrantKycTransaction.getAccountId()).isEqualTo(testAccountId); - } - - @Test - void getSetAccountIdFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setAccountId(testAccountId)); - } - - @Test - void getSetTokenId() { - var tokenGrantKycTransaction = new TokenGrantKycTransaction().setTokenId(testTokenId); - assertThat(tokenGrantKycTransaction.getTokenId()).isEqualTo(testTokenId); - } - - @Test - void getSetTokenIdFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setTokenId(testTokenId)); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenGrantKycTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenGrantKycTransactionTest.snap deleted file mode 100644 index 80980fb0bf..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenGrantKycTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.TokenGrantKycTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_grant_kyc {\n account {\n account_num: 0\n realm_num: 9\n shard_num: 6\n }\n token {\n realm_num: 2\n shard_num: 4\n token_num: 0\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenInfoQueryTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenInfoQueryTest.java deleted file mode 100644 index 5579b4e359..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenInfoQueryTest.java +++ /dev/null @@ -1,56 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class TokenInfoQueryTest { - private static final TokenId testTokenId = TokenId.fromString("4.2.0"); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - var builder = com.hedera.hashgraph.sdk.proto.Query.newBuilder(); - new TokenInfoQuery().setTokenId(testTokenId).setMaxQueryPayment(Hbar.fromTinybars(100_000)) - .onMakeRequest(builder, QueryHeader.newBuilder().build()); - SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")).toMatchSnapshot(); - } - - @Test - void getSetTokenId() { - var tokenInfoQuery = new TokenInfoQuery().setTokenId(testTokenId); - assertThat(tokenInfoQuery.getTokenId()).isEqualTo(testTokenId); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenInfoQueryTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenInfoQueryTest.snap deleted file mode 100644 index 91fafea344..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenInfoQueryTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.TokenInfoQueryTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.Query\ntoken_get_info {\n header {\n }\n token {\n realm_num: 2\n shard_num: 4\n token_num: 0\n }\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenInfoTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenInfoTest.java deleted file mode 100644 index 6e0dfa52fc..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenInfoTest.java +++ /dev/null @@ -1,259 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Duration; -import java.time.Instant; -import java.util.Arrays; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - -// TODO: update this, test deepClone() - -public class TokenInfoTest { - /* - if we will init PrivateKey using method `PrivateKey.fromSeedECDSAsecp256k1(byte[] seed)` (like in C++ SDK, for example) - => we will get public key each time we run tests on different machines - => io.github.jsonSnapshot.SnapshotMatcher will fail tests - => we need to init PrivateKey fromString to get the same key each time - => `toProtobuf()` tests uses getEd25519() method to assert equality - */ - private static final PublicKey testAdminKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e11") - .getPublicKey(); - private static final PublicKey testKycKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e12") - .getPublicKey(); - private static final PublicKey testFreezeKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e13") - .getPublicKey(); - private static final PublicKey testWipeKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e14") - .getPublicKey(); - private static final PublicKey testSupplyKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e15") - .getPublicKey(); - private static final PublicKey testFeeScheduleKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e16") - .getPublicKey(); - private static final PublicKey testPauseKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e17") - .getPublicKey(); - private static final PublicKey testMetadataKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e18") - .getPublicKey(); - private static final TokenId testTokenId = TokenId.fromString("0.6.9"); - private static final AccountId testTreasuryAccountId = AccountId.fromString("7.7.7"); - private static final AccountId testAutoRenewAccountId = AccountId.fromString("8.9.0"); - private static final String testTokenName = "test token name"; - private static final String testTokenSymbol = "TTN"; - private static final String testTokenMemo = "memo"; - private static final int testTokenDecimals = 3; - private static final long testTokenTotalSupply = 1000L; - private static final Boolean testTokenFreezeStatus = true; - private static final Boolean testTokenKycStatus = true; - private static final boolean testTokenIsDeleted = false; - private static final List testTokenCustomFees = Arrays.asList( - new CustomFixedFee().setFeeCollectorAccountId(new AccountId(4322)).setDenominatingTokenId(new TokenId(483902)) - .setAmount(10), - new CustomFractionalFee().setFeeCollectorAccountId(new AccountId(389042)).setNumerator(3).setDenominator(7) - .setMin(3).setMax(100)); - private static final TokenType testTokenType = TokenType.FUNGIBLE_COMMON; - private static final TokenSupplyType testTokenSupplyType = TokenSupplyType.FINITE; - private static final long testTokenMaxSupply = 1000000L; - private static final boolean testTokenPauseStatus = true; - private static final LedgerId testTokenLedgerId = LedgerId.MAINNET; - private static final Duration testAutoRenewPeriod = Duration.ofHours(10); - private static final Instant testExpirationTime = Instant.ofEpochSecond(1554158542); - private static final byte[] testMetadata = new byte[]{1, 2, 3, 4, 5}; - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - private static TokenInfo spawnTokenInfoExample() { - return new TokenInfo(testTokenId, testTokenName, testTokenSymbol, testTokenDecimals, testTokenTotalSupply, - testTreasuryAccountId, testAdminKey, testKycKey, testFreezeKey, testWipeKey, testSupplyKey, - testFeeScheduleKey, testTokenFreezeStatus, testTokenKycStatus, testTokenIsDeleted, testAutoRenewAccountId, - testAutoRenewPeriod, testExpirationTime, testTokenMemo, testTokenCustomFees, testTokenType, - testTokenSupplyType, testTokenMaxSupply, testPauseKey, testTokenPauseStatus, testMetadata, testMetadataKey, - testTokenLedgerId); - } - - @Test - void shouldSerialize() throws Exception { - var originalTokenInfo = spawnTokenInfoExample(); - byte[] tokenInfoBytes = originalTokenInfo.toBytes(); - var copyTokenInfo = TokenInfo.fromBytes(tokenInfoBytes); - assertThat(copyTokenInfo.toString()).isEqualTo(originalTokenInfo.toString()); - SnapshotMatcher.expect(originalTokenInfo.toString()).toMatchSnapshot(); - } - - @Test - void fromProtobuf() { - var tokenInfoProto = spawnTokenInfoExample().toProtobuf(); - - var tokenInfo = TokenInfo.fromProtobuf(tokenInfoProto); - - assertThat(tokenInfo.tokenId).isEqualTo(testTokenId); - assertThat(tokenInfo.name).isEqualTo(testTokenName); - assertThat(tokenInfo.symbol).isEqualTo(testTokenSymbol); - assertThat(tokenInfo.decimals).isEqualTo(testTokenDecimals); - assertThat(tokenInfo.totalSupply).isEqualTo(testTokenTotalSupply); - assertThat(tokenInfo.treasuryAccountId).isEqualTo(testTreasuryAccountId); - assertThat(tokenInfo.adminKey.toBytes()).isEqualTo(testAdminKey.toBytes()); - assertThat(tokenInfo.kycKey.toBytes()).isEqualTo(testKycKey.toBytes()); - assertThat(tokenInfo.freezeKey.toBytes()).isEqualTo(testFreezeKey.toBytes()); - assertThat(tokenInfo.wipeKey.toBytes()).isEqualTo(testWipeKey.toBytes()); - assertThat(tokenInfo.supplyKey.toBytes()).isEqualTo(testSupplyKey.toBytes()); - assertThat(tokenInfo.defaultFreezeStatus).isEqualTo(testTokenFreezeStatus); - assertThat(tokenInfo.defaultKycStatus).isEqualTo(testTokenKycStatus); - assertThat(tokenInfo.isDeleted).isEqualTo(testTokenIsDeleted); - assertThat(tokenInfo.autoRenewAccount).isEqualTo(testAutoRenewAccountId); - assertThat(tokenInfo.autoRenewPeriod).isEqualTo(testAutoRenewPeriod); - assertThat(tokenInfo.expirationTime).isEqualTo(testExpirationTime); - assertThat(tokenInfo.tokenMemo).isEqualTo(testTokenMemo); - assertThat(tokenInfo.tokenType).isEqualTo(testTokenType); - assertThat(tokenInfo.supplyType).isEqualTo(testTokenSupplyType); - assertThat(tokenInfo.maxSupply).isEqualTo(testTokenMaxSupply); - assertThat(tokenInfo.feeScheduleKey.toBytes()).isEqualTo(testFeeScheduleKey.toBytes()); - assertThat(tokenInfo.customFees).hasSize(testTokenCustomFees.size()); - assertThat(tokenInfo.pauseKey.toBytes()).isEqualTo(testPauseKey.toBytes()); - assertThat(tokenInfo.pauseStatus).isEqualTo(testTokenPauseStatus); - assertThat(tokenInfo.metadata).isEqualTo(testMetadata); - assertThat(tokenInfo.metadataKey.toBytes()).isEqualTo(testMetadataKey.toBytes()); - assertThat(tokenInfo.ledgerId).isEqualTo(testTokenLedgerId); - } - - @Test - void fromBytes() throws InvalidProtocolBufferException { - var tokenInfoProto = spawnTokenInfoExample().toProtobuf(); - - var tokenInfo = TokenInfo.fromBytes(tokenInfoProto.toByteArray()); - - assertThat(tokenInfo.tokenId).isEqualTo(testTokenId); - assertThat(tokenInfo.name).isEqualTo(testTokenName); - assertThat(tokenInfo.symbol).isEqualTo(testTokenSymbol); - assertThat(tokenInfo.decimals).isEqualTo(testTokenDecimals); - assertThat(tokenInfo.totalSupply).isEqualTo(testTokenTotalSupply); - assertThat(tokenInfo.treasuryAccountId).isEqualTo(testTreasuryAccountId); - assertThat(tokenInfo.adminKey.toBytes()).isEqualTo(testAdminKey.toBytes()); - assertThat(tokenInfo.kycKey.toBytes()).isEqualTo(testKycKey.toBytes()); - assertThat(tokenInfo.freezeKey.toBytes()).isEqualTo(testFreezeKey.toBytes()); - assertThat(tokenInfo.wipeKey.toBytes()).isEqualTo(testWipeKey.toBytes()); - assertThat(tokenInfo.supplyKey.toBytes()).isEqualTo(testSupplyKey.toBytes()); - assertThat(tokenInfo.defaultFreezeStatus).isEqualTo(testTokenFreezeStatus); - assertThat(tokenInfo.defaultKycStatus).isEqualTo(testTokenKycStatus); - assertThat(tokenInfo.isDeleted).isEqualTo(testTokenIsDeleted); - assertThat(tokenInfo.autoRenewAccount).isEqualTo(testAutoRenewAccountId); - assertThat(tokenInfo.autoRenewPeriod).isEqualTo(testAutoRenewPeriod); - assertThat(tokenInfo.expirationTime).isEqualTo(testExpirationTime); - assertThat(tokenInfo.tokenMemo).isEqualTo(testTokenMemo); - assertThat(tokenInfo.tokenType).isEqualTo(testTokenType); - assertThat(tokenInfo.supplyType).isEqualTo(testTokenSupplyType); - assertThat(tokenInfo.maxSupply).isEqualTo(testTokenMaxSupply); - assertThat(tokenInfo.feeScheduleKey.toBytes()).isEqualTo(testFeeScheduleKey.toBytes()); - assertThat(tokenInfo.customFees).hasSize(testTokenCustomFees.size()); - assertThat(tokenInfo.pauseKey.toBytes()).isEqualTo(testPauseKey.toBytes()); - assertThat(tokenInfo.pauseStatus).isEqualTo(testTokenPauseStatus); - assertThat(tokenInfo.metadata).isEqualTo(testMetadata); - assertThat(tokenInfo.metadataKey.toBytes()).isEqualTo(testMetadataKey.toBytes()); - assertThat(tokenInfo.ledgerId).isEqualTo(testTokenLedgerId); - } - - @Test - void toProtobuf() { - var tokenInfoProto = spawnTokenInfoExample().toProtobuf(); - - assertThat(tokenInfoProto.getTokenInfo().getTokenId().getShardNum()).isEqualTo(testTokenId.shard); - assertThat(tokenInfoProto.getTokenInfo().getTokenId().getRealmNum()).isEqualTo(testTokenId.realm); - assertThat(tokenInfoProto.getTokenInfo().getTokenId().getTokenNum()).isEqualTo(testTokenId.num); - assertThat(tokenInfoProto.getTokenInfo().getName()).isEqualTo(testTokenName); - assertThat(tokenInfoProto.getTokenInfo().getSymbol()).isEqualTo(testTokenSymbol); - assertThat(tokenInfoProto.getTokenInfo().getDecimals()).isEqualTo(testTokenDecimals); - assertThat(tokenInfoProto.getTokenInfo().getTotalSupply()).isEqualTo(testTokenTotalSupply); - assertThat(tokenInfoProto.getTokenInfo().getTreasury().getShardNum()).isEqualTo(testTreasuryAccountId.shard); - assertThat(tokenInfoProto.getTokenInfo().getTreasury().getRealmNum()).isEqualTo(testTreasuryAccountId.realm); - assertThat(tokenInfoProto.getTokenInfo().getTreasury().getAccountNum()).isEqualTo(testTreasuryAccountId.num); - assertThat(tokenInfoProto.getTokenInfo().getAdminKey().getEd25519().toByteArray()).isEqualTo( - testAdminKey.toBytesRaw()); - assertThat(tokenInfoProto.getTokenInfo().getKycKey().getEd25519().toByteArray()).isEqualTo( - testKycKey.toBytesRaw()); - assertThat(tokenInfoProto.getTokenInfo().getFreezeKey().getEd25519().toByteArray()).isEqualTo( - testFreezeKey.toBytesRaw()); - assertThat(tokenInfoProto.getTokenInfo().getWipeKey().getEd25519().toByteArray()).isEqualTo( - testWipeKey.toBytesRaw()); - assertThat(tokenInfoProto.getTokenInfo().getSupplyKey().getEd25519().toByteArray()).isEqualTo( - testSupplyKey.toBytesRaw()); - assertThat(tokenInfoProto.getTokenInfo().getDefaultFreezeStatus()).isEqualTo( - TokenInfo.freezeStatusToProtobuf(testTokenFreezeStatus)); - assertThat(tokenInfoProto.getTokenInfo().getDefaultKycStatus()).isEqualTo( - TokenInfo.kycStatusToProtobuf(testTokenKycStatus)); - assertThat(tokenInfoProto.getTokenInfo().getDeleted()).isEqualTo(testTokenIsDeleted); - assertThat(tokenInfoProto.getTokenInfo().getAutoRenewAccount().getShardNum()).isEqualTo( - testAutoRenewAccountId.shard); - assertThat(tokenInfoProto.getTokenInfo().getAutoRenewAccount().getRealmNum()).isEqualTo( - testAutoRenewAccountId.realm); - assertThat(tokenInfoProto.getTokenInfo().getAutoRenewAccount().getAccountNum()).isEqualTo( - testAutoRenewAccountId.num); - assertThat(tokenInfoProto.getTokenInfo().getAutoRenewPeriod().getSeconds()).isEqualTo( - testAutoRenewPeriod.toSeconds()); - assertThat(tokenInfoProto.getTokenInfo().getExpiry().getSeconds()).isEqualTo( - testExpirationTime.getEpochSecond()); - assertThat(tokenInfoProto.getTokenInfo().getMemo()).isEqualTo(testTokenMemo); - assertThat(tokenInfoProto.getTokenInfo().getTokenType()).isEqualTo( - com.hedera.hashgraph.sdk.proto.TokenType.valueOf(testTokenType.name())); - assertThat(tokenInfoProto.getTokenInfo().getSupplyType()).isEqualTo( - com.hedera.hashgraph.sdk.proto.TokenSupplyType.valueOf(testTokenSupplyType.name())); - assertThat(tokenInfoProto.getTokenInfo().getMaxSupply()).isEqualTo(testTokenMaxSupply); - assertThat(tokenInfoProto.getTokenInfo().getFeeScheduleKey().getEd25519().toByteArray()).isEqualTo( - testFeeScheduleKey.toBytesRaw()); - assertThat(tokenInfoProto.getTokenInfo().getCustomFeesList()).hasSize(testTokenCustomFees.size()); - assertThat(tokenInfoProto.getTokenInfo().getPauseKey().getEd25519().toByteArray()).isEqualTo( - testPauseKey.toBytesRaw()); - assertThat(tokenInfoProto.getTokenInfo().getPauseStatus()).isEqualTo( - TokenInfo.pauseStatusToProtobuf(testTokenPauseStatus)); - assertThat(tokenInfoProto.getTokenInfo().getMetadata().toByteArray()).isEqualTo( - testMetadata); - assertThat(tokenInfoProto.getTokenInfo().getMetadataKey().getEd25519().toByteArray()).isEqualTo( - testMetadataKey.toBytesRaw()); - assertThat(tokenInfoProto.getTokenInfo().getLedgerId()).isEqualTo(testTokenLedgerId.toByteString()); - } - - @Test - void toBytes() { - var tokenInfo = spawnTokenInfoExample(); - var bytes = tokenInfo.toBytes(); - assertThat(bytes).isEqualTo(tokenInfo.toProtobuf().toByteArray()); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenMintTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenMintTransactionTest.java deleted file mode 100644 index 4dd1ab71c3..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenMintTransactionTest.java +++ /dev/null @@ -1,166 +0,0 @@ -package com.hedera.hashgraph.sdk; - -import com.google.common.collect.Iterables; -import com.google.protobuf.ByteString; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenMintTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class TokenMintTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - private static final TokenId testTokenId = TokenId.fromString("4.2.0"); - private static final Long testAmount = 10L; - private static final List testMetadataList = List.of(new byte[]{1, 2, 3, 4, 5}); - private static final ByteString testMetadataByteString = ByteString.copyFrom(new byte[]{1, 2, 3, 4, 5}); - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction() - .toString() - ).toMatchSnapshot(); - } - - @Test - void shouldSerializeMetadata() { - SnapshotMatcher.expect(spawnMetadataTestTransaction() - .toString() - ).toMatchSnapshot(); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new TokenMintTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - private TokenMintTransaction spawnTestTransaction() { - return new TokenMintTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setTokenId(testTokenId) - .setAmount(testAmount) - .setMaxTransactionFee(new Hbar(1)) - .freeze() - .sign(unusedPrivateKey); - } - - private TokenMintTransaction spawnMetadataTestTransaction() { - return new TokenMintTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setTokenId(TokenId.fromString("1.2.3")) - .setMetadata(testMetadataList) - .setMaxTransactionFee(new Hbar(1)) - .freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = TokenUpdateTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldBytesMetadata() throws Exception { - var tx = spawnMetadataTestTransaction(); - var tx2 = TokenUpdateTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setTokenMint(TokenMintTransactionBody.newBuilder().build()) - .build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(TokenMintTransaction.class); - } - - @Test - void constructTokenMintTransactionFromTransactionBodyProtobuf() { - var transactionBody = TokenMintTransactionBody.newBuilder() - .setToken(testTokenId.toProtobuf()) - .setAmount(testAmount) - .addMetadata(testMetadataByteString) - .build(); - - var tx = TransactionBody.newBuilder().setTokenMint(transactionBody).build(); - var tokenMintTransaction = new TokenMintTransaction(tx); - - assertThat(tokenMintTransaction.getTokenId()).isEqualTo(testTokenId); - assertThat(tokenMintTransaction.getAmount()).isEqualTo(testAmount); - assertThat(Iterables.getLast(tokenMintTransaction.getMetadata())).isEqualTo( - testMetadataByteString.toByteArray()); - } - - @Test - void getSetTokenId() { - var tokenMintTransaction = new TokenMintTransaction().setTokenId(testTokenId); - assertThat(tokenMintTransaction.getTokenId()).isEqualTo(testTokenId); - } - - @Test - void getSetTokenIdFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setTokenId(testTokenId)); - } - - @Test - void getSetAmount() { - var tokenMintTransaction = new TokenMintTransaction().setAmount(testAmount); - assertThat(tokenMintTransaction.getAmount()).isEqualTo(testAmount); - } - - @Test - void getSetAmountFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setAmount(testAmount)); - } - - @Test - void getSetMetadata() { - var tokenMintTransaction = new TokenMintTransaction().setMetadata(testMetadataList); - assertThat(tokenMintTransaction.getMetadata()).isEqualTo(testMetadataList); - } - - @Test - void getSetMetadataFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setMetadata(testMetadataList)); - } - - @Test - void addMetadata() { - var tokenMintTransaction = new TokenMintTransaction().addMetadata(Iterables.getLast(testMetadataList)); - assertThat(Iterables.getLast(tokenMintTransaction.getMetadata())).isEqualTo( - Iterables.getLast(testMetadataList)); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenMintTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenMintTransactionTest.snap deleted file mode 100644 index deb2a4bf7f..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenMintTransactionTest.snap +++ /dev/null @@ -1,8 +0,0 @@ -com.hedera.hashgraph.sdk.TokenMintTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_mint {\n amount: 10\n token {\n realm_num: 2\n shard_num: 4\n token_num: 0\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] - - -com.hedera.hashgraph.sdk.TokenMintTransactionTest.shouldSerializeMetadata=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_mint {\n amount: 0\n metadata: \"\\001\\002\\003\\004\\005\"\n token {\n realm_num: 2\n shard_num: 1\n token_num: 3\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenNftInfoQueryTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenNftInfoQueryTest.java deleted file mode 100644 index d3910563ed..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenNftInfoQueryTest.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.hedera.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class TokenNftInfoQueryTest { - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - var builder = com.hedera.hashgraph.sdk.proto.Query.newBuilder(); - new TokenNftInfoQuery() - .setNftId(TokenId.fromString("0.0.5005").nft(101)) - .setMaxQueryPayment(Hbar.fromTinybars(100_000)) - .onMakeRequest(builder, QueryHeader.newBuilder().build()); - SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")).toMatchSnapshot(); - } - - @Test - void propertiesTest() { - var tokenId = TokenId.fromString("0.0.5005"); - var query = new TokenNftInfoQuery() - .byAccountId(AccountId.fromString("0.0.123")) - .byTokenId(tokenId) - .setStart(5) - .setEnd(8) - .setNftId(tokenId.nft(101)) - .setMaxQueryPayment(Hbar.fromTinybars(100_000)); - - assertThat(query.getNftId()).hasToString("0.0.5005/101"); - assertThat(query.getTokenId()).isEqualTo(tokenId); - assertThat(query.getAccountId()).hasToString("0.0.123"); - assertThat(query.getStart()).isEqualTo(5); - assertThat(query.getEnd()).isEqualTo(8); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenNftInfoQueryTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenNftInfoQueryTest.snap deleted file mode 100644 index 3fa3c8b9f3..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenNftInfoQueryTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.TokenNftInfoQueryTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.Query\ntoken_get_nft_info {\n header {\n }\n nft_i_d {\n serial_number: 101\n token_i_d {\n realm_num: 0\n shard_num: 0\n token_num: 5005\n }\n }\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenNftInfoTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenNftInfoTest.snap deleted file mode 100644 index 4b4007c092..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenNftInfoTest.snap +++ /dev/null @@ -1,8 +0,0 @@ -com.hedera.hashgraph.sdk.TokenNftInfoTest.shouldSerialize=[ - "TokenNftInfo{nftId=1.2.3/4, accountId=5.6.7, creationTime=2019-04-01T22:42:22Z, metadata=[-34, -83, -66, -17], ledgerId=mainnet, spenderId=8.9.10}" -] - - -com.hedera.hashgraph.sdk.TokenNftInfoTest.shouldSerializeNullSpender=[ - "TokenNftInfo{nftId=1.2.3/4, accountId=5.6.7, creationTime=2019-04-01T22:42:22Z, metadata=[-34, -83, -66, -17], ledgerId=mainnet, spenderId=null}" -] diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenPauseTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenPauseTransactionTest.java deleted file mode 100644 index fa86516a8a..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenPauseTransactionTest.java +++ /dev/null @@ -1,110 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenPauseTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class TokenPauseTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - private static final TokenId testTokenId = TokenId.fromString("4.2.0"); - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - TokenPauseTransaction spawnTestTransaction() { - return new TokenPauseTransaction().setNodeAccountIds( - Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setTokenId(testTokenId).setMaxTransactionFee(new Hbar(1)).freeze().sign(unusedPrivateKey); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); - } - - @Test - void shouldBytesNft() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new TokenPauseTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setTokenPause(TokenPauseTransactionBody.newBuilder().build()).build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(TokenPauseTransaction.class); - } - - @Test - void constructTokenPauseTransactionFromTransactionBodyProtobuf() { - var transactionBody = TokenPauseTransactionBody.newBuilder().setToken(testTokenId.toProtobuf()).build(); - - var tx = TransactionBody.newBuilder().setTokenPause(transactionBody).build(); - var tokenPauseTransaction = new TokenPauseTransaction(tx); - - assertThat(tokenPauseTransaction.getTokenId()).isEqualTo(testTokenId); - } - - @Test - void getSetTokenId() { - var tokenPauseTransaction = new TokenPauseTransaction().setTokenId(testTokenId); - assertThat(tokenPauseTransaction.getTokenId()).isEqualTo(testTokenId); - } - - @Test - void getSetTokenIdFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setTokenId(testTokenId)); - } - -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenPauseTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenPauseTransactionTest.snap deleted file mode 100644 index 6f138223e4..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenPauseTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.TokenPauseTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_pause {\n token {\n realm_num: 2\n shard_num: 4\n token_num: 0\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenRejectTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenRejectTransactionTest.java deleted file mode 100644 index 5698fd8857..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenRejectTransactionTest.java +++ /dev/null @@ -1,160 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenReference; -import com.hedera.hashgraph.sdk.proto.TokenRejectTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import java.time.Instant; -import java.util.Arrays; -import java.util.List; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -public class TokenRejectTransactionTest { - - private static final PrivateKey TEST_PRIVATE_KEY = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - private static final AccountId TEST_OWNER_ID = AccountId.fromString("0.6.9"); - - private static final List TEST_TOKEN_IDS = List.of( - TokenId.fromString("1.2.3"), - TokenId.fromString("4.5.6"), - TokenId.fromString("7.8.9")); - - private static final List TEST_NFT_IDS = List.of( - new NftId(TokenId.fromString("4.5.6"), 2), - new NftId(TokenId.fromString("7.8.9"), 3)); - - final Instant TEST_VALID_START = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); - } - - private TokenRejectTransaction spawnTestTransaction() { - return new TokenRejectTransaction().setNodeAccountIds( - Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), TEST_VALID_START)) - .setOwnerId(TEST_OWNER_ID).setTokenIds(TEST_TOKEN_IDS).setNftIds(TEST_NFT_IDS) - .setMaxTransactionFee(new Hbar(1)).freeze().sign(TEST_PRIVATE_KEY); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new TokenRejectTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = TokenUpdateNftsTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setTokenReject(TokenRejectTransactionBody.newBuilder().build()).build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(TokenRejectTransaction.class); - } - - @Test - void constructTokenRejectTransactionFromTransactionBodyProtobuf() { - var transactionBodyBuilder = TokenRejectTransactionBody.newBuilder(); - - transactionBodyBuilder.setOwner(TEST_OWNER_ID.toProtobuf()); - - for (TokenId tokenId : TEST_TOKEN_IDS) { - transactionBodyBuilder.addRejections(TokenReference.newBuilder().setFungibleToken(tokenId.toProtobuf()).build()); - } - - for (NftId nftId : TEST_NFT_IDS) { - transactionBodyBuilder.addRejections(TokenReference.newBuilder().setNft(nftId.toProtobuf()).build()); - } - - var tx = TransactionBody.newBuilder().setTokenReject(transactionBodyBuilder.build()).build(); - var tokenRejectTransaction = new TokenRejectTransaction(tx); - - assertThat(tokenRejectTransaction.getOwnerId()).isEqualTo(TEST_OWNER_ID); - assertThat(tokenRejectTransaction.getTokenIds()).hasSize(TEST_TOKEN_IDS.size()); - assertThat(tokenRejectTransaction.getNftIds()).hasSize(TEST_NFT_IDS.size()); - } - - - @Test - void getSetOwnerId() { - var transaction = new TokenRejectTransaction().setOwnerId(TEST_OWNER_ID); - assertThat(transaction.getOwnerId()).isEqualTo(TEST_OWNER_ID); - } - - @Test - void getSetOwnerIdFrozen() { - var transaction = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> transaction.setOwnerId(TEST_OWNER_ID)); - } - - @Test - void getSetTokenIds() { - var transaction = new TokenRejectTransaction().setTokenIds(TEST_TOKEN_IDS); - assertThat(transaction.getTokenIds()).isEqualTo(TEST_TOKEN_IDS); - } - - @Test - void getSetTokenIdFrozen() { - var transaction = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> transaction.setTokenIds(TEST_TOKEN_IDS)); - } - - @Test - void getSetNftIds() { - var transaction = new TokenRejectTransaction().setNftIds(TEST_NFT_IDS); - assertThat(transaction.getNftIds()).isEqualTo(TEST_NFT_IDS); - } - - @Test - void getSetNftIdFrozen() { - var transaction = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> transaction.setNftIds(TEST_NFT_IDS)); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenRejectTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenRejectTransactionTest.snap deleted file mode 100644 index 56b7080ccb..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenRejectTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.TokenRejectTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_reject {\n owner {\n account_num: 9\n realm_num: 6\n shard_num: 0\n }\n rejections {\n fungible_token {\n realm_num: 2\n shard_num: 1\n token_num: 3\n }\n }\n rejections {\n fungible_token {\n realm_num: 5\n shard_num: 4\n token_num: 6\n }\n }\n rejections {\n fungible_token {\n realm_num: 8\n shard_num: 7\n token_num: 9\n }\n }\n rejections {\n nft {\n serial_number: 2\n token_i_d {\n realm_num: 5\n shard_num: 4\n token_num: 6\n }\n }\n }\n rejections {\n nft {\n serial_number: 3\n token_i_d {\n realm_num: 8\n shard_num: 7\n token_num: 9\n }\n }\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenRelationshipTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenRelationshipTest.java deleted file mode 100644 index a0e202fb5f..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenRelationshipTest.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.hedera.hashgraph.sdk; - -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class TokenRelationshipTest { - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - TokenRelationship spawnTokenRelationshipExample() { - return new TokenRelationship( - TokenId.fromString("1.2.3"), - "ABC", - 55, - true, - true, - 4, - true - ); - } - - @Test - void shouldSerializeTokenRelationship() throws Exception { - var originalTokenRelationship = spawnTokenRelationshipExample(); - byte[] tokenRelationshipBytes = originalTokenRelationship.toBytes(); - var copyTokenRelationship = TokenRelationship.fromBytes(tokenRelationshipBytes); - assertThat(copyTokenRelationship.toString().replaceAll("@[A-Za-z0-9]+", "")) - .isEqualTo(originalTokenRelationship.toString().replaceAll("@[A-Za-z0-9]+", "")); - SnapshotMatcher.expect(originalTokenRelationship.toString().replaceAll("@[A-Za-z0-9]+", "")).toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenRelationshipTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenRelationshipTest.snap deleted file mode 100644 index 5334a5510c..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenRelationshipTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.TokenRelationshipTest.shouldSerializeTokenRelationship=[ - "TokenRelationship{tokenId=1.2.3, symbol=ABC, balance=55, kycStatus=true, freezeStatus=true, decimals=4, automaticAssociation=true}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenRevokeKycTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenRevokeKycTransactionTest.java deleted file mode 100644 index 04264afc76..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenRevokeKycTransactionTest.java +++ /dev/null @@ -1,125 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenRevokeKycTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class TokenRevokeKycTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - private static final TokenId testTokenId = TokenId.fromString("4.2.0"); - private static final AccountId testAccountId = AccountId.fromString("6.9.0"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); - } - - private TokenRevokeKycTransaction spawnTestTransaction() { - return new TokenRevokeKycTransaction().setNodeAccountIds( - Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setAccountId(testAccountId).setTokenId(testTokenId).setMaxTransactionFee(new Hbar(1)).freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new TokenRevokeKycTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = TokenRevokeKycTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setTokenRevokeKyc(TokenRevokeKycTransactionBody.newBuilder().build()).build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(TokenRevokeKycTransaction.class); - } - - @Test - void constructTokenRevokeKycTransactionFromTransactionBodyProtobuf() { - var transactionBody = TokenRevokeKycTransactionBody.newBuilder().setAccount(testAccountId.toProtobuf()) - .setToken(testTokenId.toProtobuf()).build(); - - var tx = TransactionBody.newBuilder().setTokenRevokeKyc(transactionBody).build(); - var tokenRevokeKycTransaction = new TokenRevokeKycTransaction(tx); - - assertThat(tokenRevokeKycTransaction.getTokenId()).isEqualTo(testTokenId); - } - - @Test - void getSetAccountId() { - var tokenRevokeKycTransaction = new TokenRevokeKycTransaction().setAccountId(testAccountId); - assertThat(tokenRevokeKycTransaction.getAccountId()).isEqualTo(testAccountId); - } - - @Test - void getSetAccountIdFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setAccountId(testAccountId)); - } - - @Test - void getSetTokenId() { - var tokenRevokeKycTransaction = new TokenRevokeKycTransaction().setTokenId(testTokenId); - assertThat(tokenRevokeKycTransaction.getTokenId()).isEqualTo(testTokenId); - } - - @Test - void getSetTokenIdFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setTokenId(testTokenId)); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenRevokeKycTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenRevokeKycTransactionTest.snap deleted file mode 100644 index 6c91f3c12c..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenRevokeKycTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.TokenRevokeKycTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_revoke_kyc {\n account {\n account_num: 0\n realm_num: 9\n shard_num: 6\n }\n token {\n realm_num: 2\n shard_num: 4\n token_num: 0\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenSupplyTypeTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenSupplyTypeTest.java deleted file mode 100644 index fbfdf4067a..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenSupplyTypeTest.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.hedera.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.TokenSupplyType; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class TokenSupplyTypeTest { - private final TokenSupplyType tokenSupplyTypeInfinite = TokenSupplyType.INFINITE; - private final TokenSupplyType tokenSupplyTypeFinite = TokenSupplyType.FINITE; - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void fromProtobuf() { - SnapshotMatcher.expect( - com.hedera.hashgraph.sdk.TokenSupplyType.valueOf(tokenSupplyTypeInfinite).toString(), - com.hedera.hashgraph.sdk.TokenSupplyType.valueOf(tokenSupplyTypeFinite).toString()) - .toMatchSnapshot(); - } - - @Test - void toProtobuf() { - SnapshotMatcher.expect( - com.hedera.hashgraph.sdk.TokenSupplyType.valueOf(tokenSupplyTypeInfinite).toProtobuf(), - com.hedera.hashgraph.sdk.TokenSupplyType.valueOf(tokenSupplyTypeFinite).toProtobuf()) - .toMatchSnapshot(); - } - - @Test - void tokenSupplyTestToString() { - assertThat(com.hedera.hashgraph.sdk.TokenSupplyType.INFINITE).hasToString("INFINITE"); - assertThat(com.hedera.hashgraph.sdk.TokenSupplyType.FINITE).hasToString("FINITE"); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenSupplyTypeTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenSupplyTypeTest.snap deleted file mode 100644 index 75332a5ce0..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenSupplyTypeTest.snap +++ /dev/null @@ -1,10 +0,0 @@ -com.hedera.hashgraph.sdk.TokenSupplyTypeTest.fromProtobuf=[ - "INFINITE", - "FINITE" -] - - -com.hedera.hashgraph.sdk.TokenSupplyTypeTest.toProtobuf=[ - "INFINITE", - "FINITE" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenTypeTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenTypeTest.java deleted file mode 100644 index dbc37dba6c..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenTypeTest.java +++ /dev/null @@ -1,58 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.TokenType; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -public class TokenTypeTest { - - private final TokenType tokenTypeFungible = TokenType.FUNGIBLE_COMMON; - private final TokenType tokenTypeNonFungible = TokenType.NON_FUNGIBLE_UNIQUE; - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void fromProtobuf() { - SnapshotMatcher.expect( - com.hedera.hashgraph.sdk.TokenType.valueOf(tokenTypeFungible).toString(), - com.hedera.hashgraph.sdk.TokenType.valueOf(tokenTypeNonFungible).toString()) - .toMatchSnapshot(); - } - - @Test - void toProtobuf() { - SnapshotMatcher.expect( - com.hedera.hashgraph.sdk.TokenType.valueOf(tokenTypeFungible).toProtobuf(), - com.hedera.hashgraph.sdk.TokenType.valueOf(tokenTypeNonFungible).toProtobuf()) - .toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenTypeTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenTypeTest.snap deleted file mode 100644 index 88b53690cb..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenTypeTest.snap +++ /dev/null @@ -1,10 +0,0 @@ -com.hedera.hashgraph.sdk.TokenTypeTest.fromProtobuf=[ - "FUNGIBLE_COMMON", - "NON_FUNGIBLE_UNIQUE" -] - - -com.hedera.hashgraph.sdk.TokenTypeTest.toProtobuf=[ - "FUNGIBLE_COMMON", - "NON_FUNGIBLE_UNIQUE" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenUnfreezeTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenUnfreezeTransactionTest.java deleted file mode 100644 index e06365273f..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenUnfreezeTransactionTest.java +++ /dev/null @@ -1,92 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenUnfreezeAccountTransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; - -public class TokenUnfreezeTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction() - .toString() - ).toMatchSnapshot(); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new TokenUnfreezeTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - private TokenUnfreezeTransaction spawnTestTransaction() { - return new TokenUnfreezeTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setAccountId(AccountId.fromString("0.0.222")) - .setTokenId(TokenId.fromString("6.5.4")) - .setMaxTransactionFee(new Hbar(1)) - .freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = TokenUnfreezeTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setTokenUnfreeze(TokenUnfreezeAccountTransactionBody.newBuilder().build()) - .build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(TokenUnfreezeTransaction.class); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenUnfreezeTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenUnfreezeTransactionTest.snap deleted file mode 100644 index 82a155a75b..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenUnfreezeTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.TokenUnfreezeTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_unfreeze {\n account {\n account_num: 222\n realm_num: 0\n shard_num: 0\n }\n token {\n realm_num: 5\n shard_num: 6\n token_num: 4\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenUnpauseTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenUnpauseTransactionTest.java deleted file mode 100644 index 2ae2822cb8..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenUnpauseTransactionTest.java +++ /dev/null @@ -1,110 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenUnpauseTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class TokenUnpauseTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - private static final TokenId testTokenId = TokenId.fromString("4.2.0"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - TokenUnpauseTransaction spawnTestTransaction() { - return new TokenUnpauseTransaction().setNodeAccountIds( - Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setTokenId(testTokenId).setMaxTransactionFee(new Hbar(1)).freeze().sign(unusedPrivateKey); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new TokenUnpauseTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); - } - - @Test - void shouldBytesNft() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = TokenCreateTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setTokenUnpause(TokenUnpauseTransactionBody.newBuilder().build()).build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(TokenUnpauseTransaction.class); - } - - @Test - void constructTokenUnpauseTransactionFromTransactionBodyProtobuf() { - var transactionBody = TokenUnpauseTransactionBody.newBuilder().setToken(testTokenId.toProtobuf()).build(); - - var tx = TransactionBody.newBuilder().setTokenUnpause(transactionBody).build(); - var tokenUnpauseTransaction = new TokenUnpauseTransaction(tx); - - assertThat(tokenUnpauseTransaction.getTokenId()).isEqualTo(testTokenId); - } - - @Test - void getSetTokenId() { - var tokenUnpauseTransaction = new TokenUnpauseTransaction().setTokenId(testTokenId); - assertThat(tokenUnpauseTransaction.getTokenId()).isEqualTo(testTokenId); - } - - @Test - void getSetTokenIdFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setTokenId(testTokenId)); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenUnpauseTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenUnpauseTransactionTest.snap deleted file mode 100644 index 019f591a22..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenUnpauseTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.TokenUnpauseTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_unpause {\n token {\n realm_num: 2\n shard_num: 4\n token_num: 0\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenUpdateNftsTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenUpdateNftsTransactionTest.java deleted file mode 100644 index 9c79503523..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenUpdateNftsTransactionTest.java +++ /dev/null @@ -1,142 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import com.google.protobuf.ByteString; -import com.google.protobuf.BytesValue; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenUpdateNftsTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import java.time.Instant; -import java.util.Arrays; -import java.util.List; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -public class TokenUpdateNftsTransactionTest { - private static final PrivateKey testMetadataKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - private static final TokenId testTokenId = TokenId.fromString("4.2.0"); - private static final List testSerialNumbers = Arrays.asList(8L, 9L, 10L); - private static final byte[] testMetadata = new byte[]{1, 2, 3, 4, 5}; - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); - } - - private TokenUpdateNftsTransaction spawnTestTransaction() { - return new TokenUpdateNftsTransaction().setNodeAccountIds( - Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setTokenId(testTokenId).setMetadata(testMetadata).setSerials(testSerialNumbers) - .setMaxTransactionFee(new Hbar(1)).freeze().sign(testMetadataKey); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new TokenUpdateNftsTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = TokenUpdateNftsTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setTokenUpdateNfts(TokenUpdateNftsTransactionBody.newBuilder().build()).build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(TokenUpdateNftsTransaction.class); - } - - @Test - void constructTokenUpdateTransactionFromTransactionBodyProtobuf() { - var transactionBody = TokenUpdateNftsTransactionBody.newBuilder().setToken(testTokenId.toProtobuf()) - .setMetadata(BytesValue.of(ByteString.copyFrom(testMetadata))).addAllSerialNumbers(testSerialNumbers) - .build(); - - var tx = TransactionBody.newBuilder().setTokenUpdateNfts(transactionBody).build(); - var tokenUpdateNftsTransaction = new TokenUpdateNftsTransaction(tx); - - assertThat(tokenUpdateNftsTransaction.getTokenId()).isEqualTo(testTokenId); - assertThat(tokenUpdateNftsTransaction.getMetadata()).isEqualTo(testMetadata); - assertThat(tokenUpdateNftsTransaction.getSerials()).isEqualTo(testSerialNumbers); - } - - @Test - void getSetTokenId() { - var tokenUpdateNftsTransaction = new TokenUpdateNftsTransaction().setTokenId(testTokenId); - assertThat(tokenUpdateNftsTransaction.getTokenId()).isEqualTo(testTokenId); - } - - @Test - void getSetTokenIdFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setTokenId(testTokenId)); - } - - @Test - void getSetMetadata() { - var tx = spawnTestTransaction(); - assertThat(tx.getMetadata()).isEqualTo(testMetadata); - } - - @Test - void getSetMetadataFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setMetadata(testMetadata)); - } - - @Test - void getSetSerialNumbers() { - var tx = spawnTestTransaction(); - assertThat(tx.getSerials()).isEqualTo(testSerialNumbers); - } - - @Test - void getSetSerialNumbersFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setSerials(testSerialNumbers)); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenUpdateNftsTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenUpdateNftsTransactionTest.snap deleted file mode 100644 index c00b3c6139..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenUpdateNftsTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.TokenUpdateNftsTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_update_nfts {\n metadata {\n value: \"\\001\\002\\003\\004\\005\"\n }\n serial_numbers: 8\n serial_numbers: 9\n serial_numbers: 10\n token {\n realm_num: 2\n shard_num: 4\n token_num: 0\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenUpdateTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenUpdateTransactionTest.java deleted file mode 100644 index a728b10487..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenUpdateTransactionTest.java +++ /dev/null @@ -1,386 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import com.google.protobuf.ByteString; -import com.google.protobuf.BytesValue; -import com.google.protobuf.StringValue; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.Timestamp; -import com.hedera.hashgraph.sdk.proto.TokenUpdateTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import java.time.Duration; -import java.time.Instant; -import java.util.Arrays; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - - -public class TokenUpdateTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - private static final PublicKey testAdminKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e11") - .getPublicKey(); - private static final PublicKey testKycKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e12") - .getPublicKey(); - private static final PublicKey testFreezeKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e13") - .getPublicKey(); - private static final PublicKey testWipeKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e14") - .getPublicKey(); - private static final PublicKey testSupplyKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e15") - .getPublicKey(); - private static final PublicKey testFeeScheduleKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e16") - .getPublicKey(); - private static final PublicKey testPauseKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e17") - .getPublicKey(); - private static final PublicKey testMetadataKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e18") - .getPublicKey(); - private static final AccountId testTreasuryAccountId = AccountId.fromString("7.7.7"); - private static final AccountId testAutoRenewAccountId = AccountId.fromString("8.8.8"); - private static final String testTokenName = "test name"; - private static final String testTokenSymbol = "test symbol"; - private static final String testTokenMemo = "test memo"; - private static final TokenId testTokenId = TokenId.fromString("4.2.0"); - private static final Duration testAutoRenewPeriod = Duration.ofHours(10); - private static final Instant testExpirationTime = Instant.now(); - private static final byte[] testMetadata = new byte[]{1, 2, 3, 4, 5}; - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new TokenUpdateTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - private TokenUpdateTransaction spawnTestTransaction() { - return new TokenUpdateTransaction().setNodeAccountIds( - Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setTokenId(testTokenId).setFeeScheduleKey(testFeeScheduleKey).setSupplyKey(testSupplyKey) - .setAdminKey(testAdminKey).setAutoRenewAccountId(testAutoRenewAccountId) - .setAutoRenewPeriod(testAutoRenewPeriod).setFreezeKey(testFreezeKey).setWipeKey(testWipeKey) - .setTokenSymbol(testTokenSymbol).setKycKey(testKycKey).setPauseKey(testPauseKey) - .setMetadataKey(testMetadataKey).setExpirationTime(validStart).setTreasuryAccountId(testTreasuryAccountId) - .setTokenName(testTokenName).setTokenMemo(testTokenMemo).setMaxTransactionFee(new Hbar(1)) - .setTokenMetadata(testMetadata).setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION).freeze().sign(unusedPrivateKey); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = TokenUpdateTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setTokenUpdate(TokenUpdateTransactionBody.newBuilder().build()).build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(TokenUpdateTransaction.class); - } - - @Test - void constructTokenUpdateTransactionFromTransactionBodyProtobuf() { - var transactionBody = TokenUpdateTransactionBody.newBuilder().setToken(testTokenId.toProtobuf()) - .setName(testTokenName).setSymbol(testTokenSymbol).setTreasury(testTreasuryAccountId.toProtobuf()) - .setAdminKey(testAdminKey.toProtobufKey()).setKycKey(testKycKey.toProtobufKey()) - .setFreezeKey(testFreezeKey.toProtobufKey()).setWipeKey(testWipeKey.toProtobufKey()) - .setSupplyKey(testSupplyKey.toProtobufKey()).setAutoRenewAccount(testAutoRenewAccountId.toProtobuf()) - .setAutoRenewPeriod( - com.hedera.hashgraph.sdk.proto.Duration.newBuilder().setSeconds(testAutoRenewPeriod.toSeconds()) - .build()).setExpiry(Timestamp.newBuilder().setSeconds(testExpirationTime.getEpochSecond()).build()) - .setMemo(StringValue.newBuilder().setValue(testTokenMemo).build()) - .setFeeScheduleKey(testFeeScheduleKey.toProtobufKey()).setPauseKey(testPauseKey.toProtobufKey()) - .setMetadataKey(testMetadataKey.toProtobufKey()) - .setMetadata(BytesValue.of(ByteString.copyFrom(testMetadata))) - .setKeyVerificationMode(com.hedera.hashgraph.sdk.proto.TokenKeyValidation.NO_VALIDATION).build(); - - var tx = TransactionBody.newBuilder().setTokenUpdate(transactionBody).build(); - var tokenUpdateTransaction = new TokenUpdateTransaction(tx); - - assertThat(tokenUpdateTransaction.getTokenId()).isEqualTo(testTokenId); - assertThat(tokenUpdateTransaction.getTokenName()).isEqualTo(testTokenName); - assertThat(tokenUpdateTransaction.getTokenSymbol()).isEqualTo(testTokenSymbol); - assertThat(tokenUpdateTransaction.getTreasuryAccountId()).isEqualTo(testTreasuryAccountId); - assertThat(tokenUpdateTransaction.getAdminKey()).isEqualTo(testAdminKey); - assertThat(tokenUpdateTransaction.getKycKey()).isEqualTo(testKycKey); - assertThat(tokenUpdateTransaction.getFreezeKey()).isEqualTo(testFreezeKey); - assertThat(tokenUpdateTransaction.getWipeKey()).isEqualTo(testWipeKey); - assertThat(tokenUpdateTransaction.getSupplyKey()).isEqualTo(testSupplyKey); - assertThat(tokenUpdateTransaction.getAutoRenewAccountId()).isEqualTo(testAutoRenewAccountId); - assertThat(tokenUpdateTransaction.getAutoRenewPeriod().toSeconds()).isEqualTo(testAutoRenewPeriod.toSeconds()); - assertThat(tokenUpdateTransaction.getExpirationTime().getEpochSecond()).isEqualTo( - testExpirationTime.getEpochSecond()); - assertThat(tokenUpdateTransaction.getTokenMemo()).isEqualTo(testTokenMemo); - assertThat(tokenUpdateTransaction.getFeeScheduleKey()).isEqualTo(testFeeScheduleKey); - assertThat(tokenUpdateTransaction.getPauseKey()).isEqualTo(testPauseKey); - assertThat(tokenUpdateTransaction.getMetadataKey()).isEqualTo(testMetadataKey); - assertThat(tokenUpdateTransaction.getTokenMetadata()).isEqualTo(testMetadata); - assertThat(tokenUpdateTransaction.getKeyVerificationMode()).isEqualTo(TokenKeyValidation.NO_VALIDATION); - } - - @Test - void getSetTokenId() { - var tokenUpdateTransaction = new TokenUpdateTransaction().setTokenId(testTokenId); - assertThat(tokenUpdateTransaction.getTokenId()).isEqualTo(testTokenId); - } - - @Test - void getSetTokenIdFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setTokenId(testTokenId)); - } - - @Test - void getSetName() { - var tokenUpdateTransaction = new TokenUpdateTransaction().setTokenName(testTokenName); - assertThat(tokenUpdateTransaction.getTokenName()).isEqualTo(testTokenName); - } - - @Test - void getSetNameFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setTokenName(testTokenName)); - } - - @Test - void getSetSymbol() { - var tokenUpdateTransaction = new TokenUpdateTransaction().setTokenSymbol(testTokenSymbol); - assertThat(tokenUpdateTransaction.getTokenSymbol()).isEqualTo(testTokenSymbol); - } - - @Test - void getSetSymbolFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setTokenSymbol(testTokenSymbol)); - } - - @Test - void getSetTreasuryAccountId() { - var tokenUpdateTransaction = new TokenUpdateTransaction().setTreasuryAccountId(testTreasuryAccountId); - assertThat(tokenUpdateTransaction.getTreasuryAccountId()).isEqualTo(testTreasuryAccountId); - } - - @Test - void getSetTreasuryAccountIdFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setTreasuryAccountId(testTreasuryAccountId)); - } - - @Test - void getSetAdminKey() { - var tokenUpdateTransaction = new TokenUpdateTransaction().setAdminKey(testAdminKey); - assertThat(tokenUpdateTransaction.getAdminKey()).isEqualTo(testAdminKey); - } - - @Test - void getSetAdminKeyFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setAdminKey(testAdminKey)); - } - - @Test - void getSetKycKey() { - var tokenUpdateTransaction = new TokenUpdateTransaction().setKycKey(testKycKey); - assertThat(tokenUpdateTransaction.getKycKey()).isEqualTo(testKycKey); - } - - @Test - void getSetKycKeyFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setKycKey(testKycKey)); - } - - @Test - void getSetFreezeKey() { - var tokenUpdateTransaction = new TokenUpdateTransaction().setFreezeKey(testFreezeKey); - assertThat(tokenUpdateTransaction.getFreezeKey()).isEqualTo(testFreezeKey); - } - - @Test - void getSetFreezeKeyFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setFreezeKey(testFreezeKey)); - } - - @Test - void getSetWipeKey() { - var tokenUpdateTransaction = new TokenUpdateTransaction().setWipeKey(testWipeKey); - assertThat(tokenUpdateTransaction.getWipeKey()).isEqualTo(testWipeKey); - } - - @Test - void getSetWipeKeyFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setWipeKey(testWipeKey)); - } - - @Test - void getSetSupplyKey() { - var tokenUpdateTransaction = new TokenUpdateTransaction().setSupplyKey(testSupplyKey); - assertThat(tokenUpdateTransaction.getSupplyKey()).isEqualTo(testSupplyKey); - } - - @Test - void getSetSupplyKeyFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setSupplyKey(testSupplyKey)); - } - - @Test - void getSetAutoRenewAccountId() { - var tokenUpdateTransaction = new TokenUpdateTransaction().setAutoRenewAccountId(testAutoRenewAccountId); - assertThat(tokenUpdateTransaction.getAutoRenewAccountId()).isEqualTo(testAutoRenewAccountId); - } - - @Test - void getSetAutoRenewAccountIdFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setAutoRenewAccountId(testAutoRenewAccountId)); - } - - @Test - void getSetAutoRenewPeriod() { - var tokenUpdateTransaction = new TokenUpdateTransaction().setAutoRenewPeriod(testAutoRenewPeriod); - assertThat(tokenUpdateTransaction.getAutoRenewPeriod()).isEqualTo(testAutoRenewPeriod); - } - - @Test - void getSetAutoRenewPeriodFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setAutoRenewPeriod(testAutoRenewPeriod)); - } - - @Test - void getSetExpirationTime() { - var tokenUpdateTransaction = new TokenUpdateTransaction().setExpirationTime(testExpirationTime); - assertThat(tokenUpdateTransaction.getExpirationTime()).isEqualTo(testExpirationTime); - } - - @Test - void getSetExpirationTimeFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setExpirationTime(testExpirationTime)); - } - - @Test - void getSetTokenMemo() { - var tokenUpdateTransaction = new TokenUpdateTransaction().setTokenMemo(testTokenMemo); - assertThat(tokenUpdateTransaction.getTokenMemo()).isEqualTo(testTokenMemo); - } - - @Test - void getSetTokenMemoFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setTokenMemo(testTokenMemo)); - } - - @Test - void getSetFeeScheduleKey() { - var tokenUpdateTransaction = new TokenUpdateTransaction().setFeeScheduleKey(testFeeScheduleKey); - assertThat(tokenUpdateTransaction.getFeeScheduleKey()).isEqualTo(testFeeScheduleKey); - } - - @Test - void getSetFeeScheduleKeyFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setFeeScheduleKey(testFeeScheduleKey)); - } - - @Test - void getSetPauseKey() { - var tokenUpdateTransaction = new TokenUpdateTransaction().setPauseKey(testPauseKey); - assertThat(tokenUpdateTransaction.getPauseKey()).isEqualTo(testPauseKey); - } - - @Test - void getSetPauseKeyFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setPauseKey(testPauseKey)); - } - - @Test - void getSetMetadataKey() { - var tokenUpdateTransaction = new TokenUpdateTransaction().setMetadataKey(testMetadataKey); - assertThat(tokenUpdateTransaction.getMetadataKey()).isEqualTo(testMetadataKey); - } - - @Test - void getSetMetadataKeyFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setMetadataKey(testMetadataKey)); - } - - @Test - void getSetMetadata() { - var tx = spawnTestTransaction(); - assertThat(tx.getTokenMetadata()).isEqualTo(testMetadata); - } - - @Test - void getSetMetadataFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setTokenMetadata(testMetadata)); - } - - @Test - void getSetKeyVerificationMode() { - var tx = spawnTestTransaction(); - assertThat(tx.getKeyVerificationMode()).isEqualTo(TokenKeyValidation.NO_VALIDATION); - } - - @Test - void getSetKeyVerificationModeFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION)); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenUpdateTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenUpdateTransactionTest.snap deleted file mode 100644 index 59624b9b44..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenUpdateTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.TokenUpdateTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_update {\n admin_key {\n ed25519: \"\\332\\207p\\020\\227\\206ns\\360\\335\\224,\\273>\\227\\0063)\\371\\005X\\206!\\261x\\322\\027Yh\\215G\\374\"\n }\n auto_renew_account {\n account_num: 8\n realm_num: 8\n shard_num: 8\n }\n expiry {\n seconds: 1554158542\n }\n fee_schedule_key {\n ed25519: \"K\\276\\225\\250m$\\370\\371gs\\261(&\\374\\276\\000\\226\\210\\313\\r\\312\\210\\317\\361\\027\\243\\250\\257P\\303q\\023\"\n }\n freeze_key {\n ed25519: \"=\\355S\\343\\\"3S/=\\204b2L\\321\\023\\253\\276Os!m\\360mT\\241\\034\\266\\221\\301[\\'\\315\"\n }\n key_verification_mode: NO_VALIDATION\n key_verification_mode_value: 1\n kyc_key {\n ed25519: \"\\373\\210\\2637\\337\\327ea{\\3442*\\347\\357\\2053\\326\\036d\\203\\005\\016 \\354HE\\2453\\275\\334\\244\\261\"\n }\n memo {\n value: \"test memo\"\n }\n metadata {\n value: \"\\001\\002\\003\\004\\005\"\n }\n metadata_key {\n ed25519: \"\\024m\\354\\2222\\nnF\\353\\032Cv{\\261\\354\\225\\242\\346\\300%\\032\\260\\335x\\017\\343tt\\324\\272\\304\\025\"\n }\n name: \"test name\"\n pause_key {\n ed25519: \"\\321he\\251\\214\\370\\260\\267\\370\\3727v\\262\\r\\257\\305\\276\\004\\377\\353\\232$\\227r\\a\\203\\316\\231\\036+\\031t\"\n }\n supply_key {\n ed25519: \";\\2218S\\257\\245\\233U\\253\\305\\201\\302\\254\\r6X\\n\\302\\354\\244\\275\\020\\034\\002\\027?\\357\\002\\346w\\335\\325\"\n }\n symbol: \"test symbol\"\n token {\n realm_num: 2\n shard_num: 4\n token_num: 0\n }\n treasury {\n account_num: 7\n realm_num: 7\n shard_num: 7\n }\n wipe_key {\n ed25519: \"R[\\234\\025_\\220+\\221-\\275\\201\\276\\246\\324:\\a}zb\\335\\037\\357\\317\\307}\\351aD\\325\\372\\303\\356\"\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenWipeTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenWipeTransactionTest.java deleted file mode 100644 index e8d9a043b8..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenWipeTransactionTest.java +++ /dev/null @@ -1,191 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TokenWipeAccountTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - - -public class TokenWipeTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - private static final AccountId testAccountId = AccountId.fromString("0.6.9"); - private static final TokenId testTokenId = TokenId.fromString("4.2.0"); - private static final long testAmount = 4L; - private static final List testSerialNumbers = Arrays.asList(8L, 9L, 10L); - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerializeFungible() { - SnapshotMatcher.expect(spawnTestTransaction() - .toString() - ).toMatchSnapshot(); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new TokenWipeTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - private TokenWipeTransaction spawnTestTransaction() { - return new TokenWipeTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setTokenId(TokenId.fromString("0.0.111")) - .setAccountId(testAccountId) - .setAmount(testAmount) - .setSerials(testSerialNumbers) - .setMaxTransactionFee(new Hbar(1)) - .freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldSerializeNft() { - SnapshotMatcher.expect(spawnTestTransactionNft() - .toString() - ).toMatchSnapshot(); - } - - private TokenWipeTransaction spawnTestTransactionNft() { - return new TokenWipeTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setTokenId(TokenId.fromString("0.0.111")) - .setAccountId(testAccountId) - .setSerials(Collections.singletonList(444L)) - .setMaxTransactionFee(new Hbar(1)) - .freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldBytesFungible() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = TokenWipeTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void shouldBytesNft() throws Exception { - var tx = spawnTestTransactionNft(); - var tx2 = TokenWipeTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setTokenWipe(TokenWipeAccountTransactionBody.newBuilder().build()) - .build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(TokenWipeTransaction.class); - } - - @Test - void constructTokenWipeTransactionFromTransactionBodyProtobuf() { - var transactionBody = TokenWipeAccountTransactionBody.newBuilder().setToken(testTokenId.toProtobuf()) - .setAccount(testAccountId.toProtobuf()).setAmount(testAmount).addAllSerialNumbers(testSerialNumbers) - .build(); - - var txBody = TransactionBody.newBuilder().setTokenWipe(transactionBody).build(); - var tokenWipeTransaction = new TokenWipeTransaction(txBody); - - assertThat(tokenWipeTransaction.getTokenId()).isEqualTo(testTokenId); - assertThat(tokenWipeTransaction.getAccountId()).isEqualTo(testAccountId); - assertThat(tokenWipeTransaction.getAmount()).isEqualTo(testAmount); - assertThat(tokenWipeTransaction.getSerials()).isEqualTo(testSerialNumbers); - } - - @Test - void getSetTokenId() { - var tokenWipeTransaction = new TokenWipeTransaction().setTokenId(testTokenId); - assertThat(tokenWipeTransaction.getTokenId()).isEqualTo(testTokenId); - } - - @Test - void getSetTokenIdFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setTokenId(testTokenId)); - } - - @Test - void getSetAccountId() { - var tx = spawnTestTransaction(); - assertThat(tx.getAccountId()).isEqualTo(testAccountId); - } - - @Test - void getSetAccountIdFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setAccountId(testAccountId)); - } - - @Test - void getSetAmount() { - var tx = spawnTestTransaction(); - assertThat(tx.getAmount()).isEqualTo(testAmount); - } - - @Test - void getSetAmountFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setAmount(testAmount)); - } - - @Test - void getSetSerialNumbers() { - var tx = spawnTestTransaction(); - assertThat(tx.getSerials()).isEqualTo(testSerialNumbers); - } - - @Test - void getSetSerialNumbersFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setSerials(testSerialNumbers)); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenWipeTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenWipeTransactionTest.snap deleted file mode 100644 index 62706f6caa..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenWipeTransactionTest.snap +++ /dev/null @@ -1,8 +0,0 @@ -com.hedera.hashgraph.sdk.TokenWipeTransactionTest.shouldSerializeFungible=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_wipe {\n account {\n account_num: 9\n realm_num: 6\n shard_num: 0\n }\n amount: 4\n serial_numbers: 8\n serial_numbers: 9\n serial_numbers: 10\n token {\n realm_num: 0\n shard_num: 0\n token_num: 111\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] - - -com.hedera.hashgraph.sdk.TokenWipeTransactionTest.shouldSerializeNft=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_wipe {\n account {\n account_num: 9\n realm_num: 6\n shard_num: 0\n }\n amount: 0\n serial_numbers: 444\n token {\n realm_num: 0\n shard_num: 0\n token_num: 111\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicCreateTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicCreateTransactionTest.java deleted file mode 100644 index b832663681..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicCreateTransactionTest.java +++ /dev/null @@ -1,96 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.ConsensusCreateTopicTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Duration; -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; - -public class TopicCreateTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction() - .toString() - ).toMatchSnapshot(); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new TopicCreateTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - private TopicCreateTransaction spawnTestTransaction() { - return new TopicCreateTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setSubmitKey(unusedPrivateKey) - .setAdminKey(unusedPrivateKey) - .setAutoRenewAccountId(AccountId.fromString("0.0.5007")) - .setAutoRenewPeriod(Duration.ofHours(24)) - .setMaxTransactionFee(Hbar.fromTinybars(100_000)) - .setTopicMemo("hello memo") - .freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = TopicCreateTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setConsensusCreateTopic(ConsensusCreateTopicTransactionBody.newBuilder().build()) - .build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(TopicCreateTransaction.class); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicCreateTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicCreateTransactionTest.snap deleted file mode 100644 index 7bdf12912f..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicCreateTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.TopicCreateTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nconsensus_create_topic {\n admin_key {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n auto_renew_account {\n account_num: 5007\n realm_num: 0\n shard_num: 0\n }\n auto_renew_period {\n seconds: 86400\n }\n memo: \"hello memo\"\n submit_key {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicDeleteTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicDeleteTransactionTest.java deleted file mode 100644 index 941f552c7e..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicDeleteTransactionTest.java +++ /dev/null @@ -1,90 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.ConsensusDeleteTopicTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; - -public class TopicDeleteTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction() - .toString() - ).toMatchSnapshot(); - } - - private TopicDeleteTransaction spawnTestTransaction() { - return new TopicDeleteTransaction() - .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setTopicId(TopicId.fromString("0.0.5007")) - .setMaxTransactionFee(Hbar.fromTinybars(100_000)) - .freeze() - .sign(unusedPrivateKey); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new TopicDeleteTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = TopicDeleteTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setConsensusDeleteTopic(ConsensusDeleteTopicTransactionBody.newBuilder().build()) - .build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(TopicDeleteTransaction.class); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicDeleteTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicDeleteTransactionTest.snap deleted file mode 100644 index c27d7345f0..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicDeleteTransactionTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.TopicDeleteTransactionTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nconsensus_delete_topic {\n topic_i_d {\n realm_num: 0\n shard_num: 0\n topic_num: 5007\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicIdTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicIdTest.java deleted file mode 100644 index a2d5df673c..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicIdTest.java +++ /dev/null @@ -1,64 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.bouncycastle.util.encoders.Hex; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -class TopicIdTest { - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerializeFromString() { - SnapshotMatcher.expect(TopicId.fromString("0.0.5005").toString()).toMatchSnapshot(); - } - - @Test - void toBytes() throws InvalidProtocolBufferException { - SnapshotMatcher.expect(Hex.toHexString(new TopicId(5005).toBytes())).toMatchSnapshot(); - } - - @Test - void fromBytes() throws InvalidProtocolBufferException { - SnapshotMatcher.expect(TopicId.fromBytes(new TopicId(5005).toBytes()).toString()).toMatchSnapshot(); - } - - @Test - void fromSolidityAddress() { - SnapshotMatcher.expect(TokenId.fromSolidityAddress("000000000000000000000000000000000000138D").toString()).toMatchSnapshot(); - } - - @Test - void toSolidityAddress() { - SnapshotMatcher.expect(new TokenId(5005).toSolidityAddress()).toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicIdTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicIdTest.snap deleted file mode 100644 index 49f033efda..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicIdTest.snap +++ /dev/null @@ -1,23 +0,0 @@ -com.hedera.hashgraph.sdk.TopicIdTest.fromBytes=[ - "0.0.5005" -] - - -com.hedera.hashgraph.sdk.TopicIdTest.fromSolidityAddress=[ - "0.0.5005" -] - - -com.hedera.hashgraph.sdk.TopicIdTest.shouldSerializeFromString=[ - "0.0.5005" -] - - -com.hedera.hashgraph.sdk.TopicIdTest.toBytes=[ - "188d27" -] - - -com.hedera.hashgraph.sdk.TopicIdTest.toSolidityAddress=[ - "000000000000000000000000000000000000138d" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicInfoQueryTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicInfoQueryTest.java deleted file mode 100644 index 23d2d16421..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicInfoQueryTest.java +++ /dev/null @@ -1,47 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -public class TopicInfoQueryTest { - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - var builder = com.hedera.hashgraph.sdk.proto.Query.newBuilder(); - new TopicInfoQuery() - .setTopicId(TopicId.fromString("0.0.5005")) - .onMakeRequest(builder, QueryHeader.newBuilder().build()); - SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")).toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicInfoQueryTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicInfoQueryTest.snap deleted file mode 100644 index 7c658051ed..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicInfoQueryTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.TopicInfoQueryTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.Query\nconsensus_get_topic_info {\n header {\n }\n topic_i_d {\n realm_num: 0\n shard_num: 0\n topic_num: 5005\n }\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicInfoTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicInfoTest.java deleted file mode 100644 index a75437b51b..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicInfoTest.java +++ /dev/null @@ -1,89 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.ConsensusGetTopicInfoResponse; -import com.hedera.hashgraph.sdk.proto.ConsensusTopicInfo; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.bouncycastle.util.encoders.Hex; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Duration; -import java.time.Instant; - - -public class TopicInfoTest { - private static final PrivateKey privateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - private static final byte[] hash = {2}; - - private static final ConsensusGetTopicInfoResponse info = ConsensusGetTopicInfoResponse.newBuilder() - .setTopicInfo(ConsensusTopicInfo.newBuilder() - .setMemo("1") - .setRunningHash(ByteString.copyFrom(hash)) - .setSequenceNumber(3) - .setExpirationTime(InstantConverter.toProtobuf(Instant.ofEpochMilli(4))) - .setAutoRenewPeriod(DurationConverter.toProtobuf(Duration.ofDays(5))) - .setAdminKey(privateKey.getPublicKey().toProtobufKey()) - .setSubmitKey(privateKey.getPublicKey().toProtobufKey()) - .setAutoRenewAccount(new AccountId(4).toProtobuf()) - .setLedgerId(LedgerId.TESTNET.toByteString())) - .build(); - - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void fromProtobuf() throws InvalidProtocolBufferException { - SnapshotMatcher.expect(TopicInfo.fromProtobuf(info).toString()) - .toMatchSnapshot(); - } - - @Test - void toProtobuf() throws InvalidProtocolBufferException { - SnapshotMatcher.expect(TopicInfo.fromProtobuf(info).toProtobuf().toString()) - .toMatchSnapshot(); - } - - @Test - void fromBytes() throws InvalidProtocolBufferException { - SnapshotMatcher.expect(TopicInfo.fromBytes(info.toByteArray()).toString()) - .toMatchSnapshot(); - } - - @Test - void toBytes() { - SnapshotMatcher.expect(Hex.toHexString(TopicInfo.fromProtobuf(info).toBytes())) - .toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicInfoTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicInfoTest.snap deleted file mode 100644 index 27fcc2903d..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicInfoTest.snap +++ /dev/null @@ -1,18 +0,0 @@ -com.hedera.hashgraph.sdk.TopicInfoTest.fromBytes=[ - "TopicInfo{topicId=0.0.0, topicMemo=1, runningHash=[2], sequenceNumber=3, expirationTime=1970-01-01T00:00:00.004Z, adminKey=302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7, submitKey=302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7, autoRenewPeriod=PT120H, autoRenewAccountId=0.0.4, ledgerId=testnet}" -] - - -com.hedera.hashgraph.sdk.TopicInfoTest.fromProtobuf=[ - "TopicInfo{topicId=0.0.0, topicMemo=1, runningHash=[2], sequenceNumber=3, expirationTime=1970-01-01T00:00:00.004Z, adminKey=302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7, submitKey=302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7, autoRenewPeriod=PT120H, autoRenewAccountId=0.0.4, ledgerId=testnet}" -] - - -com.hedera.hashgraph.sdk.TopicInfoTest.toBytes=[ - "12002a640a013112010218032205108092f4012a221220e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b732221220e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b73a040880af1a420218044a0101" -] - - -com.hedera.hashgraph.sdk.TopicInfoTest.toProtobuf=[ - "# com.hedera.hashgraph.sdk.proto.ConsensusGetTopicInfoResponse@a2d5b392\ntopic_i_d {\n realm_num: 0\n shard_num: 0\n topic_num: 0\n}\ntopic_info {\n admin_key {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n auto_renew_account {\n account_num: 4\n realm_num: 0\n shard_num: 0\n }\n auto_renew_period {\n seconds: 432000\n }\n expiration_time {\n nanos: 4000000\n seconds: 0\n }\n ledger_id: \"\\001\"\n memo: \"1\"\n running_hash: \"\\002\"\n sequence_number: 3\n submit_key {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicMessageChunkTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicMessageChunkTest.java deleted file mode 100644 index 0bb9517ac0..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicMessageChunkTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.hedera.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import com.hedera.hashgraph.sdk.proto.ConsensusMessageChunkInfo; -import com.hedera.hashgraph.sdk.proto.Timestamp; -import com.hedera.hashgraph.sdk.proto.mirror.ConsensusTopicResponse; -import org.junit.jupiter.api.Test; - -import java.time.Instant; - -import static org.assertj.core.api.Assertions.assertThat; - -public class TopicMessageChunkTest { - - private static final Instant testTimestamp = Instant.ofEpochSecond(1554158542); - private static final byte[] testContents = new byte[]{0x01, 0x02, 0x03}; - private static final byte[] testRunningHash = new byte[]{0x04, 0x05, 0x06}; - private static final long testSequenceNumber = 7L; - private static final TransactionId testTransactionId = new TransactionId(new AccountId(1), testTimestamp); - - @Test - void constructWithArgs() { - var consensusTopicResponse = ConsensusTopicResponse.newBuilder() - .setConsensusTimestamp(Timestamp.newBuilder().setSeconds(testTimestamp.getEpochSecond())) - .setMessage(ByteString.copyFrom(testContents)).setRunningHash(ByteString.copyFrom(testRunningHash)) - .setSequenceNumber(testSequenceNumber).setChunkInfo( - ConsensusMessageChunkInfo.newBuilder().setInitialTransactionID(testTransactionId.toProtobuf()).build()) - .build(); - - TopicMessageChunk topicMessageChunk = new TopicMessageChunk(consensusTopicResponse); - - assertThat(topicMessageChunk.consensusTimestamp).isEqualTo(testTimestamp); - assertThat(topicMessageChunk.contentSize).isEqualTo(testContents.length); - assertThat(topicMessageChunk.runningHash).isEqualTo(testRunningHash); - assertThat(topicMessageChunk.sequenceNumber).isEqualTo(testSequenceNumber); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicMessageQueryTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicMessageQueryTest.java deleted file mode 100644 index 18adba0c17..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicMessageQueryTest.java +++ /dev/null @@ -1,471 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.common.base.Stopwatch; -import com.google.common.primitives.Longs; -import com.google.common.util.concurrent.Uninterruptibles; -import com.google.protobuf.ByteString; -import com.hedera.hashgraph.sdk.proto.AccountID; -import com.hedera.hashgraph.sdk.proto.ConsensusMessageChunkInfo; -import com.hedera.hashgraph.sdk.proto.Timestamp; -import com.hedera.hashgraph.sdk.proto.TopicID; -import com.hedera.hashgraph.sdk.proto.TransactionID; -import com.hedera.hashgraph.sdk.proto.mirror.ConsensusServiceGrpc; -import com.hedera.hashgraph.sdk.proto.mirror.ConsensusTopicQuery; -import com.hedera.hashgraph.sdk.proto.mirror.ConsensusTopicResponse; -import io.github.jsonSnapshot.SnapshotMatcher; -import io.grpc.Server; -import io.grpc.Status; -import io.grpc.StatusRuntimeException; -import io.grpc.inprocess.InProcessServerBuilder; -import io.grpc.stub.StreamObserver; -import org.assertj.core.api.InstanceOfAssertFactories; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; - -import java.time.Duration; -import java.time.Instant; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.Queue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Consumer; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -class TopicMessageQueryTest { - - private static final Instant START_TIME = Instant.now(); - - private Client client; - final private AtomicBoolean complete = new AtomicBoolean(false); - final private List errors = new ArrayList<>(); - final private List received = new ArrayList<>(); - final private ConsensusServiceStub consensusServiceStub = new ConsensusServiceStub(); - private Server server; - private TopicMessageQuery topicMessageQuery; - - @BeforeAll - static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @BeforeEach - void setup() throws Exception { - client = Client.forNetwork(Collections.emptyMap()); - client.setMirrorNetwork(List.of("in-process:test")); - server = InProcessServerBuilder.forName("test") - .addService(consensusServiceStub) - .directExecutor() - .build() - .start(); - topicMessageQuery = new TopicMessageQuery(); - topicMessageQuery.setCompletionHandler(() -> complete.set(true)); - topicMessageQuery.setEndTime(START_TIME.plusSeconds(100L)); - topicMessageQuery.setErrorHandler((t, r) -> errors.add(t)); - topicMessageQuery.setMaxBackoff(Duration.ofMillis(500L)); - topicMessageQuery.setStartTime(START_TIME); - topicMessageQuery.setTopicId(TopicId.fromString("0.0.1000")); - } - - @AfterEach - void teardown() throws Exception { - consensusServiceStub.verify(); - if (client != null) { - client.close(); - } - if (server != null) { - server.shutdown(); - server.awaitTermination(); - } - } - - @Test - @SuppressWarnings("NullAway") - void setCompletionHandlerNull() { - assertThatThrownBy(() -> topicMessageQuery.setCompletionHandler(null)) - .isInstanceOf(NullPointerException.class) - .hasMessage("completionHandler must not be null"); - } - - @Test - @SuppressWarnings("NullAway") - void setEndTimeNull() { - assertThatThrownBy(() -> topicMessageQuery.setEndTime(null)) - .isInstanceOf(NullPointerException.class) - .hasMessage("endTime must not be null"); - } - - @Test - @SuppressWarnings("NullAway") - void setErrorHandlerNull() { - assertThatThrownBy(() -> topicMessageQuery.setErrorHandler(null)) - .isInstanceOf(NullPointerException.class) - .hasMessage("errorHandler must not be null"); - } - - @Test - @SuppressWarnings("NullAway") - void setMaxAttemptsNegative() { - assertThatThrownBy(() -> topicMessageQuery.setMaxAttempts(-1)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("maxAttempts must be positive"); - } - - @Test - @SuppressWarnings("NullAway") - void setMaxBackoffNull() { - assertThatThrownBy(() -> topicMessageQuery.setMaxBackoff(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("maxBackoff must be at least 500 ms"); - } - - @Test - void setMaxBackoffLow() { - assertThatThrownBy(() -> topicMessageQuery.setMaxBackoff(Duration.ofMillis(499L))) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("maxBackoff must be at least 500 ms"); - } - - @Test - @SuppressWarnings("NullAway") - void setRetryHandlerNull() { - assertThatThrownBy(() -> topicMessageQuery.setRetryHandler(null)) - .isInstanceOf(NullPointerException.class) - .hasMessage("retryHandler must not be null"); - } - - @Test - @SuppressWarnings("NullAway") - void setStartTimeNull() { - assertThatThrownBy(() -> topicMessageQuery.setStartTime(null)) - .isInstanceOf(NullPointerException.class) - .hasMessage("startTime must not be null"); - } - - @Test - @SuppressWarnings("NullAway") - void setTopicIdNull() { - assertThatThrownBy(() -> topicMessageQuery.setTopicId(null)) - .isInstanceOf(NullPointerException.class) - .hasMessage("topicId must not be null"); - } - - @Test - @Timeout(3) - void subscribe() { - consensusServiceStub.requests.add(request().build()); - consensusServiceStub.responses.add(response(1L)); - consensusServiceStub.responses.add(response(2L)); - - subscribeToMirror(received::add); - - assertThat(errors).isEmpty(); - assertThat(received).hasSize(2).extracting(t -> t.sequenceNumber).containsExactly(1L, 2L); - } - - @Test - @Timeout(3) - void subscribeChunked() { - ConsensusTopicResponse response1 = response(1L, 2); - ConsensusTopicResponse response2 = response(2L, 2); - consensusServiceStub.requests.add(request().build()); - consensusServiceStub.responses.add(response1); - consensusServiceStub.responses.add(response2); - - subscribeToMirror(received::add); - - var message = combine(response1.getMessage().toByteArray(), response2.getMessage().toByteArray()); - assertThat(errors).isEmpty(); - assertThat(received) - .hasSize(1) - .first() - .returns(toInstant(response2.getConsensusTimestamp()), t -> t.consensusTimestamp) - .returns(response2.getChunkInfo().getInitialTransactionID(), t -> Objects.requireNonNull(t.transactionId).toProtobuf()) - .returns(message, t -> t.contents) - .returns(response2.getRunningHash().toByteArray(), t -> t.runningHash) - .returns(response2.getSequenceNumber(), t -> t.sequenceNumber) - .extracting(t -> t.chunks) - .asInstanceOf(InstanceOfAssertFactories.ARRAY) - .hasSize(2) - .extracting(c -> ((TopicMessageChunk) c).sequenceNumber) - .contains(1L, 2L); - } - - @Test - @Timeout(3) - void subscribeNoResponse() { - consensusServiceStub.requests.add(request().build()); - - subscribeToMirror(received::add); - - assertThat(errors).isEmpty(); - assertThat(received).isEmpty(); - } - - @Test - @Timeout(3) - void errorDuringOnNext() { - consensusServiceStub.requests.add(request().build()); - consensusServiceStub.responses.add(response(1L)); - - subscribeToMirror(t -> { - throw new RuntimeException(); - }); - - assertThat(errors).hasSize(1).first().isInstanceOf(RuntimeException.class); - assertThat(received).isEmpty(); - } - - @ParameterizedTest(name = "Retry recovers w/ status {0} and description {1}") - @CsvSource({ - "INTERNAL, internal RST_STREAM error", - "INTERNAL, rst stream", - "NOT_FOUND, ", - "RESOURCE_EXHAUSTED, ", - "UNAVAILABLE, " - }) - @Timeout(3) - void retryRecovers(Status.Code code, String description) { - ConsensusTopicResponse response = response(1L); - Instant nextTimestamp = toInstant(response.getConsensusTimestamp()).plusNanos(1L); - ConsensusTopicQuery.Builder request = request(); - - consensusServiceStub.requests.add(request.build()); - consensusServiceStub.requests.add(request.setConsensusStartTime(toTimestamp(nextTimestamp)).build()); - consensusServiceStub.responses.add(response); - consensusServiceStub.responses.add(code.toStatus().withDescription(description).asRuntimeException()); - consensusServiceStub.responses.add(response(2L)); - - subscribeToMirror(received::add); - - assertThat(received).hasSize(2).extracting(t -> t.sequenceNumber).containsExactly(1L, 2L); - assertThat(errors).isEmpty(); - } - - @ParameterizedTest(name = "No retry w/ status {0} and description {1}") - @CsvSource({ - "INTERNAL, internal first_stream error", - "INTERNAL, internal error", - "INTERNAL, ", - "INVALID_ARGUMENT, " - }) - @Timeout(3) - void noRetry(Status.Code code, String description) { - consensusServiceStub.requests.add(request().build()); - consensusServiceStub.responses.add(code.toStatus().withDescription(description).asRuntimeException()); - - subscribeToMirror(received::add); - - assertThat(received).isEmpty(); - assertThat(errors).hasSize(1) - .first() - .isInstanceOf(StatusRuntimeException.class) - .extracting(t -> ((StatusRuntimeException) t).getStatus().getCode()) - .isEqualTo(code); - } - - @Test - @Timeout(3) - void customRetry() { - consensusServiceStub.requests.add(request().build()); - consensusServiceStub.requests.add(request().build()); - consensusServiceStub.responses.add(Status.INVALID_ARGUMENT.asRuntimeException()); - consensusServiceStub.responses.add(response(1L)); - topicMessageQuery.setRetryHandler(t -> true); - - subscribeToMirror(received::add); - - assertThat(received).hasSize(1).extracting(t -> t.sequenceNumber).containsExactly(1L); - assertThat(errors).isEmpty(); - } - - @Test - @Timeout(3) - void retryWithLimit() { - ConsensusTopicResponse response = response(1L); - Instant nextTimestamp = toInstant(response.getConsensusTimestamp()).plusNanos(1L); - ConsensusTopicQuery.Builder request = request(); - topicMessageQuery.setLimit(2); - - consensusServiceStub.requests.add(request.setLimit(2L).build()); - consensusServiceStub.requests.add(request.setConsensusStartTime(toTimestamp(nextTimestamp)).setLimit(1L).build()); - consensusServiceStub.responses.add(response); - consensusServiceStub.responses.add(Status.RESOURCE_EXHAUSTED.asRuntimeException()); - consensusServiceStub.responses.add(response(2L)); - - subscribeToMirror(received::add); - - assertThat(received).hasSize(2).extracting(t -> t.sequenceNumber).containsExactly(1L, 2L); - assertThat(errors).isEmpty(); - } - - @Test - @Timeout(3) - void retriesExhausted() { - topicMessageQuery.setMaxAttempts(1); - consensusServiceStub.requests.add(request().build()); - consensusServiceStub.requests.add(request().build()); - consensusServiceStub.responses.add(Status.RESOURCE_EXHAUSTED.asRuntimeException()); - consensusServiceStub.responses.add(Status.RESOURCE_EXHAUSTED.asRuntimeException()); - - subscribeToMirror(received::add); - - assertThat(received).isEmpty(); - assertThat(errors).hasSize(1) - .first() - .isInstanceOf(StatusRuntimeException.class) - .extracting(t -> ((StatusRuntimeException) t).getStatus()) - .isEqualTo(Status.RESOURCE_EXHAUSTED); - } - - @Test - @Timeout(5) - void errorWhenCallIsCancelled() { - consensusServiceStub.requests.add(request().build()); - consensusServiceStub.responses.add(Status.CANCELLED.asRuntimeException()); - - subscribeToMirror(received::add); - - assertThat(errors) - .hasSize(1) - .first() - .isInstanceOf(StatusRuntimeException.class) - .extracting(t -> ((StatusRuntimeException)t).getStatus()) - .isEqualTo(Status.CANCELLED); - - assertThat(received).isEmpty(); - } - - private void subscribeToMirror(Consumer onNext) { - SubscriptionHandle subscriptionHandle = topicMessageQuery.subscribe(client, onNext); - Stopwatch stopwatch = Stopwatch.createStarted(); - - while (!complete.get() && errors.isEmpty() && stopwatch.elapsed(TimeUnit.SECONDS) < 3) { - Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS); - } - - subscriptionHandle.unsubscribe(); - } - - static private ConsensusTopicQuery.Builder request() { - return ConsensusTopicQuery.newBuilder() - .setConsensusEndTime(toTimestamp(START_TIME.plusSeconds(100L))) - .setConsensusStartTime(toTimestamp(START_TIME)) - .setTopicID(TopicID.newBuilder().setTopicNum(1000).build()); - } - - static private ConsensusTopicResponse response(long sequenceNumber) { - return response(sequenceNumber, 0); - } - - static private ConsensusTopicResponse response(long sequenceNumber, int total) { - ConsensusTopicResponse.Builder consensusTopicResponseBuilder = ConsensusTopicResponse.newBuilder(); - - if (total > 0) { - var chunkInfo = ConsensusMessageChunkInfo.newBuilder() - .setInitialTransactionID(TransactionID.newBuilder() - .setAccountID(AccountID.newBuilder().setAccountNum(3).build()) - .setTransactionValidStart(toTimestamp(START_TIME)) - .build()) - .setNumber((int) sequenceNumber) - .setTotal(total) - .build(); - consensusTopicResponseBuilder.setChunkInfo(chunkInfo); - } - - var message = ByteString.copyFrom(Longs.toByteArray(sequenceNumber)); - return consensusTopicResponseBuilder - .setConsensusTimestamp(toTimestamp(START_TIME.plusSeconds(sequenceNumber))) - .setSequenceNumber(sequenceNumber) - .setMessage(message) - .setRunningHash(message) - .setRunningHashVersion(2L) - .build(); - } - - static private Instant toInstant(Timestamp timestamp) { - return Instant.ofEpochSecond(timestamp.getSeconds(), timestamp.getNanos()); - } - - static private Timestamp toTimestamp(Instant instant) { - return Timestamp.newBuilder() - .setSeconds(instant.getEpochSecond()) - .setNanos(instant.getNano()) - .build(); - } - - private static class ConsensusServiceStub extends ConsensusServiceGrpc.ConsensusServiceImplBase { - - private final Queue requests = new ArrayDeque<>(); - private final Queue responses = new ArrayDeque<>(); - - @Override - public void subscribeTopic(ConsensusTopicQuery consensusTopicQuery, - StreamObserver streamObserver) { - var request = requests.poll(); - assertThat(request).isNotNull(); - assertThat(consensusTopicQuery).isEqualTo(request); - - while (!responses.isEmpty()) { - var response = responses.poll(); - assertThat(response).isNotNull(); - - if (response instanceof Throwable) { - streamObserver.onError((Throwable) response); - return; - } - - streamObserver.onNext((ConsensusTopicResponse) response); - } - - streamObserver.onCompleted(); - } - - public void verify() { - assertThat(requests).isEmpty(); - assertThat(responses).isEmpty(); - } - } - - private byte[] combine(byte[] array1, byte[] array2) { - byte[] joinedArray = new byte[array1.length + array2.length]; - System.arraycopy(array1, 0, joinedArray, 0, array1.length); - System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); - return joinedArray; - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicMessageSubmitTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicMessageSubmitTransactionTest.java deleted file mode 100644 index d4241074df..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicMessageSubmitTransactionTest.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.hedera.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import com.hedera.hashgraph.sdk.proto.ConsensusSubmitMessageTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class TopicMessageSubmitTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - private static final TopicId testTopicId = new TopicId(0, 6, 9); - private static final byte[] testMessageBytes = {0x04, 0x05, 0x06}; - private static final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - private TopicMessageSubmitTransaction spawnTestTransactionString() { - return new TopicMessageSubmitTransaction().setNodeAccountIds( - Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setTopicId(testTopicId).setMessage(new String(testMessageBytes)).freeze().sign(unusedPrivateKey); - } - - private TopicMessageSubmitTransaction spawnTestTransactionBytes() { - return new TopicMessageSubmitTransaction().setNodeAccountIds( - Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setTopicId(testTopicId).setMessage(testMessageBytes).freeze().sign(unusedPrivateKey); - } - - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new TopicMessageSubmitTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setConsensusSubmitMessage(ConsensusSubmitMessageTransactionBody.newBuilder().build()).build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(TopicMessageSubmitTransaction.class); - } - - - @Test - void constructTopicMessageSubmitTransactionFromTransactionBodyProtobuf() { - var transactionBody = ConsensusSubmitMessageTransactionBody.newBuilder().setTopicID(testTopicId.toProtobuf()) - .setMessage(ByteString.copyFrom(testMessageBytes)).build(); - - var tx = TransactionBody.newBuilder().setConsensusSubmitMessage(transactionBody).build(); - - var topicSubmitMessageTransaction = new TopicMessageSubmitTransaction(tx); - assertThat(topicSubmitMessageTransaction.getTopicId()).isEqualTo(testTopicId); - } - - @Test - void getSetTopicId() { - var topicSubmitMessageTransaction = new TopicMessageSubmitTransaction().setTopicId(testTopicId); - assertThat(topicSubmitMessageTransaction.getTopicId()).isEqualTo(testTopicId); - } - - @Test - void getSetTopicIdFrozen() { - var tx = spawnTestTransactionString(); - assertThrows(IllegalStateException.class, () -> tx.setTopicId(testTopicId)); - } - - @Test - void getSetMessage() { - var topicSubmitMessageTransactionString = new TopicMessageSubmitTransaction().setMessage( - new String(testMessageBytes)); - var topicSubmitMessageTransactionBytes = new TopicMessageSubmitTransaction().setMessage(testMessageBytes); - assertThat(topicSubmitMessageTransactionString.getMessage().toByteArray()).isEqualTo(testMessageBytes); - assertThat(topicSubmitMessageTransactionBytes.getMessage().toByteArray()).isEqualTo(testMessageBytes); - } - - @Test - void getSetMessageFrozen() { - var topicSubmitMessageTransactionString = spawnTestTransactionString(); - var topicSubmitMessageTransactionBytes = spawnTestTransactionBytes(); - assertThrows(IllegalStateException.class, - () -> topicSubmitMessageTransactionString.setMessage(testMessageBytes)); - assertThrows(IllegalStateException.class, - () -> topicSubmitMessageTransactionBytes.setMessage(testMessageBytes)); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicMessageTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicMessageTest.java deleted file mode 100644 index d3bb8ad63f..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicMessageTest.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.hedera.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import com.hedera.hashgraph.sdk.proto.ConsensusMessageChunkInfo; -import com.hedera.hashgraph.sdk.proto.Timestamp; -import com.hedera.hashgraph.sdk.proto.mirror.ConsensusTopicResponse; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - -public class TopicMessageTest { - - private static final Instant testTimestamp = Instant.ofEpochSecond(1554158542); - private static final byte[] testContents = new byte[]{0x01, 0x02, 0x03}; - private static final byte[] testRunningHash = new byte[]{0x04, 0x05, 0x06}; - private static final long testSequenceNumber = 7L; - private static final TransactionId testTransactionId = new TransactionId(new AccountId(1), testTimestamp); - - @Test - void constructWithArgs() { - TopicMessageChunk topicMessageChunk = new TopicMessageChunk(ConsensusTopicResponse.newBuilder() - .setConsensusTimestamp(Timestamp.newBuilder().setSeconds(testTimestamp.getEpochSecond())) - .setRunningHash(ByteString.copyFrom(testRunningHash)).setSequenceNumber(testSequenceNumber).build()); - - TopicMessageChunk[] topicMessageChunkArr = {topicMessageChunk, topicMessageChunk, topicMessageChunk}; - - TopicMessage topicMessage = new TopicMessage(testTimestamp, testContents, testRunningHash, testSequenceNumber, - topicMessageChunkArr, testTransactionId); - - assertThat(topicMessage.consensusTimestamp).isEqualTo(testTimestamp); - assertThat(topicMessage.contents).isEqualTo(testContents); - assertThat(topicMessage.runningHash).isEqualTo(testRunningHash); - assertThat(topicMessage.sequenceNumber).isEqualTo(testSequenceNumber); - assertThat(topicMessage.chunks).hasSize(3); - assertThat(topicMessage.transactionId).isEqualTo(testTransactionId); - } - - @Test - void ofSingle() { - var consensusTopicResponse = ConsensusTopicResponse.newBuilder() - .setConsensusTimestamp(Timestamp.newBuilder().setSeconds(testTimestamp.getEpochSecond())) - .setMessage(ByteString.copyFrom(testContents)).setRunningHash(ByteString.copyFrom(testRunningHash)) - .setSequenceNumber(testSequenceNumber).setChunkInfo( - ConsensusMessageChunkInfo.newBuilder().setInitialTransactionID(testTransactionId.toProtobuf()).build()) - .build(); - - TopicMessage topicMessage = TopicMessage.ofSingle(consensusTopicResponse); - - assertThat(topicMessage.consensusTimestamp).isEqualTo(testTimestamp); - assertThat(topicMessage.contents).isEqualTo(testContents); - assertThat(topicMessage.runningHash).isEqualTo(testRunningHash); - assertThat(topicMessage.sequenceNumber).isEqualTo(testSequenceNumber); - assertThat(topicMessage.chunks).hasSize(1); - assertThat(topicMessage.transactionId).isEqualTo(testTransactionId); - } - - @Test - void ofMany() { - var consensusTopicResponse1 = ConsensusTopicResponse.newBuilder() - .setConsensusTimestamp(Timestamp.newBuilder().setSeconds(testTimestamp.getEpochSecond())) - .setMessage(ByteString.copyFrom(testContents)).setRunningHash(ByteString.copyFrom(testRunningHash)) - .setSequenceNumber(testSequenceNumber).setChunkInfo( - ConsensusMessageChunkInfo.newBuilder().setInitialTransactionID(testTransactionId.toProtobuf()) - .setNumber(1).setTotal(2).build()).build(); - - var consensusTopicResponse2 = ConsensusTopicResponse.newBuilder() - .setConsensusTimestamp(Timestamp.newBuilder().setSeconds(testTimestamp.getEpochSecond() + 1)) - .setMessage(ByteString.copyFrom(testContents)).setRunningHash(ByteString.copyFrom(testRunningHash)) - .setSequenceNumber(testSequenceNumber + 1L) - .setChunkInfo(ConsensusMessageChunkInfo.newBuilder().setNumber(2).setTotal(2).build()).build(); - - TopicMessage topicMessage = TopicMessage.ofMany(List.of(consensusTopicResponse1, consensusTopicResponse2)); - - byte[] totalContents = new byte[testContents.length * 2]; - System.arraycopy(testContents, 0, totalContents, 0, testContents.length); - System.arraycopy(testContents, 0, totalContents, testContents.length, testContents.length); - assertThat(topicMessage.consensusTimestamp).isEqualTo(testTimestamp.plusSeconds(1)); - assertThat(topicMessage.contents).isEqualTo(totalContents); - assertThat(topicMessage.runningHash).isEqualTo(testRunningHash); - assertThat(topicMessage.sequenceNumber).isEqualTo(testSequenceNumber + 1L); - assertThat(topicMessage.chunks).hasSize(2); - assertThat(topicMessage.transactionId).isEqualTo(testTransactionId); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicUpdateTransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicUpdateTransactionTest.java deleted file mode 100644 index 9cda568a9b..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicUpdateTransactionTest.java +++ /dev/null @@ -1,277 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.StringValue; -import com.hedera.hashgraph.sdk.proto.ConsensusUpdateTopicTransactionBody; -import com.hedera.hashgraph.sdk.proto.CryptoDeleteTransactionBody; -import com.hedera.hashgraph.sdk.proto.SchedulableTransactionBody; -import com.hedera.hashgraph.sdk.proto.Timestamp; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Duration; -import java.time.Instant; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class TopicUpdateTransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - private static final PublicKey testAdminKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e11") - .getPublicKey(); - private static final PublicKey testSubmitKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e12") - .getPublicKey(); - private static final TopicId testTopicId = TopicId.fromString("0.0.5007"); - private static final String testTopicMemo = "test memo"; - private static final Duration testAutoRenewPeriod = Duration.ofHours(10); - private static final Instant testExpirationTime = Instant.now(); - private static final AccountId testAutoRenewAccountId = AccountId.fromString("8.8.8"); - private static final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void clearShouldSerialize() { - SnapshotMatcher.expect(new TopicUpdateTransaction().setNodeAccountIds( - Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setTopicId(testTopicId).clearAdminKey().clearAutoRenewAccountId().clearSubmitKey().clearTopicMemo() - .freeze().sign(unusedPrivateKey).toString()).toMatchSnapshot(); - } - - @Test - void setShouldSerialize() { - SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); - } - @Test - void shouldBytesNoSetters() throws Exception { - var tx = new TopicUpdateTransaction(); - var tx2 = Transaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - private TopicUpdateTransaction spawnTestTransaction() { - return new TopicUpdateTransaction().setNodeAccountIds( - Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) - .setTopicId(testTopicId).setAdminKey(testAdminKey).setAutoRenewAccountId(testAutoRenewAccountId) - .setAutoRenewPeriod(testAutoRenewPeriod).setSubmitKey(testSubmitKey).setTopicMemo(testTopicMemo) - .setExpirationTime(validStart).freeze().sign(unusedPrivateKey); - } - - @Test - void shouldBytes() throws Exception { - var tx = spawnTestTransaction(); - var tx2 = TopicUpdateTransaction.fromBytes(tx.toBytes()); - assertThat(tx2.toString()).isEqualTo(tx.toString()); - } - - @Test - void fromScheduledTransaction() { - var transactionBody = SchedulableTransactionBody.newBuilder() - .setConsensusUpdateTopic(ConsensusUpdateTopicTransactionBody.newBuilder().build()).build(); - - var tx = Transaction.fromScheduledTransaction(transactionBody); - - assertThat(tx).isInstanceOf(TopicUpdateTransaction.class); - } - - @Test - void constructTopicUpdateTransactionFromTransactionBodyProtobuf() { - var transactionBody = ConsensusUpdateTopicTransactionBody.newBuilder().setTopicID(testTopicId.toProtobuf()) - .setMemo(StringValue.newBuilder().setValue(testTopicMemo).build()) - .setExpirationTime(Timestamp.newBuilder().setSeconds(testExpirationTime.getEpochSecond()).build()) - .setAdminKey(testAdminKey.toProtobufKey()).setSubmitKey(testSubmitKey.toProtobufKey()).setAutoRenewPeriod( - com.hedera.hashgraph.sdk.proto.Duration.newBuilder().setSeconds(testAutoRenewPeriod.toSeconds()) - .build()).setAutoRenewAccount(testAutoRenewAccountId.toProtobuf()).build(); - - var tx = TransactionBody.newBuilder().setConsensusUpdateTopic(transactionBody).build(); - var topicUpdateTransaction = new TopicUpdateTransaction(tx); - - assertThat(topicUpdateTransaction.getTopicId()).isEqualTo(testTopicId); - assertThat(topicUpdateTransaction.getTopicMemo()).isEqualTo(testTopicMemo); - assertThat(topicUpdateTransaction.getExpirationTime().getEpochSecond()).isEqualTo( - testExpirationTime.getEpochSecond()); - assertThat(topicUpdateTransaction.getAdminKey()).isEqualTo(testAdminKey); - assertThat(topicUpdateTransaction.getSubmitKey()).isEqualTo(testSubmitKey); - assertThat(topicUpdateTransaction.getAutoRenewPeriod().toSeconds()).isEqualTo(testAutoRenewPeriod.toSeconds()); - assertThat(topicUpdateTransaction.getAutoRenewAccountId()).isEqualTo(testAutoRenewAccountId); - } - - // doesn't throw an exception as opposed to C++ sdk - @Test - void constructTopicUpdateTransactionFromWrongTransactionBodyProtobuf() { - var transactionBody = CryptoDeleteTransactionBody.newBuilder().build(); - var tx = TransactionBody.newBuilder().setCryptoDelete(transactionBody).build(); - - new TopicUpdateTransaction(tx); - } - - @Test - void getSetTopicId() { - var topicUpdateTransaction = new TopicUpdateTransaction().setTopicId(testTopicId); - assertThat(topicUpdateTransaction.getTopicId()).isEqualTo(testTopicId); - } - - @Test - void getSetTopicIdFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setTopicId(testTopicId)); - } - - @Test - void getSetTopicMemo() { - var topicUpdateTransaction = new TopicUpdateTransaction().setTopicMemo(testTopicMemo); - assertThat(topicUpdateTransaction.getTopicMemo()).isEqualTo(testTopicMemo); - } - - @Test - void getSetTopicMemoFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setTopicMemo(testTopicMemo)); - } - - @Test - void clearTopicMemo() { - var topicUpdateTransaction = new TopicUpdateTransaction().setTopicMemo(testTopicMemo); - topicUpdateTransaction.clearTopicMemo(); - assertThat(topicUpdateTransaction.getTopicMemo()).isEmpty(); - } - - @Test - void clearTopicMemoFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.clearTopicMemo()); - } - - @Test - void getSetExpirationTime() { - var topicUpdateTransaction = new TopicUpdateTransaction().setExpirationTime(testExpirationTime); - assertThat(topicUpdateTransaction.getExpirationTime()).isEqualTo(testExpirationTime); - } - - @Test - void getSetExpirationTimeFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setExpirationTime(testExpirationTime)); - } - - @Test - void getSetAdminKey() { - var topicUpdateTransaction = new TopicUpdateTransaction().setAdminKey(testAdminKey); - assertThat(topicUpdateTransaction.getAdminKey()).isEqualTo(testAdminKey); - } - - @Test - void getSetAdminKeyFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setAdminKey(testAdminKey)); - } - - @Test - void clearAdminKey() { - var topicUpdateTransaction = new TopicUpdateTransaction().setAdminKey(testAdminKey); - topicUpdateTransaction.clearAdminKey(); - assertThat(topicUpdateTransaction.getAdminKey()).isEqualTo(new KeyList()); - } - - @Test - void clearAdminKeyFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.clearAdminKey()); - } - - @Test - void getSetSubmitKey() { - var topicUpdateTransaction = new TopicUpdateTransaction().setSubmitKey(testSubmitKey); - assertThat(topicUpdateTransaction.getSubmitKey()).isEqualTo(testSubmitKey); - } - - @Test - void getSetSubmitKeyFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setSubmitKey(testSubmitKey)); - } - - @Test - void clearSubmitKey() { - var topicUpdateTransaction = new TopicUpdateTransaction().setSubmitKey(testSubmitKey); - topicUpdateTransaction.clearSubmitKey(); - assertThat(topicUpdateTransaction.getSubmitKey()).isEqualTo(new KeyList()); - } - - @Test - void clearSubmitKeyFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.clearSubmitKey()); - } - - @Test - void getSetAutoRenewPeriod() { - var topicUpdateTransaction = new TopicUpdateTransaction().setAutoRenewPeriod(testAutoRenewPeriod); - assertThat(topicUpdateTransaction.getAutoRenewPeriod()).isEqualTo(testAutoRenewPeriod); - } - - @Test - void getSetAutoRenewPeriodFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setAutoRenewPeriod(testAutoRenewPeriod)); - } - - @Test - void getSetAutoRenewAccountId() { - var topicUpdateTransaction = new TopicUpdateTransaction().setAutoRenewAccountId(testAutoRenewAccountId); - assertThat(topicUpdateTransaction.getAutoRenewAccountId()).isEqualTo(testAutoRenewAccountId); - } - - @Test - void getSetAutoRenewAccountIdFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.setAutoRenewAccountId(testAutoRenewAccountId)); - } - - @Test - void clearAutoRenewAccountId() { - var topicUpdateTransaction = new TopicUpdateTransaction().setAutoRenewAccountId(testAutoRenewAccountId); - topicUpdateTransaction.clearAutoRenewAccountId(); - assertThat(topicUpdateTransaction.getAutoRenewAccountId()).isEqualTo(new AccountId(0)); - } - - @Test - void clearAutoRenewAccountIdFrozen() { - var tx = spawnTestTransaction(); - assertThrows(IllegalStateException.class, () -> tx.clearAutoRenewAccountId()); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicUpdateTransactionTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicUpdateTransactionTest.snap deleted file mode 100644 index bc899ce78c..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicUpdateTransactionTest.snap +++ /dev/null @@ -1,8 +0,0 @@ -com.hedera.hashgraph.sdk.TopicUpdateTransactionTest.clearShouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nconsensus_update_topic {\n admin_key {\n key_list {\n }\n }\n auto_renew_account {\n account_num: 0\n realm_num: 0\n shard_num: 0\n }\n memo {\n }\n submit_key {\n key_list {\n }\n }\n topic_i_d {\n realm_num: 0\n shard_num: 0\n topic_num: 5007\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 200000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] - - -com.hedera.hashgraph.sdk.TopicUpdateTransactionTest.setShouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.TransactionBody\nconsensus_update_topic {\n admin_key {\n ed25519: \"\\332\\207p\\020\\227\\206ns\\360\\335\\224,\\273>\\227\\0063)\\371\\005X\\206!\\261x\\322\\027Yh\\215G\\374\"\n }\n auto_renew_account {\n account_num: 8\n realm_num: 8\n shard_num: 8\n }\n auto_renew_period {\n seconds: 36000\n }\n expiration_time {\n seconds: 1554158542\n }\n memo {\n value: \"test memo\"\n }\n submit_key {\n ed25519: \"\\373\\210\\2637\\337\\327ea{\\3442*\\347\\357\\2053\\326\\036d\\203\\005\\016 \\354HE\\2453\\275\\334\\244\\261\"\n }\n topic_i_d {\n realm_num: 0\n shard_num: 0\n topic_num: 5007\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 200000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionIdTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionIdTest.snap deleted file mode 100644 index b6f60979bd..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionIdTest.snap +++ /dev/null @@ -1,8 +0,0 @@ -com.hedera.hashgraph.sdk.TransactionIdTest.shouldSerialize2=[ - "0.0.23847@1588539964.632521325?scheduled/3" -] - - -com.hedera.hashgraph.sdk.TransactionIdTest.shouldSerialize=[ - "0.0.23847@1588539964.632521325" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionReceiptQueryTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionReceiptQueryTest.java deleted file mode 100644 index 946b36c97c..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionReceiptQueryTest.java +++ /dev/null @@ -1,54 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; - -public class TransactionReceiptQueryTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - var builder = com.hedera.hashgraph.sdk.proto.Query.newBuilder(); - new TransactionReceiptQuery() - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5005"), validStart)) - .onMakeRequest(builder, QueryHeader.newBuilder().build()); - SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")).toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionReceiptQueryTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionReceiptQueryTest.snap deleted file mode 100644 index f27c7718a0..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionReceiptQueryTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.TransactionReceiptQueryTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.Query\ntransaction_get_receipt {\n header {\n }\n transaction_i_d {\n account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n }\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionReceiptTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionReceiptTest.java deleted file mode 100644 index 5e950a62fd..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionReceiptTest.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.hedera.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.time.Instant; -import java.util.ArrayList; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - - -public class TransactionReceiptTest { - final static Instant time = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - static TransactionReceipt spawnReceiptExample() { - return new TransactionReceipt( - null, - Status.SCHEDULE_ALREADY_DELETED, - new ExchangeRate(3, 4, time), - AccountId.fromString("1.2.3"), - FileId.fromString("4.5.6"), - ContractId.fromString("3.2.1"), - TopicId.fromString("9.8.7"), - TokenId.fromString("6.5.4"), - 3L, - ByteString.copyFrom("how now brown cow", StandardCharsets.UTF_8), - 30L, - ScheduleId.fromString("1.1.1"), - TransactionId.withValidStart(AccountId.fromString("3.3.3"), time), - List.of(1L, 2L, 3L), - 1, - new ArrayList<>(), - new ArrayList<>() - ); - } - - @Test - void shouldSerialize() throws Exception { - var originalTransactionReceipt = spawnReceiptExample(); - byte[] transactionReceiptBytes = originalTransactionReceipt.toBytes(); - var copyTransactionReceipt = TransactionReceipt.fromBytes(transactionReceiptBytes); - assertThat(copyTransactionReceipt.toString()).isEqualTo(originalTransactionReceipt.toString()); - SnapshotMatcher.expect(originalTransactionReceipt.toString()).toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionRecordQueryTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionRecordQueryTest.java deleted file mode 100644 index 15590be199..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionRecordQueryTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.proto.QueryHeader; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.time.Instant; - -public class TransactionRecordQueryTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - - final Instant validStart = Instant.ofEpochSecond(1554158542); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - @Test - void shouldSerialize() { - var builder = com.hedera.hashgraph.sdk.proto.Query.newBuilder(); - spawnQuery().onMakeRequest(builder, QueryHeader.newBuilder().build()); - SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")).toMatchSnapshot(); - } - - private TransactionRecordQuery spawnQuery() { - return new TransactionRecordQuery() - .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5005"), validStart)) - .setIncludeChildren(true) - .setIncludeDuplicates(true); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionRecordQueryTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionRecordQueryTest.snap deleted file mode 100644 index 1628f83513..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionRecordQueryTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -com.hedera.hashgraph.sdk.TransactionRecordQueryTest.shouldSerialize=[ - "# com.hedera.hashgraph.sdk.proto.Query\ntransaction_get_record {\n header {\n }\n include_child_records: true\n include_duplicates: true\n transaction_i_d {\n account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n }\n}" -] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionRecordTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionRecordTest.java deleted file mode 100644 index f68ed794f7..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionRecordTest.java +++ /dev/null @@ -1,116 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import com.google.protobuf.BytesValue; -import io.github.jsonSnapshot.SnapshotMatcher; -import org.bouncycastle.util.encoders.Hex; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import javax.annotation.Nullable; -import java.nio.charset.StandardCharsets; -import java.time.Instant; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import static com.hedera.hashgraph.sdk.ContractFunctionResultTest.CALL_RESULT_HEX; -import static com.hedera.hashgraph.sdk.TransactionReceiptTest.spawnReceiptExample; -import static org.assertj.core.api.Assertions.assertThat; - -public class TransactionRecordTest { - final static Instant time = Instant.ofEpochSecond(1554158542); - private static final byte[] callResult = Hex.decode(CALL_RESULT_HEX); - - @BeforeAll - public static void beforeAll() { - SnapshotMatcher.start(Snapshot::asJsonString); - } - - @AfterAll - public static void afterAll() { - SnapshotMatcher.validateSnapshots(); - } - - private static TransactionRecord spawnRecordExample(@Nullable ByteString prngBytes, @Nullable Integer prngNumber) { - return new TransactionRecord( - spawnReceiptExample(), - ByteString.copyFrom("hello", StandardCharsets.UTF_8), - time, - TransactionId.withValidStart(AccountId.fromString("3.3.3"), time), - "memo", - 3000L, - new ContractFunctionResult( - com.hedera.hashgraph.sdk.proto.ContractFunctionResult.newBuilder() - .setContractID(ContractId.fromString("1.2.3").toProtobuf()) - .setContractCallResult(ByteString.copyFrom(callResult)) - .setEvmAddress(BytesValue.newBuilder().setValue(ByteString.copyFrom(Hex.decode("98329e006610472e6B372C080833f6D79ED833cf"))).build()) - .setSenderId(AccountId.fromString("1.2.3").toProtobuf()) - ), - List.of(new Transfer(AccountId.fromString("4.4.4"), Hbar.from(5))), - Map.of(TokenId.fromString("6.6.6"), Map.of(AccountId.fromString("1.1.1"), 4L)), - List.of(new TokenTransfer(TokenId.fromString("8.9.10"), AccountId.fromString("1.2.3"), 4L, 3, true)), - Map.of(TokenId.fromString("4.4.4"), List.of(new TokenNftTransfer(TokenId.fromString("4.4.4"), AccountId.fromString("1.2.3"), AccountId.fromString("3.2.1"), 4L, true))), - ScheduleId.fromString("3.3.3"), - List.of(new AssessedCustomFee(4L, TokenId.fromString("4.5.6"), AccountId.fromString("8.6.5"), List.of(AccountId.fromString("3.3.3")))), - List.of(new TokenAssociation(TokenId.fromString("5.4.3"), AccountId.fromString("8.7.6"))), - PrivateKey.fromStringECDSA("8776c6b831a1b61ac10dac0304a2843de4716f54b1919bb91a2685d0fe3f3048").getPublicKey(), - new ArrayList<>(), - new ArrayList<>(), - time, - ByteString.copyFrom("Some hash", StandardCharsets.UTF_8), - List.of(new Transfer(AccountId.fromString("1.2.3"), Hbar.from(8))), - prngBytes, - prngNumber, - ByteString.copyFrom("0x00", StandardCharsets.UTF_8), - List.of( - new PendingAirdropRecord(new PendingAirdropId(AccountId.fromString("0.0.123"), AccountId.fromString("0.0.124"), NftId.fromString("0.0.5005/1234")), 123), - new PendingAirdropRecord(new PendingAirdropId(AccountId.fromString("0.0.123"), AccountId.fromString("0.0.124"), TokenId.fromString("0.0.12345")), 123) - ) - ); - } - - @Test - void shouldSerialize() throws Exception { - var originalRecord = spawnRecordExample( - ByteString.copyFrom("very random bytes", StandardCharsets.UTF_8), - null - ); - byte[] recordBytes = originalRecord.toBytes(); - var copyRecord = TransactionRecord.fromBytes(recordBytes); - assertThat(copyRecord.toString()).isEqualTo(originalRecord.toString()); - SnapshotMatcher.expect(originalRecord.toString()).toMatchSnapshot(); - } - - @Test - void shouldSerialize2() throws Exception { - var originalRecord = spawnRecordExample( - null, - 4 /* chosen by fair dice roll. Guaranteed to be random */ - ); - byte[] recordBytes = originalRecord.toBytes(); - var copyRecord = TransactionRecord.fromBytes(recordBytes); - assertThat(copyRecord.toString()).isEqualTo(originalRecord.toString()); - SnapshotMatcher.expect(originalRecord.toString()).toMatchSnapshot(); - } -} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionTest.java deleted file mode 100644 index db62bd65ff..0000000000 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionTest.java +++ /dev/null @@ -1,113 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.SignedTransaction; -import com.hedera.hashgraph.sdk.proto.TokenAssociateTransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import org.bouncycastle.util.encoders.Hex; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Arrays; -import java.util.List; - -import static com.hedera.hashgraph.sdk.Transaction.fromBytes; -import static org.assertj.core.api.Assertions.assertThat; - - -public class TransactionTest { - private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); - private static final List testNodeAccountIds = Arrays.asList(AccountId.fromString("0.0.5005"), - AccountId.fromString("0.0.5006")); - private static final AccountId testAccountId = AccountId.fromString("0.0.5006"); - private static final Instant validStart = Instant.ofEpochSecond(1554158542); - - @Test - void transactionFromBytesWorksWithProtobufTransactionBytes() throws InvalidProtocolBufferException { - var bytes = Hex.decode( - "1acc010a640a2046fe5013b6f6fc796c3e65ec10d2a10d03c07188fc3de13d46caad6b8ec4dfb81a4045f1186be5746c9783f68cb71d6a71becd3ffb024906b855ac1fa3a2601273d41b58446e5d6a0aaf421c229885f9e70417353fab2ce6e9d8e7b162e9944e19020a640a20f102e75ff7dc3d72c9b7075bb246fcc54e714c59714814011e8f4b922d2a6f0a1a40f2e5f061349ab03fa21075020c75cf876d80498ae4bac767f35941b8e3c393b0e0a886ede328e44c1df7028ea1474722f2dcd493812d04db339480909076a10122500a180a0c08a1cc98830610c092d09e0312080800100018e4881d120608001000180418b293072202087872240a220a0f0a080800100018e4881d10ff83af5f0a0f0a080800100018eb881d108084af5f"); - - var transaction = (TransferTransaction) fromBytes(bytes); - - assertThat(transaction.getHbarTransfers()).containsEntry(new AccountId(476260), new Hbar(1).negated()); - assertThat(transaction.getHbarTransfers()).containsEntry(new AccountId(476267), new Hbar(1)); - } - - @Test - void tokenAssociateTransactionFromTransactionBodyBytes() throws InvalidProtocolBufferException { - var tokenAssociateTransactionBodyProto = TokenAssociateTransactionBody.newBuilder().build(); - var transactionBodyProto = TransactionBody.newBuilder().setTokenAssociate(tokenAssociateTransactionBodyProto) - .build(); - - TokenAssociateTransaction tokenAssociateTransaction = spawnTestTransaction(transactionBodyProto); - - var tokenAssociateTransactionFromBytes = Transaction.fromBytes(tokenAssociateTransaction.toBytes()); - - assertThat(tokenAssociateTransactionFromBytes).isInstanceOf(TokenAssociateTransaction.class); - } - - @Test - void tokenAssociateTransactionFromSignedTransactionBytes() throws InvalidProtocolBufferException { - var tokenAssociateTransactionBodyProto = TokenAssociateTransactionBody.newBuilder().build(); - var transactionBodyProto = TransactionBody.newBuilder().setTokenAssociate(tokenAssociateTransactionBodyProto) - .build(); - - var signedTransactionProto = SignedTransaction.newBuilder().setBodyBytes(transactionBodyProto.toByteString()) - .build(); - var signedTransactionBodyProto = TransactionBody.parseFrom(signedTransactionProto.getBodyBytes()); - - TokenAssociateTransaction tokenAssociateTransaction = spawnTestTransaction(signedTransactionBodyProto); - - var tokenAssociateTransactionFromBytes = Transaction.fromBytes(tokenAssociateTransaction.toBytes()); - - assertThat(tokenAssociateTransactionFromBytes).isInstanceOf(TokenAssociateTransaction.class); - } - - @Test - void tokenAssociateTransactionFromTransactionBytes() throws InvalidProtocolBufferException { - var tokenAssociateTransactionBodyProto = TokenAssociateTransactionBody.newBuilder().build(); - var transactionBodyProto = TransactionBody.newBuilder().setTokenAssociate(tokenAssociateTransactionBodyProto) - .build(); - - var signedTransactionProto = SignedTransaction.newBuilder().setBodyBytes(transactionBodyProto.toByteString()) - .build(); - var signedTransactionBodyProto = TransactionBody.parseFrom(signedTransactionProto.getBodyBytes()); - - var transactionSignedProto = com.hedera.hashgraph.sdk.proto.Transaction.newBuilder() - .setSignedTransactionBytes(signedTransactionBodyProto.toByteString()).build(); - var transactionSignedBodyProto = TransactionBody.parseFrom(transactionSignedProto.getSignedTransactionBytes()); - - TokenAssociateTransaction tokenAssociateTransaction = spawnTestTransaction(transactionSignedBodyProto); - - var tokenAssociateTransactionFromBytes = Transaction.fromBytes(tokenAssociateTransaction.toBytes()); - - assertThat(tokenAssociateTransactionFromBytes).isInstanceOf(TokenAssociateTransaction.class); - } - - private TokenAssociateTransaction spawnTestTransaction(TransactionBody txBody) { - return new TokenAssociateTransaction( - txBody).setNodeAccountIds(testNodeAccountIds) - .setTransactionId(TransactionId.withValidStart(testAccountId, validStart)).freeze().sign(unusedPrivateKey); - } - -} diff --git a/sdk/src/test/java/org/hiero/sdk/AccountAllowanceApproveTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/AccountAllowanceApproveTransactionTest.java new file mode 100644 index 0000000000..47d0ca3c4c --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/AccountAllowanceApproveTransactionTest.java @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import org.hiero.sdk.proto.CryptoApproveAllowanceTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class AccountAllowanceApproveTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + private static final TokenId testTokenId = TokenId.fromString("1.2.3"); + private static final AccountId testOwnerAccountId = AccountId.fromString("4.5.7"); + private static final AccountId testSpenderAccountId = AccountId.fromString("8.9.0"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + AccountAllowanceApproveTransaction spawnTestTransaction() { + var ownerId = AccountId.fromString("5.6.7"); + return new AccountAllowanceApproveTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .addHbarAllowance(AccountId.fromString("1.1.1"), new Hbar(3)) + .addTokenAllowance(TokenId.fromString("2.2.2"), AccountId.fromString("3.3.3"), 6) + .addTokenNftAllowance(TokenId.fromString("4.4.4").nft(123), AccountId.fromString("5.5.5")) + .addTokenNftAllowance(TokenId.fromString("4.4.4").nft(456), AccountId.fromString("5.5.5")) + .addTokenNftAllowance(TokenId.fromString("8.8.8").nft(456), AccountId.fromString("5.5.5")) + .addTokenNftAllowance(TokenId.fromString("4.4.4").nft(789), AccountId.fromString("9.9.9")) + .addAllTokenNftAllowance(TokenId.fromString("6.6.6"), AccountId.fromString("7.7.7")) + .approveHbarAllowance(ownerId, AccountId.fromString("1.1.1"), new Hbar(3)) + .approveTokenAllowance(TokenId.fromString("2.2.2"), ownerId, AccountId.fromString("3.3.3"), 6) + .approveTokenNftAllowance(TokenId.fromString("4.4.4").nft(123), ownerId, AccountId.fromString("5.5.5")) + .approveTokenNftAllowance(TokenId.fromString("4.4.4").nft(456), ownerId, AccountId.fromString("5.5.5")) + .approveTokenNftAllowance(TokenId.fromString("8.8.8").nft(456), ownerId, AccountId.fromString("5.5.5")) + .approveTokenNftAllowance(TokenId.fromString("4.4.4").nft(789), ownerId, AccountId.fromString("9.9.9")) + .approveTokenNftAllowanceAllSerials(TokenId.fromString("6.6.6"), ownerId, AccountId.fromString("7.7.7")) + .setMaxTransactionFee(Hbar.fromTinybars(100_000)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = AccountAllowanceApproveTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void propertiesTest() { + var tx = spawnTestTransaction(); + + assertThat(tx.getHbarAllowances()).isNotEmpty(); + assertThat(tx.getHbarApprovals()).isNotEmpty(); + assertThat(tx.getTokenAllowances()).isNotEmpty(); + assertThat(tx.getTokenApprovals()).isNotEmpty(); + assertThat(tx.getTokenNftAllowances()).isNotEmpty(); + assertThat(tx.getTokenNftApprovals()).isNotEmpty(); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new AccountAllowanceApproveTransaction(); + var tx2 = AccountAllowanceApproveTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setCryptoApproveAllowance( + CryptoApproveAllowanceTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(AccountAllowanceApproveTransaction.class); + } + + @Test + void deleteNftAllowanceAllSerials() { + var accountAllowanceApproveTransaction = new AccountAllowanceApproveTransaction() + .deleteTokenNftAllowanceAllSerials(testTokenId, testOwnerAccountId, testSpenderAccountId); + + assertThat(accountAllowanceApproveTransaction.getTokenNftApprovals().size()) + .isEqualTo(1); + assertThat(accountAllowanceApproveTransaction.getTokenNftApprovals().get(0).tokenId) + .isEqualTo(testTokenId); + assertThat(accountAllowanceApproveTransaction.getTokenNftApprovals().get(0).ownerAccountId) + .isEqualTo(testOwnerAccountId); + assertThat(accountAllowanceApproveTransaction.getTokenNftApprovals().get(0).spenderAccountId) + .isEqualTo(testSpenderAccountId); + assertTrue(accountAllowanceApproveTransaction + .getTokenNftApprovals() + .get(0) + .serialNumbers + .isEmpty()); + assertFalse(accountAllowanceApproveTransaction.getTokenNftApprovals().get(0).allSerials); + assertNull(accountAllowanceApproveTransaction.getTokenNftApprovals().get(0).delegatingSpender); + } + + @Test + void deleteNftAllowanceAllSerialsFrozen() { + var tx = spawnTestTransaction(); + assertThrows( + IllegalStateException.class, + () -> tx.deleteTokenNftAllowanceAllSerials(testTokenId, testOwnerAccountId, testSpenderAccountId)); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/AccountAllowanceApproveTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/AccountAllowanceApproveTransactionTest.snap new file mode 100644 index 0000000000..23023ccade --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/AccountAllowanceApproveTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.AccountAllowanceApproveTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\ncrypto_approve_allowance {\n crypto_allowances {\n amount: 300000000\n spender {\n account_num: 1\n realm_num: 1\n shard_num: 1\n }\n }\n crypto_allowances {\n amount: 300000000\n owner {\n account_num: 7\n realm_num: 6\n shard_num: 5\n }\n spender {\n account_num: 1\n realm_num: 1\n shard_num: 1\n }\n }\n nft_allowances {\n serial_numbers: 123\n serial_numbers: 456\n spender {\n account_num: 5\n realm_num: 5\n shard_num: 5\n }\n token_id {\n realm_num: 4\n shard_num: 4\n token_num: 4\n }\n }\n nft_allowances {\n serial_numbers: 456\n spender {\n account_num: 5\n realm_num: 5\n shard_num: 5\n }\n token_id {\n realm_num: 8\n shard_num: 8\n token_num: 8\n }\n }\n nft_allowances {\n serial_numbers: 789\n spender {\n account_num: 9\n realm_num: 9\n shard_num: 9\n }\n token_id {\n realm_num: 4\n shard_num: 4\n token_num: 4\n }\n }\n nft_allowances {\n approved_for_all {\n value: true\n }\n spender {\n account_num: 7\n realm_num: 7\n shard_num: 7\n }\n token_id {\n realm_num: 6\n shard_num: 6\n token_num: 6\n }\n }\n nft_allowances {\n owner {\n account_num: 7\n realm_num: 6\n shard_num: 5\n }\n serial_numbers: 123\n serial_numbers: 456\n spender {\n account_num: 5\n realm_num: 5\n shard_num: 5\n }\n token_id {\n realm_num: 4\n shard_num: 4\n token_num: 4\n }\n }\n nft_allowances {\n owner {\n account_num: 7\n realm_num: 6\n shard_num: 5\n }\n serial_numbers: 456\n spender {\n account_num: 5\n realm_num: 5\n shard_num: 5\n }\n token_id {\n realm_num: 8\n shard_num: 8\n token_num: 8\n }\n }\n nft_allowances {\n owner {\n account_num: 7\n realm_num: 6\n shard_num: 5\n }\n serial_numbers: 789\n spender {\n account_num: 9\n realm_num: 9\n shard_num: 9\n }\n token_id {\n realm_num: 4\n shard_num: 4\n token_num: 4\n }\n }\n nft_allowances {\n approved_for_all {\n value: true\n }\n owner {\n account_num: 7\n realm_num: 6\n shard_num: 5\n }\n spender {\n account_num: 7\n realm_num: 7\n shard_num: 7\n }\n token_id {\n realm_num: 6\n shard_num: 6\n token_num: 6\n }\n }\n token_allowances {\n amount: 6\n spender {\n account_num: 3\n realm_num: 3\n shard_num: 3\n }\n token_id {\n realm_num: 2\n shard_num: 2\n token_num: 2\n }\n }\n token_allowances {\n amount: 6\n owner {\n account_num: 7\n realm_num: 6\n shard_num: 5\n }\n spender {\n account_num: 3\n realm_num: 3\n shard_num: 3\n }\n token_id {\n realm_num: 2\n shard_num: 2\n token_num: 2\n }\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/AccountAllowanceDeleteTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/AccountAllowanceDeleteTransactionTest.java new file mode 100644 index 0000000000..87b544937b --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/AccountAllowanceDeleteTransactionTest.java @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import org.hiero.sdk.proto.CryptoDeleteAllowanceTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class AccountAllowanceDeleteTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + AccountAllowanceDeleteTransaction spawnTestTransaction() { + var ownerId = AccountId.fromString("5.6.7"); + return new AccountAllowanceDeleteTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .deleteAllHbarAllowances(ownerId) + .deleteAllTokenAllowances(TokenId.fromString("2.2.2"), ownerId) + .deleteAllTokenNftAllowances(TokenId.fromString("4.4.4").nft(123), ownerId) + .deleteAllTokenNftAllowances(TokenId.fromString("4.4.4").nft(456), ownerId) + .deleteAllTokenNftAllowances(TokenId.fromString("8.8.8").nft(456), ownerId) + .deleteAllTokenNftAllowances(TokenId.fromString("4.4.4").nft(789), ownerId) + .setMaxTransactionFee(Hbar.fromTinybars(100_000)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = AccountAllowanceDeleteTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new AccountAllowanceDeleteTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setCryptoDeleteAllowance( + CryptoDeleteAllowanceTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(AccountAllowanceDeleteTransaction.class); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/AccountAllowanceDeleteTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/AccountAllowanceDeleteTransactionTest.snap new file mode 100644 index 0000000000..70808c7d29 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/AccountAllowanceDeleteTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.AccountAllowanceDeleteTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\ncrypto_delete_allowance {\n nft_allowances {\n owner {\n account_num: 7\n realm_num: 6\n shard_num: 5\n }\n serial_numbers: 123\n serial_numbers: 456\n serial_numbers: 789\n token_id {\n realm_num: 4\n shard_num: 4\n token_num: 4\n }\n }\n nft_allowances {\n owner {\n account_num: 7\n realm_num: 6\n shard_num: 5\n }\n serial_numbers: 456\n token_id {\n realm_num: 8\n shard_num: 8\n token_num: 8\n }\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/AccountBalanceQueryTest.java b/sdk/src/test/java/org/hiero/sdk/AccountBalanceQueryTest.java new file mode 100644 index 0000000000..5e771f938c --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/AccountBalanceQueryTest.java @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.github.jsonSnapshot.SnapshotMatcher; +import org.hiero.sdk.proto.QueryHeader; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class AccountBalanceQueryTest { + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerializeWithAccountId() { + var builder = org.hiero.sdk.proto.Query.newBuilder(); + new AccountBalanceQuery() + .setAccountId(AccountId.fromString("0.0.5005")) + .onMakeRequest(builder, QueryHeader.newBuilder().build()); + SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")) + .toMatchSnapshot(); + } + + @Test + void shouldSerializeWithContractId() { + var builder = org.hiero.sdk.proto.Query.newBuilder(); + new AccountBalanceQuery() + .setContractId(ContractId.fromString("0.0.5005")) + .onMakeRequest(builder, QueryHeader.newBuilder().build()); + SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")) + .toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/AccountBalanceQueryTest.snap b/sdk/src/test/java/org/hiero/sdk/AccountBalanceQueryTest.snap new file mode 100644 index 0000000000..618ddd7af2 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/AccountBalanceQueryTest.snap @@ -0,0 +1,8 @@ +org.hiero.sdk.AccountBalanceQueryTest.shouldSerializeWithAccountId=[ + "# org.hiero.sdk.proto.Query\ncryptoget_account_balance {\n account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n }\n header {\n }\n}" +] + + +org.hiero.sdk.AccountBalanceQueryTest.shouldSerializeWithContractId=[ + "# org.hiero.sdk.proto.Query\ncryptoget_account_balance {\n contract_i_d {\n contract_num: 5005\n realm_num: 0\n shard_num: 0\n }\n header {\n }\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/AccountCreateTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/AccountCreateTransactionTest.java new file mode 100644 index 0000000000..669b8ac1eb --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/AccountCreateTransactionTest.java @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Duration; +import java.time.Instant; +import java.util.Arrays; +import org.hiero.sdk.proto.CryptoCreateTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class AccountCreateTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + AccountCreateTransaction spawnTestTransaction() { + return new AccountCreateTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setKey(unusedPrivateKey) + .setInitialBalance(Hbar.fromTinybars(450)) + .setProxyAccountId(AccountId.fromString("0.0.1001")) + .setAccountMemo("some dumb memo") + .setReceiverSignatureRequired(true) + .setAutoRenewPeriod(Duration.ofHours(10)) + .setStakedAccountId(AccountId.fromString("0.0.3")) + .setAlias("0x5c562e90feaf0eebd33ea75d21024f249d451417") + .setMaxAutomaticTokenAssociations(100) + .setMaxTransactionFee(Hbar.fromTinybars(100_000)) + .freeze() + .sign(unusedPrivateKey); + } + + AccountCreateTransaction spawnTestTransaction2() { + return new AccountCreateTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setKey(unusedPrivateKey) + .setInitialBalance(Hbar.fromTinybars(450)) + .setProxyAccountId(AccountId.fromString("0.0.1001")) + .setAccountMemo("some dumb memo") + .setReceiverSignatureRequired(true) + .setAutoRenewPeriod(Duration.ofHours(10)) + .setStakedNodeId(4L) + .setMaxAutomaticTokenAssociations(100) + .setMaxTransactionFee(Hbar.fromTinybars(100_000)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = AccountCreateTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldSerialize2() { + SnapshotMatcher.expect(spawnTestTransaction2().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytes2() throws Exception { + var tx = spawnTestTransaction2(); + var tx2 = AccountCreateTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new AccountCreateTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void propertiesTest() { + var tx = spawnTestTransaction(); + + assertThat(tx.getKey()).isEqualTo(unusedPrivateKey); + assertThat(tx.getInitialBalance()).isEqualTo(Hbar.fromTinybars(450)); + assertThat(tx.getReceiverSignatureRequired()).isTrue(); + assertThat(tx.getProxyAccountId()).hasToString("0.0.1001"); + assertThat(tx.getAutoRenewPeriod().toHours()).isEqualTo(10); + assertThat(tx.getMaxAutomaticTokenAssociations()).isEqualTo(100); + assertThat(tx.getAccountMemo()).isEqualTo("some dumb memo"); + assertThat(tx.getStakedAccountId()).hasToString("0.0.3"); + assertThat(tx.getStakedNodeId()).isNull(); + assertThat(tx.getDeclineStakingReward()).isFalse(); + assertThat(tx.getAlias()).isEqualTo(EvmAddress.fromString("0x5c562e90feaf0eebd33ea75d21024f249d451417")); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setCryptoCreateAccount(CryptoCreateTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(AccountCreateTransaction.class); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/AccountCreateTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/AccountCreateTransactionTest.snap new file mode 100644 index 0000000000..387f66b81e --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/AccountCreateTransactionTest.snap @@ -0,0 +1,8 @@ +org.hiero.sdk.AccountCreateTransactionTest.shouldSerialize2=[ + "# org.hiero.sdk.proto.TransactionBody\ncrypto_create_account {\n auto_renew_period {\n seconds: 36000\n }\n initial_balance: 450\n key {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n max_automatic_token_associations: 100\n memo: \"some dumb memo\"\n proxy_account_i_d {\n account_num: 1001\n realm_num: 0\n shard_num: 0\n }\n receive_record_threshold: 0\n receiver_sig_required: true\n send_record_threshold: 0\n staked_node_id: 4\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] + + +org.hiero.sdk.AccountCreateTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\ncrypto_create_account {\n alias: \"\\\\V.\\220\\376\\257\\016\\353\\323>\\247]!\\002O$\\235E\\024\\027\"\n auto_renew_period {\n seconds: 36000\n }\n initial_balance: 450\n key {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n max_automatic_token_associations: 100\n memo: \"some dumb memo\"\n proxy_account_i_d {\n account_num: 1001\n realm_num: 0\n shard_num: 0\n }\n receive_record_threshold: 0\n receiver_sig_required: true\n send_record_threshold: 0\n staked_account_id {\n account_num: 3\n realm_num: 0\n shard_num: 0\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/AccountDeleteTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/AccountDeleteTransactionTest.java new file mode 100644 index 0000000000..ca01402d0e --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/AccountDeleteTransactionTest.java @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import org.hiero.sdk.proto.CryptoDeleteTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class AccountDeleteTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + private AccountDeleteTransaction spawnTestTransaction() { + return new AccountDeleteTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setAccountId(AccountId.fromString("0.0.5007")) + .setTransferAccountId(AccountId.fromString("0.0.5008")) + .setMaxTransactionFee(Hbar.fromTinybars(100_000)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = AccountDeleteTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new AccountDeleteTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setCryptoDelete(CryptoDeleteTransactionBody.newBuilder() + .setDeleteAccountID(AccountId.fromString("6.6.6").toProtobuf()) + .build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(AccountDeleteTransaction.class); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/AccountDeleteTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/AccountDeleteTransactionTest.snap new file mode 100644 index 0000000000..9dd69d1bc3 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/AccountDeleteTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.AccountDeleteTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\ncrypto_delete {\n delete_account_i_d {\n account_num: 5007\n realm_num: 0\n shard_num: 0\n }\n transfer_account_i_d {\n account_num: 5008\n realm_num: 0\n shard_num: 0\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/AccountIdTest.java b/sdk/src/test/java/org/hiero/sdk/AccountIdTest.java new file mode 100644 index 0000000000..2b9ca27073 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/AccountIdTest.java @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.github.jsonSnapshot.SnapshotMatcher; +import java.util.concurrent.TimeoutException; +import org.bouncycastle.util.encoders.Hex; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +class AccountIdTest { + + static Client mainnetClient; + static Client testnetClient; + static Client previewnetClient; + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + mainnetClient = Client.forMainnet(); + testnetClient = Client.forTestnet(); + previewnetClient = Client.forPreviewnet(); + } + + @AfterAll + public static void afterAll() throws TimeoutException { + mainnetClient.close(); + testnetClient.close(); + previewnetClient.close(); + SnapshotMatcher.validateSnapshots(); + } + + @Test + void fromString() { + SnapshotMatcher.expect(AccountId.fromString("0.0.5005").toString()).toMatchSnapshot(); + } + + @Test + void fromStringWithChecksumOnMainnet() { + SnapshotMatcher.expect(AccountId.fromString("0.0.123-vfmkw").toStringWithChecksum(mainnetClient)) + .toMatchSnapshot(); + } + + @Test + void fromStringWithChecksumOnTestnet() { + SnapshotMatcher.expect(AccountId.fromString("0.0.123-esxsf").toStringWithChecksum(testnetClient)) + .toMatchSnapshot(); + } + + @Test + void fromStringWithChecksumOnPreviewnet() { + SnapshotMatcher.expect(AccountId.fromString("0.0.123-ogizo").toStringWithChecksum(previewnetClient)) + .toMatchSnapshot(); + } + + @Test + void goodChecksumOnMainnet() throws BadEntityIdException { + AccountId.fromString("0.0.123-vfmkw").validateChecksum(mainnetClient); + } + + @Test + void goodChecksumOnTestnet() throws BadEntityIdException { + AccountId.fromString("0.0.123-esxsf").validateChecksum(testnetClient); + } + + @Test + void goodChecksumOnPreviewnet() throws BadEntityIdException { + AccountId.fromString("0.0.123-ogizo").validateChecksum(previewnetClient); + } + + @Test + void badChecksumOnPreviewnet() { + assertThatExceptionOfType(BadEntityIdException.class).isThrownBy(() -> { + AccountId.fromString("0.0.123-ntjli").validateChecksum(previewnetClient); + }); + } + + @Test + void malformedIdString() { + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { + AccountId.fromString("0.0."); + }); + } + + @Test + void malformedIdChecksum() { + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { + AccountId.fromString("0.0.123-ntjl"); + }); + } + + @Test + void malformedIdChecksum2() { + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { + AccountId.fromString("0.0.123-ntjl1"); + }); + } + + @Test + void malformedAliasKey() { + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { + AccountId.fromString( + "0.0.302a300506032b6570032100114e6abc371b82dab5c15ea149f02d34a012087b163516dd70f44acafabf777"); + }); + } + + @Test + void malformedAliasKey2() { + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { + AccountId.fromString( + "0.0.302a300506032b6570032100114e6abc371b82dab5c15ea149f02d34a012087b163516dd70f44acafabf777g"); + }); + } + + @Test + void malformedAliasKey3() { + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { + AccountId.fromString( + "0.0.303a300506032b6570032100114e6abc371b82dab5c15ea149f02d34a012087b163516dd70f44acafabf7777"); + }); + } + + @Test + void fromStringWithAliasKey() { + SnapshotMatcher.expect(AccountId.fromString( + "0.0.302a300506032b6570032100114e6abc371b82dab5c15ea149f02d34a012087b163516dd70f44acafabf7777") + .toString()) + .toMatchSnapshot(); + } + + @Test + void fromStringWithEvmAddress() { + SnapshotMatcher.expect(AccountId.fromString("0.0.302a300506032b6570032100114e6abc371b82da") + .toString()) + .toMatchSnapshot(); + } + + @Test + void fromSolidityAddress() { + SnapshotMatcher.expect(AccountId.fromSolidityAddress("000000000000000000000000000000000000138D") + .toString()) + .toMatchSnapshot(); + } + + @Test + void fromSolidityAddressWith0x() { + SnapshotMatcher.expect(AccountId.fromSolidityAddress("0x000000000000000000000000000000000000138D") + .toString()) + .toMatchSnapshot(); + } + + @Test + void toBytes() throws InvalidProtocolBufferException { + SnapshotMatcher.expect(Hex.toHexString(new AccountId(5005).toProtobuf().toByteArray())) + .toMatchSnapshot(); + } + + @Test + void toBytesAlias() { + SnapshotMatcher.expect(Hex.toHexString(AccountId.fromString( + "0.0.302a300506032b6570032100114e6abc371b82dab5c15ea149f02d34a012087b163516dd70f44acafabf7777") + .toBytes())) + .toMatchSnapshot(); + } + + @Test + void toBytesEvmAddress() { + SnapshotMatcher.expect(Hex.toHexString(AccountId.fromString("0.0.302a300506032b6570032100114e6abc371b82da") + .toBytes())) + .toMatchSnapshot(); + } + + @Test + void fromBytes() throws InvalidProtocolBufferException { + SnapshotMatcher.expect( + AccountId.fromBytes(new AccountId(5005).toBytes()).toString()) + .toMatchSnapshot(); + } + + @Test + void toFromProtobuf() { + var id1 = new AccountId(5005); + var id2 = AccountId.fromProtobuf(id1.toProtobuf()); + assertThat(id2).isEqualTo(id1); + } + + @Test + void fromBytesAlias() throws InvalidProtocolBufferException { + SnapshotMatcher.expect(AccountId.fromBytes(AccountId.fromString( + "0.0.302a300506032b6570032100114e6abc371b82dab5c15ea149f02d34a012087b163516dd70f44acafabf7777") + .toBytes()) + .toString()) + .toMatchSnapshot(); + } + + @Test + void toFromProtobufAliasKey() { + var id1 = AccountId.fromString( + "0.0.302a300506032b6570032100114e6abc371b82dab5c15ea149f02d34a012087b163516dd70f44acafabf7777"); + var id2 = AccountId.fromProtobuf(id1.toProtobuf()); + assertThat(id2).isEqualTo(id1); + } + + @Test + void toFromProtobufEcdsaAliasKey() { + var id1 = AccountId.fromString( + "0.0.302d300706052b8104000a032200035d348292bbb8b511fdbe24e3217ec099944b4728999d337f9a025f4193324525"); + var id2 = AccountId.fromProtobuf(id1.toProtobuf()); + assertThat(id2).isEqualTo(id1); + } + + @Test + void fromBytesEvmAddress() throws InvalidProtocolBufferException { + SnapshotMatcher.expect(AccountId.fromBytes(AccountId.fromString("0.0.302a300506032b6570032100114e6abc371b82da") + .toBytes()) + .toString()) + .toMatchSnapshot(); + } + + @Test + void toFromProtobufEvmAddress() { + var id1 = AccountId.fromString("0.0.302a300506032b6570032100114e6abc371b82da"); + var id2 = AccountId.fromProtobuf(id1.toProtobuf()); + assertThat(id2).isEqualTo(id1); + } + + @Test + void toFromProtobufRawEvmAddress() { + var id1 = AccountId.fromString("302a300506032b6570032100114e6abc371b82da"); + var id2 = AccountId.fromProtobuf(id1.toProtobuf()); + assertThat(id2).isEqualTo(id1); + } + + @Test + void toSolidityAddress() { + SnapshotMatcher.expect(new AccountId(5005).toSolidityAddress()).toMatchSnapshot(); + } + + @Test + void fromEvmAddress() { + String evmAddress = "302a300506032b6570032100114e6abc371b82da"; + var id = AccountId.fromEvmAddress(evmAddress, 5, 9); + + assertThat(id.evmAddress).hasToString(evmAddress); + assertThat(id.shard).isEqualTo(5); + assertThat(id.realm).isEqualTo(9); + } + + @Test + void fromEvmAddressWithPrefix() { + String evmAddressString = "302a300506032b6570032100114e6abc371b82da"; + EvmAddress evmAddress = EvmAddress.fromString(evmAddressString); + var id1 = AccountId.fromEvmAddress(evmAddress); + var id2 = AccountId.fromEvmAddress("0x" + evmAddressString); + assertThat(id2).isEqualTo(id1); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/AccountIdTest.snap b/sdk/src/test/java/org/hiero/sdk/AccountIdTest.snap new file mode 100644 index 0000000000..c3ba18b097 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/AccountIdTest.snap @@ -0,0 +1,88 @@ +org.hiero.sdk.AccountIdTest.fromBytes=[ + "0.0.5005" +] + + +org.hiero.sdk.AccountIdTest.fromBytesAlias=[ + "0.0.302a300506032b6570032100114e6abc371b82dab5c15ea149f02d34a012087b163516dd70f44acafabf7777" +] + + +org.hiero.sdk.AccountIdTest.fromBytesAliasEvmAddress=[ + "0.0.302a300506032b6570032100114e6abc371b82da" +] + + +org.hiero.sdk.AccountIdTest.fromBytesEvmAddress=[ + "0.0.302a300506032b6570032100114e6abc371b82da" +] + + +org.hiero.sdk.AccountIdTest.fromSolidityAddress=[ + "0.0.5005" +] + + +org.hiero.sdk.AccountIdTest.fromSolidityAddressWith0x=[ + "0.0.5005" +] + + +org.hiero.sdk.AccountIdTest.fromString=[ + "0.0.5005" +] + + +org.hiero.sdk.AccountIdTest.fromStringWithAliasEvmAddress=[ + "0.0.302a300506032b6570032100114e6abc371b82da" +] + + +org.hiero.sdk.AccountIdTest.fromStringWithAliasKey=[ + "0.0.302a300506032b6570032100114e6abc371b82dab5c15ea149f02d34a012087b163516dd70f44acafabf7777" +] + + +org.hiero.sdk.AccountIdTest.fromStringWithChecksumOnMainnet=[ + "0.0.123-vfmkw" +] + + +org.hiero.sdk.AccountIdTest.fromStringWithChecksumOnPreviewnet=[ + "0.0.123-ogizo" +] + + +org.hiero.sdk.AccountIdTest.fromStringWithChecksumOnTestnet=[ + "0.0.123-esxsf" +] + + +org.hiero.sdk.AccountIdTest.fromStringWithEvmAddress=[ + "0.0.302a300506032b6570032100114e6abc371b82da" +] + + +org.hiero.sdk.AccountIdTest.toBytes=[ + "188d27" +] + + +org.hiero.sdk.AccountIdTest.toBytesAlias=[ + "22221220114e6abc371b82dab5c15ea149f02d34a012087b163516dd70f44acafabf7777" +] + + +org.hiero.sdk.AccountIdTest.toBytesAliasEvmAddress=[ + "2214302a300506032b6570032100114e6abc371b82da" +] + + +org.hiero.sdk.AccountIdTest.toBytesEvmAddress=[ + "2214302a300506032b6570032100114e6abc371b82da" +] + + +org.hiero.sdk.AccountIdTest.toSolidityAddress=[ + "000000000000000000000000000000000000138d" +] diff --git a/sdk/src/test/java/org/hiero/sdk/AccountInfoQueryTest.java b/sdk/src/test/java/org/hiero/sdk/AccountInfoQueryTest.java new file mode 100644 index 0000000000..678dbbf178 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/AccountInfoQueryTest.java @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.github.jsonSnapshot.SnapshotMatcher; +import org.hiero.sdk.proto.QueryHeader; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class AccountInfoQueryTest { + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + var builder = org.hiero.sdk.proto.Query.newBuilder(); + new AccountInfoQuery() + .setAccountId(AccountId.fromString("0.0.5005")) + .setMaxQueryPayment(Hbar.fromTinybars(100_000)) + .onMakeRequest(builder, QueryHeader.newBuilder().build()); + SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")) + .toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/AccountInfoQueryTest.snap b/sdk/src/test/java/org/hiero/sdk/AccountInfoQueryTest.snap new file mode 100644 index 0000000000..d837d51110 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/AccountInfoQueryTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.AccountInfoQueryTest.shouldSerialize=[ + "# org.hiero.sdk.proto.Query\ncrypto_get_info {\n account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n }\n header {\n }\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/AccountInfoTest.java b/sdk/src/test/java/org/hiero/sdk/AccountInfoTest.java new file mode 100644 index 0000000000..7bb520e541 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/AccountInfoTest.java @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Duration; +import java.time.Instant; +import org.hiero.sdk.proto.CryptoGetInfoResponse; +import org.hiero.sdk.proto.KeyList; +import org.hiero.sdk.proto.LiveHash; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class AccountInfoTest { + private static final PrivateKey privateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + private static final byte[] hash = {0, 1, 2}; + private static final LiveHash liveHash = LiveHash.newBuilder() + .setAccountId(new AccountId(10).toProtobuf()) + .setDuration(DurationConverter.toProtobuf(Duration.ofDays(11))) + .setHash(ByteString.copyFrom(hash)) + .setKeys(KeyList.newBuilder().addKeys(privateKey.getPublicKey().toProtobufKey())) + .build(); + private static final CryptoGetInfoResponse.AccountInfo info = CryptoGetInfoResponse.AccountInfo.newBuilder() + .setAccountID(new AccountId(1).toProtobuf()) + .setDeleted(true) + .setProxyReceived(2) + .setKey(privateKey.getPublicKey().toProtobufKey()) + .setBalance(3) + .setGenerateSendRecordThreshold(4) + .setGenerateReceiveRecordThreshold(5) + .setReceiverSigRequired(true) + .setExpirationTime(InstantConverter.toProtobuf(Instant.ofEpochMilli(6))) + .setAutoRenewPeriod(DurationConverter.toProtobuf(Duration.ofDays(7))) + .setProxyAccountID(new AccountId(8).toProtobuf()) + .addLiveHashes(liveHash) + .setLedgerId(LedgerId.PREVIEWNET.toByteString()) + .setEthereumNonce(1001) + .build(); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void fromProtobufWithOtherOptions() { + SnapshotMatcher.expect(AccountInfo.fromProtobuf(info).toString()).toMatchSnapshot(); + } + + @Test + void fromBytes() throws InvalidProtocolBufferException { + SnapshotMatcher.expect(AccountInfo.fromBytes(info.toByteArray()).toString()) + .toMatchSnapshot(); + } + + @Test + void toBytes() throws InvalidProtocolBufferException { + SnapshotMatcher.expect(AccountInfo.fromBytes(info.toByteArray()).toBytes()) + .toMatchSnapshot(); + } + + @Test + void toProtobuf() throws InvalidProtocolBufferException { + SnapshotMatcher.expect(AccountInfo.fromProtobuf(info).toProtobuf().toString()) + .toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/AccountInfoTest.snap b/sdk/src/test/java/org/hiero/sdk/AccountInfoTest.snap new file mode 100644 index 0000000000..d97ed2a401 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/AccountInfoTest.snap @@ -0,0 +1,18 @@ +org.hiero.sdk.AccountInfoTest.fromBytes=[ + "AccountInfo{accountId=0.0.1, contractAccountId=, deleted=true, proxyAccountId=0.0.8, proxyReceived=2 tℏ, key=302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7, balance=3 tℏ, sendRecordThreshold=4 tℏ, receiveRecordThreshold=5 tℏ, receiverSignatureRequired=true, expirationTime=1970-01-01T00:00:00.006Z, autoRenewPeriod=PT168H, liveHashes=[LiveHash{accountId=0.0.10, hash=[0, 1, 2], keys=KeyList{threshold=null, keys=[302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7]}, duration=PT264H}], tokenRelationships={}, accountMemo=, ownedNfts=0, maxAutomaticTokenAssociations=0, aliasKey=null, ledgerId=previewnet, ethereumNonce=1001, stakingInfo=null}" +] + + +org.hiero.sdk.AccountInfoTest.fromProtobufWithOtherOptions=[ + "AccountInfo{accountId=0.0.1, contractAccountId=, deleted=true, proxyAccountId=0.0.8, proxyReceived=2 tℏ, key=302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7, balance=3 tℏ, sendRecordThreshold=4 tℏ, receiveRecordThreshold=5 tℏ, receiverSignatureRequired=true, expirationTime=1970-01-01T00:00:00.006Z, autoRenewPeriod=PT168H, liveHashes=[LiveHash{accountId=0.0.10, hash=[0, 1, 2], keys=KeyList{threshold=null, keys=[302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7]}, duration=PT264H}], tokenRelationships={}, accountMemo=, ownedNfts=0, maxAutomaticTokenAssociations=0, aliasKey=null, ledgerId=previewnet, ethereumNonce=1001, stakingInfo=null}" +] + + +org.hiero.sdk.AccountInfoTest.toBytes=[ + "CgIYARgBIgIYCDACOiISIODI7CdYpYef+sImoTwMUWt5nnLjUUGg3YKPlNN5iKS3QANIBFAFWAFiBRCAm+4CagQIgPUkcjUKAhgKEgMAAQIaJAoiEiDgyOwnWKWHn/rCJqE8DFFreZ5y41FBoN2Cj5TTeYiktyoECICBOqIBAQKoAekH" +] + + +org.hiero.sdk.AccountInfoTest.toProtobuf=[ + "# org.hiero.sdk.proto.CryptoGetInfoResponse$AccountInfo@57c9441\naccount_i_d {\n account_num: 1\n realm_num: 0\n shard_num: 0\n}\nauto_renew_period {\n seconds: 604800\n}\nbalance: 3\ndeleted: true\nethereum_nonce: 1001\nexpiration_time {\n nanos: 6000000\n seconds: 0\n}\ngenerate_receive_record_threshold: 5\ngenerate_send_record_threshold: 4\nkey {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n}\nledger_id: \"\\002\"\nlive_hashes {\n account_id {\n account_num: 10\n realm_num: 0\n shard_num: 0\n }\n duration {\n seconds: 950400\n }\n hash: \"\\000\\001\\002\"\n keys {\n keys {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n }\n}\nowned_nfts: 0\nproxy_account_i_d {\n account_num: 8\n realm_num: 0\n shard_num: 0\n}\nproxy_received: 2\nreceiver_sig_required: true" +] diff --git a/sdk/src/test/java/org/hiero/sdk/AccountRecordsQueryTest.java b/sdk/src/test/java/org/hiero/sdk/AccountRecordsQueryTest.java new file mode 100644 index 0000000000..d28d3a72bb --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/AccountRecordsQueryTest.java @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.github.jsonSnapshot.SnapshotMatcher; +import org.hiero.sdk.proto.QueryHeader; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class AccountRecordsQueryTest { + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + var builder = org.hiero.sdk.proto.Query.newBuilder(); + new AccountRecordsQuery() + .setAccountId(AccountId.fromString("0.0.5005")) + .setMaxQueryPayment(Hbar.fromTinybars(100_000)) + .onMakeRequest(builder, QueryHeader.newBuilder().build()); + SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")) + .toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/AccountRecordsQueryTest.snap b/sdk/src/test/java/org/hiero/sdk/AccountRecordsQueryTest.snap new file mode 100644 index 0000000000..a67d776052 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/AccountRecordsQueryTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.AccountRecordsQueryTest.shouldSerialize=[ + "# org.hiero.sdk.proto.Query\ncrypto_get_account_records {\n account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n }\n header {\n }\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/AccountStakersQueryTest.java b/sdk/src/test/java/org/hiero/sdk/AccountStakersQueryTest.java new file mode 100644 index 0000000000..3ae5778a5c --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/AccountStakersQueryTest.java @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.github.jsonSnapshot.SnapshotMatcher; +import org.hiero.sdk.proto.QueryHeader; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class AccountStakersQueryTest { + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + var builder = org.hiero.sdk.proto.Query.newBuilder(); + new AccountStakersQuery() + .setAccountId(AccountId.fromString("0.0.5005")) + .setMaxQueryPayment(Hbar.fromTinybars(100_000)) + .onMakeRequest(builder, QueryHeader.newBuilder().build()); + SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")) + .toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/AccountStakersQueryTest.snap b/sdk/src/test/java/org/hiero/sdk/AccountStakersQueryTest.snap new file mode 100644 index 0000000000..2312920192 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/AccountStakersQueryTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.AccountStakersQueryTest.shouldSerialize=[ + "# org.hiero.sdk.proto.Query\ncrypto_get_proxy_stakers {\n account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n }\n header {\n }\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/AccountUpdateTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/AccountUpdateTransactionTest.java new file mode 100644 index 0000000000..a1fbbbd743 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/AccountUpdateTransactionTest.java @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Duration; +import java.time.Instant; +import java.util.Arrays; +import org.hiero.sdk.proto.CryptoUpdateTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class AccountUpdateTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + AccountUpdateTransaction spawnTestTransaction() { + return new AccountUpdateTransaction() + .setKey(unusedPrivateKey) + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setAccountId(AccountId.fromString("0.0.2002")) + .setProxyAccountId(AccountId.fromString("0.0.1001")) + .setAutoRenewPeriod(Duration.ofHours(10)) + .setExpirationTime(Instant.ofEpochSecond(1554158543)) + .setReceiverSignatureRequired(false) + .setMaxAutomaticTokenAssociations(100) + .setAccountMemo("Some memo") + .setMaxTransactionFee(Hbar.fromTinybars(100_000)) + .setStakedAccountId(AccountId.fromString("0.0.3")) + .freeze() + .sign(unusedPrivateKey); + } + + AccountUpdateTransaction spawnTestTransaction2() { + return new AccountUpdateTransaction() + .setKey(unusedPrivateKey) + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setAccountId(AccountId.fromString("0.0.2002")) + .setProxyAccountId(AccountId.fromString("0.0.1001")) + .setAutoRenewPeriod(Duration.ofHours(10)) + .setExpirationTime(Instant.ofEpochSecond(1554158543)) + .setReceiverSignatureRequired(false) + .setMaxAutomaticTokenAssociations(100) + .setAccountMemo("Some memo") + .setMaxTransactionFee(Hbar.fromTinybars(100_000)) + .setStakedNodeId(4L) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = AccountUpdateTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new AccountUpdateTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldSerialize2() { + SnapshotMatcher.expect(spawnTestTransaction2().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytes2() throws Exception { + var tx = spawnTestTransaction2(); + var tx2 = AccountUpdateTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setCryptoUpdateAccount(CryptoUpdateTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(AccountUpdateTransaction.class); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/AccountUpdateTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/AccountUpdateTransactionTest.snap new file mode 100644 index 0000000000..ec8cc1a5cd --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/AccountUpdateTransactionTest.snap @@ -0,0 +1,8 @@ +org.hiero.sdk.AccountUpdateTransactionTest.shouldSerialize2=[ + "# org.hiero.sdk.proto.TransactionBody\ncrypto_update_account {\n account_i_d_to_update {\n account_num: 2002\n realm_num: 0\n shard_num: 0\n }\n auto_renew_period {\n seconds: 36000\n }\n expiration_time {\n seconds: 1554158543\n }\n key {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n max_automatic_token_associations {\n value: 100\n }\n memo {\n value: \"Some memo\"\n }\n proxy_account_i_d {\n account_num: 1001\n realm_num: 0\n shard_num: 0\n }\n receiver_sig_required_wrapper {\n }\n staked_node_id: 4\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] + + +org.hiero.sdk.AccountUpdateTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\ncrypto_update_account {\n account_i_d_to_update {\n account_num: 2002\n realm_num: 0\n shard_num: 0\n }\n auto_renew_period {\n seconds: 36000\n }\n expiration_time {\n seconds: 1554158543\n }\n key {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n max_automatic_token_associations {\n value: 100\n }\n memo {\n value: \"Some memo\"\n }\n proxy_account_i_d {\n account_num: 1001\n realm_num: 0\n shard_num: 0\n }\n receiver_sig_required_wrapper {\n }\n staked_account_id {\n account_num: 3\n realm_num: 0\n shard_num: 0\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/AddressBookQueryMockTest.java b/sdk/src/test/java/org/hiero/sdk/AddressBookQueryMockTest.java new file mode 100644 index 0000000000..8f5990e267 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/AddressBookQueryMockTest.java @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatException; +import static org.hiero.sdk.BaseNodeAddress.PORT_NODE_PLAIN; + +import io.grpc.Server; +import io.grpc.Status; +import io.grpc.inprocess.InProcessServerBuilder; +import io.grpc.stub.StreamObserver; +import java.time.Duration; +import java.util.ArrayDeque; +import java.util.Collections; +import java.util.List; +import java.util.Queue; +import org.hiero.sdk.proto.mirror.NetworkServiceGrpc; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class AddressBookQueryMockTest { + + private Client client; + private final AddressBookQueryStub addressBookServiceStub = new AddressBookQueryStub(); + private Server server; + private AddressBookQuery addressBookQuery; + + @BeforeEach + void setup() throws Exception { + client = Client.forNetwork(Collections.emptyMap()); + client.setMirrorNetwork(List.of("in-process:test")); + server = InProcessServerBuilder.forName("test") + .addService(addressBookServiceStub) + .directExecutor() + .build() + .start(); + addressBookQuery = new AddressBookQuery(); + addressBookQuery.setFileId(FileId.ADDRESS_BOOK); + } + + @AfterEach + void teardown() throws Exception { + addressBookServiceStub.verify(); + if (client != null) { + client.close(); + } + if (server != null) { + server.shutdown(); + server.awaitTermination(); + } + } + + @ParameterizedTest(name = "[{0}] AddressBookQuery works") + @CsvSource({"sync", "async"}) + void addressBookQueryWorks(String executeVersion) throws Throwable { + addressBookServiceStub.requests.add(org.hiero.sdk.proto.mirror.AddressBookQuery.newBuilder() + .setFileId(FileId.ADDRESS_BOOK.toProtobuf()) + .setLimit(3) + .build()); + addressBookServiceStub.responses.add(new org.hiero.sdk.NodeAddress() + .setAccountId(AccountId.fromString("0.0.3")) + .toProtobuf()); + + addressBookQuery.setLimit(3); + + var nodes = executeVersion.equals("sync") + ? addressBookQuery.execute(client) + : addressBookQuery.executeAsync(client).get(); + assertThat(nodes.nodeAddresses).hasSize(1); + assertThat(nodes.nodeAddresses.get(0).accountId).isEqualTo(AccountId.fromString("0.0.3")); + } + + Endpoint spawnEndpoint() { + return new Endpoint() + .setAddress(new byte[] {0x00, 0x01, 0x02, 0x03}) + .setDomainName("unit.test.com") + .setPort(PORT_NODE_PLAIN); + } + + @Test + void networkUpdatePeriodWorks() throws Throwable { + addressBookServiceStub.requests.add(org.hiero.sdk.proto.mirror.AddressBookQuery.newBuilder() + .setFileId(FileId.ADDRESS_BOOK.toProtobuf()) + .build()); + addressBookServiceStub.responses.add(new org.hiero.sdk.NodeAddress() + .setAccountId(AccountId.fromString("0.0.3")) + .setAddresses(Collections.singletonList(spawnEndpoint())) + .toProtobuf()); + + client.setNetworkUpdatePeriod(Duration.ofSeconds(1)); + Thread.sleep(1400); + + var clientNetwork = client.getNetwork(); + assertThat(clientNetwork).hasSize(1); + assertThat(clientNetwork.values()).contains(AccountId.fromString("0.0.3")); + } + + @ParameterizedTest(name = "[{0}] Retry recovers w/ status {1} and description {2}") + @CsvSource({ + "sync, INTERNAL, internal RST_STREAM error", + "sync, INTERNAL, rst stream", + "sync, RESOURCE_EXHAUSTED, ", + "sync, UNAVAILABLE, ", + "async, INTERNAL, internal RST_STREAM error", + "async, INTERNAL, rst stream", + "async, RESOURCE_EXHAUSTED, ", + "async, UNAVAILABLE, " + }) + void addressBookQueryRetries(String executeVersion, Status.Code code, String description) throws Throwable { + addressBookServiceStub.requests.add(org.hiero.sdk.proto.mirror.AddressBookQuery.newBuilder() + .setFileId(FileId.ADDRESS_BOOK.toProtobuf()) + .build()); + addressBookServiceStub.requests.add(org.hiero.sdk.proto.mirror.AddressBookQuery.newBuilder() + .setFileId(FileId.ADDRESS_BOOK.toProtobuf()) + .build()); + addressBookServiceStub.responses.add( + code.toStatus().withDescription(description).asRuntimeException()); + addressBookServiceStub.responses.add(new org.hiero.sdk.NodeAddress() + .setAccountId(AccountId.fromString("0.0.3")) + .toProtobuf()); + + var nodes = executeVersion.equals("sync") + ? addressBookQuery.execute(client) + : addressBookQuery.executeAsync(client).get(); + assertThat(nodes.nodeAddresses).hasSize(1); + assertThat(nodes.nodeAddresses.get(0).accountId).isEqualTo(AccountId.fromString("0.0.3")); + } + + @ParameterizedTest(name = "No retry w/ status {0} and description {1}") + @CsvSource({ + "sync, INTERNAL, internal first_stream error", + "sync, INTERNAL, internal error", + "sync, INTERNAL, ", + "sync, INVALID_ARGUMENT, ", + "async, INTERNAL, internal first_stream error", + "async, INTERNAL, internal error", + "async, INTERNAL, ", + "async, INVALID_ARGUMENT, " + }) + void addressBookQueryFails(String executeVersion, Status.Code code, String description) { + addressBookServiceStub.requests.add(org.hiero.sdk.proto.mirror.AddressBookQuery.newBuilder() + .setFileId(FileId.ADDRESS_BOOK.toProtobuf()) + .build()); + addressBookServiceStub.responses.add( + code.toStatus().withDescription(description).asRuntimeException()); + + assertThatException().isThrownBy(() -> { + var result = executeVersion.equals("sync") + ? addressBookQuery.execute(client) + : addressBookQuery.executeAsync(client).get(); + }); + } + + @ParameterizedTest(name = "[{0}] address book query stops at max attempts w/ status {1} and description {2}") + @CsvSource({ + "sync, INTERNAL, internal RST_STREAM error", + "sync, INTERNAL, rst stream", + "sync, RESOURCE_EXHAUSTED, ", + "sync, UNAVAILABLE, ", + "async, INTERNAL, internal RST_STREAM error", + "async, INTERNAL, rst stream", + "async, RESOURCE_EXHAUSTED, ", + "async, UNAVAILABLE, " + }) + void addressBookQueryStopsAtMaxAttempts(String executeVersion, Status.Code code, String description) + throws Throwable { + addressBookQuery.setMaxAttempts(2); + + addressBookServiceStub.requests.add(org.hiero.sdk.proto.mirror.AddressBookQuery.newBuilder() + .setFileId(FileId.ADDRESS_BOOK.toProtobuf()) + .build()); + addressBookServiceStub.requests.add(org.hiero.sdk.proto.mirror.AddressBookQuery.newBuilder() + .setFileId(FileId.ADDRESS_BOOK.toProtobuf()) + .build()); + addressBookServiceStub.responses.add( + code.toStatus().withDescription(description).asRuntimeException()); + addressBookServiceStub.responses.add( + code.toStatus().withDescription(description).asRuntimeException()); + + assertThatException().isThrownBy(() -> { + var result = executeVersion.equals("sync") + ? addressBookQuery.execute(client) + : addressBookQuery.executeAsync(client).get(); + }); + } + + private static class AddressBookQueryStub extends NetworkServiceGrpc.NetworkServiceImplBase { + + private final Queue requests = new ArrayDeque<>(); + private final Queue responses = new ArrayDeque<>(); + + @Override + public void getNodes( + org.hiero.sdk.proto.mirror.AddressBookQuery addressBookQuery, + StreamObserver streamObserver) { + var request = requests.poll(); + assertThat(request).isNotNull(); + assertThat(addressBookQuery).isEqualTo(request); + + while (!responses.isEmpty()) { + var response = responses.poll(); + assertThat(response).isNotNull(); + + if (response instanceof Throwable) { + streamObserver.onError((Throwable) response); + return; + } + + streamObserver.onNext((org.hiero.sdk.proto.NodeAddress) response); + } + streamObserver.onCompleted(); + } + + public void verify() { + assertThat(requests).isEmpty(); + assertThat(responses).isEmpty(); + } + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/AllowancesTest.java b/sdk/src/test/java/org/hiero/sdk/AllowancesTest.java new file mode 100644 index 0000000000..73abc3812c --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/AllowancesTest.java @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.github.jsonSnapshot.SnapshotMatcher; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class AllowancesTest { + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + TokenAllowance spawnTokenAllowance() { + return new TokenAllowance( + TokenId.fromString("1.2.3"), AccountId.fromString("4.5.6"), AccountId.fromString("5.5.5"), 777); + } + + TokenNftAllowance spawnNftAllowance() { + List serials = new ArrayList<>(); + serials.add(123L); + serials.add(456L); + return new TokenNftAllowance( + TokenId.fromString("1.1.1"), + AccountId.fromString("2.2.2"), + AccountId.fromString("3.3.3"), + null, + serials, + null); + } + + TokenNftAllowance spawnAllNftAllowance() { + return new TokenNftAllowance( + TokenId.fromString("1.1.1"), + AccountId.fromString("2.2.2"), + AccountId.fromString("3.3.3"), + null, + Collections.emptyList(), + true); + } + + HbarAllowance spawnHbarAllowance() { + return new HbarAllowance(AccountId.fromString("1.1.1"), AccountId.fromString("2.2.2"), new Hbar(3)); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect( + spawnHbarAllowance().toString(), + spawnTokenAllowance().toString(), + spawnNftAllowance().toString(), + spawnAllNftAllowance().toString()) + .toMatchSnapshot(); + } + + @Test + void shouldBytes() throws InvalidProtocolBufferException { + var hbar1 = spawnHbarAllowance(); + var token1 = spawnTokenAllowance(); + var nft1 = spawnNftAllowance(); + var allNft1 = spawnAllNftAllowance(); + var hbar2 = HbarAllowance.fromBytes(hbar1.toBytes()); + var token2 = TokenAllowance.fromBytes(token1.toBytes()); + var nft2 = TokenNftAllowance.fromBytes(nft1.toBytes()); + var allNft2 = TokenNftAllowance.fromBytes(allNft1.toBytes()); + assertThat(hbar2.toString()).isEqualTo(hbar1.toString()); + assertThat(token2.toString()).isEqualTo(token1.toString()); + assertThat(nft2.toString()).isEqualTo(nft1.toString()); + assertThat(allNft2.toString()).isEqualTo(allNft1.toString()); + } +} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/AllowancesTest.snap b/sdk/src/test/java/org/hiero/sdk/AllowancesTest.snap similarity index 87% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/AllowancesTest.snap rename to sdk/src/test/java/org/hiero/sdk/AllowancesTest.snap index 6fb7137710..30d3c5f1be 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/AllowancesTest.snap +++ b/sdk/src/test/java/org/hiero/sdk/AllowancesTest.snap @@ -1,6 +1,6 @@ -com.hedera.hashgraph.sdk.AllowancesTest.shouldSerialize=[ +org.hiero.sdk.AllowancesTest.shouldSerialize=[ "HbarAllowance{ownerAccountId=1.1.1, spenderAccountId=2.2.2, amount=3 ℏ}", "TokenAllowance{tokenId=1.2.3, ownerAccountId=4.5.6, spenderAccountId=5.5.5, amount=777}", "TokenNftAllowance{tokenId=1.1.1, ownerAccountId=2.2.2, spenderAccountId=3.3.3, delegatingSpender=null, serials=[123, 456]}", "TokenNftAllowance{tokenId=1.1.1, ownerAccountId=2.2.2, spenderAccountId=3.3.3, delegatingSpender=null, allSerials=true}" -] \ No newline at end of file +] diff --git a/sdk/src/test/java/org/hiero/sdk/AssessedCustomFeeTest.java b/sdk/src/test/java/org/hiero/sdk/AssessedCustomFeeTest.java new file mode 100644 index 0000000000..924bb5930c --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/AssessedCustomFeeTest.java @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.util.List; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class AssessedCustomFeeTest { + + private static final int amount = 1; + private static final TokenId tokenId = new TokenId(2, 3, 4); + private static final AccountId feeCollector = new AccountId(5, 6, 7); + private static final List payerAccountIds = + List.of(new AccountId(8, 9, 10), new AccountId(11, 12, 13), new AccountId(14, 15, 16)); + + private final org.hiero.sdk.proto.AssessedCustomFee fee = org.hiero.sdk.proto.AssessedCustomFee.newBuilder() + .setAmount(amount) + .setTokenId(tokenId.toProtobuf()) + .setFeeCollectorAccountId(feeCollector.toProtobuf()) + .addAllEffectivePayerAccountId( + payerAccountIds.stream().map(AccountId::toProtobuf).toList()) + .build(); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + AssessedCustomFee spawnAssessedCustomFeeExample() { + return new AssessedCustomFee( + 201, + TokenId.fromString("1.2.3"), + AccountId.fromString("4.5.6"), + List.of(AccountId.fromString("0.0.1"), AccountId.fromString("0.0.2"), AccountId.fromString("0.0.3"))); + } + + @Test + void shouldSerialize() throws Exception { + var originalAssessedCustomFee = spawnAssessedCustomFeeExample(); + byte[] assessedCustomFeeBytes = originalAssessedCustomFee.toBytes(); + var copyAssessedCustomFee = AssessedCustomFee.fromBytes(assessedCustomFeeBytes); + assertThat(originalAssessedCustomFee.toString().replaceAll("@[A-Za-z0-9]+", "")) + .isEqualTo(copyAssessedCustomFee.toString().replaceAll("@[A-Za-z0-9]+", "")); + SnapshotMatcher.expect(originalAssessedCustomFee.toString().replaceAll("@[A-Za-z0-9]+", "")) + .toMatchSnapshot(); + } + + @Test + void fromProtobuf() { + SnapshotMatcher.expect(AssessedCustomFee.fromProtobuf(fee).toString()).toMatchSnapshot(); + } + + @Test + void toProtobuf() { + SnapshotMatcher.expect(AssessedCustomFee.fromProtobuf(fee).toProtobuf().toString()) + .toMatchSnapshot(); + } + + @Test + void shouldBytes() throws Exception { + var assessedCustomFee = spawnAssessedCustomFeeExample(); + var tx2 = AssessedCustomFee.fromBytes(assessedCustomFee.toBytes()); + assertThat(tx2).hasToString(assessedCustomFee.toString()); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/AssessedCustomFeeTest.snap b/sdk/src/test/java/org/hiero/sdk/AssessedCustomFeeTest.snap new file mode 100644 index 0000000000..2dca2e49e9 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/AssessedCustomFeeTest.snap @@ -0,0 +1,13 @@ +org.hiero.sdk.AssessedCustomFeeTest.fromProtobuf=[ + "AssessedCustomFee{amount=1, tokenId=2.3.4, feeCollectorAccountId=5.6.7, payerAccountIdList=[8.9.10, 11.12.13, 14.15.16]}" +] + + +org.hiero.sdk.AssessedCustomFeeTest.shouldSerialize=[ + "AssessedCustomFee{amount=201, tokenId=1.2.3, feeCollectorAccountId=4.5.6, payerAccountIdList=[0.0.1, 0.0.2, 0.0.3]}" +] + + +org.hiero.sdk.AssessedCustomFeeTest.toProtobuf=[ + "# org.hiero.sdk.proto.AssessedCustomFee@55ba505b\namount: 1\neffective_payer_account_id {\n account_num: 10\n realm_num: 9\n shard_num: 8\n}\neffective_payer_account_id {\n account_num: 13\n realm_num: 12\n shard_num: 11\n}\neffective_payer_account_id {\n account_num: 16\n realm_num: 15\n shard_num: 14\n}\nfee_collector_account_id {\n account_num: 7\n realm_num: 6\n shard_num: 5\n}\ntoken_id {\n realm_num: 3\n shard_num: 2\n token_num: 4\n}" +] diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/BaseNodeAddressTest.java b/sdk/src/test/java/org/hiero/sdk/BaseNodeAddressTest.java similarity index 77% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/BaseNodeAddressTest.java rename to sdk/src/test/java/org/hiero/sdk/BaseNodeAddressTest.java index 576e5eb630..bdeeb4ae0a 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/BaseNodeAddressTest.java +++ b/sdk/src/test/java/org/hiero/sdk/BaseNodeAddressTest.java @@ -1,31 +1,13 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; -import org.junit.jupiter.api.Test; - -import static com.hedera.hashgraph.sdk.BaseNodeAddress.PORT_MIRROR_TLS; -import static com.hedera.hashgraph.sdk.BaseNodeAddress.PORT_NODE_PLAIN; -import static com.hedera.hashgraph.sdk.BaseNodeAddress.PORT_NODE_TLS; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.hiero.sdk.BaseNodeAddress.PORT_MIRROR_TLS; +import static org.hiero.sdk.BaseNodeAddress.PORT_NODE_PLAIN; +import static org.hiero.sdk.BaseNodeAddress.PORT_NODE_TLS; + +import org.junit.jupiter.api.Test; public class BaseNodeAddressTest { @Test @@ -96,7 +78,9 @@ void fromString() { assertThat(mirrorNodeAddressSecure.getPort()).isEqualTo(PORT_MIRROR_TLS); assertThat(mirrorNodeAddressSecure).hasToString("mainnet-public.mirrornode.hedera.com:443"); - assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> BaseNodeAddress.fromString("this is a random string with spaces:443")); - assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> BaseNodeAddress.fromString("mainnet-public.mirrornode.hedera.com:notarealport")); + assertThatExceptionOfType(IllegalStateException.class) + .isThrownBy(() -> BaseNodeAddress.fromString("this is a random string with spaces:443")); + assertThatExceptionOfType(IllegalStateException.class) + .isThrownBy(() -> BaseNodeAddress.fromString("mainnet-public.mirrornode.hedera.com:notarealport")); } } diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ClientCloseTest.java b/sdk/src/test/java/org/hiero/sdk/ClientCloseTest.java similarity index 85% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/ClientCloseTest.java rename to sdk/src/test/java/org/hiero/sdk/ClientCloseTest.java index 1751fe65f7..71392f87dc 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ClientCloseTest.java +++ b/sdk/src/test/java/org/hiero/sdk/ClientCloseTest.java @@ -1,4 +1,5 @@ -package com.hedera.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -17,6 +18,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; public class ClientCloseTest { @@ -45,12 +47,14 @@ void doesNotCloseExternalExecutor() throws TimeoutException { @Test void closeHandlesNetworkTimeout() { var executor = Client.createExecutor(); - var network = mock(Network.class); + var network = Mockito.mock(Network.class); when(network.awaitClose(any(), any())).thenReturn(new TimeoutException("network timeout")); var mirrorNetwork = MirrorNetwork.forNetwork(executor, Collections.emptyList()); var client = new Client(executor, network, mirrorNetwork, null, true, null); - assertThatExceptionOfType(TimeoutException.class).isThrownBy(client::close).withMessage("network timeout"); + assertThatExceptionOfType(TimeoutException.class) + .isThrownBy(client::close) + .withMessage("network timeout"); assertThat(mirrorNetwork.hasShutDownNow).isTrue(); } @@ -58,12 +62,14 @@ void closeHandlesNetworkTimeout() { void closeHandlesNetworkInterrupted() { var interruptedException = new InterruptedException("network interrupted"); var executor = Client.createExecutor(); - var network = mock(Network.class); + var network = Mockito.mock(Network.class); when(network.awaitClose(any(), any())).thenReturn(interruptedException); var mirrorNetwork = MirrorNetwork.forNetwork(executor, Collections.emptyList()); var client = new Client(executor, network, mirrorNetwork, null, true, null); - assertThatExceptionOfType(RuntimeException.class).isThrownBy(client::close).withCause(interruptedException); + assertThatExceptionOfType(RuntimeException.class) + .isThrownBy(client::close) + .withCause(interruptedException); assertThat(mirrorNetwork.hasShutDownNow).isTrue(); } @@ -71,11 +77,13 @@ void closeHandlesNetworkInterrupted() { void closeHandlesMirrorNetworkTimeout() { var executor = Client.createExecutor(); var network = Network.forNetwork(executor, Collections.emptyMap()); - var mirrorNetwork = mock(MirrorNetwork.class); + var mirrorNetwork = Mockito.mock(MirrorNetwork.class); when(mirrorNetwork.awaitClose(any(), any())).thenReturn(new TimeoutException("mirror timeout")); var client = new Client(executor, network, mirrorNetwork, null, true, null); - assertThatExceptionOfType(TimeoutException.class).isThrownBy(client::close).withMessage("mirror timeout"); + assertThatExceptionOfType(TimeoutException.class) + .isThrownBy(client::close) + .withMessage("mirror timeout"); assertThat(network.hasShutDownNow).isFalse(); } @@ -84,11 +92,13 @@ void closeHandlesMirrorNetworkInterrupted() { var interruptedException = new InterruptedException("network interrupted"); var executor = Client.createExecutor(); var network = Network.forNetwork(executor, Collections.emptyMap()); - var mirrorNetwork = mock(MirrorNetwork.class); + var mirrorNetwork = Mockito.mock(MirrorNetwork.class); when(mirrorNetwork.awaitClose(any(), any())).thenReturn(interruptedException); var client = new Client(executor, network, mirrorNetwork, null, true, null); - assertThatExceptionOfType(RuntimeException.class).isThrownBy(client::close).withCause(interruptedException); + assertThatExceptionOfType(RuntimeException.class) + .isThrownBy(client::close) + .withCause(interruptedException); assertThat(network.hasShutDownNow).isFalse(); } @@ -151,7 +161,7 @@ void noHealthyNodesNetwork() { var network = Network.forNetwork(executor, Collections.emptyMap()); assertThatExceptionOfType(IllegalStateException.class) - .isThrownBy(network::getRandomNode) - .withMessage("No healthy node was found"); + .isThrownBy(network::getRandomNode) + .withMessage("No healthy node was found"); } } diff --git a/sdk/src/test/java/org/hiero/sdk/ClientTest.java b/sdk/src/test/java/org/hiero/sdk/ClientTest.java new file mode 100644 index 0000000000..df90b4dc7b --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ClientTest.java @@ -0,0 +1,438 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.hiero.sdk.BaseNodeAddress.PORT_NODE_PLAIN; + +import com.google.protobuf.ByteString; +import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.time.Instant; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Function; +import javax.annotation.Nullable; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; + +class ClientTest { + + @Test + @DisplayName("Can construct mainnet client") + void forMainnet() throws TimeoutException { + Client.forMainnet().close(); + } + + @Test + @DisplayName("Can construct mainnet client with executor") + void forMainnetWithExecutor() throws TimeoutException { + var executor = new ThreadPoolExecutor( + 2, + 2, + 0L, + TimeUnit.MILLISECONDS, + new LinkedBlockingQueue<>(), + new ThreadPoolExecutor.CallerRunsPolicy()); + + Client.forMainnet(executor).close(); + } + + @Test + @DisplayName("Can construct testnet client with executor") + void forTestnetWithExecutor() throws TimeoutException { + var executor = new ThreadPoolExecutor( + 2, + 2, + 0L, + TimeUnit.MILLISECONDS, + new LinkedBlockingQueue<>(), + new ThreadPoolExecutor.CallerRunsPolicy()); + + Client.forTestnet(executor).close(); + } + + @Test + @DisplayName("Can construct previewnet client with executor") + void forPreviewnetWithWithExecutor() throws TimeoutException { + var executor = new ThreadPoolExecutor( + 2, + 2, + 0L, + TimeUnit.MILLISECONDS, + new LinkedBlockingQueue<>(), + new ThreadPoolExecutor.CallerRunsPolicy()); + + Client.forPreviewnet(executor).close(); + } + + @Test + @DisplayName("Client.setMaxQueryPayment() negative") + void setMaxQueryPaymentNegative() throws TimeoutException { + var client = Client.forTestnet(); + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { + client.setMaxQueryPayment(Hbar.MIN); + }); + client.close(); + } + + @ValueSource(ints = {-1, 0}) + @ParameterizedTest(name = "Invalid maxAttempts {0}") + void setMaxAttempts(int maxAttempts) throws TimeoutException { + var client = Client.forNetwork(Map.of()); + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { + client.setMaxAttempts(maxAttempts); + }); + client.close(); + } + + @NullSource + @ValueSource(longs = {-1, 0, 249}) + @ParameterizedTest(name = "Invalid maxBackoff {0}") + @SuppressWarnings("NullAway") + void setMaxBackoffInvalid(@Nullable Long maxBackoffMillis) throws TimeoutException { + @Nullable Duration maxBackoff = maxBackoffMillis != null ? Duration.ofMillis(maxBackoffMillis) : null; + var client = Client.forNetwork(Map.of()); + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { + client.setMaxBackoff(maxBackoff); + }); + client.close(); + } + + @ValueSource(longs = {250, 8000}) + @ParameterizedTest(name = "Valid maxBackoff {0}") + void setMaxBackoffValid(long maxBackoff) throws TimeoutException { + Client.forNetwork(Map.of()).setMaxBackoff(Duration.ofMillis(maxBackoff)).close(); + } + + @NullSource + @ValueSource(longs = {-1, 8001}) + @ParameterizedTest(name = "Invalid minBackoff {0}") + @SuppressWarnings("NullAway") + void setMinBackoffInvalid(@Nullable Long minBackoffMillis) throws TimeoutException { + @Nullable Duration minBackoff = minBackoffMillis != null ? Duration.ofMillis(minBackoffMillis) : null; + var client = Client.forNetwork(Map.of()); + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { + client.setMinBackoff(minBackoff); + }); + client.close(); + } + + @ValueSource(longs = {0, 250, 8000}) + @ParameterizedTest(name = "Valid minBackoff {0}") + void setMinBackoffValid(long minBackoff) throws TimeoutException { + Client.forNetwork(Map.of()).setMinBackoff(Duration.ofMillis(minBackoff)).close(); + } + + @Test + @DisplayName("Client.setMaxTransactionFee() negative") + void setMaxTransactionFeeNegative() throws TimeoutException { + var client = Client.forTestnet(); + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { + client.setDefaultMaxTransactionFee(Hbar.MIN); + }); + client.close(); + } + + @Test + @DisplayName("fromJsonFile() functions correctly") + void fromJsonFile() throws Exception { + Client.fromConfigFile(new File("./src/test/resources/client-config.json")) + .close(); + Client.fromConfigFile(new File("./src/test/resources/client-config-with-operator.json")) + .close(); + Client.fromConfigFile("./src/test/resources/client-config.json").close(); + Client.fromConfigFile("./src/test/resources/client-config-with-operator.json") + .close(); + } + + @Test + @DisplayName("fromJson() functions correctly") + void testFromJson() throws Exception { + // Copied content of `client-config-with-operator.json` + var client = Client.fromConfig("{\n" + " \"network\":\"mainnet\",\n" + + " \"operator\": {\n" + + " \"accountId\": \"0.0.36\",\n" + + " \"privateKey\": \"302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10\"\n" + + " }\n" + + "}\n"); + + // put it in a file for nicer formatting + InputStream clientConfig = ClientTest.class.getClassLoader().getResourceAsStream("client-config.json"); + + assertThat(clientConfig).isNotNull(); + + Client.fromConfig(new InputStreamReader(clientConfig, StandardCharsets.UTF_8)) + .close(); + + // put it in a file for nicer formatting + InputStream clientConfigWithOperator = + ClientTest.class.getClassLoader().getResourceAsStream("client-config-with-operator.json"); + + assertThat(clientConfigWithOperator).isNotNull(); + + client.close(); + } + + @Test + @DisplayName("setNetwork() functions correctly") + void setNetworkWorks() throws Exception { + var defaultNetwork = Map.of( + "0.testnet.hedera.com:50211", new AccountId(3), + "1.testnet.hedera.com:50211", new AccountId(4)); + + Client client = Client.forNetwork(defaultNetwork); + assertThat(client.getNetwork()).containsExactlyInAnyOrderEntriesOf(defaultNetwork); + + client.setNetwork(defaultNetwork); + assertThat(client.getNetwork()).containsExactlyInAnyOrderEntriesOf(defaultNetwork); + + var defaultNetworkWithExtraNode = Map.of( + "0.testnet.hedera.com:50211", new AccountId(3), + "1.testnet.hedera.com:50211", new AccountId(4), + "2.testnet.hedera.com:50211", new AccountId(5)); + + client.setNetwork(defaultNetworkWithExtraNode); + assertThat(client.getNetwork()).containsExactlyInAnyOrderEntriesOf(defaultNetworkWithExtraNode); + + var singleNodeNetwork = Map.of("2.testnet.hedera.com:50211", new AccountId(5)); + + client.setNetwork(singleNodeNetwork); + assertThat(client.getNetwork()).containsExactlyInAnyOrderEntriesOf(singleNodeNetwork); + + var singleNodeNetworkWithDifferentAccountId = Map.of("2.testnet.hedera.com:50211", new AccountId(6)); + + client.setNetwork(singleNodeNetworkWithDifferentAccountId); + assertThat(client.getNetwork()).containsExactlyInAnyOrderEntriesOf(singleNodeNetworkWithDifferentAccountId); + + var multiAddressNetwork = Map.of( + "0.testnet.hedera.com:50211", new AccountId(3), + "34.94.106.61:50211", new AccountId(3), + "50.18.132.211:50211", new AccountId(3), + "138.91.142.219:50211", new AccountId(3), + "1.testnet.hedera.com:50211", new AccountId(4), + "35.237.119.55:50211", new AccountId(4), + "3.212.6.13:50211", new AccountId(4), + "52.168.76.241:50211", new AccountId(4)); + + client.setNetwork(multiAddressNetwork); + assertThat(client.getNetwork()).containsExactlyInAnyOrderEntriesOf(multiAddressNetwork); + + client.close(); + } + + @Test + @DisplayName("setMirrorNetwork() functions correctly") + void setMirrorNetworkWorks() throws Exception { + var defaultNetwork = List.of("testnet.mirrornode.hedera.com:443"); + + Client client = Client.forNetwork(new HashMap<>()).setMirrorNetwork(defaultNetwork); + assertThat(client.getMirrorNetwork()).containsExactlyInAnyOrderElementsOf(defaultNetwork); + + client.setMirrorNetwork(defaultNetwork); + assertThat(client.getMirrorNetwork()).containsExactlyInAnyOrderElementsOf(defaultNetwork); + + var defaultNetworkWithExtraNode = + List.of("testnet.mirrornode.hedera.com:443", "testnet1.mirrornode.hedera.com:443"); + + client.setMirrorNetwork(defaultNetworkWithExtraNode); + assertThat(client.getMirrorNetwork()).containsExactlyInAnyOrderElementsOf(defaultNetworkWithExtraNode); + + var singleNodeNetwork = List.of("testnet1.mirrornode.hedera.com:443"); + + client.setMirrorNetwork(singleNodeNetwork); + assertThat(client.getMirrorNetwork()).containsExactlyInAnyOrderElementsOf(singleNodeNetwork); + + var singleNodeNetworkWithDifferentNode = List.of("testnet.mirrornode.hedera.com:443"); + + client.setMirrorNetwork(singleNodeNetworkWithDifferentNode); + assertThat(client.getMirrorNetwork()).containsExactlyInAnyOrderElementsOf(singleNodeNetworkWithDifferentNode); + + client.close(); + } + + @Test + @DisplayName("setMirrorNetwork() throws exception if there is no time to remove the old nodes") + void setMirrorNetworkFails() throws Exception { + var defaultNetwork = List.of("testnet.mirrornode.hedera.com:443", "testnet.mirrornode2.hedera.com:443"); + + Client client = Client.forNetwork(new HashMap<>()).setMirrorNetwork(defaultNetwork); + assertThat(client.getMirrorNetwork()).containsExactlyInAnyOrderElementsOf(defaultNetwork); + + client.setCloseTimeout(Duration.ZERO); + final List updatedNetwork = List.of("testnet.mirrornode.hedera.com:443"); + + assertThatThrownBy(() -> client.setMirrorNetwork(updatedNetwork)) + .hasMessageEndingWith("Failed to properly shutdown all channels"); + } + + @Test + @DisplayName("forName() sets the correct network") + void forNameReturnsCorrectNetwork() { + Client mainnetClient = Client.forName("mainnet"); + assertThat(mainnetClient.getLedgerId()).isEqualTo(LedgerId.MAINNET); + + Client testnetClient = Client.forName("testnet"); + assertThat(testnetClient.getLedgerId()).isEqualTo(LedgerId.TESTNET); + + Client previewnetClient = Client.forName("previewnet"); + assertThat(previewnetClient.getLedgerId()).isEqualTo(LedgerId.PREVIEWNET); + + assertThatThrownBy(() -> Client.forName("unknown")) + .hasMessageEndingWith("Name must be one-of `mainnet`, `testnet`, or `previewnet`"); + } + + @ParameterizedTest + @CsvSource({"onClient", "onQuery"}) + void testExecuteAsyncTimeout(String timeoutSite) throws Exception { + AccountId accountId = AccountId.fromString("0.0.1"); + Duration timeout = Duration.ofSeconds(5); + + Client client = Client.forNetwork(Map.of("1.1.1.1:50211", accountId)) + .setNodeMinBackoff(Duration.ofMillis(0)) + .setNodeMaxBackoff(Duration.ofMillis(0)) + .setMinNodeReadmitTime(Duration.ofMillis(0)) + .setMaxNodeReadmitTime(Duration.ofMillis(0)); + AccountBalanceQuery query = + new AccountBalanceQuery().setAccountId(accountId).setMaxAttempts(3); + Instant start = Instant.now(); + + try { + if (timeoutSite.equals("onClient")) { + client.setRequestTimeout(timeout); + query.executeAsync(client).get(); + } else { + query.executeAsync(client, timeout).get(); + } + } catch (ExecutionException e) { + // fine... + } + long secondsTaken = java.time.Duration.between(start, Instant.now()).toSeconds(); + + // 20 seconds would indicate we tried 2 times to connect + assertThat(secondsTaken).isLessThan(7); + + client.close(); + } + + @ParameterizedTest + @CsvSource({"onClient", "onQuery"}) + void testExecuteSyncTimeout(String timeoutSite) throws Exception { + AccountId accountId = AccountId.fromString("0.0.1"); + // Executing requests in sync mode will require at most 10 seconds to connect + // to a gRPC node. If we're not able to connect to a gRPC node within 10 seconds + // we fail that request attempt. This means setting at timeout on a request + // which hits non-connecting gRPC nodes will fail within ~10s of the set timeout + // e.g. setting a timeout of 15 seconds, the request could fail within the range + // of [5 seconds, 25 seconds]. The 10 second timeout for connecting to gRPC nodes + // is not configurable. + Duration timeout = Duration.ofSeconds(5); + + Client client = Client.forNetwork(Map.of("1.1.1.1:50211", accountId)) + .setNodeMinBackoff(Duration.ofMillis(0)) + .setNodeMaxBackoff(Duration.ofMillis(0)) + .setMinNodeReadmitTime(Duration.ofMillis(0)) + .setMaxNodeReadmitTime(Duration.ofMillis(0)); + + AccountBalanceQuery query = new AccountBalanceQuery() + .setAccountId(accountId) + .setMaxAttempts(3) + .setGrpcDeadline(Duration.ofSeconds(5)); + Instant start = Instant.now(); + + try { + if (timeoutSite.equals("onClient")) { + client.setRequestTimeout(timeout); + query.execute(client); + } else { + query.execute(client, timeout); + } + } catch (TimeoutException e) { + // fine... + } + long secondsTaken = java.time.Duration.between(start, Instant.now()).toSeconds(); + + // 20 seconds would indicate we tried 2 times to connect + assertThat(secondsTaken).isLessThan(15); + + client.close(); + } + + org.hiero.sdk.proto.NodeAddress nodeAddress(long accountNum, String rsaPubKeyHex, byte[] certHash, byte[] ipv4) { + org.hiero.sdk.proto.NodeAddress.Builder builder = org.hiero.sdk.proto.NodeAddress.newBuilder() + .setNodeAccountId(org.hiero.sdk.proto.AccountID.newBuilder() + .setAccountNum(accountNum) + .build()) + .addServiceEndpoint(org.hiero.sdk.proto.ServiceEndpoint.newBuilder() + .setIpAddressV4(ByteString.copyFrom(ipv4)) + .setPort(PORT_NODE_PLAIN) + .build()) + .setRSAPubKey(rsaPubKeyHex); + if (certHash != null) { + builder.setNodeCertHash(ByteString.copyFrom(certHash)); + } + return builder.build(); + } + + @Test + @DisplayName("setNetworkFromAddressBook() updates security parameters in the client") + void setNetworkFromAddressBook() throws Exception { + try (Client client = Client.forNetwork(Map.of())) { + Function nodeAddress = accountNum -> + client.network.network.get(new AccountId(accountNum)).get(0).getAddressBookEntry(); + + // reconfigure client network from addressbook (add new nodes) + client.setNetworkFromAddressBook(NodeAddressBook.fromBytes(org.hiero.sdk.proto.NodeAddressBook.newBuilder() + .addNodeAddress(nodeAddress(10001, "10001", new byte[] {1, 0, 1}, new byte[] {10, 0, 0, 1})) + .addNodeAddress(nodeAddress(10002, "10002", new byte[] {1, 0, 2}, new byte[] {10, 0, 0, 2})) + .build() + .toByteString())); + + // verify security parameters in client + assertThat(nodeAddress.apply(10001).certHash).isEqualTo(ByteString.copyFrom(new byte[] {1, 0, 1})); + assertThat(nodeAddress.apply(10001).publicKey).isEqualTo("10001"); + assertThat(nodeAddress.apply(10002).certHash).isEqualTo(ByteString.copyFrom(new byte[] {1, 0, 2})); + assertThat(nodeAddress.apply(10002).publicKey).isEqualTo("10002"); + + // reconfigure client network from addressbook without `certHash` + client.setNetworkFromAddressBook(NodeAddressBook.fromBytes(org.hiero.sdk.proto.NodeAddressBook.newBuilder() + .addNodeAddress(nodeAddress(10001, "10001", null, new byte[] {10, 0, 0, 1})) + .addNodeAddress(nodeAddress(10002, "10002", null, new byte[] {10, 0, 0, 2})) + .build() + .toByteString())); + + // verify security parameters in client (unchanged) + assertThat(nodeAddress.apply(10001).certHash).isEqualTo(ByteString.copyFrom(new byte[] {1, 0, 1})); + assertThat(nodeAddress.apply(10001).publicKey).isEqualTo("10001"); + assertThat(nodeAddress.apply(10002).certHash).isEqualTo(ByteString.copyFrom(new byte[] {1, 0, 2})); + assertThat(nodeAddress.apply(10002).publicKey).isEqualTo("10002"); + + // reconfigure client network from addressbook (update existing nodes) + client.setNetworkFromAddressBook(NodeAddressBook.fromBytes(org.hiero.sdk.proto.NodeAddressBook.newBuilder() + .addNodeAddress(nodeAddress(10001, "810001", new byte[] {8, 1, 0, 1}, new byte[] {10, 0, 0, 1})) + .addNodeAddress(nodeAddress(10002, "810002", new byte[] {8, 1, 0, 2}, new byte[] {10, 0, 0, 2})) + .build() + .toByteString())); + + // verify security parameters in client + assertThat(nodeAddress.apply(10001).certHash).isEqualTo(ByteString.copyFrom(new byte[] {8, 1, 0, 1})); + assertThat(nodeAddress.apply(10001).publicKey).isEqualTo("810001"); + assertThat(nodeAddress.apply(10002).certHash).isEqualTo(ByteString.copyFrom(new byte[] {8, 1, 0, 2})); + assertThat(nodeAddress.apply(10002).publicKey).isEqualTo("810002"); + } + } +} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ConsumerHelperTest.java b/sdk/src/test/java/org/hiero/sdk/ConsumerHelperTest.java similarity index 96% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/ConsumerHelperTest.java rename to sdk/src/test/java/org/hiero/sdk/ConsumerHelperTest.java index aa279445b5..4d15d55aab 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ConsumerHelperTest.java +++ b/sdk/src/test/java/org/hiero/sdk/ConsumerHelperTest.java @@ -1,10 +1,5 @@ -package com.hedera.hashgraph.sdk; - -import org.junit.jupiter.api.Test; - -import java.util.concurrent.CompletableFuture; -import java.util.function.BiConsumer; -import java.util.function.Consumer; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.any; @@ -12,6 +7,11 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import java.util.concurrent.CompletableFuture; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import org.junit.jupiter.api.Test; + class ConsumerHelperTest { @Test void biConsumer() { @@ -36,6 +36,7 @@ void twoConsumersWithoutError() { verify(onSuccess, times(1)).accept(value); verify(onFailure, times(0)).accept(any()); } + @Test void twoConsumersWithError() { var exception = new RuntimeException("Exception"); diff --git a/sdk/src/test/java/org/hiero/sdk/ContractByteCodeQueryTest.java b/sdk/src/test/java/org/hiero/sdk/ContractByteCodeQueryTest.java new file mode 100644 index 0000000000..50197a16f3 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ContractByteCodeQueryTest.java @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.github.jsonSnapshot.SnapshotMatcher; +import org.hiero.sdk.proto.QueryHeader; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class ContractByteCodeQueryTest { + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + var builder = org.hiero.sdk.proto.Query.newBuilder(); + new ContractByteCodeQuery() + .setContractId(ContractId.fromString("0.0.5005")) + .onMakeRequest(builder, QueryHeader.newBuilder().build()); + SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")) + .toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/ContractByteCodeQueryTest.snap b/sdk/src/test/java/org/hiero/sdk/ContractByteCodeQueryTest.snap new file mode 100644 index 0000000000..ec8e3fa0b1 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ContractByteCodeQueryTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.ContractByteCodeQueryTest.shouldSerialize=[ + "# org.hiero.sdk.proto.Query\ncontract_get_bytecode {\n contract_i_d {\n contract_num: 5005\n realm_num: 0\n shard_num: 0\n }\n header {\n }\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/ContractCallQueryTest.java b/sdk/src/test/java/org/hiero/sdk/ContractCallQueryTest.java new file mode 100644 index 0000000000..e574d7055c --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ContractCallQueryTest.java @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import org.hiero.sdk.proto.QueryHeader; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class ContractCallQueryTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + var builder = org.hiero.sdk.proto.Query.newBuilder(); + new ContractCallQuery() + .setContractId(ContractId.fromString("0.0.5005")) + .setGas(1541) + .setSenderAccountId(AccountId.fromString("1.2.3")) + .setFunction( + "foo", + new ContractFunctionParameters().addString("Hello").addString("world!")) + .onMakeRequest(builder, QueryHeader.newBuilder().build()); + SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")) + .toMatchSnapshot(); + } + + @Test + void setFunctionParameters() { + var builder = org.hiero.sdk.proto.Query.newBuilder(); + new ContractCallQuery() + .setContractId(ContractId.fromString("0.0.5005")) + .setGas(1541) + .setSenderAccountId(AccountId.fromString("1.2.3")) + .setFunctionParameters(new ContractFunctionParameters() + .addString("Hello") + .addString("world!") + .toBytes(null) + .toByteArray()) + .onMakeRequest(builder, QueryHeader.newBuilder().build()); + SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")) + .toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/ContractCallQueryTest.snap b/sdk/src/test/java/org/hiero/sdk/ContractCallQueryTest.snap new file mode 100644 index 0000000000..629cd29c06 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ContractCallQueryTest.snap @@ -0,0 +1,8 @@ +org.hiero.sdk.ContractCallQueryTest.setFunctionParameters=[ + "# org.hiero.sdk.proto.Query\ncontract_call_local {\n contract_i_d {\n contract_num: 5005\n realm_num: 0\n shard_num: 0\n }\n function_parameters: \"\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000@\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\200\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\005Hello\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\006world!\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\"\n gas: 1541\n header {\n }\n max_result_size: 0\n sender_id {\n account_num: 3\n realm_num: 2\n shard_num: 1\n }\n}" +] + + +org.hiero.sdk.ContractCallQueryTest.shouldSerialize=[ + "# org.hiero.sdk.proto.Query\ncontract_call_local {\n contract_i_d {\n contract_num: 5005\n realm_num: 0\n shard_num: 0\n }\n function_parameters: \"\\022J\\203\\372\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000@\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\200\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\005Hello\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\006world!\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\"\n gas: 1541\n header {\n }\n max_result_size: 0\n sender_id {\n account_num: 3\n realm_num: 2\n shard_num: 1\n }\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/ContractCreateTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/ContractCreateTransactionTest.java new file mode 100644 index 0000000000..94c440fc86 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ContractCreateTransactionTest.java @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Duration; +import java.time.Instant; +import java.util.Arrays; +import org.bouncycastle.util.encoders.Hex; +import org.hiero.sdk.proto.ContractCreateTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class ContractCreateTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + @Test + void shouldSerialize2() { + SnapshotMatcher.expect(spawnTestTransaction2().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new ContractCreateTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + private ContractCreateTransaction spawnTestTransaction() { + return new ContractCreateTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setBytecodeFileId(FileId.fromString("0.0.3003")) + .setAdminKey(unusedPrivateKey) + .setGas(0) + .setInitialBalance(Hbar.fromTinybars(1000)) + .setStakedAccountId(AccountId.fromString("0.0.3")) + .setMaxAutomaticTokenAssociations(101) + .setAutoRenewPeriod(Duration.ofHours(10)) + .setConstructorParameters(new byte[] {10, 11, 12, 13, 25}) + .setMaxTransactionFee(Hbar.fromTinybars(100_000)) + .setAutoRenewAccountId(new AccountId(30)) + .freeze() + .sign(unusedPrivateKey); + } + + private ContractCreateTransaction spawnTestTransaction2() { + return new ContractCreateTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setBytecode(Hex.decode("deadbeef")) + .setAdminKey(unusedPrivateKey) + .setGas(0) + .setInitialBalance(Hbar.fromTinybars(1000)) + .setStakedNodeId(4L) + .setMaxAutomaticTokenAssociations(101) + .setAutoRenewPeriod(Duration.ofHours(10)) + .setConstructorParameters(new byte[] {10, 11, 12, 13, 25}) + .setMaxTransactionFee(Hbar.fromTinybars(100_000)) + .setAutoRenewAccountId(new AccountId(30)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = ContractCreateTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldBytes2() throws Exception { + var tx = spawnTestTransaction2(); + var tx2 = ContractCreateTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx2.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setContractCreateInstance( + ContractCreateTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(ContractCreateTransaction.class); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/ContractCreateTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/ContractCreateTransactionTest.snap new file mode 100644 index 0000000000..21cf7b7671 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ContractCreateTransactionTest.snap @@ -0,0 +1,8 @@ +org.hiero.sdk.ContractCreateTransactionTest.shouldSerialize2=[ + "# org.hiero.sdk.proto.TransactionBody\ncontract_create_instance {\n admin_key {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n auto_renew_account_id {\n account_num: 30\n realm_num: 0\n shard_num: 0\n }\n auto_renew_period {\n seconds: 36000\n }\n constructor_parameters: \"\\n\\v\\f\\r\\031\"\n gas: 0\n initcode: \"\\336\\255\\276\\357\"\n initial_balance: 1000\n max_automatic_token_associations: 101\n staked_node_id: 4\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] + + +org.hiero.sdk.ContractCreateTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\ncontract_create_instance {\n admin_key {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n auto_renew_account_id {\n account_num: 30\n realm_num: 0\n shard_num: 0\n }\n auto_renew_period {\n seconds: 36000\n }\n constructor_parameters: \"\\n\\v\\f\\r\\031\"\n file_i_d {\n file_num: 3003\n realm_num: 0\n shard_num: 0\n }\n gas: 0\n initial_balance: 1000\n max_automatic_token_associations: 101\n staked_account_id {\n account_num: 3\n realm_num: 0\n shard_num: 0\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/ContractDeleteTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/ContractDeleteTransactionTest.java new file mode 100644 index 0000000000..140ce31c54 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ContractDeleteTransactionTest.java @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import org.hiero.sdk.proto.ContractDeleteTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class ContractDeleteTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + private ContractDeleteTransaction spawnTestTransaction() { + return new ContractDeleteTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setContractId(ContractId.fromString("0.0.5007")) + .setTransferAccountId(new AccountId(9)) + .setTransferContractId(ContractId.fromString("0.0.5008")) + .setMaxTransactionFee(Hbar.fromTinybars(100_000)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = ContractDeleteTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new ContractDeleteTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setContractDeleteInstance( + ContractDeleteTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(ContractDeleteTransaction.class); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/ContractDeleteTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/ContractDeleteTransactionTest.snap new file mode 100644 index 0000000000..792abb8932 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ContractDeleteTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.ContractDeleteTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\ncontract_delete_instance {\n contract_i_d {\n contract_num: 5007\n realm_num: 0\n shard_num: 0\n }\n transfer_contract_i_d {\n contract_num: 5008\n realm_num: 0\n shard_num: 0\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/ContractExecuteTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/ContractExecuteTransactionTest.java new file mode 100644 index 0000000000..6a871f6330 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ContractExecuteTransactionTest.java @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.google.protobuf.ByteString; +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import org.hiero.sdk.proto.ContractCallTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class ContractExecuteTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new ContractExecuteTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + private ContractExecuteTransaction spawnTestTransaction() { + return new ContractExecuteTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setContractId(ContractId.fromString("0.0.5007")) + .setGas(10) + .setPayableAmount(Hbar.fromTinybars(1000)) + .setFunctionParameters(ByteString.copyFrom(new byte[] {24, 43, 11})) + .setMaxTransactionFee(Hbar.fromTinybars(100_000)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = ContractExecuteTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setContractCall(ContractCallTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(ContractExecuteTransaction.class); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/ContractExecuteTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/ContractExecuteTransactionTest.snap new file mode 100644 index 0000000000..9cd5fd27bd --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ContractExecuteTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.ContractExecuteTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\ncontract_call {\n amount: 1000\n contract_i_d {\n contract_num: 5007\n realm_num: 0\n shard_num: 0\n }\n function_parameters: \"\\030+\\v\"\n gas: 10\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/ContractFunctionParametersTest.java b/sdk/src/test/java/org/hiero/sdk/ContractFunctionParametersTest.java new file mode 100644 index 0000000000..a50a02b719 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ContractFunctionParametersTest.java @@ -0,0 +1,634 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import com.google.protobuf.ByteString; +import io.github.jsonSnapshot.SnapshotMatcher; +import java.lang.reflect.Array; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; +import org.bouncycastle.util.encoders.Hex; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class ContractFunctionParametersTest { + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @SuppressWarnings("unused") + private static List int256Arguments() { + return List.of( + Arguments.of(0, "0000000000000000000000000000000000000000000000000000000000000000"), + Arguments.of(2, "0000000000000000000000000000000000000000000000000000000000000002"), + Arguments.of(255, "00000000000000000000000000000000000000000000000000000000000000ff"), + Arguments.of(4095, "0000000000000000000000000000000000000000000000000000000000000fff"), + Arguments.of(127 << 24, "000000000000000000000000000000000000000000000000000000007f000000"), + Arguments.of(2047 << 20, "000000000000000000000000000000000000000000000000000000007ff00000"), + // deadbeef as an integer literal is negative + Arguments.of(0xdeadbeefL, "00000000000000000000000000000000000000000000000000000000deadbeef"), + Arguments.of(-1, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + Arguments.of(-2, "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"), + Arguments.of(-256, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"), + Arguments.of(-4096, "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000"), + Arguments.of(255 << 24, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000"), + Arguments.of(4095 << 20, "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000"), + Arguments.of(0xdeadbeef, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffdeadbeef")); + } + + @SuppressWarnings("unused") + private static List uInt256Arguments() { + return List.of( + Arguments.of(0, "0000000000000000000000000000000000000000000000000000000000000000", 8), + Arguments.of(2, "0000000000000000000000000000000000000000000000000000000000000002", 8), + Arguments.of(255, "00000000000000000000000000000000000000000000000000000000000000ff", 8), + Arguments.of(4095, "0000000000000000000000000000000000000000000000000000000000000fff", 32), + Arguments.of(127 << 24, "000000000000000000000000000000000000000000000000000000007f000000", 32), + Arguments.of(2047 << 20, "000000000000000000000000000000000000000000000000000000007ff00000", 32), + // deadbeef as an integer literal is negative + Arguments.of(0xdeadbeef, "00000000000000000000000000000000000000000000000000000000deadbeef", 32), + Arguments.of(-1, "000000000000000000000000000000000000000000000000ffffffffffffffff", 64), + Arguments.of(-2, "000000000000000000000000000000000000000000000000fffffffffffffffe", 64), + Arguments.of(-256, "000000000000000000000000000000000000000000000000ffffffffffffff00", 64), + Arguments.of(-4096, "000000000000000000000000000000000000000000000000fffffffffffff000", 64), + Arguments.of(255 << 24, "000000000000000000000000000000000000000000000000ffffffffff000000", 64), + Arguments.of(4095 << 20, "000000000000000000000000000000000000000000000000fffffffffff00000", 64), + Arguments.of(0xdeadbeefL, "00000000000000000000000000000000000000000000000000000000deadbeef", 64)); + } + + @Test + @DisplayName("encodes int types correctly") + void intTypes() { + ContractFunctionParameters params = new ContractFunctionParameters() + .addUint8((byte) 0x1) + .addInt8((byte) -0x2) + .addUint32(0x3) + .addInt32(-0x4) + .addUint64(0x4) + .addInt64(-0x5) + .addUint256(BigInteger.valueOf(0x6)) + .addInt256(BigInteger.valueOf(-0x7)) + .addUint8Array(new byte[] {(byte) 0x1, (byte) 0x2, (byte) 0x3, (byte) 0x4}) + .addInt8Array(new byte[] {(byte) -0x5, (byte) 0x6, (byte) 0x7, (byte) -0x8}) + .addUint32Array(new int[] {0x9, 0xA, 0xB, 0xC}) + .addInt32Array(new int[] {-0xD, 0xE, 0xF, -0x10}) + .addUint64Array(new long[] {0x11, 0x12, 0x13, 0x14}) + .addInt64Array(new long[] {-0x15, 0x16, 0x17, -0x18}) + .addUint256Array(new BigInteger[] {BigInteger.valueOf(0x19)}) + .addInt256Array(new BigInteger[] {BigInteger.valueOf(-0x1A)}); + + assertThat("11bcd903" + "0000000000000000000000000000000000000000000000000000000000000001" + + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe" + + "0000000000000000000000000000000000000000000000000000000000000003" + + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb" + + "0000000000000000000000000000000000000000000000000000000000000006" + + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9" + + "0000000000000000000000000000000000000000000000000000000000000200" + + "00000000000000000000000000000000000000000000000000000000000002a0" + + "0000000000000000000000000000000000000000000000000000000000000340" + + "00000000000000000000000000000000000000000000000000000000000003e0" + + "0000000000000000000000000000000000000000000000000000000000000480" + + "0000000000000000000000000000000000000000000000000000000000000520" + + "00000000000000000000000000000000000000000000000000000000000005c0" + + "0000000000000000000000000000000000000000000000000000000000000600" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "0000000000000000000000000000000000000000000000000000000000000003" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb" + + "0000000000000000000000000000000000000000000000000000000000000006" + + "0000000000000000000000000000000000000000000000000000000000000007" + + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "0000000000000000000000000000000000000000000000000000000000000009" + + "000000000000000000000000000000000000000000000000000000000000000a" + + "000000000000000000000000000000000000000000000000000000000000000b" + + "000000000000000000000000000000000000000000000000000000000000000c" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3" + + "000000000000000000000000000000000000000000000000000000000000000e" + + "000000000000000000000000000000000000000000000000000000000000000f" + + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "0000000000000000000000000000000000000000000000000000000000000011" + + "0000000000000000000000000000000000000000000000000000000000000012" + + "0000000000000000000000000000000000000000000000000000000000000013" + + "0000000000000000000000000000000000000000000000000000000000000014" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeb" + + "0000000000000000000000000000000000000000000000000000000000000016" + + "0000000000000000000000000000000000000000000000000000000000000017" + + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe8" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000000019" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe6") + .isEqualTo(Hex.toHexString(params.toBytes("foo").toByteArray())); + } + + @Test + @DisplayName("uint256 chops off sign bit if length is 256 bits") + void uint256BitLength() { + var params = new ContractFunctionParameters() + .addUint256(BigInteger.valueOf(2).pow(255)); + + assertThat("2fbebd38" + "8000000000000000000000000000000000000000000000000000000000000000") + .isEqualTo(Hex.toHexString(params.toBytes("foo").toByteArray())); + } + + @Test + @DisplayName("uint256 errors if less than 0") + void uint256Errors() { + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { + new ContractFunctionParameters().addUint256(BigInteger.valueOf(-0x1)); + }); + + /* + assertThrows(IllegalArgumentException.class, () -> { + new ContractFunctionParameters() + .addUint256(BigInteger.valueOf(2).pow(256)); + }); + */ + } + + @Test + @DisplayName("encodes addresses correctly") + void addresses() { + var params = new ContractFunctionParameters() + .addAddress("1122334455667788990011223344556677889900") + .addAddress("0x1122334455667788990011223344556677889900") + .addAddressArray(new String[] { + "1122334455667788990011223344556677889900", "1122334455667788990011223344556677889900" + }); + + assertThat("7d48c86d" + "0000000000000000000000001122334455667788990011223344556677889900" + + "0000000000000000000000001122334455667788990011223344556677889900" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "0000000000000000000000001122334455667788990011223344556677889900" + + "0000000000000000000000001122334455667788990011223344556677889900") + .isEqualTo(Hex.toHexString(params.toBytes("foo").toByteArray())); + } + + @Test + @DisplayName("encodes functions correctly") + void addressesError() { + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { + new ContractFunctionParameters().addAddress("112233445566778899001122334455667788990011"); + }); + } + + @Test + @DisplayName("encodes functions correctly") + void functions() { + var params = new ContractFunctionParameters() + .addFunction("1122334455667788990011223344556677889900", new byte[] {1, 2, 3, 4}) + .addFunction( + "0x1122334455667788990011223344556677889900", + new ContractFunctionSelector("randomFunction").addBool()); + + assertThat("c99c40cd" + "1122334455667788990011223344556677889900010203040000000000000000" + + "112233445566778899001122334455667788990063441d820000000000000000") + .isEqualTo(Hex.toHexString(params.toBytes("foo").toByteArray())); + } + + @Test + @DisplayName("encodes functions correctly") + void functionsError() { + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { + new ContractFunctionParameters() + .addFunction("112233445566778899001122334455667788990011", new byte[] {1, 2, 3, 4}); + }); + + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { + new ContractFunctionParameters() + .addFunction("1122334455667788990011223344556677889900", new byte[] {1, 2, 3, 4, 5}); + }); + } + + @Test + @DisplayName("encodes bytes4 correctly") + void bytes4Encoding() { + var params = new ContractFunctionParameters().addBytes4(new byte[] {1, 2, 3, 4}); + assertThat("580526ee" + "0102030400000000000000000000000000000000000000000000000000000000") + .isEqualTo(Hex.toHexString(params.toBytes("foo").toByteArray())); + } + + @Test + @DisplayName("fails to encode bytes4 if length too long") + void bytes4EncodingError() { + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { + new ContractFunctionParameters().addBytes4(new byte[] {1, 2, 3, 4, 5}); + }); + } + + @Test + @DisplayName("encodes UTF-8 string as bytes4 correctly") + void bytes4UTF8Encoding() { + var params = new ContractFunctionParameters().addBytes4("ABCD".getBytes(StandardCharsets.UTF_8)); + assertThat("580526ee" + "4142434400000000000000000000000000000000000000000000000000000000") + .isEqualTo(Hex.toHexString(params.toBytes("foo").toByteArray())); + } + + @Test + @DisplayName("fails to encode UTF-8 string as bytes4 if length is bigger than 4 bytes") + void bytes4UTF8EncodingError() { + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { + new ContractFunctionParameters().addBytes4("ABCDE".getBytes(StandardCharsets.UTF_8)); + }); + } + + @Test + @DisplayName("encodes bytes32 correctly") + void bytes() { + var params = new ContractFunctionParameters().addBytes32(new byte[] { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 + }); + + assertThat("11e814c1" + "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20") + .isEqualTo(Hex.toHexString(params.toBytes("foo").toByteArray())); + } + + @Test + @DisplayName("fails to encode bytes32 if length too long") + void bytesError() { + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { + new ContractFunctionParameters().addBytes32(new byte[] { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33 + }); + }); + } + + @Test + @DisplayName("encodes boolean correctly") + void bool() { + var params = new ContractFunctionParameters().addBool(true).addBool(false); + + assertThat("b3cedfcf" + "0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000000000") + .isEqualTo(Hex.toHexString(params.toBytes("foo").toByteArray())); + } + + @Test + @DisplayName("encodes dynamic params correctly") + void dynamicParamsEncoding() { + ByteString paramsStringArg = + new ContractFunctionParameters().addString("Hello, world!").toBytes("set_message"); + + ByteString paramsBytesArg = new ContractFunctionParameters() + .addBytes("Hello, world!".getBytes(StandardCharsets.UTF_8)) + .toBytes("set_message"); + + String paramsStringArgHex = Hex.toHexString(paramsStringArg.toByteArray()); + String paramsBytesArgHex = Hex.toHexString(paramsBytesArg.toByteArray()); + + assertThat("2e982602" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "000000000000000000000000000000000000000000000000000000000000000d" + + "48656c6c6f2c20776f726c642100000000000000000000000000000000000000") + .isEqualTo(paramsStringArgHex); + + // signature should encode differently but the contents are identical + assertThat("010473a7" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "000000000000000000000000000000000000000000000000000000000000000d" + + "48656c6c6f2c20776f726c642100000000000000000000000000000000000000") + .isEqualTo(paramsBytesArgHex); + } + + @Test + @DisplayName("encodes static params correctly") + void staticParamsEncoding() { + ContractFunctionParameters params = new ContractFunctionParameters() + .addInt32(0x11223344) + .addInt32(-65536) + .addUint64(-65536) + .addAddress("00112233445566778899aabbccddeeff00112233"); + + String paramsHex = Hex.toHexString(params.toBytes(null).toByteArray()); + + assertThat("0000000000000000000000000000000000000000000000000000000011223344" + // sign-extended + + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000" + // zero-padded + + "000000000000000000000000000000000000000000000000ffffffffffff0000" + + "00000000000000000000000000112233445566778899aabbccddeeff00112233") + .isEqualTo(paramsHex); + } + + @Test + @DisplayName("encodes mixed static and dynamic params correctly") + void mixedParamsEncoding() { + ContractFunctionParameters params = new ContractFunctionParameters() + .addInt256(BigInteger.valueOf(0xdeadbeef).shiftLeft(8)) + .addString("Hello, world!") + .addBytes(new byte[] {-1, -18, 63, 127}) + .addBool(true) + .addUint8Array(new byte[] {-1, 127}); + + String paramsHex = Hex.toHexString(params.toBytes("foo").toByteArray()); + + assertThat("6a5bb8f2" + + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffdeadbeef00" + + "00000000000000000000000000000000000000000000000000000000000000a0" + + "00000000000000000000000000000000000000000000000000000000000000e0" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000000120" + + "000000000000000000000000000000000000000000000000000000000000000d" + + "48656c6c6f2c20776f726c642100000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "ffee3f7f00000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "00000000000000000000000000000000000000000000000000000000000000ff" + + "000000000000000000000000000000000000000000000000000000000000007f") + .isEqualTo(paramsHex); + } + + @Test + @DisplayName("encodes array types correctly") + void arrayTypesEncoding() { + ContractFunctionParameters params = new ContractFunctionParameters() + .addStringArray(new String[] {"hello", ",", "world!"}) + .addInt32Array(new int[] {0x88, 0x99, 0xAA, 0xBB}) + .addInt256Array(new BigInteger[] {BigInteger.valueOf(0x1111)}); + + assertThat("025838fc" + "0000000000000000000000000000000000000000000000000000000000000060" + + "00000000000000000000000000000000000000000000000000000000000001a0" + + "0000000000000000000000000000000000000000000000000000000000000240" + + "0000000000000000000000000000000000000000000000000000000000000003" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "00000000000000000000000000000000000000000000000000000000000000a0" + + "00000000000000000000000000000000000000000000000000000000000000e0" + + "0000000000000000000000000000000000000000000000000000000000000005" + + "68656c6c6f000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "2c00000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000006" + + "776f726c64210000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000004" + + "0000000000000000000000000000000000000000000000000000000000000088" + + "0000000000000000000000000000000000000000000000000000000000000099" + + "00000000000000000000000000000000000000000000000000000000000000aa" + + "00000000000000000000000000000000000000000000000000000000000000bb" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000001111") + .isEqualTo(Hex.toHexString(params.toBytes("foo").toByteArray())); + } + + @Test + @DisplayName("bytes4[] encodes correctly") + void fixedBytes4ArrayEncoding() { + ContractFunctionParameters params = new ContractFunctionParameters().addBytes4Array(new byte[][] { + {1, 2, 3, 4}, + {5, 6, 7, 8}, + {9, 10, 11, 12} + }); + assertThat( + "0000000000000000000000000000000000000000000000000000000000000020" + // offset of array + "0000000000000000000000000000000000000000000000000000000000000003" + + // length of array + "0102030400000000000000000000000000000000000000000000000000000000" + + // first bytes4 + "0506070800000000000000000000000000000000000000000000000000000000" + + // second bytes4 + "090a0b0c00000000000000000000000000000000000000000000000000000000" // third bytes4 + ) + .isEqualTo(Hex.toHexString(params.toBytes(null).toByteArray())); + } + + @Test + @DisplayName("bytes32[] encodes correctly") + void fixedBytesArrayEncoding() { + // each string should be padded to 32 bytes and have no length prefix + + ContractFunctionParameters params = new ContractFunctionParameters().addBytes32Array(new byte[][] { + "Hello".getBytes(StandardCharsets.UTF_8), + ",".getBytes(StandardCharsets.UTF_8), + "world!".getBytes(StandardCharsets.UTF_8) + }); + + assertThat( + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000003" + + // length of array + "48656c6c6f000000000000000000000000000000000000000000000000000000" + + // "Hello" UTF-8 encoded + "2c00000000000000000000000000000000000000000000000000000000000000" + + // "," UTF-8 encoded + "776f726c64210000000000000000000000000000000000000000000000000000" // "world!" UTF-8 + // encoded + ) + .isEqualTo(Hex.toHexString(params.toBytes(null).toByteArray())); + } + + @Test + @DisplayName("bytes[] encodes correctly") + void dynBytesArrayEncoding() { + // result should be the exact same as the strings test below + ContractFunctionParameters params = new ContractFunctionParameters().addBytesArray(new byte[][] { + "Hello".getBytes(StandardCharsets.UTF_8), + ",".getBytes(StandardCharsets.UTF_8), + "world!".getBytes(StandardCharsets.UTF_8) + }); + + assertThat( + "0000000000000000000000000000000000000000000000000000000000000020" + // offset to array + "0000000000000000000000000000000000000000000000000000000000000003" + + // length of array + "0000000000000000000000000000000000000000000000000000000000000060" + + // first element offset, relative to beginning of this list (after length) + "00000000000000000000000000000000000000000000000000000000000000a0" + + // second element offset + "00000000000000000000000000000000000000000000000000000000000000e0" + + // third element offset + "0000000000000000000000000000000000000000000000000000000000000005" + + // "Hello".length + "48656c6c6f000000000000000000000000000000000000000000000000000000" + + // "Hello" UTF-8 encoded + "0000000000000000000000000000000000000000000000000000000000000001" + + // ",".length + "2c00000000000000000000000000000000000000000000000000000000000000" + + // "," UTF-8 encoded + "0000000000000000000000000000000000000000000000000000000000000006" + + // "world!".length + "776f726c64210000000000000000000000000000000000000000000000000000" // "world!" UTF-8 + // encoded + ) + .isEqualTo(Hex.toHexString(params.toBytes(null).toByteArray())); + } + + @Test + @DisplayName("issue 376: string[] encodes correctly") + void stringArrayEncoding() { + ContractFunctionParameters params = + new ContractFunctionParameters().addStringArray(new String[] {"Hello", ",", "world!"}); + + assertThat( + "0000000000000000000000000000000000000000000000000000000000000020" + // offset to array + "0000000000000000000000000000000000000000000000000000000000000003" + + // length of array + "0000000000000000000000000000000000000000000000000000000000000060" + + // first element offset, relative to beginning of this list (after length) + "00000000000000000000000000000000000000000000000000000000000000a0" + + // second element offset + "00000000000000000000000000000000000000000000000000000000000000e0" + + // third element offset + "0000000000000000000000000000000000000000000000000000000000000005" + + // "Hello".length + "48656c6c6f000000000000000000000000000000000000000000000000000000" + + // "Hello" UTF-8 encoded + "0000000000000000000000000000000000000000000000000000000000000001" + + // ",".length + "2c00000000000000000000000000000000000000000000000000000000000000" + + // "," UTF-8 encoded + "0000000000000000000000000000000000000000000000000000000000000006" + + // "world!".length + "776f726c64210000000000000000000000000000000000000000000000000000" // "world!" UTF-8 + // encoded + ) + .isEqualTo(Hex.toHexString(params.toBytes(null).toByteArray())); + } + + @Test + @Disabled + @DisplayName("BigInteger checks") + void bigIntChecks() { + ContractFunctionParameters params = new ContractFunctionParameters(); + + // allowed values for BigInteger + params.addInt256(BigInteger.ONE.shiftLeft(254)); + params.addInt256(BigInteger.ONE.negate().shiftLeft(255)); + + String rangeErr = "BigInteger out of range for Solidity integers"; + + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> params.addInt256(BigInteger.ONE.shiftLeft(255))) + .satisfies(error -> assertThat(error.getMessage()).isEqualTo(rangeErr)); + + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> params.addInt256(BigInteger.ONE.negate().shiftLeft(256))) + .satisfies(error -> assertThat(error.getMessage()).isEqualTo(rangeErr)); + } + + @Test + @DisplayName("address param checks") + void addressParamChecks() { + ContractFunctionParameters params = new ContractFunctionParameters(); + + String lenErr = "Solidity addresses must be 40 hex chars"; + + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> params.addAddress("")) + .satisfies(error -> assertThat(error.getMessage()).isEqualTo(lenErr)); + + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> params.addAddress("aabbccdd")) + .satisfies(error -> assertThat(error.getMessage()).isEqualTo(lenErr)); + + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> params.addAddress("00112233445566778899aabbccddeeff0011223344")) + .satisfies(error -> assertThat(error.getMessage()).isEqualTo(lenErr)); + + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> params.addAddress("gghhii--__zz66778899aabbccddeeff00112233")) + .satisfies( + error -> assertThat(error.getMessage()).isEqualTo("failed to decode Solidity address as hex")); + } + + @ParameterizedTest + @DisplayName("int256() encodes correctly") + @MethodSource("int256Arguments") + void int256EncodesCorrectly(long val, String hexString) { + assertThat(hexString) + .isEqualTo(Hex.toHexString( + ContractFunctionParameters.int256(val, 64).toByteArray())); + } + + @ParameterizedTest + @DisplayName("uint256() encodes correctly") + @MethodSource("uInt256Arguments") + void uInt256EncodesCorrectly(long val, String hexString, int bitWidth) { + assertThat(hexString) + .isEqualTo(Hex.toHexString( + ContractFunctionParameters.uint256(val, bitWidth).toByteArray())); + } + + @Test + void intSizesEncodeCorrectly() throws Exception { + List snapshotStrings = new ArrayList<>(); + for (int n = 8; n <= 256; n += 8) { + var bitWidth = n; + + var argType = ((Supplier>) () -> { + if (bitWidth == 8) { + return byte.class; + } else if (bitWidth <= 32) { + return int.class; + } else if (bitWidth <= 64) { + return long.class; + } else { + return BigInteger.class; + } + }) + .get(); + + var argVal = ((Supplier) () -> { + if (bitWidth == 8) { + return (byte) (1 << (bitWidth - 1)); + } else if (bitWidth <= 32) { + return (1 << (bitWidth - 1)); + } else if (bitWidth <= 64) { + return (1L << (bitWidth - 1)); + } else { + return BigInteger.ONE.shiftLeft(bitWidth - 1); + } + }) + .get(); + + var argArrayVal = Array.newInstance(argType, 2); + Array.set(argArrayVal, 0, argVal); + Array.set(argArrayVal, 1, argVal); + var argArrayType = argArrayVal.getClass(); + + var cl = ContractFunctionParameters.class; + var addIntMethod = cl.getMethod("addInt" + n, argType); + var addUintMethod = cl.getMethod("addUint" + n, argType); + var addIntArrayMethod = cl.getMethod("addInt" + n + "Array", argArrayType); + var addUintArrayMethod = cl.getMethod("addUint" + n + "Array", argArrayType); + + var params = new ContractFunctionParameters(); + addIntMethod.invoke(params, argVal); + addUintMethod.invoke(params, argVal); + addIntArrayMethod.invoke(params, argArrayVal); + addUintArrayMethod.invoke(params, argArrayVal); + + snapshotStrings.add("bitWidth = " + bitWidth + ": " + + Hex.toHexString(params.toBytes(null).toByteArray())); + } + SnapshotMatcher.expect(snapshotStrings.toArray()).toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractFunctionParametersTest.snap b/sdk/src/test/java/org/hiero/sdk/ContractFunctionParametersTest.snap similarity index 99% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/ContractFunctionParametersTest.snap rename to sdk/src/test/java/org/hiero/sdk/ContractFunctionParametersTest.snap index d15abc50c3..3515eb9bfd 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractFunctionParametersTest.snap +++ b/sdk/src/test/java/org/hiero/sdk/ContractFunctionParametersTest.snap @@ -1,4 +1,4 @@ -com.hedera.hashgraph.sdk.ContractFunctionParametersTest.intSizesEncodeCorrectly=[ +org.hiero.sdk.ContractFunctionParametersTest.intSizesEncodeCorrectly=[ [ "bitWidth = 8: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000002ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000080", "bitWidth = 16: 00000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000008000", @@ -33,4 +33,4 @@ com.hedera.hashgraph.sdk.ContractFunctionParametersTest.intSizesEncodeCorrectly= "bitWidth = 248: 00800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000200800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000", "bitWidth = 256: 80000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000" ] -] \ No newline at end of file +] diff --git a/sdk/src/test/java/org/hiero/sdk/ContractFunctionResultTest.java b/sdk/src/test/java/org/hiero/sdk/ContractFunctionResultTest.java new file mode 100644 index 0000000000..be38e7c980 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ContractFunctionResultTest.java @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.google.protobuf.ByteString; +import com.google.protobuf.BytesValue; +import java.math.BigInteger; +import org.bouncycastle.util.encoders.Hex; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class ContractFunctionResultTest { + static final String CALL_RESULT_HEX = "00000000000000000000000000000000000000000000000000000000ffffffff" + + "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + + "00000000000000000000000011223344556677889900aabbccddeeff00112233" + + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + + "00000000000000000000000000000000000000000000000000000000000000c0" + + "0000000000000000000000000000000000000000000000000000000000000100" + + "000000000000000000000000000000000000000000000000000000000000000d" + + "48656c6c6f2c20776f726c642100000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000014" + + "48656c6c6f2c20776f726c642c20616761696e21000000000000000000000000"; + + private static final String STRING_ARRAY_RESULT_HEX = + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "000000000000000000000000000000000000000000000000000000000000000C" + + "72616E646F6D2062797465730000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000000C" + + "72616E646F6D2062797465730000000000000000000000000000000000000000"; + + private static final byte[] callResult = Hex.decode(CALL_RESULT_HEX); + private static final byte[] stringArrayCallResult = Hex.decode(STRING_ARRAY_RESULT_HEX); + + @Test + @DisplayName("provides results correctly") + void providesResultsCorrectly() { + var result = new ContractFunctionResult(org.hiero.sdk.proto.ContractFunctionResult.newBuilder() + .setContractID(ContractId.fromString("1.2.3").toProtobuf()) + .setContractCallResult(ByteString.copyFrom(callResult)) + .setEvmAddress(BytesValue.newBuilder() + .setValue(ByteString.copyFrom(Hex.decode("98329e006610472e6B372C080833f6D79ED833cf"))) + .build()) + // .addStateChanges( + // new ContractStateChange( + // ContractId.fromString("1.2.3"), + // Collections.singletonList( + // new StorageChange( + // BigInteger.valueOf(555), + // BigInteger.valueOf(666), + // BigInteger.valueOf(777) + // ) + // ) + // ).toProtobuf()) + .setSenderId(AccountId.fromString("1.2.3").toProtobuf()) + .addContractNonces(new ContractNonceInfo(ContractId.fromString("1.2.3"), 10L).toProtobuf())); + + // interpretation varies based on width + assertThat(result.getBool(0)).isTrue(); + assertThat(result.getInt32(0)).isEqualTo(-1); + assertThat(result.getInt64(0)).isEqualTo((1L << 32) - 1); + assertThat(result.getInt256(0)).isEqualTo(BigInteger.ONE.shiftLeft(32).subtract(BigInteger.ONE)); + + assertThat(result.getInt256(1)).isEqualTo(BigInteger.ONE.shiftLeft(255).subtract(BigInteger.ONE)); + + assertThat(result.getAddress(2)).isEqualTo("11223344556677889900aabbccddeeff00112233"); + + // unsigned integers (where applicable) + assertThat(result.getUint32(3)).isEqualTo(-1); + assertThat(result.getUint64(3)).isEqualTo(-1L); + // BigInteger can represent the full range and so should be 2^256 - 1 + assertThat(result.getUint256(3)).isEqualTo(BigInteger.ONE.shiftLeft(256).subtract(BigInteger.ONE)); + + assertThat(result.getString(4)).isEqualTo("Hello, world!"); + assertThat(result.getString(5)).isEqualTo("Hello, world, again!"); + + assertThat(result.senderAccountId).isEqualTo(AccountId.fromString("1.2.3")); + + assertThat(result.contractId).isEqualTo(ContractId.fromString("1.2.3")); + assertThat(result.evmAddress) + .isEqualTo(ContractId.fromEvmAddress(1, 2, "98329e006610472e6B372C080833f6D79ED833cf")); + // assertThat(result.stateChanges.size()).isEqualTo(1); + // ContractStateChange resultStateChange = result.stateChanges.get(0); + // assertThat(resultStateChange.contractId).isEqualTo(ContractId.fromString("1.2.3")); + // assertThat(resultStateChange.storageChanges.size()).isEqualTo(1); + // StorageChange resultStorageChange = resultStateChange.storageChanges.get(0); + // assertThat(resultStorageChange.slot).isEqualTo(BigInteger.valueOf(555)); + // assertThat(resultStorageChange.valueRead).isEqualTo(BigInteger.valueOf(666)); + // assertThat(resultStorageChange.valueWritten).isEqualTo(BigInteger.valueOf(777)); + assertThat(result.contractNonces).containsOnly(new ContractNonceInfo(ContractId.fromString("1.2.3"), 10L)); + } + + @Test + @DisplayName("can get string array result") + void canGetStringArrayResult() { + var result = new ContractFunctionResult(org.hiero.sdk.proto.ContractFunctionResult.newBuilder() + .setContractCallResult(ByteString.copyFrom(stringArrayCallResult))); + + var strings = result.getStringArray(0); + assertThat(strings.get(0)).isEqualTo("random bytes"); + assertThat(strings.get(1)).isEqualTo("random bytes"); + } + + @Test + @DisplayName("Can to/from bytes with state changes") + void canToFromBytesStateChanges() {} +} diff --git a/sdk/src/test/java/org/hiero/sdk/ContractFunctionSelectorTest.java b/sdk/src/test/java/org/hiero/sdk/ContractFunctionSelectorTest.java new file mode 100644 index 0000000000..eaf2ea8561 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ContractFunctionSelectorTest.java @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatNoException; + +import org.bouncycastle.util.encoders.Hex; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class ContractFunctionSelectorTest { + @Test + @DisplayName("Can add all types") + void selector() { + var signature = new ContractFunctionSelector("testFunction") + .addAddress() + .addAddressArray() + .addBool() + .addBytes() + .addBytes32() + .addBytes32Array() + .addBytesArray() + .addFunction() + .addInt8() + .addInt8Array() + .addInt32() + .addInt32Array() + .addInt64() + .addInt64Array() + .addInt256() + .addInt256Array() + .addUint8() + .addUint8Array() + .addUint32() + .addUint32Array() + .addUint64() + .addUint64Array() + .addUint256() + .addUint256Array() + .addString() + .addStringArray() + .finish(); + + assertThat(Hex.toHexString(signature)).isEqualTo("4438e4ce"); + } + + @Test + @DisplayName("Throws in adding after finished") + void selectorError() { + var signature = new ContractFunctionSelector("testFunction").addAddress(); + signature.finish(); + + assertThatExceptionOfType(IllegalStateException.class).isThrownBy(signature::addStringArray); + assertThatNoException().isThrownBy(signature::finish); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/ContractIdTest.java b/sdk/src/test/java/org/hiero/sdk/ContractIdTest.java new file mode 100644 index 0000000000..4768a78ede --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ContractIdTest.java @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.github.jsonSnapshot.SnapshotMatcher; +import org.bouncycastle.util.encoders.Hex; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +class ContractIdTest { + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void fromString() { + SnapshotMatcher.expect(ContractId.fromString("0.0.5005").toString()).toMatchSnapshot(); + } + + @Test + void fromSolidityAddress() { + SnapshotMatcher.expect(ContractId.fromSolidityAddress("000000000000000000000000000000000000138D") + .toString()) + .toMatchSnapshot(); + } + + @Test + void fromSolidityAddressWith0x() { + SnapshotMatcher.expect(ContractId.fromSolidityAddress("0x000000000000000000000000000000000000138D") + .toString()) + .toMatchSnapshot(); + } + + @Test + void fromEvmAddress() { + SnapshotMatcher.expect(ContractId.fromEvmAddress(1, 2, "98329e006610472e6B372C080833f6D79ED833cf") + .toString()) + .toMatchSnapshot(); + } + + @Test + void fromEvmAddressWith0x() { + SnapshotMatcher.expect(ContractId.fromEvmAddress(1, 2, "0x98329e006610472e6B372C080833f6D79ED833cf") + .toString()) + .toMatchSnapshot(); + } + + @Test + void fromStringWithEvmAddress() { + SnapshotMatcher.expect(ContractId.fromString("1.2.98329e006610472e6B372C080833f6D79ED833cf") + .toString()) + .toMatchSnapshot(); + } + + @Test + void toFromBytes() throws InvalidProtocolBufferException { + ContractId a = ContractId.fromString("1.2.3"); + assertThat(ContractId.fromBytes(a.toBytes())).isEqualTo(a); + ContractId b = ContractId.fromEvmAddress(1, 2, "0x98329e006610472e6B372C080833f6D79ED833cf"); + assertThat(ContractId.fromBytes(b.toBytes())).isEqualTo(b); + } + + @Test + void toBytes() throws InvalidProtocolBufferException { + SnapshotMatcher.expect(Hex.toHexString(new ContractId(5005).toBytes())).toMatchSnapshot(); + } + + @Test + void fromBytes() throws InvalidProtocolBufferException { + SnapshotMatcher.expect( + ContractId.fromBytes(new ContractId(5005).toBytes()).toString()) + .toMatchSnapshot(); + } + + @Test + void toSolidityAddress() { + SnapshotMatcher.expect(new ContractId(5005).toSolidityAddress()).toMatchSnapshot(); + } + + @Test + void toSolidityAddress2() { + SnapshotMatcher.expect(ContractId.fromEvmAddress(1, 2, "0x98329e006610472e6B372C080833f6D79ED833cf") + .toSolidityAddress()) + .toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/ContractIdTest.snap b/sdk/src/test/java/org/hiero/sdk/ContractIdTest.snap new file mode 100644 index 0000000000..ca70ebc528 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ContractIdTest.snap @@ -0,0 +1,48 @@ +org.hiero.sdk.ContractIdTest.fromBytes=[ + "0.0.5005" +] + + +org.hiero.sdk.ContractIdTest.fromEvmAddress=[ + "1.2.98329e006610472e6b372c080833f6d79ed833cf" +] + + +org.hiero.sdk.ContractIdTest.fromEvmAddressWith0x=[ + "1.2.98329e006610472e6b372c080833f6d79ed833cf" +] + + +org.hiero.sdk.ContractIdTest.fromSolidityAddress=[ + "0.0.5005" +] + + +org.hiero.sdk.ContractIdTest.fromSolidityAddressWith0x=[ + "0.0.5005" +] + + +org.hiero.sdk.ContractIdTest.fromString=[ + "0.0.5005" +] + + +org.hiero.sdk.ContractIdTest.fromStringWithEvmAddress=[ + "1.2.98329e006610472e6b372c080833f6d79ed833cf" +] + + +org.hiero.sdk.ContractIdTest.toBytes=[ + "188d27" +] + + +org.hiero.sdk.ContractIdTest.toSolidityAddress2=[ + "98329e006610472e6b372c080833f6d79ed833cf" +] + + +org.hiero.sdk.ContractIdTest.toSolidityAddress=[ + "000000000000000000000000000000000000138d" +] diff --git a/sdk/src/test/java/org/hiero/sdk/ContractInfoQueryTest.java b/sdk/src/test/java/org/hiero/sdk/ContractInfoQueryTest.java new file mode 100644 index 0000000000..08ae102684 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ContractInfoQueryTest.java @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.github.jsonSnapshot.SnapshotMatcher; +import org.hiero.sdk.proto.QueryHeader; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class ContractInfoQueryTest { + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + var builder = org.hiero.sdk.proto.Query.newBuilder(); + new ContractInfoQuery() + .setContractId(ContractId.fromString("0.0.5005")) + .onMakeRequest(builder, QueryHeader.newBuilder().build()); + SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")) + .toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/ContractInfoQueryTest.snap b/sdk/src/test/java/org/hiero/sdk/ContractInfoQueryTest.snap new file mode 100644 index 0000000000..3b20155c4f --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ContractInfoQueryTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.ContractInfoQueryTest.shouldSerialize=[ + "# org.hiero.sdk.proto.Query\ncontract_get_info {\n contract_i_d {\n contract_num: 5005\n realm_num: 0\n shard_num: 0\n }\n header {\n }\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/ContractInfoTest.java b/sdk/src/test/java/org/hiero/sdk/ContractInfoTest.java new file mode 100644 index 0000000000..8998f7cc66 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ContractInfoTest.java @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Duration; +import java.time.Instant; +import org.bouncycastle.util.encoders.Hex; +import org.hiero.sdk.proto.ContractGetInfoResponse; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class ContractInfoTest { + private final ContractGetInfoResponse.ContractInfo info = ContractGetInfoResponse.ContractInfo.newBuilder() + .setContractID(new ContractId(1).toProtobuf()) + .setAccountID(new AccountId(2).toProtobuf()) + .setContractAccountID("3") + .setExpirationTime(InstantConverter.toProtobuf(Instant.ofEpochMilli(4))) + .setAutoRenewPeriod(DurationConverter.toProtobuf(Duration.ofDays(5))) + .setStorage(6) + .setMemo("7") + .setBalance(8) + .setLedgerId(LedgerId.TESTNET.toByteString()) + .build(); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void fromProtobuf() { + SnapshotMatcher.expect(ContractInfo.fromProtobuf(info).toString()).toMatchSnapshot(); + } + + @Test + void toProtobuf() { + SnapshotMatcher.expect(ContractInfo.fromProtobuf(info).toProtobuf()).toMatchSnapshot(); + } + + @Test + void toBytes() { + SnapshotMatcher.expect(Hex.toHexString(ContractInfo.fromProtobuf(info).toBytes())) + .toMatchSnapshot(); + } + + @Test + void fromBytes() throws InvalidProtocolBufferException { + SnapshotMatcher.expect(ContractInfo.fromBytes(info.toByteArray()).toString()) + .toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractInfoTest.snap b/sdk/src/test/java/org/hiero/sdk/ContractInfoTest.snap similarity index 92% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/ContractInfoTest.snap rename to sdk/src/test/java/org/hiero/sdk/ContractInfoTest.snap index 5875fdf230..e234bbc587 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractInfoTest.snap +++ b/sdk/src/test/java/org/hiero/sdk/ContractInfoTest.snap @@ -1,19 +1,19 @@ -com.hedera.hashgraph.sdk.ContractInfoTest.fromBytes=[ +org.hiero.sdk.ContractInfoTest.fromBytes=[ "ContractInfo{contractId=0.0.1, accountId=0.0.2, contractAccountId=3, adminKey=null, expirationTime=1970-01-01T00:00:00.004Z, autoRenewPeriod=PT120H, autoRenewAccountId=null, storage=6, contractMemo=7, balance=8 tℏ, isDeleted=false, tokenRelationships={}, ledgerId=testnet, stakingInfo=null}" ] -com.hedera.hashgraph.sdk.ContractInfoTest.fromProtobuf=[ +org.hiero.sdk.ContractInfoTest.fromProtobuf=[ "ContractInfo{contractId=0.0.1, accountId=0.0.2, contractAccountId=3, adminKey=null, expirationTime=1970-01-01T00:00:00.004Z, autoRenewPeriod=PT120H, autoRenewAccountId=null, storage=6, contractMemo=7, balance=8 tℏ, isDeleted=false, tokenRelationships={}, ledgerId=testnet, stakingInfo=null}" ] -com.hedera.hashgraph.sdk.ContractInfoTest.toBytes=[ +org.hiero.sdk.ContractInfoTest.toBytes=[ "0a021801120218021a01332a05108092f40132040880af1a38064201374808620101" ] -com.hedera.hashgraph.sdk.ContractInfoTest.toProtobuf=[ +org.hiero.sdk.ContractInfoTest.toProtobuf=[ { "memoizedHashCode": 0, "memoizedSerializedSize": 2147483647, @@ -99,4 +99,4 @@ com.hedera.hashgraph.sdk.ContractInfoTest.toProtobuf=[ "maxAutomaticTokenAssociations_": 0, "initialized": true } -] \ No newline at end of file +] diff --git a/sdk/src/test/java/org/hiero/sdk/ContractLogInfoTest.java b/sdk/src/test/java/org/hiero/sdk/ContractLogInfoTest.java new file mode 100644 index 0000000000..ddfcdb186b --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ContractLogInfoTest.java @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import io.github.jsonSnapshot.SnapshotMatcher; +import java.nio.charset.StandardCharsets; +import org.bouncycastle.util.encoders.Hex; +import org.hiero.sdk.proto.ContractLoginfo; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class ContractLogInfoTest { + private static final ContractLoginfo info = ContractLoginfo.newBuilder() + .setContractID(new ContractId(10).toProtobuf()) + .setBloom(ByteString.copyFrom("bloom", StandardCharsets.UTF_8)) + .addTopic(ByteString.copyFrom("bloom", StandardCharsets.UTF_8)) + .setData(ByteString.copyFrom("data", StandardCharsets.UTF_8)) + .build(); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void fromProtobuf() { + SnapshotMatcher.expect(ContractLogInfo.fromProtobuf(info).toString()).toMatchSnapshot(); + } + + @Test + void toProtobuf() throws InvalidProtocolBufferException { + SnapshotMatcher.expect(ContractLogInfo.fromProtobuf(info).toProtobuf().toString()) + .toMatchSnapshot(); + } + + @Test + void fromBytes() throws InvalidProtocolBufferException { + SnapshotMatcher.expect(ContractLogInfo.fromBytes(info.toByteArray()).toString()) + .toMatchSnapshot(); + } + + @Test + void toBytes() { + SnapshotMatcher.expect( + Hex.toHexString(ContractLogInfo.fromProtobuf(info).toBytes())) + .toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/ContractLogInfoTest.snap b/sdk/src/test/java/org/hiero/sdk/ContractLogInfoTest.snap new file mode 100644 index 0000000000..3c0eea34a8 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ContractLogInfoTest.snap @@ -0,0 +1,18 @@ +org.hiero.sdk.ContractLogInfoTest.fromBytes=[ + "ContractLogInfo{contractId=0.0.10, bloom=626c6f6f6d, topics=[626c6f6f6d]}" +] + + +org.hiero.sdk.ContractLogInfoTest.fromProtobuf=[ + "ContractLogInfo{contractId=0.0.10, bloom=626c6f6f6d, topics=[626c6f6f6d]}" +] + + +org.hiero.sdk.ContractLogInfoTest.toBytes=[ + "0a02180a1205626c6f6f6d1a05626c6f6f6d" +] + + +org.hiero.sdk.ContractLogInfoTest.toProtobuf=[ + "# org.hiero.sdk.proto.ContractLoginfo@cb39e330\nbloom: \"bloom\"\ncontract_i_d {\n contract_num: 10\n realm_num: 0\n shard_num: 0\n}\ntopic: \"bloom\"" +] diff --git a/sdk/src/test/java/org/hiero/sdk/ContractNonceInfoTest.java b/sdk/src/test/java/org/hiero/sdk/ContractNonceInfoTest.java new file mode 100644 index 0000000000..446333356c --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ContractNonceInfoTest.java @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.github.jsonSnapshot.SnapshotMatcher; +import org.bouncycastle.util.encoders.Hex; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class ContractNonceInfoTest { + private final org.hiero.sdk.proto.ContractNonceInfo info = org.hiero.sdk.proto.ContractNonceInfo.newBuilder() + .setContractId(new ContractId(1).toProtobuf()) + .setNonce(2) + .build(); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void fromProtobuf() { + SnapshotMatcher.expect(ContractNonceInfo.fromProtobuf(info).toString()).toMatchSnapshot(); + } + + @Test + void toProtobuf() { + SnapshotMatcher.expect(ContractNonceInfo.fromProtobuf(info).toProtobuf()) + .toMatchSnapshot(); + } + + @Test + void toBytes() { + SnapshotMatcher.expect( + Hex.toHexString(ContractNonceInfo.fromProtobuf(info).toBytes())) + .toMatchSnapshot(); + } + + @Test + void fromBytes() throws InvalidProtocolBufferException { + SnapshotMatcher.expect(ContractNonceInfo.fromBytes(info.toByteArray()).toString()) + .toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractNonceInfoTest.snap b/sdk/src/test/java/org/hiero/sdk/ContractNonceInfoTest.snap similarity index 76% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/ContractNonceInfoTest.snap rename to sdk/src/test/java/org/hiero/sdk/ContractNonceInfoTest.snap index 1e1cc8e763..72b96475b4 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ContractNonceInfoTest.snap +++ b/sdk/src/test/java/org/hiero/sdk/ContractNonceInfoTest.snap @@ -1,19 +1,19 @@ -com.hedera.hashgraph.sdk.ContractNonceInfoTest.fromBytes=[ +org.hiero.sdk.ContractNonceInfoTest.fromBytes=[ "ContractNonceInfo{contractId=0.0.1, nonce=2}" ] -com.hedera.hashgraph.sdk.ContractNonceInfoTest.fromProtobuf=[ +org.hiero.sdk.ContractNonceInfoTest.fromProtobuf=[ "ContractNonceInfo{contractId=0.0.1, nonce=2}" ] -com.hedera.hashgraph.sdk.ContractNonceInfoTest.toBytes=[ +org.hiero.sdk.ContractNonceInfoTest.toBytes=[ "0a0218011002" ] -com.hedera.hashgraph.sdk.ContractNonceInfoTest.toProtobuf=[ +org.hiero.sdk.ContractNonceInfoTest.toProtobuf=[ { "memoizedHashCode": 0, "memoizedSerializedSize": 2147483647, @@ -44,4 +44,4 @@ com.hedera.hashgraph.sdk.ContractNonceInfoTest.toProtobuf=[ "nonce_": 2, "initialized": true } -] \ No newline at end of file +] diff --git a/sdk/src/test/java/org/hiero/sdk/ContractUpdateTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/ContractUpdateTransactionTest.java new file mode 100644 index 0000000000..5255676894 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ContractUpdateTransactionTest.java @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Duration; +import java.time.Instant; +import java.util.Arrays; +import org.hiero.sdk.proto.ContractUpdateTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class ContractUpdateTransactionTest { + private static final PrivateKey privateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + @Test + void shouldSerialize2() { + SnapshotMatcher.expect(spawnTestTransaction2().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new ContractUpdateTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + private ContractUpdateTransaction spawnTestTransaction() { + return new ContractUpdateTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setContractId(ContractId.fromString("0.0.5007")) + .setAdminKey(privateKey) + .setMaxAutomaticTokenAssociations(101) + .setAutoRenewPeriod(Duration.ofDays(1)) + .setContractMemo("3") + .setStakedAccountId(AccountId.fromString("0.0.3")) + .setExpirationTime(Instant.ofEpochMilli(4)) + .setProxyAccountId(new AccountId(4)) + .setMaxTransactionFee(Hbar.fromTinybars(100_000)) + .setAutoRenewAccountId(new AccountId(30)) + .freeze() + .sign(privateKey); + } + + private ContractUpdateTransaction spawnTestTransaction2() { + return new ContractUpdateTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setContractId(ContractId.fromString("0.0.5007")) + .setAdminKey(privateKey) + .setMaxAutomaticTokenAssociations(101) + .setAutoRenewPeriod(Duration.ofDays(1)) + .setContractMemo("3") + .setStakedNodeId(4L) + .setExpirationTime(Instant.ofEpochMilli(4)) + .setProxyAccountId(new AccountId(4)) + .setMaxTransactionFee(Hbar.fromTinybars(100_000)) + .setAutoRenewAccountId(new AccountId(30)) + .freeze() + .sign(privateKey); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = ContractUpdateTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldBytes2() throws Exception { + var tx = spawnTestTransaction2(); + var tx2 = ContractUpdateTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setContractUpdateInstance( + ContractUpdateTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(ContractUpdateTransaction.class); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/ContractUpdateTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/ContractUpdateTransactionTest.snap new file mode 100644 index 0000000000..fb7908b95d --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ContractUpdateTransactionTest.snap @@ -0,0 +1,8 @@ +org.hiero.sdk.ContractUpdateTransactionTest.shouldSerialize2=[ + "# org.hiero.sdk.proto.TransactionBody\ncontract_update_instance {\n admin_key {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n auto_renew_account_id {\n account_num: 30\n realm_num: 0\n shard_num: 0\n }\n auto_renew_period {\n seconds: 86400\n }\n contract_i_d {\n contract_num: 5007\n realm_num: 0\n shard_num: 0\n }\n expiration_time {\n nanos: 4000000\n seconds: 0\n }\n max_automatic_token_associations {\n value: 101\n }\n memo_wrapper {\n value: \"3\"\n }\n proxy_account_i_d {\n account_num: 4\n realm_num: 0\n shard_num: 0\n }\n staked_node_id: 4\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] + + +org.hiero.sdk.ContractUpdateTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\ncontract_update_instance {\n admin_key {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n auto_renew_account_id {\n account_num: 30\n realm_num: 0\n shard_num: 0\n }\n auto_renew_period {\n seconds: 86400\n }\n contract_i_d {\n contract_num: 5007\n realm_num: 0\n shard_num: 0\n }\n expiration_time {\n nanos: 4000000\n seconds: 0\n }\n max_automatic_token_associations {\n value: 101\n }\n memo_wrapper {\n value: \"3\"\n }\n proxy_account_i_d {\n account_num: 4\n realm_num: 0\n shard_num: 0\n }\n staked_account_id {\n account_num: 3\n realm_num: 0\n shard_num: 0\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/CryptoTransferTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/CryptoTransferTransactionTest.java new file mode 100644 index 0000000000..404dea8dbb --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/CryptoTransferTransactionTest.java @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import org.hiero.sdk.proto.CryptoTransferTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TransactionList; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class CryptoTransferTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new TransferTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + private TransferTransaction spawnTestTransaction() { + return new TransferTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .addHbarTransfer(AccountId.fromString("0.0.5008"), Hbar.fromTinybars(400)) + .addHbarTransfer( + AccountId.fromString("0.0.5006"), Hbar.fromTinybars(800).negated()) + .addHbarTransfer(AccountId.fromString("0.0.5007"), Hbar.fromTinybars(400)) + .addTokenTransfer(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.5008"), 400) + .addTokenTransferWithDecimals(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.5006"), -800, 3) + .addTokenTransferWithDecimals(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.5007"), 400, 3) + .addTokenTransfer(TokenId.fromString("0.0.4"), AccountId.fromString("0.0.5008"), 1) + .addTokenTransfer(TokenId.fromString("0.0.4"), AccountId.fromString("0.0.5006"), -1) + .addNftTransfer( + TokenId.fromString("0.0.3").nft(2), + AccountId.fromString("0.0.5008"), + AccountId.fromString("0.0.5007")) + .addNftTransfer( + TokenId.fromString("0.0.3").nft(1), + AccountId.fromString("0.0.5008"), + AccountId.fromString("0.0.5007")) + .addNftTransfer( + TokenId.fromString("0.0.3").nft(3), + AccountId.fromString("0.0.5008"), + AccountId.fromString("0.0.5006")) + .addNftTransfer( + TokenId.fromString("0.0.3").nft(4), + AccountId.fromString("0.0.5007"), + AccountId.fromString("0.0.5006")) + .addNftTransfer( + TokenId.fromString("0.0.2").nft(4), + AccountId.fromString("0.0.5007"), + AccountId.fromString("0.0.5006")) + .setHbarTransferApproval(AccountId.fromString("0.0.5007"), true) + .setTokenTransferApproval(TokenId.fromString("0.0.4"), AccountId.fromString("0.0.5006"), true) + .setNftTransferApproval(new NftId(TokenId.fromString("0.0.4"), 4), true) + .setMaxTransactionFee(Hbar.fromTinybars(100_000)) + .freeze() + .sign(unusedPrivateKey); + } + + private TransferTransaction spawnModifiedTestTransaction() { + return new TransferTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .addHbarTransfer(AccountId.fromString("0.0.5008"), Hbar.fromTinybars(400)) + .addHbarTransfer( + AccountId.fromString("0.0.5006"), Hbar.fromTinybars(800).negated()) + .addHbarTransfer(AccountId.fromString("0.0.5007"), Hbar.fromTinybars(400)) + .addTokenTransfer(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.5008"), 400) + .addTokenTransferWithDecimals(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.5006"), -800, 3) + .addTokenTransferWithDecimals(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.5007"), 400, 3) + .addTokenTransfer(TokenId.fromString("0.0.4"), AccountId.fromString("0.0.5008"), 1) + .addTokenTransfer(TokenId.fromString("0.0.4"), AccountId.fromString("0.0.5006"), -1) + .addNftTransfer( + TokenId.fromString("0.0.3").nft(2), + AccountId.fromString("0.0.5008"), + AccountId.fromString("0.0.5007")) + .addNftTransfer( + TokenId.fromString("0.0.3").nft(1), + AccountId.fromString("0.0.5008"), + AccountId.fromString("0.0.5007")) + .addNftTransfer( + TokenId.fromString("0.0.3").nft(3), + AccountId.fromString("0.0.5008"), + AccountId.fromString("0.0.5006")) + .addNftTransfer( + TokenId.fromString("0.0.3").nft(4), + AccountId.fromString("0.0.5007"), + AccountId.fromString("0.0.5006")) + .addNftTransfer( + TokenId.fromString("0.0.2").nft(4), + AccountId.fromString("0.0.5007"), + AccountId.fromString("0.0.5006")) + .setHbarTransferApproval(AccountId.fromString("0.0.5007"), true) + // !!! .setTokenTransferApproval(TokenId.fromString("0.0.4"), AccountId.fromString("0.0.5006"), true) + // !!! + .setNftTransferApproval(new NftId(TokenId.fromString("0.0.4"), 4), true) + .setMaxTransactionFee(Hbar.fromTinybars(100_000)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = TransferTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void decimalsMustBeConsistent() { + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { + new TransferTransaction() + .addTokenTransferWithDecimals(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.8"), 100, 2) + .addTokenTransferWithDecimals(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.7"), -100, 3); + }); + } + + @Test + void canGetDecimals() { + var tx = new TransferTransaction(); + assertThat(tx.getTokenIdDecimals().get(TokenId.fromString("0.0.5"))).isNull(); + tx.addTokenTransfer(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.8"), 100); + assertThat(tx.getTokenIdDecimals().get(TokenId.fromString("0.0.5"))).isNull(); + tx.addTokenTransferWithDecimals(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.7"), -100, 5); + assertThat(tx.getTokenIdDecimals().get(TokenId.fromString("0.0.5"))).isEqualTo(5); + } + + @Test + void transactionBodiesMustMatch() throws InvalidProtocolBufferException { + org.hiero.sdk.proto.Transaction tx1 = + TransactionList.parseFrom(spawnTestTransaction().toBytes()).getTransactionList(0); + org.hiero.sdk.proto.Transaction tx2 = TransactionList.parseFrom( + spawnModifiedTestTransaction().toBytes()) + .getTransactionList(1); + var brokenTxList = TransactionList.newBuilder().addTransactionList(tx1).addTransactionList(tx2); + var brokenTxBytes = brokenTxList.build().toByteArray(); + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { + Transaction.fromBytes(brokenTxBytes); + }); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setCryptoTransfer(CryptoTransferTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(TransferTransaction.class); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/CryptoTransferTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/CryptoTransferTransactionTest.snap new file mode 100644 index 0000000000..0a53112e44 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/CryptoTransferTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.CryptoTransferTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\ncrypto_transfer {\n token_transfers {\n nft_transfers {\n receiver_account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n sender_account_i_d {\n account_num: 5007\n realm_num: 0\n shard_num: 0\n }\n serial_number: 4\n }\n token {\n realm_num: 0\n shard_num: 0\n token_num: 2\n }\n }\n token_transfers {\n nft_transfers {\n receiver_account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n sender_account_i_d {\n account_num: 5007\n realm_num: 0\n shard_num: 0\n }\n serial_number: 4\n }\n nft_transfers {\n receiver_account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n sender_account_i_d {\n account_num: 5008\n realm_num: 0\n shard_num: 0\n }\n serial_number: 3\n }\n nft_transfers {\n receiver_account_i_d {\n account_num: 5007\n realm_num: 0\n shard_num: 0\n }\n sender_account_i_d {\n account_num: 5008\n realm_num: 0\n shard_num: 0\n }\n serial_number: 1\n }\n nft_transfers {\n receiver_account_i_d {\n account_num: 5007\n realm_num: 0\n shard_num: 0\n }\n sender_account_i_d {\n account_num: 5008\n realm_num: 0\n shard_num: 0\n }\n serial_number: 2\n }\n token {\n realm_num: 0\n shard_num: 0\n token_num: 3\n }\n }\n token_transfers {\n token {\n realm_num: 0\n shard_num: 0\n token_num: 4\n }\n transfers {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n amount: -1\n is_approval: true\n }\n transfers {\n account_i_d {\n account_num: 5008\n realm_num: 0\n shard_num: 0\n }\n amount: 1\n }\n }\n token_transfers {\n expected_decimals {\n value: 3\n }\n token {\n realm_num: 0\n shard_num: 0\n token_num: 5\n }\n transfers {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n amount: -800\n }\n transfers {\n account_i_d {\n account_num: 5007\n realm_num: 0\n shard_num: 0\n }\n amount: 400\n }\n transfers {\n account_i_d {\n account_num: 5008\n realm_num: 0\n shard_num: 0\n }\n amount: 400\n }\n }\n transfers {\n account_amounts {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n amount: -800\n }\n account_amounts {\n account_i_d {\n account_num: 5007\n realm_num: 0\n shard_num: 0\n }\n amount: 400\n is_approval: true\n }\n account_amounts {\n account_i_d {\n account_num: 5008\n realm_num: 0\n shard_num: 0\n }\n amount: 400\n }\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/CustomFeeListTest.java b/sdk/src/test/java/org/hiero/sdk/CustomFeeListTest.java new file mode 100644 index 0000000000..cd694d00d7 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/CustomFeeListTest.java @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class CustomFeeListTest { + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + private static List spawnCustomFeeListExample() { + var returnList = new ArrayList(); + returnList.add(new CustomFixedFee() + .setFeeCollectorAccountId(new AccountId(4322)) + .setDenominatingTokenId(new TokenId(483902)) + .setAmount(10)); + returnList.add(new CustomFractionalFee() + .setFeeCollectorAccountId(new AccountId(389042)) + .setNumerator(3) + .setDenominator(7) + .setMin(3) + .setMax(100)); + returnList.add(new CustomRoyaltyFee() + .setFeeCollectorAccountId(new AccountId(23423)) + .setNumerator(5) + .setDenominator(8) + .setFallbackFee(new CustomFixedFee() + .setDenominatingTokenId(new TokenId(483902)) + .setAmount(10))); + return returnList; + } + + @Test + void shouldSerialize() throws Exception { + var originalCustomFeeList = spawnCustomFeeListExample(); + byte[] customFee0Bytes = originalCustomFeeList.get(0).toBytes(); + byte[] customFee1Bytes = originalCustomFeeList.get(1).toBytes(); + byte[] customFee2Bytes = originalCustomFeeList.get(2).toBytes(); + var copyCustomFeeList = new ArrayList(); + copyCustomFeeList.add(CustomFee.fromBytes(customFee0Bytes)); + copyCustomFeeList.add(CustomFee.fromBytes(customFee1Bytes)); + copyCustomFeeList.add(CustomFee.fromBytes(customFee2Bytes)); + assertThat(originalCustomFeeList.toString()).isEqualTo(copyCustomFeeList.toString()); + SnapshotMatcher.expect(originalCustomFeeList.toString()).toMatchSnapshot(); + } + + @Test + void deepClone() throws Exception { + var originalCustomFeeList = spawnCustomFeeListExample(); + var copyCustomFeeList = new ArrayList(); + for (var fee : originalCustomFeeList) { + copyCustomFeeList.add(fee.deepClone()); + } + var originalCustomFeeListString = originalCustomFeeList.toString(); + assertThat(originalCustomFeeListString).isEqualTo(copyCustomFeeList.toString()); + + // modifying clone doesn't affect original + ((CustomFixedFee) copyCustomFeeList.get(0)).setDenominatingTokenId(new TokenId(89803)); + assertThat(originalCustomFeeListString).isEqualTo(originalCustomFeeList.toString()); + + SnapshotMatcher.expect(originalCustomFeeList.toString()).toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/CustomFeeListTest.snap b/sdk/src/test/java/org/hiero/sdk/CustomFeeListTest.snap similarity index 89% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/CustomFeeListTest.snap rename to sdk/src/test/java/org/hiero/sdk/CustomFeeListTest.snap index aa95cdebb9..26973ac3ae 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/CustomFeeListTest.snap +++ b/sdk/src/test/java/org/hiero/sdk/CustomFeeListTest.snap @@ -1,8 +1,8 @@ -com.hedera.hashgraph.sdk.CustomFeeListTest.deepClone=[ +org.hiero.sdk.CustomFeeListTest.deepClone=[ "[CustomFixedFee{feeCollectorAccountId=0.0.4322, allCollectorsAreExempt=false, amount=10, demoninatingTokenId=0.0.483902}, CustomFractionalFee{feeCollectorAccountId=0.0.389042, allCollectorsAreExempt=false, numerator=3, denominator=7, min=3, max=100, assessmentMethod=INCLUSIVE}, CustomRoyaltyFee{feeCollectorAccountId=0.0.23423, allCollectorsAreExempt=false, numerator=5, denominator=8, fallbackFee=CustomFixedFee{feeCollectorAccountId=null, allCollectorsAreExempt=false, amount=10, demoninatingTokenId=0.0.483902}}]" ] -com.hedera.hashgraph.sdk.CustomFeeListTest.shouldSerialize=[ +org.hiero.sdk.CustomFeeListTest.shouldSerialize=[ "[CustomFixedFee{feeCollectorAccountId=0.0.4322, allCollectorsAreExempt=false, amount=10, demoninatingTokenId=0.0.483902}, CustomFractionalFee{feeCollectorAccountId=0.0.389042, allCollectorsAreExempt=false, numerator=3, denominator=7, min=3, max=100, assessmentMethod=INCLUSIVE}, CustomRoyaltyFee{feeCollectorAccountId=0.0.23423, allCollectorsAreExempt=false, numerator=5, denominator=8, fallbackFee=CustomFixedFee{feeCollectorAccountId=null, allCollectorsAreExempt=false, amount=10, demoninatingTokenId=0.0.483902}}]" -] \ No newline at end of file +] diff --git a/sdk/src/test/java/org/hiero/sdk/CustomFixedFeeTest.java b/sdk/src/test/java/org/hiero/sdk/CustomFixedFeeTest.java new file mode 100644 index 0000000000..757d945779 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/CustomFixedFeeTest.java @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import org.hiero.sdk.proto.FixedFee; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class CustomFixedFeeTest { + private static final boolean allCollectorsAreExempt = true; + private static final AccountId feeCollectorAccountId = new AccountId(1, 2, 3); + private static final long amount = 4; + private static final TokenId tokenId = new TokenId(5, 6, 7); + + private final FixedFee fee = FixedFee.newBuilder() + .setAmount(amount) + .setDenominatingTokenId(tokenId.toProtobuf()) + .build(); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void fromProtobuf() { + SnapshotMatcher.expect(CustomFixedFee.fromProtobuf(fee).toString()).toMatchSnapshot(); + } + + @Test + void deepCloneSubclass() { + var customFixedFee = new CustomFixedFee() + .setFeeCollectorAccountId(feeCollectorAccountId) + .setAllCollectorsAreExempt(allCollectorsAreExempt); + var clonedCustomFixedFee = customFixedFee.deepCloneSubclass(); + + assertThat(clonedCustomFixedFee.getFeeCollectorAccountId()).isEqualTo(feeCollectorAccountId); + assertThat(clonedCustomFixedFee.getAllCollectorsAreExempt()).isEqualTo(allCollectorsAreExempt); + } + + @Test + void toProtobuf() { + SnapshotMatcher.expect(CustomFixedFee.fromProtobuf(fee).toProtobuf().toString()) + .toMatchSnapshot(); + } + + @Test + void toFixedFeeProtobuf() { + SnapshotMatcher.expect( + CustomFixedFee.fromProtobuf(fee).toFixedFeeProtobuf().toString()) + .toMatchSnapshot(); + } + + @Test + void getSetAmount() { + final var customFixedFee1 = new CustomFixedFee().setAmount(amount); + final var customFixedFee2 = new CustomFixedFee().setHbarAmount(Hbar.fromTinybars(amount)); + + assertThat(customFixedFee1.getAmount()).isEqualTo(amount); + assertThat(customFixedFee2.getHbarAmount().toTinybars()).isEqualTo(amount); + assertThat(customFixedFee1.getHbarAmount().toTinybars()).isEqualTo(customFixedFee2.getAmount()); + } + + @Test + void getSetDenominatingToken() { + final var customFixedFee = new CustomFixedFee().setDenominatingTokenId(tokenId); + assertThat(customFixedFee.getDenominatingTokenId()).isEqualTo(tokenId); + } + + @Test + void setSentinelValueToken() { + final var customFixedFee = new CustomFixedFee().setDenominatingTokenToSameToken(); + assertThat(customFixedFee.getDenominatingTokenId()).isEqualTo(new TokenId(0, 0, 0)); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/CustomFixedFeeTest.snap b/sdk/src/test/java/org/hiero/sdk/CustomFixedFeeTest.snap new file mode 100644 index 0000000000..5a45bd9ea9 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/CustomFixedFeeTest.snap @@ -0,0 +1,13 @@ +org.hiero.sdk.CustomFixedFeeTest.fromProtobuf=[ + "CustomFixedFee{feeCollectorAccountId=null, allCollectorsAreExempt=false, amount=4, demoninatingTokenId=5.6.7}" +] + + +org.hiero.sdk.CustomFixedFeeTest.toFixedFeeProtobuf=[ + "# org.hiero.sdk.proto.FixedFee@409d860\namount: 4\ndenominating_token_id {\n realm_num: 6\n shard_num: 5\n token_num: 7\n}" +] + + +org.hiero.sdk.CustomFixedFeeTest.toProtobuf=[ + "# org.hiero.sdk.proto.CustomFee@91885f65\nfixed_fee {\n amount: 4\n denominating_token_id {\n realm_num: 6\n shard_num: 5\n token_num: 7\n }\n}" +] diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/CustomFractionalFeeTest.java b/sdk/src/test/java/org/hiero/sdk/CustomFractionalFeeTest.java similarity index 79% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/CustomFractionalFeeTest.java rename to sdk/src/test/java/org/hiero/sdk/CustomFractionalFeeTest.java index 0771c91ccf..277c56ae59 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/CustomFractionalFeeTest.java +++ b/sdk/src/test/java/org/hiero/sdk/CustomFractionalFeeTest.java @@ -1,14 +1,15 @@ -package com.hedera.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; -import com.hedera.hashgraph.sdk.proto.Fraction; -import com.hedera.hashgraph.sdk.proto.FractionalFee; import io.github.jsonSnapshot.SnapshotMatcher; +import org.hiero.sdk.proto.Fraction; +import org.hiero.sdk.proto.FractionalFee; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import static org.assertj.core.api.Assertions.assertThat; - public class CustomFractionalFeeTest { private static final boolean allCollectorsAreExempt = true; private static final AccountId feeCollectorAccountId = new AccountId(1, 2, 3); @@ -19,11 +20,11 @@ public class CustomFractionalFeeTest { private static final FeeAssessmentMethod feeAssessmentMethod = FeeAssessmentMethod.EXCLUSIVE; private final FractionalFee fee = FractionalFee.newBuilder() - .setFractionalAmount(Fraction.newBuilder().setNumerator(numerator).setDenominator(denominator)) - .setMinimumAmount(minAmount) - .setMaximumAmount(maxAmount) - .setNetOfTransfers(true) - .build(); + .setFractionalAmount(Fraction.newBuilder().setNumerator(numerator).setDenominator(denominator)) + .setMinimumAmount(minAmount) + .setMaximumAmount(maxAmount) + .setNetOfTransfers(true) + .build(); @BeforeAll public static void beforeAll() { @@ -43,8 +44,8 @@ void fromProtobuf() { @Test void deepCloneSubclass() { var customFractionalFee = new CustomFractionalFee() - .setFeeCollectorAccountId(feeCollectorAccountId) - .setAllCollectorsAreExempt(allCollectorsAreExempt); + .setFeeCollectorAccountId(feeCollectorAccountId) + .setAllCollectorsAreExempt(allCollectorsAreExempt); var clonedCustomFractionalFee = customFractionalFee.deepCloneSubclass(); assertThat(clonedCustomFractionalFee.getFeeCollectorAccountId()).isEqualTo(feeCollectorAccountId); @@ -53,7 +54,9 @@ void deepCloneSubclass() { @Test void toProtobuf() { - SnapshotMatcher.expect(CustomFractionalFee.fromProtobuf(fee).toProtobuf().toString()).toMatchSnapshot(); + SnapshotMatcher.expect( + CustomFractionalFee.fromProtobuf(fee).toProtobuf().toString()) + .toMatchSnapshot(); } @Test diff --git a/sdk/src/test/java/org/hiero/sdk/CustomFractionalFeeTest.snap b/sdk/src/test/java/org/hiero/sdk/CustomFractionalFeeTest.snap new file mode 100644 index 0000000000..e84d7a2565 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/CustomFractionalFeeTest.snap @@ -0,0 +1,8 @@ +org.hiero.sdk.CustomFractionalFeeTest.fromProtobuf=[ + "CustomFractionalFee{feeCollectorAccountId=null, allCollectorsAreExempt=false, numerator=4, denominator=5, min=6, max=7, assessmentMethod=EXCLUSIVE}" +] + + +org.hiero.sdk.CustomFractionalFeeTest.toProtobuf=[ + "# org.hiero.sdk.proto.CustomFee@b2abef2c\nfractional_fee {\n fractional_amount {\n denominator: 5\n numerator: 4\n }\n maximum_amount: 7\n minimum_amount: 6\n net_of_transfers: true\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/CustomRoyaltyFeeTest.java b/sdk/src/test/java/org/hiero/sdk/CustomRoyaltyFeeTest.java new file mode 100644 index 0000000000..0f65ad0202 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/CustomRoyaltyFeeTest.java @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import org.hiero.sdk.proto.FixedFee; +import org.hiero.sdk.proto.Fraction; +import org.hiero.sdk.proto.RoyaltyFee; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class CustomRoyaltyFeeTest { + private static final boolean allCollectorsAreExempt = true; + private static final AccountId feeCollectorAccountId = new AccountId(1, 2, 3); + private static final int numerator = 4; + private static final int denominator = 5; + private static final CustomFixedFee fallbackFee = new CustomFixedFee().setAmount(6); + + private final RoyaltyFee fee = RoyaltyFee.newBuilder() + .setExchangeValueFraction( + Fraction.newBuilder().setNumerator(numerator).setDenominator(denominator)) + .setFallbackFee(FixedFee.newBuilder().setAmount(6).build()) + .build(); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void fromProtobuf() { + SnapshotMatcher.expect(CustomRoyaltyFee.fromProtobuf(fee).toString()).toMatchSnapshot(); + } + + @Test + void deepCloneSubclass() { + var customRoyaltyFee = new CustomRoyaltyFee() + .setFeeCollectorAccountId(feeCollectorAccountId) + .setAllCollectorsAreExempt(allCollectorsAreExempt); + var clonedCustomRoyaltyFee = customRoyaltyFee.deepCloneSubclass(); + + assertThat(clonedCustomRoyaltyFee.getFeeCollectorAccountId()).isEqualTo(feeCollectorAccountId); + assertThat(clonedCustomRoyaltyFee.getAllCollectorsAreExempt()).isEqualTo(allCollectorsAreExempt); + } + + @Test + void toProtobuf() { + SnapshotMatcher.expect(CustomRoyaltyFee.fromProtobuf(fee).toProtobuf().toString()) + .toMatchSnapshot(); + } + + @Test + void getSetNumerator() { + final var customRoyaltyFee = new CustomRoyaltyFee().setNumerator(numerator); + assertThat(customRoyaltyFee.getNumerator()).isEqualTo(numerator); + } + + @Test + void getSetDenominator() { + final var customRoyaltyFee = new CustomRoyaltyFee().setDenominator(denominator); + assertThat(customRoyaltyFee.getDenominator()).isEqualTo(denominator); + } + + @Test + void getSetFallbackFee() { + final var customRoyaltyFee = new CustomRoyaltyFee().setFallbackFee(fallbackFee); + assertThat(customRoyaltyFee.getFallbackFee()).isNotNull(); + assertThat(customRoyaltyFee.getFallbackFee().getAmount()).isEqualTo(fallbackFee.getAmount()); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/CustomRoyaltyFeeTest.snap b/sdk/src/test/java/org/hiero/sdk/CustomRoyaltyFeeTest.snap new file mode 100644 index 0000000000..ba1be741d8 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/CustomRoyaltyFeeTest.snap @@ -0,0 +1,8 @@ +org.hiero.sdk.CustomRoyaltyFeeTest.fromProtobuf=[ + "CustomRoyaltyFee{feeCollectorAccountId=null, allCollectorsAreExempt=false, numerator=4, denominator=5, fallbackFee=CustomFixedFee{feeCollectorAccountId=null, allCollectorsAreExempt=false, amount=6, demoninatingTokenId=null}}" +] + + +org.hiero.sdk.CustomRoyaltyFeeTest.toProtobuf=[ + "# org.hiero.sdk.proto.CustomFee@1abe2492\nroyalty_fee {\n exchange_value_fraction {\n denominator: 5\n numerator: 4\n }\n fallback_fee {\n amount: 6\n }\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/DelegateContractIdTest.java b/sdk/src/test/java/org/hiero/sdk/DelegateContractIdTest.java new file mode 100644 index 0000000000..9cc385cdb9 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/DelegateContractIdTest.java @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.github.jsonSnapshot.SnapshotMatcher; +import org.bouncycastle.util.encoders.Hex; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +class DelegateContractIdTest { + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void fromString() { + SnapshotMatcher.expect(DelegateContractId.fromString("0.0.5005").toString()) + .toMatchSnapshot(); + } + + @Test + void fromSolidityAddress() { + SnapshotMatcher.expect(DelegateContractId.fromSolidityAddress("000000000000000000000000000000000000138D") + .toString()) + .toMatchSnapshot(); + } + + @Test + void fromSolidityAddressWith0x() { + SnapshotMatcher.expect(DelegateContractId.fromSolidityAddress("0x000000000000000000000000000000000000138D") + .toString()) + .toMatchSnapshot(); + } + + @Test + void toBytes() throws InvalidProtocolBufferException { + SnapshotMatcher.expect(Hex.toHexString(new DelegateContractId(5005).toBytes())) + .toMatchSnapshot(); + } + + @Test + void fromBytes() throws InvalidProtocolBufferException { + SnapshotMatcher.expect(DelegateContractId.fromBytes(new DelegateContractId(5005).toBytes()) + .toString()) + .toMatchSnapshot(); + } + + @Test + void toSolidityAddress() { + SnapshotMatcher.expect(new DelegateContractId(5005).toSolidityAddress()).toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/DelegateContractIdTest.snap b/sdk/src/test/java/org/hiero/sdk/DelegateContractIdTest.snap new file mode 100644 index 0000000000..8e5d15a4ae --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/DelegateContractIdTest.snap @@ -0,0 +1,28 @@ +org.hiero.sdk.DelegateContractIdTest.fromBytes=[ + "0.0.5005" +] + + +org.hiero.sdk.DelegateContractIdTest.fromSolidityAddress=[ + "0.0.5005" +] + + +org.hiero.sdk.DelegateContractIdTest.fromSolidityAddressWith0x=[ + "0.0.5005" +] + + +org.hiero.sdk.DelegateContractIdTest.fromString=[ + "0.0.5005" +] + + +org.hiero.sdk.DelegateContractIdTest.toBytes=[ + "188d27" +] + + +org.hiero.sdk.DelegateContractIdTest.toSolidityAddress=[ + "000000000000000000000000000000000000138d" +] diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/DuplicateTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/DuplicateTransactionTest.java similarity index 92% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/DuplicateTransactionTest.java rename to sdk/src/test/java/org/hiero/sdk/DuplicateTransactionTest.java index 0dd799ff27..3e3ff69188 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/DuplicateTransactionTest.java +++ b/sdk/src/test/java/org/hiero/sdk/DuplicateTransactionTest.java @@ -1,11 +1,11 @@ -package com.hedera.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import java.util.HashSet; - -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; public class DuplicateTransactionTest { diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ECDSAPrivateKeyTest.java b/sdk/src/test/java/org/hiero/sdk/ECDSAPrivateKeyTest.java similarity index 84% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/ECDSAPrivateKeyTest.java rename to sdk/src/test/java/org/hiero/sdk/ECDSAPrivateKeyTest.java index efea2a0c93..72e1c40268 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ECDSAPrivateKeyTest.java +++ b/sdk/src/test/java/org/hiero/sdk/ECDSAPrivateKeyTest.java @@ -1,33 +1,14 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; - -import com.hedera.hashgraph.sdk.utils.Bip32Utils; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; import org.bouncycastle.util.encoders.Hex; +import org.hiero.sdk.utils.Bip32Utils; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import java.io.IOException; - -import static org.assertj.core.api.Assertions.assertThat; - public class ECDSAPrivateKeyTest { private static final String TEST_VECTOR_PEM_PASSPHRASE = "asdasd123"; @@ -138,8 +119,7 @@ public static byte[] hexStringToByteArray(String s) { int len = s.length(); byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { - data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) - + Character.digit(s.charAt(i+1), 16)); + data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16)); } return data; } @@ -239,10 +219,12 @@ void slip10TestVector2() { final String PRIVATE_KEY6 = "bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23"; final String PUBLIC_KEY6 = "024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c"; - var seed = hexStringToByteArray("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"); + var seed = hexStringToByteArray( + "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"); // Chain m - PrivateKey key1 = PrivateKey.fromSeedECDSAsecp256k1(seed);; + PrivateKey key1 = PrivateKey.fromSeedECDSAsecp256k1(seed); + ; assertThat(Hex.toHexString(key1.getChainCode().getKey())).isEqualTo(CHAIN_CODE1); assertThat(key1.toStringRaw()).isEqualTo(PRIVATE_KEY1); assertThat(key1.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY1); @@ -282,39 +264,37 @@ void slip10TestVector2() { @DisplayName("PEM import test vectors") void PEMImportTestVectors() throws IOException { // https://github.com/hashgraph/hedera-sdk-reference/issues/93#issue-1665972122 - var PRIVATE_KEY_PEM1 = "-----BEGIN EC PRIVATE KEY-----\n" + - "MHQCAQEEIG8I+jKi+iGVa7ttbfnlnML5AdvPugbgBWnseYjrle6qoAcGBSuBBAAK\n" + - "oUQDQgAEqf5BmMeBzkU1Ra9UAbZJo3tytVOlb7erTc36LRLP20mOLU7+mFY+3Cfe\n" + - "fAZgBtPXRAmDtRvYGODswAalW85GKA==\n" + - "-----END EC PRIVATE KEY-----"; + var PRIVATE_KEY_PEM1 = "-----BEGIN EC PRIVATE KEY-----\n" + + "MHQCAQEEIG8I+jKi+iGVa7ttbfnlnML5AdvPugbgBWnseYjrle6qoAcGBSuBBAAK\n" + + "oUQDQgAEqf5BmMeBzkU1Ra9UAbZJo3tytVOlb7erTc36LRLP20mOLU7+mFY+3Cfe\n" + + "fAZgBtPXRAmDtRvYGODswAalW85GKA==\n" + + "-----END EC PRIVATE KEY-----"; var PRIVATE_KEY1 = "6f08fa32a2fa21956bbb6d6df9e59cc2f901dbcfba06e00569ec7988eb95eeaa"; var PUBLIC_KEY1 = "02a9fe4198c781ce453545af5401b649a37b72b553a56fb7ab4dcdfa2d12cfdb49"; - var PRIVATE_KEY_PEM2 = "-----BEGIN EC PRIVATE KEY-----\n" + - "MFQCAQEEIOHyhclwHbha3f281Kvd884rhBzltxGJxCZyaQCagH9joAcGBSuBBAAK\n" + - "oSQDIgACREr6gFZa4K7hBP+bA25VdgQ+0ABFgM+g5RYw/W6T1Og=\n" + - "-----END EC PRIVATE KEY-----"; + var PRIVATE_KEY_PEM2 = "-----BEGIN EC PRIVATE KEY-----\n" + + "MFQCAQEEIOHyhclwHbha3f281Kvd884rhBzltxGJxCZyaQCagH9joAcGBSuBBAAK\n" + + "oSQDIgACREr6gFZa4K7hBP+bA25VdgQ+0ABFgM+g5RYw/W6T1Og=\n" + + "-----END EC PRIVATE KEY-----"; var PRIVATE_KEY2 = "e1f285c9701db85addfdbcd4abddf3ce2b841ce5b71189c4267269009a807f63"; var PUBLIC_KEY2 = "02444afa80565ae0aee104ff9b036e5576043ed0004580cfa0e51630fd6e93d4e8"; - var PRIVATE_KEY_PEM3 = "-----BEGIN EC PRIVATE KEY-----\n" + - "Proc-Type: 4,ENCRYPTED\n" + - "DEK-Info: AES-128-CBC,0046A9EED8D16F0CAA66A197CE8BE8BD\n" + - "\n" + - "9VU9gReUmrn4XywjMx0F0A3oGzpHIksEXma72TCSdcxI7zHy0mtzuGq4Wd25O38s\n" + - "H9c6kvhTPS1N/c6iNhx154B0HUoND8jvAvfxbGR/R87vpZJsOoKCmRxGqrxG8HER\n" + - "FIHQ1jy16DrAbU95kDyLsiF1dy2vUY/HoqFZwxl/IVc=\n" + - "-----END EC PRIVATE KEY-----"; + var PRIVATE_KEY_PEM3 = "-----BEGIN EC PRIVATE KEY-----\n" + "Proc-Type: 4,ENCRYPTED\n" + + "DEK-Info: AES-128-CBC,0046A9EED8D16F0CAA66A197CE8BE8BD\n" + + "\n" + + "9VU9gReUmrn4XywjMx0F0A3oGzpHIksEXma72TCSdcxI7zHy0mtzuGq4Wd25O38s\n" + + "H9c6kvhTPS1N/c6iNhx154B0HUoND8jvAvfxbGR/R87vpZJsOoKCmRxGqrxG8HER\n" + + "FIHQ1jy16DrAbU95kDyLsiF1dy2vUY/HoqFZwxl/IVc=\n" + + "-----END EC PRIVATE KEY-----"; var PRIVATE_KEY3 = "cf49eb5206c1b0468854d6ea7b370590619625514f71ff93608a18465e4012ad"; var PUBLIC_KEY3 = "025f0d14a7562d6319e5b8f91620d2ce9ad13d9abf21cfe9bd0a092c0f35bf1701"; - var PRIVATE_KEY_PEM4 = "-----BEGIN EC PRIVATE KEY-----\n" + - "Proc-Type: 4,ENCRYPTED\n" + - "DEK-Info: AES-128-CBC,4A9B3B987EC2EFFA405818327D14FFF7\n" + - "\n" + - "Wh756RkK5fn1Ke2denR1OYfqE9Kr4BXhgrEMTU/6o0SNhMULUhWGHrCWvmNeEQwp\n" + - "ZVZYUxgYoTlJBeREzKAZithcvxIcTbQfLABo1NZbjA6YKqAqlGpM6owwL/f9e2ST\n" + - "-----END EC PRIVATE KEY-----"; + var PRIVATE_KEY_PEM4 = "-----BEGIN EC PRIVATE KEY-----\n" + "Proc-Type: 4,ENCRYPTED\n" + + "DEK-Info: AES-128-CBC,4A9B3B987EC2EFFA405818327D14FFF7\n" + + "\n" + + "Wh756RkK5fn1Ke2denR1OYfqE9Kr4BXhgrEMTU/6o0SNhMULUhWGHrCWvmNeEQwp\n" + + "ZVZYUxgYoTlJBeREzKAZithcvxIcTbQfLABo1NZbjA6YKqAqlGpM6owwL/f9e2ST\n" + + "-----END EC PRIVATE KEY-----"; var PRIVATE_KEY4 = "c0d3e16ba5a1abbeac4cd327a3c3c1cc10438431d0bac019054e573e67768bb5"; var PUBLIC_KEY4 = "02065f736378134c53c7a2ee46f199fb93b9b32337be4e95660677046476995544"; @@ -339,19 +319,23 @@ void PEMImportTestVectors() throws IOException { @DisplayName("DER import test vectors") void DERImportTestVectors() { // https://github.com/hashgraph/hedera-sdk-reference/issues/93#issue-1665972122 - var PRIVATE_KEY_DER1 = "3030020100300706052b8104000a042204208c2cdc9575fe67493443967d74958fd7808a3787fd3337e99cfeebbc7566b586"; + var PRIVATE_KEY_DER1 = + "3030020100300706052b8104000a042204208c2cdc9575fe67493443967d74958fd7808a3787fd3337e99cfeebbc7566b586"; var PRIVATE_KEY1 = "8c2cdc9575fe67493443967d74958fd7808a3787fd3337e99cfeebbc7566b586"; var PUBLIC_KEY1 = "028173079d2e996ef6b2d064fc82d5fc7094367211e28422bec50a2f75c365f5fd"; - var PRIVATE_KEY_DER2 = "30540201010420ac318ea8ff8d991ab2f16172b4738e74dc35a56681199cfb1c0cb2e7cb560ffda00706052b8104000aa124032200036843f5cb338bbb4cdb21b0da4ea739d910951d6e8a5f703d313efe31afe788f4"; + var PRIVATE_KEY_DER2 = + "30540201010420ac318ea8ff8d991ab2f16172b4738e74dc35a56681199cfb1c0cb2e7cb560ffda00706052b8104000aa124032200036843f5cb338bbb4cdb21b0da4ea739d910951d6e8a5f703d313efe31afe788f4"; var PRIVATE_KEY2 = "ac318ea8ff8d991ab2f16172b4738e74dc35a56681199cfb1c0cb2e7cb560ffd"; var PUBLIC_KEY2 = "036843f5cb338bbb4cdb21b0da4ea739d910951d6e8a5f703d313efe31afe788f4"; - var PRIVATE_KEY_DER3 = "307402010104208927647ad12b29646a1d051da8453462937bb2c813c6815cac6c0b720526ffc6a00706052b8104000aa14403420004aaac1c3ac1bea0245b8e00ce1e2018f9eab61b6331fbef7266f2287750a6597795f855ddcad2377e22259d1fcb4e0f1d35e8f2056300c15070bcbfce3759cc9d"; + var PRIVATE_KEY_DER3 = + "307402010104208927647ad12b29646a1d051da8453462937bb2c813c6815cac6c0b720526ffc6a00706052b8104000aa14403420004aaac1c3ac1bea0245b8e00ce1e2018f9eab61b6331fbef7266f2287750a6597795f855ddcad2377e22259d1fcb4e0f1d35e8f2056300c15070bcbfce3759cc9d"; var PRIVATE_KEY3 = "8927647ad12b29646a1d051da8453462937bb2c813c6815cac6c0b720526ffc6"; var PUBLIC_KEY3 = "03aaac1c3ac1bea0245b8e00ce1e2018f9eab61b6331fbef7266f2287750a65977"; - var PRIVATE_KEY_DER4 = "302e0201010420a6170a6aa6389a5bd3a3a8f9375f57bd91aa7f7d8b8b46ce0b702e000a21a5fea00706052b8104000a"; + var PRIVATE_KEY_DER4 = + "302e0201010420a6170a6aa6389a5bd3a3a8f9375f57bd91aa7f7d8b8b46ce0b702e000a21a5fea00706052b8104000a"; var PRIVATE_KEY4 = "a6170a6aa6389a5bd3a3a8f9375f57bd91aa7f7d8b8b46ce0b702e000a21a5fe"; var PUBLIC_KEY4 = "03b69a75a5ddb1c0747e995d47555019e5d8a28003ab5202bd92f534361fb4ec8a"; diff --git a/sdk/src/test/java/org/hiero/sdk/ECDSAPublicKeyTest.java b/sdk/src/test/java/org/hiero/sdk/ECDSAPublicKeyTest.java new file mode 100644 index 0000000000..4e763bde06 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ECDSAPublicKeyTest.java @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType; + +import java.math.BigDecimal; +import java.util.Collections; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class ECDSAPublicKeyTest { + @Test + void verifyTransaction() { + var transaction = new TransferTransaction() + .setNodeAccountIds(Collections.singletonList(new AccountId(3))) + .setTransactionId(TransactionId.generate(new AccountId(4))) + .freeze(); + + var key = PrivateKey.fromStringECDSA("8776c6b831a1b61ac10dac0304a2843de4716f54b1919bb91a2685d0fe3f3048"); + key.signTransaction(transaction); + + assertThat(key.getPublicKey().verifyTransaction(transaction)).isTrue(); + } + + @Test + @DisplayName("public key can be recovered from bytes") + void keyByteSerialization() { + PublicKey key1 = PrivateKey.generateECDSA().getPublicKey(); + byte[] key1Bytes = key1.toBytes(); + PublicKey key2 = PublicKey.fromBytes(key1Bytes); + byte[] key2Bytes = key2.toBytes(); + + assertThat(key2Bytes).containsExactly(key1Bytes); + } + + @Test + @DisplayName("public key can be recovered from raw bytes") + void keyByteSerialization2() { + PublicKey key1 = PrivateKey.generateECDSA().getPublicKey(); + byte[] key1Bytes = key1.toBytesRaw(); + PublicKey key2 = PublicKey.fromBytesECDSA(key1Bytes); + byte[] key2Bytes = key2.toBytesRaw(); + // cannot use PrivateKey.fromBytes() to parse raw ECDSA bytes + // because they're indistinguishable from ED25519 raw bytes + + assertThat(key2Bytes).containsExactly(key1Bytes); + } + + @Test + void keyByteValidation() { + byte[] invalidKeyECDSA = new byte[33]; + Assertions.assertDoesNotThrow(() -> PublicKey.fromBytes(invalidKeyECDSA)); + Assertions.assertDoesNotThrow(() -> PublicKey.fromBytesECDSA(invalidKeyECDSA)); + + byte[] invalidCompressedKey = new byte[] { + 0x00, + (byte) 0xca, + (byte) 0x35, + 0x4b, + 0x7c, + (byte) 0xf4, + (byte) 0x87, + (byte) 0xd1, + (byte) 0xbc, + 0x43, + 0x5a, + 0x25, + 0x66, + 0x77, + 0x09, + (byte) 0xc1, + (byte) 0xab, + (byte) 0x98, + 0x0c, + 0x11, + 0x4d, + 0x35, + (byte) 0x94, + (byte) 0xe6, + 0x25, + (byte) 0x9e, + (byte) 0x81, + 0x2e, + 0x6a, + 0x70, + 0x3d, + 0x4f, + 0x51 + }; + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> PublicKey.fromBytesECDSA(invalidCompressedKey)); + + byte[] malformedKey = new byte[] {0x00, 0x01, 0x02}; + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> PublicKey.fromBytesECDSA(malformedKey)); + + byte[] validCompressedKey = new byte[] { + 0x02, // Prefix for compressed keys + (byte) 0xca, + (byte) 0x35, + 0x4b, + 0x7c, + (byte) 0xf4, + (byte) 0x87, + (byte) 0xd1, + (byte) 0xbc, + 0x43, + 0x5a, + 0x25, + 0x66, + 0x77, + 0x09, + (byte) 0xc1, + (byte) 0xab, + (byte) 0x98, + 0x0c, + 0x1f, + 0x4d, + 0x35, + (byte) 0x94, + (byte) 0xe6, + 0x25, + (byte) 0x9e, + (byte) 0x81, + 0x2e, + 0x6a, + 0x75, + 0x3d, + 0x4f, + 0x59 + }; + Assertions.assertDoesNotThrow(() -> PublicKey.fromBytesECDSA(validCompressedKey)); + + byte[] validDERKey = PrivateKey.generateECDSA().getPublicKey().toBytesDER(); + Assertions.assertDoesNotThrow(() -> PublicKey.fromBytesECDSA(validDERKey)); + } + + @Test + @DisplayName("public key can be recovered from DER bytes") + void keyByteSerialization3() { + PublicKey key1 = PrivateKey.generateECDSA().getPublicKey(); + byte[] key1Bytes = key1.toBytesDER(); + PublicKey key2 = PublicKey.fromBytesDER(key1Bytes); + byte[] key2Bytes = key2.toBytesDER(); + PublicKey key3 = PublicKey.fromBytes(key1Bytes); + byte[] key3Bytes = key3.toBytesDER(); + + assertThat(key2Bytes).containsExactly(key1Bytes); + assertThat(key3Bytes).isEqualTo(key1Bytes); + } + + @Test + @DisplayName("public key can be recovered after transaction serialization") + void keyByteSerializationThroughTransaction() { + var senderAccount = AccountId.fromString("0.0.1337"); + var receiverAccount = AccountId.fromString("0.0.3"); + var transferAmount = Hbar.from(new BigDecimal("0.0001"), HbarUnit.HBAR); + var privateKey = PrivateKey.generateECDSA(); + var client = Client.forTestnet().setOperator(senderAccount, privateKey); + var tx = new TransferTransaction() + .addHbarTransfer(senderAccount, transferAmount.negated()) + .addHbarTransfer(receiverAccount, transferAmount); + + tx.freezeWith(client); + tx.signWithOperator(client); + + var bytes = tx.toBytes(); + + assertThatNoException().isThrownBy(() -> Transaction.fromBytes(bytes)); + assertThat(tx.getSignatures()).isNotEmpty(); + } + + @Test + @DisplayName("public key can be recovered from string") + void keyStringSerialization() { + PublicKey key1 = PrivateKey.generateECDSA().getPublicKey(); + String key1Str = key1.toString(); + PublicKey key2 = PublicKey.fromString(key1Str); + String key2Str = key2.toString(); + PublicKey key3 = PublicKey.fromString(key1Str); + String key3Str = key3.toString(); + + assertThat(key3.getClass()).isEqualTo(PublicKeyECDSA.class); + assertThat(key2Str).isEqualTo(key1Str); + assertThat(key3Str).isEqualTo(key1Str); + } + + @Test + @DisplayName("public key can be recovered from raw string") + void keyStringSerialization2() { + PublicKey key1 = PrivateKey.generateECDSA().getPublicKey(); + String key1Str = key1.toStringRaw(); + PublicKey key2 = PublicKey.fromStringECDSA(key1Str); + String key2Str = key2.toStringRaw(); + PublicKey key3 = PublicKey.fromStringECDSA(key2Str); + String key3Str = key3.toStringRaw(); + // cannot use PublicKey.fromString() to parse raw ECDSA string + // because it's indistinguishable from ED25519 raw bytes + + assertThat(key3.getClass()).isEqualTo(PublicKeyECDSA.class); + assertThat(key2Str).isEqualTo(key1Str); + assertThat(key3Str).isEqualTo(key1Str); + } + + @Test + @DisplayName("public key can be recovered from DER string") + void keyStringSerialization3() { + PublicKey key1 = PrivateKey.generateECDSA().getPublicKey(); + String key1Str = key1.toStringDER(); + PublicKey key2 = PublicKey.fromStringDER(key1Str); + String key2Str = key2.toStringDER(); + PublicKey key3 = PublicKey.fromString(key1Str); + String key3Str = key3.toStringDER(); + + assertThat(key3.getClass()).isEqualTo(PublicKeyECDSA.class); + assertThat(key2Str).isEqualTo(key1Str); + assertThat(key3Str).isEqualTo(key1Str); + } + + @Test + @DisplayName("public key is is ECDSA") + void keyIsECDSA() { + PublicKey key = PrivateKey.generateECDSA().getPublicKey(); + + assertThat(key.isECDSA()).isTrue(); + } + + @Test + @DisplayName("public key is is not Ed25519") + void keyIsNotEd25519() { + PublicKey key = PrivateKey.generateECDSA().getPublicKey(); + + assertThat(key.isED25519()).isFalse(); + } + + @Test + @DisplayName("to EVM address") + void toEvmAddress() { + // Generated by https://www.rfctools.com/ethereum-address-test-tool/ + String privateKeyString = "DEBAE3CA62AB3157110DBA79C8DE26540DC320EE9BE73A77D70BA175643A3500"; + String expectedEvmAddress = "d8eb8db03c699faa3f47adcdcd2ae91773b10f8b"; + + PrivateKey privateKey = PrivateKey.fromStringECDSA(privateKeyString); + PublicKey key = privateKey.getPublicKey(); + + assertThat(key.toEvmAddress()).hasToString(expectedEvmAddress); + } + + @Test + @DisplayName("DER import test vectors") + void DERImportTestVectors() { + // https://github.com/hashgraph/hedera-sdk-reference/issues/93#issue-1665972122 + var PUBLIC_KEY_DER1 = + "302d300706052b8104000a032200028173079d2e996ef6b2d064fc82d5fc7094367211e28422bec50a2f75c365f5fd"; + var PUBLIC_KEY1 = "028173079d2e996ef6b2d064fc82d5fc7094367211e28422bec50a2f75c365f5fd"; + + var PUBLIC_KEY_DER2 = + "3036301006072a8648ce3d020106052b8104000a032200036843f5cb338bbb4cdb21b0da4ea739d910951d6e8a5f703d313efe31afe788f4"; + var PUBLIC_KEY2 = "036843f5cb338bbb4cdb21b0da4ea739d910951d6e8a5f703d313efe31afe788f4"; + + var PUBLIC_KEY_DER3 = + "3056301006072a8648ce3d020106052b8104000a03420004aaac1c3ac1bea0245b8e00ce1e2018f9eab61b6331fbef7266f2287750a6597795f855ddcad2377e22259d1fcb4e0f1d35e8f2056300c15070bcbfce3759cc9d"; + var PUBLIC_KEY3 = "03aaac1c3ac1bea0245b8e00ce1e2018f9eab61b6331fbef7266f2287750a65977"; + + var ecdsaPublicKey1 = PublicKey.fromStringDER(PUBLIC_KEY_DER1); + assertThat(ecdsaPublicKey1.toStringRaw()).isEqualTo(PUBLIC_KEY1); + + var ecdsaPublicKey2 = PublicKey.fromStringDER(PUBLIC_KEY_DER2); + assertThat(ecdsaPublicKey2.toStringRaw()).isEqualTo(PUBLIC_KEY2); + + var ecdsaPublicKey3 = PublicKey.fromStringDER(PUBLIC_KEY_DER3); + assertThat(ecdsaPublicKey3.toStringRaw()).isEqualTo(PUBLIC_KEY3); + } +} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/Ed25519PrivateKeyTest.java b/sdk/src/test/java/org/hiero/sdk/Ed25519PrivateKeyTest.java similarity index 77% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/Ed25519PrivateKeyTest.java rename to sdk/src/test/java/org/hiero/sdk/Ed25519PrivateKeyTest.java index 24eec4734f..d82a2fe4dc 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/Ed25519PrivateKeyTest.java +++ b/sdk/src/test/java/org/hiero/sdk/Ed25519PrivateKeyTest.java @@ -1,24 +1,14 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.io.IOException; +import java.io.StringReader; +import java.nio.charset.StandardCharsets; +import java.util.stream.Stream; +import org.assertj.core.api.Assertions; import org.bouncycastle.math.ec.rfc8032.Ed25519; import org.bouncycastle.util.encoders.Hex; import org.junit.jupiter.api.DisplayName; @@ -26,69 +16,69 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import java.io.IOException; -import java.io.StringReader; -import java.nio.charset.StandardCharsets; -import java.util.stream.Stream; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - class Ed25519PrivateKeyTest { - private static final String TEST_KEY_STR = "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"; + private static final String TEST_KEY_STR = + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"; private static final String TEST_KEY_STR_RAW = "db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"; private static final String TEST_KEY_PEM = "-----BEGIN PRIVATE KEY-----\n" - + "MC4CAQAwBQYDK2VwBCIEINtIS4KOZLLY8SzjwKDpOguMznrxu485yXcyOUSCU44Q\n" - + "-----END PRIVATE KEY-----\n"; + + "MC4CAQAwBQYDK2VwBCIEINtIS4KOZLLY8SzjwKDpOguMznrxu485yXcyOUSCU44Q\n" + + "-----END PRIVATE KEY-----\n"; // generated by hedera-sdk-js, not used anywhere - private static final String MNEMONIC_STRING = "inmate flip alley wear offer often piece magnet surge toddler submit right radio absent pear floor belt raven price stove replace reduce plate home"; - private static final String MNEMONIC_PRIVATE_KEY = "302e020100300506032b657004220420853f15aecd22706b105da1d709b4ac05b4906170c2b9c7495dff9af49e1391da"; + private static final String MNEMONIC_STRING = + "inmate flip alley wear offer often piece magnet surge toddler submit right radio absent pear floor belt raven price stove replace reduce plate home"; + private static final String MNEMONIC_PRIVATE_KEY = + "302e020100300506032b657004220420853f15aecd22706b105da1d709b4ac05b4906170c2b9c7495dff9af49e1391da"; - private static final String MNEMONIC_LEGACY_STRING = "jolly kidnap tom lawn drunk chick optic lust mutter mole bride galley dense member sage neural widow decide curb aboard margin manure"; - private static final String MNEMONIC_LEGACY_PRIVATE_KEY = "302e020100300506032b657004220420882a565ad8cb45643892b5366c1ee1c1ef4a730c5ce821a219ff49b6bf173ddf"; + private static final String MNEMONIC_LEGACY_STRING = + "jolly kidnap tom lawn drunk chick optic lust mutter mole bride galley dense member sage neural widow decide curb aboard margin manure"; + private static final String MNEMONIC_LEGACY_PRIVATE_KEY = + "302e020100300506032b657004220420882a565ad8cb45643892b5366c1ee1c1ef4a730c5ce821a219ff49b6bf173ddf"; // backup phrase generated by the iOS wallet, not used anywhere - private static final String IOS_MNEMONIC_STRING = "tiny denial casual grass skull spare awkward indoor ethics dash enough flavor good daughter early hard rug staff capable swallow raise flavor empty angle"; + private static final String IOS_MNEMONIC_STRING = + "tiny denial casual grass skull spare awkward indoor ethics dash enough flavor good daughter early hard rug staff capable swallow raise flavor empty angle"; // private key for "default account", should be index 0 - private static final String IOS_DEFAULT_PRIVATE_KEY = "5f66a51931e8c99089472e0d70516b6272b94dd772b967f8221e1077f966dbda2b60cf7ee8cf10ecd5a076bffad9a7c7b97df370ad758c0f1dd4ef738e04ceb6"; + private static final String IOS_DEFAULT_PRIVATE_KEY = + "5f66a51931e8c99089472e0d70516b6272b94dd772b967f8221e1077f966dbda2b60cf7ee8cf10ecd5a076bffad9a7c7b97df370ad758c0f1dd4ef738e04ceb6"; // backup phrase generated by the Android wallet, also not used anywhere - private static final String ANDROID_MNEMONIC_STRING = "ramp april job flavor surround pyramid fish sea good know blame gate village viable include mixed term draft among monitor swear swing novel track"; + private static final String ANDROID_MNEMONIC_STRING = + "ramp april job flavor surround pyramid fish sea good know blame gate village viable include mixed term draft among monitor swear swing novel track"; // private key for "default account", should be index 0 - private static final String ANDROID_DEFAULT_PRIVATE_KEY = "c284c25b3a1458b59423bc289e83703b125c8eefec4d5aa1b393c2beb9f2bae66188a344ba75c43918ab12fa2ea4a92960eca029a2320d8c6a1c3b94e06c9985"; + private static final String ANDROID_DEFAULT_PRIVATE_KEY = + "c284c25b3a1458b59423bc289e83703b125c8eefec4d5aa1b393c2beb9f2bae66188a344ba75c43918ab12fa2ea4a92960eca029a2320d8c6a1c3b94e06c9985"; private static final String PEM_PASSPHRASE = "this is a passphrase"; private static final String TEST_VECTOR_PEM_PASSPHRASE = "asdasd123"; /* - # enter passphrase "this is a passphrase" - echo '302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10' \ - | xxd -r -p \ - | openssl pkey -inform der -aes-128-cbc - */ + # enter passphrase "this is a passphrase" + echo '302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10' \ + | xxd -r -p \ + | openssl pkey -inform der -aes-128-cbc + */ private static final String ENCRYPTED_PEM = "-----BEGIN ENCRYPTED PRIVATE KEY-----\n" - + "MIGbMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAi8WY7Gy2tThQICCAAw\n" - + "DAYIKoZIhvcNAgkFADAdBglghkgBZQMEAQIEEOq46NPss58chbjUn20NoK0EQG1x\n" - + "R88hIXcWDOECttPTNlMXWJt7Wufm1YwBibrxmCq1QykIyTYhy1TZMyxyPxlYW6aV\n" - + "9hlo4YEh3uEaCmfJzWM=\n" - + "-----END ENCRYPTED PRIVATE KEY-----\n"; + + "MIGbMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAi8WY7Gy2tThQICCAAw\n" + + "DAYIKoZIhvcNAgkFADAdBglghkgBZQMEAQIEEOq46NPss58chbjUn20NoK0EQG1x\n" + + "R88hIXcWDOECttPTNlMXWJt7Wufm1YwBibrxmCq1QykIyTYhy1TZMyxyPxlYW6aV\n" + + "9hlo4YEh3uEaCmfJzWM=\n" + + "-----END ENCRYPTED PRIVATE KEY-----\n"; private static final String MESSAGE_STR = "This is a message about the world."; private static final byte[] MESSAGE_BYTES = MESSAGE_STR.getBytes(StandardCharsets.UTF_8); - private static final String SIG_STR = "73bea53f31ca9c42a422ecb7516ec08d0bbd1a6bfd630ccf10ec1872454814d29f4a8011129cd007eab544af01a75f508285b591e5bed24b68f927751e49e30e"; + private static final String SIG_STR = + "73bea53f31ca9c42a422ecb7516ec08d0bbd1a6bfd630ccf10ec1872454814d29f4a8011129cd007eab544af01a75f508285b591e5bed24b68f927751e49e30e"; @SuppressWarnings("unused") private static Stream privKeyStrings() { return Stream.of( - TEST_KEY_STR, - // raw hex (concatenated private + public key) - TEST_KEY_STR_RAW + - "e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7", - // raw hex (just private key) - TEST_KEY_STR_RAW - ); + TEST_KEY_STR, + // raw hex (concatenated private + public key) + TEST_KEY_STR_RAW + "e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7", + // raw hex (just private key) + TEST_KEY_STR_RAW); } @Test @@ -183,14 +173,14 @@ void keyStringification3() { @ParameterizedTest @DisplayName("private key can be recovered from external string") - @ValueSource(strings = { - TEST_KEY_STR, - // raw hex (concatenated private + public key) - TEST_KEY_STR_RAW + - "e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7", - // raw hex (just private key) - TEST_KEY_STR_RAW - }) + @ValueSource( + strings = { + TEST_KEY_STR, + // raw hex (concatenated private + public key) + TEST_KEY_STR_RAW + "e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7", + // raw hex (just private key) + TEST_KEY_STR_RAW + }) void externalKeyDeserialize(String keyStr) { PrivateKey key = PrivateKey.fromString(keyStr); assertThat(key).isNotNull(); @@ -279,15 +269,9 @@ void keyFromGeneratedMnemonic24() { byte[] signature = privateKey.sign(messageToSign); - assertThat(Ed25519.verify( - signature, - 0, - privateKey.getPublicKey().toBytes(), - 0, - messageToSign, - 0, - messageToSign.length - )).isTrue(); + Assertions.assertThat(Ed25519.verify( + signature, 0, privateKey.getPublicKey().toBytes(), 0, messageToSign, 0, messageToSign.length)) + .isTrue(); } @Test @@ -300,15 +284,9 @@ void keyFromGeneratedMnemonic12() { byte[] signature = privateKey.sign(messageToSign); - assertThat(Ed25519.verify( - signature, - 0, - privateKey.getPublicKey().toBytes(), - 0, - messageToSign, - 0, - messageToSign.length - )).isTrue(); + Assertions.assertThat(Ed25519.verify( + signature, 0, privateKey.getPublicKey().toBytes(), 0, messageToSign, 0, messageToSign.length)) + .isTrue(); } @Test @@ -321,29 +299,29 @@ void keyFromEncryptedPem() throws IOException { @Test @DisplayName("fromPem() with encrypted key without a passphrase throws useful error") void errorKeyFromEncryptedPemNoPassphrase() { - assertThatExceptionOfType(BadKeyException.class).isThrownBy( - () -> PrivateKey.fromPem(ENCRYPTED_PEM) - ).satisfies(error -> assertThat(error.getMessage()).isEqualTo( - "PEM file contained an encrypted private key but no passphrase was given" - )); + assertThatExceptionOfType(BadKeyException.class) + .isThrownBy(() -> PrivateKey.fromPem(ENCRYPTED_PEM)) + .satisfies(error -> assertThat(error.getMessage()) + .isEqualTo("PEM file contained an encrypted private key but no passphrase was given")); } @ParameterizedTest @DisplayName("reproducible signature can be computed") - @ValueSource(strings = { - TEST_KEY_STR, - // raw hex (concatenated private + public key) - TEST_KEY_STR_RAW + - "e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7", - // raw hex (just private key) - TEST_KEY_STR_RAW - }) + @ValueSource( + strings = { + TEST_KEY_STR, + // raw hex (concatenated private + public key) + TEST_KEY_STR_RAW + "e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7", + // raw hex (just private key) + TEST_KEY_STR_RAW + }) void reproducibleSignature(String keyStr) { PrivateKey key = PrivateKey.fromString(keyStr); byte[] signature = key.sign(MESSAGE_BYTES); assertThat(Hex.toHexString(signature)).isEqualTo(SIG_STR); } + @Test @DisplayName("private key is is ECDSA") void keyIsECDSA() { @@ -365,8 +343,7 @@ public static byte[] hexStringToByteArray(String s) { int len = s.length(); byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { - data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) - + Character.digit(s.charAt(i+1), 16)); + data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16)); } return data; } @@ -466,7 +443,8 @@ void slip10TestVector2() { final String PRIVATE_KEY6 = "551d333177df541ad876a60ea71f00447931c0a9da16f227c11ea080d7391b8d"; final String PUBLIC_KEY6 = "0047150c75db263559a70d5778bf36abbab30fb061ad69f69ece61a72b0cfa4fc0"; - var seed = hexStringToByteArray("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"); + var seed = hexStringToByteArray( + "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"); // Chain m PrivateKey key1 = PrivateKey.fromSeedED25519(seed); @@ -509,18 +487,17 @@ void slip10TestVector2() { @DisplayName("PEM import test vectors") void PEMImportTestVectors() throws IOException { // https://github.com/hashgraph/hedera-sdk-reference/issues/93#issue-1665972122 - var PRIVATE_KEY_PEM1 = "-----BEGIN PRIVATE KEY-----\n" + - "MC4CAQAwBQYDK2VwBCIEIOgbjaHgEqF7PY0t2dUf2VU0u1MRoKii/fywDlze4lvl\n" + - "-----END PRIVATE KEY-----"; + var PRIVATE_KEY_PEM1 = "-----BEGIN PRIVATE KEY-----\n" + + "MC4CAQAwBQYDK2VwBCIEIOgbjaHgEqF7PY0t2dUf2VU0u1MRoKii/fywDlze4lvl\n" + "-----END PRIVATE KEY-----"; var PRIVATE_KEY1 = "e81b8da1e012a17b3d8d2dd9d51fd95534bb5311a0a8a2fdfcb00e5cdee25be5"; var PUBLIC_KEY1 = "f7b9aa4a8e4eee94e4277dfe757d8d7cde027e7cd5349b7d8e6ee21c9b9395be"; - var PRIVATE_KEY_PEM2 = "-----BEGIN ENCRYPTED PRIVATE KEY-----\n" + - "MIGbMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAiho4GvPxvL6wICCAAw\n" + - "DAYIKoZIhvcNAgkFADAdBglghkgBZQMEAQIEEIdsubXR0QvxXGSprqDuDXwEQJZl\n" + - "OBtwm2p2P7WrWE0OnjGxUe24fWwdrvJUuguFtH3FVWc8C5Jbxgbyxsuzbf+utNL6\n" + - "0ey+WdbGL06Bw0HGqs8=\n" + - "-----END ENCRYPTED PRIVATE KEY-----"; + var PRIVATE_KEY_PEM2 = "-----BEGIN ENCRYPTED PRIVATE KEY-----\n" + + "MIGbMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAiho4GvPxvL6wICCAAw\n" + + "DAYIKoZIhvcNAgkFADAdBglghkgBZQMEAQIEEIdsubXR0QvxXGSprqDuDXwEQJZl\n" + + "OBtwm2p2P7WrWE0OnjGxUe24fWwdrvJUuguFtH3FVWc8C5Jbxgbyxsuzbf+utNL6\n" + + "0ey+WdbGL06Bw0HGqs8=\n" + + "-----END ENCRYPTED PRIVATE KEY-----"; var PRIVATE_KEY2 = "fa0857e963946d5f5e035684c40354d3cd3dcc80c0fb77beac2ef7c4b5271599"; var PUBLIC_KEY2 = "202af61e141465d4bf2c356d37d18bd026c246bde4eb73258722ad11f790be4e"; @@ -537,7 +514,8 @@ void PEMImportTestVectors() throws IOException { @DisplayName("DER import test vectors") void DERImportTestVectors() { // https://github.com/hashgraph/hedera-sdk-reference/issues/93#issue-1665972122 - var PRIVATE_KEY_DER1 = "302e020100300506032b657004220420feb858a4a69600a5eef2d9c76f7fb84fc0b6627f29e0ab17e160f640c267d404"; + var PRIVATE_KEY_DER1 = + "302e020100300506032b657004220420feb858a4a69600a5eef2d9c76f7fb84fc0b6627f29e0ab17e160f640c267d404"; var PRIVATE_KEY1 = "feb858a4a69600a5eef2d9c76f7fb84fc0b6627f29e0ab17e160f640c267d404"; var PUBLIC_KEY1 = "8ccd31b53d1835b467aac795dab19b274dd3b37e3daf12fcec6bc02bac87b53d"; diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/Ed25519PublicKeyTest.java b/sdk/src/test/java/org/hiero/sdk/Ed25519PublicKeyTest.java similarity index 75% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/Ed25519PublicKeyTest.java rename to sdk/src/test/java/org/hiero/sdk/Ed25519PublicKeyTest.java index ee3a535307..f5e52c75c8 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/Ed25519PublicKeyTest.java +++ b/sdk/src/test/java/org/hiero/sdk/Ed25519PublicKeyTest.java @@ -1,39 +1,21 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType; + +import java.math.BigDecimal; +import java.util.Collections; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import java.math.BigDecimal; -import java.util.Collections; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatNoException; -import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; - class Ed25519PublicKeyTest { - private static final String TEST_KEY_STR = "302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7"; + private static final String TEST_KEY_STR = + "302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7"; private static final String TEST_KEY_STR_RAW = "e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7"; @Test @@ -52,28 +34,56 @@ void verifyTransaction() { @Test void keyByteValidation() { byte[] invalidKeyED25519 = new byte[32]; - assertDoesNotThrow(() -> PublicKey.fromBytes(invalidKeyED25519)); - assertDoesNotThrow(() -> PublicKey.fromBytesED25519(invalidKeyED25519)); + Assertions.assertDoesNotThrow(() -> PublicKey.fromBytes(invalidKeyED25519)); + Assertions.assertDoesNotThrow(() -> PublicKey.fromBytesED25519(invalidKeyED25519)); - byte[] invalidKey = new byte[]{ + byte[] invalidKey = new byte[] { 0x00, - (byte) 0xca, (byte) 0x35, 0x4b, 0x7c, (byte) 0xf4, (byte) 0x87, (byte) 0xd1, (byte) 0xbc, 0x43, - 0x5a, 0x25, 0x66, 0x77, 0x09, (byte) 0xc1, (byte) 0xab, (byte) 0x98, 0x0c, 0x11, 0x4d, - 0x35, (byte) 0x94, (byte) 0xe6, 0x25, (byte) 0x9e, (byte) 0x81, 0x2e, 0x6a, 0x70, 0x3d, - 0x4f, 0x51 + (byte) 0xca, + (byte) 0x35, + 0x4b, + 0x7c, + (byte) 0xf4, + (byte) 0x87, + (byte) 0xd1, + (byte) 0xbc, + 0x43, + 0x5a, + 0x25, + 0x66, + 0x77, + 0x09, + (byte) 0xc1, + (byte) 0xab, + (byte) 0x98, + 0x0c, + 0x11, + 0x4d, + 0x35, + (byte) 0x94, + (byte) 0xe6, + 0x25, + (byte) 0x9e, + (byte) 0x81, + 0x2e, + 0x6a, + 0x70, + 0x3d, + 0x4f, + 0x51 }; assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> PublicKey.fromBytesED25519(invalidKey)); + .isThrownBy(() -> PublicKey.fromBytesED25519(invalidKey)); - byte[] malformedKey = new byte[]{0x00, 0x01, 0x02}; + byte[] malformedKey = new byte[] {0x00, 0x01, 0x02}; assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> PublicKey.fromBytesED25519(malformedKey)); + .isThrownBy(() -> PublicKey.fromBytesED25519(malformedKey)); byte[] validKey = PrivateKey.generateED25519().getPublicKey().toBytes(); - assertDoesNotThrow(() -> PublicKey.fromBytesED25519(validKey)); + Assertions.assertDoesNotThrow(() -> PublicKey.fromBytesED25519(validKey)); byte[] validDERKey = PrivateKey.generateED25519().getPublicKey().toBytesDER(); - assertDoesNotThrow(() -> PublicKey.fromBytesED25519(validDERKey)); + Assertions.assertDoesNotThrow(() -> PublicKey.fromBytesED25519(validDERKey)); } @Test @@ -122,11 +132,10 @@ void keyByteSerializationThroughTransaction() { var receiverAccount = AccountId.fromString("0.0.3"); var transferAmount = Hbar.from(new BigDecimal("0.0001"), HbarUnit.HBAR); var privateKey = PrivateKey.generateED25519(); - var client = Client.forTestnet() - .setOperator(senderAccount, privateKey); + var client = Client.forTestnet().setOperator(senderAccount, privateKey); var tx = new TransferTransaction() - .addHbarTransfer(senderAccount, transferAmount.negated()) - .addHbarTransfer(receiverAccount, transferAmount); + .addHbarTransfer(senderAccount, transferAmount.negated()) + .addHbarTransfer(receiverAccount, transferAmount); tx.freezeWith(client); tx.signWithOperator(client); @@ -184,12 +193,13 @@ void keyStringSerialization3() { @ParameterizedTest @DisplayName("public key can be recovered from external string") - @ValueSource(strings = { - // ASN1 encoded hex - "302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7", - // raw hex - "e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7", - }) + @ValueSource( + strings = { + // ASN1 encoded hex + "302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7", + // raw hex + "e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7", + }) void externalKeyDeserialize(String keyStr) { PublicKey key = PublicKey.fromString(keyStr); assertThat(key).isNotNull(); @@ -228,7 +238,8 @@ void keyIsNotEd25519() { @DisplayName("DER import test vectors") void DERImportTestVectors() { // https://github.com/hashgraph/hedera-sdk-reference/issues/93#issue-1665972122 - var PUBLIC_KEY_DER1 = "302a300506032b65700321008ccd31b53d1835b467aac795dab19b274dd3b37e3daf12fcec6bc02bac87b53d"; + var PUBLIC_KEY_DER1 = + "302a300506032b65700321008ccd31b53d1835b467aac795dab19b274dd3b37e3daf12fcec6bc02bac87b53d"; var PUBLIC_KEY1 = "8ccd31b53d1835b467aac795dab19b274dd3b37e3daf12fcec6bc02bac87b53d"; var ed25519PublicKey1 = PublicKey.fromStringDER(PUBLIC_KEY_DER1); diff --git a/sdk/src/test/java/org/hiero/sdk/EthereumFlowMockTest.java b/sdk/src/test/java/org/hiero/sdk/EthereumFlowMockTest.java new file mode 100644 index 0000000000..351ede100e --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/EthereumFlowMockTest.java @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.google.protobuf.ByteString; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; +import org.hiero.sdk.proto.FileID; +import org.hiero.sdk.proto.Response; +import org.hiero.sdk.proto.ResponseCodeEnum; +import org.hiero.sdk.proto.SignedTransaction; +import org.hiero.sdk.proto.Transaction; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionGetReceiptResponse; +import org.hiero.sdk.proto.TransactionReceipt; +import org.hiero.sdk.proto.TransactionResponse; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class EthereumFlowMockTest { + static ByteString ETHEREUM_DATA = ByteString.fromHex( + "f864012f83018000947e3a9eaf9bcc39e2ffa38eb30bf7a93feacbc18180827653820277a0f9fbff985d374be4a55f296915002eec11ac96f1ce2df183adf992baa9390b2fa00c1e867cc960d9c74ec2e6a662b7908ec4c8cc9f3091e886bcefbeb2290fb792"); + + static ByteString LONG_CALL_DATA = ByteString.fromHex("00".repeat(5121)); + + @Test + void dontTruncateEthereumDataUnnecessarily() + throws PrecheckStatusException, TimeoutException, InterruptedException, ReceiptStatusException { + List responses1 = List.of( + (Function) o -> { + var signedTransaction = SignedTransaction.parseFrom(((Transaction) o).getSignedTransactionBytes()); + var transactionBody = TransactionBody.parseFrom(signedTransaction.getBodyBytes()); + assertThat(transactionBody.getDataCase()) + .isEqualByComparingTo(TransactionBody.DataCase.ETHEREUMTRANSACTION); + assertThat(transactionBody.hasEthereumTransaction()).isTrue(); + assertThat(transactionBody.getEthereumTransaction().getEthereumData()) + .isEqualTo(ETHEREUM_DATA); + return TransactionResponse.newBuilder() + .setNodeTransactionPrecheckCodeValue(0) + .build(); + }, + Response.newBuilder() + .setTransactionGetReceipt(TransactionGetReceiptResponse.newBuilder() + .setReceipt( + TransactionReceipt.newBuilder().setStatusValue(ResponseCodeEnum.SUCCESS_VALUE))) + .build()); + + var responses = List.of(responses1); + + try (var mocker = Mocker.withResponses(responses)) { + new EthereumFlow() + .setEthereumData(ETHEREUM_DATA.toByteArray()) + .execute(mocker.client) + .getReceipt(mocker.client); + } + } + + @ParameterizedTest(name = "[{0}] ContractCreateFlow functions") + @CsvSource({"sync", "async"}) + void extractsCallData(String versionToTest) + throws PrecheckStatusException, TimeoutException, InterruptedException, ReceiptStatusException, + ExecutionException { + List responses1 = List.of( + (Function) o -> { + var signedTransaction = SignedTransaction.parseFrom(((Transaction) o).getSignedTransactionBytes()); + var transactionBody = TransactionBody.parseFrom(signedTransaction.getBodyBytes()); + assertThat(transactionBody.getDataCase()).isEqualByComparingTo(TransactionBody.DataCase.FILECREATE); + assertThat(transactionBody.hasFileCreate()).isTrue(); + assertThat(transactionBody.getFileCreate().getContents().size()) + .isEqualTo(4096); + return TransactionResponse.newBuilder() + .setNodeTransactionPrecheckCodeValue(0) + .build(); + }, + Response.newBuilder() + .setTransactionGetReceipt(TransactionGetReceiptResponse.newBuilder() + .setReceipt(TransactionReceipt.newBuilder() + .setStatusValue(ResponseCodeEnum.SUCCESS_VALUE) + .setFileID(FileID.newBuilder().setFileNum(1)))) + .build(), + (Function) o -> { + var signedTransaction = SignedTransaction.parseFrom(((Transaction) o).getSignedTransactionBytes()); + var transactionBody = TransactionBody.parseFrom(signedTransaction.getBodyBytes()); + assertThat(transactionBody.getDataCase()).isEqualByComparingTo(TransactionBody.DataCase.FILEAPPEND); + assertThat(transactionBody.hasFileAppend()).isTrue(); + assertThat(transactionBody.getFileAppend().hasFileID()).isTrue(); + assertThat(transactionBody.getFileAppend().getFileID().getFileNum()) + .isEqualTo(1); + assertThat(transactionBody.getFileAppend().getContents()).isEqualTo(LONG_CALL_DATA.substring(4096)); + return TransactionResponse.newBuilder() + .setNodeTransactionPrecheckCodeValue(0) + .build(); + }, + Response.newBuilder() + .setTransactionGetReceipt(TransactionGetReceiptResponse.newBuilder() + .setReceipt( + TransactionReceipt.newBuilder().setStatusValue(ResponseCodeEnum.SUCCESS_VALUE))) + .build(), + (Function) o -> { + var signedTransaction = SignedTransaction.parseFrom(((Transaction) o).getSignedTransactionBytes()); + var transactionBody = TransactionBody.parseFrom(signedTransaction.getBodyBytes()); + assertThat(transactionBody.getDataCase()) + .isEqualByComparingTo(TransactionBody.DataCase.ETHEREUMTRANSACTION); + assertThat(transactionBody.hasEthereumTransaction()).isTrue(); + assertThat(EthereumTransactionData.fromBytes(transactionBody + .getEthereumTransaction() + .getEthereumData() + .toByteArray()) + .callData) + .isEmpty(); + return TransactionResponse.newBuilder() + .setNodeTransactionPrecheckCodeValue(0) + .build(); + }, + Response.newBuilder() + .setTransactionGetReceipt(TransactionGetReceiptResponse.newBuilder() + .setReceipt( + TransactionReceipt.newBuilder().setStatusValue(ResponseCodeEnum.SUCCESS_VALUE))) + .build()); + + var responses = List.of(responses1); + + EthereumFlow ethereumFlow; + try (var mocker = Mocker.withResponses(responses)) { + var ethereumData = EthereumTransactionData.fromBytes(ETHEREUM_DATA.toByteArray()); + ethereumData.callData = LONG_CALL_DATA.toByteArray(); + + if (versionToTest.equals("sync")) { + ethereumFlow = new EthereumFlow() + .setMaxGasAllowance(Hbar.fromTinybars(25)) + .setEthereumData(ethereumData.toBytes()); + + ethereumFlow.execute(mocker.client).getReceipt(mocker.client); + } else { + ethereumFlow = new EthereumFlow() + .setMaxGasAllowance(Hbar.fromTinybars(25)) + .setEthereumData(ethereumData.toBytes()); + + ethereumFlow + .executeAsync(mocker.client) + .thenCompose(response -> response.getReceiptAsync(mocker.client)) + .get(); + } + + assertThat(ethereumFlow.getEthereumData()).isNotNull(); + assertThat(ethereumFlow.getMaxGasAllowance().toTinybars()).isEqualTo(25); + } + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/EthereumTransactionDataLegacyTest.java b/sdk/src/test/java/org/hiero/sdk/EthereumTransactionDataLegacyTest.java new file mode 100644 index 0000000000..18a2a05a2e --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/EthereumTransactionDataLegacyTest.java @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.bouncycastle.util.encoders.Hex; +import org.junit.jupiter.api.Test; + +public class EthereumTransactionDataLegacyTest { + // https://github.com/hashgraph/hedera-services/blob/1e01d9c6b8923639b41359c55413640b589c4ec7/hapi-utils/src/test/java/com/hedera/services/ethereum/EthTxDataTest.java#L49 + static final String RAW_TX_TYPE_0 = + "f864012f83018000947e3a9eaf9bcc39e2ffa38eb30bf7a93feacbc18180827653820277a0f9fbff985d374be4a55f296915002eec11ac96f1ce2df183adf992baa9390b2fa00c1e867cc960d9c74ec2e6a662b7908ec4c8cc9f3091e886bcefbeb2290fb792"; + static final String RAW_TX_TYPE_0_TRIMMED_LAST_BYTES = + "f864012f83018000947e3a9eaf9bcc39e2ffa38eb30bf7a93feacbc18180827653820277a0f9fbff985d374be4a55f296915002eec11ac96f1ce2df183adf992baa9390b2fa00c1e867cc960d9c74ec2e6a662b7908ec4c8cc9f3091e886bcefbeb2290000"; + static final String RAW_TX_TYPE_2 = + "02f87082012a022f2f83018000947e3a9eaf9bcc39e2ffa38eb30bf7a93feacbc181880de0b6b3a764000083123456c001a0df48f2efd10421811de2bfb125ab75b2d3c44139c4642837fb1fccce911fd479a01aaf7ae92bee896651dfc9d99ae422a296bf5d9f1ca49b2d96d82b79eb112d66"; + + @Test + public void legacyToFromBytes() { + var data = (EthereumTransactionDataLegacy) EthereumTransactionData.fromBytes(Hex.decode(RAW_TX_TYPE_0)); + assertThat(RAW_TX_TYPE_0).isEqualTo(Hex.toHexString(data.toBytes())); + + // Chain ID is not part of the legacy ethereum transaction, so why are you calculating and checking it? + // assertEquals("012a", Hex.toHexString(data.chainId())); + + assertThat(Hex.toHexString(data.nonce)).isEqualTo("01"); + assertThat(Hex.toHexString(data.gasPrice)).isEqualTo("2f"); + assertThat(Hex.toHexString(data.gasLimit)).isEqualTo("018000"); + assertThat(Hex.toHexString(data.to)).isEqualTo("7e3a9eaf9bcc39e2ffa38eb30bf7a93feacbc181"); + assertThat(Hex.toHexString(data.value)).isEqualTo(""); + assertThat(Hex.toHexString(data.callData)).isEqualTo("7653"); + assertThat(Hex.toHexString(data.v)).isEqualTo("0277"); + assertThat(Hex.toHexString(data.r)) + .isEqualTo("f9fbff985d374be4a55f296915002eec11ac96f1ce2df183adf992baa9390b2f"); + assertThat(Hex.toHexString(data.s)) + .isEqualTo("0c1e867cc960d9c74ec2e6a662b7908ec4c8cc9f3091e886bcefbeb2290fb792"); + + // We don't currently support a way to get the ethereum has, but we probably should + // assertEquals("9ffbd69c44cf643ed8d1e756b505e545e3b5dd3a6b5ef9da1d8eca6679706594", + // Hex.toHexString(data.getEthereumHash())); + } + + @Test + public void eip1559ToFromBytes() { + var data = (EthereumTransactionDataEip1559) EthereumTransactionData.fromBytes(Hex.decode(RAW_TX_TYPE_2)); + assertThat(RAW_TX_TYPE_2).isEqualTo(Hex.toHexString(data.toBytes())); + + assertThat(Hex.toHexString(data.chainId)).isEqualTo("012a"); + assertThat(Hex.toHexString(data.nonce)).isEqualTo("02"); + assertThat(Hex.toHexString(data.maxPriorityGas)).isEqualTo("2f"); + assertThat(Hex.toHexString(data.maxGas)).isEqualTo("2f"); + assertThat(Hex.toHexString(data.gasLimit)).isEqualTo("018000"); + assertThat(Hex.toHexString(data.to)).isEqualTo("7e3a9eaf9bcc39e2ffa38eb30bf7a93feacbc181"); + assertThat(Hex.toHexString(data.value)).isEqualTo("0de0b6b3a7640000"); + assertThat(Hex.toHexString(data.callData)).isEqualTo("123456"); + assertThat(Hex.toHexString(data.accessList)).isEqualTo(""); + assertThat(Hex.toHexString(data.recoveryId)).isEqualTo("01"); + assertThat(Hex.toHexString(data.r)) + .isEqualTo("df48f2efd10421811de2bfb125ab75b2d3c44139c4642837fb1fccce911fd479"); + assertThat(Hex.toHexString(data.s)) + .isEqualTo("1aaf7ae92bee896651dfc9d99ae422a296bf5d9f1ca49b2d96d82b79eb112d66"); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/EthereumTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/EthereumTransactionTest.java new file mode 100644 index 0000000000..e9adc0761a --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/EthereumTransactionTest.java @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import org.bouncycastle.util.encoders.Hex; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class EthereumTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + EthereumTransaction spawnTestTransaction() { + return new EthereumTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setEthereumData(Hex.decode("deadbeef")) + .setCallDataFileId(FileId.fromString("4.5.6")) + .setMaxGasAllowanceHbar(Hbar.fromString("3")) + .setMaxTransactionFee(new Hbar(1)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new EthereumTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytesNft() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = TokenCreateTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx2.toString()); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/EthereumTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/EthereumTransactionTest.snap new file mode 100644 index 0000000000..e49d5846e7 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/EthereumTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.EthereumTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nethereum_transaction {\n call_data {\n file_num: 6\n realm_num: 5\n shard_num: 4\n }\n ethereum_data: \"\\336\\255\\276\\357\"\n max_gas_allowance: 300000000\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/ExchangeRatesTest.java b/sdk/src/test/java/org/hiero/sdk/ExchangeRatesTest.java new file mode 100644 index 0000000000..61827c7a81 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ExchangeRatesTest.java @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.google.protobuf.InvalidProtocolBufferException; +import java.time.Instant; +import org.bouncycastle.util.encoders.Hex; +import org.junit.jupiter.api.Test; + +public class ExchangeRatesTest { + private static final String exchangeRateSetHex = + "0a1008b0ea0110b6b4231a0608f0bade9006121008b0ea01108cef231a060880d7de9006"; + + @Test + void fromProtobuf() throws InvalidProtocolBufferException { + byte[] exchangeRatesBytes = Hex.decode(exchangeRateSetHex); + + ExchangeRates exchangeRates = ExchangeRates.fromBytes(exchangeRatesBytes); + + assertThat(exchangeRates.currentRate.cents).isEqualTo(580150); + assertThat(exchangeRates.currentRate.hbars).isEqualTo(30000); + Instant currentExpirationTime = Instant.ofEpochSecond(1645714800); + assertThat(exchangeRates.currentRate.expirationTime).isEqualTo(currentExpirationTime); + assertThat(exchangeRates.currentRate.exchangeRateInCents).isEqualTo(19.338333333333335); + + assertThat(exchangeRates.nextRate.cents).isEqualTo(587660); + assertThat(exchangeRates.nextRate.hbars).isEqualTo(30000); + Instant nextExpirationTime = Instant.ofEpochSecond(1645718400); + assertThat(exchangeRates.nextRate.expirationTime).isEqualTo(nextExpirationTime); + assertThat(exchangeRates.nextRate.exchangeRateInCents).isEqualTo(19.588666666666665); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/ExecutableTest.java b/sdk/src/test/java/org/hiero/sdk/ExecutableTest.java new file mode 100644 index 0000000000..16ee180f84 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ExecutableTest.java @@ -0,0 +1,604 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import io.grpc.MethodDescriptor; +import io.grpc.StatusRuntimeException; +import java.time.Duration; +import java.time.Instant; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import javax.annotation.Nullable; +import org.hiero.sdk.Executable.GrpcRequest; +import org.hiero.sdk.logger.LogLevel; +import org.hiero.sdk.logger.Logger; +import org.hiero.sdk.proto.QueryHeader; +import org.hiero.sdk.proto.Response; +import org.hiero.sdk.proto.ResponseCodeEnum; +import org.hiero.sdk.proto.ResponseHeader; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.mockito.stubbing.Answer; + +class ExecutableTest { + Client client; + Network network; + Node node3, node4, node5; + List nodeAccountIds; + + @BeforeEach + void setup() { + client = Client.forMainnet(); + network = Mockito.mock(Network.class); + client.network = network; + client.setLogger(new Logger(LogLevel.WARN)); + + node3 = Mockito.mock(Node.class); + node4 = Mockito.mock(Node.class); + node5 = Mockito.mock(Node.class); + + when(node3.getAccountId()).thenReturn(new AccountId(3)); + when(node4.getAccountId()).thenReturn(new AccountId(4)); + when(node5.getAccountId()).thenReturn(new AccountId(5)); + when(network.getNodeProxies(new AccountId(3))).thenReturn(List.of(node3)); + when(network.getNodeProxies(new AccountId(4))).thenReturn(List.of(node4)); + when(network.getNodeProxies(new AccountId(5))).thenReturn(List.of(node5)); + + nodeAccountIds = Arrays.asList(new AccountId(3), new AccountId(4), new AccountId(5)); + } + + @Test + void firstNodeHealthy() { + when(node3.isHealthy()).thenReturn(true); + + var tx = new DummyTransaction(); + tx.setNodeAccountIds(nodeAccountIds); + tx.setNodesFromNodeAccountIds(client); + tx.setMinBackoff(Duration.ofMillis(10)); + tx.setMaxBackoff(Duration.ofMillis(1000)); + + var node = tx.getNodeForExecute(1); + assertThat(node).isEqualTo(node3); + } + + @Test + void calloptionsShouldRespectGrpcDeadline() { + when(node3.isHealthy()).thenReturn(true); + + var tx = new DummyTransaction(); + tx.setNodeAccountIds(nodeAccountIds); + tx.setNodesFromNodeAccountIds(client); + tx.setMinBackoff(Duration.ofMillis(10)); + tx.setMaxBackoff(Duration.ofMillis(1000)); + tx.setGrpcDeadline(Duration.ofSeconds(10)); + + var grpcRequest = tx.getGrpcRequest(1); + + var timeRemaining = grpcRequest.getCallOptions().getDeadline().timeRemaining(TimeUnit.MILLISECONDS); + assertThat(timeRemaining).isLessThan(10000); + assertThat(timeRemaining).isGreaterThan(9000); + } + + @Test + void executableShouldUseGrpcDeadline() throws InterruptedException, PrecheckStatusException, TimeoutException { + when(node3.isHealthy()).thenReturn(true); + + var tx = new DummyTransaction(); + tx.setNodeAccountIds(nodeAccountIds); + tx.setNodesFromNodeAccountIds(client); + tx.setMinBackoff(Duration.ofMillis(10)); + tx.setMaxBackoff(Duration.ofMillis(1000)); + tx.setMaxAttempts(10); + + var timeout = Duration.ofSeconds(5); + var currentTimeRemaining = new AtomicLong(timeout.toMillis()); + final long minimumRetryDelayMs = 100; + final long defaultDeadlineMs = timeout.toMillis() - (minimumRetryDelayMs * (tx.getMaxAttempts() / 2)); + + // later on when the transaction is executed its grpc deadline should not be modified... + tx.setGrpcDeadline(Duration.ofMillis(defaultDeadlineMs)); + + tx.blockingUnaryCall = (grpcRequest) -> { + var grpc = (GrpcRequest) grpcRequest; + + var grpcTimeRemaining = grpc.getCallOptions().getDeadline().timeRemaining(TimeUnit.MILLISECONDS); + + // the actual grpc deadline should be no larger than the smaller of the two values - + // the default transaction level grpc deadline and the remaining timeout + assertThat(grpcTimeRemaining).isLessThanOrEqualTo(defaultDeadlineMs); + assertThat(grpcTimeRemaining).isLessThanOrEqualTo(currentTimeRemaining.get()); + + assertThat(grpcTimeRemaining).isGreaterThan(0); + + // transaction's grpc deadline should keep its original value + assertThat(tx.grpcDeadline().toMillis()).isEqualTo(defaultDeadlineMs); + + currentTimeRemaining.set(currentTimeRemaining.get() - minimumRetryDelayMs); + + if (currentTimeRemaining.get() > 0) { + try { + Thread.sleep(minimumRetryDelayMs); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + // Status.UNAVAILABLE tells the Executable to retry the request + throw new StatusRuntimeException(io.grpc.Status.UNAVAILABLE); + } + + throw new StatusRuntimeException(io.grpc.Status.ABORTED); + }; + + assertThatExceptionOfType(MaxAttemptsExceededException.class).isThrownBy(() -> { + tx.execute(client, timeout); + }); + } + + @Test + void multipleNodesUnhealthy() { + when(node3.isHealthy()).thenReturn(false); + when(node4.isHealthy()).thenReturn(true); + + when(node3.getRemainingTimeForBackoff()).thenReturn(1000L); + + var tx = new DummyTransaction(); + tx.setNodeAccountIds(nodeAccountIds); + tx.setNodesFromNodeAccountIds(client); + tx.setMinBackoff(Duration.ofMillis(10)); + tx.setMaxBackoff(Duration.ofMillis(1000)); + + var node = tx.getNodeForExecute(1); + assertThat(node).isEqualTo(node4); + } + + @Test + void allNodesUnhealthy() { + when(node3.isHealthy()).thenReturn(false); + when(node4.isHealthy()).thenReturn(false); + when(node5.isHealthy()).thenReturn(false); + + when(node3.getRemainingTimeForBackoff()).thenReturn(4000L); + when(node4.getRemainingTimeForBackoff()).thenReturn(3000L); + when(node5.getRemainingTimeForBackoff()).thenReturn(5000L); + + var tx = new DummyTransaction(); + tx.setNodeAccountIds(nodeAccountIds); + tx.setNodesFromNodeAccountIds(client); + tx.setMinBackoff(Duration.ofMillis(10)); + tx.setMaxBackoff(Duration.ofMillis(1000)); + tx.nodeAccountIds.setIndex(1); + + var node = tx.getNodeForExecute(1); + assertThat(node).isEqualTo(node4); + } + + @Test + void multipleRequestsWithSingleHealthyNode() { + when(node3.isHealthy()).thenReturn(true); + when(node4.isHealthy()).thenReturn(false); + when(node5.isHealthy()).thenReturn(false); + + when(node4.getRemainingTimeForBackoff()).thenReturn(4000L); + when(node5.getRemainingTimeForBackoff()).thenReturn(3000L); + + var tx = new DummyTransaction(); + tx.setNodeAccountIds(nodeAccountIds); + tx.setNodesFromNodeAccountIds(client); + tx.setMinBackoff(Duration.ofMillis(10)); + tx.setMaxBackoff(Duration.ofMillis(1000)); + + var node = tx.getNodeForExecute(1); + assertThat(node).isEqualTo(node3); + tx.nodeAccountIds.advance(); + tx.nodes.advance(); + + node = tx.getNodeForExecute(2); + assertThat(node).isEqualTo(node3); + verify(node4).getRemainingTimeForBackoff(); + verify(node5).getRemainingTimeForBackoff(); + } + + @Test + void multipleRequestsWithNoHealthyNodes() { + AtomicInteger i = new AtomicInteger(); + + when(node3.isHealthy()).thenReturn(false); + when(node4.isHealthy()).thenReturn(false); + when(node5.isHealthy()).thenReturn(false); + + long[] node3Times = {4000, 3000, 1000}; + long[] node4Times = {3000, 1000, 4000}; + long[] node5Times = {1000, 3000, 4000}; + + when(node3.getRemainingTimeForBackoff()).thenAnswer((Answer) invocation -> node3Times[i.get()]); + when(node4.getRemainingTimeForBackoff()).thenAnswer((Answer) invocation -> node4Times[i.get()]); + when(node5.getRemainingTimeForBackoff()).thenAnswer((Answer) invocation -> node5Times[i.get()]); + + var tx = new DummyTransaction(); + tx.setNodeAccountIds(nodeAccountIds); + tx.setNodesFromNodeAccountIds(client); + tx.setMinBackoff(Duration.ofMillis(10)); + tx.setMaxBackoff(Duration.ofMillis(1000)); + + var node = tx.getNodeForExecute(1); + assertThat(node).isEqualTo(node5); + i.incrementAndGet(); + + node = tx.getNodeForExecute(2); + assertThat(node).isEqualTo(node4); + i.incrementAndGet(); + + node = tx.getNodeForExecute(3); + assertThat(node).isEqualTo(node3); + } + + @Test + void successfulExecute() throws PrecheckStatusException, TimeoutException { + var now = java.time.Instant.now(); + var tx = new DummyTransaction() { + @Nullable + @Override + TransactionResponse mapResponse( + org.hiero.sdk.proto.TransactionResponse response, + AccountId nodeId, + org.hiero.sdk.proto.Transaction request) { + return new TransactionResponse( + new AccountId(3), + TransactionId.withValidStart(new AccountId(3), now), + new byte[] {1, 2, 3}, + null) + .setValidateStatus(true); + } + }; + + var nodeAccountIds = Arrays.asList(new AccountId(3), new AccountId(4), new AccountId(5)); + tx.setNodeAccountIds(nodeAccountIds); + + var txResp = org.hiero.sdk.proto.TransactionResponse.newBuilder() + .setNodeTransactionPrecheckCode(ResponseCodeEnum.OK) + .build(); + + tx.blockingUnaryCall = (grpcRequest) -> txResp; + org.hiero.sdk.TransactionResponse resp = (org.hiero.sdk.TransactionResponse) tx.execute(client); + + assertThat(resp.nodeId).isEqualTo(new AccountId(3)); + assertThat(resp.getValidateStatus()).isTrue(); + assertThat(resp.toString()).isNotNull(); + } + + @Test + void executeWithChannelFailure() throws PrecheckStatusException, TimeoutException { + when(node3.isHealthy()).thenReturn(true); + when(node4.isHealthy()).thenReturn(true); + + when(node3.channelFailedToConnect(any(Instant.class))).thenReturn(true); + when(node4.channelFailedToConnect(any(Instant.class))).thenReturn(false); + + var now = java.time.Instant.now(); + var tx = new DummyTransaction() { + @Nullable + @Override + TransactionResponse mapResponse( + org.hiero.sdk.proto.TransactionResponse response, + AccountId nodeId, + org.hiero.sdk.proto.Transaction request) { + return new TransactionResponse( + new AccountId(4), + TransactionId.withValidStart(new AccountId(4), now), + new byte[] {1, 2, 3}, + null); + } + }; + + var nodeAccountIds = Arrays.asList(new AccountId(3), new AccountId(4), new AccountId(5)); + tx.setNodeAccountIds(nodeAccountIds); + + var txResp = org.hiero.sdk.proto.TransactionResponse.newBuilder() + .setNodeTransactionPrecheckCode(ResponseCodeEnum.OK) + .build(); + + tx.blockingUnaryCall = (grpcRequest) -> txResp; + org.hiero.sdk.TransactionResponse resp = (org.hiero.sdk.TransactionResponse) tx.execute(client); + + verify(node3).channelFailedToConnect(any(Instant.class)); + verify(node4).channelFailedToConnect(any(Instant.class)); + assertThat(resp.nodeId).isEqualTo(new AccountId(4)); + } + + @Test + void executeWithAllUnhealthyNodes() throws PrecheckStatusException, TimeoutException { + AtomicInteger i = new AtomicInteger(); + + // 1st round, pick node3, fail channel connect + // 2nd round, pick node4, fail channel connect + // 3rd round, pick node5, fail channel connect + // 4th round, pick node 3, wait for delay, channel connect ok + when(node3.isHealthy()).thenAnswer((Answer) inv -> i.get() == 0); + when(node4.isHealthy()).thenAnswer((Answer) inv -> i.get() == 0); + when(node5.isHealthy()).thenAnswer((Answer) inv -> i.get() == 0); + + when(node3.channelFailedToConnect(any(Instant.class))).thenAnswer((Answer) inv -> i.get() == 0); + when(node4.channelFailedToConnect(any(Instant.class))).thenAnswer((Answer) inv -> i.get() == 0); + when(node5.channelFailedToConnect(any(Instant.class))) + .thenAnswer((Answer) inv -> i.getAndIncrement() == 0); + + when(node3.getRemainingTimeForBackoff()).thenReturn(500L); + when(node4.getRemainingTimeForBackoff()).thenReturn(600L); + when(node5.getRemainingTimeForBackoff()).thenReturn(700L); + + var now = java.time.Instant.now(); + var tx = new DummyTransaction() { + @Nullable + @Override + TransactionResponse mapResponse( + org.hiero.sdk.proto.TransactionResponse response, + AccountId nodeId, + org.hiero.sdk.proto.Transaction request) { + return new TransactionResponse( + new AccountId(3), + TransactionId.withValidStart(new AccountId(3), now), + new byte[] {1, 2, 3}, + null); + } + }; + + var nodeAccountIds = Arrays.asList(new AccountId(3), new AccountId(4), new AccountId(5)); + tx.setNodeAccountIds(nodeAccountIds); + + var txResp = org.hiero.sdk.proto.TransactionResponse.newBuilder() + .setNodeTransactionPrecheckCode(ResponseCodeEnum.OK) + .build(); + + tx.blockingUnaryCall = (grpcRequest) -> txResp; + org.hiero.sdk.TransactionResponse resp = (org.hiero.sdk.TransactionResponse) tx.execute(client); + + verify(node3, times(2)).channelFailedToConnect(any(Instant.class)); + verify(node4).channelFailedToConnect(any(Instant.class)); + verify(node5).channelFailedToConnect(any(Instant.class)); + assertThat(resp.nodeId).isEqualTo(new AccountId(3)); + } + + @Test + void executeExhaustRetries() { + when(node3.isHealthy()).thenReturn(true); + when(node4.isHealthy()).thenReturn(true); + when(node5.isHealthy()).thenReturn(true); + + when(node3.channelFailedToConnect(any(Instant.class))).thenReturn(true); + when(node4.channelFailedToConnect(any(Instant.class))).thenReturn(true); + when(node5.channelFailedToConnect(any(Instant.class))).thenReturn(true); + + var tx = new DummyTransaction(); + var nodeAccountIds = Arrays.asList(new AccountId(3), new AccountId(4), new AccountId(5)); + tx.setNodeAccountIds(nodeAccountIds); + assertThatExceptionOfType(MaxAttemptsExceededException.class).isThrownBy(() -> tx.execute(client)); + } + + @Test + void executeRetriableErrorDuringCall() { + AtomicInteger i = new AtomicInteger(); + + when(node3.isHealthy()).thenReturn(true); + when(node4.isHealthy()).thenReturn(true); + + when(node3.channelFailedToConnect(any(Instant.class))).thenReturn(false); + when(node4.channelFailedToConnect(any(Instant.class))).thenReturn(false); + + var tx = new DummyTransaction(); + var nodeAccountIds = Arrays.asList(new AccountId(3), new AccountId(4), new AccountId(5)); + tx.setNodeAccountIds(nodeAccountIds); + + tx.blockingUnaryCall = (grpcRequest) -> { + if (i.getAndIncrement() == 0) { + throw new StatusRuntimeException(io.grpc.Status.UNAVAILABLE); + } else { + throw new StatusRuntimeException(io.grpc.Status.ABORTED); + } + }; + + assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> tx.execute(client)); + + verify(node3).channelFailedToConnect(any(Instant.class)); + verify(node4).channelFailedToConnect(any(Instant.class)); + } + + @Test + void testChannelFailedToConnectTimeout() { + TransactionResponse transactionResponse = new TransactionResponse( + new AccountId(3), + TransactionId.withValidStart(new AccountId(3), java.time.Instant.now()), + new byte[] {1, 2, 3}, + null); + var tx = new DummyTransaction(); + + tx.blockingUnaryCall = (grpcRequest) -> { + throw new StatusRuntimeException(io.grpc.Status.UNAVAILABLE); + }; + + when(node3.isHealthy()).thenReturn(true); + when(node3.channelFailedToConnect(any(Instant.class))).thenReturn(true); + + assertThatExceptionOfType(MaxAttemptsExceededException.class) + .isThrownBy(() -> transactionResponse.getReceipt(client, Duration.ofSeconds(2))); + } + + @Test + void executeQueryDelay() throws PrecheckStatusException, TimeoutException { + when(node3.isHealthy()).thenReturn(true); + when(node4.isHealthy()).thenReturn(true); + + when(node3.channelFailedToConnect()).thenReturn(false); + when(node4.channelFailedToConnect()).thenReturn(false); + + AtomicInteger i = new AtomicInteger(); + var tx = new DummyQuery() { + @Override + Status mapResponseStatus(org.hiero.sdk.proto.Response response) { + return Status.RECEIPT_NOT_FOUND; + } + + @Override + ExecutionState getExecutionState(Status status, Response response) { + return i.getAndIncrement() == 0 ? ExecutionState.RETRY : ExecutionState.SUCCESS; + } + }; + var nodeAccountIds = Arrays.asList(new AccountId(3), new AccountId(4), new AccountId(5)); + tx.setNodeAccountIds(nodeAccountIds); + + var receipt = org.hiero.sdk.proto.TransactionReceipt.newBuilder() + .setStatus(ResponseCodeEnum.OK) + .build(); + var receiptResp = org.hiero.sdk.proto.TransactionGetReceiptResponse.newBuilder() + .setReceipt(receipt) + .build(); + + var resp = Response.newBuilder().setTransactionGetReceipt(receiptResp).build(); + tx.blockingUnaryCall = (grpcRequest) -> resp; + tx.execute(client); + + verify(node3).channelFailedToConnect(any(Instant.class)); + verify(node4).channelFailedToConnect(any(Instant.class)); + } + + @Test + void executeUserError() throws PrecheckStatusException, TimeoutException { + when(node3.isHealthy()).thenReturn(true); + when(node3.channelFailedToConnect()).thenReturn(false); + + var tx = new DummyTransaction() { + @Override + Status mapResponseStatus(org.hiero.sdk.proto.TransactionResponse response) { + return Status.ACCOUNT_DELETED; + } + }; + var nodeAccountIds = Arrays.asList(new AccountId(3), new AccountId(4), new AccountId(5)); + tx.setNodeAccountIds(nodeAccountIds); + + var txResp = org.hiero.sdk.proto.TransactionResponse.newBuilder() + .setNodeTransactionPrecheckCode(ResponseCodeEnum.ACCOUNT_DELETED) + .build(); + + tx.blockingUnaryCall = (grpcRequest) -> txResp; + assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> tx.execute(client)); + + verify(node3).channelFailedToConnect(any(Instant.class)); + } + + @Test + void shouldRetryReturnsCorrectStates() { + var tx = new DummyTransaction(); + + assertThat(tx.getExecutionState(Status.PLATFORM_TRANSACTION_NOT_CREATED, null)) + .isEqualTo(ExecutionState.SERVER_ERROR); + assertThat(tx.getExecutionState(Status.PLATFORM_NOT_ACTIVE, null)).isEqualTo(ExecutionState.SERVER_ERROR); + assertThat(tx.getExecutionState(Status.BUSY, null)).isEqualTo(ExecutionState.RETRY); + assertThat(tx.getExecutionState(Status.OK, null)).isEqualTo(ExecutionState.SUCCESS); + assertThat(tx.getExecutionState(Status.ACCOUNT_DELETED, null)).isEqualTo(ExecutionState.REQUEST_ERROR); + } + + @Test + void shouldSetMaxRetry() { + var tx = new DummyTransaction(); + + tx.setMaxRetry(1); + + assertThat(tx.getMaxRetry()).isEqualTo(1); + + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> tx.setMaxRetry(0)); + } + + static class DummyTransaction> + extends Executable< + T, org.hiero.sdk.proto.Transaction, org.hiero.sdk.proto.TransactionResponse, TransactionResponse> { + + @Override + void onExecute(Client client) {} + + @Nullable + @Override + CompletableFuture onExecuteAsync(Client client) { + return null; + } + + @Nullable + @Override + org.hiero.sdk.proto.Transaction makeRequest() { + return null; + } + + @Nullable + @Override + TransactionResponse mapResponse( + org.hiero.sdk.proto.TransactionResponse response, + AccountId nodeId, + org.hiero.sdk.proto.Transaction request) { + return null; + } + + @Override + Status mapResponseStatus(org.hiero.sdk.proto.TransactionResponse response) { + return Status.OK; + } + + @Nullable + @Override + MethodDescriptor + getMethodDescriptor() { + return null; + } + + @Nullable + @Override + TransactionId getTransactionIdInternal() { + return null; + } + } + + static class DummyQuery extends Query { + @Override + void onExecute(Client client) {} + + @Override + TransactionReceipt mapResponse(Response response, AccountId nodeId, org.hiero.sdk.proto.Query request) { + return null; + } + + @Override + Status mapResponseStatus(org.hiero.sdk.proto.Response response) { + return Status.OK; + } + + @Override + MethodDescriptor getMethodDescriptor() { + return null; + } + + @Override + void onMakeRequest(org.hiero.sdk.proto.Query.Builder queryBuilder, QueryHeader header) {} + + @Override + ResponseHeader mapResponseHeader(Response response) { + return null; + } + + @Override + QueryHeader mapRequestHeader(org.hiero.sdk.proto.Query request) { + return null; + } + + @Override + void validateChecksums(Client client) {} + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/FeeAssessmentMethodTest.java b/sdk/src/test/java/org/hiero/sdk/FeeAssessmentMethodTest.java new file mode 100644 index 0000000000..6a5b3270f3 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/FeeAssessmentMethodTest.java @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class FeeAssessmentMethodTest { + @Test + void feeAssessmentMethodToString() { + assertThat(FeeAssessmentMethod.valueOf(true)).hasToString(FeeAssessmentMethod.EXCLUSIVE.toString()); + assertThat(FeeAssessmentMethod.valueOf(false)).hasToString(FeeAssessmentMethod.INCLUSIVE.toString()); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/FeeSchedulesTest.java b/sdk/src/test/java/org/hiero/sdk/FeeSchedulesTest.java new file mode 100644 index 0000000000..b918b16f40 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/FeeSchedulesTest.java @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class FeeSchedulesTest { + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + FeeSchedules spawnFeeSchedulesExample() { + return new FeeSchedules() + .setCurrent(new FeeSchedule() + .setExpirationTime(Instant.ofEpochSecond(1554158542)) + .addTransactionFeeSchedule(new TransactionFeeSchedule() + .addFee(new FeeData() + .setNodeData(new FeeComponents()) + .setNetworkData( + new FeeComponents().setMin(2).setMax(5)) + .setServiceData(new FeeComponents())))) + .setNext(new FeeSchedule() + .setExpirationTime(Instant.ofEpochSecond(1554158222)) + .addTransactionFeeSchedule(new TransactionFeeSchedule() + .addFee(new FeeData() + .setNodeData( + new FeeComponents().setMin(1).setMax(2)) + .setNetworkData(new FeeComponents()) + .setServiceData(new FeeComponents())))); + } + + @Test + void shouldSerialize() throws Exception { + var originalFeeSchedules = spawnFeeSchedulesExample(); + byte[] feeSchedulesBytes = originalFeeSchedules.toBytes(); + var copyFeeSchedules = FeeSchedules.fromBytes(feeSchedulesBytes); + assertThat(copyFeeSchedules.toString().replaceAll("@[A-Za-z0-9]+", "")) + .isEqualTo(originalFeeSchedules.toString().replaceAll("@[A-Za-z0-9]+", "")); + SnapshotMatcher.expect(originalFeeSchedules.toString().replaceAll("@[A-Za-z0-9]+", "")) + .toMatchSnapshot(); + } + + @Test + void shouldSerializeNull() throws Exception { + var originalFeeSchedules = new FeeSchedules(); + byte[] feeSchedulesBytes = originalFeeSchedules.toBytes(); + var copyFeeSchedules = FeeSchedules.fromBytes(feeSchedulesBytes); + assertThat(copyFeeSchedules.toString()).isEqualTo(originalFeeSchedules.toString()); + } +} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/FeeSchedulesTest.snap b/sdk/src/test/java/org/hiero/sdk/FeeSchedulesTest.snap similarity index 96% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/FeeSchedulesTest.snap rename to sdk/src/test/java/org/hiero/sdk/FeeSchedulesTest.snap index 5e4ae7ebeb..15fad2d09a 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/FeeSchedulesTest.snap +++ b/sdk/src/test/java/org/hiero/sdk/FeeSchedulesTest.snap @@ -1,3 +1,3 @@ -com.hedera.hashgraph.sdk.FeeSchedulesTest.shouldSerialize=[ +org.hiero.sdk.FeeSchedulesTest.shouldSerialize=[ "FeeSchedules{current=FeeSchedule{transactionFeeSchedules=[TransactionFeeSchedule{requestType=NONE, feeData=null, fees=[FeeData{nodeData=FeeComponents{min=0, max=0, constant=0, transactionBandwidthByte=0, transactionVerification=0, transactionRamByteHour=0, transactionStorageByteHour=0, contractTransactionGas=0, transferVolumeHbar=0, responseMemoryByte=0, responseDiskByte=0}, networkData=FeeComponents{min=2, max=5, constant=0, transactionBandwidthByte=0, transactionVerification=0, transactionRamByteHour=0, transactionStorageByteHour=0, contractTransactionGas=0, transferVolumeHbar=0, responseMemoryByte=0, responseDiskByte=0}, serviceData=FeeComponents{min=0, max=0, constant=0, transactionBandwidthByte=0, transactionVerification=0, transactionRamByteHour=0, transactionStorageByteHour=0, contractTransactionGas=0, transferVolumeHbar=0, responseMemoryByte=0, responseDiskByte=0}, type=DEFAULT}]}], expirationTime=2019-04-01T22:42:22Z}, next=FeeSchedule{transactionFeeSchedules=[TransactionFeeSchedule{requestType=NONE, feeData=null, fees=[FeeData{nodeData=FeeComponents{min=1, max=2, constant=0, transactionBandwidthByte=0, transactionVerification=0, transactionRamByteHour=0, transactionStorageByteHour=0, contractTransactionGas=0, transferVolumeHbar=0, responseMemoryByte=0, responseDiskByte=0}, networkData=FeeComponents{min=0, max=0, constant=0, transactionBandwidthByte=0, transactionVerification=0, transactionRamByteHour=0, transactionStorageByteHour=0, contractTransactionGas=0, transferVolumeHbar=0, responseMemoryByte=0, responseDiskByte=0}, serviceData=FeeComponents{min=0, max=0, constant=0, transactionBandwidthByte=0, transactionVerification=0, transactionRamByteHour=0, transactionStorageByteHour=0, contractTransactionGas=0, transferVolumeHbar=0, responseMemoryByte=0, responseDiskByte=0}, type=DEFAULT}]}], expirationTime=2019-04-01T22:37:02Z}}" -] \ No newline at end of file +] diff --git a/sdk/src/test/java/org/hiero/sdk/FileAppendTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/FileAppendTransactionTest.java new file mode 100644 index 0000000000..cf4466c23c --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/FileAppendTransactionTest.java @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.bouncycastle.util.encoders.Hex; +import org.hiero.sdk.proto.FileAppendTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class FileAppendTransactionTest { + public static final String BIG_CONTENTS = + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur aliquam augue sem, ut mattis dui laoreet a. Curabitur consequat est euismod, scelerisque metus et, tristique dui. Nulla commodo mauris ut faucibus ultricies. Quisque venenatis nisl nec augue tempus, at efficitur elit eleifend. Duis pharetra felis metus, sed dapibus urna vehicula id. Duis non venenatis turpis, sit amet ornare orci. Donec non interdum quam. Sed finibus nunc et risus finibus, non sagittis lorem cursus. Proin pellentesque tempor aliquam. Sed congue nisl in enim bibendum, condimentum vehicula nisi feugiat.\n" + + "\n" + + "Suspendisse non sodales arcu. Suspendisse sodales, lorem ac mollis blandit, ipsum neque porttitor nulla, et sodales arcu ante fermentum tellus. Integer sagittis dolor sed augue fringilla accumsan. Cras vitae finibus arcu, sit amet varius dolor. Etiam id finibus dolor, vitae luctus velit. Proin efficitur augue nec pharetra accumsan. Aliquam lobortis nisl diam, vel fermentum purus finibus id. Etiam at finibus orci, et tincidunt turpis. Aliquam imperdiet congue lacus vel facilisis. Phasellus id magna vitae enim dapibus vestibulum vitae quis augue. Morbi eu consequat enim. Maecenas neque nulla, pulvinar sit amet consequat sed, tempor sed magna. Mauris lacinia sem feugiat faucibus aliquet. Etiam congue non turpis at commodo. Nulla facilisi.\n" + + "\n" + + "Nunc velit turpis, cursus ornare fringilla eu, lacinia posuere leo. Mauris rutrum ultricies dui et suscipit. Curabitur in euismod ligula. Curabitur vitae faucibus orci. Phasellus volutpat vestibulum diam sit amet vestibulum. In vel purus leo. Nulla condimentum lectus vestibulum lectus faucibus, id lobortis eros consequat. Proin mollis libero elit, vel aliquet nisi imperdiet et. Morbi ornare est velit, in vehicula nunc malesuada quis. Donec vehicula convallis interdum.\n" + + "\n" + + "Integer pellentesque in nibh vitae aliquet. Ut at justo id libero dignissim hendrerit. Interdum et malesuada fames ac ante ipsum primis in faucibus. Praesent quis ornare lectus. Nam malesuada non diam quis cursus. Phasellus a libero ligula. Suspendisse ligula elit, congue et nisi sit amet, cursus euismod dolor. Morbi aliquam, nulla a posuere pellentesque, diam massa ornare risus, nec eleifend neque eros et elit.\n" + + "\n" + + "Pellentesque a sodales dui. Sed in efficitur ante. Duis eget volutpat elit, et ornare est. Nam felis dolor, placerat mattis diam id, maximus lobortis quam. Sed pellentesque lobortis sem sed placerat. Pellentesque augue odio, molestie sed lectus sit amet, congue volutpat massa. Quisque congue consequat nunc id fringilla. Duis semper nulla eget enim venenatis dapibus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Pellentesque varius turpis nibh, sit amet malesuada mauris malesuada quis. Vivamus dictum egestas placerat. Vivamus id augue elit.\n" + + "\n" + + "Cras fermentum volutpat eros, vitae euismod lorem viverra nec. Donec lectus augue, porta eleifend odio sit amet, condimentum rhoncus urna. Nunc sed odio velit. Morbi non cursus odio, eget imperdiet erat. Nunc rhoncus massa non neque volutpat, sit amet faucibus ante congue. Phasellus nec lorem vel leo accumsan lobortis. Maecenas id ligula bibendum purus suscipit posuere ac eget diam. Nam in quam pretium, semper erat auctor, iaculis odio. Maecenas placerat, nisi ac elementum tempor, felis nulla porttitor orci, ac rhoncus diam justo in elit. Etiam lobortis fermentum ligula in tincidunt. Donec quis vestibulum nunc. Sed eros diam, interdum in porta lobortis, gravida eu magna. Donec diam purus, finibus et sollicitudin quis, fringilla nec nisi. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Curabitur ultricies sagittis dapibus. Etiam ullamcorper aliquet libero, eu venenatis mauris suscipit id.\n" + + "\n" + + "Ut interdum eleifend sem, vel bibendum ipsum volutpat eget. Nunc ac dignissim augue. Aliquam ornare aliquet magna id dignissim. Vestibulum velit sem, lacinia eu rutrum in, rhoncus vitae mauris. Pellentesque scelerisque pulvinar mauris non cursus. Integer id dolor porta, bibendum sem vel, pretium tortor. Fusce a nisi convallis, interdum quam condimentum, suscipit dolor. Sed magna diam, efficitur non nunc in, tincidunt varius mi. Aliquam ullamcorper nulla eu fermentum bibendum. Vivamus a felis pretium, hendrerit enim vitae, hendrerit leo. Suspendisse lacinia lectus a diam consectetur suscipit. Aenean hendrerit nisl sed diam venenatis pellentesque. Nullam egestas lectus a consequat pharetra. Vivamus ornare tellus auctor, facilisis lacus id, feugiat dui. Nam id est ut est rhoncus varius.\n" + + "\n" + + "Aenean vel vehicula erat. Aenean gravida risus vitae purus sodales, quis dictum enim porta. Ut elit elit, fermentum sed posuere non, accumsan eu justo. Integer porta malesuada quam, et elementum massa suscipit nec. Donec in elit diam. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis suscipit vel ante volutpat vestibulum.\n" + + "\n" + + "Pellentesque ex arcu, euismod et sapien ut, vulputate suscipit enim. Donec mattis sagittis augue, et mattis lacus. Cras placerat consequat lorem sed luctus. Nam suscipit aliquam sem ac imperdiet. Mauris a semper augue, pulvinar fringilla magna. Integer pretium massa non risus commodo hendrerit. Sed dictum libero id erat sodales mattis. Etiam auctor dolor lectus, ut sagittis enim lobortis vitae. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur nec orci lobortis, cursus risus eget, sollicitudin massa. Integer vel tincidunt mi, id eleifend quam. Nullam facilisis nisl eu mauris congue, vitae euismod nisi malesuada. Vivamus sit amet urna et libero sagittis dictum.\n" + + "\n" + + "In hac habitasse platea dictumst. Aliquam erat volutpat. Ut dictum, mi a viverra venenatis, mi urna pulvinar nisi, nec gravida lectus diam eget urna. Ut dictum sit amet nisl ut dignissim. Sed sed mauris scelerisque, efficitur augue in, vulputate turpis. Proin dolor justo, bibendum et sollicitudin feugiat, imperdiet sed mi. Sed elementum vitae massa vel lobortis. Cras vitae massa sit amet libero dictum tempus.\n" + + "\n" + + "Vivamus ut mauris lectus. Curabitur placerat ornare scelerisque. Cras malesuada nunc quis tortor pretium bibendum vel sed dui. Cras lobortis nibh eu erat blandit, sit amet consequat neque fermentum. Phasellus imperdiet molestie tristique. Cras auctor purus erat, id mollis ligula porttitor eget. Mauris porta pharetra odio et finibus. Suspendisse eu est a ligula bibendum cursus. Mauris ac laoreet libero. Nullam volutpat sem quis rhoncus gravida.\n" + + "\n" + + "Donec malesuada lacus ac iaculis cursus. Sed sem orci, feugiat ac est ut, ultricies posuere nisi. Suspendisse potenti. Phasellus ut ultricies purus. Etiam sem tortor, fermentum quis aliquam eget, consequat ut nulla. Aliquam dictum metus in mi fringilla, vel gravida nulla accumsan. Cras aliquam eget leo vel posuere. Vivamus vitae malesuada nunc. Morbi placerat magna mi, id suscipit lacus auctor quis. Nam at lorem vel odio finibus fringilla ut ac velit. Donec laoreet at nibh quis vehicula.\n" + + "\n" + + "Fusce ac tristique nisi. Donec leo nisi, consectetur at tellus sit amet, consectetur ultrices dui. Quisque gravida mollis tempor. Maecenas semper, sapien ut dignissim feugiat, massa enim viverra dolor, non varius eros nulla nec felis. Nunc massa lacus, ornare et feugiat id, bibendum quis purus. Praesent viverra elit sit amet purus consectetur venenatis. Maecenas nibh risus, elementum sit amet enim sagittis, ultrices malesuada lectus. Vivamus non felis ante. Ut vulputate ex arcu. Aliquam porta gravida porta. Aliquam eros leo, malesuada quis eros non, maximus tristique neque. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam ligula orci, mollis vel luctus nec, venenatis vitae est. Fusce rutrum convallis nisi.\n" + + "\n" + + "Nunc laoreet eget nibh in feugiat. Pellentesque nec arcu cursus, gravida dolor a, pellentesque nisi. Praesent vel justo blandit, placerat risus eget, consectetur orci. Sed maximus metus mi, ut euismod augue ultricies in. Nunc id risus hendrerit, aliquet lorem nec, congue justo. Vestibulum vel nunc ac est euismod mattis ac vitae nulla. Donec blandit luctus mauris, sit amet bibendum dui egestas et. Aenean nec lorem nec elit ornare rutrum nec eget ligula. Fusce a ipsum vitae neque elementum pharetra. Pellentesque ullamcorper ullamcorper libero, vitae porta sem sagittis vel. Interdum et malesuada fames ac ante ipsum primis in faucibus.\n" + + "\n" + + "Duis at massa sit amet risus pellentesque varius sit amet vitae eros. Cras tempor aliquet sapien, vehicula varius neque volutpat et. Donec purus nibh, pellentesque ut lobortis nec, ultricies ultricies nisl. Sed accumsan ut dui sit amet vulputate. Suspendisse eu facilisis massa, a hendrerit mauris. Nulla elementum molestie tincidunt. Donec mi justo, ornare vel tempor id, gravida et orci. Nam molestie erat nec nisi bibendum accumsan. Duis vitae tempor ante. Morbi congue mauris vel sagittis facilisis. Vivamus vehicula odio orci, a tempor nibh euismod in. Proin mattis, nibh at feugiat porta, purus velit posuere felis, quis volutpat sapien dui vel odio. Nam fermentum sem nec euismod aliquet. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Aliquam erat volutpat.\n" + + "\n" + + "Mauris congue lacus tortor. Pellentesque arcu eros, accumsan imperdiet porttitor vitae, mattis nec justo. Nullam ac aliquam mauris. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Suspendisse potenti. Fusce accumsan tempus felis a sagittis. Maecenas et velit odio. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Aliquam eros lacus, volutpat non urna sed, tincidunt ullamcorper elit. Sed sit amet gravida libero. In varius mi vel diam sollicitudin mollis.\n" + + "\n" + + "Aenean varius, diam vitae hendrerit feugiat, libero augue ultrices odio, eget consequat sem tellus eu nisi. Nam dapibus enim et auctor sollicitudin. Nunc iaculis eros orci, ac accumsan eros malesuada ut. Ut semper augue felis, nec sodales lorem consectetur non. Cras gravida eleifend est, et sagittis eros imperdiet congue. Fusce id tellus dapibus nunc scelerisque tempus. Donec tempor placerat libero, in commodo nisi bibendum eu. Donec id eros non est sollicitudin luctus. Duis bibendum bibendum tellus nec viverra. Aliquam non faucibus ex, nec luctus dui. Curabitur efficitur varius urna non dignissim. Suspendisse elit elit, ultrices in elementum eu, tempor at magna.\n" + + "\n" + + "Nunc in purus sit amet mi laoreet pulvinar placerat eget sapien. Donec vel felis at dui ultricies euismod quis vel neque. Donec tincidunt urna non eros pretium blandit. Nullam congue tincidunt condimentum. Curabitur et libero nibh. Proin ultricies risus id imperdiet scelerisque. Suspendisse purus mi, viverra vitae risus ut, tempus tincidunt enim. Ut luctus lobortis nisl, eget venenatis tortor cursus non. Mauris finibus nisl quis gravida ultricies. Fusce elementum lacus sit amet nunc congue, in porta nulla tincidunt.\n" + + "\n" + + "Mauris ante risus, imperdiet blandit posuere in, blandit eu ipsum. Integer et auctor arcu. Integer quis elementum purus. Nunc in ultricies nisl, sed rutrum risus. Suspendisse venenatis eros nec lorem rhoncus, in scelerisque velit condimentum. Etiam condimentum quam felis, in elementum odio mattis et. In ut nibh porttitor, hendrerit tellus vel, luctus magna. Vestibulum et ligula et dolor pellentesque porta. Aenean efficitur porta massa et bibendum. Nulla vehicula sem in risus volutpat, a eleifend elit maximus.\n" + + "\n" + + "Proin orci lorem, auctor a felis eu, pretium lobortis nulla. Phasellus aliquam efficitur interdum. Sed sit amet velit rutrum est dictum facilisis. Duis cursus enim at nisl tincidunt, eu molestie elit vehicula. Cras pellentesque nisl id enim feugiat fringilla. In quis tincidunt neque. Nam eu consectetur dolor. Ut id interdum mauris. Mauris nunc tortor, placerat in tempor ut, vestibulum eu nisl. Integer vel dapibus ipsum. Nunc id erat pulvinar, tincidunt magna id, condimentum massa. Pellentesque consequat est eget odio placerat vehicula. Etiam augue neque, sagittis non leo eu, tristique scelerisque dui. Ut dui urna, blandit quis urna ac, tincidunt tristique turpis.\n" + + "\n" + + "Suspendisse venenatis rhoncus ligula ultrices condimentum. In id laoreet eros. Suspendisse suscipit fringilla leo id euismod. Sed in quam libero. Ut at ligula tellus. Sed tristique gravida dui, at egestas odio aliquam iaculis. Praesent imperdiet velit quis nibh consequat, quis pretium sem sagittis. Donec tortor ex, congue sit amet pulvinar ac, rutrum non est. Praesent ipsum magna, venenatis sit amet vulputate id, eleifend ac ipsum.\n" + + "\n" + + "In consequat, nisi iaculis laoreet elementum, massa mauris varius nisi, et porta nisi velit at urna. Maecenas sit amet aliquet eros, a rhoncus nisl. Maecenas convallis enim nunc. Morbi purus nisl, aliquam ac tincidunt sed, mattis in augue. Quisque et elementum quam, vitae maximus orci. Suspendisse hendrerit risus nec vehicula placerat. Nulla et lectus nunc. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.\n" + + "\n" + + "Etiam ut sodales ex. Nulla luctus, magna eu scelerisque sagittis, nibh quam consectetur neque, non rutrum dolor metus nec ex. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed egestas augue elit, sollicitudin accumsan massa lobortis ac. Curabitur placerat, dolor a aliquam maximus, velit ipsum laoreet ligula, id ullamcorper lacus nibh eget nisl. Donec eget lacus venenatis enim consequat auctor vel in.\n"; + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + private static final PrivateKey secondPrivateKey = PrivateKey.fromString( + "302e020100300506032b65700422042099b8587e5abccf6999b0d42b88c581c45284290450487ce90095561c85af11e4"); + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction(Collections.singletonList(AccountId.fromString("0.0.5005"))) + .toString()) + .toMatchSnapshot(); + } + + private FileAppendTransaction spawnTestTransaction(List accountIds) { + return new FileAppendTransaction() + .setNodeAccountIds(accountIds) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setFileId(FileId.fromString("0.0.6006")) + .setContents(new byte[] {1, 2, 3, 4}) + .setMaxTransactionFee(Hbar.fromTinybars(100_000)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new FileAppendTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldSerializeBigContents() { + var nodeAccountIds = new ArrayList(); + nodeAccountIds.add(AccountId.fromString("0.0.444")); + nodeAccountIds.add(AccountId.fromString("0.0.555")); + + SnapshotMatcher.expect(spawnTestTransactionBigContents(nodeAccountIds).toString()) + .toMatchSnapshot(); + } + + private FileAppendTransaction spawnTestTransactionBigContents(ArrayList nodeAccountIds) { + return new FileAppendTransaction() + .setNodeAccountIds(nodeAccountIds) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setFileId(FileId.fromString("0.0.6006")) + .setContents(BIG_CONTENTS) + .setMaxTransactionFee(Hbar.fromTinybars(100_000)) + .freeze() + .sign(unusedPrivateKey); + } + + String hashesToString(List> hashes) { + var outString = new StringBuilder(); + outString.append("["); + for (var tx : hashes) { + outString.append("{"); + for (var entry : tx.entrySet()) { + outString + .append(entry.getKey().toString()) + .append("=") + .append(Hex.toHexString(entry.getValue())) + .append(", "); + } + outString.append("}, "); + } + return outString + "]"; + } + + @Test + void shouldHash() { + var nodeAccountIds = new ArrayList(); + nodeAccountIds.add(AccountId.fromString("0.0.444")); + nodeAccountIds.add(AccountId.fromString("0.0.555")); + + SnapshotMatcher.expect(hashesToString( + spawnTestTransactionBigContents(nodeAccountIds).getAllTransactionHashesPerNode())) + .toMatchSnapshot(); + } + + String signaturesToString(Map> signatures) { + var outString = new StringBuilder(); + outString.append("{"); + for (var nodeEntry : signatures.entrySet()) { + outString.append(nodeEntry.getKey()).append("={"); + for (var sigEntry : nodeEntry.getValue().entrySet()) { + outString + .append(sigEntry.getKey()) + .append("=") + .append(Hex.toHexString(sigEntry.getValue())) + .append(", "); + } + outString.append("}, "); + } + return outString + "}"; + } + + String allSignaturesToString(List>> allSignatures) { + var outString = new StringBuilder(); + outString.append("["); + for (var txEntry : allSignatures) { + outString.append(signaturesToString(txEntry)).append(", "); + } + return outString + "]"; + } + + @Test + void shouldGetSignatures() { + var nodeAccountIds = new ArrayList(); + nodeAccountIds.add(AccountId.fromString("0.0.444")); + nodeAccountIds.add(AccountId.fromString("0.0.555")); + var signatures = + spawnTestTransaction(nodeAccountIds).sign(secondPrivateKey).getSignatures(); + SnapshotMatcher.expect(signaturesToString(signatures)).toMatchSnapshot(); + } + + @Test + void shouldGetAllSignatures() { + var nodeAccountIds = new ArrayList(); + nodeAccountIds.add(AccountId.fromString("0.0.444")); + nodeAccountIds.add(AccountId.fromString("0.0.555")); + var signatures = spawnTestTransactionBigContents(nodeAccountIds) + .sign(secondPrivateKey) + .getAllSignatures(); + SnapshotMatcher.expect(allSignaturesToString(signatures)).toMatchSnapshot(); + } + + @Test + void shouldBytes() throws Exception { + var nodeAccountIds = new ArrayList(); + nodeAccountIds.add(AccountId.fromString("0.0.444")); + nodeAccountIds.add(AccountId.fromString("0.0.555")); + var tx = spawnTestTransactionBigContents(nodeAccountIds); + var tx2 = FileAppendTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + + assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> tx2.getTransactionHash()); + assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> tx2.getTransactionHashPerNode()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setFileAppend(FileAppendTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(FileAppendTransaction.class); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/FileAppendTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/FileAppendTransactionTest.snap new file mode 100644 index 0000000000..236754fa7a --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/FileAppendTransactionTest.snap @@ -0,0 +1,23 @@ +org.hiero.sdk.FileAppendTransactionTest.shouldGetAllSignatures=[ + "[{0.0.555={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=40d079bcb4a9fbdf102051328ee5f3903e166c1dd7c1e748efc95a2fdd640e421c0a8e23709b61e4d8110451adecd71cd16d1293ce0e57ea974de5b6b2ef070e, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=0cbdeaac11d392081289fb67a93a7fd995e86f55764e45b3e5eefd2f64056a38b50e8478e64cb1af5c310c321478e09b60d641a6e898820625d2674dc54ce80e, }, 0.0.444={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=1490ad50f0fb5af40ee1d823e4f4b2603c135b3c7979b1b0ee6a323ba494e0b5cca833157c1106b976c876b060249e7ee2db30a8566d8682a42fac2f5f4a3406, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=23a657e362da4b3ea29673c26d8b17b967c53528449d99b3154e1b81676dc8d0aa229b824f9e613803b3262bc9cecc6dc6e2fc01a21830fb1641b5ed03722e07, }, }, {0.0.555={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=4b4ed404ef9a37752c070c5eb364b0df0fbe06afb0b64e0743d27767c26b1045e8532308288e2f1a40cced0782b87f20b6cfdcd8b9890c600c0e2146a48ace00, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=95444670cecb07fc452a4e3f350eca481e5462e3349a6741cd4e6746b21c66e67825c169f3b4cf82ab1853f1cf0acedc84b2f565346dc173a93f054c19966b08, }, 0.0.444={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=ce2b975f698ca802721c80a3792fca39d73609afaa8961bb91da569463c50eecb07d7e26e7e8febb5163474139e94533e8eb7016e0c149e0823d7102ed2b0502, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=ea31e49f64fd41773561f81459a2381e0194788853e28ffb683caded7a675d386b2a228c67ec83bfc448f14ee4fd2eae41569f06006c515621e1f26514c9bf0a, }, }, {0.0.555={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=0aedd8f92e68278823397cb0eddc1c07f1fd80c223781280a0ffb0eef21b516444e4521294ad380e8a46c7f22f1723eef8872ccf4d8aa415345e0a7fe1485c06, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=8cf7f0ae116557d5f4a5bbee82dee3cfe950c0ecedbce5d2a7b729dd7614d47c436e6951c401870f85dadd696160ecee1ab8e65aa607ca942051e78b8f09a406, }, 0.0.444={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=47c3e240259603f3e7fa41ced3eb9931bc4350367aa6529994c048fb0aca1853630bf8fbbdf9b2768c52b91b5f5d669a4adab06ef0851107f6e8012d948ba90b, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=d1a4d3349a1bc7cd0242e09ed9b1811ea99fb6123826b946651ee8c9dd593eb40591697fad4360e3941c9eea55cd2173189df349aa16b9a11db62ed28de8ad06, }, }, {0.0.555={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=aa009ad11594bb981504f8d4063c9d90bfb0bccc4bda8f9e3ffd326fc39e599fae94ba98110342a449437f7c848e2a9dd6de0d008e8d00c8387ff094c93c2700, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=5a2b40589f128f3bac99649d0aec7230e3c993130f36814c742ae37104e93d22e2b548d97e471f583d1c81bbcf6f916cc03f20375499e3a37b2c6b13a5a94507, }, 0.0.444={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=63800201fd378e81946c73104907b0b0aa70ff5913889b21b4eac730e535f8acbf08f63da5824419fc14155d6fc19f23a53bfd3324d8baa68dc5d00e8974de07, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=084bdc8f8580a2ebcc0a5140f766aca111b5488092a41871d104834dda27d6b99288423835bc7114860ce6c740300ebe9f8ab576e7646097eee7d6692b90d109, }, }, {0.0.555={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=645d9d66e90a104462741e2f49fe77d2edce5d49a033c9c51768a2fba6deda96a71ad95e00416bb5a8a07cfaf543a1fee8730c11af4a380d6f30ec88d2271c0d, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=2bc0f7e72aa2e953741815ac95135e50ece67543683e6da3a26bc95534d9ca538d802d567fcb154bf98001663dc46d8674ff4e9361021fbe2a55e83f9442f802, }, 0.0.444={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=e83759154409ce22146e31ff826a3ee4940916784e5953c4c0502ef5c2f9e7ca2d114223c3461a491dfebdcc11e511e91400007a1dc3827b7b98c7225239460f, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=595653b3f5cedf84e845f0dc2c503aad94b259eee4c10ad32cd86976b246cd81726ae8edc35e41e9ebae9e27a3ed96aff524b9b04481fad2722fdab8a4ee5a0f, }, }, {0.0.555={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=dea30ba3201c32f5c23afac9d5b013c4e41a30d08205d21a09722f5aebb0760b2452ba254eea5008de68411560081e94ccd78c5008353aa6bb64779e79078702, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=903762ba6159fcf940281d810a268980cdf2fa35683626d52e41ad18202492fef5f8de2002c49cb56ac9d9e78177823f1f97741634237943459c31540f801502, }, 0.0.444={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=c463b37297427a620f739b3a420512f897745a8451315de360391b2f6879e644ba3a8597e4f6e3a1a135bf354bb0911fe421e0174f48b650f54e3fe86247e507, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=342e41dbd4e3ef300192767ffeb9b062b1fae68027153a84f151fdb1d3ce43592f275d87a7b90a9d6db6a67d32c6ec9e585ce199ed199cdcac0b4ef4ac12200f, }, }, {0.0.555={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=9e4dcb75574c75c7e54cc76ff08bf9e6c28cb2e74f79eb4ed8e4f968e6829bd994d9fadff9fb265988de757b858e4921c7b4db119c31f6156a79eb62ffc4110c, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=195fdc0b4c9730139ede43d8e80befe7bef91ab8fa2f271173bab926cff2c797117a28716112b6d1ab3b6c669a4b923f5885b40f67885b1a64d0445ad75bbe0e, }, 0.0.444={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=0e3e02a08fd3a807c79c1ce2117612c2902557aa7c57da2494744f45b105f3f67b9977564f83baf2cd7dd4fe2cf0421f9367343a92d90199e97f22bc977f3d0f, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=a9ef610d944c989424477a2e31b970033bffcce8899116820f9eac06f2d06226cfe714e6a835135e2fb632dda80b439199e76870156acd6f81d4c3cf342a3706, }, }, ]" +] + + +org.hiero.sdk.FileAppendTransactionTest.shouldGetSignatures=[ + "{0.0.555={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=a762395ad7e3fa298ed990785699133ca64d11a28fb4dfc83d6d95a9a2639b688d5afc288fcdb8168f4a85876b2b785e3303ddec38666bbae4f5436d6334540b, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=8bea3d52ad5badc4ebf5e20f874cf2e5dec3189d3a471c39fd1062743ba7729de1c484f1e8ac2f15343494799e97b5ac220734a261994d91ffed9e113c09d700, }, 0.0.444={302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7=4b8fd70342b6c201f08177de53122be909d5d72f97d29fba69e7ac6ef04d96b19ac417c9b84fbb122bce9c8323bc38bd6165b5750ce16ee4a2f23e28d7123006, 302a300506032b657003210072a1a2b6b694902f095ac85c6a5fa96ab1a87933e3fc1fd66e57ac6af628ca2d=371b502056cbf34c1bd7f9891e82c7a53ab8d92ce1728f790acc9a09989196fbf4c1c111ca010a19f1af82f2480be92229b5a1aadfe4af9142bd212254c88b08, }, }" +] + + +org.hiero.sdk.FileAppendTransactionTest.shouldHash=[ + "[{0.0.555=d2f10044785bb7ecbfc024a171b28ec877a123a2b6f3b2a48db5595208ea8bd036e4c4babb682d87e06ce62d7c2b1bb3, 0.0.444=38603d6b2a449030ea49b7cbd7e19cd66b4e3f2891ca40df861b4840418cb08575b7ce25426b1192545d352a89f0f3da, }, {0.0.555=74ed3e44b14e41ad5964a52adde1c32932b9dd75f589d0985504bbc483dc778493866ae879769d693e2a6fa80c6a118c, 0.0.444=0939a5cbfb9bbbd545d3e9f961e7fc3bcbf49553ce7e3a00414b5b0964ca9aae0ea878e280efa217503aed65d1057880, }, {0.0.555=9ef69da0531b3828cfc5b14cb5df373ae46922ad192df9d488684b845ce4754b2a20c1e966d2835ea178890fdaac9a00, 0.0.444=1332b1dfb679ea2a91b21de2bed0d086c5139893fce55b0471e2b08ec2e0fbbcee48783eda0a1e876ee0a7086416c26a, }, {0.0.555=e62ca0554d6e40461dbecc035165d35716d61eae59a305414a91a6fb85f36ba3adc1f6b963c429366485fe514407d294, 0.0.444=2deb2d9674c3e238c53080ad4d139f172dc907ba4b898210ed9359f3db92f0880503cca4fec35cd6b47c71ba5dd46aa3, }, {0.0.555=8a7721e7a8d8eede13e0ab5adb5c9520af6099c909592f45f9dc89775f69e16a2bea4a392480f1b750c817787a94099f, 0.0.444=dbb52ed2eea79c5cc58bb0b898ce03f4ee7d4ba6ad94c0d718c0c6b46fd8cea9e2ab76c42221fce44933827e382df57f, }, {0.0.555=0292097e1f3bdd4390134f10b51b2c9928fcfbe4fd54de25d407ee8b93c6ede6dda4c4263786d983de47413f541834e0, 0.0.444=16c12a7acaa5754a1e1adfc40550e5d4b09195c2347dc4fda3882d7a2823dd212ea1682642f211290b8985d45be55a0b, }, {0.0.555=e4c53e598718933cc67491218c478c23be4637e9d89107a3c1699f04143c421fd6bf5f2ff11d31ea646103cd7cd00b57, 0.0.444=5571dfcd8a279d4c33c0e9199c865556ab3aec478ddbc1560dd9c41c53423decb95348fb54b63cbc744fd1e496b2a01f, }, ]" +] + + +org.hiero.sdk.FileAppendTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nfile_append {\n contents: \"\\001\\002\\003\\004\"\n file_i_d {\n file_num: 6006\n realm_num: 0\n shard_num: 0\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] + + +org.hiero.sdk.FileAppendTransactionTest.shouldSerializeBigContents=[ + "# org.hiero.sdk.proto.TransactionBody\nfile_append {\n contents: \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur aliquam augue sem, ut mattis dui laoreet a. Curabitur consequat est euismod, scelerisque metus et, tristique dui. Nulla commodo mauris ut faucibus ultricies. Quisque venenatis nisl nec augue tempus, at efficitur elit eleifend. Duis pharetra felis metus, sed dapibus urna vehicula id. Duis non venenatis turpis, sit amet ornare orci. Donec non interdum quam. Sed finibus nunc et risus finibus, non sagittis lorem cursus. Proin pellentesque tempor aliquam. Sed congue nisl in enim bibendum, condimentum vehicula nisi feugiat.\\n\\nSuspendisse non sodales arcu. Suspendisse sodales, lorem ac mollis blandit, ipsum neque porttitor nulla, et sodales arcu ante fermentum tellus. Integer sagittis dolor sed augue fringilla accumsan. Cras vitae finibus arcu, sit amet varius dolor. Etiam id finibus dolor, vitae luctus velit. Proin efficitur augue nec pharetra accumsan. Aliquam lobortis nisl diam, vel fermentum purus finibus id. Etiam at finibus orci, et tincidunt turpis. Aliquam imperdiet congue lacus vel facilisis. Phasellus id magna vitae enim dapibus vestibulum vitae quis augue. Morbi eu consequat enim. Maecenas neque nulla, pulvinar sit amet consequat sed, tempor sed magna. Mauris lacinia sem feugiat faucibus aliquet. Etiam congue non turpis at commodo. Nulla facilisi.\\n\\nNunc velit turpis, cursus ornare fringilla eu, lacinia posuere leo. Mauris rutrum ultricies dui et suscipit. Curabitur in euismod ligula. Curabitur vitae faucibus orci. Phasellus volutpat vestibulum diam sit amet vestibulum. In vel purus leo. Nulla condimentum lectus vestibulum lectus faucibus, id lobortis eros consequat. Proin mollis libero elit, vel aliquet nisi imperdiet et. Morbi ornare est velit, in vehicula nunc malesuada quis. Donec vehicula convallis interdum.\\n\\nInteger pellentesque in nibh vitae aliquet. Ut at justo id libero dignissim hendrerit. Interdum et malesuada fames ac ante ipsum primis in faucibus. Praesent quis ornare lectus. Nam malesuada non diam quis cursus. Phasellus a libero ligula. Suspendisse ligula elit, congue et nisi sit amet, cursus euismod dolor. Morbi aliquam, nulla a posuere pellentesque, diam massa ornare risus, nec eleifend neque eros et elit.\\n\\nPellentesque a sodales dui. Sed in efficitur ante. Duis eget volutpat elit, et ornare est. Nam felis dolor, placerat mattis diam id, maximus lobortis quam. Sed pellentesque lobortis sem sed placerat. Pellentesque augue odio, molestie sed lectus sit amet, congue volutpat massa. Quisque congue consequat nunc id fringilla. Duis semper nulla eget enim venenatis dapibus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Pellentesque varius turpis nibh, sit amet malesuada mauris malesuada quis. Vivamus dictum egestas placerat. Vivamus id augue elit.\\n\\nCras fermentum volutpat eros, vitae euismod lorem viverra nec. Donec lectus augue, porta eleifend odio sit amet, condimentum rhoncus urna. Nunc sed odio velit. Morbi non cursus odio, eget imperdiet erat. Nunc rhoncus massa non neque volutpat, sit amet faucibus ante congue. Phasellus nec lorem vel leo accumsan lobortis. Maecenas id ligula bibendum purus suscipit posuere ac eget diam. Nam in quam pretium, semper erat auctor, iaculis odio. Maecenas placerat, nisi ac elementum tempor, felis nulla porttitor orci, ac rhoncus diam justo in elit. Etiam lobortis fermentum ligula in tincidunt. Donec quis vestibulum nunc. Sed eros diam, interdum in porta lobortis, gravida eu magna. Donec diam purus, finibus et sollicitudin quis, fringilla nec nisi. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Curabitur ultricies sagittis dapibus. Etiam ullamcorper aliquet libero, eu venenatis mauris suscipit id.\\n\\nUt interdum eleifend sem, vel bibendum ipsum volutpat eget. Nunc ac dignissim augue. Aliquam ornare aliquet magna id dignissim. Vestibulum velit sem, lacinia eu rutrum in, rhoncus vitae mauris. Pellentesque scelerisque pulvinar mauris non cursus. Integer id dolor porta, bibendum sem vel, pretium tortor. Fusce a nisi convallis, interdum quam condimentum, suscipit dolor. Sed magna diam, efficitur non nunc in, tincidunt varius mi. Aliquam ullamcorper nulla eu fermentum bibendum. Vivamus a felis pretium, hendrerit enim vitae, hendrerit leo. Suspendisse lacinia lectus a diam consectetur suscipit. Aenean hendrerit nisl sed diam venenatis pellentesque. Nullam egestas lectus a consequat pharetra. Vivamus ornare tellus auctor, facilisis lacus id, feugiat dui. Nam id est ut est rhoncus varius.\\n\\nAenean vel vehicula erat. Aenean gravida risus vitae purus sodales, quis dictum enim porta. Ut elit elit, fermentum sed posuere non, accumsan eu justo. Integer porta malesuada quam, et elementum massa suscipit nec. Donec in elit diam. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis suscipit vel ante volutpat vestibulum.\\n\\nPellentesque ex arcu, euismod et sapien ut, vulputate suscipit enim. Donec mattis sagittis augue, et mattis lacus. Cras placerat consequat lorem sed luctus. Nam suscipit aliquam sem ac imperdiet. Mauris a semper augue, pulvinar fringilla magna. Integer pretium massa non risus commodo hendrerit. Sed dictum libero id erat sodales mattis. Etiam auctor dolor lectus, ut sagittis enim lobortis vitae. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur nec orci lobortis, cursus risus eget, sollicitudin massa. Integer vel tincidunt mi, id eleifend quam. Nullam facilisis nisl eu mauris congue, vitae euismod nisi malesuada. Vivamus sit amet urna et libero sagittis dictum.\\n\\nIn hac habitasse platea dictumst. Aliquam erat volutpat. Ut dictum, mi a viverra venenatis, mi urna pulvinar nisi, nec gravida lectus diam eget urna. Ut dictum sit amet nisl ut dignissim. Sed sed mauris scelerisque, efficitur augue in, vulputate turpis. Proin dolor justo, bibendum et sollicitudin feugiat, imperdiet sed mi. Sed elementum vitae massa vel lobortis. Cras vitae massa sit amet libero dictum tempus.\\n\\nVivamus ut mauris lectus. Curabitur placerat ornare scelerisque. Cras malesuada nunc quis tortor pretium bibendum vel sed dui. Cras lobortis nibh eu erat blandit, sit amet consequat neque fermentum. Phasellus imperdiet molestie tristique. Cras auctor purus erat, id mollis ligula porttitor eget. Mauris porta pharetra odio et finibus. Suspendisse eu est a ligula bibendum cursus. Mauris ac laoreet libero. Nullam volutpat sem quis rhoncus gravida.\\n\\nDonec malesuada lacus ac iaculis cursus. Sed sem orci, feugiat ac est ut, ultricies posuere nisi. Suspendisse potenti. Phasellus ut ultricies purus. Etiam sem tortor, fermentum quis aliquam eget, consequat ut nulla. Aliquam dictum metus in mi fringilla, vel gravida nulla accumsan. Cras aliquam eget leo vel posuere. Vivamus vitae malesuada nunc. Morbi placerat magna mi, id suscipit lacus auctor quis. Nam at lorem vel odio finibus fringilla ut ac velit. Donec laoreet at nibh quis vehicula.\\n\\nFusce ac tristique nisi. Donec leo nisi, consectetur at tellus sit amet, consectetur ultrices dui. Quisque gravida mollis tempor. Maecenas semper, sapien ut dignissim feugiat, massa enim viverra dolor, non varius eros nulla nec felis. Nunc massa lacus, ornare et feugiat id, bibendum quis purus. Praesent viverra elit sit amet purus consectetur venenatis. Maecenas nibh risus, elementum sit amet enim sagittis, ultrices malesuada lectus. Vivamus non felis ante. Ut vulputate ex arcu. Aliquam porta gravida porta. Aliquam eros leo, malesuada quis eros non, maximus tristique neque. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam ligula orci, mollis vel luctus nec, venenatis vitae est. Fusce rutrum convallis nisi.\\n\\nNunc laoreet eget nibh in feugiat. Pellentesque nec arcu cursus, gravida dolor a, pellentesque nisi. Praesent vel justo blandit, placerat risus eget, consectetur orci. Sed maximus metus mi, ut euismod augue ultricies in. Nunc id risus hendrerit, aliquet lorem nec, congue justo. Vestibulum vel nunc ac est euismod mattis ac vitae nulla. Donec blandit luctus mauris, sit amet bibendum dui egestas et. Aenean nec lorem nec elit ornare rutrum nec eget ligula. Fusce a ipsum vitae neque elementum pharetra. Pellentesque ullamcorper ullamcorper libero, vitae porta sem sagittis vel. Interdum et malesuada fames ac ante ipsum primis in faucibus.\\n\\nDuis at massa sit amet risus pellentesque varius sit amet vitae eros. Cras tempor aliquet sapien, vehicula varius neque volutpat et. Donec purus nibh, pellentesque ut lobortis nec, ultricies ultricies nisl. Sed accumsan ut dui sit amet vulputate. Suspendisse eu facilisis massa, a hendrerit mauris. Nulla elementum molestie tincidunt. Donec mi justo, ornare vel tempor id, gravida et orci. Nam molestie erat nec nisi bibendum accumsan. Duis vitae tempor ante. Morbi congue mauris vel sagittis facilisis. Vivamus vehicula odio orci, a tempor nibh euismod in. Proin mattis, nibh at feugiat porta, purus velit posuere felis, quis volutpat sapien dui vel odio. Nam fermentum sem nec euismod aliquet. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Aliquam erat volutpat.\\n\\nMauris congue lacus tortor. Pellentesque arcu eros, accumsan imperdiet porttitor vitae, mattis nec justo. Nullam ac aliquam mauris. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Suspendisse potenti. Fusce accumsan tempus felis a sagittis. Maecenas et velit odio. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Aliquam eros lacus, volutpat non urna sed, tincidunt ullamcorper elit. Sed sit amet gravida libero. In varius mi vel diam sollicitudin mollis.\\n\\nAenean varius, diam vitae hendrerit feugiat, libero augue ultrices odio, eget consequat sem tellus eu nisi. Nam dapibus enim et auctor sollicitudin. Nunc iaculis eros orci, ac accumsan eros malesuada ut. Ut semper augue felis, nec sodales lorem consectetur non. Cras gravida eleifend est, et sagittis eros imperdiet congue. Fusce id tellus dapibus nunc scelerisque tempus. Donec tempor placerat libero, in commodo nisi bibendum eu. Donec id eros non est sollicitudin luctus. Duis bibendum bibendum tellus nec viverra. Aliquam non faucibus ex, nec luctus dui. Curabitur efficitur varius urna non dignissim. Suspendisse elit elit, ultrices in elementum eu, tempor at magna.\\n\\nNunc in purus sit amet mi laoreet pulvinar placerat eget sapien. Donec vel felis at dui ultricies euismod quis vel neque. Donec tincidunt urna non eros pretium blandit. Nullam congue tincidunt condimentum. Curabitur et libero nibh. Proin ultricies risus id imperdiet scelerisque. Suspendisse purus mi, viverra vitae risus ut, tempus tincidunt enim. Ut luctus lobortis nisl, eget venenatis tortor cursus non. Mauris finibus nisl quis gravida ultricies. Fusce elementum lacus sit amet nunc congue, in porta nulla tincidunt.\\n\\nMauris ante risus, imperdiet blandit posuere in, blandit eu ipsum. Integer et auctor arcu. Integer quis elementum purus. Nunc in ultricies nisl, sed rutrum risus. Suspendisse venenatis eros nec lorem rhoncus, in scelerisque velit condimentum. Etiam condimentum quam felis, in elementum odio mattis et. In ut nibh porttitor, hendrerit tellus vel, luctus magna. Vestibulum et ligula et dolor pellentesque porta. Aenean efficitur porta massa et bibendum. Nulla vehicula sem in risus volutpat, a eleifend elit maximus.\\n\\nProin orci lorem, auctor a felis eu, pretium lobortis nulla. Phasellus aliquam efficitur interdum. Sed sit amet velit rutrum est dictum facilisis. Duis cursus enim at nisl tincidunt, eu molestie elit vehicula. Cras pellentesque nisl id enim feugiat fringilla. In quis tincidunt neque. Nam eu consectetur dolor. Ut id interdum mauris. Mauris nunc tortor, placerat in tempor ut, vestibulum eu nisl. Integer vel dapibus ipsum. Nunc id erat pulvinar, tincidunt magna id, condimentum massa. Pellentesque consequat est eget odio placerat vehicula. Etiam augue neque, sagittis non leo eu, tristique scelerisque dui. Ut dui urna, blandit quis urna ac, tincidunt tristique turpis.\\n\\nSuspendisse venenatis rhoncus ligula ultrices condimentum. In id laoreet eros. Suspendisse suscipit fringilla leo id euismod. Sed in quam libero. Ut at ligula tellus. Sed tristique gravida dui, at egestas odio aliquam iaculis. Praesent imperdiet velit quis nibh consequat, quis pretium sem sagittis. Donec tortor ex, congue sit amet pulvinar ac, rutrum non est. Praesent ipsum magna, venenatis sit amet vulputate id, eleifend ac ipsum.\\n\\nIn consequat, nisi iaculis laoreet elementum, massa mauris varius nisi, et porta nisi velit at urna. Maecenas sit amet aliquet eros, a rhoncus nisl. Maecenas convallis enim nunc. Morbi purus nisl, aliquam ac tincidunt sed, mattis in augue. Quisque et elementum quam, vitae maximus orci. Suspendisse hendrerit risus nec vehicula placerat. Nulla et lectus nunc. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.\\n\\nEtiam ut sodales ex. Nulla luctus, magna eu scelerisque sagittis, nibh quam consectetur neque, non rutrum dolor metus nec ex. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed egestas augue elit, sollicitudin accumsan massa lobortis ac. Curabitur placerat, dolor a aliquam maximus, velit ipsum laoreet ligula, id ullamcorper lacus nibh eget nisl. Donec eget lacus venenatis enim consequat auctor vel in.\\n\"\n file_i_d {\n file_num: 6006\n realm_num: 0\n shard_num: 0\n }\n}\nnode_account_i_d {\n account_num: 444\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/FileContentsQueryTest.java b/sdk/src/test/java/org/hiero/sdk/FileContentsQueryTest.java new file mode 100644 index 0000000000..8f640c3071 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/FileContentsQueryTest.java @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.github.jsonSnapshot.SnapshotMatcher; +import org.hiero.sdk.proto.QueryHeader; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class FileContentsQueryTest { + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + var builder = org.hiero.sdk.proto.Query.newBuilder(); + new FileContentsQuery() + .setFileId(FileId.fromString("0.0.5005")) + .onMakeRequest(builder, QueryHeader.newBuilder().build()); + SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")) + .toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/FileContentsQueryTest.snap b/sdk/src/test/java/org/hiero/sdk/FileContentsQueryTest.snap new file mode 100644 index 0000000000..03ef0af9fa --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/FileContentsQueryTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.FileContentsQueryTest.shouldSerialize=[ + "# org.hiero.sdk.proto.Query\nfile_get_contents {\n file_i_d {\n file_num: 5005\n realm_num: 0\n shard_num: 0\n }\n header {\n }\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/FileCreateTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/FileCreateTransactionTest.java new file mode 100644 index 0000000000..55c3a7948b --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/FileCreateTransactionTest.java @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import org.hiero.sdk.proto.FileCreateTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class FileCreateTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new FileCreateTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + private FileCreateTransaction spawnTestTransaction() { + return new FileCreateTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setContents(new byte[] {1, 2, 3, 4}) + .setExpirationTime(Instant.ofEpochSecond(1554158728)) + .setKeys(unusedPrivateKey) + .setMaxTransactionFee(Hbar.fromTinybars(100_000)) + .setFileMemo("Hello memo") + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = FileCreateTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setFileCreate(FileCreateTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(FileCreateTransaction.class); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/FileCreateTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/FileCreateTransactionTest.snap new file mode 100644 index 0000000000..d721547f58 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/FileCreateTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.FileCreateTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nfile_create {\n contents: \"\\001\\002\\003\\004\"\n expiration_time {\n seconds: 1554158728\n }\n keys {\n keys {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n }\n memo: \"Hello memo\"\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/FileDeleteTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/FileDeleteTransactionTest.java new file mode 100644 index 0000000000..50f885f9d8 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/FileDeleteTransactionTest.java @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import org.hiero.sdk.proto.FileDeleteTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class FileDeleteTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new FileDeleteTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + private FileDeleteTransaction spawnTestTransaction() { + return new FileDeleteTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setFileId(FileId.fromString("0.0.6006")) + .setMaxTransactionFee(Hbar.fromTinybars(100_000)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = FileDeleteTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setFileDelete(FileDeleteTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(FileDeleteTransaction.class); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/FileDeleteTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/FileDeleteTransactionTest.snap new file mode 100644 index 0000000000..2a30b33c93 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/FileDeleteTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.FileDeleteTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nfile_delete {\n file_i_d {\n file_num: 6006\n realm_num: 0\n shard_num: 0\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/FileIdTest.java b/sdk/src/test/java/org/hiero/sdk/FileIdTest.java new file mode 100644 index 0000000000..d2e6922ae1 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/FileIdTest.java @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.github.jsonSnapshot.SnapshotMatcher; +import org.bouncycastle.util.encoders.Hex; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +class FileIdTest { + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerializeFromString() { + SnapshotMatcher.expect(FileId.fromString("0.0.5005").toString()).toMatchSnapshot(); + } + + @Test + void toBytes() throws InvalidProtocolBufferException { + SnapshotMatcher.expect(Hex.toHexString(new FileId(5005).toBytes())).toMatchSnapshot(); + } + + @Test + void fromBytes() throws InvalidProtocolBufferException { + SnapshotMatcher.expect(FileId.fromBytes(new FileId(5005).toBytes()).toString()) + .toMatchSnapshot(); + } + + @Test + void fromSolidityAddress() { + SnapshotMatcher.expect(FileId.fromSolidityAddress("000000000000000000000000000000000000138D") + .toString()) + .toMatchSnapshot(); + } + + @Test + void toSolidityAddress() { + SnapshotMatcher.expect(new FileId(5005).toSolidityAddress()).toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/FileIdTest.snap b/sdk/src/test/java/org/hiero/sdk/FileIdTest.snap new file mode 100644 index 0000000000..2377b6b8a8 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/FileIdTest.snap @@ -0,0 +1,23 @@ +org.hiero.sdk.FileIdTest.fromBytes=[ + "0.0.5005" +] + + +org.hiero.sdk.FileIdTest.fromSolidityAddress=[ + "0.0.5005" +] + + +org.hiero.sdk.FileIdTest.shouldSerializeFromString=[ + "0.0.5005" +] + + +org.hiero.sdk.FileIdTest.toBytes=[ + "188d27" +] + + +org.hiero.sdk.FileIdTest.toSolidityAddress=[ + "000000000000000000000000000000000000138d" +] diff --git a/sdk/src/test/java/org/hiero/sdk/FileInfoQueryTest.java b/sdk/src/test/java/org/hiero/sdk/FileInfoQueryTest.java new file mode 100644 index 0000000000..5956569a0a --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/FileInfoQueryTest.java @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.github.jsonSnapshot.SnapshotMatcher; +import org.hiero.sdk.proto.QueryHeader; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class FileInfoQueryTest { + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + var builder = org.hiero.sdk.proto.Query.newBuilder(); + new FileInfoQuery() + .setFileId(FileId.fromString("0.0.5005")) + .onMakeRequest(builder, QueryHeader.newBuilder().build()); + SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")) + .toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/FileInfoQueryTest.snap b/sdk/src/test/java/org/hiero/sdk/FileInfoQueryTest.snap new file mode 100644 index 0000000000..ab84c36302 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/FileInfoQueryTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.FileInfoQueryTest.shouldSerialize=[ + "# org.hiero.sdk.proto.Query\nfile_get_info {\n file_i_d {\n file_num: 5005\n realm_num: 0\n shard_num: 0\n }\n header {\n }\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/FileInfoTest.java b/sdk/src/test/java/org/hiero/sdk/FileInfoTest.java new file mode 100644 index 0000000000..59e2d7f009 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/FileInfoTest.java @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import org.bouncycastle.util.encoders.Hex; +import org.hiero.sdk.proto.FileGetInfoResponse; +import org.hiero.sdk.proto.KeyList; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class FileInfoTest { + private static final PrivateKey privateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + private static final FileGetInfoResponse.FileInfo info = FileGetInfoResponse.FileInfo.newBuilder() + .setFileID(new FileId(1).toProtobuf()) + .setSize(2) + .setExpirationTime(InstantConverter.toProtobuf(Instant.ofEpochMilli(3))) + .setDeleted(true) + .setKeys(KeyList.newBuilder().addKeys(privateKey.getPublicKey().toProtobufKey())) + .setLedgerId(LedgerId.MAINNET.toByteString()) + .build(); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void fromProtobuf() throws InvalidProtocolBufferException { + SnapshotMatcher.expect(FileInfo.fromProtobuf(info).toString()).toMatchSnapshot(); + } + + @Test + void toProtobuf() throws InvalidProtocolBufferException { + SnapshotMatcher.expect(FileInfo.fromProtobuf(info).toProtobuf().toString()) + .toMatchSnapshot(); + } + + @Test + void fromBytes() throws InvalidProtocolBufferException { + SnapshotMatcher.expect(FileInfo.fromBytes(info.toByteArray()).toString()) + .toMatchSnapshot(); + } + + @Test + void toBytes() { + SnapshotMatcher.expect(Hex.toHexString(FileInfo.fromProtobuf(info).toBytes())) + .toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/FileInfoTest.snap b/sdk/src/test/java/org/hiero/sdk/FileInfoTest.snap new file mode 100644 index 0000000000..28d9c29c67 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/FileInfoTest.snap @@ -0,0 +1,18 @@ +org.hiero.sdk.FileInfoTest.fromBytes=[ + "FileInfo{fileId=0.0.1, size=2, expirationTime=1970-01-01T00:00:00.003Z, isDeleted=true, keys=KeyList{threshold=null, keys=[302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7]}, fileMemo=, ledgerId=mainnet}" +] + + +org.hiero.sdk.FileInfoTest.fromProtobuf=[ + "FileInfo{fileId=0.0.1, size=2, expirationTime=1970-01-01T00:00:00.003Z, isDeleted=true, keys=KeyList{threshold=null, keys=[302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7]}, fileMemo=, ledgerId=mainnet}" +] + + +org.hiero.sdk.FileInfoTest.toBytes=[ + "0a02180110021a0510c08db70120012a240a221220e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b73a0100" +] + + +org.hiero.sdk.FileInfoTest.toProtobuf=[ + "# org.hiero.sdk.proto.FileGetInfoResponse$FileInfo@e51665b9\ndeleted: true\nexpiration_time {\n nanos: 3000000\n seconds: 0\n}\nfile_i_d {\n file_num: 1\n realm_num: 0\n shard_num: 0\n}\nkeys {\n keys {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n}\nledger_id: \"\\000\"\nsize: 2" +] diff --git a/sdk/src/test/java/org/hiero/sdk/FileUpdateTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/FileUpdateTransactionTest.java new file mode 100644 index 0000000000..ffbcc1e4f8 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/FileUpdateTransactionTest.java @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import org.hiero.sdk.proto.FileUpdateTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class FileUpdateTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + private FileUpdateTransaction spawnTestTransaction() { + return new FileUpdateTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setFileId(FileId.fromString("0.0.6006")) + .setExpirationTime(Instant.ofEpochSecond(1554158728)) + .setContents(new byte[] {1, 2, 3, 4, 5}) + .setMaxTransactionFee(Hbar.fromTinybars(100_000)) + .setKeys(unusedPrivateKey) + .setFileMemo("Hello memo") + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = FileUpdateTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new FileUpdateTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setFileUpdate(FileUpdateTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(FileUpdateTransaction.class); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/FileUpdateTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/FileUpdateTransactionTest.snap new file mode 100644 index 0000000000..d56951dff3 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/FileUpdateTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.FileUpdateTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nfile_update {\n contents: \"\\001\\002\\003\\004\\005\"\n expiration_time {\n seconds: 1554158728\n }\n file_i_d {\n file_num: 6006\n realm_num: 0\n shard_num: 0\n }\n keys {\n keys {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n }\n memo {\n value: \"Hello memo\"\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/FreezeTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/FreezeTransactionTest.java new file mode 100644 index 0000000000..0541b117f1 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/FreezeTransactionTest.java @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.google.protobuf.ByteString; +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import org.bouncycastle.util.encoders.Hex; +import org.hiero.sdk.proto.FreezeTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.Timestamp; +import org.hiero.sdk.proto.TransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class FreezeTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + private static final FileId testFileId = FileId.fromString("4.5.6"); + private static final byte[] testFileHash = Hex.decode("1723904587120938954702349857"); + private static final FreezeType testFreezeType = FreezeType.TELEMETRY_UPGRADE; + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + private FreezeTransaction spawnTestTransaction() { + return new FreezeTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setFileId(testFileId) + .setFileHash(testFileHash) + .setStartTime(validStart) + .setFreezeType(testFreezeType) + .setMaxTransactionFee(Hbar.fromTinybars(100_000)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = FreezeTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new FreezeTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setFreeze(FreezeTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(FreezeTransaction.class); + } + + @Test + void constructFreezeTransactionFromTransactionBodyProtobuf() { + var transactionBody = FreezeTransactionBody.newBuilder() + .setUpdateFile(testFileId.toProtobuf()) + .setFileHash(ByteString.copyFrom(testFileHash)) + .setStartTime(Timestamp.newBuilder().setSeconds(validStart.getEpochSecond())) + .setFreezeType(testFreezeType.code); + + var tx = TransactionBody.newBuilder().setFreeze(transactionBody).build(); + var freezeTransaction = new FreezeTransaction(tx); + + assertNotNull(freezeTransaction.getFileId()); + assertThat(freezeTransaction.getFileId()).isEqualTo(testFileId); + assertThat(freezeTransaction.getFileHash()).isEqualTo(testFileHash); + assertNotNull(freezeTransaction.getStartTime()); + assertThat(freezeTransaction.getStartTime().getEpochSecond()).isEqualTo(validStart.getEpochSecond()); + assertThat(freezeTransaction.getFreezeType()).isEqualTo(testFreezeType); + } + + @Test + void getSetFileId() { + var freezeTransaction = new FreezeTransaction().setFileId(testFileId); + assertNotNull(freezeTransaction.getFileId()); + assertThat(freezeTransaction.getFileId()).isEqualTo(testFileId); + } + + @Test + void getSetFileIdFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setFileId(testFileId)); + } + + @Test + void getSetFileHash() { + var freezeTransaction = new FreezeTransaction().setFileHash(testFileHash); + assertNotNull(freezeTransaction.getFileHash()); + assertThat(freezeTransaction.getFileHash()).isEqualTo(testFileHash); + } + + @Test + void getSetFileHashFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setFileHash(testFileHash)); + } + + @Test + void getSetStartTime() { + var freezeTransaction = new FreezeTransaction().setStartTime(validStart); + assertNotNull(freezeTransaction.getStartTime()); + assertThat(freezeTransaction.getStartTime().getEpochSecond()).isEqualTo(validStart.getEpochSecond()); + } + + @Test + void getSetStartTimeFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setStartTime(validStart)); + } + + @Test + void getSetFreezeType() { + var freezeTransaction = new FreezeTransaction().setFreezeType(testFreezeType); + assertThat(freezeTransaction.getFreezeType()).isEqualTo(testFreezeType); + } + + @Test + void getSetFreezeTypeFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setFreezeType(testFreezeType)); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/FreezeTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/FreezeTransactionTest.snap new file mode 100644 index 0000000000..ffdabafe0b --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/FreezeTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.FreezeTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nfreeze {\n file_hash: \"\\027#\\220E\\207\\022\\t8\\225G\\0024\\230W\"\n freeze_type: TELEMETRY_UPGRADE\n freeze_type_value: 5\n start_time {\n seconds: 1554158542\n }\n update_file {\n file_num: 6\n realm_num: 5\n shard_num: 4\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/Function.java b/sdk/src/test/java/org/hiero/sdk/Function.java similarity index 78% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/Function.java rename to sdk/src/test/java/org/hiero/sdk/Function.java index 8169952e10..f7b452886f 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/Function.java +++ b/sdk/src/test/java/org/hiero/sdk/Function.java @@ -1,4 +1,5 @@ -package com.hedera.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; public interface Function { /** diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/HbarTest.java b/sdk/src/test/java/org/hiero/sdk/HbarTest.java similarity index 77% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/HbarTest.java rename to sdk/src/test/java/org/hiero/sdk/HbarTest.java index 584c1df6c9..9f116a4fca 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/HbarTest.java +++ b/sdk/src/test/java/org/hiero/sdk/HbarTest.java @@ -1,35 +1,16 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import java.math.BigDecimal; import java.util.Iterator; import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; public class HbarTest { private static final long fiftyGTinybar = 5_000_000_000L; @@ -41,13 +22,13 @@ public class HbarTest { static Iterator getValueConversions() { return List.of( - Arguments.arguments(new BigDecimal(50_000_000), HbarUnit.MICROBAR), - Arguments.arguments(new BigDecimal(50_000), HbarUnit.MILLIBAR), - Arguments.arguments(new BigDecimal(50), HbarUnit.HBAR), - Arguments.arguments(new BigDecimal("0.05"), HbarUnit.KILOBAR), - Arguments.arguments(new BigDecimal("0.00005"), HbarUnit.MEGABAR), - Arguments.arguments(new BigDecimal("0.00000005"), HbarUnit.GIGABAR) - ).iterator(); + Arguments.arguments(new BigDecimal(50_000_000), HbarUnit.MICROBAR), + Arguments.arguments(new BigDecimal(50_000), HbarUnit.MILLIBAR), + Arguments.arguments(new BigDecimal(50), HbarUnit.HBAR), + Arguments.arguments(new BigDecimal("0.05"), HbarUnit.KILOBAR), + Arguments.arguments(new BigDecimal("0.00005"), HbarUnit.MEGABAR), + Arguments.arguments(new BigDecimal("0.00000005"), HbarUnit.GIGABAR)) + .iterator(); } @Test diff --git a/sdk/src/test/java/org/hiero/sdk/HederaTrustManagerTest.java b/sdk/src/test/java/org/hiero/sdk/HederaTrustManagerTest.java new file mode 100644 index 0000000000..59ee569ffd --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/HederaTrustManagerTest.java @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import com.google.protobuf.ByteString; +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.Map; +import java.util.Objects; +import org.junit.jupiter.api.Test; + +public class HederaTrustManagerTest { + public static final String PREVIEWNET_CERT_NODE_3_STRING = + "-----BEGIN CERTIFICATE-----\n" + "MIICnzCCAiWgAwIBAgIUenyqJ4UaFBbwokatcUqAwW3o3rswCgYIKoZIzj0EAwMw\n" + + "gYQxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJUWDETMBEGA1UEBwwKUmljaGFyZHNv\n" + + "bjEPMA0GA1UECgwGSGVkZXJhMQ8wDQYDVQQLDAZIZWRlcmExEDAOBgNVBAMMBzAw\n" + + "MDAwMDAxHzAdBgkqhkiG9w0BCQEWEGFkbWluQGhlZGVyYS5jb20wIBcNMjEwODIz\n" + + "MjIyMTU4WhgPMjI5NTA2MDcyMjIxNThaMIGEMQswCQYDVQQGEwJVUzELMAkGA1UE\n" + + "CAwCVFgxEzARBgNVBAcMClJpY2hhcmRzb24xDzANBgNVBAoMBkhlZGVyYTEPMA0G\n" + + "A1UECwwGSGVkZXJhMRAwDgYDVQQDDAcwMDAwMDAwMR8wHQYJKoZIhvcNAQkBFhBh\n" + + "ZG1pbkBoZWRlcmEuY29tMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEm5b1+oG9R0qt\n" + + "zM7UZnS5l/xxUNHIHq5+NAvtlviCpJL19jrW9+/UOy00Qqc6vS6tS1hS+dNJmpiZ\n" + + "FN0EHew4VDR7ACnL4LDJKmIHWjQ0iwvZo5kCpO0r9BtPN5FvaSxyo1QwUjAPBgNV\n" + + "HREECDAGhwR/AAABMAsGA1UdDwQEAwIEsDATBgNVHSUEDDAKBggrBgEFBQcDATAd\n" + + "BgNVHQ4EFgQUeciBviJtjeuue0GPf1xllNw7qvYwCgYIKoZIzj0EAwMDaAAwZQIw\n" + + "JeG0H2HdsI1VhOYmJmYlNeKCNgAk+LMorzPmsIInVBO2HK2IrKfpReWDS/m5j51V\n" + + "AjEAxKBxDezJDqAZHTkTXCg+X9Q9V6J6M5yDy5IS90aCWEo+W8C1Hc6hkn2/NrvT\n" + + "PhwK\n" + + "-----END CERTIFICATE-----\n"; + + public static final ByteArrayInputStream PREVIEWNET_CERT_NODE_3_BYTES = + new ByteArrayInputStream(PREVIEWNET_CERT_NODE_3_STRING.getBytes(StandardCharsets.UTF_8)); + + public static final CertificateFactory CERTIFICATE_FACTORY; + + static { + try { + CERTIFICATE_FACTORY = CertificateFactory.getInstance("X.509"); + } catch (CertificateException e) { + throw new RuntimeException(e); + } + } + + public static final X509Certificate PREVIEWNET_CERT_NODE_3; + + static { + try { + PREVIEWNET_CERT_NODE_3 = + (X509Certificate) CERTIFICATE_FACTORY.generateCertificate(PREVIEWNET_CERT_NODE_3_BYTES); + } catch (CertificateException e) { + throw new RuntimeException(e); + } + } + + static final X509Certificate[] CERTIFICATE_CHAIN = new X509Certificate[] {PREVIEWNET_CERT_NODE_3}; + + @Test + void skipsCheckIfVerificationIsDisabled() throws CertificateException { + new HederaTrustManager(ByteString.EMPTY, false).checkServerTrusted(CERTIFICATE_CHAIN, ""); + } + + @Test + void skipsCheckIfCertificateIsNotProvided() throws CertificateException { + new HederaTrustManager(null, false).checkServerTrusted(CERTIFICATE_CHAIN, ""); + } + + @Test + void throwsErrorIfCertificateIsNotProvidedButVerificationIsRequired() { + assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> new HederaTrustManager(null, true)); + } + + @Test + void properlyChecksCertificateAgainstCurrentNetworkAddressBook() throws InterruptedException, CertificateException { + var client = Client.forNetwork(Map.of("0.previewnet.hedera.com:50211", new AccountId(3))) + .setTransportSecurity(true) + .setVerifyCertificates(true) + .setLedgerId(LedgerId.PREVIEWNET); + + var nodeAddress = Objects.requireNonNull( + Objects.requireNonNull(client.network.addressBook).get(new AccountId(3))); + new HederaTrustManager(nodeAddress.getCertHash(), client.isVerifyCertificates()) + .checkServerTrusted(CERTIFICATE_CHAIN, ""); + } + + @Test + void certificateCheckFailWhenHashMismatches() throws InterruptedException, CertificateException { + var client = Client.forNetwork(Map.of("0.previewnet.hedera.com:50211", new AccountId(3))) + .setTransportSecurity(true) + .setVerifyCertificates(true) + .setLedgerId(LedgerId.PREVIEWNET); + + var nodeAddress = Objects.requireNonNull( + Objects.requireNonNull(client.network.addressBook).get(new AccountId(4))); + assertThatExceptionOfType(CertificateException.class) + .isThrownBy(() -> new HederaTrustManager(nodeAddress.getCertHash(), client.isVerifyCertificates()) + .checkServerTrusted(CERTIFICATE_CHAIN, "")); + } +} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/KeyListTest.java b/sdk/src/test/java/org/hiero/sdk/KeyListTest.java similarity index 77% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/KeyListTest.java rename to sdk/src/test/java/org/hiero/sdk/KeyListTest.java index 20fe1397ab..e0751cfdb8 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/KeyListTest.java +++ b/sdk/src/test/java/org/hiero/sdk/KeyListTest.java @@ -1,39 +1,46 @@ -package com.hedera.hashgraph.sdk; - -import com.google.protobuf.ByteString; -import com.hedera.hashgraph.sdk.proto.Key; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.List; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import com.google.protobuf.ByteString; +import java.util.List; +import org.hiero.sdk.proto.Key; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + class KeyListTest { private static final PublicKey mTestPublicKey1 = PrivateKey.fromStringED25519( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10") - .getPublicKey(); + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10") + .getPublicKey(); private static final PublicKey mTestPublicKey2 = PrivateKey.fromStringED25519( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e11") - .getPublicKey(); + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e11") + .getPublicKey(); private static final PublicKey mTestPublicKey3 = PrivateKey.fromStringED25519( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e12") - .getPublicKey(); + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e12") + .getPublicKey(); @Test @DisplayName("fromProtobuf") void fromProtobuf() { // Given - var protoKey1 = Key.newBuilder().setEd25519(ByteString.copyFrom(mTestPublicKey1.toBytes())).build(); - var protoKey3 = Key.newBuilder().setEd25519(ByteString.copyFrom(mTestPublicKey2.toBytes())).build(); - var protoKey2 = Key.newBuilder().setEd25519(ByteString.copyFrom(mTestPublicKey3.toBytes())).build(); - var protoKeyList = com.hedera.hashgraph.sdk.proto.KeyList.newBuilder() - .addAllKeys(List.of(protoKey1, protoKey2, protoKey3)).build(); + var protoKey1 = Key.newBuilder() + .setEd25519(ByteString.copyFrom(mTestPublicKey1.toBytes())) + .build(); + var protoKey3 = Key.newBuilder() + .setEd25519(ByteString.copyFrom(mTestPublicKey2.toBytes())) + .build(); + var protoKey2 = Key.newBuilder() + .setEd25519(ByteString.copyFrom(mTestPublicKey3.toBytes())) + .build(); + var protoKeyList = org.hiero.sdk.proto.KeyList.newBuilder() + .addAllKeys(List.of(protoKey1, protoKey2, protoKey3)) + .build(); // When var keyList = KeyList.fromProtobuf(protoKeyList, 3); @@ -83,12 +90,9 @@ void toProtobuf() { // Then assertThat(protoKeyList.getKeysCount()).isEqualTo(3); - assertThat(protoKeyList.getKeys(0).getEd25519().toByteArray()).isEqualTo( - mTestPublicKey1.toBytesRaw()); - assertThat(protoKeyList.getKeys(1).getEd25519().toByteArray()).isEqualTo( - mTestPublicKey2.toBytesRaw()); - assertThat(protoKeyList.getKeys(2).getEd25519().toByteArray()).isEqualTo( - mTestPublicKey3.toBytesRaw()); + assertThat(protoKeyList.getKeys(0).getEd25519().toByteArray()).isEqualTo(mTestPublicKey1.toBytesRaw()); + assertThat(protoKeyList.getKeys(1).getEd25519().toByteArray()).isEqualTo(mTestPublicKey2.toBytesRaw()); + assertThat(protoKeyList.getKeys(2).getEd25519().toByteArray()).isEqualTo(mTestPublicKey3.toBytesRaw()); } @Test diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/KeyTest.java b/sdk/src/test/java/org/hiero/sdk/KeyTest.java similarity index 75% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/KeyTest.java rename to sdk/src/test/java/org/hiero/sdk/KeyTest.java index 4ca77a79e0..4a5e522ec7 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/KeyTest.java +++ b/sdk/src/test/java/org/hiero/sdk/KeyTest.java @@ -1,42 +1,23 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.hiero.sdk.Key.fromBytes; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.proto.Key; -import com.hedera.hashgraph.sdk.proto.KeyList; -import com.hedera.hashgraph.sdk.proto.ThresholdKey; +import java.math.BigInteger; +import java.util.List; import org.bouncycastle.util.encoders.Hex; +import org.hiero.sdk.proto.Key; +import org.hiero.sdk.proto.KeyList; +import org.hiero.sdk.proto.ThresholdKey; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import java.math.BigInteger; -import java.util.List; - -import static com.hedera.hashgraph.sdk.Key.fromBytes; -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatNoException; - class KeyTest { @Test @DisplayName("can sign and verify a message") @@ -75,8 +56,8 @@ void calculateRecoveryIdECDSA() { System.arraycopy(signature, 0, r, 0, 32); final byte[] s = new byte[32]; System.arraycopy(signature, 32, s, 0, 32); - var recId = ((PrivateKeyECDSA) privateKey).getRecoveryId(r,s,message); - assertThat(recId).isBetween(0,1); + var recId = ((PrivateKeyECDSA) privateKey).getRecoveryId(r, s, message); + assertThat(recId).isBetween(0, 1); } @Test @@ -91,25 +72,25 @@ void failToCalculateRecoveryIdWithIllegalInputDataECDSA() { final byte[] s = new byte[32]; System.arraycopy(signature, 32, s, 0, 32); // recover public key with recId > 1 - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy( - () -> Crypto.recoverPublicKeyECDSAFromSignature(2, BigInteger.ONE, BigInteger.ONE, Crypto.calcKeccak256(message)) - ); + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> Crypto.recoverPublicKeyECDSAFromSignature( + 2, BigInteger.ONE, BigInteger.ONE, Crypto.calcKeccak256(message))); // recover public key with negative 'r' or 's' - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy( - () -> Crypto.recoverPublicKeyECDSAFromSignature(0, BigInteger.valueOf(-1), BigInteger.ONE, Crypto.calcKeccak256(message)) - ); + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> Crypto.recoverPublicKeyECDSAFromSignature( + 0, BigInteger.valueOf(-1), BigInteger.ONE, Crypto.calcKeccak256(message))); // calculate recId with wrong message var wrongMessage = "Hello".getBytes(UTF_8); - assertThatExceptionOfType(IllegalStateException.class).isThrownBy( - () -> ((PrivateKeyECDSA) privateKey).getRecoveryId(r,s,wrongMessage) - ); + assertThatExceptionOfType(IllegalStateException.class) + .isThrownBy(() -> ((PrivateKeyECDSA) privateKey).getRecoveryId(r, s, wrongMessage)); } @Test @DisplayName("can convert from protobuf ED25519 key to PublicKey") void fromProtoKeyEd25519() { var keyBytes = Hex.decode("0011223344556677889900112233445566778899001122334455667788990011"); - var protoKey = Key.newBuilder().setEd25519(ByteString.copyFrom(keyBytes)).build(); + var protoKey = + Key.newBuilder().setEd25519(ByteString.copyFrom(keyBytes)).build(); var cut = PublicKey.fromProtobufKey(protoKey); @@ -133,7 +114,7 @@ void fromProtoKeyECDSA() throws InvalidProtocolBufferException { @DisplayName("can convert from protobuf key list to PublicKey") void fromProtoKeyKeyList() { // given - var keyBytes = new byte[][]{ + var keyBytes = new byte[][] { Hex.decode("0011223344556677889900112233445566778899001122334455667788990011"), Hex.decode("aa11223344556677889900112233445566778899001122334455667788990011") }; @@ -147,12 +128,12 @@ void fromProtoKeyKeyList() { var protoKey = Key.newBuilder().setKeyList(protoKeyList).build(); // when - var cut = com.hedera.hashgraph.sdk.Key.fromProtobufKey(protoKey); + var cut = org.hiero.sdk.Key.fromProtobufKey(protoKey); // then - assertThat(cut.getClass()).isEqualTo(com.hedera.hashgraph.sdk.KeyList.class); + assertThat(cut.getClass()).isEqualTo(org.hiero.sdk.KeyList.class); - var keyList = (com.hedera.hashgraph.sdk.KeyList) cut; + var keyList = (org.hiero.sdk.KeyList) cut; var actual = keyList.toProtobufKey().getKeyList(); assertThat(actual.getKeysCount()).isEqualTo(2); @@ -164,7 +145,7 @@ void fromProtoKeyKeyList() { @DisplayName("can convert from protobuf threshold key to PublicKey") void fromProtoKeyThresholdKey() { // given - var keyBytes = new byte[][]{ + var keyBytes = new byte[][] { Hex.decode("0011223344556677889900112233445566778899001122334455667788990011"), Hex.decode("aa11223344556677889900112233445566778899001122334455667788990011") }; @@ -179,12 +160,12 @@ void fromProtoKeyThresholdKey() { var protoKey = Key.newBuilder().setThresholdKey(protoThresholdKey).build(); // when - var cut = com.hedera.hashgraph.sdk.Key.fromProtobufKey(protoKey); + var cut = org.hiero.sdk.Key.fromProtobufKey(protoKey); // then - assertThat(cut.getClass()).isEqualTo(com.hedera.hashgraph.sdk.KeyList.class); + assertThat(cut.getClass()).isEqualTo(org.hiero.sdk.KeyList.class); - var thresholdKey = (com.hedera.hashgraph.sdk.KeyList) cut; + var thresholdKey = (org.hiero.sdk.KeyList) cut; var actual = thresholdKey.toProtobufKey().getThresholdKey(); assertThat(actual.getThreshold()).isEqualTo(1); @@ -197,20 +178,20 @@ void fromProtoKeyThresholdKey() { @DisplayName("Throws given unsupported key") void throwsUnsupportedKey() { byte[] keyBytes = {0, 1, 2}; - var protoKey = Key.newBuilder().setRSA3072(ByteString.copyFrom(keyBytes)).build(); - assertThatExceptionOfType(IllegalStateException.class).isThrownBy( - () -> com.hedera.hashgraph.sdk.Key.fromProtobufKey(protoKey) - ); + var protoKey = + Key.newBuilder().setRSA3072(ByteString.copyFrom(keyBytes)).build(); + assertThatExceptionOfType(IllegalStateException.class) + .isThrownBy(() -> org.hiero.sdk.Key.fromProtobufKey(protoKey)); } @Test @DisplayName("Key equals") void keyEquals() { var key1 = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); var key2 = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); assertThat(key2.toString()).isEqualTo(key1.toString()); assertThat(key2.getPublicKey()).isEqualTo(key1.getPublicKey()); @@ -221,7 +202,7 @@ void keyEquals() { @DisplayName("Key has hash") void keyHash() { var key = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); assertThatNoException().isThrownBy(key::hashCode); } @@ -230,15 +211,15 @@ void keyHash() { @DisplayName("KeyList methods") void keyListMethods() { var key1 = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); var key2 = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e11"); + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e11"); var key3 = PrivateKey.fromString( - "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e12"); + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e12"); - var keyList = com.hedera.hashgraph.sdk.KeyList.withThreshold(1); + var keyList = org.hiero.sdk.KeyList.withThreshold(1); keyList.add(key1); keyList.addAll(List.of(key2, key3)); @@ -253,7 +234,7 @@ void keyListMethods() { assertThat(arr[1]).isEqualTo(key2); assertThat(arr[2]).isEqualTo(key3); - arr = new com.hedera.hashgraph.sdk.Key[]{null, null, null}; + arr = new org.hiero.sdk.Key[] {null, null, null}; keyList.toArray(arr); assertThat(arr[0]).isEqualTo(key1); assertThat(arr[1]).isEqualTo(key2); @@ -280,7 +261,8 @@ void keyListMethods() { @DisplayName("can convert from bytes ED25519 key to Key") void fromBytesEd25519() throws InvalidProtocolBufferException { var keyBytes = Hex.decode("0011223344556677889900112233445566778899001122334455667788990011"); - var protoKey = Key.newBuilder().setEd25519(ByteString.copyFrom(keyBytes)).build(); + var protoKey = + Key.newBuilder().setEd25519(ByteString.copyFrom(keyBytes)).build(); var bytes = protoKey.toByteArray(); var cut = fromBytes(bytes); @@ -303,7 +285,7 @@ void fromBytesECDSA() throws InvalidProtocolBufferException { @Test @DisplayName("can convert from bytes key list to Key") void fromBytesKeyList() throws InvalidProtocolBufferException { - var keyBytes = new byte[][]{ + var keyBytes = new byte[][] { Hex.decode("0011223344556677889900112233445566778899001122334455667788990011"), Hex.decode("aa11223344556677889900112233445566778899001122334455667788990011") }; @@ -319,9 +301,9 @@ void fromBytesKeyList() throws InvalidProtocolBufferException { var cut = fromBytes(bytes); - assertThat(cut.getClass()).isEqualTo(com.hedera.hashgraph.sdk.KeyList.class); + assertThat(cut.getClass()).isEqualTo(org.hiero.sdk.KeyList.class); - var keyList = (com.hedera.hashgraph.sdk.KeyList) cut; + var keyList = (org.hiero.sdk.KeyList) cut; var actual = keyList.toProtobufKey().getKeyList(); assertThat(actual.getKeysCount()).isEqualTo(2); @@ -332,7 +314,7 @@ void fromBytesKeyList() throws InvalidProtocolBufferException { @Test @DisplayName("can convert from bytes threshold key to Key") void fromBytesThresholdKey() throws InvalidProtocolBufferException { - var keyBytes = new byte[][]{ + var keyBytes = new byte[][] { Hex.decode("0011223344556677889900112233445566778899001122334455667788990011"), Hex.decode("aa11223344556677889900112233445566778899001122334455667788990011") }; @@ -349,9 +331,9 @@ void fromBytesThresholdKey() throws InvalidProtocolBufferException { var cut = fromBytes(bytes); - assertThat(cut.getClass()).isEqualTo(com.hedera.hashgraph.sdk.KeyList.class); + assertThat(cut.getClass()).isEqualTo(org.hiero.sdk.KeyList.class); - var thresholdKey = (com.hedera.hashgraph.sdk.KeyList) cut; + var thresholdKey = (org.hiero.sdk.KeyList) cut; var actual = thresholdKey.toProtobufKey().getThresholdKey(); assertThat(actual.getThreshold()).isEqualTo(1); @@ -364,11 +346,10 @@ void fromBytesThresholdKey() throws InvalidProtocolBufferException { @DisplayName("Throws given unsupported key") void throwsUnsupportedKeyFromBytes() { byte[] keyBytes = {0, 1, 2}; - var protoKey = Key.newBuilder().setRSA3072(ByteString.copyFrom(keyBytes)).build(); + var protoKey = + Key.newBuilder().setRSA3072(ByteString.copyFrom(keyBytes)).build(); var bytes = protoKey.toByteArray(); - assertThatExceptionOfType(IllegalStateException.class).isThrownBy( - () -> fromBytes(bytes) - ); + assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> fromBytes(bytes)); } } diff --git a/sdk/src/test/java/org/hiero/sdk/KeystoreTest.java b/sdk/src/test/java/org/hiero/sdk/KeystoreTest.java new file mode 100644 index 0000000000..6e1adbe0fc --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/KeystoreTest.java @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class KeystoreTest { + private static final String TEST_KEY_STR = + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"; + private static final String PASSPHRASE = "asdf1234"; + + @Test + @DisplayName("Keystore.fromStream returns correct key") + void keystoreFromStream() throws IOException { + // keystore file generated by hedera-sdk-js from `testKeyStr` and `passphrase` + // NOT USED ANYWHERE + InputStream inputStream = KeystoreTest.class.getResourceAsStream("/test-keystore.bin"); + Keystore keystore = Keystore.fromStream(inputStream, PASSPHRASE); + + PrivateKey privateKey = keystore.getEd25519(); + assertThat(privateKey.toString()).isEqualTo(TEST_KEY_STR); + } + + @Test + @DisplayName("Keystore.fromStream returns correct key v2") + void keystoreFromStreamV2() throws IOException { + // keystore file generated by hedera-sdk-js from `testKeyStr` and `passphrase` + // NOT USED ANYWHERE + InputStream inputStream = KeystoreTest.class.getResourceAsStream("/test-keystore2.bin"); + Keystore keystore = Keystore.fromStream(inputStream, PASSPHRASE); + + PrivateKey privateKey = keystore.getEd25519(); + assertThat(privateKey.toString()).isEqualTo(TEST_KEY_STR); + } + + @Test + @DisplayName("Keystore.toStream produces decodable value") + void keystoreToStream() throws IOException { + PrivateKey privateKey = PrivateKey.fromString(TEST_KEY_STR); + Keystore keystore = new Keystore(privateKey); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + keystore.export(outputStream, PASSPHRASE); + + Keystore keystore2 = Keystore.fromStream(new ByteArrayInputStream(outputStream.toByteArray()), PASSPHRASE); + PrivateKey privateKey2 = keystore2.getEd25519(); + + assertThat(privateKey2.toString()).isEqualTo(TEST_KEY_STR); + } +} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ListInputTest.java b/sdk/src/test/java/org/hiero/sdk/ListInputTest.java similarity index 87% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/ListInputTest.java rename to sdk/src/test/java/org/hiero/sdk/ListInputTest.java index c21bd43b9e..70d8e50d7e 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ListInputTest.java +++ b/sdk/src/test/java/org/hiero/sdk/ListInputTest.java @@ -1,31 +1,12 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.ArrayList; import java.util.Objects; - -import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; // A number of transactions take List<>s as inputs. // If the list parameter is used directly/naively, it can break encapsulation. @@ -110,15 +91,15 @@ void tokenWipeListTest() { void tokenMintListTest() { var tx = new TokenMintTransaction(); var list = new ArrayList(); - list.add(new byte[]{0}); + list.add(new byte[] {0}); tx.setMetadata(list); var v1 = new ArrayList<>(tx.getMetadata()); - list.add(new byte[]{1}); + list.add(new byte[] {1}); var v2 = new ArrayList<>(tx.getMetadata()); assertEquals(v1.toString(), v2.toString()); var list2 = tx.getMetadata(); - list2.add(new byte[]{2}); + list2.add(new byte[] {2}); var v3 = tx.getMetadata(); assertEquals(v1.toString(), v3.toString()); } diff --git a/sdk/src/test/java/org/hiero/sdk/LiveHashAddTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/LiveHashAddTransactionTest.java new file mode 100644 index 0000000000..43b52acbd1 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/LiveHashAddTransactionTest.java @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.google.protobuf.ByteString; +import io.github.jsonSnapshot.SnapshotMatcher; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.time.Instant; +import java.util.Arrays; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +class LiveHashAddTransactionTest { + private static final PrivateKey privateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + private LiveHashAddTransaction spawnTestTransaction() { + return new LiveHashAddTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setAccountId(AccountId.fromString("0.0.100")) + .setHash(ByteString.copyFrom("hash", StandardCharsets.UTF_8)) + .setKeys(privateKey) + .setDuration(Duration.ofDays(30)) + .freeze() + .sign(privateKey); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new LiveHashAddTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = LiveHashAddTransaction.fromBytes(tx.toBytes()); + assertThat(tx2).hasToString(tx.toString()); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/LiveHashAddTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/LiveHashAddTransactionTest.snap new file mode 100644 index 0000000000..b5126cf751 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/LiveHashAddTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.LiveHashAddTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\ncrypto_add_live_hash {\n live_hash {\n account_id {\n account_num: 100\n realm_num: 0\n shard_num: 0\n }\n duration {\n seconds: 2592000\n }\n hash: \"hash\"\n keys {\n keys {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n }\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 200000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/LiveHashDeleteTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/LiveHashDeleteTransactionTest.java new file mode 100644 index 0000000000..eb3976ea54 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/LiveHashDeleteTransactionTest.java @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.google.protobuf.ByteString; +import io.github.jsonSnapshot.SnapshotMatcher; +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.util.Arrays; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +class LiveHashDeleteTransactionTest { + private static final PrivateKey privateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + private LiveHashDeleteTransaction spawnTestTransaction() { + return new LiveHashDeleteTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setAccountId(AccountId.fromString("0.0.100")) + .setHash(ByteString.copyFrom("hash", StandardCharsets.UTF_8)) + .freeze() + .sign(privateKey); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = LiveHashDeleteTransaction.fromBytes(tx.toBytes()); + assertThat(tx2).hasToString(tx.toString()); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new LiveHashDeleteTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/LiveHashDeleteTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/LiveHashDeleteTransactionTest.snap new file mode 100644 index 0000000000..fbbe1b5994 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/LiveHashDeleteTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.LiveHashDeleteTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\ncrypto_delete_live_hash {\n account_of_live_hash {\n account_num: 100\n realm_num: 0\n shard_num: 0\n }\n live_hash_to_delete: \"hash\"\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 200000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/LiveHashQueryTest.java b/sdk/src/test/java/org/hiero/sdk/LiveHashQueryTest.java new file mode 100644 index 0000000000..6a908cf7c1 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/LiveHashQueryTest.java @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.github.jsonSnapshot.SnapshotMatcher; +import org.hiero.sdk.proto.QueryHeader; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class LiveHashQueryTest { + private static final byte[] hash = {0, 1, 2}; + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + var builder = org.hiero.sdk.proto.Query.newBuilder(); + new LiveHashQuery() + .setAccountId(AccountId.fromString("0.0.100")) + .setHash(hash) + .onMakeRequest(builder, QueryHeader.newBuilder().build()); + SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")) + .toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/LiveHashQueryTest.snap b/sdk/src/test/java/org/hiero/sdk/LiveHashQueryTest.snap new file mode 100644 index 0000000000..775892d68b --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/LiveHashQueryTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.LiveHashQueryTest.shouldSerialize=[ + "# org.hiero.sdk.proto.Query\ncrypto_get_live_hash {\n account_i_d {\n account_num: 100\n realm_num: 0\n shard_num: 0\n }\n hash: \"\\000\\001\\002\"\n header {\n }\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/MaxQueryPaymentExceededExceptionTest.java b/sdk/src/test/java/org/hiero/sdk/MaxQueryPaymentExceededExceptionTest.java new file mode 100644 index 0000000000..1d96315a96 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/MaxQueryPaymentExceededExceptionTest.java @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class MaxQueryPaymentExceededExceptionTest { + @Test + void shouldHaveMessage() { + var e = new MaxQueryPaymentExceededException(new AccountBalanceQuery(), new Hbar(30), new Hbar(15)); + + assertThat(e.getMessage()) + .isEqualTo( + "cost for AccountBalanceQuery, of 30 ℏ, without explicit payment is greater than the maximum allowed payment of 15 ℏ"); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/MessageSubmitTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/MessageSubmitTransactionTest.java new file mode 100644 index 0000000000..97e38ca298 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/MessageSubmitTransactionTest.java @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import org.hiero.sdk.proto.ConsensusSubmitMessageTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class MessageSubmitTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = FreezeTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new TopicMessageSubmitTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + private TopicMessageSubmitTransaction spawnTestTransaction() { + return new TopicMessageSubmitTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setTopicId(TopicId.fromString("0.0.5007")) + .setMessage("hello") + .setMaxTransactionFee(Hbar.fromTinybars(100_000)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setConsensusSubmitMessage( + ConsensusSubmitMessageTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(TopicMessageSubmitTransaction.class); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/MessageSubmitTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/MessageSubmitTransactionTest.snap new file mode 100644 index 0000000000..ed2688eda5 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/MessageSubmitTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.MessageSubmitTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nconsensus_submit_message {\n message: \"hello\"\n topic_i_d {\n realm_num: 0\n shard_num: 0\n topic_num: 5007\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/MirrorNodeContractQueryTest.java b/sdk/src/test/java/org/hiero/sdk/MirrorNodeContractQueryTest.java similarity index 83% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/MirrorNodeContractQueryTest.java rename to sdk/src/test/java/org/hiero/sdk/MirrorNodeContractQueryTest.java index 888672bd1c..5c205394fa 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/MirrorNodeContractQueryTest.java +++ b/sdk/src/test/java/org/hiero/sdk/MirrorNodeContractQueryTest.java @@ -1,23 +1,5 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -132,7 +114,7 @@ void testSetAndGetGas() { mirrorNodeContractEstimateGasQuery.setGasLimit(gas); assertEquals(gas, mirrorNodeContractEstimateGasQuery.getGasLimit()); - mirrorNodeContractCallQuery.setGasLimit(gas); + mirrorNodeContractCallQuery.setGasLimit(gas); assertEquals(gas, mirrorNodeContractCallQuery.getGasLimit()); } @@ -167,8 +149,8 @@ void testCreateJsonPayloadAllFieldsSet() { String blockNumber = "latest"; boolean estimate = true; - String jsonPayload = MirrorNodeContractQuery.createJsonPayload(data, senderAddress, contractAddress, gas, - gasPrice, value, blockNumber, estimate); + String jsonPayload = MirrorNodeContractQuery.createJsonPayload( + data, senderAddress, contractAddress, gas, gasPrice, value, blockNumber, estimate); JsonObject expectedJson = new JsonObject(); expectedJson.addProperty("data", "7465737444617461"); @@ -194,8 +176,8 @@ void testCreateJsonPayloadOnlyRequiredFieldsSet() { String blockNumber = "latest"; boolean estimate = true; - String jsonPayload = MirrorNodeContractQuery.createJsonPayload(data, senderAddress, contractAddress, gas, - gasPrice, value, blockNumber, estimate); + String jsonPayload = MirrorNodeContractQuery.createJsonPayload( + data, senderAddress, contractAddress, gas, gasPrice, value, blockNumber, estimate); JsonObject expectedJson = new JsonObject(); expectedJson.addProperty("data", "7465737444617461"); @@ -217,8 +199,8 @@ void testCreateJsonPayloadSomeOptionalFieldsSet() { String blockNumber = "latest"; boolean estimate = false; - String jsonPayload = MirrorNodeContractQuery.createJsonPayload(data, senderAddress, contractAddress, gas, - gasPrice, value, blockNumber, estimate); + String jsonPayload = MirrorNodeContractQuery.createJsonPayload( + data, senderAddress, contractAddress, gas, gasPrice, value, blockNumber, estimate); JsonObject expectedJson = new JsonObject(); expectedJson.addProperty("data", "7465737444617461"); @@ -243,8 +225,8 @@ void testCreateJsonPayloadAllOptionalFieldsDefault() { String blockNumber = "latest"; boolean estimate = false; - String jsonPayload = MirrorNodeContractQuery.createJsonPayload(data, senderAddress, contractAddress, gas, - gasPrice, value, blockNumber, estimate); + String jsonPayload = MirrorNodeContractQuery.createJsonPayload( + data, senderAddress, contractAddress, gas, gasPrice, value, blockNumber, estimate); JsonObject expectedJson = new JsonObject(); expectedJson.addProperty("data", "7465737444617461"); @@ -285,31 +267,30 @@ void shouldSerialize() { long testBlockNumber = 123456L; var mirrorNodeContractEstimateGasQuery = new MirrorNodeContractEstimateGasQuery() - .setContractId(testContractId) - .setContractEvmAddress(testEvmAddress) - .setSender(testSenderId) - .setSenderEvmAddress(testSenderEvmAddress) - .setFunction(testFunctionName, testParams) - .setFunctionParameters(testCallData) - .setValue(testValue) - .setGasLimit(testGasLimit) - .setGasPrice(testGasPrice) - .setBlockNumber(testBlockNumber); + .setContractId(testContractId) + .setContractEvmAddress(testEvmAddress) + .setSender(testSenderId) + .setSenderEvmAddress(testSenderEvmAddress) + .setFunction(testFunctionName, testParams) + .setFunctionParameters(testCallData) + .setValue(testValue) + .setGasLimit(testGasLimit) + .setGasPrice(testGasPrice) + .setBlockNumber(testBlockNumber); var mirrorNodeContractCallQuery = new MirrorNodeContractCallQuery() - .setContractId(testContractId) - .setContractEvmAddress(testEvmAddress) - .setSender(testSenderId) - .setSenderEvmAddress(testSenderEvmAddress) - .setFunction(testFunctionName, testParams) - .setFunctionParameters(testCallData) - .setValue(testValue) - .setGasLimit(testGasLimit) - .setGasPrice(testGasPrice) - .setBlockNumber(testBlockNumber); - - - SnapshotMatcher.expect(mirrorNodeContractEstimateGasQuery.toString() + mirrorNodeContractCallQuery.toString() - ).toMatchSnapshot(); + .setContractId(testContractId) + .setContractEvmAddress(testEvmAddress) + .setSender(testSenderId) + .setSenderEvmAddress(testSenderEvmAddress) + .setFunction(testFunctionName, testParams) + .setFunctionParameters(testCallData) + .setValue(testValue) + .setGasLimit(testGasLimit) + .setGasPrice(testGasPrice) + .setBlockNumber(testBlockNumber); + + SnapshotMatcher.expect(mirrorNodeContractEstimateGasQuery.toString() + mirrorNodeContractCallQuery.toString()) + .toMatchSnapshot(); } } diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/MirrorNodeContractQueryTest.snap b/sdk/src/test/java/org/hiero/sdk/MirrorNodeContractQueryTest.snap similarity index 89% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/MirrorNodeContractQueryTest.snap rename to sdk/src/test/java/org/hiero/sdk/MirrorNodeContractQueryTest.snap index 2394e7ccfa..a3a86e148e 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/MirrorNodeContractQueryTest.snap +++ b/sdk/src/test/java/org/hiero/sdk/MirrorNodeContractQueryTest.snap @@ -1,3 +1,3 @@ -com.hedera.hashgraph.sdk.MirrorNodeContractQueryTest.shouldSerialize=[ +org.hiero.sdk.MirrorNodeContractQueryTest.shouldSerialize=[ "MirrorNodeContractEstimateGasQuery{contractId=null, contractEvmAddress='0x1234567890abcdef1234567890abcdef12345678', sender=null, senderEvmAddress='0xabcdefabcdefabcdefabcdefabcdefabcdef', callData=[116, 101, 115, 116, 68, 97, 116, 97], value=1000, gasLimit=500000, gasPrice=20, blockNumber=123456}MirrorNodeContractCallQuery{contractId=null, contractEvmAddress='0x1234567890abcdef1234567890abcdef12345678', sender=null, senderEvmAddress='0xabcdefabcdefabcdefabcdefabcdefabcdef', callData=[116, 101, 115, 116, 68, 97, 116, 97], value=1000, gasLimit=500000, gasPrice=20, blockNumber=123456}" -] \ No newline at end of file +] diff --git a/sdk/src/test/java/org/hiero/sdk/MnemonicTest.java b/sdk/src/test/java/org/hiero/sdk/MnemonicTest.java new file mode 100644 index 0000000000..222e37df1e --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/MnemonicTest.java @@ -0,0 +1,755 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Arrays; +import org.bouncycastle.util.encoders.Hex; +import org.hiero.sdk.utils.Bip32Utils; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +public class MnemonicTest { + private static final String MNEMONIC_LEGACY_V1_STRING = + "jolly kidnap tom lawn drunk chick optic lust mutter mole bride galley dense member sage neural widow decide curb aboard margin manure"; + private static final String MNEMONIC_LEGACY_V2_STRING = + "obvious favorite remain caution remove laptop base vacant increase video erase pass sniff sausage knock grid argue salt romance way alone fever slush dune"; + private static final String MNEMONIC_24_WORD_STRING = + "inmate flip alley wear offer often piece magnet surge toddler submit right radio absent pear floor belt raven price stove replace reduce plate home"; + private static final String MNEMONIC_12_WORD_STRING = + "finish furnace tomorrow wine mass goose festival air palm easy region guilt"; + + @Test + @DisplayName("Mnemonic.generate() creates a valid mnemonic") + void generateValidMnemonic() { + Mnemonic.generate24(); + Mnemonic.generate12(); + } + + @ParameterizedTest + @DisplayName("Mnemonic.validate() passes on known-good mnemonics") + @ValueSource( + strings = { + "inmate flip alley wear offer often piece magnet surge toddler submit right radio absent pear floor belt raven price stove replace reduce plate home", + "tiny denial casual grass skull spare awkward indoor ethics dash enough flavor good daughter early hard rug staff capable swallow raise flavor empty angle", + "ramp april job flavor surround pyramid fish sea good know blame gate village viable include mixed term draft among monitor swear swing novel track", + "evoke rich bicycle fire promote climb zero squeeze little spoil slight damage", + }) + void knownGoodMnemonics(String mnemonicStr) throws Exception { + Mnemonic.fromString(mnemonicStr); + } + + @Test + @DisplayName("Mnemonic.validate() throws on short word list") + void shortWordList() { + assertThatExceptionOfType(BadMnemonicException.class) + .isThrownBy(() -> Mnemonic.fromWords(Arrays.asList("lorem", "ipsum", "dolor"))) + .satisfies(error -> { + assertThat(error.reason).isEqualTo(BadMnemonicReason.BadLength); + assertThat(error.unknownWordIndices).isNull(); + }); + } + + @Test + @DisplayName("Mnemonic.validate() throws on long word list") + void longWordList() { + assertThatExceptionOfType(BadMnemonicException.class) + .isThrownBy(() -> Mnemonic.fromWords(Arrays.asList( + "lorem", + "ipsum", + "dolor", + "ramp", + "april", + "job", + "flavor", + "surround", + "pyramid", + "fish", + "sea", + "good", + "know", + "blame", + "gate", + "village", + "viable", + "include", + "mixed", + "term", + "draft", + "among", + "monitor", + "swear", + "swing", + "novel", + "track"))) + .satisfies(error -> { + assertThat(error.reason).isEqualTo(BadMnemonicReason.BadLength); + assertThat(error.unknownWordIndices).isNull(); + }); + } + + @Test + @DisplayName("Mnemonic.validate() throws on 12-24 words") + void betweenWordList() { + assertThatExceptionOfType(BadMnemonicException.class) + .isThrownBy(() -> Mnemonic.fromWords(Arrays.asList( + "" + "lorem", + "ipsum", + "dolor", + "ramp", + "april", + "job", + "flavor", + "surround", + "pyramid", + "fish", + "sea", + "good", + "know", + "blame"))) + .satisfies(error -> { + assertThat(error.reason).isEqualTo(BadMnemonicReason.BadLength); + assertThat(error.unknownWordIndices).isNull(); + }); + } + + @Test + @DisplayName("Mnemonic.validate() throws on unknown words") + void unknownWords() { + assertThatExceptionOfType(BadMnemonicException.class) + .isThrownBy(() -> Mnemonic.fromWords(Arrays.asList( + "abandon", + "ability", + "able", + "about", + "above", + "absent", + "adsorb", // typo from "absorb" + "abstract", + "absurd", + "abuse", + "access", + "accident", + "acount", // typo from "account" + "accuse", + "achieve", + "acid", + "acoustic", + "acquired", // typo from "acquire" + "across", + "act", + "action", + "actor", + "actress", + "actual"))) + .satisfies(error -> { + assertThat(error.reason).isEqualTo(BadMnemonicReason.UnknownWords); + assertThat(error.unknownWordIndices).containsExactly(6, 12, 17); + }); + } + + @Test + @DisplayName("Mnemonic.validate() throws on checksum mismatch, 24 words") + void checksumMismatch() { + // this mnemonic was just made up, the checksum should definitely not match + assertThatExceptionOfType(BadMnemonicException.class) + .isThrownBy(() -> Mnemonic.fromWords(Arrays.asList( + "abandon", + "ability", + "able", + "about", + "above", + "absent", + "absorb", + "abstract", + "absurd", + "abuse", + "access", + "accident", + "account", + "accuse", + "achieve", + "acid", + "acoustic", + "acquire", + "across", + "act", + "action", + "actor", + "actress", + "actual"))) + .satisfies(error -> { + assertThat(error.reason).isEqualTo(BadMnemonicReason.ChecksumMismatch); + assertThat(error.unknownWordIndices).isNull(); + }); + } + + @Test + @DisplayName("Mnemonic.validate() throws on checksum mismatch, 12 words") + void checksumMismatch12() { + // this mnemonic was just made up, the checksum should definitely not match + assertThatExceptionOfType(BadMnemonicException.class) + .isThrownBy(() -> Mnemonic.fromWords(Arrays.asList( + "abandon", + "ability", + "able", + "about", + "above", + "absent", + "absorb", + "abstract", + "absurd", + "abuse", + "access", + "accident"))) + .satisfies(error -> { + assertThat(error.reason).isEqualTo(BadMnemonicReason.ChecksumMismatch); + assertThat(error.unknownWordIndices).isNull(); + }); + } + + @Test + @DisplayName("Invalid Mnemonic can still be used to generate a private key") + void invalidToPrivateKey() { + assertThatExceptionOfType(BadMnemonicException.class) + .isThrownBy(() -> Mnemonic.fromWords(Arrays.asList( + "abandon", + "ability", + "able", + "about", + "above", + "absent", + "absorb", + "abstract", + "absurd", + "abuse", + "access", + "accident", + "account", + "accuse", + "achieve", + "acid", + "acoustic", + "acquire", + "across", + "act", + "action", + "actor", + "actress", + "actual"))) + .satisfies(error -> assertThat(error.mnemonic).isNotNull()); + } + + @Test + @DisplayName("Legacy V1 mnemonic test") + void legacyV1MnemonicTest() throws Exception { + // TODO: add link to reference test vectors + final String PRIVATE_KEY1 = "00c2f59212cb3417f0ee0d38e7bd876810d04f2dd2cb5c2d8f26ff406573f2bd"; + final String PUBLIC_KEY1 = "0c5bb4624df6b64c2f07a8cb8753945dd42d4b9a2ed4c0bf98e87ef154f473e9"; + + final String PRIVATE_KEY2 = "fae0002d2716ea3a60c9cd05ee3c4bb88723b196341b68a02d20975f9d049dc6"; + final String PUBLIC_KEY2 = "f40f9fdb1f161c31ed656794ada7af8025e8b5c70e538f38a4dfb46a0a6b0392"; + + final String PRIVATE_KEY3 = "882a565ad8cb45643892b5366c1ee1c1ef4a730c5ce821a219ff49b6bf173ddf"; + final String PUBLIC_KEY3 = "53c6b451e695d6abc52168a269316a0d20deee2331f612d4fb8b2b379e5c6854"; + + final String PRIVATE_KEY4 = "6890dc311754ce9d3fc36bdf83301aa1c8f2556e035a6d0d13c2cccdbbab1242"; + final String PUBLIC_KEY4 = "45f3a673984a0b4ee404a1f4404ed058475ecd177729daa042e437702f7791e9"; + + Mnemonic mnemonic = Mnemonic.fromString(MNEMONIC_LEGACY_V1_STRING); + + // Chain m + PrivateKey key1 = mnemonic.toLegacyPrivateKey(); + assertThat(key1.toStringRaw()).isEqualTo(PRIVATE_KEY1); + assertThat(key1.getPublicKey().toStringRaw()).isEqualTo(PUBLIC_KEY1); + + // Chain m/0 + PrivateKey key2 = key1.legacyDerive(0); + assertThat(key2.toStringRaw()).isEqualTo(PRIVATE_KEY2); + assertThat(key2.getPublicKey().toStringRaw()).isEqualTo(PUBLIC_KEY2); + + // Chain m/-1 + PrivateKey key3 = key1.legacyDerive(-1); + assertThat(key3.toStringRaw()).isEqualTo(PRIVATE_KEY3); + assertThat(key3.getPublicKey().toStringRaw()).isEqualTo(PUBLIC_KEY3); + + // Chain m/1099511627775 + PrivateKey key4 = key1.legacyDerive(1099511627775L); + assertThat(key4.toStringRaw()).isEqualTo(PRIVATE_KEY4); + assertThat(key4.getPublicKey().toStringRaw()).isEqualTo(PUBLIC_KEY4); + } + + @Test + @DisplayName("Legacy V2 mnemonic test") + void legacyV2MnemonicTest() throws Exception { + // TODO: add link to reference test vectors + final String PRIVATE_KEY1 = "98aa82d6125b5efa04bf8372be7931d05cd77f5ef3330b97d6ee7c006eaaf312"; + final String PUBLIC_KEY1 = "e0ce688d614f22f96d9d213ca513d58a7d03d954fe45790006e6e86b25456465"; + + final String PRIVATE_KEY2 = "2b7345f302a10c2a6d55bf8b7af40f125ec41d780957826006d30776f0c441fb"; + final String PUBLIC_KEY2 = "0e19f99800b007cc7c82f9d85b73e0f6e48799469450caf43f253b48c4d0d91a"; + + final String PRIVATE_KEY3 = "caffc03fdb9853e6a91a5b3c57a5c0031d164ce1c464dea88f3114786b5199e5"; + final String PUBLIC_KEY3 = "9fe11da3fcfba5d28a6645ecb611a9a43dbe6014b102279ba1d34506ea86974b"; + + Mnemonic mnemonic = Mnemonic.fromString(MNEMONIC_LEGACY_V2_STRING); + + // Chain m + PrivateKey key1 = mnemonic.toLegacyPrivateKey(); + assertThat(key1.toStringRaw()).isEqualTo(PRIVATE_KEY1); + assertThat(key1.getPublicKey().toStringRaw()).isEqualTo(PUBLIC_KEY1); + + // Chain m/0 + PrivateKey key2 = key1.legacyDerive(0); + assertThat(key2.toStringRaw()).isEqualTo(PRIVATE_KEY2); + assertThat(key2.getPublicKey().toStringRaw()).isEqualTo(PUBLIC_KEY2); + + // Chain m/-1 + PrivateKey key3 = key1.legacyDerive(-1); + assertThat(key3.toStringRaw()).isEqualTo(PRIVATE_KEY3); + assertThat(key3.getPublicKey().toStringRaw()).isEqualTo(PUBLIC_KEY3); + } + + @Test + @DisplayName("Mnemonic test") + void mnemonicTest() throws Exception { + Mnemonic mnemonic = Mnemonic.fromString(MNEMONIC_24_WORD_STRING); + PrivateKey key = mnemonic.toPrivateKey(); + assertThat(key) + .hasToString( + "302e020100300506032b657004220420853f15aecd22706b105da1d709b4ac05b4906170c2b9c7495dff9af49e1391da"); + } + + @Test + @DisplayName("Mnemonic passphrase test") + void mnemonicPassphraseTest() throws Exception { + // Test if mnemonic passphrase is BIP-39 compliant which requires unicode phrases to be NFKD normalized. + // Use unicode string as a passphrase. If it is properly normalized to NFKD, + // it should generate the expectedPrivateKey bellow: + String passphrase = "\u03B4\u03BF\u03BA\u03B9\u03BC\u03AE"; + String expectedPrivateKey = + "302e020100300506032b6570042204203fefe1000db9485372851d542453b07e7970de4e2ecede7187d733ac037f4d2c"; + + Mnemonic mnemonic = Mnemonic.fromString(MNEMONIC_24_WORD_STRING); + PrivateKey key = mnemonic.toPrivateKey(passphrase); + assertThat(key.toString()).isEqualTo(expectedPrivateKey); + } + + @Test + @DisplayName("BIP39 test vector") + void bip39() throws Exception { + final String passphrase = "TREZOR"; + + // The 18-word mnemonics are not supported by the SDK + final String[] MNEMONIC_STRINGS = { + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", + "legal winner thank year wave sausage worth useful legal winner thank yellow", + "letter advice cage absurd amount doctor acoustic avoid letter advice cage above", + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong", + // "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon + // abandon abandon abandon abandon abandon abandon agent", + // "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth + // useful legal will", + // "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount + // doctor acoustic avoid letter always", + // "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when", + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art", + "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth title", + "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic bless", + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote", + "ozone drill grab fiber curtain grace pudding thank cruise elder eight picnic", + // "gravity machine north sort system female filter attitude volume fold club stay feature office + // ecology stable narrow fog", + "hamster diagram private dutch cause delay private meat slide toddler razor book happy fancy gospel tennis maple dilemma loan word shrug inflict delay length", + "scheme spot photo card baby mountain device kick cradle pact join borrow", + // "horn tenant knee talent sponsor spell gate clip pulse soap slush warm silver nephew swap + // uncle crack brave", + "panda eyebrow bullet gorilla call smoke muffin taste mesh discover soft ostrich alcohol speed nation flash devote level hobby quick inner drive ghost inside", + "cat swing flag economy stadium alone churn speed unique patch report train", + // "light rule cinnamon wrap drastic word pride squirrel upgrade then income fatal apart sustain + // crack supply proud access", + "all hour make first leader extend hole alien behind guard gospel lava path output census museum junior mass reopen famous sing advance salt reform", + "vessel ladder alter error federal sibling chat ability sun glass valve picture", + // "scissors invite lock maple supreme raw rapid void congress muscle digital elegant little + // brisk hair mango congress clump", + "void come effort suffer camp survey warrior heavy shoot primary clutch crush open amazing screen patrol group space point ten exist slush involve unfold", + }; + + final String[] EXPECTED_SEEDS = { + "c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04", + "2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6fa457fe1296106559a3c80937a1c1069be3a3a5bd381ee6260e8d9739fce1f607", + "d71de856f81a8acc65e6fc851a38d4d7ec216fd0796d0a6827a3ad6ed5511a30fa280f12eb2e47ed2ac03b5c462a0358d18d69fe4f985ec81778c1b370b652a8", + "ac27495480225222079d7be181583751e86f571027b0497b5b5d11218e0a8a13332572917f0f8e5a589620c6f15b11c61dee327651a14c34e18231052e48c069", + // + // "035895f2f481b1b0f01fcf8c289c794660b289981a78f8106447707fdd9666ca06da5a9a565181599b79f53b844d8a71dd9f439c52a3d7b3e8a79c906ac845fa", + // + // "035895f2f481b1b0f01fcf8c289c794660b289981a78f8106447707fdd9666ca06da5a9a565181599b79f53b844d8a71dd9f439c52a3d7b3e8a79c906ac845fa", + // + // "107d7c02a5aa6f38c58083ff74f04c607c2d2c0ecc55501dadd72d025b751bc27fe913ffb796f841c49b1d33b610cf0e91d3aa239027f5e99fe4ce9e5088cd65", + // + // "0cd6e5d827bb62eb8fc1e262254223817fd068a74b5b449cc2f667c3f1f985a76379b43348d952e2265b4cd129090758b3e3c2c49103b5051aac2eaeb890a528", + "bda85446c68413707090a52022edd26a1c9462295029f2e60cd7c4f2bbd3097170af7a4d73245cafa9c3cca8d561a7c3de6f5d4a10be8ed2a5e608d68f92fcc8", + "bc09fca1804f7e69da93c2f2028eb238c227f2e9dda30cd63699232578480a4021b146ad717fbb7e451ce9eb835f43620bf5c514db0f8add49f5d121449d3e87", + "c0c519bd0e91a2ed54357d9d1ebef6f5af218a153624cf4f2da911a0ed8f7a09e2ef61af0aca007096df430022f7a2b6fb91661a9589097069720d015e4e982f", + "dd48c104698c30cfe2b6142103248622fb7bb0ff692eebb00089b32d22484e1613912f0a5b694407be899ffd31ed3992c456cdf60f5d4564b8ba3f05a69890ad", + "274ddc525802f7c828d8ef7ddbcdc5304e87ac3535913611fbbfa986d0c9e5476c91689f9c8a54fd55bd38606aa6a8595ad213d4c9c9f9aca3fb217069a41028", + // + // "628c3827a8823298ee685db84f55caa34b5cc195a778e52d45f59bcf75aba68e4d7590e101dc414bc1bbd5737666fbbef35d1f1903953b66624f910feef245ac", + "64c87cde7e12ecf6704ab95bb1408bef047c22db4cc7491c4271d170a1b213d20b385bc1588d9c7b38f1b39d415665b8a9030c9ec653d75e65f847d8fc1fc440", + "ea725895aaae8d4c1cf682c1bfd2d358d52ed9f0f0591131b559e2724bb234fca05aa9c02c57407e04ee9dc3b454aa63fbff483a8b11de949624b9f1831a9612", + // + // "fd579828af3da1d32544ce4db5c73d53fc8acc4ddb1e3b251a31179cdb71e853c56d2fcb11aed39898ce6c34b10b5382772db8796e52837b54468aeb312cfc3d", + "72be8e052fc4919d2adf28d5306b5474b0069df35b02303de8c1729c9538dbb6fc2d731d5f832193cd9fb6aeecbc469594a70e3dd50811b5067f3b88b28c3e8d", + "deb5f45449e615feff5640f2e49f933ff51895de3b4381832b3139941c57b59205a42480c52175b6efcffaa58a2503887c1e8b363a707256bdd2b587b46541f5", + // + // "4cbdff1ca2db800fd61cae72a57475fdc6bab03e441fd63f96dabd1f183ef5b782925f00105f318309a7e9c3ea6967c7801e46c8a58082674c860a37b93eda02", + "26e975ec644423f4a4c4f4215ef09b4bd7ef924e85d1d17c4cf3f136c2863cf6df0a475045652c57eb5fb41513ca2a2d67722b77e954b4b3fc11f7590449191d", + "2aaa9242daafcee6aa9d7269f17d4efe271e1b9a529178d7dc139cd18747090bf9d60295d0ce74309a78852a9caadf0af48aae1c6253839624076224374bc63f", + // + // "7b4a10be9d98e6cba265566db7f136718e1398c71cb581e1b2f464cac1ceedf4f3e274dc270003c670ad8d02c4558b2f8e39edea2775c9e232c7cb798b069e88", + "01f5bced59dec48e362f2c45b5de68b9fd6c92c6634f44d6d40aab69056506f0e35524a518034ddc1192e1dacd32c1ed3eaa3c3b131c88ed8e7e54c49a5d0998", + }; + + for (int i = 0; i < MNEMONIC_STRINGS.length; i++) { + byte[] seed = Mnemonic.fromString(MNEMONIC_STRINGS[i]).toSeed(passphrase); + assertThat(Hex.toHexString(seed)).isEqualTo(EXPECTED_SEEDS[i]); + } + } + + @Test + @DisplayName("Mnemonic.toStandardED25519PrivateKey() test vector") + void toStandardED25519PrivateKey() throws BadMnemonicException { + // https://github.com/hashgraph/hedera-sdk-reference/issues/73#issuecomment-1422330626 + final String CHAIN_CODE1 = "404914563637c92d688deb9d41f3f25cbe8d6659d859cc743712fcfac72d7eda"; + final String PRIVATE_KEY1 = "f8dcc99a1ced1cc59bc2fee161c26ca6d6af657da9aa654da724441343ecd16f"; + final String PUBLIC_KEY1 = "2e42c9f5a5cdbde64afa65ce3dbaf013d5f9ff8d177f6ef4eb89fbe8c084ec0d"; + + final String CHAIN_CODE2 = "9c2b0073ac934696cd0b52c6c521b9bd1902aac134380a737282fdfe29014bf1"; + final String PRIVATE_KEY2 = "e978a6407b74a0730f7aeb722ad64ab449b308e56006c8bff9aad070b9b66ddf"; + final String PUBLIC_KEY2 = "c4b33dca1f83509f17b69b2686ee46b8556143f79f4b9df7fe7ed3864c0c64d0"; + + final String CHAIN_CODE3 = "699344acc5e07c77eb63b154b4c5c3d33cab8bf85ee21bea4cc29ab7f0502259"; + final String PRIVATE_KEY3 = "abeca64d2337db386e289482a252334c68c7536daaefff55dc169ddb77fbae28"; + final String PUBLIC_KEY3 = "fd311925a7a04b38f7508931c6ae6a93e5dc4394d83dafda49b051c0017d3380"; + + final String CHAIN_CODE4 = "e5af7c95043a912af57a6e031ddcad191677c265d75c39954152a2733c750a3b"; + final String PRIVATE_KEY4 = "9a601db3e24b199912cec6573e6a3d01ffd3600d50524f998b8169c105165ae5"; + final String PUBLIC_KEY4 = "cf525500706faa7752dca65a086c9381d30d72cc67f23bf334f330579074a890"; + + Mnemonic mnemonic = Mnemonic.fromString(MNEMONIC_24_WORD_STRING); + + // Chain m/44'/3030'/0'/0'/0' + PrivateKey key1 = mnemonic.toStandardEd25519PrivateKey("", 0); + assertThat(Hex.toHexString(key1.getChainCode().getKey())).isEqualTo(CHAIN_CODE1); + assertThat(key1.toStringRaw()).isEqualTo(PRIVATE_KEY1); + assertThat(key1.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY1); + + // Chain m/44'/3030'/0'/0'/2147483647' + PrivateKey key2 = mnemonic.toStandardEd25519PrivateKey("", 2147483647); + assertThat(Hex.toHexString(key2.getChainCode().getKey())).isEqualTo(CHAIN_CODE2); + assertThat(key2.toStringRaw()).isEqualTo(PRIVATE_KEY2); + assertThat(key2.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY2); + + // Chain m/44'/3030'/0'/0'/0'; Passphrase: "some pass" + PrivateKey key3 = mnemonic.toStandardEd25519PrivateKey("some pass", 0); + assertThat(Hex.toHexString(key3.getChainCode().getKey())).isEqualTo(CHAIN_CODE3); + assertThat(key3.toStringRaw()).isEqualTo(PRIVATE_KEY3); + assertThat(key3.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY3); + + // Chain m/44'/3030'/0'/0'/2147483647'; Passphrase: "some pass" + PrivateKey key4 = mnemonic.toStandardEd25519PrivateKey("some pass", 2147483647); + assertThat(Hex.toHexString(key4.getChainCode().getKey())).isEqualTo(CHAIN_CODE4); + assertThat(key4.toStringRaw()).isEqualTo(PRIVATE_KEY4); + assertThat(key4.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY4); + } + + @Test + @DisplayName("Mnemonic.toStandardED25519PrivateKey() test vector 2") + void toStandardED25519PrivateKey2() throws BadMnemonicException { + // https://github.com/hashgraph/hedera-sdk-reference/issues/73#issuecomment-1424761224 + final String CHAIN_CODE1 = "48c89d67e9920e443f09d2b14525213ff83b245c8b98d63747ea0801e6d0ff3f"; + final String PRIVATE_KEY1 = "020487611f3167a68482b0f4aacdeb02cc30c52e53852af7b73779f67eeca3c5"; + final String PUBLIC_KEY1 = "2d047ff02a2091f860633f849ea2024b23e7803cfd628c9bdd635010cbd782d3"; + + final String CHAIN_CODE2 = "c0bcdbd9df6d8a4f214f20f3e5c7856415b68be34a1f406398c04690818bea16"; + final String PRIVATE_KEY2 = "d0c4484480944db698dd51936b7ecc81b0b87e8eafc3d5563c76339338f9611a"; + final String PUBLIC_KEY2 = "a1a2573c2c45bd57b0fd054865b5b3d8f492a6e1572bf04b44471e07e2f589b2"; + + final String CHAIN_CODE3 = "998a156855ab5398afcde06164b63c5523ff2c8900db53962cc2af191df59e1c"; + final String PRIVATE_KEY3 = "d06630d6e4c17942155819bbbe0db8306cd989ba7baf3c29985c8455fbefc37f"; + final String PUBLIC_KEY3 = "6bd0a51e0ca6fcc8b13cf25efd0b4814978bcaca7d1cf7dbedf538eb02969acb"; + + final String CHAIN_CODE4 = "19d99506a5ce2dc0080092068d278fe29b85ffb8d9c26f8956bfca876307c79c"; + final String PRIVATE_KEY4 = "a095ef77ee88da28f373246e9ae143f76e5839f680746c3f921e90bf76c81b08"; + final String PUBLIC_KEY4 = "35be6a2a37ff6bbb142e9f4d9b558308f4f75d7c51d5632c6a084257455e1461"; + + Mnemonic mnemonic = Mnemonic.fromString(MNEMONIC_12_WORD_STRING); + + // Chain m/44'/3030'/0'/0'/0' + PrivateKey key1 = mnemonic.toStandardEd25519PrivateKey("", 0); + assertThat(Hex.toHexString(key1.getChainCode().getKey())).isEqualTo(CHAIN_CODE1); + assertThat(key1.toStringRaw()).isEqualTo(PRIVATE_KEY1); + assertThat(key1.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY1); + + // Chain m/44'/3030'/0'/0'/2147483647' + PrivateKey key2 = mnemonic.toStandardEd25519PrivateKey("", 2147483647); + assertThat(Hex.toHexString(key2.getChainCode().getKey())).isEqualTo(CHAIN_CODE2); + assertThat(key2.toStringRaw()).isEqualTo(PRIVATE_KEY2); + assertThat(key2.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY2); + + // Chain m/44'/3030'/0'/0'/0'; Passphrase: "some pass" + PrivateKey key3 = mnemonic.toStandardEd25519PrivateKey("some pass", 0); + assertThat(Hex.toHexString(key3.getChainCode().getKey())).isEqualTo(CHAIN_CODE3); + assertThat(key3.toStringRaw()).isEqualTo(PRIVATE_KEY3); + assertThat(key3.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY3); + + // Chain m/44'/3030'/0'/0'/2147483647'; Passphrase: "some pass" + PrivateKey key4 = mnemonic.toStandardEd25519PrivateKey("some pass", 2147483647); + assertThat(Hex.toHexString(key4.getChainCode().getKey())).isEqualTo(CHAIN_CODE4); + assertThat(key4.toStringRaw()).isEqualTo(PRIVATE_KEY4); + assertThat(key4.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY4); + } + + @Test + @DisplayName("Mnemonic.toStandardED25519PrivateKey() should fail when index is pre-hardened") + void toStandardED25519PrivateKeyShouldFailWhenIndexIsPreHardened() throws BadMnemonicException { + Mnemonic mnemonic = Mnemonic.fromString(MNEMONIC_24_WORD_STRING); + int hardenedIndex = Bip32Utils.toHardenedIndex(10); + + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> mnemonic.toStandardEd25519PrivateKey("", hardenedIndex)) + .satisfies(error -> assertThat(error.getMessage()).isEqualTo("the index should not be pre-hardened")); + } + + @Test + @DisplayName("Mnemonic.toStandardECDSAsecp256k1PrivateKey() test vector") + void toStandardECDSAsecp256k1PrivateKey() throws BadMnemonicException { + // https://github.com/hashgraph/hedera-sdk-reference/issues/73#issuecomment-1422330626 + final String CHAIN_CODE1 = "7717bc71194c257d4b233e16cf48c24adef630052f874a262d19aeb2b527620d"; + final String PRIVATE_KEY1 = "0fde7bfd57ae6ec310bdd8b95967d98e8762a2c02da6f694b152cf9860860ab8"; + final String PUBLIC_KEY1 = "03b1c064b4d04d52e51f6c8e8bb1bff75d62fa7b1446412d5901d424f6aedd6fd4"; + + final String CHAIN_CODE2 = "e333da4bd9e21b5dbd2b0f6d88bad02f0fa24cf4b70b2fb613368d0364cdf8af"; + final String PRIVATE_KEY2 = "aab7d720a32c2d1ea6123f58b074c865bb07f6c621f14cb012f66c08e64996bb"; + final String PUBLIC_KEY2 = "03a0ea31bb3562f8a309b1436bc4b2f537301778e8a5e12b68cec26052f567a235"; + + final String CHAIN_CODE3 = "0ff552587f6baef1f0818136bacac0bb37236473f6ecb5a8c1cc68a716726ed1"; + final String PRIVATE_KEY3 = "6df5ed217cf6d5586fdf9c69d39c843eb9d152ca19d3e41f7bab483e62f6ac25"; + final String PUBLIC_KEY3 = "0357d69bb36fee569838fe7b325c07ca511e8c1b222873cde93fc6bb541eb7ecea"; + + final String CHAIN_CODE4 = "3a5048e93aad88f1c42907163ba4dce914d3aaf2eea87b4dd247ca7da7530f0b"; + final String PRIVATE_KEY4 = "80df01f79ee1b1f4e9ab80491c592c0ef912194ccca1e58346c3d35cb5b7c098"; + final String PUBLIC_KEY4 = "039ebe79f85573baa065af5883d0509a5634245f7864ddead76a008c9e42aa758d"; + + final String CHAIN_CODE5 = "e54254940db58ef4913a377062ac6e411daebf435ad592d262d5a66d808a8b94"; + final String PRIVATE_KEY5 = "60cb2496a623e1201d4e0e7ce5da3833cd4ec7d6c2c06bce2bcbcbc9dfef22d6"; + final String PUBLIC_KEY5 = "02b59f348a6b69bd97afa80115e2d5331749b3c89c61297255430c487d6677f404"; + + final String CHAIN_CODE6 = "cb23165e9d2d798c85effddc901a248a1a273fab2a56fe7976df97b016e7bb77"; + final String PRIVATE_KEY6 = "100477c333028c8849250035be2a0a166a347a5074a8a727bce1db1c65181a50"; + final String PUBLIC_KEY6 = "03d10ebfa2d8ff2cd34aa96e5ef59ca2e69316b4c0996e6d5f54b6932fe51be560"; + + Mnemonic mnemonic = Mnemonic.fromString(MNEMONIC_24_WORD_STRING); + + // Chain m/44'/3030'/0'/0/0 + PrivateKey key1 = mnemonic.toStandardECDSAsecp256k1PrivateKey("", 0); + assertThat(Hex.toHexString(key1.getChainCode().getKey())).isEqualTo(CHAIN_CODE1); + assertThat(key1.toStringRaw()).isEqualTo(PRIVATE_KEY1); + assertThat(key1.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY1); + + // Chain m/44'/3030'/0'/0/0' + PrivateKey key2 = mnemonic.toStandardECDSAsecp256k1PrivateKey("", Bip32Utils.toHardenedIndex(0)); + assertThat(Hex.toHexString(key2.getChainCode().getKey())).isEqualTo(CHAIN_CODE2); + assertThat(key2.toStringRaw()).isEqualTo(PRIVATE_KEY2); + assertThat(key2.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY2); + + // Chain m/44'/3030'/0'/0/0; Passphrase "some pass" + PrivateKey key3 = mnemonic.toStandardECDSAsecp256k1PrivateKey("some pass", 0); + assertThat(Hex.toHexString(key3.getChainCode().getKey())).isEqualTo(CHAIN_CODE3); + assertThat(key3.toStringRaw()).isEqualTo(PRIVATE_KEY3); + assertThat(key3.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY3); + + // Chain m/44'/3030'/0'/0/0'; Passphrase "some pass" + PrivateKey key4 = mnemonic.toStandardECDSAsecp256k1PrivateKey("some pass", Bip32Utils.toHardenedIndex(0)); + assertThat(Hex.toHexString(key4.getChainCode().getKey())).isEqualTo(CHAIN_CODE4); + assertThat(key4.toStringRaw()).isEqualTo(PRIVATE_KEY4); + assertThat(key4.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY4); + + // Chain m/44'/3030'/0'/0/2147483647; Passphrase "some pass" + PrivateKey key5 = mnemonic.toStandardECDSAsecp256k1PrivateKey("some pass", 2147483647); + assertThat(Hex.toHexString(key5.getChainCode().getKey())).isEqualTo(CHAIN_CODE5); + assertThat(key5.toStringRaw()).isEqualTo(PRIVATE_KEY5); + assertThat(key5.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY5); + + // Chain m/44'/3030'/0'/0/2147483647'; Passphrase "some pass" + PrivateKey key6 = + mnemonic.toStandardECDSAsecp256k1PrivateKey("some pass", Bip32Utils.toHardenedIndex(2147483647)); + assertThat(Hex.toHexString(key6.getChainCode().getKey())).isEqualTo(CHAIN_CODE6); + assertThat(key6.toStringRaw()).isEqualTo(PRIVATE_KEY6); + assertThat(key6.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY6); + } + + @Test + @DisplayName("Mnemonic.toStandardECDSAsecp256k1PrivateKey() test vector 2") + void toStandardECDSAsecp256k1PrivateKey2() throws BadMnemonicException { + // https://github.com/hashgraph/hedera-sdk-reference/issues/73#issuecomment-1424761224 + final String CHAIN_CODE1 = "e76e0480faf2790e62dc1a7bac9dce51db1b3571fd74d8e264abc0d240a55d09"; + final String PRIVATE_KEY1 = "f033824c20dd9949ad7a4440f67120ee02a826559ed5884077361d69b2ad51dd"; + final String PUBLIC_KEY1 = "0294bf84a54806989a74ca4b76291d386914610b40b610d303162b9e495bc06416"; + + final String CHAIN_CODE2 = "60c39c6a77bd68c0aaabfe2f4711dc9c2247214c4f4dae15ad4cb76905f5f544"; + final String PRIVATE_KEY2 = "962f549dafe2d9c8091ac918cb4fc348ab0767353f37501067897efbc84e7651"; + final String PUBLIC_KEY2 = "027123855357fd41d28130fbc59053192b771800d28ef47319ef277a1a032af78f"; + + final String CHAIN_CODE3 = "911a1095b64b01f7f3a06198df3d618654e5ed65862b211997c67515e3167892"; + final String PRIVATE_KEY3 = "c139ebb363d7f441ccbdd7f58883809ec0cc3ee7a122ef67974eec8534de65e8"; + final String PUBLIC_KEY3 = "0293bdb1507a26542ed9c1ec42afe959cf8b34f39daab4bf842cdac5fa36d50ef7"; + + final String CHAIN_CODE4 = "64173f2dcb1d65e15e787ef882fa15f54db00209e2dab16fa1661244cd98e95c"; + final String PRIVATE_KEY4 = "87c1d8d4bb0cebb4e230852f2a6d16f6847881294b14eb1d6058b729604afea0"; + final String PUBLIC_KEY4 = "03358e7761a422ca1c577f145fe845c77563f164b2c93b5b34516a8fa13c2c0888"; + + final String CHAIN_CODE5 = "a7250c2b07b368a054f5c91e6a3dbe6ca3bbe01eb0489fe8778304bd0a19c711"; + final String PRIVATE_KEY5 = "2583170ee745191d2bb83474b1de41a1621c47f6e23db3f2bf413a1acb5709e4"; + final String PUBLIC_KEY5 = "03f9eb27cc73f751e8e476dd1db79037a7df2c749fa75b6cc6951031370d2f95a5"; + + final String CHAIN_CODE6 = "66a1175e7690e3714d53ffce16ee6bb4eb02065516be2c2ad6bf6c9df81ec394"; + final String PRIVATE_KEY6 = "f2d008cd7349bdab19ed85b523ba218048f35ca141a3ecbc66377ad50819e961"; + final String PUBLIC_KEY6 = "027b653d04958d4bf83dd913a9379b4f9a1a1e64025a691830a67383bc3157c044"; + + Mnemonic mnemonic = Mnemonic.fromString(MNEMONIC_12_WORD_STRING); + + // Chain m/44'/3030'/0'/0/0 + PrivateKey key1 = mnemonic.toStandardECDSAsecp256k1PrivateKey("", 0); + assertThat(Hex.toHexString(key1.getChainCode().getKey())).isEqualTo(CHAIN_CODE1); + assertThat(key1.toStringRaw()).isEqualTo(PRIVATE_KEY1); + assertThat(key1.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY1); + + // Chain m/44'/3030'/0'/0/0' + PrivateKey key2 = mnemonic.toStandardECDSAsecp256k1PrivateKey("", Bip32Utils.toHardenedIndex(0)); + assertThat(Hex.toHexString(key2.getChainCode().getKey())).isEqualTo(CHAIN_CODE2); + assertThat(key2.toStringRaw()).isEqualTo(PRIVATE_KEY2); + assertThat(key2.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY2); + + // Chain m/44'/3030'/0'/0/0; Passphrase "some pass" + PrivateKey key3 = mnemonic.toStandardECDSAsecp256k1PrivateKey("some pass", 0); + assertThat(Hex.toHexString(key3.getChainCode().getKey())).isEqualTo(CHAIN_CODE3); + assertThat(key3.toStringRaw()).isEqualTo(PRIVATE_KEY3); + assertThat(key3.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY3); + + // Chain m/44'/3030'/0'/0/0'; Passphrase "some pass" + PrivateKey key4 = mnemonic.toStandardECDSAsecp256k1PrivateKey("some pass", Bip32Utils.toHardenedIndex(0)); + assertThat(Hex.toHexString(key4.getChainCode().getKey())).isEqualTo(CHAIN_CODE4); + assertThat(key4.toStringRaw()).isEqualTo(PRIVATE_KEY4); + assertThat(key4.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY4); + + // Chain m/44'/3030'/0'/0/2147483647; Passphrase "some pass" + PrivateKey key5 = mnemonic.toStandardECDSAsecp256k1PrivateKey("some pass", 2147483647); + assertThat(Hex.toHexString(key5.getChainCode().getKey())).isEqualTo(CHAIN_CODE5); + assertThat(key5.toStringRaw()).isEqualTo(PRIVATE_KEY5); + assertThat(key5.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY5); + + // Chain m/44'/3030'/0'/0/2147483647'; Passphrase "some pass" + PrivateKey key6 = + mnemonic.toStandardECDSAsecp256k1PrivateKey("some pass", Bip32Utils.toHardenedIndex(2147483647)); + assertThat(Hex.toHexString(key6.getChainCode().getKey())).isEqualTo(CHAIN_CODE6); + assertThat(key6.toStringRaw()).isEqualTo(PRIVATE_KEY6); + assertThat(key6.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY6); + } + + @Test + @DisplayName( + "Mnemonic.toStandardECDSAsecp256k1PrivateKeyCustomDerivationPath() with custom derivation path invalid inputs vector") + void toStandardECDSAsecp256k1PrivateKeyCustomDpathInvalidInputs() throws BadMnemonicException { + final String DPATH_1 = "XYZ/44'/60'/0'/0/0"; // invalid derivation path + final String PASSPHRASE_1 = ""; + final String CHAIN_CODE_1 = "58a9ee31eaf7499abc01952b44dbf0a2a5d6447512367f09d99381c9605bf9e8"; + final String PRIVATE_KEY_1 = "78f9545e40025cf7da9126a4d6a861ae34031d1c74c3404df06110c9fde371ad"; + final String PUBLIC_KEY_1 = "02a8f4c22eea66617d4f119e3a951b93f584949bbfee90bd555305402da6c4e569"; + + final String DPATH_2 = ""; // null or empty derivation path + final String PASSPHRASE_2 = ""; + final String CHAIN_CODE_2 = "6dcfc7a4914bd0e75b94a2f38afee8c247b34810202a2c64fe599ee1b88afdc9"; + final String PRIVATE_KEY_2 = "77ca263661ebdd5a8b33c224aeff5e7bf67eedacee68a1699d97ee8929d7b130"; + final String PUBLIC_KEY_2 = "03e84c9be9be53ad722038cc1943e79df27e5c1d31088adb4f0e62444f4dece683"; + + final String DPATH_3 = "m/44'/60'/0'/0/6-7-8-9-0"; // invalid numeric value in derivation path + final String PASSPHRASE_3 = ""; + final String CHAIN_CODE_3 = "c8c798d2b3696be1e7a29d1cea205507eedc2057006b9ef1cde1b4e346089e17"; + final String PRIVATE_KEY_3 = "31c24292eac951279b659c335e44a2e812d0f1a228b1d4d87034874d376e605a"; + final String PUBLIC_KEY_3 = "0207ff3faf4055c1aa7a5ad94d6ff561fac35b9ae695ef486706243667d2b4d10e"; + + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> { + Mnemonic mnemonic = Mnemonic.fromString(MNEMONIC_24_WORD_STRING); + mnemonic.toStandardECDSAsecp256k1PrivateKeyCustomDerivationPath(PASSPHRASE_1, DPATH_1); + }) + .satisfies((iae) -> { + assertThat(iae.getMessage()).isEqualTo("Invalid derivation path format"); + }); + + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> { + Mnemonic mnemonic = Mnemonic.fromString(MNEMONIC_24_WORD_STRING); + mnemonic.toStandardECDSAsecp256k1PrivateKeyCustomDerivationPath(PASSPHRASE_2, DPATH_2); + }) + .satisfies((iae) -> { + assertThat(iae.getMessage()).isEqualTo("Derivation path cannot be null or empty"); + }); + + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> { + Mnemonic mnemonic = Mnemonic.fromString(MNEMONIC_24_WORD_STRING); + mnemonic.toStandardECDSAsecp256k1PrivateKeyCustomDerivationPath(PASSPHRASE_3, DPATH_3); + }) + .satisfies((iae) -> { + assertThat(iae.getMessage()).isEqualTo("Invalid derivation path format"); + }); + } + + @Test + @DisplayName( + "Mnemonic.toStandardECDSAsecp256k1PrivateKeyCustomDerivationPath() with custom derivation path test vector") + void toStandardECDSAsecp256k1PrivateKeyCustomDpath() throws BadMnemonicException { + final String DPATH_1 = "m/44'/60'/0'/0/0"; + final String PASSPHRASE_1 = ""; + final String CHAIN_CODE_1 = "58a9ee31eaf7499abc01952b44dbf0a2a5d6447512367f09d99381c9605bf9e8"; + final String PRIVATE_KEY_1 = "78f9545e40025cf7da9126a4d6a861ae34031d1c74c3404df06110c9fde371ad"; + final String PUBLIC_KEY_1 = "02a8f4c22eea66617d4f119e3a951b93f584949bbfee90bd555305402da6c4e569"; + + final String DPATH_2 = "m/44'/60'/0'/0/1"; + final String PASSPHRASE_2 = ""; + final String CHAIN_CODE_2 = "6dcfc7a4914bd0e75b94a2f38afee8c247b34810202a2c64fe599ee1b88afdc9"; + final String PRIVATE_KEY_2 = "77ca263661ebdd5a8b33c224aeff5e7bf67eedacee68a1699d97ee8929d7b130"; + final String PUBLIC_KEY_2 = "03e84c9be9be53ad722038cc1943e79df27e5c1d31088adb4f0e62444f4dece683"; + + final String DPATH_3 = "m/44'/60'/0'/0/2"; + final String PASSPHRASE_3 = ""; + final String CHAIN_CODE_3 = "c8c798d2b3696be1e7a29d1cea205507eedc2057006b9ef1cde1b4e346089e17"; + final String PRIVATE_KEY_3 = "31c24292eac951279b659c335e44a2e812d0f1a228b1d4d87034874d376e605a"; + final String PUBLIC_KEY_3 = "0207ff3faf4055c1aa7a5ad94d6ff561fac35b9ae695ef486706243667d2b4d10e"; + + Mnemonic mnemonic = Mnemonic.fromString(MNEMONIC_24_WORD_STRING); + + // m/44'/60'/0'/0/0 + PrivateKey key1 = mnemonic.toStandardECDSAsecp256k1PrivateKeyCustomDerivationPath(PASSPHRASE_1, DPATH_1); + assertThat(Hex.toHexString(key1.getChainCode().getKey())).isEqualTo(CHAIN_CODE_1); + assertThat(key1.toStringRaw()).isEqualTo(PRIVATE_KEY_1); + assertThat(key1.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY_1); + + // m/44'/60'/0'/0/1 + PrivateKey key2 = mnemonic.toStandardECDSAsecp256k1PrivateKeyCustomDerivationPath(PASSPHRASE_2, DPATH_2); + assertThat(Hex.toHexString(key2.getChainCode().getKey())).isEqualTo(CHAIN_CODE_2); + assertThat(key2.toStringRaw()).isEqualTo(PRIVATE_KEY_2); + assertThat(key2.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY_2); + + // m/44'/60'/0'/0/2 + PrivateKey key3 = mnemonic.toStandardECDSAsecp256k1PrivateKeyCustomDerivationPath(PASSPHRASE_3, DPATH_3); + assertThat(Hex.toHexString(key3.getChainCode().getKey())).isEqualTo(CHAIN_CODE_3); + assertThat(key3.toStringRaw()).isEqualTo(PRIVATE_KEY_3); + assertThat(key3.getPublicKey().toStringRaw()).isSubstringOf(PUBLIC_KEY_3); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/Mocker.java b/sdk/src/test/java/org/hiero/sdk/Mocker.java new file mode 100644 index 0000000000..3a7310fe4b --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/Mocker.java @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.grpc.MethodDescriptor; +import io.grpc.Server; +import io.grpc.ServerMethodDefinition; +import io.grpc.ServerServiceDefinition; +import io.grpc.ServiceDescriptor; +import io.grpc.Status; +import io.grpc.inprocess.InProcessServerBuilder; +import io.grpc.stub.ServerCalls; +import java.io.IOException; +import java.time.Duration; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; +import org.hiero.sdk.logger.LogLevel; +import org.hiero.sdk.logger.Logger; +import org.hiero.sdk.proto.ConsensusServiceGrpc; +import org.hiero.sdk.proto.CryptoServiceGrpc; +import org.hiero.sdk.proto.FileServiceGrpc; +import org.hiero.sdk.proto.SmartContractServiceGrpc; +import org.hiero.sdk.proto.TokenServiceGrpc; + +public class Mocker implements AutoCloseable { + private static final PrivateKey PRIVATE_KEY = PrivateKey.fromString( + "302e020100300506032b657004220420d45e1557156908c967804615af59a000be88c7aa7058bfcbe0f46b16c28f887d"); + public final Client client; + private final List services = List.of( + CryptoServiceGrpc.getServiceDescriptor(), + FileServiceGrpc.getServiceDescriptor(), + SmartContractServiceGrpc.getServiceDescriptor(), + ConsensusServiceGrpc.getServiceDescriptor(), + TokenServiceGrpc.getServiceDescriptor()); + private final List> responses; + private final List servers = new ArrayList<>(); + + Mocker(List> responses) { + this.responses = responses; + + var network = new HashMap(responses.size()); + + for (var i = 0; i < responses.size(); i++) { + var index = new AtomicInteger(); + var response = responses.get(i); + var name = InProcessServerBuilder.generateName(); + var nodeAccountId = new AccountId(3 + i); + var builder = InProcessServerBuilder.forName(name); + + network.put("in-process:" + name, nodeAccountId); + + for (var service : services) { + var descriptor = ServerServiceDefinition.builder(service); + + for (MethodDescriptor method : service.getMethods()) { + var methodDefinition = ServerMethodDefinition.create( + (MethodDescriptor) method, + ServerCalls.asyncUnaryCall((request, responseObserver) -> { + var responseIndex = index.getAndIncrement(); + + if (responseIndex >= response.size()) { + responseObserver.onError( + Status.Code.ABORTED.toStatus().asRuntimeException()); + return; + } + + var r = response.get(responseIndex); + + if (r instanceof Function) { + try { + r = ((Function) r).apply(request); + } catch (Throwable e) { + r = Status.ABORTED + .withDescription(e.getMessage()) + .asRuntimeException(); + } + } + + if (r instanceof Throwable) { + responseObserver.onError((Throwable) r); + } else { + responseObserver.onNext(r); + responseObserver.onCompleted(); + } + })); + descriptor.addMethod(methodDefinition); + } + + builder.addService(descriptor.build()); + } + + try { + this.servers.add(builder.directExecutor().build().start()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + this.client = Client.forNetwork(network) + .setOperator(new AccountId(1800), PRIVATE_KEY) + .setMinBackoff(Duration.ofMillis(0)) + .setMaxBackoff(Duration.ofMillis(0)) + .setNodeMinBackoff(Duration.ofMillis(0)) + .setNodeMaxBackoff(Duration.ofMillis(0)) + .setMinNodeReadmitTime(Duration.ofMillis(0)) + .setMaxNodeReadmitTime(Duration.ofMillis(0)) + .setLogger(new Logger(LogLevel.SILENT)); + } + + public static Mocker withResponses(List> responses) { + return new Mocker(responses); + } + + public void close() throws TimeoutException, InterruptedException { + client.close(); + + for (var server : servers) { + server.shutdown(); + server.awaitTermination(); + } + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/MockingTest.java b/sdk/src/test/java/org/hiero/sdk/MockingTest.java new file mode 100644 index 0000000000..20868f4bea --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/MockingTest.java @@ -0,0 +1,763 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import io.grpc.Status; +import io.grpc.stub.StreamObserver; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.TimeoutException; +import java.util.function.Supplier; +import org.hiero.sdk.proto.AccountID; +import org.hiero.sdk.proto.CryptoGetAccountBalanceResponse; +import org.hiero.sdk.proto.CryptoGetInfoResponse; +import org.hiero.sdk.proto.CryptoServiceGrpc; +import org.hiero.sdk.proto.FileServiceGrpc; +import org.hiero.sdk.proto.Query; +import org.hiero.sdk.proto.Response; +import org.hiero.sdk.proto.ResponseCodeEnum; +import org.hiero.sdk.proto.ResponseHeader; +import org.hiero.sdk.proto.SignedTransaction; +import org.hiero.sdk.proto.SmartContractServiceGrpc; +import org.hiero.sdk.proto.Transaction; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionGetReceiptResponse; +import org.hiero.sdk.proto.TransactionGetRecordResponse; +import org.hiero.sdk.proto.TransactionReceipt; +import org.hiero.sdk.proto.TransactionRecord; +import org.hiero.sdk.proto.TransactionResponse; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class MockingTest { + @Test + void testSucceedsWithCorrectHbars() throws PrecheckStatusException, TimeoutException, InterruptedException { + List responses1 = List.of( + Status.Code.UNAVAILABLE.toStatus().asRuntimeException(), + (Function) + o -> Status.Code.UNAVAILABLE.toStatus().asRuntimeException(), + Response.newBuilder() + .setCryptogetAccountBalance(CryptoGetAccountBalanceResponse.newBuilder() + .setHeader(ResponseHeader.newBuilder() + .setNodeTransactionPrecheckCode(ResponseCodeEnum.OK) + .build()) + .setAccountID( + AccountID.newBuilder().setAccountNum(10).build()) + .setBalance(100) + .build()) + .build()); + + var responses = List.of(responses1); + + try (var mocker = Mocker.withResponses(responses)) { + var balance = + new AccountBalanceQuery().setAccountId(new AccountId(10)).execute(mocker.client); + + Assertions.assertEquals(balance.hbars, Hbar.fromTinybars(100)); + } + } + + String makeBigString(int size) { + char[] chars = new char[size]; + Arrays.fill(chars, 'A'); + return new String(chars); + } + + @ParameterizedTest(name = "[{0}] Executable retries on gRPC error with PLATFORM_NOT_ACTIVE when getting receipt") + @CsvSource({"sync", "async"}) + void shouldRetryExceptionallyFunctionsCorrectlyForPlatformNotActiveGetReceipt(String sync) throws Exception { + var service = new TestCryptoService(); + var server = new TestServer("getReceiptRetry" + sync, service); + server.client.setMaxAttempts(3); + + service.buffer.enqueueResponse(TestResponse.transaction(org.hiero.sdk.Status.PLATFORM_NOT_ACTIVE)); + service.buffer.enqueueResponse(TestResponse.transactionOk()); + + org.hiero.sdk.TransactionResponse transactionResponse; + if (sync.equals("sync")) { + transactionResponse = new AccountCreateTransaction().execute(server.client); + } else { + transactionResponse = + new AccountCreateTransaction().executeAsync(server.client).get(); + } + + service.buffer + .enqueueResponse(TestResponse.query(Response.newBuilder() + .setTransactionGetReceipt(TransactionGetReceiptResponse.newBuilder() + .setHeader(ResponseHeader.newBuilder() + .setNodeTransactionPrecheckCode(ResponseCodeEnum.PLATFORM_NOT_ACTIVE)) + .setReceipt(TransactionReceipt.newBuilder() + .setStatus(ResponseCodeEnum.SUCCESS) + .build()) + .build()) + .build())) + .enqueueResponse(TestResponse.receipt(org.hiero.sdk.Status.PLATFORM_NOT_ACTIVE)) + .enqueueResponse(TestResponse.successfulReceipt()); + + if (sync.equals("sync")) { + transactionResponse.getReceipt(server.client); + } else { + transactionResponse.getReceiptAsync(server.client).get(); + } + } + + @ParameterizedTest(name = "[{0}] Executable retries on gRPC error with PLATFORM_NOT_ACTIVE when getting record") + @CsvSource({"sync", "async"}) + void shouldRetryExceptionallyFunctionsCorrectlyForPlatformNotActiveGetRecord(String sync) throws Exception { + var service = new TestCryptoService(); + var server = new TestServer("getRecordRetry" + sync, service); + server.client.setMaxAttempts(3); + + service.buffer.enqueueResponse(TestResponse.transaction(org.hiero.sdk.Status.PLATFORM_NOT_ACTIVE)); + service.buffer.enqueueResponse(TestResponse.transactionOk()); + + org.hiero.sdk.TransactionResponse transactionResponse; + if (sync.equals("sync")) { + transactionResponse = new AccountCreateTransaction().execute(server.client); + } else { + transactionResponse = + new AccountCreateTransaction().executeAsync(server.client).get(); + } + + service.buffer + .enqueueResponse(TestResponse.successfulReceipt()) // for inner getReceipt + .enqueueResponse(TestResponse.successfulReceipt()) // for inner getCost + .enqueueResponse(TestResponse.query(Response.newBuilder() + .setTransactionGetRecord(TransactionGetRecordResponse.newBuilder() + .setHeader(ResponseHeader.newBuilder() + .setNodeTransactionPrecheckCode(ResponseCodeEnum.PLATFORM_NOT_ACTIVE) + .build()) + .setTransactionRecord(TransactionRecord.newBuilder() + .setReceipt(TransactionReceipt.newBuilder() + .setStatus(ResponseCodeEnum.SUCCESS) + .build()) + .build()) + .build()) + .build())) + .enqueueResponse(TestResponse.query(Response.newBuilder() + .setTransactionGetRecord(TransactionGetRecordResponse.newBuilder() + .setTransactionRecord(TransactionRecord.newBuilder() + .setReceipt(TransactionReceipt.newBuilder() + .setStatus(ResponseCodeEnum.PLATFORM_NOT_ACTIVE) + .build()) + .build()) + .build()) + .build())) + .enqueueResponse(TestResponse.query(Response.newBuilder() + .setTransactionGetRecord(TransactionGetRecordResponse.newBuilder() + .setTransactionRecord(TransactionRecord.newBuilder() + .setReceipt(TransactionReceipt.newBuilder() + .setStatus(ResponseCodeEnum.SUCCESS) + .build()) + .build()) + .build()) + .build())); + + if (sync.equals("sync")) { + transactionResponse.getRecord(server.client); + } else { + transactionResponse.getRecordAsync(server.client).get(); + } + } + + @ParameterizedTest(name = "[{0}, {1}] ContractCreateFlow functions") + @CsvSource({ + "sync, stakedNode", + "sync, stakedAccount", + "async, stakedNode", + "async, stakedAccount", + }) + void contractCreateFlowFunctions(String versionToTest, String stakeType) throws Throwable { + var BIG_BYTECODE = makeBigString(ContractCreateFlow.FILE_CREATE_MAX_BYTES + 1000); + var adminKey = PrivateKey.generateED25519().getPublicKey(); + + var cryptoService = new TestCryptoService(); + var fileService = new TestFileService(); + var contractService = new TestContractService(); + var server = new TestServer( + "contractCreateFlow" + versionToTest + stakeType, cryptoService, fileService, contractService); + + var fileId = FileId.fromString("1.2.3"); + var maxAutomaticTokenAssociations = 101; + var stakedAccountId = AccountId.fromString("4.3.2"); + var stakedNode = 13L; + var declineStakingReward = true; + + cryptoService + .buffer + .enqueueResponse(TestResponse.query(Response.newBuilder() + .setTransactionGetReceipt(TransactionGetReceiptResponse.newBuilder() + .setReceipt(TransactionReceipt.newBuilder() + .setFileID(fileId.toProtobuf()) + .setStatus(ResponseCodeEnum.SUCCESS) + .build()) + .build()) + .build())) + .enqueueResponse(TestResponse.successfulReceipt()) + .enqueueResponse(TestResponse.successfulReceipt()); + fileService + .buffer + .enqueueResponse(TestResponse.transactionOk()) + .enqueueResponse(TestResponse.transactionOk()) + .enqueueResponse(TestResponse.transactionOk()); + + contractService.buffer.enqueueResponse(TestResponse.transactionOk()); + + var flow = new ContractCreateFlow() + .setBytecode(BIG_BYTECODE) + .setContractMemo("memo goes here") + .setConstructorParameters(new byte[] {1, 2, 3}) + .setAutoRenewPeriod(Duration.ofMinutes(1)) + .setAdminKey(adminKey) + .setGas(100) + .setInitialBalance(new Hbar(3)) + .setMaxAutomaticTokenAssociations(maxAutomaticTokenAssociations) + .setDeclineStakingReward(declineStakingReward); + + if (stakeType.equals("stakedAccount")) { + flow.setStakedAccountId(stakedAccountId); + } else { + flow.setStakedNodeId(stakedNode); + } + + if (versionToTest.equals("sync")) { + flow.execute(server.client); + } else { + flow.executeAsync(server.client).get(); + } + + Thread.sleep(1000); + + Assertions.assertEquals(3, cryptoService.buffer.queryRequestsReceived.size()); + Assertions.assertEquals(3, fileService.buffer.transactionRequestsReceived.size()); + Assertions.assertEquals(1, contractService.buffer.transactionRequestsReceived.size()); + var transactions = new ArrayList>(); + for (var request : fileService.buffer.transactionRequestsReceived) { + transactions.add(org.hiero.sdk.Transaction.fromBytes(request.toByteArray())); + } + transactions.add(org.hiero.sdk.Transaction.fromBytes( + contractService.buffer.transactionRequestsReceived.get(0).toByteArray())); + + Assertions.assertInstanceOf(FileCreateTransaction.class, transactions.get(0)); + Assertions.assertEquals( + ContractCreateFlow.FILE_CREATE_MAX_BYTES, + ((FileCreateTransaction) transactions.get(0)).getContents().size()); + + Assertions.assertTrue(cryptoService.buffer.queryRequestsReceived.get(0).hasTransactionGetReceipt()); + + Assertions.assertInstanceOf(FileAppendTransaction.class, transactions.get(1)); + var fileAppendTx = (FileAppendTransaction) transactions.get(1); + Assertions.assertEquals(fileId, fileAppendTx.getFileId()); + Assertions.assertEquals( + BIG_BYTECODE.length() - ContractCreateFlow.FILE_CREATE_MAX_BYTES, + fileAppendTx.getContents().size()); + + Assertions.assertInstanceOf(ContractCreateTransaction.class, transactions.get(3)); + var contractCreateTx = (ContractCreateTransaction) transactions.get(3); + Assertions.assertEquals("memo goes here", contractCreateTx.getContractMemo()); + Assertions.assertEquals(fileId, contractCreateTx.getBytecodeFileId()); + Assertions.assertEquals(ByteString.copyFrom(new byte[] {1, 2, 3}), contractCreateTx.getConstructorParameters()); + Assertions.assertEquals(Duration.ofMinutes(1), contractCreateTx.getAutoRenewPeriod()); + Assertions.assertEquals(adminKey, contractCreateTx.getAdminKey()); + Assertions.assertEquals(100, contractCreateTx.getGas()); + Assertions.assertEquals(new Hbar(3), contractCreateTx.getInitialBalance()); + Assertions.assertEquals(maxAutomaticTokenAssociations, contractCreateTx.getMaxAutomaticTokenAssociations()); + Assertions.assertEquals(declineStakingReward, contractCreateTx.getDeclineStakingReward()); + + if (stakeType.equals("stakedAccount")) { + Assertions.assertEquals(stakedAccountId, contractCreateTx.getStakedAccountId()); + } else { + Assertions.assertEquals(stakedNode, contractCreateTx.getStakedNodeId()); + } + + Assertions.assertInstanceOf(FileDeleteTransaction.class, transactions.get(2)); + + server.close(); + } + + @Test + void accountInfoFlowFunctions() throws Throwable { + var BIG_BYTES = makeBigString(1000).getBytes(StandardCharsets.UTF_8); + var privateKey = PrivateKey.generateED25519(); + var otherPrivateKey = PrivateKey.generateED25519(); + var accountId = AccountId.fromString("1.2.3"); + var cost = Hbar.from(1); + + Supplier makeTx = () -> new TokenMintTransaction() + .setTokenId(TokenId.fromString("1.2.3")) + .setAmount(5) + .setTransactionId(TransactionId.generate(accountId)) + .setNodeAccountIds(List.of(AccountId.fromString("0.0.3"))) + .freeze(); + + var properlySignedTx = makeTx.get().sign(privateKey); + var improperlySignedTx = makeTx.get().sign(otherPrivateKey); + var properBigBytesSignature = privateKey.sign(BIG_BYTES); + var improperBigBytesSignature = otherPrivateKey.sign(BIG_BYTES); + + var cryptoService = new TestCryptoService(); + var server = new TestServer("accountInfoFlow", cryptoService); + + for (int i = 0; i < 8; i++) { + cryptoService.buffer.enqueueResponse(TestResponse.query(Response.newBuilder() + .setCryptoGetInfo(CryptoGetInfoResponse.newBuilder() + .setHeader(ResponseHeader.newBuilder() + .setCost(cost.toTinybars()) + .build()) + .build()) + .build())); + cryptoService.buffer.enqueueResponse(TestResponse.query(Response.newBuilder() + .setCryptoGetInfo(CryptoGetInfoResponse.newBuilder() + .setAccountInfo(CryptoGetInfoResponse.AccountInfo.newBuilder() + .setKey(privateKey.getPublicKey().toProtobufKey()) + .build()) + .build()) + .build())); + } + + Assertions.assertTrue(AccountInfoFlow.verifyTransactionSignature(server.client, accountId, properlySignedTx)); + Assertions.assertFalse( + AccountInfoFlow.verifyTransactionSignature(server.client, accountId, improperlySignedTx)); + Assertions.assertTrue( + AccountInfoFlow.verifySignature(server.client, accountId, BIG_BYTES, properBigBytesSignature)); + Assertions.assertFalse( + AccountInfoFlow.verifySignature(server.client, accountId, BIG_BYTES, improperBigBytesSignature)); + Assertions.assertTrue( + AccountInfoFlow.verifyTransactionSignatureAsync(server.client, accountId, properlySignedTx) + .get()); + Assertions.assertFalse( + AccountInfoFlow.verifyTransactionSignatureAsync(server.client, accountId, improperlySignedTx) + .get()); + Assertions.assertTrue( + AccountInfoFlow.verifySignatureAsync(server.client, accountId, BIG_BYTES, properBigBytesSignature) + .get()); + Assertions.assertFalse( + AccountInfoFlow.verifySignatureAsync(server.client, accountId, BIG_BYTES, improperBigBytesSignature) + .get()); + + Assertions.assertEquals(16, cryptoService.buffer.queryRequestsReceived.size()); + for (int i = 0; i < 16; i += 2) { + var costQueryRequest = cryptoService.buffer.queryRequestsReceived.get(i); + var queryRequest = cryptoService.buffer.queryRequestsReceived.get(i + 1); + + Assertions.assertTrue(costQueryRequest.hasCryptoGetInfo()); + Assertions.assertTrue(costQueryRequest.getCryptoGetInfo().hasHeader()); + Assertions.assertTrue( + costQueryRequest.getCryptoGetInfo().getHeader().hasPayment()); + + Assertions.assertTrue(queryRequest.hasCryptoGetInfo()); + Assertions.assertTrue(queryRequest.getCryptoGetInfo().hasAccountID()); + Assertions.assertEquals( + accountId, + AccountId.fromProtobuf(queryRequest.getCryptoGetInfo().getAccountID())); + } + server.close(); + } + + @Test + void exitOnAborted() throws PrecheckStatusException, TimeoutException, InterruptedException { + List responses1 = List.of(); + + var responses = List.of(responses1); + + try (var mocker = Mocker.withResponses(responses)) { + Assertions.assertThrows(RuntimeException.class, () -> new AccountBalanceQuery() + .setAccountId(new AccountId(10)) + .execute(mocker.client)); + } + } + + @ParameterizedTest(name = "[{2}] Executable retries on gRPC error with status {0} and description {1}") + @CsvSource({ + "INTERNAL, internal RST_STREAM error, sync", + "INTERNAL, rst stream, sync", + "RESOURCE_EXHAUSTED, , sync", + "UNAVAILABLE, , sync", + "INTERNAL, internal RST_STREAM error, async", + "INTERNAL, rst stream, async", + "RESOURCE_EXHAUSTED, , async", + "UNAVAILABLE, , async" + }) + void shouldRetryExceptionallyFunctionsCorrectly(Status.Code code, String description, String sync) + throws Exception { + var service = new TestCryptoService(); + var server = new TestServer( + "executableRetry" + code + (description != null ? description.replace(" ", "") : "NULL") + sync, + service); + + var exception = Status.fromCode(code).withDescription(description).asRuntimeException(); + + service.buffer.enqueueResponse(TestResponse.error(exception)).enqueueResponse(TestResponse.transactionOk()); + + if (sync.equals("sync")) { + new AccountCreateTransaction() + .setNodeAccountIds(List.of(AccountId.fromString("1.1.1"), AccountId.fromString("2.2.2"))) + .execute(server.client); + } else { + new AccountCreateTransaction() + .setNodeAccountIds(List.of(AccountId.fromString("1.1.1"), AccountId.fromString("2.2.2"))) + .executeAsync(server.client) + .get(); + } + + Assertions.assertEquals(2, service.buffer.transactionRequestsReceived.size()); + assertFirstTwoRequestsNotDirectedAtSameNode(service); + + server.close(); + } + + @ParameterizedTest(name = "[{2}] Executable should make max {1} attempts when there are {0} errors, and error") + @CsvSource({"2, 2, sync", "2, 2, async"}) + void hitsTxMaxAttemptsCorrectly(Integer numberOfErrors, Integer maxAttempts, String sync) throws Exception { + var service = new TestCryptoService(); + var server = new TestServer("executableMaxAttemptsSync" + numberOfErrors + maxAttempts + sync, service); + + var exception = Status.UNAVAILABLE.asRuntimeException(); + + for (var i = 0; i < numberOfErrors; i++) { + service.buffer.enqueueResponse(TestResponse.error(exception)); + } + + service.buffer.enqueueResponse(TestResponse.transactionOk()); + + if (sync.equals("sync")) { + Assertions.assertThrows(MaxAttemptsExceededException.class, () -> { + new AccountCreateTransaction() + .setMaxAttempts(maxAttempts) + .setNodeAccountIds(List.of(AccountId.fromString("1.1.1"), AccountId.fromString("2.2.2"))) + .execute(server.client); + }); + } else { + new AccountCreateTransaction() + .setMaxAttempts(maxAttempts) + .setNodeAccountIds(List.of(AccountId.fromString("1.1.1"), AccountId.fromString("2.2.2"))) + .executeAsync(server.client) + .handle((response, error) -> { + Assertions.assertNotNull(error); + System.out.println(error); + Assertions.assertTrue(error.getCause() instanceof MaxAttemptsExceededException); + + return null; + }) + .get(); + } + + Assertions.assertEquals(2, service.buffer.transactionRequestsReceived.size()); + + server.close(); + } + + @ParameterizedTest(name = "[{2}] Executable retries on {1} Hedera status error(s) {0}") + @CsvSource({ + "BUSY, 1, sync", + "PLATFORM_TRANSACTION_NOT_CREATED, 1, sync", + "TRANSACTION_EXPIRED, 1, sync", + "BUSY, 3, sync", + "PLATFORM_TRANSACTION_NOT_CREATED, 3, sync", + "TRANSACTION_EXPIRED, 3, sync", + "BUSY, 1, async", + "PLATFORM_TRANSACTION_NOT_CREATED, 1, async", + "TRANSACTION_EXPIRED, 1, async", + "BUSY, 3, async", + "PLATFORM_TRANSACTION_NOT_CREATED, 3, async", + "TRANSACTION_EXPIRED, 3, async" + }) + void shouldRetryFunctionsCorrectly(org.hiero.sdk.Status status, int numberOfErrors, String sync) throws Exception { + var service = new TestCryptoService(); + var server = new TestServer("shouldRetryFunctionsCorrectly" + status + numberOfErrors + sync, service); + + for (var i = 0; i < numberOfErrors; i++) { + service.buffer.enqueueResponse(TestResponse.transaction(status)); + } + + service.buffer.enqueueResponse(TestResponse.transactionOk()); + + server.client.setMaxAttempts(4); + + if (sync.equals("sync")) { + new AccountCreateTransaction() + .setNodeAccountIds(List.of(AccountId.fromString("1.1.1"), AccountId.fromString("2.2.2"))) + .execute(server.client); + } else { + new AccountCreateTransaction() + .setNodeAccountIds(List.of(AccountId.fromString("1.1.1"), AccountId.fromString("2.2.2"))) + .executeAsync(server.client) + .get(); + } + + Assertions.assertEquals(numberOfErrors + 1, service.buffer.transactionRequestsReceived.size()); + assertFirstTwoRequestsNotDirectedAtSameNode(service); + + server.close(); + } + + @ParameterizedTest(name = "[{2}] Executable retries on {1} Hedera status error(s) {0}") + @CsvSource({ + "BUSY, sync", + "PLATFORM_TRANSACTION_NOT_CREATED, sync", + "PLATFORM_NOT_ACTIVE, sync", + "BUSY, async", + "PLATFORM_TRANSACTION_NOT_CREATED, async", + "PLATFORM_NOT_ACTIVE, async", + }) + void hitsClientMaxAttemptsCorrectly(org.hiero.sdk.Status status, String sync) throws Exception { + var service = new TestCryptoService(); + var server = new TestServer("shouldRetryFunctionsCorrectly" + status + sync, service); + + for (var i = 0; i < 2; i++) { + service.buffer.enqueueResponse(TestResponse.transaction(status)); + } + + server.client.setMaxAttempts(2); + + if (sync.equals("sync")) { + Assertions.assertThrows(MaxAttemptsExceededException.class, () -> { + new AccountCreateTransaction() + .setNodeAccountIds(List.of(AccountId.fromString("1.1.1"), AccountId.fromString("2.2.2"))) + .execute(server.client); + }); + } else { + new AccountCreateTransaction() + .setNodeAccountIds(List.of(AccountId.fromString("1.1.1"), AccountId.fromString("2.2.2"))) + .executeAsync(server.client) + .handle((response, error) -> { + Assertions.assertNotNull(error); + Assertions.assertTrue(error.getCause() instanceof MaxAttemptsExceededException); + + return null; + }) + .get(); + } + Assertions.assertEquals(2, service.buffer.transactionRequestsReceived.size()); + assertFirstTwoRequestsNotDirectedAtSameNode(service); + + server.close(); + } + + private static void assertFirstTwoRequestsNotDirectedAtSameNode(TestCryptoService service) + throws InvalidProtocolBufferException { + var requests = service.buffer.transactionRequestsReceived; + var signedTx0 = SignedTransaction.parseFrom(requests.get(0).getSignedTransactionBytes()); + var signedTx1 = SignedTransaction.parseFrom(requests.get(1).getSignedTransactionBytes()); + var txBody0 = TransactionBody.parseFrom(signedTx0.getBodyBytes()); + var txBody1 = TransactionBody.parseFrom(signedTx1.getBodyBytes()); + Assertions.assertNotEquals(txBody0.getNodeAccountID(), txBody1.getNodeAccountID()); + } + + @Test + @DisplayName("Client.setDefaultMaxTransactionFee() functions correctly") + void defaultMaxTransactionFeeTest() throws Exception { + var service = new TestCryptoService(); + var server = new TestServer("maxTransactionFee", service); + + service.buffer + .enqueueResponse(TestResponse.transactionOk()) + .enqueueResponse(TestResponse.transactionOk()) + .enqueueResponse(TestResponse.transactionOk()) + .enqueueResponse(TestResponse.transactionOk()); + + new AccountDeleteTransaction().execute(server.client); + + new AccountDeleteTransaction().setMaxTransactionFee(new Hbar(5)).execute(server.client); + + server.client.setDefaultMaxTransactionFee(new Hbar(1)); + + new AccountDeleteTransaction().execute(server.client); + + new AccountDeleteTransaction().setMaxTransactionFee(new Hbar(3)).execute(server.client); + + Assertions.assertEquals(4, service.buffer.transactionRequestsReceived.size()); + var transactions = new ArrayList>(); + for (var request : service.buffer.transactionRequestsReceived) { + transactions.add(org.hiero.sdk.Transaction.fromBytes(request.toByteArray())); + } + Assertions.assertEquals(new Hbar(2), transactions.get(0).getMaxTransactionFee()); + Assertions.assertEquals(new Hbar(5), transactions.get(1).getMaxTransactionFee()); + Assertions.assertEquals(new Hbar(1), transactions.get(2).getMaxTransactionFee()); + Assertions.assertEquals(new Hbar(3), transactions.get(3).getMaxTransactionFee()); + + server.close(); + } + + @Disabled + @Test + @DisplayName("Client.setDefaultMaxQueryPayment() functions correctly") + void defaultMaxQueryPaymentTest() throws Exception { + var service = new TestCryptoService(); + var server = new TestServer("queryPayment", service); + + var response = Response.newBuilder() + .setCryptogetAccountBalance( + new AccountBalance(new Hbar(0), new HashMap(), new HashMap()) + .toProtobuf()) + .build(); + + service.buffer + .enqueueResponse(TestResponse.query(response)) + .enqueueResponse(TestResponse.query(response)) + .enqueueResponse(TestResponse.query(response)); + + // TODO: this will take some work, since I have to contend with Query's getCost behavior + // TODO: actually, because AccountBalanceQuery is free, I'll need some other query type to test this. + // Perhaps getAccountInfo? + + server.close(); + } + + @Test + @DisplayName("Signer is prevented from signing twice") + void signerDoesNotSignTwice() throws Exception { + var service = new TestCryptoService(); + var server = new TestServer("signerDoesNotSignTwice", service); + + service.buffer.enqueueResponse(TestResponse.transactionOk()); + + var aliceKey = PrivateKey.generateED25519(); + + var transaction = new AccountCreateTransaction() + .setTransactionId(TransactionId.generate(Objects.requireNonNull(server.client.getOperatorAccountId()))) + .setNodeAccountIds(server.client.network.getNodeAccountIdsForExecute()) + .freeze() + .sign(aliceKey); + + // This will cause the SDK Transaction to populate the sigPairLists list + transaction.getTransactionHashPerNode(); + + // This will clear the outerTransactions list while keeping the sigPairLists list + transaction.signWithOperator(server.client); + + // If Transaction.signTransaction() is not programmed correctly, it will add Alice's signature to the + // sigPairList a second time here. + transaction.execute(server.client); + + // Now we must go through the laborious process of digging info out of the response. =( + Assertions.assertEquals(1, service.buffer.transactionRequestsReceived.size()); + var request = service.buffer.transactionRequestsReceived.get(0); + var sigPairList = SignedTransaction.parseFrom(request.getSignedTransactionBytes()) + .getSigMap() + .getSigPairList(); + Assertions.assertEquals(2, sigPairList.size()); + Assertions.assertNotEquals( + sigPairList.get(0).getEd25519().toString(), + sigPairList.get(1).getEd25519().toString()); + + server.close(); + } + + @Test + @DisplayName("Can cancel executeAsync()") + void canCancelExecuteAsync() throws Exception { + var service = new TestCryptoService(); + var server = new TestServer("canCancelExecuteAsync", service); + + server.client.setMaxBackoff(Duration.ofSeconds(8)); + server.client.setMinBackoff(Duration.ofSeconds(1)); + + var noReceiptResponse = TestResponse.query(Response.newBuilder() + .setTransactionGetReceipt(TransactionGetReceiptResponse.newBuilder() + .setHeader(ResponseHeader.newBuilder() + .setNodeTransactionPrecheckCode(org.hiero.sdk.Status.RECEIPT_NOT_FOUND.code))) + .build()); + + service.buffer.enqueueResponse(noReceiptResponse); + service.buffer.enqueueResponse(noReceiptResponse); + service.buffer.enqueueResponse(noReceiptResponse); + + var future = new TransactionReceiptQuery().executeAsync(server.client); + Thread.sleep(1500); + future.cancel(true); + Thread.sleep(5000); + + Assertions.assertEquals(2, service.buffer.queryRequestsReceived.size()); + + server.close(); + } + + private static class TestCryptoService extends CryptoServiceGrpc.CryptoServiceImplBase implements TestService { + public Buffer buffer = new Buffer(); + + @Override + public Buffer getBuffer() { + return buffer; + } + + @Override + public void createAccount(Transaction request, StreamObserver responseObserver) { + respondToTransactionFromQueue(request, responseObserver); + } + + @Override + public void cryptoDelete(Transaction request, StreamObserver responseObserver) { + respondToTransactionFromQueue(request, responseObserver); + } + + @Override + public void cryptoGetBalance(Query request, StreamObserver responseObserver) { + respondToQueryFromQueue(request, responseObserver); + } + + @Override + public void getTransactionReceipts(Query request, StreamObserver responseObserver) { + respondToQueryFromQueue(request, responseObserver); + } + + @Override + public void getTxRecordByTxID(Query request, StreamObserver responseObserver) { + respondToQueryFromQueue(request, responseObserver); + } + + @Override + public void getAccountInfo(Query request, StreamObserver responseObserver) { + respondToQueryFromQueue(request, responseObserver); + } + } + + private static class TestFileService extends FileServiceGrpc.FileServiceImplBase implements TestService { + public Buffer buffer = new Buffer(); + + @Override + public Buffer getBuffer() { + return buffer; + } + + @Override + public void createFile(Transaction request, StreamObserver responseObserver) { + respondToTransactionFromQueue(request, responseObserver); + } + + @Override + public void appendContent(Transaction request, StreamObserver responseObserver) { + respondToTransactionFromQueue(request, responseObserver); + } + + @Override + public void deleteFile(Transaction request, StreamObserver responseObserver) { + respondToTransactionFromQueue(request, responseObserver); + } + } + + private static class TestContractService extends SmartContractServiceGrpc.SmartContractServiceImplBase + implements TestService { + public Buffer buffer = new Buffer(); + + @Override + public Buffer getBuffer() { + return buffer; + } + + @Override + public void createContract(Transaction request, StreamObserver responseObserver) { + respondToTransactionFromQueue(request, responseObserver); + } + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/NetworkVersionInfoQueryTest.java b/sdk/src/test/java/org/hiero/sdk/NetworkVersionInfoQueryTest.java new file mode 100644 index 0000000000..b06f5773d5 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/NetworkVersionInfoQueryTest.java @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.ByteString; +import io.github.jsonSnapshot.SnapshotMatcher; +import org.hiero.sdk.proto.QueryHeader; +import org.hiero.sdk.proto.Transaction; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class NetworkVersionInfoQueryTest { + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + var builder = org.hiero.sdk.proto.Query.newBuilder(); + new NetworkVersionInfoQuery() + .setMaxQueryPayment(Hbar.fromTinybars(100_000)) + .onMakeRequest( + builder, + QueryHeader.newBuilder() + .setPayment(Transaction.newBuilder() + .setSignedTransactionBytes(ByteString.fromHex("deadbeef")) + .build()) + .build()); + SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")) + .toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/NetworkVersionInfoQueryTest.snap b/sdk/src/test/java/org/hiero/sdk/NetworkVersionInfoQueryTest.snap new file mode 100644 index 0000000000..4d76e578d3 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/NetworkVersionInfoQueryTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.NetworkVersionInfoQueryTest.shouldSerialize=[ + "# org.hiero.sdk.proto.Query\nnetwork_get_version_info {\n header {\n payment {\n signed_transaction_bytes: \"\\336\\255\\276\\357\"\n }\n }\n}" +] diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/NetworkVersionInfoTest.java b/sdk/src/test/java/org/hiero/sdk/NetworkVersionInfoTest.java similarity index 75% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/NetworkVersionInfoTest.java rename to sdk/src/test/java/org/hiero/sdk/NetworkVersionInfoTest.java index 3920aa6b39..8e23ed6ae3 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/NetworkVersionInfoTest.java +++ b/sdk/src/test/java/org/hiero/sdk/NetworkVersionInfoTest.java @@ -1,12 +1,13 @@ -package com.hedera.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; import io.github.jsonSnapshot.SnapshotMatcher; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import static org.assertj.core.api.Assertions.assertThat; - public class NetworkVersionInfoTest { @BeforeAll public static void beforeAll() { @@ -19,10 +20,7 @@ public static void afterAll() { } NetworkVersionInfo spawnNetworkVerionInfoExample() { - return new NetworkVersionInfo( - new SemanticVersion(1, 2, 3), - new SemanticVersion(4, 5, 6) - ); + return new NetworkVersionInfo(new SemanticVersion(1, 2, 3), new SemanticVersion(4, 5, 6)); } @Test @@ -31,7 +29,8 @@ void shouldSerialize() throws Exception { byte[] networkVersionInfoBytes = originalNetworkVersionInfo.toBytes(); var copyNetworkVersionInfo = NetworkVersionInfo.fromBytes(networkVersionInfoBytes); assertThat(originalNetworkVersionInfo.toString().replaceAll("@[A-Za-z0-9]+", "")) - .isEqualTo(copyNetworkVersionInfo.toString().replaceAll("@[A-Za-z0-9]+", "")); - SnapshotMatcher.expect(originalNetworkVersionInfo.toString().replaceAll("@[A-Za-z0-9]+", "")).toMatchSnapshot(); + .isEqualTo(copyNetworkVersionInfo.toString().replaceAll("@[A-Za-z0-9]+", "")); + SnapshotMatcher.expect(originalNetworkVersionInfo.toString().replaceAll("@[A-Za-z0-9]+", "")) + .toMatchSnapshot(); } } diff --git a/sdk/src/test/java/org/hiero/sdk/NetworkVersionInfoTest.snap b/sdk/src/test/java/org/hiero/sdk/NetworkVersionInfoTest.snap new file mode 100644 index 0000000000..d2512a8e22 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/NetworkVersionInfoTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.NetworkVersionInfoTest.shouldSerialize=[ + "org.hiero.sdk.NetworkVersionInfo" +] diff --git a/sdk/src/test/java/org/hiero/sdk/NftIdTest.java b/sdk/src/test/java/org/hiero/sdk/NftIdTest.java new file mode 100644 index 0000000000..b5f2325ad7 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/NftIdTest.java @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.github.jsonSnapshot.SnapshotMatcher; +import java.util.concurrent.TimeoutException; +import org.bouncycastle.util.encoders.Hex; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +class NftIdTest { + + static Client mainnetClient; + static Client testnetClient; + static Client previewnetClient; + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + mainnetClient = Client.forMainnet(); + testnetClient = Client.forTestnet(); + previewnetClient = Client.forPreviewnet(); + } + + @AfterAll + public static void afterAll() throws TimeoutException { + mainnetClient.close(); + testnetClient.close(); + previewnetClient.close(); + SnapshotMatcher.validateSnapshots(); + } + + @Test + void fromString() { + SnapshotMatcher.expect(NftId.fromString("0.0.5005@1234").toString()).toMatchSnapshot(); + } + + @Test + void fromString2() { + SnapshotMatcher.expect(NftId.fromString("0.0.5005/1234").toString()).toMatchSnapshot(); + } + + @Test + void toFromString() { + var id1 = NftId.fromString("0.0.5005@1234"); + var id2 = NftId.fromString(id1.toString()); + assertThat(id2.toString()).isEqualTo(id1.toString()); + } + + @Test + void fromStringWithChecksumOnMainnet() { + SnapshotMatcher.expect(NftId.fromString("0.0.123-vfmkw/7584").toStringWithChecksum(mainnetClient)) + .toMatchSnapshot(); + } + + @Test + void fromStringWithChecksumOnTestnet() { + SnapshotMatcher.expect(NftId.fromString("0.0.123-esxsf@584903").toStringWithChecksum(testnetClient)) + .toMatchSnapshot(); + } + + @Test + void fromStringWithChecksumOnPreviewnet() { + SnapshotMatcher.expect(NftId.fromString("0.0.123-ogizo/487302").toStringWithChecksum(previewnetClient)) + .toMatchSnapshot(); + } + + @Test + void toBytes() throws InvalidProtocolBufferException { + SnapshotMatcher.expect(Hex.toHexString(new TokenId(5005).nft(4920).toBytes())) + .toMatchSnapshot(); + } + + @Test + void fromBytes() throws InvalidProtocolBufferException { + SnapshotMatcher.expect( + NftId.fromBytes(new TokenId(5005).nft(574489).toBytes()).toString()) + .toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/NftIdTest.snap b/sdk/src/test/java/org/hiero/sdk/NftIdTest.snap new file mode 100644 index 0000000000..93a2ac6f0c --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/NftIdTest.snap @@ -0,0 +1,33 @@ +org.hiero.sdk.NftIdTest.fromBytes=[ + "0.0.5005/574489" +] + + +org.hiero.sdk.NftIdTest.fromString2=[ + "0.0.5005/1234" +] + + +org.hiero.sdk.NftIdTest.fromString=[ + "0.0.5005/1234" +] + + +org.hiero.sdk.NftIdTest.fromStringWithChecksumOnMainnet=[ + "0.0.123-vfmkw/7584" +] + + +org.hiero.sdk.NftIdTest.fromStringWithChecksumOnPreviewnet=[ + "0.0.123-ogizo/487302" +] + + +org.hiero.sdk.NftIdTest.fromStringWithChecksumOnTestnet=[ + "0.0.123-esxsf/584903" +] + + +org.hiero.sdk.NftIdTest.toBytes=[ + "0a03188d2710b826" +] diff --git a/sdk/src/test/java/org/hiero/sdk/NodeCreateTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/NodeCreateTransactionTest.java new file mode 100644 index 0000000000..fc1a48593c --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/NodeCreateTransactionTest.java @@ -0,0 +1,247 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.google.protobuf.ByteString; +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import java.util.List; +import org.hiero.sdk.proto.NodeCreateTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class NodeCreateTransactionTest { + + private static final PrivateKey TEST_PRIVATE_KEY = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + private static final AccountId TEST_ACCOUNT_ID = AccountId.fromString("0.6.9"); + + private static final String TEST_DESCRIPTION = "Test description"; + + private static final List TEST_GOSSIP_ENDPOINTS = + List.of(spawnTestEndpoint((byte) 0), spawnTestEndpoint((byte) 1), spawnTestEndpoint((byte) 2)); + + private static final List TEST_SERVICE_ENDPOINTS = List.of( + spawnTestEndpoint((byte) 3), + spawnTestEndpoint((byte) 4), + spawnTestEndpoint((byte) 5), + spawnTestEndpoint((byte) 6)); + + private static final byte[] TEST_GOSSIP_CA_CERTIFICATE = new byte[] {0, 1, 2, 3, 4}; + + private static final byte[] TEST_GRPC_CERTIFICATE_HASH = new byte[] {5, 6, 7, 8, 9}; + + private static final PublicKey TEST_ADMIN_KEY = PrivateKey.fromString( + "302e020100300506032b65700422042062c4b69e9f45a554e5424fb5a6fe5e6ac1f19ead31dc7718c2d980fd1f998d4b") + .getPublicKey(); + + final Instant TEST_VALID_START = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + private static Endpoint spawnTestEndpoint(byte offset) { + return new Endpoint() + .setAddress(new byte[] {0x00, 0x01, 0x02, 0x03}) + .setDomainName(offset + "unit.test.com") + .setPort(42 + offset); + } + + private NodeCreateTransaction spawnTestTransaction() { + return new NodeCreateTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), TEST_VALID_START)) + .setAccountId(TEST_ACCOUNT_ID) + .setDescription(TEST_DESCRIPTION) + .setGossipEndpoints(TEST_GOSSIP_ENDPOINTS) + .setServiceEndpoints(TEST_SERVICE_ENDPOINTS) + .setGossipCaCertificate(TEST_GOSSIP_CA_CERTIFICATE) + .setGrpcCertificateHash(TEST_GRPC_CERTIFICATE_HASH) + .setAdminKey(TEST_ADMIN_KEY) + .setMaxTransactionFee(new Hbar(1)) + .freeze() + .sign(TEST_PRIVATE_KEY); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = NodeCreateTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setNodeCreate(NodeCreateTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(NodeCreateTransaction.class); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new NodeCreateTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void testUnrecognizedServicePort() throws Exception { + var tx = new NodeCreateTransaction() + .setServiceEndpoints(List.of(new Endpoint() + .setAddress(new byte[] {0x00, 0x01, 0x02, 0x03}) + .setDomainName("unit.test.com") + .setPort(50111))); + var tx2 = NodeCreateTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void testSetNull() { + new NodeCreateTransaction() + .setDescription(null) + .setAccountId(null) + .setGossipCaCertificate(null) + .setGrpcCertificateHash(null) + .setAdminKey(null); + } + + @Test + void constructNodeCreateTransactionFromTransactionBodyProtobuf() { + var transactionBodyBuilder = NodeCreateTransactionBody.newBuilder(); + + transactionBodyBuilder.setAccountId(TEST_ACCOUNT_ID.toProtobuf()); + transactionBodyBuilder.setDescription(TEST_DESCRIPTION); + + for (Endpoint gossipEndpoint : TEST_GOSSIP_ENDPOINTS) { + transactionBodyBuilder.addGossipEndpoint(gossipEndpoint.toProtobuf()); + } + + for (Endpoint serviceEndpoint : TEST_SERVICE_ENDPOINTS) { + transactionBodyBuilder.addServiceEndpoint(serviceEndpoint.toProtobuf()); + } + + transactionBodyBuilder.setGossipCaCertificate(ByteString.copyFrom(TEST_GOSSIP_CA_CERTIFICATE)); + transactionBodyBuilder.setGrpcCertificateHash(ByteString.copyFrom(TEST_GRPC_CERTIFICATE_HASH)); + transactionBodyBuilder.setAdminKey(TEST_ADMIN_KEY.toProtobufKey()); + + var tx = TransactionBody.newBuilder() + .setNodeCreate(transactionBodyBuilder.build()) + .build(); + var nodeCreateTransaction = new NodeCreateTransaction(tx); + + assertThat(nodeCreateTransaction.getAccountId()).isEqualTo(TEST_ACCOUNT_ID); + assertThat(nodeCreateTransaction.getDescription()).isEqualTo(TEST_DESCRIPTION); + assertThat(nodeCreateTransaction.getGossipEndpoints()).hasSize(TEST_GOSSIP_ENDPOINTS.size()); + assertThat(nodeCreateTransaction.getServiceEndpoints()).hasSize(TEST_SERVICE_ENDPOINTS.size()); + assertThat(nodeCreateTransaction.getGossipCaCertificate()).isEqualTo(TEST_GOSSIP_CA_CERTIFICATE); + assertThat(nodeCreateTransaction.getGrpcCertificateHash()).isEqualTo(TEST_GRPC_CERTIFICATE_HASH); + assertThat(nodeCreateTransaction.getAdminKey()).isEqualTo(TEST_ADMIN_KEY); + } + + @Test + void getSetAccountId() { + var nodeCreateTransaction = new NodeCreateTransaction().setAccountId(TEST_ACCOUNT_ID); + assertThat(nodeCreateTransaction.getAccountId()).isEqualTo(TEST_ACCOUNT_ID); + } + + @Test + void getSetAccountIdFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setAccountId(TEST_ACCOUNT_ID)); + } + + @Test + void getSetDescription() { + var nodeCreateTransaction = new NodeCreateTransaction().setDescription(TEST_DESCRIPTION); + assertThat(nodeCreateTransaction.getDescription()).isEqualTo(TEST_DESCRIPTION); + } + + @Test + void getSetDescriptionFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setDescription(TEST_DESCRIPTION)); + } + + @Test + void getSetGossipEndpoints() { + var nodeCreateTransaction = new NodeCreateTransaction().setGossipEndpoints(TEST_GOSSIP_ENDPOINTS); + assertThat(nodeCreateTransaction.getGossipEndpoints()).isEqualTo(TEST_GOSSIP_ENDPOINTS); + } + + @Test + void setTestGossipEndpointsFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setGossipEndpoints(TEST_GOSSIP_ENDPOINTS)); + } + + @Test + void getSetServiceEndpoints() { + var nodeCreateTransaction = new NodeCreateTransaction().setServiceEndpoints(TEST_SERVICE_ENDPOINTS); + assertThat(nodeCreateTransaction.getServiceEndpoints()).isEqualTo(TEST_SERVICE_ENDPOINTS); + } + + @Test + void getSetServiceEndpointsFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setServiceEndpoints(TEST_SERVICE_ENDPOINTS)); + } + + @Test + void getSetGossipCaCertificate() { + var nodeCreateTransaction = new NodeCreateTransaction().setGossipCaCertificate(TEST_GOSSIP_CA_CERTIFICATE); + assertThat(nodeCreateTransaction.getGossipCaCertificate()).isEqualTo(TEST_GOSSIP_CA_CERTIFICATE); + } + + @Test + void getSetGossipCaCertificateFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setGossipCaCertificate(TEST_GOSSIP_CA_CERTIFICATE)); + } + + @Test + void getSetGrpcCertificateHash() { + var nodeCreateTransaction = new NodeCreateTransaction().setGrpcCertificateHash(TEST_GRPC_CERTIFICATE_HASH); + assertThat(nodeCreateTransaction.getGrpcCertificateHash()).isEqualTo(TEST_GRPC_CERTIFICATE_HASH); + } + + @Test + void getSetGrpcCertificateHashFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setGrpcCertificateHash(TEST_GRPC_CERTIFICATE_HASH)); + } + + @Test + void getSetAdminKey() { + var nodeCreateTransaction = new NodeCreateTransaction().setAdminKey(TEST_ADMIN_KEY); + assertThat(nodeCreateTransaction.getAdminKey()).isEqualTo(TEST_ADMIN_KEY); + } + + @Test + void getSetAdminKeyFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setAdminKey(TEST_ADMIN_KEY)); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/NodeCreateTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/NodeCreateTransactionTest.snap new file mode 100644 index 0000000000..b42d7dfd34 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/NodeCreateTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.NodeCreateTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\nnode_create {\n account_id {\n account_num: 9\n realm_num: 6\n shard_num: 0\n }\n admin_key {\n ed25519: \"\\030\\214\\252`\\231\\024#O\\b\\370\\342\\232\\321g\\274\\273\\346\\221}\\211m\\244R\\306\\017\\230\\017j,\\246\\206\\001\"\n }\n description: \"Test description\"\n gossip_ca_certificate: \"\\000\\001\\002\\003\\004\"\n gossip_endpoint {\n domain_name: \"0unit.test.com\"\n ip_address_v4: \"\\000\\001\\002\\003\"\n port: 42\n }\n gossip_endpoint {\n domain_name: \"1unit.test.com\"\n ip_address_v4: \"\\000\\001\\002\\003\"\n port: 43\n }\n gossip_endpoint {\n domain_name: \"2unit.test.com\"\n ip_address_v4: \"\\000\\001\\002\\003\"\n port: 44\n }\n grpc_certificate_hash: \"\\005\\006\\a\\b\\t\"\n service_endpoint {\n domain_name: \"3unit.test.com\"\n ip_address_v4: \"\\000\\001\\002\\003\"\n port: 45\n }\n service_endpoint {\n domain_name: \"4unit.test.com\"\n ip_address_v4: \"\\000\\001\\002\\003\"\n port: 46\n }\n service_endpoint {\n domain_name: \"5unit.test.com\"\n ip_address_v4: \"\\000\\001\\002\\003\"\n port: 47\n }\n service_endpoint {\n domain_name: \"6unit.test.com\"\n ip_address_v4: \"\\000\\001\\002\\003\"\n port: 48\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/NodeDeleteTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/NodeDeleteTransactionTest.java new file mode 100644 index 0000000000..b8ced65711 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/NodeDeleteTransactionTest.java @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import org.hiero.sdk.proto.NodeDeleteTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class NodeDeleteTransactionTest { + + private static final PrivateKey TEST_PRIVATE_KEY = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + private static final long TEST_NODE_ID = 420; + + final Instant TEST_VALID_START = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + private NodeDeleteTransaction spawnTestTransaction() { + return new NodeDeleteTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), TEST_VALID_START)) + .setNodeId(TEST_NODE_ID) + .setMaxTransactionFee(new Hbar(1)) + .freeze() + .sign(TEST_PRIVATE_KEY); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = NodeDeleteTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new NodeDeleteTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setNodeDelete(NodeDeleteTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(NodeDeleteTransaction.class); + } + + @Test + void constructNodeDeleteTransactionFromTransactionBodyProtobuf() { + var transactionBodyBuilder = NodeDeleteTransactionBody.newBuilder(); + + transactionBodyBuilder.setNodeId(TEST_NODE_ID); + + var tx = TransactionBody.newBuilder() + .setNodeDelete(transactionBodyBuilder.build()) + .build(); + var nodeDeleteTransaction = new NodeDeleteTransaction(tx); + + assertThat(nodeDeleteTransaction.getNodeId()).isEqualTo(TEST_NODE_ID); + } + + @Test + void getSetNodeId() { + var nodeDeleteTransaction = new NodeDeleteTransaction().setNodeId(TEST_NODE_ID); + assertThat(nodeDeleteTransaction.getNodeId()).isEqualTo(TEST_NODE_ID); + } + + @Test + void getSetNodeIdFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setNodeId(TEST_NODE_ID)); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/NodeDeleteTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/NodeDeleteTransactionTest.snap new file mode 100644 index 0000000000..63526fe4ad --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/NodeDeleteTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.NodeDeleteTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\nnode_delete {\n node_id: 420\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/NodeUpdateTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/NodeUpdateTransactionTest.java new file mode 100644 index 0000000000..35eb9269f7 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/NodeUpdateTransactionTest.java @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.google.protobuf.ByteString; +import com.google.protobuf.BytesValue; +import com.google.protobuf.StringValue; +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import java.util.List; +import org.hiero.sdk.proto.NodeUpdateTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class NodeUpdateTransactionTest { + + private static final PrivateKey TEST_PRIVATE_KEY = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + private static final long TEST_NODE_ID = 420; + + private static final AccountId TEST_ACCOUNT_ID = AccountId.fromString("0.6.9"); + + private static final String TEST_DESCRIPTION = "Test description"; + + private static final List TEST_GOSSIP_ENDPOINTS = + List.of(spawnTestEndpoint((byte) 0), spawnTestEndpoint((byte) 1), spawnTestEndpoint((byte) 2)); + + private static final List TEST_SERVICE_ENDPOINTS = List.of( + spawnTestEndpoint((byte) 3), + spawnTestEndpoint((byte) 4), + spawnTestEndpoint((byte) 5), + spawnTestEndpoint((byte) 6)); + + private static final byte[] TEST_GOSSIP_CA_CERTIFICATE = new byte[] {0, 1, 2, 3, 4}; + + private static final byte[] TEST_GRPC_CERTIFICATE_HASH = new byte[] {5, 6, 7, 8, 9}; + + private static final PublicKey TEST_ADMIN_KEY = PrivateKey.fromString( + "302e020100300506032b65700422042062c4b69e9f45a554e5424fb5a6fe5e6ac1f19ead31dc7718c2d980fd1f998d4b") + .getPublicKey(); + + final Instant TEST_VALID_START = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + private static Endpoint spawnTestEndpoint(byte offset) { + return new Endpoint() + .setAddress(new byte[] {0x00, 0x01, 0x02, 0x03}) + .setDomainName(offset + "unit.test.com") + .setPort(42 + offset); + } + + private NodeUpdateTransaction spawnTestTransaction() { + return new NodeUpdateTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), TEST_VALID_START)) + .setNodeId(TEST_NODE_ID) + .setAccountId(TEST_ACCOUNT_ID) + .setDescription(TEST_DESCRIPTION) + .setGossipEndpoints(TEST_GOSSIP_ENDPOINTS) + .setServiceEndpoints(TEST_SERVICE_ENDPOINTS) + .setGossipCaCertificate(TEST_GOSSIP_CA_CERTIFICATE) + .setGrpcCertificateHash(TEST_GRPC_CERTIFICATE_HASH) + .setAdminKey(TEST_ADMIN_KEY) + .setMaxTransactionFee(new Hbar(1)) + .freeze() + .sign(TEST_PRIVATE_KEY); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = NodeUpdateTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new NodeUpdateTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void testUnrecognizedServicePort() throws Exception { + var tx = new NodeUpdateTransaction() + .setServiceEndpoints(List.of(new Endpoint() + .setAddress(new byte[] {0x00, 0x01, 0x02, 0x03}) + .setDomainName("unit.test.com") + .setPort(50111))); + var tx2 = NodeUpdateTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void testEmptyCertificates() throws Exception { + var tx = new NodeUpdateTransaction() + .setGossipCaCertificate(new byte[] {}) + .setGrpcCertificateHash(new byte[] {}); + var tx2Bytes = tx.toBytes(); + NodeUpdateTransaction deserializedTx = (NodeUpdateTransaction) Transaction.fromBytes(tx2Bytes); + assertThat(deserializedTx.getGossipCaCertificate()).isEqualTo(new byte[] {}); + assertThat(deserializedTx.getGrpcCertificateHash()).isEqualTo(new byte[] {}); + } + + @Test + void testSetNull() { + new NodeUpdateTransaction() + .setDescription(null) + .setAccountId(null) + .setGossipCaCertificate(null) + .setGrpcCertificateHash(null) + .setAdminKey(null); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setNodeUpdate(NodeUpdateTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(NodeUpdateTransaction.class); + } + + @Test + void constructNodeUpdateTransactionFromTransactionBodyProtobuf() { + var transactionBodyBuilder = NodeUpdateTransactionBody.newBuilder(); + + transactionBodyBuilder.setNodeId(TEST_NODE_ID); + transactionBodyBuilder.setAccountId(TEST_ACCOUNT_ID.toProtobuf()); + transactionBodyBuilder.setDescription(StringValue.of(TEST_DESCRIPTION)); + + for (Endpoint gossipEndpoint : TEST_GOSSIP_ENDPOINTS) { + transactionBodyBuilder.addGossipEndpoint(gossipEndpoint.toProtobuf()); + } + + for (Endpoint serviceEndpoint : TEST_SERVICE_ENDPOINTS) { + transactionBodyBuilder.addServiceEndpoint(serviceEndpoint.toProtobuf()); + } + + transactionBodyBuilder.setGossipCaCertificate(BytesValue.of(ByteString.copyFrom(TEST_GOSSIP_CA_CERTIFICATE))); + transactionBodyBuilder.setGrpcCertificateHash(BytesValue.of(ByteString.copyFrom(TEST_GRPC_CERTIFICATE_HASH))); + transactionBodyBuilder.setAdminKey(TEST_ADMIN_KEY.toProtobufKey()); + + var tx = TransactionBody.newBuilder() + .setNodeUpdate(transactionBodyBuilder.build()) + .build(); + var nodeUpdateTransaction = new NodeUpdateTransaction(tx); + + assertThat(nodeUpdateTransaction.getNodeId()).isEqualTo(TEST_NODE_ID); + assertThat(nodeUpdateTransaction.getAccountId()).isEqualTo(TEST_ACCOUNT_ID); + assertThat(nodeUpdateTransaction.getDescription()).isEqualTo(TEST_DESCRIPTION); + assertThat(nodeUpdateTransaction.getGossipEndpoints()).hasSize(TEST_GOSSIP_ENDPOINTS.size()); + assertThat(nodeUpdateTransaction.getServiceEndpoints()).hasSize(TEST_SERVICE_ENDPOINTS.size()); + assertThat(nodeUpdateTransaction.getGossipCaCertificate()).isEqualTo(TEST_GOSSIP_CA_CERTIFICATE); + assertThat(nodeUpdateTransaction.getGrpcCertificateHash()).isEqualTo(TEST_GRPC_CERTIFICATE_HASH); + assertThat(nodeUpdateTransaction.getAdminKey()).isEqualTo(TEST_ADMIN_KEY); + } + + @Test + void getSetNodeId() { + var nodeUpdateTransaction = new NodeUpdateTransaction().setNodeId(TEST_NODE_ID); + assertThat(nodeUpdateTransaction.getNodeId()).isEqualTo(TEST_NODE_ID); + } + + @Test + void getSetNodeIdFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setNodeId(TEST_NODE_ID)); + } + + @Test + void getSetAccountId() { + var nodeUpdateTransaction = new NodeUpdateTransaction().setAccountId(TEST_ACCOUNT_ID); + assertThat(nodeUpdateTransaction.getAccountId()).isEqualTo(TEST_ACCOUNT_ID); + } + + @Test + void getSetAccountIdFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setAccountId(TEST_ACCOUNT_ID)); + } + + @Test + void getSetDescription() { + var nodeUpdateTransaction = new NodeUpdateTransaction().setDescription(TEST_DESCRIPTION); + assertThat(nodeUpdateTransaction.getDescription()).isEqualTo(TEST_DESCRIPTION); + } + + @Test + void getSetDescriptionFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setDescription(TEST_DESCRIPTION)); + } + + @Test + void getSetGossipEndpoints() { + var nodeUpdateTransaction = new NodeUpdateTransaction().setGossipEndpoints(TEST_GOSSIP_ENDPOINTS); + assertThat(nodeUpdateTransaction.getGossipEndpoints()).isEqualTo(TEST_GOSSIP_ENDPOINTS); + } + + @Test + void setTestGossipEndpointsFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setGossipEndpoints(TEST_GOSSIP_ENDPOINTS)); + } + + @Test + void getSetServiceEndpoints() { + var nodeUpdateTransaction = new NodeUpdateTransaction().setServiceEndpoints(TEST_SERVICE_ENDPOINTS); + assertThat(nodeUpdateTransaction.getServiceEndpoints()).isEqualTo(TEST_SERVICE_ENDPOINTS); + } + + @Test + void getSetServiceEndpointsFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setServiceEndpoints(TEST_SERVICE_ENDPOINTS)); + } + + @Test + void getSetGossipCaCertificate() { + var nodeUpdateTransaction = new NodeUpdateTransaction().setGossipCaCertificate(TEST_GOSSIP_CA_CERTIFICATE); + assertThat(nodeUpdateTransaction.getGossipCaCertificate()).isEqualTo(TEST_GOSSIP_CA_CERTIFICATE); + } + + @Test + void getSetGossipCaCertificateFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setGossipCaCertificate(TEST_GOSSIP_CA_CERTIFICATE)); + } + + @Test + void getSetGrpcCertificateHash() { + var nodeUpdateTransaction = new NodeUpdateTransaction().setGrpcCertificateHash(TEST_GRPC_CERTIFICATE_HASH); + assertThat(nodeUpdateTransaction.getGrpcCertificateHash()).isEqualTo(TEST_GRPC_CERTIFICATE_HASH); + } + + @Test + void getSetGrpcCertificateHashFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setGrpcCertificateHash(TEST_GRPC_CERTIFICATE_HASH)); + } + + @Test + void getSetAdminKey() { + var nodeUpdateTransaction = new NodeUpdateTransaction().setAdminKey(TEST_ADMIN_KEY); + assertThat(nodeUpdateTransaction.getAdminKey()).isEqualTo(TEST_ADMIN_KEY); + } + + @Test + void getSetAdminKeyFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setAdminKey(TEST_ADMIN_KEY)); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/NodeUpdateTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/NodeUpdateTransactionTest.snap new file mode 100644 index 0000000000..bf8f7777fb --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/NodeUpdateTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.NodeUpdateTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\nnode_update {\n account_id {\n account_num: 9\n realm_num: 6\n shard_num: 0\n }\n admin_key {\n ed25519: \"\\030\\214\\252`\\231\\024#O\\b\\370\\342\\232\\321g\\274\\273\\346\\221}\\211m\\244R\\306\\017\\230\\017j,\\246\\206\\001\"\n }\n description {\n value: \"Test description\"\n }\n gossip_ca_certificate {\n value: \"\\000\\001\\002\\003\\004\"\n }\n gossip_endpoint {\n domain_name: \"0unit.test.com\"\n ip_address_v4: \"\\000\\001\\002\\003\"\n port: 42\n }\n gossip_endpoint {\n domain_name: \"1unit.test.com\"\n ip_address_v4: \"\\000\\001\\002\\003\"\n port: 43\n }\n gossip_endpoint {\n domain_name: \"2unit.test.com\"\n ip_address_v4: \"\\000\\001\\002\\003\"\n port: 44\n }\n grpc_certificate_hash {\n value: \"\\005\\006\\a\\b\\t\"\n }\n node_id: 420\n service_endpoint {\n domain_name: \"3unit.test.com\"\n ip_address_v4: \"\\000\\001\\002\\003\"\n port: 45\n }\n service_endpoint {\n domain_name: \"4unit.test.com\"\n ip_address_v4: \"\\000\\001\\002\\003\"\n port: 46\n }\n service_endpoint {\n domain_name: \"5unit.test.com\"\n ip_address_v4: \"\\000\\001\\002\\003\"\n port: 47\n }\n service_endpoint {\n domain_name: \"6unit.test.com\"\n ip_address_v4: \"\\000\\001\\002\\003\"\n port: 48\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/PendingAirdropIdTest.java b/sdk/src/test/java/org/hiero/sdk/PendingAirdropIdTest.java new file mode 100644 index 0000000000..19dd6ae388 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/PendingAirdropIdTest.java @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class PendingAirdropIdTest { + private AccountId sender; + private AccountId receiver; + private TokenId tokenId; + private NftId nftId; + + @BeforeEach + void setUp() { + sender = new AccountId(1001); + receiver = new AccountId(1002); + tokenId = new TokenId(1003); + nftId = new NftId(new TokenId(1004), 1); + } + + @Test + void testConstructorWithTokenId() { + PendingAirdropId pendingAirdropId = new PendingAirdropId(sender, receiver, tokenId); + + assertEquals(sender, pendingAirdropId.getSender()); + assertEquals(receiver, pendingAirdropId.getReceiver()); + assertEquals(tokenId, pendingAirdropId.getTokenId()); + assertNull(pendingAirdropId.getNftId()); + } + + @Test + void testConstructorWithNftId() { + PendingAirdropId pendingAirdropId = new PendingAirdropId(sender, receiver, nftId); + + assertEquals(sender, pendingAirdropId.getSender()); + assertEquals(receiver, pendingAirdropId.getReceiver()); + assertEquals(nftId, pendingAirdropId.getNftId()); + assertNull(pendingAirdropId.getTokenId()); + } + + @Test + void testSetSender() { + PendingAirdropId pendingAirdropId = new PendingAirdropId(); + pendingAirdropId.setSender(sender); + + assertEquals(sender, pendingAirdropId.getSender()); + } + + @Test + void testSetReceiver() { + PendingAirdropId pendingAirdropId = new PendingAirdropId(); + pendingAirdropId.setReceiver(receiver); + + assertEquals(receiver, pendingAirdropId.getReceiver()); + } + + @Test + void testSetTokenId() { + PendingAirdropId pendingAirdropId = new PendingAirdropId(); + pendingAirdropId.setTokenId(tokenId); + + assertEquals(tokenId, pendingAirdropId.getTokenId()); + } + + @Test + void testSetNftId() { + PendingAirdropId pendingAirdropId = new PendingAirdropId(); + pendingAirdropId.setNftId(nftId); + + assertEquals(nftId, pendingAirdropId.getNftId()); + } + + @Test + void testToProtobufWithTokenId() { + PendingAirdropId pendingAirdropId = new PendingAirdropId(sender, receiver, tokenId); + org.hiero.sdk.proto.PendingAirdropId proto = pendingAirdropId.toProtobuf(); + + assertNotNull(proto); + assertEquals(sender.toProtobuf(), proto.getSenderId()); + assertEquals(receiver.toProtobuf(), proto.getReceiverId()); + assertEquals(tokenId.toProtobuf(), proto.getFungibleTokenType()); + } + + @Test + void testToProtobufWithNftId() { + PendingAirdropId pendingAirdropId = new PendingAirdropId(sender, receiver, nftId); + org.hiero.sdk.proto.PendingAirdropId proto = pendingAirdropId.toProtobuf(); + + assertNotNull(proto); + assertEquals(sender.toProtobuf(), proto.getSenderId()); + assertEquals(receiver.toProtobuf(), proto.getReceiverId()); + assertEquals(nftId.toProtobuf(), proto.getNonFungibleToken()); + } + + @Test + void testFromProtobufWithTokenId() { + org.hiero.sdk.proto.PendingAirdropId proto = org.hiero.sdk.proto.PendingAirdropId.newBuilder() + .setSenderId(sender.toProtobuf()) + .setReceiverId(receiver.toProtobuf()) + .setFungibleTokenType(tokenId.toProtobuf()) + .build(); + + PendingAirdropId pendingAirdropId = PendingAirdropId.fromProtobuf(proto); + + assertNotNull(pendingAirdropId); + assertEquals(sender, pendingAirdropId.getSender()); + assertEquals(receiver, pendingAirdropId.getReceiver()); + assertEquals(tokenId, pendingAirdropId.getTokenId()); + assertNull(pendingAirdropId.getNftId()); + } + + @Test + void testFromProtobufWithNftId() { + org.hiero.sdk.proto.PendingAirdropId proto = org.hiero.sdk.proto.PendingAirdropId.newBuilder() + .setSenderId(sender.toProtobuf()) + .setReceiverId(receiver.toProtobuf()) + .setNonFungibleToken(nftId.toProtobuf()) + .build(); + + PendingAirdropId pendingAirdropId = PendingAirdropId.fromProtobuf(proto); + + assertNotNull(pendingAirdropId); + assertEquals(sender, pendingAirdropId.getSender()); + assertEquals(receiver, pendingAirdropId.getReceiver()); + assertEquals(nftId, pendingAirdropId.getNftId()); + assertNull(pendingAirdropId.getTokenId()); + } + + @Test + void testToString() { + PendingAirdropId pendingAirdropId = new PendingAirdropId(sender, receiver, tokenId); + String result = pendingAirdropId.toString(); + + assertTrue(result.contains("sender")); + assertTrue(result.contains("receiver")); + assertTrue(result.contains("tokenId")); + assertTrue(result.contains("nftId")); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/PrivateKeyTest.java b/sdk/src/test/java/org/hiero/sdk/PrivateKeyTest.java new file mode 100644 index 0000000000..77e5659b23 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/PrivateKeyTest.java @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.google.protobuf.InvalidProtocolBufferException; +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.util.Collections; +import org.bouncycastle.util.encoders.Hex; +import org.junit.jupiter.api.Test; + +public class PrivateKeyTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @Test + void signTransactionWorks() throws InvalidProtocolBufferException { + byte[] bytes = new AccountCreateTransaction() + .setNodeAccountIds(Collections.singletonList(AccountId.fromString("0.0.5005"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setKey(unusedPrivateKey) + .setInitialBalance(Hbar.fromTinybars(450)) + .setProxyAccountId(AccountId.fromString("0.0.1001")) + .setReceiverSignatureRequired(true) + .setMaxTransactionFee(Hbar.fromTinybars(100_000)) + .freeze() + .toBytes(); + + AccountCreateTransaction transaction = (AccountCreateTransaction) Transaction.fromBytes(bytes); + unusedPrivateKey.signTransaction(transaction); + } + + @Test + void ecdsa() { + var message = "hello world".getBytes(StandardCharsets.UTF_8); + var key = PrivateKey.fromStringECDSA("8776c6b831a1b61ac10dac0304a2843de4716f54b1919bb91a2685d0fe3f3048"); + var signature = key.sign(message); + + assertThat(Hex.toHexString(signature)) + .isEqualTo( + "f3a13a555f1f8cd6532716b8f388bd4e9d8ed0b252743e923114c0c6cbfe414cf791c8e859afd3c12009ecf2cb20dacf01636d80823bcdbd9ec1ce59afe008f0"); + } + + @Test + void supports0xPrefix() { + PrivateKey.fromString("0x8776c6b831a1b61ac10dac0304a2843de4716f54b1919bb91a2685d0fe3f3048"); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/PrngTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/PrngTransactionTest.java new file mode 100644 index 0000000000..8e1f0575f3 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/PrngTransactionTest.java @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class PrngTransactionTest { + + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + private PrngTransaction spawnTestTransaction() { + return new PrngTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setMaxTransactionFee(Hbar.fromTinybars(100_000)) + .freeze() + .sign(unusedPrivateKey); + } + + private PrngTransaction spawnTestTransaction2() { + return new PrngTransaction() + .setRange(100) + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setMaxTransactionFee(Hbar.fromTinybars(100_000)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + @Test + void shouldSerialize2() { + SnapshotMatcher.expect(spawnTestTransaction2().toString()).toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/PrngTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/PrngTransactionTest.snap new file mode 100644 index 0000000000..42712f9a75 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/PrngTransactionTest.snap @@ -0,0 +1,8 @@ +org.hiero.sdk.PrngTransactionTest.shouldSerialize2=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}\nutil_prng {\n range: 100\n}" +] + + +org.hiero.sdk.PrngTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}\nutil_prng {\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/ProxyStakerTest.java b/sdk/src/test/java/org/hiero/sdk/ProxyStakerTest.java new file mode 100644 index 0000000000..a727306af8 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ProxyStakerTest.java @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.github.jsonSnapshot.SnapshotMatcher; +import org.hiero.sdk.proto.ProxyStaker; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class ProxyStakerTest { + private static final ProxyStaker proxyStaker = ProxyStaker.newBuilder() + .setAccountID(new AccountId(100).toProtobuf()) + .setAmount(10) + .build(); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void fromProtobuf() { + SnapshotMatcher.expect( + org.hiero.sdk.ProxyStaker.fromProtobuf(proxyStaker).toString()) + .toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/ProxyStakerTest.snap b/sdk/src/test/java/org/hiero/sdk/ProxyStakerTest.snap new file mode 100644 index 0000000000..010dcf9025 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ProxyStakerTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.ProxyStakerTest.fromProtobuf=[ + "ProxyStaker{accountId=0.0.100, amount=10 tℏ}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/ReceiptStatusExceptionTest.java b/sdk/src/test/java/org/hiero/sdk/ReceiptStatusExceptionTest.java new file mode 100644 index 0000000000..2c36d90ca1 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ReceiptStatusExceptionTest.java @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.Instant; +import org.hiero.sdk.proto.ResponseCodeEnum; +import org.junit.jupiter.api.Test; + +class ReceiptStatusExceptionTest { + + @Test + void shouldHaveMessage() { + var validStart = Instant.ofEpochSecond(1554158542); + var txId = new TransactionId(new AccountId(0, 0, 100), validStart); + var txReceipt = TransactionReceipt.fromProtobuf(org.hiero.sdk.proto.TransactionReceipt.newBuilder() + .setStatusValue(ResponseCodeEnum.INSUFFICIENT_TX_FEE_VALUE) + .build()); + var e = new ReceiptStatusException(txId, txReceipt); + + assertThat(e.getMessage()) + .isEqualTo("receipt for transaction 0.0.100@1554158542.000000000 raised status INSUFFICIENT_TX_FEE"); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/RegenerateTransactionIdsTest.java b/sdk/src/test/java/org/hiero/sdk/RegenerateTransactionIdsTest.java new file mode 100644 index 0000000000..7865468065 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/RegenerateTransactionIdsTest.java @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.grpc.Status; +import java.util.HashSet; +import java.util.List; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; +import org.hiero.sdk.proto.ResponseCodeEnum; +import org.hiero.sdk.proto.SignedTransaction; +import org.hiero.sdk.proto.Transaction; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionResponse; +import org.junit.jupiter.api.Test; + +public class RegenerateTransactionIdsTest { + @Test + void regeneratesTransactionIdsWhenTransactionExpiredIsReturned() + throws PrecheckStatusException, TimeoutException, InterruptedException { + var transactionIds = new HashSet(); + AtomicInteger count = new AtomicInteger(0); + + var responses = List.of( + TransactionResponse.newBuilder() + .setNodeTransactionPrecheckCode(ResponseCodeEnum.TRANSACTION_EXPIRED) + .build(), + TransactionResponse.newBuilder() + .setNodeTransactionPrecheckCode(ResponseCodeEnum.TRANSACTION_EXPIRED) + .build(), + TransactionResponse.newBuilder() + .setNodeTransactionPrecheckCode(ResponseCodeEnum.TRANSACTION_EXPIRED) + .build(), + TransactionResponse.newBuilder() + .setNodeTransactionPrecheckCode(ResponseCodeEnum.OK) + .build()); + + var call = (Function) o -> { + try { + var transaction = (Transaction) o; + var signedTransaction = SignedTransaction.parseFrom(transaction.getSignedTransactionBytes()); + var transactionBody = TransactionBody.parseFrom(signedTransaction.getBodyBytes()); + var transactionId = TransactionId.fromProtobuf(transactionBody.getTransactionID()); + + if (transactionIds.contains(transactionId)) { + return Status.Code.ABORTED.toStatus().asRuntimeException(); + } + + transactionIds.add(transactionId); + + return responses.get(count.getAndIncrement()); + } catch (Throwable e) { + return new RuntimeException(e); + } + }; + + List responses1 = List.of(call, call, call, call); + + try (var mocker = Mocker.withResponses(List.of(responses1))) { + new FileCreateTransaction().execute(mocker.client); + } + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/RequestTypeTest.java b/sdk/src/test/java/org/hiero/sdk/RequestTypeTest.java new file mode 100644 index 0000000000..dcd03e2a8d --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/RequestTypeTest.java @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import org.hiero.sdk.proto.HederaFunctionality; +import org.junit.jupiter.api.Test; + +class RequestTypeTest { + + @Test + void valueOf() { + var codeValues = HederaFunctionality.values(); + var requestTypeValues = RequestType.values(); + var pair = IntStream.range(0, codeValues.length - 1) + .mapToObj(i -> Map.entry(codeValues[i], requestTypeValues[i])) + .collect(Collectors.toList()); + + pair.forEach((a) -> { + var code = a.getKey(); + var requestType = a.getValue(); + assertThat(RequestType.valueOf(code)).hasToString(requestType.toString()); + }); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/ScheduleCreateTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/ScheduleCreateTransactionTest.java new file mode 100644 index 0000000000..dff72fb457 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ScheduleCreateTransactionTest.java @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class ScheduleCreateTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + private ScheduleCreateTransaction spawnTestTransaction() { + var transferTransaction = new TransferTransaction() + .addHbarTransfer(AccountId.fromString("0.0.555"), new Hbar(-10)) + .addHbarTransfer(AccountId.fromString("0.0.333"), new Hbar(10)); + return transferTransaction + .schedule() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setAdminKey(unusedPrivateKey) + .setPayerAccountId(AccountId.fromString("0.0.222")) + .setScheduleMemo("hi") + .setMaxTransactionFee(new Hbar(1)) + .setExpirationTime(validStart) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = ScheduleCreateTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new ScheduleCreateTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/ScheduleCreateTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/ScheduleCreateTransactionTest.snap new file mode 100644 index 0000000000..27ae7f55af --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ScheduleCreateTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.ScheduleCreateTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\nschedule_create {\n admin_key {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n expiration_time {\n seconds: 1554158542\n }\n memo: \"hi\"\n payer_account_i_d {\n account_num: 222\n realm_num: 0\n shard_num: 0\n }\n scheduled_transaction_body {\n crypto_transfer {\n transfers {\n account_amounts {\n account_i_d {\n account_num: 333\n realm_num: 0\n shard_num: 0\n }\n amount: 1000000000\n }\n account_amounts {\n account_i_d {\n account_num: 555\n realm_num: 0\n shard_num: 0\n }\n amount: -1000000000\n }\n }\n }\n transaction_fee: 100000000\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/ScheduleDeleteTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/ScheduleDeleteTransactionTest.java new file mode 100644 index 0000000000..a310741cb0 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ScheduleDeleteTransactionTest.java @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.ScheduleDeleteTransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class ScheduleDeleteTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + private ScheduleDeleteTransaction spawnTestTransaction() { + return new ScheduleDeleteTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setScheduleId(ScheduleId.fromString("0.0.444")) + .setMaxTransactionFee(new Hbar(1)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = ScheduleDeleteTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new ScheduleDeleteTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setScheduleDelete(ScheduleDeleteTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(ScheduleDeleteTransaction.class); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/ScheduleDeleteTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/ScheduleDeleteTransactionTest.snap new file mode 100644 index 0000000000..98a1c0c4ab --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ScheduleDeleteTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.ScheduleDeleteTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\nschedule_delete {\n schedule_i_d {\n realm_num: 0\n schedule_num: 444\n shard_num: 0\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/ScheduleInfoQueryTest.java b/sdk/src/test/java/org/hiero/sdk/ScheduleInfoQueryTest.java new file mode 100644 index 0000000000..01574b189f --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ScheduleInfoQueryTest.java @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.github.jsonSnapshot.SnapshotMatcher; +import org.hiero.sdk.proto.QueryHeader; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class ScheduleInfoQueryTest { + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + var builder = org.hiero.sdk.proto.Query.newBuilder(); + new ScheduleInfoQuery() + .setScheduleId(ScheduleId.fromString("0.0.5005")) + .setMaxQueryPayment(Hbar.fromTinybars(100_000)) + .onMakeRequest(builder, QueryHeader.newBuilder().build()); + SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")) + .toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/ScheduleInfoQueryTest.snap b/sdk/src/test/java/org/hiero/sdk/ScheduleInfoQueryTest.snap new file mode 100644 index 0000000000..060c3a0fc2 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ScheduleInfoQueryTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.ScheduleInfoQueryTest.shouldSerialize=[ + "# org.hiero.sdk.proto.Query\nschedule_get_info {\n header {\n }\n schedule_i_d {\n realm_num: 0\n schedule_num: 5005\n shard_num: 0\n }\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/ScheduleInfoTest.java b/sdk/src/test/java/org/hiero/sdk/ScheduleInfoTest.java new file mode 100644 index 0000000000..c9cef88193 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ScheduleInfoTest.java @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import org.hiero.sdk.proto.CryptoDeleteTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class ScheduleInfoTest { + private static final PublicKey unusedPublicKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10") + .getPublicKey(); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + ScheduleInfo spawnScheduleInfoExample() { + return new ScheduleInfo( + ScheduleId.fromString("1.2.3"), + AccountId.fromString("4.5.6"), + AccountId.fromString("2.3.4"), + SchedulableTransactionBody.newBuilder() + .setCryptoDelete(CryptoDeleteTransactionBody.newBuilder() + .setDeleteAccountID( + AccountId.fromString("6.6.6").toProtobuf()) + .build()) + .build(), + KeyList.of(unusedPublicKey), + unusedPublicKey, + TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart), + "memo", + validStart, + validStart, + null, + LedgerId.TESTNET, + true); + } + + ScheduleInfo spawnScheduleInfoDeletedExample() { + return new ScheduleInfo( + ScheduleId.fromString("1.2.3"), + AccountId.fromString("4.5.6"), + AccountId.fromString("2.3.4"), + SchedulableTransactionBody.newBuilder() + .setCryptoDelete(CryptoDeleteTransactionBody.newBuilder() + .setDeleteAccountID( + AccountId.fromString("6.6.6").toProtobuf()) + .build()) + .build(), + KeyList.of(unusedPublicKey), + unusedPublicKey, + TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart), + "memo", + validStart, + null, + validStart, + LedgerId.TESTNET, + true); + } + + @Test + void shouldSerialize() throws Exception { + var originalScheduleInfo = spawnScheduleInfoExample(); + byte[] scheduleInfoBytes = originalScheduleInfo.toBytes(); + var copyScheduleInfo = ScheduleInfo.fromBytes(scheduleInfoBytes); + assertThat(copyScheduleInfo.toString().replaceAll("@[A-Za-z0-9]+", "")) + .isEqualTo(originalScheduleInfo.toString().replaceAll("@[A-Za-z0-9]+", "")); + SnapshotMatcher.expect(originalScheduleInfo.toString().replaceAll("@[A-Za-z0-9]+", "")) + .toMatchSnapshot(); + } + + @Test + void shouldSerializeDeleted() throws Exception { + var originalScheduleInfo = spawnScheduleInfoDeletedExample(); + byte[] scheduleInfoBytes = originalScheduleInfo.toBytes(); + var copyScheduleInfo = ScheduleInfo.fromBytes(scheduleInfoBytes); + assertThat(copyScheduleInfo.toString().replaceAll("@[A-Za-z0-9]+", "")) + .isEqualTo(originalScheduleInfo.toString().replaceAll("@[A-Za-z0-9]+", "")); + SnapshotMatcher.expect(originalScheduleInfo.toString().replaceAll("@[A-Za-z0-9]+", "")) + .toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/ScheduleInfoTest.snap b/sdk/src/test/java/org/hiero/sdk/ScheduleInfoTest.snap similarity index 88% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/ScheduleInfoTest.snap rename to sdk/src/test/java/org/hiero/sdk/ScheduleInfoTest.snap index 68bdcb1db6..a01debe3a2 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/ScheduleInfoTest.snap +++ b/sdk/src/test/java/org/hiero/sdk/ScheduleInfoTest.snap @@ -1,8 +1,8 @@ -com.hedera.hashgraph.sdk.ScheduleInfoTest.shouldSerialize=[ +org.hiero.sdk.ScheduleInfoTest.shouldSerialize=[ "ScheduleInfo{scheduleId=1.2.3, scheduledTransactionId=0.0.5006.000000000, creatorAccountId=4.5.6, payerAccountId=2.3.4, signatories=KeyList{threshold=null, keys=[302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7]}, adminKey=302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7, expirationTime=2019-04-01T22:42:22Z, memo=memo, executedAt=2019-04-01T22:42:22Z, deletedAt=null, ledgerId=testnet, waitForExpiry=true}" ] -com.hedera.hashgraph.sdk.ScheduleInfoTest.shouldSerializeDeleted=[ +org.hiero.sdk.ScheduleInfoTest.shouldSerializeDeleted=[ "ScheduleInfo{scheduleId=1.2.3, scheduledTransactionId=0.0.5006.000000000, creatorAccountId=4.5.6, payerAccountId=2.3.4, signatories=KeyList{threshold=null, keys=[302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7]}, adminKey=302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7, expirationTime=2019-04-01T22:42:22Z, memo=memo, executedAt=null, deletedAt=2019-04-01T22:42:22Z, ledgerId=testnet, waitForExpiry=true}" ] diff --git a/sdk/src/test/java/org/hiero/sdk/ScheduleSignTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/ScheduleSignTransactionTest.java new file mode 100644 index 0000000000..b4afa40005 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ScheduleSignTransactionTest.java @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class ScheduleSignTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new ScheduleSignTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + private ScheduleSignTransaction spawnTestTransaction() { + return new ScheduleSignTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setScheduleId(ScheduleId.fromString("0.0.444")) + .setMaxTransactionFee(new Hbar(1)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = ScheduleSignTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/ScheduleSignTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/ScheduleSignTransactionTest.snap new file mode 100644 index 0000000000..e0b4d254b3 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/ScheduleSignTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.ScheduleSignTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\nschedule_sign {\n schedule_i_d {\n realm_num: 0\n schedule_num: 444\n shard_num: 0\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/Snapshot.java b/sdk/src/test/java/org/hiero/sdk/Snapshot.java similarity index 80% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/Snapshot.java rename to sdk/src/test/java/org/hiero/sdk/Snapshot.java index 8487204e85..3278d02883 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/Snapshot.java +++ b/sdk/src/test/java/org/hiero/sdk/Snapshot.java @@ -1,4 +1,5 @@ -package com.hedera.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonInclude; @@ -7,13 +8,11 @@ import com.fasterxml.jackson.core.PrettyPrinter; import com.fasterxml.jackson.core.util.DefaultIndenter; import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; -import com.fasterxml.jackson.core.util.Separators; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; - -import javax.annotation.Nonnull; import java.io.IOException; import java.io.UncheckedIOException; +import javax.annotation.Nonnull; final class Snapshot { private static final ObjectMapper objectMapper = buildObjectMapper(); @@ -40,8 +39,7 @@ private static ObjectMapper buildObjectMapper() { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true); objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - objectMapper.setVisibility( - objectMapper + objectMapper.setVisibility(objectMapper .getSerializationConfig() .getDefaultVisibilityChecker() .withFieldVisibility(JsonAutoDetect.Visibility.ANY) @@ -55,18 +53,18 @@ private static ObjectMapper buildObjectMapper() { * Modified copy of {@code io.github.jsonSnapshot.SnapshotMatcher#buildDefaultPrettyPrinter} */ private static PrettyPrinter buildDefaultPrettyPrinter() { - DefaultPrettyPrinter pp = - new DefaultPrettyPrinter() { - @Override - @Nonnull - public DefaultPrettyPrinter createInstance() { - return this; - } - @Override - public void writeObjectFieldValueSeparator(JsonGenerator jg) throws IOException { - jg.writeRaw(": "); - } - }; + DefaultPrettyPrinter pp = new DefaultPrettyPrinter() { + @Override + @Nonnull + public DefaultPrettyPrinter createInstance() { + return this; + } + + @Override + public void writeObjectFieldValueSeparator(JsonGenerator jg) throws IOException { + jg.writeRaw(": "); + } + }; DefaultPrettyPrinter.Indenter lfOnlyIndenter = new DefaultIndenter(" ", "\n"); pp.indentArraysWith(lfOnlyIndenter); pp.indentObjectsWith(lfOnlyIndenter); diff --git a/sdk/src/test/java/org/hiero/sdk/StakingInfoTest.java b/sdk/src/test/java/org/hiero/sdk/StakingInfoTest.java new file mode 100644 index 0000000000..25bcd228ac --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/StakingInfoTest.java @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class StakingInfoTest { + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + StakingInfo spawnStakingInfoAccountExample() { + return new StakingInfo(true, validStart, Hbar.from(5), Hbar.from(10), AccountId.fromString("1.2.3"), null); + } + + StakingInfo spawnStakingInfoNodeExample() { + return new StakingInfo(true, validStart, Hbar.from(5), Hbar.from(10), null, 3L); + } + + @Test + void shouldSerializeAccount() throws Exception { + var originalStakingInfo = spawnStakingInfoAccountExample(); + byte[] stakingInfoBytes = originalStakingInfo.toBytes(); + var copyStakingInfo = StakingInfo.fromBytes(stakingInfoBytes); + assertThat(copyStakingInfo.toString().replaceAll("@[A-Za-z0-9]+", "")) + .isEqualTo(originalStakingInfo.toString().replaceAll("@[A-Za-z0-9]+", "")); + SnapshotMatcher.expect(originalStakingInfo.toString().replaceAll("@[A-Za-z0-9]+", "")) + .toMatchSnapshot(); + } + + @Test + void shouldSerializeNode() throws Exception { + var originalStakingInfo = spawnStakingInfoNodeExample(); + byte[] stakingInfoBytes = originalStakingInfo.toBytes(); + var copyStakingInfo = StakingInfo.fromBytes(stakingInfoBytes); + assertThat(copyStakingInfo.toString().replaceAll("@[A-Za-z0-9]+", "")) + .isEqualTo(originalStakingInfo.toString().replaceAll("@[A-Za-z0-9]+", "")); + SnapshotMatcher.expect(originalStakingInfo.toString().replaceAll("@[A-Za-z0-9]+", "")) + .toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/StakingInfoTest.snap b/sdk/src/test/java/org/hiero/sdk/StakingInfoTest.snap new file mode 100644 index 0000000000..30aef2aec3 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/StakingInfoTest.snap @@ -0,0 +1,8 @@ +org.hiero.sdk.StakingInfoTest.shouldSerializeAccount=[ + "StakingInfo{declineStakingReward=true, stakePeriodStart=2019-04-01T22:42:22Z, pendingReward=5 ℏ, stakedToMe=10 ℏ, stakedAccountId=1.2.3, stakedNodeId=null}" +] + + +org.hiero.sdk.StakingInfoTest.shouldSerializeNode=[ + "StakingInfo{declineStakingReward=true, stakePeriodStart=2019-04-01T22:42:22Z, pendingReward=5 ℏ, stakedToMe=10 ℏ, stakedAccountId=null, stakedNodeId=3}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/StatusTest.java b/sdk/src/test/java/org/hiero/sdk/StatusTest.java new file mode 100644 index 0000000000..030b6c4882 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/StatusTest.java @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import org.hiero.sdk.proto.ResponseCodeEnum; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class StatusTest { + @Test + @DisplayName("Status can be constructed from any ResponseCode") + void statusToResponseCode() { + for (ResponseCodeEnum code : ResponseCodeEnum.values()) { + // not an actual value we want to handle + // this is what we're given if an unexpected value was decoded + if (code == ResponseCodeEnum.UNRECOGNIZED) { + continue; + } + + Status status = Status.valueOf(code); + + assertThat(code.getNumber()).isEqualTo(status.code.getNumber()); + } + } + + @Test + @DisplayName("Status throws on Unrecognized") + void statusUnrecognized() { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> Status.valueOf(ResponseCodeEnum.UNRECOGNIZED)) + .withMessage("network returned unrecognized response code; your SDK may be out of date"); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/SystemDeleteTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/SystemDeleteTransactionTest.java new file mode 100644 index 0000000000..b84e341843 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/SystemDeleteTransactionTest.java @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.SystemDeleteTransactionBody; +import org.hiero.sdk.proto.TimestampSeconds; +import org.hiero.sdk.proto.TransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class SystemDeleteTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + private static final FileId testFileId = FileId.fromString("4.2.0"); + private static final ContractId testContractId = ContractId.fromString("0.6.9"); + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerializeFile() { + SnapshotMatcher.expect(spawnTestTransactionFile().toString()).toMatchSnapshot(); + } + + private SystemDeleteTransaction spawnTestTransactionFile() { + return new SystemDeleteTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setFileId(FileId.fromString("0.0.444")) + .setExpirationTime(validStart) + .setMaxTransactionFee(new Hbar(1)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldSerializeContract() { + SnapshotMatcher.expect(spawnTestTransactionContract().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new SystemDeleteTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + private SystemDeleteTransaction spawnTestTransactionContract() { + return new SystemDeleteTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setContractId(ContractId.fromString("0.0.444")) + .setExpirationTime(validStart) + .setMaxTransactionFee(new Hbar(1)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytesContract() throws Exception { + var tx = spawnTestTransactionContract(); + var tx2 = ScheduleDeleteTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldBytesFile() throws Exception { + var tx = spawnTestTransactionFile(); + var tx2 = SystemDeleteTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setSystemDelete(SystemDeleteTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(SystemDeleteTransaction.class); + } + + @Test + void constructSystemDeleteTransactionFromTransactionBodyProtobuf() { + var transactionBodyWithFileId = SystemDeleteTransactionBody.newBuilder() + .setFileID(testFileId.toProtobuf()) + .setExpirationTime(TimestampSeconds.newBuilder().setSeconds(validStart.getEpochSecond())); + + var transactionBodyWithContractId = SystemDeleteTransactionBody.newBuilder() + .setContractID(testContractId.toProtobuf()) + .setExpirationTime(TimestampSeconds.newBuilder().setSeconds(validStart.getEpochSecond())); + + var txWithFileId = TransactionBody.newBuilder() + .setSystemDelete(transactionBodyWithFileId) + .build(); + var systemDeleteTransactionWithFileId = new SystemDeleteTransaction(txWithFileId); + + var txWithContractId = TransactionBody.newBuilder() + .setSystemDelete(transactionBodyWithContractId) + .build(); + var systemDeleteTransactionWithContractId = new SystemDeleteTransaction(txWithContractId); + + assertNotNull(systemDeleteTransactionWithFileId.getFileId()); + assertThat(systemDeleteTransactionWithFileId.getFileId()).isEqualTo(testFileId); + assertNull(systemDeleteTransactionWithFileId.getContractId()); + assertThat(systemDeleteTransactionWithFileId.getExpirationTime().getEpochSecond()) + .isEqualTo(validStart.getEpochSecond()); + + assertNull(systemDeleteTransactionWithContractId.getFileId()); + assertNotNull(systemDeleteTransactionWithContractId.getContractId()); + assertThat(systemDeleteTransactionWithContractId.getContractId()).isEqualTo(testContractId); + assertThat(systemDeleteTransactionWithContractId.getExpirationTime().getEpochSecond()) + .isEqualTo(validStart.getEpochSecond()); + } + + @Test + void getSetFileId() { + var systemDeleteTransaction = new SystemDeleteTransaction().setFileId(testFileId); + assertNotNull(systemDeleteTransaction.getFileId()); + assertThat(systemDeleteTransaction.getFileId()).isEqualTo(testFileId); + } + + @Test + void getSetFileIdFrozen() { + var tx = spawnTestTransactionFile(); + assertThrows(IllegalStateException.class, () -> tx.setFileId(testFileId)); + } + + @Test + void getSetContractId() { + var systemDeleteTransaction = new SystemDeleteTransaction().setContractId(testContractId); + assertNotNull(systemDeleteTransaction.getContractId()); + assertThat(systemDeleteTransaction.getContractId()).isEqualTo(testContractId); + } + + @Test + void getSetContractIdFrozen() { + var tx = spawnTestTransactionContract(); + assertThrows(IllegalStateException.class, () -> tx.setContractId(testContractId)); + } + + @Test + void getSetExpirationTime() { + var systemDeleteTransaction = new SystemDeleteTransaction().setExpirationTime(validStart); + assertNotNull(systemDeleteTransaction.getExpirationTime()); + assertThat(systemDeleteTransaction.getExpirationTime().getEpochSecond()).isEqualTo(validStart.getEpochSecond()); + } + + @Test + void getSetExpirationTimeFrozen() { + var tx = spawnTestTransactionFile(); + assertThrows(IllegalStateException.class, () -> tx.setExpirationTime(validStart)); + } + + @Test + void resetFileId() { + var systemDeleteTransaction = new SystemDeleteTransaction(); + systemDeleteTransaction.setFileId(testFileId); + systemDeleteTransaction.setContractId(testContractId); + + assertNull(systemDeleteTransaction.getFileId()); + assertNotNull(systemDeleteTransaction.getContractId()); + } + + @Test + void resetContractId() { + var systemDeleteTransaction = new SystemDeleteTransaction(); + systemDeleteTransaction.setContractId(testContractId); + systemDeleteTransaction.setFileId(testFileId); + + assertNull(systemDeleteTransaction.getContractId()); + assertNotNull(systemDeleteTransaction.getFileId()); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/SystemDeleteTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/SystemDeleteTransactionTest.snap new file mode 100644 index 0000000000..3bdb072343 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/SystemDeleteTransactionTest.snap @@ -0,0 +1,8 @@ +org.hiero.sdk.SystemDeleteTransactionTest.shouldSerializeContract=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\nsystem_delete {\n contract_i_d {\n contract_num: 444\n realm_num: 0\n shard_num: 0\n }\n expiration_time {\n seconds: 1554158542\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] + + +org.hiero.sdk.SystemDeleteTransactionTest.shouldSerializeFile=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\nsystem_delete {\n expiration_time {\n seconds: 1554158542\n }\n file_i_d {\n file_num: 444\n realm_num: 0\n shard_num: 0\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/SystemUndeleteTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/SystemUndeleteTransactionTest.java new file mode 100644 index 0000000000..83f9995d58 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/SystemUndeleteTransactionTest.java @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.SystemUndeleteTransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class SystemUndeleteTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerializeFile() { + SnapshotMatcher.expect(spawnTestTransactionFile().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new SystemUndeleteTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + private SystemUndeleteTransaction spawnTestTransactionFile() { + return new SystemUndeleteTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setFileId(FileId.fromString("0.0.444")) + .setMaxTransactionFee(new Hbar(1)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldSerializeContract() { + SnapshotMatcher.expect(spawnTestTransactionContract().toString()).toMatchSnapshot(); + } + + private SystemUndeleteTransaction spawnTestTransactionContract() { + return new SystemUndeleteTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setContractId(ContractId.fromString("0.0.444")) + .setMaxTransactionFee(new Hbar(1)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytesContract() throws Exception { + var tx = spawnTestTransactionContract(); + var tx2 = ScheduleDeleteTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldBytesFile() throws Exception { + var tx = spawnTestTransactionFile(); + var tx2 = SystemUndeleteTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setSystemUndelete(SystemUndeleteTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(SystemUndeleteTransaction.class); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/SystemUndeleteTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/SystemUndeleteTransactionTest.snap new file mode 100644 index 0000000000..b6bc4f8105 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/SystemUndeleteTransactionTest.snap @@ -0,0 +1,8 @@ +org.hiero.sdk.SystemUndeleteTransactionTest.shouldSerializeContract=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\nsystem_undelete {\n contract_i_d {\n contract_num: 444\n realm_num: 0\n shard_num: 0\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] + + +org.hiero.sdk.SystemUndeleteTransactionTest.shouldSerializeFile=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\nsystem_undelete {\n file_i_d {\n file_num: 444\n realm_num: 0\n shard_num: 0\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TestResponse.java b/sdk/src/test/java/org/hiero/sdk/TestResponse.java new file mode 100644 index 0000000000..4dc0684fcc --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TestResponse.java @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.grpc.StatusRuntimeException; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.Response; +import org.hiero.sdk.proto.TransactionGetReceiptResponse; +import org.hiero.sdk.proto.TransactionReceipt; +import org.hiero.sdk.proto.TransactionResponse; + +public class TestResponse { + @Nullable + public final TransactionResponse transactionResponse; + + @Nullable + public final Response queryResponse; + + @Nullable + public final StatusRuntimeException errorResponse; + + private TestResponse( + @Nullable TransactionResponse transactionResponse, + @Nullable Response queryResponse, + @Nullable StatusRuntimeException errorResponse) { + this.transactionResponse = transactionResponse; + this.queryResponse = queryResponse; + this.errorResponse = errorResponse; + } + + public static TestResponse transaction(Status status, Hbar cost) { + return new TestResponse(buildTransactionResponse(status, cost), null, null); + } + + public static TestResponse transaction(Status status) { + return transaction(status, new Hbar(1)); + } + + public static TestResponse transactionOk(Hbar cost) { + return transaction(Status.OK, cost); + } + + public static TestResponse transactionOk() { + return transactionOk(new Hbar(1)); + } + + public static TestResponse query(Response queryResponse) { + return new TestResponse(null, queryResponse, null); + } + + public static TestResponse receipt(Status status) { + var response = Response.newBuilder() + .setTransactionGetReceipt(TransactionGetReceiptResponse.newBuilder() + .setReceipt(TransactionReceipt.newBuilder() + .setStatus(status.code) + .build()) + .build()) + .build(); + return new TestResponse(null, response, null); + } + + public static TestResponse successfulReceipt() { + return receipt(Status.SUCCESS); + } + + public static TestResponse error(StatusRuntimeException exception) { + return new TestResponse(null, null, exception); + } + + public static TransactionResponse buildTransactionResponse(Status status, Hbar cost) { + return TransactionResponse.newBuilder() + .setNodeTransactionPrecheckCode(status.code) + .setCost(cost.toTinybars()) + .build(); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TestServer.java b/sdk/src/test/java/org/hiero/sdk/TestServer.java new file mode 100644 index 0000000000..317f87461a --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TestServer.java @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.grpc.BindableService; +import io.grpc.Server; +import io.grpc.inprocess.InProcessServerBuilder; +import java.io.IOException; +import java.time.Duration; +import java.util.HashMap; +import java.util.concurrent.TimeoutException; + +// TODO: we may want to refactor to separate TestClient from TestServer. +// That way, we can have a client with a network of multiple test servers. +// Maybe we can test load-balancing? + +public class TestServer { + public final Client client; + private final Server[] grpcServers = new Server[2]; + + public TestServer(String name, BindableService... services) throws IOException { + for (int i = 0; i < 2; i++) { + var serverBuilder = InProcessServerBuilder.forName(name + "[" + i + "]"); + for (var service : services) { + serverBuilder.addService(service); + } + grpcServers[i] = serverBuilder.directExecutor().build().start(); + } + + var network = new HashMap(); + network.put("in-process:" + name + "[0]", AccountId.fromString("1.1.1")); + network.put("in-process:" + name + "[1]", AccountId.fromString("2.2.2")); + client = Client.forNetwork(network) + .setNodeMinBackoff(Duration.ofMillis(500)) + .setNodeMaxBackoff(Duration.ofMillis(500)) + .setOperator(AccountId.fromString("2.2.2"), PrivateKey.generate()); + } + + public void close() throws TimeoutException, InterruptedException { + client.close(); + for (var server : grpcServers) { + server.shutdown(); + server.awaitTermination(); + } + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TestService.java b/sdk/src/test/java/org/hiero/sdk/TestService.java new file mode 100644 index 0000000000..c0ab8b7190 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TestService.java @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.grpc.StatusRuntimeException; +import io.grpc.stub.StreamObserver; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.List; +import java.util.Queue; +import javax.annotation.Nullable; +import org.hiero.sdk.proto.Query; +import org.hiero.sdk.proto.Response; +import org.hiero.sdk.proto.Transaction; +import org.hiero.sdk.proto.TransactionResponse; + +public interface TestService { + + private static void respond( + StreamObserver streamObserver, + @Nullable ResponseTypeT normalResponse, + @Nullable StatusRuntimeException errorResponse, + String exceptionString) { + if (normalResponse != null) { + streamObserver.onNext(normalResponse); + streamObserver.onCompleted(); + } else if (errorResponse != null) { + streamObserver.onError(errorResponse); + } else { + throw new IllegalStateException(exceptionString); + } + } + + Buffer getBuffer(); + + default void respondToTransaction( + Transaction request, StreamObserver streamObserver, TestResponse response) { + getBuffer().transactionRequestsReceived.add(request); + + var exceptionString = "TestService tried to respond to transaction with query response"; + respond(streamObserver, response.transactionResponse, response.errorResponse, exceptionString); + } + + default void respondToQuery(Query request, StreamObserver streamObserver, TestResponse response) { + getBuffer().queryRequestsReceived.add(request); + + var exceptionString = "TestService tried to respond to query with transaction response"; + respond(streamObserver, response.queryResponse, response.errorResponse, exceptionString); + } + + default void respondToTransactionFromQueue( + Transaction request, StreamObserver streamObserver) { + respondToTransaction( + request, streamObserver, getBuffer().responsesToSend.remove()); + } + + default void respondToQueryFromQueue(Query request, StreamObserver streamObserver) { + respondToQuery(request, streamObserver, getBuffer().responsesToSend.remove()); + } + + class Buffer { + public final List transactionRequestsReceived = new ArrayList<>(); + public final List queryRequestsReceived = new ArrayList<>(); + public final Queue responsesToSend = new ArrayDeque<>(); + + public Buffer enqueueResponse(TestResponse response) { + responsesToSend.add(response); + return this; + } + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TokenAirdropTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/TokenAirdropTransactionTest.java new file mode 100644 index 0000000000..6f113ad07a --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenAirdropTransactionTest.java @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenAirdropTransactionBody; +import org.hiero.sdk.proto.TokenServiceGrpc; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class TokenAirdropTransactionTest { + private static final PrivateKey privateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + private TokenAirdropTransaction transaction; + + @BeforeEach + public void setUp() { + transaction = new TokenAirdropTransaction(); + } + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new TokenAirdropTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + private TokenAirdropTransaction spawnTestTransaction() { + return new TokenAirdropTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .addTokenTransfer(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.5008"), 400) + .addTokenTransferWithDecimals(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.5006"), -800, 3) + .addTokenTransferWithDecimals(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.5007"), 400, 3) + .addTokenTransfer(TokenId.fromString("0.0.4"), AccountId.fromString("0.0.5008"), 1) + .addTokenTransfer(TokenId.fromString("0.0.4"), AccountId.fromString("0.0.5006"), -1) + .addNftTransfer( + TokenId.fromString("0.0.3").nft(2), + AccountId.fromString("0.0.5008"), + AccountId.fromString("0.0.5007")) + .addNftTransfer( + TokenId.fromString("0.0.3").nft(1), + AccountId.fromString("0.0.5008"), + AccountId.fromString("0.0.5007")) + .addNftTransfer( + TokenId.fromString("0.0.3").nft(3), + AccountId.fromString("0.0.5008"), + AccountId.fromString("0.0.5006")) + .addNftTransfer( + TokenId.fromString("0.0.3").nft(4), + AccountId.fromString("0.0.5007"), + AccountId.fromString("0.0.5006")) + .addNftTransfer( + TokenId.fromString("0.0.2").nft(4), + AccountId.fromString("0.0.5007"), + AccountId.fromString("0.0.5006")) + .addApprovedTokenTransfer(TokenId.fromString("0.0.4"), AccountId.fromString("0.0.5006"), 123) + .addApprovedNftTransfer( + new NftId(TokenId.fromString("0.0.4"), 4), + AccountId.fromString("0.0.5005"), + AccountId.fromString("0.0.5006")) + .setMaxTransactionFee(Hbar.fromTinybars(100_000)) + .freeze() + .sign(privateKey); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = TokenAirdropTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void decimalsMustBeConsistent() { + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { + new TokenAirdropTransaction() + .addTokenTransferWithDecimals(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.8"), 100, 2) + .addTokenTransferWithDecimals(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.7"), -100, 3); + }); + } + + @Test + void canGetDecimals() { + var tx = new TokenAirdropTransaction(); + assertThat(tx.getTokenIdDecimals().get(TokenId.fromString("0.0.5"))).isNull(); + tx.addTokenTransfer(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.8"), 100); + assertThat(tx.getTokenIdDecimals().get(TokenId.fromString("0.0.5"))).isNull(); + tx.addTokenTransferWithDecimals(TokenId.fromString("0.0.5"), AccountId.fromString("0.0.7"), -100, 5); + assertThat(tx.getTokenIdDecimals().get(TokenId.fromString("0.0.5"))).isEqualTo(5); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setTokenAirdrop(TokenAirdropTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(TokenAirdropTransaction.class); + } + + @Test + void testDefaultMaxTransactionFeeIsSet() { + assertEquals( + new Hbar(1), transaction.getDefaultMaxTransactionFee(), "Default max transaction fee should be 1 Hbar"); + } + + @Test + void testAddTokenTransfer() { + TokenId tokenId = new TokenId(0, 0, 123); + AccountId accountId = new AccountId(0, 0, 456); + long value = 1000L; + + transaction.addTokenTransfer(tokenId, accountId, value); + + Map> tokenTransfers = transaction.getTokenTransfers(); + assertTrue(tokenTransfers.containsKey(tokenId)); + assertEquals(1, tokenTransfers.get(tokenId).size()); + assertEquals(value, tokenTransfers.get(tokenId).get(accountId)); + } + + @Test + void testAddApprovedTokenTransfer() { + TokenId tokenId = new TokenId(0, 0, 123); + AccountId accountId = new AccountId(0, 0, 456); + long value = 1000L; + + transaction.addApprovedTokenTransfer(tokenId, accountId, value); + + Map> tokenTransfers = transaction.getTokenTransfers(); + assertTrue(tokenTransfers.containsKey(tokenId)); + assertEquals(1, tokenTransfers.get(tokenId).size()); + assertEquals(value, tokenTransfers.get(tokenId).get(accountId)); + } + + @Test + void testAddNftTransfer() { + NftId nftId = new NftId(new TokenId(0, 0, 123), 1); + AccountId sender = new AccountId(0, 0, 456); + AccountId receiver = new AccountId(0, 0, 789); + + transaction.addNftTransfer(nftId, sender, receiver); + + Map> nftTransfers = transaction.getTokenNftTransfers(); + assertTrue(nftTransfers.containsKey(nftId.tokenId)); + assertEquals(1, nftTransfers.get(nftId.tokenId).size()); + assertEquals(sender, nftTransfers.get(nftId.tokenId).get(0).sender); + assertEquals(receiver, nftTransfers.get(nftId.tokenId).get(0).receiver); + } + + @Test + void testAddApprovedNftTransfer() { + NftId nftId = new NftId(new TokenId(0, 0, 123), 1); + AccountId sender = new AccountId(0, 0, 456); + AccountId receiver = new AccountId(0, 0, 789); + + transaction.addApprovedNftTransfer(nftId, sender, receiver); + + Map> nftTransfers = transaction.getTokenNftTransfers(); + assertTrue(nftTransfers.containsKey(nftId.tokenId)); + assertEquals(1, nftTransfers.get(nftId.tokenId).size()); + assertEquals(sender, nftTransfers.get(nftId.tokenId).get(0).sender); + assertEquals(receiver, nftTransfers.get(nftId.tokenId).get(0).receiver); + } + + @Test + void testGetTokenIdDecimals() { + TokenId tokenId = new TokenId(0, 0, 123); + AccountId accountId = new AccountId(0, 0, 456); + long value = 1000L; + int decimals = 8; + + transaction.addTokenTransferWithDecimals(tokenId, accountId, value, decimals); + + Map decimalsMap = transaction.getTokenIdDecimals(); + assertTrue(decimalsMap.containsKey(tokenId)); + assertEquals(decimals, decimalsMap.get(tokenId)); + } + + @Test + void testBuildTransactionBody() { + TokenAirdropTransactionBody.Builder builder = spawnTestTransaction().build(); + + assertNotNull(builder); + } + + @Test + void testGetMethodDescriptor() { + assertEquals(TokenServiceGrpc.getAirdropTokensMethod(), transaction.getMethodDescriptor()); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TokenAirdropTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/TokenAirdropTransactionTest.snap new file mode 100644 index 0000000000..4f498fbbb5 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenAirdropTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.TokenAirdropTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_airdrop {\n token_transfers {\n nft_transfers {\n receiver_account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n sender_account_i_d {\n account_num: 5007\n realm_num: 0\n shard_num: 0\n }\n serial_number: 4\n }\n token {\n realm_num: 0\n shard_num: 0\n token_num: 2\n }\n }\n token_transfers {\n nft_transfers {\n receiver_account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n sender_account_i_d {\n account_num: 5007\n realm_num: 0\n shard_num: 0\n }\n serial_number: 4\n }\n nft_transfers {\n receiver_account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n sender_account_i_d {\n account_num: 5008\n realm_num: 0\n shard_num: 0\n }\n serial_number: 3\n }\n nft_transfers {\n receiver_account_i_d {\n account_num: 5007\n realm_num: 0\n shard_num: 0\n }\n sender_account_i_d {\n account_num: 5008\n realm_num: 0\n shard_num: 0\n }\n serial_number: 1\n }\n nft_transfers {\n receiver_account_i_d {\n account_num: 5007\n realm_num: 0\n shard_num: 0\n }\n sender_account_i_d {\n account_num: 5008\n realm_num: 0\n shard_num: 0\n }\n serial_number: 2\n }\n token {\n realm_num: 0\n shard_num: 0\n token_num: 3\n }\n }\n token_transfers {\n nft_transfers {\n is_approval: true\n receiver_account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n sender_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n }\n serial_number: 4\n }\n token {\n realm_num: 0\n shard_num: 0\n token_num: 4\n }\n transfers {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n amount: -1\n }\n transfers {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n amount: 123\n is_approval: true\n }\n transfers {\n account_i_d {\n account_num: 5008\n realm_num: 0\n shard_num: 0\n }\n amount: 1\n }\n }\n token_transfers {\n expected_decimals {\n value: 3\n }\n token {\n realm_num: 0\n shard_num: 0\n token_num: 5\n }\n transfers {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n amount: -800\n }\n transfers {\n account_i_d {\n account_num: 5007\n realm_num: 0\n shard_num: 0\n }\n amount: 400\n }\n transfers {\n account_i_d {\n account_num: 5008\n realm_num: 0\n shard_num: 0\n }\n amount: 400\n }\n }\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenAllowanceTest.java b/sdk/src/test/java/org/hiero/sdk/TokenAllowanceTest.java similarity index 78% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/TokenAllowanceTest.java rename to sdk/src/test/java/org/hiero/sdk/TokenAllowanceTest.java index 9a38876103..7a74e8e09a 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenAllowanceTest.java +++ b/sdk/src/test/java/org/hiero/sdk/TokenAllowanceTest.java @@ -1,10 +1,11 @@ -package com.hedera.hashgraph.sdk; - -import org.junit.jupiter.api.Test; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; + public class TokenAllowanceTest { private static final TokenId testTokenId = TokenId.fromString("0.6.9"); @@ -14,8 +15,8 @@ public class TokenAllowanceTest { @Test void constructWithTokenIdOwnerSpenderAmount() { - TokenAllowance tokenAllowance = new TokenAllowance(testTokenId, testOwnerAccountId, testSpenderAccountId, - testAmount); + TokenAllowance tokenAllowance = + new TokenAllowance(testTokenId, testOwnerAccountId, testSpenderAccountId, testAmount); assertThat(tokenAllowance.tokenId).isEqualTo(testTokenId); assertThat(tokenAllowance.ownerAccountId).isEqualTo(testOwnerAccountId); @@ -25,8 +26,8 @@ void constructWithTokenIdOwnerSpenderAmount() { @Test void fromProtobuf() { - var tokenAllowanceProtobuf = new TokenAllowance(testTokenId, testOwnerAccountId, testSpenderAccountId, - testAmount).toProtobuf(); + var tokenAllowanceProtobuf = + new TokenAllowance(testTokenId, testOwnerAccountId, testSpenderAccountId, testAmount).toProtobuf(); var tokenAllowance = TokenAllowance.fromProtobuf(tokenAllowanceProtobuf); assertThat(tokenAllowance.tokenId).isEqualTo(testTokenId); @@ -37,8 +38,8 @@ void fromProtobuf() { @Test void toProtobuf() { - var tokenAllowanceProtobuf = new TokenAllowance(testTokenId, testOwnerAccountId, testSpenderAccountId, - testAmount).toProtobuf(); + var tokenAllowanceProtobuf = + new TokenAllowance(testTokenId, testOwnerAccountId, testSpenderAccountId, testAmount).toProtobuf(); assertTrue(tokenAllowanceProtobuf.hasTokenId()); assertThat(TokenId.fromProtobuf(tokenAllowanceProtobuf.getTokenId())).isEqualTo(testTokenId); @@ -48,6 +49,5 @@ void toProtobuf() { assertTrue(tokenAllowanceProtobuf.hasSpender()); assertThat(AccountId.fromProtobuf(tokenAllowanceProtobuf.getSpender())).isEqualTo(testSpenderAccountId); - } } diff --git a/sdk/src/test/java/org/hiero/sdk/TokenAssociateTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/TokenAssociateTransactionTest.java new file mode 100644 index 0000000000..cbf6baf635 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenAssociateTransactionTest.java @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenAssociateTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TokenAssociateTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + private static final AccountId accountId = AccountId.fromString("1.2.3"); + private static final List tokenIds = + List.of(TokenId.fromString("4.5.6"), TokenId.fromString("7.8.9"), TokenId.fromString("10.11.12")); + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new TokenAssociateTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + private TokenAssociateTransaction spawnTestTransaction() { + return new TokenAssociateTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setAccountId(AccountId.fromString("0.0.222")) + .setTokenIds(Collections.singletonList(TokenId.fromString("0.0.666"))) + .setMaxTransactionFee(new Hbar(1)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = TokenAssociateTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setTokenAssociate(TokenAssociateTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(TokenAssociateTransaction.class); + } + + @Test + void constructTokenDeleteTransactionFromTransactionBodyProtobuf() { + var transactionBody = TokenAssociateTransactionBody.newBuilder() + .addAllTokens(tokenIds.stream().map(TokenId::toProtobuf).toList()) + .setAccount(accountId.toProtobuf()) + .build(); + var txBody = + TransactionBody.newBuilder().setTokenAssociate(transactionBody).build(); + var tokenAssociateTransaction = new TokenAssociateTransaction(txBody); + + assertThat(tokenAssociateTransaction.getAccountId()).isEqualTo(accountId); + assertThat(tokenAssociateTransaction.getTokenIds()).hasSize(tokenIds.size()); + } + + @Test + void getSetAccountId() { + var transaction = new TokenAssociateTransaction().setAccountId(accountId); + assertThat(transaction.getAccountId()).isEqualTo(accountId); + } + + @Test + void getSetAccountIdFrozen() { + var transaction = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> transaction.setAccountId(accountId)); + } + + @Test + void getSetTokenIds() { + var transaction = new TokenAssociateTransaction().setTokenIds(tokenIds); + assertThat(transaction.getTokenIds()).isEqualTo(tokenIds); + } + + @Test + void getSetTokenIdFrozen() { + var transaction = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> transaction.setTokenIds(tokenIds)); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TokenAssociateTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/TokenAssociateTransactionTest.snap new file mode 100644 index 0000000000..99b5e93e0f --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenAssociateTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.TokenAssociateTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_associate {\n account {\n account_num: 222\n realm_num: 0\n shard_num: 0\n }\n tokens {\n realm_num: 0\n shard_num: 0\n token_num: 666\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenAssociationTest.java b/sdk/src/test/java/org/hiero/sdk/TokenAssociationTest.java similarity index 86% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/TokenAssociationTest.java rename to sdk/src/test/java/org/hiero/sdk/TokenAssociationTest.java index d2adbfee70..07e0ed017e 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenAssociationTest.java +++ b/sdk/src/test/java/org/hiero/sdk/TokenAssociationTest.java @@ -1,4 +1,8 @@ -package com.hedera.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.protobuf.InvalidProtocolBufferException; import io.github.jsonSnapshot.SnapshotMatcher; @@ -6,10 +10,6 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertTrue; - - public class TokenAssociationTest { private static final AccountId testAccountId = AccountId.fromString("4.2.0"); private static final TokenId testTokenId = TokenId.fromString("0.6.9"); @@ -25,10 +25,7 @@ public static void afterAll() { } TokenAssociation spawnTokenAssociationExample() { - return new TokenAssociation( - TokenId.fromString("1.2.3"), - AccountId.fromString("4.5.6") - ); + return new TokenAssociation(TokenId.fromString("1.2.3"), AccountId.fromString("4.5.6")); } @Test @@ -37,8 +34,9 @@ void shouldSerializeAccount() throws Exception { byte[] tokenAssociationBytes = originalTokenAssociation.toBytes(); var copyTokenAssociation = TokenAssociation.fromBytes(tokenAssociationBytes); assertThat(copyTokenAssociation.toString().replaceAll("@[A-Za-z0-9]+", "")) - .isEqualTo(originalTokenAssociation.toString().replaceAll("@[A-Za-z0-9]+", "")); - SnapshotMatcher.expect(originalTokenAssociation.toString().replaceAll("@[A-Za-z0-9]+", "")).toMatchSnapshot(); + .isEqualTo(originalTokenAssociation.toString().replaceAll("@[A-Za-z0-9]+", "")); + SnapshotMatcher.expect(originalTokenAssociation.toString().replaceAll("@[A-Za-z0-9]+", "")) + .toMatchSnapshot(); } @Test @@ -59,12 +57,9 @@ void toProtobuf() { assertThat(tokenAssociationProtobuf.getAccountId().getRealmNum()).isEqualTo(testAccountId.realm); assertThat(tokenAssociationProtobuf.getAccountId().getAccountNum()).isEqualTo(testAccountId.num); assertTrue(tokenAssociationProtobuf.hasTokenId()); - assertThat(tokenAssociationProtobuf.getTokenId().getShardNum()).isEqualTo( - testTokenId.shard); - assertThat(tokenAssociationProtobuf.getTokenId().getRealmNum()).isEqualTo( - testTokenId.realm); - assertThat(tokenAssociationProtobuf.getTokenId().getTokenNum()).isEqualTo( - testTokenId.num); + assertThat(tokenAssociationProtobuf.getTokenId().getShardNum()).isEqualTo(testTokenId.shard); + assertThat(tokenAssociationProtobuf.getTokenId().getRealmNum()).isEqualTo(testTokenId.realm); + assertThat(tokenAssociationProtobuf.getTokenId().getTokenNum()).isEqualTo(testTokenId.num); } @Test diff --git a/sdk/src/test/java/org/hiero/sdk/TokenAssociationTest.snap b/sdk/src/test/java/org/hiero/sdk/TokenAssociationTest.snap new file mode 100644 index 0000000000..611c3037cd --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenAssociationTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.TokenAssociationTest.shouldSerializeAccount=[ + "TokenAssociation{tokenId=1.2.3, accountId=4.5.6}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TokenBurnTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/TokenBurnTransactionTest.java new file mode 100644 index 0000000000..671d0517e5 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenBurnTransactionTest.java @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenBurnTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TokenBurnTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + private static final TokenId testTokenId = TokenId.fromString("4.2.0"); + private static final long testAmount = 69L; + private static final List testSerials = Collections.singletonList(420L); + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerializeFungible() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + private TokenBurnTransaction spawnTestTransaction() { + return new TokenBurnTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setTokenId(testTokenId) + .setAmount(testAmount) + .setMaxTransactionFee(new Hbar(1)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new TokenBurnTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldSerializeNft() { + SnapshotMatcher.expect(spawnTestTransactionNft().toString()).toMatchSnapshot(); + } + + private TokenBurnTransaction spawnTestTransactionNft() { + return new TokenBurnTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setTokenId(testTokenId) + .setSerials(testSerials) + .setMaxTransactionFee(new Hbar(1)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytesFungible() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = TokenBurnTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldBytesNft() throws Exception { + var tx = spawnTestTransactionNft(); + var tx2 = TokenBurnTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setTokenBurn(TokenBurnTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(TokenBurnTransaction.class); + } + + @Test + void constructTokenBurnTransactionFromTransactionBodyProtobuf() { + var transactionBody = TokenBurnTransactionBody.newBuilder() + .setToken(testTokenId.toProtobuf()) + .setAmount(testAmount) + .addAllSerialNumbers(testSerials) + .build(); + + var tx = TransactionBody.newBuilder().setTokenBurn(transactionBody).build(); + var tokenBurnTransaction = new TokenBurnTransaction(tx); + + assertThat(tokenBurnTransaction.getTokenId()).isEqualTo(testTokenId); + assertThat(tokenBurnTransaction.getAmount()).isEqualTo(testAmount); + assertThat(tokenBurnTransaction.getSerials()).isEqualTo(testSerials); + } + + @Test + void getSetTokenId() { + var tokenBurnTransaction = new TokenBurnTransaction().setTokenId(testTokenId); + assertThat(tokenBurnTransaction.getTokenId()).isEqualTo(testTokenId); + } + + @Test + void getSetTokenIdFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setTokenId(testTokenId)); + } + + @Test + void getSetAmount() { + var tokenBurnTransaction = new TokenBurnTransaction().setAmount(testAmount); + assertThat(tokenBurnTransaction.getAmount()).isEqualTo(testAmount); + } + + @Test + void getSetAmountFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setAmount(testAmount)); + } + + @Test + void getSetSerials() { + var tokenBurnTransaction = new TokenBurnTransaction().setSerials(testSerials); + assertThat(tokenBurnTransaction.getSerials()).isEqualTo(testSerials); + } + + @Test + void getSetSerialsFrozen() { + var tx = spawnTestTransactionNft(); + assertThrows(IllegalStateException.class, () -> tx.setSerials(testSerials)); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TokenBurnTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/TokenBurnTransactionTest.snap new file mode 100644 index 0000000000..6cc4287227 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenBurnTransactionTest.snap @@ -0,0 +1,8 @@ +org.hiero.sdk.TokenBurnTransactionTest.shouldSerializeFungible=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_burn {\n amount: 69\n token {\n realm_num: 2\n shard_num: 4\n token_num: 0\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] + + +org.hiero.sdk.TokenBurnTransactionTest.shouldSerializeNft=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_burn {\n amount: 0\n serial_numbers: 420\n token {\n realm_num: 2\n shard_num: 4\n token_num: 0\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TokenCancelAirdropTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/TokenCancelAirdropTransactionTest.java new file mode 100644 index 0000000000..0c1d656268 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenCancelAirdropTransactionTest.java @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenCancelAirdropTransactionBody; +import org.hiero.sdk.proto.TokenServiceGrpc; +import org.hiero.sdk.proto.TransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class TokenCancelAirdropTransactionTest { + + private static final PrivateKey privateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + final Instant validStart = Instant.ofEpochSecond(1554158542); + private TokenCancelAirdropTransaction transaction; + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + private TokenCancelAirdropTransaction spawnTestTransaction() { + List pendingAirdropIds = new ArrayList<>(); + pendingAirdropIds.add( + new PendingAirdropId(new AccountId(0, 0, 457), new AccountId(0, 0, 456), new TokenId(0, 0, 123))); + pendingAirdropIds.add(new PendingAirdropId( + new AccountId(0, 0, 457), new AccountId(0, 0, 456), new NftId(new TokenId(0, 0, 1234), 123))); + + return new TokenCancelAirdropTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setMaxTransactionFee(Hbar.fromTinybars(100_000)) + .setPendingAirdropIds(pendingAirdropIds) + .freeze() + .sign(privateKey); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new TokenCancelAirdropTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @BeforeEach + public void setUp() { + transaction = new TokenCancelAirdropTransaction(); + } + + @Test + void testConstructorSetsDefaultMaxTransactionFee() { + Assertions.assertEquals(Hbar.from(1), transaction.getDefaultMaxTransactionFee()); + } + + @Test + void testGetAndSetPendingAirdropIds() { + List pendingAirdropIds = new ArrayList<>(); + pendingAirdropIds.add( + new PendingAirdropId(new AccountId(0, 0, 457), new AccountId(0, 0, 456), new TokenId(0, 0, 123))); + pendingAirdropIds.add(new PendingAirdropId( + new AccountId(0, 0, 457), new AccountId(0, 0, 456), new NftId(new TokenId(0, 0, 1234), 123))); + + transaction.setPendingAirdropIds(pendingAirdropIds); + + Assertions.assertEquals(pendingAirdropIds, transaction.getPendingAirdropIds()); + } + + @Test + void testSetPendingAirdropIdsNullThrowsException() { + Assertions.assertThrows(NullPointerException.class, () -> transaction.setPendingAirdropIds(null)); + } + + @Test + void testClearPendingAirdropIds() { + List pendingAirdropIds = new ArrayList<>(); + PendingAirdropId pendingAirdropId = + new PendingAirdropId(new AccountId(0, 0, 457), new AccountId(0, 0, 456), new TokenId(0, 0, 123)); + pendingAirdropIds.add(pendingAirdropId); + + transaction.setPendingAirdropIds(pendingAirdropIds); + transaction.clearPendingAirdropIds(); + + Assertions.assertTrue(transaction.getPendingAirdropIds().isEmpty()); + } + + @Test + void testAddAllPendingAirdrops() { + PendingAirdropId pendingAirdropId1 = + new PendingAirdropId(new AccountId(0, 0, 457), new AccountId(0, 0, 456), new TokenId(0, 0, 123)); + PendingAirdropId pendingAirdropId2 = + new PendingAirdropId(new AccountId(0, 0, 458), new AccountId(0, 0, 459), new TokenId(0, 0, 123)); + + transaction.addPendingAirdrop(pendingAirdropId1); + transaction.addPendingAirdrop(pendingAirdropId2); + + Assertions.assertEquals(2, transaction.getPendingAirdropIds().size()); + Assertions.assertTrue(transaction.getPendingAirdropIds().contains(pendingAirdropId1)); + Assertions.assertTrue(transaction.getPendingAirdropIds().contains(pendingAirdropId2)); + } + + @Test + void testAddAllPendingAirdropsNullThrowsException() { + Assertions.assertThrows(NullPointerException.class, () -> transaction.addPendingAirdrop(null)); + } + + @Test + void testBuildTransactionBody() { + PendingAirdropId pendingAirdropId = new PendingAirdropId( + new AccountId(0, 0, 457), new AccountId(0, 0, 456), new NftId(new TokenId(0, 0, 1234), 123)); + transaction.addPendingAirdrop(pendingAirdropId); + + TokenCancelAirdropTransactionBody.Builder builder = transaction.build(); + Assertions.assertEquals(1, builder.getPendingAirdropsCount()); + Assertions.assertEquals(pendingAirdropId.toProtobuf(), builder.getPendingAirdrops(0)); + } + + @Test + void testGetMethodDescriptor() { + Assertions.assertEquals(TokenServiceGrpc.getCancelAirdropMethod(), transaction.getMethodDescriptor()); + } + + @Test + void testOnFreeze() { + var bodyBuilder = TransactionBody.newBuilder(); + transaction.onFreeze(bodyBuilder); + + Assertions.assertTrue(bodyBuilder.hasTokenCancelAirdrop()); + } + + @Test + void testOnScheduled() { + SchedulableTransactionBody.Builder scheduled = SchedulableTransactionBody.newBuilder(); + transaction.onScheduled(scheduled); + + Assertions.assertTrue(scheduled.hasTokenCancelAirdrop()); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TokenCancelAirdropTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/TokenCancelAirdropTransactionTest.snap new file mode 100644 index 0000000000..374ae1b10c --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenCancelAirdropTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.TokenCancelAirdropTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_cancel_airdrop {\n pending_airdrops {\n fungible_token_type {\n realm_num: 0\n shard_num: 0\n token_num: 123\n }\n receiver_id {\n account_num: 456\n realm_num: 0\n shard_num: 0\n }\n sender_id {\n account_num: 457\n realm_num: 0\n shard_num: 0\n }\n }\n pending_airdrops {\n non_fungible_token {\n serial_number: 123\n token_i_d {\n realm_num: 0\n shard_num: 0\n token_num: 1234\n }\n }\n receiver_id {\n account_num: 456\n realm_num: 0\n shard_num: 0\n }\n sender_id {\n account_num: 457\n realm_num: 0\n shard_num: 0\n }\n }\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TokenClaimAirdropTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/TokenClaimAirdropTransactionTest.java new file mode 100644 index 0000000000..f6553c23df --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenClaimAirdropTransactionTest.java @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenClaimAirdropTransactionBody; +import org.hiero.sdk.proto.TokenServiceGrpc; +import org.hiero.sdk.proto.TransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class TokenClaimAirdropTransactionTest { + + private static final PrivateKey privateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + final Instant validStart = Instant.ofEpochSecond(1554158542); + private TokenClaimAirdropTransaction transaction; + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + private TokenClaimAirdropTransaction spawnTestTransaction() { + List pendingAirdropIds = new ArrayList<>(); + pendingAirdropIds.add( + new PendingAirdropId(new AccountId(0, 0, 457), new AccountId(0, 0, 456), new TokenId(0, 0, 123))); + pendingAirdropIds.add(new PendingAirdropId( + new AccountId(0, 0, 457), new AccountId(0, 0, 456), new NftId(new TokenId(0, 0, 1234), 123))); + + return new TokenClaimAirdropTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setMaxTransactionFee(Hbar.fromTinybars(100_000)) + .setPendingAirdropIds(pendingAirdropIds) + .freeze() + .sign(privateKey); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new TokenClaimAirdropTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @BeforeEach + public void setUp() { + transaction = new TokenClaimAirdropTransaction(); + } + + @Test + void testConstructorSetsDefaultMaxTransactionFee() { + Assertions.assertEquals(Hbar.from(1), transaction.getDefaultMaxTransactionFee()); + } + + @Test + void testGetAndSetPendingAirdropIds() { + List pendingAirdropIds = new ArrayList<>(); + pendingAirdropIds.add( + new PendingAirdropId(new AccountId(0, 0, 457), new AccountId(0, 0, 456), new TokenId(0, 0, 123))); + pendingAirdropIds.add(new PendingAirdropId( + new AccountId(0, 0, 457), new AccountId(0, 0, 456), new NftId(new TokenId(0, 0, 1234), 123))); + + transaction.setPendingAirdropIds(pendingAirdropIds); + + Assertions.assertEquals(pendingAirdropIds, transaction.getPendingAirdropIds()); + } + + @Test + void testSetPendingAirdropIdsNullThrowsException() { + Assertions.assertThrows(NullPointerException.class, () -> transaction.setPendingAirdropIds(null)); + } + + @Test + void testClearPendingAirdropIds() { + List pendingAirdropIds = new ArrayList<>(); + PendingAirdropId pendingAirdropId = + new PendingAirdropId(new AccountId(0, 0, 457), new AccountId(0, 0, 456), new TokenId(0, 0, 123)); + pendingAirdropIds.add(pendingAirdropId); + + transaction.setPendingAirdropIds(pendingAirdropIds); + transaction.clearPendingAirdropIds(); + + Assertions.assertTrue(transaction.getPendingAirdropIds().isEmpty()); + } + + @Test + void testAddAllPendingAirdrops() { + PendingAirdropId pendingAirdropId1 = + new PendingAirdropId(new AccountId(0, 0, 457), new AccountId(0, 0, 456), new TokenId(0, 0, 123)); + PendingAirdropId pendingAirdropId2 = + new PendingAirdropId(new AccountId(0, 0, 458), new AccountId(0, 0, 459), new TokenId(0, 0, 123)); + + transaction.addPendingAirdrop(pendingAirdropId1); + transaction.addPendingAirdrop(pendingAirdropId2); + + Assertions.assertEquals(2, transaction.getPendingAirdropIds().size()); + Assertions.assertTrue(transaction.getPendingAirdropIds().contains(pendingAirdropId1)); + Assertions.assertTrue(transaction.getPendingAirdropIds().contains(pendingAirdropId2)); + } + + @Test + void testAddAllPendingAirdropsNullThrowsException() { + Assertions.assertThrows(NullPointerException.class, () -> transaction.addPendingAirdrop(null)); + } + + @Test + void testBuildTransactionBody() { + PendingAirdropId pendingAirdropId = new PendingAirdropId( + new AccountId(0, 0, 457), new AccountId(0, 0, 456), new NftId(new TokenId(0, 0, 1234), 123)); + transaction.addPendingAirdrop(pendingAirdropId); + + TokenClaimAirdropTransactionBody.Builder builder = transaction.build(); + Assertions.assertEquals(1, builder.getPendingAirdropsCount()); + Assertions.assertEquals(pendingAirdropId.toProtobuf(), builder.getPendingAirdrops(0)); + } + + @Test + void testGetMethodDescriptor() { + Assertions.assertEquals(TokenServiceGrpc.getClaimAirdropMethod(), transaction.getMethodDescriptor()); + } + + @Test + void testOnFreeze() { + var bodyBuilder = TransactionBody.newBuilder(); + transaction.onFreeze(bodyBuilder); + + Assertions.assertTrue(bodyBuilder.hasTokenClaimAirdrop()); + } + + @Test + void testOnScheduled() { + SchedulableTransactionBody.Builder scheduled = SchedulableTransactionBody.newBuilder(); + transaction.onScheduled(scheduled); + + Assertions.assertTrue(scheduled.hasTokenClaimAirdrop()); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TokenClaimAirdropTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/TokenClaimAirdropTransactionTest.snap new file mode 100644 index 0000000000..76b6ecc17e --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenClaimAirdropTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.TokenClaimAirdropTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_claim_airdrop {\n pending_airdrops {\n fungible_token_type {\n realm_num: 0\n shard_num: 0\n token_num: 123\n }\n receiver_id {\n account_num: 456\n realm_num: 0\n shard_num: 0\n }\n sender_id {\n account_num: 457\n realm_num: 0\n shard_num: 0\n }\n }\n pending_airdrops {\n non_fungible_token {\n serial_number: 123\n token_i_d {\n realm_num: 0\n shard_num: 0\n token_num: 1234\n }\n }\n receiver_id {\n account_num: 456\n realm_num: 0\n shard_num: 0\n }\n sender_id {\n account_num: 457\n realm_num: 0\n shard_num: 0\n }\n }\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TokenCreateTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/TokenCreateTransactionTest.java new file mode 100644 index 0000000000..d1d466e330 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenCreateTransactionTest.java @@ -0,0 +1,539 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.google.common.collect.Iterables; +import com.google.protobuf.ByteString; +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Duration; +import java.time.Instant; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.Timestamp; +import org.hiero.sdk.proto.TokenCreateTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TokenCreateTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + private static final PublicKey testAdminKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e11") + .getPublicKey(); + private static final PublicKey testKycKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e12") + .getPublicKey(); + private static final PublicKey testFreezeKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e13") + .getPublicKey(); + private static final PublicKey testWipeKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e14") + .getPublicKey(); + private static final PublicKey testSupplyKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e15") + .getPublicKey(); + private static final PublicKey testFeeScheduleKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e16") + .getPublicKey(); + private static final PublicKey testPauseKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e17") + .getPublicKey(); + private static final PublicKey testMetadataKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e18") + .getPublicKey(); + private static final AccountId testTreasuryAccountId = AccountId.fromString("7.7.7"); + private static final AccountId testAutoRenewAccountId = AccountId.fromString("8.8.8"); + private static final long testInitialSupply = 30; + private static final long testMaxSupply = 500; + private static final int testDecimals = 3; + private static final boolean testFreezeDefault = true; + private static final String testTokenName = "test name"; + private static final String testTokenSymbol = "test symbol"; + private static final String testTokenMemo = "test memo"; + private static final Duration testAutoRenewPeriod = Duration.ofHours(10); + private static final Instant testExpirationTime = Instant.now(); + private static final List testCustomFees = Collections.singletonList(new CustomFixedFee() + .setFeeCollectorAccountId(AccountId.fromString("0.0.543")) + .setAmount(3) + .setDenominatingTokenId(TokenId.fromString("4.3.2"))); + private static final byte[] testMetadata = new byte[] {1, 2, 3, 4, 5}; + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerializeFungible() { + SnapshotMatcher.expect(spawnTestTransactionFungible().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new TokenCreateTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldSerializeNft() { + SnapshotMatcher.expect(spawnTestTransactionNft().toString()).toMatchSnapshot(); + } + + private TokenCreateTransaction spawnTestTransactionFungible() { + return new TokenCreateTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setInitialSupply(testInitialSupply) + .setFeeScheduleKey(testFeeScheduleKey) + .setSupplyKey(testSupplyKey) + .setAdminKey(testAdminKey) + .setAutoRenewAccountId(testAutoRenewAccountId) + .setAutoRenewPeriod(testAutoRenewPeriod) + .setDecimals(testDecimals) + .setFreezeDefault(testFreezeDefault) + .setFreezeKey(testFreezeKey) + .setWipeKey(testWipeKey) + .setTokenSymbol(testTokenSymbol) + .setKycKey(testKycKey) + .setPauseKey(testPauseKey) + .setMetadataKey(testMetadataKey) + .setExpirationTime(validStart) + .setTreasuryAccountId(testTreasuryAccountId) + .setTokenName(testTokenName) + .setTokenMemo(testTokenMemo) + .setCustomFees(testCustomFees) + .setMaxTransactionFee(new Hbar(1)) + .setTokenMetadata(testMetadata) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytesFungible() throws Exception { + var tx = spawnTestTransactionFungible(); + var tx2 = TokenCreateTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + private TokenCreateTransaction spawnTestTransactionNft() { + return new TokenCreateTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setFeeScheduleKey(testFeeScheduleKey) + .setSupplyKey(testSupplyKey) + .setMaxSupply(testMaxSupply) + .setAdminKey(testAdminKey) + .setAutoRenewAccountId(testAutoRenewAccountId) + .setAutoRenewPeriod(testAutoRenewPeriod) + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setSupplyType(TokenSupplyType.FINITE) + .setFreezeKey(testFreezeKey) + .setWipeKey(testWipeKey) + .setTokenSymbol(testTokenSymbol) + .setKycKey(testKycKey) + .setPauseKey(testPauseKey) + .setMetadataKey(testMetadataKey) + .setExpirationTime(validStart) + .setTreasuryAccountId(testTreasuryAccountId) + .setTokenName(testTokenName) + .setTokenMemo(testTokenMemo) + .setMaxTransactionFee(new Hbar(1)) + .setTokenMetadata(testMetadata) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytesNft() throws Exception { + var tx = spawnTestTransactionNft(); + var tx2 = TokenCreateTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setTokenCreation(TokenCreateTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(TokenCreateTransaction.class); + } + + @Test + void constructTokenCreateTransactionFungibleFromTransactionBodyProtobuf() { + var transactionBody = TokenCreateTransactionBody.newBuilder() + .setInitialSupply(testInitialSupply) + .setFeeScheduleKey(testFeeScheduleKey.toProtobufKey()) + .setSupplyKey(testSupplyKey.toProtobufKey()) + .setAdminKey(testAdminKey.toProtobufKey()) + .setAutoRenewAccount(testAutoRenewAccountId.toProtobuf()) + .setAutoRenewPeriod(org.hiero.sdk.proto.Duration.newBuilder() + .setSeconds(testAutoRenewPeriod.toSeconds()) + .build()) + .setExpiry(Timestamp.newBuilder() + .setSeconds(testExpirationTime.getEpochSecond()) + .build()) + .setDecimals(testDecimals) + .setFreezeDefault(testFreezeDefault) + .setFreezeKey(testFreezeKey.toProtobufKey()) + .setWipeKey(testWipeKey.toProtobufKey()) + .setSymbol(testTokenSymbol) + .setKycKey(testKycKey.toProtobufKey()) + .setPauseKey(testPauseKey.toProtobufKey()) + .setMetadataKey(testMetadataKey.toProtobufKey()) + .setExpiry(Timestamp.newBuilder().setSeconds(testExpirationTime.getEpochSecond())) + .setTreasury(testTreasuryAccountId.toProtobuf()) + .setName(testTokenName) + .setMemo(testTokenMemo) + .addCustomFees(Iterables.getLast(testCustomFees).toProtobuf()) + .setMetadata(ByteString.copyFrom(testMetadata)) + .build(); + + var tx = TransactionBody.newBuilder().setTokenCreation(transactionBody).build(); + var tokenCreateTransaction = new TokenCreateTransaction(tx); + + assertThat(tokenCreateTransaction.getFeeScheduleKey()).isEqualTo(testFeeScheduleKey); + assertThat(tokenCreateTransaction.getSupplyKey()).isEqualTo(testSupplyKey); + assertThat(tokenCreateTransaction.getAdminKey()).isEqualTo(testAdminKey); + assertThat(tokenCreateTransaction.getAutoRenewAccountId()).isEqualTo(testAutoRenewAccountId); + assertThat(tokenCreateTransaction.getAutoRenewPeriod().toSeconds()).isEqualTo(testAutoRenewPeriod.toSeconds()); + assertThat(tokenCreateTransaction.getDecimals()).isEqualTo(testDecimals); + assertThat(tokenCreateTransaction.getFreezeDefault()).isEqualTo(testFreezeDefault); + assertThat(tokenCreateTransaction.getFreezeKey()).isEqualTo(testFreezeKey); + assertThat(tokenCreateTransaction.getWipeKey()).isEqualTo(testWipeKey); + assertThat(tokenCreateTransaction.getTokenSymbol()).isEqualTo(testTokenSymbol); + assertThat(tokenCreateTransaction.getKycKey()).isEqualTo(testKycKey); + assertThat(tokenCreateTransaction.getPauseKey()).isEqualTo(testPauseKey); + assertThat(tokenCreateTransaction.getMetadataKey()).isEqualTo(testMetadataKey); + assertThat(tokenCreateTransaction.getExpirationTime().getEpochSecond()) + .isEqualTo(testExpirationTime.getEpochSecond()); + assertThat(tokenCreateTransaction.getTreasuryAccountId()).isEqualTo(testTreasuryAccountId); + assertThat(tokenCreateTransaction.getTokenName()).isEqualTo(testTokenName); + assertThat(tokenCreateTransaction.getTokenMemo()).isEqualTo(testTokenMemo); + assertThat(tokenCreateTransaction.getTokenType()).isEqualTo(TokenType.FUNGIBLE_COMMON); + assertThat(Iterables.getLast(tokenCreateTransaction.getCustomFees()).toBytes()) + .isEqualTo(Iterables.getLast(testCustomFees).toBytes()); + assertThat(tokenCreateTransaction.getTokenMetadata()).isEqualTo(testMetadata); + } + + @Test + void constructTokenCreateTransactionNftFromTransactionBodyProtobuf() { + var transactionBody = TokenCreateTransactionBody.newBuilder() + .setFeeScheduleKey(testFeeScheduleKey.toProtobufKey()) + .setSupplyKey(testSupplyKey.toProtobufKey()) + .setMaxSupply(testMaxSupply) + .setAdminKey(testAdminKey.toProtobufKey()) + .setAutoRenewAccount(testAutoRenewAccountId.toProtobuf()) + .setAutoRenewPeriod(org.hiero.sdk.proto.Duration.newBuilder() + .setSeconds(testAutoRenewPeriod.toSeconds()) + .build()) + .setExpiry(Timestamp.newBuilder() + .setSeconds(testExpirationTime.getEpochSecond()) + .build()) + .setTokenType(org.hiero.sdk.proto.TokenType.NON_FUNGIBLE_UNIQUE) + .setSupplyType(org.hiero.sdk.proto.TokenSupplyType.FINITE) + .setFreezeKey(testFreezeKey.toProtobufKey()) + .setWipeKey(testWipeKey.toProtobufKey()) + .setSymbol(testTokenSymbol) + .setKycKey(testKycKey.toProtobufKey()) + .setPauseKey(testPauseKey.toProtobufKey()) + .setMetadataKey(testMetadataKey.toProtobufKey()) + .setExpiry(Timestamp.newBuilder().setSeconds(testExpirationTime.getEpochSecond())) + .setTreasury(testTreasuryAccountId.toProtobuf()) + .setName(testTokenName) + .setMemo(testTokenMemo) + .build(); + + var tx = TransactionBody.newBuilder().setTokenCreation(transactionBody).build(); + var tokenCreateTransaction = new TokenCreateTransaction(tx); + + assertThat(tokenCreateTransaction.getFeeScheduleKey()).isEqualTo(testFeeScheduleKey); + assertThat(tokenCreateTransaction.getSupplyKey()).isEqualTo(testSupplyKey); + assertThat(tokenCreateTransaction.getMaxSupply()).isEqualTo(testMaxSupply); + assertThat(tokenCreateTransaction.getAdminKey()).isEqualTo(testAdminKey); + assertThat(tokenCreateTransaction.getAutoRenewAccountId()).isEqualTo(testAutoRenewAccountId); + assertThat(tokenCreateTransaction.getAutoRenewPeriod().toSeconds()).isEqualTo(testAutoRenewPeriod.toSeconds()); + assertThat(tokenCreateTransaction.getTokenType()).isEqualTo(TokenType.NON_FUNGIBLE_UNIQUE); + assertThat(tokenCreateTransaction.getSupplyType()).isEqualTo(TokenSupplyType.FINITE); + assertThat(tokenCreateTransaction.getFreezeKey()).isEqualTo(testFreezeKey); + assertThat(tokenCreateTransaction.getWipeKey()).isEqualTo(testWipeKey); + assertThat(tokenCreateTransaction.getTokenSymbol()).isEqualTo(testTokenSymbol); + assertThat(tokenCreateTransaction.getKycKey()).isEqualTo(testKycKey); + assertThat(tokenCreateTransaction.getPauseKey()).isEqualTo(testPauseKey); + assertThat(tokenCreateTransaction.getMetadataKey()).isEqualTo(testMetadataKey); + assertThat(tokenCreateTransaction.getExpirationTime().getEpochSecond()) + .isEqualTo(testExpirationTime.getEpochSecond()); + assertThat(tokenCreateTransaction.getTreasuryAccountId()).isEqualTo(testTreasuryAccountId); + assertThat(tokenCreateTransaction.getTokenName()).isEqualTo(testTokenName); + assertThat(tokenCreateTransaction.getTokenMemo()).isEqualTo(testTokenMemo); + } + + @Test + void getSetName() { + var tokenCreateTransaction = new TokenCreateTransaction().setTokenName(testTokenName); + assertThat(tokenCreateTransaction.getTokenName()).isEqualTo(testTokenName); + } + + @Test + void getSetNameFrozen() { + var tx = spawnTestTransactionFungible(); + assertThrows(IllegalStateException.class, () -> tx.setTokenName(testTokenName)); + } + + @Test + void getSetSymbol() { + var tokenCreateTransaction = new TokenCreateTransaction().setTokenSymbol(testTokenSymbol); + assertThat(tokenCreateTransaction.getTokenSymbol()).isEqualTo(testTokenSymbol); + } + + @Test + void getSetSymbolFrozen() { + var tx = spawnTestTransactionFungible(); + assertThrows(IllegalStateException.class, () -> tx.setTokenSymbol(testTokenSymbol)); + } + + @Test + void getSetDecimals() { + var tokenCreateTransaction = new TokenCreateTransaction().setDecimals(testDecimals); + assertThat(tokenCreateTransaction.getDecimals()).isEqualTo(testDecimals); + } + + @Test + void getSetDecimalsFrozen() { + var tx = spawnTestTransactionFungible(); + assertThrows(IllegalStateException.class, () -> tx.setDecimals(testDecimals)); + } + + @Test + void getSetInitialSupply() { + var tokenCreateTransaction = new TokenCreateTransaction().setInitialSupply(testInitialSupply); + assertThat(tokenCreateTransaction.getInitialSupply()).isEqualTo(testInitialSupply); + } + + @Test + void getSetInitialSupplyFrozen() { + var tx = spawnTestTransactionFungible(); + assertThrows(IllegalStateException.class, () -> tx.setInitialSupply(testInitialSupply)); + } + + @Test + void getSetTreasuryAccountId() { + var tokenCreateTransaction = new TokenCreateTransaction().setTreasuryAccountId(testTreasuryAccountId); + assertThat(tokenCreateTransaction.getTreasuryAccountId()).isEqualTo(testTreasuryAccountId); + } + + @Test + void getSetTreasuryAccountIdFrozen() { + var tx = spawnTestTransactionFungible(); + assertThrows(IllegalStateException.class, () -> tx.setTreasuryAccountId(testTreasuryAccountId)); + } + + @Test + void getSetAdminKey() { + var tokenCreateTransaction = new TokenCreateTransaction().setAdminKey(testAdminKey); + assertThat(tokenCreateTransaction.getAdminKey()).isEqualTo(testAdminKey); + } + + @Test + void getSetAdminKeyFrozen() { + var tx = spawnTestTransactionFungible(); + assertThrows(IllegalStateException.class, () -> tx.setAdminKey(testAdminKey)); + } + + @Test + void getSetKycKey() { + var tokenCreateTransaction = new TokenCreateTransaction().setKycKey(testKycKey); + assertThat(tokenCreateTransaction.getKycKey()).isEqualTo(testKycKey); + } + + @Test + void getSetKycKeyFrozen() { + var tx = spawnTestTransactionFungible(); + assertThrows(IllegalStateException.class, () -> tx.setKycKey(testKycKey)); + } + + @Test + void getSetFreezeKey() { + var tokenCreateTransaction = new TokenCreateTransaction().setFreezeKey(testFreezeKey); + assertThat(tokenCreateTransaction.getFreezeKey()).isEqualTo(testFreezeKey); + } + + @Test + void getSetFreezeKeyFrozen() { + var tx = spawnTestTransactionFungible(); + assertThrows(IllegalStateException.class, () -> tx.setFreezeKey(testFreezeKey)); + } + + @Test + void getSetWipeKey() { + var tokenCreateTransaction = new TokenCreateTransaction().setWipeKey(testWipeKey); + assertThat(tokenCreateTransaction.getWipeKey()).isEqualTo(testWipeKey); + } + + @Test + void getSetWipeKeyFrozen() { + var tx = spawnTestTransactionFungible(); + assertThrows(IllegalStateException.class, () -> tx.setWipeKey(testWipeKey)); + } + + @Test + void getSetSupplyKey() { + var tokenCreateTransaction = new TokenCreateTransaction().setSupplyKey(testSupplyKey); + assertThat(tokenCreateTransaction.getSupplyKey()).isEqualTo(testSupplyKey); + } + + @Test + void getSetSupplyKeyFrozen() { + var tx = spawnTestTransactionFungible(); + assertThrows(IllegalStateException.class, () -> tx.setSupplyKey(testSupplyKey)); + } + + @Test + void getSetFeeScheduleKey() { + var tokenCreateTransaction = new TokenCreateTransaction().setFeeScheduleKey(testFeeScheduleKey); + assertThat(tokenCreateTransaction.getFeeScheduleKey()).isEqualTo(testFeeScheduleKey); + } + + @Test + void getSetFeeScheduleKeyFrozen() { + var tx = spawnTestTransactionFungible(); + assertThrows(IllegalStateException.class, () -> tx.setFeeScheduleKey(testFeeScheduleKey)); + } + + @Test + void getSetPauseKey() { + var tokenCreateTransaction = new TokenCreateTransaction().setPauseKey(testPauseKey); + assertThat(tokenCreateTransaction.getPauseKey()).isEqualTo(testPauseKey); + } + + @Test + void getSetPauseKeyFrozen() { + var tx = spawnTestTransactionFungible(); + assertThrows(IllegalStateException.class, () -> tx.setPauseKey(testPauseKey)); + } + + @Test + void getSetMetadataKey() { + var tokenCreateTransaction = new TokenCreateTransaction().setMetadataKey(testMetadataKey); + assertThat(tokenCreateTransaction.getMetadataKey()).isEqualTo(testMetadataKey); + } + + @Test + void getSetMetadataKeyFrozen() { + var tx = spawnTestTransactionFungible(); + assertThrows(IllegalStateException.class, () -> tx.setMetadataKey(testMetadataKey)); + } + + @Test + void getSetExpirationTime() { + var tokenCreateTransaction = new TokenCreateTransaction().setExpirationTime(testExpirationTime); + assertThat(tokenCreateTransaction.getExpirationTime()).isEqualTo(testExpirationTime); + } + + @Test + void getSetExpirationTimeFrozen() { + var tx = spawnTestTransactionFungible(); + assertThrows(IllegalStateException.class, () -> tx.setExpirationTime(testExpirationTime)); + } + + @Test + void getSetAutoRenewAccountId() { + var tokenCreateTransaction = new TokenCreateTransaction().setAutoRenewAccountId(testAutoRenewAccountId); + assertThat(tokenCreateTransaction.getAutoRenewAccountId()).isEqualTo(testAutoRenewAccountId); + } + + @Test + void getSetAutoRenewAccountIdFrozen() { + var tx = spawnTestTransactionFungible(); + assertThrows(IllegalStateException.class, () -> tx.setAutoRenewAccountId(testAutoRenewAccountId)); + } + + @Test + void getSetAutoRenewPeriod() { + var tokenCreateTransaction = new TokenCreateTransaction().setAutoRenewPeriod(testAutoRenewPeriod); + assertThat(tokenCreateTransaction.getAutoRenewPeriod()).isEqualTo(testAutoRenewPeriod); + } + + @Test + void getSetAutoRenewPeriodFrozen() { + var tx = spawnTestTransactionFungible(); + assertThrows(IllegalStateException.class, () -> tx.setAutoRenewPeriod(testAutoRenewPeriod)); + } + + @Test + void getSetTokenMemo() { + var tokenCreateTransaction = new TokenCreateTransaction().setTokenMemo(testTokenMemo); + assertThat(tokenCreateTransaction.getTokenMemo()).isEqualTo(testTokenMemo); + } + + @Test + void getSetTokenMemoFrozen() { + var tx = spawnTestTransactionFungible(); + assertThrows(IllegalStateException.class, () -> tx.setTokenMemo(testTokenMemo)); + } + + @Test + void getSetTokenType() { + final TokenType testTokenType = TokenType.FUNGIBLE_COMMON; + var tokenCreateTransaction = new TokenCreateTransaction().setTokenType(testTokenType); + assertThat(tokenCreateTransaction.getTokenType()).isEqualTo(testTokenType); + } + + @Test + void getSetTokenTypeFrozen() { + var tx = spawnTestTransactionFungible(); + assertThrows(IllegalStateException.class, () -> tx.setTokenType(TokenType.FUNGIBLE_COMMON)); + } + + @Test + void getSetSupplyType() { + final TokenSupplyType testTokenType = TokenSupplyType.FINITE; + var tokenCreateTransaction = new TokenCreateTransaction().setSupplyType(testTokenType); + assertThat(tokenCreateTransaction.getSupplyType()).isEqualTo(testTokenType); + } + + @Test + void getSetSupplyTypeFrozen() { + var tx = spawnTestTransactionFungible(); + assertThrows(IllegalStateException.class, () -> tx.setSupplyType(TokenSupplyType.FINITE)); + } + + @Test + void getSetMaxSupply() { + var tokenCreateTransaction = new TokenCreateTransaction().setMaxSupply(testMaxSupply); + assertThat(tokenCreateTransaction.getMaxSupply()).isEqualTo(testMaxSupply); + } + + @Test + void getSetMaxSupplyFrozen() { + var tx = spawnTestTransactionFungible(); + assertThrows(IllegalStateException.class, () -> tx.setMaxSupply(testMaxSupply)); + } + + @Test + void getSetMetadata() { + var tx = spawnTestTransactionFungible(); + assertThat(tx.getTokenMetadata()).isEqualTo(testMetadata); + } + + @Test + void getSetMetadataFrozen() { + var tx = spawnTestTransactionFungible(); + assertThrows(IllegalStateException.class, () -> tx.setTokenMetadata(testMetadata)); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TokenCreateTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/TokenCreateTransactionTest.snap new file mode 100644 index 0000000000..0011f61b04 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenCreateTransactionTest.snap @@ -0,0 +1,8 @@ +org.hiero.sdk.TokenCreateTransactionTest.shouldSerializeFungible=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_creation {\n admin_key {\n ed25519: \"\\332\\207p\\020\\227\\206ns\\360\\335\\224,\\273>\\227\\0063)\\371\\005X\\206!\\261x\\322\\027Yh\\215G\\374\"\n }\n auto_renew_account {\n account_num: 8\n realm_num: 8\n shard_num: 8\n }\n custom_fees {\n fee_collector_account_id {\n account_num: 543\n realm_num: 0\n shard_num: 0\n }\n fixed_fee {\n amount: 3\n denominating_token_id {\n realm_num: 3\n shard_num: 4\n token_num: 2\n }\n }\n }\n decimals: 3\n expiry {\n seconds: 1554158542\n }\n fee_schedule_key {\n ed25519: \"K\\276\\225\\250m$\\370\\371gs\\261(&\\374\\276\\000\\226\\210\\313\\r\\312\\210\\317\\361\\027\\243\\250\\257P\\303q\\023\"\n }\n freeze_default: true\n freeze_key {\n ed25519: \"=\\355S\\343\\\"3S/=\\204b2L\\321\\023\\253\\276Os!m\\360mT\\241\\034\\266\\221\\301[\\'\\315\"\n }\n initial_supply: 30\n kyc_key {\n ed25519: \"\\373\\210\\2637\\337\\327ea{\\3442*\\347\\357\\2053\\326\\036d\\203\\005\\016 \\354HE\\2453\\275\\334\\244\\261\"\n }\n max_supply: 0\n memo: \"test memo\"\n metadata: \"\\001\\002\\003\\004\\005\"\n metadata_key {\n ed25519: \"\\024m\\354\\2222\\nnF\\353\\032Cv{\\261\\354\\225\\242\\346\\300%\\032\\260\\335x\\017\\343tt\\324\\272\\304\\025\"\n }\n name: \"test name\"\n pause_key {\n ed25519: \"\\321he\\251\\214\\370\\260\\267\\370\\3727v\\262\\r\\257\\305\\276\\004\\377\\353\\232$\\227r\\a\\203\\316\\231\\036+\\031t\"\n }\n supply_key {\n ed25519: \";\\2218S\\257\\245\\233U\\253\\305\\201\\302\\254\\r6X\\n\\302\\354\\244\\275\\020\\034\\002\\027?\\357\\002\\346w\\335\\325\"\n }\n symbol: \"test symbol\"\n treasury {\n account_num: 7\n realm_num: 7\n shard_num: 7\n }\n wipe_key {\n ed25519: \"R[\\234\\025_\\220+\\221-\\275\\201\\276\\246\\324:\\a}zb\\335\\037\\357\\317\\307}\\351aD\\325\\372\\303\\356\"\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] + + +org.hiero.sdk.TokenCreateTransactionTest.shouldSerializeNft=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_creation {\n admin_key {\n ed25519: \"\\332\\207p\\020\\227\\206ns\\360\\335\\224,\\273>\\227\\0063)\\371\\005X\\206!\\261x\\322\\027Yh\\215G\\374\"\n }\n auto_renew_account {\n account_num: 8\n realm_num: 8\n shard_num: 8\n }\n expiry {\n seconds: 1554158542\n }\n fee_schedule_key {\n ed25519: \"K\\276\\225\\250m$\\370\\371gs\\261(&\\374\\276\\000\\226\\210\\313\\r\\312\\210\\317\\361\\027\\243\\250\\257P\\303q\\023\"\n }\n freeze_key {\n ed25519: \"=\\355S\\343\\\"3S/=\\204b2L\\321\\023\\253\\276Os!m\\360mT\\241\\034\\266\\221\\301[\\'\\315\"\n }\n initial_supply: 0\n kyc_key {\n ed25519: \"\\373\\210\\2637\\337\\327ea{\\3442*\\347\\357\\2053\\326\\036d\\203\\005\\016 \\354HE\\2453\\275\\334\\244\\261\"\n }\n max_supply: 500\n memo: \"test memo\"\n metadata: \"\\001\\002\\003\\004\\005\"\n metadata_key {\n ed25519: \"\\024m\\354\\2222\\nnF\\353\\032Cv{\\261\\354\\225\\242\\346\\300%\\032\\260\\335x\\017\\343tt\\324\\272\\304\\025\"\n }\n name: \"test name\"\n pause_key {\n ed25519: \"\\321he\\251\\214\\370\\260\\267\\370\\3727v\\262\\r\\257\\305\\276\\004\\377\\353\\232$\\227r\\a\\203\\316\\231\\036+\\031t\"\n }\n supply_key {\n ed25519: \";\\2218S\\257\\245\\233U\\253\\305\\201\\302\\254\\r6X\\n\\302\\354\\244\\275\\020\\034\\002\\027?\\357\\002\\346w\\335\\325\"\n }\n supply_type: FINITE\n supply_type_value: 1\n symbol: \"test symbol\"\n token_type: NON_FUNGIBLE_UNIQUE\n token_type_value: 1\n treasury {\n account_num: 7\n realm_num: 7\n shard_num: 7\n }\n wipe_key {\n ed25519: \"R[\\234\\025_\\220+\\221-\\275\\201\\276\\246\\324:\\a}zb\\335\\037\\357\\317\\307}\\351aD\\325\\372\\303\\356\"\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TokenDeleteTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/TokenDeleteTransactionTest.java new file mode 100644 index 0000000000..a3469fc052 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenDeleteTransactionTest.java @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenDeleteTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TokenDeleteTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new TokenDeleteTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + private TokenDeleteTransaction spawnTestTransaction() { + return new TokenDeleteTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setTokenId(TokenId.fromString("1.2.3")) + .setMaxTransactionFee(new Hbar(1)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = TokenDeleteTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setTokenDeletion(TokenDeleteTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(TokenDeleteTransaction.class); + } + + @Test + void constructTokenDeleteTransaction() { + var transaction = new TokenDeleteTransaction(); + + assertThat(transaction.getTokenId()).isNull(); + } + + @Test + void ConstructTokenDeleteTransactionFromTransactionBodyProtobuf() { + var tokenId = TokenId.fromString("1.2.3"); + + var transactionBody = TokenDeleteTransactionBody.newBuilder() + .setToken(tokenId.toProtobuf()) + .build(); + var txBody = + TransactionBody.newBuilder().setTokenDeletion(transactionBody).build(); + var tokenDeleteTransaction = new TokenDeleteTransaction(txBody); + + assertThat(tokenDeleteTransaction.getTokenId()).isEqualTo(tokenId); + } + + @Test + void getSetTokenId() { + var tokenId = TokenId.fromString("1.2.3"); + + var transaction = new TokenDeleteTransaction().setTokenId(tokenId); + + assertThat(transaction.getTokenId()).isEqualTo(tokenId); + } + + @Test + void getSetTokenIdFrozen() { + var tokenId = TokenId.fromString("1.2.3"); + + var tx = spawnTestTransaction(); + + assertThrows(IllegalStateException.class, () -> tx.setTokenId(tokenId)); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TokenDeleteTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/TokenDeleteTransactionTest.snap new file mode 100644 index 0000000000..100ee94e13 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenDeleteTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.TokenDeleteTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_deletion {\n token {\n realm_num: 2\n shard_num: 1\n token_num: 3\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TokenDissociateTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/TokenDissociateTransactionTest.java new file mode 100644 index 0000000000..7ef4b1e9aa --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenDissociateTransactionTest.java @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import java.util.List; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenDissociateTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TokenDissociateTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + private static final AccountId testAccountId = AccountId.fromString("6.9.0"); + + private static final List testTokenIds = + Arrays.asList(TokenId.fromString("4.2.0"), TokenId.fromString("4.2.1"), TokenId.fromString("4.2.2")); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new TokenDissociateTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + private TokenDissociateTransaction spawnTestTransaction() { + return new TokenDissociateTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setAccountId(testAccountId) + .setTokenIds(testTokenIds) + .setMaxTransactionFee(new Hbar(1)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = TokenDissociateTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setTokenDissociate(TokenDissociateTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(TokenDissociateTransaction.class); + } + + @Test + void constructTokenDissociateTransactionFromTransactionBodyProtobuf() { + var transactionBody = TokenDissociateTransactionBody.newBuilder() + .setAccount(testAccountId.toProtobuf()) + .addAllTokens(testTokenIds.stream().map(TokenId::toProtobuf).toList()) + .build(); + + var tx = + TransactionBody.newBuilder().setTokenDissociate(transactionBody).build(); + var tokenDissociateTransaction = new TokenDissociateTransaction(tx); + + assertThat(tokenDissociateTransaction.getAccountId()).isEqualTo(testAccountId); + assertThat(tokenDissociateTransaction.getTokenIds().size()).isEqualTo(testTokenIds.size()); + } + + @Test + void getSetAccountId() { + var tokenDissociateTransaction = new TokenDissociateTransaction().setAccountId(testAccountId); + assertThat(tokenDissociateTransaction.getAccountId()).isEqualTo(testAccountId); + } + + @Test + void getSetAccountIdFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setAccountId(testAccountId)); + } + + @Test + void getSetTokenIds() { + var tokenDissociateTransaction = new TokenDissociateTransaction().setTokenIds(testTokenIds); + assertThat(tokenDissociateTransaction.getTokenIds()).isEqualTo(testTokenIds); + } + + @Test + void getSetTokenIdsFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setTokenIds(testTokenIds)); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TokenDissociateTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/TokenDissociateTransactionTest.snap new file mode 100644 index 0000000000..447e42d8fd --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenDissociateTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.TokenDissociateTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_dissociate {\n account {\n account_num: 0\n realm_num: 9\n shard_num: 6\n }\n tokens {\n realm_num: 2\n shard_num: 4\n token_num: 0\n }\n tokens {\n realm_num: 2\n shard_num: 4\n token_num: 1\n }\n tokens {\n realm_num: 2\n shard_num: 4\n token_num: 2\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TokenFeeScheduleUpdateTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/TokenFeeScheduleUpdateTransactionTest.java new file mode 100644 index 0000000000..1df5b19452 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenFeeScheduleUpdateTransactionTest.java @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Arrays; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenFeeScheduleUpdateTransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TokenFeeScheduleUpdateTransactionTest { + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + private TokenFeeScheduleUpdateTransaction spawnTestTransaction() { + var customFees = new ArrayList(); + customFees.add(new CustomFixedFee() + .setFeeCollectorAccountId(new AccountId(4322)) + .setDenominatingTokenId(new TokenId(483902)) + .setAmount(10)); + customFees.add(new CustomFractionalFee() + .setFeeCollectorAccountId(new AccountId(389042)) + .setNumerator(3) + .setDenominator(7) + .setMin(3) + .setMax(100) + .setAssessmentMethod(FeeAssessmentMethod.EXCLUSIVE)); + + return new TokenFeeScheduleUpdateTransaction() + .setTokenId(new TokenId(8798)) + .setCustomFees(customFees) + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .freeze(); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new TokenFeeScheduleUpdateTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldSerialize() throws InvalidProtocolBufferException { + var originalUpdate = spawnTestTransaction(); + byte[] updateBytes = originalUpdate.toBytes(); + var copyUpdate = TokenFeeScheduleUpdateTransaction.fromBytes(updateBytes); + assertThat(copyUpdate.toString()).isEqualTo(originalUpdate.toString()); + SnapshotMatcher.expect(originalUpdate.toString()).toMatchSnapshot(); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setTokenFeeScheduleUpdate( + TokenFeeScheduleUpdateTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(TokenFeeScheduleUpdateTransaction.class); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TokenFeeScheduleUpdateTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/TokenFeeScheduleUpdateTransactionTest.snap new file mode 100644 index 0000000000..f41e40b60b --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenFeeScheduleUpdateTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.TokenFeeScheduleUpdateTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_fee_schedule_update {\n custom_fees {\n fee_collector_account_id {\n account_num: 4322\n realm_num: 0\n shard_num: 0\n }\n fixed_fee {\n amount: 10\n denominating_token_id {\n realm_num: 0\n shard_num: 0\n token_num: 483902\n }\n }\n }\n custom_fees {\n fee_collector_account_id {\n account_num: 389042\n realm_num: 0\n shard_num: 0\n }\n fractional_fee {\n fractional_amount {\n denominator: 7\n numerator: 3\n }\n maximum_amount: 100\n minimum_amount: 3\n net_of_transfers: true\n }\n }\n token_id {\n realm_num: 0\n shard_num: 0\n token_num: 8798\n }\n}\ntransaction_fee: 200000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TokenFreezeTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/TokenFreezeTransactionTest.java new file mode 100644 index 0000000000..1a85e52a1e --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenFreezeTransactionTest.java @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenFreezeAccountTransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TokenFreezeTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + private TokenFreezeTransaction spawnTestTransaction() { + return new TokenFreezeTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setAccountId(AccountId.fromString("0.0.222")) + .setTokenId(TokenId.fromString("6.5.4")) + .setMaxTransactionFee(new Hbar(1)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new TokenFreezeTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = TokenFreezeTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setTokenFreeze(TokenFreezeAccountTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(TokenFreezeTransaction.class); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TokenFreezeTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/TokenFreezeTransactionTest.snap new file mode 100644 index 0000000000..2ae2439f6f --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenFreezeTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.TokenFreezeTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_freeze {\n account {\n account_num: 222\n realm_num: 0\n shard_num: 0\n }\n token {\n realm_num: 5\n shard_num: 6\n token_num: 4\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TokenGrantKycTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/TokenGrantKycTransactionTest.java new file mode 100644 index 0000000000..e19d470e6b --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenGrantKycTransactionTest.java @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenGrantKycTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TokenGrantKycTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + private static final TokenId testTokenId = TokenId.fromString("4.2.0"); + private static final AccountId testAccountId = AccountId.fromString("6.9.0"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + private TokenGrantKycTransaction spawnTestTransaction() { + return new TokenGrantKycTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setAccountId(testAccountId) + .setTokenId(testTokenId) + .setMaxTransactionFee(new Hbar(1)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = TokenGrantKycTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new TokenGrantKycTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setTokenGrantKyc(TokenGrantKycTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(TokenGrantKycTransaction.class); + } + + @Test + void constructTokenGrantKycTransactionFromTransactionBodyProtobuf() { + var transactionBody = TokenGrantKycTransactionBody.newBuilder() + .setAccount(testAccountId.toProtobuf()) + .setToken(testTokenId.toProtobuf()) + .build(); + + var tx = TransactionBody.newBuilder().setTokenGrantKyc(transactionBody).build(); + var tokenGrantKycTransaction = new TokenGrantKycTransaction(tx); + + assertThat(tokenGrantKycTransaction.getTokenId()).isEqualTo(testTokenId); + } + + @Test + void getSetAccountId() { + var tokenGrantKycTransaction = new TokenGrantKycTransaction().setAccountId(testAccountId); + assertThat(tokenGrantKycTransaction.getAccountId()).isEqualTo(testAccountId); + } + + @Test + void getSetAccountIdFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setAccountId(testAccountId)); + } + + @Test + void getSetTokenId() { + var tokenGrantKycTransaction = new TokenGrantKycTransaction().setTokenId(testTokenId); + assertThat(tokenGrantKycTransaction.getTokenId()).isEqualTo(testTokenId); + } + + @Test + void getSetTokenIdFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setTokenId(testTokenId)); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TokenGrantKycTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/TokenGrantKycTransactionTest.snap new file mode 100644 index 0000000000..e79a110868 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenGrantKycTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.TokenGrantKycTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_grant_kyc {\n account {\n account_num: 0\n realm_num: 9\n shard_num: 6\n }\n token {\n realm_num: 2\n shard_num: 4\n token_num: 0\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TokenInfoQueryTest.java b/sdk/src/test/java/org/hiero/sdk/TokenInfoQueryTest.java new file mode 100644 index 0000000000..1febaeb6ad --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenInfoQueryTest.java @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import org.hiero.sdk.proto.QueryHeader; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TokenInfoQueryTest { + private static final TokenId testTokenId = TokenId.fromString("4.2.0"); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + var builder = org.hiero.sdk.proto.Query.newBuilder(); + new TokenInfoQuery() + .setTokenId(testTokenId) + .setMaxQueryPayment(Hbar.fromTinybars(100_000)) + .onMakeRequest(builder, QueryHeader.newBuilder().build()); + SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")) + .toMatchSnapshot(); + } + + @Test + void getSetTokenId() { + var tokenInfoQuery = new TokenInfoQuery().setTokenId(testTokenId); + assertThat(tokenInfoQuery.getTokenId()).isEqualTo(testTokenId); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TokenInfoQueryTest.snap b/sdk/src/test/java/org/hiero/sdk/TokenInfoQueryTest.snap new file mode 100644 index 0000000000..f19475fc70 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenInfoQueryTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.TokenInfoQueryTest.shouldSerialize=[ + "# org.hiero.sdk.proto.Query\ntoken_get_info {\n header {\n }\n token {\n realm_num: 2\n shard_num: 4\n token_num: 0\n }\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TokenInfoTest.java b/sdk/src/test/java/org/hiero/sdk/TokenInfoTest.java new file mode 100644 index 0000000000..209af9f896 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenInfoTest.java @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Duration; +import java.time.Instant; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +// TODO: update this, test deepClone() + +public class TokenInfoTest { + /* + if we will init PrivateKey using method `PrivateKey.fromSeedECDSAsecp256k1(byte[] seed)` (like in C++ SDK, for example) + => we will get public key each time we run tests on different machines + => io.github.jsonSnapshot.SnapshotMatcher will fail tests + => we need to init PrivateKey fromString to get the same key each time + => `toProtobuf()` tests uses getEd25519() method to assert equality + */ + private static final PublicKey testAdminKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e11") + .getPublicKey(); + private static final PublicKey testKycKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e12") + .getPublicKey(); + private static final PublicKey testFreezeKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e13") + .getPublicKey(); + private static final PublicKey testWipeKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e14") + .getPublicKey(); + private static final PublicKey testSupplyKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e15") + .getPublicKey(); + private static final PublicKey testFeeScheduleKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e16") + .getPublicKey(); + private static final PublicKey testPauseKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e17") + .getPublicKey(); + private static final PublicKey testMetadataKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e18") + .getPublicKey(); + private static final TokenId testTokenId = TokenId.fromString("0.6.9"); + private static final AccountId testTreasuryAccountId = AccountId.fromString("7.7.7"); + private static final AccountId testAutoRenewAccountId = AccountId.fromString("8.9.0"); + private static final String testTokenName = "test token name"; + private static final String testTokenSymbol = "TTN"; + private static final String testTokenMemo = "memo"; + private static final int testTokenDecimals = 3; + private static final long testTokenTotalSupply = 1000L; + private static final Boolean testTokenFreezeStatus = true; + private static final Boolean testTokenKycStatus = true; + private static final boolean testTokenIsDeleted = false; + private static final List testTokenCustomFees = Arrays.asList( + new CustomFixedFee() + .setFeeCollectorAccountId(new AccountId(4322)) + .setDenominatingTokenId(new TokenId(483902)) + .setAmount(10), + new CustomFractionalFee() + .setFeeCollectorAccountId(new AccountId(389042)) + .setNumerator(3) + .setDenominator(7) + .setMin(3) + .setMax(100)); + private static final TokenType testTokenType = TokenType.FUNGIBLE_COMMON; + private static final TokenSupplyType testTokenSupplyType = TokenSupplyType.FINITE; + private static final long testTokenMaxSupply = 1000000L; + private static final boolean testTokenPauseStatus = true; + private static final LedgerId testTokenLedgerId = LedgerId.MAINNET; + private static final Duration testAutoRenewPeriod = Duration.ofHours(10); + private static final Instant testExpirationTime = Instant.ofEpochSecond(1554158542); + private static final byte[] testMetadata = new byte[] {1, 2, 3, 4, 5}; + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + private static TokenInfo spawnTokenInfoExample() { + return new TokenInfo( + testTokenId, + testTokenName, + testTokenSymbol, + testTokenDecimals, + testTokenTotalSupply, + testTreasuryAccountId, + testAdminKey, + testKycKey, + testFreezeKey, + testWipeKey, + testSupplyKey, + testFeeScheduleKey, + testTokenFreezeStatus, + testTokenKycStatus, + testTokenIsDeleted, + testAutoRenewAccountId, + testAutoRenewPeriod, + testExpirationTime, + testTokenMemo, + testTokenCustomFees, + testTokenType, + testTokenSupplyType, + testTokenMaxSupply, + testPauseKey, + testTokenPauseStatus, + testMetadata, + testMetadataKey, + testTokenLedgerId); + } + + @Test + void shouldSerialize() throws Exception { + var originalTokenInfo = spawnTokenInfoExample(); + byte[] tokenInfoBytes = originalTokenInfo.toBytes(); + var copyTokenInfo = TokenInfo.fromBytes(tokenInfoBytes); + assertThat(copyTokenInfo.toString()).isEqualTo(originalTokenInfo.toString()); + SnapshotMatcher.expect(originalTokenInfo.toString()).toMatchSnapshot(); + } + + @Test + void fromProtobuf() { + var tokenInfoProto = spawnTokenInfoExample().toProtobuf(); + + var tokenInfo = TokenInfo.fromProtobuf(tokenInfoProto); + + assertThat(tokenInfo.tokenId).isEqualTo(testTokenId); + assertThat(tokenInfo.name).isEqualTo(testTokenName); + assertThat(tokenInfo.symbol).isEqualTo(testTokenSymbol); + assertThat(tokenInfo.decimals).isEqualTo(testTokenDecimals); + assertThat(tokenInfo.totalSupply).isEqualTo(testTokenTotalSupply); + assertThat(tokenInfo.treasuryAccountId).isEqualTo(testTreasuryAccountId); + assertThat(tokenInfo.adminKey.toBytes()).isEqualTo(testAdminKey.toBytes()); + assertThat(tokenInfo.kycKey.toBytes()).isEqualTo(testKycKey.toBytes()); + assertThat(tokenInfo.freezeKey.toBytes()).isEqualTo(testFreezeKey.toBytes()); + assertThat(tokenInfo.wipeKey.toBytes()).isEqualTo(testWipeKey.toBytes()); + assertThat(tokenInfo.supplyKey.toBytes()).isEqualTo(testSupplyKey.toBytes()); + assertThat(tokenInfo.defaultFreezeStatus).isEqualTo(testTokenFreezeStatus); + assertThat(tokenInfo.defaultKycStatus).isEqualTo(testTokenKycStatus); + assertThat(tokenInfo.isDeleted).isEqualTo(testTokenIsDeleted); + assertThat(tokenInfo.autoRenewAccount).isEqualTo(testAutoRenewAccountId); + assertThat(tokenInfo.autoRenewPeriod).isEqualTo(testAutoRenewPeriod); + assertThat(tokenInfo.expirationTime).isEqualTo(testExpirationTime); + assertThat(tokenInfo.tokenMemo).isEqualTo(testTokenMemo); + assertThat(tokenInfo.tokenType).isEqualTo(testTokenType); + assertThat(tokenInfo.supplyType).isEqualTo(testTokenSupplyType); + assertThat(tokenInfo.maxSupply).isEqualTo(testTokenMaxSupply); + assertThat(tokenInfo.feeScheduleKey.toBytes()).isEqualTo(testFeeScheduleKey.toBytes()); + assertThat(tokenInfo.customFees).hasSize(testTokenCustomFees.size()); + assertThat(tokenInfo.pauseKey.toBytes()).isEqualTo(testPauseKey.toBytes()); + assertThat(tokenInfo.pauseStatus).isEqualTo(testTokenPauseStatus); + assertThat(tokenInfo.metadata).isEqualTo(testMetadata); + assertThat(tokenInfo.metadataKey.toBytes()).isEqualTo(testMetadataKey.toBytes()); + assertThat(tokenInfo.ledgerId).isEqualTo(testTokenLedgerId); + } + + @Test + void fromBytes() throws InvalidProtocolBufferException { + var tokenInfoProto = spawnTokenInfoExample().toProtobuf(); + + var tokenInfo = TokenInfo.fromBytes(tokenInfoProto.toByteArray()); + + assertThat(tokenInfo.tokenId).isEqualTo(testTokenId); + assertThat(tokenInfo.name).isEqualTo(testTokenName); + assertThat(tokenInfo.symbol).isEqualTo(testTokenSymbol); + assertThat(tokenInfo.decimals).isEqualTo(testTokenDecimals); + assertThat(tokenInfo.totalSupply).isEqualTo(testTokenTotalSupply); + assertThat(tokenInfo.treasuryAccountId).isEqualTo(testTreasuryAccountId); + assertThat(tokenInfo.adminKey.toBytes()).isEqualTo(testAdminKey.toBytes()); + assertThat(tokenInfo.kycKey.toBytes()).isEqualTo(testKycKey.toBytes()); + assertThat(tokenInfo.freezeKey.toBytes()).isEqualTo(testFreezeKey.toBytes()); + assertThat(tokenInfo.wipeKey.toBytes()).isEqualTo(testWipeKey.toBytes()); + assertThat(tokenInfo.supplyKey.toBytes()).isEqualTo(testSupplyKey.toBytes()); + assertThat(tokenInfo.defaultFreezeStatus).isEqualTo(testTokenFreezeStatus); + assertThat(tokenInfo.defaultKycStatus).isEqualTo(testTokenKycStatus); + assertThat(tokenInfo.isDeleted).isEqualTo(testTokenIsDeleted); + assertThat(tokenInfo.autoRenewAccount).isEqualTo(testAutoRenewAccountId); + assertThat(tokenInfo.autoRenewPeriod).isEqualTo(testAutoRenewPeriod); + assertThat(tokenInfo.expirationTime).isEqualTo(testExpirationTime); + assertThat(tokenInfo.tokenMemo).isEqualTo(testTokenMemo); + assertThat(tokenInfo.tokenType).isEqualTo(testTokenType); + assertThat(tokenInfo.supplyType).isEqualTo(testTokenSupplyType); + assertThat(tokenInfo.maxSupply).isEqualTo(testTokenMaxSupply); + assertThat(tokenInfo.feeScheduleKey.toBytes()).isEqualTo(testFeeScheduleKey.toBytes()); + assertThat(tokenInfo.customFees).hasSize(testTokenCustomFees.size()); + assertThat(tokenInfo.pauseKey.toBytes()).isEqualTo(testPauseKey.toBytes()); + assertThat(tokenInfo.pauseStatus).isEqualTo(testTokenPauseStatus); + assertThat(tokenInfo.metadata).isEqualTo(testMetadata); + assertThat(tokenInfo.metadataKey.toBytes()).isEqualTo(testMetadataKey.toBytes()); + assertThat(tokenInfo.ledgerId).isEqualTo(testTokenLedgerId); + } + + @Test + void toProtobuf() { + var tokenInfoProto = spawnTokenInfoExample().toProtobuf(); + + assertThat(tokenInfoProto.getTokenInfo().getTokenId().getShardNum()).isEqualTo(testTokenId.shard); + assertThat(tokenInfoProto.getTokenInfo().getTokenId().getRealmNum()).isEqualTo(testTokenId.realm); + assertThat(tokenInfoProto.getTokenInfo().getTokenId().getTokenNum()).isEqualTo(testTokenId.num); + assertThat(tokenInfoProto.getTokenInfo().getName()).isEqualTo(testTokenName); + assertThat(tokenInfoProto.getTokenInfo().getSymbol()).isEqualTo(testTokenSymbol); + assertThat(tokenInfoProto.getTokenInfo().getDecimals()).isEqualTo(testTokenDecimals); + assertThat(tokenInfoProto.getTokenInfo().getTotalSupply()).isEqualTo(testTokenTotalSupply); + assertThat(tokenInfoProto.getTokenInfo().getTreasury().getShardNum()).isEqualTo(testTreasuryAccountId.shard); + assertThat(tokenInfoProto.getTokenInfo().getTreasury().getRealmNum()).isEqualTo(testTreasuryAccountId.realm); + assertThat(tokenInfoProto.getTokenInfo().getTreasury().getAccountNum()).isEqualTo(testTreasuryAccountId.num); + assertThat(tokenInfoProto.getTokenInfo().getAdminKey().getEd25519().toByteArray()) + .isEqualTo(testAdminKey.toBytesRaw()); + assertThat(tokenInfoProto.getTokenInfo().getKycKey().getEd25519().toByteArray()) + .isEqualTo(testKycKey.toBytesRaw()); + assertThat(tokenInfoProto.getTokenInfo().getFreezeKey().getEd25519().toByteArray()) + .isEqualTo(testFreezeKey.toBytesRaw()); + assertThat(tokenInfoProto.getTokenInfo().getWipeKey().getEd25519().toByteArray()) + .isEqualTo(testWipeKey.toBytesRaw()); + assertThat(tokenInfoProto.getTokenInfo().getSupplyKey().getEd25519().toByteArray()) + .isEqualTo(testSupplyKey.toBytesRaw()); + assertThat(tokenInfoProto.getTokenInfo().getDefaultFreezeStatus()) + .isEqualTo(TokenInfo.freezeStatusToProtobuf(testTokenFreezeStatus)); + assertThat(tokenInfoProto.getTokenInfo().getDefaultKycStatus()) + .isEqualTo(TokenInfo.kycStatusToProtobuf(testTokenKycStatus)); + assertThat(tokenInfoProto.getTokenInfo().getDeleted()).isEqualTo(testTokenIsDeleted); + assertThat(tokenInfoProto.getTokenInfo().getAutoRenewAccount().getShardNum()) + .isEqualTo(testAutoRenewAccountId.shard); + assertThat(tokenInfoProto.getTokenInfo().getAutoRenewAccount().getRealmNum()) + .isEqualTo(testAutoRenewAccountId.realm); + assertThat(tokenInfoProto.getTokenInfo().getAutoRenewAccount().getAccountNum()) + .isEqualTo(testAutoRenewAccountId.num); + assertThat(tokenInfoProto.getTokenInfo().getAutoRenewPeriod().getSeconds()) + .isEqualTo(testAutoRenewPeriod.toSeconds()); + assertThat(tokenInfoProto.getTokenInfo().getExpiry().getSeconds()) + .isEqualTo(testExpirationTime.getEpochSecond()); + assertThat(tokenInfoProto.getTokenInfo().getMemo()).isEqualTo(testTokenMemo); + assertThat(tokenInfoProto.getTokenInfo().getTokenType()) + .isEqualTo(org.hiero.sdk.proto.TokenType.valueOf(testTokenType.name())); + assertThat(tokenInfoProto.getTokenInfo().getSupplyType()) + .isEqualTo(org.hiero.sdk.proto.TokenSupplyType.valueOf(testTokenSupplyType.name())); + assertThat(tokenInfoProto.getTokenInfo().getMaxSupply()).isEqualTo(testTokenMaxSupply); + assertThat(tokenInfoProto + .getTokenInfo() + .getFeeScheduleKey() + .getEd25519() + .toByteArray()) + .isEqualTo(testFeeScheduleKey.toBytesRaw()); + assertThat(tokenInfoProto.getTokenInfo().getCustomFeesList()).hasSize(testTokenCustomFees.size()); + assertThat(tokenInfoProto.getTokenInfo().getPauseKey().getEd25519().toByteArray()) + .isEqualTo(testPauseKey.toBytesRaw()); + assertThat(tokenInfoProto.getTokenInfo().getPauseStatus()) + .isEqualTo(TokenInfo.pauseStatusToProtobuf(testTokenPauseStatus)); + assertThat(tokenInfoProto.getTokenInfo().getMetadata().toByteArray()).isEqualTo(testMetadata); + assertThat(tokenInfoProto.getTokenInfo().getMetadataKey().getEd25519().toByteArray()) + .isEqualTo(testMetadataKey.toBytesRaw()); + assertThat(tokenInfoProto.getTokenInfo().getLedgerId()).isEqualTo(testTokenLedgerId.toByteString()); + } + + @Test + void toBytes() { + var tokenInfo = spawnTokenInfoExample(); + var bytes = tokenInfo.toBytes(); + assertThat(bytes).isEqualTo(tokenInfo.toProtobuf().toByteArray()); + } +} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenInfoTest.snap b/sdk/src/test/java/org/hiero/sdk/TokenInfoTest.snap similarity index 96% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/TokenInfoTest.snap rename to sdk/src/test/java/org/hiero/sdk/TokenInfoTest.snap index 6ad928bd08..b3a4318098 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenInfoTest.snap +++ b/sdk/src/test/java/org/hiero/sdk/TokenInfoTest.snap @@ -1,3 +1,3 @@ -com.hedera.hashgraph.sdk.TokenInfoTest.shouldSerialize=[ +org.hiero.sdk.TokenInfoTest.shouldSerialize=[ "TokenInfo{tokenId=0.6.9, name=test token name, symbol=TTN, decimals=3, totalSupply=1000, treasuryAccountId=7.7.7, adminKey=302a300506032b6570032100da87701097866e73f0dd942cbb3e97063329f905588621b178d21759688d47fc, kycKey=302a300506032b6570032100fb88b337dfd765617be4322ae7ef8533d61e6483050e20ec4845a533bddca4b1, freezeKey=302a300506032b65700321003ded53e32233532f3d8462324cd113abbe4f73216df06d54a11cb691c15b27cd, wipeKey=302a300506032b6570032100525b9c155f902b912dbd81bea6d43a077d7a62dd1fefcfc77de96144d5fac3ee, supplyKey=302a300506032b65700321003b913853afa59b55abc581c2ac0d36580ac2eca4bd101c02173fef02e677ddd5, feeScheduleKey=302a300506032b65700321004bbe95a86d24f8f96773b12826fcbe009688cb0dca88cff117a3a8af50c37113, defaultFreezeStatus=true, defaultKycStatus=true, isDeleted=false, autoRenewAccount=8.9.0, autoRenewPeriod=PT10H, expirationTime=2019-04-01T22:42:22Z, tokenMemo=memo, customFees=[CustomFixedFee{feeCollectorAccountId=0.0.4322, allCollectorsAreExempt=false, amount=10, demoninatingTokenId=0.0.483902}, CustomFractionalFee{feeCollectorAccountId=0.0.389042, allCollectorsAreExempt=false, numerator=3, denominator=7, min=3, max=100, assessmentMethod=INCLUSIVE}], tokenType=FUNGIBLE_COMMON, supplyType=FINITE, maxSupply=1000000, pauseKey=302a300506032b6570032100d16865a98cf8b0b7f8fa3776b20dafc5be04ffeb9a2497720783ce991e2b1974, pauseStatus=true, metadata=[1, 2, 3, 4, 5], metadataKey=302a300506032b6570032100146dec92320a6e46eb1a43767bb1ec95a2e6c0251ab0dd780fe37474d4bac415, ledgerId=mainnet}" ] diff --git a/sdk/src/test/java/org/hiero/sdk/TokenMintTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/TokenMintTransactionTest.java new file mode 100644 index 0000000000..1f95f2dcbb --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenMintTransactionTest.java @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.google.common.collect.Iterables; +import com.google.protobuf.ByteString; +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import java.util.List; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenMintTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TokenMintTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + private static final TokenId testTokenId = TokenId.fromString("4.2.0"); + private static final Long testAmount = 10L; + private static final List testMetadataList = List.of(new byte[] {1, 2, 3, 4, 5}); + private static final ByteString testMetadataByteString = ByteString.copyFrom(new byte[] {1, 2, 3, 4, 5}); + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + @Test + void shouldSerializeMetadata() { + SnapshotMatcher.expect(spawnMetadataTestTransaction().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new TokenMintTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + private TokenMintTransaction spawnTestTransaction() { + return new TokenMintTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setTokenId(testTokenId) + .setAmount(testAmount) + .setMaxTransactionFee(new Hbar(1)) + .freeze() + .sign(unusedPrivateKey); + } + + private TokenMintTransaction spawnMetadataTestTransaction() { + return new TokenMintTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setTokenId(TokenId.fromString("1.2.3")) + .setMetadata(testMetadataList) + .setMaxTransactionFee(new Hbar(1)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = TokenUpdateTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldBytesMetadata() throws Exception { + var tx = spawnMetadataTestTransaction(); + var tx2 = TokenUpdateTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setTokenMint(TokenMintTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(TokenMintTransaction.class); + } + + @Test + void constructTokenMintTransactionFromTransactionBodyProtobuf() { + var transactionBody = TokenMintTransactionBody.newBuilder() + .setToken(testTokenId.toProtobuf()) + .setAmount(testAmount) + .addMetadata(testMetadataByteString) + .build(); + + var tx = TransactionBody.newBuilder().setTokenMint(transactionBody).build(); + var tokenMintTransaction = new TokenMintTransaction(tx); + + assertThat(tokenMintTransaction.getTokenId()).isEqualTo(testTokenId); + assertThat(tokenMintTransaction.getAmount()).isEqualTo(testAmount); + assertThat(Iterables.getLast(tokenMintTransaction.getMetadata())) + .isEqualTo(testMetadataByteString.toByteArray()); + } + + @Test + void getSetTokenId() { + var tokenMintTransaction = new TokenMintTransaction().setTokenId(testTokenId); + assertThat(tokenMintTransaction.getTokenId()).isEqualTo(testTokenId); + } + + @Test + void getSetTokenIdFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setTokenId(testTokenId)); + } + + @Test + void getSetAmount() { + var tokenMintTransaction = new TokenMintTransaction().setAmount(testAmount); + assertThat(tokenMintTransaction.getAmount()).isEqualTo(testAmount); + } + + @Test + void getSetAmountFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setAmount(testAmount)); + } + + @Test + void getSetMetadata() { + var tokenMintTransaction = new TokenMintTransaction().setMetadata(testMetadataList); + assertThat(tokenMintTransaction.getMetadata()).isEqualTo(testMetadataList); + } + + @Test + void getSetMetadataFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setMetadata(testMetadataList)); + } + + @Test + void addMetadata() { + var tokenMintTransaction = new TokenMintTransaction().addMetadata(Iterables.getLast(testMetadataList)); + assertThat(Iterables.getLast(tokenMintTransaction.getMetadata())) + .isEqualTo(Iterables.getLast(testMetadataList)); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TokenMintTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/TokenMintTransactionTest.snap new file mode 100644 index 0000000000..d2e8e9ef9f --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenMintTransactionTest.snap @@ -0,0 +1,8 @@ +org.hiero.sdk.TokenMintTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_mint {\n amount: 10\n token {\n realm_num: 2\n shard_num: 4\n token_num: 0\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] + + +org.hiero.sdk.TokenMintTransactionTest.shouldSerializeMetadata=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_mint {\n amount: 0\n metadata: \"\\001\\002\\003\\004\\005\"\n token {\n realm_num: 2\n shard_num: 1\n token_num: 3\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TokenNftInfoQueryTest.java b/sdk/src/test/java/org/hiero/sdk/TokenNftInfoQueryTest.java new file mode 100644 index 0000000000..60a6cb91f2 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenNftInfoQueryTest.java @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import org.hiero.sdk.proto.QueryHeader; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TokenNftInfoQueryTest { + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + var builder = org.hiero.sdk.proto.Query.newBuilder(); + new TokenNftInfoQuery() + .setNftId(TokenId.fromString("0.0.5005").nft(101)) + .setMaxQueryPayment(Hbar.fromTinybars(100_000)) + .onMakeRequest(builder, QueryHeader.newBuilder().build()); + SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")) + .toMatchSnapshot(); + } + + @Test + void propertiesTest() { + var tokenId = TokenId.fromString("0.0.5005"); + var query = new TokenNftInfoQuery() + .byAccountId(AccountId.fromString("0.0.123")) + .byTokenId(tokenId) + .setStart(5) + .setEnd(8) + .setNftId(tokenId.nft(101)) + .setMaxQueryPayment(Hbar.fromTinybars(100_000)); + + assertThat(query.getNftId()).hasToString("0.0.5005/101"); + assertThat(query.getTokenId()).isEqualTo(tokenId); + assertThat(query.getAccountId()).hasToString("0.0.123"); + assertThat(query.getStart()).isEqualTo(5); + assertThat(query.getEnd()).isEqualTo(8); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TokenNftInfoQueryTest.snap b/sdk/src/test/java/org/hiero/sdk/TokenNftInfoQueryTest.snap new file mode 100644 index 0000000000..a5ce43d190 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenNftInfoQueryTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.TokenNftInfoQueryTest.shouldSerialize=[ + "# org.hiero.sdk.proto.Query\ntoken_get_nft_info {\n header {\n }\n nft_i_d {\n serial_number: 101\n token_i_d {\n realm_num: 0\n shard_num: 0\n token_num: 5005\n }\n }\n}" +] diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenNftInfoTest.java b/sdk/src/test/java/org/hiero/sdk/TokenNftInfoTest.java similarity index 81% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/TokenNftInfoTest.java rename to sdk/src/test/java/org/hiero/sdk/TokenNftInfoTest.java index 4d290be154..281ca8022f 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TokenNftInfoTest.java +++ b/sdk/src/test/java/org/hiero/sdk/TokenNftInfoTest.java @@ -1,18 +1,18 @@ -package com.hedera.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import javax.annotation.Nullable; import org.bouncycastle.util.encoders.Hex; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import javax.annotation.Nullable; -import java.time.Instant; - -import static org.assertj.core.api.Assertions.assertThat; - public class TokenNftInfoTest { - final static Instant creationTime = Instant.ofEpochSecond(1554158542); + static final Instant creationTime = Instant.ofEpochSecond(1554158542); @BeforeAll public static void beforeAll() { @@ -26,13 +26,12 @@ public static void afterAll() { private static TokenNftInfo spawnTokenNftInfoExample(@Nullable AccountId spenderAccountId) { return new TokenNftInfo( - TokenId.fromString("1.2.3").nft(4), - AccountId.fromString("5.6.7"), - creationTime, - Hex.decode("deadbeef"), - LedgerId.MAINNET, - spenderAccountId - ); + TokenId.fromString("1.2.3").nft(4), + AccountId.fromString("5.6.7"), + creationTime, + Hex.decode("deadbeef"), + LedgerId.MAINNET, + spenderAccountId); } @Test diff --git a/sdk/src/test/java/org/hiero/sdk/TokenNftInfoTest.snap b/sdk/src/test/java/org/hiero/sdk/TokenNftInfoTest.snap new file mode 100644 index 0000000000..8d05aecb7f --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenNftInfoTest.snap @@ -0,0 +1,8 @@ +org.hiero.sdk.TokenNftInfoTest.shouldSerialize=[ + "TokenNftInfo{nftId=1.2.3/4, accountId=5.6.7, creationTime=2019-04-01T22:42:22Z, metadata=[-34, -83, -66, -17], ledgerId=mainnet, spenderId=8.9.10}" +] + + +org.hiero.sdk.TokenNftInfoTest.shouldSerializeNullSpender=[ + "TokenNftInfo{nftId=1.2.3/4, accountId=5.6.7, creationTime=2019-04-01T22:42:22Z, metadata=[-34, -83, -66, -17], ledgerId=mainnet, spenderId=null}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TokenPauseTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/TokenPauseTransactionTest.java new file mode 100644 index 0000000000..90a5c23cbd --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenPauseTransactionTest.java @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenPauseTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TokenPauseTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + private static final TokenId testTokenId = TokenId.fromString("4.2.0"); + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + TokenPauseTransaction spawnTestTransaction() { + return new TokenPauseTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setTokenId(testTokenId) + .setMaxTransactionFee(new Hbar(1)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytesNft() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new TokenPauseTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setTokenPause(TokenPauseTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(TokenPauseTransaction.class); + } + + @Test + void constructTokenPauseTransactionFromTransactionBodyProtobuf() { + var transactionBody = TokenPauseTransactionBody.newBuilder() + .setToken(testTokenId.toProtobuf()) + .build(); + + var tx = TransactionBody.newBuilder().setTokenPause(transactionBody).build(); + var tokenPauseTransaction = new TokenPauseTransaction(tx); + + assertThat(tokenPauseTransaction.getTokenId()).isEqualTo(testTokenId); + } + + @Test + void getSetTokenId() { + var tokenPauseTransaction = new TokenPauseTransaction().setTokenId(testTokenId); + assertThat(tokenPauseTransaction.getTokenId()).isEqualTo(testTokenId); + } + + @Test + void getSetTokenIdFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setTokenId(testTokenId)); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TokenPauseTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/TokenPauseTransactionTest.snap new file mode 100644 index 0000000000..a2f8e165f4 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenPauseTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.TokenPauseTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_pause {\n token {\n realm_num: 2\n shard_num: 4\n token_num: 0\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TokenRejectTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/TokenRejectTransactionTest.java new file mode 100644 index 0000000000..dd35096ee1 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenRejectTransactionTest.java @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import java.util.List; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenReference; +import org.hiero.sdk.proto.TokenRejectTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TokenRejectTransactionTest { + + private static final PrivateKey TEST_PRIVATE_KEY = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + private static final AccountId TEST_OWNER_ID = AccountId.fromString("0.6.9"); + + private static final List TEST_TOKEN_IDS = + List.of(TokenId.fromString("1.2.3"), TokenId.fromString("4.5.6"), TokenId.fromString("7.8.9")); + + private static final List TEST_NFT_IDS = + List.of(new NftId(TokenId.fromString("4.5.6"), 2), new NftId(TokenId.fromString("7.8.9"), 3)); + + final Instant TEST_VALID_START = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + private TokenRejectTransaction spawnTestTransaction() { + return new TokenRejectTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), TEST_VALID_START)) + .setOwnerId(TEST_OWNER_ID) + .setTokenIds(TEST_TOKEN_IDS) + .setNftIds(TEST_NFT_IDS) + .setMaxTransactionFee(new Hbar(1)) + .freeze() + .sign(TEST_PRIVATE_KEY); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new TokenRejectTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = TokenUpdateNftsTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setTokenReject(TokenRejectTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(TokenRejectTransaction.class); + } + + @Test + void constructTokenRejectTransactionFromTransactionBodyProtobuf() { + var transactionBodyBuilder = TokenRejectTransactionBody.newBuilder(); + + transactionBodyBuilder.setOwner(TEST_OWNER_ID.toProtobuf()); + + for (TokenId tokenId : TEST_TOKEN_IDS) { + transactionBodyBuilder.addRejections(TokenReference.newBuilder() + .setFungibleToken(tokenId.toProtobuf()) + .build()); + } + + for (NftId nftId : TEST_NFT_IDS) { + transactionBodyBuilder.addRejections( + TokenReference.newBuilder().setNft(nftId.toProtobuf()).build()); + } + + var tx = TransactionBody.newBuilder() + .setTokenReject(transactionBodyBuilder.build()) + .build(); + var tokenRejectTransaction = new TokenRejectTransaction(tx); + + assertThat(tokenRejectTransaction.getOwnerId()).isEqualTo(TEST_OWNER_ID); + assertThat(tokenRejectTransaction.getTokenIds()).hasSize(TEST_TOKEN_IDS.size()); + assertThat(tokenRejectTransaction.getNftIds()).hasSize(TEST_NFT_IDS.size()); + } + + @Test + void getSetOwnerId() { + var transaction = new TokenRejectTransaction().setOwnerId(TEST_OWNER_ID); + assertThat(transaction.getOwnerId()).isEqualTo(TEST_OWNER_ID); + } + + @Test + void getSetOwnerIdFrozen() { + var transaction = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> transaction.setOwnerId(TEST_OWNER_ID)); + } + + @Test + void getSetTokenIds() { + var transaction = new TokenRejectTransaction().setTokenIds(TEST_TOKEN_IDS); + assertThat(transaction.getTokenIds()).isEqualTo(TEST_TOKEN_IDS); + } + + @Test + void getSetTokenIdFrozen() { + var transaction = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> transaction.setTokenIds(TEST_TOKEN_IDS)); + } + + @Test + void getSetNftIds() { + var transaction = new TokenRejectTransaction().setNftIds(TEST_NFT_IDS); + assertThat(transaction.getNftIds()).isEqualTo(TEST_NFT_IDS); + } + + @Test + void getSetNftIdFrozen() { + var transaction = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> transaction.setNftIds(TEST_NFT_IDS)); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TokenRejectTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/TokenRejectTransactionTest.snap new file mode 100644 index 0000000000..b0e44be01b --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenRejectTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.TokenRejectTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_reject {\n owner {\n account_num: 9\n realm_num: 6\n shard_num: 0\n }\n rejections {\n fungible_token {\n realm_num: 2\n shard_num: 1\n token_num: 3\n }\n }\n rejections {\n fungible_token {\n realm_num: 5\n shard_num: 4\n token_num: 6\n }\n }\n rejections {\n fungible_token {\n realm_num: 8\n shard_num: 7\n token_num: 9\n }\n }\n rejections {\n nft {\n serial_number: 2\n token_i_d {\n realm_num: 5\n shard_num: 4\n token_num: 6\n }\n }\n }\n rejections {\n nft {\n serial_number: 3\n token_i_d {\n realm_num: 8\n shard_num: 7\n token_num: 9\n }\n }\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TokenRelationshipTest.java b/sdk/src/test/java/org/hiero/sdk/TokenRelationshipTest.java new file mode 100644 index 0000000000..0e7ddb93bb --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenRelationshipTest.java @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TokenRelationshipTest { + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + TokenRelationship spawnTokenRelationshipExample() { + return new TokenRelationship(TokenId.fromString("1.2.3"), "ABC", 55, true, true, 4, true); + } + + @Test + void shouldSerializeTokenRelationship() throws Exception { + var originalTokenRelationship = spawnTokenRelationshipExample(); + byte[] tokenRelationshipBytes = originalTokenRelationship.toBytes(); + var copyTokenRelationship = TokenRelationship.fromBytes(tokenRelationshipBytes); + assertThat(copyTokenRelationship.toString().replaceAll("@[A-Za-z0-9]+", "")) + .isEqualTo(originalTokenRelationship.toString().replaceAll("@[A-Za-z0-9]+", "")); + SnapshotMatcher.expect(originalTokenRelationship.toString().replaceAll("@[A-Za-z0-9]+", "")) + .toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TokenRelationshipTest.snap b/sdk/src/test/java/org/hiero/sdk/TokenRelationshipTest.snap new file mode 100644 index 0000000000..3bddf54f6e --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenRelationshipTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.TokenRelationshipTest.shouldSerializeTokenRelationship=[ + "TokenRelationship{tokenId=1.2.3, symbol=ABC, balance=55, kycStatus=true, freezeStatus=true, decimals=4, automaticAssociation=true}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TokenRevokeKycTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/TokenRevokeKycTransactionTest.java new file mode 100644 index 0000000000..866c9d8139 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenRevokeKycTransactionTest.java @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenRevokeKycTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TokenRevokeKycTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + private static final TokenId testTokenId = TokenId.fromString("4.2.0"); + private static final AccountId testAccountId = AccountId.fromString("6.9.0"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + private TokenRevokeKycTransaction spawnTestTransaction() { + return new TokenRevokeKycTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setAccountId(testAccountId) + .setTokenId(testTokenId) + .setMaxTransactionFee(new Hbar(1)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new TokenRevokeKycTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = TokenRevokeKycTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setTokenRevokeKyc(TokenRevokeKycTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(TokenRevokeKycTransaction.class); + } + + @Test + void constructTokenRevokeKycTransactionFromTransactionBodyProtobuf() { + var transactionBody = TokenRevokeKycTransactionBody.newBuilder() + .setAccount(testAccountId.toProtobuf()) + .setToken(testTokenId.toProtobuf()) + .build(); + + var tx = TransactionBody.newBuilder().setTokenRevokeKyc(transactionBody).build(); + var tokenRevokeKycTransaction = new TokenRevokeKycTransaction(tx); + + assertThat(tokenRevokeKycTransaction.getTokenId()).isEqualTo(testTokenId); + } + + @Test + void getSetAccountId() { + var tokenRevokeKycTransaction = new TokenRevokeKycTransaction().setAccountId(testAccountId); + assertThat(tokenRevokeKycTransaction.getAccountId()).isEqualTo(testAccountId); + } + + @Test + void getSetAccountIdFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setAccountId(testAccountId)); + } + + @Test + void getSetTokenId() { + var tokenRevokeKycTransaction = new TokenRevokeKycTransaction().setTokenId(testTokenId); + assertThat(tokenRevokeKycTransaction.getTokenId()).isEqualTo(testTokenId); + } + + @Test + void getSetTokenIdFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setTokenId(testTokenId)); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TokenRevokeKycTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/TokenRevokeKycTransactionTest.snap new file mode 100644 index 0000000000..6d1d8afa82 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenRevokeKycTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.TokenRevokeKycTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_revoke_kyc {\n account {\n account_num: 0\n realm_num: 9\n shard_num: 6\n }\n token {\n realm_num: 2\n shard_num: 4\n token_num: 0\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TokenSupplyTypeTest.java b/sdk/src/test/java/org/hiero/sdk/TokenSupplyTypeTest.java new file mode 100644 index 0000000000..68450aca6e --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenSupplyTypeTest.java @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import org.hiero.sdk.proto.TokenSupplyType; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TokenSupplyTypeTest { + private final TokenSupplyType tokenSupplyTypeInfinite = TokenSupplyType.INFINITE; + private final TokenSupplyType tokenSupplyTypeFinite = TokenSupplyType.FINITE; + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void fromProtobuf() { + SnapshotMatcher.expect( + org.hiero.sdk.TokenSupplyType.valueOf(tokenSupplyTypeInfinite) + .toString(), + org.hiero.sdk.TokenSupplyType.valueOf(tokenSupplyTypeFinite) + .toString()) + .toMatchSnapshot(); + } + + @Test + void toProtobuf() { + SnapshotMatcher.expect( + org.hiero.sdk.TokenSupplyType.valueOf(tokenSupplyTypeInfinite) + .toProtobuf(), + org.hiero.sdk.TokenSupplyType.valueOf(tokenSupplyTypeFinite) + .toProtobuf()) + .toMatchSnapshot(); + } + + @Test + void tokenSupplyTestToString() { + assertThat(org.hiero.sdk.TokenSupplyType.INFINITE).hasToString("INFINITE"); + assertThat(org.hiero.sdk.TokenSupplyType.FINITE).hasToString("FINITE"); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TokenSupplyTypeTest.snap b/sdk/src/test/java/org/hiero/sdk/TokenSupplyTypeTest.snap new file mode 100644 index 0000000000..23b560d471 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenSupplyTypeTest.snap @@ -0,0 +1,10 @@ +org.hiero.sdk.TokenSupplyTypeTest.fromProtobuf=[ + "INFINITE", + "FINITE" +] + + +org.hiero.sdk.TokenSupplyTypeTest.toProtobuf=[ + "INFINITE", + "FINITE" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TokenTypeTest.java b/sdk/src/test/java/org/hiero/sdk/TokenTypeTest.java new file mode 100644 index 0000000000..d8a2449062 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenTypeTest.java @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.github.jsonSnapshot.SnapshotMatcher; +import org.hiero.sdk.proto.TokenType; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TokenTypeTest { + + private final TokenType tokenTypeFungible = TokenType.FUNGIBLE_COMMON; + private final TokenType tokenTypeNonFungible = TokenType.NON_FUNGIBLE_UNIQUE; + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void fromProtobuf() { + SnapshotMatcher.expect( + org.hiero.sdk.TokenType.valueOf(tokenTypeFungible).toString(), + org.hiero.sdk.TokenType.valueOf(tokenTypeNonFungible).toString()) + .toMatchSnapshot(); + } + + @Test + void toProtobuf() { + SnapshotMatcher.expect( + org.hiero.sdk.TokenType.valueOf(tokenTypeFungible).toProtobuf(), + org.hiero.sdk.TokenType.valueOf(tokenTypeNonFungible).toProtobuf()) + .toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TokenTypeTest.snap b/sdk/src/test/java/org/hiero/sdk/TokenTypeTest.snap new file mode 100644 index 0000000000..650d078492 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenTypeTest.snap @@ -0,0 +1,10 @@ +org.hiero.sdk.TokenTypeTest.fromProtobuf=[ + "FUNGIBLE_COMMON", + "NON_FUNGIBLE_UNIQUE" +] + + +org.hiero.sdk.TokenTypeTest.toProtobuf=[ + "FUNGIBLE_COMMON", + "NON_FUNGIBLE_UNIQUE" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TokenUnfreezeTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/TokenUnfreezeTransactionTest.java new file mode 100644 index 0000000000..778fc687af --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenUnfreezeTransactionTest.java @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenUnfreezeAccountTransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TokenUnfreezeTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new TokenUnfreezeTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + private TokenUnfreezeTransaction spawnTestTransaction() { + return new TokenUnfreezeTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setAccountId(AccountId.fromString("0.0.222")) + .setTokenId(TokenId.fromString("6.5.4")) + .setMaxTransactionFee(new Hbar(1)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = TokenUnfreezeTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setTokenUnfreeze( + TokenUnfreezeAccountTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(TokenUnfreezeTransaction.class); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TokenUnfreezeTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/TokenUnfreezeTransactionTest.snap new file mode 100644 index 0000000000..f7514314f2 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenUnfreezeTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.TokenUnfreezeTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_unfreeze {\n account {\n account_num: 222\n realm_num: 0\n shard_num: 0\n }\n token {\n realm_num: 5\n shard_num: 6\n token_num: 4\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TokenUnpauseTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/TokenUnpauseTransactionTest.java new file mode 100644 index 0000000000..dda0450545 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenUnpauseTransactionTest.java @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenUnpauseTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TokenUnpauseTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + private static final TokenId testTokenId = TokenId.fromString("4.2.0"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + TokenUnpauseTransaction spawnTestTransaction() { + return new TokenUnpauseTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setTokenId(testTokenId) + .setMaxTransactionFee(new Hbar(1)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new TokenUnpauseTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytesNft() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = TokenCreateTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setTokenUnpause(TokenUnpauseTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(TokenUnpauseTransaction.class); + } + + @Test + void constructTokenUnpauseTransactionFromTransactionBodyProtobuf() { + var transactionBody = TokenUnpauseTransactionBody.newBuilder() + .setToken(testTokenId.toProtobuf()) + .build(); + + var tx = TransactionBody.newBuilder().setTokenUnpause(transactionBody).build(); + var tokenUnpauseTransaction = new TokenUnpauseTransaction(tx); + + assertThat(tokenUnpauseTransaction.getTokenId()).isEqualTo(testTokenId); + } + + @Test + void getSetTokenId() { + var tokenUnpauseTransaction = new TokenUnpauseTransaction().setTokenId(testTokenId); + assertThat(tokenUnpauseTransaction.getTokenId()).isEqualTo(testTokenId); + } + + @Test + void getSetTokenIdFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setTokenId(testTokenId)); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TokenUnpauseTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/TokenUnpauseTransactionTest.snap new file mode 100644 index 0000000000..a46ce30c5c --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenUnpauseTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.TokenUnpauseTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_unpause {\n token {\n realm_num: 2\n shard_num: 4\n token_num: 0\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TokenUpdateNftsTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/TokenUpdateNftsTransactionTest.java new file mode 100644 index 0000000000..6679cb0af2 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenUpdateNftsTransactionTest.java @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.google.protobuf.ByteString; +import com.google.protobuf.BytesValue; +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import java.util.List; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenUpdateNftsTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TokenUpdateNftsTransactionTest { + private static final PrivateKey testMetadataKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + private static final TokenId testTokenId = TokenId.fromString("4.2.0"); + private static final List testSerialNumbers = Arrays.asList(8L, 9L, 10L); + private static final byte[] testMetadata = new byte[] {1, 2, 3, 4, 5}; + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + private TokenUpdateNftsTransaction spawnTestTransaction() { + return new TokenUpdateNftsTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setTokenId(testTokenId) + .setMetadata(testMetadata) + .setSerials(testSerialNumbers) + .setMaxTransactionFee(new Hbar(1)) + .freeze() + .sign(testMetadataKey); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new TokenUpdateNftsTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = TokenUpdateNftsTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setTokenUpdateNfts(TokenUpdateNftsTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(TokenUpdateNftsTransaction.class); + } + + @Test + void constructTokenUpdateTransactionFromTransactionBodyProtobuf() { + var transactionBody = TokenUpdateNftsTransactionBody.newBuilder() + .setToken(testTokenId.toProtobuf()) + .setMetadata(BytesValue.of(ByteString.copyFrom(testMetadata))) + .addAllSerialNumbers(testSerialNumbers) + .build(); + + var tx = + TransactionBody.newBuilder().setTokenUpdateNfts(transactionBody).build(); + var tokenUpdateNftsTransaction = new TokenUpdateNftsTransaction(tx); + + assertThat(tokenUpdateNftsTransaction.getTokenId()).isEqualTo(testTokenId); + assertThat(tokenUpdateNftsTransaction.getMetadata()).isEqualTo(testMetadata); + assertThat(tokenUpdateNftsTransaction.getSerials()).isEqualTo(testSerialNumbers); + } + + @Test + void getSetTokenId() { + var tokenUpdateNftsTransaction = new TokenUpdateNftsTransaction().setTokenId(testTokenId); + assertThat(tokenUpdateNftsTransaction.getTokenId()).isEqualTo(testTokenId); + } + + @Test + void getSetTokenIdFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setTokenId(testTokenId)); + } + + @Test + void getSetMetadata() { + var tx = spawnTestTransaction(); + assertThat(tx.getMetadata()).isEqualTo(testMetadata); + } + + @Test + void getSetMetadataFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setMetadata(testMetadata)); + } + + @Test + void getSetSerialNumbers() { + var tx = spawnTestTransaction(); + assertThat(tx.getSerials()).isEqualTo(testSerialNumbers); + } + + @Test + void getSetSerialNumbersFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setSerials(testSerialNumbers)); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TokenUpdateNftsTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/TokenUpdateNftsTransactionTest.snap new file mode 100644 index 0000000000..0724022328 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenUpdateNftsTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.TokenUpdateNftsTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_update_nfts {\n metadata {\n value: \"\\001\\002\\003\\004\\005\"\n }\n serial_numbers: 8\n serial_numbers: 9\n serial_numbers: 10\n token {\n realm_num: 2\n shard_num: 4\n token_num: 0\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TokenUpdateTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/TokenUpdateTransactionTest.java new file mode 100644 index 0000000000..f7025f27d7 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenUpdateTransactionTest.java @@ -0,0 +1,393 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.google.protobuf.ByteString; +import com.google.protobuf.BytesValue; +import com.google.protobuf.StringValue; +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Duration; +import java.time.Instant; +import java.util.Arrays; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.Timestamp; +import org.hiero.sdk.proto.TokenUpdateTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TokenUpdateTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + private static final PublicKey testAdminKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e11") + .getPublicKey(); + private static final PublicKey testKycKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e12") + .getPublicKey(); + private static final PublicKey testFreezeKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e13") + .getPublicKey(); + private static final PublicKey testWipeKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e14") + .getPublicKey(); + private static final PublicKey testSupplyKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e15") + .getPublicKey(); + private static final PublicKey testFeeScheduleKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e16") + .getPublicKey(); + private static final PublicKey testPauseKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e17") + .getPublicKey(); + private static final PublicKey testMetadataKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e18") + .getPublicKey(); + private static final AccountId testTreasuryAccountId = AccountId.fromString("7.7.7"); + private static final AccountId testAutoRenewAccountId = AccountId.fromString("8.8.8"); + private static final String testTokenName = "test name"; + private static final String testTokenSymbol = "test symbol"; + private static final String testTokenMemo = "test memo"; + private static final TokenId testTokenId = TokenId.fromString("4.2.0"); + private static final Duration testAutoRenewPeriod = Duration.ofHours(10); + private static final Instant testExpirationTime = Instant.now(); + private static final byte[] testMetadata = new byte[] {1, 2, 3, 4, 5}; + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new TokenUpdateTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + private TokenUpdateTransaction spawnTestTransaction() { + return new TokenUpdateTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setTokenId(testTokenId) + .setFeeScheduleKey(testFeeScheduleKey) + .setSupplyKey(testSupplyKey) + .setAdminKey(testAdminKey) + .setAutoRenewAccountId(testAutoRenewAccountId) + .setAutoRenewPeriod(testAutoRenewPeriod) + .setFreezeKey(testFreezeKey) + .setWipeKey(testWipeKey) + .setTokenSymbol(testTokenSymbol) + .setKycKey(testKycKey) + .setPauseKey(testPauseKey) + .setMetadataKey(testMetadataKey) + .setExpirationTime(validStart) + .setTreasuryAccountId(testTreasuryAccountId) + .setTokenName(testTokenName) + .setTokenMemo(testTokenMemo) + .setMaxTransactionFee(new Hbar(1)) + .setTokenMetadata(testMetadata) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = TokenUpdateTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setTokenUpdate(TokenUpdateTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(TokenUpdateTransaction.class); + } + + @Test + void constructTokenUpdateTransactionFromTransactionBodyProtobuf() { + var transactionBody = TokenUpdateTransactionBody.newBuilder() + .setToken(testTokenId.toProtobuf()) + .setName(testTokenName) + .setSymbol(testTokenSymbol) + .setTreasury(testTreasuryAccountId.toProtobuf()) + .setAdminKey(testAdminKey.toProtobufKey()) + .setKycKey(testKycKey.toProtobufKey()) + .setFreezeKey(testFreezeKey.toProtobufKey()) + .setWipeKey(testWipeKey.toProtobufKey()) + .setSupplyKey(testSupplyKey.toProtobufKey()) + .setAutoRenewAccount(testAutoRenewAccountId.toProtobuf()) + .setAutoRenewPeriod(org.hiero.sdk.proto.Duration.newBuilder() + .setSeconds(testAutoRenewPeriod.toSeconds()) + .build()) + .setExpiry(Timestamp.newBuilder() + .setSeconds(testExpirationTime.getEpochSecond()) + .build()) + .setMemo(StringValue.newBuilder().setValue(testTokenMemo).build()) + .setFeeScheduleKey(testFeeScheduleKey.toProtobufKey()) + .setPauseKey(testPauseKey.toProtobufKey()) + .setMetadataKey(testMetadataKey.toProtobufKey()) + .setMetadata(BytesValue.of(ByteString.copyFrom(testMetadata))) + .setKeyVerificationMode(org.hiero.sdk.proto.TokenKeyValidation.NO_VALIDATION) + .build(); + + var tx = TransactionBody.newBuilder().setTokenUpdate(transactionBody).build(); + var tokenUpdateTransaction = new TokenUpdateTransaction(tx); + + assertThat(tokenUpdateTransaction.getTokenId()).isEqualTo(testTokenId); + assertThat(tokenUpdateTransaction.getTokenName()).isEqualTo(testTokenName); + assertThat(tokenUpdateTransaction.getTokenSymbol()).isEqualTo(testTokenSymbol); + assertThat(tokenUpdateTransaction.getTreasuryAccountId()).isEqualTo(testTreasuryAccountId); + assertThat(tokenUpdateTransaction.getAdminKey()).isEqualTo(testAdminKey); + assertThat(tokenUpdateTransaction.getKycKey()).isEqualTo(testKycKey); + assertThat(tokenUpdateTransaction.getFreezeKey()).isEqualTo(testFreezeKey); + assertThat(tokenUpdateTransaction.getWipeKey()).isEqualTo(testWipeKey); + assertThat(tokenUpdateTransaction.getSupplyKey()).isEqualTo(testSupplyKey); + assertThat(tokenUpdateTransaction.getAutoRenewAccountId()).isEqualTo(testAutoRenewAccountId); + assertThat(tokenUpdateTransaction.getAutoRenewPeriod().toSeconds()).isEqualTo(testAutoRenewPeriod.toSeconds()); + assertThat(tokenUpdateTransaction.getExpirationTime().getEpochSecond()) + .isEqualTo(testExpirationTime.getEpochSecond()); + assertThat(tokenUpdateTransaction.getTokenMemo()).isEqualTo(testTokenMemo); + assertThat(tokenUpdateTransaction.getFeeScheduleKey()).isEqualTo(testFeeScheduleKey); + assertThat(tokenUpdateTransaction.getPauseKey()).isEqualTo(testPauseKey); + assertThat(tokenUpdateTransaction.getMetadataKey()).isEqualTo(testMetadataKey); + assertThat(tokenUpdateTransaction.getTokenMetadata()).isEqualTo(testMetadata); + assertThat(tokenUpdateTransaction.getKeyVerificationMode()).isEqualTo(TokenKeyValidation.NO_VALIDATION); + } + + @Test + void getSetTokenId() { + var tokenUpdateTransaction = new TokenUpdateTransaction().setTokenId(testTokenId); + assertThat(tokenUpdateTransaction.getTokenId()).isEqualTo(testTokenId); + } + + @Test + void getSetTokenIdFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setTokenId(testTokenId)); + } + + @Test + void getSetName() { + var tokenUpdateTransaction = new TokenUpdateTransaction().setTokenName(testTokenName); + assertThat(tokenUpdateTransaction.getTokenName()).isEqualTo(testTokenName); + } + + @Test + void getSetNameFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setTokenName(testTokenName)); + } + + @Test + void getSetSymbol() { + var tokenUpdateTransaction = new TokenUpdateTransaction().setTokenSymbol(testTokenSymbol); + assertThat(tokenUpdateTransaction.getTokenSymbol()).isEqualTo(testTokenSymbol); + } + + @Test + void getSetSymbolFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setTokenSymbol(testTokenSymbol)); + } + + @Test + void getSetTreasuryAccountId() { + var tokenUpdateTransaction = new TokenUpdateTransaction().setTreasuryAccountId(testTreasuryAccountId); + assertThat(tokenUpdateTransaction.getTreasuryAccountId()).isEqualTo(testTreasuryAccountId); + } + + @Test + void getSetTreasuryAccountIdFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setTreasuryAccountId(testTreasuryAccountId)); + } + + @Test + void getSetAdminKey() { + var tokenUpdateTransaction = new TokenUpdateTransaction().setAdminKey(testAdminKey); + assertThat(tokenUpdateTransaction.getAdminKey()).isEqualTo(testAdminKey); + } + + @Test + void getSetAdminKeyFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setAdminKey(testAdminKey)); + } + + @Test + void getSetKycKey() { + var tokenUpdateTransaction = new TokenUpdateTransaction().setKycKey(testKycKey); + assertThat(tokenUpdateTransaction.getKycKey()).isEqualTo(testKycKey); + } + + @Test + void getSetKycKeyFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setKycKey(testKycKey)); + } + + @Test + void getSetFreezeKey() { + var tokenUpdateTransaction = new TokenUpdateTransaction().setFreezeKey(testFreezeKey); + assertThat(tokenUpdateTransaction.getFreezeKey()).isEqualTo(testFreezeKey); + } + + @Test + void getSetFreezeKeyFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setFreezeKey(testFreezeKey)); + } + + @Test + void getSetWipeKey() { + var tokenUpdateTransaction = new TokenUpdateTransaction().setWipeKey(testWipeKey); + assertThat(tokenUpdateTransaction.getWipeKey()).isEqualTo(testWipeKey); + } + + @Test + void getSetWipeKeyFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setWipeKey(testWipeKey)); + } + + @Test + void getSetSupplyKey() { + var tokenUpdateTransaction = new TokenUpdateTransaction().setSupplyKey(testSupplyKey); + assertThat(tokenUpdateTransaction.getSupplyKey()).isEqualTo(testSupplyKey); + } + + @Test + void getSetSupplyKeyFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setSupplyKey(testSupplyKey)); + } + + @Test + void getSetAutoRenewAccountId() { + var tokenUpdateTransaction = new TokenUpdateTransaction().setAutoRenewAccountId(testAutoRenewAccountId); + assertThat(tokenUpdateTransaction.getAutoRenewAccountId()).isEqualTo(testAutoRenewAccountId); + } + + @Test + void getSetAutoRenewAccountIdFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setAutoRenewAccountId(testAutoRenewAccountId)); + } + + @Test + void getSetAutoRenewPeriod() { + var tokenUpdateTransaction = new TokenUpdateTransaction().setAutoRenewPeriod(testAutoRenewPeriod); + assertThat(tokenUpdateTransaction.getAutoRenewPeriod()).isEqualTo(testAutoRenewPeriod); + } + + @Test + void getSetAutoRenewPeriodFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setAutoRenewPeriod(testAutoRenewPeriod)); + } + + @Test + void getSetExpirationTime() { + var tokenUpdateTransaction = new TokenUpdateTransaction().setExpirationTime(testExpirationTime); + assertThat(tokenUpdateTransaction.getExpirationTime()).isEqualTo(testExpirationTime); + } + + @Test + void getSetExpirationTimeFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setExpirationTime(testExpirationTime)); + } + + @Test + void getSetTokenMemo() { + var tokenUpdateTransaction = new TokenUpdateTransaction().setTokenMemo(testTokenMemo); + assertThat(tokenUpdateTransaction.getTokenMemo()).isEqualTo(testTokenMemo); + } + + @Test + void getSetTokenMemoFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setTokenMemo(testTokenMemo)); + } + + @Test + void getSetFeeScheduleKey() { + var tokenUpdateTransaction = new TokenUpdateTransaction().setFeeScheduleKey(testFeeScheduleKey); + assertThat(tokenUpdateTransaction.getFeeScheduleKey()).isEqualTo(testFeeScheduleKey); + } + + @Test + void getSetFeeScheduleKeyFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setFeeScheduleKey(testFeeScheduleKey)); + } + + @Test + void getSetPauseKey() { + var tokenUpdateTransaction = new TokenUpdateTransaction().setPauseKey(testPauseKey); + assertThat(tokenUpdateTransaction.getPauseKey()).isEqualTo(testPauseKey); + } + + @Test + void getSetPauseKeyFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setPauseKey(testPauseKey)); + } + + @Test + void getSetMetadataKey() { + var tokenUpdateTransaction = new TokenUpdateTransaction().setMetadataKey(testMetadataKey); + assertThat(tokenUpdateTransaction.getMetadataKey()).isEqualTo(testMetadataKey); + } + + @Test + void getSetMetadataKeyFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setMetadataKey(testMetadataKey)); + } + + @Test + void getSetMetadata() { + var tx = spawnTestTransaction(); + assertThat(tx.getTokenMetadata()).isEqualTo(testMetadata); + } + + @Test + void getSetMetadataFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setTokenMetadata(testMetadata)); + } + + @Test + void getSetKeyVerificationMode() { + var tx = spawnTestTransaction(); + assertThat(tx.getKeyVerificationMode()).isEqualTo(TokenKeyValidation.NO_VALIDATION); + } + + @Test + void getSetKeyVerificationModeFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION)); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TokenUpdateTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/TokenUpdateTransactionTest.snap new file mode 100644 index 0000000000..69d75bb4cb --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenUpdateTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.TokenUpdateTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_update {\n admin_key {\n ed25519: \"\\332\\207p\\020\\227\\206ns\\360\\335\\224,\\273>\\227\\0063)\\371\\005X\\206!\\261x\\322\\027Yh\\215G\\374\"\n }\n auto_renew_account {\n account_num: 8\n realm_num: 8\n shard_num: 8\n }\n expiry {\n seconds: 1554158542\n }\n fee_schedule_key {\n ed25519: \"K\\276\\225\\250m$\\370\\371gs\\261(&\\374\\276\\000\\226\\210\\313\\r\\312\\210\\317\\361\\027\\243\\250\\257P\\303q\\023\"\n }\n freeze_key {\n ed25519: \"=\\355S\\343\\\"3S/=\\204b2L\\321\\023\\253\\276Os!m\\360mT\\241\\034\\266\\221\\301[\\'\\315\"\n }\n key_verification_mode: NO_VALIDATION\n key_verification_mode_value: 1\n kyc_key {\n ed25519: \"\\373\\210\\2637\\337\\327ea{\\3442*\\347\\357\\2053\\326\\036d\\203\\005\\016 \\354HE\\2453\\275\\334\\244\\261\"\n }\n memo {\n value: \"test memo\"\n }\n metadata {\n value: \"\\001\\002\\003\\004\\005\"\n }\n metadata_key {\n ed25519: \"\\024m\\354\\2222\\nnF\\353\\032Cv{\\261\\354\\225\\242\\346\\300%\\032\\260\\335x\\017\\343tt\\324\\272\\304\\025\"\n }\n name: \"test name\"\n pause_key {\n ed25519: \"\\321he\\251\\214\\370\\260\\267\\370\\3727v\\262\\r\\257\\305\\276\\004\\377\\353\\232$\\227r\\a\\203\\316\\231\\036+\\031t\"\n }\n supply_key {\n ed25519: \";\\2218S\\257\\245\\233U\\253\\305\\201\\302\\254\\r6X\\n\\302\\354\\244\\275\\020\\034\\002\\027?\\357\\002\\346w\\335\\325\"\n }\n symbol: \"test symbol\"\n token {\n realm_num: 2\n shard_num: 4\n token_num: 0\n }\n treasury {\n account_num: 7\n realm_num: 7\n shard_num: 7\n }\n wipe_key {\n ed25519: \"R[\\234\\025_\\220+\\221-\\275\\201\\276\\246\\324:\\a}zb\\335\\037\\357\\317\\307}\\351aD\\325\\372\\303\\356\"\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TokenWipeTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/TokenWipeTransactionTest.java new file mode 100644 index 0000000000..ffb0c19492 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenWipeTransactionTest.java @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TokenWipeAccountTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TokenWipeTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + private static final AccountId testAccountId = AccountId.fromString("0.6.9"); + private static final TokenId testTokenId = TokenId.fromString("4.2.0"); + private static final long testAmount = 4L; + private static final List testSerialNumbers = Arrays.asList(8L, 9L, 10L); + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerializeFungible() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new TokenWipeTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + private TokenWipeTransaction spawnTestTransaction() { + return new TokenWipeTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setTokenId(TokenId.fromString("0.0.111")) + .setAccountId(testAccountId) + .setAmount(testAmount) + .setSerials(testSerialNumbers) + .setMaxTransactionFee(new Hbar(1)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldSerializeNft() { + SnapshotMatcher.expect(spawnTestTransactionNft().toString()).toMatchSnapshot(); + } + + private TokenWipeTransaction spawnTestTransactionNft() { + return new TokenWipeTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setTokenId(TokenId.fromString("0.0.111")) + .setAccountId(testAccountId) + .setSerials(Collections.singletonList(444L)) + .setMaxTransactionFee(new Hbar(1)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytesFungible() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = TokenWipeTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldBytesNft() throws Exception { + var tx = spawnTestTransactionNft(); + var tx2 = TokenWipeTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setTokenWipe(TokenWipeAccountTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(TokenWipeTransaction.class); + } + + @Test + void constructTokenWipeTransactionFromTransactionBodyProtobuf() { + var transactionBody = TokenWipeAccountTransactionBody.newBuilder() + .setToken(testTokenId.toProtobuf()) + .setAccount(testAccountId.toProtobuf()) + .setAmount(testAmount) + .addAllSerialNumbers(testSerialNumbers) + .build(); + + var txBody = TransactionBody.newBuilder().setTokenWipe(transactionBody).build(); + var tokenWipeTransaction = new TokenWipeTransaction(txBody); + + assertThat(tokenWipeTransaction.getTokenId()).isEqualTo(testTokenId); + assertThat(tokenWipeTransaction.getAccountId()).isEqualTo(testAccountId); + assertThat(tokenWipeTransaction.getAmount()).isEqualTo(testAmount); + assertThat(tokenWipeTransaction.getSerials()).isEqualTo(testSerialNumbers); + } + + @Test + void getSetTokenId() { + var tokenWipeTransaction = new TokenWipeTransaction().setTokenId(testTokenId); + assertThat(tokenWipeTransaction.getTokenId()).isEqualTo(testTokenId); + } + + @Test + void getSetTokenIdFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setTokenId(testTokenId)); + } + + @Test + void getSetAccountId() { + var tx = spawnTestTransaction(); + assertThat(tx.getAccountId()).isEqualTo(testAccountId); + } + + @Test + void getSetAccountIdFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setAccountId(testAccountId)); + } + + @Test + void getSetAmount() { + var tx = spawnTestTransaction(); + assertThat(tx.getAmount()).isEqualTo(testAmount); + } + + @Test + void getSetAmountFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setAmount(testAmount)); + } + + @Test + void getSetSerialNumbers() { + var tx = spawnTestTransaction(); + assertThat(tx.getSerials()).isEqualTo(testSerialNumbers); + } + + @Test + void getSetSerialNumbersFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setSerials(testSerialNumbers)); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TokenWipeTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/TokenWipeTransactionTest.snap new file mode 100644 index 0000000000..42ba8f25ad --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TokenWipeTransactionTest.snap @@ -0,0 +1,8 @@ +org.hiero.sdk.TokenWipeTransactionTest.shouldSerializeFungible=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_wipe {\n account {\n account_num: 9\n realm_num: 6\n shard_num: 0\n }\n amount: 4\n serial_numbers: 8\n serial_numbers: 9\n serial_numbers: 10\n token {\n realm_num: 0\n shard_num: 0\n token_num: 111\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] + + +org.hiero.sdk.TokenWipeTransactionTest.shouldSerializeNft=[ + "# org.hiero.sdk.proto.TransactionBody\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntoken_wipe {\n account {\n account_num: 9\n realm_num: 6\n shard_num: 0\n }\n amount: 0\n serial_numbers: 444\n token {\n realm_num: 0\n shard_num: 0\n token_num: 111\n }\n}\ntransaction_fee: 100000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TopicCreateTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/TopicCreateTransactionTest.java new file mode 100644 index 0000000000..034f51bc89 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TopicCreateTransactionTest.java @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Duration; +import java.time.Instant; +import java.util.Arrays; +import org.hiero.sdk.proto.ConsensusCreateTopicTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TopicCreateTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new TopicCreateTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + private TopicCreateTransaction spawnTestTransaction() { + return new TopicCreateTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setSubmitKey(unusedPrivateKey) + .setAdminKey(unusedPrivateKey) + .setAutoRenewAccountId(AccountId.fromString("0.0.5007")) + .setAutoRenewPeriod(Duration.ofHours(24)) + .setMaxTransactionFee(Hbar.fromTinybars(100_000)) + .setTopicMemo("hello memo") + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = TopicCreateTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setConsensusCreateTopic( + ConsensusCreateTopicTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(TopicCreateTransaction.class); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TopicCreateTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/TopicCreateTransactionTest.snap new file mode 100644 index 0000000000..3bac9e0702 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TopicCreateTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.TopicCreateTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nconsensus_create_topic {\n admin_key {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n auto_renew_account {\n account_num: 5007\n realm_num: 0\n shard_num: 0\n }\n auto_renew_period {\n seconds: 86400\n }\n memo: \"hello memo\"\n submit_key {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TopicDeleteTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/TopicDeleteTransactionTest.java new file mode 100644 index 0000000000..d7e0ad9fa2 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TopicDeleteTransactionTest.java @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import org.hiero.sdk.proto.ConsensusDeleteTopicTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TopicDeleteTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + private TopicDeleteTransaction spawnTestTransaction() { + return new TopicDeleteTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setTopicId(TopicId.fromString("0.0.5007")) + .setMaxTransactionFee(Hbar.fromTinybars(100_000)) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new TopicDeleteTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = TopicDeleteTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setConsensusDeleteTopic( + ConsensusDeleteTopicTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(TopicDeleteTransaction.class); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TopicDeleteTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/TopicDeleteTransactionTest.snap new file mode 100644 index 0000000000..247b1ffb70 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TopicDeleteTransactionTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.TopicDeleteTransactionTest.shouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nconsensus_delete_topic {\n topic_i_d {\n realm_num: 0\n shard_num: 0\n topic_num: 5007\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 100000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TopicIdTest.java b/sdk/src/test/java/org/hiero/sdk/TopicIdTest.java new file mode 100644 index 0000000000..6ff267108c --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TopicIdTest.java @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import io.github.jsonSnapshot.SnapshotMatcher; +import org.bouncycastle.util.encoders.Hex; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +class TopicIdTest { + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerializeFromString() { + SnapshotMatcher.expect(TopicId.fromString("0.0.5005").toString()).toMatchSnapshot(); + } + + @Test + void toBytes() throws InvalidProtocolBufferException { + SnapshotMatcher.expect(Hex.toHexString(new TopicId(5005).toBytes())).toMatchSnapshot(); + } + + @Test + void fromBytes() throws InvalidProtocolBufferException { + SnapshotMatcher.expect(TopicId.fromBytes(new TopicId(5005).toBytes()).toString()) + .toMatchSnapshot(); + } + + @Test + void fromSolidityAddress() { + SnapshotMatcher.expect(TokenId.fromSolidityAddress("000000000000000000000000000000000000138D") + .toString()) + .toMatchSnapshot(); + } + + @Test + void toSolidityAddress() { + SnapshotMatcher.expect(new TokenId(5005).toSolidityAddress()).toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TopicIdTest.snap b/sdk/src/test/java/org/hiero/sdk/TopicIdTest.snap new file mode 100644 index 0000000000..25f2f4e9b6 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TopicIdTest.snap @@ -0,0 +1,23 @@ +org.hiero.sdk.TopicIdTest.fromBytes=[ + "0.0.5005" +] + + +org.hiero.sdk.TopicIdTest.fromSolidityAddress=[ + "0.0.5005" +] + + +org.hiero.sdk.TopicIdTest.shouldSerializeFromString=[ + "0.0.5005" +] + + +org.hiero.sdk.TopicIdTest.toBytes=[ + "188d27" +] + + +org.hiero.sdk.TopicIdTest.toSolidityAddress=[ + "000000000000000000000000000000000000138d" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TopicInfoQueryTest.java b/sdk/src/test/java/org/hiero/sdk/TopicInfoQueryTest.java new file mode 100644 index 0000000000..7a2af98627 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TopicInfoQueryTest.java @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.github.jsonSnapshot.SnapshotMatcher; +import org.hiero.sdk.proto.QueryHeader; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TopicInfoQueryTest { + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + var builder = org.hiero.sdk.proto.Query.newBuilder(); + new TopicInfoQuery() + .setTopicId(TopicId.fromString("0.0.5005")) + .onMakeRequest(builder, QueryHeader.newBuilder().build()); + SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")) + .toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TopicInfoQueryTest.snap b/sdk/src/test/java/org/hiero/sdk/TopicInfoQueryTest.snap new file mode 100644 index 0000000000..330a7523d0 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TopicInfoQueryTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.TopicInfoQueryTest.shouldSerialize=[ + "# org.hiero.sdk.proto.Query\nconsensus_get_topic_info {\n header {\n }\n topic_i_d {\n realm_num: 0\n shard_num: 0\n topic_num: 5005\n }\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TopicInfoTest.java b/sdk/src/test/java/org/hiero/sdk/TopicInfoTest.java new file mode 100644 index 0000000000..2c8886eae7 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TopicInfoTest.java @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Duration; +import java.time.Instant; +import org.bouncycastle.util.encoders.Hex; +import org.hiero.sdk.proto.ConsensusGetTopicInfoResponse; +import org.hiero.sdk.proto.ConsensusTopicInfo; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TopicInfoTest { + private static final PrivateKey privateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + private static final byte[] hash = {2}; + + private static final ConsensusGetTopicInfoResponse info = ConsensusGetTopicInfoResponse.newBuilder() + .setTopicInfo(ConsensusTopicInfo.newBuilder() + .setMemo("1") + .setRunningHash(ByteString.copyFrom(hash)) + .setSequenceNumber(3) + .setExpirationTime(InstantConverter.toProtobuf(Instant.ofEpochMilli(4))) + .setAutoRenewPeriod(DurationConverter.toProtobuf(Duration.ofDays(5))) + .setAdminKey(privateKey.getPublicKey().toProtobufKey()) + .setSubmitKey(privateKey.getPublicKey().toProtobufKey()) + .setAutoRenewAccount(new AccountId(4).toProtobuf()) + .setLedgerId(LedgerId.TESTNET.toByteString())) + .build(); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void fromProtobuf() throws InvalidProtocolBufferException { + SnapshotMatcher.expect(TopicInfo.fromProtobuf(info).toString()).toMatchSnapshot(); + } + + @Test + void toProtobuf() throws InvalidProtocolBufferException { + SnapshotMatcher.expect(TopicInfo.fromProtobuf(info).toProtobuf().toString()) + .toMatchSnapshot(); + } + + @Test + void fromBytes() throws InvalidProtocolBufferException { + SnapshotMatcher.expect(TopicInfo.fromBytes(info.toByteArray()).toString()) + .toMatchSnapshot(); + } + + @Test + void toBytes() { + SnapshotMatcher.expect(Hex.toHexString(TopicInfo.fromProtobuf(info).toBytes())) + .toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TopicInfoTest.snap b/sdk/src/test/java/org/hiero/sdk/TopicInfoTest.snap new file mode 100644 index 0000000000..0cfc1ac308 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TopicInfoTest.snap @@ -0,0 +1,18 @@ +org.hiero.sdk.TopicInfoTest.fromBytes=[ + "TopicInfo{topicId=0.0.0, topicMemo=1, runningHash=[2], sequenceNumber=3, expirationTime=1970-01-01T00:00:00.004Z, adminKey=302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7, submitKey=302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7, autoRenewPeriod=PT120H, autoRenewAccountId=0.0.4, ledgerId=testnet}" +] + + +org.hiero.sdk.TopicInfoTest.fromProtobuf=[ + "TopicInfo{topicId=0.0.0, topicMemo=1, runningHash=[2], sequenceNumber=3, expirationTime=1970-01-01T00:00:00.004Z, adminKey=302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7, submitKey=302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7, autoRenewPeriod=PT120H, autoRenewAccountId=0.0.4, ledgerId=testnet}" +] + + +org.hiero.sdk.TopicInfoTest.toBytes=[ + "12002a640a013112010218032205108092f4012a221220e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b732221220e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b73a040880af1a420218044a0101" +] + + +org.hiero.sdk.TopicInfoTest.toProtobuf=[ + "# org.hiero.sdk.proto.ConsensusGetTopicInfoResponse@a2d5b392\ntopic_i_d {\n realm_num: 0\n shard_num: 0\n topic_num: 0\n}\ntopic_info {\n admin_key {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n auto_renew_account {\n account_num: 4\n realm_num: 0\n shard_num: 0\n }\n auto_renew_period {\n seconds: 432000\n }\n expiration_time {\n nanos: 4000000\n seconds: 0\n }\n ledger_id: \"\\001\"\n memo: \"1\"\n running_hash: \"\\002\"\n sequence_number: 3\n submit_key {\n ed25519: \"\\340\\310\\354\\'X\\245\\207\\237\\372\\302&\\241<\\fQky\\236r\\343QA\\240\\335\\202\\217\\224\\323y\\210\\244\\267\"\n }\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TopicMessageChunkTest.java b/sdk/src/test/java/org/hiero/sdk/TopicMessageChunkTest.java new file mode 100644 index 0000000000..2393c635ac --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TopicMessageChunkTest.java @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.google.protobuf.ByteString; +import java.time.Instant; +import org.hiero.sdk.proto.ConsensusMessageChunkInfo; +import org.hiero.sdk.proto.Timestamp; +import org.hiero.sdk.proto.mirror.ConsensusTopicResponse; +import org.junit.jupiter.api.Test; + +public class TopicMessageChunkTest { + + private static final Instant testTimestamp = Instant.ofEpochSecond(1554158542); + private static final byte[] testContents = new byte[] {0x01, 0x02, 0x03}; + private static final byte[] testRunningHash = new byte[] {0x04, 0x05, 0x06}; + private static final long testSequenceNumber = 7L; + private static final TransactionId testTransactionId = new TransactionId(new AccountId(1), testTimestamp); + + @Test + void constructWithArgs() { + var consensusTopicResponse = ConsensusTopicResponse.newBuilder() + .setConsensusTimestamp(Timestamp.newBuilder().setSeconds(testTimestamp.getEpochSecond())) + .setMessage(ByteString.copyFrom(testContents)) + .setRunningHash(ByteString.copyFrom(testRunningHash)) + .setSequenceNumber(testSequenceNumber) + .setChunkInfo(ConsensusMessageChunkInfo.newBuilder() + .setInitialTransactionID(testTransactionId.toProtobuf()) + .build()) + .build(); + + TopicMessageChunk topicMessageChunk = new TopicMessageChunk(consensusTopicResponse); + + assertThat(topicMessageChunk.consensusTimestamp).isEqualTo(testTimestamp); + assertThat(topicMessageChunk.contentSize).isEqualTo(testContents.length); + assertThat(topicMessageChunk.runningHash).isEqualTo(testRunningHash); + assertThat(topicMessageChunk.sequenceNumber).isEqualTo(testSequenceNumber); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TopicMessageQueryTest.java b/sdk/src/test/java/org/hiero/sdk/TopicMessageQueryTest.java new file mode 100644 index 0000000000..085b9b8a83 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TopicMessageQueryTest.java @@ -0,0 +1,472 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.google.common.base.Stopwatch; +import com.google.common.primitives.Longs; +import com.google.common.util.concurrent.Uninterruptibles; +import com.google.protobuf.ByteString; +import io.github.jsonSnapshot.SnapshotMatcher; +import io.grpc.Server; +import io.grpc.Status; +import io.grpc.StatusRuntimeException; +import io.grpc.inprocess.InProcessServerBuilder; +import io.grpc.stub.StreamObserver; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Queue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; +import org.assertj.core.api.Assertions; +import org.assertj.core.api.InstanceOfAssertFactories; +import org.hiero.sdk.proto.AccountID; +import org.hiero.sdk.proto.ConsensusMessageChunkInfo; +import org.hiero.sdk.proto.Timestamp; +import org.hiero.sdk.proto.TopicID; +import org.hiero.sdk.proto.TransactionID; +import org.hiero.sdk.proto.mirror.ConsensusServiceGrpc; +import org.hiero.sdk.proto.mirror.ConsensusTopicQuery; +import org.hiero.sdk.proto.mirror.ConsensusTopicResponse; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class TopicMessageQueryTest { + + private static final Instant START_TIME = Instant.now(); + + private Client client; + private final AtomicBoolean complete = new AtomicBoolean(false); + private final List errors = new ArrayList<>(); + private final List received = new ArrayList<>(); + private final ConsensusServiceStub consensusServiceStub = new ConsensusServiceStub(); + private Server server; + private TopicMessageQuery topicMessageQuery; + + @BeforeAll + static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @BeforeEach + void setup() throws Exception { + client = Client.forNetwork(Collections.emptyMap()); + client.setMirrorNetwork(List.of("in-process:test")); + server = InProcessServerBuilder.forName("test") + .addService(consensusServiceStub) + .directExecutor() + .build() + .start(); + topicMessageQuery = new TopicMessageQuery(); + topicMessageQuery.setCompletionHandler(() -> complete.set(true)); + topicMessageQuery.setEndTime(START_TIME.plusSeconds(100L)); + topicMessageQuery.setErrorHandler((t, r) -> errors.add(t)); + topicMessageQuery.setMaxBackoff(Duration.ofMillis(500L)); + topicMessageQuery.setStartTime(START_TIME); + topicMessageQuery.setTopicId(TopicId.fromString("0.0.1000")); + } + + @AfterEach + void teardown() throws Exception { + consensusServiceStub.verify(); + if (client != null) { + client.close(); + } + if (server != null) { + server.shutdown(); + server.awaitTermination(); + } + } + + @Test + @SuppressWarnings("NullAway") + void setCompletionHandlerNull() { + assertThatThrownBy(() -> topicMessageQuery.setCompletionHandler(null)) + .isInstanceOf(NullPointerException.class) + .hasMessage("completionHandler must not be null"); + } + + @Test + @SuppressWarnings("NullAway") + void setEndTimeNull() { + assertThatThrownBy(() -> topicMessageQuery.setEndTime(null)) + .isInstanceOf(NullPointerException.class) + .hasMessage("endTime must not be null"); + } + + @Test + @SuppressWarnings("NullAway") + void setErrorHandlerNull() { + assertThatThrownBy(() -> topicMessageQuery.setErrorHandler(null)) + .isInstanceOf(NullPointerException.class) + .hasMessage("errorHandler must not be null"); + } + + @Test + @SuppressWarnings("NullAway") + void setMaxAttemptsNegative() { + assertThatThrownBy(() -> topicMessageQuery.setMaxAttempts(-1)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("maxAttempts must be positive"); + } + + @Test + @SuppressWarnings("NullAway") + void setMaxBackoffNull() { + assertThatThrownBy(() -> topicMessageQuery.setMaxBackoff(null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("maxBackoff must be at least 500 ms"); + } + + @Test + void setMaxBackoffLow() { + assertThatThrownBy(() -> topicMessageQuery.setMaxBackoff(Duration.ofMillis(499L))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("maxBackoff must be at least 500 ms"); + } + + @Test + @SuppressWarnings("NullAway") + void setRetryHandlerNull() { + assertThatThrownBy(() -> topicMessageQuery.setRetryHandler(null)) + .isInstanceOf(NullPointerException.class) + .hasMessage("retryHandler must not be null"); + } + + @Test + @SuppressWarnings("NullAway") + void setStartTimeNull() { + assertThatThrownBy(() -> topicMessageQuery.setStartTime(null)) + .isInstanceOf(NullPointerException.class) + .hasMessage("startTime must not be null"); + } + + @Test + @SuppressWarnings("NullAway") + void setTopicIdNull() { + assertThatThrownBy(() -> topicMessageQuery.setTopicId(null)) + .isInstanceOf(NullPointerException.class) + .hasMessage("topicId must not be null"); + } + + @Test + @Timeout(3) + void subscribe() { + consensusServiceStub.requests.add(request().build()); + consensusServiceStub.responses.add(response(1L)); + consensusServiceStub.responses.add(response(2L)); + + subscribeToMirror(received::add); + + assertThat(errors).isEmpty(); + Assertions.assertThat(received) + .hasSize(2) + .extracting(t -> t.sequenceNumber) + .containsExactly(1L, 2L); + } + + @Test + @Timeout(3) + void subscribeChunked() { + ConsensusTopicResponse response1 = response(1L, 2); + ConsensusTopicResponse response2 = response(2L, 2); + consensusServiceStub.requests.add(request().build()); + consensusServiceStub.responses.add(response1); + consensusServiceStub.responses.add(response2); + + subscribeToMirror(received::add); + + var message = combine( + response1.getMessage().toByteArray(), response2.getMessage().toByteArray()); + assertThat(errors).isEmpty(); + Assertions.assertThat(received) + .hasSize(1) + .first() + .returns(toInstant(response2.getConsensusTimestamp()), t -> t.consensusTimestamp) + .returns( + response2.getChunkInfo().getInitialTransactionID(), + t -> Objects.requireNonNull(t.transactionId).toProtobuf()) + .returns(message, t -> t.contents) + .returns(response2.getRunningHash().toByteArray(), t -> t.runningHash) + .returns(response2.getSequenceNumber(), t -> t.sequenceNumber) + .extracting(t -> t.chunks) + .asInstanceOf(InstanceOfAssertFactories.ARRAY) + .hasSize(2) + .extracting(c -> ((TopicMessageChunk) c).sequenceNumber) + .contains(1L, 2L); + } + + @Test + @Timeout(3) + void subscribeNoResponse() { + consensusServiceStub.requests.add(request().build()); + + subscribeToMirror(received::add); + + assertThat(errors).isEmpty(); + Assertions.assertThat(received).isEmpty(); + } + + @Test + @Timeout(3) + void errorDuringOnNext() { + consensusServiceStub.requests.add(request().build()); + consensusServiceStub.responses.add(response(1L)); + + subscribeToMirror(t -> { + throw new RuntimeException(); + }); + + assertThat(errors).hasSize(1).first().isInstanceOf(RuntimeException.class); + Assertions.assertThat(received).isEmpty(); + } + + @ParameterizedTest(name = "Retry recovers w/ status {0} and description {1}") + @CsvSource({ + "INTERNAL, internal RST_STREAM error", + "INTERNAL, rst stream", + "NOT_FOUND, ", + "RESOURCE_EXHAUSTED, ", + "UNAVAILABLE, " + }) + @Timeout(3) + void retryRecovers(Status.Code code, String description) { + ConsensusTopicResponse response = response(1L); + Instant nextTimestamp = toInstant(response.getConsensusTimestamp()).plusNanos(1L); + ConsensusTopicQuery.Builder request = request(); + + consensusServiceStub.requests.add(request.build()); + consensusServiceStub.requests.add( + request.setConsensusStartTime(toTimestamp(nextTimestamp)).build()); + consensusServiceStub.responses.add(response); + consensusServiceStub.responses.add( + code.toStatus().withDescription(description).asRuntimeException()); + consensusServiceStub.responses.add(response(2L)); + + subscribeToMirror(received::add); + + Assertions.assertThat(received) + .hasSize(2) + .extracting(t -> t.sequenceNumber) + .containsExactly(1L, 2L); + assertThat(errors).isEmpty(); + } + + @ParameterizedTest(name = "No retry w/ status {0} and description {1}") + @CsvSource({"INTERNAL, internal first_stream error", "INTERNAL, internal error", "INTERNAL, ", "INVALID_ARGUMENT, " + }) + @Timeout(3) + void noRetry(Status.Code code, String description) { + consensusServiceStub.requests.add(request().build()); + consensusServiceStub.responses.add( + code.toStatus().withDescription(description).asRuntimeException()); + + subscribeToMirror(received::add); + + Assertions.assertThat(received).isEmpty(); + assertThat(errors) + .hasSize(1) + .first() + .isInstanceOf(StatusRuntimeException.class) + .extracting(t -> ((StatusRuntimeException) t).getStatus().getCode()) + .isEqualTo(code); + } + + @Test + @Timeout(3) + void customRetry() { + consensusServiceStub.requests.add(request().build()); + consensusServiceStub.requests.add(request().build()); + consensusServiceStub.responses.add(Status.INVALID_ARGUMENT.asRuntimeException()); + consensusServiceStub.responses.add(response(1L)); + topicMessageQuery.setRetryHandler(t -> true); + + subscribeToMirror(received::add); + + Assertions.assertThat(received) + .hasSize(1) + .extracting(t -> t.sequenceNumber) + .containsExactly(1L); + assertThat(errors).isEmpty(); + } + + @Test + @Timeout(3) + void retryWithLimit() { + ConsensusTopicResponse response = response(1L); + Instant nextTimestamp = toInstant(response.getConsensusTimestamp()).plusNanos(1L); + ConsensusTopicQuery.Builder request = request(); + topicMessageQuery.setLimit(2); + + consensusServiceStub.requests.add(request.setLimit(2L).build()); + consensusServiceStub.requests.add(request.setConsensusStartTime(toTimestamp(nextTimestamp)) + .setLimit(1L) + .build()); + consensusServiceStub.responses.add(response); + consensusServiceStub.responses.add(Status.RESOURCE_EXHAUSTED.asRuntimeException()); + consensusServiceStub.responses.add(response(2L)); + + subscribeToMirror(received::add); + + Assertions.assertThat(received) + .hasSize(2) + .extracting(t -> t.sequenceNumber) + .containsExactly(1L, 2L); + assertThat(errors).isEmpty(); + } + + @Test + @Timeout(3) + void retriesExhausted() { + topicMessageQuery.setMaxAttempts(1); + consensusServiceStub.requests.add(request().build()); + consensusServiceStub.requests.add(request().build()); + consensusServiceStub.responses.add(Status.RESOURCE_EXHAUSTED.asRuntimeException()); + consensusServiceStub.responses.add(Status.RESOURCE_EXHAUSTED.asRuntimeException()); + + subscribeToMirror(received::add); + + Assertions.assertThat(received).isEmpty(); + assertThat(errors) + .hasSize(1) + .first() + .isInstanceOf(StatusRuntimeException.class) + .extracting(t -> ((StatusRuntimeException) t).getStatus()) + .isEqualTo(Status.RESOURCE_EXHAUSTED); + } + + @Test + @Timeout(5) + void errorWhenCallIsCancelled() { + consensusServiceStub.requests.add(request().build()); + consensusServiceStub.responses.add(Status.CANCELLED.asRuntimeException()); + + subscribeToMirror(received::add); + + assertThat(errors) + .hasSize(1) + .first() + .isInstanceOf(StatusRuntimeException.class) + .extracting(t -> ((StatusRuntimeException) t).getStatus()) + .isEqualTo(Status.CANCELLED); + + Assertions.assertThat(received).isEmpty(); + } + + private void subscribeToMirror(Consumer onNext) { + SubscriptionHandle subscriptionHandle = topicMessageQuery.subscribe(client, onNext); + Stopwatch stopwatch = Stopwatch.createStarted(); + + while (!complete.get() && errors.isEmpty() && stopwatch.elapsed(TimeUnit.SECONDS) < 3) { + Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS); + } + + subscriptionHandle.unsubscribe(); + } + + private static ConsensusTopicQuery.Builder request() { + return ConsensusTopicQuery.newBuilder() + .setConsensusEndTime(toTimestamp(START_TIME.plusSeconds(100L))) + .setConsensusStartTime(toTimestamp(START_TIME)) + .setTopicID(TopicID.newBuilder().setTopicNum(1000).build()); + } + + private static ConsensusTopicResponse response(long sequenceNumber) { + return response(sequenceNumber, 0); + } + + private static ConsensusTopicResponse response(long sequenceNumber, int total) { + ConsensusTopicResponse.Builder consensusTopicResponseBuilder = ConsensusTopicResponse.newBuilder(); + + if (total > 0) { + var chunkInfo = ConsensusMessageChunkInfo.newBuilder() + .setInitialTransactionID(TransactionID.newBuilder() + .setAccountID( + AccountID.newBuilder().setAccountNum(3).build()) + .setTransactionValidStart(toTimestamp(START_TIME)) + .build()) + .setNumber((int) sequenceNumber) + .setTotal(total) + .build(); + consensusTopicResponseBuilder.setChunkInfo(chunkInfo); + } + + var message = ByteString.copyFrom(Longs.toByteArray(sequenceNumber)); + return consensusTopicResponseBuilder + .setConsensusTimestamp(toTimestamp(START_TIME.plusSeconds(sequenceNumber))) + .setSequenceNumber(sequenceNumber) + .setMessage(message) + .setRunningHash(message) + .setRunningHashVersion(2L) + .build(); + } + + private static Instant toInstant(Timestamp timestamp) { + return Instant.ofEpochSecond(timestamp.getSeconds(), timestamp.getNanos()); + } + + private static Timestamp toTimestamp(Instant instant) { + return Timestamp.newBuilder() + .setSeconds(instant.getEpochSecond()) + .setNanos(instant.getNano()) + .build(); + } + + private static class ConsensusServiceStub extends ConsensusServiceGrpc.ConsensusServiceImplBase { + + private final Queue requests = new ArrayDeque<>(); + private final Queue responses = new ArrayDeque<>(); + + @Override + public void subscribeTopic( + ConsensusTopicQuery consensusTopicQuery, StreamObserver streamObserver) { + var request = requests.poll(); + assertThat(request).isNotNull(); + assertThat(consensusTopicQuery).isEqualTo(request); + + while (!responses.isEmpty()) { + var response = responses.poll(); + assertThat(response).isNotNull(); + + if (response instanceof Throwable) { + streamObserver.onError((Throwable) response); + return; + } + + streamObserver.onNext((ConsensusTopicResponse) response); + } + + streamObserver.onCompleted(); + } + + public void verify() { + assertThat(requests).isEmpty(); + assertThat(responses).isEmpty(); + } + } + + private byte[] combine(byte[] array1, byte[] array2) { + byte[] joinedArray = new byte[array1.length + array2.length]; + System.arraycopy(array1, 0, joinedArray, 0, array1.length); + System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); + return joinedArray; + } +} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicMessageQueryTest.snap b/sdk/src/test/java/org/hiero/sdk/TopicMessageQueryTest.snap similarity index 100% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/TopicMessageQueryTest.snap rename to sdk/src/test/java/org/hiero/sdk/TopicMessageQueryTest.snap diff --git a/sdk/src/test/java/org/hiero/sdk/TopicMessageSubmitTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/TopicMessageSubmitTransactionTest.java new file mode 100644 index 0000000000..12da4ecda1 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TopicMessageSubmitTransactionTest.java @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.google.protobuf.ByteString; +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Arrays; +import org.hiero.sdk.proto.ConsensusSubmitMessageTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TopicMessageSubmitTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + private static final TopicId testTopicId = new TopicId(0, 6, 9); + private static final byte[] testMessageBytes = {0x04, 0x05, 0x06}; + private static final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + private TopicMessageSubmitTransaction spawnTestTransactionString() { + return new TopicMessageSubmitTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setTopicId(testTopicId) + .setMessage(new String(testMessageBytes)) + .freeze() + .sign(unusedPrivateKey); + } + + private TopicMessageSubmitTransaction spawnTestTransactionBytes() { + return new TopicMessageSubmitTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setTopicId(testTopicId) + .setMessage(testMessageBytes) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new TopicMessageSubmitTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setConsensusSubmitMessage( + ConsensusSubmitMessageTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(TopicMessageSubmitTransaction.class); + } + + @Test + void constructTopicMessageSubmitTransactionFromTransactionBodyProtobuf() { + var transactionBody = ConsensusSubmitMessageTransactionBody.newBuilder() + .setTopicID(testTopicId.toProtobuf()) + .setMessage(ByteString.copyFrom(testMessageBytes)) + .build(); + + var tx = TransactionBody.newBuilder() + .setConsensusSubmitMessage(transactionBody) + .build(); + + var topicSubmitMessageTransaction = new TopicMessageSubmitTransaction(tx); + assertThat(topicSubmitMessageTransaction.getTopicId()).isEqualTo(testTopicId); + } + + @Test + void getSetTopicId() { + var topicSubmitMessageTransaction = new TopicMessageSubmitTransaction().setTopicId(testTopicId); + assertThat(topicSubmitMessageTransaction.getTopicId()).isEqualTo(testTopicId); + } + + @Test + void getSetTopicIdFrozen() { + var tx = spawnTestTransactionString(); + assertThrows(IllegalStateException.class, () -> tx.setTopicId(testTopicId)); + } + + @Test + void getSetMessage() { + var topicSubmitMessageTransactionString = + new TopicMessageSubmitTransaction().setMessage(new String(testMessageBytes)); + var topicSubmitMessageTransactionBytes = new TopicMessageSubmitTransaction().setMessage(testMessageBytes); + assertThat(topicSubmitMessageTransactionString.getMessage().toByteArray()) + .isEqualTo(testMessageBytes); + assertThat(topicSubmitMessageTransactionBytes.getMessage().toByteArray()) + .isEqualTo(testMessageBytes); + } + + @Test + void getSetMessageFrozen() { + var topicSubmitMessageTransactionString = spawnTestTransactionString(); + var topicSubmitMessageTransactionBytes = spawnTestTransactionBytes(); + assertThrows( + IllegalStateException.class, () -> topicSubmitMessageTransactionString.setMessage(testMessageBytes)); + assertThrows( + IllegalStateException.class, () -> topicSubmitMessageTransactionBytes.setMessage(testMessageBytes)); + } +} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TopicMessageSubmitTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/TopicMessageSubmitTransactionTest.snap similarity index 100% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/TopicMessageSubmitTransactionTest.snap rename to sdk/src/test/java/org/hiero/sdk/TopicMessageSubmitTransactionTest.snap diff --git a/sdk/src/test/java/org/hiero/sdk/TopicMessageTest.java b/sdk/src/test/java/org/hiero/sdk/TopicMessageTest.java new file mode 100644 index 0000000000..7cfbd83d8c --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TopicMessageTest.java @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.google.protobuf.ByteString; +import java.time.Instant; +import java.util.List; +import org.hiero.sdk.proto.ConsensusMessageChunkInfo; +import org.hiero.sdk.proto.Timestamp; +import org.hiero.sdk.proto.mirror.ConsensusTopicResponse; +import org.junit.jupiter.api.Test; + +public class TopicMessageTest { + + private static final Instant testTimestamp = Instant.ofEpochSecond(1554158542); + private static final byte[] testContents = new byte[] {0x01, 0x02, 0x03}; + private static final byte[] testRunningHash = new byte[] {0x04, 0x05, 0x06}; + private static final long testSequenceNumber = 7L; + private static final TransactionId testTransactionId = new TransactionId(new AccountId(1), testTimestamp); + + @Test + void constructWithArgs() { + TopicMessageChunk topicMessageChunk = new TopicMessageChunk(ConsensusTopicResponse.newBuilder() + .setConsensusTimestamp(Timestamp.newBuilder().setSeconds(testTimestamp.getEpochSecond())) + .setRunningHash(ByteString.copyFrom(testRunningHash)) + .setSequenceNumber(testSequenceNumber) + .build()); + + TopicMessageChunk[] topicMessageChunkArr = {topicMessageChunk, topicMessageChunk, topicMessageChunk}; + + TopicMessage topicMessage = new TopicMessage( + testTimestamp, + testContents, + testRunningHash, + testSequenceNumber, + topicMessageChunkArr, + testTransactionId); + + assertThat(topicMessage.consensusTimestamp).isEqualTo(testTimestamp); + assertThat(topicMessage.contents).isEqualTo(testContents); + assertThat(topicMessage.runningHash).isEqualTo(testRunningHash); + assertThat(topicMessage.sequenceNumber).isEqualTo(testSequenceNumber); + assertThat(topicMessage.chunks).hasSize(3); + assertThat(topicMessage.transactionId).isEqualTo(testTransactionId); + } + + @Test + void ofSingle() { + var consensusTopicResponse = ConsensusTopicResponse.newBuilder() + .setConsensusTimestamp(Timestamp.newBuilder().setSeconds(testTimestamp.getEpochSecond())) + .setMessage(ByteString.copyFrom(testContents)) + .setRunningHash(ByteString.copyFrom(testRunningHash)) + .setSequenceNumber(testSequenceNumber) + .setChunkInfo(ConsensusMessageChunkInfo.newBuilder() + .setInitialTransactionID(testTransactionId.toProtobuf()) + .build()) + .build(); + + TopicMessage topicMessage = TopicMessage.ofSingle(consensusTopicResponse); + + assertThat(topicMessage.consensusTimestamp).isEqualTo(testTimestamp); + assertThat(topicMessage.contents).isEqualTo(testContents); + assertThat(topicMessage.runningHash).isEqualTo(testRunningHash); + assertThat(topicMessage.sequenceNumber).isEqualTo(testSequenceNumber); + assertThat(topicMessage.chunks).hasSize(1); + assertThat(topicMessage.transactionId).isEqualTo(testTransactionId); + } + + @Test + void ofMany() { + var consensusTopicResponse1 = ConsensusTopicResponse.newBuilder() + .setConsensusTimestamp(Timestamp.newBuilder().setSeconds(testTimestamp.getEpochSecond())) + .setMessage(ByteString.copyFrom(testContents)) + .setRunningHash(ByteString.copyFrom(testRunningHash)) + .setSequenceNumber(testSequenceNumber) + .setChunkInfo(ConsensusMessageChunkInfo.newBuilder() + .setInitialTransactionID(testTransactionId.toProtobuf()) + .setNumber(1) + .setTotal(2) + .build()) + .build(); + + var consensusTopicResponse2 = ConsensusTopicResponse.newBuilder() + .setConsensusTimestamp(Timestamp.newBuilder().setSeconds(testTimestamp.getEpochSecond() + 1)) + .setMessage(ByteString.copyFrom(testContents)) + .setRunningHash(ByteString.copyFrom(testRunningHash)) + .setSequenceNumber(testSequenceNumber + 1L) + .setChunkInfo(ConsensusMessageChunkInfo.newBuilder() + .setNumber(2) + .setTotal(2) + .build()) + .build(); + + TopicMessage topicMessage = TopicMessage.ofMany(List.of(consensusTopicResponse1, consensusTopicResponse2)); + + byte[] totalContents = new byte[testContents.length * 2]; + System.arraycopy(testContents, 0, totalContents, 0, testContents.length); + System.arraycopy(testContents, 0, totalContents, testContents.length, testContents.length); + assertThat(topicMessage.consensusTimestamp).isEqualTo(testTimestamp.plusSeconds(1)); + assertThat(topicMessage.contents).isEqualTo(totalContents); + assertThat(topicMessage.runningHash).isEqualTo(testRunningHash); + assertThat(topicMessage.sequenceNumber).isEqualTo(testSequenceNumber + 1L); + assertThat(topicMessage.chunks).hasSize(2); + assertThat(topicMessage.transactionId).isEqualTo(testTransactionId); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TopicUpdateTransactionTest.java b/sdk/src/test/java/org/hiero/sdk/TopicUpdateTransactionTest.java new file mode 100644 index 0000000000..1749304c14 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TopicUpdateTransactionTest.java @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.google.protobuf.StringValue; +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Duration; +import java.time.Instant; +import java.util.Arrays; +import org.hiero.sdk.proto.ConsensusUpdateTopicTransactionBody; +import org.hiero.sdk.proto.CryptoDeleteTransactionBody; +import org.hiero.sdk.proto.SchedulableTransactionBody; +import org.hiero.sdk.proto.Timestamp; +import org.hiero.sdk.proto.TransactionBody; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TopicUpdateTransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + private static final PublicKey testAdminKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e11") + .getPublicKey(); + private static final PublicKey testSubmitKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e12") + .getPublicKey(); + private static final TopicId testTopicId = TopicId.fromString("0.0.5007"); + private static final String testTopicMemo = "test memo"; + private static final Duration testAutoRenewPeriod = Duration.ofHours(10); + private static final Instant testExpirationTime = Instant.now(); + private static final AccountId testAutoRenewAccountId = AccountId.fromString("8.8.8"); + private static final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void clearShouldSerialize() { + SnapshotMatcher.expect(new TopicUpdateTransaction() + .setNodeAccountIds( + Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setTopicId(testTopicId) + .clearAdminKey() + .clearAutoRenewAccountId() + .clearSubmitKey() + .clearTopicMemo() + .freeze() + .sign(unusedPrivateKey) + .toString()) + .toMatchSnapshot(); + } + + @Test + void setShouldSerialize() { + SnapshotMatcher.expect(spawnTestTransaction().toString()).toMatchSnapshot(); + } + + @Test + void shouldBytesNoSetters() throws Exception { + var tx = new TopicUpdateTransaction(); + var tx2 = Transaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + private TopicUpdateTransaction spawnTestTransaction() { + return new TopicUpdateTransaction() + .setNodeAccountIds(Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006"))) + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5006"), validStart)) + .setTopicId(testTopicId) + .setAdminKey(testAdminKey) + .setAutoRenewAccountId(testAutoRenewAccountId) + .setAutoRenewPeriod(testAutoRenewPeriod) + .setSubmitKey(testSubmitKey) + .setTopicMemo(testTopicMemo) + .setExpirationTime(validStart) + .freeze() + .sign(unusedPrivateKey); + } + + @Test + void shouldBytes() throws Exception { + var tx = spawnTestTransaction(); + var tx2 = TopicUpdateTransaction.fromBytes(tx.toBytes()); + assertThat(tx2.toString()).isEqualTo(tx.toString()); + } + + @Test + void fromScheduledTransaction() { + var transactionBody = SchedulableTransactionBody.newBuilder() + .setConsensusUpdateTopic( + ConsensusUpdateTopicTransactionBody.newBuilder().build()) + .build(); + + var tx = Transaction.fromScheduledTransaction(transactionBody); + + assertThat(tx).isInstanceOf(TopicUpdateTransaction.class); + } + + @Test + void constructTopicUpdateTransactionFromTransactionBodyProtobuf() { + var transactionBody = ConsensusUpdateTopicTransactionBody.newBuilder() + .setTopicID(testTopicId.toProtobuf()) + .setMemo(StringValue.newBuilder().setValue(testTopicMemo).build()) + .setExpirationTime(Timestamp.newBuilder() + .setSeconds(testExpirationTime.getEpochSecond()) + .build()) + .setAdminKey(testAdminKey.toProtobufKey()) + .setSubmitKey(testSubmitKey.toProtobufKey()) + .setAutoRenewPeriod(org.hiero.sdk.proto.Duration.newBuilder() + .setSeconds(testAutoRenewPeriod.toSeconds()) + .build()) + .setAutoRenewAccount(testAutoRenewAccountId.toProtobuf()) + .build(); + + var tx = TransactionBody.newBuilder() + .setConsensusUpdateTopic(transactionBody) + .build(); + var topicUpdateTransaction = new TopicUpdateTransaction(tx); + + assertThat(topicUpdateTransaction.getTopicId()).isEqualTo(testTopicId); + assertThat(topicUpdateTransaction.getTopicMemo()).isEqualTo(testTopicMemo); + assertThat(topicUpdateTransaction.getExpirationTime().getEpochSecond()) + .isEqualTo(testExpirationTime.getEpochSecond()); + assertThat(topicUpdateTransaction.getAdminKey()).isEqualTo(testAdminKey); + assertThat(topicUpdateTransaction.getSubmitKey()).isEqualTo(testSubmitKey); + assertThat(topicUpdateTransaction.getAutoRenewPeriod().toSeconds()).isEqualTo(testAutoRenewPeriod.toSeconds()); + assertThat(topicUpdateTransaction.getAutoRenewAccountId()).isEqualTo(testAutoRenewAccountId); + } + + // doesn't throw an exception as opposed to C++ sdk + @Test + void constructTopicUpdateTransactionFromWrongTransactionBodyProtobuf() { + var transactionBody = CryptoDeleteTransactionBody.newBuilder().build(); + var tx = TransactionBody.newBuilder().setCryptoDelete(transactionBody).build(); + + new TopicUpdateTransaction(tx); + } + + @Test + void getSetTopicId() { + var topicUpdateTransaction = new TopicUpdateTransaction().setTopicId(testTopicId); + assertThat(topicUpdateTransaction.getTopicId()).isEqualTo(testTopicId); + } + + @Test + void getSetTopicIdFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setTopicId(testTopicId)); + } + + @Test + void getSetTopicMemo() { + var topicUpdateTransaction = new TopicUpdateTransaction().setTopicMemo(testTopicMemo); + assertThat(topicUpdateTransaction.getTopicMemo()).isEqualTo(testTopicMemo); + } + + @Test + void getSetTopicMemoFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setTopicMemo(testTopicMemo)); + } + + @Test + void clearTopicMemo() { + var topicUpdateTransaction = new TopicUpdateTransaction().setTopicMemo(testTopicMemo); + topicUpdateTransaction.clearTopicMemo(); + assertThat(topicUpdateTransaction.getTopicMemo()).isEmpty(); + } + + @Test + void clearTopicMemoFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.clearTopicMemo()); + } + + @Test + void getSetExpirationTime() { + var topicUpdateTransaction = new TopicUpdateTransaction().setExpirationTime(testExpirationTime); + assertThat(topicUpdateTransaction.getExpirationTime()).isEqualTo(testExpirationTime); + } + + @Test + void getSetExpirationTimeFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setExpirationTime(testExpirationTime)); + } + + @Test + void getSetAdminKey() { + var topicUpdateTransaction = new TopicUpdateTransaction().setAdminKey(testAdminKey); + assertThat(topicUpdateTransaction.getAdminKey()).isEqualTo(testAdminKey); + } + + @Test + void getSetAdminKeyFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setAdminKey(testAdminKey)); + } + + @Test + void clearAdminKey() { + var topicUpdateTransaction = new TopicUpdateTransaction().setAdminKey(testAdminKey); + topicUpdateTransaction.clearAdminKey(); + assertThat(topicUpdateTransaction.getAdminKey()).isEqualTo(new KeyList()); + } + + @Test + void clearAdminKeyFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.clearAdminKey()); + } + + @Test + void getSetSubmitKey() { + var topicUpdateTransaction = new TopicUpdateTransaction().setSubmitKey(testSubmitKey); + assertThat(topicUpdateTransaction.getSubmitKey()).isEqualTo(testSubmitKey); + } + + @Test + void getSetSubmitKeyFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setSubmitKey(testSubmitKey)); + } + + @Test + void clearSubmitKey() { + var topicUpdateTransaction = new TopicUpdateTransaction().setSubmitKey(testSubmitKey); + topicUpdateTransaction.clearSubmitKey(); + assertThat(topicUpdateTransaction.getSubmitKey()).isEqualTo(new KeyList()); + } + + @Test + void clearSubmitKeyFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.clearSubmitKey()); + } + + @Test + void getSetAutoRenewPeriod() { + var topicUpdateTransaction = new TopicUpdateTransaction().setAutoRenewPeriod(testAutoRenewPeriod); + assertThat(topicUpdateTransaction.getAutoRenewPeriod()).isEqualTo(testAutoRenewPeriod); + } + + @Test + void getSetAutoRenewPeriodFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setAutoRenewPeriod(testAutoRenewPeriod)); + } + + @Test + void getSetAutoRenewAccountId() { + var topicUpdateTransaction = new TopicUpdateTransaction().setAutoRenewAccountId(testAutoRenewAccountId); + assertThat(topicUpdateTransaction.getAutoRenewAccountId()).isEqualTo(testAutoRenewAccountId); + } + + @Test + void getSetAutoRenewAccountIdFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.setAutoRenewAccountId(testAutoRenewAccountId)); + } + + @Test + void clearAutoRenewAccountId() { + var topicUpdateTransaction = new TopicUpdateTransaction().setAutoRenewAccountId(testAutoRenewAccountId); + topicUpdateTransaction.clearAutoRenewAccountId(); + assertThat(topicUpdateTransaction.getAutoRenewAccountId()).isEqualTo(new AccountId(0)); + } + + @Test + void clearAutoRenewAccountIdFrozen() { + var tx = spawnTestTransaction(); + assertThrows(IllegalStateException.class, () -> tx.clearAutoRenewAccountId()); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TopicUpdateTransactionTest.snap b/sdk/src/test/java/org/hiero/sdk/TopicUpdateTransactionTest.snap new file mode 100644 index 0000000000..2f618a888b --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TopicUpdateTransactionTest.snap @@ -0,0 +1,8 @@ +org.hiero.sdk.TopicUpdateTransactionTest.clearShouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nconsensus_update_topic {\n admin_key {\n key_list {\n }\n }\n auto_renew_account {\n account_num: 0\n realm_num: 0\n shard_num: 0\n }\n memo {\n }\n submit_key {\n key_list {\n }\n }\n topic_i_d {\n realm_num: 0\n shard_num: 0\n topic_num: 5007\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 200000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] + + +org.hiero.sdk.TopicUpdateTransactionTest.setShouldSerialize=[ + "# org.hiero.sdk.proto.TransactionBody\nconsensus_update_topic {\n admin_key {\n ed25519: \"\\332\\207p\\020\\227\\206ns\\360\\335\\224,\\273>\\227\\0063)\\371\\005X\\206!\\261x\\322\\027Yh\\215G\\374\"\n }\n auto_renew_account {\n account_num: 8\n realm_num: 8\n shard_num: 8\n }\n auto_renew_period {\n seconds: 36000\n }\n expiration_time {\n seconds: 1554158542\n }\n memo {\n value: \"test memo\"\n }\n submit_key {\n ed25519: \"\\373\\210\\2637\\337\\327ea{\\3442*\\347\\357\\2053\\326\\036d\\203\\005\\016 \\354HE\\2453\\275\\334\\244\\261\"\n }\n topic_i_d {\n realm_num: 0\n shard_num: 0\n topic_num: 5007\n }\n}\nnode_account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n}\ntransaction_fee: 200000000\ntransaction_i_d {\n account_i_d {\n account_num: 5006\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n}\ntransaction_valid_duration {\n seconds: 120\n}" +] diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionIdTest.java b/sdk/src/test/java/org/hiero/sdk/TransactionIdTest.java similarity index 87% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionIdTest.java rename to sdk/src/test/java/org/hiero/sdk/TransactionIdTest.java index 17e212adf8..f5f80a14aa 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionIdTest.java +++ b/sdk/src/test/java/org/hiero/sdk/TransactionIdTest.java @@ -1,36 +1,17 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import com.google.protobuf.InvalidProtocolBufferException; import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import java.util.Objects; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import java.time.Instant; -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - class TransactionIdTest { @BeforeAll public static void beforeAll() { @@ -44,12 +25,16 @@ public static void afterAll() { @Test void shouldSerialize() { - SnapshotMatcher.expect(TransactionId.fromString("0.0.23847@1588539964.632521325").toString()).toMatchSnapshot(); + SnapshotMatcher.expect(TransactionId.fromString("0.0.23847@1588539964.632521325") + .toString()) + .toMatchSnapshot(); } @Test void shouldSerialize2() { - SnapshotMatcher.expect(TransactionId.fromString("0.0.23847@1588539964.632521325?scheduled/3").toString()).toMatchSnapshot(); + SnapshotMatcher.expect(TransactionId.fromString("0.0.23847@1588539964.632521325?scheduled/3") + .toString()) + .toMatchSnapshot(); } @Test @@ -165,7 +150,6 @@ void compare() { transactionId2 = new TransactionId(null, Instant.ofEpochSecond(1588539965)); assertThat(transactionId1.compareTo(transactionId2)).isEqualTo(-1); - transactionId1 = new TransactionId(null, null); transactionId2 = new TransactionId(null, null); assertThat(transactionId1).isEqualByComparingTo(transactionId2); @@ -173,13 +157,11 @@ void compare() { @Test void shouldFail() { - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy( - () -> TransactionId.fromString("0.0.23847.1588539964.632521325/4") - ); + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> TransactionId.fromString("0.0.23847.1588539964.632521325/4")); - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy( - () -> TransactionId.fromString("0.0.23847@1588539964/4") - ); + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> TransactionId.fromString("0.0.23847@1588539964/4")); } @Test diff --git a/sdk/src/test/java/org/hiero/sdk/TransactionIdTest.snap b/sdk/src/test/java/org/hiero/sdk/TransactionIdTest.snap new file mode 100644 index 0000000000..0e25e525e5 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TransactionIdTest.snap @@ -0,0 +1,8 @@ +org.hiero.sdk.TransactionIdTest.shouldSerialize2=[ + "0.0.23847@1588539964.632521325?scheduled/3" +] + + +org.hiero.sdk.TransactionIdTest.shouldSerialize=[ + "0.0.23847@1588539964.632521325" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TransactionReceiptQueryTest.java b/sdk/src/test/java/org/hiero/sdk/TransactionReceiptQueryTest.java new file mode 100644 index 0000000000..72b7c365a6 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TransactionReceiptQueryTest.java @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import org.hiero.sdk.proto.QueryHeader; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TransactionReceiptQueryTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + var builder = org.hiero.sdk.proto.Query.newBuilder(); + new TransactionReceiptQuery() + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5005"), validStart)) + .onMakeRequest(builder, QueryHeader.newBuilder().build()); + SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")) + .toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TransactionReceiptQueryTest.snap b/sdk/src/test/java/org/hiero/sdk/TransactionReceiptQueryTest.snap new file mode 100644 index 0000000000..076d50eef5 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TransactionReceiptQueryTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.TransactionReceiptQueryTest.shouldSerialize=[ + "# org.hiero.sdk.proto.Query\ntransaction_get_receipt {\n header {\n }\n transaction_i_d {\n account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n }\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TransactionReceiptTest.java b/sdk/src/test/java/org/hiero/sdk/TransactionReceiptTest.java new file mode 100644 index 0000000000..a30758604c --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TransactionReceiptTest.java @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.google.protobuf.ByteString; +import io.github.jsonSnapshot.SnapshotMatcher; +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TransactionReceiptTest { + static final Instant time = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + static TransactionReceipt spawnReceiptExample() { + return new TransactionReceipt( + null, + Status.SCHEDULE_ALREADY_DELETED, + new ExchangeRate(3, 4, time), + AccountId.fromString("1.2.3"), + FileId.fromString("4.5.6"), + ContractId.fromString("3.2.1"), + TopicId.fromString("9.8.7"), + TokenId.fromString("6.5.4"), + 3L, + ByteString.copyFrom("how now brown cow", StandardCharsets.UTF_8), + 30L, + ScheduleId.fromString("1.1.1"), + TransactionId.withValidStart(AccountId.fromString("3.3.3"), time), + List.of(1L, 2L, 3L), + 1, + new ArrayList<>(), + new ArrayList<>()); + } + + @Test + void shouldSerialize() throws Exception { + var originalTransactionReceipt = spawnReceiptExample(); + byte[] transactionReceiptBytes = originalTransactionReceipt.toBytes(); + var copyTransactionReceipt = TransactionReceipt.fromBytes(transactionReceiptBytes); + assertThat(copyTransactionReceipt.toString()).isEqualTo(originalTransactionReceipt.toString()); + SnapshotMatcher.expect(originalTransactionReceipt.toString()).toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionReceiptTest.snap b/sdk/src/test/java/org/hiero/sdk/TransactionReceiptTest.snap similarity index 90% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionReceiptTest.snap rename to sdk/src/test/java/org/hiero/sdk/TransactionReceiptTest.snap index 9a19e2b5d3..63e97b2235 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionReceiptTest.snap +++ b/sdk/src/test/java/org/hiero/sdk/TransactionReceiptTest.snap @@ -1,3 +1,3 @@ -com.hedera.hashgraph.sdk.TransactionReceiptTest.shouldSerialize=[ +org.hiero.sdk.TransactionReceiptTest.shouldSerialize=[ "TransactionReceipt{transactionId=null, status=SCHEDULE_ALREADY_DELETED, exchangeRate=ExchangeRate{hbars=3, cents=4, expirationTime=2019-04-01T22:42:22Z, exchangeRateInCents=1.3333333333333333}, accountId=1.2.3, fileId=4.5.6, contractId=3.2.1, topicId=9.8.7, tokenId=6.5.4, topicSequenceNumber=3, topicRunningHash=[54, 56, 54, 102, 55, 55, 50, 48, 54, 101, 54, 102, 55, 55, 50, 48, 54, 50, 55, 50, 54, 102, 55, 55, 54, 101, 50, 48, 54, 51, 54, 102, 55, 55], totalSupply=30, scheduleId=1.1.1, scheduledTransactionId=3.3.3@1554158542.000000000, serials=[1, 2, 3], nodeId=1, duplicates=[], children=[]}" -] \ No newline at end of file +] diff --git a/sdk/src/test/java/org/hiero/sdk/TransactionRecordQueryTest.java b/sdk/src/test/java/org/hiero/sdk/TransactionRecordQueryTest.java new file mode 100644 index 0000000000..f9ce024c62 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TransactionRecordQueryTest.java @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import io.github.jsonSnapshot.SnapshotMatcher; +import java.time.Instant; +import org.hiero.sdk.proto.QueryHeader; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TransactionRecordQueryTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + + final Instant validStart = Instant.ofEpochSecond(1554158542); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + @Test + void shouldSerialize() { + var builder = org.hiero.sdk.proto.Query.newBuilder(); + spawnQuery().onMakeRequest(builder, QueryHeader.newBuilder().build()); + SnapshotMatcher.expect(builder.build().toString().replaceAll("@[A-Za-z0-9]+", "")) + .toMatchSnapshot(); + } + + private TransactionRecordQuery spawnQuery() { + return new TransactionRecordQuery() + .setTransactionId(TransactionId.withValidStart(AccountId.fromString("0.0.5005"), validStart)) + .setIncludeChildren(true) + .setIncludeDuplicates(true); + } +} diff --git a/sdk/src/test/java/org/hiero/sdk/TransactionRecordQueryTest.snap b/sdk/src/test/java/org/hiero/sdk/TransactionRecordQueryTest.snap new file mode 100644 index 0000000000..546b1be2c4 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TransactionRecordQueryTest.snap @@ -0,0 +1,3 @@ +org.hiero.sdk.TransactionRecordQueryTest.shouldSerialize=[ + "# org.hiero.sdk.proto.Query\ntransaction_get_record {\n header {\n }\n include_child_records: true\n include_duplicates: true\n transaction_i_d {\n account_i_d {\n account_num: 5005\n realm_num: 0\n shard_num: 0\n }\n transaction_valid_start {\n seconds: 1554158542\n }\n }\n}" +] diff --git a/sdk/src/test/java/org/hiero/sdk/TransactionRecordTest.java b/sdk/src/test/java/org/hiero/sdk/TransactionRecordTest.java new file mode 100644 index 0000000000..338e1fb788 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TransactionRecordTest.java @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hiero.sdk.ContractFunctionResultTest.CALL_RESULT_HEX; +import static org.hiero.sdk.TransactionReceiptTest.spawnReceiptExample; + +import com.google.protobuf.ByteString; +import com.google.protobuf.BytesValue; +import io.github.jsonSnapshot.SnapshotMatcher; +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import javax.annotation.Nullable; +import org.bouncycastle.util.encoders.Hex; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TransactionRecordTest { + static final Instant time = Instant.ofEpochSecond(1554158542); + private static final byte[] callResult = Hex.decode(CALL_RESULT_HEX); + + @BeforeAll + public static void beforeAll() { + SnapshotMatcher.start(Snapshot::asJsonString); + } + + @AfterAll + public static void afterAll() { + SnapshotMatcher.validateSnapshots(); + } + + private static TransactionRecord spawnRecordExample(@Nullable ByteString prngBytes, @Nullable Integer prngNumber) { + return new TransactionRecord( + spawnReceiptExample(), + ByteString.copyFrom("hello", StandardCharsets.UTF_8), + time, + TransactionId.withValidStart(AccountId.fromString("3.3.3"), time), + "memo", + 3000L, + new ContractFunctionResult(org.hiero.sdk.proto.ContractFunctionResult.newBuilder() + .setContractID(ContractId.fromString("1.2.3").toProtobuf()) + .setContractCallResult(ByteString.copyFrom(callResult)) + .setEvmAddress(BytesValue.newBuilder() + .setValue(ByteString.copyFrom(Hex.decode("98329e006610472e6B372C080833f6D79ED833cf"))) + .build()) + .setSenderId(AccountId.fromString("1.2.3").toProtobuf())), + List.of(new Transfer(AccountId.fromString("4.4.4"), Hbar.from(5))), + Map.of(TokenId.fromString("6.6.6"), Map.of(AccountId.fromString("1.1.1"), 4L)), + List.of(new TokenTransfer(TokenId.fromString("8.9.10"), AccountId.fromString("1.2.3"), 4L, 3, true)), + Map.of( + TokenId.fromString("4.4.4"), + List.of(new TokenNftTransfer( + TokenId.fromString("4.4.4"), + AccountId.fromString("1.2.3"), + AccountId.fromString("3.2.1"), + 4L, + true))), + ScheduleId.fromString("3.3.3"), + List.of(new AssessedCustomFee( + 4L, + TokenId.fromString("4.5.6"), + AccountId.fromString("8.6.5"), + List.of(AccountId.fromString("3.3.3")))), + List.of(new TokenAssociation(TokenId.fromString("5.4.3"), AccountId.fromString("8.7.6"))), + PrivateKey.fromStringECDSA("8776c6b831a1b61ac10dac0304a2843de4716f54b1919bb91a2685d0fe3f3048") + .getPublicKey(), + new ArrayList<>(), + new ArrayList<>(), + time, + ByteString.copyFrom("Some hash", StandardCharsets.UTF_8), + List.of(new Transfer(AccountId.fromString("1.2.3"), Hbar.from(8))), + prngBytes, + prngNumber, + ByteString.copyFrom("0x00", StandardCharsets.UTF_8), + List.of( + new PendingAirdropRecord( + new PendingAirdropId( + AccountId.fromString("0.0.123"), + AccountId.fromString("0.0.124"), + NftId.fromString("0.0.5005/1234")), + 123), + new PendingAirdropRecord( + new PendingAirdropId( + AccountId.fromString("0.0.123"), + AccountId.fromString("0.0.124"), + TokenId.fromString("0.0.12345")), + 123))); + } + + @Test + void shouldSerialize() throws Exception { + var originalRecord = spawnRecordExample(ByteString.copyFrom("very random bytes", StandardCharsets.UTF_8), null); + byte[] recordBytes = originalRecord.toBytes(); + var copyRecord = TransactionRecord.fromBytes(recordBytes); + assertThat(copyRecord.toString()).isEqualTo(originalRecord.toString()); + SnapshotMatcher.expect(originalRecord.toString()).toMatchSnapshot(); + } + + @Test + void shouldSerialize2() throws Exception { + var originalRecord = spawnRecordExample(null, 4 /* chosen by fair dice roll. Guaranteed to be random */); + byte[] recordBytes = originalRecord.toBytes(); + var copyRecord = TransactionRecord.fromBytes(recordBytes); + assertThat(copyRecord.toString()).isEqualTo(originalRecord.toString()); + SnapshotMatcher.expect(originalRecord.toString()).toMatchSnapshot(); + } +} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionRecordTest.snap b/sdk/src/test/java/org/hiero/sdk/TransactionRecordTest.snap similarity index 97% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionRecordTest.snap rename to sdk/src/test/java/org/hiero/sdk/TransactionRecordTest.snap index d0e9381928..5452d7ec83 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionRecordTest.snap +++ b/sdk/src/test/java/org/hiero/sdk/TransactionRecordTest.snap @@ -1,8 +1,8 @@ -com.hedera.hashgraph.sdk.TransactionRecordTest.shouldSerialize2=[ +org.hiero.sdk.TransactionRecordTest.shouldSerialize2=[ "TransactionRecord{receipt=TransactionReceipt{transactionId=null, status=SCHEDULE_ALREADY_DELETED, exchangeRate=ExchangeRate{hbars=3, cents=4, expirationTime=2019-04-01T22:42:22Z, exchangeRateInCents=1.3333333333333333}, accountId=1.2.3, fileId=4.5.6, contractId=3.2.1, topicId=9.8.7, tokenId=6.5.4, topicSequenceNumber=3, topicRunningHash=[54, 56, 54, 102, 55, 55, 50, 48, 54, 101, 54, 102, 55, 55, 50, 48, 54, 50, 55, 50, 54, 102, 55, 55, 54, 101, 50, 48, 54, 51, 54, 102, 55, 55], totalSupply=30, scheduleId=1.1.1, scheduledTransactionId=3.3.3@1554158542.000000000, serials=[1, 2, 3], nodeId=1, duplicates=[], children=[]}, transactionHash=68656c6c6f, consensusTimestamp=2019-04-01T22:42:22Z, transactionId=3.3.3@1554158542.000000000, transactionMemo=memo, transactionFee=3000 tℏ, contractFunctionResult=ContractFunctionResult{contractId=1.2.3, evmAddress=1.2.98329e006610472e6b372c080833f6d79ed833cf, errorMessage=null, bloom=, gasUsed=0, logs=[], createdContractIds=[], stateChanges=[], gas=0, hbarAmount=0 tℏ, contractFunctionparametersBytes=, rawResult=00000000000000000000000000000000000000000000000000000000ffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000011223344556677889900aabbccddeeff00112233ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c642100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001448656c6c6f2c20776f726c642c20616761696e21000000000000000000000000, senderAccountId=1.2.3, contractNonces=[], signerNonce=0}, transfers=[Transfer{accountId=4.4.4, amount=5 ℏ}], tokenTransfers={6.6.6={1.1.1=4}}, tokenNftTransfers={4.4.4=[TokenNftTransfer{tokenId=4.4.4, sender=1.2.3, receiver=3.2.1, serial=4, isApproved=true}]}, scheduleRef=3.3.3, assessedCustomFees=[AssessedCustomFee{amount=4, tokenId=4.5.6, feeCollectorAccountId=8.6.5, payerAccountIdList=[3.3.3]}], automaticTokenAssociations=[TokenAssociation{tokenId=5.4.3, accountId=8.7.6}], aliasKey=3036301006072a8648ce3d020106052b8104000a03220002703a9370b0443be6ae7c507b0aec81a55e94e4a863b9655360bd65358caa6588, children=[], duplicates=[], parentConsensusTimestamp=2019-04-01T22:42:22Z, ethereumHash=536f6d652068617368, paidStakingRewards=[Transfer{accountId=1.2.3, amount=8 ℏ}], prngBytes=null, prngNumber=4, evmAddress=30783030, pendingAirdropRecords=[PendingAirdropRecord{pendingAirdropId=PendingAirdropId{sender=0.0.123, receiver=0.0.124, tokenId=null, nftId=0.0.5005/1234}, pendingAirdropAmount=123}, PendingAirdropRecord{pendingAirdropId=PendingAirdropId{sender=0.0.123, receiver=0.0.124, tokenId=0.0.12345, nftId=null}, pendingAirdropAmount=123}]}" ] -com.hedera.hashgraph.sdk.TransactionRecordTest.shouldSerialize=[ +org.hiero.sdk.TransactionRecordTest.shouldSerialize=[ "TransactionRecord{receipt=TransactionReceipt{transactionId=null, status=SCHEDULE_ALREADY_DELETED, exchangeRate=ExchangeRate{hbars=3, cents=4, expirationTime=2019-04-01T22:42:22Z, exchangeRateInCents=1.3333333333333333}, accountId=1.2.3, fileId=4.5.6, contractId=3.2.1, topicId=9.8.7, tokenId=6.5.4, topicSequenceNumber=3, topicRunningHash=[54, 56, 54, 102, 55, 55, 50, 48, 54, 101, 54, 102, 55, 55, 50, 48, 54, 50, 55, 50, 54, 102, 55, 55, 54, 101, 50, 48, 54, 51, 54, 102, 55, 55], totalSupply=30, scheduleId=1.1.1, scheduledTransactionId=3.3.3@1554158542.000000000, serials=[1, 2, 3], nodeId=1, duplicates=[], children=[]}, transactionHash=68656c6c6f, consensusTimestamp=2019-04-01T22:42:22Z, transactionId=3.3.3@1554158542.000000000, transactionMemo=memo, transactionFee=3000 tℏ, contractFunctionResult=ContractFunctionResult{contractId=1.2.3, evmAddress=1.2.98329e006610472e6b372c080833f6d79ed833cf, errorMessage=null, bloom=, gasUsed=0, logs=[], createdContractIds=[], stateChanges=[], gas=0, hbarAmount=0 tℏ, contractFunctionparametersBytes=, rawResult=00000000000000000000000000000000000000000000000000000000ffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000011223344556677889900aabbccddeeff00112233ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c642100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001448656c6c6f2c20776f726c642c20616761696e21000000000000000000000000, senderAccountId=1.2.3, contractNonces=[], signerNonce=0}, transfers=[Transfer{accountId=4.4.4, amount=5 ℏ}], tokenTransfers={6.6.6={1.1.1=4}}, tokenNftTransfers={4.4.4=[TokenNftTransfer{tokenId=4.4.4, sender=1.2.3, receiver=3.2.1, serial=4, isApproved=true}]}, scheduleRef=3.3.3, assessedCustomFees=[AssessedCustomFee{amount=4, tokenId=4.5.6, feeCollectorAccountId=8.6.5, payerAccountIdList=[3.3.3]}], automaticTokenAssociations=[TokenAssociation{tokenId=5.4.3, accountId=8.7.6}], aliasKey=3036301006072a8648ce3d020106052b8104000a03220002703a9370b0443be6ae7c507b0aec81a55e94e4a863b9655360bd65358caa6588, children=[], duplicates=[], parentConsensusTimestamp=2019-04-01T22:42:22Z, ethereumHash=536f6d652068617368, paidStakingRewards=[Transfer{accountId=1.2.3, amount=8 ℏ}], prngBytes=766572792072616e646f6d206279746573, prngNumber=null, evmAddress=30783030, pendingAirdropRecords=[PendingAirdropRecord{pendingAirdropId=PendingAirdropId{sender=0.0.123, receiver=0.0.124, tokenId=null, nftId=0.0.5005/1234}, pendingAirdropAmount=123}, PendingAirdropRecord{pendingAirdropId=PendingAirdropId{sender=0.0.123, receiver=0.0.124, tokenId=0.0.12345, nftId=null}, pendingAirdropAmount=123}]}" -] \ No newline at end of file +] diff --git a/sdk/src/test/java/org/hiero/sdk/TransactionTest.java b/sdk/src/test/java/org/hiero/sdk/TransactionTest.java new file mode 100644 index 0000000000..d0900572a8 --- /dev/null +++ b/sdk/src/test/java/org/hiero/sdk/TransactionTest.java @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hiero.sdk.Transaction.fromBytes; + +import com.google.protobuf.InvalidProtocolBufferException; +import java.time.Instant; +import java.util.Arrays; +import java.util.List; +import org.bouncycastle.util.encoders.Hex; +import org.hiero.sdk.proto.SignedTransaction; +import org.hiero.sdk.proto.TokenAssociateTransactionBody; +import org.hiero.sdk.proto.TransactionBody; +import org.junit.jupiter.api.Test; + +public class TransactionTest { + private static final PrivateKey unusedPrivateKey = PrivateKey.fromString( + "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10"); + private static final List testNodeAccountIds = + Arrays.asList(AccountId.fromString("0.0.5005"), AccountId.fromString("0.0.5006")); + private static final AccountId testAccountId = AccountId.fromString("0.0.5006"); + private static final Instant validStart = Instant.ofEpochSecond(1554158542); + + @Test + void transactionFromBytesWorksWithProtobufTransactionBytes() throws InvalidProtocolBufferException { + var bytes = Hex.decode( + "1acc010a640a2046fe5013b6f6fc796c3e65ec10d2a10d03c07188fc3de13d46caad6b8ec4dfb81a4045f1186be5746c9783f68cb71d6a71becd3ffb024906b855ac1fa3a2601273d41b58446e5d6a0aaf421c229885f9e70417353fab2ce6e9d8e7b162e9944e19020a640a20f102e75ff7dc3d72c9b7075bb246fcc54e714c59714814011e8f4b922d2a6f0a1a40f2e5f061349ab03fa21075020c75cf876d80498ae4bac767f35941b8e3c393b0e0a886ede328e44c1df7028ea1474722f2dcd493812d04db339480909076a10122500a180a0c08a1cc98830610c092d09e0312080800100018e4881d120608001000180418b293072202087872240a220a0f0a080800100018e4881d10ff83af5f0a0f0a080800100018eb881d108084af5f"); + + var transaction = (TransferTransaction) fromBytes(bytes); + + assertThat(transaction.getHbarTransfers()).containsEntry(new AccountId(476260), new Hbar(1).negated()); + assertThat(transaction.getHbarTransfers()).containsEntry(new AccountId(476267), new Hbar(1)); + } + + @Test + void tokenAssociateTransactionFromTransactionBodyBytes() throws InvalidProtocolBufferException { + var tokenAssociateTransactionBodyProto = + TokenAssociateTransactionBody.newBuilder().build(); + var transactionBodyProto = TransactionBody.newBuilder() + .setTokenAssociate(tokenAssociateTransactionBodyProto) + .build(); + + TokenAssociateTransaction tokenAssociateTransaction = spawnTestTransaction(transactionBodyProto); + + var tokenAssociateTransactionFromBytes = Transaction.fromBytes(tokenAssociateTransaction.toBytes()); + + assertThat(tokenAssociateTransactionFromBytes).isInstanceOf(TokenAssociateTransaction.class); + } + + @Test + void tokenAssociateTransactionFromSignedTransactionBytes() throws InvalidProtocolBufferException { + var tokenAssociateTransactionBodyProto = + TokenAssociateTransactionBody.newBuilder().build(); + var transactionBodyProto = TransactionBody.newBuilder() + .setTokenAssociate(tokenAssociateTransactionBodyProto) + .build(); + + var signedTransactionProto = SignedTransaction.newBuilder() + .setBodyBytes(transactionBodyProto.toByteString()) + .build(); + var signedTransactionBodyProto = TransactionBody.parseFrom(signedTransactionProto.getBodyBytes()); + + TokenAssociateTransaction tokenAssociateTransaction = spawnTestTransaction(signedTransactionBodyProto); + + var tokenAssociateTransactionFromBytes = Transaction.fromBytes(tokenAssociateTransaction.toBytes()); + + assertThat(tokenAssociateTransactionFromBytes).isInstanceOf(TokenAssociateTransaction.class); + } + + @Test + void tokenAssociateTransactionFromTransactionBytes() throws InvalidProtocolBufferException { + var tokenAssociateTransactionBodyProto = + TokenAssociateTransactionBody.newBuilder().build(); + var transactionBodyProto = TransactionBody.newBuilder() + .setTokenAssociate(tokenAssociateTransactionBodyProto) + .build(); + + var signedTransactionProto = SignedTransaction.newBuilder() + .setBodyBytes(transactionBodyProto.toByteString()) + .build(); + var signedTransactionBodyProto = TransactionBody.parseFrom(signedTransactionProto.getBodyBytes()); + + var transactionSignedProto = org.hiero.sdk.proto.Transaction.newBuilder() + .setSignedTransactionBytes(signedTransactionBodyProto.toByteString()) + .build(); + var transactionSignedBodyProto = TransactionBody.parseFrom(transactionSignedProto.getSignedTransactionBytes()); + + TokenAssociateTransaction tokenAssociateTransaction = spawnTestTransaction(transactionSignedBodyProto); + + var tokenAssociateTransactionFromBytes = Transaction.fromBytes(tokenAssociateTransaction.toBytes()); + + assertThat(tokenAssociateTransactionFromBytes).isInstanceOf(TokenAssociateTransaction.class); + } + + private TokenAssociateTransaction spawnTestTransaction(TransactionBody txBody) { + return new TokenAssociateTransaction(txBody) + .setNodeAccountIds(testNodeAccountIds) + .setTransactionId(TransactionId.withValidStart(testAccountId, validStart)) + .freeze() + .sign(unusedPrivateKey); + } +} diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/logger/LoggerTest.java b/sdk/src/test/java/org/hiero/sdk/logger/LoggerTest.java similarity index 97% rename from sdk/src/test/java/com/hedera/hashgraph/sdk/logger/LoggerTest.java rename to sdk/src/test/java/org/hiero/sdk/logger/LoggerTest.java index 84956f1306..880252ca7e 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/logger/LoggerTest.java +++ b/sdk/src/test/java/org/hiero/sdk/logger/LoggerTest.java @@ -1,13 +1,14 @@ -package com.hedera.hashgraph.sdk.logger; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.logger; import static org.mockito.Mockito.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + class LoggerTest { private Logger logger; diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountAllowanceIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountAllowanceIntegrationTest.java deleted file mode 100644 index 8f4fb7eae2..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountAllowanceIntegrationTest.java +++ /dev/null @@ -1,96 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.AccountAllowanceApproveTransaction; -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.AccountDeleteTransaction; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.TransactionId; -import com.hedera.hashgraph.sdk.TransferTransaction; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThat; - -public class AccountAllowanceIntegrationTest { - @Test - @DisplayName("Can spend hbar allowance") - void canSpendHbarAllowance() throws Throwable { - try (var testEnv = new IntegrationTestEnv(1)) { - - var aliceKey = PrivateKey.generateED25519(); - var aliceId = new AccountCreateTransaction() - .setKey(aliceKey) - .setInitialBalance(new Hbar(10)) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .accountId; - - var bobKey = PrivateKey.generateED25519(); - var bobId = new AccountCreateTransaction() - .setKey(bobKey) - .setInitialBalance(new Hbar(10)) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .accountId; - - Objects.requireNonNull(aliceId); - Objects.requireNonNull(bobId); - - new AccountAllowanceApproveTransaction() - .approveHbarAllowance(bobId, aliceId, new Hbar(10)) - .freezeWith(testEnv.client) - .sign(bobKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var transferRecord = new TransferTransaction() - .addHbarTransfer(testEnv.operatorId, new Hbar(5)) - .addApprovedHbarTransfer(bobId, new Hbar(5).negated()) - .setTransactionId(TransactionId.generate(aliceId)) - .freezeWith(testEnv.client) - .sign(aliceKey) - .execute(testEnv.client) - .getRecord(testEnv.client); - - var transferFound = false; - for (var transfer : transferRecord.transfers) { - if (transfer.accountId.equals(testEnv.operatorId) && transfer.amount.equals(new Hbar(5))) { - transferFound = true; - break; - } - } - assertThat(transferFound).isTrue(); - - new AccountDeleteTransaction() - .setAccountId(bobId) - .setTransferAccountId(testEnv.operatorId) - .freezeWith(testEnv.client) - .sign(bobKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountBalanceIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountBalanceIntegrationTest.java deleted file mode 100644 index 0669f2a570..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountBalanceIntegrationTest.java +++ /dev/null @@ -1,262 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.AccountBalanceQuery; -import com.hedera.hashgraph.sdk.AccountId; -import com.hedera.hashgraph.sdk.Client; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.Status; -import com.hedera.hashgraph.sdk.TokenCreateTransaction; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Collections; -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -class AccountBalanceIntegrationTest { - @Test - @DisplayName("can connect to previewnwet with TLS") - void canConnectToPreviewnetWithTLS() throws Exception { - var client = Client.forPreviewnet() - .setTransportSecurity(true); - - boolean succeededAtLeastOnce = false; - - for (var entry : client.getNetwork().entrySet()) { - assertThat(entry.getKey().endsWith(":50212")).isTrue(); - - try { - new AccountBalanceQuery() - .setMaxAttempts(1) - .setNodeAccountIds(Collections.singletonList(entry.getValue())) - .setAccountId(entry.getValue()) - .execute(client); - System.out.println("succeeded for " + entry); - succeededAtLeastOnce = true; - } catch (Throwable error) { - System.out.println("failed for " + entry); - } - } - - client.close(); - assertThat(succeededAtLeastOnce).isTrue(); - } - - @Test - @DisplayName("can connect to testnet with TLS") - void canConnectToTestnetWithTLS() throws Exception { - var client = Client.forTestnet() - .setTransportSecurity(true); - - boolean succeededAtLeastOnce = false; - - for (var entry : client.getNetwork().entrySet()) { - assertThat(entry.getKey().endsWith(":50212")).isTrue(); - - try { - new AccountBalanceQuery() - .setMaxAttempts(1) - .setNodeAccountIds(Collections.singletonList(entry.getValue())) - .setAccountId(entry.getValue()) - .execute(client); - System.out.println("succeeded for " + entry); - succeededAtLeastOnce = true; - } catch (Throwable error) { - System.out.println("failed for " + entry); - } - } - - client.close(); - assertThat(succeededAtLeastOnce).isTrue(); - } - - @Test - @DisplayName("can connect to mainnet with TLS") - void canConnectToMainnetWithTLS() throws Exception { - var client = Client.forMainnet() - .setTransportSecurity(true); - - boolean succeededAtLeastOnce = false; - - for (var entry : client.getNetwork().entrySet()) { - assertThat(entry.getKey().endsWith(":50212")).isTrue(); - - try { - new AccountBalanceQuery() - .setMaxAttempts(1) - .setNodeAccountIds(Collections.singletonList(entry.getValue())) - .setAccountId(entry.getValue()) - .execute(client); - System.out.println("succeeded for " + entry); - succeededAtLeastOnce = true; - } catch (Throwable error) { - System.out.println("failed for " + entry); - System.out.println(error); - } - } - - client.close(); - assertThat(succeededAtLeastOnce).isTrue(); - } - - @Test - @DisplayName("can connect to previewnet with certificate verification off") - void cannotConnectToPreviewnetWhenNetworkNameIsNullAndCertificateVerificationIsEnabled() throws Exception { - var client = Client.forPreviewnet() - .setTransportSecurity(true) - .setVerifyCertificates(true) - .setNetworkName(null); - - assertThat(client.getNetwork().isEmpty()).isFalse(); - - for (var entry : client.getNetwork().entrySet()) { - assertThat(entry.getKey().endsWith(":50212")).isTrue(); - - assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> { - new AccountBalanceQuery() - .setNodeAccountIds(Collections.singletonList(entry.getValue())) - .setAccountId(entry.getValue()) - .execute(client); - }); - } - - client.close(); - } - - @Test - @DisplayName("Can fetch balance for client operator") - void canFetchBalanceForClientOperator() throws Exception { - try (IntegrationTestEnv testEnv = new IntegrationTestEnv(1)) { - - var balance = new AccountBalanceQuery() - .setAccountId(testEnv.operatorId) - .execute(testEnv.client); - - assertThat(balance.hbars.toTinybars() > 0).isTrue(); - - } - } - - @Test - @DisplayName("Can fetch cost for the query") - void getCostBalanceForClientOperator() throws Exception { - try (IntegrationTestEnv testEnv = new IntegrationTestEnv(1)) { - - var balance = new AccountBalanceQuery() - .setAccountId(testEnv.operatorId) - .setMaxQueryPayment(new Hbar(1)); - - var cost = balance.getCost(testEnv.client); - - var accBalance = balance.setQueryPayment(cost).execute(testEnv.client); - - assertThat(accBalance.hbars.toTinybars() > 0).isTrue(); - assertThat(cost.toTinybars()).isEqualTo(0); - - } - } - - @Test - @DisplayName("Can fetch cost for the query, big max set") - void getCostBigMaxBalanceForClientOperator() throws Exception { - try (IntegrationTestEnv testEnv = new IntegrationTestEnv(1)) { - - var balance = new AccountBalanceQuery() - .setAccountId(testEnv.operatorId) - .setMaxQueryPayment(new Hbar(1000000)); - - var cost = balance.getCost(testEnv.client); - - var accBalance = balance.setQueryPayment(cost).execute(testEnv.client); - - assertThat(accBalance.hbars.toTinybars() > 0).isTrue(); - - } - } - - @Test - @DisplayName("Can fetch cost for the query, very small max set") - void getCostSmallMaxBalanceForClientOperator() throws Exception { - try (IntegrationTestEnv testEnv = new IntegrationTestEnv(1)) { - - var balance = new AccountBalanceQuery() - .setAccountId(testEnv.operatorId) - .setMaxQueryPayment(Hbar.fromTinybars(1)); - - var cost = balance.getCost(testEnv.client); - - var accBalance = balance.setQueryPayment(cost).execute(testEnv.client); - - assertThat(accBalance.hbars.toTinybars() > 0).isTrue(); - - } - } - - @Test - @DisplayName("Cannot fetch balance for invalid account ID") - void canNotFetchBalanceForInvalidAccountId() throws Exception { - try (IntegrationTestEnv testEnv = new IntegrationTestEnv(1)) { - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new AccountBalanceQuery() - .setAccountId(AccountId.fromString("1.0.3")) - .execute(testEnv.client); - }).withMessageContaining(Status.INVALID_ACCOUNT_ID.toString()); - - } - } - - @Test - @DisplayName("Can fetch token balances for client operator") - void canFetchTokenBalancesForClientOperator() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var response = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setInitialSupply(10000) - .setDecimals(50) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client); - - var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); - - var query = new AccountBalanceQuery(); - var balance = query - .setAccountId(testEnv.operatorId) - .execute(testEnv.client); - - assertThat(balance.tokens.get(tokenId)).isEqualTo(10000); - assertThat(balance.tokenDecimals.get(tokenId)).isEqualTo(50); - assertThat(query.toString()).isNotEmpty(); - assertThat(query.getPaymentTransactionId()).isNull(); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountCreateIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountCreateIntegrationTest.java deleted file mode 100644 index 4dcd7886d2..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountCreateIntegrationTest.java +++ /dev/null @@ -1,393 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.*; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import java.time.Duration; -import java.time.Instant; - -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -class AccountCreateIntegrationTest { - @Test - @DisplayName("Can create account with only initial balance and key") - void canCreateAccountWithOnlyInitialBalanceAndKey() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - var info = new AccountInfoQuery() - .setAccountId(accountId) - .execute(testEnv.client); - - assertThat(info.accountId).isEqualTo(accountId); - assertThat(info.isDeleted).isFalse(); - assertThat(info.key.toString()).isEqualTo(key.getPublicKey().toString()); - assertThat(info.balance).isEqualTo(new Hbar(1)); - assertThat(info.autoRenewPeriod).isEqualTo(Duration.ofDays(90)); - assertThat(info.proxyAccountId).isNull(); - assertThat(info.proxyReceived).isEqualTo(Hbar.ZERO); - - } - } - - @Test - @DisplayName("Can create account with no initial balance") - void canCreateAccountWithNoInitialBalance() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - var info = new AccountInfoQuery() - .setAccountId(accountId) - .execute(testEnv.client); - - assertThat(info.accountId).isEqualTo(accountId); - assertThat(info.isDeleted).isFalse(); - assertThat(info.key.toString()).isEqualTo(key.getPublicKey().toString()); - assertThat(info.balance).isEqualTo(new Hbar(0)); - assertThat(info.autoRenewPeriod).isEqualTo(Duration.ofDays(90)); - assertThat(info.proxyAccountId).isNull(); - assertThat(info.proxyReceived).isEqualTo(Hbar.ZERO); - - } - } - - @Test - @DisplayName("Cannot create account with no key") - void canNotCreateAccountWithNoKey() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> new AccountCreateTransaction() - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client) - .getReceipt(testEnv.client)).withMessageContaining(Status.KEY_REQUIRED.toString()); - - } - } - - @Test - @DisplayName("Can create account using aliasKey") - void canCreateWithAliasKey() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var key = PrivateKey.generateED25519(); - - var aliasId = key.toAccountId(0, 0); - - new TransferTransaction() - .addHbarTransfer(testEnv.operatorId, new Hbar(10).negated()) - .addHbarTransfer(aliasId, new Hbar(10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var info = new AccountInfoQuery() - .setAccountId(aliasId) - .execute(testEnv.client); - - assertThat(key.getPublicKey()).isEqualTo(info.aliasKey); - - } - } - - @Test - @DisplayName("Regenerates TransactionIds in response to expiration") - void managesExpiration() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setTransactionId(new TransactionId(testEnv.operatorId, Instant.now().minusSeconds(40))) - .setTransactionValidDuration(Duration.ofSeconds(30)) - .freezeWith(testEnv.client) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - var info = new AccountInfoQuery() - .setAccountId(accountId) - .execute(testEnv.client); - - assertThat(info.accountId).isEqualTo(accountId); - assertThat(info.isDeleted).isFalse(); - assertThat(info.key.toString()).isEqualTo(key.getPublicKey().toString()); - assertThat(info.balance).isEqualTo(new Hbar(0)); - assertThat(info.autoRenewPeriod).isEqualTo(Duration.ofDays(90)); - assertThat(info.proxyAccountId).isNull(); - assertThat(info.proxyReceived).isEqualTo(Hbar.ZERO); - - } - } - - @Test - @DisplayName("Can create account with alias from admin key") - void createAccountWithAliasFromAdminKey() throws Exception { - // Tests the third row of this table - // https://github.com/hashgraph/hedera-improvement-proposal/blob/d39f740021d7da592524cffeaf1d749803798e9a/HIP/hip-583.md#signatures - try (var testEnv = new IntegrationTestEnv(1)) { - - var adminKey = PrivateKey.generateECDSA(); - var evmAddress = adminKey.getPublicKey().toEvmAddress(); - - // Create the admin account - new AccountCreateTransaction() - .setKey(adminKey) - .freezeWith(testEnv.client) - .execute(testEnv.client); - - var accountId = new AccountCreateTransaction() - .setKey(adminKey) - .setAlias(evmAddress) - .freezeWith(testEnv.client) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .accountId; - - assertThat(accountId).isNotNull(); - - var info = new AccountInfoQuery() - .setAccountId(accountId) - .execute(testEnv.client); - - assertThat(info.accountId).isNotNull(); - assertThat(info.contractAccountId).hasToString(evmAddress.toString()); - assertThat(info.key).isEqualTo(adminKey.getPublicKey()); - - } - } - - @Test - @DisplayName("Can create account with alias from admin key with receiver sig required") - void createAccountWithAliasFromAdminKeyWithReceiverSigRequired() throws Exception { - // Tests the fourth row of this table - // https://github.com/hashgraph/hedera-improvement-proposal/blob/d39f740021d7da592524cffeaf1d749803798e9a/HIP/hip-583.md#signatures - try (var testEnv = new IntegrationTestEnv(1)) { - - var adminKey = PrivateKey.generateECDSA(); - var evmAddress = adminKey.getPublicKey().toEvmAddress(); - - // Create the admin account - new AccountCreateTransaction() - .setKey(adminKey) - .freezeWith(testEnv.client) - .execute(testEnv.client); - - var accountId = new AccountCreateTransaction() - .setReceiverSignatureRequired(true) - .setKey(adminKey) - .setAlias(evmAddress) - .freezeWith(testEnv.client) - .sign(adminKey) - .execute(testEnv.client) - .getReceipt(testEnv.client).accountId; - - assertThat(accountId).isNotNull(); - - var info = new AccountInfoQuery() - .setAccountId(accountId) - .execute(testEnv.client); - - assertThat(info.accountId).isNotNull(); - assertThat(info.contractAccountId).hasToString(evmAddress.toString()); - assertThat(info.key).isEqualTo(adminKey.getPublicKey()); - - } - } - - @Test - @DisplayName("Cannot create account with alias from admin key with receiver sig required without signature") - void cannotCreateAccountWithAliasFromAdminKeyWithReceiverSigRequiredAndNoSignature() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var adminKey = PrivateKey.generateECDSA(); - var evmAddress = adminKey.getPublicKey().toEvmAddress(); - - // Create the admin account - new AccountCreateTransaction() - .setKey(adminKey) - .freezeWith(testEnv.client) - .execute(testEnv.client); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> new AccountCreateTransaction() - .setReceiverSignatureRequired(true) - .setKey(adminKey) - .setAlias(evmAddress) - .freezeWith(testEnv.client) - .execute(testEnv.client) - .getReceipt(testEnv.client)).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - } - } - - @Test - @DisplayName("Can create account with alias different from admin key") - void createAccountWithAlias() throws Exception { - // Tests the fifth row of this table - // https://github.com/hashgraph/hedera-improvement-proposal/blob/d39f740021d7da592524cffeaf1d749803798e9a/HIP/hip-583.md#signatures - try (var testEnv = new IntegrationTestEnv(1)) { - - var adminKey = PrivateKey.generateED25519(); - - // Create the admin account - new AccountCreateTransaction() - .setKey(adminKey) - .freezeWith(testEnv.client) - .execute(testEnv.client); - - var key = PrivateKey.generateECDSA(); - var evmAddress = key.getPublicKey().toEvmAddress(); - - var accountId = new AccountCreateTransaction() - .setKey(adminKey) - .setAlias(evmAddress) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client).accountId; - - assertThat(accountId).isNotNull(); - - var info = new AccountInfoQuery() - .setAccountId(accountId) - .execute(testEnv.client); - - assertThat(info.accountId).isNotNull(); - assertThat(info.contractAccountId).hasToString(evmAddress.toString()); - assertThat(info.key).isEqualTo(adminKey.getPublicKey()); - - } - } - - @Test - @DisplayName("Cannot create account with alias different from admin key without signature") - void cannotCreateAccountWithAliasWithoutSignature() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var adminKey = PrivateKey.generateED25519(); - - // Create the admin account - new AccountCreateTransaction() - .setKey(adminKey) - .freezeWith(testEnv.client) - .execute(testEnv.client); - - var key = PrivateKey.generateECDSA(); - var evmAddress = key.getPublicKey().toEvmAddress(); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> new AccountCreateTransaction() - .setReceiverSignatureRequired(true) - .setKey(adminKey) - .setAlias(evmAddress) - .freezeWith(testEnv.client) - .execute(testEnv.client) - .getReceipt(testEnv.client)).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - } - } - - @Test - @DisplayName("Can create account with alias different from admin key with receiver sig required") - void createAccountWithAliasWithReceiverSigRequired() throws Exception { - // Tests the sixth row of this table - // https://github.com/hashgraph/hedera-improvement-proposal/blob/d39f740021d7da592524cffeaf1d749803798e9a/HIP/hip-583.md#signatures - try (var testEnv = new IntegrationTestEnv(1)) { - - var adminKey = PrivateKey.generateED25519(); - - // Create the admin account - new AccountCreateTransaction() - .setKey(adminKey) - .freezeWith(testEnv.client) - .execute(testEnv.client); - - var key = PrivateKey.generateECDSA(); - var evmAddress = key.getPublicKey().toEvmAddress(); - - var accountId = new AccountCreateTransaction() - .setReceiverSignatureRequired(true) - .setKey(adminKey) - .setAlias(evmAddress) - .freezeWith(testEnv.client) - .sign(key) - .sign(adminKey) - .execute(testEnv.client) - .getReceipt(testEnv.client).accountId; - - assertThat(accountId).isNotNull(); - - var info = new AccountInfoQuery() - .setAccountId(accountId) - .execute(testEnv.client); - - assertThat(info.accountId).isNotNull(); - assertThat(info.contractAccountId).hasToString(evmAddress.toString()); - assertThat(info.key).isEqualTo(adminKey.getPublicKey()); - - } - } - - @Test - @DisplayName("Cannot create account with alias different from admin key and receiver sig required without signature") - void cannotCreateAccountWithAliasWithReceiverSigRequiredWithoutSignature() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var adminKey = PrivateKey.generateED25519(); - - // Create the admin account - new AccountCreateTransaction() - .setKey(adminKey) - .freezeWith(testEnv.client) - .execute(testEnv.client); - - var key = PrivateKey.generateECDSA(); - var evmAddress = key.getPublicKey().toEvmAddress(); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> new AccountCreateTransaction() - .setReceiverSignatureRequired(true) - .setKey(adminKey) - .setAlias(evmAddress) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client)).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountDeleteIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountDeleteIntegrationTest.java deleted file mode 100644 index a3ed868797..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountDeleteIntegrationTest.java +++ /dev/null @@ -1,108 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.AccountDeleteTransaction; -import com.hedera.hashgraph.sdk.AccountInfoQuery; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.sdk.Status; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import java.time.Duration; - -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -class AccountDeleteIntegrationTest { - @Test - @DisplayName("Can delete account") - void canDeleteAccount() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - var info = new AccountInfoQuery() - .setAccountId(accountId) - .execute(testEnv.client); - - assertThat(info.accountId).isEqualTo(accountId); - assertThat(info.isDeleted).isFalse(); - assertThat(info.key.toString()).isEqualTo(key.getPublicKey().toString()); - assertThat(info.balance).isEqualTo(new Hbar(1)); - assertThat(info.autoRenewPeriod).isEqualTo(Duration.ofDays(90)); - assertThat(info.proxyAccountId).isNull(); - assertThat(info.proxyReceived).isEqualTo(Hbar.ZERO); - - } - } - - @Test - @DisplayName("Cannot delete invalid account ID") - void cannotCreateAccountWithNoKey() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new AccountDeleteTransaction() - .setTransferAccountId(testEnv.operatorId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.ACCOUNT_ID_DOES_NOT_EXIST.toString()); - - } - } - - @Test - @DisplayName("Cannot delete account that has not signed transaction") - void cannotDeleteAccountThatHasNotSignedTransaction() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new AccountDeleteTransaction() - .setAccountId(accountId) - .setTransferAccountId(testEnv.operatorId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountIdPopulationIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountIdPopulationIntegrationTest.java deleted file mode 100644 index cff3131038..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountIdPopulationIntegrationTest.java +++ /dev/null @@ -1,140 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.hedera.hashgraph.sdk.AccountId; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.TransactionReceiptQuery; -import com.hedera.hashgraph.sdk.TransferTransaction; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -class AccountIdPopulationIntegrationTest { - @Test - @DisplayName("Can populate AccountId num from mirror node (using sync method)") - void canPopulateAccountIdNumSync() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var privateKey = PrivateKey.generateECDSA(); - var publicKey = privateKey.getPublicKey(); - - var evmAddress = publicKey.toEvmAddress(); - var evmAddressAccount = AccountId.fromEvmAddress(evmAddress); - - var tx = new TransferTransaction().addHbarTransfer(evmAddressAccount, new Hbar(1)) - .addHbarTransfer(testEnv.operatorId, new Hbar(-1)).execute(testEnv.client); - - var receipt = new TransactionReceiptQuery().setTransactionId(tx.transactionId).setIncludeChildren(true) - .execute(testEnv.client); - - var newAccountId = receipt.children.get(0).accountId; - - var idMirror = AccountId.fromEvmAddress(evmAddress); - Thread.sleep(5000); - var accountId = idMirror.populateAccountNum(testEnv.client); - - assertThat(newAccountId.num).isEqualTo(accountId.num); - } - } - - @Test - @DisplayName("Can populate AccountId num from mirror node (using async method)") - void canPopulateAccountIdNumAsync() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var privateKey = PrivateKey.generateECDSA(); - var publicKey = privateKey.getPublicKey(); - - var evmAddress = publicKey.toEvmAddress(); - var evmAddressAccount = AccountId.fromEvmAddress(evmAddress); - - var tx = new TransferTransaction().addHbarTransfer(evmAddressAccount, new Hbar(1)) - .addHbarTransfer(testEnv.operatorId, new Hbar(-1)).execute(testEnv.client); - - var receipt = new TransactionReceiptQuery().setTransactionId(tx.transactionId).setIncludeChildren(true) - .execute(testEnv.client); - - var newAccountId = receipt.children.get(0).accountId; - - var idMirror = AccountId.fromEvmAddress(evmAddress); - Thread.sleep(5000); - var accountId = idMirror.populateAccountNumAsync(testEnv.client).get(); - - assertThat(newAccountId.num).isEqualTo(accountId.num); - - } - } - - @Test - @DisplayName("Can populate AccountId evm address from mirror node (using sync method)") - void canPopulateAccountIdEvmAddressSync() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var privateKey = PrivateKey.generateECDSA(); - var publicKey = privateKey.getPublicKey(); - - var evmAddress = publicKey.toEvmAddress(); - var evmAddressAccount = AccountId.fromEvmAddress(evmAddress); - - var tx = new TransferTransaction().addHbarTransfer(evmAddressAccount, new Hbar(1)) - .addHbarTransfer(testEnv.operatorId, new Hbar(-1)).execute(testEnv.client); - - var receipt = new TransactionReceiptQuery().setTransactionId(tx.transactionId).setIncludeChildren(true) - .execute(testEnv.client); - - var newAccountId = receipt.children.get(0).accountId; - - Thread.sleep(5000); - var accountId = newAccountId.populateAccountEvmAddress(testEnv.client); - - assertThat(evmAddressAccount.evmAddress).isEqualTo(accountId.evmAddress); - } - } - - @Test - @DisplayName("Can populate AccountId evm address from mirror node (using async method)") - void canPopulateAccountIdEvmAddressAsync() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var privateKey = PrivateKey.generateECDSA(); - var publicKey = privateKey.getPublicKey(); - - var evmAddress = publicKey.toEvmAddress(); - var evmAddressAccount = AccountId.fromEvmAddress(evmAddress); - - var tx = new TransferTransaction().addHbarTransfer(evmAddressAccount, new Hbar(1)) - .addHbarTransfer(testEnv.operatorId, new Hbar(-1)).execute(testEnv.client); - - var receipt = new TransactionReceiptQuery().setTransactionId(tx.transactionId).setIncludeChildren(true) - .execute(testEnv.client); - - var newAccountId = receipt.children.get(0).accountId; - - Thread.sleep(5000); - var accountId = newAccountId.populateAccountEvmAddressAsync(testEnv.client).get(); - - assertThat(evmAddressAccount.evmAddress).isEqualTo(accountId.evmAddress); - } - } - -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountInfoIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountInfoIntegrationTest.java deleted file mode 100644 index 45d47016cc..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountInfoIntegrationTest.java +++ /dev/null @@ -1,153 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.AccountInfoFlow; -import com.hedera.hashgraph.sdk.AccountInfoQuery; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.PublicKey; -import com.hedera.hashgraph.sdk.Transaction; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.as; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -class AccountInfoIntegrationTest { - @Test - @DisplayName("Can query account info for client operator") - void canQueryAccountInfoForClientOperator() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var info = new AccountInfoQuery() - .setAccountId(testEnv.operatorId) - .execute(testEnv.client); - - assertThat(info.accountId).isEqualTo(testEnv.operatorId); - assertThat(info.isDeleted).isFalse(); - assertThat(info.key).isEqualTo(testEnv.operatorKey); - assertThat(info.balance.toTinybars()).isGreaterThan(0); - assertThat(info.proxyAccountId).isNull(); - assertThat(info.proxyReceived).isEqualTo(Hbar.ZERO); - - } - } - - @Test - @DisplayName("Can get cost for account info query") - void getCostAccountInfoForClientOperator() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var info = new AccountInfoQuery() - .setAccountId(testEnv.operatorId) - .setMaxQueryPayment(new Hbar(1)); - - var cost = info.getCost(testEnv.client); - - var accInfo = info.setQueryPayment(cost).execute(testEnv.client); - - assertThat(accInfo.accountId).isEqualTo(testEnv.operatorId); - - } - } - - @Test - @DisplayName("Can get cost for account info query, with a bix max") - void getCostBigMaxAccountInfoForClientOperator() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var info = new AccountInfoQuery() - .setAccountId(testEnv.operatorId) - .setMaxQueryPayment(Hbar.MAX); - - var cost = info.getCost(testEnv.client); - - var accInfo = info.setQueryPayment(cost).execute(testEnv.client); - - assertThat(accInfo.accountId).isEqualTo(testEnv.operatorId); - - } - } - - @Test - @Disabled - @DisplayName("Can get cost for account info query, with a small max") - void getCostSmallMaxAccountInfoForClientOperator() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var info = new AccountInfoQuery() - .setAccountId(testEnv.operatorId) - .setMaxQueryPayment(Hbar.fromTinybars(1)); - - var cost = info.getCost(testEnv.client); - - assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> { - info.execute(testEnv.client); - }).withMessage("com.hedera.hashgraph.sdk.MaxQueryPaymentExceededException: cost for AccountInfoQuery, of " + cost.toString() + ", without explicit payment is greater than the maximum allowed payment of 1 tℏ"); - - } - } - - @Test - @DisplayName("Insufficient tx fee error.") - void getCostInsufficientTxFeeAccountInfoForClientOperator() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var info = new AccountInfoQuery() - .setAccountId(testEnv.operatorId) - .setMaxQueryPayment(Hbar.fromTinybars(10000)); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - info.setQueryPayment(Hbar.fromTinybars(1)).execute(testEnv.client); - }).satisfies(error -> assertThat(error.status.toString()).isEqualTo("INSUFFICIENT_TX_FEE")); - - } - } - - @Test - @DisplayName("AccountInfoFlow.verify functions") - void accountInfoFlowVerifyFunctions() throws Throwable { - try (var testEnv = new IntegrationTestEnv(1)) { - - var newKey = PrivateKey.generateED25519(); - var newPublicKey = newKey.getPublicKey(); - - Transaction signedTx = new AccountCreateTransaction() - .setKey(newPublicKey) - .setInitialBalance(Hbar.fromTinybars(1000)) - .freezeWith(testEnv.client) - .signWithOperator(testEnv.client); - - Transaction unsignedTx = new AccountCreateTransaction() - .setKey(newPublicKey) - .setInitialBalance(Hbar.fromTinybars(1000)) - .freezeWith(testEnv.client); - - assertThat(AccountInfoFlow.verifyTransactionSignature(testEnv.client, testEnv.operatorId, signedTx)).isTrue(); - assertThat(AccountInfoFlow.verifyTransactionSignature(testEnv.client, testEnv.operatorId, unsignedTx)).isFalse(); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountRecordsIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountRecordsIntegrationTest.java deleted file mode 100644 index 9dc2a46976..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountRecordsIntegrationTest.java +++ /dev/null @@ -1,70 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.AccountRecordsQuery; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.TransferTransaction; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThat; - -class AccountRecordsIntegrationTest { - @Test - @DisplayName("Can query account records") - void canQueryAccountRecords() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - new TransferTransaction() - .addHbarTransfer(testEnv.operatorId, new Hbar(1).negated()) - .addHbarTransfer(accountId, new Hbar(1)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TransferTransaction() - .addHbarTransfer(testEnv.operatorId, new Hbar(1)) - .addHbarTransfer(accountId, new Hbar(1).negated()) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var records = new AccountRecordsQuery() - .setAccountId(testEnv.operatorId) - .execute(testEnv.client); - - assertThat(records.isEmpty()).isFalse(); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountStakersIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountStakersIntegrationTest.java deleted file mode 100644 index 222b6b5a75..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountStakersIntegrationTest.java +++ /dev/null @@ -1,46 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.AccountStakersQuery; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.Status; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -class AccountStakersIntegrationTest { - @Test - @DisplayName("Cannot query account stakers since it is not supported") - void cannotQueryAccountStakersSinceItIsNotSupported() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new AccountStakersQuery() - .setAccountId(testEnv.operatorId) - .setMaxQueryPayment(new Hbar(1)) - .execute(testEnv.client); - }).withMessageContaining(Status.NOT_SUPPORTED.toString()); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountUpdateIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountUpdateIntegrationTest.java deleted file mode 100644 index f2aff5de2f..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountUpdateIntegrationTest.java +++ /dev/null @@ -1,102 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.AccountInfoQuery; -import com.hedera.hashgraph.sdk.AccountUpdateTransaction; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.Status; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import java.time.Duration; - -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -class AccountUpdateIntegrationTest { - @Test - @DisplayName("Can update account with a new key") - void canUpdateAccountWithNewKey() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var key1 = PrivateKey.generateED25519(); - var key2 = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key1) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - var info = new AccountInfoQuery() - .setAccountId(accountId) - .execute(testEnv.client); - - assertThat(info.accountId).isEqualTo(accountId); - assertThat(info.isDeleted).isFalse(); - assertThat(info.key.toString()).isEqualTo(key1.getPublicKey().toString()); - assertThat(info.balance).isEqualTo(new Hbar(0)); - assertThat(info.autoRenewPeriod).isEqualTo(Duration.ofDays(90)); - assertThat(info.proxyAccountId).isNull(); - assertThat(info.proxyReceived).isEqualTo(Hbar.ZERO); - - new AccountUpdateTransaction() - .setAccountId(accountId) - .setKey(key2.getPublicKey()) - .freezeWith(testEnv.client) - .sign(key1) - .sign(key2) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - info = new AccountInfoQuery() - .setAccountId(accountId) - .execute(testEnv.client); - - assertThat(info.accountId).isEqualTo(accountId); - assertThat(info.isDeleted).isFalse(); - assertThat(info.key.toString()).isEqualTo(key2.getPublicKey().toString()); - assertThat(info.balance).isEqualTo(new Hbar(0)); - assertThat(info.autoRenewPeriod).isEqualTo(Duration.ofDays(90)); - assertThat(info.proxyAccountId).isNull(); - assertThat(info.proxyReceived).isEqualTo(Hbar.ZERO); - - } - } - - @Test - @DisplayName("Cannot update account when account ID is not set") - void cannotUpdateAccountWhenAccountIdIsNotSet() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new AccountUpdateTransaction() - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.ACCOUNT_ID_DOES_NOT_EXIST.toString()); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ClientIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ClientIntegrationTest.java deleted file mode 100644 index 08da7ffbbd..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ClientIntegrationTest.java +++ /dev/null @@ -1,265 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.*; -import java.util.Collections; -import java.util.List; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import java.time.Duration; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -public class ClientIntegrationTest { - - @Test - @DisplayName("fails when all the manually set nodes are not matching the address book") - void failsWhenNoNodesAreMatching() throws Exception { - var client = Client.forTestnet() - .setTransportSecurity(true); - - var nodes = new ArrayList(); - nodes.add(new AccountId(1000)); - nodes.add(new AccountId(1001)); - assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> new AccountBalanceQuery() - .setNodeAccountIds(nodes) - .setAccountId(new AccountId(7)) - .execute(client)).withMessageContaining("All node account IDs did not map to valid nodes in the client's network"); - client.close(); - } - - @Test - @DisplayName("can skip invalid nodes") - void canSkipNodes() throws Exception { - var client = Client.forTestnet() - .setTransportSecurity(true); - - var nodes = new ArrayList<>(client.getNetwork().values().stream().toList()); - nodes.add(new AccountId(1000)); - new AccountBalanceQuery() - .setNodeAccountIds(nodes) - .setAccountId(new AccountId(7)) - .execute(client); - - client.close(); - } - - @Test - @DisplayName("setNetwork() functions correctly") - void testReplaceNodes() throws Exception { - Map network = new HashMap<>(); - network.put("0.testnet.hedera.com:50211", new AccountId(3)); - network.put("1.testnet.hedera.com:50211", new AccountId(4)); - - try (var testEnv = new IntegrationTestEnv(1)) { - - testEnv.client - .setMaxQueryPayment(new Hbar(2)) - .setRequestTimeout(Duration.ofMinutes(2)) - .setNetwork(network); - - assertThat(testEnv.operatorId).isNotNull(); - - // Execute two simple queries so we create a channel for each network node. - new AccountBalanceQuery() - .setAccountId(new AccountId(3)) - .execute(testEnv.client); - - new AccountBalanceQuery() - .setAccountId(new AccountId(3)) - .execute(testEnv.client); - - network = new HashMap<>(); - network.put("1.testnet.hedera.com:50211", new AccountId(4)); - network.put("2.testnet.hedera.com:50211", new AccountId(5)); - - testEnv.client.setNetwork(network); - - network = new HashMap<>(); - network.put("35.186.191.247:50211", new AccountId(4)); - network.put("35.192.2.25:50211", new AccountId(5)); - - testEnv.client.setNetwork(network); - - } - } - - @Test - void transactionIdNetworkIsVerified() { - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { - var client = Client.forPreviewnet(); - client.setAutoValidateChecksums(true); - - new AccountCreateTransaction() - .setTransactionId(TransactionId.generate(AccountId.fromString("0.0.123-esxsf"))) - .execute(client); - client.close(); - }); - } - - @Test - @DisplayName("`setMaxNodesPerTransaction()`") - void testMaxNodesPerTransaction() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - testEnv.client.setMaxNodesPerTransaction(1); - - var transaction = new AccountDeleteTransaction() - .setAccountId(testEnv.operatorId) - .freezeWith(testEnv.client); - - assertThat(transaction.getNodeAccountIds()).isNotNull(); - assertThat(transaction.getNodeAccountIds().size()).isEqualTo(1); - - } - } - - @Test - void ping() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - var network = testEnv.client.getNetwork(); - var nodes = new ArrayList<>(network.values()); - - assertThat(nodes.isEmpty()).isFalse(); - - var node = nodes.get(0); - - testEnv.client.setMaxNodeAttempts(1); - testEnv.client.ping(node); - } - } - - @Test - void pingAll() throws Exception { - try (var testEnv = new IntegrationTestEnv()) { - - testEnv.client.setMaxNodeAttempts(1); - testEnv.client.pingAll(); - - var network = testEnv.client.getNetwork(); - var nodes = new ArrayList<>(network.values()); - - assertThat(nodes.isEmpty()).isFalse(); - - var node = nodes.get(0); - - new AccountBalanceQuery() - .setAccountId(node) - .execute(testEnv.client); - - } - } - - @Test - void pingAllBadNetwork() throws Exception { - try (var testEnv = new IntegrationTestEnv(3)) { - - // Skip if using local node. - // Note: this check should be removed once the local node is supporting multiple nodes. - testEnv.assumeNotLocalNode(); - - testEnv.client.setMaxNodeAttempts(1); - testEnv.client.setMaxAttempts(1); - testEnv.client.setMaxNodesPerTransaction(2); - - var network = testEnv.client.getNetwork(); - - var entries = new ArrayList<>(network.entrySet()); - assertThat(entries.size()).isGreaterThan(1); - - network.clear(); - network.put("in-process:name", entries.get(0).getValue()); - network.put(entries.get(1).getKey(), entries.get(1).getValue()); - - testEnv.client.setNetwork(network); - - assertThatExceptionOfType(MaxAttemptsExceededException.class).isThrownBy(() -> { - testEnv.client.pingAll(); - }).withMessageContaining("exceeded maximum attempts"); - - var nodes = new ArrayList<>(testEnv.client.getNetwork().values()); - assertThat(nodes.isEmpty()).isFalse(); - - var node = nodes.get(0); - - new AccountBalanceQuery() - .setAccountId(node) - .execute(testEnv.client); - - assertThat(testEnv.client.getNetwork().values().size()).isEqualTo(1); - } - - } - - @Test - void pingAsync() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - var network = testEnv.client.getNetwork(); - var nodes = new ArrayList<>(network.values()); - - assertThat(nodes.isEmpty()).isFalse(); - - var node = nodes.get(0); - - testEnv.client.setMaxNodeAttempts(1); - testEnv.client.pingAsync(node).get(); - } - } - - @Test - void pingAllAsync() throws Exception { - try (var testEnv = new IntegrationTestEnv()) { - - testEnv.client.setMaxNodeAttempts(1); - testEnv.client.pingAllAsync().get(); - - var network = testEnv.client.getNetwork(); - var nodes = new ArrayList<>(network.values()); - - assertThat(nodes.isEmpty()).isFalse(); - - var node = nodes.get(0); - - new AccountBalanceQuery() - .setAccountId(node) - .execute(testEnv.client); - - } - } - - @Test - @DisplayName("`forMirrorNetwork()`") - void testClientInitWithMirrorNetwork() throws Exception { - var mirrorNetworkString = "testnet.mirrornode.hedera.com:443"; - var client = Client.forMirrorNetwork(List.of(mirrorNetworkString)); - var mirrorNetwork = client.getMirrorNetwork(); - - assertThat(mirrorNetwork).hasSize(1); - assertThat(mirrorNetwork.get(0)).isEqualTo(mirrorNetworkString); - assertThat(client.getNetwork()).isNotNull(); - assertThat(client.getNetwork()).isNotEmpty(); - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/Contents.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/Contents.java deleted file mode 100644 index e3dadb7ca1..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/Contents.java +++ /dev/null @@ -1,71 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -public class Contents { - private Contents() { - } - - public static final String BIG_CONTENTS = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur aliquam augue sem, ut mattis dui laoreet a. Curabitur consequat est euismod, scelerisque metus et, tristique dui. Nulla commodo mauris ut faucibus ultricies. Quisque venenatis nisl nec augue tempus, at efficitur elit eleifend. Duis pharetra felis metus, sed dapibus urna vehicula id. Duis non venenatis turpis, sit amet ornare orci. Donec non interdum quam. Sed finibus nunc et risus finibus, non sagittis lorem cursus. Proin pellentesque tempor aliquam. Sed congue nisl in enim bibendum, condimentum vehicula nisi feugiat.\n" + - "\n" + - "Suspendisse non sodales arcu. Suspendisse sodales, lorem ac mollis blandit, ipsum neque porttitor nulla, et sodales arcu ante fermentum tellus. Integer sagittis dolor sed augue fringilla accumsan. Cras vitae finibus arcu, sit amet varius dolor. Etiam id finibus dolor, vitae luctus velit. Proin efficitur augue nec pharetra accumsan. Aliquam lobortis nisl diam, vel fermentum purus finibus id. Etiam at finibus orci, et tincidunt turpis. Aliquam imperdiet congue lacus vel facilisis. Phasellus id magna vitae enim dapibus vestibulum vitae quis augue. Morbi eu consequat enim. Maecenas neque nulla, pulvinar sit amet consequat sed, tempor sed magna. Mauris lacinia sem feugiat faucibus aliquet. Etiam congue non turpis at commodo. Nulla facilisi.\n" + - "\n" + - "Nunc velit turpis, cursus ornare fringilla eu, lacinia posuere leo. Mauris rutrum ultricies dui et suscipit. Curabitur in euismod ligula. Curabitur vitae faucibus orci. Phasellus volutpat vestibulum diam sit amet vestibulum. In vel purus leo. Nulla condimentum lectus vestibulum lectus faucibus, id lobortis eros consequat. Proin mollis libero elit, vel aliquet nisi imperdiet et. Morbi ornare est velit, in vehicula nunc malesuada quis. Donec vehicula convallis interdum.\n" + - "\n" + - "Integer pellentesque in nibh vitae aliquet. Ut at justo id libero dignissim hendrerit. Interdum et malesuada fames ac ante ipsum primis in faucibus. Praesent quis ornare lectus. Nam malesuada non diam quis cursus. Phasellus a libero ligula. Suspendisse ligula elit, congue et nisi sit amet, cursus euismod dolor. Morbi aliquam, nulla a posuere pellentesque, diam massa ornare risus, nec eleifend neque eros et elit.\n" + - "\n" + - "Pellentesque a sodales dui. Sed in efficitur ante. Duis eget volutpat elit, et ornare est. Nam felis dolor, placerat mattis diam id, maximus lobortis quam. Sed pellentesque lobortis sem sed placerat. Pellentesque augue odio, molestie sed lectus sit amet, congue volutpat massa. Quisque congue consequat nunc id fringilla. Duis semper nulla eget enim venenatis dapibus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Pellentesque varius turpis nibh, sit amet malesuada mauris malesuada quis. Vivamus dictum egestas placerat. Vivamus id augue elit.\n" + - "\n" + - "Cras fermentum volutpat eros, vitae euismod lorem viverra nec. Donec lectus augue, porta eleifend odio sit amet, condimentum rhoncus urna. Nunc sed odio velit. Morbi non cursus odio, eget imperdiet erat. Nunc rhoncus massa non neque volutpat, sit amet faucibus ante congue. Phasellus nec lorem vel leo accumsan lobortis. Maecenas id ligula bibendum purus suscipit posuere ac eget diam. Nam in quam pretium, semper erat auctor, iaculis odio. Maecenas placerat, nisi ac elementum tempor, felis nulla porttitor orci, ac rhoncus diam justo in elit. Etiam lobortis fermentum ligula in tincidunt. Donec quis vestibulum nunc. Sed eros diam, interdum in porta lobortis, gravida eu magna. Donec diam purus, finibus et sollicitudin quis, fringilla nec nisi. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Curabitur ultricies sagittis dapibus. Etiam ullamcorper aliquet libero, eu venenatis mauris suscipit id.\n" + - "\n" + - "Ut interdum eleifend sem, vel bibendum ipsum volutpat eget. Nunc ac dignissim augue. Aliquam ornare aliquet magna id dignissim. Vestibulum velit sem, lacinia eu rutrum in, rhoncus vitae mauris. Pellentesque scelerisque pulvinar mauris non cursus. Integer id dolor porta, bibendum sem vel, pretium tortor. Fusce a nisi convallis, interdum quam condimentum, suscipit dolor. Sed magna diam, efficitur non nunc in, tincidunt varius mi. Aliquam ullamcorper nulla eu fermentum bibendum. Vivamus a felis pretium, hendrerit enim vitae, hendrerit leo. Suspendisse lacinia lectus a diam consectetur suscipit. Aenean hendrerit nisl sed diam venenatis pellentesque. Nullam egestas lectus a consequat pharetra. Vivamus ornare tellus auctor, facilisis lacus id, feugiat dui. Nam id est ut est rhoncus varius.\n" + - "\n" + - "Aenean vel vehicula erat. Aenean gravida risus vitae purus sodales, quis dictum enim porta. Ut elit elit, fermentum sed posuere non, accumsan eu justo. Integer porta malesuada quam, et elementum massa suscipit nec. Donec in elit diam. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis suscipit vel ante volutpat vestibulum.\n" + - "\n" + - "Pellentesque ex arcu, euismod et sapien ut, vulputate suscipit enim. Donec mattis sagittis augue, et mattis lacus. Cras placerat consequat lorem sed luctus. Nam suscipit aliquam sem ac imperdiet. Mauris a semper augue, pulvinar fringilla magna. Integer pretium massa non risus commodo hendrerit. Sed dictum libero id erat sodales mattis. Etiam auctor dolor lectus, ut sagittis enim lobortis vitae. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur nec orci lobortis, cursus risus eget, sollicitudin massa. Integer vel tincidunt mi, id eleifend quam. Nullam facilisis nisl eu mauris congue, vitae euismod nisi malesuada. Vivamus sit amet urna et libero sagittis dictum.\n" + - "\n" + - "In hac habitasse platea dictumst. Aliquam erat volutpat. Ut dictum, mi a viverra venenatis, mi urna pulvinar nisi, nec gravida lectus diam eget urna. Ut dictum sit amet nisl ut dignissim. Sed sed mauris scelerisque, efficitur augue in, vulputate turpis. Proin dolor justo, bibendum et sollicitudin feugiat, imperdiet sed mi. Sed elementum vitae massa vel lobortis. Cras vitae massa sit amet libero dictum tempus.\n" + - "\n" + - "Vivamus ut mauris lectus. Curabitur placerat ornare scelerisque. Cras malesuada nunc quis tortor pretium bibendum vel sed dui. Cras lobortis nibh eu erat blandit, sit amet consequat neque fermentum. Phasellus imperdiet molestie tristique. Cras auctor purus erat, id mollis ligula porttitor eget. Mauris porta pharetra odio et finibus. Suspendisse eu est a ligula bibendum cursus. Mauris ac laoreet libero. Nullam volutpat sem quis rhoncus gravida.\n" + - "\n" + - "Donec malesuada lacus ac iaculis cursus. Sed sem orci, feugiat ac est ut, ultricies posuere nisi. Suspendisse potenti. Phasellus ut ultricies purus. Etiam sem tortor, fermentum quis aliquam eget, consequat ut nulla. Aliquam dictum metus in mi fringilla, vel gravida nulla accumsan. Cras aliquam eget leo vel posuere. Vivamus vitae malesuada nunc. Morbi placerat magna mi, id suscipit lacus auctor quis. Nam at lorem vel odio finibus fringilla ut ac velit. Donec laoreet at nibh quis vehicula.\n" + - "\n" + - "Fusce ac tristique nisi. Donec leo nisi, consectetur at tellus sit amet, consectetur ultrices dui. Quisque gravida mollis tempor. Maecenas semper, sapien ut dignissim feugiat, massa enim viverra dolor, non varius eros nulla nec felis. Nunc massa lacus, ornare et feugiat id, bibendum quis purus. Praesent viverra elit sit amet purus consectetur venenatis. Maecenas nibh risus, elementum sit amet enim sagittis, ultrices malesuada lectus. Vivamus non felis ante. Ut vulputate ex arcu. Aliquam porta gravida porta. Aliquam eros leo, malesuada quis eros non, maximus tristique neque. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam ligula orci, mollis vel luctus nec, venenatis vitae est. Fusce rutrum convallis nisi.\n" + - "\n" + - "Nunc laoreet eget nibh in feugiat. Pellentesque nec arcu cursus, gravida dolor a, pellentesque nisi. Praesent vel justo blandit, placerat risus eget, consectetur orci. Sed maximus metus mi, ut euismod augue ultricies in. Nunc id risus hendrerit, aliquet lorem nec, congue justo. Vestibulum vel nunc ac est euismod mattis ac vitae nulla. Donec blandit luctus mauris, sit amet bibendum dui egestas et. Aenean nec lorem nec elit ornare rutrum nec eget ligula. Fusce a ipsum vitae neque elementum pharetra. Pellentesque ullamcorper ullamcorper libero, vitae porta sem sagittis vel. Interdum et malesuada fames ac ante ipsum primis in faucibus.\n" + - "\n" + - "Duis at massa sit amet risus pellentesque varius sit amet vitae eros. Cras tempor aliquet sapien, vehicula varius neque volutpat et. Donec purus nibh, pellentesque ut lobortis nec, ultricies ultricies nisl. Sed accumsan ut dui sit amet vulputate. Suspendisse eu facilisis massa, a hendrerit mauris. Nulla elementum molestie tincidunt. Donec mi justo, ornare vel tempor id, gravida et orci. Nam molestie erat nec nisi bibendum accumsan. Duis vitae tempor ante. Morbi congue mauris vel sagittis facilisis. Vivamus vehicula odio orci, a tempor nibh euismod in. Proin mattis, nibh at feugiat porta, purus velit posuere felis, quis volutpat sapien dui vel odio. Nam fermentum sem nec euismod aliquet. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Aliquam erat volutpat.\n" + - "\n" + - "Mauris congue lacus tortor. Pellentesque arcu eros, accumsan imperdiet porttitor vitae, mattis nec justo. Nullam ac aliquam mauris. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Suspendisse potenti. Fusce accumsan tempus felis a sagittis. Maecenas et velit odio. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Aliquam eros lacus, volutpat non urna sed, tincidunt ullamcorper elit. Sed sit amet gravida libero. In varius mi vel diam sollicitudin mollis.\n" + - "\n" + - "Aenean varius, diam vitae hendrerit feugiat, libero augue ultrices odio, eget consequat sem tellus eu nisi. Nam dapibus enim et auctor sollicitudin. Nunc iaculis eros orci, ac accumsan eros malesuada ut. Ut semper augue felis, nec sodales lorem consectetur non. Cras gravida eleifend est, et sagittis eros imperdiet congue. Fusce id tellus dapibus nunc scelerisque tempus. Donec tempor placerat libero, in commodo nisi bibendum eu. Donec id eros non est sollicitudin luctus. Duis bibendum bibendum tellus nec viverra. Aliquam non faucibus ex, nec luctus dui. Curabitur efficitur varius urna non dignissim. Suspendisse elit elit, ultrices in elementum eu, tempor at magna.\n" + - "\n" + - "Nunc in purus sit amet mi laoreet pulvinar placerat eget sapien. Donec vel felis at dui ultricies euismod quis vel neque. Donec tincidunt urna non eros pretium blandit. Nullam congue tincidunt condimentum. Curabitur et libero nibh. Proin ultricies risus id imperdiet scelerisque. Suspendisse purus mi, viverra vitae risus ut, tempus tincidunt enim. Ut luctus lobortis nisl, eget venenatis tortor cursus non. Mauris finibus nisl quis gravida ultricies. Fusce elementum lacus sit amet nunc congue, in porta nulla tincidunt.\n" + - "\n" + - "Mauris ante risus, imperdiet blandit posuere in, blandit eu ipsum. Integer et auctor arcu. Integer quis elementum purus. Nunc in ultricies nisl, sed rutrum risus. Suspendisse venenatis eros nec lorem rhoncus, in scelerisque velit condimentum. Etiam condimentum quam felis, in elementum odio mattis et. In ut nibh porttitor, hendrerit tellus vel, luctus magna. Vestibulum et ligula et dolor pellentesque porta. Aenean efficitur porta massa et bibendum. Nulla vehicula sem in risus volutpat, a eleifend elit maximus.\n" + - "\n" + - "Proin orci lorem, auctor a felis eu, pretium lobortis nulla. Phasellus aliquam efficitur interdum. Sed sit amet velit rutrum est dictum facilisis. Duis cursus enim at nisl tincidunt, eu molestie elit vehicula. Cras pellentesque nisl id enim feugiat fringilla. In quis tincidunt neque. Nam eu consectetur dolor. Ut id interdum mauris. Mauris nunc tortor, placerat in tempor ut, vestibulum eu nisl. Integer vel dapibus ipsum. Nunc id erat pulvinar, tincidunt magna id, condimentum massa. Pellentesque consequat est eget odio placerat vehicula. Etiam augue neque, sagittis non leo eu, tristique scelerisque dui. Ut dui urna, blandit quis urna ac, tincidunt tristique turpis.\n" + - "\n" + - "Suspendisse venenatis rhoncus ligula ultrices condimentum. In id laoreet eros. Suspendisse suscipit fringilla leo id euismod. Sed in quam libero. Ut at ligula tellus. Sed tristique gravida dui, at egestas odio aliquam iaculis. Praesent imperdiet velit quis nibh consequat, quis pretium sem sagittis. Donec tortor ex, congue sit amet pulvinar ac, rutrum non est. Praesent ipsum magna, venenatis sit amet vulputate id, eleifend ac ipsum.\n" + - "\n" + - "In consequat, nisi iaculis laoreet elementum, massa mauris varius nisi, et porta nisi velit at urna. Maecenas sit amet aliquet eros, a rhoncus nisl. Maecenas convallis enim nunc. Morbi purus nisl, aliquam ac tincidunt sed, mattis in augue. Quisque et elementum quam, vitae maximus orci. Suspendisse hendrerit risus nec vehicula placerat. Nulla et lectus nunc. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.\n" + - "\n" + - "Etiam ut sodales ex. Nulla luctus, magna eu scelerisque sagittis, nibh quam consectetur neque, non rutrum dolor metus nec ex. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed egestas augue elit, sollicitudin accumsan massa lobortis ac. Curabitur placerat, dolor a aliquam maximus, velit ipsum laoreet ligula, id ullamcorper lacus nibh eget nisl. Donec eget lacus venenatis enim consequat auctor vel in.\n"; -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractBytecodeIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractBytecodeIntegrationTest.java deleted file mode 100644 index 1bbd481bf5..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractBytecodeIntegrationTest.java +++ /dev/null @@ -1,231 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.ContractByteCodeQuery; -import com.hedera.hashgraph.sdk.ContractCreateTransaction; -import com.hedera.hashgraph.sdk.ContractDeleteTransaction; -import com.hedera.hashgraph.sdk.ContractFunctionParameters; -import com.hedera.hashgraph.sdk.FileCreateTransaction; -import com.hedera.hashgraph.sdk.FileDeleteTransaction; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.MaxQueryPaymentExceededException; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.Status; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -public class ContractBytecodeIntegrationTest { - private static final String SMART_CONTRACT_BYTECODE = "608060405234801561001057600080fd5b506040516104d73803806104d78339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b506040525050600080546001600160a01b0319163317905550805161010890600190602084019061010f565b50506101aa565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061015057805160ff191683800117855561017d565b8280016001018555821561017d579182015b8281111561017d578251825591602001919060010190610162565b5061018992915061018d565b5090565b6101a791905b808211156101895760008155600101610193565b90565b61031e806101b96000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063368b87721461004657806341c0e1b5146100ee578063ce6d41de146100f6575b600080fd5b6100ec6004803603602081101561005c57600080fd5b81019060208101813564010000000081111561007757600080fd5b82018360208201111561008957600080fd5b803590602001918460018302840111640100000000831117156100ab57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610173945050505050565b005b6100ec6101a2565b6100fe6101ba565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610138578181015183820152602001610120565b50505050905090810190601f1680156101655780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000546001600160a01b0316331461018a5761019f565b805161019d906001906020840190610250565b505b50565b6000546001600160a01b03163314156101b85733ff5b565b60018054604080516020601f600260001961010087891615020190951694909404938401819004810282018101909252828152606093909290918301828280156102455780601f1061021a57610100808354040283529160200191610245565b820191906000526020600020905b81548152906001019060200180831161022857829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061029157805160ff19168380011785556102be565b828001600101855582156102be579182015b828111156102be5782518255916020019190600101906102a3565b506102ca9291506102ce565b5090565b61024d91905b808211156102ca57600081556001016102d456fea264697066735822122084964d4c3f6bc912a9d20e14e449721012d625aa3c8a12de41ae5519752fc89064736f6c63430006000033"; - - @Test - @DisplayName("Can query contract bytecode") - void canQueryContractBytecode() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - response = new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(200000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(testEnv.client); - - var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); - - var bytecode = new ContractByteCodeQuery() - .setContractId(contractId) - .execute(testEnv.client); - - assertThat(bytecode.size()).isEqualTo(798); - - new ContractDeleteTransaction() - .setTransferAccountId(testEnv.operatorId) - .setContractId(contractId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can get cost, even with a big max") - void getCostBigMaxQueryContractBytecode() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - response = new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(200000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(testEnv.client); - - var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); - - var bytecodeQuery = new ContractByteCodeQuery() - .setContractId(contractId) - .setMaxQueryPayment(new Hbar(1000)); - - var cost = bytecodeQuery.getCost(testEnv.client); - - var bytecode = bytecodeQuery.setQueryPayment(cost).execute(testEnv.client); - - assertThat(bytecode.size()).isEqualTo(798); - - new ContractDeleteTransaction() - .setTransferAccountId(testEnv.operatorId) - .setContractId(contractId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Error, max is smaller than set payment.") - void getCostSmallMaxQueryContractBytecode() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - response = new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(200000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(testEnv.client); - - var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); - - var bytecodeQuery = new ContractByteCodeQuery() - .setContractId(contractId) - .setMaxQueryPayment(Hbar.fromTinybars(1)); - - assertThatExceptionOfType(MaxQueryPaymentExceededException.class).isThrownBy(() -> { - bytecodeQuery.execute(testEnv.client); - }); - - new ContractDeleteTransaction() - .setTransferAccountId(testEnv.operatorId) - .setContractId(contractId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Insufficient tx fee error.") - void getCostInsufficientTxFeeQueryContractBytecode() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - response = new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(200000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(testEnv.client); - - var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); - - var bytecodeQuery = new ContractByteCodeQuery() - .setContractId(contractId) - .setMaxQueryPayment(new Hbar(100)); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - bytecodeQuery.setQueryPayment(Hbar.fromTinybars(1)).execute(testEnv.client); - }).satisfies(error -> assertThat(error.status.toString()).isEqualTo("INSUFFICIENT_TX_FEE")); - - new ContractDeleteTransaction() - .setTransferAccountId(testEnv.operatorId) - .setContractId(contractId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot query contract bytecode when contract ID is not set") - void cannotQueryContractBytecodeWhenContractIDIsNotSet() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new ContractByteCodeQuery() - .execute(testEnv.client); - }).withMessageContaining(Status.INVALID_CONTRACT_ID.toString()); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractCallIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractCallIntegrationTest.java deleted file mode 100644 index 141bf9467f..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractCallIntegrationTest.java +++ /dev/null @@ -1,396 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.ContractCallQuery; -import com.hedera.hashgraph.sdk.ContractCreateTransaction; -import com.hedera.hashgraph.sdk.ContractDeleteTransaction; -import com.hedera.hashgraph.sdk.ContractFunctionParameters; -import com.hedera.hashgraph.sdk.FileCreateTransaction; -import com.hedera.hashgraph.sdk.FileDeleteTransaction; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.MaxQueryPaymentExceededException; -import com.hedera.hashgraph.sdk.MirrorNodeContractEstimateGasQuery; -import com.hedera.hashgraph.sdk.MirrorNodeContractQuery; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.Status; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -public class ContractCallIntegrationTest { - private static final String SMART_CONTRACT_BYTECODE = "608060405234801561001057600080fd5b506040516104d73803806104d78339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b506040525050600080546001600160a01b0319163317905550805161010890600190602084019061010f565b50506101aa565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061015057805160ff191683800117855561017d565b8280016001018555821561017d579182015b8281111561017d578251825591602001919060010190610162565b5061018992915061018d565b5090565b6101a791905b808211156101895760008155600101610193565b90565b61031e806101b96000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063368b87721461004657806341c0e1b5146100ee578063ce6d41de146100f6575b600080fd5b6100ec6004803603602081101561005c57600080fd5b81019060208101813564010000000081111561007757600080fd5b82018360208201111561008957600080fd5b803590602001918460018302840111640100000000831117156100ab57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610173945050505050565b005b6100ec6101a2565b6100fe6101ba565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610138578181015183820152602001610120565b50505050905090810190601f1680156101655780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000546001600160a01b0316331461018a5761019f565b805161019d906001906020840190610250565b505b50565b6000546001600160a01b03163314156101b85733ff5b565b60018054604080516020601f600260001961010087891615020190951694909404938401819004810282018101909252828152606093909290918301828280156102455780601f1061021a57610100808354040283529160200191610245565b820191906000526020600020905b81548152906001019060200180831161022857829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061029157805160ff19168380011785556102be565b828001600101855582156102be579182015b828111156102be5782518255916020019190600101906102a3565b506102ca9291506102ce565b5090565b61024d91905b808211156102ca57600081556001016102d456fea264697066735822122084964d4c3f6bc912a9d20e14e449721012d625aa3c8a12de41ae5519752fc89064736f6c63430006000033"; - - @Test - @DisplayName("Can call contract function") - void canCallContractFunction() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - response = new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(200000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(testEnv.client); - - var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); - - // Wait for mirror node to import data - Thread.sleep(2000); - - var gas = new MirrorNodeContractEstimateGasQuery() - .setContractId(contractId) - .setFunction("getMessage") - .execute(testEnv.client); - - var callQuery = new ContractCallQuery() - .setContractId(contractId) - .setGas(gas) - .setFunction("getMessage") - .setQueryPayment(new Hbar(1)); - - var result = callQuery - .execute(testEnv.client); - - assertThat(result.getString(0)).isEqualTo("Hello from Hedera."); - - new ContractDeleteTransaction() - .setTransferAccountId(testEnv.operatorId) - .setContractId(contractId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot call contract function when contract function is not set") - void cannotCallContractFunctionWhenContractFunctionIsNotSet() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - var contractId = Objects.requireNonNull( - new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(200000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(testEnv.client) - .getReceipt(testEnv.client) - .contractId - ); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new ContractCallQuery() - .setContractId(contractId) - .setGas(100000) - .execute(testEnv.client); - }).withMessageContaining(Status.CONTRACT_REVERT_EXECUTED.toString()); - - new ContractDeleteTransaction() - .setTransferAccountId(testEnv.operatorId) - .setContractId(contractId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot call contract function when gas is not set") - void cannotCallContractFunctionWhenGasIsNotSet() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - var contractId = Objects.requireNonNull( - new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(200000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(testEnv.client) - .getReceipt(testEnv.client) - .contractId - ); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new ContractCallQuery() - .setContractId(contractId) - .setFunction("getMessage") - .execute(testEnv.client); - }).withMessageContaining(Status.INSUFFICIENT_GAS.toString()); - - new ContractDeleteTransaction() - .setTransferAccountId(testEnv.operatorId) - .setContractId(contractId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot call contract function when contract ID is not set") - void cannotCallContractFunctionWhenContractIDIsNotSet() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - var contractId = Objects.requireNonNull( - new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(200000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(testEnv.client) - .getReceipt(testEnv.client) - .contractId - ); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new ContractCallQuery() - .setGas(100000) - .setFunction("getMessage") - .execute(testEnv.client); - }).withMessageContaining(Status.INVALID_CONTRACT_ID.toString()); - - new ContractDeleteTransaction() - .setTransferAccountId(testEnv.operatorId) - .setContractId(contractId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can get cost, even with a big max") - void getCostBigMaxContractCallFunction() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - response = new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(200000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(testEnv.client); - - var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); - - // Wait for mirror node to import data - Thread.sleep(2000); - - long gas = new MirrorNodeContractEstimateGasQuery() - .setContractId(contractId) - .setFunction("getMessage") - .execute(testEnv.client); - - var callQuery = new ContractCallQuery() - .setContractId(contractId) - .setGas(gas) - .setFunction("getMessage") - .setQueryPayment(new Hbar(1)); - - var result = callQuery - .execute(testEnv.client); - - assertThat(result.getString(0)).isEqualTo("Hello from Hedera."); - - new ContractDeleteTransaction() - .setTransferAccountId(testEnv.operatorId) - .setContractId(contractId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Error, max is smaller than set payment.") - void getCostSmallMaxContractCallFunction() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - response = new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(200000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(testEnv.client); - - var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); - - // Wait for mirror node to import data - Thread.sleep(2000); - - var gas = new MirrorNodeContractEstimateGasQuery() - .setContractId(contractId) - .setFunction("getMessage") - .execute(testEnv.client); - - var callQuery = new ContractCallQuery() - .setContractId(contractId) - .setGas(gas) - .setFunction("getMessage") - .setMaxQueryPayment(Hbar.fromTinybars(1)); - - assertThatExceptionOfType(MaxQueryPaymentExceededException.class).isThrownBy(() -> { - callQuery.execute(testEnv.client); - }); - - new ContractDeleteTransaction() - .setTransferAccountId(testEnv.operatorId) - .setContractId(contractId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Insufficient tx fee error.") - void getCostInsufficientTxFeeContractCallFunction() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - response = new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(200000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(testEnv.client); - - var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); - - // Wait for mirror node to import data - Thread.sleep(2000); - - var gas = new MirrorNodeContractEstimateGasQuery() - .setContractId(contractId) - .setFunction("getMessage") - .execute(testEnv.client); - - var callQuery = new ContractCallQuery() - .setContractId(contractId) - .setGas(gas) - .setFunction("getMessage") - .setMaxQueryPayment(new Hbar(100)); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - callQuery.setQueryPayment(Hbar.fromTinybars(1)).execute(testEnv.client); - }).satisfies(error -> assertThat(error.status.toString()).isEqualTo("INSUFFICIENT_TX_FEE")); - - new ContractDeleteTransaction() - .setTransferAccountId(testEnv.operatorId) - .setContractId(contractId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractCreateFlowIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractCreateFlowIntegrationTest.java deleted file mode 100644 index 14cf3231d0..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractCreateFlowIntegrationTest.java +++ /dev/null @@ -1,164 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.*; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -public class ContractCreateFlowIntegrationTest { - private static final String SMART_CONTRACT_BYTECODE = "608060405234801561001057600080fd5b506040516104d73803806104d78339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b506040525050600080546001600160a01b0319163317905550805161010890600190602084019061010f565b50506101aa565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061015057805160ff191683800117855561017d565b8280016001018555821561017d579182015b8281111561017d578251825591602001919060010190610162565b5061018992915061018d565b5090565b6101a791905b808211156101895760008155600101610193565b90565b61031e806101b96000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063368b87721461004657806341c0e1b5146100ee578063ce6d41de146100f6575b600080fd5b6100ec6004803603602081101561005c57600080fd5b81019060208101813564010000000081111561007757600080fd5b82018360208201111561008957600080fd5b803590602001918460018302840111640100000000831117156100ab57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610173945050505050565b005b6100ec6101a2565b6100fe6101ba565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610138578181015183820152602001610120565b50505050905090810190601f1680156101655780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000546001600160a01b0316331461018a5761019f565b805161019d906001906020840190610250565b505b50565b6000546001600160a01b03163314156101b85733ff5b565b60018054604080516020601f600260001961010087891615020190951694909404938401819004810282018101909252828152606093909290918301828280156102455780601f1061021a57610100808354040283529160200191610245565b820191906000526020600020905b81548152906001019060200180831161022857829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061029157805160ff19168380011785556102be565b828001600101855582156102be579182015b828111156102be5782518255916020019190600101906102a3565b506102ca9291506102ce565b5090565b61024d91905b808211156102ca57600081556001016102d456fea264697066735822122084964d4c3f6bc912a9d20e14e449721012d625aa3c8a12de41ae5519752fc89064736f6c63430006000033"; - - @Test - @DisplayName("Create contract with flow") - void createContractWithFlow() throws Throwable { - try(var testEnv = new IntegrationTestEnv(1)){ - - var response = new ContractCreateFlow() - .setBytecode(SMART_CONTRACT_BYTECODE) - .setAdminKey(testEnv.operatorKey) - .setGas(200000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setContractMemo("[e2e::ContractCreateFlow]") - .execute(testEnv.client); - - var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); - - var receipt = new ContractExecuteTransaction() - .setContractId(contractId) - .setGas(100000) - .setFunction("setMessage", new ContractFunctionParameters().addString("new message")) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - assertThat(receipt.status).isEqualTo(Status.SUCCESS); - - new ContractDeleteTransaction() - .setTransferAccountId(testEnv.operatorId) - .setContractId(contractId) - .setTransferAccountId(testEnv.operatorId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Create contract with flow without signing") - void createContractWithFlowWithoutSigning() throws Throwable { - try(var testEnv = new IntegrationTestEnv(1)){ - var adminKey = PrivateKey.generateED25519(); - - assertThatThrownBy(() -> new ContractCreateFlow() - .setBytecode(SMART_CONTRACT_BYTECODE) - .setAdminKey(adminKey) - .setGas(100000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setContractMemo("[e2e::ContractCreateFlow]") - .execute(testEnv.client)) - .isInstanceOf(RuntimeException.class) - .hasMessageEndingWith("raised status INVALID_SIGNATURE"); - - } - } - - @Test - @DisplayName("Create contract with flow and sign with private key") - void createContractWithFlowPrivateKeySign() throws Throwable { - try(var testEnv = new IntegrationTestEnv(1)){ - var adminKey = PrivateKey.generateED25519(); - - var response = new ContractCreateFlow() - .setBytecode(SMART_CONTRACT_BYTECODE) - .setAdminKey(testEnv.operatorKey) - .setGas(200000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setContractMemo("[e2e::ContractCreateFlow]") - .freezeWith(testEnv.client) - .sign(adminKey) - .execute(testEnv.client); - - var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); - - var receipt = new ContractExecuteTransaction() - .setContractId(contractId) - .setGas(100000) - .setFunction("setMessage", new ContractFunctionParameters().addString("new message")) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - assertThat(receipt.status).isEqualTo(Status.SUCCESS); - - new ContractDeleteTransaction() - .setTransferAccountId(testEnv.operatorId) - .setContractId(contractId) - .setTransferAccountId(testEnv.operatorId) - .freezeWith(testEnv.client) - .sign(adminKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Create contract with flow and sign with public key and transaction signer") - void createContractWithFlowPublicKeySign() throws Throwable { - try(var testEnv = new IntegrationTestEnv(1)){ - var adminKey = PrivateKey.generateED25519(); - - var response = new ContractCreateFlow() - .setBytecode(SMART_CONTRACT_BYTECODE) - .setAdminKey(testEnv.operatorKey) - .setGas(200000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setContractMemo("[e2e::ContractCreateFlow]") - .freezeWith(testEnv.client) - .signWith(adminKey.getPublicKey(), adminKey::sign) - .execute(testEnv.client); - - var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); - - var receipt = new ContractExecuteTransaction() - .setContractId(contractId) - .setGas(100000) - .setFunction("setMessage", new ContractFunctionParameters().addString("new message")) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - assertThat(receipt.status).isEqualTo(Status.SUCCESS); - - new ContractDeleteTransaction() - .setTransferAccountId(testEnv.operatorId) - .setContractId(contractId) - .setTransferAccountId(testEnv.operatorId) - .freezeWith(testEnv.client) - .sign(adminKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractCreateIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractCreateIntegrationTest.java deleted file mode 100644 index d4e5f22953..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractCreateIntegrationTest.java +++ /dev/null @@ -1,205 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.ContractCreateTransaction; -import com.hedera.hashgraph.sdk.ContractDeleteTransaction; -import com.hedera.hashgraph.sdk.ContractFunctionParameters; -import com.hedera.hashgraph.sdk.ContractInfoQuery; -import com.hedera.hashgraph.sdk.FileCreateTransaction; -import com.hedera.hashgraph.sdk.FileDeleteTransaction; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.sdk.Status; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Objects; - -import static org.assertj.core.api.Assertions.as; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -public class ContractCreateIntegrationTest { - private static final String SMART_CONTRACT_BYTECODE = "608060405234801561001057600080fd5b506040516104d73803806104d78339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b506040525050600080546001600160a01b0319163317905550805161010890600190602084019061010f565b50506101aa565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061015057805160ff191683800117855561017d565b8280016001018555821561017d579182015b8281111561017d578251825591602001919060010190610162565b5061018992915061018d565b5090565b6101a791905b808211156101895760008155600101610193565b90565b61031e806101b96000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063368b87721461004657806341c0e1b5146100ee578063ce6d41de146100f6575b600080fd5b6100ec6004803603602081101561005c57600080fd5b81019060208101813564010000000081111561007757600080fd5b82018360208201111561008957600080fd5b803590602001918460018302840111640100000000831117156100ab57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610173945050505050565b005b6100ec6101a2565b6100fe6101ba565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610138578181015183820152602001610120565b50505050905090810190601f1680156101655780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000546001600160a01b0316331461018a5761019f565b805161019d906001906020840190610250565b505b50565b6000546001600160a01b03163314156101b85733ff5b565b60018054604080516020601f600260001961010087891615020190951694909404938401819004810282018101909252828152606093909290918301828280156102455780601f1061021a57610100808354040283529160200191610245565b820191906000526020600020905b81548152906001019060200180831161022857829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061029157805160ff19168380011785556102be565b828001600101855582156102be579182015b828111156102be5782518255916020019190600101906102a3565b506102ca9291506102ce565b5090565b61024d91905b808211156102ca57600081556001016102d456fea264697066735822122084964d4c3f6bc912a9d20e14e449721012d625aa3c8a12de41ae5519752fc89064736f6c63430006000033"; - - @Test - @DisplayName("Can create contract") - void canCreateContract() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - response = new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(200000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(testEnv.client); - - var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); - - var info = new ContractInfoQuery() - .setContractId(contractId) - .execute(testEnv.client); - - assertThat(info.contractId).isEqualTo(contractId); - assertThat(info.accountId).isNotNull(); - assertThat(Objects.requireNonNull(info.accountId).toString()).isEqualTo(Objects.requireNonNull(contractId).toString()); - assertThat(info.adminKey).isNotNull(); - assertThat(Objects.requireNonNull(info.adminKey).toString()).isEqualTo(Objects.requireNonNull(testEnv.operatorKey).toString()); - assertThat(info.storage).isEqualTo(128); - assertThat(info.contractMemo).isEqualTo("[e2e::ContractCreateTransaction]"); - - new ContractDeleteTransaction() - .setTransferAccountId(testEnv.operatorId) - .setContractId(contractId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can create contract with no admin key") - void canCreateContractWithNoAdminKey() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - response = new ContractCreateTransaction() - .setGas(200000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(testEnv.client); - - var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); - - var info = new ContractInfoQuery() - .setContractId(contractId) - .execute(testEnv.client); - - assertThat(info.contractId).isEqualTo(contractId); - assertThat(info.accountId).isNotNull(); - assertThat(Objects.requireNonNull(info.accountId).toString()).isEqualTo(Objects.requireNonNull(contractId).toString()); - assertThat(info.adminKey).isNotNull(); - // assertEquals(info.adminKey, contractId); - assertThat(info.storage).isEqualTo(128); - assertThat(info.contractMemo).isEqualTo("[e2e::ContractCreateTransaction]"); - - } - } - - @Test - @DisplayName("Cannot create contract when gas is not set") - void cannotCreateContractWhenGasIsNotSet() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INSUFFICIENT_GAS.toString()); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - - } - - @Test - @DisplayName("Cannot create contract when constructor parameters are not set") - void cannotCreateContractWhenConstructorParametersAreNotSet() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(100000) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.CONTRACT_REVERT_EXECUTED.toString()); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot create contract when bytecode file ID is not set") - void cannotCreateContractWhenBytecodeFileIdIsNotSet() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(100000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_FILE_ID.toString()); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractDeleteIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractDeleteIntegrationTest.java deleted file mode 100644 index c7ec9a1d88..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractDeleteIntegrationTest.java +++ /dev/null @@ -1,150 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.ContractCreateTransaction; -import com.hedera.hashgraph.sdk.ContractDeleteTransaction; -import com.hedera.hashgraph.sdk.ContractFunctionParameters; -import com.hedera.hashgraph.sdk.ContractInfoQuery; -import com.hedera.hashgraph.sdk.FileCreateTransaction; -import com.hedera.hashgraph.sdk.FileDeleteTransaction; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.sdk.Status; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Objects; - -import static org.assertj.core.api.Assertions.as; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -public class ContractDeleteIntegrationTest { - private static final String SMART_CONTRACT_BYTECODE = "608060405234801561001057600080fd5b506040516104d73803806104d78339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b506040525050600080546001600160a01b0319163317905550805161010890600190602084019061010f565b50506101aa565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061015057805160ff191683800117855561017d565b8280016001018555821561017d579182015b8281111561017d578251825591602001919060010190610162565b5061018992915061018d565b5090565b6101a791905b808211156101895760008155600101610193565b90565b61031e806101b96000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063368b87721461004657806341c0e1b5146100ee578063ce6d41de146100f6575b600080fd5b6100ec6004803603602081101561005c57600080fd5b81019060208101813564010000000081111561007757600080fd5b82018360208201111561008957600080fd5b803590602001918460018302840111640100000000831117156100ab57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610173945050505050565b005b6100ec6101a2565b6100fe6101ba565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610138578181015183820152602001610120565b50505050905090810190601f1680156101655780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000546001600160a01b0316331461018a5761019f565b805161019d906001906020840190610250565b505b50565b6000546001600160a01b03163314156101b85733ff5b565b60018054604080516020601f600260001961010087891615020190951694909404938401819004810282018101909252828152606093909290918301828280156102455780601f1061021a57610100808354040283529160200191610245565b820191906000526020600020905b81548152906001019060200180831161022857829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061029157805160ff19168380011785556102be565b828001600101855582156102be579182015b828111156102be5782518255916020019190600101906102a3565b506102ca9291506102ce565b5090565b61024d91905b808211156102ca57600081556001016102d456fea264697066735822122084964d4c3f6bc912a9d20e14e449721012d625aa3c8a12de41ae5519752fc89064736f6c63430006000033"; - - @Test - @DisplayName("Can delete contract with admin key") - void canDeleteContractWithAdminKey() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - response = new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(200000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(testEnv.client); - - var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); - - var info = new ContractInfoQuery() - .setContractId(contractId) - .execute(testEnv.client); - - assertThat(info.contractId).isEqualTo(contractId); - assertThat(info.accountId).isNotNull(); - assertThat(Objects.requireNonNull(info.accountId).toString()).isEqualTo(contractId.toString()); - assertThat(info.adminKey).isNotNull(); - assertThat(Objects.requireNonNull(info.adminKey).toString()) - .isEqualTo(Objects.requireNonNull(testEnv.operatorKey).toString()); - assertThat(info.storage).isEqualTo(128); - assertThat(info.contractMemo).isEqualTo("[e2e::ContractCreateTransaction]"); - - new ContractDeleteTransaction() - .setTransferAccountId(testEnv.operatorId) - .setContractId(contractId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot delete contract which has no admin key") - void cannotDeleteContractWhichHasNoAdminKey() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - var contractId = Objects.requireNonNull(new ContractCreateTransaction() - .setGas(200000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(testEnv.client) - .getReceipt(testEnv.client) - .contractId - ); - - var info = new ContractInfoQuery() - .setContractId(contractId) - .execute(testEnv.client); - - assertThat(info.contractId).isEqualTo(contractId); - assertThat(info.accountId).isNotNull(); - assertThat(Objects.requireNonNull(info.accountId).toString()).isEqualTo(contractId.toString()); - assertThat(info.adminKey).isNotNull(); - // assertEquals(info.adminKey, contractId); - assertThat(info.storage).isEqualTo(128); - assertThat(info.contractMemo).isEqualTo("[e2e::ContractCreateTransaction]"); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new ContractDeleteTransaction() - .setContractId(contractId) - .setTransferAccountId(testEnv.client.getOperatorAccountId()) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.MODIFYING_IMMUTABLE_CONTRACT.toString()); - - } - } - - @Test - @DisplayName("Cannot delete contract when contract ID is not set") - void cannotDeleteContractWhenContractIDIsNotSet() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new ContractDeleteTransaction() - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_CONTRACT_ID.toString()); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractExecuteIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractExecuteIntegrationTest.java deleted file mode 100644 index 9f2b099bd9..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractExecuteIntegrationTest.java +++ /dev/null @@ -1,198 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -import com.hedera.hashgraph.sdk.ContractCreateTransaction; -import com.hedera.hashgraph.sdk.ContractDeleteTransaction; -import com.hedera.hashgraph.sdk.ContractExecuteTransaction; -import com.hedera.hashgraph.sdk.ContractFunctionParameters; -import com.hedera.hashgraph.sdk.FileCreateTransaction; -import com.hedera.hashgraph.sdk.FileDeleteTransaction; -import com.hedera.hashgraph.sdk.MirrorNodeContractEstimateGasQuery; -import com.hedera.hashgraph.sdk.MirrorNodeContractQuery; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.sdk.Status; -import java.util.Objects; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -public class ContractExecuteIntegrationTest { - private static final String SMART_CONTRACT_BYTECODE = "608060405234801561001057600080fd5b506040516104d73803806104d78339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b506040525050600080546001600160a01b0319163317905550805161010890600190602084019061010f565b50506101aa565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061015057805160ff191683800117855561017d565b8280016001018555821561017d579182015b8281111561017d578251825591602001919060010190610162565b5061018992915061018d565b5090565b6101a791905b808211156101895760008155600101610193565b90565b61031e806101b96000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063368b87721461004657806341c0e1b5146100ee578063ce6d41de146100f6575b600080fd5b6100ec6004803603602081101561005c57600080fd5b81019060208101813564010000000081111561007757600080fd5b82018360208201111561008957600080fd5b803590602001918460018302840111640100000000831117156100ab57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610173945050505050565b005b6100ec6101a2565b6100fe6101ba565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610138578181015183820152602001610120565b50505050905090810190601f1680156101655780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000546001600160a01b0316331461018a5761019f565b805161019d906001906020840190610250565b505b50565b6000546001600160a01b03163314156101b85733ff5b565b60018054604080516020601f600260001961010087891615020190951694909404938401819004810282018101909252828152606093909290918301828280156102455780601f1061021a57610100808354040283529160200191610245565b820191906000526020600020905b81548152906001019060200180831161022857829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061029157805160ff19168380011785556102be565b828001600101855582156102be579182015b828111156102be5782518255916020019190600101906102a3565b506102ca9291506102ce565b5090565b61024d91905b808211156102ca57600081556001016102d456fea264697066735822122084964d4c3f6bc912a9d20e14e449721012d625aa3c8a12de41ae5519752fc89064736f6c63430006000033"; - - @Test - @DisplayName("Can execute contract methods") - void canExecuteContractMethods() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - response = new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(200000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(testEnv.client); - - var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); - - // Wait for mirror node to import data - Thread.sleep(2000); - - var gas = new MirrorNodeContractEstimateGasQuery() - .setContractId(contractId) - .setFunction("setMessage", new ContractFunctionParameters().addString("new message")) - .execute(testEnv.client); - var receipt = new ContractExecuteTransaction() - .setContractId(contractId) - .setGas(gas + 10000) - .setFunction("setMessage", new ContractFunctionParameters().addString("new message")) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - assertThat(receipt.status).isEqualTo(Status.SUCCESS); - - new ContractDeleteTransaction() - .setTransferAccountId(testEnv.operatorId) - .setContractId(contractId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot execute contract when contract ID is not set") - void cannotExecuteContractWhenContractIDIsNotSet() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new ContractExecuteTransaction() - .setGas(100000) - .setFunction("setMessage", new ContractFunctionParameters().addString("new message")) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_CONTRACT_ID.toString()); - - } - } - - @Test - @DisplayName("Cannot execute contract when contract function parameters are not set") - void cannotExecuteContractWhenContractFunctionParametersAreNotSet() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - var contractId = Objects.requireNonNull( - new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(200000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(testEnv.client) - .getReceipt(testEnv.client).contractId); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new ContractExecuteTransaction() - .setContractId(contractId) - .setGas(100000) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.CONTRACT_REVERT_EXECUTED.toString()); - - new ContractDeleteTransaction() - .setTransferAccountId(testEnv.operatorId) - .setContractId(contractId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot execute contract when gas is not set") - void cannotExecuteContractWhenGasIsNotSet() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - var contractId = Objects.requireNonNull( - new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(200000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(testEnv.client) - .getReceipt(testEnv.client).contractId); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new ContractExecuteTransaction() - .setContractId(contractId) - .setFunction("setMessage", new ContractFunctionParameters().addString("new message")) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INSUFFICIENT_GAS.toString()); - - new ContractDeleteTransaction() - .setTransferAccountId(testEnv.operatorId) - .setContractId(contractId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractFunctionParametersIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractFunctionParametersIntegrationTest.java deleted file mode 100644 index 3ef94829bd..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractFunctionParametersIntegrationTest.java +++ /dev/null @@ -1,2855 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.esaulpaugh.headlong.abi.Address; -import com.hedera.hashgraph.sdk.ContractCallQuery; -import com.hedera.hashgraph.sdk.ContractCreateTransaction; -import com.hedera.hashgraph.sdk.ContractDeleteTransaction; -import com.hedera.hashgraph.sdk.ContractFunctionParameters; -import com.hedera.hashgraph.sdk.ContractId; -import com.hedera.hashgraph.sdk.FileAppendTransaction; -import com.hedera.hashgraph.sdk.FileCreateTransaction; -import com.hedera.hashgraph.sdk.FileDeleteTransaction; -import com.hedera.hashgraph.sdk.FileId; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.MirrorNodeContractEstimateGasQuery; -import com.hedera.hashgraph.sdk.MirrorNodeContractQuery; -import java.math.BigInteger; -import java.util.Arrays; -import java.util.Objects; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -public class ContractFunctionParametersIntegrationTest { - private static final String SMART_CONTRACT_BYTECODE = "60806040523480156200001157600080fd5b5060408051608081018252600491810191825263082d8caf60e31b6060820152818152600160208201529060009081906200004d908262000106565b5060208201518160010155905050620001d2565b634e487b7160e01b600052604160045260246000fd5b600181811c908216806200008c57607f821691505b602082108103620000ad57634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200010157600081815260208120601f850160051c81016020861015620000dc5750805b601f850160051c820191505b81811015620000fd57828155600101620000e8565b5050505b505050565b81516001600160401b0381111562000122576200012262000061565b6200013a8162000133845462000077565b84620000b3565b602080601f831160018114620001725760008415620001595750858301515b600019600386901b1c1916600185901b178555620000fd565b600085815260208120601f198616915b82811015620001a35788860151825594840194600190910190840162000182565b5085821015620001c25787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b615dfe80620001e26000396000f3fe608060405234801561001057600080fd5b50600436106108355760003560e01c80637f8082f711610437578063c5882dbb11610236578063e2a345291161013b578063f6e877f4116100c3578063fafc3b9911610092578063fafc3b9914611857578063fba1bc4c14611872578063febe099714611887578063ff5f54dd146118a2578063ffb80501146118bd57600080fd5b8063f6e877f4146117d9578063f8293f6e146117ff578063f914c4d214611821578063f962326a1461183c57600080fd5b8063eceda56a1161010a578063eceda56a1461174c578063ef65066114611767578063f0ebb92214611782578063f4e490f51461179d578063f680c24a146117be57600080fd5b8063e2a34529146116d8578063e4b90944146116f3578063e713cda81461170e578063ea945d301461173157600080fd5b8063d79d4d40116101be578063e05e91e01161018d578063e05e91e014611630578063e066de5014611656578063e0cddc551461167c578063e0f53e2414611697578063e1dbb318146116bd57600080fd5b8063d79d4d4014611592578063dade0c0b146115b8578063dbb04ed9146115da578063de9fb4841461160357600080fd5b8063cbd2e6a511610205578063cbd2e6a514611511578063cdb9e4e814611536578063cf7c3dfc1461155c578063d1b10ad714610ada578063d33c57501461157757600080fd5b8063c5882dbb1461148b578063c6c18a1c146114a6578063c7d8b87e146114d0578063cb47cdae146114f657600080fd5b8063a1bda1221161033c578063b5abe7df116102c4578063ba945bdb11610293578063ba945bdb146113dc578063bb6b524314611402578063bd90536a14611428578063be63d0b814611450578063c503772d1461146b57600080fd5b8063b5abe7df14611359578063b834bfe914611374578063b8da8d1614611395578063b989c7ee146113bb57600080fd5b8063aa016e681161030b578063aa016e68146112dc578063aa80ca2e14610c6b578063b2325c35146112f7578063b2db404a14611312578063b4e3e7b11461133357600080fd5b8063a1bda12214611259578063a401d60d1461127a578063a57ebf10146112a0578063a75761f1146112bb57600080fd5b80638f2805e0116103bf57806394cd7c801161038e57806394cd7c80146111b557806398508ba3146111d65780639b1794ae146111f7578063a08b9f671461121d578063a19962341461123e57600080fd5b80638f2805e0146111435780638ff4cfee1461115e578063909c5b2414611179578063923f5edf1461119457600080fd5b806386aba5a71161040657806386aba5a7146110ab578063881c8fb7146110c657806388b1dd37146110ec57806388b7e6f5146111075780638d7f60151461112857600080fd5b80637f8082f714610c6b578063817b24541461107557806381dbe13e14611090578063827147ce1461099a57600080fd5b806338fa6658116106435780635a8fd3b51161054857806370a5cb81116104d05780637ba844771161049f5780637ba8447714610fe25780637d0dc26214610ffd5780637d906c551461101e5780637e281630146110395780637ec32d841461105457600080fd5b806370a5cb8114610f5a57806372a06b4d14610f80578063737b801614610fa1578063796a27ea14610fbc57600080fd5b806369cbe0561161051757806369cbe05614610ecd5780636a54715c14610ee85780636a75c12f14610f095780636a9929db14610f245780636ee8f39c14610f3f57600080fd5b80635a8fd3b514610e4f578063628bc3ef14610e6a57806364e008c114610e8b57806368ef446614610eac57600080fd5b80634bbc9a67116105cb578063545e21131161059a578063545e211314610dd157806355f232a414610dfe57806357890ba914610e1957806357d9c08b14610e3457806359adb2df14610ada57600080fd5b80634bbc9a6714610d655780634e96247314610d80578063501297c214610d9b57806350370d8514610db657600080fd5b80633f396e67116106125780633f396e6714610ce3578063407b899b14610d0b578063435a33a814610d2c57806344e7b03714610af657806348d848d014610d4757600080fd5b806338fa665814610c6b5780633a73007714610c865780633b45e6e014610ca15780633e1a277114610cc257600080fd5b80631e9aa70f116107495780632ef16e8e116106d15780633135d681116106a05780633135d68114610beb57806333520ec314610c0657806333edb89614610c275780633729a2da14610c42578063382d087314610c5057600080fd5b80632ef16e8e14610b6d5780632f47a40d14610b8e5780632f6c1bb414610baf578063309f9ac914610bd057600080fd5b806322937ea91161071857806322937ea914610ada5780632421101f14610af657806324c3c3d214610b1c57806328a30eb714610b375780632a3082ce14610b5257600080fd5b80631e9aa70f14610a6e578063203ae83a14610a8957806321d1730b14610aa45780632234ea0214610abf57600080fd5b8063118b8415116107cc57806312b932a71161079b57806312b932a7146109db57806312cd95a1146109f657806315832ae414610a17578063189cea8e14610a325780631d11456214610a5357600080fd5b8063118b84151461095857806311ec6c9014610979578063126bc8151461099a578063129ed5da146109b557600080fd5b806306ac6fe11161080857806306ac6fe1146108ca57806308123e09146108f05780630a958dc81461091657806310d545531461093757600080fd5b8063017fa10b1461083a578063021d88ab14610868578063037454301461088e5780630577a846146108af575b600080fd5b61084b610848366004611a34565b90565b6040516001600160801b0390911681526020015b60405180910390f35b610876610848366004611a6d565b6040516001600160601b03909116815260200161085f565b61089c610848366004611a9a565b604051600c9190910b815260200161085f565b6108bd610848366004611b35565b60405161085f9190611bd1565b6108d8610848366004611c35565b6040516001600160781b03909116815260200161085f565b6108fe610848366004611c67565b60405166ffffffffffffff909116815260200161085f565b610924610848366004611c94565b60405160049190910b815260200161085f565b610945610848366004611cc1565b60405160119190910b815260200161085f565b610966610848366004611cee565b604051601e9190910b815260200161085f565b610987610848366004611d1b565b60405160139190910b815260200161085f565b6109a8610848366004611da5565b60405161085f9190611e27565b6109c3610848366004611e51565b6040516001600160981b03909116815260200161085f565b6109e9610848366004611e7e565b60405161085f9190611f0a565b610a04610848366004611f57565b60405160129190910b815260200161085f565b610a25610848366004611f83565b60405161085f919061200f565b610a4061084836600461205c565b60405160169190910b815260200161085f565b610a6161084836600461208f565b60405161085f919061211d565b610a7c610848366004612170565b60405161085f91906121fc565b610a97610848366004612237565b60405161085f91906122c3565b610ab261084836600461231b565b60405161085f91906123a7565b610acd6108483660046123fa565b60405161085f9190612486565b610ae86108483660046124c1565b60405190815260200161085f565b610b046108483660046124da565b6040516001600160a01b03909116815260200161085f565b610b2a61084836600461250e565b60405161085f919061259a565b610b456108483660046125db565b60405161085f9190612667565b610b606108483660046126b4565b60405161085f9190612740565b610b7b61084836600461278d565b604051601d9190910b815260200161085f565b610b9c6108483660046127a8565b604051600a9190910b815260200161085f565b610bbd6108483660046127d5565b60405160199190910b815260200161085f565b610bde610848366004612807565b60405161085f9190612893565b610bf96108483660046128eb565b60405161085f9190612977565b610c146108483660046129ca565b604051601a9190910b815260200161085f565b610c356108483660046129f7565b60405161085f9190612a83565b610a61610848366004612abe565b610c5e610848366004612b5e565b60405161085f9190612bea565b610c79610848366004612c25565b60405161085f9190612caa565b610c94610848366004612cfd565b60405161085f9190612d89565b610caf610848366004612de0565b60405160109190910b815260200161085f565b610cd0610848366004612e0d565b604051601c9190910b815260200161085f565b610cf1610848366004612e41565b60405168ffffffffffffffffff909116815260200161085f565b610d19610848366004612e6e565b60405160079190910b815260200161085f565b610d3a610848366004612e89565b60405161085f9190612f15565b610d55610848366004612f60565b604051901515815260200161085f565b610d73610848366004612f7b565b60405161085f9190613007565b610d8e610848366004613058565b60405161085f91906130e4565b610da9610848366004613137565b60405161085f91906131c3565b610dc4610848366004613215565b60405161085f91906132a1565b610de4610ddf3660046132f4565b6118e1565b60408051600093840b81529190920b60208201520161085f565b610e0c61084836600461330f565b60405161085f919061339b565b610e276108483660046133d6565b60405161085f9190613462565b610e426108483660046134b3565b60405161085f919061353f565b610e5d610848366004613593565b60405161085f919061361f565b610e7861084836600461366f565b60405160099190910b815260200161085f565b610e9961084836600461369c565b60405160179190910b815260200161085f565b610eba6108483660046136b7565b60405160149190910b815260200161085f565b610edb6108483660046136e4565b60405161085f9190613770565b610ef66108483660046137ab565b60405160069190910b815260200161085f565b610f176108483660046137d8565b60405161085f9190613864565b610f3261084836600461389f565b60405161085f919061392b565b610f4d610848366004613980565b60405161085f9190613a0c565b610f68610848366004613a67565b6040516001600160681b03909116815260200161085f565b610f8e6108483660046132f4565b60405160009190910b815260200161085f565b610faf610848366004613a99565b60405161085f9190613b25565b610fca610848366004613b66565b6040516001600160d81b03909116815260200161085f565b610ff0610848366004613b96565b60405161085f9190613c22565b61100b610848366004613c73565b604051601b9190910b815260200161085f565b61102c610848366004613ca1565b60405161085f9190613d2d565b611047610848366004613d7c565b60405161085f9190613e08565b611062610848366004613e56565b60405160029190910b815260200161085f565b611083610848366004613e88565b60405161085f9190613f14565b61109e610848366004613f55565b60405161085f9190614005565b6110b9610848366004614067565b60405161085f91906140f3565b6110d461084836600461414b565b6040516001600160b01b03909116815260200161085f565b6110fa610848366004614166565b60405161085f91906141f2565b61111561084836600461422d565b604051600f9190910b815260200161085f565b611136610848366004614248565b60405161085f91906142d4565b611151610848366004614326565b60405161085f91906143b2565b61116c6108483660046143f3565b60405161085f919061447f565b6111876108483660046144ba565b60405161085f919061455d565b6111a26108483660046145b2565b60405160159190910b815260200161085f565b6111c36108483660046145df565b60405160189190910b815260200161085f565b6111e46108483660046145fa565b604051600d9190910b815260200161085f565b61120561084836600461462c565b6040516001600160b81b03909116815260200161085f565b61122b610848366004614647565b604051600b9190910b815260200161085f565b61124c610848366004614662565b60405161085f91906146ee565b61126761084836600461472f565b60405160019190910b815260200161085f565b61128861084836600461474a565b6040516001600160f01b03909116815260200161085f565b6112ae610848366004614765565b60405161085f91906147f1565b6112c961084836600461482c565b60405161ffff909116815260200161085f565b6112ea610848366004614847565b60405161085f91906148d3565b611305610848366004614928565b60405161085f91906149b4565b611320610848366004614a01565b60405160059190910b815260200161085f565b611341610848366004614a1c565b6040516001600160701b03909116815260200161085f565b611367610848366004614a37565b60405161085f9190614ac3565b611382610848366004614b04565b604051600e9190910b815260200161085f565b6113a3610848366004614b1f565b6040516001600160a81b03909116815260200161085f565b6113c9610848366004614b3a565b60405160039190910b815260200161085f565b6113ea610848366004614b55565b6040516001600160881b03909116815260200161085f565b611410610848366004614b70565b6040516001600160c81b03909116815260200161085f565b61143b6114363660046124c1565b6118f9565b6040805192835260208301919091520161085f565b61145e610848366004614b8b565b60405161085f9190614c17565b611479610848366004614c52565b60405160ff909116815260200161085f565b611499610848366004614c6d565b60405161085f9190614cf9565b6114b4610848366004614d34565b6040516affffffffffffffffffffff909116815260200161085f565b6114de610848366004614d4f565b6040516001600160f81b03909116815260200161085f565b611504610848366004614d6a565b60405161085f9190614df6565b61151f610848366004614e31565b60405165ffffffffffff909116815260200161085f565b611544610848366004614e63565b6040516001600160e01b03909116815260200161085f565b61156a610848366004614e7e565b60405161085f9190614f0a565b611585610848366004614f45565b60405161085f9190614fd1565b6115a0610848366004615029565b6040516001600160d01b03909116815260200161085f565b6115cb6115c6366004615044565b611908565b60405161085f9392919061505f565b6115e8610848366004615096565b60405169ffffffffffffffffffff909116815260200161085f565b611616611611366004611c94565b611946565b60408051600493840b81529190920b60208201520161085f565b61163e6108483660046150b1565b6040516001600160401b03909116815260200161085f565b6116646108483660046150e3565b6040516001600160e81b03909116815260200161085f565b61168a6108483660046150fe565b60405161085f919061518a565b6116a56108483660046151c5565b6040516001600160c01b03909116815260200161085f565b6116cb6108483660046151f2565b60405161085f919061527e565b6116e66108483660046152b9565b60405161085f9190615345565b61170161084836600461538d565b60405161085f9190615419565b61171c610848366004615044565b60405163ffffffff909116815260200161085f565b61173f610848366004615454565b60405161085f91906154e0565b61175a61084836600461551b565b60405161085f91906155a7565b6117756108483660046155e2565b60405161085f919061566e565b6117906108483660046156af565b60405161085f919061573b565b6117ab610848366004615776565b60405160089190910b815260200161085f565b6117cc610848366004615791565b60405161085f919061581d565b6117e7610848366004615858565b6040516001600160901b03909116815260200161085f565b61180d610848366004615873565b60405162ffffff909116815260200161085f565b61182f61084836600461588e565b60405161085f919061591a565b61184a61084836600461595b565b60405161085f91906159e7565b611865610848366004615a28565b60405161085f9190615ab4565b61187a611955565b60405161085f9190615af5565b611895610848366004615b27565b60405161085f9190615bb3565b6118b0610848366004615bf4565b60405161085f9190615c80565b6118cb610848366004615cc1565b60405164ffffffffff909116815260200161085f565b600080826118f0816014615cf2565b91509150915091565b600080826118f0816001615d19565b600080606083611919600182615d41565b6040805180820190915260028152614f4b60f01b602082015291945063ffffffff16925090509193909250565b600080826118f0816001615d65565b604080518082019091526060815260006020820152600060405180604001604052908160008201805461198790615d8e565b80601f01602080910402602001604051908101604052809291908181526020018280546119b390615d8e565b8015611a005780601f106119d557610100808354040283529160200191611a00565b820191906000526020600020905b8154815290600101906020018083116119e357829003601f168201915b50505050508152602001600182015481525050905090565b80356001600160801b0381168114611a2f57600080fd5b919050565b600060208284031215611a4657600080fd5b611a4f82611a18565b9392505050565b80356001600160601b0381168114611a2f57600080fd5b600060208284031215611a7f57600080fd5b611a4f82611a56565b8035600c81900b8114611a2f57600080fd5b600060208284031215611aac57600080fd5b611a4f82611a88565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715611af357611af3611ab5565b604052919050565b60006001600160401b03821115611b1457611b14611ab5565b5060051b60200190565b80356001600160f01b0381168114611a2f57600080fd5b60006020808385031215611b4857600080fd5b82356001600160401b03811115611b5e57600080fd5b8301601f81018513611b6f57600080fd5b8035611b82611b7d82611afb565b611acb565b81815260059190911b82018301908381019087831115611ba157600080fd5b928401925b82841015611bc657611bb784611b1e565b82529284019290840190611ba6565b979650505050505050565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160f01b031683529284019291840191600101611bed565b50909695505050505050565b80356001600160781b0381168114611a2f57600080fd5b600060208284031215611c4757600080fd5b611a4f82611c1e565b803566ffffffffffffff81168114611a2f57600080fd5b600060208284031215611c7957600080fd5b611a4f82611c50565b8035600481900b8114611a2f57600080fd5b600060208284031215611ca657600080fd5b611a4f82611c82565b8035601181900b8114611a2f57600080fd5b600060208284031215611cd357600080fd5b611a4f82611caf565b8035601e81900b8114611a2f57600080fd5b600060208284031215611d0057600080fd5b611a4f82611cdc565b8035601381900b8114611a2f57600080fd5b600060208284031215611d2d57600080fd5b611a4f82611d09565b600082601f830112611d4757600080fd5b81356001600160401b03811115611d6057611d60611ab5565b611d73601f8201601f1916602001611acb565b818152846020838601011115611d8857600080fd5b816020850160208301376000918101602001919091529392505050565b600060208284031215611db757600080fd5b81356001600160401b03811115611dcd57600080fd5b611dd984828501611d36565b949350505050565b6000815180845260005b81811015611e0757602081850181015186830182015201611deb565b506000602082860101526020601f19601f83011685010191505092915050565b602081526000611a4f6020830184611de1565b80356001600160981b0381168114611a2f57600080fd5b600060208284031215611e6357600080fd5b611a4f82611e3a565b8035600381900b8114611a2f57600080fd5b60006020808385031215611e9157600080fd5b82356001600160401b03811115611ea757600080fd5b8301601f81018513611eb857600080fd5b8035611ec6611b7d82611afb565b81815260059190911b82018301908381019087831115611ee557600080fd5b928401925b82841015611bc657611efb84611e6c565b82529284019290840190611eea565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160030b83529284019291840191600101611f26565b8035601281900b8114611a2f57600080fd5b600060208284031215611f6957600080fd5b611a4f82611f45565b803560ff81168114611a2f57600080fd5b60006020808385031215611f9657600080fd5b82356001600160401b03811115611fac57600080fd5b8301601f81018513611fbd57600080fd5b8035611fcb611b7d82611afb565b81815260059190911b82018301908381019087831115611fea57600080fd5b928401925b82841015611bc65761200084611f72565b82529284019290840190611fef565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160ff168352928401929184019160010161202b565b8035601681900b8114611a2f57600080fd5b60006020828403121561206e57600080fd5b611a4f8261204a565b6001600160a01b038116811461208c57600080fd5b50565b600060208083850312156120a257600080fd5b82356001600160401b038111156120b857600080fd5b8301601f810185136120c957600080fd5b80356120d7611b7d82611afb565b81815260059190911b820183019083810190878311156120f657600080fd5b928401925b82841015611bc657833561210e81612077565b825292840192908401906120fb565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160a01b031683529284019291840191600101612139565b8035601481900b8114611a2f57600080fd5b6000602080838503121561218357600080fd5b82356001600160401b0381111561219957600080fd5b8301601f810185136121aa57600080fd5b80356121b8611b7d82611afb565b81815260059190911b820183019083810190878311156121d757600080fd5b928401925b82841015611bc6576121ed8461215e565b825292840192908401906121dc565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160140b83529284019291840191600101612218565b6000602080838503121561224a57600080fd5b82356001600160401b0381111561226057600080fd5b8301601f8101851361227157600080fd5b803561227f611b7d82611afb565b81815260059190911b8201830190838101908783111561229e57600080fd5b928401925b82841015611bc6576122b484611c50565b825292840192908401906122a3565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835166ffffffffffffff16835292840192918401916001016122df565b80356001600160f81b0381168114611a2f57600080fd5b6000602080838503121561232e57600080fd5b82356001600160401b0381111561234457600080fd5b8301601f8101851361235557600080fd5b8035612363611b7d82611afb565b81815260059190911b8201830190838101908783111561238257600080fd5b928401925b82841015611bc65761239884612304565b82529284019290840190612387565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160f81b0316835292840192918401916001016123c3565b8035600a81900b8114611a2f57600080fd5b6000602080838503121561240d57600080fd5b82356001600160401b0381111561242357600080fd5b8301601f8101851361243457600080fd5b8035612442611b7d82611afb565b81815260059190911b8201830190838101908783111561246157600080fd5b928401925b82841015611bc657612477846123e8565b82529284019290840190612466565b6020808252825182820181905260009190848201906040850190845b81811015611c12578351600a0b835292840192918401916001016124a2565b6000602082840312156124d357600080fd5b5035919050565b6000602082840312156124ec57600080fd5b8135611a4f81612077565b80356001600160c81b0381168114611a2f57600080fd5b6000602080838503121561252157600080fd5b82356001600160401b0381111561253757600080fd5b8301601f8101851361254857600080fd5b8035612556611b7d82611afb565b81815260059190911b8201830190838101908783111561257557600080fd5b928401925b82841015611bc65761258b846124f7565b8252928401929084019061257a565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160c81b0316835292840192918401916001016125b6565b600060208083850312156125ee57600080fd5b82356001600160401b0381111561260457600080fd5b8301601f8101851361261557600080fd5b8035612623611b7d82611afb565b81815260059190911b8201830190838101908783111561264257600080fd5b928401925b82841015611bc65761265884611d09565b82529284019290840190612647565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160130b83529284019291840191600101612683565b8035600b81900b8114611a2f57600080fd5b600060208083850312156126c757600080fd5b82356001600160401b038111156126dd57600080fd5b8301601f810185136126ee57600080fd5b80356126fc611b7d82611afb565b81815260059190911b8201830190838101908783111561271b57600080fd5b928401925b82841015611bc657612731846126a2565b82529284019290840190612720565b6020808252825182820181905260009190848201906040850190845b81811015611c12578351600b0b8352928401929184019160010161275c565b8035601d81900b8114611a2f57600080fd5b60006020828403121561279f57600080fd5b611a4f8261277b565b6000602082840312156127ba57600080fd5b611a4f826123e8565b8035601981900b8114611a2f57600080fd5b6000602082840312156127e757600080fd5b611a4f826127c3565b80356001600160c01b0381168114611a2f57600080fd5b6000602080838503121561281a57600080fd5b82356001600160401b0381111561283057600080fd5b8301601f8101851361284157600080fd5b803561284f611b7d82611afb565b81815260059190911b8201830190838101908783111561286e57600080fd5b928401925b82841015611bc657612884846127f0565b82529284019290840190612873565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160c01b0316835292840192918401916001016128af565b80356001600160401b0381168114611a2f57600080fd5b600060208083850312156128fe57600080fd5b82356001600160401b0381111561291457600080fd5b8301601f8101851361292557600080fd5b8035612933611b7d82611afb565b81815260059190911b8201830190838101908783111561295257600080fd5b928401925b82841015611bc657612968846128d4565b82529284019290840190612957565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160401b031683529284019291840191600101612993565b8035601a81900b8114611a2f57600080fd5b6000602082840312156129dc57600080fd5b611a4f826129b8565b8035600d81900b8114611a2f57600080fd5b60006020808385031215612a0a57600080fd5b82356001600160401b03811115612a2057600080fd5b8301601f81018513612a3157600080fd5b8035612a3f611b7d82611afb565b81815260059190911b82018301908381019087831115612a5e57600080fd5b928401925b82841015611bc657612a74846129e5565b82529284019290840190612a63565b6020808252825182820181905260009190848201906040850190845b81811015611c12578351600d0b83529284019291840191600101612a9f565b60006020808385031215612ad157600080fd5b82356001600160401b03811115612ae757600080fd5b8301601f81018513612af857600080fd5b8035612b06611b7d82611afb565b81815260059190911b82018301908381019087831115612b2557600080fd5b928401925b82841015611bc6578335612b3d81612077565b82529284019290840190612b2a565b8035600181900b8114611a2f57600080fd5b60006020808385031215612b7157600080fd5b82356001600160401b03811115612b8757600080fd5b8301601f81018513612b9857600080fd5b8035612ba6611b7d82611afb565b81815260059190911b82018301908381019087831115612bc557600080fd5b928401925b82841015611bc657612bdb84612b4c565b82529284019290840190612bca565b6020808252825182820181905260009190848201906040850190845b81811015611c12578351600190810b8452938501939285019201612c06565b60006020808385031215612c3857600080fd5b82356001600160401b03811115612c4e57600080fd5b8301601f81018513612c5f57600080fd5b8035612c6d611b7d82611afb565b81815260059190911b82018301908381019087831115612c8c57600080fd5b928401925b82841015611bc657833582529284019290840190612c91565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835183529284019291840191600101612cc6565b80356affffffffffffffffffffff81168114611a2f57600080fd5b60006020808385031215612d1057600080fd5b82356001600160401b03811115612d2657600080fd5b8301601f81018513612d3757600080fd5b8035612d45611b7d82611afb565b81815260059190911b82018301908381019087831115612d6457600080fd5b928401925b82841015611bc657612d7a84612ce2565b82529284019290840190612d69565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516affffffffffffffffffffff1683529284019291840191600101612da5565b8035601081900b8114611a2f57600080fd5b600060208284031215612df257600080fd5b611a4f82612dce565b8035601c81900b8114611a2f57600080fd5b600060208284031215612e1f57600080fd5b611a4f82612dfb565b803568ffffffffffffffffff81168114611a2f57600080fd5b600060208284031215612e5357600080fd5b611a4f82612e28565b8035600781900b8114611a2f57600080fd5b600060208284031215612e8057600080fd5b611a4f82612e5c565b60006020808385031215612e9c57600080fd5b82356001600160401b03811115612eb257600080fd5b8301601f81018513612ec357600080fd5b8035612ed1611b7d82611afb565b81815260059190911b82018301908381019087831115612ef057600080fd5b928401925b82841015611bc657612f06846129b8565b82529284019290840190612ef5565b6020808252825182820181905260009190848201906040850190845b81811015611c12578351601a0b83529284019291840191600101612f31565b80358015158114611a2f57600080fd5b600060208284031215612f7257600080fd5b611a4f82612f50565b60006020808385031215612f8e57600080fd5b82356001600160401b03811115612fa457600080fd5b8301601f81018513612fb557600080fd5b8035612fc3611b7d82611afb565b81815260059190911b82018301908381019087831115612fe257600080fd5b928401925b82841015611bc657612ff884612f50565b82529284019290840190612fe7565b6020808252825182820181905260009190848201906040850190845b81811015611c12578351151583529284019291840191600101613023565b80356001600160d81b0381168114611a2f57600080fd5b6000602080838503121561306b57600080fd5b82356001600160401b0381111561308157600080fd5b8301601f8101851361309257600080fd5b80356130a0611b7d82611afb565b81815260059190911b820183019083810190878311156130bf57600080fd5b928401925b82841015611bc6576130d584613041565b825292840192908401906130c4565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160d81b031683529284019291840191600101613100565b8035601581900b8114611a2f57600080fd5b6000602080838503121561314a57600080fd5b82356001600160401b0381111561316057600080fd5b8301601f8101851361317157600080fd5b803561317f611b7d82611afb565b81815260059190911b8201830190838101908783111561319e57600080fd5b928401925b82841015611bc6576131b484613125565b825292840192908401906131a3565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160150b835292840192918401916001016131df565b80356001600160a81b0381168114611a2f57600080fd5b6000602080838503121561322857600080fd5b82356001600160401b0381111561323e57600080fd5b8301601f8101851361324f57600080fd5b803561325d611b7d82611afb565b81815260059190911b8201830190838101908783111561327c57600080fd5b928401925b82841015611bc657613292846131fe565b82529284019290840190613281565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160a81b0316835292840192918401916001016132bd565b8035600081900b8114611a2f57600080fd5b60006020828403121561330657600080fd5b611a4f826132e2565b6000602080838503121561332257600080fd5b82356001600160401b0381111561333857600080fd5b8301601f8101851361334957600080fd5b8035613357611b7d82611afb565b81815260059190911b8201830190838101908783111561337657600080fd5b928401925b82841015611bc65761338c84611caf565b8252928401929084019061337b565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160110b835292840192918401916001016133b7565b600060208083850312156133e957600080fd5b82356001600160401b038111156133ff57600080fd5b8301601f8101851361341057600080fd5b803561341e611b7d82611afb565b81815260059190911b8201830190838101908783111561343d57600080fd5b928401925b82841015611bc6576134538461204a565b82529284019290840190613442565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160160b8352928401929184019160010161347e565b803565ffffffffffff81168114611a2f57600080fd5b600060208083850312156134c657600080fd5b82356001600160401b038111156134dc57600080fd5b8301601f810185136134ed57600080fd5b80356134fb611b7d82611afb565b81815260059190911b8201830190838101908783111561351a57600080fd5b928401925b82841015611bc6576135308461349d565b8252928401929084019061351f565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835165ffffffffffff168352928401929184019160010161355b565b803563ffffffff81168114611a2f57600080fd5b600060208083850312156135a657600080fd5b82356001600160401b038111156135bc57600080fd5b8301601f810185136135cd57600080fd5b80356135db611b7d82611afb565b81815260059190911b820183019083810190878311156135fa57600080fd5b928401925b82841015611bc6576136108461357f565b825292840192908401906135ff565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835163ffffffff168352928401929184019160010161363b565b8035600981900b8114611a2f57600080fd5b60006020828403121561368157600080fd5b611a4f8261365d565b8035601781900b8114611a2f57600080fd5b6000602082840312156136ae57600080fd5b611a4f8261368a565b6000602082840312156136c957600080fd5b611a4f8261215e565b8035600681900b8114611a2f57600080fd5b600060208083850312156136f757600080fd5b82356001600160401b0381111561370d57600080fd5b8301601f8101851361371e57600080fd5b803561372c611b7d82611afb565b81815260059190911b8201830190838101908783111561374b57600080fd5b928401925b82841015611bc657613761846136d2565b82529284019290840190613750565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160060b8352928401929184019160010161378c565b6000602082840312156137bd57600080fd5b611a4f826136d2565b8035600f81900b8114611a2f57600080fd5b600060208083850312156137eb57600080fd5b82356001600160401b0381111561380157600080fd5b8301601f8101851361381257600080fd5b8035613820611b7d82611afb565b81815260059190911b8201830190838101908783111561383f57600080fd5b928401925b82841015611bc657613855846137c6565b82529284019290840190613844565b6020808252825182820181905260009190848201906040850190845b81811015611c12578351600f0b83529284019291840191600101613880565b600060208083850312156138b257600080fd5b82356001600160401b038111156138c857600080fd5b8301601f810185136138d957600080fd5b80356138e7611b7d82611afb565b81815260059190911b8201830190838101908783111561390657600080fd5b928401925b82841015611bc65761391c84611cdc565b8252928401929084019061390b565b6020808252825182820181905260009190848201906040850190845b81811015611c12578351601e0b83529284019291840191600101613947565b803569ffffffffffffffffffff81168114611a2f57600080fd5b6000602080838503121561399357600080fd5b82356001600160401b038111156139a957600080fd5b8301601f810185136139ba57600080fd5b80356139c8611b7d82611afb565b81815260059190911b820183019083810190878311156139e757600080fd5b928401925b82841015611bc6576139fd84613966565b825292840192908401906139ec565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835169ffffffffffffffffffff1683529284019291840191600101613a28565b80356001600160681b0381168114611a2f57600080fd5b600060208284031215613a7957600080fd5b611a4f82613a50565b80356001600160701b0381168114611a2f57600080fd5b60006020808385031215613aac57600080fd5b82356001600160401b03811115613ac257600080fd5b8301601f81018513613ad357600080fd5b8035613ae1611b7d82611afb565b81815260059190911b82018301908381019087831115613b0057600080fd5b928401925b82841015611bc657613b1684613a82565b82529284019290840190613b05565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160701b031683529284019291840191600101613b41565b600060208284031215613b7857600080fd5b611a4f82613041565b803564ffffffffff81168114611a2f57600080fd5b60006020808385031215613ba957600080fd5b82356001600160401b03811115613bbf57600080fd5b8301601f81018513613bd057600080fd5b8035613bde611b7d82611afb565b81815260059190911b82018301908381019087831115613bfd57600080fd5b928401925b82841015611bc657613c1384613b81565b82529284019290840190613c02565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835164ffffffffff1683529284019291840191600101613c3e565b8035601b81900b8114611a2f57600080fd5b600060208284031215613c8557600080fd5b611a4f82613c61565b803562ffffff81168114611a2f57600080fd5b60006020808385031215613cb457600080fd5b82356001600160401b03811115613cca57600080fd5b8301601f81018513613cdb57600080fd5b8035613ce9611b7d82611afb565b81815260059190911b82018301908381019087831115613d0857600080fd5b928401925b82841015611bc657613d1e84613c8e565b82529284019290840190613d0d565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835162ffffff1683529284019291840191600101613d49565b803561ffff81168114611a2f57600080fd5b60006020808385031215613d8f57600080fd5b82356001600160401b03811115613da557600080fd5b8301601f81018513613db657600080fd5b8035613dc4611b7d82611afb565b81815260059190911b82018301908381019087831115613de357600080fd5b928401925b82841015611bc657613df984613d6a565b82529284019290840190613de8565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835161ffff1683529284019291840191600101613e24565b8035600281900b8114611a2f57600080fd5b600060208284031215613e6857600080fd5b611a4f82613e44565b80356001600160901b0381168114611a2f57600080fd5b60006020808385031215613e9b57600080fd5b82356001600160401b03811115613eb157600080fd5b8301601f81018513613ec257600080fd5b8035613ed0611b7d82611afb565b81815260059190911b82018301908381019087831115613eef57600080fd5b928401925b82841015611bc657613f0584613e71565b82529284019290840190613ef4565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160901b031683529284019291840191600101613f30565b60006020808385031215613f6857600080fd5b82356001600160401b0380821115613f7f57600080fd5b818501915085601f830112613f9357600080fd5b8135613fa1611b7d82611afb565b81815260059190911b83018401908481019088831115613fc057600080fd5b8585015b83811015613ff857803585811115613fdc5760008081fd5b613fea8b89838a0101611d36565b845250918601918601613fc4565b5098975050505050505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561405a57603f19888603018452614048858351611de1565b9450928501929085019060010161402c565b5092979650505050505050565b6000602080838503121561407a57600080fd5b82356001600160401b0381111561409057600080fd5b8301601f810185136140a157600080fd5b80356140af611b7d82611afb565b81815260059190911b820183019083810190878311156140ce57600080fd5b928401925b82841015611bc6576140e484611e3a565b825292840192908401906140d3565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160981b03168352928401929184019160010161410f565b80356001600160b01b0381168114611a2f57600080fd5b60006020828403121561415d57600080fd5b611a4f82614134565b6000602080838503121561417957600080fd5b82356001600160401b0381111561418f57600080fd5b8301601f810185136141a057600080fd5b80356141ae611b7d82611afb565b81815260059190911b820183019083810190878311156141cd57600080fd5b928401925b82841015611bc6576141e384612dce565b825292840192908401906141d2565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160100b8352928401929184019160010161420e565b60006020828403121561423f57600080fd5b611a4f826137c6565b6000602080838503121561425b57600080fd5b82356001600160401b0381111561427157600080fd5b8301601f8101851361428257600080fd5b8035614290611b7d82611afb565b81815260059190911b820183019083810190878311156142af57600080fd5b928401925b82841015611bc6576142c5846127c3565b825292840192908401906142b4565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160190b835292840192918401916001016142f0565b80356001600160881b0381168114611a2f57600080fd5b6000602080838503121561433957600080fd5b82356001600160401b0381111561434f57600080fd5b8301601f8101851361436057600080fd5b803561436e611b7d82611afb565b81815260059190911b8201830190838101908783111561438d57600080fd5b928401925b82841015611bc6576143a38461430f565b82529284019290840190614392565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160881b0316835292840192918401916001016143ce565b6000602080838503121561440657600080fd5b82356001600160401b0381111561441c57600080fd5b8301601f8101851361442d57600080fd5b803561443b611b7d82611afb565b81815260059190911b8201830190838101908783111561445a57600080fd5b928401925b82841015611bc65761447084613c61565b8252928401929084019061445f565b6020808252825182820181905260009190848201906040850190845b81811015611c12578351601b0b8352928401929184019160010161449b565b600060208083850312156144cd57600080fd5b82356001600160401b03808211156144e457600080fd5b818501915085601f8301126144f857600080fd5b8135614506611b7d82611afb565b81815260059190911b8301840190848101908883111561452557600080fd5b8585015b83811015613ff8578035858111156145415760008081fd5b61454f8b89838a0101611d36565b845250918601918601614529565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561405a57603f198886030184526145a0858351611de1565b94509285019290850190600101614584565b6000602082840312156145c457600080fd5b611a4f82613125565b8035601881900b8114611a2f57600080fd5b6000602082840312156145f157600080fd5b611a4f826145cd565b60006020828403121561460c57600080fd5b611a4f826129e5565b80356001600160b81b0381168114611a2f57600080fd5b60006020828403121561463e57600080fd5b611a4f82614615565b60006020828403121561465957600080fd5b611a4f826126a2565b6000602080838503121561467557600080fd5b82356001600160401b0381111561468b57600080fd5b8301601f8101851361469c57600080fd5b80356146aa611b7d82611afb565b81815260059190911b820183019083810190878311156146c957600080fd5b928401925b82841015611bc6576146df84614134565b825292840192908401906146ce565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160b01b03168352928401929184019160010161470a565b60006020828403121561474157600080fd5b611a4f82612b4c565b60006020828403121561475c57600080fd5b611a4f82611b1e565b6000602080838503121561477857600080fd5b82356001600160401b0381111561478e57600080fd5b8301601f8101851361479f57600080fd5b80356147ad611b7d82611afb565b81815260059190911b820183019083810190878311156147cc57600080fd5b928401925b82841015611bc6576147e284611a88565b825292840192908401906147d1565b6020808252825182820181905260009190848201906040850190845b81811015611c12578351600c0b8352928401929184019160010161480d565b60006020828403121561483e57600080fd5b611a4f82613d6a565b6000602080838503121561485a57600080fd5b82356001600160401b0381111561487057600080fd5b8301601f8101851361488157600080fd5b803561488f611b7d82611afb565b81815260059190911b820183019083810190878311156148ae57600080fd5b928401925b82841015611bc6576148c484612e28565b825292840192908401906148b3565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835168ffffffffffffffffff16835292840192918401916001016148ef565b8035600e81900b8114611a2f57600080fd5b6000602080838503121561493b57600080fd5b82356001600160401b0381111561495157600080fd5b8301601f8101851361496257600080fd5b8035614970611b7d82611afb565b81815260059190911b8201830190838101908783111561498f57600080fd5b928401925b82841015611bc6576149a584614916565b82529284019290840190614994565b6020808252825182820181905260009190848201906040850190845b81811015611c12578351600e0b835292840192918401916001016149d0565b8035600581900b8114611a2f57600080fd5b600060208284031215614a1357600080fd5b611a4f826149ef565b600060208284031215614a2e57600080fd5b611a4f82613a82565b60006020808385031215614a4a57600080fd5b82356001600160401b03811115614a6057600080fd5b8301601f81018513614a7157600080fd5b8035614a7f611b7d82611afb565b81815260059190911b82018301908381019087831115614a9e57600080fd5b928401925b82841015611bc657614ab484613a50565b82529284019290840190614aa3565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160681b031683529284019291840191600101614adf565b600060208284031215614b1657600080fd5b611a4f82614916565b600060208284031215614b3157600080fd5b611a4f826131fe565b600060208284031215614b4c57600080fd5b611a4f82611e6c565b600060208284031215614b6757600080fd5b611a4f8261430f565b600060208284031215614b8257600080fd5b611a4f826124f7565b60006020808385031215614b9e57600080fd5b82356001600160401b03811115614bb457600080fd5b8301601f81018513614bc557600080fd5b8035614bd3611b7d82611afb565b81815260059190911b82018301908381019087831115614bf257600080fd5b928401925b82841015611bc657614c08846149ef565b82529284019290840190614bf7565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160050b83529284019291840191600101614c33565b600060208284031215614c6457600080fd5b611a4f82611f72565b60006020808385031215614c8057600080fd5b82356001600160401b03811115614c9657600080fd5b8301601f81018513614ca757600080fd5b8035614cb5611b7d82611afb565b81815260059190911b82018301908381019087831115614cd457600080fd5b928401925b82841015611bc657614cea84611f45565b82529284019290840190614cd9565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160120b83529284019291840191600101614d15565b600060208284031215614d4657600080fd5b611a4f82612ce2565b600060208284031215614d6157600080fd5b611a4f82612304565b60006020808385031215614d7d57600080fd5b82356001600160401b03811115614d9357600080fd5b8301601f81018513614da457600080fd5b8035614db2611b7d82611afb565b81815260059190911b82018301908381019087831115614dd157600080fd5b928401925b82841015611bc657614de7846145cd565b82529284019290840190614dd6565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160180b83529284019291840191600101614e12565b600060208284031215614e4357600080fd5b611a4f8261349d565b80356001600160e01b0381168114611a2f57600080fd5b600060208284031215614e7557600080fd5b611a4f82614e4c565b60006020808385031215614e9157600080fd5b82356001600160401b03811115614ea757600080fd5b8301601f81018513614eb857600080fd5b8035614ec6611b7d82611afb565b81815260059190911b82018301908381019087831115614ee557600080fd5b928401925b82841015611bc657614efb8461365d565b82529284019290840190614eea565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160090b83529284019291840191600101614f26565b60006020808385031215614f5857600080fd5b82356001600160401b03811115614f6e57600080fd5b8301601f81018513614f7f57600080fd5b8035614f8d611b7d82611afb565b81815260059190911b82018301908381019087831115614fac57600080fd5b928401925b82841015611bc657614fc284614e4c565b82529284019290840190614fb1565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160e01b031683529284019291840191600101614fed565b80356001600160d01b0381168114611a2f57600080fd5b60006020828403121561503b57600080fd5b611a4f82615012565b60006020828403121561505657600080fd5b611a4f8261357f565b63ffffffff841681526001600160401b038316602082015260606040820152600061508d6060830184611de1565b95945050505050565b6000602082840312156150a857600080fd5b611a4f82613966565b6000602082840312156150c357600080fd5b611a4f826128d4565b80356001600160e81b0381168114611a2f57600080fd5b6000602082840312156150f557600080fd5b611a4f826150cc565b6000602080838503121561511157600080fd5b82356001600160401b0381111561512757600080fd5b8301601f8101851361513857600080fd5b8035615146611b7d82611afb565b81815260059190911b8201830190838101908783111561516557600080fd5b928401925b82841015611bc65761517b84612dfb565b8252928401929084019061516a565b6020808252825182820181905260009190848201906040850190845b81811015611c12578351601c0b835292840192918401916001016151a6565b6000602082840312156151d757600080fd5b611a4f826127f0565b8035600881900b8114611a2f57600080fd5b6000602080838503121561520557600080fd5b82356001600160401b0381111561521b57600080fd5b8301601f8101851361522c57600080fd5b803561523a611b7d82611afb565b81815260059190911b8201830190838101908783111561525957600080fd5b928401925b82841015611bc65761526f846151e0565b8252928401929084019061525e565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160080b8352928401929184019160010161529a565b600060208083850312156152cc57600080fd5b82356001600160401b038111156152e257600080fd5b8301601f810185136152f357600080fd5b8035615301611b7d82611afb565b81815260059190911b8201830190838101908783111561532057600080fd5b928401925b82841015611bc657615336846132e2565b82529284019290840190615325565b602080825282518282018190526000919084820190604085019084805b82811015615380578451820b84529385019392850192600101615362565b5091979650505050505050565b600060208083850312156153a057600080fd5b82356001600160401b038111156153b657600080fd5b8301601f810185136153c757600080fd5b80356153d5611b7d82611afb565b81815260059190911b820183019083810190878311156153f457600080fd5b928401925b82841015611bc65761540a8461277b565b825292840192908401906153f9565b6020808252825182820181905260009190848201906040850190845b81811015611c12578351601d0b83529284019291840191600101615435565b6000602080838503121561546757600080fd5b82356001600160401b0381111561547d57600080fd5b8301601f8101851361548e57600080fd5b803561549c611b7d82611afb565b81815260059190911b820183019083810190878311156154bb57600080fd5b928401925b82841015611bc6576154d184611c82565b825292840192908401906154c0565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160040b835292840192918401916001016154fc565b6000602080838503121561552e57600080fd5b82356001600160401b0381111561554457600080fd5b8301601f8101851361555557600080fd5b8035615563611b7d82611afb565b81815260059190911b8201830190838101908783111561558257600080fd5b928401925b82841015611bc65761559884613e44565b82529284019290840190615587565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160020b835292840192918401916001016155c3565b600060208083850312156155f557600080fd5b82356001600160401b0381111561560b57600080fd5b8301601f8101851361561c57600080fd5b803561562a611b7d82611afb565b81815260059190911b8201830190838101908783111561564957600080fd5b928401925b82841015611bc65761565f846150cc565b8252928401929084019061564e565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160e81b03168352928401929184019160010161568a565b600060208083850312156156c257600080fd5b82356001600160401b038111156156d857600080fd5b8301601f810185136156e957600080fd5b80356156f7611b7d82611afb565b81815260059190911b8201830190838101908783111561571657600080fd5b928401925b82841015611bc65761572c8461368a565b8252928401929084019061571b565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160170b83529284019291840191600101615757565b60006020828403121561578857600080fd5b611a4f826151e0565b600060208083850312156157a457600080fd5b82356001600160401b038111156157ba57600080fd5b8301601f810185136157cb57600080fd5b80356157d9611b7d82611afb565b81815260059190911b820183019083810190878311156157f857600080fd5b928401925b82841015611bc65761580e84612e5c565b825292840192908401906157fd565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160070b83529284019291840191600101615839565b60006020828403121561586a57600080fd5b611a4f82613e71565b60006020828403121561588557600080fd5b611a4f82613c8e565b600060208083850312156158a157600080fd5b82356001600160401b038111156158b757600080fd5b8301601f810185136158c857600080fd5b80356158d6611b7d82611afb565b81815260059190911b820183019083810190878311156158f557600080fd5b928401925b82841015611bc65761590b84611a18565b825292840192908401906158fa565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160801b031683529284019291840191600101615936565b6000602080838503121561596e57600080fd5b82356001600160401b0381111561598457600080fd5b8301601f8101851361599557600080fd5b80356159a3611b7d82611afb565b81815260059190911b820183019083810190878311156159c257600080fd5b928401925b82841015611bc6576159d884615012565b825292840192908401906159c7565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160d01b031683529284019291840191600101615a03565b60006020808385031215615a3b57600080fd5b82356001600160401b03811115615a5157600080fd5b8301601f81018513615a6257600080fd5b8035615a70611b7d82611afb565b81815260059190911b82018301908381019087831115615a8f57600080fd5b928401925b82841015611bc657615aa584614615565b82529284019290840190615a94565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160b81b031683529284019291840191600101615ad0565b602081526000825160406020840152615b116060840182611de1565b9050602084015160408401528091505092915050565b60006020808385031215615b3a57600080fd5b82356001600160401b03811115615b5057600080fd5b8301601f81018513615b6157600080fd5b8035615b6f611b7d82611afb565b81815260059190911b82018301908381019087831115615b8e57600080fd5b928401925b82841015611bc657615ba484611c1e565b82529284019290840190615b93565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160781b031683529284019291840191600101615bcf565b60006020808385031215615c0757600080fd5b82356001600160401b03811115615c1d57600080fd5b8301601f81018513615c2e57600080fd5b8035615c3c611b7d82611afb565b81815260059190911b82018301908381019087831115615c5b57600080fd5b928401925b82841015611bc657615c7184611a56565b82529284019290840190615c60565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160601b031683529284019291840191600101615c9c565b600060208284031215615cd357600080fd5b611a4f82613b81565b634e487b7160e01b600052601160045260246000fd5b600081810b9083900b01607f8113607f1982121715615d1357615d13615cdc565b92915050565b8082018281126000831280158216821582161715615d3957615d39615cdc565b505092915050565b63ffffffff828116828216039080821115615d5e57615d5e615cdc565b5092915050565b600481810b9083900b01647fffffffff8113647fffffffff1982121715615d1357615d13615cdc565b600181811c90821680615da257607f821691505b602082108103615dc257634e487b7160e01b600052602260045260246000fd5b5091905056fea2646970667358221220a5b87b6881e1a012e4a59d55c230cfcf3a2342b83efdbfb3c5f5cfa532fc854864736f6c63430008120033"; - private static IntegrationTestEnv testEnv; - private static FileId fileId; - private static ContractId contractId; - - @BeforeAll - public static void beforeAll() throws Exception { - testEnv = new IntegrationTestEnv(1); - - var response = new FileCreateTransaction().setKeys(testEnv.operatorKey).execute(testEnv.client); - - fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - new FileAppendTransaction().setFileId(fileId).setContents(SMART_CONTRACT_BYTECODE).setMaxChunks(31) - .execute(testEnv.client); - - response = new ContractCreateTransaction().setAdminKey(testEnv.operatorKey).setGas(1500000) - .setConstructorParameters(new ContractFunctionParameters()).setBytecodeFileId(fileId) - .execute(testEnv.client); - - contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); - } - - @AfterAll - public static void afterAll() throws Exception { - new ContractDeleteTransaction().setTransferAccountId(testEnv.operatorId).setContractId(contractId) - .execute(testEnv.client).getReceipt(testEnv.client); - - new FileDeleteTransaction().setFileId(fileId).execute(testEnv.client).getReceipt(testEnv.client); - - testEnv.close(); - } - - // so we don't get "network is busy" error - @AfterEach - public void afterEach() throws InterruptedException { - Thread.sleep(150); - } - - @Test - @DisplayName("Can receive uint8 min value from contract call") - void canCallContractFunctionUint8Min() throws Exception { - var gas = new MirrorNodeContractEstimateGasQuery().setContractId(contractId) - .setFunction("returnUint8", new ContractFunctionParameters().addUint8((byte) 0x0)) - .execute(testEnv.client); - - var response = new ContractCallQuery().setContractId(contractId).setGas(gas) - .setFunction("returnUint8", new ContractFunctionParameters().addUint8((byte) 0x0)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint8(0)).isEqualTo((byte) 0x0); - } - - @Test - @DisplayName("Can receive uint8 max value from contract call") - void canCallContractFunctionUint8Max() throws Exception { - int uint8Max = 255; - byte uint8MaxByte = (byte) uint8Max; - - var gas = new MirrorNodeContractEstimateGasQuery().setContractId(contractId) - .setFunction("returnUint8", new ContractFunctionParameters().addUint8(uint8MaxByte)) - .execute(testEnv.client); - - var response = new ContractCallQuery().setContractId(contractId).setGas(gas) - .setFunction("returnUint8", new ContractFunctionParameters().addUint8(uint8MaxByte)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var uint8MaxFromResponse = Byte.toUnsignedInt(response.getUint8(0)); - - assertThat(uint8MaxFromResponse).isEqualTo(uint8Max); - } - - @Test - @DisplayName("Can receive uint8 array value from contract call") - void canCallContractFunctionUint8Array() throws Exception { - byte uint8MinByte = (byte) 0x0; - int uint8Max = 255; - byte uint8MaxByte = (byte) uint8Max; - byte[] uint8Array = {uint8MinByte, uint8MaxByte}; - - var gas = new MirrorNodeContractEstimateGasQuery().setContractId(contractId) - .setFunction("returnUint8Arr", new ContractFunctionParameters().addUint8Array(uint8Array)) - .execute(testEnv.client); - - var response = new ContractCallQuery().setContractId(contractId).setGas(gas) - .setFunction("returnUint8Arr", new ContractFunctionParameters().addUint8Array(uint8Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (int[]) response.getResult("(uint8[])").get(0); - - assertThat(responseResult[0]).isEqualTo(uint8MinByte); - assertThat(responseResult[1]).isEqualTo(uint8Max); - } - - @Test - @DisplayName("Can receive uint16 min value from contract call") - void canCallContractFunctionUint16Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint16", new ContractFunctionParameters().addUint16(0)).setQueryPayment(new Hbar(10)) - .execute(testEnv.client); - - assertThat(response.getUint32(0)).isEqualTo(0); - } - - @Test - @DisplayName("Can receive uint16 max value from contract call") - void canCallContractFunctionUint16Max() throws Exception { - var uint16Max = "65535"; - int uint16MaxInt = Integer.parseUnsignedInt(uint16Max); - - var gas = new MirrorNodeContractEstimateGasQuery().setContractId(contractId) - .setFunction("returnUint16", new ContractFunctionParameters().addUint16(uint16MaxInt)) - .execute(testEnv.client); - - var response = new ContractCallQuery().setContractId(contractId).setGas(gas) - .setFunction("returnUint16", new ContractFunctionParameters().addUint16(uint16MaxInt)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var uint16MaxIntFromResponse = Integer.toUnsignedString(response.getUint32(0)); - - assertThat(uint16MaxIntFromResponse).isEqualTo(uint16Max); - } - - @Test - @DisplayName("Can receive uint16 array value from contract call") - void canCallContractFunctionUint16Array() throws Exception { - int uint16MinInt = 0; - var uint16Max = "65535"; - int uint16MaxInt = Integer.parseUnsignedInt(uint16Max); - int[] uint16Array = {uint16MinInt, uint16MaxInt}; - - var gas = new MirrorNodeContractEstimateGasQuery().setContractId(contractId) - .setFunction("returnUint16Arr", new ContractFunctionParameters().addUint16Array(uint16Array)) - .execute(testEnv.client); - - var response = new ContractCallQuery().setContractId(contractId).setGas(gas) - .setFunction("returnUint16Arr", new ContractFunctionParameters().addUint16Array(uint16Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (int[]) response.getResult("(uint16[])").get(0); - - assertThat(responseResult).isEqualTo(uint16Array); - } - - @Test - @DisplayName("Can receive uint24 min value from contract call") - void canCallContractFunctionUint24Min() throws Exception { - var gas = new MirrorNodeContractEstimateGasQuery().setContractId(contractId) - .setFunction("returnUint24", new ContractFunctionParameters().addUint24(0)) - .execute(testEnv.client); - - var response = new ContractCallQuery().setContractId(contractId).setGas(gas) - .setFunction("returnUint24", new ContractFunctionParameters().addUint24(0)).setQueryPayment(new Hbar(10)) - .execute(testEnv.client); - - assertThat(response.getUint32(0)).isEqualTo(0); - } - - @Test - @DisplayName("Can receive uint24 max value from contract call") - void canCallContractFunctionUint24Max() throws Exception { - var uint24Max = "16777215"; - int uint24MaxInt = Integer.parseUnsignedInt(uint24Max); - - var gas = new MirrorNodeContractEstimateGasQuery().setContractId(contractId) - .setFunction("returnUint24", new ContractFunctionParameters().addUint24(uint24MaxInt)) - .execute(testEnv.client); - - var response = new ContractCallQuery().setContractId(contractId).setGas(gas) - .setFunction("returnUint24", new ContractFunctionParameters().addUint24(uint24MaxInt)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var uint24MaxIntFromResponse = Integer.toUnsignedString(response.getUint32(0)); - - assertThat(uint24MaxIntFromResponse).isEqualTo(uint24Max); - } - - @Test - @DisplayName("Can receive uint24 array value from contract call") - void canCallContractFunctionUint24Array() throws Exception { - int uint24MinInt = 0; - var uint24Max = "16777215"; - int uint24MaxInt = Integer.parseUnsignedInt(uint24Max); - int[] uint24Array = {uint24MinInt, uint24MaxInt}; - - var gas = new MirrorNodeContractEstimateGasQuery().setContractId(contractId) - .setFunction("returnUint24Arr", new ContractFunctionParameters().addUint24Array(uint24Array)) - .execute(testEnv.client); - - var response = new ContractCallQuery().setContractId(contractId).setGas(gas) - .setFunction("returnUint24Arr", new ContractFunctionParameters().addUint24Array(uint24Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (int[]) response.getResult("(uint24[])").get(0); - - assertThat(responseResult).isEqualTo(uint24Array); - } - - @Test - @DisplayName("Can receive uint32 min value from contract call") - void canCallContractFunctionUint32Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint32", new ContractFunctionParameters().addUint32(0)).setQueryPayment(new Hbar(10)) - .execute(testEnv.client); - - assertThat(response.getUint32(0)).isEqualTo(0); - } - - @Test - @DisplayName("Can receive uint32 max value from contract call") - void canCallContractFunctionUint32Max() throws Exception { - var uint32Max = "4294967295"; - int uint32MaxInt = Integer.parseUnsignedInt(uint32Max); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint32", new ContractFunctionParameters().addUint32(uint32MaxInt)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var uint32MaxIntFromResponse = Integer.toUnsignedString(response.getUint32(0)); - - assertThat(uint32MaxIntFromResponse).isEqualTo(uint32Max); - } - - @Test - @DisplayName("Can receive uint32 array value from contract call") - void canCallContractFunctionUint32Array() throws Exception { - int uint32MinInt = 0; - var uint32Max = "4294967295"; - int uint32MaxInt = Integer.parseUnsignedInt(uint32Max); - int[] uint32Array = {uint32MinInt, uint32MaxInt}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint32Arr", new ContractFunctionParameters().addUint32Array(uint32Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (long[]) response.getResult("(uint32[])").get(0); - - assertThat(responseResult[0]).isEqualTo(uint32MinInt); - assertThat(responseResult[1]).isEqualTo(Long.parseUnsignedLong(uint32Max)); - } - - @Test - @DisplayName("Can receive uint40 min value from contract call") - void canCallContractFunctionUint40Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint40", new ContractFunctionParameters().addUint40(0)).setQueryPayment(new Hbar(10)) - .execute(testEnv.client); - - assertThat(response.getUint64(0)).isEqualTo(0); - } - - @Test - @DisplayName("Can receive uint40 max value from contract call") - void canCallContractFunctionUint40Max() throws Exception { - var uint40Max = "109951162777"; - long uint40MaxLong = Long.parseUnsignedLong(uint40Max); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint40", new ContractFunctionParameters().addUint40(uint40MaxLong)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var uint64MaxLongFromResponse = Long.toUnsignedString(response.getUint64(0)); - - assertThat(uint64MaxLongFromResponse).isEqualTo(uint40Max); - } - - @Test - @DisplayName("Can receive uint40 array value from contract call") - void canCallContractFunctionUint40Array() throws Exception { - long uint40MinLong = 0; - var uint40Max = "109951162777"; - long uint40MaxLong = Long.parseUnsignedLong(uint40Max); - long[] uint40Array = {uint40MinLong, uint40MaxLong}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint40Arr", new ContractFunctionParameters().addUint40Array(uint40Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (long[]) response.getResult("(uint40[])").get(0); - - assertThat(responseResult).isEqualTo(uint40Array); - } - - @Test - @DisplayName("Can receive uint48 min value from contract call") - void canCallContractFunctionUint48Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint48", new ContractFunctionParameters().addUint48(0)).setQueryPayment(new Hbar(10)) - .execute(testEnv.client); - - assertThat(response.getUint64(0)).isEqualTo(0); - } - - @Test - @DisplayName("Can receive uint48 max value from contract call") - void canCallContractFunctionUint48Max() throws Exception { - var uint48Max = "281474976710655"; - long uint48MaxLong = Long.parseUnsignedLong(uint48Max); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint48", new ContractFunctionParameters().addUint48(uint48MaxLong)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var uint64MaxLongFromResponse = Long.toUnsignedString(response.getUint64(0)); - - assertThat(uint64MaxLongFromResponse).isEqualTo(uint48Max); - } - - @Test - @DisplayName("Can receive uint48 array value from contract call") - void canCallContractFunctionUint48Array() throws Exception { - long uint48MinLong = 0; - var uint48Max = "281474976710655"; - long uint48MaxLong = Long.parseUnsignedLong(uint48Max); - long[] uint48Array = {uint48MinLong, uint48MaxLong}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint48Arr", new ContractFunctionParameters().addUint48Array(uint48Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (long[]) response.getResult("(uint48[])").get(0); - - assertThat(responseResult).isEqualTo(uint48Array); - } - - @Test - @DisplayName("Can receive uint56 min value from contract call") - void canCallContractFunctionUint56Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint56", new ContractFunctionParameters().addUint56(0)).setQueryPayment(new Hbar(10)) - .execute(testEnv.client); - - assertThat(response.getUint64(0)).isEqualTo(0); - } - - @Test - @DisplayName("Can receive uint56 max value from contract call") - void canCallContractFunctionUint56Max() throws Exception { - var uint56Max = "72057594037927935"; - long uint56MaxLong = Long.parseUnsignedLong(uint56Max); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint56", new ContractFunctionParameters().addUint56(uint56MaxLong)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var uint64MaxLongFromResponse = Long.toUnsignedString(response.getUint64(0)); - - assertThat(uint64MaxLongFromResponse).isEqualTo(uint56Max); - } - - @Test - @DisplayName("Can receive uint56 array value from contract call") - void canCallContractFunctionUint56Array() throws Exception { - long uint56MinLong = 0; - var uint56Max = "72057594037927935"; - long uint56MaxLong = Long.parseUnsignedLong(uint56Max); - long[] uint56Array = {uint56MinLong, uint56MaxLong}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint56Arr", new ContractFunctionParameters().addUint56Array(uint56Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (long[]) response.getResult("(uint56[])").get(0); - - assertThat(responseResult).isEqualTo(uint56Array); - } - - @Test - @DisplayName("Can receive uint64 min value from contract call") - void canCallContractFunctionUint64Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint64", new ContractFunctionParameters().addUint64(0)).setQueryPayment(new Hbar(10)) - .execute(testEnv.client); - - assertThat(response.getUint64(0)).isEqualTo(0); - } - - @Test - @DisplayName("Can receive uint64 max value from contract call") - void canCallContractFunctionUint64Max() throws Exception { - var uint64Max = "9223372036854775807"; - long uint64MaxLong = Long.parseUnsignedLong(uint64Max); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint64", new ContractFunctionParameters().addUint64(uint64MaxLong)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var uint64MaxLongFromResponse = Long.toUnsignedString(response.getUint64(0)); - - assertThat(uint64MaxLongFromResponse).isEqualTo(uint64Max); - } - - @Test - @DisplayName("Can receive uint64 array value from contract call") - void canCallContractFunctionUint64Array() throws Exception { - long uint64MinLong = 0; - var uint64Max = "9223372036854775807"; - long uint64MaxLong = Long.parseUnsignedLong(uint64Max); - long[] uint64Array = {uint64MinLong, uint64MaxLong}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint64Arr", new ContractFunctionParameters().addUint64Array(uint64Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(uint64[])").get(0); - - assertThat(responseResult[0]).isEqualTo(uint64MinLong); - assertThat(responseResult[1]).isEqualTo(Long.parseUnsignedLong(uint64Max)); - } - - @Test - @DisplayName("Can receive uint72 min value from contract call") - void canCallContractFunctionUint72Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint72", new ContractFunctionParameters().addUint72(BigInteger.ZERO)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); - } - - @Test - @DisplayName("Can receive uint72 max value from contract call") - void canCallContractFunctionUint72Max() throws Exception { - BigInteger uint72Max = new BigInteger("4722366482869645213695"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint72", new ContractFunctionParameters().addUint72(uint72Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(uint72Max); - } - - @Test - @DisplayName("Can receive uint72 array value from contract call") - void canCallContractFunctionUint72Array() throws Exception { - BigInteger uint72Min = BigInteger.ZERO; - BigInteger uint72Max = new BigInteger("4722366482869645213695"); - BigInteger[] uint72Array = {uint72Min, uint72Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint72Arr", new ContractFunctionParameters().addUint72Array(uint72Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(uint72[])").get(0); - - assertThat(responseResult).isEqualTo(uint72Array); - } - - @Test - @DisplayName("Can receive uint80 min value from contract call") - void canCallContractFunctionUint80Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint80", new ContractFunctionParameters().addUint80(BigInteger.ZERO)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); - } - - @Test - @DisplayName("Can receive uint80 max value from contract call") - void canCallContractFunctionUint80Max() throws Exception { - BigInteger uint80Max = new BigInteger("1208925819614629174706175"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint80", new ContractFunctionParameters().addUint80(uint80Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(uint80Max); - } - - @Test - @DisplayName("Can receive uint80 array value from contract call") - void canCallContractFunctionUint80Array() throws Exception { - BigInteger uint80Min = BigInteger.ZERO; - BigInteger uint80Max = new BigInteger("1208925819614629174706175"); - BigInteger[] uint80Array = {uint80Min, uint80Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint80Arr", new ContractFunctionParameters().addUint80Array(uint80Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(uint80[])").get(0); - - assertThat(responseResult).isEqualTo(uint80Array); - } - - @Test - @DisplayName("Can receive uint88 min value from contract call") - void canCallContractFunctionUint88Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint88", new ContractFunctionParameters().addUint88(BigInteger.ZERO)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); - } - - @Test - @DisplayName("Can receive uint88 max value from contract call") - void canCallContractFunctionUint88Max() throws Exception { - BigInteger uint88Max = new BigInteger("309485009821345068724781055"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint88", new ContractFunctionParameters().addUint88(uint88Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(uint88Max); - } - - @Test - @DisplayName("Can receive uint88 array value from contract call") - void canCallContractFunctionUint88Array() throws Exception { - BigInteger uint88Min = BigInteger.ZERO; - BigInteger uint88Max = new BigInteger("309485009821345068724781055"); - BigInteger[] uint88Array = {uint88Min, uint88Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint88Arr", new ContractFunctionParameters().addUint88Array(uint88Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(uint88[])").get(0); - - assertThat(responseResult).isEqualTo(uint88Array); - } - - @Test - @DisplayName("Can receive uint96 min value from contract call") - void canCallContractFunctionUint96Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint96", new ContractFunctionParameters().addUint96(BigInteger.ZERO)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); - } - - @Test - @DisplayName("Can receive uint96 max value from contract call") - void canCallContractFunctionUint96Max() throws Exception { - BigInteger uint96Max = new BigInteger("79228162514264337593543950335"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint96", new ContractFunctionParameters().addUint96(uint96Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(uint96Max); - } - - @Test - @DisplayName("Can receive uint96 array value from contract call") - void canCallContractFunctionUint96Array() throws Exception { - BigInteger uint96Min = BigInteger.ZERO; - BigInteger uint96Max = new BigInteger("79228162514264337593543950335"); - BigInteger[] uint96Array = {uint96Min, uint96Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint96Arr", new ContractFunctionParameters().addUint96Array(uint96Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(uint96[])").get(0); - - assertThat(responseResult).isEqualTo(uint96Array); - } - - @Test - @DisplayName("Can receive uint104 min value from contract call") - void canCallContractFunctionUint104Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint104", new ContractFunctionParameters().addUint104(BigInteger.ZERO)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); - } - - @Test - @DisplayName("Can receive uint104 max value from contract call") - void canCallContractFunctionUint104Max() throws Exception { - BigInteger uint104Max = new BigInteger("20282409603651670423947251286015"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint104", new ContractFunctionParameters().addUint104(uint104Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(uint104Max); - } - - @Test - @DisplayName("Can receive uint104 array value from contract call") - void canCallContractFunctionUint104Array() throws Exception { - BigInteger uint104Min = BigInteger.ZERO; - BigInteger uint104Max = new BigInteger("20282409603651670423947251286015"); - BigInteger[] uint104Array = {uint104Min, uint104Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint104Arr", new ContractFunctionParameters().addUint104Array(uint104Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(uint104[])").get(0); - - assertThat(responseResult).isEqualTo(uint104Array); - } - - @Test - @DisplayName("Can receive uint112 min value from contract call") - void canCallContractFunctionUint112Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint112", new ContractFunctionParameters().addUint112(BigInteger.ZERO)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); - } - - @Test - @DisplayName("Can receive uint112 max value from contract call") - void canCallContractFunctionUint112Max() throws Exception { - BigInteger uint112Max = new BigInteger("5192296858534827628530496329220095"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint112", new ContractFunctionParameters().addUint112(uint112Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(uint112Max); - } - - @Test - @DisplayName("Can receive uint112 array value from contract call") - void canCallContractFunctionUint112Array() throws Exception { - BigInteger uint112Min = BigInteger.ZERO; - BigInteger uint112Max = new BigInteger("5192296858534827628530496329220095"); - BigInteger[] uint112Array = {uint112Min, uint112Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint112Arr", new ContractFunctionParameters().addUint112Array(uint112Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(uint112[])").get(0); - - assertThat(responseResult).isEqualTo(uint112Array); - } - - @Test - @DisplayName("Can receive uint120 min value from contract call") - void canCallContractFunctionUint120Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint120", new ContractFunctionParameters().addUint120(BigInteger.ZERO)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); - } - - @Test - @DisplayName("Can receive uint120 max value from contract call") - void canCallContractFunctionUint120Max() throws Exception { - BigInteger uint120Max = new BigInteger("1329227995784915872903807060280344575"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint120", new ContractFunctionParameters().addUint120(uint120Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(uint120Max); - } - - @Test - @DisplayName("Can receive uint120 array value from contract call") - void canCallContractFunctionUint120Array() throws Exception { - BigInteger uint120Min = BigInteger.ZERO; - BigInteger uint120Max = new BigInteger("1329227995784915872903807060280344575"); - BigInteger[] uint120Array = {uint120Min, uint120Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint120Arr", new ContractFunctionParameters().addUint120Array(uint120Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(uint120[])").get(0); - - assertThat(responseResult).isEqualTo(uint120Array); - } - - @Test - @DisplayName("Can receive uint128 min value from contract call") - void canCallContractFunctionUint128Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint128", new ContractFunctionParameters().addUint128(BigInteger.ZERO)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); - } - - @Test - @DisplayName("Can receive uint128 max value from contract call") - void canCallContractFunctionUint128Max() throws Exception { - BigInteger uint128Max = new BigInteger("340282366920938463463374607431768211455"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint128", new ContractFunctionParameters().addUint128(uint128Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(uint128Max); - } - - @Test - @DisplayName("Can receive uint128 array value from contract call") - void canCallContractFunctionUint128Array() throws Exception { - BigInteger uint128Min = BigInteger.ZERO; - BigInteger uint128Max = new BigInteger("340282366920938463463374607431768211455"); - BigInteger[] uint128Array = {uint128Min, uint128Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint128Arr", new ContractFunctionParameters().addUint128Array(uint128Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(uint128[])").get(0); - - assertThat(responseResult).isEqualTo(uint128Array); - } - - @Test - @DisplayName("Can receive uint136 min value from contract call") - void canCallContractFunctionUint136Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint136", new ContractFunctionParameters().addUint136(BigInteger.ZERO)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); - } - - @Test - @DisplayName("Can receive uint136 max value from contract call") - void canCallContractFunctionUint136Max() throws Exception { - BigInteger uint136Max = new BigInteger("87112285931760246646623899502532662132735"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint136", new ContractFunctionParameters().addUint136(uint136Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(uint136Max); - } - - @Test - @DisplayName("Can receive uint136 array value from contract call") - void canCallContractFunctionUint136Array() throws Exception { - BigInteger uint136Min = BigInteger.ZERO; - BigInteger uint136Max = new BigInteger("87112285931760246646623899502532662132735"); - BigInteger[] uint136Array = {uint136Min, uint136Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint136Arr", new ContractFunctionParameters().addUint136Array(uint136Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(uint136[])").get(0); - - assertThat(responseResult).isEqualTo(uint136Array); - } - - @Test - @DisplayName("Can receive uint144 min value from contract call") - void canCallContractFunctionUint144Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint144", new ContractFunctionParameters().addUint144(BigInteger.ZERO)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); - } - - @Test - @DisplayName("Can receive uint144 max value from contract call") - void canCallContractFunctionUint144Max() throws Exception { - BigInteger uint144Max = new BigInteger("22300745198530623141535718272648361505980415"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint144", new ContractFunctionParameters().addUint144(uint144Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(uint144Max); - } - - @Test - @DisplayName("Can receive uint144 array value from contract call") - void canCallContractFunctionUint144Array() throws Exception { - BigInteger uint144Min = BigInteger.ZERO; - BigInteger uint144Max = new BigInteger("22300745198530623141535718272648361505980415"); - BigInteger[] uint144Array = {uint144Min, uint144Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint144Arr", new ContractFunctionParameters().addUint144Array(uint144Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(uint144[])").get(0); - - assertThat(responseResult).isEqualTo(uint144Array); - } - - @Test - @DisplayName("Can receive uint152 min value from contract call") - void canCallContractFunctionUint152Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint152", new ContractFunctionParameters().addUint152(BigInteger.ZERO)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); - } - - @Test - @DisplayName("Can receive uint152 max value from contract call") - void canCallContractFunctionUint152Max() throws Exception { - BigInteger uint152Max = new BigInteger("5708990770823839524233143877797980545530986495"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint152", new ContractFunctionParameters().addUint152(uint152Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(uint152Max); - } - - @Test - @DisplayName("Can receive uint152 array value from contract call") - void canCallContractFunctionUint152Array() throws Exception { - BigInteger uint152Min = BigInteger.ZERO; - BigInteger uint152Max = new BigInteger("5708990770823839524233143877797980545530986495"); - BigInteger[] uint152Array = {uint152Min, uint152Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint152Arr", new ContractFunctionParameters().addUint152Array(uint152Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(uint152[])").get(0); - - assertThat(responseResult).isEqualTo(uint152Array); - } - - @Test - @DisplayName("Can receive uint160 min value from contract call") - void canCallContractFunctionUint160Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint160", new ContractFunctionParameters().addUint160(BigInteger.ZERO)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); - } - - @Test - @DisplayName("Can receive uint160 max value from contract call") - void canCallContractFunctionUint160Max() throws Exception { - BigInteger uint160Max = new BigInteger("1461501637330902918203684832716283019655932542975"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint160", new ContractFunctionParameters().addUint160(uint160Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(uint160Max); - } - - @Test - @DisplayName("Can receive uint160 array value from contract call") - void canCallContractFunctionUint160Array() throws Exception { - BigInteger uint160Min = BigInteger.ZERO; - BigInteger uint160Max = new BigInteger("1461501637330902918203684832716283019655932542975"); - BigInteger[] uint160Array = {uint160Min, uint160Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint160Arr", new ContractFunctionParameters().addUint160Array(uint160Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(uint160[])").get(0); - - assertThat(responseResult).isEqualTo(uint160Array); - } - - @Test - @DisplayName("Can receive uint168 min value from contract call") - void canCallContractFunctionUint168Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint168", new ContractFunctionParameters().addUint168(BigInteger.ZERO)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); - } - - @Test - @DisplayName("Can receive uint168 max value from contract call") - void canCallContractFunctionUint168Max() throws Exception { - BigInteger uint168Max = new BigInteger("374144419156711147060143317175368453031918731001855"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint168", new ContractFunctionParameters().addUint168(uint168Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(uint168Max); - } - - @Test - @DisplayName("Can receive uint168 array value from contract call") - void canCallContractFunctionUint168Array() throws Exception { - BigInteger uint168Min = BigInteger.ZERO; - BigInteger uint168Max = new BigInteger("374144419156711147060143317175368453031918731001855"); - BigInteger[] uint168Array = {uint168Min, uint168Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint168Arr", new ContractFunctionParameters().addUint168Array(uint168Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(uint168[])").get(0); - - assertThat(responseResult).isEqualTo(uint168Array); - } - - @Test - @DisplayName("Can receive uint176 min value from contract call") - void canCallContractFunctionUint176Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint176", new ContractFunctionParameters().addUint176(BigInteger.ZERO)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); - } - - @Test - @DisplayName("Can receive uint176 max value from contract call") - void canCallContractFunctionUint176Max() throws Exception { - BigInteger uint176Max = new BigInteger("95780971304118053647396689196894323976171195136475135"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint176", new ContractFunctionParameters().addUint176(uint176Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(uint176Max); - } - - @Test - @DisplayName("Can receive uint176 array value from contract call") - void canCallContractFunctionUint176Array() throws Exception { - BigInteger uint176Min = BigInteger.ZERO; - BigInteger uint176Max = new BigInteger("95780971304118053647396689196894323976171195136475135"); - BigInteger[] uint176Array = {uint176Min, uint176Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint176Arr", new ContractFunctionParameters().addUint176Array(uint176Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(uint176[])").get(0); - - assertThat(responseResult).isEqualTo(uint176Array); - } - - @Test - @DisplayName("Can receive uint184 min value from contract call") - void canCallContractFunctionUint184Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint184", new ContractFunctionParameters().addUint184(BigInteger.ZERO)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); - } - - @Test - @DisplayName("Can receive uint184 max value from contract call") - void canCallContractFunctionUint184Max() throws Exception { - BigInteger uint184Max = new BigInteger("24519928653854221733733552434404946937899825954937634815"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint184", new ContractFunctionParameters().addUint184(uint184Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(uint184Max); - } - - @Test - @DisplayName("Can receive uint184 array value from contract call") - void canCallContractFunctionUint184Array() throws Exception { - BigInteger uint184Min = BigInteger.ZERO; - BigInteger uint184Max = new BigInteger("24519928653854221733733552434404946937899825954937634815"); - BigInteger[] uint184Array = {uint184Min, uint184Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint184Arr", new ContractFunctionParameters().addUint184Array(uint184Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(uint184[])").get(0); - - assertThat(responseResult).isEqualTo(uint184Array); - } - - @Test - @DisplayName("Can receive uint192 min value from contract call") - void canCallContractFunctionUint192Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint192", new ContractFunctionParameters().addUint192(BigInteger.ZERO)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); - } - - @Test - @DisplayName("Can receive uint192 max value from contract call") - void canCallContractFunctionUint192Max() throws Exception { - BigInteger uint192Max = new BigInteger("6277101735386680763835789423207666416102355444464034512895"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint192", new ContractFunctionParameters().addUint192(uint192Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(uint192Max); - } - - @Test - @DisplayName("Can receive uint192 array value from contract call") - void canCallContractFunctionUint192Array() throws Exception { - BigInteger uint192Min = BigInteger.ZERO; - BigInteger uint192Max = new BigInteger("6277101735386680763835789423207666416102355444464034512895"); - BigInteger[] uint192Array = {uint192Min, uint192Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint192Arr", new ContractFunctionParameters().addUint192Array(uint192Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(uint192[])").get(0); - - assertThat(responseResult).isEqualTo(uint192Array); - } - - @Test - @DisplayName("Can receive uint200 min value from contract call") - void canCallContractFunctionUint200Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint200", new ContractFunctionParameters().addUint200(BigInteger.ZERO)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); - } - - @Test - @DisplayName("Can receive uint200 max value from contract call") - void canCallContractFunctionUint200Max() throws Exception { - BigInteger uint200Max = new BigInteger("1606938044258990275541962092341162602522202993782792835301375"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint200", new ContractFunctionParameters().addUint200(uint200Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(uint200Max); - } - - @Test - @DisplayName("Can receive uint200 array value from contract call") - void canCallContractFunctionUint200Array() throws Exception { - BigInteger uint200Min = BigInteger.ZERO; - BigInteger uint200Max = new BigInteger("1606938044258990275541962092341162602522202993782792835301375"); - BigInteger[] uint200Array = {uint200Min, uint200Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint200Arr", new ContractFunctionParameters().addUint200Array(uint200Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(uint200[])").get(0); - - assertThat(responseResult).isEqualTo(uint200Array); - } - - @Test - @DisplayName("Can receive uint208 min value from contract call") - void canCallContractFunctionUint208Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint208", new ContractFunctionParameters().addUint208(BigInteger.ZERO)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); - } - - @Test - @DisplayName("Can receive uint208 max value from contract call") - void canCallContractFunctionUint208Max() throws Exception { - BigInteger uint208Max = new BigInteger("411376139330301510538742295639337626245683966408394965837152255"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint208", new ContractFunctionParameters().addUint208(uint208Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(uint208Max); - } - - @Test - @DisplayName("Can receive uint208 array value from contract call") - void canCallContractFunctionUint208Array() throws Exception { - BigInteger uint208Min = BigInteger.ZERO; - BigInteger uint208Max = new BigInteger("411376139330301510538742295639337626245683966408394965837152255"); - BigInteger[] uint208Array = {uint208Min, uint208Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint208Arr", new ContractFunctionParameters().addUint208Array(uint208Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(uint208[])").get(0); - - assertThat(responseResult).isEqualTo(uint208Array); - } - - @Test - @DisplayName("Can receive uint216 min value from contract call") - void canCallContractFunctionUint216Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint216", new ContractFunctionParameters().addUint216(BigInteger.ZERO)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); - } - - @Test - @DisplayName("Can receive uint216 max value from contract call") - void canCallContractFunctionUint216Max() throws Exception { - BigInteger uint216Max = new BigInteger("105312291668557186697918027683670432318895095400549111254310977535"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint216", new ContractFunctionParameters().addUint216(uint216Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(uint216Max); - } - - @Test - @DisplayName("Can receive uint216 array value from contract call") - void canCallContractFunctionUint216Array() throws Exception { - BigInteger uint216Min = BigInteger.ZERO; - BigInteger uint216Max = new BigInteger("105312291668557186697918027683670432318895095400549111254310977535"); - BigInteger[] uint216Array = {uint216Min, uint216Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint216Arr", new ContractFunctionParameters().addUint216Array(uint216Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(uint216[])").get(0); - - assertThat(responseResult).isEqualTo(uint216Array); - } - - @Test - @DisplayName("Can receive uint224 min value from contract call") - void canCallContractFunctionUint224Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint224", new ContractFunctionParameters().addUint224(BigInteger.ZERO)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); - } - - @Test - @DisplayName("Can receive uint224 max value from contract call") - void canCallContractFunctionUint224Max() throws Exception { - BigInteger uint224Max = new BigInteger("26959946667150639794667015087019630673637144422540572481103610249215"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint224", new ContractFunctionParameters().addUint224(uint224Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(uint224Max); - } - - @Test - @DisplayName("Can receive uint224 array value from contract call") - void canCallContractFunctionUint224Array() throws Exception { - BigInteger uint224Min = BigInteger.ZERO; - BigInteger uint224Max = new BigInteger("26959946667150639794667015087019630673637144422540572481103610249215"); - BigInteger[] uint224Array = {uint224Min, uint224Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint224Arr", new ContractFunctionParameters().addUint224Array(uint224Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(uint224[])").get(0); - - assertThat(responseResult).isEqualTo(uint224Array); - } - - @Test - @DisplayName("Can receive uint232 min value from contract call") - void canCallContractFunctionUint232Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint232", new ContractFunctionParameters().addUint232(BigInteger.ZERO)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); - } - - @Test - @DisplayName("Can receive uint232 max value from contract call") - void canCallContractFunctionUint232Max() throws Exception { - BigInteger uint232Max = new BigInteger( - "6901746346790563787434755862277025452451108972170386555162524223799295"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint232", new ContractFunctionParameters().addUint232(uint232Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(uint232Max); - } - - @Test - @DisplayName("Can receive uint232 array value from contract call") - void canCallContractFunctionUint232Array() throws Exception { - BigInteger uint232Min = BigInteger.ZERO; - BigInteger uint232Max = new BigInteger( - "6901746346790563787434755862277025452451108972170386555162524223799295"); - BigInteger[] uint232Array = {uint232Min, uint232Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint232Arr", new ContractFunctionParameters().addUint232Array(uint232Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(uint232[])").get(0); - - assertThat(responseResult).isEqualTo(uint232Array); - } - - @Test - @DisplayName("Can receive uint240 min value from contract call") - void canCallContractFunctionUint240Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint240", new ContractFunctionParameters().addUint240(BigInteger.ZERO)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); - } - - @Test - @DisplayName("Can receive uint240 max value from contract call") - void canCallContractFunctionUint240Max() throws Exception { - BigInteger uint240Max = new BigInteger( - "1766847064778384329583297500742918515827483896875618958121606201292619775"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint240", new ContractFunctionParameters().addUint240(uint240Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(uint240Max); - } - - @Test - @DisplayName("Can receive uint240 array value from contract call") - void canCallContractFunctionUint240Array() throws Exception { - BigInteger uint240Min = BigInteger.ZERO; - BigInteger uint240Max = new BigInteger( - "1766847064778384329583297500742918515827483896875618958121606201292619775"); - BigInteger[] uint240Array = {uint240Min, uint240Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint240Arr", new ContractFunctionParameters().addUint240Array(uint240Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(uint240[])").get(0); - - assertThat(responseResult).isEqualTo(uint240Array); - } - - @Test - @DisplayName("Can receive uint248 min value from contract call") - void canCallContractFunctionUint248Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint248", new ContractFunctionParameters().addUint248(BigInteger.ZERO)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); - } - - @Test - @DisplayName("Can receive uint248 max value from contract call") - void canCallContractFunctionUint248Max() throws Exception { - BigInteger uint248Max = new BigInteger( - "452312848583266388373324160190187140051835877600158453279131187530910662655"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint248", new ContractFunctionParameters().addUint248(uint248Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(uint248Max); - } - - @Test - @DisplayName("Can receive uint248 array value from contract call") - void canCallContractFunctionUint248Array() throws Exception { - BigInteger uint248Min = BigInteger.ZERO; - BigInteger uint248Max = new BigInteger( - "452312848583266388373324160190187140051835877600158453279131187530910662655"); - BigInteger[] uint248Array = {uint248Min, uint248Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint248Arr", new ContractFunctionParameters().addUint248Array(uint248Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(uint248[])").get(0); - - assertThat(responseResult).isEqualTo(uint248Array); - } - - @Test - @DisplayName("Can receive uint256 min value from contract call") - void canCallContractFunctionUint256Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint256", new ContractFunctionParameters().addUint256(BigInteger.ZERO)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); - } - - @Test - @DisplayName("Can receive uint256 max value from contract call") - void canCallContractFunctionUint256Max() throws Exception { - BigInteger uint256Max = new BigInteger("2").pow(256).subtract(BigInteger.ONE); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint256", new ContractFunctionParameters().addUint256(uint256Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint256(0)).isEqualTo(uint256Max); - } - - @Test - @DisplayName("Can receive uint256 array value from contract call") - void canCallContractFunctionUint256Array() throws Exception { - BigInteger uint256Min = BigInteger.ZERO; - BigInteger uint256Max = new BigInteger("2").pow(256).subtract(BigInteger.ONE); - BigInteger[] uint256Array = {uint256Min, uint256Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint256Arr", new ContractFunctionParameters().addUint256Array(uint256Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(uint256[])").get(0); - - assertThat(responseResult).isEqualTo(uint256Array); - } - - @Test - @DisplayName("Can receive int8 min value from contract call") - void canCallContractFunctionInt8Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt8", new ContractFunctionParameters().addInt8(Byte.MIN_VALUE)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt8(0)).isEqualTo(Byte.MIN_VALUE); - } - - @Test - @DisplayName("Can receive int8 max value from contract call") - void canCallContractFunctionInt8Max() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt8", new ContractFunctionParameters().addInt8(Byte.MAX_VALUE)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt8(0)).isEqualTo(Byte.MAX_VALUE); - } - - @Test - @DisplayName("Can receive int8 array value from contract call") - void canCallContractFunctionInt8Array() throws Exception { - byte[] int8Array = {Byte.MIN_VALUE, Byte.MAX_VALUE}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt8Arr", new ContractFunctionParameters().addInt8Array(int8Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (int[]) response.getResult("(int8[])").get(0); - - assertThat(responseResult[0]).isEqualTo(int8Array[0]); - assertThat(responseResult[1]).isEqualTo(int8Array[1]); - } - - @Test - @DisplayName("Can receive int16 min value from contract call") - void canCallContractFunctionInt16Min() throws Exception { - int int16Min = -32768; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt16", new ContractFunctionParameters().addInt16(int16Min)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt32(0)).isEqualTo(int16Min); - } - - @Test - @DisplayName("Can receive int16 max value from contract call") - void canCallContractFunctionInt16Max() throws Exception { - int int16Max = 32767; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt16", new ContractFunctionParameters().addInt16(int16Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt32(0)).isEqualTo(int16Max); - } - - @Test - @DisplayName("Can receive int16 array value from contract call") - void canCallContractFunctionInt16Array() throws Exception { - int int16Min = -32768; - int int16Max = 32767; - int[] int16Array = {int16Min, int16Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt16Arr", new ContractFunctionParameters().addInt16Array(int16Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (int[]) response.getResult("(int16[])").get(0); - - assertThat(responseResult).isEqualTo(int16Array); - } - - @Test - @DisplayName("Can receive int24 min value from contract call") - void canCallContractFunctionInt24Min() throws Exception { - int int24Min = -8388608; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt24", new ContractFunctionParameters().addInt24(int24Min)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt32(0)).isEqualTo(int24Min); - } - - @Test - @DisplayName("Can receive int24 max value from contract call") - void canCallContractFunctionInt24Max() throws Exception { - int int24Max = 8388607; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt24", new ContractFunctionParameters().addInt24(int24Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt32(0)).isEqualTo(int24Max); - } - - @Test - @DisplayName("Can receive int24 array value from contract call") - void canCallContractFunctionInt24Array() throws Exception { - int int24Min = -8388608; - int int24Max = 8388607; - int[] int24Array = {int24Min, int24Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt24Arr", new ContractFunctionParameters().addInt24Array(int24Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (int[]) response.getResult("(int24[])").get(0); - - assertThat(responseResult).isEqualTo(int24Array); - } - - @Test - @DisplayName("Can receive int32 min value from contract call") - void canCallContractFunctionInt32Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt32", new ContractFunctionParameters().addInt32(Integer.MIN_VALUE)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt32(0)).isEqualTo(Integer.MIN_VALUE); - } - - @Test - @DisplayName("Can receive int32 max value from contract call") - void canCallContractFunctionInt32Max() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt32", new ContractFunctionParameters().addInt32(Integer.MAX_VALUE)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt32(0)).isEqualTo(Integer.MAX_VALUE); - } - - @Test - @DisplayName("Can receive int32 array value from contract call") - void canCallContractFunctionInt32Array() throws Exception { - int int32Min = Integer.MIN_VALUE; - int int32Max = Integer.MAX_VALUE; - int[] int32Array = {int32Min, int32Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt32Arr", new ContractFunctionParameters().addInt32Array(int32Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (int[]) response.getResult("(int32[])").get(0); - - assertThat(responseResult).isEqualTo(int32Array); - } - - @Test - @DisplayName("Can receive int40 min value from contract call") - void canCallContractFunctionInt40Min() throws Exception { - long int40Min = -549755813888L; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt40", new ContractFunctionParameters().addInt40(int40Min)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt64(0)).isEqualTo(int40Min); - } - - @Test - @DisplayName("Can receive int40 max value from contract call") - void canCallContractFunctionInt40Max() throws Exception { - long int40Max = 549755813887L; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt40", new ContractFunctionParameters().addInt40(int40Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt64(0)).isEqualTo(int40Max); - } - - @Test - @DisplayName("Can receive int40 array value from contract call") - void canCallContractFunctionInt40Array() throws Exception { - long int40Min = -549755813888L; - long int40Max = 549755813887L; - long[] int40Array = {int40Min, int40Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt40Arr", new ContractFunctionParameters().addInt40Array(int40Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (long[]) response.getResult("(int40[])").get(0); - - assertThat(responseResult).isEqualTo(int40Array); - } - - @Test - @DisplayName("Can receive int48 min value from contract call") - void canCallContractFunctionInt48Min() throws Exception { - long int48Min = -140737488355328L; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt48", new ContractFunctionParameters().addInt48(int48Min)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt64(0)).isEqualTo(int48Min); - } - - @Test - @DisplayName("Can receive int48 max value from contract call") - void canCallContractFunctionInt48Max() throws Exception { - long int48Max = 140737488355327L; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt48", new ContractFunctionParameters().addInt48(int48Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt64(0)).isEqualTo(int48Max); - } - - @Test - @DisplayName("Can receive int48 array value from contract call") - void canCallContractFunctionInt48Array() throws Exception { - long int48Min = -140737488355328L; - long int48Max = 140737488355327L; - long[] int48Array = {int48Min, int48Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt48Arr", new ContractFunctionParameters().addInt48Array(int48Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (long[]) response.getResult("(int48[])").get(0); - - assertThat(responseResult).isEqualTo(int48Array); - } - - @Test - @DisplayName("Can receive int56 min value from contract call") - void canCallContractFunctionInt56Min() throws Exception { - long int56Min = -36028797018963968L; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt56", new ContractFunctionParameters().addInt56(int56Min)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt64(0)).isEqualTo(int56Min); - } - - @Test - @DisplayName("Can receive int56 max value from contract call") - void canCallContractFunctionInt56Max() throws Exception { - long int56Max = 36028797018963967L; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt56", new ContractFunctionParameters().addInt56(int56Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt64(0)).isEqualTo(int56Max); - } - - @Test - @DisplayName("Can receive int56 array value from contract call") - void canCallContractFunctionInt56Array() throws Exception { - long int56Min = -36028797018963968L; - long int56Max = 36028797018963967L; - long[] int56Array = {int56Min, int56Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt56Arr", new ContractFunctionParameters().addInt56Array(int56Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (long[]) response.getResult("(int56[])").get(0); - - assertThat(responseResult).isEqualTo(int56Array); - } - - @Test - @DisplayName("Can receive int64 min value from contract call") - void canCallContractFunctionInt64Min() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt64", new ContractFunctionParameters().addInt64(Long.MIN_VALUE)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint64(0)).isEqualTo(Long.MIN_VALUE); - } - - @Test - @DisplayName("Can receive int64 max value from contract call") - void canCallContractFunctionInt64Max() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnUint64", new ContractFunctionParameters().addUint64(Long.MAX_VALUE)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getUint64(0)).isEqualTo(Long.MAX_VALUE); - } - - @Test - @DisplayName("Can receive int64 array value from contract call") - void canCallContractFunctionInt64Array() throws Exception { - long int64Min = Long.MIN_VALUE; - long int64Max = Long.MAX_VALUE; - long[] int64Array = {int64Min, int64Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt64Arr", new ContractFunctionParameters().addInt64Array(int64Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (long[]) response.getResult("(int64[])").get(0); - - assertThat(responseResult).isEqualTo(int64Array); - } - - @Test - @DisplayName("Can receive int72 min value from contract call") - void canCallContractFunctionInt72Min() throws Exception { - BigInteger int72Min = new BigInteger("-2361183241434822606848"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt72", new ContractFunctionParameters().addInt72(int72Min)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int72Min); - } - - @Test - @DisplayName("Can receive int72 max value from contract call") - void canCallContractFunctionInt72Max() throws Exception { - BigInteger int72Max = new BigInteger("2361183241434822606847"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt72", new ContractFunctionParameters().addInt72(int72Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int72Max); - } - - @Test - @DisplayName("Can receive int72 array value from contract call") - void canCallContractFunctionInt72Array() throws Exception { - BigInteger int72Min = new BigInteger("-2361183241434822606848"); - BigInteger int72Max = new BigInteger("2361183241434822606847"); - BigInteger[] int72Array = {int72Min, int72Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt72Arr", new ContractFunctionParameters().addInt72Array(int72Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(int72[])").get(0); - - assertThat(responseResult).isEqualTo(int72Array); - } - - @Test - @DisplayName("Can receive int80 min value from contract call") - void canCallContractFunctionInt80Min() throws Exception { - BigInteger int80Min = new BigInteger("-604462909807314587353088"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt80", new ContractFunctionParameters().addInt80(int80Min)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int80Min); - } - - @Test - @DisplayName("Can receive int80 max value from contract call") - void canCallContractFunctionInt80Max() throws Exception { - BigInteger int80Max = new BigInteger("604462909807314587353087"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt80", new ContractFunctionParameters().addInt80(int80Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int80Max); - } - - @Test - @DisplayName("Can receive int80 array value from contract call") - void canCallContractFunctionInt80Array() throws Exception { - BigInteger int80Min = new BigInteger("-604462909807314587353088"); - BigInteger int80Max = new BigInteger("604462909807314587353087"); - BigInteger[] int80Array = {int80Min, int80Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt80Arr", new ContractFunctionParameters().addInt80Array(int80Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(int80[])").get(0); - - assertThat(responseResult).isEqualTo(int80Array); - } - - @Test - @DisplayName("Can receive int88 min value from contract call") - void canCallContractFunctionInt88Min() throws Exception { - BigInteger int88Min = new BigInteger("-154742504910672534362390528"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt88", new ContractFunctionParameters().addInt88(int88Min)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int88Min); - } - - @Test - @DisplayName("Can receive int88 max value from contract call") - void canCallContractFunctionInt88Max() throws Exception { - BigInteger int88Max = new BigInteger("154742504910672534362390527"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt88", new ContractFunctionParameters().addInt88(int88Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int88Max); - } - - @Test - @DisplayName("Can receive int88 array value from contract call") - void canCallContractFunctionInt88Array() throws Exception { - BigInteger int88Min = new BigInteger("-154742504910672534362390528"); - BigInteger int88Max = new BigInteger("154742504910672534362390527"); - BigInteger[] int88Array = {int88Min, int88Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt88Arr", new ContractFunctionParameters().addInt88Array(int88Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(int88[])").get(0); - - assertThat(responseResult).isEqualTo(int88Array); - } - - @Test - @DisplayName("Can receive int96 min value from contract call") - void canCallContractFunctionInt96Min() throws Exception { - BigInteger int96Min = new BigInteger("-39614081257132168796771975168"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt96", new ContractFunctionParameters().addInt96(int96Min)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int96Min); - } - - @Test - @DisplayName("Can receive int96 max value from contract call") - void canCallContractFunctionInt96Max() throws Exception { - BigInteger int96Max = new BigInteger("39614081257132168796771975167"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt96", new ContractFunctionParameters().addInt96(int96Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int96Max); - } - - @Test - @DisplayName("Can receive int96 array value from contract call") - void canCallContractFunctionInt96Array() throws Exception { - BigInteger int96Min = new BigInteger("-39614081257132168796771975168"); - BigInteger int96Max = new BigInteger("39614081257132168796771975167"); - BigInteger[] int96Array = {int96Min, int96Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt96Arr", new ContractFunctionParameters().addInt96Array(int96Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(int96[])").get(0); - - assertThat(responseResult).isEqualTo(int96Array); - } - - @Test - @DisplayName("Can receive int104 min value from contract call") - void canCallContractFunctionInt104Min() throws Exception { - BigInteger int104Min = new BigInteger("-10141204801825835211973625643008"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt104", new ContractFunctionParameters().addInt104(int104Min)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int104Min); - } - - @Test - @DisplayName("Can receive int104 max value from contract call") - void canCallContractFunctionInt104Max() throws Exception { - BigInteger int104Max = new BigInteger("10141204801825835211973625643007"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt104", new ContractFunctionParameters().addInt104(int104Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int104Max); - } - - @Test - @DisplayName("Can receive int104 array value from contract call") - void canCallContractFunctionInt104Array() throws Exception { - BigInteger int104Min = new BigInteger("-10141204801825835211973625643008"); - BigInteger int104Max = new BigInteger("10141204801825835211973625643007"); - BigInteger[] int104Array = {int104Min, int104Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt104Arr", new ContractFunctionParameters().addInt104Array(int104Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(int104[])").get(0); - - assertThat(responseResult).isEqualTo(int104Array); - } - - @Test - @DisplayName("Can receive int112 min value from contract call") - void canCallContractFunctionInt112Min() throws Exception { - BigInteger int112Min = new BigInteger("-2596148429267413814265248164610048"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt112", new ContractFunctionParameters().addInt112(int112Min)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int112Min); - } - - @Test - @DisplayName("Can receive int112 max value from contract call") - void canCallContractFunctionInt112Max() throws Exception { - BigInteger int112Max = new BigInteger("2596148429267413814265248164610047"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt112", new ContractFunctionParameters().addInt112(int112Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int112Max); - } - - @Test - @DisplayName("Can receive int112 array value from contract call") - void canCallContractFunctionInt112Array() throws Exception { - BigInteger int112Min = new BigInteger("-2596148429267413814265248164610048"); - BigInteger int112Max = new BigInteger("2596148429267413814265248164610047"); - BigInteger[] int112Array = {int112Min, int112Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt112Arr", new ContractFunctionParameters().addInt112Array(int112Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(int112[])").get(0); - - assertThat(responseResult).isEqualTo(int112Array); - } - - @Test - @DisplayName("Can receive int120 min value from contract call") - void canCallContractFunctionInt120Min() throws Exception { - BigInteger int120Min = new BigInteger("-664613997892457936451903530140172288"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt120", new ContractFunctionParameters().addInt120(int120Min)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int120Min); - } - - @Test - @DisplayName("Can receive int120 max value from contract call") - void canCallContractFunctionInt120Max() throws Exception { - BigInteger int120Max = new BigInteger("664613997892457936451903530140172287"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt120", new ContractFunctionParameters().addInt120(int120Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int120Max); - } - - @Test - @DisplayName("Can receive int120 array value from contract call") - void canCallContractFunctionInt120Array() throws Exception { - BigInteger int120Min = new BigInteger("-664613997892457936451903530140172288"); - BigInteger int120Max = new BigInteger("664613997892457936451903530140172287"); - BigInteger[] int120Array = {int120Min, int120Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt120Arr", new ContractFunctionParameters().addInt120Array(int120Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(int120[])").get(0); - - assertThat(responseResult).isEqualTo(int120Array); - } - - @Test - @DisplayName("Can receive int128 min value from contract call") - void canCallContractFunctionInt128Min() throws Exception { - BigInteger int128Min = new BigInteger("-170141183460469231731687303715884105728"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt128", new ContractFunctionParameters().addInt128(int128Min)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int128Min); - } - - @Test - @DisplayName("Can receive int128 max value from contract call") - void canCallContractFunctionInt128Max() throws Exception { - BigInteger int128Max = new BigInteger("170141183460469231731687303715884105727"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt128", new ContractFunctionParameters().addInt128(int128Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int128Max); - } - - @Test - @DisplayName("Can receive int128 array value from contract call") - void canCallContractFunctionInt128Array() throws Exception { - BigInteger int128Min = new BigInteger("-170141183460469231731687303715884105728"); - BigInteger int128Max = new BigInteger("170141183460469231731687303715884105727"); - BigInteger[] int128Array = {int128Min, int128Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt128Arr", new ContractFunctionParameters().addInt128Array(int128Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(int128[])").get(0); - - assertThat(responseResult).isEqualTo(int128Array); - } - - @Test - @DisplayName("Can receive int136 min value from contract call") - void canCallContractFunctionInt136Min() throws Exception { - BigInteger int136Min = new BigInteger("-43556142965880123323311949751266331066368"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt136", new ContractFunctionParameters().addInt136(int136Min)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int136Min); - } - - @Test - @DisplayName("Can receive int136 max value from contract call") - void canCallContractFunctionInt136Max() throws Exception { - BigInteger int136Max = new BigInteger("43556142965880123323311949751266331066367"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt136", new ContractFunctionParameters().addInt136(int136Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int136Max); - } - - @Test - @DisplayName("Can receive int136 array value from contract call") - void canCallContractFunctionInt136Array() throws Exception { - BigInteger int136Min = new BigInteger("-43556142965880123323311949751266331066368"); - BigInteger int136Max = new BigInteger("43556142965880123323311949751266331066367"); - BigInteger[] int136Array = {int136Min, int136Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt136Arr", new ContractFunctionParameters().addInt136Array(int136Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(int136[])").get(0); - - assertThat(responseResult).isEqualTo(int136Array); - } - - @Test - @DisplayName("Can receive int144 min value from contract call") - void canCallContractFunctionInt144Min() throws Exception { - BigInteger int144Min = new BigInteger("-11150372599265311570767859136324180752990208"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt144", new ContractFunctionParameters().addInt144(int144Min)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int144Min); - } - - @Test - @DisplayName("Can receive int144 max value from contract call") - void canCallContractFunctionInt144Max() throws Exception { - BigInteger int144Max = new BigInteger("11150372599265311570767859136324180752990207"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt144", new ContractFunctionParameters().addInt144(int144Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int144Max); - } - - @Test - @DisplayName("Can receive int144 array value from contract call") - void canCallContractFunctionInt144Array() throws Exception { - BigInteger int144Min = new BigInteger("-11150372599265311570767859136324180752990208"); - BigInteger int144Max = new BigInteger("11150372599265311570767859136324180752990207"); - BigInteger[] int144Array = {int144Min, int144Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt144Arr", new ContractFunctionParameters().addInt144Array(int144Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(int144[])").get(0); - - assertThat(responseResult).isEqualTo(int144Array); - } - - @Test - @DisplayName("Can receive int152 min value from contract call") - void canCallContractFunctionInt152Min() throws Exception { - BigInteger int152Min = new BigInteger("-2854495385411919762116571938898990272765493248"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt152", new ContractFunctionParameters().addInt152(int152Min)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int152Min); - } - - @Test - @DisplayName("Can receive int152 max value from contract call") - void canCallContractFunctionInt152Max() throws Exception { - BigInteger int152Max = new BigInteger("2854495385411919762116571938898990272765493247"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt152", new ContractFunctionParameters().addInt152(int152Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int152Max); - } - - @Test - @DisplayName("Can receive int152 array value from contract call") - void canCallContractFunctionInt152Array() throws Exception { - BigInteger int152Min = new BigInteger("-2854495385411919762116571938898990272765493248"); - BigInteger int152Max = new BigInteger("2854495385411919762116571938898990272765493247"); - BigInteger[] int152Array = {int152Min, int152Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt152Arr", new ContractFunctionParameters().addInt152Array(int152Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(int152[])").get(0); - - assertThat(responseResult).isEqualTo(int152Array); - } - - @Test - @DisplayName("Can receive int160 min value from contract call") - void canCallContractFunctionInt160Min() throws Exception { - BigInteger int160Min = new BigInteger("-730750818665451459101842416358141509827966271488"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt160", new ContractFunctionParameters().addInt160(int160Min)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int160Min); - } - - @Test - @DisplayName("Can receive int160 max value from contract call") - void canCallContractFunctionInt160Max() throws Exception { - BigInteger int160Max = new BigInteger("730750818665451459101842416358141509827966271487"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt160", new ContractFunctionParameters().addInt160(int160Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int160Max); - } - - @Test - @DisplayName("Can receive int160 array value from contract call") - void canCallContractFunctionInt160Array() throws Exception { - BigInteger int160Min = new BigInteger("-730750818665451459101842416358141509827966271488"); - BigInteger int160Max = new BigInteger("730750818665451459101842416358141509827966271487"); - BigInteger[] int160Array = {int160Min, int160Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt160Arr", new ContractFunctionParameters().addInt160Array(int160Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(int160[])").get(0); - - assertThat(responseResult).isEqualTo(int160Array); - } - - @Test - @DisplayName("Can receive int168 min value from contract call") - void canCallContractFunctionInt168Min() throws Exception { - BigInteger int168Min = new BigInteger("-187072209578355573530071658587684226515959365500928"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt168", new ContractFunctionParameters().addInt168(int168Min)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int168Min); - } - - @Test - @DisplayName("Can receive int168 max value from contract call") - void canCallContractFunctionInt168Max() throws Exception { - BigInteger int168Max = new BigInteger("187072209578355573530071658587684226515959365500927"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt168", new ContractFunctionParameters().addInt168(int168Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int168Max); - } - - @Test - @DisplayName("Can receive int168 array value from contract call") - void canCallContractFunctionInt168Array() throws Exception { - BigInteger int168Min = new BigInteger("-187072209578355573530071658587684226515959365500928"); - BigInteger int168Max = new BigInteger("187072209578355573530071658587684226515959365500927"); - BigInteger[] int168Array = {int168Min, int168Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt168Arr", new ContractFunctionParameters().addInt168Array(int168Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(int168[])").get(0); - - assertThat(responseResult).isEqualTo(int168Array); - } - - @Test - @DisplayName("Can receive int176 min value from contract call") - void canCallContractFunctionInt176Min() throws Exception { - BigInteger int176Min = new BigInteger("-47890485652059026823698344598447161988085597568237568"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt176", new ContractFunctionParameters().addInt176(int176Min)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int176Min); - } - - @Test - @DisplayName("Can receive int176 max value from contract call") - void canCallContractFunctionInt176Max() throws Exception { - BigInteger int176Max = new BigInteger("47890485652059026823698344598447161988085597568237567"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt176", new ContractFunctionParameters().addInt176(int176Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int176Max); - } - - @Test - @DisplayName("Can receive int176 array value from contract call") - void canCallContractFunctionInt176Array() throws Exception { - BigInteger int176Min = new BigInteger("-47890485652059026823698344598447161988085597568237568"); - BigInteger int176Max = new BigInteger("47890485652059026823698344598447161988085597568237567"); - BigInteger[] int176Array = {int176Min, int176Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt176Arr", new ContractFunctionParameters().addInt176Array(int176Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(int176[])").get(0); - - assertThat(responseResult).isEqualTo(int176Array); - } - - @Test - @DisplayName("Can receive int184 min value from contract call") - void canCallContractFunctionInt184Min() throws Exception { - BigInteger int184Min = new BigInteger("-12259964326927110866866776217202473468949912977468817408"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt184", new ContractFunctionParameters().addInt184(int184Min)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int184Min); - } - - @Test - @DisplayName("Can receive int184 max value from contract call") - void canCallContractFunctionInt184Max() throws Exception { - BigInteger int184Max = new BigInteger("12259964326927110866866776217202473468949912977468817407"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt184", new ContractFunctionParameters().addInt184(int184Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int184Max); - } - - @Test - @DisplayName("Can receive int184 array value from contract call") - void canCallContractFunctionInt184Array() throws Exception { - BigInteger int184Min = new BigInteger("-12259964326927110866866776217202473468949912977468817408"); - BigInteger int184Max = new BigInteger("12259964326927110866866776217202473468949912977468817407"); - BigInteger[] int184Array = {int184Min, int184Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt184Arr", new ContractFunctionParameters().addInt184Array(int184Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(int184[])").get(0); - - assertThat(responseResult).isEqualTo(int184Array); - } - - @Test - @DisplayName("Can receive int192 min value from contract call") - void canCallContractFunctionInt192Min() throws Exception { - BigInteger int192Min = new BigInteger("-3138550867693340381917894711603833208051177722232017256448"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt192", new ContractFunctionParameters().addInt192(int192Min)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int192Min); - } - - @Test - @DisplayName("Can receive int192 max value from contract call") - void canCallContractFunctionInt192Max() throws Exception { - BigInteger int192Max = new BigInteger("3138550867693340381917894711603833208051177722232017256447"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt192", new ContractFunctionParameters().addInt192(int192Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int192Max); - } - - @Test - @DisplayName("Can receive int192 array value from contract call") - void canCallContractFunctionInt192Array() throws Exception { - BigInteger int192Min = new BigInteger("-3138550867693340381917894711603833208051177722232017256448"); - BigInteger int192Max = new BigInteger("3138550867693340381917894711603833208051177722232017256447"); - BigInteger[] int192Array = {int192Min, int192Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt192Arr", new ContractFunctionParameters().addInt192Array(int192Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(int192[])").get(0); - - assertThat(responseResult).isEqualTo(int192Array); - } - - @Test - @DisplayName("Can receive int200 min value from contract call") - void canCallContractFunctionInt200Min() throws Exception { - BigInteger int200Min = new BigInteger("-803469022129495137770981046170581301261101496891396417650688"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt200", new ContractFunctionParameters().addInt200(int200Min)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int200Min); - } - - @Test - @DisplayName("Can receive int200 max value from contract call") - void canCallContractFunctionInt200Max() throws Exception { - BigInteger int200Max = new BigInteger("803469022129495137770981046170581301261101496891396417650687"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt200", new ContractFunctionParameters().addInt200(int200Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int200Max); - } - - @Test - @DisplayName("Can receive int200 array value from contract call") - void canCallContractFunctionInt200Array() throws Exception { - BigInteger int200Min = new BigInteger("-803469022129495137770981046170581301261101496891396417650688"); - BigInteger int200Max = new BigInteger("803469022129495137770981046170581301261101496891396417650687"); - BigInteger[] int200Array = {int200Min, int200Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt200Arr", new ContractFunctionParameters().addInt200Array(int200Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(int200[])").get(0); - - assertThat(responseResult).isEqualTo(int200Array); - } - - @Test - @DisplayName("Can receive int208 min value from contract call") - void canCallContractFunctionInt208Min() throws Exception { - BigInteger int208Min = new BigInteger("-205688069665150755269371147819668813122841983204197482918576128"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt208", new ContractFunctionParameters().addInt208(int208Min)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int208Min); - } - - @Test - @DisplayName("Can receive int208 max value from contract call") - void canCallContractFunctionInt208Max() throws Exception { - BigInteger int208Max = new BigInteger("205688069665150755269371147819668813122841983204197482918576127"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt208", new ContractFunctionParameters().addInt208(int208Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int208Max); - } - - @Test - @DisplayName("Can receive int208 array value from contract call") - void canCallContractFunctionInt208Array() throws Exception { - BigInteger int208Min = new BigInteger("-205688069665150755269371147819668813122841983204197482918576128"); - BigInteger int208Max = new BigInteger("205688069665150755269371147819668813122841983204197482918576127"); - BigInteger[] int208Array = {int208Min, int208Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt208Arr", new ContractFunctionParameters().addInt208Array(int208Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(int208[])").get(0); - - assertThat(responseResult).isEqualTo(int208Array); - } - - @Test - @DisplayName("Can receive int216 min value from contract call") - void canCallContractFunctionInt216Min() throws Exception { - BigInteger int216Min = new BigInteger("-52656145834278593348959013841835216159447547700274555627155488768"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt216", new ContractFunctionParameters().addInt216(int216Min)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int216Min); - } - - @Test - @DisplayName("Can receive int216 max value from contract call") - void canCallContractFunctionInt216Max() throws Exception { - BigInteger int216Max = new BigInteger("52656145834278593348959013841835216159447547700274555627155488767"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt216", new ContractFunctionParameters().addInt216(int216Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int216Max); - } - - @Test - @DisplayName("Can receive int216 array value from contract call") - void canCallContractFunctionInt216Array() throws Exception { - BigInteger int216Min = new BigInteger("-52656145834278593348959013841835216159447547700274555627155488768"); - BigInteger int216Max = new BigInteger("52656145834278593348959013841835216159447547700274555627155488767"); - BigInteger[] int216Array = {int216Min, int216Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt216Arr", new ContractFunctionParameters().addInt216Array(int216Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(int216[])").get(0); - - assertThat(responseResult).isEqualTo(int216Array); - } - - @Test - @DisplayName("Can receive int224 min value from contract call") - void canCallContractFunctionInt224Min() throws Exception { - BigInteger int224Min = new BigInteger("-13479973333575319897333507543509815336818572211270286240551805124608"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt224", new ContractFunctionParameters().addInt224(int224Min)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int224Min); - } - - @Test - @DisplayName("Can receive int224 max value from contract call") - void canCallContractFunctionInt224Max() throws Exception { - BigInteger int224Max = new BigInteger("13479973333575319897333507543509815336818572211270286240551805124607"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt224", new ContractFunctionParameters().addInt224(int224Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int224Max); - } - - @Test - @DisplayName("Can receive int224 array value from contract call") - void canCallContractFunctionInt224Array() throws Exception { - BigInteger int224Min = new BigInteger("-13479973333575319897333507543509815336818572211270286240551805124608"); - BigInteger int224Max = new BigInteger("13479973333575319897333507543509815336818572211270286240551805124607"); - BigInteger[] int224Array = {int224Min, int224Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt224Arr", new ContractFunctionParameters().addInt224Array(int224Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(int224[])").get(0); - - assertThat(responseResult).isEqualTo(int224Array); - } - - @Test - @DisplayName("Can receive int232 min value from contract call") - void canCallContractFunctionInt232Min() throws Exception { - BigInteger int232Min = new BigInteger( - "-3450873173395281893717377931138512726225554486085193277581262111899648"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt232", new ContractFunctionParameters().addInt232(int232Min)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int232Min); - } - - @Test - @DisplayName("Can receive int232 max value from contract call") - void canCallContractFunctionInt232Max() throws Exception { - BigInteger int232Max = new BigInteger("3450873173395281893717377931138512726225554486085193277581262111899647"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt232", new ContractFunctionParameters().addInt232(int232Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int232Max); - } - - @Test - @DisplayName("Can receive int232 array value from contract call") - void canCallContractFunctionInt232Array() throws Exception { - BigInteger int232Min = new BigInteger( - "-3450873173395281893717377931138512726225554486085193277581262111899648"); - BigInteger int232Max = new BigInteger("3450873173395281893717377931138512726225554486085193277581262111899647"); - BigInteger[] int232Array = {int232Min, int232Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt232Arr", new ContractFunctionParameters().addInt232Array(int232Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(int232[])").get(0); - - assertThat(responseResult).isEqualTo(int232Array); - } - - @Test - @DisplayName("Can receive int240 min value from contract call") - void canCallContractFunctionInt240Min() throws Exception { - BigInteger int240Min = new BigInteger( - "-883423532389192164791648750371459257913741948437809479060803100646309888"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt240", new ContractFunctionParameters().addInt240(int240Min)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int240Min); - } - - @Test - @DisplayName("Can receive int240 max value from contract call") - void canCallContractFunctionInt240Max() throws Exception { - BigInteger int240Max = new BigInteger( - "883423532389192164791648750371459257913741948437809479060803100646309887"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt240", new ContractFunctionParameters().addInt240(int240Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int240Max); - } - - @Test - @DisplayName("Can receive int240 array value from contract call") - void canCallContractFunctionInt240Array() throws Exception { - BigInteger int240Min = new BigInteger( - "-883423532389192164791648750371459257913741948437809479060803100646309888"); - BigInteger int240Max = new BigInteger( - "883423532389192164791648750371459257913741948437809479060803100646309887"); - BigInteger[] int240Array = {int240Min, int240Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt240Arr", new ContractFunctionParameters().addInt240Array(int240Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(int240[])").get(0); - - assertThat(responseResult).isEqualTo(int240Array); - } - - @Test - @DisplayName("Can receive int248 min value from contract call") - void canCallContractFunctionInt248Min() throws Exception { - BigInteger int248Min = new BigInteger( - "-226156424291633194186662080095093570025917938800079226639565593765455331328"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt248", new ContractFunctionParameters().addInt248(int248Min)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int248Min); - } - - @Test - @DisplayName("Can receive int248 max value from contract call") - void canCallContractFunctionInt248Max() throws Exception { - BigInteger int248Max = new BigInteger( - "226156424291633194186662080095093570025917938800079226639565593765455331327"); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt248", new ContractFunctionParameters().addInt248(int248Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int248Max); - } - - @Test - @DisplayName("Can receive int248 array value from contract call") - void canCallContractFunctionInt248Array() throws Exception { - BigInteger int248Min = new BigInteger( - "-226156424291633194186662080095093570025917938800079226639565593765455331328"); - BigInteger int248Max = new BigInteger( - "226156424291633194186662080095093570025917938800079226639565593765455331327"); - BigInteger[] int248Array = {int248Min, int248Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt248Arr", new ContractFunctionParameters().addInt248Array(int248Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(int248[])").get(0); - - assertThat(responseResult).isEqualTo(int248Array); - } - - @Test - @DisplayName("Can receive int256 min value from contract call") - void canCallContractFunctionInt256Min() throws Exception { - BigInteger int256Min = new BigInteger("2").pow(256).divide(BigInteger.TWO).negate(); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt256", new ContractFunctionParameters().addInt256(int256Min)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int256Min); - } - - @Test - @DisplayName("Can receive int256 max value from contract call") - void canCallContractFunctionInt256Max() throws Exception { - BigInteger int256Max = new BigInteger("2").pow(256).subtract(BigInteger.ONE).divide(BigInteger.TWO); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt256", new ContractFunctionParameters().addInt256(int256Max)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int256Max); - } - - @Test - @DisplayName("Can receive int256 array value from contract call") - void canCallContractFunctionInt256Array() throws Exception { - BigInteger int256Min = new BigInteger("2").pow(256).divide(BigInteger.TWO).negate(); - BigInteger int256Max = new BigInteger("2").pow(256).subtract(BigInteger.ONE).divide(BigInteger.TWO); - BigInteger[] int256Array = {int256Min, int256Max}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt256Arr", new ContractFunctionParameters().addInt256Array(int256Array)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (BigInteger[]) response.getResult("(int256[])").get(0); - - assertThat(responseResult).isEqualTo(int256Array); - } - - @Test - @DisplayName("Can receive multiple int8 values from contract call") - void canCallContractFunctionMultipleInt8() throws Exception { - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnInt8Multiple", new ContractFunctionParameters().addInt8(Byte.MIN_VALUE)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt8(0)).isEqualTo(Byte.MIN_VALUE); - assertThat(response.getInt8(1)).isEqualTo((byte) -108); - } - - @Test - @DisplayName("Can receive multiple int40 values from contract call") - void canCallContractFunctionMultipleInt40() throws Exception { - long int40 = 549755813885L; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnMultipleInt40", new ContractFunctionParameters().addInt40(int40)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt64(0)).isEqualTo(int40); - assertThat(response.getInt64(1)).isEqualTo(int40 + 1); - } - - @Test - @DisplayName("Can receive multiple int256 values from contract call") - void canCallContractFunctionMultipleInt256() throws Exception { - BigInteger int256Min = new BigInteger("2").pow(256).divide(BigInteger.TWO).negate(); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnMultipleInt256", new ContractFunctionParameters().addInt256(int256Min)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getInt256(0)).isEqualTo(int256Min); - assertThat(response.getInt256(1)).isEqualTo(int256Min.add(BigInteger.ONE)); - } - - @Test - @DisplayName("Can receive multiple types of values from contract call") - void canCallContractFunctionMultipleTypes() throws Exception { - var uint32Max = "4294967295"; - int uint32MaxInt = Integer.parseUnsignedInt(uint32Max); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnMultipleTypeParams", new ContractFunctionParameters().addUint32(uint32MaxInt)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(Integer.toUnsignedString(response.getUint32(0))).isEqualTo(uint32Max); - assertThat(response.getUint64(1)).isEqualTo(Long.parseUnsignedLong(uint32Max) - 1); - assertThat(response.getString(2)).isEqualTo("OK"); - } - - @Test - @DisplayName("Can receive string value from contract call") - void canCallContractFunctionStringType() throws Exception { - var testString = "test"; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnString", new ContractFunctionParameters().addString(testString)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getString(0)).isEqualTo(testString); - } - - @Test - @DisplayName("Can receive string array value from contract call") - void canCallContractFunctionStringArrayType() throws Exception { - var testStringArray = new String[]{"Test1", "Test2"}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnStringArr", new ContractFunctionParameters().addStringArray(testStringArray)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getStringArray(0).get(0)).isEqualTo(testStringArray[0]); - assertThat(response.getStringArray(0).get(1)).isEqualTo(testStringArray[1]); - } - - @Test - @DisplayName("Can receive string array value from contract call with getResult function") - void canCallContractFunctionStringArrayType_getResult() throws Exception { - var testStringArray = new String[]{"Test1", "Test2"}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnStringArr", new ContractFunctionParameters().addStringArray(testStringArray)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (String[]) response.getResult("(string[])").get(0); - assertThat(responseResult).isEqualTo(testStringArray); - } - - @Test - @DisplayName("Can receive address value from contract call") - void canCallContractFunctionAddressType() throws Exception { - var testAddress = "1234567890123456789012345678901234567890"; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnAddress", new ContractFunctionParameters().addAddress(testAddress)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getAddress(0)).isEqualTo(testAddress); - } - - @Test - @DisplayName("Can receive address array value from contract call") - void canCallContractFunctionAddressArrayType() throws Exception { - var testAddressArray = new String[]{"1234567890123456789012345678901234567890", - "1234567890123456789012345678901234567891"}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnAddressArr", new ContractFunctionParameters().addAddressArray(testAddressArray)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (Address[]) response.getResult("(address[])").get(0); - - Address[] testAddressArray_Address = Arrays.stream(testAddressArray).map(addressStr -> "0x" + addressStr) - .map(Address::wrap).toArray(Address[]::new); - - assertThat(responseResult).isEqualTo(testAddressArray_Address); - } - - @Test - @DisplayName("Can receive boolean value from contract call") - void canCallContractFunctionBooleanType() throws Exception { - var testBoolean = true; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnBoolean", new ContractFunctionParameters().addBool(testBoolean)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getBool(0)).isEqualTo(testBoolean); - } - - @Test - @DisplayName("Can receive boolean array value from contract call") - void canCallContractFunctionBooleanArrayType() throws Exception { - var testBooleanArray = new boolean[]{true, false}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnBooleanArr", new ContractFunctionParameters().addBoolArray(testBooleanArray)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (boolean[]) response.getResult("(bool[])").get(0); - - assertThat(responseResult).isEqualTo(testBooleanArray); - } - - @Test - @DisplayName("Can receive bytes value from contract call") - void canCallContractFunctionBytesType() throws Exception { - var testBytes = "Test".getBytes(); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnBytes", new ContractFunctionParameters().addBytes(testBytes)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getBytes(0)).isEqualTo(testBytes); - } - - @Test - @DisplayName("Can receive bytes array value from contract call") - void canCallContractFunctionBytesArrayType() throws Exception { - byte[][] testBytes = new byte[][]{"Test1".getBytes(), "Test2".getBytes()}; - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnBytesArr", new ContractFunctionParameters().addBytesArray(testBytes)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (byte[][]) response.getResult("(bytes[])").get(0); - - assertThat(responseResult).isEqualTo(testBytes); - } - - @Test - @DisplayName("Can receive bytes32 value from contract call") - void canCallContractFunctionBytes32Type() throws Exception { - byte[] testBytes = "Test".getBytes(); - byte[] testBytesLen32 = new byte[32]; - System.arraycopy(testBytes, 0, testBytesLen32, 0, testBytes.length); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnBytes32", new ContractFunctionParameters().addBytes32(testBytesLen32)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - assertThat(response.getBytes32(0)).isEqualTo(testBytesLen32); - } - - @Test - @DisplayName("Can receive bytes32 array value from contract call") - void canCallContractFunctionBytes32ArrayType() throws Exception { - byte[] testBytes = "Test".getBytes(); - byte[] testBytes2 = "Test2".getBytes(); - byte[][] testBytesLen32 = new byte[2][32]; - System.arraycopy(testBytes, 0, testBytesLen32[0], 0, testBytes.length); - System.arraycopy(testBytes2, 0, testBytesLen32[1], 0, testBytes2.length); - - var response = new ContractCallQuery().setContractId(contractId).setGas(1500000) - .setFunction("returnBytes32Arr", new ContractFunctionParameters().addBytes32Array(testBytesLen32)) - .setQueryPayment(new Hbar(10)).execute(testEnv.client); - - var responseResult = (byte[][]) response.getResult("(bytes32[])").get(0); - - assertThat(responseResult).isEqualTo(testBytesLen32); - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractIdPopulationIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractIdPopulationIntegrationTest.java deleted file mode 100644 index 74e3f5f9a7..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractIdPopulationIntegrationTest.java +++ /dev/null @@ -1,114 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.hedera.hashgraph.sdk.ContractCreateTransaction; -import com.hedera.hashgraph.sdk.ContractFunctionParameters; -import com.hedera.hashgraph.sdk.ContractId; -import com.hedera.hashgraph.sdk.ContractInfoQuery; -import com.hedera.hashgraph.sdk.FileCreateTransaction; -import java.util.Objects; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -class ContractIdPopulationIntegrationTest { - @Test - @DisplayName("Can populate ContractId num from mirror node (using sync method)") - void canPopulateContractIdNumSync() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var testContractByteCode = "608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506101cb806100606000396000f3fe608060405260043610610046576000357c01000000000000000000000000000000000000000000000000000000009004806341c0e1b51461004b578063cfae321714610062575b600080fd5b34801561005757600080fd5b506100606100f2565b005b34801561006e57600080fd5b50610077610162565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100b757808201518184015260208101905061009c565b50505050905090810190601f1680156100e45780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415610160573373ffffffffffffffffffffffffffffffffffffffff16ff5b565b60606040805190810160405280600d81526020017f48656c6c6f2c20776f726c64210000000000000000000000000000000000000081525090509056fea165627a7a72305820ae96fb3af7cde9c0abfe365272441894ab717f816f07f41f07b1cbede54e256e0029"; - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(testContractByteCode) - .execute(testEnv.client); - - var receipt = response.setValidateStatus(true).getReceipt(testEnv.client); - var fileId = Objects.requireNonNull(receipt.fileId); - - response = new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(100000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::canPopulateContractIdNum]") - .execute(testEnv.client); - - receipt = response.setValidateStatus(true).getReceipt(testEnv.client); - - var contractId = Objects.requireNonNull(receipt.contractId); - - var info = new ContractInfoQuery() - .setContractId(contractId) - .execute(testEnv.client); - - var idMirror = ContractId.fromEvmAddress(0, 0, info.contractAccountId); - Thread.sleep(5000); - - var newContractId = idMirror.populateContractNum(testEnv.client); - - assertThat(contractId.num).isEqualTo(newContractId.num); - } - } - - @Test - @DisplayName("Can populate ContractId num from mirror node (using async method)") - void canPopulateContractIdNumAsync() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var testContractByteCode = "608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506101cb806100606000396000f3fe608060405260043610610046576000357c01000000000000000000000000000000000000000000000000000000009004806341c0e1b51461004b578063cfae321714610062575b600080fd5b34801561005757600080fd5b506100606100f2565b005b34801561006e57600080fd5b50610077610162565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100b757808201518184015260208101905061009c565b50505050905090810190601f1680156100e45780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415610160573373ffffffffffffffffffffffffffffffffffffffff16ff5b565b60606040805190810160405280600d81526020017f48656c6c6f2c20776f726c64210000000000000000000000000000000000000081525090509056fea165627a7a72305820ae96fb3af7cde9c0abfe365272441894ab717f816f07f41f07b1cbede54e256e0029"; - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(testContractByteCode) - .execute(testEnv.client); - - var receipt = response.setValidateStatus(true).getReceipt(testEnv.client); - var fileId = Objects.requireNonNull(receipt.fileId); - - response = new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(100000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::canPopulateContractIdNum]") - .execute(testEnv.client); - - receipt = response.setValidateStatus(true).getReceipt(testEnv.client); - - var contractId = Objects.requireNonNull(receipt.contractId); - - var info = new ContractInfoQuery() - .setContractId(contractId) - .execute(testEnv.client); - - var idMirror = ContractId.fromEvmAddress(0, 0, info.contractAccountId); - Thread.sleep(5000); - - var newContractId = idMirror.populateContractNumAsync(testEnv.client).get(); - - assertThat(contractId.num).isEqualTo(newContractId.num); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractInfoIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractInfoIntegrationTest.java deleted file mode 100644 index 08435ce466..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractInfoIntegrationTest.java +++ /dev/null @@ -1,267 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.*; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Objects; - -import static org.assertj.core.api.Assertions.*; - -public class ContractInfoIntegrationTest { - private static final String SMART_CONTRACT_BYTECODE = "608060405234801561001057600080fd5b506040516104d73803806104d78339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b506040525050600080546001600160a01b0319163317905550805161010890600190602084019061010f565b50506101aa565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061015057805160ff191683800117855561017d565b8280016001018555821561017d579182015b8281111561017d578251825591602001919060010190610162565b5061018992915061018d565b5090565b6101a791905b808211156101895760008155600101610193565b90565b61031e806101b96000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063368b87721461004657806341c0e1b5146100ee578063ce6d41de146100f6575b600080fd5b6100ec6004803603602081101561005c57600080fd5b81019060208101813564010000000081111561007757600080fd5b82018360208201111561008957600080fd5b803590602001918460018302840111640100000000831117156100ab57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610173945050505050565b005b6100ec6101a2565b6100fe6101ba565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610138578181015183820152602001610120565b50505050905090810190601f1680156101655780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000546001600160a01b0316331461018a5761019f565b805161019d906001906020840190610250565b505b50565b6000546001600160a01b03163314156101b85733ff5b565b60018054604080516020601f600260001961010087891615020190951694909404938401819004810282018101909252828152606093909290918301828280156102455780601f1061021a57610100808354040283529160200191610245565b820191906000526020600020905b81548152906001019060200180831161022857829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061029157805160ff19168380011785556102be565b828001600101855582156102be579182015b828111156102be5782518255916020019190600101906102a3565b506102ca9291506102ce565b5090565b61024d91905b808211156102ca57600081556001016102d456fea264697066735822122084964d4c3f6bc912a9d20e14e449721012d625aa3c8a12de41ae5519752fc89064736f6c63430006000033"; - - @Test - @DisplayName("Can query contract info") - void canQueryContractInfo() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - response = new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(200000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(testEnv.client); - - var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); - - assertThat(contractId.hashCode()).isGreaterThan(0); - assertThat(contractId.compareTo(ContractId.fromBytes(contractId.toBytes()))).isZero(); - - var info = new ContractInfoQuery() - .setContractId(contractId) - .execute(testEnv.client); - - assertThat(info.contractId).isEqualTo(contractId); - assertThat(info.accountId).isNotNull(); - assertThat(Objects.requireNonNull(info.accountId).toString()).isEqualTo(contractId.toString()); - assertThat(info.adminKey).isNotNull(); - assertThat(Objects.requireNonNull(info.adminKey).toString()).isEqualTo(Objects.requireNonNull(testEnv.operatorKey).toString()); - assertThat(info.storage).isEqualTo(128); - assertThat(info.contractMemo).isEqualTo("[e2e::ContractCreateTransaction]"); - - new ContractDeleteTransaction() - .setTransferAccountId(testEnv.operatorId) - .setContractId(contractId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can query contract info when admin key is null") - void canQueryContractInfoWhenAdminKeyIsNull() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - response = new ContractCreateTransaction() - .setGas(200000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(testEnv.client); - - var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); - - var info = new ContractInfoQuery() - .setContractId(contractId) - .execute(testEnv.client); - - assertThat(info.contractId).isEqualTo(contractId); - assertThat(info.accountId).isNotNull(); - assertThat(Objects.requireNonNull(info.accountId).toString()).isEqualTo(contractId.toString()); - assertThat(info.adminKey).isNotNull(); - // TODO: Fix this when we know it's correct - // assertEquals(info.adminKey, contractId); - assertThat(info.storage).isEqualTo(128); - assertThat(info.contractMemo).isEqualTo("[e2e::ContractCreateTransaction]"); - - } - } - - @Test - @DisplayName("Cannot query contract info when contract ID is not set") - void cannotQueryContractInfoWhenContractIDIsNotSet() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new ContractInfoQuery() - .execute(testEnv.client); - }).withMessageContaining(Status.INVALID_CONTRACT_ID.toString()); - - } - } - - @Test - @DisplayName("Can get cost, even with a big max") - @SuppressWarnings("UnusedVariable") - void getCostBigMaxContractInfoFunction() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - response = new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(200000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(testEnv.client); - - var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); - - var infoQuery = new ContractInfoQuery() - .setContractId(contractId) - .setMaxQueryPayment(new Hbar(10000)); - - var cost = infoQuery.getCost(testEnv.client); - - var result = infoQuery - .execute(testEnv.client); - - new ContractDeleteTransaction() - .setTransferAccountId(testEnv.operatorId) - .setContractId(contractId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Error, max is smaller than set payment.") - void getCostSmallMaxContractInfoFunction() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - response = new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(200000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(testEnv.client); - - var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); - - var infoQuery = new ContractInfoQuery() - .setContractId(contractId) - .setMaxQueryPayment(Hbar.fromTinybars(1)); - - assertThatExceptionOfType(MaxQueryPaymentExceededException.class).isThrownBy(() -> { - infoQuery.execute(testEnv.client); - }); - - new ContractDeleteTransaction() - .setTransferAccountId(testEnv.operatorId) - .setContractId(contractId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Insufficient tx fee error.") - void getCostInsufficientTxFeeContractInfoFunction() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - response = new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(200000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(testEnv.client); - - var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); - - var infoQuery = new ContractInfoQuery() - .setContractId(contractId) - .setMaxQueryPayment(new Hbar(100)); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - infoQuery.setQueryPayment(Hbar.fromTinybars(1)).execute(testEnv.client); - }).satisfies(error -> assertThat(error.status.toString()).isEqualTo("INSUFFICIENT_TX_FEE")); - - new ContractDeleteTransaction() - .setTransferAccountId(testEnv.operatorId) - .setContractId(contractId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractNonceInfoIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractNonceInfoIntegrationTest.java deleted file mode 100644 index 5a2b7c2361..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractNonceInfoIntegrationTest.java +++ /dev/null @@ -1,89 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.hedera.hashgraph.sdk.ContractCreateTransaction; -import com.hedera.hashgraph.sdk.ContractDeleteTransaction; -import com.hedera.hashgraph.sdk.ContractId; -import com.hedera.hashgraph.sdk.ContractNonceInfo; -import com.hedera.hashgraph.sdk.FileCreateTransaction; -import com.hedera.hashgraph.sdk.FileDeleteTransaction; -import java.util.Objects; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -public class ContractNonceInfoIntegrationTest { - private static final String SMART_CONTRACT_BYTECODE = "6080604052348015600f57600080fd5b50604051601a90603b565b604051809103906000f0801580156035573d6000803e3d6000fd5b50506047565b605c8061009483390190565b603f806100556000396000f3fe6080604052600080fdfea2646970667358221220a20122cbad3457fedcc0600363d6e895f17048f5caa4afdab9e655123737567d64736f6c634300081200336080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea264697066735822122053dfd8835e3dc6fedfb8b4806460b9b7163f8a7248bac510c6d6808d9da9d6d364736f6c63430008120033"; - - @Test - @DisplayName("Contract Create of A nonce, which deploys contract B in CONSTRUCTOR") - void canIncrementNonceThroughContractConstructor() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - response = new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(100000) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::ContractADeploysContractBInConstructor]") - .execute(testEnv.client); - - var contractFunctionResult = response.getRecord(testEnv.client).contractFunctionResult; - - ContractId contractA = contractFunctionResult.contractId; - ContractId contractB = contractFunctionResult.contractNonces.stream() - .filter(contractNonce -> !contractNonce.contractId.equals(contractA)).findFirst().get().contractId; - - ContractNonceInfo contractANonceInfo = contractFunctionResult.contractNonces.stream() - .filter(contractNonce -> contractNonce.contractId.equals(contractA)).findFirst().get(); - ContractNonceInfo contractBNonceInfo = contractFunctionResult.contractNonces.stream() - .filter(contractNonce -> contractNonce.contractId.equals(contractB)).findFirst().get(); - - // A.nonce = 2 - assertThat(contractANonceInfo.nonce).isEqualTo(2); - // B.nonce = 1 - assertThat(contractBNonceInfo.nonce).isEqualTo(1); - // validate HIP-844 case - signer nonce should be set only for Ethereum transactions - assertThat(contractFunctionResult.signerNonce).isEqualTo(0); - - var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); - - new ContractDeleteTransaction() - .setTransferAccountId(testEnv.operatorId) - .setContractId(contractId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractUpdateIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractUpdateIntegrationTest.java deleted file mode 100644 index 902a231dba..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ContractUpdateIntegrationTest.java +++ /dev/null @@ -1,157 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.ContractCreateTransaction; -import com.hedera.hashgraph.sdk.ContractDeleteTransaction; -import com.hedera.hashgraph.sdk.ContractFunctionParameters; -import com.hedera.hashgraph.sdk.ContractInfoQuery; -import com.hedera.hashgraph.sdk.ContractUpdateTransaction; -import com.hedera.hashgraph.sdk.FileCreateTransaction; -import com.hedera.hashgraph.sdk.FileDeleteTransaction; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.sdk.Status; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -class ContractUpdateIntegrationTest { - private static final String SMART_CONTRACT_BYTECODE = "608060405234801561001057600080fd5b506040516104d73803806104d78339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b506040525050600080546001600160a01b0319163317905550805161010890600190602084019061010f565b50506101aa565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061015057805160ff191683800117855561017d565b8280016001018555821561017d579182015b8281111561017d578251825591602001919060010190610162565b5061018992915061018d565b5090565b6101a791905b808211156101895760008155600101610193565b90565b61031e806101b96000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063368b87721461004657806341c0e1b5146100ee578063ce6d41de146100f6575b600080fd5b6100ec6004803603602081101561005c57600080fd5b81019060208101813564010000000081111561007757600080fd5b82018360208201111561008957600080fd5b803590602001918460018302840111640100000000831117156100ab57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610173945050505050565b005b6100ec6101a2565b6100fe6101ba565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610138578181015183820152602001610120565b50505050905090810190601f1680156101655780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000546001600160a01b0316331461018a5761019f565b805161019d906001906020840190610250565b505b50565b6000546001600160a01b03163314156101b85733ff5b565b60018054604080516020601f600260001961010087891615020190951694909404938401819004810282018101909252828152606093909290918301828280156102455780601f1061021a57610100808354040283529160200191610245565b820191906000526020600020905b81548152906001019060200180831161022857829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061029157805160ff19168380011785556102be565b828001600101855582156102be579182015b828111156102be5782518255916020019190600101906102a3565b506102ca9291506102ce565b5090565b61024d91905b808211156102ca57600081556001016102d456fea264697066735822122084964d4c3f6bc912a9d20e14e449721012d625aa3c8a12de41ae5519752fc89064736f6c63430006000033"; - - @Test - @DisplayName("Can update contract") - void canUpdateContract() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - response = new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(200000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(testEnv.client); - - var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); - - var info = new ContractInfoQuery() - .setContractId(contractId) - .execute(testEnv.client); - - assertThat(info.contractId).isEqualTo(contractId); - assertThat(info.accountId).isNotNull(); - assertThat(Objects.requireNonNull(info.accountId).toString()).isEqualTo(contractId.toString()); - assertThat(info.adminKey).isNotNull(); - assertThat(Objects.requireNonNull(info.adminKey).toString()).isEqualTo(Objects.requireNonNull(testEnv.operatorKey).toString()); - assertThat(info.storage).isEqualTo(128); - assertThat(info.contractMemo).isEqualTo("[e2e::ContractCreateTransaction]"); - - new ContractUpdateTransaction() - .setContractId(contractId) - .setContractMemo("[e2e::ContractUpdateTransaction]") - .execute(testEnv.client) - .getReceipt(testEnv.client); - - info = new ContractInfoQuery() - .setContractId(contractId) - .execute(testEnv.client); - - assertThat(info.contractId).isEqualTo(contractId); - assertThat(info.accountId).isNotNull(); - assertThat(Objects.requireNonNull(info.accountId).toString()).isEqualTo(contractId.toString()); - assertThat(info.adminKey).isNotNull(); - assertThat(Objects.requireNonNull(info.adminKey).toString()).isEqualTo(Objects.requireNonNull(testEnv.operatorKey).toString()); - assertThat(info.storage).isEqualTo(128); - assertThat(info.contractMemo).isEqualTo("[e2e::ContractUpdateTransaction]"); - - new ContractDeleteTransaction() - .setTransferAccountId(testEnv.operatorId) - .setContractId(contractId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot update contract when contract ID is not set") - void cannotUpdateContractWhenContractIDIsNotSet() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new ContractUpdateTransaction() - .setContractMemo("[e2e::ContractUpdateTransaction]") - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_CONTRACT_ID.toString()); - - } - } - - @Test - @DisplayName("Cannot update contract that is immutable") - void cannotUpdateContractThatIsImmutable() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - var contractId = Objects.requireNonNull( - new ContractCreateTransaction() - .setGas(200000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(testEnv.client) - .getReceipt(testEnv.client) - .contractId - ); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new ContractUpdateTransaction() - .setContractId(contractId) - .setContractMemo("[e2e::ContractUpdateTransaction]") - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.MODIFYING_IMMUTABLE_CONTRACT.toString()); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/EntityHelper.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/EntityHelper.java deleted file mode 100644 index 78053bd3f7..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/EntityHelper.java +++ /dev/null @@ -1,154 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.AccountId; -import com.hedera.hashgraph.sdk.ContractCreateFlow; -import com.hedera.hashgraph.sdk.ContractFunctionParameters; -import com.hedera.hashgraph.sdk.ContractId; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.Key; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.sdk.TokenCreateTransaction; -import com.hedera.hashgraph.sdk.TokenId; -import com.hedera.hashgraph.sdk.TokenSupplyType; -import com.hedera.hashgraph.sdk.TokenType; -import java.util.concurrent.TimeoutException; - -/** - * The EntityCreator class provides static methods for creating different entities in a Hedera network, such as token, account, and contract. - */ -public final class EntityHelper { - - private EntityHelper() {} - - public static int fungibleInitialBalance = 1_000_000; - public static int mitedNfts = 10; - - /** - * Create a non-fungible unique token. - * - * @param testEnv The integration test environment. - * @return The token ID of the created token. - * @throws PrecheckStatusException - * @throws TimeoutException - * @throws ReceiptStatusException - */ - public static TokenId createNft(IntegrationTestEnv testEnv) - throws PrecheckStatusException, TimeoutException, ReceiptStatusException { - return new TokenCreateTransaction() - .setTokenName("Test NFT") - .setTokenSymbol("TNFT") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setSupplyType(TokenSupplyType.FINITE) - .setMaxSupply(10) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setMetadataKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setPauseKey(testEnv.operatorKey) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId; - } - - /** - * Creates a fungible token. - * - * @param testEnv The integration test environment. - * @param decimals The number of decimal places for the token. - * @return The token ID of the created token. - * @throws PrecheckStatusException If the transaction fails pre-check. - * @throws TimeoutException If the transaction times out. - * @throws ReceiptStatusException If the receipt status is not success. - */ - public static TokenId createFungibleToken(IntegrationTestEnv testEnv, int decimals) - throws PrecheckStatusException, TimeoutException, ReceiptStatusException { - return new TokenCreateTransaction() - .setTokenName("Test Fungible Token") - .setTokenSymbol("TFT") - .setTokenMemo("I was created for integration tests") - .setDecimals(decimals) - .setInitialSupply(fungibleInitialBalance) - .setMaxSupply(fungibleInitialBalance) - .setTreasuryAccountId(testEnv.operatorId) - .setSupplyType(TokenSupplyType.FINITE) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setMetadataKey(testEnv.operatorKey) - .setPauseKey(testEnv.operatorKey) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId; - } - - /** - * Creates a new account with the specified account key and maximum automatic token associations. - * - * @param testEnv The integration test environment. - * @param accountKey The account key. - * @param maxAutomaticTokenAssociations The maximum number of automatic token associations allowed. - * @return The account ID of the newly created account. - * @throws PrecheckStatusException If the transaction fails pre-check. - * @throws TimeoutException If the transaction times out. - * @throws ReceiptStatusException If the receipt status is not success. - */ - public static AccountId createAccount(IntegrationTestEnv testEnv, Key accountKey, int maxAutomaticTokenAssociations) - throws PrecheckStatusException, TimeoutException, ReceiptStatusException { - return new AccountCreateTransaction() - .setKey(accountKey) - .setInitialBalance(new Hbar(10)) - .setMaxAutomaticTokenAssociations(maxAutomaticTokenAssociations) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .accountId; - } - - /** - * Creates a contract with the specified contract key. - * - * @param testEnv The integration test environment. - * @param contractKey The contract key. - * @return The contract ID of the created contract. - * @throws PrecheckStatusException if the transaction fails pre-check. - * @throws TimeoutException if the transaction times out. - * @throws ReceiptStatusException if the receipt status is not success. - */ - public static ContractId createContract(IntegrationTestEnv testEnv, Key contractKey) - throws PrecheckStatusException, TimeoutException, ReceiptStatusException { - final String SMART_CONTRACT_BYTECODE = "608060405234801561001057600080fd5b506040516104d73803806104d78339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b506040525050600080546001600160a01b0319163317905550805161010890600190602084019061010f565b50506101aa565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061015057805160ff191683800117855561017d565b8280016001018555821561017d579182015b8281111561017d578251825591602001919060010190610162565b5061018992915061018d565b5090565b6101a791905b808211156101895760008155600101610193565b90565b61031e806101b96000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063368b87721461004657806341c0e1b5146100ee578063ce6d41de146100f6575b600080fd5b6100ec6004803603602081101561005c57600080fd5b81019060208101813564010000000081111561007757600080fd5b82018360208201111561008957600080fd5b803590602001918460018302840111640100000000831117156100ab57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610173945050505050565b005b6100ec6101a2565b6100fe6101ba565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610138578181015183820152602001610120565b50505050905090810190601f1680156101655780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000546001600160a01b0316331461018a5761019f565b805161019d906001906020840190610250565b505b50565b6000546001600160a01b03163314156101b85733ff5b565b60018054604080516020601f600260001961010087891615020190951694909404938401819004810282018101909252828152606093909290918301828280156102455780601f1061021a57610100808354040283529160200191610245565b820191906000526020600020905b81548152906001019060200180831161022857829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061029157805160ff19168380011785556102be565b828001600101855582156102be579182015b828111156102be5782518255916020019190600101906102a3565b506102ca9291506102ce565b5090565b61024d91905b808211156102ca57600081556001016102d456fea264697066735822122084964d4c3f6bc912a9d20e14e449721012d625aa3c8a12de41ae5519752fc89064736f6c63430006000033"; - - return new ContractCreateFlow() - .setAdminKey(contractKey) - .setGas(200_000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecode(SMART_CONTRACT_BYTECODE) - .setContractMemo("[e2e::ContractMemo]") - .execute(testEnv.client) - .getReceipt(testEnv.client) - .contractId; - } -} - diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/EthereumTransactionIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/EthereumTransactionIntegrationTest.java deleted file mode 100644 index 80c7c68497..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/EthereumTransactionIntegrationTest.java +++ /dev/null @@ -1,153 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.esaulpaugh.headlong.rlp.RLPEncoder; -import com.esaulpaugh.headlong.util.Integers; -import com.hedera.hashgraph.sdk.ContractCreateTransaction; -import com.hedera.hashgraph.sdk.ContractDeleteTransaction; -import com.hedera.hashgraph.sdk.ContractExecuteTransaction; -import com.hedera.hashgraph.sdk.ContractFunctionParameters; -import com.hedera.hashgraph.sdk.EthereumTransaction; -import com.hedera.hashgraph.sdk.FileCreateTransaction; -import com.hedera.hashgraph.sdk.FileDeleteTransaction; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.PrivateKeyECDSA; -import com.hedera.hashgraph.sdk.TransferTransaction; -import java.math.BigInteger; -import java.util.List; -import java.util.Objects; -import org.bouncycastle.util.encoders.Hex; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -public class EthereumTransactionIntegrationTest { - - private static final String SMART_CONTRACT_BYTECODE = "608060405234801561001057600080fd5b506040516104d73803806104d78339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b506040525050600080546001600160a01b0319163317905550805161010890600190602084019061010f565b50506101aa565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061015057805160ff191683800117855561017d565b8280016001018555821561017d579182015b8281111561017d578251825591602001919060010190610162565b5061018992915061018d565b5090565b6101a791905b808211156101895760008155600101610193565b90565b61031e806101b96000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063368b87721461004657806341c0e1b5146100ee578063ce6d41de146100f6575b600080fd5b6100ec6004803603602081101561005c57600080fd5b81019060208101813564010000000081111561007757600080fd5b82018360208201111561008957600080fd5b803590602001918460018302840111640100000000831117156100ab57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610173945050505050565b005b6100ec6101a2565b6100fe6101ba565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610138578181015183820152602001610120565b50505050905090810190601f1680156101655780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000546001600160a01b0316331461018a5761019f565b805161019d906001906020840190610250565b505b50565b6000546001600160a01b03163314156101b85733ff5b565b60018054604080516020601f600260001961010087891615020190951694909404938401819004810282018101909252828152606093909290918301828280156102455780601f1061021a57610100808354040283529160200191610245565b820191906000526020600020905b81548152906001019060200180831161022857829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061029157805160ff19168380011785556102be565b828001600101855582156102be579182015b828111156102be5782518255916020019190600101906102a3565b506102ca9291506102ce565b5090565b61024d91905b808211156102ca57600081556001016102d456fea264697066735822122084964d4c3f6bc912a9d20e14e449721012d625aa3c8a12de41ae5519752fc89064736f6c63430006000033"; - - /** - * @notice E2E-HIP-844 - * @url https://hips.hedera.com/hip/hip-844 - */ - @Test - @DisplayName("Signer nonce changed on Ethereum transaction") - void signerNonceChangedOnEthereumTransaction() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var privateKey = PrivateKey.generateECDSA(); - var newAccountAliasId = privateKey.toAccountId(0, 0); - - new TransferTransaction() - .addHbarTransfer(testEnv.operatorId, new Hbar(1).negated()) - .addHbarTransfer(newAccountAliasId, new Hbar(1)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var fileCreateTransactionResponse = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(fileCreateTransactionResponse.getReceipt(testEnv.client).fileId); - - var contractCreateTransactionResponse = new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(200000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(testEnv.client); - - var contractId = Objects.requireNonNull( - contractCreateTransactionResponse.getReceipt(testEnv.client).contractId); - - int nonce = 0; - byte[] chainId = Hex.decode("012a"); - byte[] maxPriorityGas = Hex.decode("00"); - byte[] maxGas = Hex.decode("d1385c7bf0"); - byte[] to = Hex.decode(contractId.toSolidityAddress()); - byte[] callData = new ContractExecuteTransaction() - .setFunction("setMessage", new ContractFunctionParameters().addString("new message")) - .getFunctionParameters() - .toByteArray(); - - var sequence = RLPEncoder.sequence(Integers.toBytes(2), new Object[]{ - chainId, - Integers.toBytes(nonce), // nonce - maxPriorityGas, - maxGas, - Integers.toBytes(150000), // gasLimit - to, - Integers.toBytesUnsigned(BigInteger.ZERO), // value - callData, - new Object[0] - }); - - byte[] signedBytes = privateKey.sign(sequence); - - // wrap in signature object - final byte[] r = new byte[32]; - System.arraycopy(signedBytes, 0, r, 0, 32); - final byte[] s = new byte[32]; - System.arraycopy(signedBytes, 32, s, 0, 32); - - final int recId = ((PrivateKeyECDSA) privateKey).getRecoveryId(r, s, sequence); - - byte[] ethereumData = RLPEncoder.sequence( - Integers.toBytes(0x02), - List.of( - chainId, - Integers.toBytes(nonce), // nonce - maxPriorityGas, - maxGas, - Integers.toBytes(150000), // gasLimit - to, - Integers.toBytesUnsigned(BigInteger.ZERO), // value - callData, - List.of(/*accessList*/), - Integers.toBytes(recId), // recId - r, - s)); - - EthereumTransaction ethereumTransaction = new EthereumTransaction() - .setEthereumData(ethereumData); - var ethereumTransactionResponse = ethereumTransaction.execute(testEnv.client); - var ethereumTransactionRecord = ethereumTransactionResponse.getRecord(testEnv.client); - - assertThat(ethereumTransactionRecord.contractFunctionResult.signerNonce).isEqualTo(1); - - new ContractDeleteTransaction() - .setTransferAccountId(testEnv.operatorId) - .setContractId(contractId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/FeeSchedulesTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/FeeSchedulesTest.java deleted file mode 100644 index 08f408ed8e..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/FeeSchedulesTest.java +++ /dev/null @@ -1,50 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * Copyright (C) 2021 - 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.hashgraph.sdk.test.integration; - -import com.google.protobuf.ByteString; -import com.hedera.hashgraph.sdk.FeeSchedules; -import com.hedera.hashgraph.sdk.FileContentsQuery; -import com.hedera.hashgraph.sdk.FileId; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class FeeSchedulesTest { - @Test - @DisplayName("FeeSchedules (CurrentAndNextFeeSchedule) is fetched and parsed from file 0.0.111") - void canFetchFeeSchedules() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - ByteString feeSchedulesBytes = new FileContentsQuery() - .setFileId(new FileId(0, 0, 111)) - .execute(testEnv.client); - - FeeSchedules feeSchedules = FeeSchedules.fromBytes(feeSchedulesBytes.toByteArray()); - - /* - * Test whether the file 0.0.111 actually contains stuff - */ - assertThat(feeSchedules.getCurrent()).isNotNull(); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/FileAppendIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/FileAppendIntegrationTest.java deleted file mode 100644 index 8a9c6d4b4e..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/FileAppendIntegrationTest.java +++ /dev/null @@ -1,208 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.FileAppendTransaction; -import com.hedera.hashgraph.sdk.FileContentsQuery; -import com.hedera.hashgraph.sdk.FileCreateTransaction; -import com.hedera.hashgraph.sdk.FileDeleteTransaction; -import com.hedera.hashgraph.sdk.FileInfoQuery; -import com.hedera.hashgraph.sdk.KeyList; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import java.time.Duration; - -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThat; - -public class FileAppendIntegrationTest { - @Test - @DisplayName("Can append to file") - void canAppendToFile() throws Exception { - // There are potential bugs in FileAppendTransaction which require more than one node to trigger. - try(var testEnv = new IntegrationTestEnv(1)){ - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents("[e2e::FileCreateTransaction]") - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - var info = new FileInfoQuery() - .setFileId(fileId) - .execute(testEnv.client); - - assertThat(info.fileId).isEqualTo(fileId); - assertThat(info.size).isEqualTo(28); - assertThat(info.isDeleted).isFalse(); - assertThat(info.keys).isNotNull(); - assertThat(info.keys.getThreshold()).isNull(); - assertThat(info.keys).isEqualTo(KeyList.of(testEnv.operatorKey)); - - new FileAppendTransaction() - .setFileId(fileId) - .setContents("[e2e::FileAppendTransaction]") - .execute(testEnv.client) - .getReceipt(testEnv.client); - - info = new FileInfoQuery() - .setFileId(fileId) - .execute(testEnv.client); - - assertThat(info.fileId).isEqualTo(fileId); - assertThat(info.size).isEqualTo(56); - assertThat(info.isDeleted).isFalse(); - assertThat(info.keys).isNotNull(); - assertThat(info.keys.getThreshold()).isNull(); - assertThat(info.keys).isEqualTo(KeyList.of(testEnv.operatorKey)); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can append large contents to file") - void canAppendLargeContentsToFile() throws Exception { - // There are potential bugs in FileAppendTransaction which require more than one node to trigger. - try(var testEnv = new IntegrationTestEnv(2)){ - - // Skip if using local node. - // Note: this check should be removed once the local node is supporting multiple nodes. - testEnv.assumeNotLocalNode(); - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents("[e2e::FileCreateTransaction]") - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - Thread.sleep(5000); - - var info = new FileInfoQuery() - .setFileId(fileId) - .execute(testEnv.client); - - assertThat(info.fileId).isEqualTo(fileId); - assertThat(info.size).isEqualTo(28); - assertThat(info.isDeleted).isFalse(); - assertThat(info.keys).isNotNull(); - assertThat(info.keys.getThreshold()).isNull(); - assertThat(info.keys).isEqualTo(KeyList.of(testEnv.operatorKey)); - - new FileAppendTransaction() - .setFileId(fileId) - .setContents(Contents.BIG_CONTENTS) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var contents = new FileContentsQuery() - .setFileId(fileId) - .execute(testEnv.client); - - assertThat(contents.toStringUtf8()).isEqualTo("[e2e::FileCreateTransaction]" + Contents.BIG_CONTENTS); - - info = new FileInfoQuery() - .setFileId(fileId) - .execute(testEnv.client); - - assertThat(info.fileId).isEqualTo(fileId); - assertThat(info.size).isEqualTo(13522); - assertThat(info.isDeleted).isFalse(); - assertThat(info.keys).isNotNull(); - assertThat(info.keys.getThreshold()).isNull(); - assertThat(info.keys).isEqualTo(KeyList.of(testEnv.operatorKey)); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can append large contents to file despite TRANSACTION_EXPIRATION response codes") - void canAppendLargeContentsToFileDespiteExpiration() throws Exception { - // There are potential bugs in FileAppendTransaction which require more than one node to trigger. - try(var testEnv = new IntegrationTestEnv(2)){ - - // Skip if using local node. - // Note: this check should be removed once the local node is supporting multiple nodes. - testEnv.assumeNotLocalNode(); - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents("[e2e::FileCreateTransaction]") - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - Thread.sleep(5000); - - var info = new FileInfoQuery() - .setFileId(fileId) - .execute(testEnv.client); - - assertThat(info.fileId).isEqualTo(fileId); - assertThat(info.size).isEqualTo(28); - assertThat(info.isDeleted).isFalse(); - assertThat(info.keys).isNotNull(); - assertThat(info.keys.getThreshold()).isNull(); - assertThat(info.keys).isEqualTo(KeyList.of(testEnv.operatorKey)); - - var appendTx = new FileAppendTransaction() - .setFileId(fileId) - .setContents(Contents.BIG_CONTENTS) - .setTransactionValidDuration(Duration.ofSeconds(25)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var contents = new FileContentsQuery() - .setFileId(fileId) - .execute(testEnv.client); - - assertThat(contents.toStringUtf8()).isEqualTo("[e2e::FileCreateTransaction]" + Contents.BIG_CONTENTS); - - info = new FileInfoQuery() - .setFileId(fileId) - .execute(testEnv.client); - - assertThat(info.fileId).isEqualTo(fileId); - assertThat(info.size).isEqualTo(13522); - assertThat(info.isDeleted).isFalse(); - assertThat(info.keys).isNotNull(); - assertThat(info.keys.getThreshold()).isNull(); - assertThat(info.keys).isEqualTo(KeyList.of(testEnv.operatorKey)); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/FileContentsIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/FileContentsIntegrationTest.java deleted file mode 100644 index 929ff8f8d6..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/FileContentsIntegrationTest.java +++ /dev/null @@ -1,186 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.FileContentsQuery; -import com.hedera.hashgraph.sdk.FileCreateTransaction; -import com.hedera.hashgraph.sdk.FileDeleteTransaction; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.MaxQueryPaymentExceededException; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.Status; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -public class FileContentsIntegrationTest { - - @Test - @DisplayName("Can query file contents") - void canQueryFileContents() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents("[e2e::FileCreateTransaction]") - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - var contents = new FileContentsQuery() - .setFileId(fileId) - .execute(testEnv.client); - - assertThat(contents.toStringUtf8()).isEqualTo("[e2e::FileCreateTransaction]"); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can query empty file contents") - void canQueryEmptyFileContents() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - var contents = new FileContentsQuery() - .setFileId(fileId) - .execute(testEnv.client); - - assertThat(contents.size()).isEqualTo(0); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot query file contents when file ID is not set") - void cannotQueryFileContentsWhenFileIDIsNotSet() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new FileContentsQuery() - .execute(testEnv.client); - }).withMessageContaining(Status.INVALID_FILE_ID.toString()); - - } - } - - @Test - @DisplayName("Can get cost, even with a big max") - void getCostBigMaxQueryFileContents() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents("[e2e::FileCreateTransaction]") - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - var contentsQuery = new FileContentsQuery() - .setFileId(fileId) - .setMaxQueryPayment(new Hbar(1000)); - - var contents = contentsQuery.execute(testEnv.client); - - assertThat(contents.toStringUtf8()).isEqualTo("[e2e::FileCreateTransaction]"); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Error, max is smaller than set payment.") - void getCostSmallMaxQueryFileContents() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents("[e2e::FileCreateTransaction]") - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - var contentsQuery = new FileContentsQuery() - .setFileId(fileId) - .setMaxQueryPayment(Hbar.fromTinybars(1)); - - assertThatExceptionOfType(MaxQueryPaymentExceededException.class).isThrownBy(() -> { - contentsQuery.execute(testEnv.client); - }); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Insufficient tx fee error.") - void getCostInsufficientTxFeeQueryFileContents() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents("[e2e::FileCreateTransaction]") - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - var contentsQuery = new FileContentsQuery() - .setFileId(fileId) - .setMaxQueryPayment(new Hbar(100)); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - contentsQuery.setQueryPayment(Hbar.fromTinybars(1)).execute(testEnv.client); - }).satisfies(error -> assertThat(error.status.toString()).isEqualTo("INSUFFICIENT_TX_FEE")); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/FileCreateIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/FileCreateIntegrationTest.java deleted file mode 100644 index 92a93c1989..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/FileCreateIntegrationTest.java +++ /dev/null @@ -1,116 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.FileCreateTransaction; -import com.hedera.hashgraph.sdk.FileDeleteTransaction; -import com.hedera.hashgraph.sdk.FileInfoQuery; -import com.hedera.hashgraph.sdk.KeyList; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThat; - -public class FileCreateIntegrationTest { - @Test - @DisplayName("Can create file") - void canCreateFile() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents("[e2e::FileCreateTransaction]") - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - var info = new FileInfoQuery() - .setFileId(fileId) - .execute(testEnv.client); - - assertThat(info.fileId).isEqualTo(fileId); - assertThat(info.size).isEqualTo(28); - assertThat(info.isDeleted).isFalse(); - assertThat(info.keys).isNotNull(); - assertThat(info.keys.getThreshold()).isNull(); - assertThat(info.keys).isEqualTo(KeyList.of(testEnv.operatorKey)); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can create file with no contents") - void canCreateFileWithNoContents() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - var info = new FileInfoQuery() - .setFileId(fileId) - .execute(testEnv.client); - - assertThat(info.fileId).isEqualTo(fileId); - assertThat(info.size).isEqualTo(0); - assertThat(info.isDeleted).isFalse(); - assertThat(info.keys).isNotNull(); - assertThat(info.keys.getThreshold()).isNull(); - assertThat(info.keys).isEqualTo(KeyList.of(testEnv.operatorKey)); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can create file with no keys") - void canCreateFileWithNoKeys() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var response = new FileCreateTransaction() - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - var info = new FileInfoQuery() - .setFileId(fileId) - .execute(testEnv.client); - - assertThat(info.fileId).isEqualTo(fileId); - assertThat(info.size).isEqualTo(0); - assertThat(info.isDeleted).isFalse(); - assertThat(info.keys).isNull(); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/FileDeleteIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/FileDeleteIntegrationTest.java deleted file mode 100644 index 04bc27eb27..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/FileDeleteIntegrationTest.java +++ /dev/null @@ -1,97 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.FileCreateTransaction; -import com.hedera.hashgraph.sdk.FileDeleteTransaction; -import com.hedera.hashgraph.sdk.FileInfoQuery; -import com.hedera.hashgraph.sdk.KeyList; -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.sdk.Status; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -public class FileDeleteIntegrationTest { - @Test - @DisplayName("Can delete file") - void canDeleteFile() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents("[e2e::FileCreateTransaction]") - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - var info = new FileInfoQuery() - .setFileId(fileId) - .execute(testEnv.client); - - assertThat(info.fileId).isEqualTo(fileId); - assertThat(info.size).isEqualTo(28); - assertThat(info.isDeleted).isFalse(); - assertThat(info.keys).isNotNull(); - assertThat(info.keys.getThreshold()).isNull(); - assertThat(info.keys).isEqualTo(KeyList.of(testEnv.operatorKey)); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot delete immutable file") - void cannotDeleteImmutableFile() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var response = new FileCreateTransaction() - .setContents("[e2e::FileCreateTransaction]") - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - var info = new FileInfoQuery() - .setFileId(fileId) - .execute(testEnv.client); - - assertThat(info.fileId).isEqualTo(fileId); - assertThat(info.size).isEqualTo(28); - assertThat(info.isDeleted).isFalse(); - assertThat(info.keys).isNull(); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.UNAUTHORIZED.toString()); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/FileInfoIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/FileInfoIntegrationTest.java deleted file mode 100644 index 8eef3c92a7..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/FileInfoIntegrationTest.java +++ /dev/null @@ -1,175 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.FileCreateTransaction; -import com.hedera.hashgraph.sdk.FileDeleteTransaction; -import com.hedera.hashgraph.sdk.FileInfoQuery; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.KeyList; -import com.hedera.hashgraph.sdk.MaxQueryPaymentExceededException; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -public class FileInfoIntegrationTest { - @Test - @DisplayName("Can query file info") - void canQueryFileInfo() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents("[e2e::FileCreateTransaction]") - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - var info = new FileInfoQuery() - .setFileId(fileId) - .execute(testEnv.client); - - assertThat(info.fileId).isEqualTo(fileId); - assertThat(info.size).isEqualTo(28); - assertThat(info.isDeleted).isFalse(); - assertThat(info.keys).isNotNull(); - assertThat(info.keys.getThreshold()).isNull(); - assertThat(info.keys).isEqualTo(KeyList.of(testEnv.operatorKey)); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can query file info with no admin key or contents") - void canQueryFileInfoWithNoAdminKeyOrContents() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var response = new FileCreateTransaction() - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - var info = new FileInfoQuery() - .setFileId(fileId) - .execute(testEnv.client); - - assertThat(info.fileId).isEqualTo(fileId); - assertThat(info.size).isEqualTo(0); - assertThat(info.isDeleted).isFalse(); - assertThat(info.keys).isNull(); - - } - } - - @Test - @DisplayName("Can get cost, even with a big max") - @SuppressWarnings("UnusedVariable") - void getCostBigMaxQueryFileInfo() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents("[e2e::FileCreateTransaction]") - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - var infoQuery = new FileInfoQuery() - .setFileId(fileId) - .setMaxQueryPayment(new Hbar(1000)); - - var cost = infoQuery.getCost(testEnv.client); - - var info = infoQuery.setQueryPayment(cost).execute(testEnv.client); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Error, max is smaller than set payment.") - void getCostSmallMaxQueryFileInfo() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents("[e2e::FileCreateTransaction]") - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - var infoQuery = new FileInfoQuery() - .setFileId(fileId) - .setMaxQueryPayment(Hbar.fromTinybars(1)); - - assertThatExceptionOfType(MaxQueryPaymentExceededException.class).isThrownBy(() -> { - infoQuery.execute(testEnv.client); - }); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Insufficient tx fee error.") - void getCostInsufficientTxFeeQueryFileInfo() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents("[e2e::FileCreateTransaction]") - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - var infoQuery = new FileInfoQuery() - .setFileId(fileId) - .setMaxQueryPayment(Hbar.fromTinybars(1)); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - infoQuery.setQueryPayment(Hbar.fromTinybars(1)).execute(testEnv.client); - }).satisfies(error -> assertThat(error.status.toString()).isEqualTo("INSUFFICIENT_TX_FEE")); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/FileUpdateIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/FileUpdateIntegrationTest.java deleted file mode 100644 index f41a62f998..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/FileUpdateIntegrationTest.java +++ /dev/null @@ -1,153 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.AccountId; -import com.hedera.hashgraph.sdk.FileCreateTransaction; -import com.hedera.hashgraph.sdk.FileDeleteTransaction; -import com.hedera.hashgraph.sdk.FileId; -import com.hedera.hashgraph.sdk.FileInfoQuery; -import com.hedera.hashgraph.sdk.FileUpdateTransaction; -import com.hedera.hashgraph.sdk.KeyList; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.sdk.Status; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -public class FileUpdateIntegrationTest { - @Test - @DisplayName("Can update file") - void canUpdateFile() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents("[e2e::FileCreateTransaction]") - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - var info = new FileInfoQuery() - .setFileId(fileId) - .execute(testEnv.client); - - assertThat(info.fileId).isEqualTo(fileId); - assertThat(info.size).isEqualTo(28); - assertThat(info.isDeleted).isFalse(); - assertThat(info.keys).isNotNull(); - assertThat(info.keys.getThreshold()).isNull(); - assertThat(info.keys).isEqualTo(KeyList.of(testEnv.operatorKey)); - - new FileUpdateTransaction() - .setFileId(fileId) - .setContents("[e2e::FileUpdateTransaction]") - .execute(testEnv.client) - .getReceipt(testEnv.client); - - info = new FileInfoQuery() - .setFileId(fileId) - .execute(testEnv.client); - - assertThat(info.fileId).isEqualTo(fileId); - assertThat(info.size).isEqualTo(28); - assertThat(info.isDeleted).isFalse(); - assertThat(info.keys).isNotNull(); - assertThat(info.keys.getThreshold()).isNull(); - assertThat(info.keys).isEqualTo(KeyList.of(testEnv.operatorKey)); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot update immutable file") - void cannotUpdateImmutableFile() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var response = new FileCreateTransaction() - .setContents("[e2e::FileCreateTransaction]") - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - var info = new FileInfoQuery() - .setFileId(fileId) - .execute(testEnv.client); - - assertThat(info.fileId).isEqualTo(fileId); - assertThat(info.size).isEqualTo(28); - assertThat(info.isDeleted).isFalse(); - assertThat(info.keys).isNull(); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new FileUpdateTransaction() - .setFileId(fileId) - .setContents("[e2e::FileUpdateTransaction]") - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.UNAUTHORIZED.toString()); - - } - } - - @Test - @DisplayName("Cannot update file when file ID is not set") - void cannotUpdateFileWhenFileIDIsNotSet() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new FileUpdateTransaction() - .setContents("[e2e::FileUpdateTransaction]") - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_FILE_ID.toString()); - - } - } - - @Test - @DisplayName("Can update fee schedule file") - void canUpdateFeeScheduleFile() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - testEnv.client.setOperator(new AccountId(0, 0, 2), PrivateKey.fromString( - "302e020100300506032b65700422042091132178e72057a1d7528025956fe39b0b847f200ab59b2fdd367017f3087137")); - - var fileId = new FileId(0, 0, 111); - var receipt = new FileUpdateTransaction() - .setFileId(fileId) - .setContents("[e2e::FileUpdateTransaction]") - .execute(testEnv.client) - .getReceipt(testEnv.client); - - assertThat(receipt.status).isEqualTo(Status.FEE_SCHEDULE_FILE_PART_UPLOADED); - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/IntegrationTestEnv.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/IntegrationTestEnv.java deleted file mode 100644 index 86037ebdc7..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/IntegrationTestEnv.java +++ /dev/null @@ -1,214 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.hedera.hashgraph.sdk.AccountBalanceQuery; -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.AccountId; -import com.hedera.hashgraph.sdk.Client; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.PublicKey; -import com.hedera.hashgraph.sdk.TokenId; -import com.hedera.hashgraph.sdk.TransferTransaction; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import javax.annotation.Nullable; -import org.junit.jupiter.api.Assumptions; - -public class IntegrationTestEnv implements AutoCloseable { - static final String LOCAL_CONSENSUS_NODE_ENDPOINT = "127.0.0.1:50211"; - static final String LOCAL_MIRROR_NODE_GRPC_ENDPOINT = "127.0.0.1:5600"; - static final AccountId LOCAL_CONSENSUS_NODE_ACCOUNT_ID = new AccountId(3); - private final Client originalClient; - public Client client; - public PublicKey operatorKey; - public AccountId operatorId; - public boolean isLocalNode = false; - private static ExecutorService clientExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); - - public IntegrationTestEnv() throws Exception { - this(0); - } - - @SuppressWarnings("EmptyCatch") - public IntegrationTestEnv(int maxNodesPerTransaction) throws Exception { - client = createTestEnvClient(); - - if (maxNodesPerTransaction == 0) { - maxNodesPerTransaction = client.getNetwork().size(); - } - - client.setMaxNodesPerTransaction(maxNodesPerTransaction); - originalClient = client; - - try { - var operatorPrivateKey = PrivateKey.fromString(System.getProperty("OPERATOR_KEY")); - operatorId = AccountId.fromString(System.getProperty("OPERATOR_ID")); - operatorKey = operatorPrivateKey.getPublicKey(); - - client.setOperator(operatorId, operatorPrivateKey); - } catch (RuntimeException ignored) { - } - - operatorKey = client.getOperatorPublicKey(); - operatorId = client.getOperatorAccountId(); - - assertThat(client.getOperatorAccountId()).isNotNull(); - assertThat(client.getOperatorPublicKey()).isNotNull(); - - if (client.getNetwork().size() > 0 && (client.getNetwork().containsKey(LOCAL_CONSENSUS_NODE_ENDPOINT))) { - isLocalNode = true; - } - - var nodeGetter = new TestEnvNodeGetter(client); - var network = new HashMap(); - - var nodeCount = Math.min(client.getNetwork().size(), maxNodesPerTransaction); - for (int i = 0; i < nodeCount; i++) { - nodeGetter.nextNode(network); - } - client.setNetwork(network); - } - - @SuppressWarnings("EmptyCatch") - private static Client createTestEnvClient() throws Exception { - if (System.getProperty("HEDERA_NETWORK").equals("previewnet")) { - return Client.forPreviewnet(); - } else if (System.getProperty("HEDERA_NETWORK").equals("testnet")) { - return Client.forTestnet(); - } else if (System.getProperty("HEDERA_NETWORK").equals("localhost")) { - var network = new HashMap(); - network.put(LOCAL_CONSENSUS_NODE_ENDPOINT, LOCAL_CONSENSUS_NODE_ACCOUNT_ID); - - return Client - .forNetwork(network, clientExecutor) - .setMirrorNetwork(List.of(LOCAL_MIRROR_NODE_GRPC_ENDPOINT)); - } else if (!System.getProperty("CONFIG_FILE").equals("")) { - try { - return Client.fromConfigFile(System.getProperty("CONFIG_FILE")); - } catch (Exception configFileException) { - configFileException.printStackTrace(); - } - } - throw new IllegalStateException("Failed to construct client for IntegrationTestEnv"); - } - - public IntegrationTestEnv useThrowawayAccount(Hbar initialBalance) throws Exception { - var key = PrivateKey.generateED25519(); - operatorKey = key.getPublicKey(); - operatorId = new AccountCreateTransaction() - .setInitialBalance(initialBalance) - .setKey(key) - .execute(client) - .getReceipt(client) - .accountId; - - client = Client.forNetwork(originalClient.getNetwork()); - client.setMirrorNetwork(originalClient.getMirrorNetwork()); - client.setOperator(Objects.requireNonNull(operatorId), key); - client.setLedgerId(originalClient.getLedgerId()); - return this; - } - - public IntegrationTestEnv useThrowawayAccount() throws Exception { - return useThrowawayAccount(new Hbar(50)); - } - - // Note: this is a temporary workaround. - // The assumption should be removed once the local node is supporting multiple nodes. - public void assumeNotLocalNode() throws Exception { - // first clean up the current IntegrationTestEnv... - if (isLocalNode) { - close(); - } - - // then skip the current test - Assumptions.assumeFalse(isLocalNode); - } - - @Override - public void close() throws Exception { - if (!operatorId.equals(originalClient.getOperatorAccountId())) { - try { - var hbarsBalance = new AccountBalanceQuery() - .setAccountId(operatorId) - .execute(originalClient) - .hbars; - new TransferTransaction() - .addHbarTransfer(operatorId, hbarsBalance.negated()) - .addHbarTransfer(Objects.requireNonNull(originalClient.getOperatorAccountId()), hbarsBalance) - .freezeWith(originalClient) - .signWithOperator(client) - .execute(originalClient) - .getReceipt(originalClient); - - } catch (Exception e) { - client.close(); - } - } - originalClient.close(); - } - - private static class TestEnvNodeGetter { - private final Client client; - private final List> nodes; - private int index = 0; - - public TestEnvNodeGetter(Client client) { - this.client = client; - nodes = new ArrayList<>(client.getNetwork().entrySet()); - Collections.shuffle(nodes); - } - - public void nextNode(Map outMap) throws Exception { - if (nodes.isEmpty()) { - throw new IllegalStateException( - "IntegrationTestEnv needs another node, but there aren't enough nodes in client network"); - } - for (; index < nodes.size(); index++) { - var node = nodes.get(index); - try { - new TransferTransaction() - .setNodeAccountIds(Collections.singletonList(node.getValue())) - .setMaxAttempts(1) - .addHbarTransfer(client.getOperatorAccountId(), Hbar.fromTinybars(1).negated()) - .addHbarTransfer(AccountId.fromString("0.0.3"), Hbar.fromTinybars(1)) - .execute(client) - .getReceipt(client); - nodes.remove(index); - outMap.put(node.getKey(), node.getValue()); - return; - } catch (Throwable err) { - System.err.println(err); - } - } - throw new Exception("Failed to find working node in " + nodes + " for IntegrationTestEnv"); - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/LiveHashAddIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/LiveHashAddIntegrationTest.java deleted file mode 100644 index 8d590d749f..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/LiveHashAddIntegrationTest.java +++ /dev/null @@ -1,66 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.LiveHashAddTransaction; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.Status; -import org.bouncycastle.util.encoders.Hex; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import java.time.Duration; - -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -class LiveHashAddIntegrationTest { - private static final byte[] HASH = Hex.decode("100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002"); - - @Test - @DisplayName("Cannot create live hash because it's not supported") - void cannotCreateLiveHashBecauseItsNotSupported() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new LiveHashAddTransaction() - .setAccountId(accountId) - .setDuration(Duration.ofDays(30)) - .setHash(HASH) - .setKeys(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.NOT_SUPPORTED.toString()); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/LiveHashDeleteIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/LiveHashDeleteIntegrationTest.java deleted file mode 100644 index 4c313c074d..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/LiveHashDeleteIntegrationTest.java +++ /dev/null @@ -1,63 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.LiveHashDeleteTransaction; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.Status; -import org.bouncycastle.util.encoders.Hex; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -class LiveHashDeleteIntegrationTest { - private static final byte[] HASH = Hex.decode("100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002"); - - @Test - @DisplayName("Cannot delete live hash because it's not supported") - void cannotDeleteLiveHashBecauseItsNotSupported() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new LiveHashDeleteTransaction() - .setAccountId(accountId) - .setHash(HASH) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.NOT_SUPPORTED.toString()); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/LoadIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/LoadIntegrationTest.java deleted file mode 100644 index b03abbc0d6..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/LoadIntegrationTest.java +++ /dev/null @@ -1,92 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import static org.junit.jupiter.api.Assertions.fail; - -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.AccountId; -import com.hedera.hashgraph.sdk.Client; -import com.hedera.hashgraph.sdk.PrivateKey; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -class LoadIntegrationTest { - - @Test - @DisplayName("Load test with multiple clients and single executor") - void loadTest() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var operatorPrivateKey = PrivateKey.fromString(System.getProperty("OPERATOR_KEY")); - var operatorId = AccountId.fromString(System.getProperty("OPERATOR_ID")); - - int nThreads = 10; - var clientExecutor = Executors.newFixedThreadPool(16); - - var threadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(nThreads); - - long startTime = System.currentTimeMillis(); - - System.out.println("Finished executing tasks:"); - for (int i = 0; i < nThreads; i++) { - int finalI = i; - threadPoolExecutor.submit(() -> { - try (var client = Client.forNetwork(testEnv.client.getNetwork(), clientExecutor); - ) { - client.setOperator(operatorId, operatorPrivateKey); - client.setMaxAttempts(10); - new AccountCreateTransaction() - .setKey(PrivateKey.generateED25519()) - .execute(client) - .getReceipt(client); - System.out.println(finalI); - } catch (Exception e) { - fail("AccountCreateTransaction failed, " + e); - } - }); - } - - threadPoolExecutor.shutdown(); - - // Wait for all tasks to finish - try { - if (!threadPoolExecutor.awaitTermination(60, TimeUnit.SECONDS)) { - System.out.println(); - System.out.println("Forcing shutdown"); - threadPoolExecutor.shutdownNow(); - } - } catch (InterruptedException e) { - threadPoolExecutor.shutdownNow(); - } - - long endTime = System.currentTimeMillis(); - long executionTime = endTime - startTime; - System.out.println("All tasks have finished execution in " + executionTime + "ms"); - clientExecutor.shutdownNow(); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/MirrorNodeContractQueryIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/MirrorNodeContractQueryIntegrationTest.java deleted file mode 100644 index 1b24e271cd..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/MirrorNodeContractQueryIntegrationTest.java +++ /dev/null @@ -1,284 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import static com.hedera.hashgraph.sdk.EntityIdHelper.getEvmAddressFromMirrorNodeAsync; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.ContractCallQuery; -import com.hedera.hashgraph.sdk.ContractCreateTransaction; -import com.hedera.hashgraph.sdk.ContractDeleteTransaction; -import com.hedera.hashgraph.sdk.ContractExecuteTransaction; -import com.hedera.hashgraph.sdk.ContractFunctionParameters; -import com.hedera.hashgraph.sdk.ContractId; -import com.hedera.hashgraph.sdk.FileCreateTransaction; -import com.hedera.hashgraph.sdk.FileDeleteTransaction; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.MirrorNodeContractCallQuery; -import com.hedera.hashgraph.sdk.MirrorNodeContractEstimateGasQuery; -import com.hedera.hashgraph.sdk.MirrorNodeContractQuery; -import com.hedera.hashgraph.sdk.PrivateKey; -import java.util.Objects; -import java.util.concurrent.ExecutionException; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -class MirrorNodeContractQueryIntegrationTest { - private static final String SMART_CONTRACT_BYTECODE = "6080604052348015600e575f80fd5b50335f806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506104a38061005b5f395ff3fe608060405260043610610033575f3560e01c8063607a4427146100375780637065cb4814610053578063893d20e81461007b575b5f80fd5b610051600480360381019061004c919061033c565b6100a5565b005b34801561005e575f80fd5b50610079600480360381019061007491906103a2565b610215565b005b348015610086575f80fd5b5061008f6102b7565b60405161009c91906103dc565b60405180910390f35b3373ffffffffffffffffffffffffffffffffffffffff165f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146100fb575f80fd5b805f806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600181908060018154018082558091505060019003905f5260205f20015f9091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f8173ffffffffffffffffffffffffffffffffffffffff166108fc3490811502906040515f60405180830381858888f19350505050905080610211576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102089061044f565b60405180910390fd5b5050565b805f806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600181908060018154018082558091505060019003905f5260205f20015f9091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b5f805f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61030b826102e2565b9050919050565b61031b81610301565b8114610325575f80fd5b50565b5f8135905061033681610312565b92915050565b5f60208284031215610351576103506102de565b5b5f61035e84828501610328565b91505092915050565b5f610371826102e2565b9050919050565b61038181610367565b811461038b575f80fd5b50565b5f8135905061039c81610378565b92915050565b5f602082840312156103b7576103b66102de565b5b5f6103c48482850161038e565b91505092915050565b6103d681610367565b82525050565b5f6020820190506103ef5f8301846103cd565b92915050565b5f82825260208201905092915050565b7f5472616e73666572206661696c656400000000000000000000000000000000005f82015250565b5f610439600f836103f5565b915061044482610405565b602082019050919050565b5f6020820190508181035f8301526104668161042d565b905091905056fea26469706673582212206c46ddb2acdbcc4290e15be83eb90cd0b2ce5bd82b9bfe58a0709c5aec96305564736f6c634300081a0033"; - private static final String ADDRESS = "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"; - - @Test - @DisplayName("Can estimate and simulate transaction") - void canSimulateTransaction() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - response = new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(200000) - .setBytecodeFileId(fileId) - .execute(testEnv.client); - - var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); - - // Wait for mirror node to import data - Thread.sleep(2000); - - var gas = new MirrorNodeContractEstimateGasQuery() - .setContractId(contractId) - .setFunction("getOwner") - .execute(testEnv.client); - - var result = new ContractCallQuery() - .setContractId(contractId) - .setGas(gas) - .setFunction("getOwner") - .setQueryPayment(new Hbar(1)) - .execute(testEnv.client); - - var simulationResult = new MirrorNodeContractCallQuery() - .setContractId(contractId) - .setFunction("getOwner") - .execute(testEnv.client); - - assertThat(result.getAddress(0)).isEqualTo(simulationResult.substring(26)); - - gas = new MirrorNodeContractEstimateGasQuery() - .setContractId(contractId) - .setFunction("addOwner", new ContractFunctionParameters().addAddress(ADDRESS)) - .execute(testEnv.client); - - new ContractExecuteTransaction() - .setContractId(contractId) - .setGas(gas) - .setFunction("addOwner", new ContractFunctionParameters().addAddress(ADDRESS)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new MirrorNodeContractCallQuery() - .setContractId(contractId) - .setFunction("addOwner", new ContractFunctionParameters().addAddress(ADDRESS)) - .execute(testEnv.client); - - new ContractDeleteTransaction() - .setTransferAccountId(testEnv.operatorId) - .setContractId(contractId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new FileDeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - } - } - - @Test - @DisplayName("Fails when contract is not deployed") - void failsWhenContractIsNotDeployed() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - var contractId = new ContractId(1231456); - - assertThatExceptionOfType(ExecutionException.class).isThrownBy(() -> { - new MirrorNodeContractEstimateGasQuery() - .setContractId(contractId) - .setFunction("getOwner") - .execute(testEnv.client); - }).withMessageContaining("Received non-200 response from Mirror Node"); - - assertThatExceptionOfType(ExecutionException.class).isThrownBy(() -> { - new MirrorNodeContractCallQuery() - .setContractId(contractId) - .setFunction("getOwner") - .execute(testEnv.client); - }).withMessageContaining("Received non-200 response from Mirror Node"); - } - } - - @Test - @DisplayName("Fails when gas limit is low") - void failsWhenGasLimitIsLow() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - response = new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(200000) - .setBytecodeFileId(fileId) - .execute(testEnv.client); - - var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); - - // Wait for mirror node to import data - Thread.sleep(2000); - - assertThatExceptionOfType(ExecutionException.class).isThrownBy(() -> { - new MirrorNodeContractEstimateGasQuery() - .setContractId(contractId) - .setGasLimit(100) - .setFunction("addOwnerAndTransfer", new ContractFunctionParameters().addAddress(ADDRESS)) - .execute(testEnv.client); - }).withMessageContaining("Received non-200 response from Mirror Node"); - - assertThatExceptionOfType(ExecutionException.class).isThrownBy(() -> { - new MirrorNodeContractCallQuery() - .setContractId(contractId) - .setGasLimit(100) - .setFunction("addOwnerAndTransfer", new ContractFunctionParameters().addAddress(ADDRESS)) - .execute(testEnv.client); - }).withMessageContaining("Received non-200 response from Mirror Node"); - } - } - - @Test - @DisplayName("Fails when sender is not set") - void failsWhenSenderIsNotSet() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - response = new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(200000) - .setBytecodeFileId(fileId) - .execute(testEnv.client); - - var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); - - // Wait for mirror node to import data - Thread.sleep(2000); - - assertThatExceptionOfType(ExecutionException.class).isThrownBy(() -> { - new MirrorNodeContractEstimateGasQuery() - .setContractId(contractId) - .setFunction("addOwnerAndTransfer", new ContractFunctionParameters().addAddress(ADDRESS)) - .execute(testEnv.client); - }).withMessageContaining("Received non-200 response from Mirror Node"); - - assertThatExceptionOfType(ExecutionException.class).isThrownBy(() -> { - new MirrorNodeContractCallQuery() - .setContractId(contractId) - .setFunction("addOwnerAndTransfer", new ContractFunctionParameters().addAddress(ADDRESS)) - .execute(testEnv.client); - }).withMessageContaining("Received non-200 response from Mirror Node"); - - } - } - - @Test - @DisplayName("Can simulate with sender set") - void canSimulateWithSenderSet() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - var response = new FileCreateTransaction() - .setKeys(testEnv.operatorKey) - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client); - - var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); - - response = new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(200000) - .setBytecodeFileId(fileId) - .execute(testEnv.client); - - var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); - - var receiverAccountId = new AccountCreateTransaction() - .setKey(PrivateKey.generateED25519()) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .accountId; - - // Wait for mirror node to import data - Thread.sleep(2000); - - var receiverEvmAddress = getEvmAddressFromMirrorNodeAsync(testEnv.client, receiverAccountId.num).get() - .toString(); - - var owner = new MirrorNodeContractCallQuery() - .setContractId(contractId) - .setFunction("getOwner") - .execute(testEnv.client) - .substring(26); - - var gas = new MirrorNodeContractEstimateGasQuery() - .setContractId(contractId) - .setGasLimit(1_000_000) - .setFunction("addOwnerAndTransfer", new ContractFunctionParameters().addAddress(receiverEvmAddress)) - .setSenderEvmAddress(owner) - .setValue(123) - .execute(testEnv.client); - - new ContractExecuteTransaction() - .setContractId(contractId) - .setGas(gas) - .setPayableAmount(new Hbar(1)) - .setFunction("addOwnerAndTransfer", new ContractFunctionParameters().addAddress(receiverEvmAddress)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new MirrorNodeContractCallQuery() - .setContractId(contractId) - .setGasLimit(1_000_000) - .setFunction("addOwnerAndTransfer", new ContractFunctionParameters().addAddress(receiverEvmAddress)) - .setSenderEvmAddress(owner) - .setValue(123) - .execute(testEnv.client); - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/NetworkVersionInfoIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/NetworkVersionInfoIntegrationTest.java deleted file mode 100644 index e006fb5f7c..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/NetworkVersionInfoIntegrationTest.java +++ /dev/null @@ -1,37 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.NetworkVersionInfoQuery; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -public class NetworkVersionInfoIntegrationTest { - @Test - @DisplayName("Cannot query network version info") - void cannotQueryNetworkVersionInfo() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - new NetworkVersionInfoQuery() - .execute(testEnv.client); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/NftAllowancesIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/NftAllowancesIntegrationTest.java deleted file mode 100644 index a10dfcd24e..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/NftAllowancesIntegrationTest.java +++ /dev/null @@ -1,412 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -import com.hedera.hashgraph.sdk.AccountAllowanceApproveTransaction; -import com.hedera.hashgraph.sdk.AccountAllowanceDeleteTransaction; -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.NftId; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.sdk.Status; -import com.hedera.hashgraph.sdk.TokenAssociateTransaction; -import com.hedera.hashgraph.sdk.TokenCreateTransaction; -import com.hedera.hashgraph.sdk.TokenId; -import com.hedera.hashgraph.sdk.TokenMintTransaction; -import com.hedera.hashgraph.sdk.TokenNftInfoQuery; -import com.hedera.hashgraph.sdk.TokenType; -import com.hedera.hashgraph.sdk.TransactionId; -import com.hedera.hashgraph.sdk.TransferTransaction; -import java.nio.charset.StandardCharsets; -import java.util.List; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -public class NftAllowancesIntegrationTest { - @Test - @DisplayName("Cannot transfer on behalf of `spender` account without allowance approval") - void cannotTransferWithoutAllowanceApproval() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var spenderKey = PrivateKey.generateED25519(); - var spenderAccountId = new AccountCreateTransaction() - .setKey(spenderKey) - .setInitialBalance(new Hbar(2)) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .accountId; - - var receiverKey = PrivateKey.generateED25519(); - var receiverAccountId = new AccountCreateTransaction() - .setKey(receiverKey) - .setMaxAutomaticTokenAssociations(10) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .accountId; - - TokenId nftTokenId = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .freezeWith(testEnv.client) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId; - - new TokenAssociateTransaction() - .setAccountId(spenderAccountId) - .setTokenIds(List.of(nftTokenId)) - .freezeWith(testEnv.client) - .sign(spenderKey) - .execute(testEnv.client); - - var serials = new TokenMintTransaction() - .setTokenId(nftTokenId) - .addMetadata("asd".getBytes(StandardCharsets.UTF_8)) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .serials; - - var nft1 = new NftId(nftTokenId, serials.get(0)); - - var onBehalfOfTransactionId = TransactionId.generate(spenderAccountId); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> new TransferTransaction() - .addApprovedNftTransfer(nft1, testEnv.operatorId, receiverAccountId) - .setTransactionId(onBehalfOfTransactionId) - .freezeWith(testEnv.client) - .sign(spenderKey) - .execute(testEnv.client) - .getReceipt(testEnv.client)).withMessageContaining(Status.SPENDER_DOES_NOT_HAVE_ALLOWANCE.toString()); - - } - } - - @Test - @DisplayName("Cannot transfer on behalf of `spender` account after removing the allowance approval") - void cannotTransferAfterAllowanceRemove() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var spenderKey = PrivateKey.generateED25519(); - var spenderAccountId = new AccountCreateTransaction() - .setKey(spenderKey) - .setInitialBalance(new Hbar(2)) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .accountId; - - var receiverKey = PrivateKey.generateED25519(); - var receiverAccountId = new AccountCreateTransaction() - .setKey(receiverKey) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .accountId; - - TokenId nftTokenId = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .freezeWith(testEnv.client) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId; - - new TokenAssociateTransaction() - .setAccountId(spenderAccountId) - .setTokenIds(List.of(nftTokenId)) - .freezeWith(testEnv.client) - .sign(spenderKey) - .execute(testEnv.client); - - new TokenAssociateTransaction() - .setAccountId(receiverAccountId) - .setTokenIds(List.of(nftTokenId)) - .freezeWith(testEnv.client) - .sign(receiverKey) - .execute(testEnv.client); - - var serials = new TokenMintTransaction() - .setTokenId(nftTokenId) - .addMetadata("asd1".getBytes(StandardCharsets.UTF_8)) - .addMetadata("asd2".getBytes(StandardCharsets.UTF_8)) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .serials; - - var nft1 = new NftId(nftTokenId, serials.get(0)); - var nft2 = new NftId(nftTokenId, serials.get(1)); - - new AccountAllowanceApproveTransaction() - .approveTokenNftAllowance(nft1, testEnv.operatorId, spenderAccountId) - .approveTokenNftAllowance(nft2, testEnv.operatorId, spenderAccountId) - .execute(testEnv.client); - - new AccountAllowanceDeleteTransaction() - .deleteAllTokenNftAllowances(nft2, testEnv.operatorId) - .execute(testEnv.client); - - var onBehalfOfTransactionId = TransactionId.generate(spenderAccountId); - - new TransferTransaction() - .addApprovedNftTransfer(nft1, testEnv.operatorId, receiverAccountId) - .setTransactionId(onBehalfOfTransactionId) - .freezeWith(testEnv.client) - .sign(spenderKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var info = new TokenNftInfoQuery() - .setNftId(nft1) - .execute(testEnv.client); - assertThat(info.get(0).accountId).isEqualTo(receiverAccountId); - - var onBehalfOfTransactionId2 = TransactionId.generate(spenderAccountId); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> new TransferTransaction() - .addApprovedNftTransfer(nft2, testEnv.operatorId, receiverAccountId) - .setTransactionId(onBehalfOfTransactionId2) - .freezeWith(testEnv.client) - .sign(spenderKey) - .execute(testEnv.client) - .getReceipt(testEnv.client)).withMessageContaining(Status.SPENDER_DOES_NOT_HAVE_ALLOWANCE.toString()); - - } - } - - @Test - @DisplayName("Cannot remove single serial number allowance when the allowance is given for all serials at once") - void cannotRemoveSingleSerialWhenAllowanceIsGivenForAll() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var spenderKey = PrivateKey.generateED25519(); - var spenderAccountId = new AccountCreateTransaction() - .setKey(spenderKey) - .setInitialBalance(new Hbar(2)) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .accountId; - - var receiverKey = PrivateKey.generateED25519(); - var receiverAccountId = new AccountCreateTransaction() - .setKey(receiverKey) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .accountId; - - TokenId nftTokenId = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .freezeWith(testEnv.client) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId; - - new TokenAssociateTransaction() - .setAccountId(spenderAccountId) - .setTokenIds(List.of(nftTokenId)) - .freezeWith(testEnv.client) - .sign(spenderKey) - .execute(testEnv.client); - - new TokenAssociateTransaction() - .setAccountId(receiverAccountId) - .setTokenIds(List.of(nftTokenId)) - .freezeWith(testEnv.client) - .sign(receiverKey) - .execute(testEnv.client); - - var serials = new TokenMintTransaction() - .setTokenId(nftTokenId) - .addMetadata("asd1".getBytes(StandardCharsets.UTF_8)) - .addMetadata("asd2".getBytes(StandardCharsets.UTF_8)) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .serials; - - var nft1 = new NftId(nftTokenId, serials.get(0)); - var nft2 = new NftId(nftTokenId, serials.get(1)); - - new AccountAllowanceApproveTransaction() - .approveTokenNftAllowanceAllSerials(nftTokenId, testEnv.operatorId, spenderAccountId) - .execute(testEnv.client); - - var onBehalfOfTransactionId = TransactionId.generate(spenderAccountId); - - new TransferTransaction() - .addApprovedNftTransfer(nft1, testEnv.operatorId, receiverAccountId) - .setTransactionId(onBehalfOfTransactionId) - .freezeWith(testEnv.client) - .sign(spenderKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // hopefully in the future this should end up with a precheck error provided from services - new AccountAllowanceDeleteTransaction() - .deleteAllTokenNftAllowances(nft2, testEnv.operatorId) - .execute(testEnv.client); - - var onBehalfOfTransactionId2 = TransactionId.generate(spenderAccountId); - - new TransferTransaction() - .addApprovedNftTransfer(nft2, testEnv.operatorId, receiverAccountId) - .setTransactionId(onBehalfOfTransactionId2) - .freezeWith(testEnv.client) - .sign(spenderKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var infoNft1 = new TokenNftInfoQuery() - .setNftId(nft1) - .execute(testEnv.client); - - var infoNft2 = new TokenNftInfoQuery() - .setNftId(nft2) - .execute(testEnv.client); - - assertThat(infoNft1.get(0).accountId).isEqualTo(receiverAccountId); - assertThat(infoNft2.get(0).accountId).isEqualTo(receiverAccountId); - } - } - - @Test - @DisplayName("Account, which given the allowance for all serials at once, should be able to give allowances for single serial numbers to other accounts") - void accountGivenAllowanceForAllShouldBeAbleToGiveAllowanceForSingle() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var delegatingSpenderKey = PrivateKey.generateED25519(); - var delegatingSpenderAccountId = new AccountCreateTransaction() - .setKey(delegatingSpenderKey) - .setInitialBalance(new Hbar(2)) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .accountId; - - var spenderKey = PrivateKey.generateED25519(); - var spenderAccountId = new AccountCreateTransaction() - .setKey(spenderKey) - .setInitialBalance(new Hbar(2)) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .accountId; - - var receiverKey = PrivateKey.generateED25519(); - var receiverAccountId = new AccountCreateTransaction() - .setKey(receiverKey) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .accountId; - - TokenId nftTokenId = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .freezeWith(testEnv.client) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId; - - new TokenAssociateTransaction() - .setAccountId(delegatingSpenderAccountId) - .setTokenIds(List.of(nftTokenId)) - .freezeWith(testEnv.client) - .sign(spenderKey) - .execute(testEnv.client); - - new TokenAssociateTransaction() - .setAccountId(receiverAccountId) - .setTokenIds(List.of(nftTokenId)) - .freezeWith(testEnv.client) - .sign(receiverKey) - .execute(testEnv.client); - - var serials = new TokenMintTransaction() - .setTokenId(nftTokenId) - .addMetadata("asd1".getBytes(StandardCharsets.UTF_8)) - .addMetadata("asd2".getBytes(StandardCharsets.UTF_8)) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .serials; - - var nft1 = new NftId(nftTokenId, serials.get(0)); - var nft2 = new NftId(nftTokenId, serials.get(1)); - - new AccountAllowanceApproveTransaction() - .approveTokenNftAllowanceAllSerials(nftTokenId, testEnv.operatorId, delegatingSpenderAccountId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new AccountAllowanceApproveTransaction() - .approveTokenNftAllowance(nft1, testEnv.operatorId, spenderAccountId, delegatingSpenderAccountId) - .freezeWith(testEnv.client) - .sign(delegatingSpenderKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var onBehalfOfTransactionId = TransactionId.generate(spenderAccountId); - - new TransferTransaction() - .addApprovedNftTransfer(nft1, testEnv.operatorId, receiverAccountId) - .setTransactionId(onBehalfOfTransactionId) - .freezeWith(testEnv.client) - .sign(spenderKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var onBehalfOfTransactionId2 = TransactionId.generate(spenderAccountId); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> new TransferTransaction() - .addApprovedNftTransfer(nft2, testEnv.operatorId, receiverAccountId) - .setTransactionId(onBehalfOfTransactionId2) - .freezeWith(testEnv.client) - .sign(spenderKey) - .execute(testEnv.client) - .getReceipt(testEnv.client)).withMessageContaining(Status.SPENDER_DOES_NOT_HAVE_ALLOWANCE.toString()); - - var infoNft1 = new TokenNftInfoQuery() - .setNftId(nft1) - .execute(testEnv.client); - - var infoNft2 = new TokenNftInfoQuery() - .setNftId(nft2) - .execute(testEnv.client); - - assertThat(infoNft1.get(0).accountId).isEqualTo(receiverAccountId); - assertThat(infoNft2.get(0).accountId).isEqualTo(testEnv.operatorId); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/NftMetadataGenerator.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/NftMetadataGenerator.java deleted file mode 100644 index 2c731905cf..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/NftMetadataGenerator.java +++ /dev/null @@ -1,50 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * Copyright (C) 2021 - 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.hashgraph.sdk.test.integration; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -public class NftMetadataGenerator { - private NftMetadataGenerator() { - } - - public static List generate(byte metadataCount) { - List metadatas = new ArrayList<>(); - for (byte i = 0; i < metadataCount; i++) { - byte[] md = {i}; - metadatas.add(md); - } - return metadatas; - } - - public static List generate(byte[] metadata, int count) { - return IntStream.range(0, count) - .mapToObj(i -> metadata.clone()) - .collect(Collectors.toList()); - } - - public static List generateOneLarge() { - return Collections.singletonList(new byte[101]); - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/NodeCreateTransactionIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/NodeCreateTransactionIntegrationTest.java deleted file mode 100644 index bda6cb10ba..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/NodeCreateTransactionIntegrationTest.java +++ /dev/null @@ -1,86 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import static com.hedera.hashgraph.sdk.test.integration.IntegrationTestEnv.LOCAL_CONSENSUS_NODE_ACCOUNT_ID; - -import com.hedera.hashgraph.sdk.AccountId; -import com.hedera.hashgraph.sdk.Client; -import com.hedera.hashgraph.sdk.Endpoint; -import com.hedera.hashgraph.sdk.NodeCreateTransaction; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.PrivateKeyECDSA; -import java.util.HashMap; -import java.util.List; -import org.bouncycastle.util.encoders.Hex; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -class NodeCreateTransactionIntegrationTest { - String validGossipCertDER = "3082052830820310a003020102020101300d06092a864886f70d01010c05003010310e300c060355040313056e6f6465333024170d3234313030383134333233395a181332313234313030383134333233392e3337395a3010310e300c060355040313056e6f64653330820222300d06092a864886f70d01010105000382020f003082020a0282020100af111cff0c4ad8125d2f4b8691ce87332fecc867f7a94ddc0f3f96514cc4224d44af516394f7384c1ef0a515d29aa6116b65bc7e4d7e2d848cf79fbfffedae3a6583b3957a438bdd780c4981b800676ea509bc8c619ae04093b5fc642c4484152f0e8bcaabf19eae025b630028d183a2f47caf6d9f1075efb30a4248679d871beef1b7e9115382270cbdb68682fae4b1fd592cadb414d918c0a8c23795c7c5a91e22b3e90c410825a2bc1a840efc5bf9976a7f474c7ed7dc047e4ddd2db631b68bb4475f173baa3edc234c4bed79c83e2f826f79e07d0aade2d984da447a8514135bfa4145274a7f62959a23c4f0fae5adc6855974e7c04164951d052beb5d45cb1f3cdfd005da894dea9151cb62ba43f4731c6bb0c83e10fd842763ba6844ef499f71bc67fa13e4917fb39f2ad18112170d31cdcb3c61c9e3253accf703dbd8427fdcb87ece78b787b6cfdc091e8fedea8ad95dc64074e1fc6d0e42ea2337e18a5e54e4aaab3791a98dfcef282e2ae1caec9cf986fabe8f36e6a21c8711647177e492d264415e765a86c58599cd97b103cb4f6a01d2edd06e3b60470cf64daca7aecf831197b466cae04baeeac19840a05394bef628aed04b611cfa13677724b08ddfd662b02fd0ef0af17eb7f4fb8c1c17fbe9324f6dc7bcc02449622636cc45ec04909b3120ab4df4726b21bf79e955fe8f832699d2196dcd7a58bfeafb170203010001a38186308183300f0603551d130101ff04053003020100300e0603551d0f0101ff0404030204b030200603551d250101ff0416301406082b0601050507030106082b06010505070302301d0603551d0e04160414643118e05209035edd83d44a0c368de2fb2fe4c0301f0603551d23041830168014643118e05209035edd83d44a0c368de2fb2fe4c0300d06092a864886f70d01010c05000382020100ad41c32bb52650eb4b76fce439c9404e84e4538a94916b3dc7983e8b5c58890556e7384601ca7440dde68233bb07b97bf879b64487b447df510897d2a0a4e789c409a9b237a6ad240ad5464f2ce80c58ddc4d07a29a74eb25e1223db6c00e334d7a27d32bfa6183a82f5e35bccf497c2445a526eabb0c068aba9b94cc092ea4756b0dcfb574f6179f0089e52b174ccdbd04123eeb6d70daeabd8513fcba6be0bc2b45ca9a69802dae11cc4d9ff6053b3a87fd8b0c6bf72fffc3b81167f73cca2b3fd656c5d353c8defca8a76e2ad535f984870a590af4e28fed5c5a125bf360747c5e7742e7813d1bd39b5498c8eb6ba72f267eda034314fdbc596f6b967a0ef8be5231d364e634444c84e64bd7919425171016fcd9bb05f01c58a303dee28241f6e860fc3aac3d92aad7dac2801ce79a3b41a0e1f1509fc0d86e96d94edb18616c000152490f64561713102128990fedd3a5fa642f2ff22dc11bc4dc5b209986a0c3e4eb2bdfdd40e9fdf246f702441cac058dd8d0d51eb0796e2bea2ce1b37b2a2f468505e1f8980a9f66d719df034a6fbbd2f9585991d259678fb9a4aebdc465d22c240351ed44abffbdd11b79a706fdf7c40158d3da87f68d7bd557191a8016b5b899c07bf1b87590feb4fa4203feea9a2a7a73ec224813a12b7a21e5dc93fcde4f0a7620f570d31fe27e9b8d65b74db7dc18a5e51adc42d7805d4661938"; - - @Test - @Disabled("The test has to be disabled so it doesn't fail calls to local-node") - @DisplayName("Can create new network node") - void canCreateNewNetworkNode() throws Exception { - // Set the network - var network = new HashMap(); - network.put("localhost:50211", LOCAL_CONSENSUS_NODE_ACCOUNT_ID); - - try (var client = Client - .forNetwork(network) - .setMirrorNetwork(List.of("localhost:5600"))) { - - // Set the operator to be account 0.0.2 - var originalOperatorKey = PrivateKey.fromString( - "302e020100300506032b65700422042091132178e72057a1d7528025956fe39b0b847f200ab59b2fdd367017f3087137"); - client.setOperator(new AccountId(2), originalOperatorKey); - - // The account of the new node - var accountID = AccountId.fromString("0.0.4"); - - // Node description - String description = "test"; - - // Endpoint address can be any IPV4 address - var endpoint = new Endpoint().setDomainName("tests.com").setPort(1234); - var endpoint2 = new Endpoint().setDomainName("test.com").setPort(123); - - // Convert hex string to byte array - var validGossipCert = Hex.decode(validGossipCertDER.getBytes()); - - // Generate admin key - var adminKey = PrivateKeyECDSA.generateED25519(); - - new NodeCreateTransaction() - .setAccountId(accountID) - .setAdminKey(adminKey) - .setDescription(description) - .setGossipCaCertificate(validGossipCert) - .setGossipEndpoints(List.of(endpoint, endpoint2)) - .setServiceEndpoints(List.of(endpoint, endpoint2)) - .freezeWith(client) - .sign(adminKey) - .execute(client) - .getReceipt(client); - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ReceiptQueryIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ReceiptQueryIntegrationTest.java deleted file mode 100644 index e2e1601101..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ReceiptQueryIntegrationTest.java +++ /dev/null @@ -1,174 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * Copyright (C) 2021 - 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.TransactionReceiptQuery; -import com.hedera.hashgraph.sdk.TransactionRecordQuery; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -public class ReceiptQueryIntegrationTest { - @Test - @DisplayName("Can get Receipt") - void canGetTransactionReceipt() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .execute(testEnv.client); - - var receipt = new TransactionReceiptQuery() - .setTransactionId(response.transactionId) - .execute(testEnv.client); - - } - } - - @Test - @DisplayName("Can get Record") - void canGetTransactionRecord() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .execute(testEnv.client); - - new TransactionReceiptQuery() - .setTransactionId(response.transactionId) - .execute(testEnv.client); - - new TransactionRecordQuery() - .setTransactionId(response.transactionId) - .execute(testEnv.client); - - } - } - - @Test - @DisplayName("Can get Record cost") - @SuppressWarnings("UnusedVariable") - void getCostTransactionRecord() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .execute(testEnv.client); - - new TransactionReceiptQuery() - .setTransactionId(response.transactionId) - .execute(testEnv.client); - - var recordQuery = new TransactionRecordQuery() - .setTransactionId(response.transactionId); - - recordQuery.getCost(testEnv.client); - recordQuery.execute(testEnv.client); - - } - } - - @Test - @DisplayName("Can get Record cost with big max set") - @SuppressWarnings("UnusedVariable") - void getCostBigMaxTransactionRecord() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .execute(testEnv.client); - - new TransactionReceiptQuery() - .setTransactionId(response.transactionId) - .execute(testEnv.client); - - var recordQuery = new TransactionRecordQuery() - .setTransactionId(response.transactionId) - .setMaxQueryPayment(new Hbar(1000)); - - recordQuery.getCost(testEnv.client); - - recordQuery.execute(testEnv.client); - - } - } - - @Test - @DisplayName("Error at very small max, getRecord") - void getCostSmallMaxTransactionRecord() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .execute(testEnv.client); - - var receipt = new TransactionReceiptQuery() - .setTransactionId(response.transactionId) - .execute(testEnv.client); - - var recordQuery = new TransactionRecordQuery() - .setTransactionId(response.transactionId) - .setMaxQueryPayment(Hbar.fromTinybars(1)); - - var cost = recordQuery.getCost(testEnv.client); - - assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> { - recordQuery.execute(testEnv.client); - }).withMessage("cost for TransactionRecordQuery, of " + cost.toString() + ", without explicit payment is greater than the maximum allowed payment of 1 tℏ"); - - } - } - - @Test - @DisplayName("Insufficient transaction fee error for transaction record query") - void getCostInsufficientTxFeeTransactionRecord() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .execute(testEnv.client); - - var receipt = new TransactionReceiptQuery() - .setTransactionId(response.transactionId) - .execute(testEnv.client); - - var recordQuery = new TransactionRecordQuery() - .setTransactionId(response.transactionId); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - recordQuery.setQueryPayment(Hbar.fromTinybars(1)).execute(testEnv.client); - }).satisfies(error -> assertThat(error.status.toString()).isEqualTo("INSUFFICIENT_TX_FEE")); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ScheduleCreateIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ScheduleCreateIntegrationTest.java deleted file mode 100644 index 2f6e483d19..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/ScheduleCreateIntegrationTest.java +++ /dev/null @@ -1,855 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * Copyright (C) 2021 - 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.hashgraph.sdk.test.integration; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -import com.hedera.hashgraph.sdk.AccountBalanceQuery; -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.AccountDeleteTransaction; -import com.hedera.hashgraph.sdk.AccountId; -import com.hedera.hashgraph.sdk.AccountUpdateTransaction; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.KeyList; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.sdk.ScheduleCreateTransaction; -import com.hedera.hashgraph.sdk.ScheduleId; -import com.hedera.hashgraph.sdk.ScheduleInfo; -import com.hedera.hashgraph.sdk.ScheduleInfoQuery; -import com.hedera.hashgraph.sdk.ScheduleSignTransaction; -import com.hedera.hashgraph.sdk.Status; -import com.hedera.hashgraph.sdk.TokenAssociateTransaction; -import com.hedera.hashgraph.sdk.TokenCreateTransaction; -import com.hedera.hashgraph.sdk.TopicCreateTransaction; -import com.hedera.hashgraph.sdk.TopicMessageSubmitTransaction; -import com.hedera.hashgraph.sdk.TransactionId; -import com.hedera.hashgraph.sdk.TransactionReceipt; -import com.hedera.hashgraph.sdk.TransactionResponse; -import com.hedera.hashgraph.sdk.TransferTransaction; -import java.nio.charset.StandardCharsets; -import java.time.Duration; -import java.time.Instant; -import java.util.Collections; -import java.util.Objects; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -class ScheduleCreateIntegrationTest { - - private final int oneDayInSecs = 86400; - - @Test - @Disabled - @DisplayName("Can create schedule") - void canCreateSchedule() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var key = PrivateKey.generateED25519(); - - var transaction = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(10)); - - var response = new ScheduleCreateTransaction() - .setScheduledTransaction(transaction) - .setAdminKey(testEnv.operatorKey) - .setPayerAccountId(testEnv.operatorId) - .execute(testEnv.client); - - var scheduleId = Objects.requireNonNull(response.getReceipt(testEnv.client).scheduleId); - - var info = new ScheduleInfoQuery() - .setScheduleId(scheduleId) - .execute(testEnv.client); - - assertThat(info.executedAt).isNotNull(); - - } - } - - @Test - @Disabled - @DisplayName("Can get Transaction") - void canGetTransactionSchedule() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var key = PrivateKey.generateED25519(); - - var transaction = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(10)); - - var response = new ScheduleCreateTransaction() - .setScheduledTransaction(transaction) - .setAdminKey(testEnv.operatorKey) - .setPayerAccountId(testEnv.operatorId) - .execute(testEnv.client); - - var scheduleId = Objects.requireNonNull(response.getReceipt(testEnv.client).scheduleId); - - var info = new ScheduleInfoQuery() - .setScheduleId(scheduleId) - .execute(testEnv.client); - - assertThat(info.executedAt).isNotNull(); - assertThat(info.getScheduledTransaction()).isNotNull(); - - } - } - - @Test - @Disabled - @DisplayName("Can create schedule with schedule()") - void canCreateWithSchedule() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var key = PrivateKey.generateED25519(); - - var transaction = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(10)); - - var tx = transaction.schedule(); - - var response = tx - .setAdminKey(testEnv.operatorKey) - .setPayerAccountId(testEnv.operatorId) - .execute(testEnv.client); - - var scheduleId = Objects.requireNonNull(response.getReceipt(testEnv.client).scheduleId); - - var info = new ScheduleInfoQuery() - .setScheduleId(scheduleId) - .execute(testEnv.client); - - assertThat(info.executedAt).isNotNull(); - assertThat(info.getScheduledTransaction()).isNotNull(); - - } - } - - @Test - @DisplayName("Can sign schedule") - void canSignSchedule2() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - PrivateKey key1 = PrivateKey.generateED25519(); - PrivateKey key2 = PrivateKey.generateED25519(); - PrivateKey key3 = PrivateKey.generateED25519(); - - KeyList keyList = new KeyList(); - - keyList.add(key1.getPublicKey()); - keyList.add(key2.getPublicKey()); - keyList.add(key3.getPublicKey()); - - // Creat the account with the `KeyList` - TransactionResponse response = new AccountCreateTransaction() - .setKey(keyList) - .setInitialBalance(new Hbar(10)) - .execute(testEnv.client); - - // This will wait for the receipt to become available - TransactionReceipt receipt = response.getReceipt(testEnv.client); - - AccountId accountId = Objects.requireNonNull(receipt.accountId); - - // Generate a `TransactionId`. This id is used to query the inner scheduled transaction - // after we expect it to have been executed - TransactionId transactionId = TransactionId.generate(testEnv.operatorId); - - // Create a transfer transaction with 2/3 signatures. - TransferTransaction transfer = new TransferTransaction() - .setTransactionId(transactionId) - .addHbarTransfer(accountId, new Hbar(1).negated()) - .addHbarTransfer(testEnv.operatorId, new Hbar(1)); - - // Schedule the transaction - ScheduleCreateTransaction scheduled = transfer.schedule(); - - receipt = scheduled.execute(testEnv.client).getReceipt(testEnv.client); - - // Get the schedule ID from the receipt - ScheduleId scheduleId = Objects.requireNonNull(receipt.scheduleId); - - // Get the schedule info to see if `signatories` is populated with 2/3 signatures - ScheduleInfo info = new ScheduleInfoQuery() - .setScheduleId(scheduleId) - .execute(testEnv.client); - - assertThat(info.executedAt).isNull(); - - // Finally send this last signature to Hedera. This last signature _should_ mean the transaction executes - // since all 3 signatures have been provided. - ScheduleSignTransaction signTransaction = new ScheduleSignTransaction() - .setScheduleId(scheduleId) - .freezeWith(testEnv.client); - - signTransaction.sign(key1).sign(key2).sign(key3).execute(testEnv.client).getReceipt(testEnv.client); - - info = new ScheduleInfoQuery() - .setScheduleId(scheduleId) - .execute(testEnv.client); - - assertThat(info.executedAt).isNotNull(); - - assertThat(scheduleId.getChecksum()).isNull(); - assertThat(scheduleId.hashCode()).isNotZero(); - assertThat(scheduleId.compareTo(ScheduleId.fromBytes(scheduleId.toBytes()))).isZero(); - - new AccountDeleteTransaction() - .setAccountId(accountId) - .setTransferAccountId(testEnv.operatorId) - .freezeWith(testEnv.client) - .sign(key1).sign(key2).sign(key3) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can schedule token transfer") - void canScheduleTokenTransfer() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - PrivateKey key = PrivateKey.generateED25519(); - - var accountId = new AccountCreateTransaction() - .setReceiverSignatureRequired(true) - .setKey(key) - .setInitialBalance(new Hbar(10)) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .accountId; - - Objects.requireNonNull(accountId); - - var tokenId = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setInitialSupply(100) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId; - - Objects.requireNonNull(tokenId); - - new TokenAssociateTransaction() - .setAccountId(accountId) - .setTokenIds(Collections.singletonList(tokenId)) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var scheduleId = new TransferTransaction() - .addTokenTransfer(tokenId, testEnv.operatorId, -10) - .addTokenTransfer(tokenId, accountId, 10) - .schedule() - .execute(testEnv.client) - .getReceipt(testEnv.client) - .scheduleId; - - Objects.requireNonNull(scheduleId); - - var balanceQuery1 = new AccountBalanceQuery() - .setAccountId(accountId) - .execute(testEnv.client); - - assertThat(balanceQuery1.tokens.get(tokenId)).isEqualTo(0); - - new ScheduleSignTransaction() - .setScheduleId(scheduleId) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var balanceQuery2 = new AccountBalanceQuery() - .setAccountId(accountId) - .execute(testEnv.client); - - assertThat(balanceQuery2.tokens.get(tokenId)).isEqualTo(10); - - } - } - - @Test - @DisplayName("Cannot schedule two identical transactions") - void cannotScheduleTwoTransactions() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var key = PrivateKey.generateED25519(); - var accountId = new AccountCreateTransaction() - .setInitialBalance(new Hbar(10)) - .setKey(key) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .accountId; - - var transferTx = new TransferTransaction() - .addHbarTransfer(testEnv.operatorId, new Hbar(-10)) - .addHbarTransfer(accountId, new Hbar(10)); - - var scheduleId1 = transferTx.schedule() - .execute(testEnv.client) - .getReceipt(testEnv.client) - .scheduleId; - - var info1 = new ScheduleInfoQuery() - .setScheduleId(scheduleId1) - .execute(testEnv.client); - - assertThat(info1.executedAt).isNotNull(); - - var transferTxFromInfo = info1.getScheduledTransaction(); - - var scheduleCreateTx1 = transferTx.schedule(); - var scheduleCreateTx2 = transferTxFromInfo.schedule(); - - assertThat(scheduleCreateTx2.toString()).isEqualTo(scheduleCreateTx1.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - transferTxFromInfo.schedule() - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining("IDENTICAL_SCHEDULE_ALREADY_CREATED"); - - } - } - - @Test - @DisplayName("Can schedule topic message") - void canScheduleTopicMessage() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - // Generate 3 random keys - var key1 = PrivateKey.generateED25519(); - - // This is the submit key - var key2 = PrivateKey.generateED25519(); - - var key3 = PrivateKey.generateED25519(); - - var keyList = new KeyList(); - - keyList.add(key1.getPublicKey()); - keyList.add(key2.getPublicKey()); - keyList.add(key3.getPublicKey()); - - var response = new AccountCreateTransaction() - .setInitialBalance(new Hbar(100)) - .setKey(keyList) - .execute(testEnv.client); - - assertThat(response.getReceipt(testEnv.client).accountId).isNotNull(); - - var topicId = Objects.requireNonNull(new TopicCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setAutoRenewAccountId(testEnv.operatorId) - .setTopicMemo("HCS Topic_") - .setSubmitKey(key2.getPublicKey()) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .topicId - ); - - var transaction = new TopicMessageSubmitTransaction() - .setTopicId(topicId) - .setMessage("scheduled hcs message".getBytes(StandardCharsets.UTF_8)); - - // create schedule - var scheduledTx = transaction.schedule() - .setAdminKey(testEnv.operatorKey) - .setPayerAccountId(testEnv.operatorId) - .setScheduleMemo("mirror scheduled E2E signature on create and sign_" + Instant.now()); - - var scheduled = scheduledTx.freezeWith(testEnv.client); - - var scheduleId = Objects.requireNonNull(scheduled - .execute(testEnv.client) - .getReceipt(testEnv.client).scheduleId); - - // verify schedule has been created and has 1 of 2 signatures - var info = new ScheduleInfoQuery() - .setScheduleId(scheduleId) - .execute(testEnv.client); - - assertThat(info).isNotNull(); - assertThat(info.scheduleId).isEqualTo(scheduleId); - - var infoTransaction = (TopicMessageSubmitTransaction) info.getScheduledTransaction(); - - assertThat(transaction.getTopicId()).isEqualTo(infoTransaction.getTopicId()); - assertThat(transaction.getNodeAccountIds()).isEqualTo(infoTransaction.getNodeAccountIds()); - - var scheduleSign = new ScheduleSignTransaction() - .setScheduleId(scheduleId) - .freezeWith(testEnv.client); - - scheduleSign - .sign(key2) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - info = new ScheduleInfoQuery() - .setScheduleId(scheduleId) - .execute(testEnv.client); - - assertThat(info.executedAt).isNotNull(); - - } - } - - @Test - @DisplayName("Can sign schedule") - void canSignSchedule() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - PrivateKey key = PrivateKey.generateED25519(); - - var accountId = new AccountCreateTransaction() - .setKey(key.getPublicKey()) - .setInitialBalance(new Hbar(10)) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .accountId; - - // Create the transaction - TransferTransaction transfer = new TransferTransaction() - .addHbarTransfer(accountId, new Hbar(1).negated()) - .addHbarTransfer(testEnv.operatorId, new Hbar(1)); - - // Schedule the transaction - var scheduleId = transfer - .schedule() - .setExpirationTime(Instant.now().plusSeconds(oneDayInSecs)) - .setScheduleMemo("HIP-423 Integration Test") - .execute(testEnv.client) - .getReceipt(testEnv.client) - .scheduleId; - - ScheduleInfo info = new ScheduleInfoQuery() - .setScheduleId(scheduleId) - .execute(testEnv.client); - - // Verify the transaction is not yet executed - assertThat(info.executedAt).isNull(); - - // Schedule sign - new ScheduleSignTransaction() - .setScheduleId(scheduleId) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - info = new ScheduleInfoQuery() - .setScheduleId(scheduleId) - .execute(testEnv.client); - - // Verify the transaction is executed - assertThat(info.executedAt).isNotNull(); - - assertThat(scheduleId.getChecksum()).isNull(); - assertThat(scheduleId.hashCode()).isNotZero(); - assertThat(scheduleId.compareTo(ScheduleId.fromBytes(scheduleId.toBytes()))).isZero(); - } - } - - @Test - @DisplayName("Cannot schedule one year into the future") - void cannotScheduleTransactionOneYearIntoTheFuture() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - PrivateKey key = PrivateKey.generateED25519(); - - var accountId = new AccountCreateTransaction() - .setKey(key.getPublicKey()) - .setInitialBalance(new Hbar(10)) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .accountId; - - // Create the transaction - TransferTransaction transfer = new TransferTransaction() - .addHbarTransfer(accountId, new Hbar(1).negated()) - .addHbarTransfer(testEnv.operatorId, new Hbar(1)); - - // Schedule the transaction - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> - transfer - .schedule() - .setExpirationTime(Instant.now().plus(Duration.ofDays(365))) - .setScheduleMemo("HIP-423 Integration Test") - .execute(testEnv.client) - .getReceipt(testEnv.client)) - .withMessageContaining(Status.SCHEDULE_EXPIRATION_TIME_TOO_FAR_IN_FUTURE.toString()); - } - } - - @Test - @DisplayName("Cannot schedule in the past") - void cannotScheduleTransactionInThePast() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - PrivateKey key = PrivateKey.generateED25519(); - - var accountId = new AccountCreateTransaction() - .setKey(key.getPublicKey()) - .setInitialBalance(new Hbar(10)) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .accountId; - - // Create the transaction - TransferTransaction transfer = new TransferTransaction() - .addHbarTransfer(accountId, new Hbar(1).negated()) - .addHbarTransfer(testEnv.operatorId, new Hbar(1)); - - // Schedule the transaction - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> - transfer - .schedule() - .setExpirationTime(Instant.now().minusSeconds(10)) - .setScheduleMemo("HIP-423 Integration Test") - .execute(testEnv.client) - .getReceipt(testEnv.client)) - .withMessageContaining(Status.SCHEDULE_EXPIRATION_TIME_MUST_BE_HIGHER_THAN_CONSENSUS_TIME.toString()); - } - } - - @Test - @DisplayName("Can sign schedule and wait for expiry") - void canSignScheduleAndWaitForExpiry() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - PrivateKey key = PrivateKey.generateED25519(); - - var accountId = new AccountCreateTransaction() - .setKey(key.getPublicKey()) - .setInitialBalance(new Hbar(10)) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .accountId; - - // Create the transaction - TransferTransaction transfer = new TransferTransaction() - .addHbarTransfer(accountId, new Hbar(1).negated()) - .addHbarTransfer(testEnv.operatorId, new Hbar(1)); - - // Schedule the transaction - var scheduleId = transfer - .schedule() - .setExpirationTime(Instant.now().plusSeconds(oneDayInSecs)) - .setWaitForExpiry(true) - .setScheduleMemo("HIP-423 Integration Test") - .execute(testEnv.client) - .getReceipt(testEnv.client) - .scheduleId; - - ScheduleInfo info = new ScheduleInfoQuery() - .setScheduleId(scheduleId) - .execute(testEnv.client); - - // Verify the transaction is not yet executed - assertThat(info.executedAt).isNull(); - - // Schedule sign - new ScheduleSignTransaction() - .setScheduleId(scheduleId) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - info = new ScheduleInfoQuery() - .setScheduleId(scheduleId) - .execute(testEnv.client); - - // Verify the transaction is still not executed - assertThat(info.executedAt).isNull(); - - assertThat(scheduleId.getChecksum()).isNull(); - assertThat(scheduleId.hashCode()).isNotZero(); - assertThat(scheduleId.compareTo(ScheduleId.fromBytes(scheduleId.toBytes()))).isZero(); - } - } - - @Test - @DisplayName("Can sign with multisig and update signing requirements") - void canSignWithMultiSigAndUpdateSigningRequirements() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - PrivateKey key1 = PrivateKey.generateED25519(); - PrivateKey key2 = PrivateKey.generateED25519(); - PrivateKey key3 = PrivateKey.generateED25519(); - PrivateKey key4 = PrivateKey.generateED25519(); - - KeyList keyList = KeyList.withThreshold(2); - - keyList.add(key1.getPublicKey()); - keyList.add(key2.getPublicKey()); - keyList.add(key3.getPublicKey()); - - var accountId = new AccountCreateTransaction() - .setKey(keyList) - .setInitialBalance(new Hbar(10)) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .accountId; - - // Create the transaction - TransferTransaction transfer = new TransferTransaction() - .addHbarTransfer(accountId, new Hbar(1).negated()) - .addHbarTransfer(testEnv.operatorId, new Hbar(1)); - - // Schedule the transaction - var scheduleId = transfer - .schedule() - .setExpirationTime(Instant.now().plusSeconds(oneDayInSecs)) - .setScheduleMemo("HIP-423 Integration Test") - .execute(testEnv.client) - .getReceipt(testEnv.client) - .scheduleId; - - ScheduleInfo info = new ScheduleInfoQuery() - .setScheduleId(scheduleId) - .execute(testEnv.client); - - // Verify the transaction is not executed - assertThat(info.executedAt).isNull(); - - // Sign with one key - new ScheduleSignTransaction() - .setScheduleId(scheduleId) - .freezeWith(testEnv.client) - .sign(key1) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - info = new ScheduleInfoQuery() - .setScheduleId(scheduleId) - .execute(testEnv.client); - - // Verify the transaction is still not executed - assertThat(info.executedAt).isNull(); - - // Update the signing requirements - new AccountUpdateTransaction() - .setAccountId(accountId) - .setKey(key4.getPublicKey()) - .freezeWith(testEnv.client) - .sign(key1) - .sign(key2) - .sign(key4) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - info = new ScheduleInfoQuery() - .setScheduleId(scheduleId) - .execute(testEnv.client); - - // Verify the transaction is still not executed - assertThat(info.executedAt).isNull(); - - // Sign with the updated key - new ScheduleSignTransaction() - .setScheduleId(scheduleId) - .freezeWith(testEnv.client) - .sign(key4) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - info = new ScheduleInfoQuery() - .setScheduleId(scheduleId) - .execute(testEnv.client); - - // Verify the transaction is executed - assertThat(info.executedAt).isNotNull(); - } - } - - @Test - @DisplayName("Can sign with multisig") - void canSignWithMultiSig() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - PrivateKey key1 = PrivateKey.generateED25519(); - PrivateKey key2 = PrivateKey.generateED25519(); - PrivateKey key3 = PrivateKey.generateED25519(); - - KeyList keyList = KeyList.withThreshold(2); - - keyList.add(key1.getPublicKey()); - keyList.add(key2.getPublicKey()); - keyList.add(key3.getPublicKey()); - - var accountId = new AccountCreateTransaction() - .setKey(keyList) - .setInitialBalance(new Hbar(10)) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .accountId; - - // Create the transaction - TransferTransaction transfer = new TransferTransaction() - .addHbarTransfer(accountId, new Hbar(1).negated()) - .addHbarTransfer(testEnv.operatorId, new Hbar(1)); - - // Schedule the transaction - var scheduleId = transfer - .schedule() - .setExpirationTime(Instant.now().plusSeconds(oneDayInSecs)) - .setScheduleMemo("HIP-423 Integration Test") - .execute(testEnv.client) - .getReceipt(testEnv.client) - .scheduleId; - - ScheduleInfo info = new ScheduleInfoQuery() - .setScheduleId(scheduleId) - .execute(testEnv.client); - - // Verify the transaction is not executed - assertThat(info.executedAt).isNull(); - - // Sign with one key - new ScheduleSignTransaction() - .setScheduleId(scheduleId) - .freezeWith(testEnv.client) - .sign(key1) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - info = new ScheduleInfoQuery() - .setScheduleId(scheduleId) - .execute(testEnv.client); - - // Verify the transaction is still not executed - assertThat(info.executedAt).isNull(); - - // Update the signing requirements - new AccountUpdateTransaction() - .setAccountId(accountId) - .setKey(key1.getPublicKey()) - .freezeWith(testEnv.client) - .sign(key1) - .sign(key2) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - info = new ScheduleInfoQuery() - .setScheduleId(scheduleId) - .execute(testEnv.client); - - // Verify the transaction is still not executed - assertThat(info.executedAt).isNull(); - - // Sign with one more key - new ScheduleSignTransaction() - .setScheduleId(scheduleId) - .freezeWith(testEnv.client) - .sign(key2) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - info = new ScheduleInfoQuery() - .setScheduleId(scheduleId) - .execute(testEnv.client); - - // Verify the transaction is executed - assertThat(info.executedAt).isNotNull(); - } - } - - @Test - @DisplayName("Can execute with short expiration time") - void canExecuteWithShortExpirationTime() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - PrivateKey key1 = PrivateKey.generateED25519(); - - var accountId = new AccountCreateTransaction() - .setKey(key1) - .setInitialBalance(new Hbar(10)) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .accountId; - - // Create the transaction - TransferTransaction transfer = new TransferTransaction() - .addHbarTransfer(accountId, new Hbar(1).negated()) - .addHbarTransfer(testEnv.operatorId, new Hbar(1)); - - // Schedule the transaction - var scheduleId = transfer - .schedule() - .setExpirationTime(Instant.now().plusSeconds(10)) - .setWaitForExpiry(true) - .setScheduleMemo("HIP-423 Integration Test") - .execute(testEnv.client) - .getReceipt(testEnv.client) - .scheduleId; - - ScheduleInfo info = new ScheduleInfoQuery() - .setScheduleId(scheduleId) - .execute(testEnv.client); - - // Verify the transaction is not executed - assertThat(info.executedAt).isNull(); - - // Sign - new ScheduleSignTransaction() - .setScheduleId(scheduleId) - .freezeWith(testEnv.client) - .sign(key1) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - info = new ScheduleInfoQuery() - .setScheduleId(scheduleId) - .execute(testEnv.client); - - // Verify the transaction is still not executed - assertThat(info.executedAt).isNull(); - - var accountBalanceBefore = new AccountBalanceQuery() - .setAccountId(accountId) - .execute(testEnv.client); - - Thread.sleep(10_000); - - var accountBalanceAfter = new AccountBalanceQuery() - .setAccountId(accountId) - .execute(testEnv.client); - - // Verify the transaction executed after 10 seconds - assertThat(accountBalanceBefore.hbars.compareTo(accountBalanceAfter.hbars)).isEqualTo(1); - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/SystemIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/SystemIntegrationTest.java deleted file mode 100644 index f94f4a675e..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/SystemIntegrationTest.java +++ /dev/null @@ -1,92 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.ContractCreateTransaction; -import com.hedera.hashgraph.sdk.ContractFunctionParameters; -import com.hedera.hashgraph.sdk.FileCreateTransaction; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.Status; -import com.hedera.hashgraph.sdk.SystemDeleteTransaction; -import com.hedera.hashgraph.sdk.SystemUndeleteTransaction; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import java.time.Instant; - -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -public class SystemIntegrationTest { - private static final String SMART_CONTRACT_BYTECODE = "608060405234801561001057600080fd5b506040516104d73803806104d78339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b506040525050600080546001600160a01b0319163317905550805161010890600190602084019061010f565b50506101aa565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061015057805160ff191683800117855561017d565b8280016001018555821561017d579182015b8281111561017d578251825591602001919060010190610162565b5061018992915061018d565b5090565b6101a791905b808211156101895760008155600101610193565b90565b61031e806101b96000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063368b87721461004657806341c0e1b5146100ee578063ce6d41de146100f6575b600080fd5b6100ec6004803603602081101561005c57600080fd5b81019060208101813564010000000081111561007757600080fd5b82018360208201111561008957600080fd5b803590602001918460018302840111640100000000831117156100ab57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610173945050505050565b005b6100ec6101a2565b6100fe6101ba565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610138578181015183820152602001610120565b50505050905090810190601f1680156101655780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000546001600160a01b0316331461018a5761019f565b805161019d906001906020840190610250565b505b50565b6000546001600160a01b03163314156101b85733ff5b565b60018054604080516020601f600260001961010087891615020190951694909404938401819004810282018101909252828152606093909290918301828280156102455780601f1061021a57610100808354040283529160200191610245565b820191906000526020600020905b81548152906001019060200180831161022857829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061029157805160ff19168380011785556102be565b828001600101855582156102be579182015b828111156102be5782518255916020019190600101906102a3565b506102ca9291506102ce565b5090565b61024d91905b808211156102ca57600081556001016102d456fea264697066735822122084964d4c3f6bc912a9d20e14e449721012d625aa3c8a12de41ae5519752fc89064736f6c63430006000033"; - - @Test - @DisplayName("All system transactions are not supported") - void allSystemTransactionsAreNotSupported() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var fileId = Objects.requireNonNull( - new FileCreateTransaction() - .setContents(SMART_CONTRACT_BYTECODE) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .fileId); - - var contractId = Objects.requireNonNull( - new ContractCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setGas(200000) - .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) - .setBytecodeFileId(fileId) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(testEnv.client) - .getReceipt(testEnv.client) - .contractId - ); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new SystemDeleteTransaction() - .setContractId(contractId) - .setExpirationTime(Instant.now()) - .execute(testEnv.client); - }).withMessageContaining(Status.NOT_SUPPORTED.toString()); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new SystemDeleteTransaction() - .setFileId(fileId) - .setExpirationTime(Instant.now()) - .execute(testEnv.client); - }).withMessageContaining(Status.NOT_SUPPORTED.toString()); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new SystemUndeleteTransaction() - .setContractId(contractId) - .execute(testEnv.client); - }).withMessageContaining(Status.NOT_SUPPORTED.toString()); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new SystemUndeleteTransaction() - .setFileId(fileId) - .execute(testEnv.client); - }).withMessageContaining(Status.NOT_SUPPORTED.toString()); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenAirdropCancelIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenAirdropCancelIntegrationTest.java deleted file mode 100644 index 3d15da8e23..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenAirdropCancelIntegrationTest.java +++ /dev/null @@ -1,479 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import static com.hedera.hashgraph.sdk.test.integration.EntityHelper.fungibleInitialBalance; -import static com.hedera.hashgraph.sdk.test.integration.EntityHelper.mitedNfts; -import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -import com.hedera.hashgraph.sdk.AccountBalanceQuery; -import com.hedera.hashgraph.sdk.PendingAirdropId; -import com.hedera.hashgraph.sdk.PendingAirdropRecord; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.sdk.Status; -import com.hedera.hashgraph.sdk.TokenAirdropTransaction; -import com.hedera.hashgraph.sdk.TokenAssociateTransaction; -import com.hedera.hashgraph.sdk.TokenCancelAirdropTransaction; -import com.hedera.hashgraph.sdk.TokenDeleteTransaction; -import com.hedera.hashgraph.sdk.TokenFreezeTransaction; -import com.hedera.hashgraph.sdk.TokenMintTransaction; -import com.hedera.hashgraph.sdk.TokenPauseTransaction; -import com.hedera.hashgraph.sdk.TransactionId; -import java.util.ArrayList; -import java.util.Collections; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -class TokenAirdropCancelIntegrationTest { - - private final int amount = 100; - - @Test - @DisplayName("Cancels the tokens when they are in pending state") - void canCancelTokens() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - // create fungible and nf token - var tokenID = EntityHelper.createFungibleToken(testEnv, 3); - var nftID = EntityHelper.createNft(testEnv); - // mint some NFTs - var mintReceipt = new TokenMintTransaction() - .setTokenId(nftID) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - var nftSerials = mintReceipt.serials; - - // create receiver with 0 auto associations - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); - - // airdrop the tokens - var record = new TokenAirdropTransaction() - .addNftTransfer(nftID.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) - .addNftTransfer(nftID.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) - .addTokenTransfer(tokenID, receiverAccountId, amount) - .addTokenTransfer(tokenID, testEnv.operatorId, -amount) - .execute(testEnv.client) - .getRecord(testEnv.client); - - // sender cancels the tokens - record = new TokenCancelAirdropTransaction() - .addPendingAirdrop(record.pendingAirdropRecords.get(0).getPendingAirdropId()) - .addPendingAirdrop(record.pendingAirdropRecords.get(1).getPendingAirdropId()) - .addPendingAirdrop(record.pendingAirdropRecords.get(2).getPendingAirdropId()) - .execute(testEnv.client) - .getRecord(testEnv.client); - - // verify in the transaction record the pending airdrop ids for nft and ft - should no longer exist - assertEquals(0, record.pendingAirdropRecords.size()); - - // verify the receiver does not hold the tokens via query - var receiverAccountBalance = new AccountBalanceQuery() - .setAccountId(receiverAccountId) - .execute(testEnv.client); - assertNull(receiverAccountBalance.tokens.get(tokenID)); - assertNull(receiverAccountBalance.tokens.get(nftID)); - - // verify the operator does hold the tokens - var operatorBalance = new AccountBalanceQuery() - .setAccountId(testEnv.operatorId) - .execute(testEnv.client); - assertEquals(fungibleInitialBalance, operatorBalance.tokens.get(tokenID)); - assertEquals(mitedNfts, operatorBalance.tokens.get(nftID)); - - } - } - - @Test - @DisplayName("Cancels the tokens when token is frozen") - void canCancelTokensWhenTokenIsFrozen() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - // create fungible token - var tokenID = EntityHelper.createFungibleToken(testEnv, 3); - - // create receiver with 0 auto associations - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); - - // airdrop the tokens - var record = new TokenAirdropTransaction() - .addTokenTransfer(tokenID, receiverAccountId, amount) - .addTokenTransfer(tokenID, testEnv.operatorId, -amount) - .execute(testEnv.client) - .getRecord(testEnv.client); - - // associate - new TokenAssociateTransaction() - .setAccountId(receiverAccountId) - .setTokenIds(Collections.singletonList(tokenID)) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // freeze the token - new TokenFreezeTransaction() - .setAccountId(receiverAccountId) - .setTokenId(tokenID) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // cancel - new TokenCancelAirdropTransaction() - .addPendingAirdrop(record.pendingAirdropRecords.get(0).getPendingAirdropId()) - .execute(testEnv.client) - .getRecord(testEnv.client); - - } - } - - @Test - @DisplayName("Cancels the tokens when token is paused") - void canCancelTokensWhenTokenIsPaused() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - // create fungible token - var tokenID = EntityHelper.createFungibleToken(testEnv, 3); - - // create receiver with 0 auto associations - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); - - // airdrop the tokens - var record = new TokenAirdropTransaction() - .addTokenTransfer(tokenID, receiverAccountId, amount) - .addTokenTransfer(tokenID, testEnv.operatorId, -amount) - .execute(testEnv.client) - .getRecord(testEnv.client); - - // pause the token - new TokenPauseTransaction().setTokenId(tokenID).execute(testEnv.client).getReceipt(testEnv.client); - - // cancel - new TokenCancelAirdropTransaction() - .addPendingAirdrop(record.pendingAirdropRecords.get(0).getPendingAirdropId()) - .execute(testEnv.client) - .getRecord(testEnv.client); - - } - } - - @Test - @DisplayName("Cancels the tokens when token is deleted") - void canCancelTokensWhenTokenIsDeleted() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - // create fungible token - var tokenID = EntityHelper.createFungibleToken(testEnv, 3); - - // create receiver with 0 auto associations - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); - - // airdrop the tokens - var record = new TokenAirdropTransaction() - .addTokenTransfer(tokenID, receiverAccountId, amount) - .addTokenTransfer(tokenID, testEnv.operatorId, -amount) - .execute(testEnv.client) - .getRecord(testEnv.client); - - // delete the token - new TokenDeleteTransaction().setTokenId(tokenID).execute(testEnv.client).getReceipt(testEnv.client); - - // cancel - new TokenCancelAirdropTransaction() - .addPendingAirdrop(record.pendingAirdropRecords.get(0).getPendingAirdropId()) - .execute(testEnv.client) - .getRecord(testEnv.client); - - } - } - - @Test - @DisplayName("Cancels the tokens when they are in pending state to multiple receivers") - void canCancelTokensToMultipleReceivers() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - // create fungible and nf token - var tokenID = EntityHelper.createFungibleToken(testEnv, 3); - var nftID = EntityHelper.createNft(testEnv); - // mint some NFTs - var mintReceipt = new TokenMintTransaction() - .setTokenId(nftID) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - var nftSerials = mintReceipt.serials; - - // create receiver1 with 0 auto associations - var receiver1AccountKey = PrivateKey.generateED25519(); - var receiver1AccountId = EntityHelper.createAccount(testEnv, receiver1AccountKey, 0); - - // create receiver2 with 0 auto associations - var receiver2AccountKey = PrivateKey.generateED25519(); - var receiver2AccountId = EntityHelper.createAccount(testEnv, receiver2AccountKey, 0); - - // airdrop the tokens to both - var record = new TokenAirdropTransaction() - .addNftTransfer(nftID.nft(nftSerials.get(0)), testEnv.operatorId, receiver1AccountId) - .addNftTransfer(nftID.nft(nftSerials.get(1)), testEnv.operatorId, receiver1AccountId) - .addTokenTransfer(tokenID, receiver1AccountId, amount) - .addTokenTransfer(tokenID, testEnv.operatorId, -amount) - .addNftTransfer(nftID.nft(nftSerials.get(2)), testEnv.operatorId, receiver2AccountId) - .addNftTransfer(nftID.nft(nftSerials.get(3)), testEnv.operatorId, receiver2AccountId) - .addTokenTransfer(tokenID, receiver2AccountId, amount) - .addTokenTransfer(tokenID, testEnv.operatorId, -amount) - .execute(testEnv.client) - .getRecord(testEnv.client); - - // verify the txn record - assertEquals(6, record.pendingAirdropRecords.size()); - - // cancel the tokens signing with receiver1 and receiver2 - var pendingAirdropIDs = record.pendingAirdropRecords.stream().map(PendingAirdropRecord::getPendingAirdropId) - .toList(); - record = new TokenCancelAirdropTransaction() - .setPendingAirdropIds(pendingAirdropIDs) - .execute(testEnv.client) - .getRecord(testEnv.client); - - // verify in the transaction record the pending airdrop ids for nft and ft - should no longer exist - assertEquals(0, record.pendingAirdropRecords.size()); - - // verify receiver1 does not hold the tokens via query - var receiverAccountBalance = new AccountBalanceQuery() - .setAccountId(receiver1AccountId) - .execute(testEnv.client); - assertNull(receiverAccountBalance.tokens.get(tokenID)); - assertNull(receiverAccountBalance.tokens.get(nftID)); - - // verify receiver2 does not hold the tokens via query - var receiver2AccountBalance = new AccountBalanceQuery() - .setAccountId(receiver1AccountId) - .execute(testEnv.client); - assertNull(receiver2AccountBalance.tokens.get(tokenID)); - assertNull(receiver2AccountBalance.tokens.get(nftID)); - - // verify the operator does hold the tokens - var operatorBalance = new AccountBalanceQuery() - .setAccountId(testEnv.operatorId) - .execute(testEnv.client); - assertEquals(fungibleInitialBalance, operatorBalance.tokens.get(tokenID)); - assertEquals(mitedNfts, operatorBalance.tokens.get(nftID)); - - } - } - - @Test - @DisplayName("Cancels the tokens when they are in pending state from multiple airdrop transactions") - void canCancelTokensFromMultipleAirdropTxns() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - // create fungible and nf token - var tokenID = EntityHelper.createFungibleToken(testEnv, 3); - var nftID = EntityHelper.createNft(testEnv); - // mint some NFTs - var mintReceipt = new TokenMintTransaction() - .setTokenId(nftID) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - var nftSerials = mintReceipt.serials; - - // create receiver with 0 auto associations - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); - - // airdrop some of the tokens to the receiver - var record1 = new TokenAirdropTransaction() - .addNftTransfer(nftID.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) - .execute(testEnv.client) - .getRecord(testEnv.client); - // airdrop some of the tokens to the receiver - var record2 = new TokenAirdropTransaction() - .addNftTransfer(nftID.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) - .execute(testEnv.client) - .getRecord(testEnv.client); - // airdrop some of the tokens to the receiver - var record3 = new TokenAirdropTransaction() - .addTokenTransfer(tokenID, receiverAccountId, amount) - .addTokenTransfer(tokenID, testEnv.operatorId, -amount) - .execute(testEnv.client) - .getRecord(testEnv.client); - - // get the PendingIds from the records - var pendingAirdropIDs = new ArrayList(); - pendingAirdropIDs.add(record1.pendingAirdropRecords.get(0).getPendingAirdropId()); - pendingAirdropIDs.add(record2.pendingAirdropRecords.get(0).getPendingAirdropId()); - pendingAirdropIDs.add(record3.pendingAirdropRecords.get(0).getPendingAirdropId()); - - // cancel the all the tokens with the receiver - var record = new TokenCancelAirdropTransaction() - .setPendingAirdropIds(pendingAirdropIDs) - .execute(testEnv.client) - .getRecord(testEnv.client); - - // verify in the transaction record the pending airdrop ids for nft and ft - should no longer exist - assertEquals(0, record.pendingAirdropRecords.size()); - - // verify the receiver does not hold the tokens via query - var receiverAccountBalance = new AccountBalanceQuery() - .setAccountId(receiverAccountId) - .execute(testEnv.client); - assertNull(receiverAccountBalance.tokens.get(tokenID)); - assertNull(receiverAccountBalance.tokens.get(nftID)); - - // verify the operator does hold the tokens - var operatorBalance = new AccountBalanceQuery() - .setAccountId(testEnv.operatorId) - .execute(testEnv.client); - assertEquals(fungibleInitialBalance, operatorBalance.tokens.get(tokenID)); - assertEquals(mitedNfts, operatorBalance.tokens.get(nftID)); - - } - } - - @Test - @DisplayName("Cannot cancel the tokens when they are not airdropped") - void cannotCancelTokensForNonExistingAirdrop() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - // create fungible token - var tokenID = EntityHelper.createFungibleToken(testEnv, 3); - - // create receiver with 0 auto associations - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); - - // airdrop the tokens - var record = new TokenAirdropTransaction() - .addTokenTransfer(tokenID, receiverAccountId, amount) - .addTokenTransfer(tokenID, testEnv.operatorId, -amount) - .execute(testEnv.client) - .getRecord(testEnv.client); - - // create receiver with 0 auto associations - var randomAccountKey = PrivateKey.generateED25519(); - var randomAccount = EntityHelper.createAccount(testEnv, randomAccountKey, 0); - - // cancel the tokens with the random account which has not created pending airdrops - // fails with INVALID_SIGNATURE - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenCancelAirdropTransaction() - .setTransactionId(TransactionId.generate(randomAccount)) - .addPendingAirdrop(record.pendingAirdropRecords.get(0).getPendingAirdropId()) - .execute(testEnv.client) - .getRecord(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - } - } - - @Test - @DisplayName("Cannot cancel the tokens when they are already canceled") - void canonCancelTokensForAlreadyCanceledAirdrop() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - // create fungible token - var tokenID = EntityHelper.createFungibleToken(testEnv, 3); - - // create receiver with 0 auto associations - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); - - // airdrop the tokens - var record = new TokenAirdropTransaction() - .addTokenTransfer(tokenID, receiverAccountId, amount) - .addTokenTransfer(tokenID, testEnv.operatorId, -amount) - .execute(testEnv.client) - .getRecord(testEnv.client); - - // cancel the tokens with the receiver - new TokenCancelAirdropTransaction() - .addPendingAirdrop(record.pendingAirdropRecords.get(0).getPendingAirdropId()) - .execute(testEnv.client) - .getRecord(testEnv.client); - - // cancel the tokens with the receiver again - // fails with INVALID_PENDING_AIRDROP_ID - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenCancelAirdropTransaction() - .addPendingAirdrop(record.pendingAirdropRecords.get(0).getPendingAirdropId()) - .execute(testEnv.client) - .getRecord(testEnv.client); - }).withMessageContaining(Status.INVALID_PENDING_AIRDROP_ID.toString()); - - } - } - - @Test - @DisplayName("Cannot cancel the tokens with empty list") - void canonCancelWithEmptyPendingAirdropsList() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - // cancel the tokens with the receiver without setting pendingAirdropIds - // fails with EMPTY_PENDING_AIRDROP_ID_LIST - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenCancelAirdropTransaction() - .execute(testEnv.client) - .getRecord(testEnv.client); - }).withMessageContaining(Status.EMPTY_PENDING_AIRDROP_ID_LIST.toString()); - - } - } - - @Test - @DisplayName("Cannot cancel the tokens with duplicate entries") - void cannotCancelTokensWithDuplicateEntries() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - // create fungible token - var tokenID = EntityHelper.createFungibleToken(testEnv, 3); - - // create receiver with 0 auto associations - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); - - // airdrop the tokens - var record = new TokenAirdropTransaction() - .addTokenTransfer(tokenID, receiverAccountId, amount) - .addTokenTransfer(tokenID, testEnv.operatorId, -amount) - .execute(testEnv.client) - .getRecord(testEnv.client); - - // cancel the tokens with duplicate pending airdrop token ids - // fails with PENDING_AIRDROP_ID_REPEATED - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenCancelAirdropTransaction() - .addPendingAirdrop(record.pendingAirdropRecords.get(0).getPendingAirdropId()) - .addPendingAirdrop(record.pendingAirdropRecords.get(0).getPendingAirdropId()) - .execute(testEnv.client) - .getRecord(testEnv.client); - }).withMessageContaining(Status.PENDING_AIRDROP_ID_REPEATED.toString()); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenAirdropClaimIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenAirdropClaimIntegrationTest.java deleted file mode 100644 index 07817fbc00..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenAirdropClaimIntegrationTest.java +++ /dev/null @@ -1,513 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import static com.hedera.hashgraph.sdk.test.integration.EntityHelper.fungibleInitialBalance; -import static com.hedera.hashgraph.sdk.test.integration.EntityHelper.mitedNfts; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -import com.hedera.hashgraph.sdk.AccountBalanceQuery; -import com.hedera.hashgraph.sdk.PendingAirdropId; -import com.hedera.hashgraph.sdk.PendingAirdropRecord; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.sdk.Status; -import com.hedera.hashgraph.sdk.TokenAirdropTransaction; -import com.hedera.hashgraph.sdk.TokenAssociateTransaction; -import com.hedera.hashgraph.sdk.TokenClaimAirdropTransaction; -import com.hedera.hashgraph.sdk.TokenDeleteTransaction; -import com.hedera.hashgraph.sdk.TokenFreezeTransaction; -import com.hedera.hashgraph.sdk.TokenMintTransaction; -import com.hedera.hashgraph.sdk.TokenPauseTransaction; -import java.util.ArrayList; -import java.util.Collections; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -class TokenAirdropClaimIntegrationTest { - - private final int amount = 100; - - @Test - @DisplayName("Claims the tokens when they are in pending state") - void canClaimTokens() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - // create fungible and nf token - var tokenID = EntityHelper.createFungibleToken(testEnv, 3); - var nftID = EntityHelper.createNft(testEnv); - // mint some NFTs - var mintReceipt = new TokenMintTransaction() - .setTokenId(nftID) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - var nftSerials = mintReceipt.serials; - - // create receiver with 0 auto associations - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); - - // airdrop the tokens - var record = new TokenAirdropTransaction() - .addNftTransfer(nftID.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) - .addNftTransfer(nftID.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) - .addTokenTransfer(tokenID, receiverAccountId, amount) - .addTokenTransfer(tokenID, testEnv.operatorId, -amount) - .execute(testEnv.client) - .getRecord(testEnv.client); - - // verify the txn record - assertEquals(3, record.pendingAirdropRecords.size()); - - assertEquals(100, record.pendingAirdropRecords.get(0).getPendingAirdropAmount()); - assertEquals(tokenID, record.pendingAirdropRecords.get(0).getPendingAirdropId().getTokenId()); - assertNull(record.pendingAirdropRecords.get(0).getPendingAirdropId().getNftId()); - - assertEquals(0, record.pendingAirdropRecords.get(1).getPendingAirdropAmount()); - assertEquals(nftID.nft(1), record.pendingAirdropRecords.get(1).getPendingAirdropId().getNftId()); - assertNull(record.pendingAirdropRecords.get(1).getPendingAirdropId().getTokenId()); - - assertEquals(0, record.pendingAirdropRecords.get(2).getPendingAirdropAmount()); - assertEquals(nftID.nft(2), record.pendingAirdropRecords.get(2).getPendingAirdropId().getNftId()); - assertNull(record.pendingAirdropRecords.get(2).getPendingAirdropId().getTokenId()); - - // claim the tokens with the receiver - record = new TokenClaimAirdropTransaction() - .addPendingAirdrop(record.pendingAirdropRecords.get(0).getPendingAirdropId()) - .addPendingAirdrop(record.pendingAirdropRecords.get(1).getPendingAirdropId()) - .addPendingAirdrop(record.pendingAirdropRecords.get(2).getPendingAirdropId()) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getRecord(testEnv.client); - - // verify in the transaction record the pending airdrop ids for nft and ft - should no longer exist - assertEquals(0, record.pendingAirdropRecords.size()); - - // verify the receiver holds the tokens via query - var receiverAccountBalance = new AccountBalanceQuery() - .setAccountId(receiverAccountId) - .execute(testEnv.client); - assertEquals(amount, receiverAccountBalance.tokens.get(tokenID)); - assertEquals(2, receiverAccountBalance.tokens.get(nftID)); - - // verify the operator does not hold the tokens - var operatorBalance = new AccountBalanceQuery() - .setAccountId(testEnv.operatorId) - .execute(testEnv.client); - assertEquals(fungibleInitialBalance - amount, operatorBalance.tokens.get(tokenID)); - assertEquals(mitedNfts - 2, operatorBalance.tokens.get(nftID)); - - } - } - - @Test - @DisplayName("Claims the tokens when they are in pending state to multiple receivers") - void canClaimTokensToMultipleReceivers() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - // create fungible and nf token - var tokenID = EntityHelper.createFungibleToken(testEnv, 3); - var nftID = EntityHelper.createNft(testEnv); - // mint some NFTs - var mintReceipt = new TokenMintTransaction() - .setTokenId(nftID) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - var nftSerials = mintReceipt.serials; - - // create receiver1 with 0 auto associations - var receiver1AccountKey = PrivateKey.generateED25519(); - var receiver1AccountId = EntityHelper.createAccount(testEnv, receiver1AccountKey, 0); - - // create receiver2 with 0 auto associations - var receiver2AccountKey = PrivateKey.generateED25519(); - var receiver2AccountId = EntityHelper.createAccount(testEnv, receiver2AccountKey, 0); - - // airdrop the tokens to both - var record = new TokenAirdropTransaction() - .addNftTransfer(nftID.nft(nftSerials.get(0)), testEnv.operatorId, receiver1AccountId) - .addNftTransfer(nftID.nft(nftSerials.get(1)), testEnv.operatorId, receiver1AccountId) - .addTokenTransfer(tokenID, receiver1AccountId, amount) - .addTokenTransfer(tokenID, testEnv.operatorId, -amount) - .addNftTransfer(nftID.nft(nftSerials.get(2)), testEnv.operatorId, receiver2AccountId) - .addNftTransfer(nftID.nft(nftSerials.get(3)), testEnv.operatorId, receiver2AccountId) - .addTokenTransfer(tokenID, receiver2AccountId, amount) - .addTokenTransfer(tokenID, testEnv.operatorId, -amount) - .execute(testEnv.client) - .getRecord(testEnv.client); - - // verify the txn record - assertEquals(6, record.pendingAirdropRecords.size()); - - // claim the tokens signing with receiver1 and receiver2 - var pendingAirdropIDs = record.pendingAirdropRecords.stream().map(PendingAirdropRecord::getPendingAirdropId) - .toList(); - record = new TokenClaimAirdropTransaction() - .setPendingAirdropIds(pendingAirdropIDs) - .freezeWith(testEnv.client) - .sign(receiver1AccountKey) - .sign(receiver2AccountKey) - .execute(testEnv.client) - .getRecord(testEnv.client); - - // verify in the transaction record the pending airdrop ids for nft and ft - should no longer exist - assertEquals(0, record.pendingAirdropRecords.size()); - // verify receiver1 holds the tokens via query - var receiverAccountBalance = new AccountBalanceQuery() - .setAccountId(receiver1AccountId) - .execute(testEnv.client); - assertEquals(amount, receiverAccountBalance.tokens.get(tokenID)); - assertEquals(2, receiverAccountBalance.tokens.get(nftID)); - - // verify receiver2 holds the tokens via query - var receiver2AccountBalance = new AccountBalanceQuery() - .setAccountId(receiver1AccountId) - .execute(testEnv.client); - assertEquals(amount, receiver2AccountBalance.tokens.get(tokenID)); - assertEquals(2, receiver2AccountBalance.tokens.get(nftID)); - - // verify the operator does not hold the tokens - var operatorBalance = new AccountBalanceQuery() - .setAccountId(testEnv.operatorId) - .execute(testEnv.client); - assertEquals(fungibleInitialBalance - amount * 2, operatorBalance.tokens.get(tokenID)); - assertEquals(mitedNfts - 4, operatorBalance.tokens.get(nftID)); - - } - } - - @Test - @DisplayName("Claims the tokens when they are in pending state from multiple airdrop transactions") - void canClaimTokensFromMultipleAirdropTxns() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - // create fungible and nf token - var tokenID = EntityHelper.createFungibleToken(testEnv, 3); - var nftID = EntityHelper.createNft(testEnv); - // mint some NFTs - var mintReceipt = new TokenMintTransaction() - .setTokenId(nftID) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - var nftSerials = mintReceipt.serials; - - // create receiver with 0 auto associations - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); - - // airdrop some of the tokens to the receiver - var record1 = new TokenAirdropTransaction() - .addNftTransfer(nftID.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) - .execute(testEnv.client) - .getRecord(testEnv.client); - // airdrop some of the tokens to the receiver - var record2 = new TokenAirdropTransaction() - .addNftTransfer(nftID.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) - .execute(testEnv.client) - .getRecord(testEnv.client); - // airdrop some of the tokens to the receiver - var record3 = new TokenAirdropTransaction() - .addTokenTransfer(tokenID, receiverAccountId, amount) - .addTokenTransfer(tokenID, testEnv.operatorId, -amount) - .execute(testEnv.client) - .getRecord(testEnv.client); - - // get the PendingIds from the records - var pendingAirdropIDs = new ArrayList(); - pendingAirdropIDs.add(record1.pendingAirdropRecords.get(0).getPendingAirdropId()); - pendingAirdropIDs.add(record2.pendingAirdropRecords.get(0).getPendingAirdropId()); - pendingAirdropIDs.add(record3.pendingAirdropRecords.get(0).getPendingAirdropId()); - - // claim the all the tokens with the receiver - var record = new TokenClaimAirdropTransaction() - .setPendingAirdropIds(pendingAirdropIDs) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getRecord(testEnv.client); - - // verify in the transaction record the pending airdrop ids for nft and ft - should no longer exist - assertEquals(0, record.pendingAirdropRecords.size()); - - // verify the receiver holds the tokens via query - var receiverAccountBalance = new AccountBalanceQuery() - .setAccountId(receiverAccountId) - .execute(testEnv.client); - assertEquals(amount, receiverAccountBalance.tokens.get(tokenID)); - assertEquals(2, receiverAccountBalance.tokens.get(nftID)); - - // verify the operator does not hold the tokens - var operatorBalance = new AccountBalanceQuery() - .setAccountId(testEnv.operatorId) - .execute(testEnv.client); - assertEquals(fungibleInitialBalance - amount, operatorBalance.tokens.get(tokenID)); - assertEquals(mitedNfts - 2, operatorBalance.tokens.get(nftID)); - - } - } - - @Test - @DisplayName("Cannot claim the tokens when they are not airdropped") - void cannotClaimTokensForNonExistingAirdrop() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - // create fungible token - var tokenID = EntityHelper.createFungibleToken(testEnv, 3); - - // create receiver with 0 auto associations - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); - - // airdrop the tokens - var record = new TokenAirdropTransaction() - .addTokenTransfer(tokenID, receiverAccountId, amount) - .addTokenTransfer(tokenID, testEnv.operatorId, -amount) - .execute(testEnv.client) - .getRecord(testEnv.client); - - // claim the tokens with the operator which does not have pending airdrops - // fails with INVALID_SIGNATURE - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenClaimAirdropTransaction() - .addPendingAirdrop(record.pendingAirdropRecords.get(0).getPendingAirdropId()) - .execute(testEnv.client) - .getRecord(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - } - } - - @Test - @DisplayName("Cannot claim the tokens when they are already claimed") - void cannotClaimTokensForAlreadyClaimedAirdrop() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - // create fungible token - var tokenID = EntityHelper.createFungibleToken(testEnv, 3); - - // create receiver with 0 auto associations - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); - - // airdrop the tokens - var record = new TokenAirdropTransaction() - .addTokenTransfer(tokenID, receiverAccountId, amount) - .addTokenTransfer(tokenID, testEnv.operatorId, -amount) - .execute(testEnv.client) - .getRecord(testEnv.client); - - // claim the tokens with the receiver - new TokenClaimAirdropTransaction() - .addPendingAirdrop(record.pendingAirdropRecords.get(0).getPendingAirdropId()) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getRecord(testEnv.client); - - // claim the tokens with the receiver again - // fails with INVALID_PENDING_AIRDROP_ID - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenClaimAirdropTransaction() - .addPendingAirdrop(record.pendingAirdropRecords.get(0).getPendingAirdropId()) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getRecord(testEnv.client); - }).withMessageContaining(Status.INVALID_PENDING_AIRDROP_ID.toString()); - - } - } - - @Test - @DisplayName("Cannot claim the tokens with empty list") - void cannotClaimWithEmptyPendingAirdropsList() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - // claim the tokens with the receiver without setting pendingAirdropIds - // fails with EMPTY_PENDING_AIRDROP_ID_LIST - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenClaimAirdropTransaction() - .execute(testEnv.client) - .getRecord(testEnv.client); - }).withMessageContaining(Status.EMPTY_PENDING_AIRDROP_ID_LIST.toString()); - - } - } - - @Test - @DisplayName("Cannot claim the tokens with duplicate entries") - void cannotClaimTokensWithDuplicateEntries() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - // create fungible token - var tokenID = EntityHelper.createFungibleToken(testEnv, 3); - - // create receiver with 0 auto associations - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); - - // airdrop the tokens - var record = new TokenAirdropTransaction() - .addTokenTransfer(tokenID, receiverAccountId, amount) - .addTokenTransfer(tokenID, testEnv.operatorId, -amount) - .execute(testEnv.client) - .getRecord(testEnv.client); - - // claim the tokens with duplicate pending airdrop token ids - // fails with PENDING_AIRDROP_ID_REPEATED - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenClaimAirdropTransaction() - .addPendingAirdrop(record.pendingAirdropRecords.get(0).getPendingAirdropId()) - .addPendingAirdrop(record.pendingAirdropRecords.get(0).getPendingAirdropId()) - .execute(testEnv.client) - .getRecord(testEnv.client); - }).withMessageContaining(Status.PENDING_AIRDROP_ID_REPEATED.toString()); - - } - } - - @Test - @DisplayName("Cannot claim the tokens when token is paused") - void cannotClaimTokensWhenTokenIsPaused() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - // create fungible token - var tokenID = EntityHelper.createFungibleToken(testEnv, 3); - - // create receiver with 0 auto associations - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); - - // airdrop the tokens - var record = new TokenAirdropTransaction() - .addTokenTransfer(tokenID, receiverAccountId, amount) - .addTokenTransfer(tokenID, testEnv.operatorId, -amount) - .execute(testEnv.client) - .getRecord(testEnv.client); - - // pause the token - new TokenPauseTransaction().setTokenId(tokenID).execute(testEnv.client).getReceipt(testEnv.client); - - // claim the tokens with receiver - // fails with TOKEN_IS_PAUSED - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenClaimAirdropTransaction() - .addPendingAirdrop(record.pendingAirdropRecords.get(0).getPendingAirdropId()) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getRecord(testEnv.client); - }).withMessageContaining(Status.TOKEN_IS_PAUSED.toString()); - - } - } - - @Test - @DisplayName("Cannot claim the tokens when token is deleted") - void cannotClaimTokensWhenTokenIsDeleted() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - // create fungible token - var tokenID = EntityHelper.createFungibleToken(testEnv, 3); - - // create receiver with 0 auto associations - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); - - // airdrop the tokens - var record = new TokenAirdropTransaction() - .addTokenTransfer(tokenID, receiverAccountId, amount) - .addTokenTransfer(tokenID, testEnv.operatorId, -amount) - .execute(testEnv.client) - .getRecord(testEnv.client); - - // delete the token - new TokenDeleteTransaction().setTokenId(tokenID).execute(testEnv.client).getReceipt(testEnv.client); - - // claim the tokens with receiver - // fails with TOKEN_IS_DELETED - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenClaimAirdropTransaction() - .addPendingAirdrop(record.pendingAirdropRecords.get(0).getPendingAirdropId()) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getRecord(testEnv.client); - }).withMessageContaining(Status.TOKEN_WAS_DELETED.toString()); - - } - } - - @Test - @DisplayName("Cannot claim the tokens when token is frozen") - void cannotClaimTokensWhenTokenIsFrozen() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - // create fungible token - var tokenID = EntityHelper.createFungibleToken(testEnv, 3); - - // create receiver with 0 auto associations - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); - - // airdrop the tokens - var record = new TokenAirdropTransaction() - .addTokenTransfer(tokenID, receiverAccountId, amount) - .addTokenTransfer(tokenID, testEnv.operatorId, -amount) - .execute(testEnv.client) - .getRecord(testEnv.client); - - // associate - new TokenAssociateTransaction() - .setAccountId(receiverAccountId) - .setTokenIds(Collections.singletonList(tokenID)) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // freeze the token - new TokenFreezeTransaction() - .setAccountId(receiverAccountId) - .setTokenId(tokenID) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // claim the tokens with receiver - // fails with ACCOUNT_FROZEN_FOR_TOKEN - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenClaimAirdropTransaction() - .addPendingAirdrop(record.pendingAirdropRecords.get(0).getPendingAirdropId()) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getRecord(testEnv.client); - }).withMessageContaining(Status.ACCOUNT_FROZEN_FOR_TOKEN.toString()); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenAirdropTransactionIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenAirdropTransactionIntegrationTest.java deleted file mode 100644 index 899d6c3742..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenAirdropTransactionIntegrationTest.java +++ /dev/null @@ -1,493 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import static com.hedera.hashgraph.sdk.test.integration.EntityHelper.fungibleInitialBalance; -import static com.hedera.hashgraph.sdk.test.integration.EntityHelper.mitedNfts; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; - -import com.hedera.hashgraph.sdk.AccountAllowanceApproveTransaction; -import com.hedera.hashgraph.sdk.AccountBalanceQuery; -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.AccountId; -import com.hedera.hashgraph.sdk.CustomFixedFee; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.PublicKey; -import com.hedera.hashgraph.sdk.Status; -import com.hedera.hashgraph.sdk.TokenAirdropTransaction; -import com.hedera.hashgraph.sdk.TokenAssociateTransaction; -import com.hedera.hashgraph.sdk.TokenCreateTransaction; -import com.hedera.hashgraph.sdk.TokenMintTransaction; -import com.hedera.hashgraph.sdk.TokenSupplyType; -import com.hedera.hashgraph.sdk.TransactionId; -import com.hedera.hashgraph.sdk.TransferTransaction; -import java.util.Collections; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -class TokenAirdropTransactionIntegrationTest { - - private final int amount = 100; - - @Test - @DisplayName("Transfers tokens when the account is associated") - void canAirdropAssociatedTokens() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - // create fungible and nf token - var tokenID = EntityHelper.createFungibleToken(testEnv, 3); - var nftID = EntityHelper.createNft(testEnv); - // mint some NFTs - var mintReceipt = new TokenMintTransaction() - .setTokenId(nftID) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - var nftSerials = mintReceipt.serials; - - // create receiver with unlimited auto associations and receiverSig = false - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, -1); - - // airdrop the tokens - new TokenAirdropTransaction() - .addNftTransfer(nftID.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) - .addNftTransfer(nftID.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) - .addTokenTransfer(tokenID, receiverAccountId, amount) - .addTokenTransfer(tokenID, testEnv.operatorId, -amount) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // verify the receiver holds the tokens via query - var receiverAccountBalance = new AccountBalanceQuery() - .setAccountId(receiverAccountId) - .execute(testEnv.client); - assertEquals(amount, receiverAccountBalance.tokens.get(tokenID)); - assertEquals(2, receiverAccountBalance.tokens.get(nftID)); - - // verify the operator does not hold the tokens - var operatorBalance = new AccountBalanceQuery() - .setAccountId(testEnv.operatorId) - .execute(testEnv.client); - assertEquals(fungibleInitialBalance - amount, operatorBalance.tokens.get(tokenID)); - assertEquals(mitedNfts - 2, operatorBalance.tokens.get(nftID)); - - } - } - - @Test - @DisplayName("Tokens are in pending state when the account is not associated") - void canAirdropNonAssociatedTokens() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - // create fungible and nf token - var tokenID = EntityHelper.createFungibleToken(testEnv, 3); - var nftID = EntityHelper.createNft(testEnv); - // mint some NFTs - var mintReceipt = new TokenMintTransaction() - .setTokenId(nftID) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - var nftSerials = mintReceipt.serials; - - // create receiver with 0 auto associations and receiverSig = false - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); - - // airdrop the tokens - var txn = new TokenAirdropTransaction() - .addNftTransfer(nftID.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) - .addNftTransfer(nftID.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) - .addTokenTransfer(tokenID, receiverAccountId, amount) - .addTokenTransfer(tokenID, testEnv.operatorId, -amount) - .execute(testEnv.client); - txn.setValidateStatus(true).getReceipt(testEnv.client); - var record = txn.getRecord(testEnv.client); - - // verify in the transaction record the pending airdrops - assertThat(record.pendingAirdropRecords).isNotNull(); - assertFalse(record.pendingAirdropRecords.isEmpty()); - - // verify the receiver does not hold the tokens via query - var receiverAccountBalance = new AccountBalanceQuery() - .setAccountId(receiverAccountId) - .execute(testEnv.client); - assertNull(receiverAccountBalance.tokens.get(tokenID)); - assertNull(receiverAccountBalance.tokens.get(nftID)); - - // verify the operator does hold the tokens - var operatorBalance = new AccountBalanceQuery() - .setAccountId(testEnv.operatorId) - .execute(testEnv.client); - assertEquals(fungibleInitialBalance, operatorBalance.tokens.get(tokenID)); - assertEquals(mitedNfts, operatorBalance.tokens.get(nftID)); - - } - } - - @Test - @DisplayName("Airdrop creates a hollow account and transfers the tokens") - void canAirdropToAlias() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - // create fungible and nf token - var tokenID = EntityHelper.createFungibleToken(testEnv, 3); - var nftID = EntityHelper.createNft(testEnv); - // mint some NFTs - var mintReceipt = new TokenMintTransaction() - .setTokenId(nftID) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - var nftSerials = mintReceipt.serials; - - // airdrop the tokens to an alias - PrivateKey privateKey = PrivateKey.generateED25519(); - PublicKey publicKey = privateKey.getPublicKey(); - - AccountId aliasAccountId = publicKey.toAccountId(0, 0); - - // should lazy-create and transfer the tokens - new TokenAirdropTransaction() - .addNftTransfer(nftID.nft(nftSerials.get(0)), testEnv.operatorId, aliasAccountId) - .addNftTransfer(nftID.nft(nftSerials.get(1)), testEnv.operatorId, aliasAccountId) - .addTokenTransfer(tokenID, aliasAccountId, amount) - .addTokenTransfer(tokenID, testEnv.operatorId, -amount) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // verify the receiver holds the tokens via query - var receiverAccountBalance = new AccountBalanceQuery() - .setAccountId(aliasAccountId) - .execute(testEnv.client); - assertEquals(amount, receiverAccountBalance.tokens.get(tokenID)); - assertEquals(2, receiverAccountBalance.tokens.get(nftID)); - - // verify the operator does not hold the tokens - var operatorBalance = new AccountBalanceQuery() - .setAccountId(testEnv.operatorId) - .execute(testEnv.client); - assertEquals(fungibleInitialBalance - amount, operatorBalance.tokens.get(tokenID)); - assertEquals(mitedNfts - 2, operatorBalance.tokens.get(nftID)); - - } - } - - @Test - @DisplayName("Can airdrop with custom fees") - void canAirdropWithCustomFee() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - // create receiver unlimited auto associations and receiverSig = false - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, -1); - - // create fungible token with custom fee another token - var customFeeTokenID = EntityHelper.createFungibleToken(testEnv, 3); - - // make the custom fee to be paid by the sender and the fee collector to be the operator account - CustomFixedFee fee = new CustomFixedFee() - .setFeeCollectorAccountId(testEnv.operatorId) - .setDenominatingTokenId(customFeeTokenID) - .setAmount(1) - .setAllCollectorsAreExempt(true); - - var tokenID = new TokenCreateTransaction() - .setTokenName("Test Fungible Token") - .setTokenSymbol("TFT") - .setTokenMemo("I was created for integration tests") - .setDecimals(3) - .setInitialSupply(fungibleInitialBalance) - .setMaxSupply(fungibleInitialBalance) - .setTreasuryAccountId(testEnv.operatorId) - .setSupplyType(TokenSupplyType.FINITE) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setMetadataKey(testEnv.operatorKey) - .setPauseKey(testEnv.operatorKey) - .setCustomFees(Collections.singletonList(fee)) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId; - - // create sender account with unlimited associations and send some tokens to it - var senderKey = PrivateKey.generateED25519(); - var senderAccountID = EntityHelper.createAccount(testEnv, senderKey, -1); - - // associate the token to the sender - new TokenAssociateTransaction() - .setAccountId(senderAccountID) - .setTokenIds(Collections.singletonList(customFeeTokenID)) - .freezeWith(testEnv.client) - .sign(senderKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // send tokens to the sender - new TransferTransaction() - .addTokenTransfer(customFeeTokenID, testEnv.operatorId, -amount) - .addTokenTransfer(customFeeTokenID, senderAccountID, amount) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TransferTransaction() - .addTokenTransfer(tokenID, testEnv.operatorId, -amount) - .addTokenTransfer(tokenID, senderAccountID, amount) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // airdrop the tokens from the sender to the receiver - new TokenAirdropTransaction() - .addTokenTransfer(tokenID, receiverAccountId, amount) - .addTokenTransfer(tokenID, senderAccountID, -amount) - .freezeWith(testEnv.client) - .sign(senderKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // verify the custom fee has been paid by the sender to the collector - var receiverAccountBalance = new AccountBalanceQuery() - .setAccountId(receiverAccountId) - .execute(testEnv.client); - assertEquals(amount, receiverAccountBalance.tokens.get(tokenID)); - - var senderAccountBalance = new AccountBalanceQuery() - .setAccountId(senderAccountID) - .execute(testEnv.client); - assertEquals(0, senderAccountBalance.tokens.get(tokenID)); - assertEquals(amount - 1, senderAccountBalance.tokens.get(customFeeTokenID)); - - var operatorBalance = new AccountBalanceQuery() - .setAccountId(testEnv.operatorId) - .execute(testEnv.client); - assertEquals(fungibleInitialBalance - amount + 1, operatorBalance.tokens.get(customFeeTokenID)); - assertEquals(fungibleInitialBalance - amount, operatorBalance.tokens.get(tokenID)); - - } - } - - @Test - @DisplayName("Can airdrop ft with receiverSig=true") - void canAirdropTokensWithReceiverSigRequiredFungible() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - // create fungible token - var tokenID = EntityHelper.createFungibleToken(testEnv, 3); - - // create receiver with unlimited auto associations and receiverSig = true - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = new AccountCreateTransaction() - .setKey(receiverAccountKey) - .setInitialBalance(new Hbar(1)) - .setReceiverSignatureRequired(true) - .setMaxAutomaticTokenAssociations(-1) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .accountId; - - // airdrop the tokens - new TokenAirdropTransaction() - .addTokenTransfer(tokenID, receiverAccountId, amount) - .addTokenTransfer(tokenID, testEnv.operatorId, -amount) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can airdrop nft with receiverSig=true") - void canAirdropTokensWithReceiverSigRequiredNFT() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - // create nft - var nftID = EntityHelper.createNft(testEnv); - // mint some NFTs - var mintReceipt = new TokenMintTransaction() - .setTokenId(nftID) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - var nftSerials = mintReceipt.serials; - - // create receiver with unlimited auto associations and receiverSig = true - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = new AccountCreateTransaction() - .setKey(receiverAccountKey) - .setInitialBalance(new Hbar(1)) - .setReceiverSignatureRequired(true) - .setMaxAutomaticTokenAssociations(-1) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .accountId; - - // airdrop the tokens - new TokenAirdropTransaction() - .addNftTransfer(nftID.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) - .addNftTransfer(nftID.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot airdrop ft with no balance") - void cannotAirdropTokensWithAllowanceAndWithoutBalanceFungible() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - // create fungible token - var tokenID = EntityHelper.createFungibleToken(testEnv, 3); - - // create spender and approve to it some tokens - var spenderKey = PrivateKey.generateED25519(); - var spenderAccountID = EntityHelper.createAccount(testEnv, spenderKey, -1); - - // create sender - var senderKey = PrivateKey.generateED25519(); - var senderAccountID = EntityHelper.createAccount(testEnv, senderKey, -1); - - // transfer ft to sender - new TransferTransaction() - .addTokenTransfer(tokenID, testEnv.operatorId, -amount) - .addTokenTransfer(tokenID, senderAccountID, amount) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // approve allowance to the spender - new AccountAllowanceApproveTransaction() - .approveTokenAllowance(tokenID, senderAccountID, spenderAccountID, amount) - .freezeWith(testEnv.client) - .sign(senderKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // airdrop the tokens from the sender to the spender via approval - // fails with NOT_SUPPORTED - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenAirdropTransaction() - .addTokenTransfer(tokenID, spenderAccountID, amount) - .addApprovedTokenTransfer(tokenID, spenderAccountID, -amount) - .setTransactionId(TransactionId.generate(spenderAccountID)) - .freezeWith(testEnv.client) - .sign(spenderKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.NOT_SUPPORTED.toString()); - - } - } - - @Test - @DisplayName("Cannot airdrop nft with no balance") - void cannotAirdropTokensWithAllowanceAndWithoutBalanceNFT() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - // create nft - var nftID = EntityHelper.createNft(testEnv); - // mint some NFTs - var mintReceipt = new TokenMintTransaction() - .setTokenId(nftID) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - var nftSerials = mintReceipt.serials; - - // create spender and approve to it some tokens - var spenderKey = PrivateKey.generateED25519(); - var spenderAccountID = EntityHelper.createAccount(testEnv, spenderKey, -1); - - // create sender - var senderKey = PrivateKey.generateED25519(); - var senderAccountID = EntityHelper.createAccount(testEnv, senderKey, -1); - - // transfer ft to sender - new TransferTransaction() - .addNftTransfer(nftID.nft(nftSerials.get(0)), testEnv.operatorId, senderAccountID) - .addNftTransfer(nftID.nft(nftSerials.get(1)), testEnv.operatorId, senderAccountID) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // approve allowance to the spender - new AccountAllowanceApproveTransaction() - .approveTokenNftAllowance(nftID.nft(nftSerials.get(0)), senderAccountID, spenderAccountID) - .approveTokenNftAllowance(nftID.nft(nftSerials.get(1)), senderAccountID, spenderAccountID) - .freezeWith(testEnv.client) - .sign(senderKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // airdrop the tokens from the sender to the spender via approval - // fails with NOT_SUPPORTED - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenAirdropTransaction() - .addApprovedNftTransfer(nftID.nft(nftSerials.get(0)), senderAccountID, spenderAccountID) - .addApprovedNftTransfer(nftID.nft(nftSerials.get(1)), senderAccountID, spenderAccountID) - .setTransactionId(TransactionId.generate(spenderAccountID)) - .freezeWith(testEnv.client) - .sign(spenderKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.NOT_SUPPORTED.toString()); - - } - } - - @Test - @DisplayName("Cannot airdrop with invalid body") - void cannotAirdropTokensWithInvalidBody() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - // airdrop with no tokenID or NftID - // fails with EMPTY_TOKEN_TRANSFER_BODY - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenAirdropTransaction() - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.EMPTY_TOKEN_TRANSFER_BODY.toString()); - - // create fungible token - var tokenID = EntityHelper.createFungibleToken(testEnv, 3); - - // airdrop with invalid transfers - // fails with INVALID_TRANSACTION_BODY - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenAirdropTransaction() - .addTokenTransfer(tokenID, testEnv.operatorId, 100) - .addTokenTransfer(tokenID, testEnv.operatorId, 100) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_TRANSACTION_BODY.toString()); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenAutomaticAssociationIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenAutomaticAssociationIntegrationTest.java deleted file mode 100644 index 80d98aef93..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenAutomaticAssociationIntegrationTest.java +++ /dev/null @@ -1,574 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.AccountAllowanceApproveTransaction; -import com.hedera.hashgraph.sdk.AccountBalanceQuery; -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.AccountInfoQuery; -import com.hedera.hashgraph.sdk.AccountUpdateTransaction; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.TokenDeleteTransaction; -import com.hedera.hashgraph.sdk.TokenMintTransaction; -import com.hedera.hashgraph.sdk.TransactionId; -import com.hedera.hashgraph.sdk.TransferTransaction; -import java.util.ArrayList; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -public class TokenAutomaticAssociationIntegrationTest { - - @Test - @DisplayName("Can transfer Fungible Tokens to accounts with Limited Max Auto Associations") - void canTransferFungibleTokensToAccountsWithLimitedMaxAutoAssociations() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var tokenId1 = EntityHelper.createFungibleToken(testEnv, 0); - var tokenId2 = EntityHelper.createFungibleToken(testEnv, 0); - var accountKey = PrivateKey.generateED25519(); - var accountMaxAutomaticTokenAssociations = 1; - var receiverAccountId = EntityHelper.createAccount(testEnv, accountKey, accountMaxAutomaticTokenAssociations); - - var accountInfoBeforeTokenAssociation = new AccountInfoQuery() - .setAccountId(receiverAccountId) - .execute(testEnv.client); - assertThat(accountInfoBeforeTokenAssociation.maxAutomaticTokenAssociations).isEqualTo(1); - assertThat(accountInfoBeforeTokenAssociation.tokenRelationships.size()).isEqualTo(0); - - - var transferRecord = new TransferTransaction() - .addTokenTransfer(tokenId1, testEnv.operatorId, -1) - .addTokenTransfer(tokenId1, receiverAccountId, 1) - .execute(testEnv.client) - .getRecord(testEnv.client); - assertThat(transferRecord.automaticTokenAssociations.size()).isEqualTo(1); - assertThat(transferRecord.automaticTokenAssociations.get(0).accountId).isEqualTo(receiverAccountId); - assertThat(transferRecord.automaticTokenAssociations.get(0).tokenId).isEqualTo(tokenId1); - - var accountInfoAfterTokenAssociation = new AccountInfoQuery() - .setAccountId(receiverAccountId) - .execute(testEnv.client); - assertThat(accountInfoAfterTokenAssociation.tokenRelationships.size()).isEqualTo(1); - assertThat(accountInfoAfterTokenAssociation.tokenRelationships.get(tokenId1).automaticAssociation).isTrue(); - - assertThatExceptionOfType(Exception.class).isThrownBy(() -> { - new TransferTransaction() - .addTokenTransfer(tokenId2, testEnv.operatorId, -1) - .addTokenTransfer(tokenId2, receiverAccountId, 1) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining("NO_REMAINING_AUTOMATIC_ASSOCIATIONS"); - - new AccountUpdateTransaction() - .setAccountId(receiverAccountId) - .setMaxAutomaticTokenAssociations(2) - .freezeWith(testEnv.client) - .sign(accountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var accountInfoAfterMaxAssocUpdate = new AccountInfoQuery() - .setAccountId(receiverAccountId) - .execute(testEnv.client); - assertThat(accountInfoAfterMaxAssocUpdate.maxAutomaticTokenAssociations).isEqualTo(2); - - new TokenDeleteTransaction() - .setTokenId(tokenId1) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenDeleteTransaction() - .setTokenId(tokenId2) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can transfer Nfts to accounts with Limited Max Auto Associations") - void canTransferNftsToAccountsWithLimitedMaxAutoAssociations() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var tokenId1 = EntityHelper.createNft(testEnv); - var tokenId2 = EntityHelper.createNft(testEnv); - var accountKey = PrivateKey.generateED25519(); - var accountMaxAutomaticTokenAssociations = 1; - var receiverAccountId = EntityHelper.createAccount(testEnv, accountKey, accountMaxAutomaticTokenAssociations); - - var mintReceiptToken1 = new TokenMintTransaction() - .setTokenId(tokenId1) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var mintReceiptToken2 = new TokenMintTransaction() - .setTokenId(tokenId2) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var accountInfoBeforeTokenAssociation = new AccountInfoQuery() - .setAccountId(receiverAccountId) - .execute(testEnv.client); - assertThat(accountInfoBeforeTokenAssociation.maxAutomaticTokenAssociations).isEqualTo(1); - assertThat(accountInfoBeforeTokenAssociation.tokenRelationships.size()).isEqualTo(0); - - - var serialsToTransfer = new ArrayList<>(mintReceiptToken2.serials); - var nftTransferTransaction = new TransferTransaction(); - for (var serial : serialsToTransfer) { - nftTransferTransaction.addNftTransfer(tokenId1.nft(serial), testEnv.operatorId, receiverAccountId); - } - var transferRecord = nftTransferTransaction - .execute(testEnv.client) - .getRecord(testEnv.client); - - assertThat(transferRecord.automaticTokenAssociations.size()).isEqualTo(1); - assertThat(transferRecord.automaticTokenAssociations.get(0).accountId).isEqualTo(receiverAccountId); - assertThat(transferRecord.automaticTokenAssociations.get(0).tokenId).isEqualTo(tokenId1); - - var accountInfoAfterTokenAssociation = new AccountInfoQuery() - .setAccountId(receiverAccountId) - .execute(testEnv.client); - assertThat(accountInfoAfterTokenAssociation.tokenRelationships.size()).isEqualTo(1); - assertThat(accountInfoAfterTokenAssociation.tokenRelationships.get(tokenId1).automaticAssociation).isTrue(); - - assertThatExceptionOfType(Exception.class).isThrownBy(() -> { - var serial = mintReceiptToken2.serials.get(0); - new TransferTransaction() - .addNftTransfer(tokenId2.nft(serial), testEnv.operatorId, receiverAccountId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining("NO_REMAINING_AUTOMATIC_ASSOCIATIONS"); - - new AccountUpdateTransaction() - .setAccountId(receiverAccountId) - .setMaxAutomaticTokenAssociations(2) - .freezeWith(testEnv.client) - .sign(accountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var accountInfoAfterMaxAssocUpdate = new AccountInfoQuery() - .setAccountId(receiverAccountId) - .execute(testEnv.client); - assertThat(accountInfoAfterMaxAssocUpdate.maxAutomaticTokenAssociations).isEqualTo(2); - - new TokenDeleteTransaction() - .setTokenId(tokenId1) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenDeleteTransaction() - .setTokenId(tokenId2) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - /** - * @notice E2E-HIP-904 - * @url https://hips.hedera.com/hip/hip-904 - */ - @Test - @DisplayName("Can set unlimited max auto associations for Account") - void canSetUnlimitedMaxAutoAssociationsForAccount() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var accountKey = PrivateKey.generateED25519(); - var accountMaxAutomaticTokenAssociations = -1; - var accountId = EntityHelper.createAccount(testEnv, accountKey, accountMaxAutomaticTokenAssociations); - - new AccountUpdateTransaction() - .setAccountId(accountId) - .setMaxAutomaticTokenAssociations(accountMaxAutomaticTokenAssociations) - .freezeWith(testEnv.client) - .sign(accountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var accountInfoBeforeTokenAssociation = new AccountInfoQuery() - .setAccountId(accountId) - .execute(testEnv.client); - assertThat(accountInfoBeforeTokenAssociation.maxAutomaticTokenAssociations).isEqualTo(-1); - - } - } - - /** - * @notice E2E-HIP-904 - * @url https://hips.hedera.com/hip/hip-904 - */ - @Test - @DisplayName("Can transfer Fungible Tokens to accounts with Unlimited Max Auto Associations") - void canTransferFungibleTokensToAccountsWithUnlimitedMaxAutoAssociations() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var tokenId1 = EntityHelper.createFungibleToken(testEnv, 3); - var tokenId2 = EntityHelper.createFungibleToken(testEnv, 3); - var accountKey = PrivateKey.generateED25519(); - var accountId1 = EntityHelper.createAccount(testEnv, accountKey, -1); - var accountId2 = EntityHelper.createAccount(testEnv, accountKey, 100); - - new AccountUpdateTransaction() - .setAccountId(accountId2) - .setMaxAutomaticTokenAssociations(-1) - .freezeWith(testEnv.client) - .sign(accountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // transfer to both receivers some token1 tokens - new TransferTransaction() - .addTokenTransfer(tokenId1, testEnv.operatorId, -1000) - .addTokenTransfer(tokenId1, accountId1, 1000) - .addTokenTransfer(tokenId1, testEnv.operatorId, -1000) - .addTokenTransfer(tokenId1, accountId2, 1000) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // transfer to both receivers some token2 tokens - new TransferTransaction() - .addTokenTransfer(tokenId2, testEnv.operatorId, -1000) - .addTokenTransfer(tokenId2, accountId1, 1000) - .addTokenTransfer(tokenId2, testEnv.operatorId, -1000) - .addTokenTransfer(tokenId2, accountId2, 1000) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // verify the balance of the receivers is 1000 - var accountId1Balance = new AccountBalanceQuery() - .setAccountId(accountId1) - .execute(testEnv.client); - - assertThat(accountId1Balance.tokens.get(tokenId1)).isEqualTo(1000); - assertThat(accountId1Balance.tokens.get(tokenId2)).isEqualTo(1000); - - var accountId2Balance = new AccountBalanceQuery() - .setAccountId(accountId2) - .execute(testEnv.client); - - assertThat(accountId2Balance.tokens.get(tokenId1)).isEqualTo(1000); - assertThat(accountId2Balance.tokens.get(tokenId2)).isEqualTo(1000); - - new TokenDeleteTransaction() - .setTokenId(tokenId1) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenDeleteTransaction() - .setTokenId(tokenId2) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - /** - * @notice E2E-HIP-904 - * @url https://hips.hedera.com/hip/hip-904 - */ - @Test - @DisplayName("Can transfer Fungible Tokens (With Decimals) to accounts with Unlimited Max Auto Associations") - void canTransferFungibleTokensWithDecimalsToAccountsWithUnlimitedMaxAutoAssociations() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var tokenDecimals = 10; - var tokenId1 = EntityHelper.createFungibleToken(testEnv, tokenDecimals); - var tokenId2 = EntityHelper.createFungibleToken(testEnv, tokenDecimals); - var accountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, accountKey, -1); - - new TransferTransaction() - .addTokenTransferWithDecimals(tokenId1, testEnv.operatorId, -1000, tokenDecimals) - .addTokenTransferWithDecimals(tokenId1, receiverAccountId, 1000, tokenDecimals) - .addTokenTransferWithDecimals(tokenId2, testEnv.operatorId, -1000, tokenDecimals) - .addTokenTransferWithDecimals(tokenId2, receiverAccountId, 1000, tokenDecimals) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var receiverAccountBalance = new AccountBalanceQuery() - .setAccountId(receiverAccountId) - .execute(testEnv.client); - - assertThat(receiverAccountBalance.tokens.get(tokenId1)).isEqualTo(1000); - assertThat(receiverAccountBalance.tokens.get(tokenId2)).isEqualTo(1000); - - new TokenDeleteTransaction() - .setTokenId(tokenId1) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenDeleteTransaction() - .setTokenId(tokenId2) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - /** - * @notice E2E-HIP-904 - * @url https://hips.hedera.com/hip/hip-904 - */ - @Test - @DisplayName("Can transfer Fungible Tokens on Behalf Of Owner to account with Unlimited Max Auto Associations") - void canTransferFungibleTokensOnBehalfOfOwnerToAccountWithUnlimitedMaxAutoAssociations() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var tokenId1 = EntityHelper.createFungibleToken(testEnv, 3); - var tokenId2 = EntityHelper.createFungibleToken(testEnv, 3); - var accountKey = PrivateKey.generateED25519(); - var accountId = EntityHelper.createAccount(testEnv, accountKey, -1); - var spenderAccountKey = PrivateKey.generateED25519(); - var spenderAccountId = EntityHelper.createAccount(testEnv, spenderAccountKey, -1); - - new AccountAllowanceApproveTransaction() - .approveTokenAllowance(tokenId1, testEnv.operatorId, spenderAccountId, 2000) - .approveTokenAllowance(tokenId2, testEnv.operatorId, spenderAccountId, 2000) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var record = new TransferTransaction() - .addApprovedTokenTransfer(tokenId1, testEnv.operatorId, -1000) - .addTokenTransfer(tokenId1, accountId, 1000) - .addApprovedTokenTransfer(tokenId2, testEnv.operatorId, -1000) - .addTokenTransfer(tokenId2, accountId, 1000) - .setTransactionId(TransactionId.generate(spenderAccountId)) - .freezeWith(testEnv.client) - .sign(spenderAccountKey) - .execute(testEnv.client) - .getRecord(testEnv.client); - - var accountBalance = new AccountBalanceQuery() - .setAccountId(accountId) - .execute(testEnv.client); - - assertThat(accountBalance.tokens.get(tokenId1)).isEqualTo(1000); - assertThat(accountBalance.tokens.get(tokenId2)).isEqualTo(1000); - - new TokenDeleteTransaction() - .setTokenId(tokenId1) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenDeleteTransaction() - .setTokenId(tokenId2) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - /** - * @notice E2E-HIP-904 - * @url https://hips.hedera.com/hip/hip-904 - */ - @Test - @DisplayName("Can transfer Nfts to accounts with Unlimited Max Auto Associations") - void canTransferNftsToAccountsWithUnlimitedMaxAutoAssociations() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var tokenId1 = EntityHelper.createNft(testEnv); - var tokenId2 = EntityHelper.createNft(testEnv); - var accountKey = PrivateKey.generateED25519(); - var accountId1 = EntityHelper.createAccount(testEnv, accountKey, -1); - var accountId2 = EntityHelper.createAccount(testEnv, accountKey, 100); - - var mintReceiptToken1 = new TokenMintTransaction() - .setTokenId(tokenId1) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var mintReceiptToken2 = new TokenMintTransaction() - .setTokenId(tokenId2) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var nftSerials = mintReceiptToken2.serials; - - new AccountUpdateTransaction() - .setAccountId(accountId2) - .setMaxAutomaticTokenAssociations(-1) - .freezeWith(testEnv.client) - .sign(accountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // transfer nft1 to both receivers, 2 for each - new TransferTransaction() - .addNftTransfer(tokenId1.nft(nftSerials.get(0)), testEnv.operatorId, accountId1) - .addNftTransfer(tokenId1.nft(nftSerials.get(1)), testEnv.operatorId, accountId1) - .addNftTransfer(tokenId1.nft(nftSerials.get(2)), testEnv.operatorId, accountId2) - .addNftTransfer(tokenId1.nft(nftSerials.get(3)), testEnv.operatorId, accountId2) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // transfer nft2 to both receivers, 2 for each - new TransferTransaction() - .addNftTransfer(tokenId2.nft(nftSerials.get(0)), testEnv.operatorId, accountId1) - .addNftTransfer(tokenId2.nft(nftSerials.get(1)), testEnv.operatorId, accountId1) - .addNftTransfer(tokenId2.nft(nftSerials.get(2)), testEnv.operatorId, accountId2) - .addNftTransfer(tokenId2.nft(nftSerials.get(3)), testEnv.operatorId, accountId2) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // verify the balance of the receivers is 2 - var accountId1Balance = new AccountBalanceQuery() - .setAccountId(accountId1) - .execute(testEnv.client); - - assertThat(accountId1Balance.tokens.get(tokenId1)).isEqualTo(2); - assertThat(accountId1Balance.tokens.get(tokenId2)).isEqualTo(2); - - var accountId2Balance = new AccountBalanceQuery() - .setAccountId(accountId2) - .execute(testEnv.client); - - assertThat(accountId2Balance.tokens.get(tokenId1)).isEqualTo(2); - assertThat(accountId2Balance.tokens.get(tokenId2)).isEqualTo(2); - - new TokenDeleteTransaction() - .setTokenId(tokenId1) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenDeleteTransaction() - .setTokenId(tokenId2) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - /** - * @notice E2E-HIP-904 - * @url https://hips.hedera.com/hip/hip-904 - */ - @Test - @DisplayName("Can transfer Nfts on Behalf Of Owner to account with Unlimited Max Auto Associations") - void canTransferNftsOnBehalfOfOwnerToAccountWithUnlimitedMaxAutoAssociations() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var tokenId1 = EntityHelper.createNft(testEnv); - var tokenId2 = EntityHelper.createNft(testEnv); - var accountKey = PrivateKey.generateED25519(); - var accountId = EntityHelper.createAccount(testEnv, accountKey, -1); - var spenderAccountKey = PrivateKey.generateED25519(); - var spenderAccountId = EntityHelper.createAccount(testEnv, spenderAccountKey, -1); - - var mintReceiptToken1 = new TokenMintTransaction() - .setTokenId(tokenId1) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var mintReceiptToken2 = new TokenMintTransaction() - .setTokenId(tokenId2) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var nftSerials = mintReceiptToken2.serials; - - new AccountAllowanceApproveTransaction() - .approveTokenNftAllowanceAllSerials(tokenId1, testEnv.operatorId, spenderAccountId) - .approveTokenNftAllowanceAllSerials(tokenId2, testEnv.operatorId, spenderAccountId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TransferTransaction() - .addApprovedNftTransfer(tokenId1.nft(nftSerials.get(0)), testEnv.operatorId, accountId) - .addApprovedNftTransfer(tokenId1.nft(nftSerials.get(1)), testEnv.operatorId, accountId) - .addApprovedNftTransfer(tokenId2.nft(nftSerials.get(0)), testEnv.operatorId, accountId) - .addApprovedNftTransfer(tokenId2.nft(nftSerials.get(1)), testEnv.operatorId, accountId) - .setTransactionId(TransactionId.generate(spenderAccountId)) - .freezeWith(testEnv.client) - .sign(spenderAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var accountBalance = new AccountBalanceQuery() - .setAccountId(accountId) - .execute(testEnv.client); - - assertThat(accountBalance.tokens.get(tokenId1)).isEqualTo(2); - assertThat(accountBalance.tokens.get(tokenId2)).isEqualTo(2); - - new TokenDeleteTransaction() - .setTokenId(tokenId1) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenDeleteTransaction() - .setTokenId(tokenId2) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - /** - * @notice E2E-HIP-904 - * @url https://hips.hedera.com/hip/hip-904 - */ - @Test - @DisplayName("Cannot Set Invalid Max Auto Associations Values") - void cannotSetInvalidMaxAutoAssociationsValues() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var accountKey = PrivateKey.generateED25519(); - - assertThatExceptionOfType(Exception.class).isThrownBy(() -> { - new AccountCreateTransaction() - .setKey(accountKey) - .setMaxAutomaticTokenAssociations(-2) - .execute(testEnv.client); - }).withMessageContaining("INVALID_MAX_AUTO_ASSOCIATIONS"); - - assertThatExceptionOfType(Exception.class).isThrownBy(() -> { - new AccountCreateTransaction() - .setKey(accountKey) - .setMaxAutomaticTokenAssociations(-1000) - .execute(testEnv.client); - }).withMessageContaining("INVALID_MAX_AUTO_ASSOCIATIONS"); - - var accountId = EntityHelper.createAccount(testEnv, accountKey, 100); - - assertThatExceptionOfType(Exception.class).isThrownBy(() -> { - new AccountUpdateTransaction() - .setAccountId(accountId) - .setMaxAutomaticTokenAssociations(-2) - .freezeWith(testEnv.client) - .sign(accountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining("INVALID_MAX_AUTO_ASSOCIATIONS"); - - assertThatExceptionOfType(Exception.class).isThrownBy(() -> { - new AccountUpdateTransaction() - .setAccountId(accountId) - .setMaxAutomaticTokenAssociations(-1000) - .freezeWith(testEnv.client) - .sign(accountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining("INVALID_MAX_AUTO_ASSOCIATIONS"); - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenBurnIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenBurnIntegrationTest.java deleted file mode 100644 index dde7405f2d..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenBurnIntegrationTest.java +++ /dev/null @@ -1,250 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.sdk.Status; -import com.hedera.hashgraph.sdk.TokenAssociateTransaction; -import com.hedera.hashgraph.sdk.TokenBurnTransaction; -import com.hedera.hashgraph.sdk.TokenCreateTransaction; -import com.hedera.hashgraph.sdk.TokenMintTransaction; -import com.hedera.hashgraph.sdk.TokenType; -import com.hedera.hashgraph.sdk.TransferTransaction; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Collections; -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -class TokenBurnIntegrationTest { - @Test - @DisplayName("Can burn tokens") - void canBurnTokens() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var response = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client); - - var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); - - var receipt = new TokenBurnTransaction() - .setAmount(10) - .setTokenId(tokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - assertThat(receipt.totalSupply).isEqualTo(1000000 - 10); - - } - } - - @Test - @DisplayName("Cannot burn tokens when token ID is not set") - void cannotBurnTokensWhenTokenIDIsNotSet() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenBurnTransaction() - .setAmount(10) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_TOKEN_ID.toString()); - - } - } - - @Test - @DisplayName("Can burn tokens when amount is not set") - void canBurnTokensWhenAmountIsNotSet() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var response = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client); - - var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); - - var receipt = new TokenBurnTransaction() - .setTokenId(tokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - assertThat(receipt.status).isEqualTo(Status.SUCCESS); - - } - } - - @Test - @DisplayName("Cannot burn tokens when supply key does not sign transaction") - void cannotBurnTokensWhenSupplyKeyDoesNotSignTransaction() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var response = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(PrivateKey.generate()) - .setFreezeDefault(false) - .execute(testEnv.client); - - var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenBurnTransaction() - .setTokenId(tokenId) - .setAmount(10) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - } - } - - @Test - @DisplayName("Can burn NFTs") - void canBurnNfts() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var createReceipt = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var tokenId = Objects.requireNonNull(createReceipt.tokenId); - - var mintReceipt = new TokenMintTransaction() - .setTokenId(tokenId) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenBurnTransaction() - .setSerials(mintReceipt.serials.subList(0, 4)) - .setTokenId(tokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot burn NFTs when NFT is not owned by treasury") - void cannotBurnNftsWhenNftIsNotOwned() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var createReceipt = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var tokenId = Objects.requireNonNull(createReceipt.tokenId); - - var serials = new TokenMintTransaction() - .setTokenId(tokenId) - .setMetadata(NftMetadataGenerator.generate((byte) 1)) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .serials; - - var key = PrivateKey.generateED25519(); - - var accountId = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .accountId; - - new TokenAssociateTransaction() - .setAccountId(accountId) - .setTokenIds(Collections.singletonList(tokenId)) - .freezeWith(testEnv.client) - .signWithOperator(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TransferTransaction() - .addNftTransfer(tokenId.nft(serials.get(0)), testEnv.operatorId, accountId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenBurnTransaction() - .setSerials(serials) - .setTokenId(tokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.TREASURY_MUST_OWN_BURNED_NFT.toString()); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenCreateIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenCreateIntegrationTest.java deleted file mode 100644 index 598d259691..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenCreateIntegrationTest.java +++ /dev/null @@ -1,415 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -import com.hedera.hashgraph.sdk.AccountId; -import com.hedera.hashgraph.sdk.CustomFee; -import com.hedera.hashgraph.sdk.CustomFixedFee; -import com.hedera.hashgraph.sdk.CustomFractionalFee; -import com.hedera.hashgraph.sdk.CustomRoyaltyFee; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.sdk.Status; -import com.hedera.hashgraph.sdk.TokenCreateTransaction; -import com.hedera.hashgraph.sdk.TokenType; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -class TokenCreateIntegrationTest { - private static List createFixedFeeList(int count, AccountId feeCollector) { - var feeList = new ArrayList(); - for (int i = 0; i < count; i++) { - feeList.add(new CustomFixedFee() - .setAmount(10) - .setFeeCollectorAccountId(feeCollector)); - } - return feeList; - } - - private static List createFractionalFeeList(int count, AccountId feeCollector) { - var feeList = new ArrayList(); - for (int i = 0; i < count; i++) { - feeList.add(new CustomFractionalFee() - .setNumerator(1) - .setDenominator(20) - .setMin(1) - .setMax(10) - .setFeeCollectorAccountId(feeCollector)); - } - return feeList; - } - - @Test - @DisplayName("Can create token with operator as all keys") - void canCreateTokenWithOperatorAsAllKeys() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var response = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFeeScheduleKey(testEnv.operatorKey) - .setPauseKey(testEnv.operatorKey) - .setMetadataKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client); - - Objects.requireNonNull(response.getReceipt(testEnv.client)); - - } - } - - @Test - @DisplayName("Can create token with minimal properties set") - @SuppressWarnings("UnusedVariable") - void canCreateTokenWithMinimalPropertiesSet() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTreasuryAccountId(testEnv.operatorId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot create token when token name is not set") - void cannotCreateTokenWhenTokenNameIsNotSet() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenCreateTransaction() - .setTokenSymbol("F") - .setTreasuryAccountId(testEnv.operatorId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - }).withMessageContaining(Status.MISSING_TOKEN_NAME.toString()); - - } - } - - @Test - @DisplayName("Cannot create token when token symbol is not set") - void cannotCreateTokenWhenTokenSymbolIsNotSet() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenCreateTransaction() - .setTokenName("ffff") - .setTreasuryAccountId(testEnv.operatorId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - }).withMessageContaining(Status.MISSING_TOKEN_SYMBOL.toString()); - - } - } - - @Test - @DisplayName("Cannot create token when token treasury account ID is not set") - void cannotCreateTokenWhenTokenTreasuryAccountIDIsNotSet() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .execute(testEnv.client) - .getReceipt(testEnv.client); - - }).withMessageContaining(Status.INVALID_TREASURY_ACCOUNT_FOR_TOKEN.toString()); - - } - } - - @Test - @DisplayName("Cannot create token when token treasury account ID does not sign transaction") - void cannotCreateTokenWhenTokenTreasuryAccountIDDoesNotSignTransaction() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTreasuryAccountId(AccountId.fromString("0.0.3")) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - } - } - - @Test - @DisplayName("Cannot create token when admin key does not sign transaction") - void cannotCreateTokenWhenAdminKeyDoesNotSignTransaction() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - var key = PrivateKey.generateED25519(); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - } - } - - @Test - @DisplayName("Can create token with custom fees") - void canCreateTokenWithCustomFees() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var customFees = new ArrayList(); - customFees.add(new CustomFixedFee() - .setAmount(10) - .setFeeCollectorAccountId(testEnv.operatorId) - ); - customFees.add(new CustomFractionalFee() - .setNumerator(1) - .setDenominator(20) - .setMin(1) - .setMax(10) - .setFeeCollectorAccountId(testEnv.operatorId) - ); - - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setCustomFees(customFees) - .execute(testEnv.client) - .getReceipt(testEnv.client); - } - } - - @Test - @DisplayName("Cannot create custom fee list with > 10 entries") - void cannotCreateMoreThanTenCustomFees() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setAdminKey(testEnv.operatorKey) - .setTreasuryAccountId(testEnv.operatorId) - .setCustomFees(createFixedFeeList(11, testEnv.operatorId)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.CUSTOM_FEES_LIST_TOO_LONG.toString()); - - } - } - - @Test - @DisplayName("Can create custom fee list with 10 fixed fees") - void canCreateTenFixedFees() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setCustomFees(createFixedFeeList(10, testEnv.operatorId)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can create custom fee list with 10 fractional fees") - void canCreateTenFractionalFees() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setAdminKey(testEnv.operatorKey) - .setTreasuryAccountId(testEnv.operatorId) - .setCustomFees(createFractionalFeeList(10, testEnv.operatorId)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot create a token with a custom fee where min > max") - void cannotCreateMinGreaterThanMax() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setCustomFees(Collections.singletonList(new CustomFractionalFee() - .setNumerator(1) - .setDenominator(3) - .setMin(3) - .setMax(2) - .setFeeCollectorAccountId(testEnv.operatorId))) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.FRACTIONAL_FEE_MAX_AMOUNT_LESS_THAN_MIN_AMOUNT.toString()); - - } - } - - @Test - @DisplayName("Cannot create a token with invalid fee collector account ID") - void cannotCreateInvalidFeeCollector() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setAdminKey(testEnv.operatorKey) - .setTreasuryAccountId(testEnv.operatorId) - .setCustomFees(Collections.singletonList(new CustomFixedFee() - .setAmount(1))) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_CUSTOM_FEE_COLLECTOR.toString()); - - } - } - - @Test - @DisplayName("Cannot create a token with a negative custom fee") - void cannotCreateNegativeFee() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setAdminKey(testEnv.operatorKey) - .setTreasuryAccountId(testEnv.operatorId) - .setCustomFees(Collections.singletonList(new CustomFixedFee() - .setAmount(-1) - .setFeeCollectorAccountId(testEnv.operatorId))) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.CUSTOM_FEE_MUST_BE_POSITIVE.toString()); - - } - } - - @Test - @DisplayName("Cannot create custom fee with 0 denominator") - void cannotCreateZeroDenominator() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setCustomFees(Collections.singletonList(new CustomFractionalFee() - .setNumerator(1) - .setDenominator(0) - .setMin(1) - .setMax(10) - .setFeeCollectorAccountId(testEnv.operatorId))) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.FRACTION_DIVIDES_BY_ZERO.toString()); - - } - } - - @Test - @DisplayName("Can create NFT") - void canCreateNfts() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var response = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client); - - Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); - - } - } - - @Test - @DisplayName("Can create NFT with royalty fee") - void canCreateRoyaltyFee() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTreasuryAccountId(testEnv.operatorId) - .setSupplyKey(testEnv.operatorKey) - .setAdminKey(testEnv.operatorKey) - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setCustomFees(Collections.singletonList(new CustomRoyaltyFee() - .setNumerator(1) - .setDenominator(10) - .setFallbackFee(new CustomFixedFee().setHbarAmount(new Hbar(1))) - .setFeeCollectorAccountId(testEnv.operatorId))) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenDeleteIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenDeleteIntegrationTest.java deleted file mode 100644 index 31f909c1e1..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenDeleteIntegrationTest.java +++ /dev/null @@ -1,130 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.sdk.Status; -import com.hedera.hashgraph.sdk.TokenCreateTransaction; -import com.hedera.hashgraph.sdk.TokenDeleteTransaction; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -class TokenDeleteIntegrationTest { - @Test - @DisplayName("Can delete token") - void canDeleteToken() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - var response = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client); - - var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); - - new TokenDeleteTransaction() - .setTokenId(tokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can delete token with only admin key set") - void canDeleteTokenWithOnlyAdminKeySet() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var response = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .execute(testEnv.client); - - Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); - - } - } - - @Test - @DisplayName("Cannot delete token when admin key does not sign transaction") - void cannotDeleteTokenWhenAdminKeyDoesNotSignTransaction() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - var key = PrivateKey.generateED25519(); - - var response = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(key) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client); - - var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenDeleteTransaction() - .setTokenId(tokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - new TokenDeleteTransaction() - .setTokenId(tokenId) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot delete token when token ID is not set") - void cannotDeleteTokenWhenTokenIDIsNotSet() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenDeleteTransaction() - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_TOKEN_ID.toString()); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenDissociateIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenDissociateIntegrationTest.java deleted file mode 100644 index f6dcaceacd..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenDissociateIntegrationTest.java +++ /dev/null @@ -1,227 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.sdk.Status; -import com.hedera.hashgraph.sdk.TokenAssociateTransaction; -import com.hedera.hashgraph.sdk.TokenCreateTransaction; -import com.hedera.hashgraph.sdk.TokenDissociateTransaction; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Collections; -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -class TokenDissociateIntegrationTest { - @Test - @DisplayName("Can dissociate account with token") - void canAssociateAccountWithToken() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - new TokenAssociateTransaction() - .setAccountId(accountId) - .setTokenIds(Collections.singletonList(tokenId)) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenDissociateTransaction() - .setAccountId(accountId) - .setTokenIds(Collections.singletonList(tokenId)) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can execute token dissociate transaction even when token IDs are not set") - void canExecuteTokenDissociateTransactionEvenWhenTokenIDsAreNotSet() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - new TokenDissociateTransaction() - .setAccountId(accountId) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot dissociate account with tokens when account ID is not set") - void cannotDissociateAccountWithTokensWhenAccountIDIsNotSet() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenDissociateTransaction() - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_ACCOUNT_ID.toString()); - - } - } - - @Test - @DisplayName("Cannot dissociate account with tokens when account does not sign transaction") - void cannotDissociateAccountWhenAccountDoesNotSignTransaction() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenDissociateTransaction() - .setAccountId(accountId) - .setTokenIds(Collections.singletonList(tokenId)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - } - } - - @Test - @DisplayName("Cannot dissociate account from token when account was not associated with") - void cannotDissociateAccountFromTokenWhenAccountWasNotAssociatedWith() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenDissociateTransaction() - .setAccountId(accountId) - .setTokenIds(Collections.singletonList(tokenId)) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.TOKEN_NOT_ASSOCIATED_TO_ACCOUNT.toString()); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenFeeScheduleUpdateIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenFeeScheduleUpdateIntegrationTest.java deleted file mode 100644 index fa544a9498..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenFeeScheduleUpdateIntegrationTest.java +++ /dev/null @@ -1,200 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * Copyright (C) 2021 - 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.CustomFee; -import com.hedera.hashgraph.sdk.CustomFixedFee; -import com.hedera.hashgraph.sdk.CustomFractionalFee; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.sdk.Status; -import com.hedera.hashgraph.sdk.TokenCreateTransaction; -import com.hedera.hashgraph.sdk.TokenFeeScheduleUpdateTransaction; -import com.hedera.hashgraph.sdk.TokenInfoQuery; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.Objects; - -import static org.assertj.core.api.Assertions.as; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -class TokenFeeScheduleUpdateIntegrationTest { - @Test - @DisplayName("Can update token fees") - void canUpdateToken() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var response = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFeeScheduleKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client); - - var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); - - var info = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(info.tokenId).isEqualTo(tokenId); - assertThat(info.name).isEqualTo("ffff"); - assertThat(info.symbol).isEqualTo("F"); - assertThat(info.decimals).isEqualTo(3); - assertThat(testEnv.operatorId).isEqualTo(info.treasuryAccountId); - assertThat(info.adminKey).isNotNull(); - assertThat(info.freezeKey).isNotNull(); - assertThat(info.wipeKey).isNotNull(); - assertThat(info.kycKey).isNotNull(); - assertThat(info.supplyKey).isNotNull(); - assertThat(info.adminKey.toString()).isEqualTo(testEnv.operatorKey.toString()); - assertThat(info.freezeKey.toString()).isEqualTo(testEnv.operatorKey.toString()); - assertThat(info.wipeKey.toString()).isEqualTo(testEnv.operatorKey.toString()); - assertThat(info.kycKey.toString()).isEqualTo(testEnv.operatorKey.toString()); - assertThat(info.supplyKey.toString()).isEqualTo(testEnv.operatorKey.toString()); - assertThat(info.feeScheduleKey.toString()).isEqualTo(testEnv.operatorKey.toString()); - assertThat(info.defaultFreezeStatus).isNotNull(); - assertThat(info.defaultFreezeStatus).isFalse(); - assertThat(info.defaultKycStatus).isNotNull(); - assertThat(info.defaultKycStatus).isFalse(); - assertThat(info.customFees.size()).isEqualTo(0); - - var customFees = new ArrayList(); - customFees.add(new CustomFixedFee() - .setAmount(10) - .setFeeCollectorAccountId(testEnv.operatorId) - ); - customFees.add(new CustomFractionalFee() - .setNumerator(1) - .setDenominator(20) - .setMin(1) - .setMax(10) - .setFeeCollectorAccountId(testEnv.operatorId) - ); - - new TokenFeeScheduleUpdateTransaction() - .setTokenId(tokenId) - .setCustomFees(customFees) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - info = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(info.tokenId).isEqualTo(tokenId); - assertThat(info.name).isEqualTo("ffff"); - assertThat(info.symbol).isEqualTo("F"); - assertThat(info.decimals).isEqualTo(3); - assertThat(info.treasuryAccountId).isEqualTo(testEnv.operatorId); - assertThat(info.adminKey).isNotNull(); - assertThat(info.freezeKey).isNotNull(); - assertThat(info.wipeKey).isNotNull(); - assertThat(info.kycKey).isNotNull(); - assertThat(info.supplyKey).isNotNull(); - assertThat(info.adminKey.toString()).isEqualTo(testEnv.operatorKey.toString()); - assertThat(info.freezeKey.toString()).isEqualTo(testEnv.operatorKey.toString()); - assertThat(info.wipeKey.toString()).isEqualTo(testEnv.operatorKey.toString()); - assertThat(info.kycKey.toString()).isEqualTo(testEnv.operatorKey.toString()); - assertThat(info.supplyKey.toString()).isEqualTo(testEnv.operatorKey.toString()); - assertThat(info.feeScheduleKey.toString()).isEqualTo(testEnv.operatorKey.toString()); - assertThat(info.defaultFreezeStatus).isNotNull(); - assertThat(info.defaultFreezeStatus).isFalse(); - assertThat(info.defaultKycStatus).isNotNull(); - assertThat(info.defaultKycStatus).isFalse(); - - var fees = info.customFees; - assertThat(fees.size()).isEqualTo(2); - int fixedCount = 0; - int fractionalCount = 0; - for (var fee : fees) { - if (fee instanceof CustomFixedFee) { - fixedCount++; - var fixed = (CustomFixedFee) fee; - assertThat(fixed.getAmount()).isEqualTo(10); - assertThat(fixed.getFeeCollectorAccountId()).isEqualTo(testEnv.operatorId); - assertThat(fixed.getDenominatingTokenId()).isNull(); - } else if (fee instanceof CustomFractionalFee) { - fractionalCount++; - var fractional = (CustomFractionalFee) fee; - assertThat(fractional.getNumerator()).isEqualTo(1); - assertThat(fractional.getDenominator()).isEqualTo(20); - assertThat(fractional.getMin()).isEqualTo(1); - assertThat(fractional.getMax()).isEqualTo(10); - assertThat(fractional.getFeeCollectorAccountId()).isEqualTo(testEnv.operatorId); - } - } - assertThat(fixedCount).isEqualTo(1); - assertThat(fractionalCount).isEqualTo(1); - - } - } - - @Test - @DisplayName("Cannot update fee schedule with any key other than fee schedule key") - void cannotUpdateWithAnyOtherKey() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var response = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFeeScheduleKey(PrivateKey.generate()) - .setFreezeDefault(false) - .execute(testEnv.client); - - var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); - - var customFees = new ArrayList(); - customFees.add(new CustomFixedFee() - .setAmount(10) - .setFeeCollectorAccountId(testEnv.operatorId) - ); - customFees.add(new CustomFractionalFee() - .setNumerator(1) - .setDenominator(20) - .setMin(1) - .setMax(10) - .setFeeCollectorAccountId(testEnv.operatorId) - ); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenFeeScheduleUpdateTransaction() - .setTokenId(tokenId) - .setCustomFees(customFees) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenFreezeIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenFreezeIntegrationTest.java deleted file mode 100644 index c9fb87a6db..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenFreezeIntegrationTest.java +++ /dev/null @@ -1,196 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.sdk.Status; -import com.hedera.hashgraph.sdk.TokenAssociateTransaction; -import com.hedera.hashgraph.sdk.TokenCreateTransaction; -import com.hedera.hashgraph.sdk.TokenFreezeTransaction; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Collections; -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -class TokenFreezeIntegrationTest { - @Test - @DisplayName("Can freeze account with token") - void canFreezeAccountWithToken() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - new TokenAssociateTransaction() - .setAccountId(accountId) - .setTokenIds(Collections.singletonList(tokenId)) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenFreezeTransaction() - .setAccountId(accountId) - .setTokenId(tokenId) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot freeze account on token when token ID is not set") - void cannotFreezeAccountOnTokenWhenTokenIDIsNotSet() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenFreezeTransaction() - .setAccountId(accountId) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_TOKEN_ID.toString()); - - } - } - - @Test - @DisplayName("Cannot freeze account on token when account ID is not set") - void cannotFreezeAccountOnTokenWhenAccountIDIsNotSet() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var key = PrivateKey.generateED25519(); - - var response = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client); - - var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenFreezeTransaction() - .setTokenId(tokenId) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_ACCOUNT_ID.toString()); - - } - } - - @Test - @DisplayName("Cannot freeze account on token when account was not associated with") - void cannotFreezeAccountOnTokenWhenAccountWasNotAssociatedWith() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenFreezeTransaction() - .setAccountId(accountId) - .setTokenId(tokenId) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.TOKEN_NOT_ASSOCIATED_TO_ACCOUNT.toString()); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenGrantKycIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenGrantKycIntegrationTest.java deleted file mode 100644 index 08b7efb4e6..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenGrantKycIntegrationTest.java +++ /dev/null @@ -1,196 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.sdk.Status; -import com.hedera.hashgraph.sdk.TokenAssociateTransaction; -import com.hedera.hashgraph.sdk.TokenCreateTransaction; -import com.hedera.hashgraph.sdk.TokenGrantKycTransaction; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Collections; -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -class TokenGrantKycIntegrationTest { - @Test - @DisplayName("Can grant kyc to account with token") - void canGrantKycAccountWithToken() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - new TokenAssociateTransaction() - .setAccountId(accountId) - .setTokenIds(Collections.singletonList(tokenId)) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenGrantKycTransaction() - .setAccountId(accountId) - .setTokenId(tokenId) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot grant kyc to account on token when token ID is not set") - void cannotGrantKycToAccountOnTokenWhenTokenIDIsNotSet() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenGrantKycTransaction() - .setAccountId(accountId) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_TOKEN_ID.toString()); - - } - } - - @Test - @DisplayName("Cannot grant kyc to account on token when account ID is not set") - void cannotGrantKycToAccountOnTokenWhenAccountIDIsNotSet() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var key = PrivateKey.generateED25519(); - - var response = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client); - - var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenGrantKycTransaction() - .setTokenId(tokenId) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_ACCOUNT_ID.toString()); - - } - } - - @Test - @DisplayName("Cannot grant kyc to account on token when account was not associated with") - void cannotGrantKycToAccountOnTokenWhenAccountWasNotAssociatedWith() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenGrantKycTransaction() - .setAccountId(accountId) - .setTokenId(tokenId) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.TOKEN_NOT_ASSOCIATED_TO_ACCOUNT.toString()); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenInfoIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenInfoIntegrationTest.java deleted file mode 100644 index ee79505363..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenInfoIntegrationTest.java +++ /dev/null @@ -1,303 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.MaxQueryPaymentExceededException; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.TokenCreateTransaction; -import com.hedera.hashgraph.sdk.TokenDeleteTransaction; -import com.hedera.hashgraph.sdk.TokenInfoQuery; -import com.hedera.hashgraph.sdk.TokenMintTransaction; -import com.hedera.hashgraph.sdk.TokenSupplyType; -import com.hedera.hashgraph.sdk.TokenType; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -class TokenInfoIntegrationTest { - - @Test - @DisplayName("Can query token info when all keys are different") - void canQueryTokenInfoWhenAllKeysAreDifferent() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - var key1 = PrivateKey.generateED25519(); - var key2 = PrivateKey.generateED25519(); - var key3 = PrivateKey.generateED25519(); - var key4 = PrivateKey.generateED25519(); - var key5 = PrivateKey.generateED25519(); - var key6 = PrivateKey.generateED25519(); - var key7 = PrivateKey.generateED25519(); - - var response = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(key1) - .setFreezeKey(key2) - .setWipeKey(key3) - .setKycKey(key4) - .setSupplyKey(key5) - .setPauseKey(key6) - .setMetadataKey(key7) - .setFreezeDefault(false) - .freezeWith(testEnv.client) - .sign(key1) - .execute(testEnv.client); - - var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); - - var info = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(info.tokenId).isEqualTo(tokenId); - assertThat(info.name).isEqualTo("ffff"); - assertThat(info.symbol).isEqualTo("F"); - assertThat(info.decimals).isEqualTo(3); - assertThat(info.treasuryAccountId).isEqualTo(testEnv.operatorId); - assertThat(info.adminKey).isNotNull(); - assertThat(info.freezeKey).isNotNull(); - assertThat(info.wipeKey).isNotNull(); - assertThat(info.kycKey).isNotNull(); - assertThat(info.supplyKey).isNotNull(); - assertThat(info.pauseKey).isNotNull(); - assertThat(info.metadataKey).isNotNull(); - assertThat(info.adminKey.toString()).isEqualTo(key1.getPublicKey().toString()); - assertThat(info.freezeKey.toString()).isEqualTo(key2.getPublicKey().toString()); - assertThat(info.wipeKey.toString()).isEqualTo(key3.getPublicKey().toString()); - assertThat(info.kycKey.toString()).isEqualTo(key4.getPublicKey().toString()); - assertThat(info.supplyKey.toString()).isEqualTo(key5.getPublicKey().toString()); - assertThat(info.pauseKey.toString()).isEqualTo(key6.getPublicKey().toString()); - assertThat(info.metadataKey.toString()).isEqualTo(key7.getPublicKey().toString()); - assertThat(info.defaultFreezeStatus).isNotNull(); - assertThat(info.defaultFreezeStatus).isFalse(); - assertThat(info.defaultKycStatus).isNotNull(); - assertThat(info.defaultKycStatus).isFalse(); - assertThat(info.tokenType).isEqualTo(TokenType.FUNGIBLE_COMMON); - assertThat(info.supplyType).isEqualTo(TokenSupplyType.INFINITE); - - new TokenDeleteTransaction() - .setTokenId(tokenId) - .freezeWith(testEnv.client) - .sign(key1) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can query token with minimal properties") - void canQueryTokenInfoWhenTokenIsCreatedWithMinimalProperties() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - var response = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTreasuryAccountId(testEnv.operatorId) - .execute(testEnv.client); - - var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); - - var info = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(info.tokenId).isEqualTo(tokenId); - assertThat(info.name).isEqualTo("ffff"); - assertThat(info.symbol).isEqualTo("F"); - assertThat(info.decimals).isEqualTo(0); - assertThat(info.totalSupply).isEqualTo(0); - assertThat(info.treasuryAccountId).isEqualTo(testEnv.operatorId); - assertThat(info.adminKey).isNull(); - assertThat(info.freezeKey).isNull(); - assertThat(info.wipeKey).isNull(); - assertThat(info.kycKey).isNull(); - assertThat(info.supplyKey).isNull(); - assertThat(info.pauseKey).isNull(); - assertThat(info.metadataKey).isNull(); - assertThat(info.defaultFreezeStatus).isNull(); - assertThat(info.defaultKycStatus).isNull(); - assertThat(info.tokenType).isEqualTo(TokenType.FUNGIBLE_COMMON); - assertThat(info.supplyType).isEqualTo(TokenSupplyType.INFINITE); - - } - } - - @Test - @DisplayName("Can query NFT") - void canQueryNfts() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var response = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setSupplyType(TokenSupplyType.FINITE) - .setMaxSupply(5000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .execute(testEnv.client); - - var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); - - var mintReceipt = new TokenMintTransaction() - .setTokenId(tokenId) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - assertThat(mintReceipt.serials.size()).isEqualTo(10); - - var info = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(info.tokenId).isEqualTo(tokenId); - assertThat(info.name).isEqualTo("ffff"); - assertThat(info.symbol).isEqualTo("F"); - assertThat(info.decimals).isEqualTo(0); - assertThat(info.totalSupply).isEqualTo(10); - assertThat(testEnv.operatorId).isEqualTo(info.treasuryAccountId); - assertThat(info.adminKey).isNotNull(); - assertThat(info.freezeKey).isNull(); - assertThat(info.wipeKey).isNull(); - assertThat(info.kycKey).isNull(); - assertThat(info.supplyKey).isNotNull(); - assertThat(info.pauseKey).isNull(); - assertThat(info.metadataKey).isNull(); - assertThat(info.defaultFreezeStatus).isNull(); - assertThat(info.defaultKycStatus).isNull(); - assertThat(info.tokenType).isEqualTo(TokenType.NON_FUNGIBLE_UNIQUE); - assertThat(info.supplyType).isEqualTo(TokenSupplyType.FINITE); - assertThat(info.maxSupply).isEqualTo(5000); - - } - } - - @Test - @DisplayName("Get cost of token info query") - void getCostQueryTokenInfo() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var response = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .execute(testEnv.client); - - var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); - - var infoQuery = new TokenInfoQuery() - .setTokenId(tokenId); - - var cost = infoQuery.getCost(testEnv.client); - - infoQuery.setQueryPayment(cost).execute(testEnv.client); - - } - } - - @Test - @DisplayName("Get cost of token info query, with big max") - void getCostBigMaxQueryTokenInfo() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var response = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .execute(testEnv.client); - - var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); - - var infoQuery = new TokenInfoQuery() - .setTokenId(tokenId) - .setMaxQueryPayment(new Hbar(1000)); - - var cost = infoQuery.getCost(testEnv.client); - - infoQuery.setQueryPayment(cost).execute(testEnv.client); - - } - } - - @Test - @DisplayName("Can query token info when all keys are different") - void getCostSmallMaxTokenInfo() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var response = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .execute(testEnv.client); - - var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); - - var infoQuery = new TokenInfoQuery() - .setTokenId(tokenId) - .setMaxQueryPayment(Hbar.fromTinybars(1)); - - assertThatExceptionOfType(MaxQueryPaymentExceededException.class).isThrownBy(() -> { - infoQuery.execute(testEnv.client); - }); - - } - } - - @Test - @DisplayName("Throws insufficient transaction fee error") - void getCostInsufficientTxFeeQueryTokenInfo() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var response = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .execute(testEnv.client); - - var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); - - var infoQuery = new TokenInfoQuery() - .setTokenId(tokenId) - .setMaxQueryPayment(new Hbar(1000)); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - infoQuery.setQueryPayment(Hbar.fromTinybars(1)).execute(testEnv.client); - }).satisfies(error -> assertThat(error.status.toString()).isEqualTo("INSUFFICIENT_TX_FEE")); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenManualAssociationIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenManualAssociationIntegrationTest.java deleted file mode 100644 index e41bdcb61f..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenManualAssociationIntegrationTest.java +++ /dev/null @@ -1,251 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.AccountBalanceQuery; -import com.hedera.hashgraph.sdk.AccountId; -import com.hedera.hashgraph.sdk.AccountInfoQuery; -import com.hedera.hashgraph.sdk.ContractDeleteTransaction; -import com.hedera.hashgraph.sdk.ContractInfoQuery; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.sdk.Status; -import com.hedera.hashgraph.sdk.TokenAssociateTransaction; -import com.hedera.hashgraph.sdk.TokenMintTransaction; -import com.hedera.hashgraph.sdk.TransferTransaction; -import java.util.ArrayList; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Collections; -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -class TokenManualAssociationIntegrationTest { - - @Test - @DisplayName("Can Manually associate Account with a Fungible Token") - void canManuallyAssociateAccountWithFungibleToken() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var tokenDecimals = 3; - var tokenId = EntityHelper.createFungibleToken(testEnv, 3); - var accountKey = PrivateKey.generateED25519(); - var accountMaxAutomaticTokenAssociations = 0; - var receiverAccountId = EntityHelper.createAccount(testEnv, accountKey, accountMaxAutomaticTokenAssociations); - - new TokenAssociateTransaction() - .setAccountId(receiverAccountId) - .setTokenIds(Collections.singletonList(tokenId)) - .freezeWith(testEnv.client) - .sign(accountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var accountInfo = new AccountInfoQuery() - .setAccountId(receiverAccountId) - .execute(testEnv.client); - - assertThat(accountInfo.tokenRelationships.get(tokenId).decimals).isEqualTo(tokenDecimals); - - new TransferTransaction() - .addTokenTransfer(tokenId, testEnv.operatorId, -10) - .addTokenTransfer(tokenId, receiverAccountId, 10) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var accountBalance = new AccountBalanceQuery() - .setAccountId(receiverAccountId) - .execute(testEnv.client); - - assertThat(accountBalance.tokens.get(tokenId)).isEqualTo(10); - - } - } - - @Test - @DisplayName("Can Manually associate Account with Nft") - void canManuallyAssociateAccountWithNft() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var tokenId = EntityHelper.createNft(testEnv); - var accountKey = PrivateKey.generateED25519(); - var accountMaxAutomaticTokenAssociations = 0; - var receiverAccountId = EntityHelper.createAccount(testEnv, accountKey, accountMaxAutomaticTokenAssociations); - - var mintReceiptToken = new TokenMintTransaction() - .setTokenId(tokenId) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenAssociateTransaction() - .setAccountId(receiverAccountId) - .setTokenIds(Collections.singletonList(tokenId)) - .freezeWith(testEnv.client) - .sign(accountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var serialsToTransfer = new ArrayList<>(mintReceiptToken.serials); - var nftTransferTransaction = new TransferTransaction(); - for (var serial : serialsToTransfer) { - nftTransferTransaction.addNftTransfer(tokenId.nft(serial), testEnv.operatorId, receiverAccountId); - } - nftTransferTransaction - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can Manually associate Contract with a Fungible Token") - void canManuallyAssociateContractWithFungibleToken() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - var tokenDecimals = 3; - var tokenId = EntityHelper.createFungibleToken(testEnv, 3); - var contractId = EntityHelper.createContract(testEnv, testEnv.operatorKey); - - new TokenAssociateTransaction() - .setAccountId(new AccountId(contractId.num)) - .setTokenIds(Collections.singletonList(tokenId)) - .freezeWith(testEnv.client) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var contractInfo = new ContractInfoQuery() - .setContractId(contractId) - .execute(testEnv.client); - - assertThat(contractInfo.contractId).isEqualTo(contractId); - assertThat(contractInfo.accountId).isNotNull(); - assertThat(Objects.requireNonNull(contractInfo.accountId).toString()).isEqualTo(Objects.requireNonNull(contractId).toString()); - assertThat(contractInfo.adminKey).isNotNull(); - assertThat(Objects.requireNonNull(contractInfo.adminKey).toString()).isEqualTo(Objects.requireNonNull(testEnv.operatorKey).toString()); - assertThat(contractInfo.storage).isEqualTo(128); - assertThat(contractInfo.contractMemo).isEqualTo("[e2e::ContractMemo]"); - assertThat(contractInfo.tokenRelationships.get(tokenId).decimals).isEqualTo(tokenDecimals); - - new ContractDeleteTransaction() - .setTransferAccountId(testEnv.operatorId) - .setContractId(contractId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can Manually associate contract with Nft") - void canManuallyAssociateContractWithNft() throws Exception { - try(var testEnv = new IntegrationTestEnv(1)){ - var tokenId = EntityHelper.createNft(testEnv); - var contractId = EntityHelper.createContract(testEnv, testEnv.operatorKey); - - new TokenAssociateTransaction() - .setAccountId(new AccountId(contractId.num)) - .setTokenIds(Collections.singletonList(tokenId)) - .freezeWith(testEnv.client) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var contractInfo = new ContractInfoQuery() - .setContractId(contractId) - .execute(testEnv.client); - - assertThat(contractInfo.contractId).isEqualTo(contractId); - assertThat(contractInfo.accountId).isNotNull(); - assertThat(Objects.requireNonNull(contractInfo.accountId).toString()).isEqualTo(Objects.requireNonNull(contractId).toString()); - assertThat(contractInfo.adminKey).isNotNull(); - assertThat(Objects.requireNonNull(contractInfo.adminKey).toString()).isEqualTo(Objects.requireNonNull(testEnv.operatorKey).toString()); - assertThat(contractInfo.storage).isEqualTo(128); - assertThat(contractInfo.contractMemo).isEqualTo("[e2e::ContractMemo]"); - - new ContractDeleteTransaction() - .setTransferAccountId(testEnv.operatorId) - .setContractId(contractId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can execute token associate transaction even when token IDs are not set") - void canExecuteTokenAssociateTransactionEvenWhenTokenIDsAreNotSet() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var accountKey = PrivateKey.generateED25519(); - var accountMaxAutomaticTokenAssociations = 0; - var accountId = EntityHelper.createAccount(testEnv, accountKey, accountMaxAutomaticTokenAssociations); - - new TokenAssociateTransaction() - .setAccountId(accountId) - .freezeWith(testEnv.client) - .sign(accountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot Manually associate Account with a Token when Account ID is not set") - void cannotAssociateAccountWithTokensWhenAccountIDIsNotSet() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var accountKey = PrivateKey.generateED25519(); - var accountMaxAutomaticTokenAssociations = 0; - var accountId = EntityHelper.createAccount(testEnv, accountKey, accountMaxAutomaticTokenAssociations); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenAssociateTransaction() - .freezeWith(testEnv.client) - .sign(accountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_ACCOUNT_ID.toString()); - - } - } - - @Test - @DisplayName("Cannot Manually Associate Account with a Token when Account Does Not sign transaction") - void cannotAssociateAccountWhenAccountDoesNotSignTransaction() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var tokenDecimals = 3; - var tokenId = EntityHelper.createFungibleToken(testEnv, tokenDecimals); - var accountKey = PrivateKey.generateED25519(); - var accountMaxAutomaticTokenAssociations = 0; - var accountId = EntityHelper.createAccount(testEnv, accountKey, accountMaxAutomaticTokenAssociations); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenAssociateTransaction() - .setAccountId(accountId) - .setTokenIds(Collections.singletonList(tokenId)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenMintIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenMintIntegrationTest.java deleted file mode 100644 index 44bf44fb99..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenMintIntegrationTest.java +++ /dev/null @@ -1,261 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.sdk.Status; -import com.hedera.hashgraph.sdk.TokenCreateTransaction; -import com.hedera.hashgraph.sdk.TokenMintTransaction; -import com.hedera.hashgraph.sdk.TokenSupplyType; -import com.hedera.hashgraph.sdk.TokenType; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -class TokenMintIntegrationTest { - @Test - @DisplayName("Can mint tokens") - void canMintTokens() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var receipt = new TokenMintTransaction() - .setAmount(10) - .setTokenId(tokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - assertThat(receipt.totalSupply).isEqualTo(1000000 + 10); - - } - } - - @Test - @DisplayName("Cannot mint more tokens than max supply") - void cannotMintMoreThanMaxSupply() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setSupplyType(TokenSupplyType.FINITE) - .setMaxSupply(5) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenMintTransaction() - .setTokenId(tokenId) - .setAmount(6) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.TOKEN_MAX_SUPPLY_REACHED.toString()); - - } - } - - @Test - @DisplayName("Cannot mint tokens when token ID is not set") - void cannotMintTokensWhenTokenIDIsNotSet() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenMintTransaction() - .setAmount(10) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_TOKEN_ID.toString()); - - } - } - - @Test - @DisplayName("Can mint tokens when amount is not set") - void canMintTokensWhenAmountIsNotSet() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var receipt = new TokenMintTransaction() - .setTokenId(tokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - assertThat(receipt.status).isEqualTo(Status.SUCCESS); - - } - } - - @Test - @DisplayName("Cannot mint tokens when supply key does not sign transaction") - void cannotMintTokensWhenSupplyKeyDoesNotSignTransaction() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(key) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenMintTransaction() - .setTokenId(tokenId) - .setAmount(10) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - } - } - - @Test - @DisplayName("Can mint NFTs") - void canMintNfts() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var receipt = new TokenMintTransaction() - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .setTokenId(tokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - assertThat(receipt.serials.size()).isEqualTo(10); - - } - } - - @Test - @DisplayName("Cannot mint NFTs if metadata too big") - void cannotMintNftsIfMetadataTooBig() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenMintTransaction() - .setMetadata(NftMetadataGenerator.generateOneLarge()) - .setTokenId(tokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.METADATA_TOO_LONG.toString()); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenNftInfoIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenNftInfoIntegrationTest.java deleted file mode 100644 index e9b8952bc3..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenNftInfoIntegrationTest.java +++ /dev/null @@ -1,265 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * Copyright (C) 2020 - 2021 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.NftId; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.Status; -import com.hedera.hashgraph.sdk.TokenCreateTransaction; -import com.hedera.hashgraph.sdk.TokenMintTransaction; -import com.hedera.hashgraph.sdk.TokenNftInfoQuery; -import com.hedera.hashgraph.sdk.TokenType; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -class TokenNftInfoIntegrationTest { - - @Test - @DisplayName("Can query NFT info by NftId") - void canQueryNftInfoByNftId() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var createReceipt = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var tokenId = Objects.requireNonNull(createReceipt.tokenId); - - byte[] metadata = {50}; - - var mintReceipt = new TokenMintTransaction() - .setTokenId(tokenId) - .addMetadata(metadata) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var nftId = tokenId.nft(mintReceipt.serials.get(0)); - - var nftInfos = new TokenNftInfoQuery() - .setNftId(nftId) - .execute(testEnv.client); - - assertThat(nftInfos.size()).isEqualTo(1); - assertThat(nftInfos.get(0).nftId).isEqualTo(nftId); - assertThat(nftInfos.get(0).accountId).isEqualTo(testEnv.operatorId); - assertThat(nftInfos.get(0).metadata[0]).isEqualTo((byte) 50); - - } - } - - @Test - @DisplayName("Cannot query NFT info by invalid NftId") - void cannotQueryNftInfoByInvalidNftId() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var createReceipt = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var tokenId = Objects.requireNonNull(createReceipt.tokenId); - - byte[] metadata = {50}; - - var mintReceipt = new TokenMintTransaction() - .setTokenId(tokenId) - .addMetadata(metadata) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var nftId = tokenId.nft(mintReceipt.serials.get(0)); - var invalidNftId = new NftId(nftId.tokenId, nftId.serial + 1); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenNftInfoQuery() - .setNftId(invalidNftId) - .execute(testEnv.client); - }).withMessageContaining(Status.INVALID_NFT_ID.toString()); - - } - } - - @Test - @DisplayName("Cannot query NFT info by invalid NftId Serial Number") - void cannotQueryNftInfoByInvalidSerialNumber() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var createReceipt = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var tokenId = Objects.requireNonNull(createReceipt.tokenId); - - byte[] metadata = {50}; - - var mintReceipt = new TokenMintTransaction() - .setTokenId(tokenId) - .addMetadata(metadata) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var nftId = tokenId.nft(mintReceipt.serials.get(0)); - var invalidNftId = new NftId(nftId.tokenId, -1L); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenNftInfoQuery() - .byNftId(invalidNftId) - .execute(testEnv.client); - }).withMessageContaining(Status.INVALID_TOKEN_NFT_SERIAL_NUMBER.toString()); - - } - } - - @Disabled - @Test - @DisplayName("Can query NFT info by AccountId") - void canQueryNftInfoByAccountId() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var createReceipt = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var tokenId = Objects.requireNonNull(createReceipt.tokenId); - - List metadatas = NftMetadataGenerator.generate((byte) 10); - - var mintReceipt = new TokenMintTransaction() - .setTokenId(tokenId) - .setMetadata(metadatas) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var nftInfos = new TokenNftInfoQuery() - .byAccountId(testEnv.operatorId) - .setEnd(10) - .execute(testEnv.client); - - assertThat(nftInfos.size()).isEqualTo(10); - - var serials = new ArrayList(mintReceipt.serials); - - for (var info : nftInfos) { - assertThat(info.nftId.tokenId).isEqualTo(tokenId); - assertThat(serials.remove(info.nftId.serial)).isTrue(); - assertThat(info.accountId).isEqualTo(testEnv.operatorId); - } - - } - } - - @Disabled - @Test - @DisplayName("Can query NFT info by TokenId") - void canQueryNftInfoByTokenId() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var createReceipt = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var tokenId = Objects.requireNonNull(createReceipt.tokenId); - - List metadatas = NftMetadataGenerator.generate((byte) 10); - - var mintReceipt = new TokenMintTransaction() - .setTokenId(tokenId) - .setMetadata(metadatas) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var nftInfos = new TokenNftInfoQuery() - .byTokenId(tokenId) - .setEnd(10) - .execute(testEnv.client); - - assertThat(nftInfos.size()).isEqualTo(10); - - var serials = new ArrayList(mintReceipt.serials); - - for (var info : nftInfos) { - assertThat(info.nftId.tokenId).isEqualTo(tokenId); - assertThat(serials.remove(info.nftId.serial)).isTrue(); - assertThat(info.accountId).isEqualTo(testEnv.operatorId); - } - - } - } -} - - - diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenNftTransferIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenNftTransferIntegrationTest.java deleted file mode 100644 index 0d552b8fa5..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenNftTransferIntegrationTest.java +++ /dev/null @@ -1,173 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * Copyright (C) 2021 - 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.sdk.Status; -import com.hedera.hashgraph.sdk.TokenAssociateTransaction; -import com.hedera.hashgraph.sdk.TokenCreateTransaction; -import com.hedera.hashgraph.sdk.TokenGrantKycTransaction; -import com.hedera.hashgraph.sdk.TokenMintTransaction; -import com.hedera.hashgraph.sdk.TokenType; -import com.hedera.hashgraph.sdk.TokenWipeTransaction; -import com.hedera.hashgraph.sdk.TransactionResponse; -import com.hedera.hashgraph.sdk.TransferTransaction; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.Collections; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -class TokenNftTransferIntegrationTest { - @Test - @DisplayName("Can transfer NFTs") - void canTransferNfts() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var key = PrivateKey.generateED25519(); - - TransactionResponse response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = response.getReceipt(testEnv.client).accountId; - assertThat(accountId).isNotNull(); - - response = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client); - - var tokenId = response.getReceipt(testEnv.client).tokenId; - assertThat(tokenId).isNotNull(); - - var mintReceipt = new TokenMintTransaction() - .setTokenId(tokenId) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenAssociateTransaction() - .setAccountId(accountId) - .setTokenIds(Collections.singletonList(tokenId)) - .freezeWith(testEnv.client) - .signWithOperator(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenGrantKycTransaction() - .setAccountId(accountId) - .setTokenId(tokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var serialsToTransfer = new ArrayList(mintReceipt.serials.subList(0, 4)); - var transfer = new TransferTransaction(); - for (var serial : serialsToTransfer) { - transfer.addNftTransfer(tokenId.nft(serial), testEnv.operatorId, accountId); - } - transfer.execute(testEnv.client).getReceipt(testEnv.client); - - new TokenWipeTransaction() - .setTokenId(tokenId) - .setAccountId(accountId) - .setSerials(serialsToTransfer) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot transfer NFTs you don't own") - void cannotTransferUnownedNfts() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var key = PrivateKey.generateED25519(); - - TransactionResponse response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = response.getReceipt(testEnv.client).accountId; - assertThat(accountId).isNotNull(); - - response = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client); - - var tokenId = response.getReceipt(testEnv.client).tokenId; - assertThat(tokenId).isNotNull(); - - var mintReceipt = new TokenMintTransaction() - .setTokenId(tokenId) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenAssociateTransaction() - .setAccountId(accountId) - .setTokenIds(Collections.singletonList(tokenId)) - .freezeWith(testEnv.client) - .signWithOperator(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var serialsToTransfer = new ArrayList(mintReceipt.serials.subList(0, 4)); - var transfer = new TransferTransaction(); - for (var serial : serialsToTransfer) { - // Try to transfer in wrong direction - transfer.addNftTransfer(tokenId.nft(serial), accountId, testEnv.operatorId); - } - transfer.freezeWith(testEnv.client).sign(key); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - transfer.execute(testEnv.client).getReceipt(testEnv.client); - }).withMessageContaining(Status.SENDER_DOES_NOT_OWN_NFT_SERIAL_NO.toString()); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenPauseIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenPauseIntegrationTest.java deleted file mode 100644 index f637439353..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenPauseIntegrationTest.java +++ /dev/null @@ -1,85 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import static org.junit.jupiter.api.Assertions.assertThrows; - -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.sdk.TokenAssociateTransaction; -import com.hedera.hashgraph.sdk.TokenCreateTransaction; -import com.hedera.hashgraph.sdk.TokenPauseTransaction; -import com.hedera.hashgraph.sdk.TransferTransaction; -import java.util.Collections; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -public class TokenPauseIntegrationTest { - - @Test - @DisplayName("Can execute token pause transaction") - void canExecuteTokenPauseTransaction() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - var accountKey = PrivateKey.generateED25519(); - var testTokenAmount = 10; - var accountId = new AccountCreateTransaction().setKey(accountKey).setInitialBalance(new Hbar(2)) - .execute(testEnv.client).getReceipt(testEnv.client) - .accountId; - - var tokenId = new TokenCreateTransaction().setTokenName("ffff").setTokenSymbol("F").setInitialSupply(1000000) - .setDecimals(3).setTreasuryAccountId(testEnv.operatorId).setAdminKey(testEnv.operatorKey) - .setPauseKey(testEnv.operatorKey).setFreezeDefault(false).execute(testEnv.client) - .getReceipt(testEnv.client).tokenId; - - new TokenAssociateTransaction().setAccountId(accountId).setTokenIds(Collections.singletonList(tokenId)) - .freezeWith(testEnv.client).sign(accountKey).execute(testEnv.client).getReceipt(testEnv.client); - - new TransferTransaction().addTokenTransfer(tokenId, accountId, testTokenAmount) - .addTokenTransfer(tokenId, testEnv.operatorId, -testTokenAmount).execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenPauseTransaction().setTokenId(tokenId).freezeWith(testEnv.client).execute(testEnv.client) - .getReceipt(testEnv.client); - - assertThrows(ReceiptStatusException.class, () -> { - new TransferTransaction().addTokenTransfer(tokenId, accountId, testTokenAmount) - .addTokenTransfer(tokenId, testEnv.operatorId, -testTokenAmount).freezeWith(testEnv.client) - .sign(accountKey).execute(testEnv.client).getReceipt(testEnv.client); - }); - - } - } - - @Test - @DisplayName("Cannot pause with no token ID") - void cannotPauseWithNoTokenId() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - assertThrows(PrecheckStatusException.class, () -> { - new TokenPauseTransaction().execute(testEnv.client).getReceipt(testEnv.client); - }); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenRejectFlowIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenRejectFlowIntegrationTest.java deleted file mode 100644 index 6571974341..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenRejectFlowIntegrationTest.java +++ /dev/null @@ -1,263 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -import com.hedera.hashgraph.sdk.AccountBalanceQuery; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.TokenAssociateTransaction; -import com.hedera.hashgraph.sdk.TokenDeleteTransaction; -import com.hedera.hashgraph.sdk.TokenMintTransaction; -import com.hedera.hashgraph.sdk.TokenNftInfoQuery; -import com.hedera.hashgraph.sdk.TokenRejectFlow; -import com.hedera.hashgraph.sdk.TransferTransaction; -import java.util.Collections; -import java.util.List; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -public class TokenRejectFlowIntegrationTest { - - @Test - @DisplayName("Can execute TokenReject flow for Fungible Token") - void canExecuteTokenRejectFlowForFungibleToken() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var ftTokenId = EntityHelper.createFungibleToken(testEnv, 3); - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); - - // manually associate ft - new TokenAssociateTransaction() - .setAccountId(receiverAccountId) - .setTokenIds(Collections.singletonList(ftTokenId)) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // transfer fts to the receiver - new TransferTransaction() - .addTokenTransfer(ftTokenId, testEnv.operatorId, -10) - .addTokenTransfer(ftTokenId, receiverAccountId, 10) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // execute the token reject flow - new TokenRejectFlow() - .setOwnerId(receiverAccountId) - .addTokenId(ftTokenId) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // verify the tokens are transferred back to the treasury - var treasuryAccountBalance = new AccountBalanceQuery() - .setAccountId(testEnv.operatorId) - .execute(testEnv.client); - - assertThat(treasuryAccountBalance.tokens.get(ftTokenId)).isEqualTo(1_000_000); - - // verify the allowance - should be 0, because TokenRejectFlow dissociates - assertThatExceptionOfType(Exception.class).isThrownBy(() -> { - new TransferTransaction() - .addTokenTransfer(ftTokenId, testEnv.operatorId, -10) - .addTokenTransfer(ftTokenId, receiverAccountId, 10) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining("TOKEN_NOT_ASSOCIATED_TO_ACCOUNT"); - - new TokenDeleteTransaction() - .setTokenId(ftTokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can execute TokenReject flow for Fungible Token (Async)") - void canExecuteTokenRejectFlowForFungibleTokenAsync() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var ftTokenId = EntityHelper.createFungibleToken(testEnv, 3); - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); - - // manually associate ft - new TokenAssociateTransaction() - .setAccountId(receiverAccountId) - .setTokenIds(Collections.singletonList(ftTokenId)) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // transfer fts to the receiver - new TransferTransaction() - .addTokenTransfer(ftTokenId, testEnv.operatorId, -10) - .addTokenTransfer(ftTokenId, receiverAccountId, 10) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // execute the token reject flow - new TokenRejectFlow() - .setOwnerId(receiverAccountId) - .addTokenId(ftTokenId) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .executeAsync(testEnv.client).get() - .getReceipt(testEnv.client); - - // verify the tokens are transferred back to the treasury - var treasuryAccountBalance = new AccountBalanceQuery() - .setAccountId(testEnv.operatorId) - .execute(testEnv.client); - - assertThat(treasuryAccountBalance.tokens.get(ftTokenId)).isEqualTo(1_000_000); - - // verify the tokens are not associated with the receiver - assertThatExceptionOfType(Exception.class).isThrownBy(() -> { - new TransferTransaction() - .addTokenTransfer(ftTokenId, testEnv.operatorId, -10) - .addTokenTransfer(ftTokenId, receiverAccountId, 10) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining("TOKEN_NOT_ASSOCIATED_TO_ACCOUNT"); - - new TokenDeleteTransaction() - .setTokenId(ftTokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can execute TokenReject flow for NFT") - void canExecuteTokenRejectFlowForNft() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var nftTokenId = EntityHelper.createNft(testEnv); - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); - - var mintReceiptToken = new TokenMintTransaction() - .setTokenId(nftTokenId) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var nftSerials = mintReceiptToken.serials; - - // manually associate bft - new TokenAssociateTransaction() - .setAccountId(receiverAccountId) - .setTokenIds(Collections.singletonList(nftTokenId)) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // transfer nfts to the receiver - new TransferTransaction() - .addNftTransfer(nftTokenId.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) - .addNftTransfer(nftTokenId.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // execute the token reject flow - new TokenRejectFlow() - .setOwnerId(receiverAccountId) - .setNftIds(List.of(nftTokenId.nft(nftSerials.get(0)), nftTokenId.nft(nftSerials.get(1)))) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // verify the token is transferred back to the treasury - var nftTokenIdNftInfo = new TokenNftInfoQuery() - .setNftId(nftTokenId.nft(nftSerials.get(1))) - .execute(testEnv.client); - - assertThat(nftTokenIdNftInfo.get(0).accountId).isEqualTo(testEnv.operatorId); - - // verify the tokens are not associated with the receiver - assertThatExceptionOfType(Exception.class).isThrownBy(() -> { - new TransferTransaction() - .addNftTransfer(nftTokenId.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining("TOKEN_NOT_ASSOCIATED_TO_ACCOUNT"); - - new TokenDeleteTransaction() - .setTokenId(nftTokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot execute TokenReject flow for NFT when rejecting Only Part Of Owned NFTs") - void canExecuteTokenRejectFlowForNftWhenRejectingOnlyPartOfOwnedNFTs() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var nftTokenId1 = EntityHelper.createNft(testEnv); - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); - - var mintReceiptToken = new TokenMintTransaction() - .setTokenId(nftTokenId1) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var nftSerials = mintReceiptToken.serials; - - // manually associate bft - new TokenAssociateTransaction() - .setAccountId(receiverAccountId) - .setTokenIds(Collections.singletonList(nftTokenId1)) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // transfer nfts to the receiver - new TransferTransaction() - .addNftTransfer(nftTokenId1.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) - .addNftTransfer(nftTokenId1.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // execute the token reject flow - assertThatExceptionOfType(Exception.class).isThrownBy(() -> { - new TokenRejectFlow() - .setOwnerId(receiverAccountId) - .addNftId(nftTokenId1.nft(nftSerials.get(1))) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining("ACCOUNT_STILL_OWNS_NFTS"); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenRejectIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenRejectIntegrationTest.java deleted file mode 100644 index 01746132d4..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenRejectIntegrationTest.java +++ /dev/null @@ -1,1071 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -import com.hedera.hashgraph.sdk.AccountAllowanceApproveTransaction; -import com.hedera.hashgraph.sdk.AccountBalanceQuery; -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.TokenAssociateTransaction; -import com.hedera.hashgraph.sdk.TokenCreateTransaction; -import com.hedera.hashgraph.sdk.TokenDeleteTransaction; -import com.hedera.hashgraph.sdk.TokenFreezeTransaction; -import com.hedera.hashgraph.sdk.TokenMintTransaction; -import com.hedera.hashgraph.sdk.TokenNftInfoQuery; -import com.hedera.hashgraph.sdk.TokenPauseTransaction; -import com.hedera.hashgraph.sdk.TokenRejectTransaction; -import com.hedera.hashgraph.sdk.TokenSupplyType; -import com.hedera.hashgraph.sdk.TokenType; -import com.hedera.hashgraph.sdk.TransactionId; -import com.hedera.hashgraph.sdk.TransferTransaction; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -public class TokenRejectIntegrationTest { - - @Test - @DisplayName("Can execute TokenReject transaction for Fungible Token") - void canExecuteTokenRejectTransactionForFungibleToken() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var tokenId1 = EntityHelper.createFungibleToken(testEnv, 3); - var tokenId2 = EntityHelper.createFungibleToken(testEnv, 3); - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 100); - - // transfer fts to the receiver - new TransferTransaction() - .addTokenTransfer(tokenId1, testEnv.operatorId, -10) - .addTokenTransfer(tokenId1, receiverAccountId, 10) - .addTokenTransfer(tokenId2, testEnv.operatorId, -10) - .addTokenTransfer(tokenId2, receiverAccountId, 10) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // reject the token - new TokenRejectTransaction() - .setOwnerId(receiverAccountId) - .setTokenIds(List.of(tokenId1, tokenId2)) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // verify the balance of the receiver is 0 - var receiverAccountBalance = new AccountBalanceQuery() - .setAccountId(receiverAccountId) - .execute(testEnv.client); - - assertThat(receiverAccountBalance.tokens.get(tokenId1)).isEqualTo(0); - assertThat(receiverAccountBalance.tokens.get(tokenId2)).isEqualTo(0); - - // verify the tokens are transferred back to the treasury - var treasuryAccountBalance = new AccountBalanceQuery() - .setAccountId(testEnv.operatorId) - .execute(testEnv.client); - - assertThat(treasuryAccountBalance.tokens.get(tokenId1)).isEqualTo(1_000_000); - assertThat(treasuryAccountBalance.tokens.get(tokenId2)).isEqualTo(1_000_000); - - new TokenDeleteTransaction() - .setTokenId(tokenId1) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenDeleteTransaction() - .setTokenId(tokenId2) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can execute TokenReject transaction for NFT") - void canExecuteTokenRejectTransactionForNft() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var tokenId1 = EntityHelper.createNft(testEnv); - var tokenId2 = EntityHelper.createNft(testEnv); - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 100); - - var mintReceiptToken1 = new TokenMintTransaction() - .setTokenId(tokenId1) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var mintReceiptToken2 = new TokenMintTransaction() - .setTokenId(tokenId2) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var nftSerials = mintReceiptToken2.serials; - - // transfer nfts to the receiver - new TransferTransaction() - .addNftTransfer(tokenId1.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) - .addNftTransfer(tokenId1.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) - .addNftTransfer(tokenId2.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) - .addNftTransfer(tokenId2.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // reject one of the nfts - new TokenRejectTransaction() - .setOwnerId(receiverAccountId) - .setNftIds(List.of(tokenId1.nft(nftSerials.get(1)), tokenId2.nft(nftSerials.get(1)))) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // verify the balance is decremented by 1 - var receiverAccountBalance = new AccountBalanceQuery() - .setAccountId(receiverAccountId) - .execute(testEnv.client); - - assertThat(receiverAccountBalance.tokens.get(tokenId1)).isEqualTo(1); - assertThat(receiverAccountBalance.tokens.get(tokenId2)).isEqualTo(1); - - // verify the token is transferred back to the treasury - var tokenId1NftInfo = new TokenNftInfoQuery() - .setNftId(tokenId1.nft(nftSerials.get(1))) - .execute(testEnv.client); - - assertThat(tokenId1NftInfo.get(0).accountId).isEqualTo(testEnv.operatorId); - - var tokenId2NftInfo = new TokenNftInfoQuery() - .setNftId(tokenId2.nft(nftSerials.get(1))) - .execute(testEnv.client); - - assertThat(tokenId2NftInfo.get(0).accountId).isEqualTo(testEnv.operatorId); - - new TokenDeleteTransaction() - .setTokenId(tokenId1) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenDeleteTransaction() - .setTokenId(tokenId2) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can execute TokenReject transaction for FT and NFT in One Tx") - void canExecuteTokenRejectTransactionForFtAndNftInOneTx() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var ftTokenId1 = EntityHelper.createFungibleToken(testEnv, 3); - var ftTokenId2 = EntityHelper.createFungibleToken(testEnv, 3); - var nftTokenId1 = EntityHelper.createNft(testEnv); - var nftTokenId2 = EntityHelper.createNft(testEnv); - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 100); - - var mintReceiptNftToken1 = new TokenMintTransaction() - .setTokenId(nftTokenId1) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var mintReceiptNftToken2 = new TokenMintTransaction() - .setTokenId(nftTokenId2) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var nftSerials = mintReceiptNftToken2.serials; - - // transfer fts to the receiver - new TransferTransaction() - .addTokenTransfer(ftTokenId1, testEnv.operatorId, -10) - .addTokenTransfer(ftTokenId1, receiverAccountId, 10) - .addTokenTransfer(ftTokenId2, testEnv.operatorId, -10) - .addTokenTransfer(ftTokenId2, receiverAccountId, 10) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // transfer nfts to the receiver - new TransferTransaction() - .addNftTransfer(nftTokenId1.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) - .addNftTransfer(nftTokenId1.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) - .addNftTransfer(nftTokenId2.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) - .addNftTransfer(nftTokenId2.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // reject the token - new TokenRejectTransaction() - .setOwnerId(receiverAccountId) - .setTokenIds(List.of(ftTokenId1, ftTokenId2)) - .setNftIds(List.of(nftTokenId1.nft(nftSerials.get(1)), nftTokenId2.nft(nftSerials.get(1)))) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // verify the balance of the receiver is 0 - var receiverAccountBalance = new AccountBalanceQuery() - .setAccountId(receiverAccountId) - .execute(testEnv.client); - - assertThat(receiverAccountBalance.tokens.get(ftTokenId1)).isEqualTo(0); - assertThat(receiverAccountBalance.tokens.get(ftTokenId2)).isEqualTo(0); - assertThat(receiverAccountBalance.tokens.get(nftTokenId1)).isEqualTo(1); - assertThat(receiverAccountBalance.tokens.get(nftTokenId2)).isEqualTo(1); - - // verify the tokens are transferred back to the treasury - var treasuryAccountBalance = new AccountBalanceQuery() - .setAccountId(testEnv.operatorId) - .execute(testEnv.client); - - assertThat(treasuryAccountBalance.tokens.get(ftTokenId1)).isEqualTo(1_000_000); - assertThat(treasuryAccountBalance.tokens.get(ftTokenId2)).isEqualTo(1_000_000); - - var tokenId1NftInfo = new TokenNftInfoQuery() - .setNftId(nftTokenId1.nft(nftSerials.get(1))) - .execute(testEnv.client); - - assertThat(tokenId1NftInfo.get(0).accountId).isEqualTo(testEnv.operatorId); - - var tokenId2NftInfo = new TokenNftInfoQuery() - .setNftId(nftTokenId2.nft(nftSerials.get(1))) - .execute(testEnv.client); - - assertThat(tokenId2NftInfo.get(0).accountId).isEqualTo(testEnv.operatorId); - - new TokenDeleteTransaction() - .setTokenId(ftTokenId1) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenDeleteTransaction() - .setTokenId(ftTokenId2) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenDeleteTransaction() - .setTokenId(nftTokenId1) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenDeleteTransaction() - .setTokenId(nftTokenId2) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can execute TokenReject transaction for FT and NFT when Treasury receiverSigRequired is Enabled") - void canExecuteTokenRejectTransactionForFtAndNftWhenTreasuryReceiverSigRequiredIsEnabled() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 100); - - var treasuryAccountKey = PrivateKey.generateED25519(); - var treasuryAccountId = new AccountCreateTransaction() - .setKey(treasuryAccountKey) - .setInitialBalance(new Hbar(0)) - .setReceiverSignatureRequired(true) - .setMaxAutomaticTokenAssociations(100) - .freezeWith(testEnv.client) - .sign(treasuryAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .accountId; - - var ftTokenId = new TokenCreateTransaction() - .setTokenName("Test Fungible Token") - .setTokenSymbol("TFT") - .setTokenMemo("I was created for integration tests") - .setDecimals(18) - .setInitialSupply(1_000_000) - .setMaxSupply(1_000_000) - .setTreasuryAccountId(treasuryAccountId) - .setSupplyType(TokenSupplyType.FINITE) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setMetadataKey(testEnv.operatorKey) - .freezeWith(testEnv.client) - .sign(treasuryAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId; - - // transfer fts to the receiver - new TransferTransaction() - .addTokenTransfer(ftTokenId, treasuryAccountId, -10) - .addTokenTransfer(ftTokenId, receiverAccountId, 10) - .freezeWith(testEnv.client) - .sign(treasuryAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // reject the token - new TokenRejectTransaction() - .setOwnerId(receiverAccountId) - .addTokenId(ftTokenId) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // verify the balance of the receiver is 0 - var receiverAccountBalanceFt = new AccountBalanceQuery() - .setAccountId(receiverAccountId) - .execute(testEnv.client); - - assertThat(receiverAccountBalanceFt.tokens.get(ftTokenId)).isEqualTo(0); - - // verify the tokens are transferred back to the treasury - var treasuryAccountBalance = new AccountBalanceQuery() - .setAccountId(treasuryAccountId) - .execute(testEnv.client); - - assertThat(treasuryAccountBalance.tokens.get(ftTokenId)).isEqualTo(1_000_000); - - // same test for nft - - var nftTokenId = new TokenCreateTransaction() - .setTokenName("Test NFT") - .setTokenSymbol("TNFT") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(treasuryAccountId) - .setSupplyType(TokenSupplyType.FINITE) - .setMaxSupply(10) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setMetadataKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .freezeWith(testEnv.client) - .sign(treasuryAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId; - - var mintReceiptNftToken = new TokenMintTransaction() - .setTokenId(nftTokenId) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var nftSerials = mintReceiptNftToken.serials; - - // transfer nfts to the receiver - new TransferTransaction() - .addNftTransfer(nftTokenId.nft(nftSerials.get(0)), treasuryAccountId, receiverAccountId) - .addNftTransfer(nftTokenId.nft(nftSerials.get(1)), treasuryAccountId, receiverAccountId) - .freezeWith(testEnv.client) - .sign(treasuryAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // reject the token - new TokenRejectTransaction() - .setOwnerId(receiverAccountId) - .addNftId(nftTokenId.nft(nftSerials.get(1))) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // verify the balance is decremented by 1 - var receiverAccountBalanceNft = new AccountBalanceQuery() - .setAccountId(receiverAccountId) - .execute(testEnv.client); - - assertThat(receiverAccountBalanceNft.tokens.get(nftTokenId)).isEqualTo(1); - - // verify the token is transferred back to the treasury - var nftTokenIdInfo = new TokenNftInfoQuery() - .setNftId(nftTokenId.nft(nftSerials.get(1))) - .execute(testEnv.client); - - assertThat(nftTokenIdInfo.get(0).accountId).isEqualTo(treasuryAccountId); - - new TokenDeleteTransaction() - .setTokenId(ftTokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenDeleteTransaction() - .setTokenId(nftTokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot execute TokenReject transaction for FT and NFT when Token is Frozen") - void canExecuteTokenRejectTransactionForFtAndNftWhenTokenIsFrozen() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var ftTokenId = EntityHelper.createFungibleToken(testEnv, 18); - var nftTokenId = EntityHelper.createNft(testEnv); - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 100); - - // transfer fts to the receiver - new TransferTransaction() - .addTokenTransfer(ftTokenId, testEnv.operatorId, -10) - .addTokenTransfer(ftTokenId, receiverAccountId, 10) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // freeze ft - new TokenFreezeTransaction() - .setTokenId(ftTokenId) - .setAccountId(receiverAccountId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // reject the token - should fail with ACCOUNT_FROZEN_FOR_TOKEN - assertThatExceptionOfType(Exception.class).isThrownBy(() -> { - new TokenRejectTransaction() - .setOwnerId(receiverAccountId) - .addTokenId(ftTokenId) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining("ACCOUNT_FROZEN_FOR_TOKEN"); - - // same test for nft - - var mintReceipt = new TokenMintTransaction() - .setTokenId(nftTokenId) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var nftSerials = mintReceipt.serials; - - // transfer nfts to the receiver - new TransferTransaction() - .addNftTransfer(nftTokenId.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) - .addNftTransfer(nftTokenId.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // freeze nft - new TokenFreezeTransaction() - .setTokenId(nftTokenId) - .setAccountId(receiverAccountId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // reject the token - should fail with ACCOUNT_FROZEN_FOR_TOKEN - assertThatExceptionOfType(Exception.class).isThrownBy(() -> { - new TokenRejectTransaction() - .setOwnerId(receiverAccountId) - .addNftId(nftTokenId.nft(nftSerials.get(1))) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining("ACCOUNT_FROZEN_FOR_TOKEN"); - - new TokenDeleteTransaction() - .setTokenId(ftTokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenDeleteTransaction() - .setTokenId(nftTokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot execute TokenReject transaction for FT and NFT when Token is Paused") - void canExecuteTokenRejectTransactionForFtAndNftWhenTokenIsPaused() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var ftTokenId = EntityHelper.createFungibleToken(testEnv, 18); - var nftTokenId = EntityHelper.createNft(testEnv); - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 100); - - // transfer fts to the receiver - new TransferTransaction() - .addTokenTransfer(ftTokenId, testEnv.operatorId, -10) - .addTokenTransfer(ftTokenId, receiverAccountId, 10) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // pause ft - new TokenPauseTransaction() - .setTokenId(ftTokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // reject the token - should fail with TOKEN_IS_PAUSED - assertThatExceptionOfType(Exception.class).isThrownBy(() -> { - new TokenRejectTransaction() - .setOwnerId(receiverAccountId) - .addTokenId(ftTokenId) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining("TOKEN_IS_PAUSED"); - - // same test for nft - - var mintReceipt = new TokenMintTransaction() - .setTokenId(nftTokenId) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var nftSerials = mintReceipt.serials; - - // transfer nfts to the receiver - new TransferTransaction() - .addNftTransfer(nftTokenId.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) - .addNftTransfer(nftTokenId.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // pause nft - new TokenPauseTransaction() - .setTokenId(nftTokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // reject the token - should fail with TOKEN_IS_PAUSED - assertThatExceptionOfType(Exception.class).isThrownBy(() -> { - new TokenRejectTransaction() - .setOwnerId(receiverAccountId) - .addNftId(nftTokenId.nft(nftSerials.get(1))) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining("TOKEN_IS_PAUSED"); - - } - } - - @Test - @Disabled // temp disabled till issue re nfts will be resolved on services side - @DisplayName("Can remove allowance when executing TokenReject transaction for FT and NFT") - void canRemoveAllowanceWhenExecutingTokenRejectForFtAndNft() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var ftTokenId = EntityHelper.createFungibleToken(testEnv, 3); - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, -1); - var spenderAccountKey = PrivateKey.generateED25519(); - var spenderAccountId = EntityHelper.createAccount(testEnv, spenderAccountKey, -1); - - // transfer ft to the receiver - new TransferTransaction() - .addTokenTransfer(ftTokenId, testEnv.operatorId, -10) - .addTokenTransfer(ftTokenId, receiverAccountId, 10) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // approve allowance to the spender - new AccountAllowanceApproveTransaction() - .approveTokenAllowance(ftTokenId, receiverAccountId, spenderAccountId, 10) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // verify the spender has allowance - new TransferTransaction() - .addApprovedTokenTransfer(ftTokenId, receiverAccountId, -5) - .addTokenTransfer(ftTokenId, spenderAccountId, 5) - .setTransactionId(TransactionId.generate(spenderAccountId)) - .freezeWith(testEnv.client) - .sign(spenderAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // reject the token - new TokenRejectTransaction() - .setOwnerId(receiverAccountId) - .addTokenId(ftTokenId) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // verify the allowance - should be 0 , because the receiver is no longer the owner - assertThatExceptionOfType(Exception.class).isThrownBy(() -> { - new TransferTransaction() - .addApprovedTokenTransfer(ftTokenId, receiverAccountId, -5) - .addTokenTransfer(ftTokenId, spenderAccountId, 5) - .setTransactionId(TransactionId.generate(spenderAccountId)) - .freezeWith(testEnv.client) - .sign(spenderAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining("SPENDER_DOES_NOT_HAVE_ALLOWANCE"); - - // same test for nft - - var nftTokenId = EntityHelper.createNft(testEnv); - - var mintReceipt = new TokenMintTransaction() - .setTokenId(nftTokenId) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var nftSerials = mintReceipt.serials; - - // transfer nfts to the receiver - new TransferTransaction() - .addNftTransfer(nftTokenId.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) - .addNftTransfer(nftTokenId.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) - .addNftTransfer(nftTokenId.nft(nftSerials.get(2)), testEnv.operatorId, receiverAccountId) - .addNftTransfer(nftTokenId.nft(nftSerials.get(3)), testEnv.operatorId, receiverAccountId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // approve allowance to the spender - new AccountAllowanceApproveTransaction() - .approveTokenNftAllowance(nftTokenId.nft(nftSerials.get(0)), receiverAccountId, spenderAccountId) - .approveTokenNftAllowance(nftTokenId.nft(nftSerials.get(1)), receiverAccountId, spenderAccountId) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // verify the spender has allowance - new TransferTransaction() - .addApprovedNftTransfer(nftTokenId.nft(nftSerials.get(0)), receiverAccountId, spenderAccountId) - .setTransactionId(TransactionId.generate(spenderAccountId)) - .freezeWith(testEnv.client) - .sign(spenderAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // reject the token - new TokenRejectTransaction() - .setOwnerId(receiverAccountId) - .setNftIds(List.of(nftTokenId.nft(nftSerials.get(1)), nftTokenId.nft(nftSerials.get(2)))) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // verify the allowance - should be 0 , because the receiver is no longer the owner - assertThatExceptionOfType(Exception.class).isThrownBy(() -> { - new TransferTransaction() - .addApprovedNftTransfer(nftTokenId.nft(nftSerials.get(1)), receiverAccountId, spenderAccountId) - .addApprovedNftTransfer(nftTokenId.nft(nftSerials.get(2)), receiverAccountId, spenderAccountId) - .setTransactionId(TransactionId.generate(spenderAccountId)) - .freezeWith(testEnv.client) - .sign(spenderAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining("SPENDER_DOES_NOT_HAVE_ALLOWANCE"); - - new TokenDeleteTransaction() - .setTokenId(ftTokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenDeleteTransaction() - .setTokenId(nftTokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot reject NFT when executing TokenReject with Add or Set TokenId") - void cannotRejectNftWhenUsingAddOrSetTokenId() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var nftTokenId = EntityHelper.createNft(testEnv); - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 100); - - var mintReceiptNftToken = new TokenMintTransaction() - .setTokenId(nftTokenId) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var nftSerials = mintReceiptNftToken.serials; - - // transfer nfts to the receiver - new TransferTransaction() - .addNftTransfer(nftTokenId.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) - .addNftTransfer(nftTokenId.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) - .addNftTransfer(nftTokenId.nft(nftSerials.get(2)), testEnv.operatorId, receiverAccountId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // reject the whole collection (addTokenId) - should fail - assertThatExceptionOfType(Exception.class).isThrownBy(() -> { - new TokenRejectTransaction() - .setOwnerId(receiverAccountId) - .addTokenId(nftTokenId) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining("ACCOUNT_AMOUNT_TRANSFERS_ONLY_ALLOWED_FOR_FUNGIBLE_COMMON"); - - // reject the whole collection (setTokenIds) - should fail - assertThatExceptionOfType(Exception.class).isThrownBy(() -> { - new TokenRejectTransaction() - .setOwnerId(receiverAccountId) - .setTokenIds(List.of(nftTokenId)) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining("ACCOUNT_AMOUNT_TRANSFERS_ONLY_ALLOWED_FOR_FUNGIBLE_COMMON"); - - new TokenDeleteTransaction() - .setTokenId(nftTokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot Reject a Token when executing TokenReject and Duplicating Token Reference") - void cannotRejectTokenWhenExecutingTokenRejectAndDuplicatingTokenReference() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var ftTokenId = EntityHelper.createFungibleToken(testEnv, 3); - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 100); - - // transfer fts to the receiver - new TransferTransaction() - .addTokenTransfer(ftTokenId, testEnv.operatorId, -10) - .addTokenTransfer(ftTokenId, receiverAccountId, 10) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // reject the token with duplicate token id - should fail with TOKEN_REFERENCE_REPEATED - assertThatExceptionOfType(Exception.class).isThrownBy(() -> { - new TokenRejectTransaction() - .setOwnerId(receiverAccountId) - .setTokenIds(List.of(ftTokenId, ftTokenId)) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining("TOKEN_REFERENCE_REPEATED"); - - // same test for nft - - var nftTokenId = EntityHelper.createNft(testEnv); - - var mintReceipt = new TokenMintTransaction() - .setTokenId(nftTokenId) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var nftSerials = mintReceipt.serials; - - // transfer nfts to the receiver - new TransferTransaction() - .addNftTransfer(nftTokenId.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // reject the nft with duplicate nft id - should fail with TOKEN_REFERENCE_REPEATED - assertThatExceptionOfType(Exception.class).isThrownBy(() -> { - new TokenRejectTransaction() - .setOwnerId(receiverAccountId) - .setNftIds(List.of(nftTokenId.nft(nftSerials.get(0)), nftTokenId.nft(nftSerials.get(0)))) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining("TOKEN_REFERENCE_REPEATED"); - - new TokenDeleteTransaction() - .setTokenId(ftTokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenDeleteTransaction() - .setTokenId(nftTokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot Reject a Token when Owner Has Empty Balance") - void cannotRejectTokenWhenOwnerHasEmptyBalance() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var ftTokenId = EntityHelper.createFungibleToken(testEnv, 3); - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 100); - - // skip the transfer - // associate the receiver - new TokenAssociateTransaction() - .setAccountId(receiverAccountId) - .setTokenIds(Collections.singletonList(ftTokenId)) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // reject the token - should fail with INSUFFICIENT_TOKEN_BALANCE - assertThatExceptionOfType(Exception.class).isThrownBy(() -> { - new TokenRejectTransaction() - .setOwnerId(receiverAccountId) - .addTokenId(ftTokenId) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining("INSUFFICIENT_TOKEN_BALANCE"); - - // same test for nft - - var nftTokenId = EntityHelper.createNft(testEnv); - - var mintReceipt = new TokenMintTransaction() - .setTokenId(nftTokenId) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var nftSerials = mintReceipt.serials; - - // skip the transfer - // associate the receiver - new TokenAssociateTransaction() - .setAccountId(receiverAccountId) - .setTokenIds(Collections.singletonList(nftTokenId)) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // reject the nft - should fail with INVALID_OWNER_ID - assertThatExceptionOfType(Exception.class).isThrownBy(() -> { - new TokenRejectTransaction() - .setOwnerId(receiverAccountId) - .addNftId(nftTokenId.nft(nftSerials.get(0))) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining("INVALID_OWNER_ID"); - - new TokenDeleteTransaction() - .setTokenId(ftTokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenDeleteTransaction() - .setTokenId(nftTokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot Reject a Token when Treasury Rejects itself") - void cannotRejectTokenWhenTreasuryRejects() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var ftTokenId = EntityHelper.createFungibleToken(testEnv, 3); - - // skip the transfer - // reject the token with the treasury - should fail with ACCOUNT_IS_TREASURY - assertThatExceptionOfType(Exception.class).isThrownBy(() -> { - new TokenRejectTransaction() - .setOwnerId(testEnv.operatorId) - .addTokenId(ftTokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining("ACCOUNT_IS_TREASURY"); - - // same test for nft - - var nftTokenId = EntityHelper.createNft(testEnv); - - var mintReceipt = new TokenMintTransaction() - .setTokenId(nftTokenId) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var nftSerials = mintReceipt.serials; - - // skip the transfer - // reject the nft with the treasury - should fail with ACCOUNT_IS_TREASURY - assertThatExceptionOfType(Exception.class).isThrownBy(() -> { - new TokenRejectTransaction() - .setOwnerId(testEnv.operatorId) - .addNftId(nftTokenId.nft(nftSerials.get(0))) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining("ACCOUNT_IS_TREASURY"); - - } - } - - @Test - @DisplayName("Cannot Reject a Token with Invalid Signature") - void cannotRejectTokenWithInvalidSignature() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var ftTokenId = EntityHelper.createFungibleToken(testEnv, 3); - var randomKey = PrivateKey.generateED25519(); - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 100); - - // transfer fts to the receiver - new TransferTransaction() - .addTokenTransfer(ftTokenId, testEnv.operatorId, -10) - .addTokenTransfer(ftTokenId, receiverAccountId, 10) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // reject the token with different key - should fail with INVALID_SIGNATURE - assertThatExceptionOfType(Exception.class).isThrownBy(() -> { - new TokenRejectTransaction() - .setOwnerId(receiverAccountId) - .addTokenId(ftTokenId) - .freezeWith(testEnv.client) - .sign(randomKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining("INVALID_SIGNATURE"); - - new TokenDeleteTransaction() - .setTokenId(ftTokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot Reject a Token when Token Or NFT ID is not set") - void cannotRejectTokenWhenTokenOrNFTIdIsNotSet() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - // reject the token with invalid token - should fail with EMPTY_TOKEN_REFERENCE_LIST - assertThatExceptionOfType(Exception.class).isThrownBy(() -> { - new TokenRejectTransaction() - .setOwnerId(testEnv.operatorId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining("EMPTY_TOKEN_REFERENCE_LIST"); - - } - } - - @Test - @DisplayName("Cannot Reject a Token when executing TokenReject and Token Reference List Size Exceeded") - void cannotRejectTokenWhenTokenReferenceListSizeExceeded() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var ftTokenId = EntityHelper.createFungibleToken(testEnv, 18); - var receiverAccountKey = PrivateKey.generateED25519(); - var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, -1); - var nftTokenId = EntityHelper.createNft(testEnv); - - var mintReceipt = new TokenMintTransaction() - .setTokenId(nftTokenId) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var nftSerials = mintReceipt.serials; - - // transfer the tokens to the receiver - new TransferTransaction() - .addTokenTransfer(ftTokenId, testEnv.operatorId, -10) - .addTokenTransfer(ftTokenId, receiverAccountId, 10) - .addNftTransfer(nftTokenId.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) - .addNftTransfer(nftTokenId.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) - .addNftTransfer(nftTokenId.nft(nftSerials.get(2)), testEnv.operatorId, receiverAccountId) - .addNftTransfer(nftTokenId.nft(nftSerials.get(3)), testEnv.operatorId, receiverAccountId) - .addNftTransfer(nftTokenId.nft(nftSerials.get(4)), testEnv.operatorId, receiverAccountId) - .addNftTransfer(nftTokenId.nft(nftSerials.get(5)), testEnv.operatorId, receiverAccountId) - .addNftTransfer(nftTokenId.nft(nftSerials.get(6)), testEnv.operatorId, receiverAccountId) - .addNftTransfer(nftTokenId.nft(nftSerials.get(7)), testEnv.operatorId, receiverAccountId) - .addNftTransfer(nftTokenId.nft(nftSerials.get(8)), testEnv.operatorId, receiverAccountId) - .addNftTransfer(nftTokenId.nft(nftSerials.get(9)), testEnv.operatorId, receiverAccountId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // reject the token with 11 token references - should fail with TOKEN_REFERENCE_LIST_SIZE_LIMIT_EXCEEDED - assertThatExceptionOfType(Exception.class).isThrownBy(() -> { - new TokenRejectTransaction() - .setOwnerId(receiverAccountId) - .addTokenId(ftTokenId) - .setNftIds(List.of( - nftTokenId.nft(nftSerials.get(0)), - nftTokenId.nft(nftSerials.get(1)), - nftTokenId.nft(nftSerials.get(2)), - nftTokenId.nft(nftSerials.get(3)), - nftTokenId.nft(nftSerials.get(4)), - nftTokenId.nft(nftSerials.get(5)), - nftTokenId.nft(nftSerials.get(6)), - nftTokenId.nft(nftSerials.get(7)), - nftTokenId.nft(nftSerials.get(8)), - nftTokenId.nft(nftSerials.get(9)) - )) - .freezeWith(testEnv.client) - .sign(receiverAccountKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining("TOKEN_REFERENCE_LIST_SIZE_LIMIT_EXCEEDED"); - - new TokenDeleteTransaction() - .setTokenId(ftTokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenDeleteTransaction() - .setTokenId(nftTokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenRevokeKycIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenRevokeKycIntegrationTest.java deleted file mode 100644 index 31c57e6aa4..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenRevokeKycIntegrationTest.java +++ /dev/null @@ -1,196 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.sdk.Status; -import com.hedera.hashgraph.sdk.TokenAssociateTransaction; -import com.hedera.hashgraph.sdk.TokenCreateTransaction; -import com.hedera.hashgraph.sdk.TokenRevokeKycTransaction; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Collections; -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -class TokenRevokeKycIntegrationTest { - @Test - @DisplayName("Can revoke kyc to account with token") - void canRevokeKycAccountWithToken() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - new TokenAssociateTransaction() - .setAccountId(accountId) - .setTokenIds(Collections.singletonList(tokenId)) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenRevokeKycTransaction() - .setAccountId(accountId) - .setTokenId(tokenId) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot revoke kyc to account on token when token ID is not set") - void cannotRevokeKycToAccountOnTokenWhenTokenIDIsNotSet() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenRevokeKycTransaction() - .setAccountId(accountId) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_TOKEN_ID.toString()); - - } - } - - @Test - @DisplayName("Cannot revoke kyc to account on token when account ID is not set") - void cannotRevokeKycToAccountOnTokenWhenAccountIDIsNotSet() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var key = PrivateKey.generateED25519(); - - var response = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client); - - var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenRevokeKycTransaction() - .setTokenId(tokenId) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_ACCOUNT_ID.toString()); - - } - } - - @Test - @DisplayName("Cannot revoke kyc to account on token when account was not associated with") - void cannotRevokeKycToAccountOnTokenWhenAccountWasNotAssociatedWith() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenRevokeKycTransaction() - .setAccountId(accountId) - .setTokenId(tokenId) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.TOKEN_NOT_ASSOCIATED_TO_ACCOUNT.toString()); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenTransferIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenTransferIntegrationTest.java deleted file mode 100644 index 13e52fa8c5..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenTransferIntegrationTest.java +++ /dev/null @@ -1,172 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.CustomFixedFee; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.sdk.Status; -import com.hedera.hashgraph.sdk.TokenAssociateTransaction; -import com.hedera.hashgraph.sdk.TokenCreateTransaction; -import com.hedera.hashgraph.sdk.TokenDeleteTransaction; -import com.hedera.hashgraph.sdk.TokenGrantKycTransaction; -import com.hedera.hashgraph.sdk.TransactionResponse; -import com.hedera.hashgraph.sdk.TransferTransaction; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Collections; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -class TokenTransferIntegrationTest { - @Test - @DisplayName("Can transfer tokens") - void tokenTransferTest() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var key = PrivateKey.generateED25519(); - - TransactionResponse response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = response.getReceipt(testEnv.client).accountId; - assertThat(accountId).isNotNull(); - - response = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client); - - var tokenId = response.getReceipt(testEnv.client).tokenId; - assertThat(tokenId).isNotNull(); - - new TokenAssociateTransaction() - .setAccountId(accountId) - .setTokenIds(Collections.singletonList(tokenId)) - .freezeWith(testEnv.client) - .signWithOperator(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenGrantKycTransaction() - .setAccountId(accountId) - .setTokenId(tokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TransferTransaction() - .addTokenTransfer(tokenId, testEnv.operatorId, -10) - .addTokenTransfer(tokenId, accountId, 10) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot transfer tokens if balance is insufficient to pay fee") - void insufficientBalanceForFee() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - PrivateKey key1 = PrivateKey.generateED25519(); - PrivateKey key2 = PrivateKey.generateED25519(); - var accountId1 = new AccountCreateTransaction() - .setKey(key1) - .setInitialBalance(new Hbar(2)) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .accountId; - var accountId2 = new AccountCreateTransaction() - .setKey(key2) - .setInitialBalance(new Hbar(2)) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .accountId; - - var tokenId = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setInitialSupply(1) - .setCustomFees(Collections.singletonList(new CustomFixedFee() - .setAmount(5000_000_000L) - .setFeeCollectorAccountId(testEnv.operatorId))) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFeeScheduleKey(testEnv.operatorKey) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId; - - new TokenAssociateTransaction() - .setAccountId(accountId1) - .setTokenIds(Collections.singletonList(tokenId)) - .freezeWith(testEnv.client) - .sign(key1) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenAssociateTransaction() - .setAccountId(accountId2) - .setTokenIds(Collections.singletonList(tokenId)) - .freezeWith(testEnv.client) - .sign(key2) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TransferTransaction() - .addTokenTransfer(tokenId, testEnv.operatorId, -1) - .addTokenTransfer(tokenId, accountId1, 1) - .freezeWith(testEnv.client) - .sign(key1) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TransferTransaction() - .addTokenTransfer(tokenId, accountId1, -1) - .addTokenTransfer(tokenId, accountId2, 1) - .freezeWith(testEnv.client) - .sign(key1) - .sign(key2) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).satisfies(error -> assertThat(error.getMessage()).containsAnyOf( - Status.INSUFFICIENT_SENDER_ACCOUNT_BALANCE_FOR_CUSTOM_FEE.toString(), - Status.INSUFFICIENT_PAYER_BALANCE_FOR_CUSTOM_FEE.toString() - )); - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenUnfreezeIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenUnfreezeIntegrationTest.java deleted file mode 100644 index 678c49687c..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenUnfreezeIntegrationTest.java +++ /dev/null @@ -1,196 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.sdk.Status; -import com.hedera.hashgraph.sdk.TokenAssociateTransaction; -import com.hedera.hashgraph.sdk.TokenCreateTransaction; -import com.hedera.hashgraph.sdk.TokenUnfreezeTransaction; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Collections; -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -class TokenUnfreezeIntegrationTest { - @Test - @DisplayName("Can unfreeze account with token") - void canUnfreezeAccountWithToken() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - new TokenAssociateTransaction() - .setAccountId(accountId) - .setTokenIds(Collections.singletonList(tokenId)) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenUnfreezeTransaction() - .setAccountId(accountId) - .setTokenId(tokenId) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot unfreeze account on token when token ID is not set") - void cannotUnfreezeAccountOnTokenWhenTokenIDIsNotSet() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenUnfreezeTransaction() - .setAccountId(accountId) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_TOKEN_ID.toString()); - - } - } - - @Test - @DisplayName("Cannot unfreeze account on token when account ID is not set") - void cannotUnfreezeAccountOnTokenWhenAccountIDIsNotSet() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var key = PrivateKey.generateED25519(); - - var response = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client); - - var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenUnfreezeTransaction() - .setTokenId(tokenId) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_ACCOUNT_ID.toString()); - - } - } - - @Test - @DisplayName("Cannot unfreeze account on token when account was not associated with") - void cannotUnfreezeAccountOnTokenWhenAccountWasNotAssociatedWith() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUnfreezeTransaction() - .setAccountId(accountId) - .setTokenId(tokenId) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.TOKEN_NOT_ASSOCIATED_TO_ACCOUNT.toString()); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenUnpauseIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenUnpauseIntegrationTest.java deleted file mode 100644 index 7c0d037c2e..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenUnpauseIntegrationTest.java +++ /dev/null @@ -1,88 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import static org.junit.jupiter.api.Assertions.assertThrows; - -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.AccountDeleteTransaction; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.TokenAssociateTransaction; -import com.hedera.hashgraph.sdk.TokenCreateTransaction; -import com.hedera.hashgraph.sdk.TokenDeleteTransaction; -import com.hedera.hashgraph.sdk.TokenUnpauseTransaction; -import com.hedera.hashgraph.sdk.TokenWipeTransaction; -import com.hedera.hashgraph.sdk.TransferTransaction; -import java.util.Collections; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -public class TokenUnpauseIntegrationTest { - - @Test - @DisplayName("Can execute token unpause transaction") - void canExecuteTokenUnpauseTransaction() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - var accountKey = PrivateKey.generateED25519(); - var testTokenAmount = 10; - var accountId = new AccountCreateTransaction().setKey(accountKey).setInitialBalance(new Hbar(2)) - .execute(testEnv.client).getReceipt(testEnv.client).accountId; - - var tokenId = new TokenCreateTransaction().setTokenName("ffff").setTokenSymbol("F").setInitialSupply(1000000) - .setDecimals(3).setTreasuryAccountId(testEnv.operatorId).setAdminKey(testEnv.operatorKey) - .setPauseKey(testEnv.operatorKey).setWipeKey(testEnv.operatorKey).setFreezeDefault(false) - .execute(testEnv.client).getReceipt(testEnv.client).tokenId; - - new TokenAssociateTransaction().setAccountId(accountId).setTokenIds(Collections.singletonList(tokenId)) - .freezeWith(testEnv.client).sign(accountKey).execute(testEnv.client).getReceipt(testEnv.client); - - new TokenUnpauseTransaction().setTokenId(tokenId).freezeWith(testEnv.client).execute(testEnv.client) - .getReceipt(testEnv.client); - - new TransferTransaction().addTokenTransfer(tokenId, accountId, testTokenAmount) - .addTokenTransfer(tokenId, testEnv.operatorId, -testTokenAmount).execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenWipeTransaction().setTokenId(tokenId).setAccountId(accountId).setAmount(testTokenAmount) - .execute(testEnv.client).getReceipt(testEnv.client); - - new TokenDeleteTransaction().setTokenId(tokenId).execute(testEnv.client).getReceipt(testEnv.client); - - new AccountDeleteTransaction().setTransferAccountId(testEnv.operatorId).setAccountId(accountId) - .freezeWith(testEnv.client).sign(accountKey).execute(testEnv.client).getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot unpause with no token ID") - void cannotUnpauseWithNoTokenId() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - assertThrows(PrecheckStatusException.class, () -> { - new TokenUnpauseTransaction().execute(testEnv.client).getReceipt(testEnv.client); - }); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenUpdateIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenUpdateIntegrationTest.java deleted file mode 100644 index 9e5799c3e8..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenUpdateIntegrationTest.java +++ /dev/null @@ -1,2527 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -import com.hedera.hashgraph.sdk.KeyList; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.PublicKey; -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.sdk.Status; -import com.hedera.hashgraph.sdk.TokenCreateTransaction; -import com.hedera.hashgraph.sdk.TokenInfoQuery; -import com.hedera.hashgraph.sdk.TokenKeyValidation; -import com.hedera.hashgraph.sdk.TokenType; -import com.hedera.hashgraph.sdk.TokenUpdateTransaction; -import java.util.Objects; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -class TokenUpdateIntegrationTest { - - @Test - @DisplayName("Can update token") - void canUpdateToken() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var response = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setPauseKey(testEnv.operatorKey) - .setMetadataKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client); - - var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); - - var info = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(info.tokenId).isEqualTo(tokenId); - assertThat(info.name).isEqualTo("ffff"); - assertThat(info.symbol).isEqualTo("F"); - assertThat(info.decimals).isEqualTo(3); - assertThat(info.treasuryAccountId).isEqualTo(testEnv.operatorId); - assertThat(info.adminKey).isNotNull(); - assertThat(info.freezeKey).isNotNull(); - assertThat(info.wipeKey).isNotNull(); - assertThat(info.kycKey).isNotNull(); - assertThat(info.supplyKey).isNotNull(); - assertThat(info.adminKey.toString()).isEqualTo(testEnv.operatorKey.toString()); - assertThat(info.freezeKey.toString()).isEqualTo(testEnv.operatorKey.toString()); - assertThat(info.wipeKey.toString()).isEqualTo(testEnv.operatorKey.toString()); - assertThat(info.kycKey.toString()).isEqualTo(testEnv.operatorKey.toString()); - assertThat(info.supplyKey.toString()).isEqualTo(testEnv.operatorKey.toString()); - assertThat(info.pauseKey.toString()).isEqualTo(testEnv.operatorKey.toString()); - assertThat(info.metadataKey.toString()).isEqualTo(testEnv.operatorKey.toString()); - assertThat(info.defaultFreezeStatus).isNotNull().isFalse(); - assertThat(info.defaultKycStatus).isNotNull().isFalse(); - - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setTokenName("aaaa") - .setTokenSymbol("A") - .execute(testEnv.client) - .getReceipt(testEnv.client); - - info = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(info.tokenId).isEqualTo(tokenId); - assertThat(info.name).isEqualTo("aaaa"); - assertThat(info.symbol).isEqualTo("A"); - assertThat(info.decimals).isEqualTo(3); - assertThat(info.treasuryAccountId).isEqualTo(testEnv.operatorId); - assertThat(info.adminKey).isNotNull(); - assertThat(info.freezeKey).isNotNull(); - assertThat(info.wipeKey).isNotNull(); - assertThat(info.kycKey).isNotNull(); - assertThat(info.supplyKey).isNotNull(); - assertThat(info.adminKey.toString()).isEqualTo(testEnv.operatorKey.toString()); - assertThat(info.freezeKey.toString()).isEqualTo(testEnv.operatorKey.toString()); - assertThat(info.wipeKey.toString()).isEqualTo(testEnv.operatorKey.toString()); - assertThat(info.kycKey.toString()).isEqualTo(testEnv.operatorKey.toString()); - assertThat(info.supplyKey.toString()).isEqualTo(testEnv.operatorKey.toString()); - assertThat(info.pauseKey.toString()).isEqualTo(testEnv.operatorKey.toString()); - assertThat(info.metadataKey.toString()).isEqualTo(testEnv.operatorKey.toString()); - assertThat(info.defaultFreezeStatus).isNotNull(); - assertThat(info.defaultFreezeStatus).isFalse(); - assertThat(info.defaultKycStatus).isNotNull(); - assertThat(info.defaultKycStatus).isFalse(); - - } - } - - @Test - @DisplayName("Cannot update immutable token") - void cannotUpdateImmutableToken() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - var response = new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTreasuryAccountId(testEnv.operatorId) - .setFreezeDefault(false) - .execute(testEnv.client); - - var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setTokenName("aaaa") - .setTokenSymbol("A") - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.TOKEN_IS_IMMUTABLE.toString()); - - } - } - - /** - * @notice E2E-HIP-646 - * @url https://hips.hedera.com/hip/hip-646 - */ - @Test - @DisplayName("Can update a fungible token's metadata") - void canUpdateFungibleTokenMetadata() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var initialTokenMetadata = new byte[]{1, 1, 1, 1, 1}; - var updatedTokenMetadata = new byte[]{2, 2, 2, 2, 2}; - - // create a fungible token with metadata - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenMetadata(initialTokenMetadata) - .setTokenType(TokenType.FUNGIBLE_COMMON) - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var tokenInfoAfterCreation = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoAfterCreation.metadata).isEqualTo(initialTokenMetadata); - - // update token's metadata - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setTokenMetadata(updatedTokenMetadata) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var tokenInfoAfterMetadataUpdate = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoAfterMetadataUpdate.metadata).isEqualTo(updatedTokenMetadata); - - } - } - - /** - * @notice E2E-HIP-765 - * @url https://hips.hedera.com/hip/hip-765 - */ - @Test - @DisplayName("Can update a non fungible token's metadata") - void canUpdateNonFungibleTokenMetadata() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var initialTokenMetadata = new byte[]{1, 1, 1, 1, 1}; - var updatedTokenMetadata = new byte[]{2, 2, 2, 2, 2}; - - // create a non fungible token with metadata - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenMetadata(initialTokenMetadata) - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var tokenInfoAfterCreation = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoAfterCreation.metadata).isEqualTo(initialTokenMetadata); - - // update token's metadata - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setTokenMetadata(updatedTokenMetadata) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var tokenInfoAfterMetadataUpdate = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoAfterMetadataUpdate.metadata).isEqualTo(updatedTokenMetadata); - - } - } - - /** - * @notice E2E-HIP-646 - * @url https://hips.hedera.com/hip/hip-646 - */ - @Test - @DisplayName("Can update an immutable fungible token's metadata") - void canUpdateImmutableFungibleTokenMetadata() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var initialTokenMetadata = new byte[]{1, 1, 1, 1, 1}; - var updatedTokenMetadata = new byte[]{2, 2, 2, 2, 2}; - var metadataKey = PrivateKey.generateED25519(); - - // create a fungible token with metadata and metadata key - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenMetadata(initialTokenMetadata) - .setTokenType(TokenType.FUNGIBLE_COMMON) - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setMetadataKey(metadataKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var tokenInfoAfterCreation = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoAfterCreation.metadata).isEqualTo(initialTokenMetadata); - assertThat(tokenInfoAfterCreation.metadataKey.toString()).isEqualTo(metadataKey.getPublicKey().toString()); - - // update token's metadata - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setTokenMetadata(updatedTokenMetadata) - .freezeWith(testEnv.client) - .sign(metadataKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var tokenInfoAfterMetadataUpdate = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoAfterMetadataUpdate.metadata).isEqualTo(updatedTokenMetadata); - - } - } - - /** - * @notice E2E-HIP-765 - * @url https://hips.hedera.com/hip/hip-765 - */ - @Test - @DisplayName("Can update an immutable non fungible token's metadata") - void canUpdateImmutableNonFungibleTokenMetadata() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var initialTokenMetadata = new byte[]{1, 1, 1, 1, 1}; - var updatedTokenMetadata = new byte[]{2, 2, 2, 2, 2}; - var metadataKey = PrivateKey.generateED25519(); - - // create a non fungible token with metadata and metadata key - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenMetadata(initialTokenMetadata) - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setSupplyKey(testEnv.operatorKey) - .setMetadataKey(metadataKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var tokenInfoAfterCreation = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoAfterCreation.metadata).isEqualTo(initialTokenMetadata); - assertThat(tokenInfoAfterCreation.metadataKey.toString()).isEqualTo(metadataKey.getPublicKey().toString()); - - // update token's metadata - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setTokenMetadata(updatedTokenMetadata) - .freezeWith(testEnv.client) - .sign(metadataKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var tokenInfoAfterMetadataUpdate = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoAfterMetadataUpdate.metadata).isEqualTo(updatedTokenMetadata); - - } - } - - /** - * @notice E2E-HIP-646 - * @url https://hips.hedera.com/hip/hip-646 - */ - @Test - @DisplayName("Cannot update a fungible token with metadata when it is not set") - void cannotUpdateFungibleTokenMetadataWhenItsNotSet() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var initialTokenMetadata = new byte[]{1, 1, 1, 1, 1}; - - // create a fungible token with metadata - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenMetadata(initialTokenMetadata) - .setTokenType(TokenType.FUNGIBLE_COMMON) - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var tokenInfoAfterCreation = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoAfterCreation.metadata).isEqualTo(initialTokenMetadata); - - // update token, but don't update metadata - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setTokenMemo("abc") - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var tokenInfoAfterMemoUpdate = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoAfterMemoUpdate.metadata).isEqualTo(initialTokenMetadata); - - } - } - - /** - * @notice E2E-HIP-765 - * @url https://hips.hedera.com/hip/hip-765 - */ - @Test - @DisplayName("Cannot update a non fungible token with metadata when it is not set") - void cannotUpdateNonFungibleTokenMetadataWhenItsNotSet() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var initialTokenMetadata = new byte[]{1, 1, 1, 1, 1}; - - // create a non fungible token with metadata - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenMetadata(initialTokenMetadata) - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var tokenInfoAfterCreation = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoAfterCreation.metadata).isEqualTo(initialTokenMetadata); - - // update token, but don't update metadata - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setTokenMemo("abc") - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var tokenInfoAfterMemoUpdate = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoAfterMemoUpdate.metadata).isEqualTo(initialTokenMetadata); - - } - } - - /** - * @notice E2E-HIP-646 - * @url https://hips.hedera.com/hip/hip-646 - */ - @Test - @DisplayName("Can erase fungible token metadata") - void canEraseFungibleTokenMetadata() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var initialTokenMetadata = new byte[]{1, 1, 1, 1, 1}; - var emptyTokenMetadata = new byte[]{}; - - // create a fungible token with metadata - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenMetadata(initialTokenMetadata) - .setTokenType(TokenType.FUNGIBLE_COMMON) - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var tokenInfoAfterCreation = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoAfterCreation.metadata).isEqualTo(initialTokenMetadata); - - // erase token metadata (update token with empty metadata) - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setTokenMetadata(emptyTokenMetadata) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var tokenInfoAfterSettingEmptyMetadata = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoAfterSettingEmptyMetadata.metadata).isEqualTo(emptyTokenMetadata); - - } - } - - /** - * @notice E2E-HIP-765 - * @url https://hips.hedera.com/hip/hip-765 - */ - @Test - @DisplayName("Can erase non fungible token metadata") - void canEraseNonFungibleTokenMetadata() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var initialTokenMetadata = new byte[]{1, 1, 1, 1, 1}; - var emptyTokenMetadata = new byte[]{}; - - // create a non fungible token with metadata - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenMetadata(initialTokenMetadata) - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var tokenInfoAfterCreation = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoAfterCreation.metadata).isEqualTo(initialTokenMetadata); - - // erase token metadata (update token with empty metadata) - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setTokenMetadata(emptyTokenMetadata) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var tokenInfoAfterSettingEmptyMetadata = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoAfterSettingEmptyMetadata.metadata).isEqualTo(emptyTokenMetadata); - - } - } - - /** - * @notice E2E-HIP-646 - * @url https://hips.hedera.com/hip/hip-646 - */ - @Test - @DisplayName("Cannot update a fungible token with metadata when transaction is not signed with an admin or a metadata key") - void cannotUpdateFungibleTokenMetadataWhenTransactionIsNotSignedWithMetadataKey() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { - - var initialTokenMetadata = new byte[]{1, 1, 1, 1, 1}; - var updatedTokenMetadata = new byte[]{2, 2, 2, 2, 2}; - var adminKey = PrivateKey.generateED25519(); - var metadataKey = PrivateKey.generateED25519(); - - // create a fungible token with metadata and metadata key - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenMetadata(initialTokenMetadata) - .setTokenType(TokenType.FUNGIBLE_COMMON) - .setTreasuryAccountId(testEnv.operatorId) - .setDecimals(3) - .setInitialSupply(1000000) - .setAdminKey(adminKey) - .setMetadataKey(metadataKey) - .freezeWith(testEnv.client) - .sign(adminKey) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setTokenMetadata(updatedTokenMetadata) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - } - } - - /** - * @notice E2E-HIP-765 - * @url https://hips.hedera.com/hip/hip-765 - */ - @Test - @DisplayName("Cannot update a non fungible token with metadata when transaction is not signed with an admin or a metadata key") - void cannotUpdateNonFungibleTokenMetadataWhenTransactionIsNotSignedWithMetadataKey() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var initialTokenMetadata = new byte[]{1, 1, 1, 1, 1}; - var updatedTokenMetadata = new byte[]{2, 2, 2, 2, 2}; - var adminKey = PrivateKey.generateED25519(); - var metadataKey = PrivateKey.generateED25519(); - - // create a non fungible token with metadata and metadata key - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenMetadata(initialTokenMetadata) - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(adminKey) - .setSupplyKey(testEnv.operatorKey) - .setMetadataKey(metadataKey) - .freezeWith(testEnv.client) - .sign(adminKey) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setTokenMetadata(updatedTokenMetadata) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - } - } - - /** - * @notice E2E-HIP-646 - * @url https://hips.hedera.com/hip/hip-646 - */ - @Test - @DisplayName("Cannot update a fungible token with metadata when admin and metadata keys are not set") - void cannotUpdateFungibleTokenMetadataWhenMetadataKeyNotSet() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var initialTokenMetadata = new byte[]{1, 1, 1, 1, 1}; - var updatedTokenMetadata = new byte[]{2, 2, 2, 2, 2}; - - // create a fungible token with metadata and without a metadata key and admin key - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenMetadata(initialTokenMetadata) - .setTokenType(TokenType.FUNGIBLE_COMMON) - .setTreasuryAccountId(testEnv.operatorId) - .setDecimals(3) - .setInitialSupply(1000000) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setTokenMetadata(updatedTokenMetadata) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.TOKEN_IS_IMMUTABLE.toString()); - - } - } - - /** - * @notice E2E-HIP-765 - * @url https://hips.hedera.com/hip/hip-765 - */ - @Test - @DisplayName("Cannot update a non fungible token with metadata when admin and metadata keys are not set") - void cannotUpdateNonFungibleTokenMetadataWhenMetadataKeyNotSet() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - var initialTokenMetadata = new byte[]{1, 1, 1, 1, 1}; - var updatedTokenMetadata = new byte[]{2, 2, 2, 2, 2}; - - // create a non fungible token with metadata and without a metadata key and admin key - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenMetadata(initialTokenMetadata) - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setSupplyKey(testEnv.operatorKey) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setTokenMetadata(updatedTokenMetadata) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.TOKEN_IS_IMMUTABLE.toString()); - - } - } - - /** - * @notice E2E-HIP-540 - * @url https://hips.hedera.com/hip/hip-540 - */ - @Test - @DisplayName("Can make a token immutable when updating keys to an empty KeyList, signing with an Admin Key, and setting the key verification mode to NO_VALIDATION") - void canMakeTokenImmutableWhenUpdatingKeysToEmptyKeyListSigningWithAdminKeyWithKeyVerificationSetToNoValidation() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - // Admin, Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys - var adminKey = PrivateKey.generateED25519(); - var wipeKey = PrivateKey.generateED25519(); - var kycKey = PrivateKey.generateED25519(); - var freezeKey = PrivateKey.generateED25519(); - var pauseKey = PrivateKey.generateED25519(); - var supplyKey = PrivateKey.generateED25519(); - var feeScheduleKey = PrivateKey.generateED25519(); - var metadataKey = PrivateKey.generateED25519(); - - // Create a non-fungible token - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("Test NFT") - .setTokenSymbol("TNFT") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(adminKey.getPublicKey()) - .setWipeKey(wipeKey.getPublicKey()) - .setKycKey(kycKey.getPublicKey()) - .setFreezeKey(freezeKey.getPublicKey()) - .setPauseKey(pauseKey.getPublicKey()) - .setSupplyKey(supplyKey.getPublicKey()) - .setFeeScheduleKey(feeScheduleKey.getPublicKey()) - .setMetadataKey(metadataKey.getPublicKey()) - .freezeWith(testEnv.client) - .sign(adminKey) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var tokenInfoBeforeUpdate = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoBeforeUpdate.adminKey.toString()).isEqualTo(adminKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.wipeKey.toString()).isEqualTo(wipeKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.kycKey.toString()).isEqualTo(kycKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.freezeKey.toString()).isEqualTo(freezeKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.pauseKey.toString()).isEqualTo(pauseKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.supplyKey.toString()).isEqualTo(supplyKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.feeScheduleKey.toString()).isEqualTo(feeScheduleKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.metadataKey.toString()).isEqualTo(metadataKey.getPublicKey().toString()); - - var emptyKeyList = new KeyList(); - - // Make a token immutable by removing all of its keys when updating them to an empty KeyList, - // signing with an Admin Key, and setting the key verification mode to NO_VALIDATION - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setWipeKey(emptyKeyList) - .setKycKey(emptyKeyList) - .setFreezeKey(emptyKeyList) - .setPauseKey(emptyKeyList) - .setSupplyKey(emptyKeyList) - .setFeeScheduleKey(emptyKeyList) - .setMetadataKey(emptyKeyList) - .setAdminKey(emptyKeyList) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .freezeWith(testEnv.client) - .sign(adminKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var tokenInfoAfterUpdate = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoAfterUpdate.adminKey).isNull(); - assertThat(tokenInfoAfterUpdate.wipeKey).isNull(); - assertThat(tokenInfoAfterUpdate.kycKey).isNull(); - assertThat(tokenInfoAfterUpdate.freezeKey).isNull(); - assertThat(tokenInfoAfterUpdate.pauseKey).isNull(); - assertThat(tokenInfoAfterUpdate.supplyKey).isNull(); - assertThat(tokenInfoAfterUpdate.feeScheduleKey).isNull(); - assertThat(tokenInfoAfterUpdate.metadataKey).isNull(); - - } - } - - /** - * @notice E2E-HIP-540 - * @url https://hips.hedera.com/hip/hip-540 - */ - @Test - @DisplayName("Can remove all of token’s lower-privilege keys when updating keys to an empty KeyList, signing with an Admin Key, and setting the key verification mode to FULL_VALIDATION") - void canRemoveAllLowerPrivilegeKeysWhenUpdatingKeysToEmptyKeyListSigningWithAdminKeyWithKeyVerificationSetToFullValidation() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - // Admin, Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys - var adminKey = PrivateKey.generateED25519(); - var wipeKey = PrivateKey.generateED25519(); - var kycKey = PrivateKey.generateED25519(); - var freezeKey = PrivateKey.generateED25519(); - var pauseKey = PrivateKey.generateED25519(); - var supplyKey = PrivateKey.generateED25519(); - var feeScheduleKey = PrivateKey.generateED25519(); - var metadataKey = PrivateKey.generateED25519(); - - // Create a non-fungible token - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("Test NFT") - .setTokenSymbol("TNFT") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(adminKey.getPublicKey()) - .setWipeKey(wipeKey.getPublicKey()) - .setKycKey(kycKey.getPublicKey()) - .setFreezeKey(freezeKey.getPublicKey()) - .setPauseKey(pauseKey.getPublicKey()) - .setSupplyKey(supplyKey.getPublicKey()) - .setFeeScheduleKey(feeScheduleKey.getPublicKey()) - .setMetadataKey(metadataKey.getPublicKey()) - .freezeWith(testEnv.client) - .sign(adminKey) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var tokenInfoBeforeUpdate = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoBeforeUpdate.adminKey.toString()).isEqualTo(adminKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.wipeKey.toString()).isEqualTo(wipeKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.kycKey.toString()).isEqualTo(kycKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.freezeKey.toString()).isEqualTo(freezeKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.pauseKey.toString()).isEqualTo(pauseKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.supplyKey.toString()).isEqualTo(supplyKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.feeScheduleKey.toString()).isEqualTo(feeScheduleKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.metadataKey.toString()).isEqualTo(metadataKey.getPublicKey().toString()); - - var emptyKeyList = new KeyList(); - - // Remove all of token’s lower-privilege keys when updating them to an empty KeyList, - // signing with an Admin Key, and setting the key verification mode to FULL_VALIDATION - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setWipeKey(emptyKeyList) - .setKycKey(emptyKeyList) - .setFreezeKey(emptyKeyList) - .setPauseKey(emptyKeyList) - .setSupplyKey(emptyKeyList) - .setFeeScheduleKey(emptyKeyList) - .setMetadataKey(emptyKeyList) - .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) - .freezeWith(testEnv.client) - .sign(adminKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var tokenInfoAfterUpdate = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoAfterUpdate.wipeKey).isNull(); - assertThat(tokenInfoAfterUpdate.kycKey).isNull(); - assertThat(tokenInfoAfterUpdate.freezeKey).isNull(); - assertThat(tokenInfoAfterUpdate.pauseKey).isNull(); - assertThat(tokenInfoAfterUpdate.supplyKey).isNull(); - assertThat(tokenInfoAfterUpdate.feeScheduleKey).isNull(); - assertThat(tokenInfoAfterUpdate.metadataKey).isNull(); - - } - } - - /** - * @notice E2E-HIP-540 - * @url https://hips.hedera.com/hip/hip-540 - */ - @Test - @DisplayName("Can update all of token’s lower-privilege keys to an unusable key (i.e. all-zeros key), when signing with an Admin Key, and setting the key verification mode to FULL_VALIDATION, and then revert previous keys") - void canUpdateAllLowerPrivilegeKeysToUnusableKeyWhenSigningWithAdminKeyWithKeyVerificationSetToFullValidationAndThenRevertPreviousKeys() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - // Admin, Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys - var adminKey = PrivateKey.generateED25519(); - var wipeKey = PrivateKey.generateED25519(); - var kycKey = PrivateKey.generateED25519(); - var freezeKey = PrivateKey.generateED25519(); - var pauseKey = PrivateKey.generateED25519(); - var supplyKey = PrivateKey.generateED25519(); - var feeScheduleKey = PrivateKey.generateED25519(); - var metadataKey = PrivateKey.generateED25519(); - - // Create a non-fungible token - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("Test NFT") - .setTokenSymbol("TNFT") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(adminKey.getPublicKey()) - .setWipeKey(wipeKey.getPublicKey()) - .setKycKey(kycKey.getPublicKey()) - .setFreezeKey(freezeKey.getPublicKey()) - .setPauseKey(pauseKey.getPublicKey()) - .setSupplyKey(supplyKey.getPublicKey()) - .setFeeScheduleKey(feeScheduleKey.getPublicKey()) - .setMetadataKey(metadataKey.getPublicKey()) - .freezeWith(testEnv.client) - .sign(adminKey) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var tokenInfoBeforeUpdate = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoBeforeUpdate.adminKey.toString()).isEqualTo(adminKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.wipeKey.toString()).isEqualTo(wipeKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.kycKey.toString()).isEqualTo(kycKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.freezeKey.toString()).isEqualTo(freezeKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.pauseKey.toString()).isEqualTo(pauseKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.supplyKey.toString()).isEqualTo(supplyKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.feeScheduleKey.toString()).isEqualTo(feeScheduleKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.metadataKey.toString()).isEqualTo(metadataKey.getPublicKey().toString()); - - // Update all of token’s lower-privilege keys to an unusable key (i.e., all-zeros key), - // signing with an Admin Key, and setting the key verification mode to FULL_VALIDATION - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setWipeKey(PublicKey.unusableKey()) - .setKycKey(PublicKey.unusableKey()) - .setFreezeKey(PublicKey.unusableKey()) - .setPauseKey(PublicKey.unusableKey()) - .setSupplyKey(PublicKey.unusableKey()) - .setFeeScheduleKey(PublicKey.unusableKey()) - .setMetadataKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) - .freezeWith(testEnv.client) - .sign(adminKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var tokenInfoAfterUpdate = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoAfterUpdate.wipeKey.toString()).isEqualTo(PublicKey.unusableKey().toString()); - assertThat(tokenInfoAfterUpdate.kycKey.toString()).isEqualTo(PublicKey.unusableKey().toString()); - assertThat(tokenInfoAfterUpdate.freezeKey.toString()).isEqualTo(PublicKey.unusableKey().toString()); - assertThat(tokenInfoAfterUpdate.pauseKey.toString()).isEqualTo(PublicKey.unusableKey().toString()); - assertThat(tokenInfoAfterUpdate.supplyKey.toString()).isEqualTo(PublicKey.unusableKey().toString()); - assertThat(tokenInfoAfterUpdate.feeScheduleKey.toString()).isEqualTo(PublicKey.unusableKey().toString()); - assertThat(tokenInfoAfterUpdate.metadataKey.toString()).isEqualTo(PublicKey.unusableKey().toString()); - - // Set all lower-privilege keys back by signing with an Admin Key, - // and setting key verification mode to NO_VALIDATION - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setWipeKey(wipeKey.getPublicKey()) - .setKycKey(kycKey.getPublicKey()) - .setFreezeKey(freezeKey.getPublicKey()) - .setPauseKey(pauseKey.getPublicKey()) - .setSupplyKey(supplyKey.getPublicKey()) - .setFeeScheduleKey(feeScheduleKey.getPublicKey()) - .setMetadataKey(metadataKey.getPublicKey()) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .freezeWith(testEnv.client) - .sign(adminKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var tokenInfoAfterRevert = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoAfterRevert.adminKey.toString()).isEqualTo(adminKey.getPublicKey().toString()); - assertThat(tokenInfoAfterRevert.wipeKey.toString()).isEqualTo(wipeKey.getPublicKey().toString()); - assertThat(tokenInfoAfterRevert.kycKey.toString()).isEqualTo(kycKey.getPublicKey().toString()); - assertThat(tokenInfoAfterRevert.freezeKey.toString()).isEqualTo(freezeKey.getPublicKey().toString()); - assertThat(tokenInfoAfterRevert.pauseKey.toString()).isEqualTo(pauseKey.getPublicKey().toString()); - assertThat(tokenInfoAfterRevert.supplyKey.toString()).isEqualTo(supplyKey.getPublicKey().toString()); - assertThat(tokenInfoAfterRevert.feeScheduleKey.toString()).isEqualTo(feeScheduleKey.getPublicKey().toString()); - assertThat(tokenInfoAfterRevert.metadataKey.toString()).isEqualTo(metadataKey.getPublicKey().toString()); - - } - } - - /** - * @notice E2E-HIP-540 - * @url https://hips.hedera.com/hip/hip-540 - */ - @Test - @DisplayName("Can update all of token’s lower-privilege keys when signing with an Admin Key and new respective lower-privilege key, and setting key verification mode to FULL_VALIDATION") - void canUpdateAllLowerPrivilegeKeysWhenSigningWithAdminKeyAndNewLowerPrivilegeKeyWithKeyVerificationSetToFullValidation() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - // Admin, Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys - var adminKey = PrivateKey.generateED25519(); - var wipeKey = PrivateKey.generateED25519(); - var kycKey = PrivateKey.generateED25519(); - var freezeKey = PrivateKey.generateED25519(); - var pauseKey = PrivateKey.generateED25519(); - var supplyKey = PrivateKey.generateED25519(); - var feeScheduleKey = PrivateKey.generateED25519(); - var metadataKey = PrivateKey.generateED25519(); - - // New Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys - var newWipeKey = PrivateKey.generateED25519(); - var newKycKey = PrivateKey.generateED25519(); - var newFreezeKey = PrivateKey.generateED25519(); - var newPauseKey = PrivateKey.generateED25519(); - var newSupplyKey = PrivateKey.generateED25519(); - var newFeeScheduleKey = PrivateKey.generateED25519(); - var newMetadataKey = PrivateKey.generateED25519(); - - // Create a non-fungible token - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("Test NFT") - .setTokenSymbol("TNFT") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(adminKey.getPublicKey()) - .setWipeKey(wipeKey.getPublicKey()) - .setKycKey(kycKey.getPublicKey()) - .setFreezeKey(freezeKey.getPublicKey()) - .setPauseKey(pauseKey.getPublicKey()) - .setSupplyKey(supplyKey.getPublicKey()) - .setFeeScheduleKey(feeScheduleKey.getPublicKey()) - .setMetadataKey(metadataKey.getPublicKey()) - .freezeWith(testEnv.client) - .sign(adminKey) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var tokenInfoBeforeUpdate = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoBeforeUpdate.adminKey.toString()).isEqualTo(adminKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.wipeKey.toString()).isEqualTo(wipeKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.kycKey.toString()).isEqualTo(kycKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.freezeKey.toString()).isEqualTo(freezeKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.pauseKey.toString()).isEqualTo(pauseKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.supplyKey.toString()).isEqualTo(supplyKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.feeScheduleKey.toString()).isEqualTo(feeScheduleKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.metadataKey.toString()).isEqualTo(metadataKey.getPublicKey().toString()); - - // Update all of token’s lower-privilege keys when signing with an Admin Key and new respective lower-privilege key, - // and setting key verification mode to FULL_VALIDATION - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setWipeKey(newWipeKey.getPublicKey()) - .setKycKey(newKycKey.getPublicKey()) - .setFreezeKey(newFreezeKey.getPublicKey()) - .setPauseKey(newPauseKey.getPublicKey()) - .setSupplyKey(newSupplyKey.getPublicKey()) - .setFeeScheduleKey(newFeeScheduleKey.getPublicKey()) - .setMetadataKey(newMetadataKey.getPublicKey()) - .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) - .freezeWith(testEnv.client) - .sign(adminKey) - .sign(newWipeKey) - .sign(newKycKey) - .sign(newFreezeKey) - .sign(newPauseKey) - .sign(newSupplyKey) - .sign(newFeeScheduleKey) - .sign(newMetadataKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var tokenInfoAfterUpdate = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoAfterUpdate.wipeKey.toString()).isEqualTo(newWipeKey.getPublicKey().toString()); - assertThat(tokenInfoAfterUpdate.kycKey.toString()).isEqualTo(newKycKey.getPublicKey().toString()); - assertThat(tokenInfoAfterUpdate.freezeKey.toString()).isEqualTo(newFreezeKey.getPublicKey().toString()); - assertThat(tokenInfoAfterUpdate.pauseKey.toString()).isEqualTo(newPauseKey.getPublicKey().toString()); - assertThat(tokenInfoAfterUpdate.supplyKey.toString()).isEqualTo(newSupplyKey.getPublicKey().toString()); - assertThat(tokenInfoAfterUpdate.feeScheduleKey.toString()).isEqualTo(newFeeScheduleKey.getPublicKey().toString()); - assertThat(tokenInfoAfterUpdate.metadataKey.toString()).isEqualTo(newMetadataKey.getPublicKey().toString()); - - } - } - - /** - * @notice E2E-HIP-540 - * @url https://hips.hedera.com/hip/hip-540 - */ - @Test - @DisplayName("Cannot make a token immutable when updating keys to an empty KeyList, signing with a key that is different from an Admin Key, and setting the key verification mode to NO_VALIDATION") - void cannotMakeTokenImmutableWhenUpdatingKeysToEmptyKeyListSigningWithDifferentKeyWithKeyVerificationSetToNoValidation() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - // Admin, Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys - var adminKey = PrivateKey.generateED25519(); - var wipeKey = PrivateKey.generateED25519(); - var kycKey = PrivateKey.generateED25519(); - var freezeKey = PrivateKey.generateED25519(); - var pauseKey = PrivateKey.generateED25519(); - var supplyKey = PrivateKey.generateED25519(); - var feeScheduleKey = PrivateKey.generateED25519(); - var metadataKey = PrivateKey.generateED25519(); - - // Create a non-fungible token - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("Test NFT") - .setTokenSymbol("TNFT") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(adminKey.getPublicKey()) - .setWipeKey(wipeKey.getPublicKey()) - .setKycKey(kycKey.getPublicKey()) - .setFreezeKey(freezeKey.getPublicKey()) - .setPauseKey(pauseKey.getPublicKey()) - .setSupplyKey(supplyKey.getPublicKey()) - .setFeeScheduleKey(feeScheduleKey.getPublicKey()) - .setMetadataKey(metadataKey.getPublicKey()) - .freezeWith(testEnv.client) - .sign(adminKey) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var tokenInfoBeforeUpdate = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoBeforeUpdate.adminKey.toString()).isEqualTo(adminKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.wipeKey.toString()).isEqualTo(wipeKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.kycKey.toString()).isEqualTo(kycKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.freezeKey.toString()).isEqualTo(freezeKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.pauseKey.toString()).isEqualTo(pauseKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.supplyKey.toString()).isEqualTo(supplyKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.feeScheduleKey.toString()).isEqualTo(feeScheduleKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.metadataKey.toString()).isEqualTo(metadataKey.getPublicKey().toString()); - - var emptyKeyList = new KeyList(); - - // Make the token immutable when updating all of its keys to an empty KeyList - // (trying to remove keys one by one to check all errors), - // signing with a key that is different from an Admin Key (implicitly with an operator key), - // and setting the key verification mode to NO_VALIDATION - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setWipeKey(emptyKeyList) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setKycKey(emptyKeyList) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setFreezeKey(emptyKeyList) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setPauseKey(emptyKeyList) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setSupplyKey(emptyKeyList) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setFeeScheduleKey(emptyKeyList) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setMetadataKey(emptyKeyList) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setAdminKey(emptyKeyList) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - } - } - - /** - * @notice E2E-HIP-540 - * @url https://hips.hedera.com/hip/hip-540 - */ - @Test - @DisplayName("Cannot make a token immutable when updating keys to an unusable key (i.e. all-zeros key), signing with a key that is different from an Admin Key, and setting the key verification mode to NO_VALIDATION") - void cannotMakeTokenImmutableWhenUpdatingKeysToUnusableKeySigningWithDifferentKeyWithKeyVerificationSetToNoValidation() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - // Admin, Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys - var adminKey = PrivateKey.generateED25519(); - var wipeKey = PrivateKey.generateED25519(); - var kycKey = PrivateKey.generateED25519(); - var freezeKey = PrivateKey.generateED25519(); - var pauseKey = PrivateKey.generateED25519(); - var supplyKey = PrivateKey.generateED25519(); - var feeScheduleKey = PrivateKey.generateED25519(); - var metadataKey = PrivateKey.generateED25519(); - - // Create a non-fungible token - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("Test NFT") - .setTokenSymbol("TNFT") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(adminKey.getPublicKey()) - .setWipeKey(wipeKey.getPublicKey()) - .setKycKey(kycKey.getPublicKey()) - .setFreezeKey(freezeKey.getPublicKey()) - .setPauseKey(pauseKey.getPublicKey()) - .setSupplyKey(supplyKey.getPublicKey()) - .setFeeScheduleKey(feeScheduleKey.getPublicKey()) - .setMetadataKey(metadataKey.getPublicKey()) - .freezeWith(testEnv.client) - .sign(adminKey) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var tokenInfoBeforeUpdate = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoBeforeUpdate.adminKey.toString()).isEqualTo(adminKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.wipeKey.toString()).isEqualTo(wipeKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.kycKey.toString()).isEqualTo(kycKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.freezeKey.toString()).isEqualTo(freezeKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.pauseKey.toString()).isEqualTo(pauseKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.supplyKey.toString()).isEqualTo(supplyKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.feeScheduleKey.toString()).isEqualTo(feeScheduleKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.metadataKey.toString()).isEqualTo(metadataKey.getPublicKey().toString()); - - // Make the token immutable when updating all of its keys to an unusable key (i.e. all-zeros key) - // (trying to remove keys one by one to check all errors), - // signing with a key that is different from an Admin Key (implicitly with an operator key), - // and setting the key verification mode to NO_VALIDATION - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setWipeKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setKycKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setFreezeKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setPauseKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setSupplyKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setFeeScheduleKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setMetadataKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setAdminKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - } - } - - /** - * @notice E2E-HIP-540 - * @url https://hips.hedera.com/hip/hip-540 - */ - @Test - @DisplayName("Cannot update the Admin Key to an unusable key (i.e. all-zeros key), signing with an Admin Key, and setting the key verification mode to NO_VALIDATION") - void cannotUpdateAdminKeyToUnusableKeySigningWithAdminKeyWithKeyVerificationSetToNoValidation() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - // Admin and supply keys - var adminKey = PrivateKey.generateED25519(); - var supplyKey = PrivateKey.generateED25519(); - - // Create a non-fungible token - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("Test NFT") - .setTokenSymbol("TNFT") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(adminKey.getPublicKey()) - .setSupplyKey(supplyKey.getPublicKey()) - .freezeWith(testEnv.client) - .sign(adminKey) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var tokenInfoBeforeUpdate = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoBeforeUpdate.adminKey.toString()).isEqualTo(adminKey.getPublicKey().toString()); - - // Update the Admin Key to an unusable key (i.e., all-zeros key), - // signing with an Admin Key, and setting the key verification mode to NO_VALIDATION - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setAdminKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .freezeWith(testEnv.client) - .sign(adminKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - } - } - - /** - * @notice E2E-HIP-540 - * @url https://hips.hedera.com/hip/hip-540 - */ - @Test - @DisplayName("Can update all of token’s lower-privilege keys to an unusable key (i.e. all-zeros key), when signing with a respective lower-privilege key, and setting the key verification mode to NO_VALIDATION") - void canUpdateAllLowerPrivilegeKeysToUnusableKeyWhenSigningWithRespectiveLowerPrivilegeKeyWithKeyVerificationSetToNoValidation() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - // Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys - var wipeKey = PrivateKey.generateED25519(); - var kycKey = PrivateKey.generateED25519(); - var freezeKey = PrivateKey.generateED25519(); - var pauseKey = PrivateKey.generateED25519(); - var supplyKey = PrivateKey.generateED25519(); - var feeScheduleKey = PrivateKey.generateED25519(); - var metadataKey = PrivateKey.generateED25519(); - - // Create a non-fungible token - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("Test NFT") - .setTokenSymbol("TNFT") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setWipeKey(wipeKey.getPublicKey()) - .setKycKey(kycKey.getPublicKey()) - .setFreezeKey(freezeKey.getPublicKey()) - .setPauseKey(pauseKey.getPublicKey()) - .setSupplyKey(supplyKey.getPublicKey()) - .setFeeScheduleKey(feeScheduleKey.getPublicKey()) - .setMetadataKey(metadataKey.getPublicKey()) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var tokenInfoBeforeUpdate = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoBeforeUpdate.wipeKey.toString()).isEqualTo(wipeKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.kycKey.toString()).isEqualTo(kycKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.freezeKey.toString()).isEqualTo(freezeKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.pauseKey.toString()).isEqualTo(pauseKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.supplyKey.toString()).isEqualTo(supplyKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.feeScheduleKey.toString()).isEqualTo(feeScheduleKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.metadataKey.toString()).isEqualTo(metadataKey.getPublicKey().toString()); - - // Update all of token’s lower-privilege keys to an unusable key (i.e., all-zeros key), - // when signing with a respective lower-privilege key, - // and setting the key verification mode to NO_VALIDATION - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setWipeKey(PublicKey.unusableKey()) - .setKycKey(PublicKey.unusableKey()) - .setFreezeKey(PublicKey.unusableKey()) - .setPauseKey(PublicKey.unusableKey()) - .setSupplyKey(PublicKey.unusableKey()) - .setFeeScheduleKey(PublicKey.unusableKey()) - .setMetadataKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .freezeWith(testEnv.client) - .sign(wipeKey) - .sign(kycKey) - .sign(freezeKey) - .sign(pauseKey) - .sign(supplyKey) - .sign(feeScheduleKey) - .sign(metadataKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var tokenInfoAfterUpdate = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoAfterUpdate.wipeKey.toString()).isEqualTo(PublicKey.unusableKey().toString()); - assertThat(tokenInfoAfterUpdate.kycKey.toString()).isEqualTo(PublicKey.unusableKey().toString()); - assertThat(tokenInfoAfterUpdate.freezeKey.toString()).isEqualTo(PublicKey.unusableKey().toString()); - assertThat(tokenInfoAfterUpdate.pauseKey.toString()).isEqualTo(PublicKey.unusableKey().toString()); - assertThat(tokenInfoAfterUpdate.supplyKey.toString()).isEqualTo(PublicKey.unusableKey().toString()); - assertThat(tokenInfoAfterUpdate.feeScheduleKey.toString()).isEqualTo(PublicKey.unusableKey().toString()); - assertThat(tokenInfoAfterUpdate.metadataKey.toString()).isEqualTo(PublicKey.unusableKey().toString()); - - } - } - - /** - * @notice E2E-HIP-540 - * @url https://hips.hedera.com/hip/hip-540 - */ - @Test - @DisplayName("Can update all of token’s lower-privilege keys when signing with an old lower-privilege key and with a new lower-privilege key, and setting key verification mode to FULL_VALIDATION") - void canUpdateAllLowerPrivilegeKeysWhenSigningWithOldLowerPrivilegeKeyAndNewLowerPrivilegeKeyWithKeyVerificationSetToFulValidation() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - // Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys - var wipeKey = PrivateKey.generateED25519(); - var kycKey = PrivateKey.generateED25519(); - var freezeKey = PrivateKey.generateED25519(); - var pauseKey = PrivateKey.generateED25519(); - var supplyKey = PrivateKey.generateED25519(); - var feeScheduleKey = PrivateKey.generateED25519(); - var metadataKey = PrivateKey.generateED25519(); - - // New Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys - var newWipeKey = PrivateKey.generateED25519(); - var newKycKey = PrivateKey.generateED25519(); - var newFreezeKey = PrivateKey.generateED25519(); - var newPauseKey = PrivateKey.generateED25519(); - var newSupplyKey = PrivateKey.generateED25519(); - var newFeeScheduleKey = PrivateKey.generateED25519(); - var newMetadataKey = PrivateKey.generateED25519(); - - // Create a non-fungible token - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("Test NFT") - .setTokenSymbol("TNFT") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setWipeKey(wipeKey.getPublicKey()) - .setKycKey(kycKey.getPublicKey()) - .setFreezeKey(freezeKey.getPublicKey()) - .setPauseKey(pauseKey.getPublicKey()) - .setSupplyKey(supplyKey.getPublicKey()) - .setFeeScheduleKey(feeScheduleKey.getPublicKey()) - .setMetadataKey(metadataKey.getPublicKey()) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var tokenInfoBeforeUpdate = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoBeforeUpdate.wipeKey.toString()).isEqualTo(wipeKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.kycKey.toString()).isEqualTo(kycKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.freezeKey.toString()).isEqualTo(freezeKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.pauseKey.toString()).isEqualTo(pauseKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.supplyKey.toString()).isEqualTo(supplyKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.feeScheduleKey.toString()).isEqualTo(feeScheduleKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.metadataKey.toString()).isEqualTo(metadataKey.getPublicKey().toString()); - - // Update all of token’s lower-privilege keys when signing with an old respective lower-privilege key, - // and setting key verification mode to NO_VALIDATION - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setWipeKey(newWipeKey.getPublicKey()) - .setKycKey(newKycKey.getPublicKey()) - .setFreezeKey(newFreezeKey.getPublicKey()) - .setPauseKey(newPauseKey.getPublicKey()) - .setSupplyKey(newSupplyKey.getPublicKey()) - .setFeeScheduleKey(newFeeScheduleKey.getPublicKey()) - .setMetadataKey(newMetadataKey.getPublicKey()) - .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) - .freezeWith(testEnv.client) - .sign(wipeKey) - .sign(newWipeKey) - .sign(kycKey) - .sign(newKycKey) - .sign(freezeKey) - .sign(newFreezeKey) - .sign(pauseKey) - .sign(newPauseKey) - .sign(supplyKey) - .sign(newSupplyKey) - .sign(feeScheduleKey) - .sign(newFeeScheduleKey) - .sign(metadataKey) - .sign(newMetadataKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var tokenInfoAfterUpdate = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoAfterUpdate.wipeKey.toString()).isEqualTo(newWipeKey.getPublicKey().toString()); - assertThat(tokenInfoAfterUpdate.kycKey.toString()).isEqualTo(newKycKey.getPublicKey().toString()); - assertThat(tokenInfoAfterUpdate.freezeKey.toString()).isEqualTo(newFreezeKey.getPublicKey().toString()); - assertThat(tokenInfoAfterUpdate.pauseKey.toString()).isEqualTo(newPauseKey.getPublicKey().toString()); - assertThat(tokenInfoAfterUpdate.supplyKey.toString()).isEqualTo(newSupplyKey.getPublicKey().toString()); - assertThat(tokenInfoAfterUpdate.feeScheduleKey.toString()).isEqualTo(newFeeScheduleKey.getPublicKey().toString()); - assertThat(tokenInfoAfterUpdate.metadataKey.toString()).isEqualTo(newMetadataKey.getPublicKey().toString()); - - } - } - - /** - * @notice E2E-HIP-540 - * @url https://hips.hedera.com/hip/hip-540 - */ - @Test - @DisplayName("Can update all of token’s lower-privilege keys when signing ONLY with an old lower-privilege key, and setting key verification mode to NO_VALIDATION") - void canUpdateAllLowerPrivilegeKeysWhenSigningOnlyWithOldLowerPrivilegeKeyWithKeyVerificationSetToNoValidation() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - // Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys - var wipeKey = PrivateKey.generateED25519(); - var kycKey = PrivateKey.generateED25519(); - var freezeKey = PrivateKey.generateED25519(); - var pauseKey = PrivateKey.generateED25519(); - var supplyKey = PrivateKey.generateED25519(); - var feeScheduleKey = PrivateKey.generateED25519(); - var metadataKey = PrivateKey.generateED25519(); - - // New Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys - var newWipeKey = PrivateKey.generateED25519(); - var newKycKey = PrivateKey.generateED25519(); - var newFreezeKey = PrivateKey.generateED25519(); - var newPauseKey = PrivateKey.generateED25519(); - var newSupplyKey = PrivateKey.generateED25519(); - var newFeeScheduleKey = PrivateKey.generateED25519(); - var newMetadataKey = PrivateKey.generateED25519(); - - // Create a non-fungible token - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("Test NFT") - .setTokenSymbol("TNFT") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setWipeKey(wipeKey.getPublicKey()) - .setKycKey(kycKey.getPublicKey()) - .setFreezeKey(freezeKey.getPublicKey()) - .setPauseKey(pauseKey.getPublicKey()) - .setSupplyKey(supplyKey.getPublicKey()) - .setFeeScheduleKey(feeScheduleKey.getPublicKey()) - .setMetadataKey(metadataKey.getPublicKey()) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var tokenInfoBeforeUpdate = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoBeforeUpdate.wipeKey.toString()).isEqualTo(wipeKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.kycKey.toString()).isEqualTo(kycKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.freezeKey.toString()).isEqualTo(freezeKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.pauseKey.toString()).isEqualTo(pauseKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.supplyKey.toString()).isEqualTo(supplyKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.feeScheduleKey.toString()).isEqualTo(feeScheduleKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.metadataKey.toString()).isEqualTo(metadataKey.getPublicKey().toString()); - - // Update all of token’s lower-privilege keys when signing with an old respective lower-privilege key, - // and setting key verification mode to NO_VALIDATION - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setWipeKey(newWipeKey.getPublicKey()) - .setKycKey(newKycKey.getPublicKey()) - .setFreezeKey(newFreezeKey.getPublicKey()) - .setPauseKey(newPauseKey.getPublicKey()) - .setSupplyKey(newSupplyKey.getPublicKey()) - .setFeeScheduleKey(newFeeScheduleKey.getPublicKey()) - .setMetadataKey(newMetadataKey.getPublicKey()) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .freezeWith(testEnv.client) - .sign(wipeKey) - .sign(kycKey) - .sign(freezeKey) - .sign(pauseKey) - .sign(supplyKey) - .sign(feeScheduleKey) - .sign(metadataKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var tokenInfoAfterUpdate = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoAfterUpdate.wipeKey.toString()).isEqualTo(newWipeKey.getPublicKey().toString()); - assertThat(tokenInfoAfterUpdate.kycKey.toString()).isEqualTo(newKycKey.getPublicKey().toString()); - assertThat(tokenInfoAfterUpdate.freezeKey.toString()).isEqualTo(newFreezeKey.getPublicKey().toString()); - assertThat(tokenInfoAfterUpdate.pauseKey.toString()).isEqualTo(newPauseKey.getPublicKey().toString()); - assertThat(tokenInfoAfterUpdate.supplyKey.toString()).isEqualTo(newSupplyKey.getPublicKey().toString()); - assertThat(tokenInfoAfterUpdate.feeScheduleKey.toString()).isEqualTo(newFeeScheduleKey.getPublicKey().toString()); - assertThat(tokenInfoAfterUpdate.metadataKey.toString()).isEqualTo(newMetadataKey.getPublicKey().toString()); - - } - } - - /** - * @notice E2E-HIP-540 - * @url https://hips.hedera.com/hip/hip-540 - */ - @Test - @DisplayName("Cannot remove all of token’s lower-privilege keys when updating them to an empty KeyList, signing with a respective lower-privilege key, and setting the key verification mode to NO_VALIDATION") - void cannotRemoveAllLowerPrivilegeKeysWhenUpdatingKeysToEmptyKeyListSigningWithRespectiveLowerPrivilegeKeyWithKeyVerificationSetToNoValidation() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - // Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys - var wipeKey = PrivateKey.generateED25519(); - var kycKey = PrivateKey.generateED25519(); - var freezeKey = PrivateKey.generateED25519(); - var pauseKey = PrivateKey.generateED25519(); - var supplyKey = PrivateKey.generateED25519(); - var feeScheduleKey = PrivateKey.generateED25519(); - var metadataKey = PrivateKey.generateED25519(); - - // Create a non-fungible token - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("Test NFT") - .setTokenSymbol("TNFT") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setWipeKey(wipeKey.getPublicKey()) - .setKycKey(kycKey.getPublicKey()) - .setFreezeKey(freezeKey.getPublicKey()) - .setPauseKey(pauseKey.getPublicKey()) - .setSupplyKey(supplyKey.getPublicKey()) - .setFeeScheduleKey(feeScheduleKey.getPublicKey()) - .setMetadataKey(metadataKey.getPublicKey()) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var tokenInfoBeforeUpdate = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoBeforeUpdate.wipeKey.toString()).isEqualTo(wipeKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.kycKey.toString()).isEqualTo(kycKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.freezeKey.toString()).isEqualTo(freezeKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.pauseKey.toString()).isEqualTo(pauseKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.supplyKey.toString()).isEqualTo(supplyKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.feeScheduleKey.toString()).isEqualTo(feeScheduleKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.metadataKey.toString()).isEqualTo(metadataKey.getPublicKey().toString()); - - var emptyKeyList = new KeyList(); - - // Remove all of token’s lower-privilege keys - // when updating them to an empty KeyList (trying to remove keys one by one to check all errors), - // signing with a respective lower-privilege key, - // and setting the key verification mode to NO_VALIDATION - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setWipeKey(emptyKeyList) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .freezeWith(testEnv.client) - .sign(wipeKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.TOKEN_IS_IMMUTABLE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setKycKey(emptyKeyList) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .freezeWith(testEnv.client) - .sign(kycKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.TOKEN_IS_IMMUTABLE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setFreezeKey(emptyKeyList) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .freezeWith(testEnv.client) - .sign(freezeKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.TOKEN_IS_IMMUTABLE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setPauseKey(emptyKeyList) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .freezeWith(testEnv.client) - .sign(pauseKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.TOKEN_IS_IMMUTABLE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setSupplyKey(emptyKeyList) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .freezeWith(testEnv.client) - .sign(supplyKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.TOKEN_IS_IMMUTABLE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setFeeScheduleKey(emptyKeyList) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .freezeWith(testEnv.client) - .sign(feeScheduleKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.TOKEN_IS_IMMUTABLE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setMetadataKey(emptyKeyList) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .freezeWith(testEnv.client) - .sign(metadataKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.TOKEN_IS_IMMUTABLE.toString()); - - } - } - - /** - * @notice E2E-HIP-540 - * @url https://hips.hedera.com/hip/hip-540 - */ - @Test - @DisplayName("Cannot update all of token’s lower-privilege keys to an unusable key (i.e. all-zeros key), when signing with a key that is different from a respective lower-privilege key, and setting the key verification mode to NO_VALIDATION") - void cannotUpdateAllLowerPrivilegeKeysToUnusableKeyWhenSigningWithDifferentKeyWithKeyVerificationSetToNoValidation() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - // Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys - var wipeKey = PrivateKey.generateED25519(); - var kycKey = PrivateKey.generateED25519(); - var freezeKey = PrivateKey.generateED25519(); - var pauseKey = PrivateKey.generateED25519(); - var supplyKey = PrivateKey.generateED25519(); - var feeScheduleKey = PrivateKey.generateED25519(); - var metadataKey = PrivateKey.generateED25519(); - - // Create a non-fungible token - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("Test NFT") - .setTokenSymbol("TNFT") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setWipeKey(wipeKey.getPublicKey()) - .setKycKey(kycKey.getPublicKey()) - .setFreezeKey(freezeKey.getPublicKey()) - .setPauseKey(pauseKey.getPublicKey()) - .setSupplyKey(supplyKey.getPublicKey()) - .setFeeScheduleKey(feeScheduleKey.getPublicKey()) - .setMetadataKey(metadataKey.getPublicKey()) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var tokenInfoBeforeUpdate = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoBeforeUpdate.wipeKey.toString()).isEqualTo(wipeKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.kycKey.toString()).isEqualTo(kycKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.freezeKey.toString()).isEqualTo(freezeKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.pauseKey.toString()).isEqualTo(pauseKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.supplyKey.toString()).isEqualTo(supplyKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.feeScheduleKey.toString()).isEqualTo(feeScheduleKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.metadataKey.toString()).isEqualTo(metadataKey.getPublicKey().toString()); - - // Update all of token’s lower-privilege keys to an unusable key (i.e. all-zeros key) - // (trying to remove keys one by one to check all errors), - // signing with a key that is different from a respective lower-privilege key (implicitly with an operator key), - // and setting the key verification mode to NO_VALIDATION - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setWipeKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setKycKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setFreezeKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setPauseKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setSupplyKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setFeeScheduleKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setMetadataKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - } - } - - /** - * @notice E2E-HIP-540 - * @url https://hips.hedera.com/hip/hip-540 - */ - @Test - @DisplayName("Cannot update all of token’s lower-privilege keys to an unusable key (i.e. all-zeros key), when signing ONLY with an old respective lower-privilege key, and setting the key verification mode to FULL_VALIDATION") - void cannotUpdateAllLowerPrivilegeKeysToUnusableKeyWhenSigningOnlyWithOldRespectiveLowerPrivilegeKeyWithKeyVerificationSetToFullValidation() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - // Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys - var wipeKey = PrivateKey.generateED25519(); - var kycKey = PrivateKey.generateED25519(); - var freezeKey = PrivateKey.generateED25519(); - var pauseKey = PrivateKey.generateED25519(); - var supplyKey = PrivateKey.generateED25519(); - var feeScheduleKey = PrivateKey.generateED25519(); - var metadataKey = PrivateKey.generateED25519(); - - // Create a non-fungible token - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("Test NFT") - .setTokenSymbol("TNFT") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setWipeKey(wipeKey.getPublicKey()) - .setKycKey(kycKey.getPublicKey()) - .setFreezeKey(freezeKey.getPublicKey()) - .setPauseKey(pauseKey.getPublicKey()) - .setSupplyKey(supplyKey.getPublicKey()) - .setFeeScheduleKey(feeScheduleKey.getPublicKey()) - .setMetadataKey(metadataKey.getPublicKey()) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var tokenInfoBeforeUpdate = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoBeforeUpdate.wipeKey.toString()).isEqualTo(wipeKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.kycKey.toString()).isEqualTo(kycKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.freezeKey.toString()).isEqualTo(freezeKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.pauseKey.toString()).isEqualTo(pauseKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.supplyKey.toString()).isEqualTo(supplyKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.feeScheduleKey.toString()).isEqualTo(feeScheduleKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.metadataKey.toString()).isEqualTo(metadataKey.getPublicKey().toString()); - - // Update all of token’s lower-privilege keys to an unusable key (i.e., all-zeros key) - // (trying to remove keys one by one to check all errors), - // signing ONLY with an old respective lower-privilege key, - // and setting the key verification mode to FULL_VALIDATION - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setWipeKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) - .freezeWith(testEnv.client) - .sign(wipeKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setKycKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) - .freezeWith(testEnv.client) - .sign(kycKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setFreezeKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) - .freezeWith(testEnv.client) - .sign(freezeKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setPauseKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) - .freezeWith(testEnv.client) - .sign(pauseKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setSupplyKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) - .freezeWith(testEnv.client) - .sign(supplyKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setFeeScheduleKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) - .freezeWith(testEnv.client) - .sign(feeScheduleKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setMetadataKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) - .freezeWith(testEnv.client) - .sign(metadataKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - } - } - - /** - * @notice E2E-HIP-540 - * @url https://hips.hedera.com/hip/hip-540 - */ - @Test - @DisplayName("Cannot update all of token’s lower-privilege keys to an unusable key (i.e. all-zeros key), when signing with an old respective lower-privilege key and new respective lower-privilege key, and setting the key verification mode to FULL_VALIDATION") - void cannotUpdateAllLowerPrivilegeKeysToUnusableKeyWhenSigningWithOldRespectiveLowerPrivilegeKeyAndNewRespectiveLowerPrivilegeKeyWithKeyVerificationSetToFullValidation() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - // Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys - var wipeKey = PrivateKey.generateED25519(); - var kycKey = PrivateKey.generateED25519(); - var freezeKey = PrivateKey.generateED25519(); - var pauseKey = PrivateKey.generateED25519(); - var supplyKey = PrivateKey.generateED25519(); - var feeScheduleKey = PrivateKey.generateED25519(); - var metadataKey = PrivateKey.generateED25519(); - - // New Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys - var newWipeKey = PrivateKey.generateED25519(); - var newKycKey = PrivateKey.generateED25519(); - var newFreezeKey = PrivateKey.generateED25519(); - var newPauseKey = PrivateKey.generateED25519(); - var newSupplyKey = PrivateKey.generateED25519(); - var newFeeScheduleKey = PrivateKey.generateED25519(); - var newMetadataKey = PrivateKey.generateED25519(); - - // Create a non-fungible token - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("Test NFT") - .setTokenSymbol("TNFT") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setWipeKey(wipeKey.getPublicKey()) - .setKycKey(kycKey.getPublicKey()) - .setFreezeKey(freezeKey.getPublicKey()) - .setPauseKey(pauseKey.getPublicKey()) - .setSupplyKey(supplyKey.getPublicKey()) - .setFeeScheduleKey(feeScheduleKey.getPublicKey()) - .setMetadataKey(metadataKey.getPublicKey()) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var tokenInfoBeforeUpdate = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoBeforeUpdate.wipeKey.toString()).isEqualTo(wipeKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.kycKey.toString()).isEqualTo(kycKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.freezeKey.toString()).isEqualTo(freezeKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.pauseKey.toString()).isEqualTo(pauseKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.supplyKey.toString()).isEqualTo(supplyKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.feeScheduleKey.toString()).isEqualTo(feeScheduleKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.metadataKey.toString()).isEqualTo(metadataKey.getPublicKey().toString()); - - // Update all of token’s lower-privilege keys to an unusable key (i.e., all-zeros key) - // (trying to remove keys one by one to check all errors), - // signing with an old respective lower-privilege key and new respective lower-privilege key, - // and setting the key verification mode to FULL_VALIDATION - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setWipeKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) - .freezeWith(testEnv.client) - .sign(wipeKey) - .sign(newWipeKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setKycKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) - .freezeWith(testEnv.client) - .sign(kycKey) - .sign(newKycKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setFreezeKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) - .freezeWith(testEnv.client) - .sign(freezeKey) - .sign(newFreezeKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setPauseKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) - .freezeWith(testEnv.client) - .sign(pauseKey) - .sign(newPauseKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setSupplyKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) - .freezeWith(testEnv.client) - .sign(supplyKey) - .sign(newSupplyKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setFeeScheduleKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) - .freezeWith(testEnv.client) - .sign(feeScheduleKey) - .sign(newFeeScheduleKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setMetadataKey(PublicKey.unusableKey()) - .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) - .freezeWith(testEnv.client) - .sign(metadataKey) - .sign(newMetadataKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - } - } - - /** - * @notice E2E-HIP-540 - * @url https://hips.hedera.com/hip/hip-540 - */ - @Test - @DisplayName("Cannot update all of token’s lower-privilege keys, when signing ONLY with an old respective lower-privilege key, and setting the key verification mode to FULL_VALIDATION") - void cannotUpdateAllLowerPrivilegeKeysWhenSigningOnlyWithOldRespectiveLowerPrivilegeKeyWithKeyVerificationSetToFullValidation() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - // Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys - var wipeKey = PrivateKey.generateED25519(); - var kycKey = PrivateKey.generateED25519(); - var freezeKey = PrivateKey.generateED25519(); - var pauseKey = PrivateKey.generateED25519(); - var supplyKey = PrivateKey.generateED25519(); - var feeScheduleKey = PrivateKey.generateED25519(); - var metadataKey = PrivateKey.generateED25519(); - - // New Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys - var newWipeKey = PrivateKey.generateED25519(); - var newKycKey = PrivateKey.generateED25519(); - var newFreezeKey = PrivateKey.generateED25519(); - var newPauseKey = PrivateKey.generateED25519(); - var newSupplyKey = PrivateKey.generateED25519(); - var newFeeScheduleKey = PrivateKey.generateED25519(); - var newMetadataKey = PrivateKey.generateED25519(); - - // Create a non-fungible token - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("Test NFT") - .setTokenSymbol("TNFT") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setWipeKey(wipeKey.getPublicKey()) - .setKycKey(kycKey.getPublicKey()) - .setFreezeKey(freezeKey.getPublicKey()) - .setPauseKey(pauseKey.getPublicKey()) - .setSupplyKey(supplyKey.getPublicKey()) - .setFeeScheduleKey(feeScheduleKey.getPublicKey()) - .setMetadataKey(metadataKey.getPublicKey()) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var tokenInfoBeforeUpdate = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoBeforeUpdate.wipeKey.toString()).isEqualTo(wipeKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.kycKey.toString()).isEqualTo(kycKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.freezeKey.toString()).isEqualTo(freezeKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.pauseKey.toString()).isEqualTo(pauseKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.supplyKey.toString()).isEqualTo(supplyKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.feeScheduleKey.toString()).isEqualTo(feeScheduleKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.metadataKey.toString()).isEqualTo(metadataKey.getPublicKey().toString()); - - // Update all of token’s lower-privilege keys - // (trying to update keys one by one to check all errors), - // signing ONLY with an old respective lower-privilege key, - // and setting the key verification mode to FULL_VALIDATION - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setWipeKey(newWipeKey) - .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) - .freezeWith(testEnv.client) - .sign(wipeKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setKycKey(newKycKey) - .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) - .freezeWith(testEnv.client) - .sign(kycKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setFreezeKey(newFreezeKey) - .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) - .freezeWith(testEnv.client) - .sign(freezeKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setPauseKey(newPauseKey) - .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) - .freezeWith(testEnv.client) - .sign(pauseKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setSupplyKey(newSupplyKey) - .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) - .freezeWith(testEnv.client) - .sign(supplyKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setFeeScheduleKey(newFeeScheduleKey) - .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) - .freezeWith(testEnv.client) - .sign(feeScheduleKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setMetadataKey(newMetadataKey) - .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) - .freezeWith(testEnv.client) - .sign(metadataKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - } - } - - /** - * @notice E2E-HIP-540 - * @url https://hips.hedera.com/hip/hip-540 - */ - @Test - @DisplayName("Cannot update all of token’s lower-privilege keys when updating them to a keys with an invalid structure and signing with an old respective lower-privilege and setting key verification mode to NO_VALIDATION") - void cannotUpdateAllLowerPrivilegeKeysWhenUpdatingKeysToStructurallyInvalidKeysSigningOnlyWithOldRespectiveLowerPrivilegeKeyWithKeyVerificationSetToNoValidation() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - // Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys - var wipeKey = PrivateKey.generateED25519(); - var kycKey = PrivateKey.generateED25519(); - var freezeKey = PrivateKey.generateED25519(); - var pauseKey = PrivateKey.generateED25519(); - var supplyKey = PrivateKey.generateED25519(); - var feeScheduleKey = PrivateKey.generateED25519(); - var metadataKey = PrivateKey.generateED25519(); - - // create a non-fungible token - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("Test NFT") - .setTokenSymbol("TNFT") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setWipeKey(wipeKey.getPublicKey()) - .setKycKey(kycKey.getPublicKey()) - .setFreezeKey(freezeKey.getPublicKey()) - .setPauseKey(pauseKey.getPublicKey()) - .setSupplyKey(supplyKey.getPublicKey()) - .setFeeScheduleKey(feeScheduleKey.getPublicKey()) - .setMetadataKey(metadataKey.getPublicKey()) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var tokenInfoBeforeUpdate = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfoBeforeUpdate.wipeKey.toString()).isEqualTo(wipeKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.kycKey.toString()).isEqualTo(kycKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.freezeKey.toString()).isEqualTo(freezeKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.pauseKey.toString()).isEqualTo(pauseKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.supplyKey.toString()).isEqualTo(supplyKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.feeScheduleKey.toString()).isEqualTo(feeScheduleKey.getPublicKey().toString()); - assertThat(tokenInfoBeforeUpdate.metadataKey.toString()).isEqualTo(metadataKey.getPublicKey().toString()); - - // invalid ecdsa key - var ecdsaKey = PublicKey.fromBytesECDSA(new byte[33]); - - // update all of token’s lower-privilege keys - // to a structurally invalid key (trying to update keys one by one to check all errors), - // signing with an old respective lower-privilege - // and setting key verification mode to NO_VALIDATION - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setWipeKey(ecdsaKey) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .freezeWith(testEnv.client) - .sign(wipeKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_WIPE_KEY.toString()); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setKycKey(ecdsaKey) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .freezeWith(testEnv.client) - .sign(kycKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_KYC_KEY.toString()); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setFreezeKey(ecdsaKey) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .freezeWith(testEnv.client) - .sign(freezeKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_FREEZE_KEY.toString()); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setPauseKey(ecdsaKey) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .freezeWith(testEnv.client) - .sign(pauseKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_PAUSE_KEY.toString()); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setSupplyKey(ecdsaKey) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .freezeWith(testEnv.client) - .sign(supplyKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SUPPLY_KEY.toString()); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setFeeScheduleKey(ecdsaKey) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .freezeWith(testEnv.client) - .sign(feeScheduleKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_CUSTOM_FEE_SCHEDULE_KEY.toString()); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenUpdateTransaction() - .setTokenId(tokenId) - .setMetadataKey(ecdsaKey) - .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) - .freezeWith(testEnv.client) - .sign(metadataKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_METADATA_KEY.toString()); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenUpdateNftsIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenUpdateNftsIntegrationTest.java deleted file mode 100644 index 5b256e1596..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenUpdateNftsIntegrationTest.java +++ /dev/null @@ -1,414 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -import com.hedera.hashgraph.sdk.Client; -import com.hedera.hashgraph.sdk.NftId; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.sdk.Status; -import com.hedera.hashgraph.sdk.TokenCreateTransaction; -import com.hedera.hashgraph.sdk.TokenId; -import com.hedera.hashgraph.sdk.TokenInfoQuery; -import com.hedera.hashgraph.sdk.TokenMintTransaction; -import com.hedera.hashgraph.sdk.TokenNftInfoQuery; -import com.hedera.hashgraph.sdk.TokenType; -import com.hedera.hashgraph.sdk.TokenUpdateNftsTransaction; -import java.util.List; -import java.util.Objects; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -/** - * @notice E2E-HIP-657 - * @url https://hips.hedera.com/hip/hip-657 - */ -public class TokenUpdateNftsIntegrationTest { - - @Test - @DisplayName("Can update the metadata of the entire NFT collection") - void canUpdateNFTMetadataOfEntireCollection() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var metadataKey = PrivateKey.generateED25519(); - var nftCount = 4; - var initialMetadataList = NftMetadataGenerator.generate(new byte[]{4, 2, 0}, nftCount); - var updatedMetadata = new byte[]{6, 9}; - var updatedMetadataList = NftMetadataGenerator.generate(updatedMetadata, nftCount); - - // create a token with metadata key - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setMetadataKey(metadataKey) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - // mint tokens - var tokenMintTransactionReceipt = new TokenMintTransaction() - .setMetadata(initialMetadataList) - .setTokenId(tokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // check that metadata was set correctly - var nftSerials = tokenMintTransactionReceipt.serials; - List metadataListAfterMint = getMetadataList(testEnv.client, tokenId, nftSerials); - - assertThat(metadataListAfterMint.toArray()).isEqualTo(initialMetadataList.toArray()); - - // update metadata all minted NFTs - new TokenUpdateNftsTransaction() - .setTokenId(tokenId) - .setSerials(nftSerials) - .setMetadata(updatedMetadata) - .freezeWith(testEnv.client) - .sign(metadataKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // check updated NFTs' metadata - List metadataListAfterUpdate = getMetadataList(testEnv.client, tokenId, nftSerials); - assertThat(metadataListAfterUpdate.toArray()).isEqualTo(updatedMetadataList.toArray()); - - } - } - - @Test - @DisplayName("Can update the metadata of a part of the NFT collection") - void canUpdateNFTMetadataOfPartOfCollection() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var metadataKey = PrivateKey.generateED25519(); - var nftCount = 4; - var initialMetadataList = NftMetadataGenerator.generate(new byte[]{4, 2, 0}, nftCount); - var updatedMetadata = new byte[]{6, 9}; - var updatedMetadataList = NftMetadataGenerator.generate(updatedMetadata, nftCount / 2); - - // create a token with metadata key - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setMetadataKey(metadataKey) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - // mint tokens - var tokenMintTransactionReceipt = new TokenMintTransaction() - .setMetadata(initialMetadataList) - .setTokenId(tokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // check that metadata was set correctly - var nftSerials = tokenMintTransactionReceipt.serials; - List metadataListAfterMint = getMetadataList(testEnv.client, tokenId, nftSerials); - - assertThat(metadataListAfterMint.toArray()).isEqualTo(initialMetadataList.toArray()); - - // update metadata of the first two minted NFTs - var nftSerialsToUpdate = nftSerials.subList(0, nftCount / 2); - - new TokenUpdateNftsTransaction() - .setTokenId(tokenId) - .setSerials(nftSerialsToUpdate) - .setMetadata(updatedMetadata) - .freezeWith(testEnv.client) - .sign(metadataKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // check updated NFTs' metadata - List metadataListAfterUpdate = getMetadataList(testEnv.client, tokenId, nftSerialsToUpdate); - - assertThat(metadataListAfterUpdate.toArray()).isEqualTo(updatedMetadataList.toArray()); - - // check that remaining NFTs were not updated - var nftSerialsSame = nftSerials.subList(nftCount / 2, nftCount); - List metadataList = getMetadataList(testEnv.client, tokenId, nftSerialsSame); - - assertThat(metadataList.toArray()).isEqualTo(initialMetadataList.subList(nftCount / 2, nftCount).toArray()); - - } - } - - @Test - @DisplayName("Cannot update NFTs metadata when it is not set") - void cannotUpdateNFTMetadataWhenItsNotSet() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var metadataKey = PrivateKey.generateED25519(); - var nftCount = 4; - var initialMetadataList = NftMetadataGenerator.generate(new byte[]{4, 2, 0}, nftCount); - - // create a token with metadata key - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setMetadataKey(metadataKey) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - // mint tokens - var tokenMintTransactionReceipt = new TokenMintTransaction() - .setMetadata(initialMetadataList) - .setTokenId(tokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // check that metadata was set correctly - var nftSerials = tokenMintTransactionReceipt.serials; - List metadataListAfterMint = getMetadataList(testEnv.client, tokenId, nftSerials); - - assertThat(metadataListAfterMint.toArray()).isEqualTo(initialMetadataList.toArray()); - - // run `TokenUpdateNftsTransaction` without `setMetadata` - new TokenUpdateNftsTransaction() - .setTokenId(tokenId) - .setSerials(nftSerials) - .freezeWith(testEnv.client) - .sign(metadataKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // check that NFTs' metadata was not updated - List metadataListAfterUpdate = getMetadataList(testEnv.client, tokenId, nftSerials); - assertThat(metadataListAfterUpdate.toArray()).isEqualTo(initialMetadataList.toArray()); - - } - } - - @Test - @DisplayName("Can erase NFTs metadata") - void canEraseNFTsMetadata() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var metadataKey = PrivateKey.generateED25519(); - var nftCount = 4; - var initialMetadataList = NftMetadataGenerator.generate(new byte[]{4, 2, 0}, nftCount); - var emptyMetadata = new byte[]{}; - var emptyMetadataList = NftMetadataGenerator.generate(emptyMetadata, nftCount); - - // create a token with metadata key - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setMetadataKey(metadataKey) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - // mint tokens - var tokenMintTransactionReceipt = new TokenMintTransaction() - .setMetadata(initialMetadataList) - .setTokenId(tokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // check that metadata was set correctly - var nftSerials = tokenMintTransactionReceipt.serials; - List metadataListAfterMint = getMetadataList(testEnv.client, tokenId, nftSerials); - - assertThat(metadataListAfterMint.toArray()).isEqualTo(initialMetadataList.toArray()); - - // erase metadata all minted NFTs (update to an empty byte array) - new TokenUpdateNftsTransaction() - .setTokenId(tokenId) - .setSerials(nftSerials) - .setMetadata(emptyMetadata) - .freezeWith(testEnv.client) - .sign(metadataKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - // check that NFTs' metadata was erased - List metadataListAfterUpdate = getMetadataList(testEnv.client, tokenId, nftSerials); - assertThat(metadataListAfterUpdate.toArray()).isEqualTo(emptyMetadataList.toArray()); - - } - } - - @Test - @DisplayName("Cannot update NFT metadata when transaction is not signed with metadata key") - void cannotUpdateNFTMetadataWhenTransactionIsNotSignedWithMetadataKey() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var supplyKey = PrivateKey.generateED25519(); - var metadataKey = PrivateKey.generateED25519(); - var nftCount = 4; - var initialMetadataList = NftMetadataGenerator.generate(new byte[]{4, 2, 0}, nftCount); - var updatedMetadata = new byte[]{6, 9}; - - // create a token with a metadata key and check it - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setSupplyKey(supplyKey) - .setMetadataKey(metadataKey) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var tokenInfo = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfo.metadataKey.toString()).isEqualTo(metadataKey.getPublicKey().toString()); - - // mint tokens - var tokenMintTransactionReceipt = new TokenMintTransaction() - .setMetadata(initialMetadataList) - .setTokenId(tokenId) - .freezeWith(testEnv.client) - .sign(supplyKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var nftSerials = tokenMintTransactionReceipt.serials; - - // update nfts without signing - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateNftsTransaction() - .setTokenId(tokenId) - .setSerials(nftSerials) - .setMetadata(updatedMetadata) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - } - } - - @Test - @DisplayName("Cannot update NFT metadata when metadata key is not set") - void cannotUpdateNFTMetadataWhenMetadataKeyNotSet() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var supplyKey = PrivateKey.generateED25519(); - var metadataKey = PrivateKey.generateED25519(); - var nftCount = 4; - var initialMetadataList = NftMetadataGenerator.generate(new byte[]{4, 2, 0}, nftCount); - var updatedMetadata = new byte[]{6, 9}; - - // create a token without a metadata key and check it - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setSupplyKey(supplyKey) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var tokenInfo = new TokenInfoQuery() - .setTokenId(tokenId) - .execute(testEnv.client); - - assertThat(tokenInfo.metadataKey).isNull(); - - // mint tokens - var tokenMintTransactionReceipt = new TokenMintTransaction() - .setMetadata(initialMetadataList) - .setTokenId(tokenId) - .freezeWith(testEnv.client) - .sign(supplyKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var nftSerials = tokenMintTransactionReceipt.serials; - - // check NFTs' metadata can't be updated when a metadata key is not set - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenUpdateNftsTransaction() - .setTokenId(tokenId) - .setSerials(nftSerials) - .setMetadata(updatedMetadata) - .freezeWith(testEnv.client) - .sign(metadataKey) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_SIGNATURE.toString()); - - } - } - - /** - * Retrieves the metadata information for a given list of NFT serials associated with a token. - * - * @param client The Hedera client used for executing the query. - * @param tokenId The ID of the token. - * @param nftSerials The list of serial numbers of the NFTs. - * @return A list of byte arrays representing the metadata information for the NFTs. - */ - private List getMetadataList(Client client, TokenId tokenId, List nftSerials) { - return nftSerials.stream() - .map(serial -> new NftId(tokenId, serial)) - .flatMap(nftId -> { - try { - return new TokenNftInfoQuery() - .setNftId(nftId) - .execute(client).stream(); - } catch (Exception e) { - throw new RuntimeException(e); - } - }) - .map(tokenNftInfo -> tokenNftInfo.metadata) - .toList(); - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenWipeIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenWipeIntegrationTest.java deleted file mode 100644 index 2eeb3d1c47..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TokenWipeIntegrationTest.java +++ /dev/null @@ -1,432 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.sdk.Status; -import com.hedera.hashgraph.sdk.TokenAssociateTransaction; -import com.hedera.hashgraph.sdk.TokenCreateTransaction; -import com.hedera.hashgraph.sdk.TokenGrantKycTransaction; -import com.hedera.hashgraph.sdk.TokenMintTransaction; -import com.hedera.hashgraph.sdk.TokenType; -import com.hedera.hashgraph.sdk.TokenWipeTransaction; -import com.hedera.hashgraph.sdk.TransferTransaction; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Collections; -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -class TokenWipeIntegrationTest { - @Test - @DisplayName("Can wipe accounts balance") - void canWipeAccountsBalance() throws Exception { - try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - new TokenAssociateTransaction() - .setAccountId(accountId) - .setTokenIds(Collections.singletonList(tokenId)) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenGrantKycTransaction() - .setAccountId(accountId) - .setTokenId(tokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TransferTransaction() - .addTokenTransfer(tokenId, testEnv.operatorId, -10) - .addTokenTransfer(tokenId, accountId, 10) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenWipeTransaction() - .setTokenId(tokenId) - .setAccountId(accountId) - .setAmount(10) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - - @Test - @DisplayName("Can wipe accounts NFTs") - void canWipeAccountsNfts() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var mintReceipt = new TokenMintTransaction() - .setTokenId(tokenId) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenAssociateTransaction() - .setAccountId(accountId) - .setTokenIds(Collections.singletonList(tokenId)) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenGrantKycTransaction() - .setAccountId(accountId) - .setTokenId(tokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var serialsToTransfer = mintReceipt.serials.subList(0, 4); - var transfer = new TransferTransaction(); - for (var serial : serialsToTransfer) { - transfer.addNftTransfer(tokenId.nft(serial), testEnv.operatorId, accountId); - } - transfer.execute(testEnv.client).getReceipt(testEnv.client); - - new TokenWipeTransaction() - .setTokenId(tokenId) - .setAccountId(accountId) - .setSerials(serialsToTransfer) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - - @Test - @DisplayName("Cannot wipe accounts NFTs if the account doesn't own them") - void cannotWipeAccountsNftsIfNotOwned() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - var mintReceipt = new TokenMintTransaction() - .setTokenId(tokenId) - .setMetadata(NftMetadataGenerator.generate((byte) 10)) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenAssociateTransaction() - .setAccountId(accountId) - .setTokenIds(Collections.singletonList(tokenId)) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenGrantKycTransaction() - .setAccountId(accountId) - .setTokenId(tokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var serialsToTransfer = mintReceipt.serials.subList(0, 4); - // don't transfer them - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TokenWipeTransaction() - .setTokenId(tokenId) - .setAccountId(accountId) - .setSerials(serialsToTransfer) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.ACCOUNT_DOES_NOT_OWN_WIPED_NFT.toString()); - - } - } - - @Test - @DisplayName("Cannot wipe accounts balance when account ID is not set") - void cannotWipeAccountsBalanceWhenAccountIDIsNotSet() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - new TokenAssociateTransaction() - .setAccountId(accountId) - .setTokenIds(Collections.singletonList(tokenId)) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenGrantKycTransaction() - .setAccountId(accountId) - .setTokenId(tokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TransferTransaction() - .addTokenTransfer(tokenId, testEnv.operatorId, -10) - .addTokenTransfer(tokenId, accountId, 10) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenWipeTransaction() - .setTokenId(tokenId) - .setAmount(10) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_ACCOUNT_ID.toString()); - - } - } - - @Test - @DisplayName("Cannot wipe accounts balance when token ID is not set") - void cannotWipeAccountsBalanceWhenTokenIDIsNotSet() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - new TokenAssociateTransaction() - .setAccountId(accountId) - .setTokenIds(Collections.singletonList(tokenId)) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenGrantKycTransaction() - .setAccountId(accountId) - .setTokenId(tokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TransferTransaction() - .addTokenTransfer(tokenId, testEnv.operatorId, -10) - .addTokenTransfer(tokenId, accountId, 10) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TokenWipeTransaction() - .setAccountId(accountId) - .setAmount(10) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_TOKEN_ID.toString()); - - } - } - - @Test - @DisplayName("Can wipe accounts balance when amount is not set") - void canWipeAccountsBalanceWhenAmountIsNotSet() throws Exception { - try(var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()){ - - var key = PrivateKey.generateED25519(); - - var response = new AccountCreateTransaction() - .setKey(key) - .setInitialBalance(new Hbar(1)) - .execute(testEnv.client); - - var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); - - var tokenId = Objects.requireNonNull( - new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setDecimals(3) - .setInitialSupply(1000000) - .setTreasuryAccountId(testEnv.operatorId) - .setAdminKey(testEnv.operatorKey) - .setFreezeKey(testEnv.operatorKey) - .setWipeKey(testEnv.operatorKey) - .setKycKey(testEnv.operatorKey) - .setSupplyKey(testEnv.operatorKey) - .setFreezeDefault(false) - .execute(testEnv.client) - .getReceipt(testEnv.client) - .tokenId - ); - - new TokenAssociateTransaction() - .setAccountId(accountId) - .setTokenIds(Collections.singletonList(tokenId)) - .freezeWith(testEnv.client) - .sign(key) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TokenGrantKycTransaction() - .setAccountId(accountId) - .setTokenId(tokenId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - new TransferTransaction() - .addTokenTransfer(tokenId, testEnv.operatorId, -10) - .addTokenTransfer(tokenId, accountId, 10) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - - var receipt = new TokenWipeTransaction() - .setTokenId(tokenId) - .setAccountId(accountId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - assertThat(receipt.status).isEqualTo(Status.SUCCESS); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TopicCreateIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TopicCreateIntegrationTest.java deleted file mode 100644 index 375f7ee137..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TopicCreateIntegrationTest.java +++ /dev/null @@ -1,63 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.TopicCreateTransaction; -import com.hedera.hashgraph.sdk.TopicDeleteTransaction; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThat; - -public class TopicCreateIntegrationTest { - @Test - @DisplayName("Can create topic") - void canCreateTopic() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new TopicCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setTopicMemo("[e2e::TopicCreateTransaction]") - .execute(testEnv.client); - - var topicId = Objects.requireNonNull(response.getReceipt(testEnv.client).topicId); - - new TopicDeleteTransaction() - .setTopicId(topicId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can create topic with no field set") - void canCreateTopicWithNoFieldsSet() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new TopicCreateTransaction() - .execute(testEnv.client); - assertThat(response.getReceipt(testEnv.client).topicId).isNotNull(); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TopicDeleteIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TopicDeleteIntegrationTest.java deleted file mode 100644 index 6a33a35b1b..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TopicDeleteIntegrationTest.java +++ /dev/null @@ -1,73 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.sdk.Status; -import com.hedera.hashgraph.sdk.TopicCreateTransaction; -import com.hedera.hashgraph.sdk.TopicDeleteTransaction; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -public class TopicDeleteIntegrationTest { - @Test - @DisplayName("Can delete topic") - void canDeleteTopic() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new TopicCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setTopicMemo("[e2e::TopicCreateTransaction]") - .execute(testEnv.client); - - var topicId = Objects.requireNonNull(response.getReceipt(testEnv.client).topicId); - - new TopicDeleteTransaction() - .setTopicId(topicId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Cannot delete immutable topic") - void cannotDeleteImmutableTopic() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new TopicCreateTransaction() - .execute(testEnv.client); - - var topicId = Objects.requireNonNull(response.getReceipt(testEnv.client).topicId); - - assertThatExceptionOfType(ReceiptStatusException.class).isThrownBy(() -> { - new TopicDeleteTransaction() - .setTopicId(topicId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.UNAUTHORIZED.toString()); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TopicInfoIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TopicInfoIntegrationTest.java deleted file mode 100644 index 014f40399b..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TopicInfoIntegrationTest.java +++ /dev/null @@ -1,186 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * Copyright (C) 2021 - 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.MaxQueryPaymentExceededException; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.TopicCreateTransaction; -import com.hedera.hashgraph.sdk.TopicDeleteTransaction; -import com.hedera.hashgraph.sdk.TopicInfoQuery; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -public class TopicInfoIntegrationTest { - - @Test - @DisplayName("Can query topic info") - void canQueryTopicInfo() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new TopicCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setTopicMemo("[e2e::TopicCreateTransaction]") - .execute(testEnv.client); - - var topicId = Objects.requireNonNull(response.getReceipt(testEnv.client).topicId); - - var info = new TopicInfoQuery() - .setTopicId(topicId) - .execute(testEnv.client); - - assertThat(info.topicMemo).isEqualTo("[e2e::TopicCreateTransaction]"); - - new TopicDeleteTransaction() - .setTopicId(topicId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can get cost for topic info query") - void getCostQueryTopicInfo() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new TopicCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setTopicMemo("[e2e::TopicCreateTransaction]") - .execute(testEnv.client); - - var topicId = Objects.requireNonNull(response.getReceipt(testEnv.client).topicId); - - var infoQuery = new TopicInfoQuery() - .setTopicId(topicId); - - var cost = infoQuery.getCost(testEnv.client); - - assertThat(cost).isNotNull(); - - var info = infoQuery.execute(testEnv.client); - - assertThat(info.topicMemo).isEqualTo("[e2e::TopicCreateTransaction]"); - - new TopicDeleteTransaction() - .setTopicId(topicId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can get cost for topic info query") - void getCostBigMaxQueryTopicInfo() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new TopicCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setTopicMemo("[e2e::TopicCreateTransaction]") - .execute(testEnv.client); - - var topicId = Objects.requireNonNull(response.getReceipt(testEnv.client).topicId); - - var infoQuery = new TopicInfoQuery() - .setTopicId(topicId) - .setMaxQueryPayment(new Hbar(1000)); - - var cost = infoQuery.getCost(testEnv.client); - - assertThat(cost).isNotNull(); - - var info = infoQuery.execute(testEnv.client); - - assertThat(info.topicMemo).isEqualTo("[e2e::TopicCreateTransaction]"); - - new TopicDeleteTransaction() - .setTopicId(topicId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can get cost for topic info query") - void getCostSmallMaxQueryTopicInfo() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new TopicCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setTopicMemo("[e2e::TopicCreateTransaction]") - .execute(testEnv.client); - - var topicId = Objects.requireNonNull(response.getReceipt(testEnv.client).topicId); - - var infoQuery = new TopicInfoQuery() - .setTopicId(topicId) - .setMaxQueryPayment(Hbar.fromTinybars(1)); - - assertThatExceptionOfType(MaxQueryPaymentExceededException.class).isThrownBy(() -> { - infoQuery.execute(testEnv.client); - }); - - new TopicDeleteTransaction() - .setTopicId(topicId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can get cost for topic info query") - void getCostInsufficientTxFeeQueryTopicInfo() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new TopicCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setTopicMemo("[e2e::TopicCreateTransaction]") - .execute(testEnv.client); - - var topicId = Objects.requireNonNull(response.getReceipt(testEnv.client).topicId); - - var infoQuery = new TopicInfoQuery() - .setTopicId(topicId); - - var cost = infoQuery.getCost(testEnv.client); - - assertThat(cost).isNotNull(); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - infoQuery.setQueryPayment(Hbar.fromTinybars(1)).execute(testEnv.client); - }).satisfies(error -> assertThat(error.status.toString()).isEqualTo("INSUFFICIENT_TX_FEE")); - - new TopicDeleteTransaction() - .setTopicId(topicId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TopicMessageIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TopicMessageIntegrationTest.java deleted file mode 100644 index a8369b4edd..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TopicMessageIntegrationTest.java +++ /dev/null @@ -1,149 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.*; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import java.time.Duration; -import java.time.Instant; - -import java.nio.charset.StandardCharsets; -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThat; - -public class TopicMessageIntegrationTest { - @Test - @DisplayName("Can receive a topic message") - void canReceiveATopicMessage() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new TopicCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setTopicMemo("[e2e::TopicCreateTransaction]") - .execute(testEnv.client); - - var topicId = Objects.requireNonNull(response.getReceipt(testEnv.client).topicId); - - var info = new TopicInfoQuery() - .setTopicId(topicId) - .execute(testEnv.client); - - assertThat(info.topicId).isEqualTo(topicId); - assertThat(info.topicMemo).isEqualTo("[e2e::TopicCreateTransaction]"); - assertThat(info.sequenceNumber).isEqualTo(0); - assertThat(info.adminKey).isEqualTo(testEnv.operatorKey); - - Thread.sleep(3000); - - var receivedMessage = new boolean[]{false}; - var start = Instant.now(); - - var handle = new TopicMessageQuery() - .setTopicId(topicId) - .setStartTime(Instant.EPOCH) - .subscribe(testEnv.client, (message) -> { - receivedMessage[0] = new String(message.contents, StandardCharsets.UTF_8).equals("Hello, from HCS!"); - }); - - Thread.sleep(3000); - - new TopicMessageSubmitTransaction() - .setTopicId(topicId) - .setMessage("Hello, from HCS!") - .execute(testEnv.client) - .getReceipt(testEnv.client); - - while (!receivedMessage[0]) { - if (Duration.between(start, Instant.now()).compareTo(Duration.ofSeconds(60)) > 0) { - throw new Exception("TopicMessage was not received in 60 seconds or less"); - } - - Thread.sleep(2000); - } - - new TopicDeleteTransaction() - .setTopicId(topicId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can receive a large topic message") - void canReceiveALargeTopicMessage() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - // Skip if using local node. - // Note: this check should be removed once the local node is supporting multiple nodes. - testEnv.assumeNotLocalNode(); - - var response = new TopicCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setTopicMemo("[e2e::TopicCreateTransaction]") - .execute(testEnv.client); - - var topicId = Objects.requireNonNull(response.getReceipt(testEnv.client).topicId); - - Thread.sleep(5000); - - var info = new TopicInfoQuery() - .setTopicId(topicId) - .execute(testEnv.client); - - assertThat(info.topicId).isEqualTo(topicId); - assertThat(info.topicMemo).isEqualTo("[e2e::TopicCreateTransaction]"); - assertThat(info.sequenceNumber).isEqualTo(0); - assertThat(info.adminKey).isEqualTo(testEnv.operatorKey); - - var receivedMessage = new boolean[]{false}; - var start = Instant.now(); - - var handle = new TopicMessageQuery() - .setTopicId(topicId) - .setStartTime(Instant.EPOCH) - .subscribe(testEnv.client, (message) -> { - receivedMessage[0] = new String(message.contents, StandardCharsets.UTF_8).equals(Contents.BIG_CONTENTS); - }); - - new TopicMessageSubmitTransaction() - .setTopicId(topicId) - .setMessage(Contents.BIG_CONTENTS) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - while (!receivedMessage[0]) { - if (Duration.between(start, Instant.now()).compareTo(Duration.ofSeconds(60)) > 0) { - throw new Exception("TopicMessage was not received in 60 seconds or less"); - } - - Thread.sleep(1000); - } - - new TopicDeleteTransaction() - .setTopicId(topicId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TopicMessageSubmitIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TopicMessageSubmitIntegrationTest.java deleted file mode 100644 index e729532657..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TopicMessageSubmitIntegrationTest.java +++ /dev/null @@ -1,217 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.Status; -import com.hedera.hashgraph.sdk.TopicCreateTransaction; -import com.hedera.hashgraph.sdk.TopicDeleteTransaction; -import com.hedera.hashgraph.sdk.TopicInfoQuery; -import com.hedera.hashgraph.sdk.TopicMessageSubmitTransaction; -import com.hedera.hashgraph.sdk.Transaction; -import org.bouncycastle.util.encoders.Hex; -import org.junit.jupiter.api.Assumptions; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatNoException; - -public class TopicMessageSubmitIntegrationTest { - @Test - @DisplayName("Can submit a topic message") - void canSubmitATopicMessage() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new TopicCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setTopicMemo("[e2e::TopicCreateTransaction]") - .execute(testEnv.client); - - var topicId = Objects.requireNonNull(response.getReceipt(testEnv.client).topicId); - - var info = new TopicInfoQuery() - .setTopicId(topicId) - .execute(testEnv.client); - - assertThat(info.topicId).isEqualTo(topicId); - assertThat(info.topicMemo).isEqualTo("[e2e::TopicCreateTransaction]"); - assertThat(info.sequenceNumber).isEqualTo(0); - assertThat(info.adminKey).isEqualTo(testEnv.operatorKey); - - new TopicMessageSubmitTransaction() - .setTopicId(topicId) - .setMessage("Hello, from HCS!") - .execute(testEnv.client) - .getReceipt(testEnv.client); - - info = new TopicInfoQuery() - .setTopicId(topicId) - .execute(testEnv.client); - - assertThat(info.topicId).isEqualTo(topicId); - assertThat(info.topicMemo).isEqualTo("[e2e::TopicCreateTransaction]"); - assertThat(info.sequenceNumber).isEqualTo(1); - assertThat(info.adminKey).isEqualTo(testEnv.operatorKey); - - new TopicDeleteTransaction() - .setTopicId(topicId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } - - @Test - @DisplayName("Can submit a large topic message") - void canSubmitALargeTopicMessage() { - // Skip if using PreviewNet - Assumptions.assumeTrue(!System.getProperty("HEDERA_NETWORK").equals("previewnet")); - - assertThatNoException().isThrownBy(() -> { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new TopicCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setTopicMemo("[e2e::TopicCreateTransaction]") - .execute(testEnv.client); - - var topicId = Objects.requireNonNull(response.getReceipt(testEnv.client).topicId); - - Thread.sleep(5000); - - var info = new TopicInfoQuery() - .setTopicId(topicId) - .execute(testEnv.client); - - assertThat(info.topicId).isEqualTo(topicId); - assertThat(info.topicMemo).isEqualTo("[e2e::TopicCreateTransaction]"); - assertThat(info.sequenceNumber).isEqualTo(0); - assertThat(info.adminKey).isEqualTo(testEnv.operatorKey); - - var responses = new TopicMessageSubmitTransaction() - .setTopicId(topicId) - .setMaxChunks(15) - .setMessage(Contents.BIG_CONTENTS) - .executeAll(testEnv.client); - - for (var resp : responses) { - resp.getReceipt(testEnv.client); - } - - info = new TopicInfoQuery() - .setTopicId(topicId) - .execute(testEnv.client); - - assertThat(info.topicId).isEqualTo(topicId); - assertThat(info.topicMemo).isEqualTo("[e2e::TopicCreateTransaction]"); - assertThat(info.sequenceNumber).isEqualTo(14); - assertThat(info.adminKey).isEqualTo(testEnv.operatorKey); - - new TopicDeleteTransaction() - .setTopicId(topicId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - }); - } - - @Test - @DisplayName("Cannot submit message when topic ID is not set") - void cannotSubmitMessageWhenTopicIDIsNotSet() { - // Skip if using PreviewNet - Assumptions.assumeTrue(!System.getProperty("HEDERA_NETWORK").equals("previewnet")); - - assertThatNoException().isThrownBy(() -> { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new TopicCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setTopicMemo("[e2e::TopicCreateTransaction]") - .execute(testEnv.client); - - var topicId = Objects.requireNonNull(response.getReceipt(testEnv.client).topicId); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TopicMessageSubmitTransaction() - .setMessage(Contents.BIG_CONTENTS) - .setMaxChunks(15) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_TOPIC_ID.toString()); - - new TopicDeleteTransaction() - .setTopicId(topicId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - }); - } - - @Test - @DisplayName("Cannot submit message when message is not set") - void cannotSubmitMessageWhenMessageIsNotSet() { - // Skip if using PreviewNet - Assumptions.assumeTrue(!System.getProperty("HEDERA_NETWORK").equals("previewnet")); - - assertThatNoException().isThrownBy(() -> { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new TopicCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setTopicMemo("[e2e::TopicCreateTransaction]") - .execute(testEnv.client); - - var topicId = Objects.requireNonNull(response.getReceipt(testEnv.client).topicId); - - assertThatExceptionOfType(PrecheckStatusException.class).isThrownBy(() -> { - new TopicMessageSubmitTransaction() - .setTopicId(topicId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - }).withMessageContaining(Status.INVALID_TOPIC_MESSAGE.toString()); - - new TopicDeleteTransaction() - .setTopicId(topicId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - }); - } - - @Test - @DisplayName("Hex Decode Regression Test") - @SuppressWarnings("UnusedVariable") - void decodeHexRegressionTest() throws Exception { - String binaryHex = "2ac2010a580a130a0b08d38f8f880610a09be91512041899e11c120218041880c2d72f22020878da01330a0418a5a1201210303030303030313632373633373731351a190a130a0b08d38f8f880610a09be91512041899e11c1001180112660a640a20603edaec5d1c974c92cb5bee7b011310c3b84b13dc048424cd6ef146d6a0d4a41a40b6a08f310ee29923e5868aac074468b2bde05da95a806e2f4a4f452177f129ca0abae7831e595b5beaa1c947e2cb71201642bab33fece5184b04547afc40850a"; - byte[] transactionBytes = Hex.decode(binaryHex); - - var transaction = Objects.requireNonNull(Transaction.fromBytes(transactionBytes)); - - String idString = Objects.requireNonNull(transaction.getTransactionId()).toString(); - String transactionString = transaction.toString(); - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TopicUpdateIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TopicUpdateIntegrationTest.java deleted file mode 100644 index ad5ae1ef6b..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TopicUpdateIntegrationTest.java +++ /dev/null @@ -1,68 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * Copyright (C) 2021 - 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.TopicCreateTransaction; -import com.hedera.hashgraph.sdk.TopicDeleteTransaction; -import com.hedera.hashgraph.sdk.TopicInfoQuery; -import com.hedera.hashgraph.sdk.TopicUpdateTransaction; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThat; - -public class TopicUpdateIntegrationTest { - @Test - @DisplayName("Can update topic") - void canUpdateTopic() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var response = new TopicCreateTransaction() - .setAdminKey(testEnv.operatorKey) - .setAutoRenewAccountId(testEnv.operatorId) - .setTopicMemo("[e2e::TopicCreateTransaction]") - .execute(testEnv.client); - - var topicId = Objects.requireNonNull(response.getReceipt(testEnv.client).topicId); - - new TopicUpdateTransaction() - .clearAutoRenewAccountId() - .setTopicMemo("hello") - .setTopicId(topicId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - var topicInfo = new TopicInfoQuery() - .setTopicId(topicId) - .execute(testEnv.client); - - assertThat(topicInfo.topicMemo).isEqualTo("hello"); - assertThat(topicInfo.autoRenewAccountId).isNull(); - - new TopicDeleteTransaction() - .setTopicId(topicId) - .execute(testEnv.client) - .getReceipt(testEnv.client); - - } - } -} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TransactionResponseTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TransactionResponseTest.java deleted file mode 100644 index ab85d144d5..0000000000 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TransactionResponseTest.java +++ /dev/null @@ -1,51 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; - -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.PrivateKey; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class TransactionResponseTest { - @Test - @DisplayName("transaction hash in transaction record is equal to the transaction response transaction hash") - void transactionHashInTransactionRecordIsEqualToTheTransactionResponseTransactionHash() throws Exception { - try (var testEnv = new IntegrationTestEnv(1)) { - - var key = PrivateKey.generateED25519(); - - var transaction = new AccountCreateTransaction() - .setKey(key) - .execute(testEnv.client); - - var record = transaction.getRecord(testEnv.client); - - assertThat(record.transactionHash.toByteArray()).containsExactly(transaction.transactionHash); - - var accountId = record.receipt.accountId; - assertThat(accountId).isNotNull(); - - } - } -} - diff --git a/sdk/src/testIntegration/java/module-info.java b/sdk/src/testIntegration/java/module-info.java index 358c841ee3..e4fb4fd780 100644 --- a/sdk/src/testIntegration/java/module-info.java +++ b/sdk/src/testIntegration/java/module-info.java @@ -18,8 +18,8 @@ * */ -open module com.hedera.hashgraph.sdk.test.integration { - requires com.hedera.hashgraph.sdk; +open module org.hiero.sdk.test.integration { + requires org.hiero.sdk; requires com.esaulpaugh.headlong; requires org.assertj.core; requires org.bouncycastle.provider; diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/AccountAllowanceIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/AccountAllowanceIntegrationTest.java new file mode 100644 index 0000000000..f32255a18b --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/AccountAllowanceIntegrationTest.java @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Objects; +import org.hiero.sdk.AccountAllowanceApproveTransaction; +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.AccountDeleteTransaction; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.TransactionId; +import org.hiero.sdk.TransferTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class AccountAllowanceIntegrationTest { + @Test + @DisplayName("Can spend hbar allowance") + void canSpendHbarAllowance() throws Throwable { + try (var testEnv = new IntegrationTestEnv(1)) { + + var aliceKey = PrivateKey.generateED25519(); + var aliceId = new AccountCreateTransaction() + .setKey(aliceKey) + .setInitialBalance(new Hbar(10)) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + var bobKey = PrivateKey.generateED25519(); + var bobId = new AccountCreateTransaction() + .setKey(bobKey) + .setInitialBalance(new Hbar(10)) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + Objects.requireNonNull(aliceId); + Objects.requireNonNull(bobId); + + new AccountAllowanceApproveTransaction() + .approveHbarAllowance(bobId, aliceId, new Hbar(10)) + .freezeWith(testEnv.client) + .sign(bobKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var transferRecord = new TransferTransaction() + .addHbarTransfer(testEnv.operatorId, new Hbar(5)) + .addApprovedHbarTransfer(bobId, new Hbar(5).negated()) + .setTransactionId(TransactionId.generate(aliceId)) + .freezeWith(testEnv.client) + .sign(aliceKey) + .execute(testEnv.client) + .getRecord(testEnv.client); + + var transferFound = false; + for (var transfer : transferRecord.transfers) { + if (transfer.accountId.equals(testEnv.operatorId) && transfer.amount.equals(new Hbar(5))) { + transferFound = true; + break; + } + } + assertThat(transferFound).isTrue(); + + new AccountDeleteTransaction() + .setAccountId(bobId) + .setTransferAccountId(testEnv.operatorId) + .freezeWith(testEnv.client) + .sign(bobKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/AccountBalanceIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/AccountBalanceIntegrationTest.java new file mode 100644 index 0000000000..3657ba3180 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/AccountBalanceIntegrationTest.java @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Collections; +import java.util.Objects; +import org.hiero.sdk.AccountBalanceQuery; +import org.hiero.sdk.AccountId; +import org.hiero.sdk.Client; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.Status; +import org.hiero.sdk.TokenCreateTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class AccountBalanceIntegrationTest { + @Test + @DisplayName("can connect to previewnwet with TLS") + void canConnectToPreviewnetWithTLS() throws Exception { + var client = Client.forPreviewnet().setTransportSecurity(true); + + boolean succeededAtLeastOnce = false; + + for (var entry : client.getNetwork().entrySet()) { + assertThat(entry.getKey().endsWith(":50212")).isTrue(); + + try { + new AccountBalanceQuery() + .setMaxAttempts(1) + .setNodeAccountIds(Collections.singletonList(entry.getValue())) + .setAccountId(entry.getValue()) + .execute(client); + System.out.println("succeeded for " + entry); + succeededAtLeastOnce = true; + } catch (Throwable error) { + System.out.println("failed for " + entry); + } + } + + client.close(); + assertThat(succeededAtLeastOnce).isTrue(); + } + + @Test + @DisplayName("can connect to testnet with TLS") + void canConnectToTestnetWithTLS() throws Exception { + var client = Client.forTestnet().setTransportSecurity(true); + + boolean succeededAtLeastOnce = false; + + for (var entry : client.getNetwork().entrySet()) { + assertThat(entry.getKey().endsWith(":50212")).isTrue(); + + try { + new AccountBalanceQuery() + .setMaxAttempts(1) + .setNodeAccountIds(Collections.singletonList(entry.getValue())) + .setAccountId(entry.getValue()) + .execute(client); + System.out.println("succeeded for " + entry); + succeededAtLeastOnce = true; + } catch (Throwable error) { + System.out.println("failed for " + entry); + } + } + + client.close(); + assertThat(succeededAtLeastOnce).isTrue(); + } + + @Test + @DisplayName("can connect to mainnet with TLS") + void canConnectToMainnetWithTLS() throws Exception { + var client = Client.forMainnet().setTransportSecurity(true); + + boolean succeededAtLeastOnce = false; + + for (var entry : client.getNetwork().entrySet()) { + assertThat(entry.getKey().endsWith(":50212")).isTrue(); + + try { + new AccountBalanceQuery() + .setMaxAttempts(1) + .setNodeAccountIds(Collections.singletonList(entry.getValue())) + .setAccountId(entry.getValue()) + .execute(client); + System.out.println("succeeded for " + entry); + succeededAtLeastOnce = true; + } catch (Throwable error) { + System.out.println("failed for " + entry); + System.out.println(error); + } + } + + client.close(); + assertThat(succeededAtLeastOnce).isTrue(); + } + + @Test + @DisplayName("can connect to previewnet with certificate verification off") + void cannotConnectToPreviewnetWhenNetworkNameIsNullAndCertificateVerificationIsEnabled() throws Exception { + var client = Client.forPreviewnet() + .setTransportSecurity(true) + .setVerifyCertificates(true) + .setNetworkName(null); + + assertThat(client.getNetwork().isEmpty()).isFalse(); + + for (var entry : client.getNetwork().entrySet()) { + assertThat(entry.getKey().endsWith(":50212")).isTrue(); + + assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> { + new AccountBalanceQuery() + .setNodeAccountIds(Collections.singletonList(entry.getValue())) + .setAccountId(entry.getValue()) + .execute(client); + }); + } + + client.close(); + } + + @Test + @DisplayName("Can fetch balance for client operator") + void canFetchBalanceForClientOperator() throws Exception { + try (IntegrationTestEnv testEnv = new IntegrationTestEnv(1)) { + + var balance = + new AccountBalanceQuery().setAccountId(testEnv.operatorId).execute(testEnv.client); + + assertThat(balance.hbars.toTinybars() > 0).isTrue(); + } + } + + @Test + @DisplayName("Can fetch cost for the query") + void getCostBalanceForClientOperator() throws Exception { + try (IntegrationTestEnv testEnv = new IntegrationTestEnv(1)) { + + var balance = + new AccountBalanceQuery().setAccountId(testEnv.operatorId).setMaxQueryPayment(new Hbar(1)); + + var cost = balance.getCost(testEnv.client); + + var accBalance = balance.setQueryPayment(cost).execute(testEnv.client); + + assertThat(accBalance.hbars.toTinybars() > 0).isTrue(); + assertThat(cost.toTinybars()).isEqualTo(0); + } + } + + @Test + @DisplayName("Can fetch cost for the query, big max set") + void getCostBigMaxBalanceForClientOperator() throws Exception { + try (IntegrationTestEnv testEnv = new IntegrationTestEnv(1)) { + + var balance = + new AccountBalanceQuery().setAccountId(testEnv.operatorId).setMaxQueryPayment(new Hbar(1000000)); + + var cost = balance.getCost(testEnv.client); + + var accBalance = balance.setQueryPayment(cost).execute(testEnv.client); + + assertThat(accBalance.hbars.toTinybars() > 0).isTrue(); + } + } + + @Test + @DisplayName("Can fetch cost for the query, very small max set") + void getCostSmallMaxBalanceForClientOperator() throws Exception { + try (IntegrationTestEnv testEnv = new IntegrationTestEnv(1)) { + + var balance = + new AccountBalanceQuery().setAccountId(testEnv.operatorId).setMaxQueryPayment(Hbar.fromTinybars(1)); + + var cost = balance.getCost(testEnv.client); + + var accBalance = balance.setQueryPayment(cost).execute(testEnv.client); + + assertThat(accBalance.hbars.toTinybars() > 0).isTrue(); + } + } + + @Test + @DisplayName("Cannot fetch balance for invalid account ID") + void canNotFetchBalanceForInvalidAccountId() throws Exception { + try (IntegrationTestEnv testEnv = new IntegrationTestEnv(1)) { + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new AccountBalanceQuery() + .setAccountId(AccountId.fromString("1.0.3")) + .execute(testEnv.client); + }) + .withMessageContaining(Status.INVALID_ACCOUNT_ID.toString()); + } + } + + @Test + @DisplayName("Can fetch token balances for client operator") + void canFetchTokenBalancesForClientOperator() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var response = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setInitialSupply(10000) + .setDecimals(50) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client); + + var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); + + var query = new AccountBalanceQuery(); + var balance = query.setAccountId(testEnv.operatorId).execute(testEnv.client); + + assertThat(balance.tokens.get(tokenId)).isEqualTo(10000); + assertThat(balance.tokenDecimals.get(tokenId)).isEqualTo(50); + assertThat(query.toString()).isNotEmpty(); + assertThat(query.getPaymentTransactionId()).isNull(); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/AccountCreateIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/AccountCreateIntegrationTest.java new file mode 100644 index 0000000000..ae0724598c --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/AccountCreateIntegrationTest.java @@ -0,0 +1,357 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.time.Duration; +import java.time.Instant; +import java.util.Objects; +import org.hiero.sdk.*; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class AccountCreateIntegrationTest { + @Test + @DisplayName("Can create account with only initial balance and key") + void canCreateAccountWithOnlyInitialBalanceAndKey() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + var info = new AccountInfoQuery().setAccountId(accountId).execute(testEnv.client); + + assertThat(info.accountId).isEqualTo(accountId); + assertThat(info.isDeleted).isFalse(); + assertThat(info.key.toString()).isEqualTo(key.getPublicKey().toString()); + assertThat(info.balance).isEqualTo(new Hbar(1)); + assertThat(info.autoRenewPeriod).isEqualTo(Duration.ofDays(90)); + assertThat(info.proxyAccountId).isNull(); + assertThat(info.proxyReceived).isEqualTo(Hbar.ZERO); + } + } + + @Test + @DisplayName("Can create account with no initial balance") + void canCreateAccountWithNoInitialBalance() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction().setKey(key).execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + var info = new AccountInfoQuery().setAccountId(accountId).execute(testEnv.client); + + assertThat(info.accountId).isEqualTo(accountId); + assertThat(info.isDeleted).isFalse(); + assertThat(info.key.toString()).isEqualTo(key.getPublicKey().toString()); + assertThat(info.balance).isEqualTo(new Hbar(0)); + assertThat(info.autoRenewPeriod).isEqualTo(Duration.ofDays(90)); + assertThat(info.proxyAccountId).isNull(); + assertThat(info.proxyReceived).isEqualTo(Hbar.ZERO); + } + } + + @Test + @DisplayName("Cannot create account with no key") + void canNotCreateAccountWithNoKey() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> new AccountCreateTransaction() + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client) + .getReceipt(testEnv.client)) + .withMessageContaining(Status.KEY_REQUIRED.toString()); + } + } + + @Test + @DisplayName("Can create account using aliasKey") + void canCreateWithAliasKey() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var key = PrivateKey.generateED25519(); + + var aliasId = key.toAccountId(0, 0); + + new TransferTransaction() + .addHbarTransfer(testEnv.operatorId, new Hbar(10).negated()) + .addHbarTransfer(aliasId, new Hbar(10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var info = new AccountInfoQuery().setAccountId(aliasId).execute(testEnv.client); + + assertThat(key.getPublicKey()).isEqualTo(info.aliasKey); + } + } + + @Test + @DisplayName("Regenerates TransactionIds in response to expiration") + void managesExpiration() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setTransactionId( + new TransactionId(testEnv.operatorId, Instant.now().minusSeconds(40))) + .setTransactionValidDuration(Duration.ofSeconds(30)) + .freezeWith(testEnv.client) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + var info = new AccountInfoQuery().setAccountId(accountId).execute(testEnv.client); + + assertThat(info.accountId).isEqualTo(accountId); + assertThat(info.isDeleted).isFalse(); + assertThat(info.key.toString()).isEqualTo(key.getPublicKey().toString()); + assertThat(info.balance).isEqualTo(new Hbar(0)); + assertThat(info.autoRenewPeriod).isEqualTo(Duration.ofDays(90)); + assertThat(info.proxyAccountId).isNull(); + assertThat(info.proxyReceived).isEqualTo(Hbar.ZERO); + } + } + + @Test + @DisplayName("Can create account with alias from admin key") + void createAccountWithAliasFromAdminKey() throws Exception { + // Tests the third row of this table + // https://github.com/hashgraph/hedera-improvement-proposal/blob/d39f740021d7da592524cffeaf1d749803798e9a/HIP/hip-583.md#signatures + try (var testEnv = new IntegrationTestEnv(1)) { + + var adminKey = PrivateKey.generateECDSA(); + var evmAddress = adminKey.getPublicKey().toEvmAddress(); + + // Create the admin account + new AccountCreateTransaction() + .setKey(adminKey) + .freezeWith(testEnv.client) + .execute(testEnv.client); + + var accountId = new AccountCreateTransaction() + .setKey(adminKey) + .setAlias(evmAddress) + .freezeWith(testEnv.client) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + assertThat(accountId).isNotNull(); + + var info = new AccountInfoQuery().setAccountId(accountId).execute(testEnv.client); + + assertThat(info.accountId).isNotNull(); + assertThat(info.contractAccountId).hasToString(evmAddress.toString()); + assertThat(info.key).isEqualTo(adminKey.getPublicKey()); + } + } + + @Test + @DisplayName("Can create account with alias from admin key with receiver sig required") + void createAccountWithAliasFromAdminKeyWithReceiverSigRequired() throws Exception { + // Tests the fourth row of this table + // https://github.com/hashgraph/hedera-improvement-proposal/blob/d39f740021d7da592524cffeaf1d749803798e9a/HIP/hip-583.md#signatures + try (var testEnv = new IntegrationTestEnv(1)) { + + var adminKey = PrivateKey.generateECDSA(); + var evmAddress = adminKey.getPublicKey().toEvmAddress(); + + // Create the admin account + new AccountCreateTransaction() + .setKey(adminKey) + .freezeWith(testEnv.client) + .execute(testEnv.client); + + var accountId = new AccountCreateTransaction() + .setReceiverSignatureRequired(true) + .setKey(adminKey) + .setAlias(evmAddress) + .freezeWith(testEnv.client) + .sign(adminKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + assertThat(accountId).isNotNull(); + + var info = new AccountInfoQuery().setAccountId(accountId).execute(testEnv.client); + + assertThat(info.accountId).isNotNull(); + assertThat(info.contractAccountId).hasToString(evmAddress.toString()); + assertThat(info.key).isEqualTo(adminKey.getPublicKey()); + } + } + + @Test + @DisplayName("Cannot create account with alias from admin key with receiver sig required without signature") + void cannotCreateAccountWithAliasFromAdminKeyWithReceiverSigRequiredAndNoSignature() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var adminKey = PrivateKey.generateECDSA(); + var evmAddress = adminKey.getPublicKey().toEvmAddress(); + + // Create the admin account + new AccountCreateTransaction() + .setKey(adminKey) + .freezeWith(testEnv.client) + .execute(testEnv.client); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> new AccountCreateTransaction() + .setReceiverSignatureRequired(true) + .setKey(adminKey) + .setAlias(evmAddress) + .freezeWith(testEnv.client) + .execute(testEnv.client) + .getReceipt(testEnv.client)) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + } + } + + @Test + @DisplayName("Can create account with alias different from admin key") + void createAccountWithAlias() throws Exception { + // Tests the fifth row of this table + // https://github.com/hashgraph/hedera-improvement-proposal/blob/d39f740021d7da592524cffeaf1d749803798e9a/HIP/hip-583.md#signatures + try (var testEnv = new IntegrationTestEnv(1)) { + + var adminKey = PrivateKey.generateED25519(); + + // Create the admin account + new AccountCreateTransaction() + .setKey(adminKey) + .freezeWith(testEnv.client) + .execute(testEnv.client); + + var key = PrivateKey.generateECDSA(); + var evmAddress = key.getPublicKey().toEvmAddress(); + + var accountId = new AccountCreateTransaction() + .setKey(adminKey) + .setAlias(evmAddress) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + assertThat(accountId).isNotNull(); + + var info = new AccountInfoQuery().setAccountId(accountId).execute(testEnv.client); + + assertThat(info.accountId).isNotNull(); + assertThat(info.contractAccountId).hasToString(evmAddress.toString()); + assertThat(info.key).isEqualTo(adminKey.getPublicKey()); + } + } + + @Test + @DisplayName("Cannot create account with alias different from admin key without signature") + void cannotCreateAccountWithAliasWithoutSignature() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var adminKey = PrivateKey.generateED25519(); + + // Create the admin account + new AccountCreateTransaction() + .setKey(adminKey) + .freezeWith(testEnv.client) + .execute(testEnv.client); + + var key = PrivateKey.generateECDSA(); + var evmAddress = key.getPublicKey().toEvmAddress(); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> new AccountCreateTransaction() + .setReceiverSignatureRequired(true) + .setKey(adminKey) + .setAlias(evmAddress) + .freezeWith(testEnv.client) + .execute(testEnv.client) + .getReceipt(testEnv.client)) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + } + } + + @Test + @DisplayName("Can create account with alias different from admin key with receiver sig required") + void createAccountWithAliasWithReceiverSigRequired() throws Exception { + // Tests the sixth row of this table + // https://github.com/hashgraph/hedera-improvement-proposal/blob/d39f740021d7da592524cffeaf1d749803798e9a/HIP/hip-583.md#signatures + try (var testEnv = new IntegrationTestEnv(1)) { + + var adminKey = PrivateKey.generateED25519(); + + // Create the admin account + new AccountCreateTransaction() + .setKey(adminKey) + .freezeWith(testEnv.client) + .execute(testEnv.client); + + var key = PrivateKey.generateECDSA(); + var evmAddress = key.getPublicKey().toEvmAddress(); + + var accountId = new AccountCreateTransaction() + .setReceiverSignatureRequired(true) + .setKey(adminKey) + .setAlias(evmAddress) + .freezeWith(testEnv.client) + .sign(key) + .sign(adminKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + assertThat(accountId).isNotNull(); + + var info = new AccountInfoQuery().setAccountId(accountId).execute(testEnv.client); + + assertThat(info.accountId).isNotNull(); + assertThat(info.contractAccountId).hasToString(evmAddress.toString()); + assertThat(info.key).isEqualTo(adminKey.getPublicKey()); + } + } + + @Test + @DisplayName( + "Cannot create account with alias different from admin key and receiver sig required without signature") + void cannotCreateAccountWithAliasWithReceiverSigRequiredWithoutSignature() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var adminKey = PrivateKey.generateED25519(); + + // Create the admin account + new AccountCreateTransaction() + .setKey(adminKey) + .freezeWith(testEnv.client) + .execute(testEnv.client); + + var key = PrivateKey.generateECDSA(); + var evmAddress = key.getPublicKey().toEvmAddress(); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> new AccountCreateTransaction() + .setReceiverSignatureRequired(true) + .setKey(adminKey) + .setAlias(evmAddress) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client)) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/AccountDeleteIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/AccountDeleteIntegrationTest.java new file mode 100644 index 0000000000..abb217c01f --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/AccountDeleteIntegrationTest.java @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.time.Duration; +import java.util.Objects; +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.AccountDeleteTransaction; +import org.hiero.sdk.AccountInfoQuery; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.sdk.Status; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class AccountDeleteIntegrationTest { + @Test + @DisplayName("Can delete account") + void canDeleteAccount() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + var info = new AccountInfoQuery().setAccountId(accountId).execute(testEnv.client); + + assertThat(info.accountId).isEqualTo(accountId); + assertThat(info.isDeleted).isFalse(); + assertThat(info.key.toString()).isEqualTo(key.getPublicKey().toString()); + assertThat(info.balance).isEqualTo(new Hbar(1)); + assertThat(info.autoRenewPeriod).isEqualTo(Duration.ofDays(90)); + assertThat(info.proxyAccountId).isNull(); + assertThat(info.proxyReceived).isEqualTo(Hbar.ZERO); + } + } + + @Test + @DisplayName("Cannot delete invalid account ID") + void cannotCreateAccountWithNoKey() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new AccountDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.ACCOUNT_ID_DOES_NOT_EXIST.toString()); + } + } + + @Test + @DisplayName("Cannot delete account that has not signed transaction") + void cannotDeleteAccountThatHasNotSignedTransaction() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new AccountDeleteTransaction() + .setAccountId(accountId) + .setTransferAccountId(testEnv.operatorId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/AccountIdPopulationIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/AccountIdPopulationIntegrationTest.java new file mode 100644 index 0000000000..4c09c8751e --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/AccountIdPopulationIntegrationTest.java @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hiero.sdk.AccountId; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.TransactionReceiptQuery; +import org.hiero.sdk.TransferTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class AccountIdPopulationIntegrationTest { + @Test + @DisplayName("Can populate AccountId num from mirror node (using sync method)") + void canPopulateAccountIdNumSync() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var privateKey = PrivateKey.generateECDSA(); + var publicKey = privateKey.getPublicKey(); + + var evmAddress = publicKey.toEvmAddress(); + var evmAddressAccount = AccountId.fromEvmAddress(evmAddress); + + var tx = new TransferTransaction() + .addHbarTransfer(evmAddressAccount, new Hbar(1)) + .addHbarTransfer(testEnv.operatorId, new Hbar(-1)) + .execute(testEnv.client); + + var receipt = new TransactionReceiptQuery() + .setTransactionId(tx.transactionId) + .setIncludeChildren(true) + .execute(testEnv.client); + + var newAccountId = receipt.children.get(0).accountId; + + var idMirror = AccountId.fromEvmAddress(evmAddress); + Thread.sleep(5000); + var accountId = idMirror.populateAccountNum(testEnv.client); + + assertThat(newAccountId.num).isEqualTo(accountId.num); + } + } + + @Test + @DisplayName("Can populate AccountId num from mirror node (using async method)") + void canPopulateAccountIdNumAsync() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var privateKey = PrivateKey.generateECDSA(); + var publicKey = privateKey.getPublicKey(); + + var evmAddress = publicKey.toEvmAddress(); + var evmAddressAccount = AccountId.fromEvmAddress(evmAddress); + + var tx = new TransferTransaction() + .addHbarTransfer(evmAddressAccount, new Hbar(1)) + .addHbarTransfer(testEnv.operatorId, new Hbar(-1)) + .execute(testEnv.client); + + var receipt = new TransactionReceiptQuery() + .setTransactionId(tx.transactionId) + .setIncludeChildren(true) + .execute(testEnv.client); + + var newAccountId = receipt.children.get(0).accountId; + + var idMirror = AccountId.fromEvmAddress(evmAddress); + Thread.sleep(5000); + var accountId = idMirror.populateAccountNumAsync(testEnv.client).get(); + + assertThat(newAccountId.num).isEqualTo(accountId.num); + } + } + + @Test + @DisplayName("Can populate AccountId evm address from mirror node (using sync method)") + void canPopulateAccountIdEvmAddressSync() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var privateKey = PrivateKey.generateECDSA(); + var publicKey = privateKey.getPublicKey(); + + var evmAddress = publicKey.toEvmAddress(); + var evmAddressAccount = AccountId.fromEvmAddress(evmAddress); + + var tx = new TransferTransaction() + .addHbarTransfer(evmAddressAccount, new Hbar(1)) + .addHbarTransfer(testEnv.operatorId, new Hbar(-1)) + .execute(testEnv.client); + + var receipt = new TransactionReceiptQuery() + .setTransactionId(tx.transactionId) + .setIncludeChildren(true) + .execute(testEnv.client); + + var newAccountId = receipt.children.get(0).accountId; + + Thread.sleep(5000); + var accountId = newAccountId.populateAccountEvmAddress(testEnv.client); + + assertThat(evmAddressAccount.evmAddress).isEqualTo(accountId.evmAddress); + } + } + + @Test + @DisplayName("Can populate AccountId evm address from mirror node (using async method)") + void canPopulateAccountIdEvmAddressAsync() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var privateKey = PrivateKey.generateECDSA(); + var publicKey = privateKey.getPublicKey(); + + var evmAddress = publicKey.toEvmAddress(); + var evmAddressAccount = AccountId.fromEvmAddress(evmAddress); + + var tx = new TransferTransaction() + .addHbarTransfer(evmAddressAccount, new Hbar(1)) + .addHbarTransfer(testEnv.operatorId, new Hbar(-1)) + .execute(testEnv.client); + + var receipt = new TransactionReceiptQuery() + .setTransactionId(tx.transactionId) + .setIncludeChildren(true) + .execute(testEnv.client); + + var newAccountId = receipt.children.get(0).accountId; + + Thread.sleep(5000); + var accountId = + newAccountId.populateAccountEvmAddressAsync(testEnv.client).get(); + + assertThat(evmAddressAccount.evmAddress).isEqualTo(accountId.evmAddress); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/AccountInfoIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/AccountInfoIntegrationTest.java new file mode 100644 index 0000000000..9c1cb73d3d --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/AccountInfoIntegrationTest.java @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.AccountInfoFlow; +import org.hiero.sdk.AccountInfoQuery; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.Transaction; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class AccountInfoIntegrationTest { + @Test + @DisplayName("Can query account info for client operator") + void canQueryAccountInfoForClientOperator() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var info = new AccountInfoQuery().setAccountId(testEnv.operatorId).execute(testEnv.client); + + assertThat(info.accountId).isEqualTo(testEnv.operatorId); + assertThat(info.isDeleted).isFalse(); + assertThat(info.key).isEqualTo(testEnv.operatorKey); + assertThat(info.balance.toTinybars()).isGreaterThan(0); + assertThat(info.proxyAccountId).isNull(); + assertThat(info.proxyReceived).isEqualTo(Hbar.ZERO); + } + } + + @Test + @DisplayName("Can get cost for account info query") + void getCostAccountInfoForClientOperator() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var info = new AccountInfoQuery().setAccountId(testEnv.operatorId).setMaxQueryPayment(new Hbar(1)); + + var cost = info.getCost(testEnv.client); + + var accInfo = info.setQueryPayment(cost).execute(testEnv.client); + + assertThat(accInfo.accountId).isEqualTo(testEnv.operatorId); + } + } + + @Test + @DisplayName("Can get cost for account info query, with a bix max") + void getCostBigMaxAccountInfoForClientOperator() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var info = new AccountInfoQuery().setAccountId(testEnv.operatorId).setMaxQueryPayment(Hbar.MAX); + + var cost = info.getCost(testEnv.client); + + var accInfo = info.setQueryPayment(cost).execute(testEnv.client); + + assertThat(accInfo.accountId).isEqualTo(testEnv.operatorId); + } + } + + @Test + @Disabled + @DisplayName("Can get cost for account info query, with a small max") + void getCostSmallMaxAccountInfoForClientOperator() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var info = new AccountInfoQuery().setAccountId(testEnv.operatorId).setMaxQueryPayment(Hbar.fromTinybars(1)); + + var cost = info.getCost(testEnv.client); + + assertThatExceptionOfType(RuntimeException.class) + .isThrownBy(() -> { + info.execute(testEnv.client); + }) + .withMessage("org.hiero.sdk.MaxQueryPaymentExceededException: cost for AccountInfoQuery, of " + + cost.toString() + + ", without explicit payment is greater than the maximum allowed payment of 1 tℏ"); + } + } + + @Test + @DisplayName("Insufficient tx fee error.") + void getCostInsufficientTxFeeAccountInfoForClientOperator() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var info = new AccountInfoQuery() + .setAccountId(testEnv.operatorId) + .setMaxQueryPayment(Hbar.fromTinybars(10000)); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + info.setQueryPayment(Hbar.fromTinybars(1)).execute(testEnv.client); + }) + .satisfies(error -> assertThat(error.status.toString()).isEqualTo("INSUFFICIENT_TX_FEE")); + } + } + + @Test + @DisplayName("AccountInfoFlow.verify functions") + void accountInfoFlowVerifyFunctions() throws Throwable { + try (var testEnv = new IntegrationTestEnv(1)) { + + var newKey = PrivateKey.generateED25519(); + var newPublicKey = newKey.getPublicKey(); + + Transaction signedTx = new AccountCreateTransaction() + .setKey(newPublicKey) + .setInitialBalance(Hbar.fromTinybars(1000)) + .freezeWith(testEnv.client) + .signWithOperator(testEnv.client); + + Transaction unsignedTx = new AccountCreateTransaction() + .setKey(newPublicKey) + .setInitialBalance(Hbar.fromTinybars(1000)) + .freezeWith(testEnv.client); + + assertThat(AccountInfoFlow.verifyTransactionSignature(testEnv.client, testEnv.operatorId, signedTx)) + .isTrue(); + assertThat(AccountInfoFlow.verifyTransactionSignature(testEnv.client, testEnv.operatorId, unsignedTx)) + .isFalse(); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/AccountRecordsIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/AccountRecordsIntegrationTest.java new file mode 100644 index 0000000000..47404ace3d --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/AccountRecordsIntegrationTest.java @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Objects; +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.AccountRecordsQuery; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.TransferTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class AccountRecordsIntegrationTest { + @Test + @DisplayName("Can query account records") + void canQueryAccountRecords() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + new TransferTransaction() + .addHbarTransfer(testEnv.operatorId, new Hbar(1).negated()) + .addHbarTransfer(accountId, new Hbar(1)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TransferTransaction() + .addHbarTransfer(testEnv.operatorId, new Hbar(1)) + .addHbarTransfer(accountId, new Hbar(1).negated()) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var records = + new AccountRecordsQuery().setAccountId(testEnv.operatorId).execute(testEnv.client); + + assertThat(records.isEmpty()).isFalse(); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/AccountStakersIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/AccountStakersIntegrationTest.java new file mode 100644 index 0000000000..3b66a2f0d0 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/AccountStakersIntegrationTest.java @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import org.hiero.sdk.AccountStakersQuery; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.Status; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class AccountStakersIntegrationTest { + @Test + @DisplayName("Cannot query account stakers since it is not supported") + void cannotQueryAccountStakersSinceItIsNotSupported() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new AccountStakersQuery() + .setAccountId(testEnv.operatorId) + .setMaxQueryPayment(new Hbar(1)) + .execute(testEnv.client); + }) + .withMessageContaining(Status.NOT_SUPPORTED.toString()); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/AccountUpdateIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/AccountUpdateIntegrationTest.java new file mode 100644 index 0000000000..86ab81b2c3 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/AccountUpdateIntegrationTest.java @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.time.Duration; +import java.util.Objects; +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.AccountInfoQuery; +import org.hiero.sdk.AccountUpdateTransaction; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.Status; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class AccountUpdateIntegrationTest { + @Test + @DisplayName("Can update account with a new key") + void canUpdateAccountWithNewKey() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var key1 = PrivateKey.generateED25519(); + var key2 = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction().setKey(key1).execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + var info = new AccountInfoQuery().setAccountId(accountId).execute(testEnv.client); + + assertThat(info.accountId).isEqualTo(accountId); + assertThat(info.isDeleted).isFalse(); + assertThat(info.key.toString()).isEqualTo(key1.getPublicKey().toString()); + assertThat(info.balance).isEqualTo(new Hbar(0)); + assertThat(info.autoRenewPeriod).isEqualTo(Duration.ofDays(90)); + assertThat(info.proxyAccountId).isNull(); + assertThat(info.proxyReceived).isEqualTo(Hbar.ZERO); + + new AccountUpdateTransaction() + .setAccountId(accountId) + .setKey(key2.getPublicKey()) + .freezeWith(testEnv.client) + .sign(key1) + .sign(key2) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + info = new AccountInfoQuery().setAccountId(accountId).execute(testEnv.client); + + assertThat(info.accountId).isEqualTo(accountId); + assertThat(info.isDeleted).isFalse(); + assertThat(info.key.toString()).isEqualTo(key2.getPublicKey().toString()); + assertThat(info.balance).isEqualTo(new Hbar(0)); + assertThat(info.autoRenewPeriod).isEqualTo(Duration.ofDays(90)); + assertThat(info.proxyAccountId).isNull(); + assertThat(info.proxyReceived).isEqualTo(Hbar.ZERO); + } + } + + @Test + @DisplayName("Cannot update account when account ID is not set") + void cannotUpdateAccountWhenAccountIdIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new AccountUpdateTransaction().execute(testEnv.client).getReceipt(testEnv.client); + }) + .withMessageContaining(Status.ACCOUNT_ID_DOES_NOT_EXIST.toString()); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ClientIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ClientIntegrationTest.java new file mode 100644 index 0000000000..288ba3e981 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ClientIntegrationTest.java @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.hiero.sdk.*; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class ClientIntegrationTest { + + @Test + @DisplayName("fails when all the manually set nodes are not matching the address book") + void failsWhenNoNodesAreMatching() throws Exception { + var client = Client.forTestnet().setTransportSecurity(true); + + var nodes = new ArrayList(); + nodes.add(new AccountId(1000)); + nodes.add(new AccountId(1001)); + assertThatExceptionOfType(IllegalStateException.class) + .isThrownBy(() -> new AccountBalanceQuery() + .setNodeAccountIds(nodes) + .setAccountId(new AccountId(7)) + .execute(client)) + .withMessageContaining("All node account IDs did not map to valid nodes in the client's network"); + client.close(); + } + + @Test + @DisplayName("can skip invalid nodes") + void canSkipNodes() throws Exception { + var client = Client.forTestnet().setTransportSecurity(true); + + var nodes = new ArrayList<>(client.getNetwork().values().stream().toList()); + nodes.add(new AccountId(1000)); + new AccountBalanceQuery() + .setNodeAccountIds(nodes) + .setAccountId(new AccountId(7)) + .execute(client); + + client.close(); + } + + @Test + @DisplayName("setNetwork() functions correctly") + void testReplaceNodes() throws Exception { + Map network = new HashMap<>(); + network.put("0.testnet.hedera.com:50211", new AccountId(3)); + network.put("1.testnet.hedera.com:50211", new AccountId(4)); + + try (var testEnv = new IntegrationTestEnv(1)) { + + testEnv.client + .setMaxQueryPayment(new Hbar(2)) + .setRequestTimeout(Duration.ofMinutes(2)) + .setNetwork(network); + + assertThat(testEnv.operatorId).isNotNull(); + + // Execute two simple queries so we create a channel for each network node. + new AccountBalanceQuery().setAccountId(new AccountId(3)).execute(testEnv.client); + + new AccountBalanceQuery().setAccountId(new AccountId(3)).execute(testEnv.client); + + network = new HashMap<>(); + network.put("1.testnet.hedera.com:50211", new AccountId(4)); + network.put("2.testnet.hedera.com:50211", new AccountId(5)); + + testEnv.client.setNetwork(network); + + network = new HashMap<>(); + network.put("35.186.191.247:50211", new AccountId(4)); + network.put("35.192.2.25:50211", new AccountId(5)); + + testEnv.client.setNetwork(network); + } + } + + @Test + void transactionIdNetworkIsVerified() { + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> { + var client = Client.forPreviewnet(); + client.setAutoValidateChecksums(true); + + new AccountCreateTransaction() + .setTransactionId(TransactionId.generate(AccountId.fromString("0.0.123-esxsf"))) + .execute(client); + client.close(); + }); + } + + @Test + @DisplayName("`setMaxNodesPerTransaction()`") + void testMaxNodesPerTransaction() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + testEnv.client.setMaxNodesPerTransaction(1); + + var transaction = new AccountDeleteTransaction() + .setAccountId(testEnv.operatorId) + .freezeWith(testEnv.client); + + assertThat(transaction.getNodeAccountIds()).isNotNull(); + assertThat(transaction.getNodeAccountIds().size()).isEqualTo(1); + } + } + + @Test + void ping() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + var network = testEnv.client.getNetwork(); + var nodes = new ArrayList<>(network.values()); + + assertThat(nodes.isEmpty()).isFalse(); + + var node = nodes.get(0); + + testEnv.client.setMaxNodeAttempts(1); + testEnv.client.ping(node); + } + } + + @Test + void pingAll() throws Exception { + try (var testEnv = new IntegrationTestEnv()) { + + testEnv.client.setMaxNodeAttempts(1); + testEnv.client.pingAll(); + + var network = testEnv.client.getNetwork(); + var nodes = new ArrayList<>(network.values()); + + assertThat(nodes.isEmpty()).isFalse(); + + var node = nodes.get(0); + + new AccountBalanceQuery().setAccountId(node).execute(testEnv.client); + } + } + + @Test + void pingAllBadNetwork() throws Exception { + try (var testEnv = new IntegrationTestEnv(3)) { + + // Skip if using local node. + // Note: this check should be removed once the local node is supporting multiple nodes. + testEnv.assumeNotLocalNode(); + + testEnv.client.setMaxNodeAttempts(1); + testEnv.client.setMaxAttempts(1); + testEnv.client.setMaxNodesPerTransaction(2); + + var network = testEnv.client.getNetwork(); + + var entries = new ArrayList<>(network.entrySet()); + assertThat(entries.size()).isGreaterThan(1); + + network.clear(); + network.put("in-process:name", entries.get(0).getValue()); + network.put(entries.get(1).getKey(), entries.get(1).getValue()); + + testEnv.client.setNetwork(network); + + assertThatExceptionOfType(MaxAttemptsExceededException.class) + .isThrownBy(() -> { + testEnv.client.pingAll(); + }) + .withMessageContaining("exceeded maximum attempts"); + + var nodes = new ArrayList<>(testEnv.client.getNetwork().values()); + assertThat(nodes.isEmpty()).isFalse(); + + var node = nodes.get(0); + + new AccountBalanceQuery().setAccountId(node).execute(testEnv.client); + + assertThat(testEnv.client.getNetwork().values().size()).isEqualTo(1); + } + } + + @Test + void pingAsync() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + var network = testEnv.client.getNetwork(); + var nodes = new ArrayList<>(network.values()); + + assertThat(nodes.isEmpty()).isFalse(); + + var node = nodes.get(0); + + testEnv.client.setMaxNodeAttempts(1); + testEnv.client.pingAsync(node).get(); + } + } + + @Test + void pingAllAsync() throws Exception { + try (var testEnv = new IntegrationTestEnv()) { + + testEnv.client.setMaxNodeAttempts(1); + testEnv.client.pingAllAsync().get(); + + var network = testEnv.client.getNetwork(); + var nodes = new ArrayList<>(network.values()); + + assertThat(nodes.isEmpty()).isFalse(); + + var node = nodes.get(0); + + new AccountBalanceQuery().setAccountId(node).execute(testEnv.client); + } + } + + @Test + @DisplayName("`forMirrorNetwork()`") + void testClientInitWithMirrorNetwork() throws Exception { + var mirrorNetworkString = "testnet.mirrornode.hedera.com:443"; + var client = Client.forMirrorNetwork(List.of(mirrorNetworkString)); + var mirrorNetwork = client.getMirrorNetwork(); + + assertThat(mirrorNetwork).hasSize(1); + assertThat(mirrorNetwork.get(0)).isEqualTo(mirrorNetworkString); + assertThat(client.getNetwork()).isNotNull(); + assertThat(client.getNetwork()).isNotEmpty(); + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/Contents.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/Contents.java new file mode 100644 index 0000000000..a23d575e3f --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/Contents.java @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +public class Contents { + private Contents() {} + + public static final String BIG_CONTENTS = + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur aliquam augue sem, ut mattis dui laoreet a. Curabitur consequat est euismod, scelerisque metus et, tristique dui. Nulla commodo mauris ut faucibus ultricies. Quisque venenatis nisl nec augue tempus, at efficitur elit eleifend. Duis pharetra felis metus, sed dapibus urna vehicula id. Duis non venenatis turpis, sit amet ornare orci. Donec non interdum quam. Sed finibus nunc et risus finibus, non sagittis lorem cursus. Proin pellentesque tempor aliquam. Sed congue nisl in enim bibendum, condimentum vehicula nisi feugiat.\n" + + "\n" + + "Suspendisse non sodales arcu. Suspendisse sodales, lorem ac mollis blandit, ipsum neque porttitor nulla, et sodales arcu ante fermentum tellus. Integer sagittis dolor sed augue fringilla accumsan. Cras vitae finibus arcu, sit amet varius dolor. Etiam id finibus dolor, vitae luctus velit. Proin efficitur augue nec pharetra accumsan. Aliquam lobortis nisl diam, vel fermentum purus finibus id. Etiam at finibus orci, et tincidunt turpis. Aliquam imperdiet congue lacus vel facilisis. Phasellus id magna vitae enim dapibus vestibulum vitae quis augue. Morbi eu consequat enim. Maecenas neque nulla, pulvinar sit amet consequat sed, tempor sed magna. Mauris lacinia sem feugiat faucibus aliquet. Etiam congue non turpis at commodo. Nulla facilisi.\n" + + "\n" + + "Nunc velit turpis, cursus ornare fringilla eu, lacinia posuere leo. Mauris rutrum ultricies dui et suscipit. Curabitur in euismod ligula. Curabitur vitae faucibus orci. Phasellus volutpat vestibulum diam sit amet vestibulum. In vel purus leo. Nulla condimentum lectus vestibulum lectus faucibus, id lobortis eros consequat. Proin mollis libero elit, vel aliquet nisi imperdiet et. Morbi ornare est velit, in vehicula nunc malesuada quis. Donec vehicula convallis interdum.\n" + + "\n" + + "Integer pellentesque in nibh vitae aliquet. Ut at justo id libero dignissim hendrerit. Interdum et malesuada fames ac ante ipsum primis in faucibus. Praesent quis ornare lectus. Nam malesuada non diam quis cursus. Phasellus a libero ligula. Suspendisse ligula elit, congue et nisi sit amet, cursus euismod dolor. Morbi aliquam, nulla a posuere pellentesque, diam massa ornare risus, nec eleifend neque eros et elit.\n" + + "\n" + + "Pellentesque a sodales dui. Sed in efficitur ante. Duis eget volutpat elit, et ornare est. Nam felis dolor, placerat mattis diam id, maximus lobortis quam. Sed pellentesque lobortis sem sed placerat. Pellentesque augue odio, molestie sed lectus sit amet, congue volutpat massa. Quisque congue consequat nunc id fringilla. Duis semper nulla eget enim venenatis dapibus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Pellentesque varius turpis nibh, sit amet malesuada mauris malesuada quis. Vivamus dictum egestas placerat. Vivamus id augue elit.\n" + + "\n" + + "Cras fermentum volutpat eros, vitae euismod lorem viverra nec. Donec lectus augue, porta eleifend odio sit amet, condimentum rhoncus urna. Nunc sed odio velit. Morbi non cursus odio, eget imperdiet erat. Nunc rhoncus massa non neque volutpat, sit amet faucibus ante congue. Phasellus nec lorem vel leo accumsan lobortis. Maecenas id ligula bibendum purus suscipit posuere ac eget diam. Nam in quam pretium, semper erat auctor, iaculis odio. Maecenas placerat, nisi ac elementum tempor, felis nulla porttitor orci, ac rhoncus diam justo in elit. Etiam lobortis fermentum ligula in tincidunt. Donec quis vestibulum nunc. Sed eros diam, interdum in porta lobortis, gravida eu magna. Donec diam purus, finibus et sollicitudin quis, fringilla nec nisi. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Curabitur ultricies sagittis dapibus. Etiam ullamcorper aliquet libero, eu venenatis mauris suscipit id.\n" + + "\n" + + "Ut interdum eleifend sem, vel bibendum ipsum volutpat eget. Nunc ac dignissim augue. Aliquam ornare aliquet magna id dignissim. Vestibulum velit sem, lacinia eu rutrum in, rhoncus vitae mauris. Pellentesque scelerisque pulvinar mauris non cursus. Integer id dolor porta, bibendum sem vel, pretium tortor. Fusce a nisi convallis, interdum quam condimentum, suscipit dolor. Sed magna diam, efficitur non nunc in, tincidunt varius mi. Aliquam ullamcorper nulla eu fermentum bibendum. Vivamus a felis pretium, hendrerit enim vitae, hendrerit leo. Suspendisse lacinia lectus a diam consectetur suscipit. Aenean hendrerit nisl sed diam venenatis pellentesque. Nullam egestas lectus a consequat pharetra. Vivamus ornare tellus auctor, facilisis lacus id, feugiat dui. Nam id est ut est rhoncus varius.\n" + + "\n" + + "Aenean vel vehicula erat. Aenean gravida risus vitae purus sodales, quis dictum enim porta. Ut elit elit, fermentum sed posuere non, accumsan eu justo. Integer porta malesuada quam, et elementum massa suscipit nec. Donec in elit diam. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis suscipit vel ante volutpat vestibulum.\n" + + "\n" + + "Pellentesque ex arcu, euismod et sapien ut, vulputate suscipit enim. Donec mattis sagittis augue, et mattis lacus. Cras placerat consequat lorem sed luctus. Nam suscipit aliquam sem ac imperdiet. Mauris a semper augue, pulvinar fringilla magna. Integer pretium massa non risus commodo hendrerit. Sed dictum libero id erat sodales mattis. Etiam auctor dolor lectus, ut sagittis enim lobortis vitae. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur nec orci lobortis, cursus risus eget, sollicitudin massa. Integer vel tincidunt mi, id eleifend quam. Nullam facilisis nisl eu mauris congue, vitae euismod nisi malesuada. Vivamus sit amet urna et libero sagittis dictum.\n" + + "\n" + + "In hac habitasse platea dictumst. Aliquam erat volutpat. Ut dictum, mi a viverra venenatis, mi urna pulvinar nisi, nec gravida lectus diam eget urna. Ut dictum sit amet nisl ut dignissim. Sed sed mauris scelerisque, efficitur augue in, vulputate turpis. Proin dolor justo, bibendum et sollicitudin feugiat, imperdiet sed mi. Sed elementum vitae massa vel lobortis. Cras vitae massa sit amet libero dictum tempus.\n" + + "\n" + + "Vivamus ut mauris lectus. Curabitur placerat ornare scelerisque. Cras malesuada nunc quis tortor pretium bibendum vel sed dui. Cras lobortis nibh eu erat blandit, sit amet consequat neque fermentum. Phasellus imperdiet molestie tristique. Cras auctor purus erat, id mollis ligula porttitor eget. Mauris porta pharetra odio et finibus. Suspendisse eu est a ligula bibendum cursus. Mauris ac laoreet libero. Nullam volutpat sem quis rhoncus gravida.\n" + + "\n" + + "Donec malesuada lacus ac iaculis cursus. Sed sem orci, feugiat ac est ut, ultricies posuere nisi. Suspendisse potenti. Phasellus ut ultricies purus. Etiam sem tortor, fermentum quis aliquam eget, consequat ut nulla. Aliquam dictum metus in mi fringilla, vel gravida nulla accumsan. Cras aliquam eget leo vel posuere. Vivamus vitae malesuada nunc. Morbi placerat magna mi, id suscipit lacus auctor quis. Nam at lorem vel odio finibus fringilla ut ac velit. Donec laoreet at nibh quis vehicula.\n" + + "\n" + + "Fusce ac tristique nisi. Donec leo nisi, consectetur at tellus sit amet, consectetur ultrices dui. Quisque gravida mollis tempor. Maecenas semper, sapien ut dignissim feugiat, massa enim viverra dolor, non varius eros nulla nec felis. Nunc massa lacus, ornare et feugiat id, bibendum quis purus. Praesent viverra elit sit amet purus consectetur venenatis. Maecenas nibh risus, elementum sit amet enim sagittis, ultrices malesuada lectus. Vivamus non felis ante. Ut vulputate ex arcu. Aliquam porta gravida porta. Aliquam eros leo, malesuada quis eros non, maximus tristique neque. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam ligula orci, mollis vel luctus nec, venenatis vitae est. Fusce rutrum convallis nisi.\n" + + "\n" + + "Nunc laoreet eget nibh in feugiat. Pellentesque nec arcu cursus, gravida dolor a, pellentesque nisi. Praesent vel justo blandit, placerat risus eget, consectetur orci. Sed maximus metus mi, ut euismod augue ultricies in. Nunc id risus hendrerit, aliquet lorem nec, congue justo. Vestibulum vel nunc ac est euismod mattis ac vitae nulla. Donec blandit luctus mauris, sit amet bibendum dui egestas et. Aenean nec lorem nec elit ornare rutrum nec eget ligula. Fusce a ipsum vitae neque elementum pharetra. Pellentesque ullamcorper ullamcorper libero, vitae porta sem sagittis vel. Interdum et malesuada fames ac ante ipsum primis in faucibus.\n" + + "\n" + + "Duis at massa sit amet risus pellentesque varius sit amet vitae eros. Cras tempor aliquet sapien, vehicula varius neque volutpat et. Donec purus nibh, pellentesque ut lobortis nec, ultricies ultricies nisl. Sed accumsan ut dui sit amet vulputate. Suspendisse eu facilisis massa, a hendrerit mauris. Nulla elementum molestie tincidunt. Donec mi justo, ornare vel tempor id, gravida et orci. Nam molestie erat nec nisi bibendum accumsan. Duis vitae tempor ante. Morbi congue mauris vel sagittis facilisis. Vivamus vehicula odio orci, a tempor nibh euismod in. Proin mattis, nibh at feugiat porta, purus velit posuere felis, quis volutpat sapien dui vel odio. Nam fermentum sem nec euismod aliquet. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Aliquam erat volutpat.\n" + + "\n" + + "Mauris congue lacus tortor. Pellentesque arcu eros, accumsan imperdiet porttitor vitae, mattis nec justo. Nullam ac aliquam mauris. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Suspendisse potenti. Fusce accumsan tempus felis a sagittis. Maecenas et velit odio. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Aliquam eros lacus, volutpat non urna sed, tincidunt ullamcorper elit. Sed sit amet gravida libero. In varius mi vel diam sollicitudin mollis.\n" + + "\n" + + "Aenean varius, diam vitae hendrerit feugiat, libero augue ultrices odio, eget consequat sem tellus eu nisi. Nam dapibus enim et auctor sollicitudin. Nunc iaculis eros orci, ac accumsan eros malesuada ut. Ut semper augue felis, nec sodales lorem consectetur non. Cras gravida eleifend est, et sagittis eros imperdiet congue. Fusce id tellus dapibus nunc scelerisque tempus. Donec tempor placerat libero, in commodo nisi bibendum eu. Donec id eros non est sollicitudin luctus. Duis bibendum bibendum tellus nec viverra. Aliquam non faucibus ex, nec luctus dui. Curabitur efficitur varius urna non dignissim. Suspendisse elit elit, ultrices in elementum eu, tempor at magna.\n" + + "\n" + + "Nunc in purus sit amet mi laoreet pulvinar placerat eget sapien. Donec vel felis at dui ultricies euismod quis vel neque. Donec tincidunt urna non eros pretium blandit. Nullam congue tincidunt condimentum. Curabitur et libero nibh. Proin ultricies risus id imperdiet scelerisque. Suspendisse purus mi, viverra vitae risus ut, tempus tincidunt enim. Ut luctus lobortis nisl, eget venenatis tortor cursus non. Mauris finibus nisl quis gravida ultricies. Fusce elementum lacus sit amet nunc congue, in porta nulla tincidunt.\n" + + "\n" + + "Mauris ante risus, imperdiet blandit posuere in, blandit eu ipsum. Integer et auctor arcu. Integer quis elementum purus. Nunc in ultricies nisl, sed rutrum risus. Suspendisse venenatis eros nec lorem rhoncus, in scelerisque velit condimentum. Etiam condimentum quam felis, in elementum odio mattis et. In ut nibh porttitor, hendrerit tellus vel, luctus magna. Vestibulum et ligula et dolor pellentesque porta. Aenean efficitur porta massa et bibendum. Nulla vehicula sem in risus volutpat, a eleifend elit maximus.\n" + + "\n" + + "Proin orci lorem, auctor a felis eu, pretium lobortis nulla. Phasellus aliquam efficitur interdum. Sed sit amet velit rutrum est dictum facilisis. Duis cursus enim at nisl tincidunt, eu molestie elit vehicula. Cras pellentesque nisl id enim feugiat fringilla. In quis tincidunt neque. Nam eu consectetur dolor. Ut id interdum mauris. Mauris nunc tortor, placerat in tempor ut, vestibulum eu nisl. Integer vel dapibus ipsum. Nunc id erat pulvinar, tincidunt magna id, condimentum massa. Pellentesque consequat est eget odio placerat vehicula. Etiam augue neque, sagittis non leo eu, tristique scelerisque dui. Ut dui urna, blandit quis urna ac, tincidunt tristique turpis.\n" + + "\n" + + "Suspendisse venenatis rhoncus ligula ultrices condimentum. In id laoreet eros. Suspendisse suscipit fringilla leo id euismod. Sed in quam libero. Ut at ligula tellus. Sed tristique gravida dui, at egestas odio aliquam iaculis. Praesent imperdiet velit quis nibh consequat, quis pretium sem sagittis. Donec tortor ex, congue sit amet pulvinar ac, rutrum non est. Praesent ipsum magna, venenatis sit amet vulputate id, eleifend ac ipsum.\n" + + "\n" + + "In consequat, nisi iaculis laoreet elementum, massa mauris varius nisi, et porta nisi velit at urna. Maecenas sit amet aliquet eros, a rhoncus nisl. Maecenas convallis enim nunc. Morbi purus nisl, aliquam ac tincidunt sed, mattis in augue. Quisque et elementum quam, vitae maximus orci. Suspendisse hendrerit risus nec vehicula placerat. Nulla et lectus nunc. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.\n" + + "\n" + + "Etiam ut sodales ex. Nulla luctus, magna eu scelerisque sagittis, nibh quam consectetur neque, non rutrum dolor metus nec ex. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed egestas augue elit, sollicitudin accumsan massa lobortis ac. Curabitur placerat, dolor a aliquam maximus, velit ipsum laoreet ligula, id ullamcorper lacus nibh eget nisl. Donec eget lacus venenatis enim consequat auctor vel in.\n"; +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractBytecodeIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractBytecodeIntegrationTest.java new file mode 100644 index 0000000000..74e25aa24c --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractBytecodeIntegrationTest.java @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Objects; +import org.hiero.sdk.ContractByteCodeQuery; +import org.hiero.sdk.ContractCreateTransaction; +import org.hiero.sdk.ContractDeleteTransaction; +import org.hiero.sdk.ContractFunctionParameters; +import org.hiero.sdk.FileCreateTransaction; +import org.hiero.sdk.FileDeleteTransaction; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.MaxQueryPaymentExceededException; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.Status; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class ContractBytecodeIntegrationTest { + private static final String SMART_CONTRACT_BYTECODE = + "608060405234801561001057600080fd5b506040516104d73803806104d78339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b506040525050600080546001600160a01b0319163317905550805161010890600190602084019061010f565b50506101aa565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061015057805160ff191683800117855561017d565b8280016001018555821561017d579182015b8281111561017d578251825591602001919060010190610162565b5061018992915061018d565b5090565b6101a791905b808211156101895760008155600101610193565b90565b61031e806101b96000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063368b87721461004657806341c0e1b5146100ee578063ce6d41de146100f6575b600080fd5b6100ec6004803603602081101561005c57600080fd5b81019060208101813564010000000081111561007757600080fd5b82018360208201111561008957600080fd5b803590602001918460018302840111640100000000831117156100ab57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610173945050505050565b005b6100ec6101a2565b6100fe6101ba565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610138578181015183820152602001610120565b50505050905090810190601f1680156101655780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000546001600160a01b0316331461018a5761019f565b805161019d906001906020840190610250565b505b50565b6000546001600160a01b03163314156101b85733ff5b565b60018054604080516020601f600260001961010087891615020190951694909404938401819004810282018101909252828152606093909290918301828280156102455780601f1061021a57610100808354040283529160200191610245565b820191906000526020600020905b81548152906001019060200180831161022857829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061029157805160ff19168380011785556102be565b828001600101855582156102be579182015b828111156102be5782518255916020019190600101906102a3565b506102ca9291506102ce565b5090565b61024d91905b808211156102ca57600081556001016102d456fea264697066735822122084964d4c3f6bc912a9d20e14e449721012d625aa3c8a12de41ae5519752fc89064736f6c63430006000033"; + + @Test + @DisplayName("Can query contract bytecode") + void canQueryContractBytecode() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + response = new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(200000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(testEnv.client); + + var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); + + var bytecode = new ContractByteCodeQuery().setContractId(contractId).execute(testEnv.client); + + assertThat(bytecode.size()).isEqualTo(798); + + new ContractDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setContractId(contractId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can get cost, even with a big max") + void getCostBigMaxQueryContractBytecode() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + response = new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(200000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(testEnv.client); + + var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); + + var bytecodeQuery = + new ContractByteCodeQuery().setContractId(contractId).setMaxQueryPayment(new Hbar(1000)); + + var cost = bytecodeQuery.getCost(testEnv.client); + + var bytecode = bytecodeQuery.setQueryPayment(cost).execute(testEnv.client); + + assertThat(bytecode.size()).isEqualTo(798); + + new ContractDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setContractId(contractId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Error, max is smaller than set payment.") + void getCostSmallMaxQueryContractBytecode() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + response = new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(200000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(testEnv.client); + + var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); + + var bytecodeQuery = + new ContractByteCodeQuery().setContractId(contractId).setMaxQueryPayment(Hbar.fromTinybars(1)); + + assertThatExceptionOfType(MaxQueryPaymentExceededException.class).isThrownBy(() -> { + bytecodeQuery.execute(testEnv.client); + }); + + new ContractDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setContractId(contractId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Insufficient tx fee error.") + void getCostInsufficientTxFeeQueryContractBytecode() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + response = new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(200000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(testEnv.client); + + var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); + + var bytecodeQuery = + new ContractByteCodeQuery().setContractId(contractId).setMaxQueryPayment(new Hbar(100)); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + bytecodeQuery.setQueryPayment(Hbar.fromTinybars(1)).execute(testEnv.client); + }) + .satisfies(error -> assertThat(error.status.toString()).isEqualTo("INSUFFICIENT_TX_FEE")); + + new ContractDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setContractId(contractId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot query contract bytecode when contract ID is not set") + void cannotQueryContractBytecodeWhenContractIDIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new ContractByteCodeQuery().execute(testEnv.client); + }) + .withMessageContaining(Status.INVALID_CONTRACT_ID.toString()); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractCallIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractCallIntegrationTest.java new file mode 100644 index 0000000000..f6e131ee9e --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractCallIntegrationTest.java @@ -0,0 +1,371 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Objects; +import org.hiero.sdk.ContractCallQuery; +import org.hiero.sdk.ContractCreateTransaction; +import org.hiero.sdk.ContractDeleteTransaction; +import org.hiero.sdk.ContractFunctionParameters; +import org.hiero.sdk.FileCreateTransaction; +import org.hiero.sdk.FileDeleteTransaction; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.MaxQueryPaymentExceededException; +import org.hiero.sdk.MirrorNodeContractEstimateGasQuery; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.Status; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class ContractCallIntegrationTest { + private static final String SMART_CONTRACT_BYTECODE = + "608060405234801561001057600080fd5b506040516104d73803806104d78339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b506040525050600080546001600160a01b0319163317905550805161010890600190602084019061010f565b50506101aa565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061015057805160ff191683800117855561017d565b8280016001018555821561017d579182015b8281111561017d578251825591602001919060010190610162565b5061018992915061018d565b5090565b6101a791905b808211156101895760008155600101610193565b90565b61031e806101b96000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063368b87721461004657806341c0e1b5146100ee578063ce6d41de146100f6575b600080fd5b6100ec6004803603602081101561005c57600080fd5b81019060208101813564010000000081111561007757600080fd5b82018360208201111561008957600080fd5b803590602001918460018302840111640100000000831117156100ab57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610173945050505050565b005b6100ec6101a2565b6100fe6101ba565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610138578181015183820152602001610120565b50505050905090810190601f1680156101655780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000546001600160a01b0316331461018a5761019f565b805161019d906001906020840190610250565b505b50565b6000546001600160a01b03163314156101b85733ff5b565b60018054604080516020601f600260001961010087891615020190951694909404938401819004810282018101909252828152606093909290918301828280156102455780601f1061021a57610100808354040283529160200191610245565b820191906000526020600020905b81548152906001019060200180831161022857829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061029157805160ff19168380011785556102be565b828001600101855582156102be579182015b828111156102be5782518255916020019190600101906102a3565b506102ca9291506102ce565b5090565b61024d91905b808211156102ca57600081556001016102d456fea264697066735822122084964d4c3f6bc912a9d20e14e449721012d625aa3c8a12de41ae5519752fc89064736f6c63430006000033"; + + @Test + @DisplayName("Can call contract function") + void canCallContractFunction() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + response = new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(200000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(testEnv.client); + + var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); + + // Wait for mirror node to import data + Thread.sleep(2000); + + var gas = new MirrorNodeContractEstimateGasQuery() + .setContractId(contractId) + .setFunction("getMessage") + .execute(testEnv.client); + + var callQuery = new ContractCallQuery() + .setContractId(contractId) + .setGas(gas) + .setFunction("getMessage") + .setQueryPayment(new Hbar(1)); + + var result = callQuery.execute(testEnv.client); + + assertThat(result.getString(0)).isEqualTo("Hello from Hedera."); + + new ContractDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setContractId(contractId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot call contract function when contract function is not set") + void cannotCallContractFunctionWhenContractFunctionIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + var contractId = Objects.requireNonNull(new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(200000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(testEnv.client) + .getReceipt(testEnv.client) + .contractId); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new ContractCallQuery() + .setContractId(contractId) + .setGas(100000) + .execute(testEnv.client); + }) + .withMessageContaining(Status.CONTRACT_REVERT_EXECUTED.toString()); + + new ContractDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setContractId(contractId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot call contract function when gas is not set") + void cannotCallContractFunctionWhenGasIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + var contractId = Objects.requireNonNull(new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(200000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(testEnv.client) + .getReceipt(testEnv.client) + .contractId); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new ContractCallQuery() + .setContractId(contractId) + .setFunction("getMessage") + .execute(testEnv.client); + }) + .withMessageContaining(Status.INSUFFICIENT_GAS.toString()); + + new ContractDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setContractId(contractId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot call contract function when contract ID is not set") + void cannotCallContractFunctionWhenContractIDIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + var contractId = Objects.requireNonNull(new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(200000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(testEnv.client) + .getReceipt(testEnv.client) + .contractId); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new ContractCallQuery() + .setGas(100000) + .setFunction("getMessage") + .execute(testEnv.client); + }) + .withMessageContaining(Status.INVALID_CONTRACT_ID.toString()); + + new ContractDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setContractId(contractId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can get cost, even with a big max") + void getCostBigMaxContractCallFunction() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + response = new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(200000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(testEnv.client); + + var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); + + // Wait for mirror node to import data + Thread.sleep(2000); + + long gas = new MirrorNodeContractEstimateGasQuery() + .setContractId(contractId) + .setFunction("getMessage") + .execute(testEnv.client); + + var callQuery = new ContractCallQuery() + .setContractId(contractId) + .setGas(gas) + .setFunction("getMessage") + .setQueryPayment(new Hbar(1)); + + var result = callQuery.execute(testEnv.client); + + assertThat(result.getString(0)).isEqualTo("Hello from Hedera."); + + new ContractDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setContractId(contractId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Error, max is smaller than set payment.") + void getCostSmallMaxContractCallFunction() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + response = new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(200000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(testEnv.client); + + var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); + + // Wait for mirror node to import data + Thread.sleep(2000); + + var gas = new MirrorNodeContractEstimateGasQuery() + .setContractId(contractId) + .setFunction("getMessage") + .execute(testEnv.client); + + var callQuery = new ContractCallQuery() + .setContractId(contractId) + .setGas(gas) + .setFunction("getMessage") + .setMaxQueryPayment(Hbar.fromTinybars(1)); + + assertThatExceptionOfType(MaxQueryPaymentExceededException.class).isThrownBy(() -> { + callQuery.execute(testEnv.client); + }); + + new ContractDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setContractId(contractId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Insufficient tx fee error.") + void getCostInsufficientTxFeeContractCallFunction() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + response = new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(200000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(testEnv.client); + + var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); + + // Wait for mirror node to import data + Thread.sleep(2000); + + var gas = new MirrorNodeContractEstimateGasQuery() + .setContractId(contractId) + .setFunction("getMessage") + .execute(testEnv.client); + + var callQuery = new ContractCallQuery() + .setContractId(contractId) + .setGas(gas) + .setFunction("getMessage") + .setMaxQueryPayment(new Hbar(100)); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + callQuery.setQueryPayment(Hbar.fromTinybars(1)).execute(testEnv.client); + }) + .satisfies(error -> assertThat(error.status.toString()).isEqualTo("INSUFFICIENT_TX_FEE")); + + new ContractDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setContractId(contractId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractCreateFlowIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractCreateFlowIntegrationTest.java new file mode 100644 index 0000000000..45417df3ac --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractCreateFlowIntegrationTest.java @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.Objects; +import org.hiero.sdk.*; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class ContractCreateFlowIntegrationTest { + private static final String SMART_CONTRACT_BYTECODE = + "608060405234801561001057600080fd5b506040516104d73803806104d78339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b506040525050600080546001600160a01b0319163317905550805161010890600190602084019061010f565b50506101aa565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061015057805160ff191683800117855561017d565b8280016001018555821561017d579182015b8281111561017d578251825591602001919060010190610162565b5061018992915061018d565b5090565b6101a791905b808211156101895760008155600101610193565b90565b61031e806101b96000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063368b87721461004657806341c0e1b5146100ee578063ce6d41de146100f6575b600080fd5b6100ec6004803603602081101561005c57600080fd5b81019060208101813564010000000081111561007757600080fd5b82018360208201111561008957600080fd5b803590602001918460018302840111640100000000831117156100ab57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610173945050505050565b005b6100ec6101a2565b6100fe6101ba565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610138578181015183820152602001610120565b50505050905090810190601f1680156101655780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000546001600160a01b0316331461018a5761019f565b805161019d906001906020840190610250565b505b50565b6000546001600160a01b03163314156101b85733ff5b565b60018054604080516020601f600260001961010087891615020190951694909404938401819004810282018101909252828152606093909290918301828280156102455780601f1061021a57610100808354040283529160200191610245565b820191906000526020600020905b81548152906001019060200180831161022857829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061029157805160ff19168380011785556102be565b828001600101855582156102be579182015b828111156102be5782518255916020019190600101906102a3565b506102ca9291506102ce565b5090565b61024d91905b808211156102ca57600081556001016102d456fea264697066735822122084964d4c3f6bc912a9d20e14e449721012d625aa3c8a12de41ae5519752fc89064736f6c63430006000033"; + + @Test + @DisplayName("Create contract with flow") + void createContractWithFlow() throws Throwable { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new ContractCreateFlow() + .setBytecode(SMART_CONTRACT_BYTECODE) + .setAdminKey(testEnv.operatorKey) + .setGas(200000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setContractMemo("[e2e::ContractCreateFlow]") + .execute(testEnv.client); + + var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); + + var receipt = new ContractExecuteTransaction() + .setContractId(contractId) + .setGas(100000) + .setFunction("setMessage", new ContractFunctionParameters().addString("new message")) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + assertThat(receipt.status).isEqualTo(Status.SUCCESS); + + new ContractDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setContractId(contractId) + .setTransferAccountId(testEnv.operatorId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Create contract with flow without signing") + void createContractWithFlowWithoutSigning() throws Throwable { + try (var testEnv = new IntegrationTestEnv(1)) { + var adminKey = PrivateKey.generateED25519(); + + assertThatThrownBy(() -> new ContractCreateFlow() + .setBytecode(SMART_CONTRACT_BYTECODE) + .setAdminKey(adminKey) + .setGas(100000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setContractMemo("[e2e::ContractCreateFlow]") + .execute(testEnv.client)) + .isInstanceOf(RuntimeException.class) + .hasMessageEndingWith("raised status INVALID_SIGNATURE"); + } + } + + @Test + @DisplayName("Create contract with flow and sign with private key") + void createContractWithFlowPrivateKeySign() throws Throwable { + try (var testEnv = new IntegrationTestEnv(1)) { + var adminKey = PrivateKey.generateED25519(); + + var response = new ContractCreateFlow() + .setBytecode(SMART_CONTRACT_BYTECODE) + .setAdminKey(testEnv.operatorKey) + .setGas(200000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setContractMemo("[e2e::ContractCreateFlow]") + .freezeWith(testEnv.client) + .sign(adminKey) + .execute(testEnv.client); + + var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); + + var receipt = new ContractExecuteTransaction() + .setContractId(contractId) + .setGas(100000) + .setFunction("setMessage", new ContractFunctionParameters().addString("new message")) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + assertThat(receipt.status).isEqualTo(Status.SUCCESS); + + new ContractDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setContractId(contractId) + .setTransferAccountId(testEnv.operatorId) + .freezeWith(testEnv.client) + .sign(adminKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Create contract with flow and sign with public key and transaction signer") + void createContractWithFlowPublicKeySign() throws Throwable { + try (var testEnv = new IntegrationTestEnv(1)) { + var adminKey = PrivateKey.generateED25519(); + + var response = new ContractCreateFlow() + .setBytecode(SMART_CONTRACT_BYTECODE) + .setAdminKey(testEnv.operatorKey) + .setGas(200000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setContractMemo("[e2e::ContractCreateFlow]") + .freezeWith(testEnv.client) + .signWith(adminKey.getPublicKey(), adminKey::sign) + .execute(testEnv.client); + + var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); + + var receipt = new ContractExecuteTransaction() + .setContractId(contractId) + .setGas(100000) + .setFunction("setMessage", new ContractFunctionParameters().addString("new message")) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + assertThat(receipt.status).isEqualTo(Status.SUCCESS); + + new ContractDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setContractId(contractId) + .setTransferAccountId(testEnv.operatorId) + .freezeWith(testEnv.client) + .sign(adminKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractCreateIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractCreateIntegrationTest.java new file mode 100644 index 0000000000..047fdec986 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractCreateIntegrationTest.java @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Objects; +import org.hiero.sdk.ContractCreateTransaction; +import org.hiero.sdk.ContractDeleteTransaction; +import org.hiero.sdk.ContractFunctionParameters; +import org.hiero.sdk.ContractInfoQuery; +import org.hiero.sdk.FileCreateTransaction; +import org.hiero.sdk.FileDeleteTransaction; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.sdk.Status; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class ContractCreateIntegrationTest { + private static final String SMART_CONTRACT_BYTECODE = + "608060405234801561001057600080fd5b506040516104d73803806104d78339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b506040525050600080546001600160a01b0319163317905550805161010890600190602084019061010f565b50506101aa565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061015057805160ff191683800117855561017d565b8280016001018555821561017d579182015b8281111561017d578251825591602001919060010190610162565b5061018992915061018d565b5090565b6101a791905b808211156101895760008155600101610193565b90565b61031e806101b96000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063368b87721461004657806341c0e1b5146100ee578063ce6d41de146100f6575b600080fd5b6100ec6004803603602081101561005c57600080fd5b81019060208101813564010000000081111561007757600080fd5b82018360208201111561008957600080fd5b803590602001918460018302840111640100000000831117156100ab57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610173945050505050565b005b6100ec6101a2565b6100fe6101ba565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610138578181015183820152602001610120565b50505050905090810190601f1680156101655780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000546001600160a01b0316331461018a5761019f565b805161019d906001906020840190610250565b505b50565b6000546001600160a01b03163314156101b85733ff5b565b60018054604080516020601f600260001961010087891615020190951694909404938401819004810282018101909252828152606093909290918301828280156102455780601f1061021a57610100808354040283529160200191610245565b820191906000526020600020905b81548152906001019060200180831161022857829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061029157805160ff19168380011785556102be565b828001600101855582156102be579182015b828111156102be5782518255916020019190600101906102a3565b506102ca9291506102ce565b5090565b61024d91905b808211156102ca57600081556001016102d456fea264697066735822122084964d4c3f6bc912a9d20e14e449721012d625aa3c8a12de41ae5519752fc89064736f6c63430006000033"; + + @Test + @DisplayName("Can create contract") + void canCreateContract() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + response = new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(200000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(testEnv.client); + + var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); + + var info = new ContractInfoQuery().setContractId(contractId).execute(testEnv.client); + + assertThat(info.contractId).isEqualTo(contractId); + assertThat(info.accountId).isNotNull(); + assertThat(Objects.requireNonNull(info.accountId).toString()) + .isEqualTo(Objects.requireNonNull(contractId).toString()); + assertThat(info.adminKey).isNotNull(); + assertThat(Objects.requireNonNull(info.adminKey).toString()) + .isEqualTo(Objects.requireNonNull(testEnv.operatorKey).toString()); + assertThat(info.storage).isEqualTo(128); + assertThat(info.contractMemo).isEqualTo("[e2e::ContractCreateTransaction]"); + + new ContractDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setContractId(contractId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can create contract with no admin key") + void canCreateContractWithNoAdminKey() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + response = new ContractCreateTransaction() + .setGas(200000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(testEnv.client); + + var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); + + var info = new ContractInfoQuery().setContractId(contractId).execute(testEnv.client); + + assertThat(info.contractId).isEqualTo(contractId); + assertThat(info.accountId).isNotNull(); + assertThat(Objects.requireNonNull(info.accountId).toString()) + .isEqualTo(Objects.requireNonNull(contractId).toString()); + assertThat(info.adminKey).isNotNull(); + // assertEquals(info.adminKey, contractId); + assertThat(info.storage).isEqualTo(128); + assertThat(info.contractMemo).isEqualTo("[e2e::ContractCreateTransaction]"); + } + } + + @Test + @DisplayName("Cannot create contract when gas is not set") + void cannotCreateContractWhenGasIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setConstructorParameters( + new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INSUFFICIENT_GAS.toString()); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot create contract when constructor parameters are not set") + void cannotCreateContractWhenConstructorParametersAreNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(100000) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.CONTRACT_REVERT_EXECUTED.toString()); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot create contract when bytecode file ID is not set") + void cannotCreateContractWhenBytecodeFileIdIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(100000) + .setConstructorParameters( + new ContractFunctionParameters().addString("Hello from Hedera.")) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_FILE_ID.toString()); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractDeleteIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractDeleteIntegrationTest.java new file mode 100644 index 0000000000..e9ce11947e --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractDeleteIntegrationTest.java @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Objects; +import org.hiero.sdk.ContractCreateTransaction; +import org.hiero.sdk.ContractDeleteTransaction; +import org.hiero.sdk.ContractFunctionParameters; +import org.hiero.sdk.ContractInfoQuery; +import org.hiero.sdk.FileCreateTransaction; +import org.hiero.sdk.FileDeleteTransaction; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.sdk.Status; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class ContractDeleteIntegrationTest { + private static final String SMART_CONTRACT_BYTECODE = + "608060405234801561001057600080fd5b506040516104d73803806104d78339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b506040525050600080546001600160a01b0319163317905550805161010890600190602084019061010f565b50506101aa565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061015057805160ff191683800117855561017d565b8280016001018555821561017d579182015b8281111561017d578251825591602001919060010190610162565b5061018992915061018d565b5090565b6101a791905b808211156101895760008155600101610193565b90565b61031e806101b96000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063368b87721461004657806341c0e1b5146100ee578063ce6d41de146100f6575b600080fd5b6100ec6004803603602081101561005c57600080fd5b81019060208101813564010000000081111561007757600080fd5b82018360208201111561008957600080fd5b803590602001918460018302840111640100000000831117156100ab57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610173945050505050565b005b6100ec6101a2565b6100fe6101ba565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610138578181015183820152602001610120565b50505050905090810190601f1680156101655780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000546001600160a01b0316331461018a5761019f565b805161019d906001906020840190610250565b505b50565b6000546001600160a01b03163314156101b85733ff5b565b60018054604080516020601f600260001961010087891615020190951694909404938401819004810282018101909252828152606093909290918301828280156102455780601f1061021a57610100808354040283529160200191610245565b820191906000526020600020905b81548152906001019060200180831161022857829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061029157805160ff19168380011785556102be565b828001600101855582156102be579182015b828111156102be5782518255916020019190600101906102a3565b506102ca9291506102ce565b5090565b61024d91905b808211156102ca57600081556001016102d456fea264697066735822122084964d4c3f6bc912a9d20e14e449721012d625aa3c8a12de41ae5519752fc89064736f6c63430006000033"; + + @Test + @DisplayName("Can delete contract with admin key") + void canDeleteContractWithAdminKey() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + response = new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(200000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(testEnv.client); + + var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); + + var info = new ContractInfoQuery().setContractId(contractId).execute(testEnv.client); + + assertThat(info.contractId).isEqualTo(contractId); + assertThat(info.accountId).isNotNull(); + assertThat(Objects.requireNonNull(info.accountId).toString()).isEqualTo(contractId.toString()); + assertThat(info.adminKey).isNotNull(); + assertThat(Objects.requireNonNull(info.adminKey).toString()) + .isEqualTo(Objects.requireNonNull(testEnv.operatorKey).toString()); + assertThat(info.storage).isEqualTo(128); + assertThat(info.contractMemo).isEqualTo("[e2e::ContractCreateTransaction]"); + + new ContractDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setContractId(contractId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot delete contract which has no admin key") + void cannotDeleteContractWhichHasNoAdminKey() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + var contractId = Objects.requireNonNull(new ContractCreateTransaction() + .setGas(200000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(testEnv.client) + .getReceipt(testEnv.client) + .contractId); + + var info = new ContractInfoQuery().setContractId(contractId).execute(testEnv.client); + + assertThat(info.contractId).isEqualTo(contractId); + assertThat(info.accountId).isNotNull(); + assertThat(Objects.requireNonNull(info.accountId).toString()).isEqualTo(contractId.toString()); + assertThat(info.adminKey).isNotNull(); + // assertEquals(info.adminKey, contractId); + assertThat(info.storage).isEqualTo(128); + assertThat(info.contractMemo).isEqualTo("[e2e::ContractCreateTransaction]"); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new ContractDeleteTransaction() + .setContractId(contractId) + .setTransferAccountId(testEnv.client.getOperatorAccountId()) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.MODIFYING_IMMUTABLE_CONTRACT.toString()); + } + } + + @Test + @DisplayName("Cannot delete contract when contract ID is not set") + void cannotDeleteContractWhenContractIDIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new ContractDeleteTransaction().execute(testEnv.client).getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_CONTRACT_ID.toString()); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractExecuteIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractExecuteIntegrationTest.java new file mode 100644 index 0000000000..89fb629a26 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractExecuteIntegrationTest.java @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Objects; +import org.hiero.sdk.ContractCreateTransaction; +import org.hiero.sdk.ContractDeleteTransaction; +import org.hiero.sdk.ContractExecuteTransaction; +import org.hiero.sdk.ContractFunctionParameters; +import org.hiero.sdk.FileCreateTransaction; +import org.hiero.sdk.FileDeleteTransaction; +import org.hiero.sdk.MirrorNodeContractEstimateGasQuery; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.sdk.Status; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class ContractExecuteIntegrationTest { + private static final String SMART_CONTRACT_BYTECODE = + "608060405234801561001057600080fd5b506040516104d73803806104d78339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b506040525050600080546001600160a01b0319163317905550805161010890600190602084019061010f565b50506101aa565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061015057805160ff191683800117855561017d565b8280016001018555821561017d579182015b8281111561017d578251825591602001919060010190610162565b5061018992915061018d565b5090565b6101a791905b808211156101895760008155600101610193565b90565b61031e806101b96000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063368b87721461004657806341c0e1b5146100ee578063ce6d41de146100f6575b600080fd5b6100ec6004803603602081101561005c57600080fd5b81019060208101813564010000000081111561007757600080fd5b82018360208201111561008957600080fd5b803590602001918460018302840111640100000000831117156100ab57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610173945050505050565b005b6100ec6101a2565b6100fe6101ba565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610138578181015183820152602001610120565b50505050905090810190601f1680156101655780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000546001600160a01b0316331461018a5761019f565b805161019d906001906020840190610250565b505b50565b6000546001600160a01b03163314156101b85733ff5b565b60018054604080516020601f600260001961010087891615020190951694909404938401819004810282018101909252828152606093909290918301828280156102455780601f1061021a57610100808354040283529160200191610245565b820191906000526020600020905b81548152906001019060200180831161022857829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061029157805160ff19168380011785556102be565b828001600101855582156102be579182015b828111156102be5782518255916020019190600101906102a3565b506102ca9291506102ce565b5090565b61024d91905b808211156102ca57600081556001016102d456fea264697066735822122084964d4c3f6bc912a9d20e14e449721012d625aa3c8a12de41ae5519752fc89064736f6c63430006000033"; + + @Test + @DisplayName("Can execute contract methods") + void canExecuteContractMethods() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + response = new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(200000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(testEnv.client); + + var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); + + // Wait for mirror node to import data + Thread.sleep(2000); + + var gas = new MirrorNodeContractEstimateGasQuery() + .setContractId(contractId) + .setFunction("setMessage", new ContractFunctionParameters().addString("new message")) + .execute(testEnv.client); + var receipt = new ContractExecuteTransaction() + .setContractId(contractId) + .setGas(gas + 10000) + .setFunction("setMessage", new ContractFunctionParameters().addString("new message")) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + assertThat(receipt.status).isEqualTo(Status.SUCCESS); + + new ContractDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setContractId(contractId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot execute contract when contract ID is not set") + void cannotExecuteContractWhenContractIDIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new ContractExecuteTransaction() + .setGas(100000) + .setFunction("setMessage", new ContractFunctionParameters().addString("new message")) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_CONTRACT_ID.toString()); + } + } + + @Test + @DisplayName("Cannot execute contract when contract function parameters are not set") + void cannotExecuteContractWhenContractFunctionParametersAreNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + var contractId = Objects.requireNonNull(new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(200000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(testEnv.client) + .getReceipt(testEnv.client) + .contractId); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new ContractExecuteTransaction() + .setContractId(contractId) + .setGas(100000) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.CONTRACT_REVERT_EXECUTED.toString()); + + new ContractDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setContractId(contractId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot execute contract when gas is not set") + void cannotExecuteContractWhenGasIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + var contractId = Objects.requireNonNull(new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(200000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(testEnv.client) + .getReceipt(testEnv.client) + .contractId); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new ContractExecuteTransaction() + .setContractId(contractId) + .setFunction("setMessage", new ContractFunctionParameters().addString("new message")) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INSUFFICIENT_GAS.toString()); + + new ContractDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setContractId(contractId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractFunctionParametersIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractFunctionParametersIntegrationTest.java new file mode 100644 index 0000000000..1df3af4626 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractFunctionParametersIntegrationTest.java @@ -0,0 +1,3482 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.esaulpaugh.headlong.abi.Address; +import java.math.BigInteger; +import java.util.Arrays; +import java.util.Objects; +import org.hiero.sdk.ContractCallQuery; +import org.hiero.sdk.ContractCreateTransaction; +import org.hiero.sdk.ContractDeleteTransaction; +import org.hiero.sdk.ContractFunctionParameters; +import org.hiero.sdk.ContractId; +import org.hiero.sdk.FileAppendTransaction; +import org.hiero.sdk.FileCreateTransaction; +import org.hiero.sdk.FileDeleteTransaction; +import org.hiero.sdk.FileId; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.MirrorNodeContractEstimateGasQuery; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class ContractFunctionParametersIntegrationTest { + private static final String SMART_CONTRACT_BYTECODE = + "60806040523480156200001157600080fd5b5060408051608081018252600491810191825263082d8caf60e31b6060820152818152600160208201529060009081906200004d908262000106565b5060208201518160010155905050620001d2565b634e487b7160e01b600052604160045260246000fd5b600181811c908216806200008c57607f821691505b602082108103620000ad57634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200010157600081815260208120601f850160051c81016020861015620000dc5750805b601f850160051c820191505b81811015620000fd57828155600101620000e8565b5050505b505050565b81516001600160401b0381111562000122576200012262000061565b6200013a8162000133845462000077565b84620000b3565b602080601f831160018114620001725760008415620001595750858301515b600019600386901b1c1916600185901b178555620000fd565b600085815260208120601f198616915b82811015620001a35788860151825594840194600190910190840162000182565b5085821015620001c25787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b615dfe80620001e26000396000f3fe608060405234801561001057600080fd5b50600436106108355760003560e01c80637f8082f711610437578063c5882dbb11610236578063e2a345291161013b578063f6e877f4116100c3578063fafc3b9911610092578063fafc3b9914611857578063fba1bc4c14611872578063febe099714611887578063ff5f54dd146118a2578063ffb80501146118bd57600080fd5b8063f6e877f4146117d9578063f8293f6e146117ff578063f914c4d214611821578063f962326a1461183c57600080fd5b8063eceda56a1161010a578063eceda56a1461174c578063ef65066114611767578063f0ebb92214611782578063f4e490f51461179d578063f680c24a146117be57600080fd5b8063e2a34529146116d8578063e4b90944146116f3578063e713cda81461170e578063ea945d301461173157600080fd5b8063d79d4d40116101be578063e05e91e01161018d578063e05e91e014611630578063e066de5014611656578063e0cddc551461167c578063e0f53e2414611697578063e1dbb318146116bd57600080fd5b8063d79d4d4014611592578063dade0c0b146115b8578063dbb04ed9146115da578063de9fb4841461160357600080fd5b8063cbd2e6a511610205578063cbd2e6a514611511578063cdb9e4e814611536578063cf7c3dfc1461155c578063d1b10ad714610ada578063d33c57501461157757600080fd5b8063c5882dbb1461148b578063c6c18a1c146114a6578063c7d8b87e146114d0578063cb47cdae146114f657600080fd5b8063a1bda1221161033c578063b5abe7df116102c4578063ba945bdb11610293578063ba945bdb146113dc578063bb6b524314611402578063bd90536a14611428578063be63d0b814611450578063c503772d1461146b57600080fd5b8063b5abe7df14611359578063b834bfe914611374578063b8da8d1614611395578063b989c7ee146113bb57600080fd5b8063aa016e681161030b578063aa016e68146112dc578063aa80ca2e14610c6b578063b2325c35146112f7578063b2db404a14611312578063b4e3e7b11461133357600080fd5b8063a1bda12214611259578063a401d60d1461127a578063a57ebf10146112a0578063a75761f1146112bb57600080fd5b80638f2805e0116103bf57806394cd7c801161038e57806394cd7c80146111b557806398508ba3146111d65780639b1794ae146111f7578063a08b9f671461121d578063a19962341461123e57600080fd5b80638f2805e0146111435780638ff4cfee1461115e578063909c5b2414611179578063923f5edf1461119457600080fd5b806386aba5a71161040657806386aba5a7146110ab578063881c8fb7146110c657806388b1dd37146110ec57806388b7e6f5146111075780638d7f60151461112857600080fd5b80637f8082f714610c6b578063817b24541461107557806381dbe13e14611090578063827147ce1461099a57600080fd5b806338fa6658116106435780635a8fd3b51161054857806370a5cb81116104d05780637ba844771161049f5780637ba8447714610fe25780637d0dc26214610ffd5780637d906c551461101e5780637e281630146110395780637ec32d841461105457600080fd5b806370a5cb8114610f5a57806372a06b4d14610f80578063737b801614610fa1578063796a27ea14610fbc57600080fd5b806369cbe0561161051757806369cbe05614610ecd5780636a54715c14610ee85780636a75c12f14610f095780636a9929db14610f245780636ee8f39c14610f3f57600080fd5b80635a8fd3b514610e4f578063628bc3ef14610e6a57806364e008c114610e8b57806368ef446614610eac57600080fd5b80634bbc9a67116105cb578063545e21131161059a578063545e211314610dd157806355f232a414610dfe57806357890ba914610e1957806357d9c08b14610e3457806359adb2df14610ada57600080fd5b80634bbc9a6714610d655780634e96247314610d80578063501297c214610d9b57806350370d8514610db657600080fd5b80633f396e67116106125780633f396e6714610ce3578063407b899b14610d0b578063435a33a814610d2c57806344e7b03714610af657806348d848d014610d4757600080fd5b806338fa665814610c6b5780633a73007714610c865780633b45e6e014610ca15780633e1a277114610cc257600080fd5b80631e9aa70f116107495780632ef16e8e116106d15780633135d681116106a05780633135d68114610beb57806333520ec314610c0657806333edb89614610c275780633729a2da14610c42578063382d087314610c5057600080fd5b80632ef16e8e14610b6d5780632f47a40d14610b8e5780632f6c1bb414610baf578063309f9ac914610bd057600080fd5b806322937ea91161071857806322937ea914610ada5780632421101f14610af657806324c3c3d214610b1c57806328a30eb714610b375780632a3082ce14610b5257600080fd5b80631e9aa70f14610a6e578063203ae83a14610a8957806321d1730b14610aa45780632234ea0214610abf57600080fd5b8063118b8415116107cc57806312b932a71161079b57806312b932a7146109db57806312cd95a1146109f657806315832ae414610a17578063189cea8e14610a325780631d11456214610a5357600080fd5b8063118b84151461095857806311ec6c9014610979578063126bc8151461099a578063129ed5da146109b557600080fd5b806306ac6fe11161080857806306ac6fe1146108ca57806308123e09146108f05780630a958dc81461091657806310d545531461093757600080fd5b8063017fa10b1461083a578063021d88ab14610868578063037454301461088e5780630577a846146108af575b600080fd5b61084b610848366004611a34565b90565b6040516001600160801b0390911681526020015b60405180910390f35b610876610848366004611a6d565b6040516001600160601b03909116815260200161085f565b61089c610848366004611a9a565b604051600c9190910b815260200161085f565b6108bd610848366004611b35565b60405161085f9190611bd1565b6108d8610848366004611c35565b6040516001600160781b03909116815260200161085f565b6108fe610848366004611c67565b60405166ffffffffffffff909116815260200161085f565b610924610848366004611c94565b60405160049190910b815260200161085f565b610945610848366004611cc1565b60405160119190910b815260200161085f565b610966610848366004611cee565b604051601e9190910b815260200161085f565b610987610848366004611d1b565b60405160139190910b815260200161085f565b6109a8610848366004611da5565b60405161085f9190611e27565b6109c3610848366004611e51565b6040516001600160981b03909116815260200161085f565b6109e9610848366004611e7e565b60405161085f9190611f0a565b610a04610848366004611f57565b60405160129190910b815260200161085f565b610a25610848366004611f83565b60405161085f919061200f565b610a4061084836600461205c565b60405160169190910b815260200161085f565b610a6161084836600461208f565b60405161085f919061211d565b610a7c610848366004612170565b60405161085f91906121fc565b610a97610848366004612237565b60405161085f91906122c3565b610ab261084836600461231b565b60405161085f91906123a7565b610acd6108483660046123fa565b60405161085f9190612486565b610ae86108483660046124c1565b60405190815260200161085f565b610b046108483660046124da565b6040516001600160a01b03909116815260200161085f565b610b2a61084836600461250e565b60405161085f919061259a565b610b456108483660046125db565b60405161085f9190612667565b610b606108483660046126b4565b60405161085f9190612740565b610b7b61084836600461278d565b604051601d9190910b815260200161085f565b610b9c6108483660046127a8565b604051600a9190910b815260200161085f565b610bbd6108483660046127d5565b60405160199190910b815260200161085f565b610bde610848366004612807565b60405161085f9190612893565b610bf96108483660046128eb565b60405161085f9190612977565b610c146108483660046129ca565b604051601a9190910b815260200161085f565b610c356108483660046129f7565b60405161085f9190612a83565b610a61610848366004612abe565b610c5e610848366004612b5e565b60405161085f9190612bea565b610c79610848366004612c25565b60405161085f9190612caa565b610c94610848366004612cfd565b60405161085f9190612d89565b610caf610848366004612de0565b60405160109190910b815260200161085f565b610cd0610848366004612e0d565b604051601c9190910b815260200161085f565b610cf1610848366004612e41565b60405168ffffffffffffffffff909116815260200161085f565b610d19610848366004612e6e565b60405160079190910b815260200161085f565b610d3a610848366004612e89565b60405161085f9190612f15565b610d55610848366004612f60565b604051901515815260200161085f565b610d73610848366004612f7b565b60405161085f9190613007565b610d8e610848366004613058565b60405161085f91906130e4565b610da9610848366004613137565b60405161085f91906131c3565b610dc4610848366004613215565b60405161085f91906132a1565b610de4610ddf3660046132f4565b6118e1565b60408051600093840b81529190920b60208201520161085f565b610e0c61084836600461330f565b60405161085f919061339b565b610e276108483660046133d6565b60405161085f9190613462565b610e426108483660046134b3565b60405161085f919061353f565b610e5d610848366004613593565b60405161085f919061361f565b610e7861084836600461366f565b60405160099190910b815260200161085f565b610e9961084836600461369c565b60405160179190910b815260200161085f565b610eba6108483660046136b7565b60405160149190910b815260200161085f565b610edb6108483660046136e4565b60405161085f9190613770565b610ef66108483660046137ab565b60405160069190910b815260200161085f565b610f176108483660046137d8565b60405161085f9190613864565b610f3261084836600461389f565b60405161085f919061392b565b610f4d610848366004613980565b60405161085f9190613a0c565b610f68610848366004613a67565b6040516001600160681b03909116815260200161085f565b610f8e6108483660046132f4565b60405160009190910b815260200161085f565b610faf610848366004613a99565b60405161085f9190613b25565b610fca610848366004613b66565b6040516001600160d81b03909116815260200161085f565b610ff0610848366004613b96565b60405161085f9190613c22565b61100b610848366004613c73565b604051601b9190910b815260200161085f565b61102c610848366004613ca1565b60405161085f9190613d2d565b611047610848366004613d7c565b60405161085f9190613e08565b611062610848366004613e56565b60405160029190910b815260200161085f565b611083610848366004613e88565b60405161085f9190613f14565b61109e610848366004613f55565b60405161085f9190614005565b6110b9610848366004614067565b60405161085f91906140f3565b6110d461084836600461414b565b6040516001600160b01b03909116815260200161085f565b6110fa610848366004614166565b60405161085f91906141f2565b61111561084836600461422d565b604051600f9190910b815260200161085f565b611136610848366004614248565b60405161085f91906142d4565b611151610848366004614326565b60405161085f91906143b2565b61116c6108483660046143f3565b60405161085f919061447f565b6111876108483660046144ba565b60405161085f919061455d565b6111a26108483660046145b2565b60405160159190910b815260200161085f565b6111c36108483660046145df565b60405160189190910b815260200161085f565b6111e46108483660046145fa565b604051600d9190910b815260200161085f565b61120561084836600461462c565b6040516001600160b81b03909116815260200161085f565b61122b610848366004614647565b604051600b9190910b815260200161085f565b61124c610848366004614662565b60405161085f91906146ee565b61126761084836600461472f565b60405160019190910b815260200161085f565b61128861084836600461474a565b6040516001600160f01b03909116815260200161085f565b6112ae610848366004614765565b60405161085f91906147f1565b6112c961084836600461482c565b60405161ffff909116815260200161085f565b6112ea610848366004614847565b60405161085f91906148d3565b611305610848366004614928565b60405161085f91906149b4565b611320610848366004614a01565b60405160059190910b815260200161085f565b611341610848366004614a1c565b6040516001600160701b03909116815260200161085f565b611367610848366004614a37565b60405161085f9190614ac3565b611382610848366004614b04565b604051600e9190910b815260200161085f565b6113a3610848366004614b1f565b6040516001600160a81b03909116815260200161085f565b6113c9610848366004614b3a565b60405160039190910b815260200161085f565b6113ea610848366004614b55565b6040516001600160881b03909116815260200161085f565b611410610848366004614b70565b6040516001600160c81b03909116815260200161085f565b61143b6114363660046124c1565b6118f9565b6040805192835260208301919091520161085f565b61145e610848366004614b8b565b60405161085f9190614c17565b611479610848366004614c52565b60405160ff909116815260200161085f565b611499610848366004614c6d565b60405161085f9190614cf9565b6114b4610848366004614d34565b6040516affffffffffffffffffffff909116815260200161085f565b6114de610848366004614d4f565b6040516001600160f81b03909116815260200161085f565b611504610848366004614d6a565b60405161085f9190614df6565b61151f610848366004614e31565b60405165ffffffffffff909116815260200161085f565b611544610848366004614e63565b6040516001600160e01b03909116815260200161085f565b61156a610848366004614e7e565b60405161085f9190614f0a565b611585610848366004614f45565b60405161085f9190614fd1565b6115a0610848366004615029565b6040516001600160d01b03909116815260200161085f565b6115cb6115c6366004615044565b611908565b60405161085f9392919061505f565b6115e8610848366004615096565b60405169ffffffffffffffffffff909116815260200161085f565b611616611611366004611c94565b611946565b60408051600493840b81529190920b60208201520161085f565b61163e6108483660046150b1565b6040516001600160401b03909116815260200161085f565b6116646108483660046150e3565b6040516001600160e81b03909116815260200161085f565b61168a6108483660046150fe565b60405161085f919061518a565b6116a56108483660046151c5565b6040516001600160c01b03909116815260200161085f565b6116cb6108483660046151f2565b60405161085f919061527e565b6116e66108483660046152b9565b60405161085f9190615345565b61170161084836600461538d565b60405161085f9190615419565b61171c610848366004615044565b60405163ffffffff909116815260200161085f565b61173f610848366004615454565b60405161085f91906154e0565b61175a61084836600461551b565b60405161085f91906155a7565b6117756108483660046155e2565b60405161085f919061566e565b6117906108483660046156af565b60405161085f919061573b565b6117ab610848366004615776565b60405160089190910b815260200161085f565b6117cc610848366004615791565b60405161085f919061581d565b6117e7610848366004615858565b6040516001600160901b03909116815260200161085f565b61180d610848366004615873565b60405162ffffff909116815260200161085f565b61182f61084836600461588e565b60405161085f919061591a565b61184a61084836600461595b565b60405161085f91906159e7565b611865610848366004615a28565b60405161085f9190615ab4565b61187a611955565b60405161085f9190615af5565b611895610848366004615b27565b60405161085f9190615bb3565b6118b0610848366004615bf4565b60405161085f9190615c80565b6118cb610848366004615cc1565b60405164ffffffffff909116815260200161085f565b600080826118f0816014615cf2565b91509150915091565b600080826118f0816001615d19565b600080606083611919600182615d41565b6040805180820190915260028152614f4b60f01b602082015291945063ffffffff16925090509193909250565b600080826118f0816001615d65565b604080518082019091526060815260006020820152600060405180604001604052908160008201805461198790615d8e565b80601f01602080910402602001604051908101604052809291908181526020018280546119b390615d8e565b8015611a005780601f106119d557610100808354040283529160200191611a00565b820191906000526020600020905b8154815290600101906020018083116119e357829003601f168201915b50505050508152602001600182015481525050905090565b80356001600160801b0381168114611a2f57600080fd5b919050565b600060208284031215611a4657600080fd5b611a4f82611a18565b9392505050565b80356001600160601b0381168114611a2f57600080fd5b600060208284031215611a7f57600080fd5b611a4f82611a56565b8035600c81900b8114611a2f57600080fd5b600060208284031215611aac57600080fd5b611a4f82611a88565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715611af357611af3611ab5565b604052919050565b60006001600160401b03821115611b1457611b14611ab5565b5060051b60200190565b80356001600160f01b0381168114611a2f57600080fd5b60006020808385031215611b4857600080fd5b82356001600160401b03811115611b5e57600080fd5b8301601f81018513611b6f57600080fd5b8035611b82611b7d82611afb565b611acb565b81815260059190911b82018301908381019087831115611ba157600080fd5b928401925b82841015611bc657611bb784611b1e565b82529284019290840190611ba6565b979650505050505050565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160f01b031683529284019291840191600101611bed565b50909695505050505050565b80356001600160781b0381168114611a2f57600080fd5b600060208284031215611c4757600080fd5b611a4f82611c1e565b803566ffffffffffffff81168114611a2f57600080fd5b600060208284031215611c7957600080fd5b611a4f82611c50565b8035600481900b8114611a2f57600080fd5b600060208284031215611ca657600080fd5b611a4f82611c82565b8035601181900b8114611a2f57600080fd5b600060208284031215611cd357600080fd5b611a4f82611caf565b8035601e81900b8114611a2f57600080fd5b600060208284031215611d0057600080fd5b611a4f82611cdc565b8035601381900b8114611a2f57600080fd5b600060208284031215611d2d57600080fd5b611a4f82611d09565b600082601f830112611d4757600080fd5b81356001600160401b03811115611d6057611d60611ab5565b611d73601f8201601f1916602001611acb565b818152846020838601011115611d8857600080fd5b816020850160208301376000918101602001919091529392505050565b600060208284031215611db757600080fd5b81356001600160401b03811115611dcd57600080fd5b611dd984828501611d36565b949350505050565b6000815180845260005b81811015611e0757602081850181015186830182015201611deb565b506000602082860101526020601f19601f83011685010191505092915050565b602081526000611a4f6020830184611de1565b80356001600160981b0381168114611a2f57600080fd5b600060208284031215611e6357600080fd5b611a4f82611e3a565b8035600381900b8114611a2f57600080fd5b60006020808385031215611e9157600080fd5b82356001600160401b03811115611ea757600080fd5b8301601f81018513611eb857600080fd5b8035611ec6611b7d82611afb565b81815260059190911b82018301908381019087831115611ee557600080fd5b928401925b82841015611bc657611efb84611e6c565b82529284019290840190611eea565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160030b83529284019291840191600101611f26565b8035601281900b8114611a2f57600080fd5b600060208284031215611f6957600080fd5b611a4f82611f45565b803560ff81168114611a2f57600080fd5b60006020808385031215611f9657600080fd5b82356001600160401b03811115611fac57600080fd5b8301601f81018513611fbd57600080fd5b8035611fcb611b7d82611afb565b81815260059190911b82018301908381019087831115611fea57600080fd5b928401925b82841015611bc65761200084611f72565b82529284019290840190611fef565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160ff168352928401929184019160010161202b565b8035601681900b8114611a2f57600080fd5b60006020828403121561206e57600080fd5b611a4f8261204a565b6001600160a01b038116811461208c57600080fd5b50565b600060208083850312156120a257600080fd5b82356001600160401b038111156120b857600080fd5b8301601f810185136120c957600080fd5b80356120d7611b7d82611afb565b81815260059190911b820183019083810190878311156120f657600080fd5b928401925b82841015611bc657833561210e81612077565b825292840192908401906120fb565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160a01b031683529284019291840191600101612139565b8035601481900b8114611a2f57600080fd5b6000602080838503121561218357600080fd5b82356001600160401b0381111561219957600080fd5b8301601f810185136121aa57600080fd5b80356121b8611b7d82611afb565b81815260059190911b820183019083810190878311156121d757600080fd5b928401925b82841015611bc6576121ed8461215e565b825292840192908401906121dc565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160140b83529284019291840191600101612218565b6000602080838503121561224a57600080fd5b82356001600160401b0381111561226057600080fd5b8301601f8101851361227157600080fd5b803561227f611b7d82611afb565b81815260059190911b8201830190838101908783111561229e57600080fd5b928401925b82841015611bc6576122b484611c50565b825292840192908401906122a3565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835166ffffffffffffff16835292840192918401916001016122df565b80356001600160f81b0381168114611a2f57600080fd5b6000602080838503121561232e57600080fd5b82356001600160401b0381111561234457600080fd5b8301601f8101851361235557600080fd5b8035612363611b7d82611afb565b81815260059190911b8201830190838101908783111561238257600080fd5b928401925b82841015611bc65761239884612304565b82529284019290840190612387565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160f81b0316835292840192918401916001016123c3565b8035600a81900b8114611a2f57600080fd5b6000602080838503121561240d57600080fd5b82356001600160401b0381111561242357600080fd5b8301601f8101851361243457600080fd5b8035612442611b7d82611afb565b81815260059190911b8201830190838101908783111561246157600080fd5b928401925b82841015611bc657612477846123e8565b82529284019290840190612466565b6020808252825182820181905260009190848201906040850190845b81811015611c12578351600a0b835292840192918401916001016124a2565b6000602082840312156124d357600080fd5b5035919050565b6000602082840312156124ec57600080fd5b8135611a4f81612077565b80356001600160c81b0381168114611a2f57600080fd5b6000602080838503121561252157600080fd5b82356001600160401b0381111561253757600080fd5b8301601f8101851361254857600080fd5b8035612556611b7d82611afb565b81815260059190911b8201830190838101908783111561257557600080fd5b928401925b82841015611bc65761258b846124f7565b8252928401929084019061257a565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160c81b0316835292840192918401916001016125b6565b600060208083850312156125ee57600080fd5b82356001600160401b0381111561260457600080fd5b8301601f8101851361261557600080fd5b8035612623611b7d82611afb565b81815260059190911b8201830190838101908783111561264257600080fd5b928401925b82841015611bc65761265884611d09565b82529284019290840190612647565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160130b83529284019291840191600101612683565b8035600b81900b8114611a2f57600080fd5b600060208083850312156126c757600080fd5b82356001600160401b038111156126dd57600080fd5b8301601f810185136126ee57600080fd5b80356126fc611b7d82611afb565b81815260059190911b8201830190838101908783111561271b57600080fd5b928401925b82841015611bc657612731846126a2565b82529284019290840190612720565b6020808252825182820181905260009190848201906040850190845b81811015611c12578351600b0b8352928401929184019160010161275c565b8035601d81900b8114611a2f57600080fd5b60006020828403121561279f57600080fd5b611a4f8261277b565b6000602082840312156127ba57600080fd5b611a4f826123e8565b8035601981900b8114611a2f57600080fd5b6000602082840312156127e757600080fd5b611a4f826127c3565b80356001600160c01b0381168114611a2f57600080fd5b6000602080838503121561281a57600080fd5b82356001600160401b0381111561283057600080fd5b8301601f8101851361284157600080fd5b803561284f611b7d82611afb565b81815260059190911b8201830190838101908783111561286e57600080fd5b928401925b82841015611bc657612884846127f0565b82529284019290840190612873565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160c01b0316835292840192918401916001016128af565b80356001600160401b0381168114611a2f57600080fd5b600060208083850312156128fe57600080fd5b82356001600160401b0381111561291457600080fd5b8301601f8101851361292557600080fd5b8035612933611b7d82611afb565b81815260059190911b8201830190838101908783111561295257600080fd5b928401925b82841015611bc657612968846128d4565b82529284019290840190612957565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160401b031683529284019291840191600101612993565b8035601a81900b8114611a2f57600080fd5b6000602082840312156129dc57600080fd5b611a4f826129b8565b8035600d81900b8114611a2f57600080fd5b60006020808385031215612a0a57600080fd5b82356001600160401b03811115612a2057600080fd5b8301601f81018513612a3157600080fd5b8035612a3f611b7d82611afb565b81815260059190911b82018301908381019087831115612a5e57600080fd5b928401925b82841015611bc657612a74846129e5565b82529284019290840190612a63565b6020808252825182820181905260009190848201906040850190845b81811015611c12578351600d0b83529284019291840191600101612a9f565b60006020808385031215612ad157600080fd5b82356001600160401b03811115612ae757600080fd5b8301601f81018513612af857600080fd5b8035612b06611b7d82611afb565b81815260059190911b82018301908381019087831115612b2557600080fd5b928401925b82841015611bc6578335612b3d81612077565b82529284019290840190612b2a565b8035600181900b8114611a2f57600080fd5b60006020808385031215612b7157600080fd5b82356001600160401b03811115612b8757600080fd5b8301601f81018513612b9857600080fd5b8035612ba6611b7d82611afb565b81815260059190911b82018301908381019087831115612bc557600080fd5b928401925b82841015611bc657612bdb84612b4c565b82529284019290840190612bca565b6020808252825182820181905260009190848201906040850190845b81811015611c12578351600190810b8452938501939285019201612c06565b60006020808385031215612c3857600080fd5b82356001600160401b03811115612c4e57600080fd5b8301601f81018513612c5f57600080fd5b8035612c6d611b7d82611afb565b81815260059190911b82018301908381019087831115612c8c57600080fd5b928401925b82841015611bc657833582529284019290840190612c91565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835183529284019291840191600101612cc6565b80356affffffffffffffffffffff81168114611a2f57600080fd5b60006020808385031215612d1057600080fd5b82356001600160401b03811115612d2657600080fd5b8301601f81018513612d3757600080fd5b8035612d45611b7d82611afb565b81815260059190911b82018301908381019087831115612d6457600080fd5b928401925b82841015611bc657612d7a84612ce2565b82529284019290840190612d69565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516affffffffffffffffffffff1683529284019291840191600101612da5565b8035601081900b8114611a2f57600080fd5b600060208284031215612df257600080fd5b611a4f82612dce565b8035601c81900b8114611a2f57600080fd5b600060208284031215612e1f57600080fd5b611a4f82612dfb565b803568ffffffffffffffffff81168114611a2f57600080fd5b600060208284031215612e5357600080fd5b611a4f82612e28565b8035600781900b8114611a2f57600080fd5b600060208284031215612e8057600080fd5b611a4f82612e5c565b60006020808385031215612e9c57600080fd5b82356001600160401b03811115612eb257600080fd5b8301601f81018513612ec357600080fd5b8035612ed1611b7d82611afb565b81815260059190911b82018301908381019087831115612ef057600080fd5b928401925b82841015611bc657612f06846129b8565b82529284019290840190612ef5565b6020808252825182820181905260009190848201906040850190845b81811015611c12578351601a0b83529284019291840191600101612f31565b80358015158114611a2f57600080fd5b600060208284031215612f7257600080fd5b611a4f82612f50565b60006020808385031215612f8e57600080fd5b82356001600160401b03811115612fa457600080fd5b8301601f81018513612fb557600080fd5b8035612fc3611b7d82611afb565b81815260059190911b82018301908381019087831115612fe257600080fd5b928401925b82841015611bc657612ff884612f50565b82529284019290840190612fe7565b6020808252825182820181905260009190848201906040850190845b81811015611c12578351151583529284019291840191600101613023565b80356001600160d81b0381168114611a2f57600080fd5b6000602080838503121561306b57600080fd5b82356001600160401b0381111561308157600080fd5b8301601f8101851361309257600080fd5b80356130a0611b7d82611afb565b81815260059190911b820183019083810190878311156130bf57600080fd5b928401925b82841015611bc6576130d584613041565b825292840192908401906130c4565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160d81b031683529284019291840191600101613100565b8035601581900b8114611a2f57600080fd5b6000602080838503121561314a57600080fd5b82356001600160401b0381111561316057600080fd5b8301601f8101851361317157600080fd5b803561317f611b7d82611afb565b81815260059190911b8201830190838101908783111561319e57600080fd5b928401925b82841015611bc6576131b484613125565b825292840192908401906131a3565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160150b835292840192918401916001016131df565b80356001600160a81b0381168114611a2f57600080fd5b6000602080838503121561322857600080fd5b82356001600160401b0381111561323e57600080fd5b8301601f8101851361324f57600080fd5b803561325d611b7d82611afb565b81815260059190911b8201830190838101908783111561327c57600080fd5b928401925b82841015611bc657613292846131fe565b82529284019290840190613281565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160a81b0316835292840192918401916001016132bd565b8035600081900b8114611a2f57600080fd5b60006020828403121561330657600080fd5b611a4f826132e2565b6000602080838503121561332257600080fd5b82356001600160401b0381111561333857600080fd5b8301601f8101851361334957600080fd5b8035613357611b7d82611afb565b81815260059190911b8201830190838101908783111561337657600080fd5b928401925b82841015611bc65761338c84611caf565b8252928401929084019061337b565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160110b835292840192918401916001016133b7565b600060208083850312156133e957600080fd5b82356001600160401b038111156133ff57600080fd5b8301601f8101851361341057600080fd5b803561341e611b7d82611afb565b81815260059190911b8201830190838101908783111561343d57600080fd5b928401925b82841015611bc6576134538461204a565b82529284019290840190613442565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160160b8352928401929184019160010161347e565b803565ffffffffffff81168114611a2f57600080fd5b600060208083850312156134c657600080fd5b82356001600160401b038111156134dc57600080fd5b8301601f810185136134ed57600080fd5b80356134fb611b7d82611afb565b81815260059190911b8201830190838101908783111561351a57600080fd5b928401925b82841015611bc6576135308461349d565b8252928401929084019061351f565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835165ffffffffffff168352928401929184019160010161355b565b803563ffffffff81168114611a2f57600080fd5b600060208083850312156135a657600080fd5b82356001600160401b038111156135bc57600080fd5b8301601f810185136135cd57600080fd5b80356135db611b7d82611afb565b81815260059190911b820183019083810190878311156135fa57600080fd5b928401925b82841015611bc6576136108461357f565b825292840192908401906135ff565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835163ffffffff168352928401929184019160010161363b565b8035600981900b8114611a2f57600080fd5b60006020828403121561368157600080fd5b611a4f8261365d565b8035601781900b8114611a2f57600080fd5b6000602082840312156136ae57600080fd5b611a4f8261368a565b6000602082840312156136c957600080fd5b611a4f8261215e565b8035600681900b8114611a2f57600080fd5b600060208083850312156136f757600080fd5b82356001600160401b0381111561370d57600080fd5b8301601f8101851361371e57600080fd5b803561372c611b7d82611afb565b81815260059190911b8201830190838101908783111561374b57600080fd5b928401925b82841015611bc657613761846136d2565b82529284019290840190613750565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160060b8352928401929184019160010161378c565b6000602082840312156137bd57600080fd5b611a4f826136d2565b8035600f81900b8114611a2f57600080fd5b600060208083850312156137eb57600080fd5b82356001600160401b0381111561380157600080fd5b8301601f8101851361381257600080fd5b8035613820611b7d82611afb565b81815260059190911b8201830190838101908783111561383f57600080fd5b928401925b82841015611bc657613855846137c6565b82529284019290840190613844565b6020808252825182820181905260009190848201906040850190845b81811015611c12578351600f0b83529284019291840191600101613880565b600060208083850312156138b257600080fd5b82356001600160401b038111156138c857600080fd5b8301601f810185136138d957600080fd5b80356138e7611b7d82611afb565b81815260059190911b8201830190838101908783111561390657600080fd5b928401925b82841015611bc65761391c84611cdc565b8252928401929084019061390b565b6020808252825182820181905260009190848201906040850190845b81811015611c12578351601e0b83529284019291840191600101613947565b803569ffffffffffffffffffff81168114611a2f57600080fd5b6000602080838503121561399357600080fd5b82356001600160401b038111156139a957600080fd5b8301601f810185136139ba57600080fd5b80356139c8611b7d82611afb565b81815260059190911b820183019083810190878311156139e757600080fd5b928401925b82841015611bc6576139fd84613966565b825292840192908401906139ec565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835169ffffffffffffffffffff1683529284019291840191600101613a28565b80356001600160681b0381168114611a2f57600080fd5b600060208284031215613a7957600080fd5b611a4f82613a50565b80356001600160701b0381168114611a2f57600080fd5b60006020808385031215613aac57600080fd5b82356001600160401b03811115613ac257600080fd5b8301601f81018513613ad357600080fd5b8035613ae1611b7d82611afb565b81815260059190911b82018301908381019087831115613b0057600080fd5b928401925b82841015611bc657613b1684613a82565b82529284019290840190613b05565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160701b031683529284019291840191600101613b41565b600060208284031215613b7857600080fd5b611a4f82613041565b803564ffffffffff81168114611a2f57600080fd5b60006020808385031215613ba957600080fd5b82356001600160401b03811115613bbf57600080fd5b8301601f81018513613bd057600080fd5b8035613bde611b7d82611afb565b81815260059190911b82018301908381019087831115613bfd57600080fd5b928401925b82841015611bc657613c1384613b81565b82529284019290840190613c02565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835164ffffffffff1683529284019291840191600101613c3e565b8035601b81900b8114611a2f57600080fd5b600060208284031215613c8557600080fd5b611a4f82613c61565b803562ffffff81168114611a2f57600080fd5b60006020808385031215613cb457600080fd5b82356001600160401b03811115613cca57600080fd5b8301601f81018513613cdb57600080fd5b8035613ce9611b7d82611afb565b81815260059190911b82018301908381019087831115613d0857600080fd5b928401925b82841015611bc657613d1e84613c8e565b82529284019290840190613d0d565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835162ffffff1683529284019291840191600101613d49565b803561ffff81168114611a2f57600080fd5b60006020808385031215613d8f57600080fd5b82356001600160401b03811115613da557600080fd5b8301601f81018513613db657600080fd5b8035613dc4611b7d82611afb565b81815260059190911b82018301908381019087831115613de357600080fd5b928401925b82841015611bc657613df984613d6a565b82529284019290840190613de8565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835161ffff1683529284019291840191600101613e24565b8035600281900b8114611a2f57600080fd5b600060208284031215613e6857600080fd5b611a4f82613e44565b80356001600160901b0381168114611a2f57600080fd5b60006020808385031215613e9b57600080fd5b82356001600160401b03811115613eb157600080fd5b8301601f81018513613ec257600080fd5b8035613ed0611b7d82611afb565b81815260059190911b82018301908381019087831115613eef57600080fd5b928401925b82841015611bc657613f0584613e71565b82529284019290840190613ef4565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160901b031683529284019291840191600101613f30565b60006020808385031215613f6857600080fd5b82356001600160401b0380821115613f7f57600080fd5b818501915085601f830112613f9357600080fd5b8135613fa1611b7d82611afb565b81815260059190911b83018401908481019088831115613fc057600080fd5b8585015b83811015613ff857803585811115613fdc5760008081fd5b613fea8b89838a0101611d36565b845250918601918601613fc4565b5098975050505050505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561405a57603f19888603018452614048858351611de1565b9450928501929085019060010161402c565b5092979650505050505050565b6000602080838503121561407a57600080fd5b82356001600160401b0381111561409057600080fd5b8301601f810185136140a157600080fd5b80356140af611b7d82611afb565b81815260059190911b820183019083810190878311156140ce57600080fd5b928401925b82841015611bc6576140e484611e3a565b825292840192908401906140d3565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160981b03168352928401929184019160010161410f565b80356001600160b01b0381168114611a2f57600080fd5b60006020828403121561415d57600080fd5b611a4f82614134565b6000602080838503121561417957600080fd5b82356001600160401b0381111561418f57600080fd5b8301601f810185136141a057600080fd5b80356141ae611b7d82611afb565b81815260059190911b820183019083810190878311156141cd57600080fd5b928401925b82841015611bc6576141e384612dce565b825292840192908401906141d2565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160100b8352928401929184019160010161420e565b60006020828403121561423f57600080fd5b611a4f826137c6565b6000602080838503121561425b57600080fd5b82356001600160401b0381111561427157600080fd5b8301601f8101851361428257600080fd5b8035614290611b7d82611afb565b81815260059190911b820183019083810190878311156142af57600080fd5b928401925b82841015611bc6576142c5846127c3565b825292840192908401906142b4565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160190b835292840192918401916001016142f0565b80356001600160881b0381168114611a2f57600080fd5b6000602080838503121561433957600080fd5b82356001600160401b0381111561434f57600080fd5b8301601f8101851361436057600080fd5b803561436e611b7d82611afb565b81815260059190911b8201830190838101908783111561438d57600080fd5b928401925b82841015611bc6576143a38461430f565b82529284019290840190614392565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160881b0316835292840192918401916001016143ce565b6000602080838503121561440657600080fd5b82356001600160401b0381111561441c57600080fd5b8301601f8101851361442d57600080fd5b803561443b611b7d82611afb565b81815260059190911b8201830190838101908783111561445a57600080fd5b928401925b82841015611bc65761447084613c61565b8252928401929084019061445f565b6020808252825182820181905260009190848201906040850190845b81811015611c12578351601b0b8352928401929184019160010161449b565b600060208083850312156144cd57600080fd5b82356001600160401b03808211156144e457600080fd5b818501915085601f8301126144f857600080fd5b8135614506611b7d82611afb565b81815260059190911b8301840190848101908883111561452557600080fd5b8585015b83811015613ff8578035858111156145415760008081fd5b61454f8b89838a0101611d36565b845250918601918601614529565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561405a57603f198886030184526145a0858351611de1565b94509285019290850190600101614584565b6000602082840312156145c457600080fd5b611a4f82613125565b8035601881900b8114611a2f57600080fd5b6000602082840312156145f157600080fd5b611a4f826145cd565b60006020828403121561460c57600080fd5b611a4f826129e5565b80356001600160b81b0381168114611a2f57600080fd5b60006020828403121561463e57600080fd5b611a4f82614615565b60006020828403121561465957600080fd5b611a4f826126a2565b6000602080838503121561467557600080fd5b82356001600160401b0381111561468b57600080fd5b8301601f8101851361469c57600080fd5b80356146aa611b7d82611afb565b81815260059190911b820183019083810190878311156146c957600080fd5b928401925b82841015611bc6576146df84614134565b825292840192908401906146ce565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160b01b03168352928401929184019160010161470a565b60006020828403121561474157600080fd5b611a4f82612b4c565b60006020828403121561475c57600080fd5b611a4f82611b1e565b6000602080838503121561477857600080fd5b82356001600160401b0381111561478e57600080fd5b8301601f8101851361479f57600080fd5b80356147ad611b7d82611afb565b81815260059190911b820183019083810190878311156147cc57600080fd5b928401925b82841015611bc6576147e284611a88565b825292840192908401906147d1565b6020808252825182820181905260009190848201906040850190845b81811015611c12578351600c0b8352928401929184019160010161480d565b60006020828403121561483e57600080fd5b611a4f82613d6a565b6000602080838503121561485a57600080fd5b82356001600160401b0381111561487057600080fd5b8301601f8101851361488157600080fd5b803561488f611b7d82611afb565b81815260059190911b820183019083810190878311156148ae57600080fd5b928401925b82841015611bc6576148c484612e28565b825292840192908401906148b3565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835168ffffffffffffffffff16835292840192918401916001016148ef565b8035600e81900b8114611a2f57600080fd5b6000602080838503121561493b57600080fd5b82356001600160401b0381111561495157600080fd5b8301601f8101851361496257600080fd5b8035614970611b7d82611afb565b81815260059190911b8201830190838101908783111561498f57600080fd5b928401925b82841015611bc6576149a584614916565b82529284019290840190614994565b6020808252825182820181905260009190848201906040850190845b81811015611c12578351600e0b835292840192918401916001016149d0565b8035600581900b8114611a2f57600080fd5b600060208284031215614a1357600080fd5b611a4f826149ef565b600060208284031215614a2e57600080fd5b611a4f82613a82565b60006020808385031215614a4a57600080fd5b82356001600160401b03811115614a6057600080fd5b8301601f81018513614a7157600080fd5b8035614a7f611b7d82611afb565b81815260059190911b82018301908381019087831115614a9e57600080fd5b928401925b82841015611bc657614ab484613a50565b82529284019290840190614aa3565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160681b031683529284019291840191600101614adf565b600060208284031215614b1657600080fd5b611a4f82614916565b600060208284031215614b3157600080fd5b611a4f826131fe565b600060208284031215614b4c57600080fd5b611a4f82611e6c565b600060208284031215614b6757600080fd5b611a4f8261430f565b600060208284031215614b8257600080fd5b611a4f826124f7565b60006020808385031215614b9e57600080fd5b82356001600160401b03811115614bb457600080fd5b8301601f81018513614bc557600080fd5b8035614bd3611b7d82611afb565b81815260059190911b82018301908381019087831115614bf257600080fd5b928401925b82841015611bc657614c08846149ef565b82529284019290840190614bf7565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160050b83529284019291840191600101614c33565b600060208284031215614c6457600080fd5b611a4f82611f72565b60006020808385031215614c8057600080fd5b82356001600160401b03811115614c9657600080fd5b8301601f81018513614ca757600080fd5b8035614cb5611b7d82611afb565b81815260059190911b82018301908381019087831115614cd457600080fd5b928401925b82841015611bc657614cea84611f45565b82529284019290840190614cd9565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160120b83529284019291840191600101614d15565b600060208284031215614d4657600080fd5b611a4f82612ce2565b600060208284031215614d6157600080fd5b611a4f82612304565b60006020808385031215614d7d57600080fd5b82356001600160401b03811115614d9357600080fd5b8301601f81018513614da457600080fd5b8035614db2611b7d82611afb565b81815260059190911b82018301908381019087831115614dd157600080fd5b928401925b82841015611bc657614de7846145cd565b82529284019290840190614dd6565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160180b83529284019291840191600101614e12565b600060208284031215614e4357600080fd5b611a4f8261349d565b80356001600160e01b0381168114611a2f57600080fd5b600060208284031215614e7557600080fd5b611a4f82614e4c565b60006020808385031215614e9157600080fd5b82356001600160401b03811115614ea757600080fd5b8301601f81018513614eb857600080fd5b8035614ec6611b7d82611afb565b81815260059190911b82018301908381019087831115614ee557600080fd5b928401925b82841015611bc657614efb8461365d565b82529284019290840190614eea565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160090b83529284019291840191600101614f26565b60006020808385031215614f5857600080fd5b82356001600160401b03811115614f6e57600080fd5b8301601f81018513614f7f57600080fd5b8035614f8d611b7d82611afb565b81815260059190911b82018301908381019087831115614fac57600080fd5b928401925b82841015611bc657614fc284614e4c565b82529284019290840190614fb1565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160e01b031683529284019291840191600101614fed565b80356001600160d01b0381168114611a2f57600080fd5b60006020828403121561503b57600080fd5b611a4f82615012565b60006020828403121561505657600080fd5b611a4f8261357f565b63ffffffff841681526001600160401b038316602082015260606040820152600061508d6060830184611de1565b95945050505050565b6000602082840312156150a857600080fd5b611a4f82613966565b6000602082840312156150c357600080fd5b611a4f826128d4565b80356001600160e81b0381168114611a2f57600080fd5b6000602082840312156150f557600080fd5b611a4f826150cc565b6000602080838503121561511157600080fd5b82356001600160401b0381111561512757600080fd5b8301601f8101851361513857600080fd5b8035615146611b7d82611afb565b81815260059190911b8201830190838101908783111561516557600080fd5b928401925b82841015611bc65761517b84612dfb565b8252928401929084019061516a565b6020808252825182820181905260009190848201906040850190845b81811015611c12578351601c0b835292840192918401916001016151a6565b6000602082840312156151d757600080fd5b611a4f826127f0565b8035600881900b8114611a2f57600080fd5b6000602080838503121561520557600080fd5b82356001600160401b0381111561521b57600080fd5b8301601f8101851361522c57600080fd5b803561523a611b7d82611afb565b81815260059190911b8201830190838101908783111561525957600080fd5b928401925b82841015611bc65761526f846151e0565b8252928401929084019061525e565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160080b8352928401929184019160010161529a565b600060208083850312156152cc57600080fd5b82356001600160401b038111156152e257600080fd5b8301601f810185136152f357600080fd5b8035615301611b7d82611afb565b81815260059190911b8201830190838101908783111561532057600080fd5b928401925b82841015611bc657615336846132e2565b82529284019290840190615325565b602080825282518282018190526000919084820190604085019084805b82811015615380578451820b84529385019392850192600101615362565b5091979650505050505050565b600060208083850312156153a057600080fd5b82356001600160401b038111156153b657600080fd5b8301601f810185136153c757600080fd5b80356153d5611b7d82611afb565b81815260059190911b820183019083810190878311156153f457600080fd5b928401925b82841015611bc65761540a8461277b565b825292840192908401906153f9565b6020808252825182820181905260009190848201906040850190845b81811015611c12578351601d0b83529284019291840191600101615435565b6000602080838503121561546757600080fd5b82356001600160401b0381111561547d57600080fd5b8301601f8101851361548e57600080fd5b803561549c611b7d82611afb565b81815260059190911b820183019083810190878311156154bb57600080fd5b928401925b82841015611bc6576154d184611c82565b825292840192908401906154c0565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160040b835292840192918401916001016154fc565b6000602080838503121561552e57600080fd5b82356001600160401b0381111561554457600080fd5b8301601f8101851361555557600080fd5b8035615563611b7d82611afb565b81815260059190911b8201830190838101908783111561558257600080fd5b928401925b82841015611bc65761559884613e44565b82529284019290840190615587565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160020b835292840192918401916001016155c3565b600060208083850312156155f557600080fd5b82356001600160401b0381111561560b57600080fd5b8301601f8101851361561c57600080fd5b803561562a611b7d82611afb565b81815260059190911b8201830190838101908783111561564957600080fd5b928401925b82841015611bc65761565f846150cc565b8252928401929084019061564e565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160e81b03168352928401929184019160010161568a565b600060208083850312156156c257600080fd5b82356001600160401b038111156156d857600080fd5b8301601f810185136156e957600080fd5b80356156f7611b7d82611afb565b81815260059190911b8201830190838101908783111561571657600080fd5b928401925b82841015611bc65761572c8461368a565b8252928401929084019061571b565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160170b83529284019291840191600101615757565b60006020828403121561578857600080fd5b611a4f826151e0565b600060208083850312156157a457600080fd5b82356001600160401b038111156157ba57600080fd5b8301601f810185136157cb57600080fd5b80356157d9611b7d82611afb565b81815260059190911b820183019083810190878311156157f857600080fd5b928401925b82841015611bc65761580e84612e5c565b825292840192908401906157fd565b6020808252825182820181905260009190848201906040850190845b81811015611c1257835160070b83529284019291840191600101615839565b60006020828403121561586a57600080fd5b611a4f82613e71565b60006020828403121561588557600080fd5b611a4f82613c8e565b600060208083850312156158a157600080fd5b82356001600160401b038111156158b757600080fd5b8301601f810185136158c857600080fd5b80356158d6611b7d82611afb565b81815260059190911b820183019083810190878311156158f557600080fd5b928401925b82841015611bc65761590b84611a18565b825292840192908401906158fa565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160801b031683529284019291840191600101615936565b6000602080838503121561596e57600080fd5b82356001600160401b0381111561598457600080fd5b8301601f8101851361599557600080fd5b80356159a3611b7d82611afb565b81815260059190911b820183019083810190878311156159c257600080fd5b928401925b82841015611bc6576159d884615012565b825292840192908401906159c7565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160d01b031683529284019291840191600101615a03565b60006020808385031215615a3b57600080fd5b82356001600160401b03811115615a5157600080fd5b8301601f81018513615a6257600080fd5b8035615a70611b7d82611afb565b81815260059190911b82018301908381019087831115615a8f57600080fd5b928401925b82841015611bc657615aa584614615565b82529284019290840190615a94565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160b81b031683529284019291840191600101615ad0565b602081526000825160406020840152615b116060840182611de1565b9050602084015160408401528091505092915050565b60006020808385031215615b3a57600080fd5b82356001600160401b03811115615b5057600080fd5b8301601f81018513615b6157600080fd5b8035615b6f611b7d82611afb565b81815260059190911b82018301908381019087831115615b8e57600080fd5b928401925b82841015611bc657615ba484611c1e565b82529284019290840190615b93565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160781b031683529284019291840191600101615bcf565b60006020808385031215615c0757600080fd5b82356001600160401b03811115615c1d57600080fd5b8301601f81018513615c2e57600080fd5b8035615c3c611b7d82611afb565b81815260059190911b82018301908381019087831115615c5b57600080fd5b928401925b82841015611bc657615c7184611a56565b82529284019290840190615c60565b6020808252825182820181905260009190848201906040850190845b81811015611c125783516001600160601b031683529284019291840191600101615c9c565b600060208284031215615cd357600080fd5b611a4f82613b81565b634e487b7160e01b600052601160045260246000fd5b600081810b9083900b01607f8113607f1982121715615d1357615d13615cdc565b92915050565b8082018281126000831280158216821582161715615d3957615d39615cdc565b505092915050565b63ffffffff828116828216039080821115615d5e57615d5e615cdc565b5092915050565b600481810b9083900b01647fffffffff8113647fffffffff1982121715615d1357615d13615cdc565b600181811c90821680615da257607f821691505b602082108103615dc257634e487b7160e01b600052602260045260246000fd5b5091905056fea2646970667358221220a5b87b6881e1a012e4a59d55c230cfcf3a2342b83efdbfb3c5f5cfa532fc854864736f6c63430008120033"; + private static IntegrationTestEnv testEnv; + private static FileId fileId; + private static ContractId contractId; + + @BeforeAll + public static void beforeAll() throws Exception { + testEnv = new IntegrationTestEnv(1); + + var response = new FileCreateTransaction().setKeys(testEnv.operatorKey).execute(testEnv.client); + + fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + new FileAppendTransaction() + .setFileId(fileId) + .setContents(SMART_CONTRACT_BYTECODE) + .setMaxChunks(31) + .execute(testEnv.client); + + response = new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(1500000) + .setConstructorParameters(new ContractFunctionParameters()) + .setBytecodeFileId(fileId) + .execute(testEnv.client); + + contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); + } + + @AfterAll + public static void afterAll() throws Exception { + new ContractDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setContractId(contractId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new FileDeleteTransaction().setFileId(fileId).execute(testEnv.client).getReceipt(testEnv.client); + + testEnv.close(); + } + + // so we don't get "network is busy" error + @AfterEach + public void afterEach() throws InterruptedException { + Thread.sleep(150); + } + + @Test + @DisplayName("Can receive uint8 min value from contract call") + void canCallContractFunctionUint8Min() throws Exception { + var gas = new MirrorNodeContractEstimateGasQuery() + .setContractId(contractId) + .setFunction("returnUint8", new ContractFunctionParameters().addUint8((byte) 0x0)) + .execute(testEnv.client); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(gas) + .setFunction("returnUint8", new ContractFunctionParameters().addUint8((byte) 0x0)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint8(0)).isEqualTo((byte) 0x0); + } + + @Test + @DisplayName("Can receive uint8 max value from contract call") + void canCallContractFunctionUint8Max() throws Exception { + int uint8Max = 255; + byte uint8MaxByte = (byte) uint8Max; + + var gas = new MirrorNodeContractEstimateGasQuery() + .setContractId(contractId) + .setFunction("returnUint8", new ContractFunctionParameters().addUint8(uint8MaxByte)) + .execute(testEnv.client); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(gas) + .setFunction("returnUint8", new ContractFunctionParameters().addUint8(uint8MaxByte)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var uint8MaxFromResponse = Byte.toUnsignedInt(response.getUint8(0)); + + assertThat(uint8MaxFromResponse).isEqualTo(uint8Max); + } + + @Test + @DisplayName("Can receive uint8 array value from contract call") + void canCallContractFunctionUint8Array() throws Exception { + byte uint8MinByte = (byte) 0x0; + int uint8Max = 255; + byte uint8MaxByte = (byte) uint8Max; + byte[] uint8Array = {uint8MinByte, uint8MaxByte}; + + var gas = new MirrorNodeContractEstimateGasQuery() + .setContractId(contractId) + .setFunction("returnUint8Arr", new ContractFunctionParameters().addUint8Array(uint8Array)) + .execute(testEnv.client); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(gas) + .setFunction("returnUint8Arr", new ContractFunctionParameters().addUint8Array(uint8Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (int[]) response.getResult("(uint8[])").get(0); + + assertThat(responseResult[0]).isEqualTo(uint8MinByte); + assertThat(responseResult[1]).isEqualTo(uint8Max); + } + + @Test + @DisplayName("Can receive uint16 min value from contract call") + void canCallContractFunctionUint16Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint16", new ContractFunctionParameters().addUint16(0)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint32(0)).isEqualTo(0); + } + + @Test + @DisplayName("Can receive uint16 max value from contract call") + void canCallContractFunctionUint16Max() throws Exception { + var uint16Max = "65535"; + int uint16MaxInt = Integer.parseUnsignedInt(uint16Max); + + var gas = new MirrorNodeContractEstimateGasQuery() + .setContractId(contractId) + .setFunction("returnUint16", new ContractFunctionParameters().addUint16(uint16MaxInt)) + .execute(testEnv.client); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(gas) + .setFunction("returnUint16", new ContractFunctionParameters().addUint16(uint16MaxInt)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var uint16MaxIntFromResponse = Integer.toUnsignedString(response.getUint32(0)); + + assertThat(uint16MaxIntFromResponse).isEqualTo(uint16Max); + } + + @Test + @DisplayName("Can receive uint16 array value from contract call") + void canCallContractFunctionUint16Array() throws Exception { + int uint16MinInt = 0; + var uint16Max = "65535"; + int uint16MaxInt = Integer.parseUnsignedInt(uint16Max); + int[] uint16Array = {uint16MinInt, uint16MaxInt}; + + var gas = new MirrorNodeContractEstimateGasQuery() + .setContractId(contractId) + .setFunction("returnUint16Arr", new ContractFunctionParameters().addUint16Array(uint16Array)) + .execute(testEnv.client); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(gas) + .setFunction("returnUint16Arr", new ContractFunctionParameters().addUint16Array(uint16Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (int[]) response.getResult("(uint16[])").get(0); + + assertThat(responseResult).isEqualTo(uint16Array); + } + + @Test + @DisplayName("Can receive uint24 min value from contract call") + void canCallContractFunctionUint24Min() throws Exception { + var gas = new MirrorNodeContractEstimateGasQuery() + .setContractId(contractId) + .setFunction("returnUint24", new ContractFunctionParameters().addUint24(0)) + .execute(testEnv.client); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(gas) + .setFunction("returnUint24", new ContractFunctionParameters().addUint24(0)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint32(0)).isEqualTo(0); + } + + @Test + @DisplayName("Can receive uint24 max value from contract call") + void canCallContractFunctionUint24Max() throws Exception { + var uint24Max = "16777215"; + int uint24MaxInt = Integer.parseUnsignedInt(uint24Max); + + var gas = new MirrorNodeContractEstimateGasQuery() + .setContractId(contractId) + .setFunction("returnUint24", new ContractFunctionParameters().addUint24(uint24MaxInt)) + .execute(testEnv.client); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(gas) + .setFunction("returnUint24", new ContractFunctionParameters().addUint24(uint24MaxInt)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var uint24MaxIntFromResponse = Integer.toUnsignedString(response.getUint32(0)); + + assertThat(uint24MaxIntFromResponse).isEqualTo(uint24Max); + } + + @Test + @DisplayName("Can receive uint24 array value from contract call") + void canCallContractFunctionUint24Array() throws Exception { + int uint24MinInt = 0; + var uint24Max = "16777215"; + int uint24MaxInt = Integer.parseUnsignedInt(uint24Max); + int[] uint24Array = {uint24MinInt, uint24MaxInt}; + + var gas = new MirrorNodeContractEstimateGasQuery() + .setContractId(contractId) + .setFunction("returnUint24Arr", new ContractFunctionParameters().addUint24Array(uint24Array)) + .execute(testEnv.client); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(gas) + .setFunction("returnUint24Arr", new ContractFunctionParameters().addUint24Array(uint24Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (int[]) response.getResult("(uint24[])").get(0); + + assertThat(responseResult).isEqualTo(uint24Array); + } + + @Test + @DisplayName("Can receive uint32 min value from contract call") + void canCallContractFunctionUint32Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint32", new ContractFunctionParameters().addUint32(0)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint32(0)).isEqualTo(0); + } + + @Test + @DisplayName("Can receive uint32 max value from contract call") + void canCallContractFunctionUint32Max() throws Exception { + var uint32Max = "4294967295"; + int uint32MaxInt = Integer.parseUnsignedInt(uint32Max); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint32", new ContractFunctionParameters().addUint32(uint32MaxInt)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var uint32MaxIntFromResponse = Integer.toUnsignedString(response.getUint32(0)); + + assertThat(uint32MaxIntFromResponse).isEqualTo(uint32Max); + } + + @Test + @DisplayName("Can receive uint32 array value from contract call") + void canCallContractFunctionUint32Array() throws Exception { + int uint32MinInt = 0; + var uint32Max = "4294967295"; + int uint32MaxInt = Integer.parseUnsignedInt(uint32Max); + int[] uint32Array = {uint32MinInt, uint32MaxInt}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint32Arr", new ContractFunctionParameters().addUint32Array(uint32Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (long[]) response.getResult("(uint32[])").get(0); + + assertThat(responseResult[0]).isEqualTo(uint32MinInt); + assertThat(responseResult[1]).isEqualTo(Long.parseUnsignedLong(uint32Max)); + } + + @Test + @DisplayName("Can receive uint40 min value from contract call") + void canCallContractFunctionUint40Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint40", new ContractFunctionParameters().addUint40(0)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint64(0)).isEqualTo(0); + } + + @Test + @DisplayName("Can receive uint40 max value from contract call") + void canCallContractFunctionUint40Max() throws Exception { + var uint40Max = "109951162777"; + long uint40MaxLong = Long.parseUnsignedLong(uint40Max); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint40", new ContractFunctionParameters().addUint40(uint40MaxLong)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var uint64MaxLongFromResponse = Long.toUnsignedString(response.getUint64(0)); + + assertThat(uint64MaxLongFromResponse).isEqualTo(uint40Max); + } + + @Test + @DisplayName("Can receive uint40 array value from contract call") + void canCallContractFunctionUint40Array() throws Exception { + long uint40MinLong = 0; + var uint40Max = "109951162777"; + long uint40MaxLong = Long.parseUnsignedLong(uint40Max); + long[] uint40Array = {uint40MinLong, uint40MaxLong}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint40Arr", new ContractFunctionParameters().addUint40Array(uint40Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (long[]) response.getResult("(uint40[])").get(0); + + assertThat(responseResult).isEqualTo(uint40Array); + } + + @Test + @DisplayName("Can receive uint48 min value from contract call") + void canCallContractFunctionUint48Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint48", new ContractFunctionParameters().addUint48(0)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint64(0)).isEqualTo(0); + } + + @Test + @DisplayName("Can receive uint48 max value from contract call") + void canCallContractFunctionUint48Max() throws Exception { + var uint48Max = "281474976710655"; + long uint48MaxLong = Long.parseUnsignedLong(uint48Max); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint48", new ContractFunctionParameters().addUint48(uint48MaxLong)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var uint64MaxLongFromResponse = Long.toUnsignedString(response.getUint64(0)); + + assertThat(uint64MaxLongFromResponse).isEqualTo(uint48Max); + } + + @Test + @DisplayName("Can receive uint48 array value from contract call") + void canCallContractFunctionUint48Array() throws Exception { + long uint48MinLong = 0; + var uint48Max = "281474976710655"; + long uint48MaxLong = Long.parseUnsignedLong(uint48Max); + long[] uint48Array = {uint48MinLong, uint48MaxLong}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint48Arr", new ContractFunctionParameters().addUint48Array(uint48Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (long[]) response.getResult("(uint48[])").get(0); + + assertThat(responseResult).isEqualTo(uint48Array); + } + + @Test + @DisplayName("Can receive uint56 min value from contract call") + void canCallContractFunctionUint56Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint56", new ContractFunctionParameters().addUint56(0)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint64(0)).isEqualTo(0); + } + + @Test + @DisplayName("Can receive uint56 max value from contract call") + void canCallContractFunctionUint56Max() throws Exception { + var uint56Max = "72057594037927935"; + long uint56MaxLong = Long.parseUnsignedLong(uint56Max); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint56", new ContractFunctionParameters().addUint56(uint56MaxLong)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var uint64MaxLongFromResponse = Long.toUnsignedString(response.getUint64(0)); + + assertThat(uint64MaxLongFromResponse).isEqualTo(uint56Max); + } + + @Test + @DisplayName("Can receive uint56 array value from contract call") + void canCallContractFunctionUint56Array() throws Exception { + long uint56MinLong = 0; + var uint56Max = "72057594037927935"; + long uint56MaxLong = Long.parseUnsignedLong(uint56Max); + long[] uint56Array = {uint56MinLong, uint56MaxLong}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint56Arr", new ContractFunctionParameters().addUint56Array(uint56Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (long[]) response.getResult("(uint56[])").get(0); + + assertThat(responseResult).isEqualTo(uint56Array); + } + + @Test + @DisplayName("Can receive uint64 min value from contract call") + void canCallContractFunctionUint64Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint64", new ContractFunctionParameters().addUint64(0)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint64(0)).isEqualTo(0); + } + + @Test + @DisplayName("Can receive uint64 max value from contract call") + void canCallContractFunctionUint64Max() throws Exception { + var uint64Max = "9223372036854775807"; + long uint64MaxLong = Long.parseUnsignedLong(uint64Max); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint64", new ContractFunctionParameters().addUint64(uint64MaxLong)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var uint64MaxLongFromResponse = Long.toUnsignedString(response.getUint64(0)); + + assertThat(uint64MaxLongFromResponse).isEqualTo(uint64Max); + } + + @Test + @DisplayName("Can receive uint64 array value from contract call") + void canCallContractFunctionUint64Array() throws Exception { + long uint64MinLong = 0; + var uint64Max = "9223372036854775807"; + long uint64MaxLong = Long.parseUnsignedLong(uint64Max); + long[] uint64Array = {uint64MinLong, uint64MaxLong}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint64Arr", new ContractFunctionParameters().addUint64Array(uint64Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(uint64[])").get(0); + + assertThat(responseResult[0]).isEqualTo(uint64MinLong); + assertThat(responseResult[1]).isEqualTo(Long.parseUnsignedLong(uint64Max)); + } + + @Test + @DisplayName("Can receive uint72 min value from contract call") + void canCallContractFunctionUint72Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint72", new ContractFunctionParameters().addUint72(BigInteger.ZERO)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); + } + + @Test + @DisplayName("Can receive uint72 max value from contract call") + void canCallContractFunctionUint72Max() throws Exception { + BigInteger uint72Max = new BigInteger("4722366482869645213695"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint72", new ContractFunctionParameters().addUint72(uint72Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(uint72Max); + } + + @Test + @DisplayName("Can receive uint72 array value from contract call") + void canCallContractFunctionUint72Array() throws Exception { + BigInteger uint72Min = BigInteger.ZERO; + BigInteger uint72Max = new BigInteger("4722366482869645213695"); + BigInteger[] uint72Array = {uint72Min, uint72Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint72Arr", new ContractFunctionParameters().addUint72Array(uint72Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(uint72[])").get(0); + + assertThat(responseResult).isEqualTo(uint72Array); + } + + @Test + @DisplayName("Can receive uint80 min value from contract call") + void canCallContractFunctionUint80Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint80", new ContractFunctionParameters().addUint80(BigInteger.ZERO)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); + } + + @Test + @DisplayName("Can receive uint80 max value from contract call") + void canCallContractFunctionUint80Max() throws Exception { + BigInteger uint80Max = new BigInteger("1208925819614629174706175"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint80", new ContractFunctionParameters().addUint80(uint80Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(uint80Max); + } + + @Test + @DisplayName("Can receive uint80 array value from contract call") + void canCallContractFunctionUint80Array() throws Exception { + BigInteger uint80Min = BigInteger.ZERO; + BigInteger uint80Max = new BigInteger("1208925819614629174706175"); + BigInteger[] uint80Array = {uint80Min, uint80Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint80Arr", new ContractFunctionParameters().addUint80Array(uint80Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(uint80[])").get(0); + + assertThat(responseResult).isEqualTo(uint80Array); + } + + @Test + @DisplayName("Can receive uint88 min value from contract call") + void canCallContractFunctionUint88Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint88", new ContractFunctionParameters().addUint88(BigInteger.ZERO)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); + } + + @Test + @DisplayName("Can receive uint88 max value from contract call") + void canCallContractFunctionUint88Max() throws Exception { + BigInteger uint88Max = new BigInteger("309485009821345068724781055"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint88", new ContractFunctionParameters().addUint88(uint88Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(uint88Max); + } + + @Test + @DisplayName("Can receive uint88 array value from contract call") + void canCallContractFunctionUint88Array() throws Exception { + BigInteger uint88Min = BigInteger.ZERO; + BigInteger uint88Max = new BigInteger("309485009821345068724781055"); + BigInteger[] uint88Array = {uint88Min, uint88Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint88Arr", new ContractFunctionParameters().addUint88Array(uint88Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(uint88[])").get(0); + + assertThat(responseResult).isEqualTo(uint88Array); + } + + @Test + @DisplayName("Can receive uint96 min value from contract call") + void canCallContractFunctionUint96Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint96", new ContractFunctionParameters().addUint96(BigInteger.ZERO)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); + } + + @Test + @DisplayName("Can receive uint96 max value from contract call") + void canCallContractFunctionUint96Max() throws Exception { + BigInteger uint96Max = new BigInteger("79228162514264337593543950335"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint96", new ContractFunctionParameters().addUint96(uint96Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(uint96Max); + } + + @Test + @DisplayName("Can receive uint96 array value from contract call") + void canCallContractFunctionUint96Array() throws Exception { + BigInteger uint96Min = BigInteger.ZERO; + BigInteger uint96Max = new BigInteger("79228162514264337593543950335"); + BigInteger[] uint96Array = {uint96Min, uint96Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint96Arr", new ContractFunctionParameters().addUint96Array(uint96Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(uint96[])").get(0); + + assertThat(responseResult).isEqualTo(uint96Array); + } + + @Test + @DisplayName("Can receive uint104 min value from contract call") + void canCallContractFunctionUint104Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint104", new ContractFunctionParameters().addUint104(BigInteger.ZERO)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); + } + + @Test + @DisplayName("Can receive uint104 max value from contract call") + void canCallContractFunctionUint104Max() throws Exception { + BigInteger uint104Max = new BigInteger("20282409603651670423947251286015"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint104", new ContractFunctionParameters().addUint104(uint104Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(uint104Max); + } + + @Test + @DisplayName("Can receive uint104 array value from contract call") + void canCallContractFunctionUint104Array() throws Exception { + BigInteger uint104Min = BigInteger.ZERO; + BigInteger uint104Max = new BigInteger("20282409603651670423947251286015"); + BigInteger[] uint104Array = {uint104Min, uint104Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint104Arr", new ContractFunctionParameters().addUint104Array(uint104Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(uint104[])").get(0); + + assertThat(responseResult).isEqualTo(uint104Array); + } + + @Test + @DisplayName("Can receive uint112 min value from contract call") + void canCallContractFunctionUint112Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint112", new ContractFunctionParameters().addUint112(BigInteger.ZERO)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); + } + + @Test + @DisplayName("Can receive uint112 max value from contract call") + void canCallContractFunctionUint112Max() throws Exception { + BigInteger uint112Max = new BigInteger("5192296858534827628530496329220095"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint112", new ContractFunctionParameters().addUint112(uint112Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(uint112Max); + } + + @Test + @DisplayName("Can receive uint112 array value from contract call") + void canCallContractFunctionUint112Array() throws Exception { + BigInteger uint112Min = BigInteger.ZERO; + BigInteger uint112Max = new BigInteger("5192296858534827628530496329220095"); + BigInteger[] uint112Array = {uint112Min, uint112Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint112Arr", new ContractFunctionParameters().addUint112Array(uint112Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(uint112[])").get(0); + + assertThat(responseResult).isEqualTo(uint112Array); + } + + @Test + @DisplayName("Can receive uint120 min value from contract call") + void canCallContractFunctionUint120Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint120", new ContractFunctionParameters().addUint120(BigInteger.ZERO)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); + } + + @Test + @DisplayName("Can receive uint120 max value from contract call") + void canCallContractFunctionUint120Max() throws Exception { + BigInteger uint120Max = new BigInteger("1329227995784915872903807060280344575"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint120", new ContractFunctionParameters().addUint120(uint120Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(uint120Max); + } + + @Test + @DisplayName("Can receive uint120 array value from contract call") + void canCallContractFunctionUint120Array() throws Exception { + BigInteger uint120Min = BigInteger.ZERO; + BigInteger uint120Max = new BigInteger("1329227995784915872903807060280344575"); + BigInteger[] uint120Array = {uint120Min, uint120Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint120Arr", new ContractFunctionParameters().addUint120Array(uint120Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(uint120[])").get(0); + + assertThat(responseResult).isEqualTo(uint120Array); + } + + @Test + @DisplayName("Can receive uint128 min value from contract call") + void canCallContractFunctionUint128Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint128", new ContractFunctionParameters().addUint128(BigInteger.ZERO)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); + } + + @Test + @DisplayName("Can receive uint128 max value from contract call") + void canCallContractFunctionUint128Max() throws Exception { + BigInteger uint128Max = new BigInteger("340282366920938463463374607431768211455"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint128", new ContractFunctionParameters().addUint128(uint128Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(uint128Max); + } + + @Test + @DisplayName("Can receive uint128 array value from contract call") + void canCallContractFunctionUint128Array() throws Exception { + BigInteger uint128Min = BigInteger.ZERO; + BigInteger uint128Max = new BigInteger("340282366920938463463374607431768211455"); + BigInteger[] uint128Array = {uint128Min, uint128Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint128Arr", new ContractFunctionParameters().addUint128Array(uint128Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(uint128[])").get(0); + + assertThat(responseResult).isEqualTo(uint128Array); + } + + @Test + @DisplayName("Can receive uint136 min value from contract call") + void canCallContractFunctionUint136Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint136", new ContractFunctionParameters().addUint136(BigInteger.ZERO)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); + } + + @Test + @DisplayName("Can receive uint136 max value from contract call") + void canCallContractFunctionUint136Max() throws Exception { + BigInteger uint136Max = new BigInteger("87112285931760246646623899502532662132735"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint136", new ContractFunctionParameters().addUint136(uint136Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(uint136Max); + } + + @Test + @DisplayName("Can receive uint136 array value from contract call") + void canCallContractFunctionUint136Array() throws Exception { + BigInteger uint136Min = BigInteger.ZERO; + BigInteger uint136Max = new BigInteger("87112285931760246646623899502532662132735"); + BigInteger[] uint136Array = {uint136Min, uint136Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint136Arr", new ContractFunctionParameters().addUint136Array(uint136Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(uint136[])").get(0); + + assertThat(responseResult).isEqualTo(uint136Array); + } + + @Test + @DisplayName("Can receive uint144 min value from contract call") + void canCallContractFunctionUint144Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint144", new ContractFunctionParameters().addUint144(BigInteger.ZERO)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); + } + + @Test + @DisplayName("Can receive uint144 max value from contract call") + void canCallContractFunctionUint144Max() throws Exception { + BigInteger uint144Max = new BigInteger("22300745198530623141535718272648361505980415"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint144", new ContractFunctionParameters().addUint144(uint144Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(uint144Max); + } + + @Test + @DisplayName("Can receive uint144 array value from contract call") + void canCallContractFunctionUint144Array() throws Exception { + BigInteger uint144Min = BigInteger.ZERO; + BigInteger uint144Max = new BigInteger("22300745198530623141535718272648361505980415"); + BigInteger[] uint144Array = {uint144Min, uint144Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint144Arr", new ContractFunctionParameters().addUint144Array(uint144Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(uint144[])").get(0); + + assertThat(responseResult).isEqualTo(uint144Array); + } + + @Test + @DisplayName("Can receive uint152 min value from contract call") + void canCallContractFunctionUint152Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint152", new ContractFunctionParameters().addUint152(BigInteger.ZERO)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); + } + + @Test + @DisplayName("Can receive uint152 max value from contract call") + void canCallContractFunctionUint152Max() throws Exception { + BigInteger uint152Max = new BigInteger("5708990770823839524233143877797980545530986495"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint152", new ContractFunctionParameters().addUint152(uint152Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(uint152Max); + } + + @Test + @DisplayName("Can receive uint152 array value from contract call") + void canCallContractFunctionUint152Array() throws Exception { + BigInteger uint152Min = BigInteger.ZERO; + BigInteger uint152Max = new BigInteger("5708990770823839524233143877797980545530986495"); + BigInteger[] uint152Array = {uint152Min, uint152Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint152Arr", new ContractFunctionParameters().addUint152Array(uint152Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(uint152[])").get(0); + + assertThat(responseResult).isEqualTo(uint152Array); + } + + @Test + @DisplayName("Can receive uint160 min value from contract call") + void canCallContractFunctionUint160Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint160", new ContractFunctionParameters().addUint160(BigInteger.ZERO)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); + } + + @Test + @DisplayName("Can receive uint160 max value from contract call") + void canCallContractFunctionUint160Max() throws Exception { + BigInteger uint160Max = new BigInteger("1461501637330902918203684832716283019655932542975"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint160", new ContractFunctionParameters().addUint160(uint160Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(uint160Max); + } + + @Test + @DisplayName("Can receive uint160 array value from contract call") + void canCallContractFunctionUint160Array() throws Exception { + BigInteger uint160Min = BigInteger.ZERO; + BigInteger uint160Max = new BigInteger("1461501637330902918203684832716283019655932542975"); + BigInteger[] uint160Array = {uint160Min, uint160Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint160Arr", new ContractFunctionParameters().addUint160Array(uint160Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(uint160[])").get(0); + + assertThat(responseResult).isEqualTo(uint160Array); + } + + @Test + @DisplayName("Can receive uint168 min value from contract call") + void canCallContractFunctionUint168Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint168", new ContractFunctionParameters().addUint168(BigInteger.ZERO)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); + } + + @Test + @DisplayName("Can receive uint168 max value from contract call") + void canCallContractFunctionUint168Max() throws Exception { + BigInteger uint168Max = new BigInteger("374144419156711147060143317175368453031918731001855"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint168", new ContractFunctionParameters().addUint168(uint168Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(uint168Max); + } + + @Test + @DisplayName("Can receive uint168 array value from contract call") + void canCallContractFunctionUint168Array() throws Exception { + BigInteger uint168Min = BigInteger.ZERO; + BigInteger uint168Max = new BigInteger("374144419156711147060143317175368453031918731001855"); + BigInteger[] uint168Array = {uint168Min, uint168Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint168Arr", new ContractFunctionParameters().addUint168Array(uint168Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(uint168[])").get(0); + + assertThat(responseResult).isEqualTo(uint168Array); + } + + @Test + @DisplayName("Can receive uint176 min value from contract call") + void canCallContractFunctionUint176Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint176", new ContractFunctionParameters().addUint176(BigInteger.ZERO)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); + } + + @Test + @DisplayName("Can receive uint176 max value from contract call") + void canCallContractFunctionUint176Max() throws Exception { + BigInteger uint176Max = new BigInteger("95780971304118053647396689196894323976171195136475135"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint176", new ContractFunctionParameters().addUint176(uint176Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(uint176Max); + } + + @Test + @DisplayName("Can receive uint176 array value from contract call") + void canCallContractFunctionUint176Array() throws Exception { + BigInteger uint176Min = BigInteger.ZERO; + BigInteger uint176Max = new BigInteger("95780971304118053647396689196894323976171195136475135"); + BigInteger[] uint176Array = {uint176Min, uint176Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint176Arr", new ContractFunctionParameters().addUint176Array(uint176Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(uint176[])").get(0); + + assertThat(responseResult).isEqualTo(uint176Array); + } + + @Test + @DisplayName("Can receive uint184 min value from contract call") + void canCallContractFunctionUint184Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint184", new ContractFunctionParameters().addUint184(BigInteger.ZERO)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); + } + + @Test + @DisplayName("Can receive uint184 max value from contract call") + void canCallContractFunctionUint184Max() throws Exception { + BigInteger uint184Max = new BigInteger("24519928653854221733733552434404946937899825954937634815"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint184", new ContractFunctionParameters().addUint184(uint184Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(uint184Max); + } + + @Test + @DisplayName("Can receive uint184 array value from contract call") + void canCallContractFunctionUint184Array() throws Exception { + BigInteger uint184Min = BigInteger.ZERO; + BigInteger uint184Max = new BigInteger("24519928653854221733733552434404946937899825954937634815"); + BigInteger[] uint184Array = {uint184Min, uint184Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint184Arr", new ContractFunctionParameters().addUint184Array(uint184Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(uint184[])").get(0); + + assertThat(responseResult).isEqualTo(uint184Array); + } + + @Test + @DisplayName("Can receive uint192 min value from contract call") + void canCallContractFunctionUint192Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint192", new ContractFunctionParameters().addUint192(BigInteger.ZERO)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); + } + + @Test + @DisplayName("Can receive uint192 max value from contract call") + void canCallContractFunctionUint192Max() throws Exception { + BigInteger uint192Max = new BigInteger("6277101735386680763835789423207666416102355444464034512895"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint192", new ContractFunctionParameters().addUint192(uint192Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(uint192Max); + } + + @Test + @DisplayName("Can receive uint192 array value from contract call") + void canCallContractFunctionUint192Array() throws Exception { + BigInteger uint192Min = BigInteger.ZERO; + BigInteger uint192Max = new BigInteger("6277101735386680763835789423207666416102355444464034512895"); + BigInteger[] uint192Array = {uint192Min, uint192Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint192Arr", new ContractFunctionParameters().addUint192Array(uint192Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(uint192[])").get(0); + + assertThat(responseResult).isEqualTo(uint192Array); + } + + @Test + @DisplayName("Can receive uint200 min value from contract call") + void canCallContractFunctionUint200Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint200", new ContractFunctionParameters().addUint200(BigInteger.ZERO)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); + } + + @Test + @DisplayName("Can receive uint200 max value from contract call") + void canCallContractFunctionUint200Max() throws Exception { + BigInteger uint200Max = new BigInteger("1606938044258990275541962092341162602522202993782792835301375"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint200", new ContractFunctionParameters().addUint200(uint200Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(uint200Max); + } + + @Test + @DisplayName("Can receive uint200 array value from contract call") + void canCallContractFunctionUint200Array() throws Exception { + BigInteger uint200Min = BigInteger.ZERO; + BigInteger uint200Max = new BigInteger("1606938044258990275541962092341162602522202993782792835301375"); + BigInteger[] uint200Array = {uint200Min, uint200Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint200Arr", new ContractFunctionParameters().addUint200Array(uint200Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(uint200[])").get(0); + + assertThat(responseResult).isEqualTo(uint200Array); + } + + @Test + @DisplayName("Can receive uint208 min value from contract call") + void canCallContractFunctionUint208Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint208", new ContractFunctionParameters().addUint208(BigInteger.ZERO)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); + } + + @Test + @DisplayName("Can receive uint208 max value from contract call") + void canCallContractFunctionUint208Max() throws Exception { + BigInteger uint208Max = new BigInteger("411376139330301510538742295639337626245683966408394965837152255"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint208", new ContractFunctionParameters().addUint208(uint208Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(uint208Max); + } + + @Test + @DisplayName("Can receive uint208 array value from contract call") + void canCallContractFunctionUint208Array() throws Exception { + BigInteger uint208Min = BigInteger.ZERO; + BigInteger uint208Max = new BigInteger("411376139330301510538742295639337626245683966408394965837152255"); + BigInteger[] uint208Array = {uint208Min, uint208Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint208Arr", new ContractFunctionParameters().addUint208Array(uint208Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(uint208[])").get(0); + + assertThat(responseResult).isEqualTo(uint208Array); + } + + @Test + @DisplayName("Can receive uint216 min value from contract call") + void canCallContractFunctionUint216Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint216", new ContractFunctionParameters().addUint216(BigInteger.ZERO)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); + } + + @Test + @DisplayName("Can receive uint216 max value from contract call") + void canCallContractFunctionUint216Max() throws Exception { + BigInteger uint216Max = new BigInteger("105312291668557186697918027683670432318895095400549111254310977535"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint216", new ContractFunctionParameters().addUint216(uint216Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(uint216Max); + } + + @Test + @DisplayName("Can receive uint216 array value from contract call") + void canCallContractFunctionUint216Array() throws Exception { + BigInteger uint216Min = BigInteger.ZERO; + BigInteger uint216Max = new BigInteger("105312291668557186697918027683670432318895095400549111254310977535"); + BigInteger[] uint216Array = {uint216Min, uint216Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint216Arr", new ContractFunctionParameters().addUint216Array(uint216Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(uint216[])").get(0); + + assertThat(responseResult).isEqualTo(uint216Array); + } + + @Test + @DisplayName("Can receive uint224 min value from contract call") + void canCallContractFunctionUint224Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint224", new ContractFunctionParameters().addUint224(BigInteger.ZERO)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); + } + + @Test + @DisplayName("Can receive uint224 max value from contract call") + void canCallContractFunctionUint224Max() throws Exception { + BigInteger uint224Max = new BigInteger("26959946667150639794667015087019630673637144422540572481103610249215"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint224", new ContractFunctionParameters().addUint224(uint224Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(uint224Max); + } + + @Test + @DisplayName("Can receive uint224 array value from contract call") + void canCallContractFunctionUint224Array() throws Exception { + BigInteger uint224Min = BigInteger.ZERO; + BigInteger uint224Max = new BigInteger("26959946667150639794667015087019630673637144422540572481103610249215"); + BigInteger[] uint224Array = {uint224Min, uint224Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint224Arr", new ContractFunctionParameters().addUint224Array(uint224Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(uint224[])").get(0); + + assertThat(responseResult).isEqualTo(uint224Array); + } + + @Test + @DisplayName("Can receive uint232 min value from contract call") + void canCallContractFunctionUint232Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint232", new ContractFunctionParameters().addUint232(BigInteger.ZERO)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); + } + + @Test + @DisplayName("Can receive uint232 max value from contract call") + void canCallContractFunctionUint232Max() throws Exception { + BigInteger uint232Max = + new BigInteger("6901746346790563787434755862277025452451108972170386555162524223799295"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint232", new ContractFunctionParameters().addUint232(uint232Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(uint232Max); + } + + @Test + @DisplayName("Can receive uint232 array value from contract call") + void canCallContractFunctionUint232Array() throws Exception { + BigInteger uint232Min = BigInteger.ZERO; + BigInteger uint232Max = + new BigInteger("6901746346790563787434755862277025452451108972170386555162524223799295"); + BigInteger[] uint232Array = {uint232Min, uint232Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint232Arr", new ContractFunctionParameters().addUint232Array(uint232Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(uint232[])").get(0); + + assertThat(responseResult).isEqualTo(uint232Array); + } + + @Test + @DisplayName("Can receive uint240 min value from contract call") + void canCallContractFunctionUint240Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint240", new ContractFunctionParameters().addUint240(BigInteger.ZERO)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); + } + + @Test + @DisplayName("Can receive uint240 max value from contract call") + void canCallContractFunctionUint240Max() throws Exception { + BigInteger uint240Max = + new BigInteger("1766847064778384329583297500742918515827483896875618958121606201292619775"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint240", new ContractFunctionParameters().addUint240(uint240Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(uint240Max); + } + + @Test + @DisplayName("Can receive uint240 array value from contract call") + void canCallContractFunctionUint240Array() throws Exception { + BigInteger uint240Min = BigInteger.ZERO; + BigInteger uint240Max = + new BigInteger("1766847064778384329583297500742918515827483896875618958121606201292619775"); + BigInteger[] uint240Array = {uint240Min, uint240Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint240Arr", new ContractFunctionParameters().addUint240Array(uint240Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(uint240[])").get(0); + + assertThat(responseResult).isEqualTo(uint240Array); + } + + @Test + @DisplayName("Can receive uint248 min value from contract call") + void canCallContractFunctionUint248Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint248", new ContractFunctionParameters().addUint248(BigInteger.ZERO)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); + } + + @Test + @DisplayName("Can receive uint248 max value from contract call") + void canCallContractFunctionUint248Max() throws Exception { + BigInteger uint248Max = + new BigInteger("452312848583266388373324160190187140051835877600158453279131187530910662655"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint248", new ContractFunctionParameters().addUint248(uint248Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(uint248Max); + } + + @Test + @DisplayName("Can receive uint248 array value from contract call") + void canCallContractFunctionUint248Array() throws Exception { + BigInteger uint248Min = BigInteger.ZERO; + BigInteger uint248Max = + new BigInteger("452312848583266388373324160190187140051835877600158453279131187530910662655"); + BigInteger[] uint248Array = {uint248Min, uint248Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint248Arr", new ContractFunctionParameters().addUint248Array(uint248Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(uint248[])").get(0); + + assertThat(responseResult).isEqualTo(uint248Array); + } + + @Test + @DisplayName("Can receive uint256 min value from contract call") + void canCallContractFunctionUint256Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint256", new ContractFunctionParameters().addUint256(BigInteger.ZERO)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(BigInteger.ZERO); + } + + @Test + @DisplayName("Can receive uint256 max value from contract call") + void canCallContractFunctionUint256Max() throws Exception { + BigInteger uint256Max = new BigInteger("2").pow(256).subtract(BigInteger.ONE); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint256", new ContractFunctionParameters().addUint256(uint256Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint256(0)).isEqualTo(uint256Max); + } + + @Test + @DisplayName("Can receive uint256 array value from contract call") + void canCallContractFunctionUint256Array() throws Exception { + BigInteger uint256Min = BigInteger.ZERO; + BigInteger uint256Max = new BigInteger("2").pow(256).subtract(BigInteger.ONE); + BigInteger[] uint256Array = {uint256Min, uint256Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint256Arr", new ContractFunctionParameters().addUint256Array(uint256Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(uint256[])").get(0); + + assertThat(responseResult).isEqualTo(uint256Array); + } + + @Test + @DisplayName("Can receive int8 min value from contract call") + void canCallContractFunctionInt8Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt8", new ContractFunctionParameters().addInt8(Byte.MIN_VALUE)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt8(0)).isEqualTo(Byte.MIN_VALUE); + } + + @Test + @DisplayName("Can receive int8 max value from contract call") + void canCallContractFunctionInt8Max() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt8", new ContractFunctionParameters().addInt8(Byte.MAX_VALUE)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt8(0)).isEqualTo(Byte.MAX_VALUE); + } + + @Test + @DisplayName("Can receive int8 array value from contract call") + void canCallContractFunctionInt8Array() throws Exception { + byte[] int8Array = {Byte.MIN_VALUE, Byte.MAX_VALUE}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt8Arr", new ContractFunctionParameters().addInt8Array(int8Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (int[]) response.getResult("(int8[])").get(0); + + assertThat(responseResult[0]).isEqualTo(int8Array[0]); + assertThat(responseResult[1]).isEqualTo(int8Array[1]); + } + + @Test + @DisplayName("Can receive int16 min value from contract call") + void canCallContractFunctionInt16Min() throws Exception { + int int16Min = -32768; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt16", new ContractFunctionParameters().addInt16(int16Min)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt32(0)).isEqualTo(int16Min); + } + + @Test + @DisplayName("Can receive int16 max value from contract call") + void canCallContractFunctionInt16Max() throws Exception { + int int16Max = 32767; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt16", new ContractFunctionParameters().addInt16(int16Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt32(0)).isEqualTo(int16Max); + } + + @Test + @DisplayName("Can receive int16 array value from contract call") + void canCallContractFunctionInt16Array() throws Exception { + int int16Min = -32768; + int int16Max = 32767; + int[] int16Array = {int16Min, int16Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt16Arr", new ContractFunctionParameters().addInt16Array(int16Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (int[]) response.getResult("(int16[])").get(0); + + assertThat(responseResult).isEqualTo(int16Array); + } + + @Test + @DisplayName("Can receive int24 min value from contract call") + void canCallContractFunctionInt24Min() throws Exception { + int int24Min = -8388608; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt24", new ContractFunctionParameters().addInt24(int24Min)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt32(0)).isEqualTo(int24Min); + } + + @Test + @DisplayName("Can receive int24 max value from contract call") + void canCallContractFunctionInt24Max() throws Exception { + int int24Max = 8388607; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt24", new ContractFunctionParameters().addInt24(int24Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt32(0)).isEqualTo(int24Max); + } + + @Test + @DisplayName("Can receive int24 array value from contract call") + void canCallContractFunctionInt24Array() throws Exception { + int int24Min = -8388608; + int int24Max = 8388607; + int[] int24Array = {int24Min, int24Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt24Arr", new ContractFunctionParameters().addInt24Array(int24Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (int[]) response.getResult("(int24[])").get(0); + + assertThat(responseResult).isEqualTo(int24Array); + } + + @Test + @DisplayName("Can receive int32 min value from contract call") + void canCallContractFunctionInt32Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt32", new ContractFunctionParameters().addInt32(Integer.MIN_VALUE)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt32(0)).isEqualTo(Integer.MIN_VALUE); + } + + @Test + @DisplayName("Can receive int32 max value from contract call") + void canCallContractFunctionInt32Max() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt32", new ContractFunctionParameters().addInt32(Integer.MAX_VALUE)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt32(0)).isEqualTo(Integer.MAX_VALUE); + } + + @Test + @DisplayName("Can receive int32 array value from contract call") + void canCallContractFunctionInt32Array() throws Exception { + int int32Min = Integer.MIN_VALUE; + int int32Max = Integer.MAX_VALUE; + int[] int32Array = {int32Min, int32Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt32Arr", new ContractFunctionParameters().addInt32Array(int32Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (int[]) response.getResult("(int32[])").get(0); + + assertThat(responseResult).isEqualTo(int32Array); + } + + @Test + @DisplayName("Can receive int40 min value from contract call") + void canCallContractFunctionInt40Min() throws Exception { + long int40Min = -549755813888L; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt40", new ContractFunctionParameters().addInt40(int40Min)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt64(0)).isEqualTo(int40Min); + } + + @Test + @DisplayName("Can receive int40 max value from contract call") + void canCallContractFunctionInt40Max() throws Exception { + long int40Max = 549755813887L; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt40", new ContractFunctionParameters().addInt40(int40Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt64(0)).isEqualTo(int40Max); + } + + @Test + @DisplayName("Can receive int40 array value from contract call") + void canCallContractFunctionInt40Array() throws Exception { + long int40Min = -549755813888L; + long int40Max = 549755813887L; + long[] int40Array = {int40Min, int40Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt40Arr", new ContractFunctionParameters().addInt40Array(int40Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (long[]) response.getResult("(int40[])").get(0); + + assertThat(responseResult).isEqualTo(int40Array); + } + + @Test + @DisplayName("Can receive int48 min value from contract call") + void canCallContractFunctionInt48Min() throws Exception { + long int48Min = -140737488355328L; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt48", new ContractFunctionParameters().addInt48(int48Min)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt64(0)).isEqualTo(int48Min); + } + + @Test + @DisplayName("Can receive int48 max value from contract call") + void canCallContractFunctionInt48Max() throws Exception { + long int48Max = 140737488355327L; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt48", new ContractFunctionParameters().addInt48(int48Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt64(0)).isEqualTo(int48Max); + } + + @Test + @DisplayName("Can receive int48 array value from contract call") + void canCallContractFunctionInt48Array() throws Exception { + long int48Min = -140737488355328L; + long int48Max = 140737488355327L; + long[] int48Array = {int48Min, int48Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt48Arr", new ContractFunctionParameters().addInt48Array(int48Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (long[]) response.getResult("(int48[])").get(0); + + assertThat(responseResult).isEqualTo(int48Array); + } + + @Test + @DisplayName("Can receive int56 min value from contract call") + void canCallContractFunctionInt56Min() throws Exception { + long int56Min = -36028797018963968L; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt56", new ContractFunctionParameters().addInt56(int56Min)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt64(0)).isEqualTo(int56Min); + } + + @Test + @DisplayName("Can receive int56 max value from contract call") + void canCallContractFunctionInt56Max() throws Exception { + long int56Max = 36028797018963967L; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt56", new ContractFunctionParameters().addInt56(int56Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt64(0)).isEqualTo(int56Max); + } + + @Test + @DisplayName("Can receive int56 array value from contract call") + void canCallContractFunctionInt56Array() throws Exception { + long int56Min = -36028797018963968L; + long int56Max = 36028797018963967L; + long[] int56Array = {int56Min, int56Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt56Arr", new ContractFunctionParameters().addInt56Array(int56Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (long[]) response.getResult("(int56[])").get(0); + + assertThat(responseResult).isEqualTo(int56Array); + } + + @Test + @DisplayName("Can receive int64 min value from contract call") + void canCallContractFunctionInt64Min() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt64", new ContractFunctionParameters().addInt64(Long.MIN_VALUE)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint64(0)).isEqualTo(Long.MIN_VALUE); + } + + @Test + @DisplayName("Can receive int64 max value from contract call") + void canCallContractFunctionInt64Max() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnUint64", new ContractFunctionParameters().addUint64(Long.MAX_VALUE)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getUint64(0)).isEqualTo(Long.MAX_VALUE); + } + + @Test + @DisplayName("Can receive int64 array value from contract call") + void canCallContractFunctionInt64Array() throws Exception { + long int64Min = Long.MIN_VALUE; + long int64Max = Long.MAX_VALUE; + long[] int64Array = {int64Min, int64Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt64Arr", new ContractFunctionParameters().addInt64Array(int64Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (long[]) response.getResult("(int64[])").get(0); + + assertThat(responseResult).isEqualTo(int64Array); + } + + @Test + @DisplayName("Can receive int72 min value from contract call") + void canCallContractFunctionInt72Min() throws Exception { + BigInteger int72Min = new BigInteger("-2361183241434822606848"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt72", new ContractFunctionParameters().addInt72(int72Min)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int72Min); + } + + @Test + @DisplayName("Can receive int72 max value from contract call") + void canCallContractFunctionInt72Max() throws Exception { + BigInteger int72Max = new BigInteger("2361183241434822606847"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt72", new ContractFunctionParameters().addInt72(int72Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int72Max); + } + + @Test + @DisplayName("Can receive int72 array value from contract call") + void canCallContractFunctionInt72Array() throws Exception { + BigInteger int72Min = new BigInteger("-2361183241434822606848"); + BigInteger int72Max = new BigInteger("2361183241434822606847"); + BigInteger[] int72Array = {int72Min, int72Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt72Arr", new ContractFunctionParameters().addInt72Array(int72Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(int72[])").get(0); + + assertThat(responseResult).isEqualTo(int72Array); + } + + @Test + @DisplayName("Can receive int80 min value from contract call") + void canCallContractFunctionInt80Min() throws Exception { + BigInteger int80Min = new BigInteger("-604462909807314587353088"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt80", new ContractFunctionParameters().addInt80(int80Min)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int80Min); + } + + @Test + @DisplayName("Can receive int80 max value from contract call") + void canCallContractFunctionInt80Max() throws Exception { + BigInteger int80Max = new BigInteger("604462909807314587353087"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt80", new ContractFunctionParameters().addInt80(int80Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int80Max); + } + + @Test + @DisplayName("Can receive int80 array value from contract call") + void canCallContractFunctionInt80Array() throws Exception { + BigInteger int80Min = new BigInteger("-604462909807314587353088"); + BigInteger int80Max = new BigInteger("604462909807314587353087"); + BigInteger[] int80Array = {int80Min, int80Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt80Arr", new ContractFunctionParameters().addInt80Array(int80Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(int80[])").get(0); + + assertThat(responseResult).isEqualTo(int80Array); + } + + @Test + @DisplayName("Can receive int88 min value from contract call") + void canCallContractFunctionInt88Min() throws Exception { + BigInteger int88Min = new BigInteger("-154742504910672534362390528"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt88", new ContractFunctionParameters().addInt88(int88Min)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int88Min); + } + + @Test + @DisplayName("Can receive int88 max value from contract call") + void canCallContractFunctionInt88Max() throws Exception { + BigInteger int88Max = new BigInteger("154742504910672534362390527"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt88", new ContractFunctionParameters().addInt88(int88Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int88Max); + } + + @Test + @DisplayName("Can receive int88 array value from contract call") + void canCallContractFunctionInt88Array() throws Exception { + BigInteger int88Min = new BigInteger("-154742504910672534362390528"); + BigInteger int88Max = new BigInteger("154742504910672534362390527"); + BigInteger[] int88Array = {int88Min, int88Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt88Arr", new ContractFunctionParameters().addInt88Array(int88Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(int88[])").get(0); + + assertThat(responseResult).isEqualTo(int88Array); + } + + @Test + @DisplayName("Can receive int96 min value from contract call") + void canCallContractFunctionInt96Min() throws Exception { + BigInteger int96Min = new BigInteger("-39614081257132168796771975168"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt96", new ContractFunctionParameters().addInt96(int96Min)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int96Min); + } + + @Test + @DisplayName("Can receive int96 max value from contract call") + void canCallContractFunctionInt96Max() throws Exception { + BigInteger int96Max = new BigInteger("39614081257132168796771975167"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt96", new ContractFunctionParameters().addInt96(int96Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int96Max); + } + + @Test + @DisplayName("Can receive int96 array value from contract call") + void canCallContractFunctionInt96Array() throws Exception { + BigInteger int96Min = new BigInteger("-39614081257132168796771975168"); + BigInteger int96Max = new BigInteger("39614081257132168796771975167"); + BigInteger[] int96Array = {int96Min, int96Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt96Arr", new ContractFunctionParameters().addInt96Array(int96Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(int96[])").get(0); + + assertThat(responseResult).isEqualTo(int96Array); + } + + @Test + @DisplayName("Can receive int104 min value from contract call") + void canCallContractFunctionInt104Min() throws Exception { + BigInteger int104Min = new BigInteger("-10141204801825835211973625643008"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt104", new ContractFunctionParameters().addInt104(int104Min)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int104Min); + } + + @Test + @DisplayName("Can receive int104 max value from contract call") + void canCallContractFunctionInt104Max() throws Exception { + BigInteger int104Max = new BigInteger("10141204801825835211973625643007"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt104", new ContractFunctionParameters().addInt104(int104Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int104Max); + } + + @Test + @DisplayName("Can receive int104 array value from contract call") + void canCallContractFunctionInt104Array() throws Exception { + BigInteger int104Min = new BigInteger("-10141204801825835211973625643008"); + BigInteger int104Max = new BigInteger("10141204801825835211973625643007"); + BigInteger[] int104Array = {int104Min, int104Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt104Arr", new ContractFunctionParameters().addInt104Array(int104Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(int104[])").get(0); + + assertThat(responseResult).isEqualTo(int104Array); + } + + @Test + @DisplayName("Can receive int112 min value from contract call") + void canCallContractFunctionInt112Min() throws Exception { + BigInteger int112Min = new BigInteger("-2596148429267413814265248164610048"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt112", new ContractFunctionParameters().addInt112(int112Min)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int112Min); + } + + @Test + @DisplayName("Can receive int112 max value from contract call") + void canCallContractFunctionInt112Max() throws Exception { + BigInteger int112Max = new BigInteger("2596148429267413814265248164610047"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt112", new ContractFunctionParameters().addInt112(int112Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int112Max); + } + + @Test + @DisplayName("Can receive int112 array value from contract call") + void canCallContractFunctionInt112Array() throws Exception { + BigInteger int112Min = new BigInteger("-2596148429267413814265248164610048"); + BigInteger int112Max = new BigInteger("2596148429267413814265248164610047"); + BigInteger[] int112Array = {int112Min, int112Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt112Arr", new ContractFunctionParameters().addInt112Array(int112Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(int112[])").get(0); + + assertThat(responseResult).isEqualTo(int112Array); + } + + @Test + @DisplayName("Can receive int120 min value from contract call") + void canCallContractFunctionInt120Min() throws Exception { + BigInteger int120Min = new BigInteger("-664613997892457936451903530140172288"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt120", new ContractFunctionParameters().addInt120(int120Min)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int120Min); + } + + @Test + @DisplayName("Can receive int120 max value from contract call") + void canCallContractFunctionInt120Max() throws Exception { + BigInteger int120Max = new BigInteger("664613997892457936451903530140172287"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt120", new ContractFunctionParameters().addInt120(int120Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int120Max); + } + + @Test + @DisplayName("Can receive int120 array value from contract call") + void canCallContractFunctionInt120Array() throws Exception { + BigInteger int120Min = new BigInteger("-664613997892457936451903530140172288"); + BigInteger int120Max = new BigInteger("664613997892457936451903530140172287"); + BigInteger[] int120Array = {int120Min, int120Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt120Arr", new ContractFunctionParameters().addInt120Array(int120Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(int120[])").get(0); + + assertThat(responseResult).isEqualTo(int120Array); + } + + @Test + @DisplayName("Can receive int128 min value from contract call") + void canCallContractFunctionInt128Min() throws Exception { + BigInteger int128Min = new BigInteger("-170141183460469231731687303715884105728"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt128", new ContractFunctionParameters().addInt128(int128Min)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int128Min); + } + + @Test + @DisplayName("Can receive int128 max value from contract call") + void canCallContractFunctionInt128Max() throws Exception { + BigInteger int128Max = new BigInteger("170141183460469231731687303715884105727"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt128", new ContractFunctionParameters().addInt128(int128Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int128Max); + } + + @Test + @DisplayName("Can receive int128 array value from contract call") + void canCallContractFunctionInt128Array() throws Exception { + BigInteger int128Min = new BigInteger("-170141183460469231731687303715884105728"); + BigInteger int128Max = new BigInteger("170141183460469231731687303715884105727"); + BigInteger[] int128Array = {int128Min, int128Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt128Arr", new ContractFunctionParameters().addInt128Array(int128Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(int128[])").get(0); + + assertThat(responseResult).isEqualTo(int128Array); + } + + @Test + @DisplayName("Can receive int136 min value from contract call") + void canCallContractFunctionInt136Min() throws Exception { + BigInteger int136Min = new BigInteger("-43556142965880123323311949751266331066368"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt136", new ContractFunctionParameters().addInt136(int136Min)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int136Min); + } + + @Test + @DisplayName("Can receive int136 max value from contract call") + void canCallContractFunctionInt136Max() throws Exception { + BigInteger int136Max = new BigInteger("43556142965880123323311949751266331066367"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt136", new ContractFunctionParameters().addInt136(int136Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int136Max); + } + + @Test + @DisplayName("Can receive int136 array value from contract call") + void canCallContractFunctionInt136Array() throws Exception { + BigInteger int136Min = new BigInteger("-43556142965880123323311949751266331066368"); + BigInteger int136Max = new BigInteger("43556142965880123323311949751266331066367"); + BigInteger[] int136Array = {int136Min, int136Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt136Arr", new ContractFunctionParameters().addInt136Array(int136Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(int136[])").get(0); + + assertThat(responseResult).isEqualTo(int136Array); + } + + @Test + @DisplayName("Can receive int144 min value from contract call") + void canCallContractFunctionInt144Min() throws Exception { + BigInteger int144Min = new BigInteger("-11150372599265311570767859136324180752990208"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt144", new ContractFunctionParameters().addInt144(int144Min)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int144Min); + } + + @Test + @DisplayName("Can receive int144 max value from contract call") + void canCallContractFunctionInt144Max() throws Exception { + BigInteger int144Max = new BigInteger("11150372599265311570767859136324180752990207"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt144", new ContractFunctionParameters().addInt144(int144Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int144Max); + } + + @Test + @DisplayName("Can receive int144 array value from contract call") + void canCallContractFunctionInt144Array() throws Exception { + BigInteger int144Min = new BigInteger("-11150372599265311570767859136324180752990208"); + BigInteger int144Max = new BigInteger("11150372599265311570767859136324180752990207"); + BigInteger[] int144Array = {int144Min, int144Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt144Arr", new ContractFunctionParameters().addInt144Array(int144Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(int144[])").get(0); + + assertThat(responseResult).isEqualTo(int144Array); + } + + @Test + @DisplayName("Can receive int152 min value from contract call") + void canCallContractFunctionInt152Min() throws Exception { + BigInteger int152Min = new BigInteger("-2854495385411919762116571938898990272765493248"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt152", new ContractFunctionParameters().addInt152(int152Min)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int152Min); + } + + @Test + @DisplayName("Can receive int152 max value from contract call") + void canCallContractFunctionInt152Max() throws Exception { + BigInteger int152Max = new BigInteger("2854495385411919762116571938898990272765493247"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt152", new ContractFunctionParameters().addInt152(int152Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int152Max); + } + + @Test + @DisplayName("Can receive int152 array value from contract call") + void canCallContractFunctionInt152Array() throws Exception { + BigInteger int152Min = new BigInteger("-2854495385411919762116571938898990272765493248"); + BigInteger int152Max = new BigInteger("2854495385411919762116571938898990272765493247"); + BigInteger[] int152Array = {int152Min, int152Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt152Arr", new ContractFunctionParameters().addInt152Array(int152Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(int152[])").get(0); + + assertThat(responseResult).isEqualTo(int152Array); + } + + @Test + @DisplayName("Can receive int160 min value from contract call") + void canCallContractFunctionInt160Min() throws Exception { + BigInteger int160Min = new BigInteger("-730750818665451459101842416358141509827966271488"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt160", new ContractFunctionParameters().addInt160(int160Min)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int160Min); + } + + @Test + @DisplayName("Can receive int160 max value from contract call") + void canCallContractFunctionInt160Max() throws Exception { + BigInteger int160Max = new BigInteger("730750818665451459101842416358141509827966271487"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt160", new ContractFunctionParameters().addInt160(int160Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int160Max); + } + + @Test + @DisplayName("Can receive int160 array value from contract call") + void canCallContractFunctionInt160Array() throws Exception { + BigInteger int160Min = new BigInteger("-730750818665451459101842416358141509827966271488"); + BigInteger int160Max = new BigInteger("730750818665451459101842416358141509827966271487"); + BigInteger[] int160Array = {int160Min, int160Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt160Arr", new ContractFunctionParameters().addInt160Array(int160Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(int160[])").get(0); + + assertThat(responseResult).isEqualTo(int160Array); + } + + @Test + @DisplayName("Can receive int168 min value from contract call") + void canCallContractFunctionInt168Min() throws Exception { + BigInteger int168Min = new BigInteger("-187072209578355573530071658587684226515959365500928"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt168", new ContractFunctionParameters().addInt168(int168Min)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int168Min); + } + + @Test + @DisplayName("Can receive int168 max value from contract call") + void canCallContractFunctionInt168Max() throws Exception { + BigInteger int168Max = new BigInteger("187072209578355573530071658587684226515959365500927"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt168", new ContractFunctionParameters().addInt168(int168Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int168Max); + } + + @Test + @DisplayName("Can receive int168 array value from contract call") + void canCallContractFunctionInt168Array() throws Exception { + BigInteger int168Min = new BigInteger("-187072209578355573530071658587684226515959365500928"); + BigInteger int168Max = new BigInteger("187072209578355573530071658587684226515959365500927"); + BigInteger[] int168Array = {int168Min, int168Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt168Arr", new ContractFunctionParameters().addInt168Array(int168Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(int168[])").get(0); + + assertThat(responseResult).isEqualTo(int168Array); + } + + @Test + @DisplayName("Can receive int176 min value from contract call") + void canCallContractFunctionInt176Min() throws Exception { + BigInteger int176Min = new BigInteger("-47890485652059026823698344598447161988085597568237568"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt176", new ContractFunctionParameters().addInt176(int176Min)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int176Min); + } + + @Test + @DisplayName("Can receive int176 max value from contract call") + void canCallContractFunctionInt176Max() throws Exception { + BigInteger int176Max = new BigInteger("47890485652059026823698344598447161988085597568237567"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt176", new ContractFunctionParameters().addInt176(int176Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int176Max); + } + + @Test + @DisplayName("Can receive int176 array value from contract call") + void canCallContractFunctionInt176Array() throws Exception { + BigInteger int176Min = new BigInteger("-47890485652059026823698344598447161988085597568237568"); + BigInteger int176Max = new BigInteger("47890485652059026823698344598447161988085597568237567"); + BigInteger[] int176Array = {int176Min, int176Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt176Arr", new ContractFunctionParameters().addInt176Array(int176Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(int176[])").get(0); + + assertThat(responseResult).isEqualTo(int176Array); + } + + @Test + @DisplayName("Can receive int184 min value from contract call") + void canCallContractFunctionInt184Min() throws Exception { + BigInteger int184Min = new BigInteger("-12259964326927110866866776217202473468949912977468817408"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt184", new ContractFunctionParameters().addInt184(int184Min)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int184Min); + } + + @Test + @DisplayName("Can receive int184 max value from contract call") + void canCallContractFunctionInt184Max() throws Exception { + BigInteger int184Max = new BigInteger("12259964326927110866866776217202473468949912977468817407"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt184", new ContractFunctionParameters().addInt184(int184Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int184Max); + } + + @Test + @DisplayName("Can receive int184 array value from contract call") + void canCallContractFunctionInt184Array() throws Exception { + BigInteger int184Min = new BigInteger("-12259964326927110866866776217202473468949912977468817408"); + BigInteger int184Max = new BigInteger("12259964326927110866866776217202473468949912977468817407"); + BigInteger[] int184Array = {int184Min, int184Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt184Arr", new ContractFunctionParameters().addInt184Array(int184Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(int184[])").get(0); + + assertThat(responseResult).isEqualTo(int184Array); + } + + @Test + @DisplayName("Can receive int192 min value from contract call") + void canCallContractFunctionInt192Min() throws Exception { + BigInteger int192Min = new BigInteger("-3138550867693340381917894711603833208051177722232017256448"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt192", new ContractFunctionParameters().addInt192(int192Min)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int192Min); + } + + @Test + @DisplayName("Can receive int192 max value from contract call") + void canCallContractFunctionInt192Max() throws Exception { + BigInteger int192Max = new BigInteger("3138550867693340381917894711603833208051177722232017256447"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt192", new ContractFunctionParameters().addInt192(int192Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int192Max); + } + + @Test + @DisplayName("Can receive int192 array value from contract call") + void canCallContractFunctionInt192Array() throws Exception { + BigInteger int192Min = new BigInteger("-3138550867693340381917894711603833208051177722232017256448"); + BigInteger int192Max = new BigInteger("3138550867693340381917894711603833208051177722232017256447"); + BigInteger[] int192Array = {int192Min, int192Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt192Arr", new ContractFunctionParameters().addInt192Array(int192Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(int192[])").get(0); + + assertThat(responseResult).isEqualTo(int192Array); + } + + @Test + @DisplayName("Can receive int200 min value from contract call") + void canCallContractFunctionInt200Min() throws Exception { + BigInteger int200Min = new BigInteger("-803469022129495137770981046170581301261101496891396417650688"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt200", new ContractFunctionParameters().addInt200(int200Min)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int200Min); + } + + @Test + @DisplayName("Can receive int200 max value from contract call") + void canCallContractFunctionInt200Max() throws Exception { + BigInteger int200Max = new BigInteger("803469022129495137770981046170581301261101496891396417650687"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt200", new ContractFunctionParameters().addInt200(int200Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int200Max); + } + + @Test + @DisplayName("Can receive int200 array value from contract call") + void canCallContractFunctionInt200Array() throws Exception { + BigInteger int200Min = new BigInteger("-803469022129495137770981046170581301261101496891396417650688"); + BigInteger int200Max = new BigInteger("803469022129495137770981046170581301261101496891396417650687"); + BigInteger[] int200Array = {int200Min, int200Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt200Arr", new ContractFunctionParameters().addInt200Array(int200Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(int200[])").get(0); + + assertThat(responseResult).isEqualTo(int200Array); + } + + @Test + @DisplayName("Can receive int208 min value from contract call") + void canCallContractFunctionInt208Min() throws Exception { + BigInteger int208Min = new BigInteger("-205688069665150755269371147819668813122841983204197482918576128"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt208", new ContractFunctionParameters().addInt208(int208Min)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int208Min); + } + + @Test + @DisplayName("Can receive int208 max value from contract call") + void canCallContractFunctionInt208Max() throws Exception { + BigInteger int208Max = new BigInteger("205688069665150755269371147819668813122841983204197482918576127"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt208", new ContractFunctionParameters().addInt208(int208Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int208Max); + } + + @Test + @DisplayName("Can receive int208 array value from contract call") + void canCallContractFunctionInt208Array() throws Exception { + BigInteger int208Min = new BigInteger("-205688069665150755269371147819668813122841983204197482918576128"); + BigInteger int208Max = new BigInteger("205688069665150755269371147819668813122841983204197482918576127"); + BigInteger[] int208Array = {int208Min, int208Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt208Arr", new ContractFunctionParameters().addInt208Array(int208Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(int208[])").get(0); + + assertThat(responseResult).isEqualTo(int208Array); + } + + @Test + @DisplayName("Can receive int216 min value from contract call") + void canCallContractFunctionInt216Min() throws Exception { + BigInteger int216Min = new BigInteger("-52656145834278593348959013841835216159447547700274555627155488768"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt216", new ContractFunctionParameters().addInt216(int216Min)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int216Min); + } + + @Test + @DisplayName("Can receive int216 max value from contract call") + void canCallContractFunctionInt216Max() throws Exception { + BigInteger int216Max = new BigInteger("52656145834278593348959013841835216159447547700274555627155488767"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt216", new ContractFunctionParameters().addInt216(int216Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int216Max); + } + + @Test + @DisplayName("Can receive int216 array value from contract call") + void canCallContractFunctionInt216Array() throws Exception { + BigInteger int216Min = new BigInteger("-52656145834278593348959013841835216159447547700274555627155488768"); + BigInteger int216Max = new BigInteger("52656145834278593348959013841835216159447547700274555627155488767"); + BigInteger[] int216Array = {int216Min, int216Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt216Arr", new ContractFunctionParameters().addInt216Array(int216Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(int216[])").get(0); + + assertThat(responseResult).isEqualTo(int216Array); + } + + @Test + @DisplayName("Can receive int224 min value from contract call") + void canCallContractFunctionInt224Min() throws Exception { + BigInteger int224Min = new BigInteger("-13479973333575319897333507543509815336818572211270286240551805124608"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt224", new ContractFunctionParameters().addInt224(int224Min)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int224Min); + } + + @Test + @DisplayName("Can receive int224 max value from contract call") + void canCallContractFunctionInt224Max() throws Exception { + BigInteger int224Max = new BigInteger("13479973333575319897333507543509815336818572211270286240551805124607"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt224", new ContractFunctionParameters().addInt224(int224Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int224Max); + } + + @Test + @DisplayName("Can receive int224 array value from contract call") + void canCallContractFunctionInt224Array() throws Exception { + BigInteger int224Min = new BigInteger("-13479973333575319897333507543509815336818572211270286240551805124608"); + BigInteger int224Max = new BigInteger("13479973333575319897333507543509815336818572211270286240551805124607"); + BigInteger[] int224Array = {int224Min, int224Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt224Arr", new ContractFunctionParameters().addInt224Array(int224Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(int224[])").get(0); + + assertThat(responseResult).isEqualTo(int224Array); + } + + @Test + @DisplayName("Can receive int232 min value from contract call") + void canCallContractFunctionInt232Min() throws Exception { + BigInteger int232Min = + new BigInteger("-3450873173395281893717377931138512726225554486085193277581262111899648"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt232", new ContractFunctionParameters().addInt232(int232Min)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int232Min); + } + + @Test + @DisplayName("Can receive int232 max value from contract call") + void canCallContractFunctionInt232Max() throws Exception { + BigInteger int232Max = new BigInteger("3450873173395281893717377931138512726225554486085193277581262111899647"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt232", new ContractFunctionParameters().addInt232(int232Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int232Max); + } + + @Test + @DisplayName("Can receive int232 array value from contract call") + void canCallContractFunctionInt232Array() throws Exception { + BigInteger int232Min = + new BigInteger("-3450873173395281893717377931138512726225554486085193277581262111899648"); + BigInteger int232Max = new BigInteger("3450873173395281893717377931138512726225554486085193277581262111899647"); + BigInteger[] int232Array = {int232Min, int232Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt232Arr", new ContractFunctionParameters().addInt232Array(int232Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(int232[])").get(0); + + assertThat(responseResult).isEqualTo(int232Array); + } + + @Test + @DisplayName("Can receive int240 min value from contract call") + void canCallContractFunctionInt240Min() throws Exception { + BigInteger int240Min = + new BigInteger("-883423532389192164791648750371459257913741948437809479060803100646309888"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt240", new ContractFunctionParameters().addInt240(int240Min)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int240Min); + } + + @Test + @DisplayName("Can receive int240 max value from contract call") + void canCallContractFunctionInt240Max() throws Exception { + BigInteger int240Max = + new BigInteger("883423532389192164791648750371459257913741948437809479060803100646309887"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt240", new ContractFunctionParameters().addInt240(int240Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int240Max); + } + + @Test + @DisplayName("Can receive int240 array value from contract call") + void canCallContractFunctionInt240Array() throws Exception { + BigInteger int240Min = + new BigInteger("-883423532389192164791648750371459257913741948437809479060803100646309888"); + BigInteger int240Max = + new BigInteger("883423532389192164791648750371459257913741948437809479060803100646309887"); + BigInteger[] int240Array = {int240Min, int240Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt240Arr", new ContractFunctionParameters().addInt240Array(int240Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(int240[])").get(0); + + assertThat(responseResult).isEqualTo(int240Array); + } + + @Test + @DisplayName("Can receive int248 min value from contract call") + void canCallContractFunctionInt248Min() throws Exception { + BigInteger int248Min = + new BigInteger("-226156424291633194186662080095093570025917938800079226639565593765455331328"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt248", new ContractFunctionParameters().addInt248(int248Min)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int248Min); + } + + @Test + @DisplayName("Can receive int248 max value from contract call") + void canCallContractFunctionInt248Max() throws Exception { + BigInteger int248Max = + new BigInteger("226156424291633194186662080095093570025917938800079226639565593765455331327"); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt248", new ContractFunctionParameters().addInt248(int248Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int248Max); + } + + @Test + @DisplayName("Can receive int248 array value from contract call") + void canCallContractFunctionInt248Array() throws Exception { + BigInteger int248Min = + new BigInteger("-226156424291633194186662080095093570025917938800079226639565593765455331328"); + BigInteger int248Max = + new BigInteger("226156424291633194186662080095093570025917938800079226639565593765455331327"); + BigInteger[] int248Array = {int248Min, int248Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt248Arr", new ContractFunctionParameters().addInt248Array(int248Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(int248[])").get(0); + + assertThat(responseResult).isEqualTo(int248Array); + } + + @Test + @DisplayName("Can receive int256 min value from contract call") + void canCallContractFunctionInt256Min() throws Exception { + BigInteger int256Min = + new BigInteger("2").pow(256).divide(BigInteger.TWO).negate(); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt256", new ContractFunctionParameters().addInt256(int256Min)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int256Min); + } + + @Test + @DisplayName("Can receive int256 max value from contract call") + void canCallContractFunctionInt256Max() throws Exception { + BigInteger int256Max = + new BigInteger("2").pow(256).subtract(BigInteger.ONE).divide(BigInteger.TWO); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt256", new ContractFunctionParameters().addInt256(int256Max)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int256Max); + } + + @Test + @DisplayName("Can receive int256 array value from contract call") + void canCallContractFunctionInt256Array() throws Exception { + BigInteger int256Min = + new BigInteger("2").pow(256).divide(BigInteger.TWO).negate(); + BigInteger int256Max = + new BigInteger("2").pow(256).subtract(BigInteger.ONE).divide(BigInteger.TWO); + BigInteger[] int256Array = {int256Min, int256Max}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt256Arr", new ContractFunctionParameters().addInt256Array(int256Array)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (BigInteger[]) response.getResult("(int256[])").get(0); + + assertThat(responseResult).isEqualTo(int256Array); + } + + @Test + @DisplayName("Can receive multiple int8 values from contract call") + void canCallContractFunctionMultipleInt8() throws Exception { + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnInt8Multiple", new ContractFunctionParameters().addInt8(Byte.MIN_VALUE)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt8(0)).isEqualTo(Byte.MIN_VALUE); + assertThat(response.getInt8(1)).isEqualTo((byte) -108); + } + + @Test + @DisplayName("Can receive multiple int40 values from contract call") + void canCallContractFunctionMultipleInt40() throws Exception { + long int40 = 549755813885L; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnMultipleInt40", new ContractFunctionParameters().addInt40(int40)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt64(0)).isEqualTo(int40); + assertThat(response.getInt64(1)).isEqualTo(int40 + 1); + } + + @Test + @DisplayName("Can receive multiple int256 values from contract call") + void canCallContractFunctionMultipleInt256() throws Exception { + BigInteger int256Min = + new BigInteger("2").pow(256).divide(BigInteger.TWO).negate(); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnMultipleInt256", new ContractFunctionParameters().addInt256(int256Min)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getInt256(0)).isEqualTo(int256Min); + assertThat(response.getInt256(1)).isEqualTo(int256Min.add(BigInteger.ONE)); + } + + @Test + @DisplayName("Can receive multiple types of values from contract call") + void canCallContractFunctionMultipleTypes() throws Exception { + var uint32Max = "4294967295"; + int uint32MaxInt = Integer.parseUnsignedInt(uint32Max); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnMultipleTypeParams", new ContractFunctionParameters().addUint32(uint32MaxInt)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(Integer.toUnsignedString(response.getUint32(0))).isEqualTo(uint32Max); + assertThat(response.getUint64(1)).isEqualTo(Long.parseUnsignedLong(uint32Max) - 1); + assertThat(response.getString(2)).isEqualTo("OK"); + } + + @Test + @DisplayName("Can receive string value from contract call") + void canCallContractFunctionStringType() throws Exception { + var testString = "test"; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnString", new ContractFunctionParameters().addString(testString)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getString(0)).isEqualTo(testString); + } + + @Test + @DisplayName("Can receive string array value from contract call") + void canCallContractFunctionStringArrayType() throws Exception { + var testStringArray = new String[] {"Test1", "Test2"}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnStringArr", new ContractFunctionParameters().addStringArray(testStringArray)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getStringArray(0).get(0)).isEqualTo(testStringArray[0]); + assertThat(response.getStringArray(0).get(1)).isEqualTo(testStringArray[1]); + } + + @Test + @DisplayName("Can receive string array value from contract call with getResult function") + void canCallContractFunctionStringArrayType_getResult() throws Exception { + var testStringArray = new String[] {"Test1", "Test2"}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnStringArr", new ContractFunctionParameters().addStringArray(testStringArray)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (String[]) response.getResult("(string[])").get(0); + assertThat(responseResult).isEqualTo(testStringArray); + } + + @Test + @DisplayName("Can receive address value from contract call") + void canCallContractFunctionAddressType() throws Exception { + var testAddress = "1234567890123456789012345678901234567890"; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnAddress", new ContractFunctionParameters().addAddress(testAddress)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getAddress(0)).isEqualTo(testAddress); + } + + @Test + @DisplayName("Can receive address array value from contract call") + void canCallContractFunctionAddressArrayType() throws Exception { + var testAddressArray = + new String[] {"1234567890123456789012345678901234567890", "1234567890123456789012345678901234567891"}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnAddressArr", new ContractFunctionParameters().addAddressArray(testAddressArray)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (Address[]) response.getResult("(address[])").get(0); + + Address[] testAddressArray_Address = Arrays.stream(testAddressArray) + .map(addressStr -> "0x" + addressStr) + .map(Address::wrap) + .toArray(Address[]::new); + + assertThat(responseResult).isEqualTo(testAddressArray_Address); + } + + @Test + @DisplayName("Can receive boolean value from contract call") + void canCallContractFunctionBooleanType() throws Exception { + var testBoolean = true; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnBoolean", new ContractFunctionParameters().addBool(testBoolean)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getBool(0)).isEqualTo(testBoolean); + } + + @Test + @DisplayName("Can receive boolean array value from contract call") + void canCallContractFunctionBooleanArrayType() throws Exception { + var testBooleanArray = new boolean[] {true, false}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnBooleanArr", new ContractFunctionParameters().addBoolArray(testBooleanArray)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (boolean[]) response.getResult("(bool[])").get(0); + + assertThat(responseResult).isEqualTo(testBooleanArray); + } + + @Test + @DisplayName("Can receive bytes value from contract call") + void canCallContractFunctionBytesType() throws Exception { + var testBytes = "Test".getBytes(); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnBytes", new ContractFunctionParameters().addBytes(testBytes)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getBytes(0)).isEqualTo(testBytes); + } + + @Test + @DisplayName("Can receive bytes array value from contract call") + void canCallContractFunctionBytesArrayType() throws Exception { + byte[][] testBytes = new byte[][] {"Test1".getBytes(), "Test2".getBytes()}; + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnBytesArr", new ContractFunctionParameters().addBytesArray(testBytes)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (byte[][]) response.getResult("(bytes[])").get(0); + + assertThat(responseResult).isEqualTo(testBytes); + } + + @Test + @DisplayName("Can receive bytes32 value from contract call") + void canCallContractFunctionBytes32Type() throws Exception { + byte[] testBytes = "Test".getBytes(); + byte[] testBytesLen32 = new byte[32]; + System.arraycopy(testBytes, 0, testBytesLen32, 0, testBytes.length); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnBytes32", new ContractFunctionParameters().addBytes32(testBytesLen32)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + assertThat(response.getBytes32(0)).isEqualTo(testBytesLen32); + } + + @Test + @DisplayName("Can receive bytes32 array value from contract call") + void canCallContractFunctionBytes32ArrayType() throws Exception { + byte[] testBytes = "Test".getBytes(); + byte[] testBytes2 = "Test2".getBytes(); + byte[][] testBytesLen32 = new byte[2][32]; + System.arraycopy(testBytes, 0, testBytesLen32[0], 0, testBytes.length); + System.arraycopy(testBytes2, 0, testBytesLen32[1], 0, testBytes2.length); + + var response = new ContractCallQuery() + .setContractId(contractId) + .setGas(1500000) + .setFunction("returnBytes32Arr", new ContractFunctionParameters().addBytes32Array(testBytesLen32)) + .setQueryPayment(new Hbar(10)) + .execute(testEnv.client); + + var responseResult = (byte[][]) response.getResult("(bytes32[])").get(0); + + assertThat(responseResult).isEqualTo(testBytesLen32); + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractIdPopulationIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractIdPopulationIntegrationTest.java new file mode 100644 index 0000000000..72c87aca10 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractIdPopulationIntegrationTest.java @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Objects; +import org.hiero.sdk.ContractCreateTransaction; +import org.hiero.sdk.ContractFunctionParameters; +import org.hiero.sdk.ContractId; +import org.hiero.sdk.ContractInfoQuery; +import org.hiero.sdk.FileCreateTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class ContractIdPopulationIntegrationTest { + @Test + @DisplayName("Can populate ContractId num from mirror node (using sync method)") + void canPopulateContractIdNumSync() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var testContractByteCode = + "608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506101cb806100606000396000f3fe608060405260043610610046576000357c01000000000000000000000000000000000000000000000000000000009004806341c0e1b51461004b578063cfae321714610062575b600080fd5b34801561005757600080fd5b506100606100f2565b005b34801561006e57600080fd5b50610077610162565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100b757808201518184015260208101905061009c565b50505050905090810190601f1680156100e45780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415610160573373ffffffffffffffffffffffffffffffffffffffff16ff5b565b60606040805190810160405280600d81526020017f48656c6c6f2c20776f726c64210000000000000000000000000000000000000081525090509056fea165627a7a72305820ae96fb3af7cde9c0abfe365272441894ab717f816f07f41f07b1cbede54e256e0029"; + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(testContractByteCode) + .execute(testEnv.client); + + var receipt = response.setValidateStatus(true).getReceipt(testEnv.client); + var fileId = Objects.requireNonNull(receipt.fileId); + + response = new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(100000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::canPopulateContractIdNum]") + .execute(testEnv.client); + + receipt = response.setValidateStatus(true).getReceipt(testEnv.client); + + var contractId = Objects.requireNonNull(receipt.contractId); + + var info = new ContractInfoQuery().setContractId(contractId).execute(testEnv.client); + + var idMirror = ContractId.fromEvmAddress(0, 0, info.contractAccountId); + Thread.sleep(5000); + + var newContractId = idMirror.populateContractNum(testEnv.client); + + assertThat(contractId.num).isEqualTo(newContractId.num); + } + } + + @Test + @DisplayName("Can populate ContractId num from mirror node (using async method)") + void canPopulateContractIdNumAsync() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var testContractByteCode = + "608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506101cb806100606000396000f3fe608060405260043610610046576000357c01000000000000000000000000000000000000000000000000000000009004806341c0e1b51461004b578063cfae321714610062575b600080fd5b34801561005757600080fd5b506100606100f2565b005b34801561006e57600080fd5b50610077610162565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100b757808201518184015260208101905061009c565b50505050905090810190601f1680156100e45780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415610160573373ffffffffffffffffffffffffffffffffffffffff16ff5b565b60606040805190810160405280600d81526020017f48656c6c6f2c20776f726c64210000000000000000000000000000000000000081525090509056fea165627a7a72305820ae96fb3af7cde9c0abfe365272441894ab717f816f07f41f07b1cbede54e256e0029"; + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(testContractByteCode) + .execute(testEnv.client); + + var receipt = response.setValidateStatus(true).getReceipt(testEnv.client); + var fileId = Objects.requireNonNull(receipt.fileId); + + response = new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(100000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::canPopulateContractIdNum]") + .execute(testEnv.client); + + receipt = response.setValidateStatus(true).getReceipt(testEnv.client); + + var contractId = Objects.requireNonNull(receipt.contractId); + + var info = new ContractInfoQuery().setContractId(contractId).execute(testEnv.client); + + var idMirror = ContractId.fromEvmAddress(0, 0, info.contractAccountId); + Thread.sleep(5000); + + var newContractId = + idMirror.populateContractNumAsync(testEnv.client).get(); + + assertThat(contractId.num).isEqualTo(newContractId.num); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractInfoIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractInfoIntegrationTest.java new file mode 100644 index 0000000000..70ddf340ce --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractInfoIntegrationTest.java @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Objects; +import org.hiero.sdk.*; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class ContractInfoIntegrationTest { + private static final String SMART_CONTRACT_BYTECODE = + "608060405234801561001057600080fd5b506040516104d73803806104d78339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b506040525050600080546001600160a01b0319163317905550805161010890600190602084019061010f565b50506101aa565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061015057805160ff191683800117855561017d565b8280016001018555821561017d579182015b8281111561017d578251825591602001919060010190610162565b5061018992915061018d565b5090565b6101a791905b808211156101895760008155600101610193565b90565b61031e806101b96000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063368b87721461004657806341c0e1b5146100ee578063ce6d41de146100f6575b600080fd5b6100ec6004803603602081101561005c57600080fd5b81019060208101813564010000000081111561007757600080fd5b82018360208201111561008957600080fd5b803590602001918460018302840111640100000000831117156100ab57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610173945050505050565b005b6100ec6101a2565b6100fe6101ba565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610138578181015183820152602001610120565b50505050905090810190601f1680156101655780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000546001600160a01b0316331461018a5761019f565b805161019d906001906020840190610250565b505b50565b6000546001600160a01b03163314156101b85733ff5b565b60018054604080516020601f600260001961010087891615020190951694909404938401819004810282018101909252828152606093909290918301828280156102455780601f1061021a57610100808354040283529160200191610245565b820191906000526020600020905b81548152906001019060200180831161022857829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061029157805160ff19168380011785556102be565b828001600101855582156102be579182015b828111156102be5782518255916020019190600101906102a3565b506102ca9291506102ce565b5090565b61024d91905b808211156102ca57600081556001016102d456fea264697066735822122084964d4c3f6bc912a9d20e14e449721012d625aa3c8a12de41ae5519752fc89064736f6c63430006000033"; + + @Test + @DisplayName("Can query contract info") + void canQueryContractInfo() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + response = new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(200000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(testEnv.client); + + var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); + + assertThat(contractId.hashCode()).isGreaterThan(0); + assertThat(contractId.compareTo(ContractId.fromBytes(contractId.toBytes()))) + .isZero(); + + var info = new ContractInfoQuery().setContractId(contractId).execute(testEnv.client); + + assertThat(info.contractId).isEqualTo(contractId); + assertThat(info.accountId).isNotNull(); + assertThat(Objects.requireNonNull(info.accountId).toString()).isEqualTo(contractId.toString()); + assertThat(info.adminKey).isNotNull(); + assertThat(Objects.requireNonNull(info.adminKey).toString()) + .isEqualTo(Objects.requireNonNull(testEnv.operatorKey).toString()); + assertThat(info.storage).isEqualTo(128); + assertThat(info.contractMemo).isEqualTo("[e2e::ContractCreateTransaction]"); + + new ContractDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setContractId(contractId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can query contract info when admin key is null") + void canQueryContractInfoWhenAdminKeyIsNull() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + response = new ContractCreateTransaction() + .setGas(200000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(testEnv.client); + + var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); + + var info = new ContractInfoQuery().setContractId(contractId).execute(testEnv.client); + + assertThat(info.contractId).isEqualTo(contractId); + assertThat(info.accountId).isNotNull(); + assertThat(Objects.requireNonNull(info.accountId).toString()).isEqualTo(contractId.toString()); + assertThat(info.adminKey).isNotNull(); + // TODO: Fix this when we know it's correct + // assertEquals(info.adminKey, contractId); + assertThat(info.storage).isEqualTo(128); + assertThat(info.contractMemo).isEqualTo("[e2e::ContractCreateTransaction]"); + } + } + + @Test + @DisplayName("Cannot query contract info when contract ID is not set") + void cannotQueryContractInfoWhenContractIDIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new ContractInfoQuery().execute(testEnv.client); + }) + .withMessageContaining(Status.INVALID_CONTRACT_ID.toString()); + } + } + + @Test + @DisplayName("Can get cost, even with a big max") + @SuppressWarnings("UnusedVariable") + void getCostBigMaxContractInfoFunction() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + response = new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(200000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(testEnv.client); + + var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); + + var infoQuery = new ContractInfoQuery().setContractId(contractId).setMaxQueryPayment(new Hbar(10000)); + + var cost = infoQuery.getCost(testEnv.client); + + var result = infoQuery.execute(testEnv.client); + + new ContractDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setContractId(contractId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Error, max is smaller than set payment.") + void getCostSmallMaxContractInfoFunction() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + response = new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(200000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(testEnv.client); + + var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); + + var infoQuery = new ContractInfoQuery().setContractId(contractId).setMaxQueryPayment(Hbar.fromTinybars(1)); + + assertThatExceptionOfType(MaxQueryPaymentExceededException.class).isThrownBy(() -> { + infoQuery.execute(testEnv.client); + }); + + new ContractDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setContractId(contractId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Insufficient tx fee error.") + void getCostInsufficientTxFeeContractInfoFunction() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + response = new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(200000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(testEnv.client); + + var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); + + var infoQuery = new ContractInfoQuery().setContractId(contractId).setMaxQueryPayment(new Hbar(100)); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + infoQuery.setQueryPayment(Hbar.fromTinybars(1)).execute(testEnv.client); + }) + .satisfies(error -> assertThat(error.status.toString()).isEqualTo("INSUFFICIENT_TX_FEE")); + + new ContractDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setContractId(contractId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractNonceInfoIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractNonceInfoIntegrationTest.java new file mode 100644 index 0000000000..0fc9f5d7e3 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractNonceInfoIntegrationTest.java @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Objects; +import org.hiero.sdk.ContractCreateTransaction; +import org.hiero.sdk.ContractDeleteTransaction; +import org.hiero.sdk.ContractId; +import org.hiero.sdk.ContractNonceInfo; +import org.hiero.sdk.FileCreateTransaction; +import org.hiero.sdk.FileDeleteTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class ContractNonceInfoIntegrationTest { + private static final String SMART_CONTRACT_BYTECODE = + "6080604052348015600f57600080fd5b50604051601a90603b565b604051809103906000f0801580156035573d6000803e3d6000fd5b50506047565b605c8061009483390190565b603f806100556000396000f3fe6080604052600080fdfea2646970667358221220a20122cbad3457fedcc0600363d6e895f17048f5caa4afdab9e655123737567d64736f6c634300081200336080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea264697066735822122053dfd8835e3dc6fedfb8b4806460b9b7163f8a7248bac510c6d6808d9da9d6d364736f6c63430008120033"; + + @Test + @DisplayName("Contract Create of A nonce, which deploys contract B in CONSTRUCTOR") + void canIncrementNonceThroughContractConstructor() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + response = new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(100000) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::ContractADeploysContractBInConstructor]") + .execute(testEnv.client); + + var contractFunctionResult = response.getRecord(testEnv.client).contractFunctionResult; + + ContractId contractA = contractFunctionResult.contractId; + ContractId contractB = contractFunctionResult.contractNonces.stream() + .filter(contractNonce -> !contractNonce.contractId.equals(contractA)) + .findFirst() + .get() + .contractId; + + ContractNonceInfo contractANonceInfo = contractFunctionResult.contractNonces.stream() + .filter(contractNonce -> contractNonce.contractId.equals(contractA)) + .findFirst() + .get(); + ContractNonceInfo contractBNonceInfo = contractFunctionResult.contractNonces.stream() + .filter(contractNonce -> contractNonce.contractId.equals(contractB)) + .findFirst() + .get(); + + // A.nonce = 2 + assertThat(contractANonceInfo.nonce).isEqualTo(2); + // B.nonce = 1 + assertThat(contractBNonceInfo.nonce).isEqualTo(1); + // validate HIP-844 case - signer nonce should be set only for Ethereum transactions + assertThat(contractFunctionResult.signerNonce).isEqualTo(0); + + var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); + + new ContractDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setContractId(contractId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractUpdateIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractUpdateIntegrationTest.java new file mode 100644 index 0000000000..a5716c5fc4 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ContractUpdateIntegrationTest.java @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Objects; +import org.hiero.sdk.ContractCreateTransaction; +import org.hiero.sdk.ContractDeleteTransaction; +import org.hiero.sdk.ContractFunctionParameters; +import org.hiero.sdk.ContractInfoQuery; +import org.hiero.sdk.ContractUpdateTransaction; +import org.hiero.sdk.FileCreateTransaction; +import org.hiero.sdk.FileDeleteTransaction; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.sdk.Status; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class ContractUpdateIntegrationTest { + private static final String SMART_CONTRACT_BYTECODE = + "608060405234801561001057600080fd5b506040516104d73803806104d78339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b506040525050600080546001600160a01b0319163317905550805161010890600190602084019061010f565b50506101aa565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061015057805160ff191683800117855561017d565b8280016001018555821561017d579182015b8281111561017d578251825591602001919060010190610162565b5061018992915061018d565b5090565b6101a791905b808211156101895760008155600101610193565b90565b61031e806101b96000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063368b87721461004657806341c0e1b5146100ee578063ce6d41de146100f6575b600080fd5b6100ec6004803603602081101561005c57600080fd5b81019060208101813564010000000081111561007757600080fd5b82018360208201111561008957600080fd5b803590602001918460018302840111640100000000831117156100ab57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610173945050505050565b005b6100ec6101a2565b6100fe6101ba565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610138578181015183820152602001610120565b50505050905090810190601f1680156101655780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000546001600160a01b0316331461018a5761019f565b805161019d906001906020840190610250565b505b50565b6000546001600160a01b03163314156101b85733ff5b565b60018054604080516020601f600260001961010087891615020190951694909404938401819004810282018101909252828152606093909290918301828280156102455780601f1061021a57610100808354040283529160200191610245565b820191906000526020600020905b81548152906001019060200180831161022857829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061029157805160ff19168380011785556102be565b828001600101855582156102be579182015b828111156102be5782518255916020019190600101906102a3565b506102ca9291506102ce565b5090565b61024d91905b808211156102ca57600081556001016102d456fea264697066735822122084964d4c3f6bc912a9d20e14e449721012d625aa3c8a12de41ae5519752fc89064736f6c63430006000033"; + + @Test + @DisplayName("Can update contract") + void canUpdateContract() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + response = new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(200000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(testEnv.client); + + var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); + + var info = new ContractInfoQuery().setContractId(contractId).execute(testEnv.client); + + assertThat(info.contractId).isEqualTo(contractId); + assertThat(info.accountId).isNotNull(); + assertThat(Objects.requireNonNull(info.accountId).toString()).isEqualTo(contractId.toString()); + assertThat(info.adminKey).isNotNull(); + assertThat(Objects.requireNonNull(info.adminKey).toString()) + .isEqualTo(Objects.requireNonNull(testEnv.operatorKey).toString()); + assertThat(info.storage).isEqualTo(128); + assertThat(info.contractMemo).isEqualTo("[e2e::ContractCreateTransaction]"); + + new ContractUpdateTransaction() + .setContractId(contractId) + .setContractMemo("[e2e::ContractUpdateTransaction]") + .execute(testEnv.client) + .getReceipt(testEnv.client); + + info = new ContractInfoQuery().setContractId(contractId).execute(testEnv.client); + + assertThat(info.contractId).isEqualTo(contractId); + assertThat(info.accountId).isNotNull(); + assertThat(Objects.requireNonNull(info.accountId).toString()).isEqualTo(contractId.toString()); + assertThat(info.adminKey).isNotNull(); + assertThat(Objects.requireNonNull(info.adminKey).toString()) + .isEqualTo(Objects.requireNonNull(testEnv.operatorKey).toString()); + assertThat(info.storage).isEqualTo(128); + assertThat(info.contractMemo).isEqualTo("[e2e::ContractUpdateTransaction]"); + + new ContractDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setContractId(contractId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot update contract when contract ID is not set") + void cannotUpdateContractWhenContractIDIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new ContractUpdateTransaction() + .setContractMemo("[e2e::ContractUpdateTransaction]") + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_CONTRACT_ID.toString()); + } + } + + @Test + @DisplayName("Cannot update contract that is immutable") + void cannotUpdateContractThatIsImmutable() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + var contractId = Objects.requireNonNull(new ContractCreateTransaction() + .setGas(200000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(testEnv.client) + .getReceipt(testEnv.client) + .contractId); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new ContractUpdateTransaction() + .setContractId(contractId) + .setContractMemo("[e2e::ContractUpdateTransaction]") + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.MODIFYING_IMMUTABLE_CONTRACT.toString()); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/EntityHelper.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/EntityHelper.java new file mode 100644 index 0000000000..63893a7529 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/EntityHelper.java @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import java.util.concurrent.TimeoutException; +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.AccountId; +import org.hiero.sdk.ContractCreateFlow; +import org.hiero.sdk.ContractFunctionParameters; +import org.hiero.sdk.ContractId; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.Key; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.sdk.TokenCreateTransaction; +import org.hiero.sdk.TokenId; +import org.hiero.sdk.TokenSupplyType; +import org.hiero.sdk.TokenType; + +/** + * The EntityCreator class provides static methods for creating different entities in a Hedera network, such as token, account, and contract. + */ +public final class EntityHelper { + + private EntityHelper() {} + + public static int fungibleInitialBalance = 1_000_000; + public static int mitedNfts = 10; + + /** + * Create a non-fungible unique token. + * + * @param testEnv The integration test environment. + * @return The token ID of the created token. + * @throws PrecheckStatusException + * @throws TimeoutException + * @throws ReceiptStatusException + */ + public static TokenId createNft(IntegrationTestEnv testEnv) + throws PrecheckStatusException, TimeoutException, ReceiptStatusException { + return new TokenCreateTransaction() + .setTokenName("Test NFT") + .setTokenSymbol("TNFT") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setSupplyType(TokenSupplyType.FINITE) + .setMaxSupply(10) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setMetadataKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setPauseKey(testEnv.operatorKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId; + } + + /** + * Creates a fungible token. + * + * @param testEnv The integration test environment. + * @param decimals The number of decimal places for the token. + * @return The token ID of the created token. + * @throws PrecheckStatusException If the transaction fails pre-check. + * @throws TimeoutException If the transaction times out. + * @throws ReceiptStatusException If the receipt status is not success. + */ + public static TokenId createFungibleToken(IntegrationTestEnv testEnv, int decimals) + throws PrecheckStatusException, TimeoutException, ReceiptStatusException { + return new TokenCreateTransaction() + .setTokenName("Test Fungible Token") + .setTokenSymbol("TFT") + .setTokenMemo("I was created for integration tests") + .setDecimals(decimals) + .setInitialSupply(fungibleInitialBalance) + .setMaxSupply(fungibleInitialBalance) + .setTreasuryAccountId(testEnv.operatorId) + .setSupplyType(TokenSupplyType.FINITE) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setMetadataKey(testEnv.operatorKey) + .setPauseKey(testEnv.operatorKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId; + } + + /** + * Creates a new account with the specified account key and maximum automatic token associations. + * + * @param testEnv The integration test environment. + * @param accountKey The account key. + * @param maxAutomaticTokenAssociations The maximum number of automatic token associations allowed. + * @return The account ID of the newly created account. + * @throws PrecheckStatusException If the transaction fails pre-check. + * @throws TimeoutException If the transaction times out. + * @throws ReceiptStatusException If the receipt status is not success. + */ + public static AccountId createAccount(IntegrationTestEnv testEnv, Key accountKey, int maxAutomaticTokenAssociations) + throws PrecheckStatusException, TimeoutException, ReceiptStatusException { + return new AccountCreateTransaction() + .setKey(accountKey) + .setInitialBalance(new Hbar(10)) + .setMaxAutomaticTokenAssociations(maxAutomaticTokenAssociations) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + } + + /** + * Creates a contract with the specified contract key. + * + * @param testEnv The integration test environment. + * @param contractKey The contract key. + * @return The contract ID of the created contract. + * @throws PrecheckStatusException if the transaction fails pre-check. + * @throws TimeoutException if the transaction times out. + * @throws ReceiptStatusException if the receipt status is not success. + */ + public static ContractId createContract(IntegrationTestEnv testEnv, Key contractKey) + throws PrecheckStatusException, TimeoutException, ReceiptStatusException { + final String SMART_CONTRACT_BYTECODE = + "608060405234801561001057600080fd5b506040516104d73803806104d78339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b506040525050600080546001600160a01b0319163317905550805161010890600190602084019061010f565b50506101aa565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061015057805160ff191683800117855561017d565b8280016001018555821561017d579182015b8281111561017d578251825591602001919060010190610162565b5061018992915061018d565b5090565b6101a791905b808211156101895760008155600101610193565b90565b61031e806101b96000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063368b87721461004657806341c0e1b5146100ee578063ce6d41de146100f6575b600080fd5b6100ec6004803603602081101561005c57600080fd5b81019060208101813564010000000081111561007757600080fd5b82018360208201111561008957600080fd5b803590602001918460018302840111640100000000831117156100ab57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610173945050505050565b005b6100ec6101a2565b6100fe6101ba565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610138578181015183820152602001610120565b50505050905090810190601f1680156101655780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000546001600160a01b0316331461018a5761019f565b805161019d906001906020840190610250565b505b50565b6000546001600160a01b03163314156101b85733ff5b565b60018054604080516020601f600260001961010087891615020190951694909404938401819004810282018101909252828152606093909290918301828280156102455780601f1061021a57610100808354040283529160200191610245565b820191906000526020600020905b81548152906001019060200180831161022857829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061029157805160ff19168380011785556102be565b828001600101855582156102be579182015b828111156102be5782518255916020019190600101906102a3565b506102ca9291506102ce565b5090565b61024d91905b808211156102ca57600081556001016102d456fea264697066735822122084964d4c3f6bc912a9d20e14e449721012d625aa3c8a12de41ae5519752fc89064736f6c63430006000033"; + + return new ContractCreateFlow() + .setAdminKey(contractKey) + .setGas(200_000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecode(SMART_CONTRACT_BYTECODE) + .setContractMemo("[e2e::ContractMemo]") + .execute(testEnv.client) + .getReceipt(testEnv.client) + .contractId; + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/EthereumTransactionIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/EthereumTransactionIntegrationTest.java new file mode 100644 index 0000000000..cf10993258 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/EthereumTransactionIntegrationTest.java @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.esaulpaugh.headlong.rlp.RLPEncoder; +import com.esaulpaugh.headlong.util.Integers; +import java.math.BigInteger; +import java.util.List; +import java.util.Objects; +import org.bouncycastle.util.encoders.Hex; +import org.hiero.sdk.ContractCreateTransaction; +import org.hiero.sdk.ContractDeleteTransaction; +import org.hiero.sdk.ContractExecuteTransaction; +import org.hiero.sdk.ContractFunctionParameters; +import org.hiero.sdk.EthereumTransaction; +import org.hiero.sdk.FileCreateTransaction; +import org.hiero.sdk.FileDeleteTransaction; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.PrivateKeyECDSA; +import org.hiero.sdk.TransferTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class EthereumTransactionIntegrationTest { + + private static final String SMART_CONTRACT_BYTECODE = + "608060405234801561001057600080fd5b506040516104d73803806104d78339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b506040525050600080546001600160a01b0319163317905550805161010890600190602084019061010f565b50506101aa565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061015057805160ff191683800117855561017d565b8280016001018555821561017d579182015b8281111561017d578251825591602001919060010190610162565b5061018992915061018d565b5090565b6101a791905b808211156101895760008155600101610193565b90565b61031e806101b96000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063368b87721461004657806341c0e1b5146100ee578063ce6d41de146100f6575b600080fd5b6100ec6004803603602081101561005c57600080fd5b81019060208101813564010000000081111561007757600080fd5b82018360208201111561008957600080fd5b803590602001918460018302840111640100000000831117156100ab57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610173945050505050565b005b6100ec6101a2565b6100fe6101ba565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610138578181015183820152602001610120565b50505050905090810190601f1680156101655780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000546001600160a01b0316331461018a5761019f565b805161019d906001906020840190610250565b505b50565b6000546001600160a01b03163314156101b85733ff5b565b60018054604080516020601f600260001961010087891615020190951694909404938401819004810282018101909252828152606093909290918301828280156102455780601f1061021a57610100808354040283529160200191610245565b820191906000526020600020905b81548152906001019060200180831161022857829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061029157805160ff19168380011785556102be565b828001600101855582156102be579182015b828111156102be5782518255916020019190600101906102a3565b506102ca9291506102ce565b5090565b61024d91905b808211156102ca57600081556001016102d456fea264697066735822122084964d4c3f6bc912a9d20e14e449721012d625aa3c8a12de41ae5519752fc89064736f6c63430006000033"; + + /** + * @notice E2E-HIP-844 + * @url https://hips.hedera.com/hip/hip-844 + */ + @Test + @DisplayName("Signer nonce changed on Ethereum transaction") + void signerNonceChangedOnEthereumTransaction() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var privateKey = PrivateKey.generateECDSA(); + var newAccountAliasId = privateKey.toAccountId(0, 0); + + new TransferTransaction() + .addHbarTransfer(testEnv.operatorId, new Hbar(1).negated()) + .addHbarTransfer(newAccountAliasId, new Hbar(1)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var fileCreateTransactionResponse = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(fileCreateTransactionResponse.getReceipt(testEnv.client).fileId); + + var contractCreateTransactionResponse = new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(200000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(testEnv.client); + + var contractId = + Objects.requireNonNull(contractCreateTransactionResponse.getReceipt(testEnv.client).contractId); + + int nonce = 0; + byte[] chainId = Hex.decode("012a"); + byte[] maxPriorityGas = Hex.decode("00"); + byte[] maxGas = Hex.decode("d1385c7bf0"); + byte[] to = Hex.decode(contractId.toSolidityAddress()); + byte[] callData = new ContractExecuteTransaction() + .setFunction("setMessage", new ContractFunctionParameters().addString("new message")) + .getFunctionParameters() + .toByteArray(); + + var sequence = RLPEncoder.sequence(Integers.toBytes(2), new Object[] { + chainId, + Integers.toBytes(nonce), // nonce + maxPriorityGas, + maxGas, + Integers.toBytes(150000), // gasLimit + to, + Integers.toBytesUnsigned(BigInteger.ZERO), // value + callData, + new Object[0] + }); + + byte[] signedBytes = privateKey.sign(sequence); + + // wrap in signature object + final byte[] r = new byte[32]; + System.arraycopy(signedBytes, 0, r, 0, 32); + final byte[] s = new byte[32]; + System.arraycopy(signedBytes, 32, s, 0, 32); + + final int recId = ((PrivateKeyECDSA) privateKey).getRecoveryId(r, s, sequence); + + byte[] ethereumData = RLPEncoder.sequence( + Integers.toBytes(0x02), + List.of( + chainId, + Integers.toBytes(nonce), // nonce + maxPriorityGas, + maxGas, + Integers.toBytes(150000), // gasLimit + to, + Integers.toBytesUnsigned(BigInteger.ZERO), // value + callData, + List.of(/*accessList*/ ), + Integers.toBytes(recId), // recId + r, + s)); + + EthereumTransaction ethereumTransaction = new EthereumTransaction().setEthereumData(ethereumData); + var ethereumTransactionResponse = ethereumTransaction.execute(testEnv.client); + var ethereumTransactionRecord = ethereumTransactionResponse.getRecord(testEnv.client); + + assertThat(ethereumTransactionRecord.contractFunctionResult.signerNonce) + .isEqualTo(1); + + new ContractDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setContractId(contractId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/FeeSchedulesTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/FeeSchedulesTest.java new file mode 100644 index 0000000000..de4146933d --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/FeeSchedulesTest.java @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.google.protobuf.ByteString; +import org.hiero.sdk.FeeSchedules; +import org.hiero.sdk.FileContentsQuery; +import org.hiero.sdk.FileId; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class FeeSchedulesTest { + @Test + @DisplayName("FeeSchedules (CurrentAndNextFeeSchedule) is fetched and parsed from file 0.0.111") + void canFetchFeeSchedules() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + ByteString feeSchedulesBytes = + new FileContentsQuery().setFileId(new FileId(0, 0, 111)).execute(testEnv.client); + + FeeSchedules feeSchedules = FeeSchedules.fromBytes(feeSchedulesBytes.toByteArray()); + + /* + * Test whether the file 0.0.111 actually contains stuff + */ + assertThat(feeSchedules.getCurrent()).isNotNull(); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/FileAppendIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/FileAppendIntegrationTest.java new file mode 100644 index 0000000000..b49453c236 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/FileAppendIntegrationTest.java @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.Duration; +import java.util.Objects; +import org.hiero.sdk.FileAppendTransaction; +import org.hiero.sdk.FileContentsQuery; +import org.hiero.sdk.FileCreateTransaction; +import org.hiero.sdk.FileDeleteTransaction; +import org.hiero.sdk.FileInfoQuery; +import org.hiero.sdk.KeyList; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class FileAppendIntegrationTest { + @Test + @DisplayName("Can append to file") + void canAppendToFile() throws Exception { + // There are potential bugs in FileAppendTransaction which require more than one node to trigger. + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents("[e2e::FileCreateTransaction]") + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + var info = new FileInfoQuery().setFileId(fileId).execute(testEnv.client); + + assertThat(info.fileId).isEqualTo(fileId); + assertThat(info.size).isEqualTo(28); + assertThat(info.isDeleted).isFalse(); + assertThat(info.keys).isNotNull(); + assertThat(info.keys.getThreshold()).isNull(); + assertThat(info.keys).isEqualTo(KeyList.of(testEnv.operatorKey)); + + new FileAppendTransaction() + .setFileId(fileId) + .setContents("[e2e::FileAppendTransaction]") + .execute(testEnv.client) + .getReceipt(testEnv.client); + + info = new FileInfoQuery().setFileId(fileId).execute(testEnv.client); + + assertThat(info.fileId).isEqualTo(fileId); + assertThat(info.size).isEqualTo(56); + assertThat(info.isDeleted).isFalse(); + assertThat(info.keys).isNotNull(); + assertThat(info.keys.getThreshold()).isNull(); + assertThat(info.keys).isEqualTo(KeyList.of(testEnv.operatorKey)); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can append large contents to file") + void canAppendLargeContentsToFile() throws Exception { + // There are potential bugs in FileAppendTransaction which require more than one node to trigger. + try (var testEnv = new IntegrationTestEnv(2)) { + + // Skip if using local node. + // Note: this check should be removed once the local node is supporting multiple nodes. + testEnv.assumeNotLocalNode(); + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents("[e2e::FileCreateTransaction]") + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + Thread.sleep(5000); + + var info = new FileInfoQuery().setFileId(fileId).execute(testEnv.client); + + assertThat(info.fileId).isEqualTo(fileId); + assertThat(info.size).isEqualTo(28); + assertThat(info.isDeleted).isFalse(); + assertThat(info.keys).isNotNull(); + assertThat(info.keys.getThreshold()).isNull(); + assertThat(info.keys).isEqualTo(KeyList.of(testEnv.operatorKey)); + + new FileAppendTransaction() + .setFileId(fileId) + .setContents(Contents.BIG_CONTENTS) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var contents = new FileContentsQuery().setFileId(fileId).execute(testEnv.client); + + assertThat(contents.toStringUtf8()).isEqualTo("[e2e::FileCreateTransaction]" + Contents.BIG_CONTENTS); + + info = new FileInfoQuery().setFileId(fileId).execute(testEnv.client); + + assertThat(info.fileId).isEqualTo(fileId); + assertThat(info.size).isEqualTo(13522); + assertThat(info.isDeleted).isFalse(); + assertThat(info.keys).isNotNull(); + assertThat(info.keys.getThreshold()).isNull(); + assertThat(info.keys).isEqualTo(KeyList.of(testEnv.operatorKey)); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can append large contents to file despite TRANSACTION_EXPIRATION response codes") + void canAppendLargeContentsToFileDespiteExpiration() throws Exception { + // There are potential bugs in FileAppendTransaction which require more than one node to trigger. + try (var testEnv = new IntegrationTestEnv(2)) { + + // Skip if using local node. + // Note: this check should be removed once the local node is supporting multiple nodes. + testEnv.assumeNotLocalNode(); + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents("[e2e::FileCreateTransaction]") + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + Thread.sleep(5000); + + var info = new FileInfoQuery().setFileId(fileId).execute(testEnv.client); + + assertThat(info.fileId).isEqualTo(fileId); + assertThat(info.size).isEqualTo(28); + assertThat(info.isDeleted).isFalse(); + assertThat(info.keys).isNotNull(); + assertThat(info.keys.getThreshold()).isNull(); + assertThat(info.keys).isEqualTo(KeyList.of(testEnv.operatorKey)); + + var appendTx = new FileAppendTransaction() + .setFileId(fileId) + .setContents(Contents.BIG_CONTENTS) + .setTransactionValidDuration(Duration.ofSeconds(25)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var contents = new FileContentsQuery().setFileId(fileId).execute(testEnv.client); + + assertThat(contents.toStringUtf8()).isEqualTo("[e2e::FileCreateTransaction]" + Contents.BIG_CONTENTS); + + info = new FileInfoQuery().setFileId(fileId).execute(testEnv.client); + + assertThat(info.fileId).isEqualTo(fileId); + assertThat(info.size).isEqualTo(13522); + assertThat(info.isDeleted).isFalse(); + assertThat(info.keys).isNotNull(); + assertThat(info.keys.getThreshold()).isNull(); + assertThat(info.keys).isEqualTo(KeyList.of(testEnv.operatorKey)); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/FileContentsIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/FileContentsIntegrationTest.java new file mode 100644 index 0000000000..ffb2d6915a --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/FileContentsIntegrationTest.java @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Objects; +import org.hiero.sdk.FileContentsQuery; +import org.hiero.sdk.FileCreateTransaction; +import org.hiero.sdk.FileDeleteTransaction; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.MaxQueryPaymentExceededException; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.Status; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class FileContentsIntegrationTest { + + @Test + @DisplayName("Can query file contents") + void canQueryFileContents() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents("[e2e::FileCreateTransaction]") + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + var contents = new FileContentsQuery().setFileId(fileId).execute(testEnv.client); + + assertThat(contents.toStringUtf8()).isEqualTo("[e2e::FileCreateTransaction]"); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can query empty file contents") + void canQueryEmptyFileContents() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = + new FileCreateTransaction().setKeys(testEnv.operatorKey).execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + var contents = new FileContentsQuery().setFileId(fileId).execute(testEnv.client); + + assertThat(contents.size()).isEqualTo(0); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot query file contents when file ID is not set") + void cannotQueryFileContentsWhenFileIDIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new FileContentsQuery().execute(testEnv.client); + }) + .withMessageContaining(Status.INVALID_FILE_ID.toString()); + } + } + + @Test + @DisplayName("Can get cost, even with a big max") + void getCostBigMaxQueryFileContents() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents("[e2e::FileCreateTransaction]") + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + var contentsQuery = new FileContentsQuery().setFileId(fileId).setMaxQueryPayment(new Hbar(1000)); + + var contents = contentsQuery.execute(testEnv.client); + + assertThat(contents.toStringUtf8()).isEqualTo("[e2e::FileCreateTransaction]"); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Error, max is smaller than set payment.") + void getCostSmallMaxQueryFileContents() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents("[e2e::FileCreateTransaction]") + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + var contentsQuery = new FileContentsQuery().setFileId(fileId).setMaxQueryPayment(Hbar.fromTinybars(1)); + + assertThatExceptionOfType(MaxQueryPaymentExceededException.class).isThrownBy(() -> { + contentsQuery.execute(testEnv.client); + }); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Insufficient tx fee error.") + void getCostInsufficientTxFeeQueryFileContents() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents("[e2e::FileCreateTransaction]") + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + var contentsQuery = new FileContentsQuery().setFileId(fileId).setMaxQueryPayment(new Hbar(100)); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + contentsQuery.setQueryPayment(Hbar.fromTinybars(1)).execute(testEnv.client); + }) + .satisfies(error -> assertThat(error.status.toString()).isEqualTo("INSUFFICIENT_TX_FEE")); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/FileCreateIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/FileCreateIntegrationTest.java new file mode 100644 index 0000000000..bed1bb4cf3 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/FileCreateIntegrationTest.java @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Objects; +import org.hiero.sdk.FileCreateTransaction; +import org.hiero.sdk.FileDeleteTransaction; +import org.hiero.sdk.FileInfoQuery; +import org.hiero.sdk.KeyList; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class FileCreateIntegrationTest { + @Test + @DisplayName("Can create file") + void canCreateFile() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents("[e2e::FileCreateTransaction]") + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + var info = new FileInfoQuery().setFileId(fileId).execute(testEnv.client); + + assertThat(info.fileId).isEqualTo(fileId); + assertThat(info.size).isEqualTo(28); + assertThat(info.isDeleted).isFalse(); + assertThat(info.keys).isNotNull(); + assertThat(info.keys.getThreshold()).isNull(); + assertThat(info.keys).isEqualTo(KeyList.of(testEnv.operatorKey)); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can create file with no contents") + void canCreateFileWithNoContents() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = + new FileCreateTransaction().setKeys(testEnv.operatorKey).execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + var info = new FileInfoQuery().setFileId(fileId).execute(testEnv.client); + + assertThat(info.fileId).isEqualTo(fileId); + assertThat(info.size).isEqualTo(0); + assertThat(info.isDeleted).isFalse(); + assertThat(info.keys).isNotNull(); + assertThat(info.keys.getThreshold()).isNull(); + assertThat(info.keys).isEqualTo(KeyList.of(testEnv.operatorKey)); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can create file with no keys") + void canCreateFileWithNoKeys() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction().execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + var info = new FileInfoQuery().setFileId(fileId).execute(testEnv.client); + + assertThat(info.fileId).isEqualTo(fileId); + assertThat(info.size).isEqualTo(0); + assertThat(info.isDeleted).isFalse(); + assertThat(info.keys).isNull(); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/FileDeleteIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/FileDeleteIntegrationTest.java new file mode 100644 index 0000000000..29e39f6719 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/FileDeleteIntegrationTest.java @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Objects; +import org.hiero.sdk.FileCreateTransaction; +import org.hiero.sdk.FileDeleteTransaction; +import org.hiero.sdk.FileInfoQuery; +import org.hiero.sdk.KeyList; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.sdk.Status; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class FileDeleteIntegrationTest { + @Test + @DisplayName("Can delete file") + void canDeleteFile() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents("[e2e::FileCreateTransaction]") + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + var info = new FileInfoQuery().setFileId(fileId).execute(testEnv.client); + + assertThat(info.fileId).isEqualTo(fileId); + assertThat(info.size).isEqualTo(28); + assertThat(info.isDeleted).isFalse(); + assertThat(info.keys).isNotNull(); + assertThat(info.keys.getThreshold()).isNull(); + assertThat(info.keys).isEqualTo(KeyList.of(testEnv.operatorKey)); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot delete immutable file") + void cannotDeleteImmutableFile() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setContents("[e2e::FileCreateTransaction]") + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + var info = new FileInfoQuery().setFileId(fileId).execute(testEnv.client); + + assertThat(info.fileId).isEqualTo(fileId); + assertThat(info.size).isEqualTo(28); + assertThat(info.isDeleted).isFalse(); + assertThat(info.keys).isNull(); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.UNAUTHORIZED.toString()); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/FileInfoIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/FileInfoIntegrationTest.java new file mode 100644 index 0000000000..b2d08c9519 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/FileInfoIntegrationTest.java @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Objects; +import org.hiero.sdk.FileCreateTransaction; +import org.hiero.sdk.FileDeleteTransaction; +import org.hiero.sdk.FileInfoQuery; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.KeyList; +import org.hiero.sdk.MaxQueryPaymentExceededException; +import org.hiero.sdk.PrecheckStatusException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class FileInfoIntegrationTest { + @Test + @DisplayName("Can query file info") + void canQueryFileInfo() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents("[e2e::FileCreateTransaction]") + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + var info = new FileInfoQuery().setFileId(fileId).execute(testEnv.client); + + assertThat(info.fileId).isEqualTo(fileId); + assertThat(info.size).isEqualTo(28); + assertThat(info.isDeleted).isFalse(); + assertThat(info.keys).isNotNull(); + assertThat(info.keys.getThreshold()).isNull(); + assertThat(info.keys).isEqualTo(KeyList.of(testEnv.operatorKey)); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can query file info with no admin key or contents") + void canQueryFileInfoWithNoAdminKeyOrContents() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction().execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + var info = new FileInfoQuery().setFileId(fileId).execute(testEnv.client); + + assertThat(info.fileId).isEqualTo(fileId); + assertThat(info.size).isEqualTo(0); + assertThat(info.isDeleted).isFalse(); + assertThat(info.keys).isNull(); + } + } + + @Test + @DisplayName("Can get cost, even with a big max") + @SuppressWarnings("UnusedVariable") + void getCostBigMaxQueryFileInfo() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents("[e2e::FileCreateTransaction]") + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + var infoQuery = new FileInfoQuery().setFileId(fileId).setMaxQueryPayment(new Hbar(1000)); + + var cost = infoQuery.getCost(testEnv.client); + + var info = infoQuery.setQueryPayment(cost).execute(testEnv.client); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Error, max is smaller than set payment.") + void getCostSmallMaxQueryFileInfo() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents("[e2e::FileCreateTransaction]") + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + var infoQuery = new FileInfoQuery().setFileId(fileId).setMaxQueryPayment(Hbar.fromTinybars(1)); + + assertThatExceptionOfType(MaxQueryPaymentExceededException.class).isThrownBy(() -> { + infoQuery.execute(testEnv.client); + }); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Insufficient tx fee error.") + void getCostInsufficientTxFeeQueryFileInfo() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents("[e2e::FileCreateTransaction]") + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + var infoQuery = new FileInfoQuery().setFileId(fileId).setMaxQueryPayment(Hbar.fromTinybars(1)); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + infoQuery.setQueryPayment(Hbar.fromTinybars(1)).execute(testEnv.client); + }) + .satisfies(error -> assertThat(error.status.toString()).isEqualTo("INSUFFICIENT_TX_FEE")); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/FileUpdateIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/FileUpdateIntegrationTest.java new file mode 100644 index 0000000000..8f6d72c9b0 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/FileUpdateIntegrationTest.java @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Objects; +import org.hiero.sdk.AccountId; +import org.hiero.sdk.FileCreateTransaction; +import org.hiero.sdk.FileDeleteTransaction; +import org.hiero.sdk.FileId; +import org.hiero.sdk.FileInfoQuery; +import org.hiero.sdk.FileUpdateTransaction; +import org.hiero.sdk.KeyList; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.sdk.Status; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class FileUpdateIntegrationTest { + @Test + @DisplayName("Can update file") + void canUpdateFile() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents("[e2e::FileCreateTransaction]") + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + var info = new FileInfoQuery().setFileId(fileId).execute(testEnv.client); + + assertThat(info.fileId).isEqualTo(fileId); + assertThat(info.size).isEqualTo(28); + assertThat(info.isDeleted).isFalse(); + assertThat(info.keys).isNotNull(); + assertThat(info.keys.getThreshold()).isNull(); + assertThat(info.keys).isEqualTo(KeyList.of(testEnv.operatorKey)); + + new FileUpdateTransaction() + .setFileId(fileId) + .setContents("[e2e::FileUpdateTransaction]") + .execute(testEnv.client) + .getReceipt(testEnv.client); + + info = new FileInfoQuery().setFileId(fileId).execute(testEnv.client); + + assertThat(info.fileId).isEqualTo(fileId); + assertThat(info.size).isEqualTo(28); + assertThat(info.isDeleted).isFalse(); + assertThat(info.keys).isNotNull(); + assertThat(info.keys.getThreshold()).isNull(); + assertThat(info.keys).isEqualTo(KeyList.of(testEnv.operatorKey)); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot update immutable file") + void cannotUpdateImmutableFile() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new FileCreateTransaction() + .setContents("[e2e::FileCreateTransaction]") + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + var info = new FileInfoQuery().setFileId(fileId).execute(testEnv.client); + + assertThat(info.fileId).isEqualTo(fileId); + assertThat(info.size).isEqualTo(28); + assertThat(info.isDeleted).isFalse(); + assertThat(info.keys).isNull(); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new FileUpdateTransaction() + .setFileId(fileId) + .setContents("[e2e::FileUpdateTransaction]") + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.UNAUTHORIZED.toString()); + } + } + + @Test + @DisplayName("Cannot update file when file ID is not set") + void cannotUpdateFileWhenFileIDIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new FileUpdateTransaction() + .setContents("[e2e::FileUpdateTransaction]") + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_FILE_ID.toString()); + } + } + + @Test + @DisplayName("Can update fee schedule file") + void canUpdateFeeScheduleFile() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + testEnv.client.setOperator( + new AccountId(0, 0, 2), + PrivateKey.fromString( + "302e020100300506032b65700422042091132178e72057a1d7528025956fe39b0b847f200ab59b2fdd367017f3087137")); + + var fileId = new FileId(0, 0, 111); + var receipt = new FileUpdateTransaction() + .setFileId(fileId) + .setContents("[e2e::FileUpdateTransaction]") + .execute(testEnv.client) + .getReceipt(testEnv.client); + + assertThat(receipt.status).isEqualTo(Status.FEE_SCHEDULE_FILE_PART_UPLOADED); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/IntegrationTestEnv.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/IntegrationTestEnv.java new file mode 100644 index 0000000000..36fa20163c --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/IntegrationTestEnv.java @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import org.hiero.sdk.AccountBalanceQuery; +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.AccountId; +import org.hiero.sdk.Client; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.PublicKey; +import org.hiero.sdk.TransferTransaction; +import org.junit.jupiter.api.Assumptions; + +public class IntegrationTestEnv implements AutoCloseable { + static final String LOCAL_CONSENSUS_NODE_ENDPOINT = "127.0.0.1:50211"; + static final String LOCAL_MIRROR_NODE_GRPC_ENDPOINT = "127.0.0.1:5600"; + static final AccountId LOCAL_CONSENSUS_NODE_ACCOUNT_ID = new AccountId(3); + private final Client originalClient; + public Client client; + public PublicKey operatorKey; + public AccountId operatorId; + public boolean isLocalNode = false; + private static ExecutorService clientExecutor = + Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); + + public IntegrationTestEnv() throws Exception { + this(0); + } + + @SuppressWarnings("EmptyCatch") + public IntegrationTestEnv(int maxNodesPerTransaction) throws Exception { + client = createTestEnvClient(); + + if (maxNodesPerTransaction == 0) { + maxNodesPerTransaction = client.getNetwork().size(); + } + + client.setMaxNodesPerTransaction(maxNodesPerTransaction); + originalClient = client; + + try { + var operatorPrivateKey = PrivateKey.fromString(System.getProperty("OPERATOR_KEY")); + operatorId = AccountId.fromString(System.getProperty("OPERATOR_ID")); + operatorKey = operatorPrivateKey.getPublicKey(); + + client.setOperator(operatorId, operatorPrivateKey); + } catch (RuntimeException ignored) { + } + + operatorKey = client.getOperatorPublicKey(); + operatorId = client.getOperatorAccountId(); + + assertThat(client.getOperatorAccountId()).isNotNull(); + assertThat(client.getOperatorPublicKey()).isNotNull(); + + if (client.getNetwork().size() > 0 && (client.getNetwork().containsKey(LOCAL_CONSENSUS_NODE_ENDPOINT))) { + isLocalNode = true; + } + + var nodeGetter = new TestEnvNodeGetter(client); + var network = new HashMap(); + + var nodeCount = Math.min(client.getNetwork().size(), maxNodesPerTransaction); + for (int i = 0; i < nodeCount; i++) { + nodeGetter.nextNode(network); + } + client.setNetwork(network); + } + + @SuppressWarnings("EmptyCatch") + private static Client createTestEnvClient() throws Exception { + if (System.getProperty("HEDERA_NETWORK").equals("previewnet")) { + return Client.forPreviewnet(); + } else if (System.getProperty("HEDERA_NETWORK").equals("testnet")) { + return Client.forTestnet(); + } else if (System.getProperty("HEDERA_NETWORK").equals("localhost")) { + var network = new HashMap(); + network.put(LOCAL_CONSENSUS_NODE_ENDPOINT, LOCAL_CONSENSUS_NODE_ACCOUNT_ID); + + return Client.forNetwork(network, clientExecutor) + .setMirrorNetwork(List.of(LOCAL_MIRROR_NODE_GRPC_ENDPOINT)); + } else if (!System.getProperty("CONFIG_FILE").equals("")) { + try { + return Client.fromConfigFile(System.getProperty("CONFIG_FILE")); + } catch (Exception configFileException) { + configFileException.printStackTrace(); + } + } + throw new IllegalStateException("Failed to construct client for IntegrationTestEnv"); + } + + public IntegrationTestEnv useThrowawayAccount(Hbar initialBalance) throws Exception { + var key = PrivateKey.generateED25519(); + operatorKey = key.getPublicKey(); + operatorId = new AccountCreateTransaction() + .setInitialBalance(initialBalance) + .setKey(key) + .execute(client) + .getReceipt(client) + .accountId; + + client = Client.forNetwork(originalClient.getNetwork()); + client.setMirrorNetwork(originalClient.getMirrorNetwork()); + client.setOperator(Objects.requireNonNull(operatorId), key); + client.setLedgerId(originalClient.getLedgerId()); + return this; + } + + public IntegrationTestEnv useThrowawayAccount() throws Exception { + return useThrowawayAccount(new Hbar(50)); + } + + // Note: this is a temporary workaround. + // The assumption should be removed once the local node is supporting multiple nodes. + public void assumeNotLocalNode() throws Exception { + // first clean up the current IntegrationTestEnv... + if (isLocalNode) { + close(); + } + + // then skip the current test + Assumptions.assumeFalse(isLocalNode); + } + + @Override + public void close() throws Exception { + if (!operatorId.equals(originalClient.getOperatorAccountId())) { + try { + var hbarsBalance = + new AccountBalanceQuery().setAccountId(operatorId).execute(originalClient).hbars; + new TransferTransaction() + .addHbarTransfer(operatorId, hbarsBalance.negated()) + .addHbarTransfer(Objects.requireNonNull(originalClient.getOperatorAccountId()), hbarsBalance) + .freezeWith(originalClient) + .signWithOperator(client) + .execute(originalClient) + .getReceipt(originalClient); + + } catch (Exception e) { + client.close(); + } + } + originalClient.close(); + } + + private static class TestEnvNodeGetter { + private final Client client; + private final List> nodes; + private int index = 0; + + public TestEnvNodeGetter(Client client) { + this.client = client; + nodes = new ArrayList<>(client.getNetwork().entrySet()); + Collections.shuffle(nodes); + } + + public void nextNode(Map outMap) throws Exception { + if (nodes.isEmpty()) { + throw new IllegalStateException( + "IntegrationTestEnv needs another node, but there aren't enough nodes in client network"); + } + for (; index < nodes.size(); index++) { + var node = nodes.get(index); + try { + new TransferTransaction() + .setNodeAccountIds(Collections.singletonList(node.getValue())) + .setMaxAttempts(1) + .addHbarTransfer( + client.getOperatorAccountId(), + Hbar.fromTinybars(1).negated()) + .addHbarTransfer(AccountId.fromString("0.0.3"), Hbar.fromTinybars(1)) + .execute(client) + .getReceipt(client); + nodes.remove(index); + outMap.put(node.getKey(), node.getValue()); + return; + } catch (Throwable err) { + System.err.println(err); + } + } + throw new Exception("Failed to find working node in " + nodes + " for IntegrationTestEnv"); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/LiveHashAddIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/LiveHashAddIntegrationTest.java new file mode 100644 index 0000000000..023fba5f14 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/LiveHashAddIntegrationTest.java @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.time.Duration; +import java.util.Objects; +import org.bouncycastle.util.encoders.Hex; +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.LiveHashAddTransaction; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.Status; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class LiveHashAddIntegrationTest { + private static final byte[] HASH = Hex.decode( + "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002"); + + @Test + @DisplayName("Cannot create live hash because it's not supported") + void cannotCreateLiveHashBecauseItsNotSupported() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new LiveHashAddTransaction() + .setAccountId(accountId) + .setDuration(Duration.ofDays(30)) + .setHash(HASH) + .setKeys(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.NOT_SUPPORTED.toString()); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/LiveHashDeleteIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/LiveHashDeleteIntegrationTest.java new file mode 100644 index 0000000000..10b93fb0af --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/LiveHashDeleteIntegrationTest.java @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Objects; +import org.bouncycastle.util.encoders.Hex; +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.LiveHashDeleteTransaction; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.Status; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class LiveHashDeleteIntegrationTest { + private static final byte[] HASH = Hex.decode( + "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002"); + + @Test + @DisplayName("Cannot delete live hash because it's not supported") + void cannotDeleteLiveHashBecauseItsNotSupported() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new LiveHashDeleteTransaction() + .setAccountId(accountId) + .setHash(HASH) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.NOT_SUPPORTED.toString()); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/LoadIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/LoadIntegrationTest.java new file mode 100644 index 0000000000..9908678452 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/LoadIntegrationTest.java @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.AccountId; +import org.hiero.sdk.Client; +import org.hiero.sdk.PrivateKey; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class LoadIntegrationTest { + + @Test + @DisplayName("Load test with multiple clients and single executor") + void loadTest() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var operatorPrivateKey = PrivateKey.fromString(System.getProperty("OPERATOR_KEY")); + var operatorId = AccountId.fromString(System.getProperty("OPERATOR_ID")); + + int nThreads = 10; + var clientExecutor = Executors.newFixedThreadPool(16); + + var threadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(nThreads); + + long startTime = System.currentTimeMillis(); + + System.out.println("Finished executing tasks:"); + for (int i = 0; i < nThreads; i++) { + int finalI = i; + threadPoolExecutor.submit(() -> { + try (var client = Client.forNetwork(testEnv.client.getNetwork(), clientExecutor); ) { + client.setOperator(operatorId, operatorPrivateKey); + client.setMaxAttempts(10); + new AccountCreateTransaction() + .setKey(PrivateKey.generateED25519()) + .execute(client) + .getReceipt(client); + System.out.println(finalI); + } catch (Exception e) { + fail("AccountCreateTransaction failed, " + e); + } + }); + } + + threadPoolExecutor.shutdown(); + + // Wait for all tasks to finish + try { + if (!threadPoolExecutor.awaitTermination(60, TimeUnit.SECONDS)) { + System.out.println(); + System.out.println("Forcing shutdown"); + threadPoolExecutor.shutdownNow(); + } + } catch (InterruptedException e) { + threadPoolExecutor.shutdownNow(); + } + + long endTime = System.currentTimeMillis(); + long executionTime = endTime - startTime; + System.out.println("All tasks have finished execution in " + executionTime + "ms"); + clientExecutor.shutdownNow(); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/MirrorNodeContractQueryIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/MirrorNodeContractQueryIntegrationTest.java new file mode 100644 index 0000000000..5d02331670 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/MirrorNodeContractQueryIntegrationTest.java @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.hiero.sdk.EntityIdHelper.getEvmAddressFromMirrorNodeAsync; + +import java.util.Objects; +import java.util.concurrent.ExecutionException; +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.ContractCallQuery; +import org.hiero.sdk.ContractCreateTransaction; +import org.hiero.sdk.ContractDeleteTransaction; +import org.hiero.sdk.ContractExecuteTransaction; +import org.hiero.sdk.ContractFunctionParameters; +import org.hiero.sdk.ContractId; +import org.hiero.sdk.FileCreateTransaction; +import org.hiero.sdk.FileDeleteTransaction; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.MirrorNodeContractCallQuery; +import org.hiero.sdk.MirrorNodeContractEstimateGasQuery; +import org.hiero.sdk.PrivateKey; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class MirrorNodeContractQueryIntegrationTest { + private static final String SMART_CONTRACT_BYTECODE = + "6080604052348015600e575f80fd5b50335f806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506104a38061005b5f395ff3fe608060405260043610610033575f3560e01c8063607a4427146100375780637065cb4814610053578063893d20e81461007b575b5f80fd5b610051600480360381019061004c919061033c565b6100a5565b005b34801561005e575f80fd5b50610079600480360381019061007491906103a2565b610215565b005b348015610086575f80fd5b5061008f6102b7565b60405161009c91906103dc565b60405180910390f35b3373ffffffffffffffffffffffffffffffffffffffff165f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146100fb575f80fd5b805f806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600181908060018154018082558091505060019003905f5260205f20015f9091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f8173ffffffffffffffffffffffffffffffffffffffff166108fc3490811502906040515f60405180830381858888f19350505050905080610211576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102089061044f565b60405180910390fd5b5050565b805f806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600181908060018154018082558091505060019003905f5260205f20015f9091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b5f805f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61030b826102e2565b9050919050565b61031b81610301565b8114610325575f80fd5b50565b5f8135905061033681610312565b92915050565b5f60208284031215610351576103506102de565b5b5f61035e84828501610328565b91505092915050565b5f610371826102e2565b9050919050565b61038181610367565b811461038b575f80fd5b50565b5f8135905061039c81610378565b92915050565b5f602082840312156103b7576103b66102de565b5b5f6103c48482850161038e565b91505092915050565b6103d681610367565b82525050565b5f6020820190506103ef5f8301846103cd565b92915050565b5f82825260208201905092915050565b7f5472616e73666572206661696c656400000000000000000000000000000000005f82015250565b5f610439600f836103f5565b915061044482610405565b602082019050919050565b5f6020820190508181035f8301526104668161042d565b905091905056fea26469706673582212206c46ddb2acdbcc4290e15be83eb90cd0b2ce5bd82b9bfe58a0709c5aec96305564736f6c634300081a0033"; + private static final String ADDRESS = "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"; + + @Test + @DisplayName("Can estimate and simulate transaction") + void canSimulateTransaction() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + response = new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(200000) + .setBytecodeFileId(fileId) + .execute(testEnv.client); + + var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); + + // Wait for mirror node to import data + Thread.sleep(2000); + + var gas = new MirrorNodeContractEstimateGasQuery() + .setContractId(contractId) + .setFunction("getOwner") + .execute(testEnv.client); + + var result = new ContractCallQuery() + .setContractId(contractId) + .setGas(gas) + .setFunction("getOwner") + .setQueryPayment(new Hbar(1)) + .execute(testEnv.client); + + var simulationResult = new MirrorNodeContractCallQuery() + .setContractId(contractId) + .setFunction("getOwner") + .execute(testEnv.client); + + assertThat(result.getAddress(0)).isEqualTo(simulationResult.substring(26)); + + gas = new MirrorNodeContractEstimateGasQuery() + .setContractId(contractId) + .setFunction("addOwner", new ContractFunctionParameters().addAddress(ADDRESS)) + .execute(testEnv.client); + + new ContractExecuteTransaction() + .setContractId(contractId) + .setGas(gas) + .setFunction("addOwner", new ContractFunctionParameters().addAddress(ADDRESS)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new MirrorNodeContractCallQuery() + .setContractId(contractId) + .setFunction("addOwner", new ContractFunctionParameters().addAddress(ADDRESS)) + .execute(testEnv.client); + + new ContractDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setContractId(contractId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new FileDeleteTransaction() + .setFileId(fileId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Fails when contract is not deployed") + void failsWhenContractIsNotDeployed() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + var contractId = new ContractId(1231456); + + assertThatExceptionOfType(ExecutionException.class) + .isThrownBy(() -> { + new MirrorNodeContractEstimateGasQuery() + .setContractId(contractId) + .setFunction("getOwner") + .execute(testEnv.client); + }) + .withMessageContaining("Received non-200 response from Mirror Node"); + + assertThatExceptionOfType(ExecutionException.class) + .isThrownBy(() -> { + new MirrorNodeContractCallQuery() + .setContractId(contractId) + .setFunction("getOwner") + .execute(testEnv.client); + }) + .withMessageContaining("Received non-200 response from Mirror Node"); + } + } + + @Test + @DisplayName("Fails when gas limit is low") + void failsWhenGasLimitIsLow() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + response = new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(200000) + .setBytecodeFileId(fileId) + .execute(testEnv.client); + + var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); + + // Wait for mirror node to import data + Thread.sleep(2000); + + assertThatExceptionOfType(ExecutionException.class) + .isThrownBy(() -> { + new MirrorNodeContractEstimateGasQuery() + .setContractId(contractId) + .setGasLimit(100) + .setFunction( + "addOwnerAndTransfer", new ContractFunctionParameters().addAddress(ADDRESS)) + .execute(testEnv.client); + }) + .withMessageContaining("Received non-200 response from Mirror Node"); + + assertThatExceptionOfType(ExecutionException.class) + .isThrownBy(() -> { + new MirrorNodeContractCallQuery() + .setContractId(contractId) + .setGasLimit(100) + .setFunction( + "addOwnerAndTransfer", new ContractFunctionParameters().addAddress(ADDRESS)) + .execute(testEnv.client); + }) + .withMessageContaining("Received non-200 response from Mirror Node"); + } + } + + @Test + @DisplayName("Fails when sender is not set") + void failsWhenSenderIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + response = new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(200000) + .setBytecodeFileId(fileId) + .execute(testEnv.client); + + var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); + + // Wait for mirror node to import data + Thread.sleep(2000); + + assertThatExceptionOfType(ExecutionException.class) + .isThrownBy(() -> { + new MirrorNodeContractEstimateGasQuery() + .setContractId(contractId) + .setFunction( + "addOwnerAndTransfer", new ContractFunctionParameters().addAddress(ADDRESS)) + .execute(testEnv.client); + }) + .withMessageContaining("Received non-200 response from Mirror Node"); + + assertThatExceptionOfType(ExecutionException.class) + .isThrownBy(() -> { + new MirrorNodeContractCallQuery() + .setContractId(contractId) + .setFunction( + "addOwnerAndTransfer", new ContractFunctionParameters().addAddress(ADDRESS)) + .execute(testEnv.client); + }) + .withMessageContaining("Received non-200 response from Mirror Node"); + } + } + + @Test + @DisplayName("Can simulate with sender set") + void canSimulateWithSenderSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + var response = new FileCreateTransaction() + .setKeys(testEnv.operatorKey) + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client); + + var fileId = Objects.requireNonNull(response.getReceipt(testEnv.client).fileId); + + response = new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(200000) + .setBytecodeFileId(fileId) + .execute(testEnv.client); + + var contractId = Objects.requireNonNull(response.getReceipt(testEnv.client).contractId); + + var receiverAccountId = new AccountCreateTransaction() + .setKey(PrivateKey.generateED25519()) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + // Wait for mirror node to import data + Thread.sleep(2000); + + var receiverEvmAddress = getEvmAddressFromMirrorNodeAsync(testEnv.client, receiverAccountId.num) + .get() + .toString(); + + var owner = new MirrorNodeContractCallQuery() + .setContractId(contractId) + .setFunction("getOwner") + .execute(testEnv.client) + .substring(26); + + var gas = new MirrorNodeContractEstimateGasQuery() + .setContractId(contractId) + .setGasLimit(1_000_000) + .setFunction("addOwnerAndTransfer", new ContractFunctionParameters().addAddress(receiverEvmAddress)) + .setSenderEvmAddress(owner) + .setValue(123) + .execute(testEnv.client); + + new ContractExecuteTransaction() + .setContractId(contractId) + .setGas(gas) + .setPayableAmount(new Hbar(1)) + .setFunction("addOwnerAndTransfer", new ContractFunctionParameters().addAddress(receiverEvmAddress)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new MirrorNodeContractCallQuery() + .setContractId(contractId) + .setGasLimit(1_000_000) + .setFunction("addOwnerAndTransfer", new ContractFunctionParameters().addAddress(receiverEvmAddress)) + .setSenderEvmAddress(owner) + .setValue(123) + .execute(testEnv.client); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/NetworkVersionInfoIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/NetworkVersionInfoIntegrationTest.java new file mode 100644 index 0000000000..28ca4ed71a --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/NetworkVersionInfoIntegrationTest.java @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import org.hiero.sdk.NetworkVersionInfoQuery; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class NetworkVersionInfoIntegrationTest { + @Test + @DisplayName("Cannot query network version info") + void cannotQueryNetworkVersionInfo() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + new NetworkVersionInfoQuery().execute(testEnv.client); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/NftAllowancesIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/NftAllowancesIntegrationTest.java new file mode 100644 index 0000000000..324f0cd459 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/NftAllowancesIntegrationTest.java @@ -0,0 +1,388 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.nio.charset.StandardCharsets; +import java.util.List; +import org.hiero.sdk.AccountAllowanceApproveTransaction; +import org.hiero.sdk.AccountAllowanceDeleteTransaction; +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.NftId; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.sdk.Status; +import org.hiero.sdk.TokenAssociateTransaction; +import org.hiero.sdk.TokenCreateTransaction; +import org.hiero.sdk.TokenId; +import org.hiero.sdk.TokenMintTransaction; +import org.hiero.sdk.TokenNftInfoQuery; +import org.hiero.sdk.TokenType; +import org.hiero.sdk.TransactionId; +import org.hiero.sdk.TransferTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class NftAllowancesIntegrationTest { + @Test + @DisplayName("Cannot transfer on behalf of `spender` account without allowance approval") + void cannotTransferWithoutAllowanceApproval() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var spenderKey = PrivateKey.generateED25519(); + var spenderAccountId = new AccountCreateTransaction() + .setKey(spenderKey) + .setInitialBalance(new Hbar(2)) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + var receiverKey = PrivateKey.generateED25519(); + var receiverAccountId = new AccountCreateTransaction() + .setKey(receiverKey) + .setMaxAutomaticTokenAssociations(10) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + TokenId nftTokenId = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .freezeWith(testEnv.client) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId; + + new TokenAssociateTransaction() + .setAccountId(spenderAccountId) + .setTokenIds(List.of(nftTokenId)) + .freezeWith(testEnv.client) + .sign(spenderKey) + .execute(testEnv.client); + + var serials = new TokenMintTransaction() + .setTokenId(nftTokenId) + .addMetadata("asd".getBytes(StandardCharsets.UTF_8)) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .serials; + + var nft1 = new NftId(nftTokenId, serials.get(0)); + + var onBehalfOfTransactionId = TransactionId.generate(spenderAccountId); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> new TransferTransaction() + .addApprovedNftTransfer(nft1, testEnv.operatorId, receiverAccountId) + .setTransactionId(onBehalfOfTransactionId) + .freezeWith(testEnv.client) + .sign(spenderKey) + .execute(testEnv.client) + .getReceipt(testEnv.client)) + .withMessageContaining(Status.SPENDER_DOES_NOT_HAVE_ALLOWANCE.toString()); + } + } + + @Test + @DisplayName("Cannot transfer on behalf of `spender` account after removing the allowance approval") + void cannotTransferAfterAllowanceRemove() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var spenderKey = PrivateKey.generateED25519(); + var spenderAccountId = new AccountCreateTransaction() + .setKey(spenderKey) + .setInitialBalance(new Hbar(2)) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + var receiverKey = PrivateKey.generateED25519(); + var receiverAccountId = new AccountCreateTransaction() + .setKey(receiverKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + TokenId nftTokenId = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .freezeWith(testEnv.client) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId; + + new TokenAssociateTransaction() + .setAccountId(spenderAccountId) + .setTokenIds(List.of(nftTokenId)) + .freezeWith(testEnv.client) + .sign(spenderKey) + .execute(testEnv.client); + + new TokenAssociateTransaction() + .setAccountId(receiverAccountId) + .setTokenIds(List.of(nftTokenId)) + .freezeWith(testEnv.client) + .sign(receiverKey) + .execute(testEnv.client); + + var serials = new TokenMintTransaction() + .setTokenId(nftTokenId) + .addMetadata("asd1".getBytes(StandardCharsets.UTF_8)) + .addMetadata("asd2".getBytes(StandardCharsets.UTF_8)) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .serials; + + var nft1 = new NftId(nftTokenId, serials.get(0)); + var nft2 = new NftId(nftTokenId, serials.get(1)); + + new AccountAllowanceApproveTransaction() + .approveTokenNftAllowance(nft1, testEnv.operatorId, spenderAccountId) + .approveTokenNftAllowance(nft2, testEnv.operatorId, spenderAccountId) + .execute(testEnv.client); + + new AccountAllowanceDeleteTransaction() + .deleteAllTokenNftAllowances(nft2, testEnv.operatorId) + .execute(testEnv.client); + + var onBehalfOfTransactionId = TransactionId.generate(spenderAccountId); + + new TransferTransaction() + .addApprovedNftTransfer(nft1, testEnv.operatorId, receiverAccountId) + .setTransactionId(onBehalfOfTransactionId) + .freezeWith(testEnv.client) + .sign(spenderKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var info = new TokenNftInfoQuery().setNftId(nft1).execute(testEnv.client); + assertThat(info.get(0).accountId).isEqualTo(receiverAccountId); + + var onBehalfOfTransactionId2 = TransactionId.generate(spenderAccountId); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> new TransferTransaction() + .addApprovedNftTransfer(nft2, testEnv.operatorId, receiverAccountId) + .setTransactionId(onBehalfOfTransactionId2) + .freezeWith(testEnv.client) + .sign(spenderKey) + .execute(testEnv.client) + .getReceipt(testEnv.client)) + .withMessageContaining(Status.SPENDER_DOES_NOT_HAVE_ALLOWANCE.toString()); + } + } + + @Test + @DisplayName("Cannot remove single serial number allowance when the allowance is given for all serials at once") + void cannotRemoveSingleSerialWhenAllowanceIsGivenForAll() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var spenderKey = PrivateKey.generateED25519(); + var spenderAccountId = new AccountCreateTransaction() + .setKey(spenderKey) + .setInitialBalance(new Hbar(2)) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + var receiverKey = PrivateKey.generateED25519(); + var receiverAccountId = new AccountCreateTransaction() + .setKey(receiverKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + TokenId nftTokenId = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .freezeWith(testEnv.client) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId; + + new TokenAssociateTransaction() + .setAccountId(spenderAccountId) + .setTokenIds(List.of(nftTokenId)) + .freezeWith(testEnv.client) + .sign(spenderKey) + .execute(testEnv.client); + + new TokenAssociateTransaction() + .setAccountId(receiverAccountId) + .setTokenIds(List.of(nftTokenId)) + .freezeWith(testEnv.client) + .sign(receiverKey) + .execute(testEnv.client); + + var serials = new TokenMintTransaction() + .setTokenId(nftTokenId) + .addMetadata("asd1".getBytes(StandardCharsets.UTF_8)) + .addMetadata("asd2".getBytes(StandardCharsets.UTF_8)) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .serials; + + var nft1 = new NftId(nftTokenId, serials.get(0)); + var nft2 = new NftId(nftTokenId, serials.get(1)); + + new AccountAllowanceApproveTransaction() + .approveTokenNftAllowanceAllSerials(nftTokenId, testEnv.operatorId, spenderAccountId) + .execute(testEnv.client); + + var onBehalfOfTransactionId = TransactionId.generate(spenderAccountId); + + new TransferTransaction() + .addApprovedNftTransfer(nft1, testEnv.operatorId, receiverAccountId) + .setTransactionId(onBehalfOfTransactionId) + .freezeWith(testEnv.client) + .sign(spenderKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // hopefully in the future this should end up with a precheck error provided from services + new AccountAllowanceDeleteTransaction() + .deleteAllTokenNftAllowances(nft2, testEnv.operatorId) + .execute(testEnv.client); + + var onBehalfOfTransactionId2 = TransactionId.generate(spenderAccountId); + + new TransferTransaction() + .addApprovedNftTransfer(nft2, testEnv.operatorId, receiverAccountId) + .setTransactionId(onBehalfOfTransactionId2) + .freezeWith(testEnv.client) + .sign(spenderKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var infoNft1 = new TokenNftInfoQuery().setNftId(nft1).execute(testEnv.client); + + var infoNft2 = new TokenNftInfoQuery().setNftId(nft2).execute(testEnv.client); + + assertThat(infoNft1.get(0).accountId).isEqualTo(receiverAccountId); + assertThat(infoNft2.get(0).accountId).isEqualTo(receiverAccountId); + } + } + + @Test + @DisplayName( + "Account, which given the allowance for all serials at once, should be able to give allowances for single serial numbers to other accounts") + void accountGivenAllowanceForAllShouldBeAbleToGiveAllowanceForSingle() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var delegatingSpenderKey = PrivateKey.generateED25519(); + var delegatingSpenderAccountId = new AccountCreateTransaction() + .setKey(delegatingSpenderKey) + .setInitialBalance(new Hbar(2)) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + var spenderKey = PrivateKey.generateED25519(); + var spenderAccountId = new AccountCreateTransaction() + .setKey(spenderKey) + .setInitialBalance(new Hbar(2)) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + var receiverKey = PrivateKey.generateED25519(); + var receiverAccountId = new AccountCreateTransaction() + .setKey(receiverKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + TokenId nftTokenId = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .freezeWith(testEnv.client) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId; + + new TokenAssociateTransaction() + .setAccountId(delegatingSpenderAccountId) + .setTokenIds(List.of(nftTokenId)) + .freezeWith(testEnv.client) + .sign(spenderKey) + .execute(testEnv.client); + + new TokenAssociateTransaction() + .setAccountId(receiverAccountId) + .setTokenIds(List.of(nftTokenId)) + .freezeWith(testEnv.client) + .sign(receiverKey) + .execute(testEnv.client); + + var serials = new TokenMintTransaction() + .setTokenId(nftTokenId) + .addMetadata("asd1".getBytes(StandardCharsets.UTF_8)) + .addMetadata("asd2".getBytes(StandardCharsets.UTF_8)) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .serials; + + var nft1 = new NftId(nftTokenId, serials.get(0)); + var nft2 = new NftId(nftTokenId, serials.get(1)); + + new AccountAllowanceApproveTransaction() + .approveTokenNftAllowanceAllSerials(nftTokenId, testEnv.operatorId, delegatingSpenderAccountId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new AccountAllowanceApproveTransaction() + .approveTokenNftAllowance(nft1, testEnv.operatorId, spenderAccountId, delegatingSpenderAccountId) + .freezeWith(testEnv.client) + .sign(delegatingSpenderKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var onBehalfOfTransactionId = TransactionId.generate(spenderAccountId); + + new TransferTransaction() + .addApprovedNftTransfer(nft1, testEnv.operatorId, receiverAccountId) + .setTransactionId(onBehalfOfTransactionId) + .freezeWith(testEnv.client) + .sign(spenderKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var onBehalfOfTransactionId2 = TransactionId.generate(spenderAccountId); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> new TransferTransaction() + .addApprovedNftTransfer(nft2, testEnv.operatorId, receiverAccountId) + .setTransactionId(onBehalfOfTransactionId2) + .freezeWith(testEnv.client) + .sign(spenderKey) + .execute(testEnv.client) + .getReceipt(testEnv.client)) + .withMessageContaining(Status.SPENDER_DOES_NOT_HAVE_ALLOWANCE.toString()); + + var infoNft1 = new TokenNftInfoQuery().setNftId(nft1).execute(testEnv.client); + + var infoNft2 = new TokenNftInfoQuery().setNftId(nft2).execute(testEnv.client); + + assertThat(infoNft1.get(0).accountId).isEqualTo(receiverAccountId); + assertThat(infoNft2.get(0).accountId).isEqualTo(testEnv.operatorId); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/NftMetadataGenerator.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/NftMetadataGenerator.java new file mode 100644 index 0000000000..6cd6c8ff0a --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/NftMetadataGenerator.java @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public class NftMetadataGenerator { + private NftMetadataGenerator() {} + + public static List generate(byte metadataCount) { + List metadatas = new ArrayList<>(); + for (byte i = 0; i < metadataCount; i++) { + byte[] md = {i}; + metadatas.add(md); + } + return metadatas; + } + + public static List generate(byte[] metadata, int count) { + return IntStream.range(0, count).mapToObj(i -> metadata.clone()).collect(Collectors.toList()); + } + + public static List generateOneLarge() { + return Collections.singletonList(new byte[101]); + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/NodeCreateTransactionIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/NodeCreateTransactionIntegrationTest.java new file mode 100644 index 0000000000..c572a18fde --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/NodeCreateTransactionIntegrationTest.java @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.hiero.sdk.test.integration.IntegrationTestEnv.LOCAL_CONSENSUS_NODE_ACCOUNT_ID; + +import java.util.HashMap; +import java.util.List; +import org.bouncycastle.util.encoders.Hex; +import org.hiero.sdk.AccountId; +import org.hiero.sdk.Client; +import org.hiero.sdk.Endpoint; +import org.hiero.sdk.NodeCreateTransaction; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.PrivateKeyECDSA; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class NodeCreateTransactionIntegrationTest { + String validGossipCertDER = + "3082052830820310a003020102020101300d06092a864886f70d01010c05003010310e300c060355040313056e6f6465333024170d3234313030383134333233395a181332313234313030383134333233392e3337395a3010310e300c060355040313056e6f64653330820222300d06092a864886f70d01010105000382020f003082020a0282020100af111cff0c4ad8125d2f4b8691ce87332fecc867f7a94ddc0f3f96514cc4224d44af516394f7384c1ef0a515d29aa6116b65bc7e4d7e2d848cf79fbfffedae3a6583b3957a438bdd780c4981b800676ea509bc8c619ae04093b5fc642c4484152f0e8bcaabf19eae025b630028d183a2f47caf6d9f1075efb30a4248679d871beef1b7e9115382270cbdb68682fae4b1fd592cadb414d918c0a8c23795c7c5a91e22b3e90c410825a2bc1a840efc5bf9976a7f474c7ed7dc047e4ddd2db631b68bb4475f173baa3edc234c4bed79c83e2f826f79e07d0aade2d984da447a8514135bfa4145274a7f62959a23c4f0fae5adc6855974e7c04164951d052beb5d45cb1f3cdfd005da894dea9151cb62ba43f4731c6bb0c83e10fd842763ba6844ef499f71bc67fa13e4917fb39f2ad18112170d31cdcb3c61c9e3253accf703dbd8427fdcb87ece78b787b6cfdc091e8fedea8ad95dc64074e1fc6d0e42ea2337e18a5e54e4aaab3791a98dfcef282e2ae1caec9cf986fabe8f36e6a21c8711647177e492d264415e765a86c58599cd97b103cb4f6a01d2edd06e3b60470cf64daca7aecf831197b466cae04baeeac19840a05394bef628aed04b611cfa13677724b08ddfd662b02fd0ef0af17eb7f4fb8c1c17fbe9324f6dc7bcc02449622636cc45ec04909b3120ab4df4726b21bf79e955fe8f832699d2196dcd7a58bfeafb170203010001a38186308183300f0603551d130101ff04053003020100300e0603551d0f0101ff0404030204b030200603551d250101ff0416301406082b0601050507030106082b06010505070302301d0603551d0e04160414643118e05209035edd83d44a0c368de2fb2fe4c0301f0603551d23041830168014643118e05209035edd83d44a0c368de2fb2fe4c0300d06092a864886f70d01010c05000382020100ad41c32bb52650eb4b76fce439c9404e84e4538a94916b3dc7983e8b5c58890556e7384601ca7440dde68233bb07b97bf879b64487b447df510897d2a0a4e789c409a9b237a6ad240ad5464f2ce80c58ddc4d07a29a74eb25e1223db6c00e334d7a27d32bfa6183a82f5e35bccf497c2445a526eabb0c068aba9b94cc092ea4756b0dcfb574f6179f0089e52b174ccdbd04123eeb6d70daeabd8513fcba6be0bc2b45ca9a69802dae11cc4d9ff6053b3a87fd8b0c6bf72fffc3b81167f73cca2b3fd656c5d353c8defca8a76e2ad535f984870a590af4e28fed5c5a125bf360747c5e7742e7813d1bd39b5498c8eb6ba72f267eda034314fdbc596f6b967a0ef8be5231d364e634444c84e64bd7919425171016fcd9bb05f01c58a303dee28241f6e860fc3aac3d92aad7dac2801ce79a3b41a0e1f1509fc0d86e96d94edb18616c000152490f64561713102128990fedd3a5fa642f2ff22dc11bc4dc5b209986a0c3e4eb2bdfdd40e9fdf246f702441cac058dd8d0d51eb0796e2bea2ce1b37b2a2f468505e1f8980a9f66d719df034a6fbbd2f9585991d259678fb9a4aebdc465d22c240351ed44abffbdd11b79a706fdf7c40158d3da87f68d7bd557191a8016b5b899c07bf1b87590feb4fa4203feea9a2a7a73ec224813a12b7a21e5dc93fcde4f0a7620f570d31fe27e9b8d65b74db7dc18a5e51adc42d7805d4661938"; + + @Test + @Disabled("The test has to be disabled so it doesn't fail calls to local-node") + @DisplayName("Can create new network node") + void canCreateNewNetworkNode() throws Exception { + // Set the network + var network = new HashMap(); + network.put("localhost:50211", LOCAL_CONSENSUS_NODE_ACCOUNT_ID); + + try (var client = Client.forNetwork(network).setMirrorNetwork(List.of("localhost:5600"))) { + + // Set the operator to be account 0.0.2 + var originalOperatorKey = PrivateKey.fromString( + "302e020100300506032b65700422042091132178e72057a1d7528025956fe39b0b847f200ab59b2fdd367017f3087137"); + client.setOperator(new AccountId(2), originalOperatorKey); + + // The account of the new node + var accountID = AccountId.fromString("0.0.4"); + + // Node description + String description = "test"; + + // Endpoint address can be any IPV4 address + var endpoint = new Endpoint().setDomainName("tests.com").setPort(1234); + var endpoint2 = new Endpoint().setDomainName("test.com").setPort(123); + + // Convert hex string to byte array + var validGossipCert = Hex.decode(validGossipCertDER.getBytes()); + + // Generate admin key + var adminKey = PrivateKeyECDSA.generateED25519(); + + new NodeCreateTransaction() + .setAccountId(accountID) + .setAdminKey(adminKey) + .setDescription(description) + .setGossipCaCertificate(validGossipCert) + .setGossipEndpoints(List.of(endpoint, endpoint2)) + .setServiceEndpoints(List.of(endpoint, endpoint2)) + .freezeWith(client) + .sign(adminKey) + .execute(client) + .getReceipt(client); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ReceiptQueryIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ReceiptQueryIntegrationTest.java new file mode 100644 index 0000000000..7e2f0214c1 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ReceiptQueryIntegrationTest.java @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.TransactionReceiptQuery; +import org.hiero.sdk.TransactionRecordQuery; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class ReceiptQueryIntegrationTest { + @Test + @DisplayName("Can get Receipt") + void canGetTransactionReceipt() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction().setKey(key).execute(testEnv.client); + + var receipt = new TransactionReceiptQuery() + .setTransactionId(response.transactionId) + .execute(testEnv.client); + } + } + + @Test + @DisplayName("Can get Record") + void canGetTransactionRecord() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction().setKey(key).execute(testEnv.client); + + new TransactionReceiptQuery() + .setTransactionId(response.transactionId) + .execute(testEnv.client); + + new TransactionRecordQuery() + .setTransactionId(response.transactionId) + .execute(testEnv.client); + } + } + + @Test + @DisplayName("Can get Record cost") + @SuppressWarnings("UnusedVariable") + void getCostTransactionRecord() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction().setKey(key).execute(testEnv.client); + + new TransactionReceiptQuery() + .setTransactionId(response.transactionId) + .execute(testEnv.client); + + var recordQuery = new TransactionRecordQuery().setTransactionId(response.transactionId); + + recordQuery.getCost(testEnv.client); + recordQuery.execute(testEnv.client); + } + } + + @Test + @DisplayName("Can get Record cost with big max set") + @SuppressWarnings("UnusedVariable") + void getCostBigMaxTransactionRecord() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction().setKey(key).execute(testEnv.client); + + new TransactionReceiptQuery() + .setTransactionId(response.transactionId) + .execute(testEnv.client); + + var recordQuery = new TransactionRecordQuery() + .setTransactionId(response.transactionId) + .setMaxQueryPayment(new Hbar(1000)); + + recordQuery.getCost(testEnv.client); + + recordQuery.execute(testEnv.client); + } + } + + @Test + @DisplayName("Error at very small max, getRecord") + void getCostSmallMaxTransactionRecord() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction().setKey(key).execute(testEnv.client); + + var receipt = new TransactionReceiptQuery() + .setTransactionId(response.transactionId) + .execute(testEnv.client); + + var recordQuery = new TransactionRecordQuery() + .setTransactionId(response.transactionId) + .setMaxQueryPayment(Hbar.fromTinybars(1)); + + var cost = recordQuery.getCost(testEnv.client); + + assertThatExceptionOfType(RuntimeException.class) + .isThrownBy(() -> { + recordQuery.execute(testEnv.client); + }) + .withMessage("cost for TransactionRecordQuery, of " + cost.toString() + + ", without explicit payment is greater than the maximum allowed payment of 1 tℏ"); + } + } + + @Test + @DisplayName("Insufficient transaction fee error for transaction record query") + void getCostInsufficientTxFeeTransactionRecord() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction().setKey(key).execute(testEnv.client); + + var receipt = new TransactionReceiptQuery() + .setTransactionId(response.transactionId) + .execute(testEnv.client); + + var recordQuery = new TransactionRecordQuery().setTransactionId(response.transactionId); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + recordQuery.setQueryPayment(Hbar.fromTinybars(1)).execute(testEnv.client); + }) + .satisfies(error -> assertThat(error.status.toString()).isEqualTo("INSUFFICIENT_TX_FEE")); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ScheduleCreateIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ScheduleCreateIntegrationTest.java new file mode 100644 index 0000000000..bb1893840b --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/ScheduleCreateIntegrationTest.java @@ -0,0 +1,777 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.time.Instant; +import java.util.Collections; +import java.util.Objects; +import org.hiero.sdk.AccountBalanceQuery; +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.AccountDeleteTransaction; +import org.hiero.sdk.AccountId; +import org.hiero.sdk.AccountUpdateTransaction; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.KeyList; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.sdk.ScheduleCreateTransaction; +import org.hiero.sdk.ScheduleId; +import org.hiero.sdk.ScheduleInfo; +import org.hiero.sdk.ScheduleInfoQuery; +import org.hiero.sdk.ScheduleSignTransaction; +import org.hiero.sdk.Status; +import org.hiero.sdk.TokenAssociateTransaction; +import org.hiero.sdk.TokenCreateTransaction; +import org.hiero.sdk.TopicCreateTransaction; +import org.hiero.sdk.TopicMessageSubmitTransaction; +import org.hiero.sdk.TransactionId; +import org.hiero.sdk.TransactionReceipt; +import org.hiero.sdk.TransactionResponse; +import org.hiero.sdk.TransferTransaction; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class ScheduleCreateIntegrationTest { + + private final int oneDayInSecs = 86400; + + @Test + @Disabled + @DisplayName("Can create schedule") + void canCreateSchedule() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var key = PrivateKey.generateED25519(); + + var transaction = new AccountCreateTransaction().setKey(key).setInitialBalance(new Hbar(10)); + + var response = new ScheduleCreateTransaction() + .setScheduledTransaction(transaction) + .setAdminKey(testEnv.operatorKey) + .setPayerAccountId(testEnv.operatorId) + .execute(testEnv.client); + + var scheduleId = Objects.requireNonNull(response.getReceipt(testEnv.client).scheduleId); + + var info = new ScheduleInfoQuery().setScheduleId(scheduleId).execute(testEnv.client); + + assertThat(info.executedAt).isNotNull(); + } + } + + @Test + @Disabled + @DisplayName("Can get Transaction") + void canGetTransactionSchedule() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var key = PrivateKey.generateED25519(); + + var transaction = new AccountCreateTransaction().setKey(key).setInitialBalance(new Hbar(10)); + + var response = new ScheduleCreateTransaction() + .setScheduledTransaction(transaction) + .setAdminKey(testEnv.operatorKey) + .setPayerAccountId(testEnv.operatorId) + .execute(testEnv.client); + + var scheduleId = Objects.requireNonNull(response.getReceipt(testEnv.client).scheduleId); + + var info = new ScheduleInfoQuery().setScheduleId(scheduleId).execute(testEnv.client); + + assertThat(info.executedAt).isNotNull(); + assertThat(info.getScheduledTransaction()).isNotNull(); + } + } + + @Test + @Disabled + @DisplayName("Can create schedule with schedule()") + void canCreateWithSchedule() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var key = PrivateKey.generateED25519(); + + var transaction = new AccountCreateTransaction().setKey(key).setInitialBalance(new Hbar(10)); + + var tx = transaction.schedule(); + + var response = tx.setAdminKey(testEnv.operatorKey) + .setPayerAccountId(testEnv.operatorId) + .execute(testEnv.client); + + var scheduleId = Objects.requireNonNull(response.getReceipt(testEnv.client).scheduleId); + + var info = new ScheduleInfoQuery().setScheduleId(scheduleId).execute(testEnv.client); + + assertThat(info.executedAt).isNotNull(); + assertThat(info.getScheduledTransaction()).isNotNull(); + } + } + + @Test + @DisplayName("Can sign schedule") + void canSignSchedule2() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + PrivateKey key1 = PrivateKey.generateED25519(); + PrivateKey key2 = PrivateKey.generateED25519(); + PrivateKey key3 = PrivateKey.generateED25519(); + + KeyList keyList = new KeyList(); + + keyList.add(key1.getPublicKey()); + keyList.add(key2.getPublicKey()); + keyList.add(key3.getPublicKey()); + + // Creat the account with the `KeyList` + TransactionResponse response = new AccountCreateTransaction() + .setKey(keyList) + .setInitialBalance(new Hbar(10)) + .execute(testEnv.client); + + // This will wait for the receipt to become available + TransactionReceipt receipt = response.getReceipt(testEnv.client); + + AccountId accountId = Objects.requireNonNull(receipt.accountId); + + // Generate a `TransactionId`. This id is used to query the inner scheduled transaction + // after we expect it to have been executed + TransactionId transactionId = TransactionId.generate(testEnv.operatorId); + + // Create a transfer transaction with 2/3 signatures. + TransferTransaction transfer = new TransferTransaction() + .setTransactionId(transactionId) + .addHbarTransfer(accountId, new Hbar(1).negated()) + .addHbarTransfer(testEnv.operatorId, new Hbar(1)); + + // Schedule the transaction + ScheduleCreateTransaction scheduled = transfer.schedule(); + + receipt = scheduled.execute(testEnv.client).getReceipt(testEnv.client); + + // Get the schedule ID from the receipt + ScheduleId scheduleId = Objects.requireNonNull(receipt.scheduleId); + + // Get the schedule info to see if `signatories` is populated with 2/3 signatures + ScheduleInfo info = + new ScheduleInfoQuery().setScheduleId(scheduleId).execute(testEnv.client); + + assertThat(info.executedAt).isNull(); + + // Finally send this last signature to Hedera. This last signature _should_ mean the transaction executes + // since all 3 signatures have been provided. + ScheduleSignTransaction signTransaction = + new ScheduleSignTransaction().setScheduleId(scheduleId).freezeWith(testEnv.client); + + signTransaction + .sign(key1) + .sign(key2) + .sign(key3) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + info = new ScheduleInfoQuery().setScheduleId(scheduleId).execute(testEnv.client); + + assertThat(info.executedAt).isNotNull(); + + assertThat(scheduleId.getChecksum()).isNull(); + assertThat(scheduleId.hashCode()).isNotZero(); + assertThat(scheduleId.compareTo(ScheduleId.fromBytes(scheduleId.toBytes()))) + .isZero(); + + new AccountDeleteTransaction() + .setAccountId(accountId) + .setTransferAccountId(testEnv.operatorId) + .freezeWith(testEnv.client) + .sign(key1) + .sign(key2) + .sign(key3) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can schedule token transfer") + void canScheduleTokenTransfer() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + PrivateKey key = PrivateKey.generateED25519(); + + var accountId = new AccountCreateTransaction() + .setReceiverSignatureRequired(true) + .setKey(key) + .setInitialBalance(new Hbar(10)) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + Objects.requireNonNull(accountId); + + var tokenId = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setInitialSupply(100) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId; + + Objects.requireNonNull(tokenId); + + new TokenAssociateTransaction() + .setAccountId(accountId) + .setTokenIds(Collections.singletonList(tokenId)) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var scheduleId = new TransferTransaction() + .addTokenTransfer(tokenId, testEnv.operatorId, -10) + .addTokenTransfer(tokenId, accountId, 10) + .schedule() + .execute(testEnv.client) + .getReceipt(testEnv.client) + .scheduleId; + + Objects.requireNonNull(scheduleId); + + var balanceQuery1 = + new AccountBalanceQuery().setAccountId(accountId).execute(testEnv.client); + + assertThat(balanceQuery1.tokens.get(tokenId)).isEqualTo(0); + + new ScheduleSignTransaction() + .setScheduleId(scheduleId) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var balanceQuery2 = + new AccountBalanceQuery().setAccountId(accountId).execute(testEnv.client); + + assertThat(balanceQuery2.tokens.get(tokenId)).isEqualTo(10); + } + } + + @Test + @DisplayName("Cannot schedule two identical transactions") + void cannotScheduleTwoTransactions() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var key = PrivateKey.generateED25519(); + var accountId = new AccountCreateTransaction() + .setInitialBalance(new Hbar(10)) + .setKey(key) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + var transferTx = new TransferTransaction() + .addHbarTransfer(testEnv.operatorId, new Hbar(-10)) + .addHbarTransfer(accountId, new Hbar(10)); + + var scheduleId1 = transferTx.schedule().execute(testEnv.client).getReceipt(testEnv.client).scheduleId; + + var info1 = new ScheduleInfoQuery().setScheduleId(scheduleId1).execute(testEnv.client); + + assertThat(info1.executedAt).isNotNull(); + + var transferTxFromInfo = info1.getScheduledTransaction(); + + var scheduleCreateTx1 = transferTx.schedule(); + var scheduleCreateTx2 = transferTxFromInfo.schedule(); + + assertThat(scheduleCreateTx2.toString()).isEqualTo(scheduleCreateTx1.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + transferTxFromInfo.schedule().execute(testEnv.client).getReceipt(testEnv.client); + }) + .withMessageContaining("IDENTICAL_SCHEDULE_ALREADY_CREATED"); + } + } + + @Test + @DisplayName("Can schedule topic message") + void canScheduleTopicMessage() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + // Generate 3 random keys + var key1 = PrivateKey.generateED25519(); + + // This is the submit key + var key2 = PrivateKey.generateED25519(); + + var key3 = PrivateKey.generateED25519(); + + var keyList = new KeyList(); + + keyList.add(key1.getPublicKey()); + keyList.add(key2.getPublicKey()); + keyList.add(key3.getPublicKey()); + + var response = new AccountCreateTransaction() + .setInitialBalance(new Hbar(100)) + .setKey(keyList) + .execute(testEnv.client); + + assertThat(response.getReceipt(testEnv.client).accountId).isNotNull(); + + var topicId = Objects.requireNonNull(new TopicCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setAutoRenewAccountId(testEnv.operatorId) + .setTopicMemo("HCS Topic_") + .setSubmitKey(key2.getPublicKey()) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .topicId); + + var transaction = new TopicMessageSubmitTransaction() + .setTopicId(topicId) + .setMessage("scheduled hcs message".getBytes(StandardCharsets.UTF_8)); + + // create schedule + var scheduledTx = transaction + .schedule() + .setAdminKey(testEnv.operatorKey) + .setPayerAccountId(testEnv.operatorId) + .setScheduleMemo("mirror scheduled E2E signature on create and sign_" + Instant.now()); + + var scheduled = scheduledTx.freezeWith(testEnv.client); + + var scheduleId = + Objects.requireNonNull(scheduled.execute(testEnv.client).getReceipt(testEnv.client).scheduleId); + + // verify schedule has been created and has 1 of 2 signatures + var info = new ScheduleInfoQuery().setScheduleId(scheduleId).execute(testEnv.client); + + assertThat(info).isNotNull(); + assertThat(info.scheduleId).isEqualTo(scheduleId); + + var infoTransaction = (TopicMessageSubmitTransaction) info.getScheduledTransaction(); + + assertThat(transaction.getTopicId()).isEqualTo(infoTransaction.getTopicId()); + assertThat(transaction.getNodeAccountIds()).isEqualTo(infoTransaction.getNodeAccountIds()); + + var scheduleSign = + new ScheduleSignTransaction().setScheduleId(scheduleId).freezeWith(testEnv.client); + + scheduleSign.sign(key2).execute(testEnv.client).getReceipt(testEnv.client); + + info = new ScheduleInfoQuery().setScheduleId(scheduleId).execute(testEnv.client); + + assertThat(info.executedAt).isNotNull(); + } + } + + @Test + @DisplayName("Can sign schedule") + void canSignSchedule() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + PrivateKey key = PrivateKey.generateED25519(); + + var accountId = new AccountCreateTransaction() + .setKey(key.getPublicKey()) + .setInitialBalance(new Hbar(10)) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + // Create the transaction + TransferTransaction transfer = new TransferTransaction() + .addHbarTransfer(accountId, new Hbar(1).negated()) + .addHbarTransfer(testEnv.operatorId, new Hbar(1)); + + // Schedule the transaction + var scheduleId = transfer.schedule() + .setExpirationTime(Instant.now().plusSeconds(oneDayInSecs)) + .setScheduleMemo("HIP-423 Integration Test") + .execute(testEnv.client) + .getReceipt(testEnv.client) + .scheduleId; + + ScheduleInfo info = + new ScheduleInfoQuery().setScheduleId(scheduleId).execute(testEnv.client); + + // Verify the transaction is not yet executed + assertThat(info.executedAt).isNull(); + + // Schedule sign + new ScheduleSignTransaction() + .setScheduleId(scheduleId) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + info = new ScheduleInfoQuery().setScheduleId(scheduleId).execute(testEnv.client); + + // Verify the transaction is executed + assertThat(info.executedAt).isNotNull(); + + assertThat(scheduleId.getChecksum()).isNull(); + assertThat(scheduleId.hashCode()).isNotZero(); + assertThat(scheduleId.compareTo(ScheduleId.fromBytes(scheduleId.toBytes()))) + .isZero(); + } + } + + @Test + @DisplayName("Cannot schedule one year into the future") + void cannotScheduleTransactionOneYearIntoTheFuture() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + PrivateKey key = PrivateKey.generateED25519(); + + var accountId = new AccountCreateTransaction() + .setKey(key.getPublicKey()) + .setInitialBalance(new Hbar(10)) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + // Create the transaction + TransferTransaction transfer = new TransferTransaction() + .addHbarTransfer(accountId, new Hbar(1).negated()) + .addHbarTransfer(testEnv.operatorId, new Hbar(1)); + + // Schedule the transaction + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> transfer.schedule() + .setExpirationTime(Instant.now().plus(Duration.ofDays(365))) + .setScheduleMemo("HIP-423 Integration Test") + .execute(testEnv.client) + .getReceipt(testEnv.client)) + .withMessageContaining(Status.SCHEDULE_EXPIRATION_TIME_TOO_FAR_IN_FUTURE.toString()); + } + } + + @Test + @DisplayName("Cannot schedule in the past") + void cannotScheduleTransactionInThePast() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + PrivateKey key = PrivateKey.generateED25519(); + + var accountId = new AccountCreateTransaction() + .setKey(key.getPublicKey()) + .setInitialBalance(new Hbar(10)) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + // Create the transaction + TransferTransaction transfer = new TransferTransaction() + .addHbarTransfer(accountId, new Hbar(1).negated()) + .addHbarTransfer(testEnv.operatorId, new Hbar(1)); + + // Schedule the transaction + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> transfer.schedule() + .setExpirationTime(Instant.now().minusSeconds(10)) + .setScheduleMemo("HIP-423 Integration Test") + .execute(testEnv.client) + .getReceipt(testEnv.client)) + .withMessageContaining( + Status.SCHEDULE_EXPIRATION_TIME_MUST_BE_HIGHER_THAN_CONSENSUS_TIME.toString()); + } + } + + @Test + @DisplayName("Can sign schedule and wait for expiry") + void canSignScheduleAndWaitForExpiry() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + PrivateKey key = PrivateKey.generateED25519(); + + var accountId = new AccountCreateTransaction() + .setKey(key.getPublicKey()) + .setInitialBalance(new Hbar(10)) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + // Create the transaction + TransferTransaction transfer = new TransferTransaction() + .addHbarTransfer(accountId, new Hbar(1).negated()) + .addHbarTransfer(testEnv.operatorId, new Hbar(1)); + + // Schedule the transaction + var scheduleId = transfer.schedule() + .setExpirationTime(Instant.now().plusSeconds(oneDayInSecs)) + .setWaitForExpiry(true) + .setScheduleMemo("HIP-423 Integration Test") + .execute(testEnv.client) + .getReceipt(testEnv.client) + .scheduleId; + + ScheduleInfo info = + new ScheduleInfoQuery().setScheduleId(scheduleId).execute(testEnv.client); + + // Verify the transaction is not yet executed + assertThat(info.executedAt).isNull(); + + // Schedule sign + new ScheduleSignTransaction() + .setScheduleId(scheduleId) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + info = new ScheduleInfoQuery().setScheduleId(scheduleId).execute(testEnv.client); + + // Verify the transaction is still not executed + assertThat(info.executedAt).isNull(); + + assertThat(scheduleId.getChecksum()).isNull(); + assertThat(scheduleId.hashCode()).isNotZero(); + assertThat(scheduleId.compareTo(ScheduleId.fromBytes(scheduleId.toBytes()))) + .isZero(); + } + } + + @Test + @DisplayName("Can sign with multisig and update signing requirements") + void canSignWithMultiSigAndUpdateSigningRequirements() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + PrivateKey key1 = PrivateKey.generateED25519(); + PrivateKey key2 = PrivateKey.generateED25519(); + PrivateKey key3 = PrivateKey.generateED25519(); + PrivateKey key4 = PrivateKey.generateED25519(); + + KeyList keyList = KeyList.withThreshold(2); + + keyList.add(key1.getPublicKey()); + keyList.add(key2.getPublicKey()); + keyList.add(key3.getPublicKey()); + + var accountId = new AccountCreateTransaction() + .setKey(keyList) + .setInitialBalance(new Hbar(10)) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + // Create the transaction + TransferTransaction transfer = new TransferTransaction() + .addHbarTransfer(accountId, new Hbar(1).negated()) + .addHbarTransfer(testEnv.operatorId, new Hbar(1)); + + // Schedule the transaction + var scheduleId = transfer.schedule() + .setExpirationTime(Instant.now().plusSeconds(oneDayInSecs)) + .setScheduleMemo("HIP-423 Integration Test") + .execute(testEnv.client) + .getReceipt(testEnv.client) + .scheduleId; + + ScheduleInfo info = + new ScheduleInfoQuery().setScheduleId(scheduleId).execute(testEnv.client); + + // Verify the transaction is not executed + assertThat(info.executedAt).isNull(); + + // Sign with one key + new ScheduleSignTransaction() + .setScheduleId(scheduleId) + .freezeWith(testEnv.client) + .sign(key1) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + info = new ScheduleInfoQuery().setScheduleId(scheduleId).execute(testEnv.client); + + // Verify the transaction is still not executed + assertThat(info.executedAt).isNull(); + + // Update the signing requirements + new AccountUpdateTransaction() + .setAccountId(accountId) + .setKey(key4.getPublicKey()) + .freezeWith(testEnv.client) + .sign(key1) + .sign(key2) + .sign(key4) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + info = new ScheduleInfoQuery().setScheduleId(scheduleId).execute(testEnv.client); + + // Verify the transaction is still not executed + assertThat(info.executedAt).isNull(); + + // Sign with the updated key + new ScheduleSignTransaction() + .setScheduleId(scheduleId) + .freezeWith(testEnv.client) + .sign(key4) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + info = new ScheduleInfoQuery().setScheduleId(scheduleId).execute(testEnv.client); + + // Verify the transaction is executed + assertThat(info.executedAt).isNotNull(); + } + } + + @Test + @DisplayName("Can sign with multisig") + void canSignWithMultiSig() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + PrivateKey key1 = PrivateKey.generateED25519(); + PrivateKey key2 = PrivateKey.generateED25519(); + PrivateKey key3 = PrivateKey.generateED25519(); + + KeyList keyList = KeyList.withThreshold(2); + + keyList.add(key1.getPublicKey()); + keyList.add(key2.getPublicKey()); + keyList.add(key3.getPublicKey()); + + var accountId = new AccountCreateTransaction() + .setKey(keyList) + .setInitialBalance(new Hbar(10)) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + // Create the transaction + TransferTransaction transfer = new TransferTransaction() + .addHbarTransfer(accountId, new Hbar(1).negated()) + .addHbarTransfer(testEnv.operatorId, new Hbar(1)); + + // Schedule the transaction + var scheduleId = transfer.schedule() + .setExpirationTime(Instant.now().plusSeconds(oneDayInSecs)) + .setScheduleMemo("HIP-423 Integration Test") + .execute(testEnv.client) + .getReceipt(testEnv.client) + .scheduleId; + + ScheduleInfo info = + new ScheduleInfoQuery().setScheduleId(scheduleId).execute(testEnv.client); + + // Verify the transaction is not executed + assertThat(info.executedAt).isNull(); + + // Sign with one key + new ScheduleSignTransaction() + .setScheduleId(scheduleId) + .freezeWith(testEnv.client) + .sign(key1) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + info = new ScheduleInfoQuery().setScheduleId(scheduleId).execute(testEnv.client); + + // Verify the transaction is still not executed + assertThat(info.executedAt).isNull(); + + // Update the signing requirements + new AccountUpdateTransaction() + .setAccountId(accountId) + .setKey(key1.getPublicKey()) + .freezeWith(testEnv.client) + .sign(key1) + .sign(key2) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + info = new ScheduleInfoQuery().setScheduleId(scheduleId).execute(testEnv.client); + + // Verify the transaction is still not executed + assertThat(info.executedAt).isNull(); + + // Sign with one more key + new ScheduleSignTransaction() + .setScheduleId(scheduleId) + .freezeWith(testEnv.client) + .sign(key2) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + info = new ScheduleInfoQuery().setScheduleId(scheduleId).execute(testEnv.client); + + // Verify the transaction is executed + assertThat(info.executedAt).isNotNull(); + } + } + + @Test + @DisplayName("Can execute with short expiration time") + void canExecuteWithShortExpirationTime() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + PrivateKey key1 = PrivateKey.generateED25519(); + + var accountId = new AccountCreateTransaction() + .setKey(key1) + .setInitialBalance(new Hbar(10)) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + // Create the transaction + TransferTransaction transfer = new TransferTransaction() + .addHbarTransfer(accountId, new Hbar(1).negated()) + .addHbarTransfer(testEnv.operatorId, new Hbar(1)); + + // Schedule the transaction + var scheduleId = transfer.schedule() + .setExpirationTime(Instant.now().plusSeconds(10)) + .setWaitForExpiry(true) + .setScheduleMemo("HIP-423 Integration Test") + .execute(testEnv.client) + .getReceipt(testEnv.client) + .scheduleId; + + ScheduleInfo info = + new ScheduleInfoQuery().setScheduleId(scheduleId).execute(testEnv.client); + + // Verify the transaction is not executed + assertThat(info.executedAt).isNull(); + + // Sign + new ScheduleSignTransaction() + .setScheduleId(scheduleId) + .freezeWith(testEnv.client) + .sign(key1) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + info = new ScheduleInfoQuery().setScheduleId(scheduleId).execute(testEnv.client); + + // Verify the transaction is still not executed + assertThat(info.executedAt).isNull(); + + var accountBalanceBefore = + new AccountBalanceQuery().setAccountId(accountId).execute(testEnv.client); + + Thread.sleep(10_000); + + var accountBalanceAfter = + new AccountBalanceQuery().setAccountId(accountId).execute(testEnv.client); + + // Verify the transaction executed after 10 seconds + assertThat(accountBalanceBefore.hbars.compareTo(accountBalanceAfter.hbars)) + .isEqualTo(1); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/SystemIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/SystemIntegrationTest.java new file mode 100644 index 0000000000..f2846b71c0 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/SystemIntegrationTest.java @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.time.Instant; +import java.util.Objects; +import org.hiero.sdk.ContractCreateTransaction; +import org.hiero.sdk.ContractFunctionParameters; +import org.hiero.sdk.FileCreateTransaction; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.Status; +import org.hiero.sdk.SystemDeleteTransaction; +import org.hiero.sdk.SystemUndeleteTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class SystemIntegrationTest { + private static final String SMART_CONTRACT_BYTECODE = + "608060405234801561001057600080fd5b506040516104d73803806104d78339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b506040525050600080546001600160a01b0319163317905550805161010890600190602084019061010f565b50506101aa565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061015057805160ff191683800117855561017d565b8280016001018555821561017d579182015b8281111561017d578251825591602001919060010190610162565b5061018992915061018d565b5090565b6101a791905b808211156101895760008155600101610193565b90565b61031e806101b96000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063368b87721461004657806341c0e1b5146100ee578063ce6d41de146100f6575b600080fd5b6100ec6004803603602081101561005c57600080fd5b81019060208101813564010000000081111561007757600080fd5b82018360208201111561008957600080fd5b803590602001918460018302840111640100000000831117156100ab57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610173945050505050565b005b6100ec6101a2565b6100fe6101ba565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610138578181015183820152602001610120565b50505050905090810190601f1680156101655780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000546001600160a01b0316331461018a5761019f565b805161019d906001906020840190610250565b505b50565b6000546001600160a01b03163314156101b85733ff5b565b60018054604080516020601f600260001961010087891615020190951694909404938401819004810282018101909252828152606093909290918301828280156102455780601f1061021a57610100808354040283529160200191610245565b820191906000526020600020905b81548152906001019060200180831161022857829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061029157805160ff19168380011785556102be565b828001600101855582156102be579182015b828111156102be5782518255916020019190600101906102a3565b506102ca9291506102ce565b5090565b61024d91905b808211156102ca57600081556001016102d456fea264697066735822122084964d4c3f6bc912a9d20e14e449721012d625aa3c8a12de41ae5519752fc89064736f6c63430006000033"; + + @Test + @DisplayName("All system transactions are not supported") + void allSystemTransactionsAreNotSupported() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var fileId = Objects.requireNonNull(new FileCreateTransaction() + .setContents(SMART_CONTRACT_BYTECODE) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .fileId); + + var contractId = Objects.requireNonNull(new ContractCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setGas(200000) + .setConstructorParameters(new ContractFunctionParameters().addString("Hello from Hedera.")) + .setBytecodeFileId(fileId) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(testEnv.client) + .getReceipt(testEnv.client) + .contractId); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new SystemDeleteTransaction() + .setContractId(contractId) + .setExpirationTime(Instant.now()) + .execute(testEnv.client); + }) + .withMessageContaining(Status.NOT_SUPPORTED.toString()); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new SystemDeleteTransaction() + .setFileId(fileId) + .setExpirationTime(Instant.now()) + .execute(testEnv.client); + }) + .withMessageContaining(Status.NOT_SUPPORTED.toString()); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new SystemUndeleteTransaction() + .setContractId(contractId) + .execute(testEnv.client); + }) + .withMessageContaining(Status.NOT_SUPPORTED.toString()); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new SystemUndeleteTransaction().setFileId(fileId).execute(testEnv.client); + }) + .withMessageContaining(Status.NOT_SUPPORTED.toString()); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenAirdropCancelIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenAirdropCancelIntegrationTest.java new file mode 100644 index 0000000000..13d71dd43a --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenAirdropCancelIntegrationTest.java @@ -0,0 +1,463 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType; +import static org.hiero.sdk.test.integration.EntityHelper.fungibleInitialBalance; +import static org.hiero.sdk.test.integration.EntityHelper.mitedNfts; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.util.ArrayList; +import java.util.Collections; +import org.hiero.sdk.AccountBalanceQuery; +import org.hiero.sdk.PendingAirdropId; +import org.hiero.sdk.PendingAirdropRecord; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.sdk.Status; +import org.hiero.sdk.TokenAirdropTransaction; +import org.hiero.sdk.TokenAssociateTransaction; +import org.hiero.sdk.TokenCancelAirdropTransaction; +import org.hiero.sdk.TokenDeleteTransaction; +import org.hiero.sdk.TokenFreezeTransaction; +import org.hiero.sdk.TokenMintTransaction; +import org.hiero.sdk.TokenPauseTransaction; +import org.hiero.sdk.TransactionId; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class TokenAirdropCancelIntegrationTest { + + private final int amount = 100; + + @Test + @DisplayName("Cancels the tokens when they are in pending state") + void canCancelTokens() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // create fungible and nf token + var tokenID = EntityHelper.createFungibleToken(testEnv, 3); + var nftID = EntityHelper.createNft(testEnv); + // mint some NFTs + var mintReceipt = new TokenMintTransaction() + .setTokenId(nftID) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + var nftSerials = mintReceipt.serials; + + // create receiver with 0 auto associations + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); + + // airdrop the tokens + var record = new TokenAirdropTransaction() + .addNftTransfer(nftID.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) + .addNftTransfer(nftID.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) + .addTokenTransfer(tokenID, receiverAccountId, amount) + .addTokenTransfer(tokenID, testEnv.operatorId, -amount) + .execute(testEnv.client) + .getRecord(testEnv.client); + + // sender cancels the tokens + record = new TokenCancelAirdropTransaction() + .addPendingAirdrop(record.pendingAirdropRecords.get(0).getPendingAirdropId()) + .addPendingAirdrop(record.pendingAirdropRecords.get(1).getPendingAirdropId()) + .addPendingAirdrop(record.pendingAirdropRecords.get(2).getPendingAirdropId()) + .execute(testEnv.client) + .getRecord(testEnv.client); + + // verify in the transaction record the pending airdrop ids for nft and ft - should no longer exist + assertEquals(0, record.pendingAirdropRecords.size()); + + // verify the receiver does not hold the tokens via query + var receiverAccountBalance = + new AccountBalanceQuery().setAccountId(receiverAccountId).execute(testEnv.client); + assertNull(receiverAccountBalance.tokens.get(tokenID)); + assertNull(receiverAccountBalance.tokens.get(nftID)); + + // verify the operator does hold the tokens + var operatorBalance = + new AccountBalanceQuery().setAccountId(testEnv.operatorId).execute(testEnv.client); + assertEquals(fungibleInitialBalance, operatorBalance.tokens.get(tokenID)); + assertEquals(mitedNfts, operatorBalance.tokens.get(nftID)); + } + } + + @Test + @DisplayName("Cancels the tokens when token is frozen") + void canCancelTokensWhenTokenIsFrozen() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // create fungible token + var tokenID = EntityHelper.createFungibleToken(testEnv, 3); + + // create receiver with 0 auto associations + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); + + // airdrop the tokens + var record = new TokenAirdropTransaction() + .addTokenTransfer(tokenID, receiverAccountId, amount) + .addTokenTransfer(tokenID, testEnv.operatorId, -amount) + .execute(testEnv.client) + .getRecord(testEnv.client); + + // associate + new TokenAssociateTransaction() + .setAccountId(receiverAccountId) + .setTokenIds(Collections.singletonList(tokenID)) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // freeze the token + new TokenFreezeTransaction() + .setAccountId(receiverAccountId) + .setTokenId(tokenID) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // cancel + new TokenCancelAirdropTransaction() + .addPendingAirdrop(record.pendingAirdropRecords.get(0).getPendingAirdropId()) + .execute(testEnv.client) + .getRecord(testEnv.client); + } + } + + @Test + @DisplayName("Cancels the tokens when token is paused") + void canCancelTokensWhenTokenIsPaused() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // create fungible token + var tokenID = EntityHelper.createFungibleToken(testEnv, 3); + + // create receiver with 0 auto associations + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); + + // airdrop the tokens + var record = new TokenAirdropTransaction() + .addTokenTransfer(tokenID, receiverAccountId, amount) + .addTokenTransfer(tokenID, testEnv.operatorId, -amount) + .execute(testEnv.client) + .getRecord(testEnv.client); + + // pause the token + new TokenPauseTransaction() + .setTokenId(tokenID) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // cancel + new TokenCancelAirdropTransaction() + .addPendingAirdrop(record.pendingAirdropRecords.get(0).getPendingAirdropId()) + .execute(testEnv.client) + .getRecord(testEnv.client); + } + } + + @Test + @DisplayName("Cancels the tokens when token is deleted") + void canCancelTokensWhenTokenIsDeleted() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // create fungible token + var tokenID = EntityHelper.createFungibleToken(testEnv, 3); + + // create receiver with 0 auto associations + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); + + // airdrop the tokens + var record = new TokenAirdropTransaction() + .addTokenTransfer(tokenID, receiverAccountId, amount) + .addTokenTransfer(tokenID, testEnv.operatorId, -amount) + .execute(testEnv.client) + .getRecord(testEnv.client); + + // delete the token + new TokenDeleteTransaction() + .setTokenId(tokenID) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // cancel + new TokenCancelAirdropTransaction() + .addPendingAirdrop(record.pendingAirdropRecords.get(0).getPendingAirdropId()) + .execute(testEnv.client) + .getRecord(testEnv.client); + } + } + + @Test + @DisplayName("Cancels the tokens when they are in pending state to multiple receivers") + void canCancelTokensToMultipleReceivers() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // create fungible and nf token + var tokenID = EntityHelper.createFungibleToken(testEnv, 3); + var nftID = EntityHelper.createNft(testEnv); + // mint some NFTs + var mintReceipt = new TokenMintTransaction() + .setTokenId(nftID) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + var nftSerials = mintReceipt.serials; + + // create receiver1 with 0 auto associations + var receiver1AccountKey = PrivateKey.generateED25519(); + var receiver1AccountId = EntityHelper.createAccount(testEnv, receiver1AccountKey, 0); + + // create receiver2 with 0 auto associations + var receiver2AccountKey = PrivateKey.generateED25519(); + var receiver2AccountId = EntityHelper.createAccount(testEnv, receiver2AccountKey, 0); + + // airdrop the tokens to both + var record = new TokenAirdropTransaction() + .addNftTransfer(nftID.nft(nftSerials.get(0)), testEnv.operatorId, receiver1AccountId) + .addNftTransfer(nftID.nft(nftSerials.get(1)), testEnv.operatorId, receiver1AccountId) + .addTokenTransfer(tokenID, receiver1AccountId, amount) + .addTokenTransfer(tokenID, testEnv.operatorId, -amount) + .addNftTransfer(nftID.nft(nftSerials.get(2)), testEnv.operatorId, receiver2AccountId) + .addNftTransfer(nftID.nft(nftSerials.get(3)), testEnv.operatorId, receiver2AccountId) + .addTokenTransfer(tokenID, receiver2AccountId, amount) + .addTokenTransfer(tokenID, testEnv.operatorId, -amount) + .execute(testEnv.client) + .getRecord(testEnv.client); + + // verify the txn record + assertEquals(6, record.pendingAirdropRecords.size()); + + // cancel the tokens signing with receiver1 and receiver2 + var pendingAirdropIDs = record.pendingAirdropRecords.stream() + .map(PendingAirdropRecord::getPendingAirdropId) + .toList(); + record = new TokenCancelAirdropTransaction() + .setPendingAirdropIds(pendingAirdropIDs) + .execute(testEnv.client) + .getRecord(testEnv.client); + + // verify in the transaction record the pending airdrop ids for nft and ft - should no longer exist + assertEquals(0, record.pendingAirdropRecords.size()); + + // verify receiver1 does not hold the tokens via query + var receiverAccountBalance = + new AccountBalanceQuery().setAccountId(receiver1AccountId).execute(testEnv.client); + assertNull(receiverAccountBalance.tokens.get(tokenID)); + assertNull(receiverAccountBalance.tokens.get(nftID)); + + // verify receiver2 does not hold the tokens via query + var receiver2AccountBalance = + new AccountBalanceQuery().setAccountId(receiver1AccountId).execute(testEnv.client); + assertNull(receiver2AccountBalance.tokens.get(tokenID)); + assertNull(receiver2AccountBalance.tokens.get(nftID)); + + // verify the operator does hold the tokens + var operatorBalance = + new AccountBalanceQuery().setAccountId(testEnv.operatorId).execute(testEnv.client); + assertEquals(fungibleInitialBalance, operatorBalance.tokens.get(tokenID)); + assertEquals(mitedNfts, operatorBalance.tokens.get(nftID)); + } + } + + @Test + @DisplayName("Cancels the tokens when they are in pending state from multiple airdrop transactions") + void canCancelTokensFromMultipleAirdropTxns() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // create fungible and nf token + var tokenID = EntityHelper.createFungibleToken(testEnv, 3); + var nftID = EntityHelper.createNft(testEnv); + // mint some NFTs + var mintReceipt = new TokenMintTransaction() + .setTokenId(nftID) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + var nftSerials = mintReceipt.serials; + + // create receiver with 0 auto associations + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); + + // airdrop some of the tokens to the receiver + var record1 = new TokenAirdropTransaction() + .addNftTransfer(nftID.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) + .execute(testEnv.client) + .getRecord(testEnv.client); + // airdrop some of the tokens to the receiver + var record2 = new TokenAirdropTransaction() + .addNftTransfer(nftID.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) + .execute(testEnv.client) + .getRecord(testEnv.client); + // airdrop some of the tokens to the receiver + var record3 = new TokenAirdropTransaction() + .addTokenTransfer(tokenID, receiverAccountId, amount) + .addTokenTransfer(tokenID, testEnv.operatorId, -amount) + .execute(testEnv.client) + .getRecord(testEnv.client); + + // get the PendingIds from the records + var pendingAirdropIDs = new ArrayList(); + pendingAirdropIDs.add(record1.pendingAirdropRecords.get(0).getPendingAirdropId()); + pendingAirdropIDs.add(record2.pendingAirdropRecords.get(0).getPendingAirdropId()); + pendingAirdropIDs.add(record3.pendingAirdropRecords.get(0).getPendingAirdropId()); + + // cancel the all the tokens with the receiver + var record = new TokenCancelAirdropTransaction() + .setPendingAirdropIds(pendingAirdropIDs) + .execute(testEnv.client) + .getRecord(testEnv.client); + + // verify in the transaction record the pending airdrop ids for nft and ft - should no longer exist + assertEquals(0, record.pendingAirdropRecords.size()); + + // verify the receiver does not hold the tokens via query + var receiverAccountBalance = + new AccountBalanceQuery().setAccountId(receiverAccountId).execute(testEnv.client); + assertNull(receiverAccountBalance.tokens.get(tokenID)); + assertNull(receiverAccountBalance.tokens.get(nftID)); + + // verify the operator does hold the tokens + var operatorBalance = + new AccountBalanceQuery().setAccountId(testEnv.operatorId).execute(testEnv.client); + assertEquals(fungibleInitialBalance, operatorBalance.tokens.get(tokenID)); + assertEquals(mitedNfts, operatorBalance.tokens.get(nftID)); + } + } + + @Test + @DisplayName("Cannot cancel the tokens when they are not airdropped") + void cannotCancelTokensForNonExistingAirdrop() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // create fungible token + var tokenID = EntityHelper.createFungibleToken(testEnv, 3); + + // create receiver with 0 auto associations + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); + + // airdrop the tokens + var record = new TokenAirdropTransaction() + .addTokenTransfer(tokenID, receiverAccountId, amount) + .addTokenTransfer(tokenID, testEnv.operatorId, -amount) + .execute(testEnv.client) + .getRecord(testEnv.client); + + // create receiver with 0 auto associations + var randomAccountKey = PrivateKey.generateED25519(); + var randomAccount = EntityHelper.createAccount(testEnv, randomAccountKey, 0); + + // cancel the tokens with the random account which has not created pending airdrops + // fails with INVALID_SIGNATURE + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenCancelAirdropTransaction() + .setTransactionId(TransactionId.generate(randomAccount)) + .addPendingAirdrop( + record.pendingAirdropRecords.get(0).getPendingAirdropId()) + .execute(testEnv.client) + .getRecord(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + } + } + + @Test + @DisplayName("Cannot cancel the tokens when they are already canceled") + void canonCancelTokensForAlreadyCanceledAirdrop() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // create fungible token + var tokenID = EntityHelper.createFungibleToken(testEnv, 3); + + // create receiver with 0 auto associations + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); + + // airdrop the tokens + var record = new TokenAirdropTransaction() + .addTokenTransfer(tokenID, receiverAccountId, amount) + .addTokenTransfer(tokenID, testEnv.operatorId, -amount) + .execute(testEnv.client) + .getRecord(testEnv.client); + + // cancel the tokens with the receiver + new TokenCancelAirdropTransaction() + .addPendingAirdrop(record.pendingAirdropRecords.get(0).getPendingAirdropId()) + .execute(testEnv.client) + .getRecord(testEnv.client); + + // cancel the tokens with the receiver again + // fails with INVALID_PENDING_AIRDROP_ID + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenCancelAirdropTransaction() + .addPendingAirdrop( + record.pendingAirdropRecords.get(0).getPendingAirdropId()) + .execute(testEnv.client) + .getRecord(testEnv.client); + }) + .withMessageContaining(Status.INVALID_PENDING_AIRDROP_ID.toString()); + } + } + + @Test + @DisplayName("Cannot cancel the tokens with empty list") + void canonCancelWithEmptyPendingAirdropsList() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // cancel the tokens with the receiver without setting pendingAirdropIds + // fails with EMPTY_PENDING_AIRDROP_ID_LIST + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenCancelAirdropTransaction() + .execute(testEnv.client) + .getRecord(testEnv.client); + }) + .withMessageContaining(Status.EMPTY_PENDING_AIRDROP_ID_LIST.toString()); + } + } + + @Test + @DisplayName("Cannot cancel the tokens with duplicate entries") + void cannotCancelTokensWithDuplicateEntries() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // create fungible token + var tokenID = EntityHelper.createFungibleToken(testEnv, 3); + + // create receiver with 0 auto associations + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); + + // airdrop the tokens + var record = new TokenAirdropTransaction() + .addTokenTransfer(tokenID, receiverAccountId, amount) + .addTokenTransfer(tokenID, testEnv.operatorId, -amount) + .execute(testEnv.client) + .getRecord(testEnv.client); + + // cancel the tokens with duplicate pending airdrop token ids + // fails with PENDING_AIRDROP_ID_REPEATED + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenCancelAirdropTransaction() + .addPendingAirdrop( + record.pendingAirdropRecords.get(0).getPendingAirdropId()) + .addPendingAirdrop( + record.pendingAirdropRecords.get(0).getPendingAirdropId()) + .execute(testEnv.client) + .getRecord(testEnv.client); + }) + .withMessageContaining(Status.PENDING_AIRDROP_ID_REPEATED.toString()); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenAirdropClaimIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenAirdropClaimIntegrationTest.java new file mode 100644 index 0000000000..96da7546bb --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenAirdropClaimIntegrationTest.java @@ -0,0 +1,512 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.hiero.sdk.test.integration.EntityHelper.fungibleInitialBalance; +import static org.hiero.sdk.test.integration.EntityHelper.mitedNfts; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.util.ArrayList; +import java.util.Collections; +import org.hiero.sdk.AccountBalanceQuery; +import org.hiero.sdk.PendingAirdropId; +import org.hiero.sdk.PendingAirdropRecord; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.sdk.Status; +import org.hiero.sdk.TokenAirdropTransaction; +import org.hiero.sdk.TokenAssociateTransaction; +import org.hiero.sdk.TokenClaimAirdropTransaction; +import org.hiero.sdk.TokenDeleteTransaction; +import org.hiero.sdk.TokenFreezeTransaction; +import org.hiero.sdk.TokenMintTransaction; +import org.hiero.sdk.TokenPauseTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class TokenAirdropClaimIntegrationTest { + + private final int amount = 100; + + @Test + @DisplayName("Claims the tokens when they are in pending state") + void canClaimTokens() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // create fungible and nf token + var tokenID = EntityHelper.createFungibleToken(testEnv, 3); + var nftID = EntityHelper.createNft(testEnv); + // mint some NFTs + var mintReceipt = new TokenMintTransaction() + .setTokenId(nftID) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + var nftSerials = mintReceipt.serials; + + // create receiver with 0 auto associations + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); + + // airdrop the tokens + var record = new TokenAirdropTransaction() + .addNftTransfer(nftID.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) + .addNftTransfer(nftID.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) + .addTokenTransfer(tokenID, receiverAccountId, amount) + .addTokenTransfer(tokenID, testEnv.operatorId, -amount) + .execute(testEnv.client) + .getRecord(testEnv.client); + + // verify the txn record + assertEquals(3, record.pendingAirdropRecords.size()); + + assertEquals(100, record.pendingAirdropRecords.get(0).getPendingAirdropAmount()); + assertEquals( + tokenID, + record.pendingAirdropRecords.get(0).getPendingAirdropId().getTokenId()); + assertNull(record.pendingAirdropRecords.get(0).getPendingAirdropId().getNftId()); + + assertEquals(0, record.pendingAirdropRecords.get(1).getPendingAirdropAmount()); + assertEquals( + nftID.nft(1), + record.pendingAirdropRecords.get(1).getPendingAirdropId().getNftId()); + assertNull(record.pendingAirdropRecords.get(1).getPendingAirdropId().getTokenId()); + + assertEquals(0, record.pendingAirdropRecords.get(2).getPendingAirdropAmount()); + assertEquals( + nftID.nft(2), + record.pendingAirdropRecords.get(2).getPendingAirdropId().getNftId()); + assertNull(record.pendingAirdropRecords.get(2).getPendingAirdropId().getTokenId()); + + // claim the tokens with the receiver + record = new TokenClaimAirdropTransaction() + .addPendingAirdrop(record.pendingAirdropRecords.get(0).getPendingAirdropId()) + .addPendingAirdrop(record.pendingAirdropRecords.get(1).getPendingAirdropId()) + .addPendingAirdrop(record.pendingAirdropRecords.get(2).getPendingAirdropId()) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getRecord(testEnv.client); + + // verify in the transaction record the pending airdrop ids for nft and ft - should no longer exist + assertEquals(0, record.pendingAirdropRecords.size()); + + // verify the receiver holds the tokens via query + var receiverAccountBalance = + new AccountBalanceQuery().setAccountId(receiverAccountId).execute(testEnv.client); + assertEquals(amount, receiverAccountBalance.tokens.get(tokenID)); + assertEquals(2, receiverAccountBalance.tokens.get(nftID)); + + // verify the operator does not hold the tokens + var operatorBalance = + new AccountBalanceQuery().setAccountId(testEnv.operatorId).execute(testEnv.client); + assertEquals(fungibleInitialBalance - amount, operatorBalance.tokens.get(tokenID)); + assertEquals(mitedNfts - 2, operatorBalance.tokens.get(nftID)); + } + } + + @Test + @DisplayName("Claims the tokens when they are in pending state to multiple receivers") + void canClaimTokensToMultipleReceivers() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // create fungible and nf token + var tokenID = EntityHelper.createFungibleToken(testEnv, 3); + var nftID = EntityHelper.createNft(testEnv); + // mint some NFTs + var mintReceipt = new TokenMintTransaction() + .setTokenId(nftID) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + var nftSerials = mintReceipt.serials; + + // create receiver1 with 0 auto associations + var receiver1AccountKey = PrivateKey.generateED25519(); + var receiver1AccountId = EntityHelper.createAccount(testEnv, receiver1AccountKey, 0); + + // create receiver2 with 0 auto associations + var receiver2AccountKey = PrivateKey.generateED25519(); + var receiver2AccountId = EntityHelper.createAccount(testEnv, receiver2AccountKey, 0); + + // airdrop the tokens to both + var record = new TokenAirdropTransaction() + .addNftTransfer(nftID.nft(nftSerials.get(0)), testEnv.operatorId, receiver1AccountId) + .addNftTransfer(nftID.nft(nftSerials.get(1)), testEnv.operatorId, receiver1AccountId) + .addTokenTransfer(tokenID, receiver1AccountId, amount) + .addTokenTransfer(tokenID, testEnv.operatorId, -amount) + .addNftTransfer(nftID.nft(nftSerials.get(2)), testEnv.operatorId, receiver2AccountId) + .addNftTransfer(nftID.nft(nftSerials.get(3)), testEnv.operatorId, receiver2AccountId) + .addTokenTransfer(tokenID, receiver2AccountId, amount) + .addTokenTransfer(tokenID, testEnv.operatorId, -amount) + .execute(testEnv.client) + .getRecord(testEnv.client); + + // verify the txn record + assertEquals(6, record.pendingAirdropRecords.size()); + + // claim the tokens signing with receiver1 and receiver2 + var pendingAirdropIDs = record.pendingAirdropRecords.stream() + .map(PendingAirdropRecord::getPendingAirdropId) + .toList(); + record = new TokenClaimAirdropTransaction() + .setPendingAirdropIds(pendingAirdropIDs) + .freezeWith(testEnv.client) + .sign(receiver1AccountKey) + .sign(receiver2AccountKey) + .execute(testEnv.client) + .getRecord(testEnv.client); + + // verify in the transaction record the pending airdrop ids for nft and ft - should no longer exist + assertEquals(0, record.pendingAirdropRecords.size()); + // verify receiver1 holds the tokens via query + var receiverAccountBalance = + new AccountBalanceQuery().setAccountId(receiver1AccountId).execute(testEnv.client); + assertEquals(amount, receiverAccountBalance.tokens.get(tokenID)); + assertEquals(2, receiverAccountBalance.tokens.get(nftID)); + + // verify receiver2 holds the tokens via query + var receiver2AccountBalance = + new AccountBalanceQuery().setAccountId(receiver1AccountId).execute(testEnv.client); + assertEquals(amount, receiver2AccountBalance.tokens.get(tokenID)); + assertEquals(2, receiver2AccountBalance.tokens.get(nftID)); + + // verify the operator does not hold the tokens + var operatorBalance = + new AccountBalanceQuery().setAccountId(testEnv.operatorId).execute(testEnv.client); + assertEquals(fungibleInitialBalance - amount * 2, operatorBalance.tokens.get(tokenID)); + assertEquals(mitedNfts - 4, operatorBalance.tokens.get(nftID)); + } + } + + @Test + @DisplayName("Claims the tokens when they are in pending state from multiple airdrop transactions") + void canClaimTokensFromMultipleAirdropTxns() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // create fungible and nf token + var tokenID = EntityHelper.createFungibleToken(testEnv, 3); + var nftID = EntityHelper.createNft(testEnv); + // mint some NFTs + var mintReceipt = new TokenMintTransaction() + .setTokenId(nftID) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + var nftSerials = mintReceipt.serials; + + // create receiver with 0 auto associations + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); + + // airdrop some of the tokens to the receiver + var record1 = new TokenAirdropTransaction() + .addNftTransfer(nftID.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) + .execute(testEnv.client) + .getRecord(testEnv.client); + // airdrop some of the tokens to the receiver + var record2 = new TokenAirdropTransaction() + .addNftTransfer(nftID.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) + .execute(testEnv.client) + .getRecord(testEnv.client); + // airdrop some of the tokens to the receiver + var record3 = new TokenAirdropTransaction() + .addTokenTransfer(tokenID, receiverAccountId, amount) + .addTokenTransfer(tokenID, testEnv.operatorId, -amount) + .execute(testEnv.client) + .getRecord(testEnv.client); + + // get the PendingIds from the records + var pendingAirdropIDs = new ArrayList(); + pendingAirdropIDs.add(record1.pendingAirdropRecords.get(0).getPendingAirdropId()); + pendingAirdropIDs.add(record2.pendingAirdropRecords.get(0).getPendingAirdropId()); + pendingAirdropIDs.add(record3.pendingAirdropRecords.get(0).getPendingAirdropId()); + + // claim the all the tokens with the receiver + var record = new TokenClaimAirdropTransaction() + .setPendingAirdropIds(pendingAirdropIDs) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getRecord(testEnv.client); + + // verify in the transaction record the pending airdrop ids for nft and ft - should no longer exist + assertEquals(0, record.pendingAirdropRecords.size()); + + // verify the receiver holds the tokens via query + var receiverAccountBalance = + new AccountBalanceQuery().setAccountId(receiverAccountId).execute(testEnv.client); + assertEquals(amount, receiverAccountBalance.tokens.get(tokenID)); + assertEquals(2, receiverAccountBalance.tokens.get(nftID)); + + // verify the operator does not hold the tokens + var operatorBalance = + new AccountBalanceQuery().setAccountId(testEnv.operatorId).execute(testEnv.client); + assertEquals(fungibleInitialBalance - amount, operatorBalance.tokens.get(tokenID)); + assertEquals(mitedNfts - 2, operatorBalance.tokens.get(nftID)); + } + } + + @Test + @DisplayName("Cannot claim the tokens when they are not airdropped") + void cannotClaimTokensForNonExistingAirdrop() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // create fungible token + var tokenID = EntityHelper.createFungibleToken(testEnv, 3); + + // create receiver with 0 auto associations + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); + + // airdrop the tokens + var record = new TokenAirdropTransaction() + .addTokenTransfer(tokenID, receiverAccountId, amount) + .addTokenTransfer(tokenID, testEnv.operatorId, -amount) + .execute(testEnv.client) + .getRecord(testEnv.client); + + // claim the tokens with the operator which does not have pending airdrops + // fails with INVALID_SIGNATURE + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenClaimAirdropTransaction() + .addPendingAirdrop( + record.pendingAirdropRecords.get(0).getPendingAirdropId()) + .execute(testEnv.client) + .getRecord(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + } + } + + @Test + @DisplayName("Cannot claim the tokens when they are already claimed") + void cannotClaimTokensForAlreadyClaimedAirdrop() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // create fungible token + var tokenID = EntityHelper.createFungibleToken(testEnv, 3); + + // create receiver with 0 auto associations + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); + + // airdrop the tokens + var record = new TokenAirdropTransaction() + .addTokenTransfer(tokenID, receiverAccountId, amount) + .addTokenTransfer(tokenID, testEnv.operatorId, -amount) + .execute(testEnv.client) + .getRecord(testEnv.client); + + // claim the tokens with the receiver + new TokenClaimAirdropTransaction() + .addPendingAirdrop(record.pendingAirdropRecords.get(0).getPendingAirdropId()) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getRecord(testEnv.client); + + // claim the tokens with the receiver again + // fails with INVALID_PENDING_AIRDROP_ID + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenClaimAirdropTransaction() + .addPendingAirdrop( + record.pendingAirdropRecords.get(0).getPendingAirdropId()) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getRecord(testEnv.client); + }) + .withMessageContaining(Status.INVALID_PENDING_AIRDROP_ID.toString()); + } + } + + @Test + @DisplayName("Cannot claim the tokens with empty list") + void cannotClaimWithEmptyPendingAirdropsList() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // claim the tokens with the receiver without setting pendingAirdropIds + // fails with EMPTY_PENDING_AIRDROP_ID_LIST + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenClaimAirdropTransaction() + .execute(testEnv.client) + .getRecord(testEnv.client); + }) + .withMessageContaining(Status.EMPTY_PENDING_AIRDROP_ID_LIST.toString()); + } + } + + @Test + @DisplayName("Cannot claim the tokens with duplicate entries") + void cannotClaimTokensWithDuplicateEntries() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // create fungible token + var tokenID = EntityHelper.createFungibleToken(testEnv, 3); + + // create receiver with 0 auto associations + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); + + // airdrop the tokens + var record = new TokenAirdropTransaction() + .addTokenTransfer(tokenID, receiverAccountId, amount) + .addTokenTransfer(tokenID, testEnv.operatorId, -amount) + .execute(testEnv.client) + .getRecord(testEnv.client); + + // claim the tokens with duplicate pending airdrop token ids + // fails with PENDING_AIRDROP_ID_REPEATED + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenClaimAirdropTransaction() + .addPendingAirdrop( + record.pendingAirdropRecords.get(0).getPendingAirdropId()) + .addPendingAirdrop( + record.pendingAirdropRecords.get(0).getPendingAirdropId()) + .execute(testEnv.client) + .getRecord(testEnv.client); + }) + .withMessageContaining(Status.PENDING_AIRDROP_ID_REPEATED.toString()); + } + } + + @Test + @DisplayName("Cannot claim the tokens when token is paused") + void cannotClaimTokensWhenTokenIsPaused() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // create fungible token + var tokenID = EntityHelper.createFungibleToken(testEnv, 3); + + // create receiver with 0 auto associations + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); + + // airdrop the tokens + var record = new TokenAirdropTransaction() + .addTokenTransfer(tokenID, receiverAccountId, amount) + .addTokenTransfer(tokenID, testEnv.operatorId, -amount) + .execute(testEnv.client) + .getRecord(testEnv.client); + + // pause the token + new TokenPauseTransaction() + .setTokenId(tokenID) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // claim the tokens with receiver + // fails with TOKEN_IS_PAUSED + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenClaimAirdropTransaction() + .addPendingAirdrop( + record.pendingAirdropRecords.get(0).getPendingAirdropId()) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getRecord(testEnv.client); + }) + .withMessageContaining(Status.TOKEN_IS_PAUSED.toString()); + } + } + + @Test + @DisplayName("Cannot claim the tokens when token is deleted") + void cannotClaimTokensWhenTokenIsDeleted() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // create fungible token + var tokenID = EntityHelper.createFungibleToken(testEnv, 3); + + // create receiver with 0 auto associations + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); + + // airdrop the tokens + var record = new TokenAirdropTransaction() + .addTokenTransfer(tokenID, receiverAccountId, amount) + .addTokenTransfer(tokenID, testEnv.operatorId, -amount) + .execute(testEnv.client) + .getRecord(testEnv.client); + + // delete the token + new TokenDeleteTransaction() + .setTokenId(tokenID) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // claim the tokens with receiver + // fails with TOKEN_IS_DELETED + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenClaimAirdropTransaction() + .addPendingAirdrop( + record.pendingAirdropRecords.get(0).getPendingAirdropId()) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getRecord(testEnv.client); + }) + .withMessageContaining(Status.TOKEN_WAS_DELETED.toString()); + } + } + + @Test + @DisplayName("Cannot claim the tokens when token is frozen") + void cannotClaimTokensWhenTokenIsFrozen() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // create fungible token + var tokenID = EntityHelper.createFungibleToken(testEnv, 3); + + // create receiver with 0 auto associations + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); + + // airdrop the tokens + var record = new TokenAirdropTransaction() + .addTokenTransfer(tokenID, receiverAccountId, amount) + .addTokenTransfer(tokenID, testEnv.operatorId, -amount) + .execute(testEnv.client) + .getRecord(testEnv.client); + + // associate + new TokenAssociateTransaction() + .setAccountId(receiverAccountId) + .setTokenIds(Collections.singletonList(tokenID)) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // freeze the token + new TokenFreezeTransaction() + .setAccountId(receiverAccountId) + .setTokenId(tokenID) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // claim the tokens with receiver + // fails with ACCOUNT_FROZEN_FOR_TOKEN + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenClaimAirdropTransaction() + .addPendingAirdrop( + record.pendingAirdropRecords.get(0).getPendingAirdropId()) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getRecord(testEnv.client); + }) + .withMessageContaining(Status.ACCOUNT_FROZEN_FOR_TOKEN.toString()); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenAirdropTransactionIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenAirdropTransactionIntegrationTest.java new file mode 100644 index 0000000000..715831b94f --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenAirdropTransactionIntegrationTest.java @@ -0,0 +1,463 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.hiero.sdk.test.integration.EntityHelper.fungibleInitialBalance; +import static org.hiero.sdk.test.integration.EntityHelper.mitedNfts; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.util.Collections; +import org.hiero.sdk.AccountAllowanceApproveTransaction; +import org.hiero.sdk.AccountBalanceQuery; +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.AccountId; +import org.hiero.sdk.CustomFixedFee; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.PublicKey; +import org.hiero.sdk.Status; +import org.hiero.sdk.TokenAirdropTransaction; +import org.hiero.sdk.TokenAssociateTransaction; +import org.hiero.sdk.TokenCreateTransaction; +import org.hiero.sdk.TokenMintTransaction; +import org.hiero.sdk.TokenSupplyType; +import org.hiero.sdk.TransactionId; +import org.hiero.sdk.TransferTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class TokenAirdropTransactionIntegrationTest { + + private final int amount = 100; + + @Test + @DisplayName("Transfers tokens when the account is associated") + void canAirdropAssociatedTokens() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // create fungible and nf token + var tokenID = EntityHelper.createFungibleToken(testEnv, 3); + var nftID = EntityHelper.createNft(testEnv); + // mint some NFTs + var mintReceipt = new TokenMintTransaction() + .setTokenId(nftID) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + var nftSerials = mintReceipt.serials; + + // create receiver with unlimited auto associations and receiverSig = false + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, -1); + + // airdrop the tokens + new TokenAirdropTransaction() + .addNftTransfer(nftID.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) + .addNftTransfer(nftID.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) + .addTokenTransfer(tokenID, receiverAccountId, amount) + .addTokenTransfer(tokenID, testEnv.operatorId, -amount) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // verify the receiver holds the tokens via query + var receiverAccountBalance = + new AccountBalanceQuery().setAccountId(receiverAccountId).execute(testEnv.client); + assertEquals(amount, receiverAccountBalance.tokens.get(tokenID)); + assertEquals(2, receiverAccountBalance.tokens.get(nftID)); + + // verify the operator does not hold the tokens + var operatorBalance = + new AccountBalanceQuery().setAccountId(testEnv.operatorId).execute(testEnv.client); + assertEquals(fungibleInitialBalance - amount, operatorBalance.tokens.get(tokenID)); + assertEquals(mitedNfts - 2, operatorBalance.tokens.get(nftID)); + } + } + + @Test + @DisplayName("Tokens are in pending state when the account is not associated") + void canAirdropNonAssociatedTokens() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // create fungible and nf token + var tokenID = EntityHelper.createFungibleToken(testEnv, 3); + var nftID = EntityHelper.createNft(testEnv); + // mint some NFTs + var mintReceipt = new TokenMintTransaction() + .setTokenId(nftID) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + var nftSerials = mintReceipt.serials; + + // create receiver with 0 auto associations and receiverSig = false + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); + + // airdrop the tokens + var txn = new TokenAirdropTransaction() + .addNftTransfer(nftID.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) + .addNftTransfer(nftID.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) + .addTokenTransfer(tokenID, receiverAccountId, amount) + .addTokenTransfer(tokenID, testEnv.operatorId, -amount) + .execute(testEnv.client); + txn.setValidateStatus(true).getReceipt(testEnv.client); + var record = txn.getRecord(testEnv.client); + + // verify in the transaction record the pending airdrops + assertThat(record.pendingAirdropRecords).isNotNull(); + assertFalse(record.pendingAirdropRecords.isEmpty()); + + // verify the receiver does not hold the tokens via query + var receiverAccountBalance = + new AccountBalanceQuery().setAccountId(receiverAccountId).execute(testEnv.client); + assertNull(receiverAccountBalance.tokens.get(tokenID)); + assertNull(receiverAccountBalance.tokens.get(nftID)); + + // verify the operator does hold the tokens + var operatorBalance = + new AccountBalanceQuery().setAccountId(testEnv.operatorId).execute(testEnv.client); + assertEquals(fungibleInitialBalance, operatorBalance.tokens.get(tokenID)); + assertEquals(mitedNfts, operatorBalance.tokens.get(nftID)); + } + } + + @Test + @DisplayName("Airdrop creates a hollow account and transfers the tokens") + void canAirdropToAlias() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // create fungible and nf token + var tokenID = EntityHelper.createFungibleToken(testEnv, 3); + var nftID = EntityHelper.createNft(testEnv); + // mint some NFTs + var mintReceipt = new TokenMintTransaction() + .setTokenId(nftID) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + var nftSerials = mintReceipt.serials; + + // airdrop the tokens to an alias + PrivateKey privateKey = PrivateKey.generateED25519(); + PublicKey publicKey = privateKey.getPublicKey(); + + AccountId aliasAccountId = publicKey.toAccountId(0, 0); + + // should lazy-create and transfer the tokens + new TokenAirdropTransaction() + .addNftTransfer(nftID.nft(nftSerials.get(0)), testEnv.operatorId, aliasAccountId) + .addNftTransfer(nftID.nft(nftSerials.get(1)), testEnv.operatorId, aliasAccountId) + .addTokenTransfer(tokenID, aliasAccountId, amount) + .addTokenTransfer(tokenID, testEnv.operatorId, -amount) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // verify the receiver holds the tokens via query + var receiverAccountBalance = + new AccountBalanceQuery().setAccountId(aliasAccountId).execute(testEnv.client); + assertEquals(amount, receiverAccountBalance.tokens.get(tokenID)); + assertEquals(2, receiverAccountBalance.tokens.get(nftID)); + + // verify the operator does not hold the tokens + var operatorBalance = + new AccountBalanceQuery().setAccountId(testEnv.operatorId).execute(testEnv.client); + assertEquals(fungibleInitialBalance - amount, operatorBalance.tokens.get(tokenID)); + assertEquals(mitedNfts - 2, operatorBalance.tokens.get(nftID)); + } + } + + @Test + @DisplayName("Can airdrop with custom fees") + void canAirdropWithCustomFee() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // create receiver unlimited auto associations and receiverSig = false + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, -1); + + // create fungible token with custom fee another token + var customFeeTokenID = EntityHelper.createFungibleToken(testEnv, 3); + + // make the custom fee to be paid by the sender and the fee collector to be the operator account + CustomFixedFee fee = new CustomFixedFee() + .setFeeCollectorAccountId(testEnv.operatorId) + .setDenominatingTokenId(customFeeTokenID) + .setAmount(1) + .setAllCollectorsAreExempt(true); + + var tokenID = new TokenCreateTransaction() + .setTokenName("Test Fungible Token") + .setTokenSymbol("TFT") + .setTokenMemo("I was created for integration tests") + .setDecimals(3) + .setInitialSupply(fungibleInitialBalance) + .setMaxSupply(fungibleInitialBalance) + .setTreasuryAccountId(testEnv.operatorId) + .setSupplyType(TokenSupplyType.FINITE) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setMetadataKey(testEnv.operatorKey) + .setPauseKey(testEnv.operatorKey) + .setCustomFees(Collections.singletonList(fee)) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId; + + // create sender account with unlimited associations and send some tokens to it + var senderKey = PrivateKey.generateED25519(); + var senderAccountID = EntityHelper.createAccount(testEnv, senderKey, -1); + + // associate the token to the sender + new TokenAssociateTransaction() + .setAccountId(senderAccountID) + .setTokenIds(Collections.singletonList(customFeeTokenID)) + .freezeWith(testEnv.client) + .sign(senderKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // send tokens to the sender + new TransferTransaction() + .addTokenTransfer(customFeeTokenID, testEnv.operatorId, -amount) + .addTokenTransfer(customFeeTokenID, senderAccountID, amount) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TransferTransaction() + .addTokenTransfer(tokenID, testEnv.operatorId, -amount) + .addTokenTransfer(tokenID, senderAccountID, amount) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // airdrop the tokens from the sender to the receiver + new TokenAirdropTransaction() + .addTokenTransfer(tokenID, receiverAccountId, amount) + .addTokenTransfer(tokenID, senderAccountID, -amount) + .freezeWith(testEnv.client) + .sign(senderKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // verify the custom fee has been paid by the sender to the collector + var receiverAccountBalance = + new AccountBalanceQuery().setAccountId(receiverAccountId).execute(testEnv.client); + assertEquals(amount, receiverAccountBalance.tokens.get(tokenID)); + + var senderAccountBalance = + new AccountBalanceQuery().setAccountId(senderAccountID).execute(testEnv.client); + assertEquals(0, senderAccountBalance.tokens.get(tokenID)); + assertEquals(amount - 1, senderAccountBalance.tokens.get(customFeeTokenID)); + + var operatorBalance = + new AccountBalanceQuery().setAccountId(testEnv.operatorId).execute(testEnv.client); + assertEquals(fungibleInitialBalance - amount + 1, operatorBalance.tokens.get(customFeeTokenID)); + assertEquals(fungibleInitialBalance - amount, operatorBalance.tokens.get(tokenID)); + } + } + + @Test + @DisplayName("Can airdrop ft with receiverSig=true") + void canAirdropTokensWithReceiverSigRequiredFungible() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // create fungible token + var tokenID = EntityHelper.createFungibleToken(testEnv, 3); + + // create receiver with unlimited auto associations and receiverSig = true + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = new AccountCreateTransaction() + .setKey(receiverAccountKey) + .setInitialBalance(new Hbar(1)) + .setReceiverSignatureRequired(true) + .setMaxAutomaticTokenAssociations(-1) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + // airdrop the tokens + new TokenAirdropTransaction() + .addTokenTransfer(tokenID, receiverAccountId, amount) + .addTokenTransfer(tokenID, testEnv.operatorId, -amount) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can airdrop nft with receiverSig=true") + void canAirdropTokensWithReceiverSigRequiredNFT() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // create nft + var nftID = EntityHelper.createNft(testEnv); + // mint some NFTs + var mintReceipt = new TokenMintTransaction() + .setTokenId(nftID) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + var nftSerials = mintReceipt.serials; + + // create receiver with unlimited auto associations and receiverSig = true + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = new AccountCreateTransaction() + .setKey(receiverAccountKey) + .setInitialBalance(new Hbar(1)) + .setReceiverSignatureRequired(true) + .setMaxAutomaticTokenAssociations(-1) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + // airdrop the tokens + new TokenAirdropTransaction() + .addNftTransfer(nftID.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) + .addNftTransfer(nftID.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot airdrop ft with no balance") + void cannotAirdropTokensWithAllowanceAndWithoutBalanceFungible() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // create fungible token + var tokenID = EntityHelper.createFungibleToken(testEnv, 3); + + // create spender and approve to it some tokens + var spenderKey = PrivateKey.generateED25519(); + var spenderAccountID = EntityHelper.createAccount(testEnv, spenderKey, -1); + + // create sender + var senderKey = PrivateKey.generateED25519(); + var senderAccountID = EntityHelper.createAccount(testEnv, senderKey, -1); + + // transfer ft to sender + new TransferTransaction() + .addTokenTransfer(tokenID, testEnv.operatorId, -amount) + .addTokenTransfer(tokenID, senderAccountID, amount) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // approve allowance to the spender + new AccountAllowanceApproveTransaction() + .approveTokenAllowance(tokenID, senderAccountID, spenderAccountID, amount) + .freezeWith(testEnv.client) + .sign(senderKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // airdrop the tokens from the sender to the spender via approval + // fails with NOT_SUPPORTED + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenAirdropTransaction() + .addTokenTransfer(tokenID, spenderAccountID, amount) + .addApprovedTokenTransfer(tokenID, spenderAccountID, -amount) + .setTransactionId(TransactionId.generate(spenderAccountID)) + .freezeWith(testEnv.client) + .sign(spenderKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.NOT_SUPPORTED.toString()); + } + } + + @Test + @DisplayName("Cannot airdrop nft with no balance") + void cannotAirdropTokensWithAllowanceAndWithoutBalanceNFT() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // create nft + var nftID = EntityHelper.createNft(testEnv); + // mint some NFTs + var mintReceipt = new TokenMintTransaction() + .setTokenId(nftID) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + var nftSerials = mintReceipt.serials; + + // create spender and approve to it some tokens + var spenderKey = PrivateKey.generateED25519(); + var spenderAccountID = EntityHelper.createAccount(testEnv, spenderKey, -1); + + // create sender + var senderKey = PrivateKey.generateED25519(); + var senderAccountID = EntityHelper.createAccount(testEnv, senderKey, -1); + + // transfer ft to sender + new TransferTransaction() + .addNftTransfer(nftID.nft(nftSerials.get(0)), testEnv.operatorId, senderAccountID) + .addNftTransfer(nftID.nft(nftSerials.get(1)), testEnv.operatorId, senderAccountID) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // approve allowance to the spender + new AccountAllowanceApproveTransaction() + .approveTokenNftAllowance(nftID.nft(nftSerials.get(0)), senderAccountID, spenderAccountID) + .approveTokenNftAllowance(nftID.nft(nftSerials.get(1)), senderAccountID, spenderAccountID) + .freezeWith(testEnv.client) + .sign(senderKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // airdrop the tokens from the sender to the spender via approval + // fails with NOT_SUPPORTED + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenAirdropTransaction() + .addApprovedNftTransfer(nftID.nft(nftSerials.get(0)), senderAccountID, spenderAccountID) + .addApprovedNftTransfer(nftID.nft(nftSerials.get(1)), senderAccountID, spenderAccountID) + .setTransactionId(TransactionId.generate(spenderAccountID)) + .freezeWith(testEnv.client) + .sign(spenderKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.NOT_SUPPORTED.toString()); + } + } + + @Test + @DisplayName("Cannot airdrop with invalid body") + void cannotAirdropTokensWithInvalidBody() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // airdrop with no tokenID or NftID + // fails with EMPTY_TOKEN_TRANSFER_BODY + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenAirdropTransaction().execute(testEnv.client).getReceipt(testEnv.client); + }) + .withMessageContaining(Status.EMPTY_TOKEN_TRANSFER_BODY.toString()); + + // create fungible token + var tokenID = EntityHelper.createFungibleToken(testEnv, 3); + + // airdrop with invalid transfers + // fails with INVALID_TRANSACTION_BODY + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenAirdropTransaction() + .addTokenTransfer(tokenID, testEnv.operatorId, 100) + .addTokenTransfer(tokenID, testEnv.operatorId, 100) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_TRANSACTION_BODY.toString()); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenAutomaticAssociationIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenAutomaticAssociationIntegrationTest.java new file mode 100644 index 0000000000..50da6d2339 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenAutomaticAssociationIntegrationTest.java @@ -0,0 +1,557 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.ArrayList; +import org.hiero.sdk.AccountAllowanceApproveTransaction; +import org.hiero.sdk.AccountBalanceQuery; +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.AccountInfoQuery; +import org.hiero.sdk.AccountUpdateTransaction; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.TokenDeleteTransaction; +import org.hiero.sdk.TokenMintTransaction; +import org.hiero.sdk.TransactionId; +import org.hiero.sdk.TransferTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class TokenAutomaticAssociationIntegrationTest { + + @Test + @DisplayName("Can transfer Fungible Tokens to accounts with Limited Max Auto Associations") + void canTransferFungibleTokensToAccountsWithLimitedMaxAutoAssociations() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var tokenId1 = EntityHelper.createFungibleToken(testEnv, 0); + var tokenId2 = EntityHelper.createFungibleToken(testEnv, 0); + var accountKey = PrivateKey.generateED25519(); + var accountMaxAutomaticTokenAssociations = 1; + var receiverAccountId = + EntityHelper.createAccount(testEnv, accountKey, accountMaxAutomaticTokenAssociations); + + var accountInfoBeforeTokenAssociation = + new AccountInfoQuery().setAccountId(receiverAccountId).execute(testEnv.client); + assertThat(accountInfoBeforeTokenAssociation.maxAutomaticTokenAssociations) + .isEqualTo(1); + assertThat(accountInfoBeforeTokenAssociation.tokenRelationships.size()) + .isEqualTo(0); + + var transferRecord = new TransferTransaction() + .addTokenTransfer(tokenId1, testEnv.operatorId, -1) + .addTokenTransfer(tokenId1, receiverAccountId, 1) + .execute(testEnv.client) + .getRecord(testEnv.client); + assertThat(transferRecord.automaticTokenAssociations.size()).isEqualTo(1); + assertThat(transferRecord.automaticTokenAssociations.get(0).accountId) + .isEqualTo(receiverAccountId); + assertThat(transferRecord.automaticTokenAssociations.get(0).tokenId).isEqualTo(tokenId1); + + var accountInfoAfterTokenAssociation = + new AccountInfoQuery().setAccountId(receiverAccountId).execute(testEnv.client); + assertThat(accountInfoAfterTokenAssociation.tokenRelationships.size()) + .isEqualTo(1); + assertThat(accountInfoAfterTokenAssociation.tokenRelationships.get(tokenId1).automaticAssociation) + .isTrue(); + + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> { + new TransferTransaction() + .addTokenTransfer(tokenId2, testEnv.operatorId, -1) + .addTokenTransfer(tokenId2, receiverAccountId, 1) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining("NO_REMAINING_AUTOMATIC_ASSOCIATIONS"); + + new AccountUpdateTransaction() + .setAccountId(receiverAccountId) + .setMaxAutomaticTokenAssociations(2) + .freezeWith(testEnv.client) + .sign(accountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var accountInfoAfterMaxAssocUpdate = + new AccountInfoQuery().setAccountId(receiverAccountId).execute(testEnv.client); + assertThat(accountInfoAfterMaxAssocUpdate.maxAutomaticTokenAssociations) + .isEqualTo(2); + + new TokenDeleteTransaction() + .setTokenId(tokenId1) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenDeleteTransaction() + .setTokenId(tokenId2) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can transfer Nfts to accounts with Limited Max Auto Associations") + void canTransferNftsToAccountsWithLimitedMaxAutoAssociations() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var tokenId1 = EntityHelper.createNft(testEnv); + var tokenId2 = EntityHelper.createNft(testEnv); + var accountKey = PrivateKey.generateED25519(); + var accountMaxAutomaticTokenAssociations = 1; + var receiverAccountId = + EntityHelper.createAccount(testEnv, accountKey, accountMaxAutomaticTokenAssociations); + + var mintReceiptToken1 = new TokenMintTransaction() + .setTokenId(tokenId1) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var mintReceiptToken2 = new TokenMintTransaction() + .setTokenId(tokenId2) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var accountInfoBeforeTokenAssociation = + new AccountInfoQuery().setAccountId(receiverAccountId).execute(testEnv.client); + assertThat(accountInfoBeforeTokenAssociation.maxAutomaticTokenAssociations) + .isEqualTo(1); + assertThat(accountInfoBeforeTokenAssociation.tokenRelationships.size()) + .isEqualTo(0); + + var serialsToTransfer = new ArrayList<>(mintReceiptToken2.serials); + var nftTransferTransaction = new TransferTransaction(); + for (var serial : serialsToTransfer) { + nftTransferTransaction.addNftTransfer(tokenId1.nft(serial), testEnv.operatorId, receiverAccountId); + } + var transferRecord = nftTransferTransaction.execute(testEnv.client).getRecord(testEnv.client); + + assertThat(transferRecord.automaticTokenAssociations.size()).isEqualTo(1); + assertThat(transferRecord.automaticTokenAssociations.get(0).accountId) + .isEqualTo(receiverAccountId); + assertThat(transferRecord.automaticTokenAssociations.get(0).tokenId).isEqualTo(tokenId1); + + var accountInfoAfterTokenAssociation = + new AccountInfoQuery().setAccountId(receiverAccountId).execute(testEnv.client); + assertThat(accountInfoAfterTokenAssociation.tokenRelationships.size()) + .isEqualTo(1); + assertThat(accountInfoAfterTokenAssociation.tokenRelationships.get(tokenId1).automaticAssociation) + .isTrue(); + + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> { + var serial = mintReceiptToken2.serials.get(0); + new TransferTransaction() + .addNftTransfer(tokenId2.nft(serial), testEnv.operatorId, receiverAccountId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining("NO_REMAINING_AUTOMATIC_ASSOCIATIONS"); + + new AccountUpdateTransaction() + .setAccountId(receiverAccountId) + .setMaxAutomaticTokenAssociations(2) + .freezeWith(testEnv.client) + .sign(accountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var accountInfoAfterMaxAssocUpdate = + new AccountInfoQuery().setAccountId(receiverAccountId).execute(testEnv.client); + assertThat(accountInfoAfterMaxAssocUpdate.maxAutomaticTokenAssociations) + .isEqualTo(2); + + new TokenDeleteTransaction() + .setTokenId(tokenId1) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenDeleteTransaction() + .setTokenId(tokenId2) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + /** + * @notice E2E-HIP-904 + * @url https://hips.hedera.com/hip/hip-904 + */ + @Test + @DisplayName("Can set unlimited max auto associations for Account") + void canSetUnlimitedMaxAutoAssociationsForAccount() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var accountKey = PrivateKey.generateED25519(); + var accountMaxAutomaticTokenAssociations = -1; + var accountId = EntityHelper.createAccount(testEnv, accountKey, accountMaxAutomaticTokenAssociations); + + new AccountUpdateTransaction() + .setAccountId(accountId) + .setMaxAutomaticTokenAssociations(accountMaxAutomaticTokenAssociations) + .freezeWith(testEnv.client) + .sign(accountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var accountInfoBeforeTokenAssociation = + new AccountInfoQuery().setAccountId(accountId).execute(testEnv.client); + assertThat(accountInfoBeforeTokenAssociation.maxAutomaticTokenAssociations) + .isEqualTo(-1); + } + } + + /** + * @notice E2E-HIP-904 + * @url https://hips.hedera.com/hip/hip-904 + */ + @Test + @DisplayName("Can transfer Fungible Tokens to accounts with Unlimited Max Auto Associations") + void canTransferFungibleTokensToAccountsWithUnlimitedMaxAutoAssociations() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var tokenId1 = EntityHelper.createFungibleToken(testEnv, 3); + var tokenId2 = EntityHelper.createFungibleToken(testEnv, 3); + var accountKey = PrivateKey.generateED25519(); + var accountId1 = EntityHelper.createAccount(testEnv, accountKey, -1); + var accountId2 = EntityHelper.createAccount(testEnv, accountKey, 100); + + new AccountUpdateTransaction() + .setAccountId(accountId2) + .setMaxAutomaticTokenAssociations(-1) + .freezeWith(testEnv.client) + .sign(accountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // transfer to both receivers some token1 tokens + new TransferTransaction() + .addTokenTransfer(tokenId1, testEnv.operatorId, -1000) + .addTokenTransfer(tokenId1, accountId1, 1000) + .addTokenTransfer(tokenId1, testEnv.operatorId, -1000) + .addTokenTransfer(tokenId1, accountId2, 1000) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // transfer to both receivers some token2 tokens + new TransferTransaction() + .addTokenTransfer(tokenId2, testEnv.operatorId, -1000) + .addTokenTransfer(tokenId2, accountId1, 1000) + .addTokenTransfer(tokenId2, testEnv.operatorId, -1000) + .addTokenTransfer(tokenId2, accountId2, 1000) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // verify the balance of the receivers is 1000 + var accountId1Balance = + new AccountBalanceQuery().setAccountId(accountId1).execute(testEnv.client); + + assertThat(accountId1Balance.tokens.get(tokenId1)).isEqualTo(1000); + assertThat(accountId1Balance.tokens.get(tokenId2)).isEqualTo(1000); + + var accountId2Balance = + new AccountBalanceQuery().setAccountId(accountId2).execute(testEnv.client); + + assertThat(accountId2Balance.tokens.get(tokenId1)).isEqualTo(1000); + assertThat(accountId2Balance.tokens.get(tokenId2)).isEqualTo(1000); + + new TokenDeleteTransaction() + .setTokenId(tokenId1) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenDeleteTransaction() + .setTokenId(tokenId2) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + /** + * @notice E2E-HIP-904 + * @url https://hips.hedera.com/hip/hip-904 + */ + @Test + @DisplayName("Can transfer Fungible Tokens (With Decimals) to accounts with Unlimited Max Auto Associations") + void canTransferFungibleTokensWithDecimalsToAccountsWithUnlimitedMaxAutoAssociations() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var tokenDecimals = 10; + var tokenId1 = EntityHelper.createFungibleToken(testEnv, tokenDecimals); + var tokenId2 = EntityHelper.createFungibleToken(testEnv, tokenDecimals); + var accountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, accountKey, -1); + + new TransferTransaction() + .addTokenTransferWithDecimals(tokenId1, testEnv.operatorId, -1000, tokenDecimals) + .addTokenTransferWithDecimals(tokenId1, receiverAccountId, 1000, tokenDecimals) + .addTokenTransferWithDecimals(tokenId2, testEnv.operatorId, -1000, tokenDecimals) + .addTokenTransferWithDecimals(tokenId2, receiverAccountId, 1000, tokenDecimals) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var receiverAccountBalance = + new AccountBalanceQuery().setAccountId(receiverAccountId).execute(testEnv.client); + + assertThat(receiverAccountBalance.tokens.get(tokenId1)).isEqualTo(1000); + assertThat(receiverAccountBalance.tokens.get(tokenId2)).isEqualTo(1000); + + new TokenDeleteTransaction() + .setTokenId(tokenId1) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenDeleteTransaction() + .setTokenId(tokenId2) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + /** + * @notice E2E-HIP-904 + * @url https://hips.hedera.com/hip/hip-904 + */ + @Test + @DisplayName("Can transfer Fungible Tokens on Behalf Of Owner to account with Unlimited Max Auto Associations") + void canTransferFungibleTokensOnBehalfOfOwnerToAccountWithUnlimitedMaxAutoAssociations() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var tokenId1 = EntityHelper.createFungibleToken(testEnv, 3); + var tokenId2 = EntityHelper.createFungibleToken(testEnv, 3); + var accountKey = PrivateKey.generateED25519(); + var accountId = EntityHelper.createAccount(testEnv, accountKey, -1); + var spenderAccountKey = PrivateKey.generateED25519(); + var spenderAccountId = EntityHelper.createAccount(testEnv, spenderAccountKey, -1); + + new AccountAllowanceApproveTransaction() + .approveTokenAllowance(tokenId1, testEnv.operatorId, spenderAccountId, 2000) + .approveTokenAllowance(tokenId2, testEnv.operatorId, spenderAccountId, 2000) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var record = new TransferTransaction() + .addApprovedTokenTransfer(tokenId1, testEnv.operatorId, -1000) + .addTokenTransfer(tokenId1, accountId, 1000) + .addApprovedTokenTransfer(tokenId2, testEnv.operatorId, -1000) + .addTokenTransfer(tokenId2, accountId, 1000) + .setTransactionId(TransactionId.generate(spenderAccountId)) + .freezeWith(testEnv.client) + .sign(spenderAccountKey) + .execute(testEnv.client) + .getRecord(testEnv.client); + + var accountBalance = + new AccountBalanceQuery().setAccountId(accountId).execute(testEnv.client); + + assertThat(accountBalance.tokens.get(tokenId1)).isEqualTo(1000); + assertThat(accountBalance.tokens.get(tokenId2)).isEqualTo(1000); + + new TokenDeleteTransaction() + .setTokenId(tokenId1) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenDeleteTransaction() + .setTokenId(tokenId2) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + /** + * @notice E2E-HIP-904 + * @url https://hips.hedera.com/hip/hip-904 + */ + @Test + @DisplayName("Can transfer Nfts to accounts with Unlimited Max Auto Associations") + void canTransferNftsToAccountsWithUnlimitedMaxAutoAssociations() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var tokenId1 = EntityHelper.createNft(testEnv); + var tokenId2 = EntityHelper.createNft(testEnv); + var accountKey = PrivateKey.generateED25519(); + var accountId1 = EntityHelper.createAccount(testEnv, accountKey, -1); + var accountId2 = EntityHelper.createAccount(testEnv, accountKey, 100); + + var mintReceiptToken1 = new TokenMintTransaction() + .setTokenId(tokenId1) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var mintReceiptToken2 = new TokenMintTransaction() + .setTokenId(tokenId2) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var nftSerials = mintReceiptToken2.serials; + + new AccountUpdateTransaction() + .setAccountId(accountId2) + .setMaxAutomaticTokenAssociations(-1) + .freezeWith(testEnv.client) + .sign(accountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // transfer nft1 to both receivers, 2 for each + new TransferTransaction() + .addNftTransfer(tokenId1.nft(nftSerials.get(0)), testEnv.operatorId, accountId1) + .addNftTransfer(tokenId1.nft(nftSerials.get(1)), testEnv.operatorId, accountId1) + .addNftTransfer(tokenId1.nft(nftSerials.get(2)), testEnv.operatorId, accountId2) + .addNftTransfer(tokenId1.nft(nftSerials.get(3)), testEnv.operatorId, accountId2) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // transfer nft2 to both receivers, 2 for each + new TransferTransaction() + .addNftTransfer(tokenId2.nft(nftSerials.get(0)), testEnv.operatorId, accountId1) + .addNftTransfer(tokenId2.nft(nftSerials.get(1)), testEnv.operatorId, accountId1) + .addNftTransfer(tokenId2.nft(nftSerials.get(2)), testEnv.operatorId, accountId2) + .addNftTransfer(tokenId2.nft(nftSerials.get(3)), testEnv.operatorId, accountId2) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // verify the balance of the receivers is 2 + var accountId1Balance = + new AccountBalanceQuery().setAccountId(accountId1).execute(testEnv.client); + + assertThat(accountId1Balance.tokens.get(tokenId1)).isEqualTo(2); + assertThat(accountId1Balance.tokens.get(tokenId2)).isEqualTo(2); + + var accountId2Balance = + new AccountBalanceQuery().setAccountId(accountId2).execute(testEnv.client); + + assertThat(accountId2Balance.tokens.get(tokenId1)).isEqualTo(2); + assertThat(accountId2Balance.tokens.get(tokenId2)).isEqualTo(2); + + new TokenDeleteTransaction() + .setTokenId(tokenId1) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenDeleteTransaction() + .setTokenId(tokenId2) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + /** + * @notice E2E-HIP-904 + * @url https://hips.hedera.com/hip/hip-904 + */ + @Test + @DisplayName("Can transfer Nfts on Behalf Of Owner to account with Unlimited Max Auto Associations") + void canTransferNftsOnBehalfOfOwnerToAccountWithUnlimitedMaxAutoAssociations() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var tokenId1 = EntityHelper.createNft(testEnv); + var tokenId2 = EntityHelper.createNft(testEnv); + var accountKey = PrivateKey.generateED25519(); + var accountId = EntityHelper.createAccount(testEnv, accountKey, -1); + var spenderAccountKey = PrivateKey.generateED25519(); + var spenderAccountId = EntityHelper.createAccount(testEnv, spenderAccountKey, -1); + + var mintReceiptToken1 = new TokenMintTransaction() + .setTokenId(tokenId1) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var mintReceiptToken2 = new TokenMintTransaction() + .setTokenId(tokenId2) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var nftSerials = mintReceiptToken2.serials; + + new AccountAllowanceApproveTransaction() + .approveTokenNftAllowanceAllSerials(tokenId1, testEnv.operatorId, spenderAccountId) + .approveTokenNftAllowanceAllSerials(tokenId2, testEnv.operatorId, spenderAccountId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TransferTransaction() + .addApprovedNftTransfer(tokenId1.nft(nftSerials.get(0)), testEnv.operatorId, accountId) + .addApprovedNftTransfer(tokenId1.nft(nftSerials.get(1)), testEnv.operatorId, accountId) + .addApprovedNftTransfer(tokenId2.nft(nftSerials.get(0)), testEnv.operatorId, accountId) + .addApprovedNftTransfer(tokenId2.nft(nftSerials.get(1)), testEnv.operatorId, accountId) + .setTransactionId(TransactionId.generate(spenderAccountId)) + .freezeWith(testEnv.client) + .sign(spenderAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var accountBalance = + new AccountBalanceQuery().setAccountId(accountId).execute(testEnv.client); + + assertThat(accountBalance.tokens.get(tokenId1)).isEqualTo(2); + assertThat(accountBalance.tokens.get(tokenId2)).isEqualTo(2); + + new TokenDeleteTransaction() + .setTokenId(tokenId1) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenDeleteTransaction() + .setTokenId(tokenId2) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + /** + * @notice E2E-HIP-904 + * @url https://hips.hedera.com/hip/hip-904 + */ + @Test + @DisplayName("Cannot Set Invalid Max Auto Associations Values") + void cannotSetInvalidMaxAutoAssociationsValues() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var accountKey = PrivateKey.generateED25519(); + + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> { + new AccountCreateTransaction() + .setKey(accountKey) + .setMaxAutomaticTokenAssociations(-2) + .execute(testEnv.client); + }) + .withMessageContaining("INVALID_MAX_AUTO_ASSOCIATIONS"); + + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> { + new AccountCreateTransaction() + .setKey(accountKey) + .setMaxAutomaticTokenAssociations(-1000) + .execute(testEnv.client); + }) + .withMessageContaining("INVALID_MAX_AUTO_ASSOCIATIONS"); + + var accountId = EntityHelper.createAccount(testEnv, accountKey, 100); + + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> { + new AccountUpdateTransaction() + .setAccountId(accountId) + .setMaxAutomaticTokenAssociations(-2) + .freezeWith(testEnv.client) + .sign(accountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining("INVALID_MAX_AUTO_ASSOCIATIONS"); + + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> { + new AccountUpdateTransaction() + .setAccountId(accountId) + .setMaxAutomaticTokenAssociations(-1000) + .freezeWith(testEnv.client) + .sign(accountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining("INVALID_MAX_AUTO_ASSOCIATIONS"); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenBurnIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenBurnIntegrationTest.java new file mode 100644 index 0000000000..13d9a92919 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenBurnIntegrationTest.java @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Collections; +import java.util.Objects; +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.sdk.Status; +import org.hiero.sdk.TokenAssociateTransaction; +import org.hiero.sdk.TokenBurnTransaction; +import org.hiero.sdk.TokenCreateTransaction; +import org.hiero.sdk.TokenMintTransaction; +import org.hiero.sdk.TokenType; +import org.hiero.sdk.TransferTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class TokenBurnIntegrationTest { + @Test + @DisplayName("Can burn tokens") + void canBurnTokens() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var response = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client); + + var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); + + var receipt = new TokenBurnTransaction() + .setAmount(10) + .setTokenId(tokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + assertThat(receipt.totalSupply).isEqualTo(1000000 - 10); + } + } + + @Test + @DisplayName("Cannot burn tokens when token ID is not set") + void cannotBurnTokensWhenTokenIDIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenBurnTransaction() + .setAmount(10) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_TOKEN_ID.toString()); + } + } + + @Test + @DisplayName("Can burn tokens when amount is not set") + void canBurnTokensWhenAmountIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var response = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client); + + var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); + + var receipt = new TokenBurnTransaction() + .setTokenId(tokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + assertThat(receipt.status).isEqualTo(Status.SUCCESS); + } + } + + @Test + @DisplayName("Cannot burn tokens when supply key does not sign transaction") + void cannotBurnTokensWhenSupplyKeyDoesNotSignTransaction() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var response = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(PrivateKey.generate()) + .setFreezeDefault(false) + .execute(testEnv.client); + + var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenBurnTransaction() + .setTokenId(tokenId) + .setAmount(10) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + } + } + + @Test + @DisplayName("Can burn NFTs") + void canBurnNfts() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var createReceipt = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var tokenId = Objects.requireNonNull(createReceipt.tokenId); + + var mintReceipt = new TokenMintTransaction() + .setTokenId(tokenId) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenBurnTransaction() + .setSerials(mintReceipt.serials.subList(0, 4)) + .setTokenId(tokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot burn NFTs when NFT is not owned by treasury") + void cannotBurnNftsWhenNftIsNotOwned() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var createReceipt = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var tokenId = Objects.requireNonNull(createReceipt.tokenId); + + var serials = new TokenMintTransaction() + .setTokenId(tokenId) + .setMetadata(NftMetadataGenerator.generate((byte) 1)) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .serials; + + var key = PrivateKey.generateED25519(); + + var accountId = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + new TokenAssociateTransaction() + .setAccountId(accountId) + .setTokenIds(Collections.singletonList(tokenId)) + .freezeWith(testEnv.client) + .signWithOperator(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TransferTransaction() + .addNftTransfer(tokenId.nft(serials.get(0)), testEnv.operatorId, accountId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenBurnTransaction() + .setSerials(serials) + .setTokenId(tokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.TREASURY_MUST_OWN_BURNED_NFT.toString()); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenCreateIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenCreateIntegrationTest.java new file mode 100644 index 0000000000..580059e55e --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenCreateIntegrationTest.java @@ -0,0 +1,390 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import org.hiero.sdk.AccountId; +import org.hiero.sdk.CustomFee; +import org.hiero.sdk.CustomFixedFee; +import org.hiero.sdk.CustomFractionalFee; +import org.hiero.sdk.CustomRoyaltyFee; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.sdk.Status; +import org.hiero.sdk.TokenCreateTransaction; +import org.hiero.sdk.TokenType; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class TokenCreateIntegrationTest { + private static List createFixedFeeList(int count, AccountId feeCollector) { + var feeList = new ArrayList(); + for (int i = 0; i < count; i++) { + feeList.add(new CustomFixedFee().setAmount(10).setFeeCollectorAccountId(feeCollector)); + } + return feeList; + } + + private static List createFractionalFeeList(int count, AccountId feeCollector) { + var feeList = new ArrayList(); + for (int i = 0; i < count; i++) { + feeList.add(new CustomFractionalFee() + .setNumerator(1) + .setDenominator(20) + .setMin(1) + .setMax(10) + .setFeeCollectorAccountId(feeCollector)); + } + return feeList; + } + + @Test + @DisplayName("Can create token with operator as all keys") + void canCreateTokenWithOperatorAsAllKeys() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var response = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFeeScheduleKey(testEnv.operatorKey) + .setPauseKey(testEnv.operatorKey) + .setMetadataKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client); + + Objects.requireNonNull(response.getReceipt(testEnv.client)); + } + } + + @Test + @DisplayName("Can create token with minimal properties set") + @SuppressWarnings("UnusedVariable") + void canCreateTokenWithMinimalPropertiesSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(testEnv.operatorId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot create token when token name is not set") + void cannotCreateTokenWhenTokenNameIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenCreateTransaction() + .setTokenSymbol("F") + .setTreasuryAccountId(testEnv.operatorId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.MISSING_TOKEN_NAME.toString()); + } + } + + @Test + @DisplayName("Cannot create token when token symbol is not set") + void cannotCreateTokenWhenTokenSymbolIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenCreateTransaction() + .setTokenName("ffff") + .setTreasuryAccountId(testEnv.operatorId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.MISSING_TOKEN_SYMBOL.toString()); + } + } + + @Test + @DisplayName("Cannot create token when token treasury account ID is not set") + void cannotCreateTokenWhenTokenTreasuryAccountIDIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_TREASURY_ACCOUNT_FOR_TOKEN.toString()); + } + } + + @Test + @DisplayName("Cannot create token when token treasury account ID does not sign transaction") + void cannotCreateTokenWhenTokenTreasuryAccountIDDoesNotSignTransaction() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(AccountId.fromString("0.0.3")) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + } + } + + @Test + @DisplayName("Cannot create token when admin key does not sign transaction") + void cannotCreateTokenWhenAdminKeyDoesNotSignTransaction() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + } + } + + @Test + @DisplayName("Can create token with custom fees") + void canCreateTokenWithCustomFees() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var customFees = new ArrayList(); + customFees.add(new CustomFixedFee().setAmount(10).setFeeCollectorAccountId(testEnv.operatorId)); + customFees.add(new CustomFractionalFee() + .setNumerator(1) + .setDenominator(20) + .setMin(1) + .setMax(10) + .setFeeCollectorAccountId(testEnv.operatorId)); + + new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setCustomFees(customFees) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot create custom fee list with > 10 entries") + void cannotCreateMoreThanTenCustomFees() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setAdminKey(testEnv.operatorKey) + .setTreasuryAccountId(testEnv.operatorId) + .setCustomFees(createFixedFeeList(11, testEnv.operatorId)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.CUSTOM_FEES_LIST_TOO_LONG.toString()); + } + } + + @Test + @DisplayName("Can create custom fee list with 10 fixed fees") + void canCreateTenFixedFees() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setCustomFees(createFixedFeeList(10, testEnv.operatorId)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can create custom fee list with 10 fractional fees") + void canCreateTenFractionalFees() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setAdminKey(testEnv.operatorKey) + .setTreasuryAccountId(testEnv.operatorId) + .setCustomFees(createFractionalFeeList(10, testEnv.operatorId)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot create a token with a custom fee where min > max") + void cannotCreateMinGreaterThanMax() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setCustomFees(Collections.singletonList(new CustomFractionalFee() + .setNumerator(1) + .setDenominator(3) + .setMin(3) + .setMax(2) + .setFeeCollectorAccountId(testEnv.operatorId))) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.FRACTIONAL_FEE_MAX_AMOUNT_LESS_THAN_MIN_AMOUNT.toString()); + } + } + + @Test + @DisplayName("Cannot create a token with invalid fee collector account ID") + void cannotCreateInvalidFeeCollector() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setAdminKey(testEnv.operatorKey) + .setTreasuryAccountId(testEnv.operatorId) + .setCustomFees(Collections.singletonList(new CustomFixedFee().setAmount(1))) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_CUSTOM_FEE_COLLECTOR.toString()); + } + } + + @Test + @DisplayName("Cannot create a token with a negative custom fee") + void cannotCreateNegativeFee() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setAdminKey(testEnv.operatorKey) + .setTreasuryAccountId(testEnv.operatorId) + .setCustomFees(Collections.singletonList(new CustomFixedFee() + .setAmount(-1) + .setFeeCollectorAccountId(testEnv.operatorId))) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.CUSTOM_FEE_MUST_BE_POSITIVE.toString()); + } + } + + @Test + @DisplayName("Cannot create custom fee with 0 denominator") + void cannotCreateZeroDenominator() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setCustomFees(Collections.singletonList(new CustomFractionalFee() + .setNumerator(1) + .setDenominator(0) + .setMin(1) + .setMax(10) + .setFeeCollectorAccountId(testEnv.operatorId))) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.FRACTION_DIVIDES_BY_ZERO.toString()); + } + } + + @Test + @DisplayName("Can create NFT") + void canCreateNfts() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var response = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client); + + Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); + } + } + + @Test + @DisplayName("Can create NFT with royalty fee") + void canCreateRoyaltyFee() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(testEnv.operatorId) + .setSupplyKey(testEnv.operatorKey) + .setAdminKey(testEnv.operatorKey) + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setCustomFees(Collections.singletonList(new CustomRoyaltyFee() + .setNumerator(1) + .setDenominator(10) + .setFallbackFee(new CustomFixedFee().setHbarAmount(new Hbar(1))) + .setFeeCollectorAccountId(testEnv.operatorId))) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenDeleteIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenDeleteIntegrationTest.java new file mode 100644 index 0000000000..d035cacdd6 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenDeleteIntegrationTest.java @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Objects; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.sdk.Status; +import org.hiero.sdk.TokenCreateTransaction; +import org.hiero.sdk.TokenDeleteTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class TokenDeleteIntegrationTest { + @Test + @DisplayName("Can delete token") + void canDeleteToken() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var response = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client); + + var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); + + new TokenDeleteTransaction() + .setTokenId(tokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can delete token with only admin key set") + void canDeleteTokenWithOnlyAdminKeySet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var response = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .execute(testEnv.client); + + Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); + } + } + + @Test + @DisplayName("Cannot delete token when admin key does not sign transaction") + void cannotDeleteTokenWhenAdminKeyDoesNotSignTransaction() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + var response = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(key) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client); + + var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenDeleteTransaction() + .setTokenId(tokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + new TokenDeleteTransaction() + .setTokenId(tokenId) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot delete token when token ID is not set") + void cannotDeleteTokenWhenTokenIDIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenDeleteTransaction().execute(testEnv.client).getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_TOKEN_ID.toString()); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenDissociateIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenDissociateIntegrationTest.java new file mode 100644 index 0000000000..cfc1b588b4 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenDissociateIntegrationTest.java @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Collections; +import java.util.Objects; +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.sdk.Status; +import org.hiero.sdk.TokenAssociateTransaction; +import org.hiero.sdk.TokenCreateTransaction; +import org.hiero.sdk.TokenDissociateTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class TokenDissociateIntegrationTest { + @Test + @DisplayName("Can dissociate account with token") + void canAssociateAccountWithToken() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + new TokenAssociateTransaction() + .setAccountId(accountId) + .setTokenIds(Collections.singletonList(tokenId)) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenDissociateTransaction() + .setAccountId(accountId) + .setTokenIds(Collections.singletonList(tokenId)) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can execute token dissociate transaction even when token IDs are not set") + void canExecuteTokenDissociateTransactionEvenWhenTokenIDsAreNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + new TokenDissociateTransaction() + .setAccountId(accountId) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot dissociate account with tokens when account ID is not set") + void cannotDissociateAccountWithTokensWhenAccountIDIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenDissociateTransaction() + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_ACCOUNT_ID.toString()); + } + } + + @Test + @DisplayName("Cannot dissociate account with tokens when account does not sign transaction") + void cannotDissociateAccountWhenAccountDoesNotSignTransaction() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenDissociateTransaction() + .setAccountId(accountId) + .setTokenIds(Collections.singletonList(tokenId)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + } + } + + @Test + @DisplayName("Cannot dissociate account from token when account was not associated with") + void cannotDissociateAccountFromTokenWhenAccountWasNotAssociatedWith() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenDissociateTransaction() + .setAccountId(accountId) + .setTokenIds(Collections.singletonList(tokenId)) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.TOKEN_NOT_ASSOCIATED_TO_ACCOUNT.toString()); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenFeeScheduleUpdateIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenFeeScheduleUpdateIntegrationTest.java new file mode 100644 index 0000000000..ab2479c671 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenFeeScheduleUpdateIntegrationTest.java @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.ArrayList; +import java.util.Objects; +import org.hiero.sdk.CustomFee; +import org.hiero.sdk.CustomFixedFee; +import org.hiero.sdk.CustomFractionalFee; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.sdk.Status; +import org.hiero.sdk.TokenCreateTransaction; +import org.hiero.sdk.TokenFeeScheduleUpdateTransaction; +import org.hiero.sdk.TokenInfoQuery; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class TokenFeeScheduleUpdateIntegrationTest { + @Test + @DisplayName("Can update token fees") + void canUpdateToken() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var response = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFeeScheduleKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client); + + var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); + + var info = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(info.tokenId).isEqualTo(tokenId); + assertThat(info.name).isEqualTo("ffff"); + assertThat(info.symbol).isEqualTo("F"); + assertThat(info.decimals).isEqualTo(3); + assertThat(testEnv.operatorId).isEqualTo(info.treasuryAccountId); + assertThat(info.adminKey).isNotNull(); + assertThat(info.freezeKey).isNotNull(); + assertThat(info.wipeKey).isNotNull(); + assertThat(info.kycKey).isNotNull(); + assertThat(info.supplyKey).isNotNull(); + assertThat(info.adminKey.toString()).isEqualTo(testEnv.operatorKey.toString()); + assertThat(info.freezeKey.toString()).isEqualTo(testEnv.operatorKey.toString()); + assertThat(info.wipeKey.toString()).isEqualTo(testEnv.operatorKey.toString()); + assertThat(info.kycKey.toString()).isEqualTo(testEnv.operatorKey.toString()); + assertThat(info.supplyKey.toString()).isEqualTo(testEnv.operatorKey.toString()); + assertThat(info.feeScheduleKey.toString()).isEqualTo(testEnv.operatorKey.toString()); + assertThat(info.defaultFreezeStatus).isNotNull(); + assertThat(info.defaultFreezeStatus).isFalse(); + assertThat(info.defaultKycStatus).isNotNull(); + assertThat(info.defaultKycStatus).isFalse(); + assertThat(info.customFees.size()).isEqualTo(0); + + var customFees = new ArrayList(); + customFees.add(new CustomFixedFee().setAmount(10).setFeeCollectorAccountId(testEnv.operatorId)); + customFees.add(new CustomFractionalFee() + .setNumerator(1) + .setDenominator(20) + .setMin(1) + .setMax(10) + .setFeeCollectorAccountId(testEnv.operatorId)); + + new TokenFeeScheduleUpdateTransaction() + .setTokenId(tokenId) + .setCustomFees(customFees) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + info = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(info.tokenId).isEqualTo(tokenId); + assertThat(info.name).isEqualTo("ffff"); + assertThat(info.symbol).isEqualTo("F"); + assertThat(info.decimals).isEqualTo(3); + assertThat(info.treasuryAccountId).isEqualTo(testEnv.operatorId); + assertThat(info.adminKey).isNotNull(); + assertThat(info.freezeKey).isNotNull(); + assertThat(info.wipeKey).isNotNull(); + assertThat(info.kycKey).isNotNull(); + assertThat(info.supplyKey).isNotNull(); + assertThat(info.adminKey.toString()).isEqualTo(testEnv.operatorKey.toString()); + assertThat(info.freezeKey.toString()).isEqualTo(testEnv.operatorKey.toString()); + assertThat(info.wipeKey.toString()).isEqualTo(testEnv.operatorKey.toString()); + assertThat(info.kycKey.toString()).isEqualTo(testEnv.operatorKey.toString()); + assertThat(info.supplyKey.toString()).isEqualTo(testEnv.operatorKey.toString()); + assertThat(info.feeScheduleKey.toString()).isEqualTo(testEnv.operatorKey.toString()); + assertThat(info.defaultFreezeStatus).isNotNull(); + assertThat(info.defaultFreezeStatus).isFalse(); + assertThat(info.defaultKycStatus).isNotNull(); + assertThat(info.defaultKycStatus).isFalse(); + + var fees = info.customFees; + assertThat(fees.size()).isEqualTo(2); + int fixedCount = 0; + int fractionalCount = 0; + for (var fee : fees) { + if (fee instanceof CustomFixedFee) { + fixedCount++; + var fixed = (CustomFixedFee) fee; + assertThat(fixed.getAmount()).isEqualTo(10); + assertThat(fixed.getFeeCollectorAccountId()).isEqualTo(testEnv.operatorId); + assertThat(fixed.getDenominatingTokenId()).isNull(); + } else if (fee instanceof CustomFractionalFee) { + fractionalCount++; + var fractional = (CustomFractionalFee) fee; + assertThat(fractional.getNumerator()).isEqualTo(1); + assertThat(fractional.getDenominator()).isEqualTo(20); + assertThat(fractional.getMin()).isEqualTo(1); + assertThat(fractional.getMax()).isEqualTo(10); + assertThat(fractional.getFeeCollectorAccountId()).isEqualTo(testEnv.operatorId); + } + } + assertThat(fixedCount).isEqualTo(1); + assertThat(fractionalCount).isEqualTo(1); + } + } + + @Test + @DisplayName("Cannot update fee schedule with any key other than fee schedule key") + void cannotUpdateWithAnyOtherKey() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var response = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFeeScheduleKey(PrivateKey.generate()) + .setFreezeDefault(false) + .execute(testEnv.client); + + var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); + + var customFees = new ArrayList(); + customFees.add(new CustomFixedFee().setAmount(10).setFeeCollectorAccountId(testEnv.operatorId)); + customFees.add(new CustomFractionalFee() + .setNumerator(1) + .setDenominator(20) + .setMin(1) + .setMax(10) + .setFeeCollectorAccountId(testEnv.operatorId)); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenFeeScheduleUpdateTransaction() + .setTokenId(tokenId) + .setCustomFees(customFees) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenFreezeIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenFreezeIntegrationTest.java new file mode 100644 index 0000000000..4d49fb3692 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenFreezeIntegrationTest.java @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Collections; +import java.util.Objects; +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.sdk.Status; +import org.hiero.sdk.TokenAssociateTransaction; +import org.hiero.sdk.TokenCreateTransaction; +import org.hiero.sdk.TokenFreezeTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class TokenFreezeIntegrationTest { + @Test + @DisplayName("Can freeze account with token") + void canFreezeAccountWithToken() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + new TokenAssociateTransaction() + .setAccountId(accountId) + .setTokenIds(Collections.singletonList(tokenId)) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenFreezeTransaction() + .setAccountId(accountId) + .setTokenId(tokenId) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot freeze account on token when token ID is not set") + void cannotFreezeAccountOnTokenWhenTokenIDIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenFreezeTransaction() + .setAccountId(accountId) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_TOKEN_ID.toString()); + } + } + + @Test + @DisplayName("Cannot freeze account on token when account ID is not set") + void cannotFreezeAccountOnTokenWhenAccountIDIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + var response = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client); + + var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenFreezeTransaction() + .setTokenId(tokenId) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_ACCOUNT_ID.toString()); + } + } + + @Test + @DisplayName("Cannot freeze account on token when account was not associated with") + void cannotFreezeAccountOnTokenWhenAccountWasNotAssociatedWith() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenFreezeTransaction() + .setAccountId(accountId) + .setTokenId(tokenId) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.TOKEN_NOT_ASSOCIATED_TO_ACCOUNT.toString()); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenGrantKycIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenGrantKycIntegrationTest.java new file mode 100644 index 0000000000..9bee000db9 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenGrantKycIntegrationTest.java @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Collections; +import java.util.Objects; +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.sdk.Status; +import org.hiero.sdk.TokenAssociateTransaction; +import org.hiero.sdk.TokenCreateTransaction; +import org.hiero.sdk.TokenGrantKycTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class TokenGrantKycIntegrationTest { + @Test + @DisplayName("Can grant kyc to account with token") + void canGrantKycAccountWithToken() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + new TokenAssociateTransaction() + .setAccountId(accountId) + .setTokenIds(Collections.singletonList(tokenId)) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenGrantKycTransaction() + .setAccountId(accountId) + .setTokenId(tokenId) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot grant kyc to account on token when token ID is not set") + void cannotGrantKycToAccountOnTokenWhenTokenIDIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenGrantKycTransaction() + .setAccountId(accountId) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_TOKEN_ID.toString()); + } + } + + @Test + @DisplayName("Cannot grant kyc to account on token when account ID is not set") + void cannotGrantKycToAccountOnTokenWhenAccountIDIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + var response = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client); + + var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenGrantKycTransaction() + .setTokenId(tokenId) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_ACCOUNT_ID.toString()); + } + } + + @Test + @DisplayName("Cannot grant kyc to account on token when account was not associated with") + void cannotGrantKycToAccountOnTokenWhenAccountWasNotAssociatedWith() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenGrantKycTransaction() + .setAccountId(accountId) + .setTokenId(tokenId) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.TOKEN_NOT_ASSOCIATED_TO_ACCOUNT.toString()); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenInfoIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenInfoIntegrationTest.java new file mode 100644 index 0000000000..7d12b8a269 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenInfoIntegrationTest.java @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Objects; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.MaxQueryPaymentExceededException; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.TokenCreateTransaction; +import org.hiero.sdk.TokenDeleteTransaction; +import org.hiero.sdk.TokenInfoQuery; +import org.hiero.sdk.TokenMintTransaction; +import org.hiero.sdk.TokenSupplyType; +import org.hiero.sdk.TokenType; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class TokenInfoIntegrationTest { + + @Test + @DisplayName("Can query token info when all keys are different") + void canQueryTokenInfoWhenAllKeysAreDifferent() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key1 = PrivateKey.generateED25519(); + var key2 = PrivateKey.generateED25519(); + var key3 = PrivateKey.generateED25519(); + var key4 = PrivateKey.generateED25519(); + var key5 = PrivateKey.generateED25519(); + var key6 = PrivateKey.generateED25519(); + var key7 = PrivateKey.generateED25519(); + + var response = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(key1) + .setFreezeKey(key2) + .setWipeKey(key3) + .setKycKey(key4) + .setSupplyKey(key5) + .setPauseKey(key6) + .setMetadataKey(key7) + .setFreezeDefault(false) + .freezeWith(testEnv.client) + .sign(key1) + .execute(testEnv.client); + + var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); + + var info = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(info.tokenId).isEqualTo(tokenId); + assertThat(info.name).isEqualTo("ffff"); + assertThat(info.symbol).isEqualTo("F"); + assertThat(info.decimals).isEqualTo(3); + assertThat(info.treasuryAccountId).isEqualTo(testEnv.operatorId); + assertThat(info.adminKey).isNotNull(); + assertThat(info.freezeKey).isNotNull(); + assertThat(info.wipeKey).isNotNull(); + assertThat(info.kycKey).isNotNull(); + assertThat(info.supplyKey).isNotNull(); + assertThat(info.pauseKey).isNotNull(); + assertThat(info.metadataKey).isNotNull(); + assertThat(info.adminKey.toString()).isEqualTo(key1.getPublicKey().toString()); + assertThat(info.freezeKey.toString()).isEqualTo(key2.getPublicKey().toString()); + assertThat(info.wipeKey.toString()).isEqualTo(key3.getPublicKey().toString()); + assertThat(info.kycKey.toString()).isEqualTo(key4.getPublicKey().toString()); + assertThat(info.supplyKey.toString()).isEqualTo(key5.getPublicKey().toString()); + assertThat(info.pauseKey.toString()).isEqualTo(key6.getPublicKey().toString()); + assertThat(info.metadataKey.toString()) + .isEqualTo(key7.getPublicKey().toString()); + assertThat(info.defaultFreezeStatus).isNotNull(); + assertThat(info.defaultFreezeStatus).isFalse(); + assertThat(info.defaultKycStatus).isNotNull(); + assertThat(info.defaultKycStatus).isFalse(); + assertThat(info.tokenType).isEqualTo(TokenType.FUNGIBLE_COMMON); + assertThat(info.supplyType).isEqualTo(TokenSupplyType.INFINITE); + + new TokenDeleteTransaction() + .setTokenId(tokenId) + .freezeWith(testEnv.client) + .sign(key1) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can query token with minimal properties") + void canQueryTokenInfoWhenTokenIsCreatedWithMinimalProperties() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var response = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(testEnv.operatorId) + .execute(testEnv.client); + + var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); + + var info = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(info.tokenId).isEqualTo(tokenId); + assertThat(info.name).isEqualTo("ffff"); + assertThat(info.symbol).isEqualTo("F"); + assertThat(info.decimals).isEqualTo(0); + assertThat(info.totalSupply).isEqualTo(0); + assertThat(info.treasuryAccountId).isEqualTo(testEnv.operatorId); + assertThat(info.adminKey).isNull(); + assertThat(info.freezeKey).isNull(); + assertThat(info.wipeKey).isNull(); + assertThat(info.kycKey).isNull(); + assertThat(info.supplyKey).isNull(); + assertThat(info.pauseKey).isNull(); + assertThat(info.metadataKey).isNull(); + assertThat(info.defaultFreezeStatus).isNull(); + assertThat(info.defaultKycStatus).isNull(); + assertThat(info.tokenType).isEqualTo(TokenType.FUNGIBLE_COMMON); + assertThat(info.supplyType).isEqualTo(TokenSupplyType.INFINITE); + } + } + + @Test + @DisplayName("Can query NFT") + void canQueryNfts() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var response = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setSupplyType(TokenSupplyType.FINITE) + .setMaxSupply(5000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .execute(testEnv.client); + + var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); + + var mintReceipt = new TokenMintTransaction() + .setTokenId(tokenId) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + assertThat(mintReceipt.serials.size()).isEqualTo(10); + + var info = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(info.tokenId).isEqualTo(tokenId); + assertThat(info.name).isEqualTo("ffff"); + assertThat(info.symbol).isEqualTo("F"); + assertThat(info.decimals).isEqualTo(0); + assertThat(info.totalSupply).isEqualTo(10); + assertThat(testEnv.operatorId).isEqualTo(info.treasuryAccountId); + assertThat(info.adminKey).isNotNull(); + assertThat(info.freezeKey).isNull(); + assertThat(info.wipeKey).isNull(); + assertThat(info.kycKey).isNull(); + assertThat(info.supplyKey).isNotNull(); + assertThat(info.pauseKey).isNull(); + assertThat(info.metadataKey).isNull(); + assertThat(info.defaultFreezeStatus).isNull(); + assertThat(info.defaultKycStatus).isNull(); + assertThat(info.tokenType).isEqualTo(TokenType.NON_FUNGIBLE_UNIQUE); + assertThat(info.supplyType).isEqualTo(TokenSupplyType.FINITE); + assertThat(info.maxSupply).isEqualTo(5000); + } + } + + @Test + @DisplayName("Get cost of token info query") + void getCostQueryTokenInfo() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var response = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .execute(testEnv.client); + + var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); + + var infoQuery = new TokenInfoQuery().setTokenId(tokenId); + + var cost = infoQuery.getCost(testEnv.client); + + infoQuery.setQueryPayment(cost).execute(testEnv.client); + } + } + + @Test + @DisplayName("Get cost of token info query, with big max") + void getCostBigMaxQueryTokenInfo() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var response = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .execute(testEnv.client); + + var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); + + var infoQuery = new TokenInfoQuery().setTokenId(tokenId).setMaxQueryPayment(new Hbar(1000)); + + var cost = infoQuery.getCost(testEnv.client); + + infoQuery.setQueryPayment(cost).execute(testEnv.client); + } + } + + @Test + @DisplayName("Can query token info when all keys are different") + void getCostSmallMaxTokenInfo() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var response = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .execute(testEnv.client); + + var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); + + var infoQuery = new TokenInfoQuery().setTokenId(tokenId).setMaxQueryPayment(Hbar.fromTinybars(1)); + + assertThatExceptionOfType(MaxQueryPaymentExceededException.class).isThrownBy(() -> { + infoQuery.execute(testEnv.client); + }); + } + } + + @Test + @DisplayName("Throws insufficient transaction fee error") + void getCostInsufficientTxFeeQueryTokenInfo() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var response = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .execute(testEnv.client); + + var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); + + var infoQuery = new TokenInfoQuery().setTokenId(tokenId).setMaxQueryPayment(new Hbar(1000)); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + infoQuery.setQueryPayment(Hbar.fromTinybars(1)).execute(testEnv.client); + }) + .satisfies(error -> assertThat(error.status.toString()).isEqualTo("INSUFFICIENT_TX_FEE")); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenManualAssociationIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenManualAssociationIntegrationTest.java new file mode 100644 index 0000000000..ce6391501e --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenManualAssociationIntegrationTest.java @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Objects; +import org.hiero.sdk.AccountBalanceQuery; +import org.hiero.sdk.AccountId; +import org.hiero.sdk.AccountInfoQuery; +import org.hiero.sdk.ContractDeleteTransaction; +import org.hiero.sdk.ContractInfoQuery; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.sdk.Status; +import org.hiero.sdk.TokenAssociateTransaction; +import org.hiero.sdk.TokenMintTransaction; +import org.hiero.sdk.TransferTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class TokenManualAssociationIntegrationTest { + + @Test + @DisplayName("Can Manually associate Account with a Fungible Token") + void canManuallyAssociateAccountWithFungibleToken() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var tokenDecimals = 3; + var tokenId = EntityHelper.createFungibleToken(testEnv, 3); + var accountKey = PrivateKey.generateED25519(); + var accountMaxAutomaticTokenAssociations = 0; + var receiverAccountId = + EntityHelper.createAccount(testEnv, accountKey, accountMaxAutomaticTokenAssociations); + + new TokenAssociateTransaction() + .setAccountId(receiverAccountId) + .setTokenIds(Collections.singletonList(tokenId)) + .freezeWith(testEnv.client) + .sign(accountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var accountInfo = + new AccountInfoQuery().setAccountId(receiverAccountId).execute(testEnv.client); + + assertThat(accountInfo.tokenRelationships.get(tokenId).decimals).isEqualTo(tokenDecimals); + + new TransferTransaction() + .addTokenTransfer(tokenId, testEnv.operatorId, -10) + .addTokenTransfer(tokenId, receiverAccountId, 10) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var accountBalance = + new AccountBalanceQuery().setAccountId(receiverAccountId).execute(testEnv.client); + + assertThat(accountBalance.tokens.get(tokenId)).isEqualTo(10); + } + } + + @Test + @DisplayName("Can Manually associate Account with Nft") + void canManuallyAssociateAccountWithNft() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var tokenId = EntityHelper.createNft(testEnv); + var accountKey = PrivateKey.generateED25519(); + var accountMaxAutomaticTokenAssociations = 0; + var receiverAccountId = + EntityHelper.createAccount(testEnv, accountKey, accountMaxAutomaticTokenAssociations); + + var mintReceiptToken = new TokenMintTransaction() + .setTokenId(tokenId) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenAssociateTransaction() + .setAccountId(receiverAccountId) + .setTokenIds(Collections.singletonList(tokenId)) + .freezeWith(testEnv.client) + .sign(accountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var serialsToTransfer = new ArrayList<>(mintReceiptToken.serials); + var nftTransferTransaction = new TransferTransaction(); + for (var serial : serialsToTransfer) { + nftTransferTransaction.addNftTransfer(tokenId.nft(serial), testEnv.operatorId, receiverAccountId); + } + nftTransferTransaction.execute(testEnv.client).getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can Manually associate Contract with a Fungible Token") + void canManuallyAssociateContractWithFungibleToken() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + var tokenDecimals = 3; + var tokenId = EntityHelper.createFungibleToken(testEnv, 3); + var contractId = EntityHelper.createContract(testEnv, testEnv.operatorKey); + + new TokenAssociateTransaction() + .setAccountId(new AccountId(contractId.num)) + .setTokenIds(Collections.singletonList(tokenId)) + .freezeWith(testEnv.client) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var contractInfo = new ContractInfoQuery().setContractId(contractId).execute(testEnv.client); + + assertThat(contractInfo.contractId).isEqualTo(contractId); + assertThat(contractInfo.accountId).isNotNull(); + assertThat(Objects.requireNonNull(contractInfo.accountId).toString()) + .isEqualTo(Objects.requireNonNull(contractId).toString()); + assertThat(contractInfo.adminKey).isNotNull(); + assertThat(Objects.requireNonNull(contractInfo.adminKey).toString()) + .isEqualTo(Objects.requireNonNull(testEnv.operatorKey).toString()); + assertThat(contractInfo.storage).isEqualTo(128); + assertThat(contractInfo.contractMemo).isEqualTo("[e2e::ContractMemo]"); + assertThat(contractInfo.tokenRelationships.get(tokenId).decimals).isEqualTo(tokenDecimals); + + new ContractDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setContractId(contractId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can Manually associate contract with Nft") + void canManuallyAssociateContractWithNft() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + var tokenId = EntityHelper.createNft(testEnv); + var contractId = EntityHelper.createContract(testEnv, testEnv.operatorKey); + + new TokenAssociateTransaction() + .setAccountId(new AccountId(contractId.num)) + .setTokenIds(Collections.singletonList(tokenId)) + .freezeWith(testEnv.client) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var contractInfo = new ContractInfoQuery().setContractId(contractId).execute(testEnv.client); + + assertThat(contractInfo.contractId).isEqualTo(contractId); + assertThat(contractInfo.accountId).isNotNull(); + assertThat(Objects.requireNonNull(contractInfo.accountId).toString()) + .isEqualTo(Objects.requireNonNull(contractId).toString()); + assertThat(contractInfo.adminKey).isNotNull(); + assertThat(Objects.requireNonNull(contractInfo.adminKey).toString()) + .isEqualTo(Objects.requireNonNull(testEnv.operatorKey).toString()); + assertThat(contractInfo.storage).isEqualTo(128); + assertThat(contractInfo.contractMemo).isEqualTo("[e2e::ContractMemo]"); + + new ContractDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setContractId(contractId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can execute token associate transaction even when token IDs are not set") + void canExecuteTokenAssociateTransactionEvenWhenTokenIDsAreNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var accountKey = PrivateKey.generateED25519(); + var accountMaxAutomaticTokenAssociations = 0; + var accountId = EntityHelper.createAccount(testEnv, accountKey, accountMaxAutomaticTokenAssociations); + + new TokenAssociateTransaction() + .setAccountId(accountId) + .freezeWith(testEnv.client) + .sign(accountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot Manually associate Account with a Token when Account ID is not set") + void cannotAssociateAccountWithTokensWhenAccountIDIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var accountKey = PrivateKey.generateED25519(); + var accountMaxAutomaticTokenAssociations = 0; + var accountId = EntityHelper.createAccount(testEnv, accountKey, accountMaxAutomaticTokenAssociations); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenAssociateTransaction() + .freezeWith(testEnv.client) + .sign(accountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_ACCOUNT_ID.toString()); + } + } + + @Test + @DisplayName("Cannot Manually Associate Account with a Token when Account Does Not sign transaction") + void cannotAssociateAccountWhenAccountDoesNotSignTransaction() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var tokenDecimals = 3; + var tokenId = EntityHelper.createFungibleToken(testEnv, tokenDecimals); + var accountKey = PrivateKey.generateED25519(); + var accountMaxAutomaticTokenAssociations = 0; + var accountId = EntityHelper.createAccount(testEnv, accountKey, accountMaxAutomaticTokenAssociations); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenAssociateTransaction() + .setAccountId(accountId) + .setTokenIds(Collections.singletonList(tokenId)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenMintIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenMintIntegrationTest.java new file mode 100644 index 0000000000..8982bd7fa5 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenMintIntegrationTest.java @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Objects; +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.sdk.Status; +import org.hiero.sdk.TokenCreateTransaction; +import org.hiero.sdk.TokenMintTransaction; +import org.hiero.sdk.TokenSupplyType; +import org.hiero.sdk.TokenType; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class TokenMintIntegrationTest { + @Test + @DisplayName("Can mint tokens") + void canMintTokens() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var receipt = new TokenMintTransaction() + .setAmount(10) + .setTokenId(tokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + assertThat(receipt.totalSupply).isEqualTo(1000000 + 10); + } + } + + @Test + @DisplayName("Cannot mint more tokens than max supply") + void cannotMintMoreThanMaxSupply() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setSupplyType(TokenSupplyType.FINITE) + .setMaxSupply(5) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenMintTransaction() + .setTokenId(tokenId) + .setAmount(6) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.TOKEN_MAX_SUPPLY_REACHED.toString()); + } + } + + @Test + @DisplayName("Cannot mint tokens when token ID is not set") + void cannotMintTokensWhenTokenIDIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenMintTransaction() + .setAmount(10) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_TOKEN_ID.toString()); + } + } + + @Test + @DisplayName("Can mint tokens when amount is not set") + void canMintTokensWhenAmountIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var receipt = new TokenMintTransaction() + .setTokenId(tokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + assertThat(receipt.status).isEqualTo(Status.SUCCESS); + } + } + + @Test + @DisplayName("Cannot mint tokens when supply key does not sign transaction") + void cannotMintTokensWhenSupplyKeyDoesNotSignTransaction() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(key) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenMintTransaction() + .setTokenId(tokenId) + .setAmount(10) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + } + } + + @Test + @DisplayName("Can mint NFTs") + void canMintNfts() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var receipt = new TokenMintTransaction() + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .setTokenId(tokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + assertThat(receipt.serials.size()).isEqualTo(10); + } + } + + @Test + @DisplayName("Cannot mint NFTs if metadata too big") + void cannotMintNftsIfMetadataTooBig() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenMintTransaction() + .setMetadata(NftMetadataGenerator.generateOneLarge()) + .setTokenId(tokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.METADATA_TOO_LONG.toString()); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenNftInfoIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenNftInfoIntegrationTest.java new file mode 100644 index 0000000000..22b91e42a4 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenNftInfoIntegrationTest.java @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import org.hiero.sdk.NftId; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.Status; +import org.hiero.sdk.TokenCreateTransaction; +import org.hiero.sdk.TokenMintTransaction; +import org.hiero.sdk.TokenNftInfoQuery; +import org.hiero.sdk.TokenType; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class TokenNftInfoIntegrationTest { + + @Test + @DisplayName("Can query NFT info by NftId") + void canQueryNftInfoByNftId() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var createReceipt = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var tokenId = Objects.requireNonNull(createReceipt.tokenId); + + byte[] metadata = {50}; + + var mintReceipt = new TokenMintTransaction() + .setTokenId(tokenId) + .addMetadata(metadata) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var nftId = tokenId.nft(mintReceipt.serials.get(0)); + + var nftInfos = new TokenNftInfoQuery().setNftId(nftId).execute(testEnv.client); + + assertThat(nftInfos.size()).isEqualTo(1); + assertThat(nftInfos.get(0).nftId).isEqualTo(nftId); + assertThat(nftInfos.get(0).accountId).isEqualTo(testEnv.operatorId); + assertThat(nftInfos.get(0).metadata[0]).isEqualTo((byte) 50); + } + } + + @Test + @DisplayName("Cannot query NFT info by invalid NftId") + void cannotQueryNftInfoByInvalidNftId() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var createReceipt = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var tokenId = Objects.requireNonNull(createReceipt.tokenId); + + byte[] metadata = {50}; + + var mintReceipt = new TokenMintTransaction() + .setTokenId(tokenId) + .addMetadata(metadata) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var nftId = tokenId.nft(mintReceipt.serials.get(0)); + var invalidNftId = new NftId(nftId.tokenId, nftId.serial + 1); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenNftInfoQuery().setNftId(invalidNftId).execute(testEnv.client); + }) + .withMessageContaining(Status.INVALID_NFT_ID.toString()); + } + } + + @Test + @DisplayName("Cannot query NFT info by invalid NftId Serial Number") + void cannotQueryNftInfoByInvalidSerialNumber() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var createReceipt = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var tokenId = Objects.requireNonNull(createReceipt.tokenId); + + byte[] metadata = {50}; + + var mintReceipt = new TokenMintTransaction() + .setTokenId(tokenId) + .addMetadata(metadata) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var nftId = tokenId.nft(mintReceipt.serials.get(0)); + var invalidNftId = new NftId(nftId.tokenId, -1L); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenNftInfoQuery().byNftId(invalidNftId).execute(testEnv.client); + }) + .withMessageContaining(Status.INVALID_TOKEN_NFT_SERIAL_NUMBER.toString()); + } + } + + @Disabled + @Test + @DisplayName("Can query NFT info by AccountId") + void canQueryNftInfoByAccountId() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var createReceipt = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var tokenId = Objects.requireNonNull(createReceipt.tokenId); + + List metadatas = NftMetadataGenerator.generate((byte) 10); + + var mintReceipt = new TokenMintTransaction() + .setTokenId(tokenId) + .setMetadata(metadatas) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var nftInfos = new TokenNftInfoQuery() + .byAccountId(testEnv.operatorId) + .setEnd(10) + .execute(testEnv.client); + + assertThat(nftInfos.size()).isEqualTo(10); + + var serials = new ArrayList(mintReceipt.serials); + + for (var info : nftInfos) { + assertThat(info.nftId.tokenId).isEqualTo(tokenId); + assertThat(serials.remove(info.nftId.serial)).isTrue(); + assertThat(info.accountId).isEqualTo(testEnv.operatorId); + } + } + } + + @Disabled + @Test + @DisplayName("Can query NFT info by TokenId") + void canQueryNftInfoByTokenId() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var createReceipt = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var tokenId = Objects.requireNonNull(createReceipt.tokenId); + + List metadatas = NftMetadataGenerator.generate((byte) 10); + + var mintReceipt = new TokenMintTransaction() + .setTokenId(tokenId) + .setMetadata(metadatas) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var nftInfos = new TokenNftInfoQuery().byTokenId(tokenId).setEnd(10).execute(testEnv.client); + + assertThat(nftInfos.size()).isEqualTo(10); + + var serials = new ArrayList(mintReceipt.serials); + + for (var info : nftInfos) { + assertThat(info.nftId.tokenId).isEqualTo(tokenId); + assertThat(serials.remove(info.nftId.serial)).isTrue(); + assertThat(info.accountId).isEqualTo(testEnv.operatorId); + } + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenNftTransferIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenNftTransferIntegrationTest.java new file mode 100644 index 0000000000..c70904a559 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenNftTransferIntegrationTest.java @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.ArrayList; +import java.util.Collections; +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.sdk.Status; +import org.hiero.sdk.TokenAssociateTransaction; +import org.hiero.sdk.TokenCreateTransaction; +import org.hiero.sdk.TokenGrantKycTransaction; +import org.hiero.sdk.TokenMintTransaction; +import org.hiero.sdk.TokenType; +import org.hiero.sdk.TokenWipeTransaction; +import org.hiero.sdk.TransactionResponse; +import org.hiero.sdk.TransferTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class TokenNftTransferIntegrationTest { + @Test + @DisplayName("Can transfer NFTs") + void canTransferNfts() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + TransactionResponse response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = response.getReceipt(testEnv.client).accountId; + assertThat(accountId).isNotNull(); + + response = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client); + + var tokenId = response.getReceipt(testEnv.client).tokenId; + assertThat(tokenId).isNotNull(); + + var mintReceipt = new TokenMintTransaction() + .setTokenId(tokenId) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenAssociateTransaction() + .setAccountId(accountId) + .setTokenIds(Collections.singletonList(tokenId)) + .freezeWith(testEnv.client) + .signWithOperator(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenGrantKycTransaction() + .setAccountId(accountId) + .setTokenId(tokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var serialsToTransfer = new ArrayList(mintReceipt.serials.subList(0, 4)); + var transfer = new TransferTransaction(); + for (var serial : serialsToTransfer) { + transfer.addNftTransfer(tokenId.nft(serial), testEnv.operatorId, accountId); + } + transfer.execute(testEnv.client).getReceipt(testEnv.client); + + new TokenWipeTransaction() + .setTokenId(tokenId) + .setAccountId(accountId) + .setSerials(serialsToTransfer) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot transfer NFTs you don't own") + void cannotTransferUnownedNfts() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + TransactionResponse response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = response.getReceipt(testEnv.client).accountId; + assertThat(accountId).isNotNull(); + + response = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client); + + var tokenId = response.getReceipt(testEnv.client).tokenId; + assertThat(tokenId).isNotNull(); + + var mintReceipt = new TokenMintTransaction() + .setTokenId(tokenId) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenAssociateTransaction() + .setAccountId(accountId) + .setTokenIds(Collections.singletonList(tokenId)) + .freezeWith(testEnv.client) + .signWithOperator(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var serialsToTransfer = new ArrayList(mintReceipt.serials.subList(0, 4)); + var transfer = new TransferTransaction(); + for (var serial : serialsToTransfer) { + // Try to transfer in wrong direction + transfer.addNftTransfer(tokenId.nft(serial), accountId, testEnv.operatorId); + } + transfer.freezeWith(testEnv.client).sign(key); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + transfer.execute(testEnv.client).getReceipt(testEnv.client); + }) + .withMessageContaining(Status.SENDER_DOES_NOT_OWN_NFT_SERIAL_NO.toString()); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenPauseIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenPauseIntegrationTest.java new file mode 100644 index 0000000000..49305b45b5 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenPauseIntegrationTest.java @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.Collections; +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.sdk.TokenAssociateTransaction; +import org.hiero.sdk.TokenCreateTransaction; +import org.hiero.sdk.TokenPauseTransaction; +import org.hiero.sdk.TransferTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class TokenPauseIntegrationTest { + + @Test + @DisplayName("Can execute token pause transaction") + void canExecuteTokenPauseTransaction() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var accountKey = PrivateKey.generateED25519(); + var testTokenAmount = 10; + var accountId = new AccountCreateTransaction() + .setKey(accountKey) + .setInitialBalance(new Hbar(2)) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + var tokenId = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setInitialSupply(1000000) + .setDecimals(3) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setPauseKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId; + + new TokenAssociateTransaction() + .setAccountId(accountId) + .setTokenIds(Collections.singletonList(tokenId)) + .freezeWith(testEnv.client) + .sign(accountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TransferTransaction() + .addTokenTransfer(tokenId, accountId, testTokenAmount) + .addTokenTransfer(tokenId, testEnv.operatorId, -testTokenAmount) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenPauseTransaction() + .setTokenId(tokenId) + .freezeWith(testEnv.client) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + assertThrows(ReceiptStatusException.class, () -> { + new TransferTransaction() + .addTokenTransfer(tokenId, accountId, testTokenAmount) + .addTokenTransfer(tokenId, testEnv.operatorId, -testTokenAmount) + .freezeWith(testEnv.client) + .sign(accountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }); + } + } + + @Test + @DisplayName("Cannot pause with no token ID") + void cannotPauseWithNoTokenId() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + assertThrows(PrecheckStatusException.class, () -> { + new TokenPauseTransaction().execute(testEnv.client).getReceipt(testEnv.client); + }); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenRejectFlowIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenRejectFlowIntegrationTest.java new file mode 100644 index 0000000000..0db1e378a7 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenRejectFlowIntegrationTest.java @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Collections; +import java.util.List; +import org.hiero.sdk.AccountBalanceQuery; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.TokenAssociateTransaction; +import org.hiero.sdk.TokenDeleteTransaction; +import org.hiero.sdk.TokenMintTransaction; +import org.hiero.sdk.TokenNftInfoQuery; +import org.hiero.sdk.TokenRejectFlow; +import org.hiero.sdk.TransferTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class TokenRejectFlowIntegrationTest { + + @Test + @DisplayName("Can execute TokenReject flow for Fungible Token") + void canExecuteTokenRejectFlowForFungibleToken() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var ftTokenId = EntityHelper.createFungibleToken(testEnv, 3); + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); + + // manually associate ft + new TokenAssociateTransaction() + .setAccountId(receiverAccountId) + .setTokenIds(Collections.singletonList(ftTokenId)) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // transfer fts to the receiver + new TransferTransaction() + .addTokenTransfer(ftTokenId, testEnv.operatorId, -10) + .addTokenTransfer(ftTokenId, receiverAccountId, 10) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // execute the token reject flow + new TokenRejectFlow() + .setOwnerId(receiverAccountId) + .addTokenId(ftTokenId) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // verify the tokens are transferred back to the treasury + var treasuryAccountBalance = + new AccountBalanceQuery().setAccountId(testEnv.operatorId).execute(testEnv.client); + + assertThat(treasuryAccountBalance.tokens.get(ftTokenId)).isEqualTo(1_000_000); + + // verify the allowance - should be 0, because TokenRejectFlow dissociates + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> { + new TransferTransaction() + .addTokenTransfer(ftTokenId, testEnv.operatorId, -10) + .addTokenTransfer(ftTokenId, receiverAccountId, 10) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining("TOKEN_NOT_ASSOCIATED_TO_ACCOUNT"); + + new TokenDeleteTransaction() + .setTokenId(ftTokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can execute TokenReject flow for Fungible Token (Async)") + void canExecuteTokenRejectFlowForFungibleTokenAsync() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var ftTokenId = EntityHelper.createFungibleToken(testEnv, 3); + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); + + // manually associate ft + new TokenAssociateTransaction() + .setAccountId(receiverAccountId) + .setTokenIds(Collections.singletonList(ftTokenId)) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // transfer fts to the receiver + new TransferTransaction() + .addTokenTransfer(ftTokenId, testEnv.operatorId, -10) + .addTokenTransfer(ftTokenId, receiverAccountId, 10) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // execute the token reject flow + new TokenRejectFlow() + .setOwnerId(receiverAccountId) + .addTokenId(ftTokenId) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .executeAsync(testEnv.client) + .get() + .getReceipt(testEnv.client); + + // verify the tokens are transferred back to the treasury + var treasuryAccountBalance = + new AccountBalanceQuery().setAccountId(testEnv.operatorId).execute(testEnv.client); + + assertThat(treasuryAccountBalance.tokens.get(ftTokenId)).isEqualTo(1_000_000); + + // verify the tokens are not associated with the receiver + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> { + new TransferTransaction() + .addTokenTransfer(ftTokenId, testEnv.operatorId, -10) + .addTokenTransfer(ftTokenId, receiverAccountId, 10) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining("TOKEN_NOT_ASSOCIATED_TO_ACCOUNT"); + + new TokenDeleteTransaction() + .setTokenId(ftTokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can execute TokenReject flow for NFT") + void canExecuteTokenRejectFlowForNft() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var nftTokenId = EntityHelper.createNft(testEnv); + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); + + var mintReceiptToken = new TokenMintTransaction() + .setTokenId(nftTokenId) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var nftSerials = mintReceiptToken.serials; + + // manually associate bft + new TokenAssociateTransaction() + .setAccountId(receiverAccountId) + .setTokenIds(Collections.singletonList(nftTokenId)) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // transfer nfts to the receiver + new TransferTransaction() + .addNftTransfer(nftTokenId.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) + .addNftTransfer(nftTokenId.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // execute the token reject flow + new TokenRejectFlow() + .setOwnerId(receiverAccountId) + .setNftIds(List.of(nftTokenId.nft(nftSerials.get(0)), nftTokenId.nft(nftSerials.get(1)))) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // verify the token is transferred back to the treasury + var nftTokenIdNftInfo = new TokenNftInfoQuery() + .setNftId(nftTokenId.nft(nftSerials.get(1))) + .execute(testEnv.client); + + assertThat(nftTokenIdNftInfo.get(0).accountId).isEqualTo(testEnv.operatorId); + + // verify the tokens are not associated with the receiver + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> { + new TransferTransaction() + .addNftTransfer( + nftTokenId.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining("TOKEN_NOT_ASSOCIATED_TO_ACCOUNT"); + + new TokenDeleteTransaction() + .setTokenId(nftTokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot execute TokenReject flow for NFT when rejecting Only Part Of Owned NFTs") + void canExecuteTokenRejectFlowForNftWhenRejectingOnlyPartOfOwnedNFTs() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var nftTokenId1 = EntityHelper.createNft(testEnv); + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 0); + + var mintReceiptToken = new TokenMintTransaction() + .setTokenId(nftTokenId1) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var nftSerials = mintReceiptToken.serials; + + // manually associate bft + new TokenAssociateTransaction() + .setAccountId(receiverAccountId) + .setTokenIds(Collections.singletonList(nftTokenId1)) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // transfer nfts to the receiver + new TransferTransaction() + .addNftTransfer(nftTokenId1.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) + .addNftTransfer(nftTokenId1.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // execute the token reject flow + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> { + new TokenRejectFlow() + .setOwnerId(receiverAccountId) + .addNftId(nftTokenId1.nft(nftSerials.get(1))) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining("ACCOUNT_STILL_OWNS_NFTS"); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenRejectIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenRejectIntegrationTest.java new file mode 100644 index 0000000000..5c34888ac9 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenRejectIntegrationTest.java @@ -0,0 +1,1066 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Collections; +import java.util.List; +import org.hiero.sdk.AccountAllowanceApproveTransaction; +import org.hiero.sdk.AccountBalanceQuery; +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.TokenAssociateTransaction; +import org.hiero.sdk.TokenCreateTransaction; +import org.hiero.sdk.TokenDeleteTransaction; +import org.hiero.sdk.TokenFreezeTransaction; +import org.hiero.sdk.TokenMintTransaction; +import org.hiero.sdk.TokenNftInfoQuery; +import org.hiero.sdk.TokenPauseTransaction; +import org.hiero.sdk.TokenRejectTransaction; +import org.hiero.sdk.TokenSupplyType; +import org.hiero.sdk.TokenType; +import org.hiero.sdk.TransactionId; +import org.hiero.sdk.TransferTransaction; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class TokenRejectIntegrationTest { + + @Test + @DisplayName("Can execute TokenReject transaction for Fungible Token") + void canExecuteTokenRejectTransactionForFungibleToken() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var tokenId1 = EntityHelper.createFungibleToken(testEnv, 3); + var tokenId2 = EntityHelper.createFungibleToken(testEnv, 3); + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 100); + + // transfer fts to the receiver + new TransferTransaction() + .addTokenTransfer(tokenId1, testEnv.operatorId, -10) + .addTokenTransfer(tokenId1, receiverAccountId, 10) + .addTokenTransfer(tokenId2, testEnv.operatorId, -10) + .addTokenTransfer(tokenId2, receiverAccountId, 10) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // reject the token + new TokenRejectTransaction() + .setOwnerId(receiverAccountId) + .setTokenIds(List.of(tokenId1, tokenId2)) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // verify the balance of the receiver is 0 + var receiverAccountBalance = + new AccountBalanceQuery().setAccountId(receiverAccountId).execute(testEnv.client); + + assertThat(receiverAccountBalance.tokens.get(tokenId1)).isEqualTo(0); + assertThat(receiverAccountBalance.tokens.get(tokenId2)).isEqualTo(0); + + // verify the tokens are transferred back to the treasury + var treasuryAccountBalance = + new AccountBalanceQuery().setAccountId(testEnv.operatorId).execute(testEnv.client); + + assertThat(treasuryAccountBalance.tokens.get(tokenId1)).isEqualTo(1_000_000); + assertThat(treasuryAccountBalance.tokens.get(tokenId2)).isEqualTo(1_000_000); + + new TokenDeleteTransaction() + .setTokenId(tokenId1) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenDeleteTransaction() + .setTokenId(tokenId2) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can execute TokenReject transaction for NFT") + void canExecuteTokenRejectTransactionForNft() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var tokenId1 = EntityHelper.createNft(testEnv); + var tokenId2 = EntityHelper.createNft(testEnv); + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 100); + + var mintReceiptToken1 = new TokenMintTransaction() + .setTokenId(tokenId1) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var mintReceiptToken2 = new TokenMintTransaction() + .setTokenId(tokenId2) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var nftSerials = mintReceiptToken2.serials; + + // transfer nfts to the receiver + new TransferTransaction() + .addNftTransfer(tokenId1.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) + .addNftTransfer(tokenId1.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) + .addNftTransfer(tokenId2.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) + .addNftTransfer(tokenId2.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // reject one of the nfts + new TokenRejectTransaction() + .setOwnerId(receiverAccountId) + .setNftIds(List.of(tokenId1.nft(nftSerials.get(1)), tokenId2.nft(nftSerials.get(1)))) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // verify the balance is decremented by 1 + var receiverAccountBalance = + new AccountBalanceQuery().setAccountId(receiverAccountId).execute(testEnv.client); + + assertThat(receiverAccountBalance.tokens.get(tokenId1)).isEqualTo(1); + assertThat(receiverAccountBalance.tokens.get(tokenId2)).isEqualTo(1); + + // verify the token is transferred back to the treasury + var tokenId1NftInfo = new TokenNftInfoQuery() + .setNftId(tokenId1.nft(nftSerials.get(1))) + .execute(testEnv.client); + + assertThat(tokenId1NftInfo.get(0).accountId).isEqualTo(testEnv.operatorId); + + var tokenId2NftInfo = new TokenNftInfoQuery() + .setNftId(tokenId2.nft(nftSerials.get(1))) + .execute(testEnv.client); + + assertThat(tokenId2NftInfo.get(0).accountId).isEqualTo(testEnv.operatorId); + + new TokenDeleteTransaction() + .setTokenId(tokenId1) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenDeleteTransaction() + .setTokenId(tokenId2) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can execute TokenReject transaction for FT and NFT in One Tx") + void canExecuteTokenRejectTransactionForFtAndNftInOneTx() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var ftTokenId1 = EntityHelper.createFungibleToken(testEnv, 3); + var ftTokenId2 = EntityHelper.createFungibleToken(testEnv, 3); + var nftTokenId1 = EntityHelper.createNft(testEnv); + var nftTokenId2 = EntityHelper.createNft(testEnv); + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 100); + + var mintReceiptNftToken1 = new TokenMintTransaction() + .setTokenId(nftTokenId1) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var mintReceiptNftToken2 = new TokenMintTransaction() + .setTokenId(nftTokenId2) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var nftSerials = mintReceiptNftToken2.serials; + + // transfer fts to the receiver + new TransferTransaction() + .addTokenTransfer(ftTokenId1, testEnv.operatorId, -10) + .addTokenTransfer(ftTokenId1, receiverAccountId, 10) + .addTokenTransfer(ftTokenId2, testEnv.operatorId, -10) + .addTokenTransfer(ftTokenId2, receiverAccountId, 10) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // transfer nfts to the receiver + new TransferTransaction() + .addNftTransfer(nftTokenId1.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) + .addNftTransfer(nftTokenId1.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) + .addNftTransfer(nftTokenId2.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) + .addNftTransfer(nftTokenId2.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // reject the token + new TokenRejectTransaction() + .setOwnerId(receiverAccountId) + .setTokenIds(List.of(ftTokenId1, ftTokenId2)) + .setNftIds(List.of(nftTokenId1.nft(nftSerials.get(1)), nftTokenId2.nft(nftSerials.get(1)))) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // verify the balance of the receiver is 0 + var receiverAccountBalance = + new AccountBalanceQuery().setAccountId(receiverAccountId).execute(testEnv.client); + + assertThat(receiverAccountBalance.tokens.get(ftTokenId1)).isEqualTo(0); + assertThat(receiverAccountBalance.tokens.get(ftTokenId2)).isEqualTo(0); + assertThat(receiverAccountBalance.tokens.get(nftTokenId1)).isEqualTo(1); + assertThat(receiverAccountBalance.tokens.get(nftTokenId2)).isEqualTo(1); + + // verify the tokens are transferred back to the treasury + var treasuryAccountBalance = + new AccountBalanceQuery().setAccountId(testEnv.operatorId).execute(testEnv.client); + + assertThat(treasuryAccountBalance.tokens.get(ftTokenId1)).isEqualTo(1_000_000); + assertThat(treasuryAccountBalance.tokens.get(ftTokenId2)).isEqualTo(1_000_000); + + var tokenId1NftInfo = new TokenNftInfoQuery() + .setNftId(nftTokenId1.nft(nftSerials.get(1))) + .execute(testEnv.client); + + assertThat(tokenId1NftInfo.get(0).accountId).isEqualTo(testEnv.operatorId); + + var tokenId2NftInfo = new TokenNftInfoQuery() + .setNftId(nftTokenId2.nft(nftSerials.get(1))) + .execute(testEnv.client); + + assertThat(tokenId2NftInfo.get(0).accountId).isEqualTo(testEnv.operatorId); + + new TokenDeleteTransaction() + .setTokenId(ftTokenId1) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenDeleteTransaction() + .setTokenId(ftTokenId2) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenDeleteTransaction() + .setTokenId(nftTokenId1) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenDeleteTransaction() + .setTokenId(nftTokenId2) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can execute TokenReject transaction for FT and NFT when Treasury receiverSigRequired is Enabled") + void canExecuteTokenRejectTransactionForFtAndNftWhenTreasuryReceiverSigRequiredIsEnabled() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 100); + + var treasuryAccountKey = PrivateKey.generateED25519(); + var treasuryAccountId = new AccountCreateTransaction() + .setKey(treasuryAccountKey) + .setInitialBalance(new Hbar(0)) + .setReceiverSignatureRequired(true) + .setMaxAutomaticTokenAssociations(100) + .freezeWith(testEnv.client) + .sign(treasuryAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + var ftTokenId = new TokenCreateTransaction() + .setTokenName("Test Fungible Token") + .setTokenSymbol("TFT") + .setTokenMemo("I was created for integration tests") + .setDecimals(18) + .setInitialSupply(1_000_000) + .setMaxSupply(1_000_000) + .setTreasuryAccountId(treasuryAccountId) + .setSupplyType(TokenSupplyType.FINITE) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setMetadataKey(testEnv.operatorKey) + .freezeWith(testEnv.client) + .sign(treasuryAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId; + + // transfer fts to the receiver + new TransferTransaction() + .addTokenTransfer(ftTokenId, treasuryAccountId, -10) + .addTokenTransfer(ftTokenId, receiverAccountId, 10) + .freezeWith(testEnv.client) + .sign(treasuryAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // reject the token + new TokenRejectTransaction() + .setOwnerId(receiverAccountId) + .addTokenId(ftTokenId) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // verify the balance of the receiver is 0 + var receiverAccountBalanceFt = + new AccountBalanceQuery().setAccountId(receiverAccountId).execute(testEnv.client); + + assertThat(receiverAccountBalanceFt.tokens.get(ftTokenId)).isEqualTo(0); + + // verify the tokens are transferred back to the treasury + var treasuryAccountBalance = + new AccountBalanceQuery().setAccountId(treasuryAccountId).execute(testEnv.client); + + assertThat(treasuryAccountBalance.tokens.get(ftTokenId)).isEqualTo(1_000_000); + + // same test for nft + + var nftTokenId = new TokenCreateTransaction() + .setTokenName("Test NFT") + .setTokenSymbol("TNFT") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(treasuryAccountId) + .setSupplyType(TokenSupplyType.FINITE) + .setMaxSupply(10) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setMetadataKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .freezeWith(testEnv.client) + .sign(treasuryAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId; + + var mintReceiptNftToken = new TokenMintTransaction() + .setTokenId(nftTokenId) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var nftSerials = mintReceiptNftToken.serials; + + // transfer nfts to the receiver + new TransferTransaction() + .addNftTransfer(nftTokenId.nft(nftSerials.get(0)), treasuryAccountId, receiverAccountId) + .addNftTransfer(nftTokenId.nft(nftSerials.get(1)), treasuryAccountId, receiverAccountId) + .freezeWith(testEnv.client) + .sign(treasuryAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // reject the token + new TokenRejectTransaction() + .setOwnerId(receiverAccountId) + .addNftId(nftTokenId.nft(nftSerials.get(1))) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // verify the balance is decremented by 1 + var receiverAccountBalanceNft = + new AccountBalanceQuery().setAccountId(receiverAccountId).execute(testEnv.client); + + assertThat(receiverAccountBalanceNft.tokens.get(nftTokenId)).isEqualTo(1); + + // verify the token is transferred back to the treasury + var nftTokenIdInfo = new TokenNftInfoQuery() + .setNftId(nftTokenId.nft(nftSerials.get(1))) + .execute(testEnv.client); + + assertThat(nftTokenIdInfo.get(0).accountId).isEqualTo(treasuryAccountId); + + new TokenDeleteTransaction() + .setTokenId(ftTokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenDeleteTransaction() + .setTokenId(nftTokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot execute TokenReject transaction for FT and NFT when Token is Frozen") + void canExecuteTokenRejectTransactionForFtAndNftWhenTokenIsFrozen() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var ftTokenId = EntityHelper.createFungibleToken(testEnv, 18); + var nftTokenId = EntityHelper.createNft(testEnv); + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 100); + + // transfer fts to the receiver + new TransferTransaction() + .addTokenTransfer(ftTokenId, testEnv.operatorId, -10) + .addTokenTransfer(ftTokenId, receiverAccountId, 10) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // freeze ft + new TokenFreezeTransaction() + .setTokenId(ftTokenId) + .setAccountId(receiverAccountId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // reject the token - should fail with ACCOUNT_FROZEN_FOR_TOKEN + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> { + new TokenRejectTransaction() + .setOwnerId(receiverAccountId) + .addTokenId(ftTokenId) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining("ACCOUNT_FROZEN_FOR_TOKEN"); + + // same test for nft + + var mintReceipt = new TokenMintTransaction() + .setTokenId(nftTokenId) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var nftSerials = mintReceipt.serials; + + // transfer nfts to the receiver + new TransferTransaction() + .addNftTransfer(nftTokenId.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) + .addNftTransfer(nftTokenId.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // freeze nft + new TokenFreezeTransaction() + .setTokenId(nftTokenId) + .setAccountId(receiverAccountId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // reject the token - should fail with ACCOUNT_FROZEN_FOR_TOKEN + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> { + new TokenRejectTransaction() + .setOwnerId(receiverAccountId) + .addNftId(nftTokenId.nft(nftSerials.get(1))) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining("ACCOUNT_FROZEN_FOR_TOKEN"); + + new TokenDeleteTransaction() + .setTokenId(ftTokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenDeleteTransaction() + .setTokenId(nftTokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot execute TokenReject transaction for FT and NFT when Token is Paused") + void canExecuteTokenRejectTransactionForFtAndNftWhenTokenIsPaused() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var ftTokenId = EntityHelper.createFungibleToken(testEnv, 18); + var nftTokenId = EntityHelper.createNft(testEnv); + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 100); + + // transfer fts to the receiver + new TransferTransaction() + .addTokenTransfer(ftTokenId, testEnv.operatorId, -10) + .addTokenTransfer(ftTokenId, receiverAccountId, 10) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // pause ft + new TokenPauseTransaction() + .setTokenId(ftTokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // reject the token - should fail with TOKEN_IS_PAUSED + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> { + new TokenRejectTransaction() + .setOwnerId(receiverAccountId) + .addTokenId(ftTokenId) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining("TOKEN_IS_PAUSED"); + + // same test for nft + + var mintReceipt = new TokenMintTransaction() + .setTokenId(nftTokenId) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var nftSerials = mintReceipt.serials; + + // transfer nfts to the receiver + new TransferTransaction() + .addNftTransfer(nftTokenId.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) + .addNftTransfer(nftTokenId.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // pause nft + new TokenPauseTransaction() + .setTokenId(nftTokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // reject the token - should fail with TOKEN_IS_PAUSED + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> { + new TokenRejectTransaction() + .setOwnerId(receiverAccountId) + .addNftId(nftTokenId.nft(nftSerials.get(1))) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining("TOKEN_IS_PAUSED"); + } + } + + @Test + @Disabled // temp disabled till issue re nfts will be resolved on services side + @DisplayName("Can remove allowance when executing TokenReject transaction for FT and NFT") + void canRemoveAllowanceWhenExecutingTokenRejectForFtAndNft() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var ftTokenId = EntityHelper.createFungibleToken(testEnv, 3); + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, -1); + var spenderAccountKey = PrivateKey.generateED25519(); + var spenderAccountId = EntityHelper.createAccount(testEnv, spenderAccountKey, -1); + + // transfer ft to the receiver + new TransferTransaction() + .addTokenTransfer(ftTokenId, testEnv.operatorId, -10) + .addTokenTransfer(ftTokenId, receiverAccountId, 10) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // approve allowance to the spender + new AccountAllowanceApproveTransaction() + .approveTokenAllowance(ftTokenId, receiverAccountId, spenderAccountId, 10) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // verify the spender has allowance + new TransferTransaction() + .addApprovedTokenTransfer(ftTokenId, receiverAccountId, -5) + .addTokenTransfer(ftTokenId, spenderAccountId, 5) + .setTransactionId(TransactionId.generate(spenderAccountId)) + .freezeWith(testEnv.client) + .sign(spenderAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // reject the token + new TokenRejectTransaction() + .setOwnerId(receiverAccountId) + .addTokenId(ftTokenId) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // verify the allowance - should be 0 , because the receiver is no longer the owner + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> { + new TransferTransaction() + .addApprovedTokenTransfer(ftTokenId, receiverAccountId, -5) + .addTokenTransfer(ftTokenId, spenderAccountId, 5) + .setTransactionId(TransactionId.generate(spenderAccountId)) + .freezeWith(testEnv.client) + .sign(spenderAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining("SPENDER_DOES_NOT_HAVE_ALLOWANCE"); + + // same test for nft + + var nftTokenId = EntityHelper.createNft(testEnv); + + var mintReceipt = new TokenMintTransaction() + .setTokenId(nftTokenId) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var nftSerials = mintReceipt.serials; + + // transfer nfts to the receiver + new TransferTransaction() + .addNftTransfer(nftTokenId.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) + .addNftTransfer(nftTokenId.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) + .addNftTransfer(nftTokenId.nft(nftSerials.get(2)), testEnv.operatorId, receiverAccountId) + .addNftTransfer(nftTokenId.nft(nftSerials.get(3)), testEnv.operatorId, receiverAccountId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // approve allowance to the spender + new AccountAllowanceApproveTransaction() + .approveTokenNftAllowance(nftTokenId.nft(nftSerials.get(0)), receiverAccountId, spenderAccountId) + .approveTokenNftAllowance(nftTokenId.nft(nftSerials.get(1)), receiverAccountId, spenderAccountId) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // verify the spender has allowance + new TransferTransaction() + .addApprovedNftTransfer(nftTokenId.nft(nftSerials.get(0)), receiverAccountId, spenderAccountId) + .setTransactionId(TransactionId.generate(spenderAccountId)) + .freezeWith(testEnv.client) + .sign(spenderAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // reject the token + new TokenRejectTransaction() + .setOwnerId(receiverAccountId) + .setNftIds(List.of(nftTokenId.nft(nftSerials.get(1)), nftTokenId.nft(nftSerials.get(2)))) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // verify the allowance - should be 0 , because the receiver is no longer the owner + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> { + new TransferTransaction() + .addApprovedNftTransfer( + nftTokenId.nft(nftSerials.get(1)), receiverAccountId, spenderAccountId) + .addApprovedNftTransfer( + nftTokenId.nft(nftSerials.get(2)), receiverAccountId, spenderAccountId) + .setTransactionId(TransactionId.generate(spenderAccountId)) + .freezeWith(testEnv.client) + .sign(spenderAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining("SPENDER_DOES_NOT_HAVE_ALLOWANCE"); + + new TokenDeleteTransaction() + .setTokenId(ftTokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenDeleteTransaction() + .setTokenId(nftTokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot reject NFT when executing TokenReject with Add or Set TokenId") + void cannotRejectNftWhenUsingAddOrSetTokenId() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var nftTokenId = EntityHelper.createNft(testEnv); + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 100); + + var mintReceiptNftToken = new TokenMintTransaction() + .setTokenId(nftTokenId) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var nftSerials = mintReceiptNftToken.serials; + + // transfer nfts to the receiver + new TransferTransaction() + .addNftTransfer(nftTokenId.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) + .addNftTransfer(nftTokenId.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) + .addNftTransfer(nftTokenId.nft(nftSerials.get(2)), testEnv.operatorId, receiverAccountId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // reject the whole collection (addTokenId) - should fail + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> { + new TokenRejectTransaction() + .setOwnerId(receiverAccountId) + .addTokenId(nftTokenId) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining("ACCOUNT_AMOUNT_TRANSFERS_ONLY_ALLOWED_FOR_FUNGIBLE_COMMON"); + + // reject the whole collection (setTokenIds) - should fail + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> { + new TokenRejectTransaction() + .setOwnerId(receiverAccountId) + .setTokenIds(List.of(nftTokenId)) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining("ACCOUNT_AMOUNT_TRANSFERS_ONLY_ALLOWED_FOR_FUNGIBLE_COMMON"); + + new TokenDeleteTransaction() + .setTokenId(nftTokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot Reject a Token when executing TokenReject and Duplicating Token Reference") + void cannotRejectTokenWhenExecutingTokenRejectAndDuplicatingTokenReference() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var ftTokenId = EntityHelper.createFungibleToken(testEnv, 3); + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 100); + + // transfer fts to the receiver + new TransferTransaction() + .addTokenTransfer(ftTokenId, testEnv.operatorId, -10) + .addTokenTransfer(ftTokenId, receiverAccountId, 10) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // reject the token with duplicate token id - should fail with TOKEN_REFERENCE_REPEATED + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> { + new TokenRejectTransaction() + .setOwnerId(receiverAccountId) + .setTokenIds(List.of(ftTokenId, ftTokenId)) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining("TOKEN_REFERENCE_REPEATED"); + + // same test for nft + + var nftTokenId = EntityHelper.createNft(testEnv); + + var mintReceipt = new TokenMintTransaction() + .setTokenId(nftTokenId) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var nftSerials = mintReceipt.serials; + + // transfer nfts to the receiver + new TransferTransaction() + .addNftTransfer(nftTokenId.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // reject the nft with duplicate nft id - should fail with TOKEN_REFERENCE_REPEATED + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> { + new TokenRejectTransaction() + .setOwnerId(receiverAccountId) + .setNftIds( + List.of(nftTokenId.nft(nftSerials.get(0)), nftTokenId.nft(nftSerials.get(0)))) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining("TOKEN_REFERENCE_REPEATED"); + + new TokenDeleteTransaction() + .setTokenId(ftTokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenDeleteTransaction() + .setTokenId(nftTokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot Reject a Token when Owner Has Empty Balance") + void cannotRejectTokenWhenOwnerHasEmptyBalance() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var ftTokenId = EntityHelper.createFungibleToken(testEnv, 3); + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 100); + + // skip the transfer + // associate the receiver + new TokenAssociateTransaction() + .setAccountId(receiverAccountId) + .setTokenIds(Collections.singletonList(ftTokenId)) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // reject the token - should fail with INSUFFICIENT_TOKEN_BALANCE + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> { + new TokenRejectTransaction() + .setOwnerId(receiverAccountId) + .addTokenId(ftTokenId) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining("INSUFFICIENT_TOKEN_BALANCE"); + + // same test for nft + + var nftTokenId = EntityHelper.createNft(testEnv); + + var mintReceipt = new TokenMintTransaction() + .setTokenId(nftTokenId) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var nftSerials = mintReceipt.serials; + + // skip the transfer + // associate the receiver + new TokenAssociateTransaction() + .setAccountId(receiverAccountId) + .setTokenIds(Collections.singletonList(nftTokenId)) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // reject the nft - should fail with INVALID_OWNER_ID + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> { + new TokenRejectTransaction() + .setOwnerId(receiverAccountId) + .addNftId(nftTokenId.nft(nftSerials.get(0))) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining("INVALID_OWNER_ID"); + + new TokenDeleteTransaction() + .setTokenId(ftTokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenDeleteTransaction() + .setTokenId(nftTokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot Reject a Token when Treasury Rejects itself") + void cannotRejectTokenWhenTreasuryRejects() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var ftTokenId = EntityHelper.createFungibleToken(testEnv, 3); + + // skip the transfer + // reject the token with the treasury - should fail with ACCOUNT_IS_TREASURY + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> { + new TokenRejectTransaction() + .setOwnerId(testEnv.operatorId) + .addTokenId(ftTokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining("ACCOUNT_IS_TREASURY"); + + // same test for nft + + var nftTokenId = EntityHelper.createNft(testEnv); + + var mintReceipt = new TokenMintTransaction() + .setTokenId(nftTokenId) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var nftSerials = mintReceipt.serials; + + // skip the transfer + // reject the nft with the treasury - should fail with ACCOUNT_IS_TREASURY + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> { + new TokenRejectTransaction() + .setOwnerId(testEnv.operatorId) + .addNftId(nftTokenId.nft(nftSerials.get(0))) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining("ACCOUNT_IS_TREASURY"); + } + } + + @Test + @DisplayName("Cannot Reject a Token with Invalid Signature") + void cannotRejectTokenWithInvalidSignature() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var ftTokenId = EntityHelper.createFungibleToken(testEnv, 3); + var randomKey = PrivateKey.generateED25519(); + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, 100); + + // transfer fts to the receiver + new TransferTransaction() + .addTokenTransfer(ftTokenId, testEnv.operatorId, -10) + .addTokenTransfer(ftTokenId, receiverAccountId, 10) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // reject the token with different key - should fail with INVALID_SIGNATURE + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> { + new TokenRejectTransaction() + .setOwnerId(receiverAccountId) + .addTokenId(ftTokenId) + .freezeWith(testEnv.client) + .sign(randomKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining("INVALID_SIGNATURE"); + + new TokenDeleteTransaction() + .setTokenId(ftTokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot Reject a Token when Token Or NFT ID is not set") + void cannotRejectTokenWhenTokenOrNFTIdIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // reject the token with invalid token - should fail with EMPTY_TOKEN_REFERENCE_LIST + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> { + new TokenRejectTransaction() + .setOwnerId(testEnv.operatorId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining("EMPTY_TOKEN_REFERENCE_LIST"); + } + } + + @Test + @DisplayName("Cannot Reject a Token when executing TokenReject and Token Reference List Size Exceeded") + void cannotRejectTokenWhenTokenReferenceListSizeExceeded() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var ftTokenId = EntityHelper.createFungibleToken(testEnv, 18); + var receiverAccountKey = PrivateKey.generateED25519(); + var receiverAccountId = EntityHelper.createAccount(testEnv, receiverAccountKey, -1); + var nftTokenId = EntityHelper.createNft(testEnv); + + var mintReceipt = new TokenMintTransaction() + .setTokenId(nftTokenId) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var nftSerials = mintReceipt.serials; + + // transfer the tokens to the receiver + new TransferTransaction() + .addTokenTransfer(ftTokenId, testEnv.operatorId, -10) + .addTokenTransfer(ftTokenId, receiverAccountId, 10) + .addNftTransfer(nftTokenId.nft(nftSerials.get(0)), testEnv.operatorId, receiverAccountId) + .addNftTransfer(nftTokenId.nft(nftSerials.get(1)), testEnv.operatorId, receiverAccountId) + .addNftTransfer(nftTokenId.nft(nftSerials.get(2)), testEnv.operatorId, receiverAccountId) + .addNftTransfer(nftTokenId.nft(nftSerials.get(3)), testEnv.operatorId, receiverAccountId) + .addNftTransfer(nftTokenId.nft(nftSerials.get(4)), testEnv.operatorId, receiverAccountId) + .addNftTransfer(nftTokenId.nft(nftSerials.get(5)), testEnv.operatorId, receiverAccountId) + .addNftTransfer(nftTokenId.nft(nftSerials.get(6)), testEnv.operatorId, receiverAccountId) + .addNftTransfer(nftTokenId.nft(nftSerials.get(7)), testEnv.operatorId, receiverAccountId) + .addNftTransfer(nftTokenId.nft(nftSerials.get(8)), testEnv.operatorId, receiverAccountId) + .addNftTransfer(nftTokenId.nft(nftSerials.get(9)), testEnv.operatorId, receiverAccountId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // reject the token with 11 token references - should fail with TOKEN_REFERENCE_LIST_SIZE_LIMIT_EXCEEDED + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> { + new TokenRejectTransaction() + .setOwnerId(receiverAccountId) + .addTokenId(ftTokenId) + .setNftIds(List.of( + nftTokenId.nft(nftSerials.get(0)), + nftTokenId.nft(nftSerials.get(1)), + nftTokenId.nft(nftSerials.get(2)), + nftTokenId.nft(nftSerials.get(3)), + nftTokenId.nft(nftSerials.get(4)), + nftTokenId.nft(nftSerials.get(5)), + nftTokenId.nft(nftSerials.get(6)), + nftTokenId.nft(nftSerials.get(7)), + nftTokenId.nft(nftSerials.get(8)), + nftTokenId.nft(nftSerials.get(9)))) + .freezeWith(testEnv.client) + .sign(receiverAccountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining("TOKEN_REFERENCE_LIST_SIZE_LIMIT_EXCEEDED"); + + new TokenDeleteTransaction() + .setTokenId(ftTokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenDeleteTransaction() + .setTokenId(nftTokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenRevokeKycIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenRevokeKycIntegrationTest.java new file mode 100644 index 0000000000..cc9c93e623 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenRevokeKycIntegrationTest.java @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Collections; +import java.util.Objects; +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.sdk.Status; +import org.hiero.sdk.TokenAssociateTransaction; +import org.hiero.sdk.TokenCreateTransaction; +import org.hiero.sdk.TokenRevokeKycTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class TokenRevokeKycIntegrationTest { + @Test + @DisplayName("Can revoke kyc to account with token") + void canRevokeKycAccountWithToken() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + new TokenAssociateTransaction() + .setAccountId(accountId) + .setTokenIds(Collections.singletonList(tokenId)) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenRevokeKycTransaction() + .setAccountId(accountId) + .setTokenId(tokenId) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot revoke kyc to account on token when token ID is not set") + void cannotRevokeKycToAccountOnTokenWhenTokenIDIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenRevokeKycTransaction() + .setAccountId(accountId) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_TOKEN_ID.toString()); + } + } + + @Test + @DisplayName("Cannot revoke kyc to account on token when account ID is not set") + void cannotRevokeKycToAccountOnTokenWhenAccountIDIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + var response = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client); + + var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenRevokeKycTransaction() + .setTokenId(tokenId) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_ACCOUNT_ID.toString()); + } + } + + @Test + @DisplayName("Cannot revoke kyc to account on token when account was not associated with") + void cannotRevokeKycToAccountOnTokenWhenAccountWasNotAssociatedWith() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenRevokeKycTransaction() + .setAccountId(accountId) + .setTokenId(tokenId) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.TOKEN_NOT_ASSOCIATED_TO_ACCOUNT.toString()); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenTransferIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenTransferIntegrationTest.java new file mode 100644 index 0000000000..2712f9cf57 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenTransferIntegrationTest.java @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Collections; +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.CustomFixedFee; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.sdk.Status; +import org.hiero.sdk.TokenAssociateTransaction; +import org.hiero.sdk.TokenCreateTransaction; +import org.hiero.sdk.TokenGrantKycTransaction; +import org.hiero.sdk.TransactionResponse; +import org.hiero.sdk.TransferTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class TokenTransferIntegrationTest { + @Test + @DisplayName("Can transfer tokens") + void tokenTransferTest() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + TransactionResponse response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = response.getReceipt(testEnv.client).accountId; + assertThat(accountId).isNotNull(); + + response = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client); + + var tokenId = response.getReceipt(testEnv.client).tokenId; + assertThat(tokenId).isNotNull(); + + new TokenAssociateTransaction() + .setAccountId(accountId) + .setTokenIds(Collections.singletonList(tokenId)) + .freezeWith(testEnv.client) + .signWithOperator(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenGrantKycTransaction() + .setAccountId(accountId) + .setTokenId(tokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TransferTransaction() + .addTokenTransfer(tokenId, testEnv.operatorId, -10) + .addTokenTransfer(tokenId, accountId, 10) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot transfer tokens if balance is insufficient to pay fee") + void insufficientBalanceForFee() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + PrivateKey key1 = PrivateKey.generateED25519(); + PrivateKey key2 = PrivateKey.generateED25519(); + var accountId1 = new AccountCreateTransaction() + .setKey(key1) + .setInitialBalance(new Hbar(2)) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + var accountId2 = new AccountCreateTransaction() + .setKey(key2) + .setInitialBalance(new Hbar(2)) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + var tokenId = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setInitialSupply(1) + .setCustomFees(Collections.singletonList( + new CustomFixedFee().setAmount(5000_000_000L).setFeeCollectorAccountId(testEnv.operatorId))) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFeeScheduleKey(testEnv.operatorKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId; + + new TokenAssociateTransaction() + .setAccountId(accountId1) + .setTokenIds(Collections.singletonList(tokenId)) + .freezeWith(testEnv.client) + .sign(key1) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenAssociateTransaction() + .setAccountId(accountId2) + .setTokenIds(Collections.singletonList(tokenId)) + .freezeWith(testEnv.client) + .sign(key2) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TransferTransaction() + .addTokenTransfer(tokenId, testEnv.operatorId, -1) + .addTokenTransfer(tokenId, accountId1, 1) + .freezeWith(testEnv.client) + .sign(key1) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TransferTransaction() + .addTokenTransfer(tokenId, accountId1, -1) + .addTokenTransfer(tokenId, accountId2, 1) + .freezeWith(testEnv.client) + .sign(key1) + .sign(key2) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .satisfies(error -> assertThat(error.getMessage()) + .containsAnyOf( + Status.INSUFFICIENT_SENDER_ACCOUNT_BALANCE_FOR_CUSTOM_FEE.toString(), + Status.INSUFFICIENT_PAYER_BALANCE_FOR_CUSTOM_FEE.toString())); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenUnfreezeIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenUnfreezeIntegrationTest.java new file mode 100644 index 0000000000..467bba2815 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenUnfreezeIntegrationTest.java @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Collections; +import java.util.Objects; +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.sdk.Status; +import org.hiero.sdk.TokenAssociateTransaction; +import org.hiero.sdk.TokenCreateTransaction; +import org.hiero.sdk.TokenUnfreezeTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class TokenUnfreezeIntegrationTest { + @Test + @DisplayName("Can unfreeze account with token") + void canUnfreezeAccountWithToken() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + new TokenAssociateTransaction() + .setAccountId(accountId) + .setTokenIds(Collections.singletonList(tokenId)) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenUnfreezeTransaction() + .setAccountId(accountId) + .setTokenId(tokenId) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot unfreeze account on token when token ID is not set") + void cannotUnfreezeAccountOnTokenWhenTokenIDIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenUnfreezeTransaction() + .setAccountId(accountId) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_TOKEN_ID.toString()); + } + } + + @Test + @DisplayName("Cannot unfreeze account on token when account ID is not set") + void cannotUnfreezeAccountOnTokenWhenAccountIDIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + var response = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client); + + var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenUnfreezeTransaction() + .setTokenId(tokenId) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_ACCOUNT_ID.toString()); + } + } + + @Test + @DisplayName("Cannot unfreeze account on token when account was not associated with") + void cannotUnfreezeAccountOnTokenWhenAccountWasNotAssociatedWith() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUnfreezeTransaction() + .setAccountId(accountId) + .setTokenId(tokenId) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.TOKEN_NOT_ASSOCIATED_TO_ACCOUNT.toString()); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenUnpauseIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenUnpauseIntegrationTest.java new file mode 100644 index 0000000000..7f3588b98a --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenUnpauseIntegrationTest.java @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.Collections; +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.AccountDeleteTransaction; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.TokenAssociateTransaction; +import org.hiero.sdk.TokenCreateTransaction; +import org.hiero.sdk.TokenDeleteTransaction; +import org.hiero.sdk.TokenUnpauseTransaction; +import org.hiero.sdk.TokenWipeTransaction; +import org.hiero.sdk.TransferTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class TokenUnpauseIntegrationTest { + + @Test + @DisplayName("Can execute token unpause transaction") + void canExecuteTokenUnpauseTransaction() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var accountKey = PrivateKey.generateED25519(); + var testTokenAmount = 10; + var accountId = new AccountCreateTransaction() + .setKey(accountKey) + .setInitialBalance(new Hbar(2)) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .accountId; + + var tokenId = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setInitialSupply(1000000) + .setDecimals(3) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setPauseKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId; + + new TokenAssociateTransaction() + .setAccountId(accountId) + .setTokenIds(Collections.singletonList(tokenId)) + .freezeWith(testEnv.client) + .sign(accountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenUnpauseTransaction() + .setTokenId(tokenId) + .freezeWith(testEnv.client) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TransferTransaction() + .addTokenTransfer(tokenId, accountId, testTokenAmount) + .addTokenTransfer(tokenId, testEnv.operatorId, -testTokenAmount) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenWipeTransaction() + .setTokenId(tokenId) + .setAccountId(accountId) + .setAmount(testTokenAmount) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenDeleteTransaction() + .setTokenId(tokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new AccountDeleteTransaction() + .setTransferAccountId(testEnv.operatorId) + .setAccountId(accountId) + .freezeWith(testEnv.client) + .sign(accountKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot unpause with no token ID") + void cannotUnpauseWithNoTokenId() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + assertThrows(PrecheckStatusException.class, () -> { + new TokenUnpauseTransaction().execute(testEnv.client).getReceipt(testEnv.client); + }); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenUpdateIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenUpdateIntegrationTest.java new file mode 100644 index 0000000000..e48adce5bc --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenUpdateIntegrationTest.java @@ -0,0 +1,2687 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Objects; +import org.hiero.sdk.KeyList; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.PublicKey; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.sdk.Status; +import org.hiero.sdk.TokenCreateTransaction; +import org.hiero.sdk.TokenInfoQuery; +import org.hiero.sdk.TokenKeyValidation; +import org.hiero.sdk.TokenType; +import org.hiero.sdk.TokenUpdateTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class TokenUpdateIntegrationTest { + + @Test + @DisplayName("Can update token") + void canUpdateToken() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var response = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setPauseKey(testEnv.operatorKey) + .setMetadataKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client); + + var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); + + var info = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(info.tokenId).isEqualTo(tokenId); + assertThat(info.name).isEqualTo("ffff"); + assertThat(info.symbol).isEqualTo("F"); + assertThat(info.decimals).isEqualTo(3); + assertThat(info.treasuryAccountId).isEqualTo(testEnv.operatorId); + assertThat(info.adminKey).isNotNull(); + assertThat(info.freezeKey).isNotNull(); + assertThat(info.wipeKey).isNotNull(); + assertThat(info.kycKey).isNotNull(); + assertThat(info.supplyKey).isNotNull(); + assertThat(info.adminKey.toString()).isEqualTo(testEnv.operatorKey.toString()); + assertThat(info.freezeKey.toString()).isEqualTo(testEnv.operatorKey.toString()); + assertThat(info.wipeKey.toString()).isEqualTo(testEnv.operatorKey.toString()); + assertThat(info.kycKey.toString()).isEqualTo(testEnv.operatorKey.toString()); + assertThat(info.supplyKey.toString()).isEqualTo(testEnv.operatorKey.toString()); + assertThat(info.pauseKey.toString()).isEqualTo(testEnv.operatorKey.toString()); + assertThat(info.metadataKey.toString()).isEqualTo(testEnv.operatorKey.toString()); + assertThat(info.defaultFreezeStatus).isNotNull().isFalse(); + assertThat(info.defaultKycStatus).isNotNull().isFalse(); + + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setTokenName("aaaa") + .setTokenSymbol("A") + .execute(testEnv.client) + .getReceipt(testEnv.client); + + info = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(info.tokenId).isEqualTo(tokenId); + assertThat(info.name).isEqualTo("aaaa"); + assertThat(info.symbol).isEqualTo("A"); + assertThat(info.decimals).isEqualTo(3); + assertThat(info.treasuryAccountId).isEqualTo(testEnv.operatorId); + assertThat(info.adminKey).isNotNull(); + assertThat(info.freezeKey).isNotNull(); + assertThat(info.wipeKey).isNotNull(); + assertThat(info.kycKey).isNotNull(); + assertThat(info.supplyKey).isNotNull(); + assertThat(info.adminKey.toString()).isEqualTo(testEnv.operatorKey.toString()); + assertThat(info.freezeKey.toString()).isEqualTo(testEnv.operatorKey.toString()); + assertThat(info.wipeKey.toString()).isEqualTo(testEnv.operatorKey.toString()); + assertThat(info.kycKey.toString()).isEqualTo(testEnv.operatorKey.toString()); + assertThat(info.supplyKey.toString()).isEqualTo(testEnv.operatorKey.toString()); + assertThat(info.pauseKey.toString()).isEqualTo(testEnv.operatorKey.toString()); + assertThat(info.metadataKey.toString()).isEqualTo(testEnv.operatorKey.toString()); + assertThat(info.defaultFreezeStatus).isNotNull(); + assertThat(info.defaultFreezeStatus).isFalse(); + assertThat(info.defaultKycStatus).isNotNull(); + assertThat(info.defaultKycStatus).isFalse(); + } + } + + @Test + @DisplayName("Cannot update immutable token") + void cannotUpdateImmutableToken() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var response = new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(testEnv.operatorId) + .setFreezeDefault(false) + .execute(testEnv.client); + + var tokenId = Objects.requireNonNull(response.getReceipt(testEnv.client).tokenId); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setTokenName("aaaa") + .setTokenSymbol("A") + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.TOKEN_IS_IMMUTABLE.toString()); + } + } + + /** + * @notice E2E-HIP-646 + * @url https://hips.hedera.com/hip/hip-646 + */ + @Test + @DisplayName("Can update a fungible token's metadata") + void canUpdateFungibleTokenMetadata() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var initialTokenMetadata = new byte[] {1, 1, 1, 1, 1}; + var updatedTokenMetadata = new byte[] {2, 2, 2, 2, 2}; + + // create a fungible token with metadata + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenMetadata(initialTokenMetadata) + .setTokenType(TokenType.FUNGIBLE_COMMON) + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var tokenInfoAfterCreation = + new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoAfterCreation.metadata).isEqualTo(initialTokenMetadata); + + // update token's metadata + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setTokenMetadata(updatedTokenMetadata) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var tokenInfoAfterMetadataUpdate = + new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoAfterMetadataUpdate.metadata).isEqualTo(updatedTokenMetadata); + } + } + + /** + * @notice E2E-HIP-765 + * @url https://hips.hedera.com/hip/hip-765 + */ + @Test + @DisplayName("Can update a non fungible token's metadata") + void canUpdateNonFungibleTokenMetadata() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var initialTokenMetadata = new byte[] {1, 1, 1, 1, 1}; + var updatedTokenMetadata = new byte[] {2, 2, 2, 2, 2}; + + // create a non fungible token with metadata + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenMetadata(initialTokenMetadata) + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var tokenInfoAfterCreation = + new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoAfterCreation.metadata).isEqualTo(initialTokenMetadata); + + // update token's metadata + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setTokenMetadata(updatedTokenMetadata) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var tokenInfoAfterMetadataUpdate = + new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoAfterMetadataUpdate.metadata).isEqualTo(updatedTokenMetadata); + } + } + + /** + * @notice E2E-HIP-646 + * @url https://hips.hedera.com/hip/hip-646 + */ + @Test + @DisplayName("Can update an immutable fungible token's metadata") + void canUpdateImmutableFungibleTokenMetadata() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var initialTokenMetadata = new byte[] {1, 1, 1, 1, 1}; + var updatedTokenMetadata = new byte[] {2, 2, 2, 2, 2}; + var metadataKey = PrivateKey.generateED25519(); + + // create a fungible token with metadata and metadata key + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenMetadata(initialTokenMetadata) + .setTokenType(TokenType.FUNGIBLE_COMMON) + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setMetadataKey(metadataKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var tokenInfoAfterCreation = + new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoAfterCreation.metadata).isEqualTo(initialTokenMetadata); + assertThat(tokenInfoAfterCreation.metadataKey.toString()) + .isEqualTo(metadataKey.getPublicKey().toString()); + + // update token's metadata + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setTokenMetadata(updatedTokenMetadata) + .freezeWith(testEnv.client) + .sign(metadataKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var tokenInfoAfterMetadataUpdate = + new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoAfterMetadataUpdate.metadata).isEqualTo(updatedTokenMetadata); + } + } + + /** + * @notice E2E-HIP-765 + * @url https://hips.hedera.com/hip/hip-765 + */ + @Test + @DisplayName("Can update an immutable non fungible token's metadata") + void canUpdateImmutableNonFungibleTokenMetadata() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var initialTokenMetadata = new byte[] {1, 1, 1, 1, 1}; + var updatedTokenMetadata = new byte[] {2, 2, 2, 2, 2}; + var metadataKey = PrivateKey.generateED25519(); + + // create a non fungible token with metadata and metadata key + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenMetadata(initialTokenMetadata) + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setSupplyKey(testEnv.operatorKey) + .setMetadataKey(metadataKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var tokenInfoAfterCreation = + new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoAfterCreation.metadata).isEqualTo(initialTokenMetadata); + assertThat(tokenInfoAfterCreation.metadataKey.toString()) + .isEqualTo(metadataKey.getPublicKey().toString()); + + // update token's metadata + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setTokenMetadata(updatedTokenMetadata) + .freezeWith(testEnv.client) + .sign(metadataKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var tokenInfoAfterMetadataUpdate = + new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoAfterMetadataUpdate.metadata).isEqualTo(updatedTokenMetadata); + } + } + + /** + * @notice E2E-HIP-646 + * @url https://hips.hedera.com/hip/hip-646 + */ + @Test + @DisplayName("Cannot update a fungible token with metadata when it is not set") + void cannotUpdateFungibleTokenMetadataWhenItsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var initialTokenMetadata = new byte[] {1, 1, 1, 1, 1}; + + // create a fungible token with metadata + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenMetadata(initialTokenMetadata) + .setTokenType(TokenType.FUNGIBLE_COMMON) + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var tokenInfoAfterCreation = + new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoAfterCreation.metadata).isEqualTo(initialTokenMetadata); + + // update token, but don't update metadata + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setTokenMemo("abc") + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var tokenInfoAfterMemoUpdate = + new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoAfterMemoUpdate.metadata).isEqualTo(initialTokenMetadata); + } + } + + /** + * @notice E2E-HIP-765 + * @url https://hips.hedera.com/hip/hip-765 + */ + @Test + @DisplayName("Cannot update a non fungible token with metadata when it is not set") + void cannotUpdateNonFungibleTokenMetadataWhenItsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var initialTokenMetadata = new byte[] {1, 1, 1, 1, 1}; + + // create a non fungible token with metadata + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenMetadata(initialTokenMetadata) + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var tokenInfoAfterCreation = + new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoAfterCreation.metadata).isEqualTo(initialTokenMetadata); + + // update token, but don't update metadata + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setTokenMemo("abc") + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var tokenInfoAfterMemoUpdate = + new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoAfterMemoUpdate.metadata).isEqualTo(initialTokenMetadata); + } + } + + /** + * @notice E2E-HIP-646 + * @url https://hips.hedera.com/hip/hip-646 + */ + @Test + @DisplayName("Can erase fungible token metadata") + void canEraseFungibleTokenMetadata() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var initialTokenMetadata = new byte[] {1, 1, 1, 1, 1}; + var emptyTokenMetadata = new byte[] {}; + + // create a fungible token with metadata + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenMetadata(initialTokenMetadata) + .setTokenType(TokenType.FUNGIBLE_COMMON) + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var tokenInfoAfterCreation = + new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoAfterCreation.metadata).isEqualTo(initialTokenMetadata); + + // erase token metadata (update token with empty metadata) + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setTokenMetadata(emptyTokenMetadata) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var tokenInfoAfterSettingEmptyMetadata = + new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoAfterSettingEmptyMetadata.metadata).isEqualTo(emptyTokenMetadata); + } + } + + /** + * @notice E2E-HIP-765 + * @url https://hips.hedera.com/hip/hip-765 + */ + @Test + @DisplayName("Can erase non fungible token metadata") + void canEraseNonFungibleTokenMetadata() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var initialTokenMetadata = new byte[] {1, 1, 1, 1, 1}; + var emptyTokenMetadata = new byte[] {}; + + // create a non fungible token with metadata + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenMetadata(initialTokenMetadata) + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var tokenInfoAfterCreation = + new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoAfterCreation.metadata).isEqualTo(initialTokenMetadata); + + // erase token metadata (update token with empty metadata) + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setTokenMetadata(emptyTokenMetadata) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var tokenInfoAfterSettingEmptyMetadata = + new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoAfterSettingEmptyMetadata.metadata).isEqualTo(emptyTokenMetadata); + } + } + + /** + * @notice E2E-HIP-646 + * @url https://hips.hedera.com/hip/hip-646 + */ + @Test + @DisplayName( + "Cannot update a fungible token with metadata when transaction is not signed with an admin or a metadata key") + void cannotUpdateFungibleTokenMetadataWhenTransactionIsNotSignedWithMetadataKey() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var initialTokenMetadata = new byte[] {1, 1, 1, 1, 1}; + var updatedTokenMetadata = new byte[] {2, 2, 2, 2, 2}; + var adminKey = PrivateKey.generateED25519(); + var metadataKey = PrivateKey.generateED25519(); + + // create a fungible token with metadata and metadata key + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenMetadata(initialTokenMetadata) + .setTokenType(TokenType.FUNGIBLE_COMMON) + .setTreasuryAccountId(testEnv.operatorId) + .setDecimals(3) + .setInitialSupply(1000000) + .setAdminKey(adminKey) + .setMetadataKey(metadataKey) + .freezeWith(testEnv.client) + .sign(adminKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setTokenMetadata(updatedTokenMetadata) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + } + } + + /** + * @notice E2E-HIP-765 + * @url https://hips.hedera.com/hip/hip-765 + */ + @Test + @DisplayName( + "Cannot update a non fungible token with metadata when transaction is not signed with an admin or a metadata key") + void cannotUpdateNonFungibleTokenMetadataWhenTransactionIsNotSignedWithMetadataKey() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var initialTokenMetadata = new byte[] {1, 1, 1, 1, 1}; + var updatedTokenMetadata = new byte[] {2, 2, 2, 2, 2}; + var adminKey = PrivateKey.generateED25519(); + var metadataKey = PrivateKey.generateED25519(); + + // create a non fungible token with metadata and metadata key + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenMetadata(initialTokenMetadata) + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(adminKey) + .setSupplyKey(testEnv.operatorKey) + .setMetadataKey(metadataKey) + .freezeWith(testEnv.client) + .sign(adminKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setTokenMetadata(updatedTokenMetadata) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + } + } + + /** + * @notice E2E-HIP-646 + * @url https://hips.hedera.com/hip/hip-646 + */ + @Test + @DisplayName("Cannot update a fungible token with metadata when admin and metadata keys are not set") + void cannotUpdateFungibleTokenMetadataWhenMetadataKeyNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var initialTokenMetadata = new byte[] {1, 1, 1, 1, 1}; + var updatedTokenMetadata = new byte[] {2, 2, 2, 2, 2}; + + // create a fungible token with metadata and without a metadata key and admin key + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenMetadata(initialTokenMetadata) + .setTokenType(TokenType.FUNGIBLE_COMMON) + .setTreasuryAccountId(testEnv.operatorId) + .setDecimals(3) + .setInitialSupply(1000000) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setTokenMetadata(updatedTokenMetadata) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.TOKEN_IS_IMMUTABLE.toString()); + } + } + + /** + * @notice E2E-HIP-765 + * @url https://hips.hedera.com/hip/hip-765 + */ + @Test + @DisplayName("Cannot update a non fungible token with metadata when admin and metadata keys are not set") + void cannotUpdateNonFungibleTokenMetadataWhenMetadataKeyNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + var initialTokenMetadata = new byte[] {1, 1, 1, 1, 1}; + var updatedTokenMetadata = new byte[] {2, 2, 2, 2, 2}; + + // create a non fungible token with metadata and without a metadata key and admin key + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenMetadata(initialTokenMetadata) + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setSupplyKey(testEnv.operatorKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setTokenMetadata(updatedTokenMetadata) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.TOKEN_IS_IMMUTABLE.toString()); + } + } + + /** + * @notice E2E-HIP-540 + * @url https://hips.hedera.com/hip/hip-540 + */ + @Test + @DisplayName( + "Can make a token immutable when updating keys to an empty KeyList, signing with an Admin Key, and setting the key verification mode to NO_VALIDATION") + void canMakeTokenImmutableWhenUpdatingKeysToEmptyKeyListSigningWithAdminKeyWithKeyVerificationSetToNoValidation() + throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // Admin, Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys + var adminKey = PrivateKey.generateED25519(); + var wipeKey = PrivateKey.generateED25519(); + var kycKey = PrivateKey.generateED25519(); + var freezeKey = PrivateKey.generateED25519(); + var pauseKey = PrivateKey.generateED25519(); + var supplyKey = PrivateKey.generateED25519(); + var feeScheduleKey = PrivateKey.generateED25519(); + var metadataKey = PrivateKey.generateED25519(); + + // Create a non-fungible token + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("Test NFT") + .setTokenSymbol("TNFT") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(adminKey.getPublicKey()) + .setWipeKey(wipeKey.getPublicKey()) + .setKycKey(kycKey.getPublicKey()) + .setFreezeKey(freezeKey.getPublicKey()) + .setPauseKey(pauseKey.getPublicKey()) + .setSupplyKey(supplyKey.getPublicKey()) + .setFeeScheduleKey(feeScheduleKey.getPublicKey()) + .setMetadataKey(metadataKey.getPublicKey()) + .freezeWith(testEnv.client) + .sign(adminKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var tokenInfoBeforeUpdate = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoBeforeUpdate.adminKey.toString()) + .isEqualTo(adminKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.wipeKey.toString()) + .isEqualTo(wipeKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.kycKey.toString()) + .isEqualTo(kycKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.freezeKey.toString()) + .isEqualTo(freezeKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.pauseKey.toString()) + .isEqualTo(pauseKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.supplyKey.toString()) + .isEqualTo(supplyKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.feeScheduleKey.toString()) + .isEqualTo(feeScheduleKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.metadataKey.toString()) + .isEqualTo(metadataKey.getPublicKey().toString()); + + var emptyKeyList = new KeyList(); + + // Make a token immutable by removing all of its keys when updating them to an empty KeyList, + // signing with an Admin Key, and setting the key verification mode to NO_VALIDATION + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setWipeKey(emptyKeyList) + .setKycKey(emptyKeyList) + .setFreezeKey(emptyKeyList) + .setPauseKey(emptyKeyList) + .setSupplyKey(emptyKeyList) + .setFeeScheduleKey(emptyKeyList) + .setMetadataKey(emptyKeyList) + .setAdminKey(emptyKeyList) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .freezeWith(testEnv.client) + .sign(adminKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var tokenInfoAfterUpdate = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoAfterUpdate.adminKey).isNull(); + assertThat(tokenInfoAfterUpdate.wipeKey).isNull(); + assertThat(tokenInfoAfterUpdate.kycKey).isNull(); + assertThat(tokenInfoAfterUpdate.freezeKey).isNull(); + assertThat(tokenInfoAfterUpdate.pauseKey).isNull(); + assertThat(tokenInfoAfterUpdate.supplyKey).isNull(); + assertThat(tokenInfoAfterUpdate.feeScheduleKey).isNull(); + assertThat(tokenInfoAfterUpdate.metadataKey).isNull(); + } + } + + /** + * @notice E2E-HIP-540 + * @url https://hips.hedera.com/hip/hip-540 + */ + @Test + @DisplayName( + "Can remove all of token’s lower-privilege keys when updating keys to an empty KeyList, signing with an Admin Key, and setting the key verification mode to FULL_VALIDATION") + void + canRemoveAllLowerPrivilegeKeysWhenUpdatingKeysToEmptyKeyListSigningWithAdminKeyWithKeyVerificationSetToFullValidation() + throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // Admin, Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys + var adminKey = PrivateKey.generateED25519(); + var wipeKey = PrivateKey.generateED25519(); + var kycKey = PrivateKey.generateED25519(); + var freezeKey = PrivateKey.generateED25519(); + var pauseKey = PrivateKey.generateED25519(); + var supplyKey = PrivateKey.generateED25519(); + var feeScheduleKey = PrivateKey.generateED25519(); + var metadataKey = PrivateKey.generateED25519(); + + // Create a non-fungible token + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("Test NFT") + .setTokenSymbol("TNFT") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(adminKey.getPublicKey()) + .setWipeKey(wipeKey.getPublicKey()) + .setKycKey(kycKey.getPublicKey()) + .setFreezeKey(freezeKey.getPublicKey()) + .setPauseKey(pauseKey.getPublicKey()) + .setSupplyKey(supplyKey.getPublicKey()) + .setFeeScheduleKey(feeScheduleKey.getPublicKey()) + .setMetadataKey(metadataKey.getPublicKey()) + .freezeWith(testEnv.client) + .sign(adminKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var tokenInfoBeforeUpdate = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoBeforeUpdate.adminKey.toString()) + .isEqualTo(adminKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.wipeKey.toString()) + .isEqualTo(wipeKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.kycKey.toString()) + .isEqualTo(kycKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.freezeKey.toString()) + .isEqualTo(freezeKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.pauseKey.toString()) + .isEqualTo(pauseKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.supplyKey.toString()) + .isEqualTo(supplyKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.feeScheduleKey.toString()) + .isEqualTo(feeScheduleKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.metadataKey.toString()) + .isEqualTo(metadataKey.getPublicKey().toString()); + + var emptyKeyList = new KeyList(); + + // Remove all of token’s lower-privilege keys when updating them to an empty KeyList, + // signing with an Admin Key, and setting the key verification mode to FULL_VALIDATION + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setWipeKey(emptyKeyList) + .setKycKey(emptyKeyList) + .setFreezeKey(emptyKeyList) + .setPauseKey(emptyKeyList) + .setSupplyKey(emptyKeyList) + .setFeeScheduleKey(emptyKeyList) + .setMetadataKey(emptyKeyList) + .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) + .freezeWith(testEnv.client) + .sign(adminKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var tokenInfoAfterUpdate = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoAfterUpdate.wipeKey).isNull(); + assertThat(tokenInfoAfterUpdate.kycKey).isNull(); + assertThat(tokenInfoAfterUpdate.freezeKey).isNull(); + assertThat(tokenInfoAfterUpdate.pauseKey).isNull(); + assertThat(tokenInfoAfterUpdate.supplyKey).isNull(); + assertThat(tokenInfoAfterUpdate.feeScheduleKey).isNull(); + assertThat(tokenInfoAfterUpdate.metadataKey).isNull(); + } + } + + /** + * @notice E2E-HIP-540 + * @url https://hips.hedera.com/hip/hip-540 + */ + @Test + @DisplayName( + "Can update all of token’s lower-privilege keys to an unusable key (i.e. all-zeros key), when signing with an Admin Key, and setting the key verification mode to FULL_VALIDATION, and then revert previous keys") + void + canUpdateAllLowerPrivilegeKeysToUnusableKeyWhenSigningWithAdminKeyWithKeyVerificationSetToFullValidationAndThenRevertPreviousKeys() + throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // Admin, Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys + var adminKey = PrivateKey.generateED25519(); + var wipeKey = PrivateKey.generateED25519(); + var kycKey = PrivateKey.generateED25519(); + var freezeKey = PrivateKey.generateED25519(); + var pauseKey = PrivateKey.generateED25519(); + var supplyKey = PrivateKey.generateED25519(); + var feeScheduleKey = PrivateKey.generateED25519(); + var metadataKey = PrivateKey.generateED25519(); + + // Create a non-fungible token + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("Test NFT") + .setTokenSymbol("TNFT") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(adminKey.getPublicKey()) + .setWipeKey(wipeKey.getPublicKey()) + .setKycKey(kycKey.getPublicKey()) + .setFreezeKey(freezeKey.getPublicKey()) + .setPauseKey(pauseKey.getPublicKey()) + .setSupplyKey(supplyKey.getPublicKey()) + .setFeeScheduleKey(feeScheduleKey.getPublicKey()) + .setMetadataKey(metadataKey.getPublicKey()) + .freezeWith(testEnv.client) + .sign(adminKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var tokenInfoBeforeUpdate = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoBeforeUpdate.adminKey.toString()) + .isEqualTo(adminKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.wipeKey.toString()) + .isEqualTo(wipeKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.kycKey.toString()) + .isEqualTo(kycKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.freezeKey.toString()) + .isEqualTo(freezeKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.pauseKey.toString()) + .isEqualTo(pauseKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.supplyKey.toString()) + .isEqualTo(supplyKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.feeScheduleKey.toString()) + .isEqualTo(feeScheduleKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.metadataKey.toString()) + .isEqualTo(metadataKey.getPublicKey().toString()); + + // Update all of token’s lower-privilege keys to an unusable key (i.e., all-zeros key), + // signing with an Admin Key, and setting the key verification mode to FULL_VALIDATION + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setWipeKey(PublicKey.unusableKey()) + .setKycKey(PublicKey.unusableKey()) + .setFreezeKey(PublicKey.unusableKey()) + .setPauseKey(PublicKey.unusableKey()) + .setSupplyKey(PublicKey.unusableKey()) + .setFeeScheduleKey(PublicKey.unusableKey()) + .setMetadataKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) + .freezeWith(testEnv.client) + .sign(adminKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var tokenInfoAfterUpdate = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoAfterUpdate.wipeKey.toString()) + .isEqualTo(PublicKey.unusableKey().toString()); + assertThat(tokenInfoAfterUpdate.kycKey.toString()) + .isEqualTo(PublicKey.unusableKey().toString()); + assertThat(tokenInfoAfterUpdate.freezeKey.toString()) + .isEqualTo(PublicKey.unusableKey().toString()); + assertThat(tokenInfoAfterUpdate.pauseKey.toString()) + .isEqualTo(PublicKey.unusableKey().toString()); + assertThat(tokenInfoAfterUpdate.supplyKey.toString()) + .isEqualTo(PublicKey.unusableKey().toString()); + assertThat(tokenInfoAfterUpdate.feeScheduleKey.toString()) + .isEqualTo(PublicKey.unusableKey().toString()); + assertThat(tokenInfoAfterUpdate.metadataKey.toString()) + .isEqualTo(PublicKey.unusableKey().toString()); + + // Set all lower-privilege keys back by signing with an Admin Key, + // and setting key verification mode to NO_VALIDATION + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setWipeKey(wipeKey.getPublicKey()) + .setKycKey(kycKey.getPublicKey()) + .setFreezeKey(freezeKey.getPublicKey()) + .setPauseKey(pauseKey.getPublicKey()) + .setSupplyKey(supplyKey.getPublicKey()) + .setFeeScheduleKey(feeScheduleKey.getPublicKey()) + .setMetadataKey(metadataKey.getPublicKey()) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .freezeWith(testEnv.client) + .sign(adminKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var tokenInfoAfterRevert = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoAfterRevert.adminKey.toString()) + .isEqualTo(adminKey.getPublicKey().toString()); + assertThat(tokenInfoAfterRevert.wipeKey.toString()) + .isEqualTo(wipeKey.getPublicKey().toString()); + assertThat(tokenInfoAfterRevert.kycKey.toString()) + .isEqualTo(kycKey.getPublicKey().toString()); + assertThat(tokenInfoAfterRevert.freezeKey.toString()) + .isEqualTo(freezeKey.getPublicKey().toString()); + assertThat(tokenInfoAfterRevert.pauseKey.toString()) + .isEqualTo(pauseKey.getPublicKey().toString()); + assertThat(tokenInfoAfterRevert.supplyKey.toString()) + .isEqualTo(supplyKey.getPublicKey().toString()); + assertThat(tokenInfoAfterRevert.feeScheduleKey.toString()) + .isEqualTo(feeScheduleKey.getPublicKey().toString()); + assertThat(tokenInfoAfterRevert.metadataKey.toString()) + .isEqualTo(metadataKey.getPublicKey().toString()); + } + } + + /** + * @notice E2E-HIP-540 + * @url https://hips.hedera.com/hip/hip-540 + */ + @Test + @DisplayName( + "Can update all of token’s lower-privilege keys when signing with an Admin Key and new respective lower-privilege key, and setting key verification mode to FULL_VALIDATION") + void + canUpdateAllLowerPrivilegeKeysWhenSigningWithAdminKeyAndNewLowerPrivilegeKeyWithKeyVerificationSetToFullValidation() + throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // Admin, Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys + var adminKey = PrivateKey.generateED25519(); + var wipeKey = PrivateKey.generateED25519(); + var kycKey = PrivateKey.generateED25519(); + var freezeKey = PrivateKey.generateED25519(); + var pauseKey = PrivateKey.generateED25519(); + var supplyKey = PrivateKey.generateED25519(); + var feeScheduleKey = PrivateKey.generateED25519(); + var metadataKey = PrivateKey.generateED25519(); + + // New Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys + var newWipeKey = PrivateKey.generateED25519(); + var newKycKey = PrivateKey.generateED25519(); + var newFreezeKey = PrivateKey.generateED25519(); + var newPauseKey = PrivateKey.generateED25519(); + var newSupplyKey = PrivateKey.generateED25519(); + var newFeeScheduleKey = PrivateKey.generateED25519(); + var newMetadataKey = PrivateKey.generateED25519(); + + // Create a non-fungible token + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("Test NFT") + .setTokenSymbol("TNFT") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(adminKey.getPublicKey()) + .setWipeKey(wipeKey.getPublicKey()) + .setKycKey(kycKey.getPublicKey()) + .setFreezeKey(freezeKey.getPublicKey()) + .setPauseKey(pauseKey.getPublicKey()) + .setSupplyKey(supplyKey.getPublicKey()) + .setFeeScheduleKey(feeScheduleKey.getPublicKey()) + .setMetadataKey(metadataKey.getPublicKey()) + .freezeWith(testEnv.client) + .sign(adminKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var tokenInfoBeforeUpdate = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoBeforeUpdate.adminKey.toString()) + .isEqualTo(adminKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.wipeKey.toString()) + .isEqualTo(wipeKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.kycKey.toString()) + .isEqualTo(kycKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.freezeKey.toString()) + .isEqualTo(freezeKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.pauseKey.toString()) + .isEqualTo(pauseKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.supplyKey.toString()) + .isEqualTo(supplyKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.feeScheduleKey.toString()) + .isEqualTo(feeScheduleKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.metadataKey.toString()) + .isEqualTo(metadataKey.getPublicKey().toString()); + + // Update all of token’s lower-privilege keys when signing with an Admin Key and new respective + // lower-privilege key, + // and setting key verification mode to FULL_VALIDATION + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setWipeKey(newWipeKey.getPublicKey()) + .setKycKey(newKycKey.getPublicKey()) + .setFreezeKey(newFreezeKey.getPublicKey()) + .setPauseKey(newPauseKey.getPublicKey()) + .setSupplyKey(newSupplyKey.getPublicKey()) + .setFeeScheduleKey(newFeeScheduleKey.getPublicKey()) + .setMetadataKey(newMetadataKey.getPublicKey()) + .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) + .freezeWith(testEnv.client) + .sign(adminKey) + .sign(newWipeKey) + .sign(newKycKey) + .sign(newFreezeKey) + .sign(newPauseKey) + .sign(newSupplyKey) + .sign(newFeeScheduleKey) + .sign(newMetadataKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var tokenInfoAfterUpdate = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoAfterUpdate.wipeKey.toString()) + .isEqualTo(newWipeKey.getPublicKey().toString()); + assertThat(tokenInfoAfterUpdate.kycKey.toString()) + .isEqualTo(newKycKey.getPublicKey().toString()); + assertThat(tokenInfoAfterUpdate.freezeKey.toString()) + .isEqualTo(newFreezeKey.getPublicKey().toString()); + assertThat(tokenInfoAfterUpdate.pauseKey.toString()) + .isEqualTo(newPauseKey.getPublicKey().toString()); + assertThat(tokenInfoAfterUpdate.supplyKey.toString()) + .isEqualTo(newSupplyKey.getPublicKey().toString()); + assertThat(tokenInfoAfterUpdate.feeScheduleKey.toString()) + .isEqualTo(newFeeScheduleKey.getPublicKey().toString()); + assertThat(tokenInfoAfterUpdate.metadataKey.toString()) + .isEqualTo(newMetadataKey.getPublicKey().toString()); + } + } + + /** + * @notice E2E-HIP-540 + * @url https://hips.hedera.com/hip/hip-540 + */ + @Test + @DisplayName( + "Cannot make a token immutable when updating keys to an empty KeyList, signing with a key that is different from an Admin Key, and setting the key verification mode to NO_VALIDATION") + void + cannotMakeTokenImmutableWhenUpdatingKeysToEmptyKeyListSigningWithDifferentKeyWithKeyVerificationSetToNoValidation() + throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // Admin, Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys + var adminKey = PrivateKey.generateED25519(); + var wipeKey = PrivateKey.generateED25519(); + var kycKey = PrivateKey.generateED25519(); + var freezeKey = PrivateKey.generateED25519(); + var pauseKey = PrivateKey.generateED25519(); + var supplyKey = PrivateKey.generateED25519(); + var feeScheduleKey = PrivateKey.generateED25519(); + var metadataKey = PrivateKey.generateED25519(); + + // Create a non-fungible token + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("Test NFT") + .setTokenSymbol("TNFT") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(adminKey.getPublicKey()) + .setWipeKey(wipeKey.getPublicKey()) + .setKycKey(kycKey.getPublicKey()) + .setFreezeKey(freezeKey.getPublicKey()) + .setPauseKey(pauseKey.getPublicKey()) + .setSupplyKey(supplyKey.getPublicKey()) + .setFeeScheduleKey(feeScheduleKey.getPublicKey()) + .setMetadataKey(metadataKey.getPublicKey()) + .freezeWith(testEnv.client) + .sign(adminKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var tokenInfoBeforeUpdate = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoBeforeUpdate.adminKey.toString()) + .isEqualTo(adminKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.wipeKey.toString()) + .isEqualTo(wipeKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.kycKey.toString()) + .isEqualTo(kycKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.freezeKey.toString()) + .isEqualTo(freezeKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.pauseKey.toString()) + .isEqualTo(pauseKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.supplyKey.toString()) + .isEqualTo(supplyKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.feeScheduleKey.toString()) + .isEqualTo(feeScheduleKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.metadataKey.toString()) + .isEqualTo(metadataKey.getPublicKey().toString()); + + var emptyKeyList = new KeyList(); + + // Make the token immutable when updating all of its keys to an empty KeyList + // (trying to remove keys one by one to check all errors), + // signing with a key that is different from an Admin Key (implicitly with an operator key), + // and setting the key verification mode to NO_VALIDATION + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setWipeKey(emptyKeyList) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKycKey(emptyKeyList) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setFreezeKey(emptyKeyList) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setPauseKey(emptyKeyList) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setSupplyKey(emptyKeyList) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setFeeScheduleKey(emptyKeyList) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setMetadataKey(emptyKeyList) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setAdminKey(emptyKeyList) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + } + } + + /** + * @notice E2E-HIP-540 + * @url https://hips.hedera.com/hip/hip-540 + */ + @Test + @DisplayName( + "Cannot make a token immutable when updating keys to an unusable key (i.e. all-zeros key), signing with a key that is different from an Admin Key, and setting the key verification mode to NO_VALIDATION") + void + cannotMakeTokenImmutableWhenUpdatingKeysToUnusableKeySigningWithDifferentKeyWithKeyVerificationSetToNoValidation() + throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // Admin, Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys + var adminKey = PrivateKey.generateED25519(); + var wipeKey = PrivateKey.generateED25519(); + var kycKey = PrivateKey.generateED25519(); + var freezeKey = PrivateKey.generateED25519(); + var pauseKey = PrivateKey.generateED25519(); + var supplyKey = PrivateKey.generateED25519(); + var feeScheduleKey = PrivateKey.generateED25519(); + var metadataKey = PrivateKey.generateED25519(); + + // Create a non-fungible token + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("Test NFT") + .setTokenSymbol("TNFT") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(adminKey.getPublicKey()) + .setWipeKey(wipeKey.getPublicKey()) + .setKycKey(kycKey.getPublicKey()) + .setFreezeKey(freezeKey.getPublicKey()) + .setPauseKey(pauseKey.getPublicKey()) + .setSupplyKey(supplyKey.getPublicKey()) + .setFeeScheduleKey(feeScheduleKey.getPublicKey()) + .setMetadataKey(metadataKey.getPublicKey()) + .freezeWith(testEnv.client) + .sign(adminKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var tokenInfoBeforeUpdate = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoBeforeUpdate.adminKey.toString()) + .isEqualTo(adminKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.wipeKey.toString()) + .isEqualTo(wipeKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.kycKey.toString()) + .isEqualTo(kycKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.freezeKey.toString()) + .isEqualTo(freezeKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.pauseKey.toString()) + .isEqualTo(pauseKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.supplyKey.toString()) + .isEqualTo(supplyKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.feeScheduleKey.toString()) + .isEqualTo(feeScheduleKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.metadataKey.toString()) + .isEqualTo(metadataKey.getPublicKey().toString()); + + // Make the token immutable when updating all of its keys to an unusable key (i.e. all-zeros key) + // (trying to remove keys one by one to check all errors), + // signing with a key that is different from an Admin Key (implicitly with an operator key), + // and setting the key verification mode to NO_VALIDATION + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setWipeKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKycKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setFreezeKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setPauseKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setSupplyKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setFeeScheduleKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setMetadataKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setAdminKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + } + } + + /** + * @notice E2E-HIP-540 + * @url https://hips.hedera.com/hip/hip-540 + */ + @Test + @DisplayName( + "Cannot update the Admin Key to an unusable key (i.e. all-zeros key), signing with an Admin Key, and setting the key verification mode to NO_VALIDATION") + void cannotUpdateAdminKeyToUnusableKeySigningWithAdminKeyWithKeyVerificationSetToNoValidation() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // Admin and supply keys + var adminKey = PrivateKey.generateED25519(); + var supplyKey = PrivateKey.generateED25519(); + + // Create a non-fungible token + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("Test NFT") + .setTokenSymbol("TNFT") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(adminKey.getPublicKey()) + .setSupplyKey(supplyKey.getPublicKey()) + .freezeWith(testEnv.client) + .sign(adminKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var tokenInfoBeforeUpdate = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoBeforeUpdate.adminKey.toString()) + .isEqualTo(adminKey.getPublicKey().toString()); + + // Update the Admin Key to an unusable key (i.e., all-zeros key), + // signing with an Admin Key, and setting the key verification mode to NO_VALIDATION + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setAdminKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .freezeWith(testEnv.client) + .sign(adminKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + } + } + + /** + * @notice E2E-HIP-540 + * @url https://hips.hedera.com/hip/hip-540 + */ + @Test + @DisplayName( + "Can update all of token’s lower-privilege keys to an unusable key (i.e. all-zeros key), when signing with a respective lower-privilege key, and setting the key verification mode to NO_VALIDATION") + void + canUpdateAllLowerPrivilegeKeysToUnusableKeyWhenSigningWithRespectiveLowerPrivilegeKeyWithKeyVerificationSetToNoValidation() + throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys + var wipeKey = PrivateKey.generateED25519(); + var kycKey = PrivateKey.generateED25519(); + var freezeKey = PrivateKey.generateED25519(); + var pauseKey = PrivateKey.generateED25519(); + var supplyKey = PrivateKey.generateED25519(); + var feeScheduleKey = PrivateKey.generateED25519(); + var metadataKey = PrivateKey.generateED25519(); + + // Create a non-fungible token + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("Test NFT") + .setTokenSymbol("TNFT") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setWipeKey(wipeKey.getPublicKey()) + .setKycKey(kycKey.getPublicKey()) + .setFreezeKey(freezeKey.getPublicKey()) + .setPauseKey(pauseKey.getPublicKey()) + .setSupplyKey(supplyKey.getPublicKey()) + .setFeeScheduleKey(feeScheduleKey.getPublicKey()) + .setMetadataKey(metadataKey.getPublicKey()) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var tokenInfoBeforeUpdate = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoBeforeUpdate.wipeKey.toString()) + .isEqualTo(wipeKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.kycKey.toString()) + .isEqualTo(kycKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.freezeKey.toString()) + .isEqualTo(freezeKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.pauseKey.toString()) + .isEqualTo(pauseKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.supplyKey.toString()) + .isEqualTo(supplyKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.feeScheduleKey.toString()) + .isEqualTo(feeScheduleKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.metadataKey.toString()) + .isEqualTo(metadataKey.getPublicKey().toString()); + + // Update all of token’s lower-privilege keys to an unusable key (i.e., all-zeros key), + // when signing with a respective lower-privilege key, + // and setting the key verification mode to NO_VALIDATION + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setWipeKey(PublicKey.unusableKey()) + .setKycKey(PublicKey.unusableKey()) + .setFreezeKey(PublicKey.unusableKey()) + .setPauseKey(PublicKey.unusableKey()) + .setSupplyKey(PublicKey.unusableKey()) + .setFeeScheduleKey(PublicKey.unusableKey()) + .setMetadataKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .freezeWith(testEnv.client) + .sign(wipeKey) + .sign(kycKey) + .sign(freezeKey) + .sign(pauseKey) + .sign(supplyKey) + .sign(feeScheduleKey) + .sign(metadataKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var tokenInfoAfterUpdate = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoAfterUpdate.wipeKey.toString()) + .isEqualTo(PublicKey.unusableKey().toString()); + assertThat(tokenInfoAfterUpdate.kycKey.toString()) + .isEqualTo(PublicKey.unusableKey().toString()); + assertThat(tokenInfoAfterUpdate.freezeKey.toString()) + .isEqualTo(PublicKey.unusableKey().toString()); + assertThat(tokenInfoAfterUpdate.pauseKey.toString()) + .isEqualTo(PublicKey.unusableKey().toString()); + assertThat(tokenInfoAfterUpdate.supplyKey.toString()) + .isEqualTo(PublicKey.unusableKey().toString()); + assertThat(tokenInfoAfterUpdate.feeScheduleKey.toString()) + .isEqualTo(PublicKey.unusableKey().toString()); + assertThat(tokenInfoAfterUpdate.metadataKey.toString()) + .isEqualTo(PublicKey.unusableKey().toString()); + } + } + + /** + * @notice E2E-HIP-540 + * @url https://hips.hedera.com/hip/hip-540 + */ + @Test + @DisplayName( + "Can update all of token’s lower-privilege keys when signing with an old lower-privilege key and with a new lower-privilege key, and setting key verification mode to FULL_VALIDATION") + void + canUpdateAllLowerPrivilegeKeysWhenSigningWithOldLowerPrivilegeKeyAndNewLowerPrivilegeKeyWithKeyVerificationSetToFulValidation() + throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys + var wipeKey = PrivateKey.generateED25519(); + var kycKey = PrivateKey.generateED25519(); + var freezeKey = PrivateKey.generateED25519(); + var pauseKey = PrivateKey.generateED25519(); + var supplyKey = PrivateKey.generateED25519(); + var feeScheduleKey = PrivateKey.generateED25519(); + var metadataKey = PrivateKey.generateED25519(); + + // New Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys + var newWipeKey = PrivateKey.generateED25519(); + var newKycKey = PrivateKey.generateED25519(); + var newFreezeKey = PrivateKey.generateED25519(); + var newPauseKey = PrivateKey.generateED25519(); + var newSupplyKey = PrivateKey.generateED25519(); + var newFeeScheduleKey = PrivateKey.generateED25519(); + var newMetadataKey = PrivateKey.generateED25519(); + + // Create a non-fungible token + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("Test NFT") + .setTokenSymbol("TNFT") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setWipeKey(wipeKey.getPublicKey()) + .setKycKey(kycKey.getPublicKey()) + .setFreezeKey(freezeKey.getPublicKey()) + .setPauseKey(pauseKey.getPublicKey()) + .setSupplyKey(supplyKey.getPublicKey()) + .setFeeScheduleKey(feeScheduleKey.getPublicKey()) + .setMetadataKey(metadataKey.getPublicKey()) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var tokenInfoBeforeUpdate = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoBeforeUpdate.wipeKey.toString()) + .isEqualTo(wipeKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.kycKey.toString()) + .isEqualTo(kycKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.freezeKey.toString()) + .isEqualTo(freezeKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.pauseKey.toString()) + .isEqualTo(pauseKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.supplyKey.toString()) + .isEqualTo(supplyKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.feeScheduleKey.toString()) + .isEqualTo(feeScheduleKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.metadataKey.toString()) + .isEqualTo(metadataKey.getPublicKey().toString()); + + // Update all of token’s lower-privilege keys when signing with an old respective lower-privilege key, + // and setting key verification mode to NO_VALIDATION + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setWipeKey(newWipeKey.getPublicKey()) + .setKycKey(newKycKey.getPublicKey()) + .setFreezeKey(newFreezeKey.getPublicKey()) + .setPauseKey(newPauseKey.getPublicKey()) + .setSupplyKey(newSupplyKey.getPublicKey()) + .setFeeScheduleKey(newFeeScheduleKey.getPublicKey()) + .setMetadataKey(newMetadataKey.getPublicKey()) + .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) + .freezeWith(testEnv.client) + .sign(wipeKey) + .sign(newWipeKey) + .sign(kycKey) + .sign(newKycKey) + .sign(freezeKey) + .sign(newFreezeKey) + .sign(pauseKey) + .sign(newPauseKey) + .sign(supplyKey) + .sign(newSupplyKey) + .sign(feeScheduleKey) + .sign(newFeeScheduleKey) + .sign(metadataKey) + .sign(newMetadataKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var tokenInfoAfterUpdate = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoAfterUpdate.wipeKey.toString()) + .isEqualTo(newWipeKey.getPublicKey().toString()); + assertThat(tokenInfoAfterUpdate.kycKey.toString()) + .isEqualTo(newKycKey.getPublicKey().toString()); + assertThat(tokenInfoAfterUpdate.freezeKey.toString()) + .isEqualTo(newFreezeKey.getPublicKey().toString()); + assertThat(tokenInfoAfterUpdate.pauseKey.toString()) + .isEqualTo(newPauseKey.getPublicKey().toString()); + assertThat(tokenInfoAfterUpdate.supplyKey.toString()) + .isEqualTo(newSupplyKey.getPublicKey().toString()); + assertThat(tokenInfoAfterUpdate.feeScheduleKey.toString()) + .isEqualTo(newFeeScheduleKey.getPublicKey().toString()); + assertThat(tokenInfoAfterUpdate.metadataKey.toString()) + .isEqualTo(newMetadataKey.getPublicKey().toString()); + } + } + + /** + * @notice E2E-HIP-540 + * @url https://hips.hedera.com/hip/hip-540 + */ + @Test + @DisplayName( + "Can update all of token’s lower-privilege keys when signing ONLY with an old lower-privilege key, and setting key verification mode to NO_VALIDATION") + void canUpdateAllLowerPrivilegeKeysWhenSigningOnlyWithOldLowerPrivilegeKeyWithKeyVerificationSetToNoValidation() + throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys + var wipeKey = PrivateKey.generateED25519(); + var kycKey = PrivateKey.generateED25519(); + var freezeKey = PrivateKey.generateED25519(); + var pauseKey = PrivateKey.generateED25519(); + var supplyKey = PrivateKey.generateED25519(); + var feeScheduleKey = PrivateKey.generateED25519(); + var metadataKey = PrivateKey.generateED25519(); + + // New Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys + var newWipeKey = PrivateKey.generateED25519(); + var newKycKey = PrivateKey.generateED25519(); + var newFreezeKey = PrivateKey.generateED25519(); + var newPauseKey = PrivateKey.generateED25519(); + var newSupplyKey = PrivateKey.generateED25519(); + var newFeeScheduleKey = PrivateKey.generateED25519(); + var newMetadataKey = PrivateKey.generateED25519(); + + // Create a non-fungible token + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("Test NFT") + .setTokenSymbol("TNFT") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setWipeKey(wipeKey.getPublicKey()) + .setKycKey(kycKey.getPublicKey()) + .setFreezeKey(freezeKey.getPublicKey()) + .setPauseKey(pauseKey.getPublicKey()) + .setSupplyKey(supplyKey.getPublicKey()) + .setFeeScheduleKey(feeScheduleKey.getPublicKey()) + .setMetadataKey(metadataKey.getPublicKey()) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var tokenInfoBeforeUpdate = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoBeforeUpdate.wipeKey.toString()) + .isEqualTo(wipeKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.kycKey.toString()) + .isEqualTo(kycKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.freezeKey.toString()) + .isEqualTo(freezeKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.pauseKey.toString()) + .isEqualTo(pauseKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.supplyKey.toString()) + .isEqualTo(supplyKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.feeScheduleKey.toString()) + .isEqualTo(feeScheduleKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.metadataKey.toString()) + .isEqualTo(metadataKey.getPublicKey().toString()); + + // Update all of token’s lower-privilege keys when signing with an old respective lower-privilege key, + // and setting key verification mode to NO_VALIDATION + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setWipeKey(newWipeKey.getPublicKey()) + .setKycKey(newKycKey.getPublicKey()) + .setFreezeKey(newFreezeKey.getPublicKey()) + .setPauseKey(newPauseKey.getPublicKey()) + .setSupplyKey(newSupplyKey.getPublicKey()) + .setFeeScheduleKey(newFeeScheduleKey.getPublicKey()) + .setMetadataKey(newMetadataKey.getPublicKey()) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .freezeWith(testEnv.client) + .sign(wipeKey) + .sign(kycKey) + .sign(freezeKey) + .sign(pauseKey) + .sign(supplyKey) + .sign(feeScheduleKey) + .sign(metadataKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var tokenInfoAfterUpdate = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoAfterUpdate.wipeKey.toString()) + .isEqualTo(newWipeKey.getPublicKey().toString()); + assertThat(tokenInfoAfterUpdate.kycKey.toString()) + .isEqualTo(newKycKey.getPublicKey().toString()); + assertThat(tokenInfoAfterUpdate.freezeKey.toString()) + .isEqualTo(newFreezeKey.getPublicKey().toString()); + assertThat(tokenInfoAfterUpdate.pauseKey.toString()) + .isEqualTo(newPauseKey.getPublicKey().toString()); + assertThat(tokenInfoAfterUpdate.supplyKey.toString()) + .isEqualTo(newSupplyKey.getPublicKey().toString()); + assertThat(tokenInfoAfterUpdate.feeScheduleKey.toString()) + .isEqualTo(newFeeScheduleKey.getPublicKey().toString()); + assertThat(tokenInfoAfterUpdate.metadataKey.toString()) + .isEqualTo(newMetadataKey.getPublicKey().toString()); + } + } + + /** + * @notice E2E-HIP-540 + * @url https://hips.hedera.com/hip/hip-540 + */ + @Test + @DisplayName( + "Cannot remove all of token’s lower-privilege keys when updating them to an empty KeyList, signing with a respective lower-privilege key, and setting the key verification mode to NO_VALIDATION") + void + cannotRemoveAllLowerPrivilegeKeysWhenUpdatingKeysToEmptyKeyListSigningWithRespectiveLowerPrivilegeKeyWithKeyVerificationSetToNoValidation() + throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys + var wipeKey = PrivateKey.generateED25519(); + var kycKey = PrivateKey.generateED25519(); + var freezeKey = PrivateKey.generateED25519(); + var pauseKey = PrivateKey.generateED25519(); + var supplyKey = PrivateKey.generateED25519(); + var feeScheduleKey = PrivateKey.generateED25519(); + var metadataKey = PrivateKey.generateED25519(); + + // Create a non-fungible token + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("Test NFT") + .setTokenSymbol("TNFT") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setWipeKey(wipeKey.getPublicKey()) + .setKycKey(kycKey.getPublicKey()) + .setFreezeKey(freezeKey.getPublicKey()) + .setPauseKey(pauseKey.getPublicKey()) + .setSupplyKey(supplyKey.getPublicKey()) + .setFeeScheduleKey(feeScheduleKey.getPublicKey()) + .setMetadataKey(metadataKey.getPublicKey()) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var tokenInfoBeforeUpdate = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoBeforeUpdate.wipeKey.toString()) + .isEqualTo(wipeKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.kycKey.toString()) + .isEqualTo(kycKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.freezeKey.toString()) + .isEqualTo(freezeKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.pauseKey.toString()) + .isEqualTo(pauseKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.supplyKey.toString()) + .isEqualTo(supplyKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.feeScheduleKey.toString()) + .isEqualTo(feeScheduleKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.metadataKey.toString()) + .isEqualTo(metadataKey.getPublicKey().toString()); + + var emptyKeyList = new KeyList(); + + // Remove all of token’s lower-privilege keys + // when updating them to an empty KeyList (trying to remove keys one by one to check all errors), + // signing with a respective lower-privilege key, + // and setting the key verification mode to NO_VALIDATION + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setWipeKey(emptyKeyList) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .freezeWith(testEnv.client) + .sign(wipeKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.TOKEN_IS_IMMUTABLE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKycKey(emptyKeyList) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .freezeWith(testEnv.client) + .sign(kycKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.TOKEN_IS_IMMUTABLE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setFreezeKey(emptyKeyList) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .freezeWith(testEnv.client) + .sign(freezeKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.TOKEN_IS_IMMUTABLE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setPauseKey(emptyKeyList) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .freezeWith(testEnv.client) + .sign(pauseKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.TOKEN_IS_IMMUTABLE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setSupplyKey(emptyKeyList) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .freezeWith(testEnv.client) + .sign(supplyKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.TOKEN_IS_IMMUTABLE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setFeeScheduleKey(emptyKeyList) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .freezeWith(testEnv.client) + .sign(feeScheduleKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.TOKEN_IS_IMMUTABLE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setMetadataKey(emptyKeyList) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .freezeWith(testEnv.client) + .sign(metadataKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.TOKEN_IS_IMMUTABLE.toString()); + } + } + + /** + * @notice E2E-HIP-540 + * @url https://hips.hedera.com/hip/hip-540 + */ + @Test + @DisplayName( + "Cannot update all of token’s lower-privilege keys to an unusable key (i.e. all-zeros key), when signing with a key that is different from a respective lower-privilege key, and setting the key verification mode to NO_VALIDATION") + void cannotUpdateAllLowerPrivilegeKeysToUnusableKeyWhenSigningWithDifferentKeyWithKeyVerificationSetToNoValidation() + throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys + var wipeKey = PrivateKey.generateED25519(); + var kycKey = PrivateKey.generateED25519(); + var freezeKey = PrivateKey.generateED25519(); + var pauseKey = PrivateKey.generateED25519(); + var supplyKey = PrivateKey.generateED25519(); + var feeScheduleKey = PrivateKey.generateED25519(); + var metadataKey = PrivateKey.generateED25519(); + + // Create a non-fungible token + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("Test NFT") + .setTokenSymbol("TNFT") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setWipeKey(wipeKey.getPublicKey()) + .setKycKey(kycKey.getPublicKey()) + .setFreezeKey(freezeKey.getPublicKey()) + .setPauseKey(pauseKey.getPublicKey()) + .setSupplyKey(supplyKey.getPublicKey()) + .setFeeScheduleKey(feeScheduleKey.getPublicKey()) + .setMetadataKey(metadataKey.getPublicKey()) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var tokenInfoBeforeUpdate = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoBeforeUpdate.wipeKey.toString()) + .isEqualTo(wipeKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.kycKey.toString()) + .isEqualTo(kycKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.freezeKey.toString()) + .isEqualTo(freezeKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.pauseKey.toString()) + .isEqualTo(pauseKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.supplyKey.toString()) + .isEqualTo(supplyKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.feeScheduleKey.toString()) + .isEqualTo(feeScheduleKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.metadataKey.toString()) + .isEqualTo(metadataKey.getPublicKey().toString()); + + // Update all of token’s lower-privilege keys to an unusable key (i.e. all-zeros key) + // (trying to remove keys one by one to check all errors), + // signing with a key that is different from a respective lower-privilege key (implicitly with an operator + // key), + // and setting the key verification mode to NO_VALIDATION + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setWipeKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKycKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setFreezeKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setPauseKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setSupplyKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setFeeScheduleKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setMetadataKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + } + } + + /** + * @notice E2E-HIP-540 + * @url https://hips.hedera.com/hip/hip-540 + */ + @Test + @DisplayName( + "Cannot update all of token’s lower-privilege keys to an unusable key (i.e. all-zeros key), when signing ONLY with an old respective lower-privilege key, and setting the key verification mode to FULL_VALIDATION") + void + cannotUpdateAllLowerPrivilegeKeysToUnusableKeyWhenSigningOnlyWithOldRespectiveLowerPrivilegeKeyWithKeyVerificationSetToFullValidation() + throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys + var wipeKey = PrivateKey.generateED25519(); + var kycKey = PrivateKey.generateED25519(); + var freezeKey = PrivateKey.generateED25519(); + var pauseKey = PrivateKey.generateED25519(); + var supplyKey = PrivateKey.generateED25519(); + var feeScheduleKey = PrivateKey.generateED25519(); + var metadataKey = PrivateKey.generateED25519(); + + // Create a non-fungible token + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("Test NFT") + .setTokenSymbol("TNFT") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setWipeKey(wipeKey.getPublicKey()) + .setKycKey(kycKey.getPublicKey()) + .setFreezeKey(freezeKey.getPublicKey()) + .setPauseKey(pauseKey.getPublicKey()) + .setSupplyKey(supplyKey.getPublicKey()) + .setFeeScheduleKey(feeScheduleKey.getPublicKey()) + .setMetadataKey(metadataKey.getPublicKey()) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var tokenInfoBeforeUpdate = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoBeforeUpdate.wipeKey.toString()) + .isEqualTo(wipeKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.kycKey.toString()) + .isEqualTo(kycKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.freezeKey.toString()) + .isEqualTo(freezeKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.pauseKey.toString()) + .isEqualTo(pauseKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.supplyKey.toString()) + .isEqualTo(supplyKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.feeScheduleKey.toString()) + .isEqualTo(feeScheduleKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.metadataKey.toString()) + .isEqualTo(metadataKey.getPublicKey().toString()); + + // Update all of token’s lower-privilege keys to an unusable key (i.e., all-zeros key) + // (trying to remove keys one by one to check all errors), + // signing ONLY with an old respective lower-privilege key, + // and setting the key verification mode to FULL_VALIDATION + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setWipeKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) + .freezeWith(testEnv.client) + .sign(wipeKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKycKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) + .freezeWith(testEnv.client) + .sign(kycKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setFreezeKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) + .freezeWith(testEnv.client) + .sign(freezeKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setPauseKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) + .freezeWith(testEnv.client) + .sign(pauseKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setSupplyKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) + .freezeWith(testEnv.client) + .sign(supplyKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setFeeScheduleKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) + .freezeWith(testEnv.client) + .sign(feeScheduleKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setMetadataKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) + .freezeWith(testEnv.client) + .sign(metadataKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + } + } + + /** + * @notice E2E-HIP-540 + * @url https://hips.hedera.com/hip/hip-540 + */ + @Test + @DisplayName( + "Cannot update all of token’s lower-privilege keys to an unusable key (i.e. all-zeros key), when signing with an old respective lower-privilege key and new respective lower-privilege key, and setting the key verification mode to FULL_VALIDATION") + void + cannotUpdateAllLowerPrivilegeKeysToUnusableKeyWhenSigningWithOldRespectiveLowerPrivilegeKeyAndNewRespectiveLowerPrivilegeKeyWithKeyVerificationSetToFullValidation() + throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys + var wipeKey = PrivateKey.generateED25519(); + var kycKey = PrivateKey.generateED25519(); + var freezeKey = PrivateKey.generateED25519(); + var pauseKey = PrivateKey.generateED25519(); + var supplyKey = PrivateKey.generateED25519(); + var feeScheduleKey = PrivateKey.generateED25519(); + var metadataKey = PrivateKey.generateED25519(); + + // New Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys + var newWipeKey = PrivateKey.generateED25519(); + var newKycKey = PrivateKey.generateED25519(); + var newFreezeKey = PrivateKey.generateED25519(); + var newPauseKey = PrivateKey.generateED25519(); + var newSupplyKey = PrivateKey.generateED25519(); + var newFeeScheduleKey = PrivateKey.generateED25519(); + var newMetadataKey = PrivateKey.generateED25519(); + + // Create a non-fungible token + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("Test NFT") + .setTokenSymbol("TNFT") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setWipeKey(wipeKey.getPublicKey()) + .setKycKey(kycKey.getPublicKey()) + .setFreezeKey(freezeKey.getPublicKey()) + .setPauseKey(pauseKey.getPublicKey()) + .setSupplyKey(supplyKey.getPublicKey()) + .setFeeScheduleKey(feeScheduleKey.getPublicKey()) + .setMetadataKey(metadataKey.getPublicKey()) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var tokenInfoBeforeUpdate = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoBeforeUpdate.wipeKey.toString()) + .isEqualTo(wipeKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.kycKey.toString()) + .isEqualTo(kycKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.freezeKey.toString()) + .isEqualTo(freezeKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.pauseKey.toString()) + .isEqualTo(pauseKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.supplyKey.toString()) + .isEqualTo(supplyKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.feeScheduleKey.toString()) + .isEqualTo(feeScheduleKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.metadataKey.toString()) + .isEqualTo(metadataKey.getPublicKey().toString()); + + // Update all of token’s lower-privilege keys to an unusable key (i.e., all-zeros key) + // (trying to remove keys one by one to check all errors), + // signing with an old respective lower-privilege key and new respective lower-privilege key, + // and setting the key verification mode to FULL_VALIDATION + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setWipeKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) + .freezeWith(testEnv.client) + .sign(wipeKey) + .sign(newWipeKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKycKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) + .freezeWith(testEnv.client) + .sign(kycKey) + .sign(newKycKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setFreezeKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) + .freezeWith(testEnv.client) + .sign(freezeKey) + .sign(newFreezeKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setPauseKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) + .freezeWith(testEnv.client) + .sign(pauseKey) + .sign(newPauseKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setSupplyKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) + .freezeWith(testEnv.client) + .sign(supplyKey) + .sign(newSupplyKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setFeeScheduleKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) + .freezeWith(testEnv.client) + .sign(feeScheduleKey) + .sign(newFeeScheduleKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setMetadataKey(PublicKey.unusableKey()) + .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) + .freezeWith(testEnv.client) + .sign(metadataKey) + .sign(newMetadataKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + } + } + + /** + * @notice E2E-HIP-540 + * @url https://hips.hedera.com/hip/hip-540 + */ + @Test + @DisplayName( + "Cannot update all of token’s lower-privilege keys, when signing ONLY with an old respective lower-privilege key, and setting the key verification mode to FULL_VALIDATION") + void + cannotUpdateAllLowerPrivilegeKeysWhenSigningOnlyWithOldRespectiveLowerPrivilegeKeyWithKeyVerificationSetToFullValidation() + throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys + var wipeKey = PrivateKey.generateED25519(); + var kycKey = PrivateKey.generateED25519(); + var freezeKey = PrivateKey.generateED25519(); + var pauseKey = PrivateKey.generateED25519(); + var supplyKey = PrivateKey.generateED25519(); + var feeScheduleKey = PrivateKey.generateED25519(); + var metadataKey = PrivateKey.generateED25519(); + + // New Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys + var newWipeKey = PrivateKey.generateED25519(); + var newKycKey = PrivateKey.generateED25519(); + var newFreezeKey = PrivateKey.generateED25519(); + var newPauseKey = PrivateKey.generateED25519(); + var newSupplyKey = PrivateKey.generateED25519(); + var newFeeScheduleKey = PrivateKey.generateED25519(); + var newMetadataKey = PrivateKey.generateED25519(); + + // Create a non-fungible token + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("Test NFT") + .setTokenSymbol("TNFT") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setWipeKey(wipeKey.getPublicKey()) + .setKycKey(kycKey.getPublicKey()) + .setFreezeKey(freezeKey.getPublicKey()) + .setPauseKey(pauseKey.getPublicKey()) + .setSupplyKey(supplyKey.getPublicKey()) + .setFeeScheduleKey(feeScheduleKey.getPublicKey()) + .setMetadataKey(metadataKey.getPublicKey()) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var tokenInfoBeforeUpdate = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoBeforeUpdate.wipeKey.toString()) + .isEqualTo(wipeKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.kycKey.toString()) + .isEqualTo(kycKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.freezeKey.toString()) + .isEqualTo(freezeKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.pauseKey.toString()) + .isEqualTo(pauseKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.supplyKey.toString()) + .isEqualTo(supplyKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.feeScheduleKey.toString()) + .isEqualTo(feeScheduleKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.metadataKey.toString()) + .isEqualTo(metadataKey.getPublicKey().toString()); + + // Update all of token’s lower-privilege keys + // (trying to update keys one by one to check all errors), + // signing ONLY with an old respective lower-privilege key, + // and setting the key verification mode to FULL_VALIDATION + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setWipeKey(newWipeKey) + .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) + .freezeWith(testEnv.client) + .sign(wipeKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKycKey(newKycKey) + .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) + .freezeWith(testEnv.client) + .sign(kycKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setFreezeKey(newFreezeKey) + .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) + .freezeWith(testEnv.client) + .sign(freezeKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setPauseKey(newPauseKey) + .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) + .freezeWith(testEnv.client) + .sign(pauseKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setSupplyKey(newSupplyKey) + .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) + .freezeWith(testEnv.client) + .sign(supplyKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setFeeScheduleKey(newFeeScheduleKey) + .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) + .freezeWith(testEnv.client) + .sign(feeScheduleKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setMetadataKey(newMetadataKey) + .setKeyVerificationMode(TokenKeyValidation.FULL_VALIDATION) + .freezeWith(testEnv.client) + .sign(metadataKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + } + } + + /** + * @notice E2E-HIP-540 + * @url https://hips.hedera.com/hip/hip-540 + */ + @Test + @DisplayName( + "Cannot update all of token’s lower-privilege keys when updating them to a keys with an invalid structure and signing with an old respective lower-privilege and setting key verification mode to NO_VALIDATION") + void + cannotUpdateAllLowerPrivilegeKeysWhenUpdatingKeysToStructurallyInvalidKeysSigningOnlyWithOldRespectiveLowerPrivilegeKeyWithKeyVerificationSetToNoValidation() + throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + // Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata keys + var wipeKey = PrivateKey.generateED25519(); + var kycKey = PrivateKey.generateED25519(); + var freezeKey = PrivateKey.generateED25519(); + var pauseKey = PrivateKey.generateED25519(); + var supplyKey = PrivateKey.generateED25519(); + var feeScheduleKey = PrivateKey.generateED25519(); + var metadataKey = PrivateKey.generateED25519(); + + // create a non-fungible token + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("Test NFT") + .setTokenSymbol("TNFT") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setWipeKey(wipeKey.getPublicKey()) + .setKycKey(kycKey.getPublicKey()) + .setFreezeKey(freezeKey.getPublicKey()) + .setPauseKey(pauseKey.getPublicKey()) + .setSupplyKey(supplyKey.getPublicKey()) + .setFeeScheduleKey(feeScheduleKey.getPublicKey()) + .setMetadataKey(metadataKey.getPublicKey()) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var tokenInfoBeforeUpdate = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfoBeforeUpdate.wipeKey.toString()) + .isEqualTo(wipeKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.kycKey.toString()) + .isEqualTo(kycKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.freezeKey.toString()) + .isEqualTo(freezeKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.pauseKey.toString()) + .isEqualTo(pauseKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.supplyKey.toString()) + .isEqualTo(supplyKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.feeScheduleKey.toString()) + .isEqualTo(feeScheduleKey.getPublicKey().toString()); + assertThat(tokenInfoBeforeUpdate.metadataKey.toString()) + .isEqualTo(metadataKey.getPublicKey().toString()); + + // invalid ecdsa key + var ecdsaKey = PublicKey.fromBytesECDSA(new byte[33]); + + // update all of token’s lower-privilege keys + // to a structurally invalid key (trying to update keys one by one to check all errors), + // signing with an old respective lower-privilege + // and setting key verification mode to NO_VALIDATION + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setWipeKey(ecdsaKey) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .freezeWith(testEnv.client) + .sign(wipeKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_WIPE_KEY.toString()); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKycKey(ecdsaKey) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .freezeWith(testEnv.client) + .sign(kycKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_KYC_KEY.toString()); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setFreezeKey(ecdsaKey) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .freezeWith(testEnv.client) + .sign(freezeKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_FREEZE_KEY.toString()); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setPauseKey(ecdsaKey) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .freezeWith(testEnv.client) + .sign(pauseKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_PAUSE_KEY.toString()); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setSupplyKey(ecdsaKey) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .freezeWith(testEnv.client) + .sign(supplyKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SUPPLY_KEY.toString()); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setFeeScheduleKey(ecdsaKey) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .freezeWith(testEnv.client) + .sign(feeScheduleKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_CUSTOM_FEE_SCHEDULE_KEY.toString()); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenUpdateTransaction() + .setTokenId(tokenId) + .setMetadataKey(ecdsaKey) + .setKeyVerificationMode(TokenKeyValidation.NO_VALIDATION) + .freezeWith(testEnv.client) + .sign(metadataKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_METADATA_KEY.toString()); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenUpdateNftsIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenUpdateNftsIntegrationTest.java new file mode 100644 index 0000000000..15f4251667 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenUpdateNftsIntegrationTest.java @@ -0,0 +1,379 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.List; +import java.util.Objects; +import org.hiero.sdk.Client; +import org.hiero.sdk.NftId; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.sdk.Status; +import org.hiero.sdk.TokenCreateTransaction; +import org.hiero.sdk.TokenId; +import org.hiero.sdk.TokenInfoQuery; +import org.hiero.sdk.TokenMintTransaction; +import org.hiero.sdk.TokenNftInfoQuery; +import org.hiero.sdk.TokenType; +import org.hiero.sdk.TokenUpdateNftsTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +/** + * @notice E2E-HIP-657 + * @url https://hips.hedera.com/hip/hip-657 + */ +public class TokenUpdateNftsIntegrationTest { + + @Test + @DisplayName("Can update the metadata of the entire NFT collection") + void canUpdateNFTMetadataOfEntireCollection() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var metadataKey = PrivateKey.generateED25519(); + var nftCount = 4; + var initialMetadataList = NftMetadataGenerator.generate(new byte[] {4, 2, 0}, nftCount); + var updatedMetadata = new byte[] {6, 9}; + var updatedMetadataList = NftMetadataGenerator.generate(updatedMetadata, nftCount); + + // create a token with metadata key + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setMetadataKey(metadataKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + // mint tokens + var tokenMintTransactionReceipt = new TokenMintTransaction() + .setMetadata(initialMetadataList) + .setTokenId(tokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // check that metadata was set correctly + var nftSerials = tokenMintTransactionReceipt.serials; + List metadataListAfterMint = getMetadataList(testEnv.client, tokenId, nftSerials); + + assertThat(metadataListAfterMint.toArray()).isEqualTo(initialMetadataList.toArray()); + + // update metadata all minted NFTs + new TokenUpdateNftsTransaction() + .setTokenId(tokenId) + .setSerials(nftSerials) + .setMetadata(updatedMetadata) + .freezeWith(testEnv.client) + .sign(metadataKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // check updated NFTs' metadata + List metadataListAfterUpdate = getMetadataList(testEnv.client, tokenId, nftSerials); + assertThat(metadataListAfterUpdate.toArray()).isEqualTo(updatedMetadataList.toArray()); + } + } + + @Test + @DisplayName("Can update the metadata of a part of the NFT collection") + void canUpdateNFTMetadataOfPartOfCollection() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var metadataKey = PrivateKey.generateED25519(); + var nftCount = 4; + var initialMetadataList = NftMetadataGenerator.generate(new byte[] {4, 2, 0}, nftCount); + var updatedMetadata = new byte[] {6, 9}; + var updatedMetadataList = NftMetadataGenerator.generate(updatedMetadata, nftCount / 2); + + // create a token with metadata key + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setMetadataKey(metadataKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + // mint tokens + var tokenMintTransactionReceipt = new TokenMintTransaction() + .setMetadata(initialMetadataList) + .setTokenId(tokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // check that metadata was set correctly + var nftSerials = tokenMintTransactionReceipt.serials; + List metadataListAfterMint = getMetadataList(testEnv.client, tokenId, nftSerials); + + assertThat(metadataListAfterMint.toArray()).isEqualTo(initialMetadataList.toArray()); + + // update metadata of the first two minted NFTs + var nftSerialsToUpdate = nftSerials.subList(0, nftCount / 2); + + new TokenUpdateNftsTransaction() + .setTokenId(tokenId) + .setSerials(nftSerialsToUpdate) + .setMetadata(updatedMetadata) + .freezeWith(testEnv.client) + .sign(metadataKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // check updated NFTs' metadata + List metadataListAfterUpdate = getMetadataList(testEnv.client, tokenId, nftSerialsToUpdate); + + assertThat(metadataListAfterUpdate.toArray()).isEqualTo(updatedMetadataList.toArray()); + + // check that remaining NFTs were not updated + var nftSerialsSame = nftSerials.subList(nftCount / 2, nftCount); + List metadataList = getMetadataList(testEnv.client, tokenId, nftSerialsSame); + + assertThat(metadataList.toArray()) + .isEqualTo( + initialMetadataList.subList(nftCount / 2, nftCount).toArray()); + } + } + + @Test + @DisplayName("Cannot update NFTs metadata when it is not set") + void cannotUpdateNFTMetadataWhenItsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var metadataKey = PrivateKey.generateED25519(); + var nftCount = 4; + var initialMetadataList = NftMetadataGenerator.generate(new byte[] {4, 2, 0}, nftCount); + + // create a token with metadata key + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setMetadataKey(metadataKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + // mint tokens + var tokenMintTransactionReceipt = new TokenMintTransaction() + .setMetadata(initialMetadataList) + .setTokenId(tokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // check that metadata was set correctly + var nftSerials = tokenMintTransactionReceipt.serials; + List metadataListAfterMint = getMetadataList(testEnv.client, tokenId, nftSerials); + + assertThat(metadataListAfterMint.toArray()).isEqualTo(initialMetadataList.toArray()); + + // run `TokenUpdateNftsTransaction` without `setMetadata` + new TokenUpdateNftsTransaction() + .setTokenId(tokenId) + .setSerials(nftSerials) + .freezeWith(testEnv.client) + .sign(metadataKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // check that NFTs' metadata was not updated + List metadataListAfterUpdate = getMetadataList(testEnv.client, tokenId, nftSerials); + assertThat(metadataListAfterUpdate.toArray()).isEqualTo(initialMetadataList.toArray()); + } + } + + @Test + @DisplayName("Can erase NFTs metadata") + void canEraseNFTsMetadata() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var metadataKey = PrivateKey.generateED25519(); + var nftCount = 4; + var initialMetadataList = NftMetadataGenerator.generate(new byte[] {4, 2, 0}, nftCount); + var emptyMetadata = new byte[] {}; + var emptyMetadataList = NftMetadataGenerator.generate(emptyMetadata, nftCount); + + // create a token with metadata key + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setMetadataKey(metadataKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + // mint tokens + var tokenMintTransactionReceipt = new TokenMintTransaction() + .setMetadata(initialMetadataList) + .setTokenId(tokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // check that metadata was set correctly + var nftSerials = tokenMintTransactionReceipt.serials; + List metadataListAfterMint = getMetadataList(testEnv.client, tokenId, nftSerials); + + assertThat(metadataListAfterMint.toArray()).isEqualTo(initialMetadataList.toArray()); + + // erase metadata all minted NFTs (update to an empty byte array) + new TokenUpdateNftsTransaction() + .setTokenId(tokenId) + .setSerials(nftSerials) + .setMetadata(emptyMetadata) + .freezeWith(testEnv.client) + .sign(metadataKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + // check that NFTs' metadata was erased + List metadataListAfterUpdate = getMetadataList(testEnv.client, tokenId, nftSerials); + assertThat(metadataListAfterUpdate.toArray()).isEqualTo(emptyMetadataList.toArray()); + } + } + + @Test + @DisplayName("Cannot update NFT metadata when transaction is not signed with metadata key") + void cannotUpdateNFTMetadataWhenTransactionIsNotSignedWithMetadataKey() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var supplyKey = PrivateKey.generateED25519(); + var metadataKey = PrivateKey.generateED25519(); + var nftCount = 4; + var initialMetadataList = NftMetadataGenerator.generate(new byte[] {4, 2, 0}, nftCount); + var updatedMetadata = new byte[] {6, 9}; + + // create a token with a metadata key and check it + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setSupplyKey(supplyKey) + .setMetadataKey(metadataKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var tokenInfo = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfo.metadataKey.toString()) + .isEqualTo(metadataKey.getPublicKey().toString()); + + // mint tokens + var tokenMintTransactionReceipt = new TokenMintTransaction() + .setMetadata(initialMetadataList) + .setTokenId(tokenId) + .freezeWith(testEnv.client) + .sign(supplyKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var nftSerials = tokenMintTransactionReceipt.serials; + + // update nfts without signing + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateNftsTransaction() + .setTokenId(tokenId) + .setSerials(nftSerials) + .setMetadata(updatedMetadata) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + } + } + + @Test + @DisplayName("Cannot update NFT metadata when metadata key is not set") + void cannotUpdateNFTMetadataWhenMetadataKeyNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var supplyKey = PrivateKey.generateED25519(); + var metadataKey = PrivateKey.generateED25519(); + var nftCount = 4; + var initialMetadataList = NftMetadataGenerator.generate(new byte[] {4, 2, 0}, nftCount); + var updatedMetadata = new byte[] {6, 9}; + + // create a token without a metadata key and check it + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setSupplyKey(supplyKey) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var tokenInfo = new TokenInfoQuery().setTokenId(tokenId).execute(testEnv.client); + + assertThat(tokenInfo.metadataKey).isNull(); + + // mint tokens + var tokenMintTransactionReceipt = new TokenMintTransaction() + .setMetadata(initialMetadataList) + .setTokenId(tokenId) + .freezeWith(testEnv.client) + .sign(supplyKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var nftSerials = tokenMintTransactionReceipt.serials; + + // check NFTs' metadata can't be updated when a metadata key is not set + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenUpdateNftsTransaction() + .setTokenId(tokenId) + .setSerials(nftSerials) + .setMetadata(updatedMetadata) + .freezeWith(testEnv.client) + .sign(metadataKey) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_SIGNATURE.toString()); + } + } + + /** + * Retrieves the metadata information for a given list of NFT serials associated with a token. + * + * @param client The Hedera client used for executing the query. + * @param tokenId The ID of the token. + * @param nftSerials The list of serial numbers of the NFTs. + * @return A list of byte arrays representing the metadata information for the NFTs. + */ + private List getMetadataList(Client client, TokenId tokenId, List nftSerials) { + return nftSerials.stream() + .map(serial -> new NftId(tokenId, serial)) + .flatMap(nftId -> { + try { + return new TokenNftInfoQuery().setNftId(nftId).execute(client).stream(); + } catch (Exception e) { + throw new RuntimeException(e); + } + }) + .map(tokenNftInfo -> tokenNftInfo.metadata) + .toList(); + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenWipeIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenWipeIntegrationTest.java new file mode 100644 index 0000000000..f802a9312e --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TokenWipeIntegrationTest.java @@ -0,0 +1,398 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Collections; +import java.util.Objects; +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.sdk.Status; +import org.hiero.sdk.TokenAssociateTransaction; +import org.hiero.sdk.TokenCreateTransaction; +import org.hiero.sdk.TokenGrantKycTransaction; +import org.hiero.sdk.TokenMintTransaction; +import org.hiero.sdk.TokenType; +import org.hiero.sdk.TokenWipeTransaction; +import org.hiero.sdk.TransferTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class TokenWipeIntegrationTest { + @Test + @DisplayName("Can wipe accounts balance") + void canWipeAccountsBalance() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + new TokenAssociateTransaction() + .setAccountId(accountId) + .setTokenIds(Collections.singletonList(tokenId)) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenGrantKycTransaction() + .setAccountId(accountId) + .setTokenId(tokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TransferTransaction() + .addTokenTransfer(tokenId, testEnv.operatorId, -10) + .addTokenTransfer(tokenId, accountId, 10) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenWipeTransaction() + .setTokenId(tokenId) + .setAccountId(accountId) + .setAmount(10) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can wipe accounts NFTs") + void canWipeAccountsNfts() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var mintReceipt = new TokenMintTransaction() + .setTokenId(tokenId) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenAssociateTransaction() + .setAccountId(accountId) + .setTokenIds(Collections.singletonList(tokenId)) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenGrantKycTransaction() + .setAccountId(accountId) + .setTokenId(tokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var serialsToTransfer = mintReceipt.serials.subList(0, 4); + var transfer = new TransferTransaction(); + for (var serial : serialsToTransfer) { + transfer.addNftTransfer(tokenId.nft(serial), testEnv.operatorId, accountId); + } + transfer.execute(testEnv.client).getReceipt(testEnv.client); + + new TokenWipeTransaction() + .setTokenId(tokenId) + .setAccountId(accountId) + .setSerials(serialsToTransfer) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot wipe accounts NFTs if the account doesn't own them") + void cannotWipeAccountsNftsIfNotOwned() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + var mintReceipt = new TokenMintTransaction() + .setTokenId(tokenId) + .setMetadata(NftMetadataGenerator.generate((byte) 10)) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenAssociateTransaction() + .setAccountId(accountId) + .setTokenIds(Collections.singletonList(tokenId)) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenGrantKycTransaction() + .setAccountId(accountId) + .setTokenId(tokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var serialsToTransfer = mintReceipt.serials.subList(0, 4); + // don't transfer them + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TokenWipeTransaction() + .setTokenId(tokenId) + .setAccountId(accountId) + .setSerials(serialsToTransfer) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.ACCOUNT_DOES_NOT_OWN_WIPED_NFT.toString()); + } + } + + @Test + @DisplayName("Cannot wipe accounts balance when account ID is not set") + void cannotWipeAccountsBalanceWhenAccountIDIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + new TokenAssociateTransaction() + .setAccountId(accountId) + .setTokenIds(Collections.singletonList(tokenId)) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenGrantKycTransaction() + .setAccountId(accountId) + .setTokenId(tokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TransferTransaction() + .addTokenTransfer(tokenId, testEnv.operatorId, -10) + .addTokenTransfer(tokenId, accountId, 10) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenWipeTransaction() + .setTokenId(tokenId) + .setAmount(10) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_ACCOUNT_ID.toString()); + } + } + + @Test + @DisplayName("Cannot wipe accounts balance when token ID is not set") + void cannotWipeAccountsBalanceWhenTokenIDIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + new TokenAssociateTransaction() + .setAccountId(accountId) + .setTokenIds(Collections.singletonList(tokenId)) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenGrantKycTransaction() + .setAccountId(accountId) + .setTokenId(tokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TransferTransaction() + .addTokenTransfer(tokenId, testEnv.operatorId, -10) + .addTokenTransfer(tokenId, accountId, 10) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TokenWipeTransaction() + .setAccountId(accountId) + .setAmount(10) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_TOKEN_ID.toString()); + } + } + + @Test + @DisplayName("Can wipe accounts balance when amount is not set") + void canWipeAccountsBalanceWhenAmountIsNotSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount()) { + + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(1)) + .execute(testEnv.client); + + var accountId = Objects.requireNonNull(response.getReceipt(testEnv.client).accountId); + + var tokenId = Objects.requireNonNull(new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(testEnv.operatorId) + .setAdminKey(testEnv.operatorKey) + .setFreezeKey(testEnv.operatorKey) + .setWipeKey(testEnv.operatorKey) + .setKycKey(testEnv.operatorKey) + .setSupplyKey(testEnv.operatorKey) + .setFreezeDefault(false) + .execute(testEnv.client) + .getReceipt(testEnv.client) + .tokenId); + + new TokenAssociateTransaction() + .setAccountId(accountId) + .setTokenIds(Collections.singletonList(tokenId)) + .freezeWith(testEnv.client) + .sign(key) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TokenGrantKycTransaction() + .setAccountId(accountId) + .setTokenId(tokenId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + new TransferTransaction() + .addTokenTransfer(tokenId, testEnv.operatorId, -10) + .addTokenTransfer(tokenId, accountId, 10) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var receipt = new TokenWipeTransaction() + .setTokenId(tokenId) + .setAccountId(accountId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + assertThat(receipt.status).isEqualTo(Status.SUCCESS); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TopicCreateIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TopicCreateIntegrationTest.java new file mode 100644 index 0000000000..6ac12eec8b --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TopicCreateIntegrationTest.java @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Objects; +import org.hiero.sdk.TopicCreateTransaction; +import org.hiero.sdk.TopicDeleteTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class TopicCreateIntegrationTest { + @Test + @DisplayName("Can create topic") + void canCreateTopic() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new TopicCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setTopicMemo("[e2e::TopicCreateTransaction]") + .execute(testEnv.client); + + var topicId = Objects.requireNonNull(response.getReceipt(testEnv.client).topicId); + + new TopicDeleteTransaction() + .setTopicId(topicId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can create topic with no field set") + void canCreateTopicWithNoFieldsSet() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new TopicCreateTransaction().execute(testEnv.client); + assertThat(response.getReceipt(testEnv.client).topicId).isNotNull(); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TopicDeleteIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TopicDeleteIntegrationTest.java new file mode 100644 index 0000000000..1e90624d6e --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TopicDeleteIntegrationTest.java @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Objects; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.sdk.Status; +import org.hiero.sdk.TopicCreateTransaction; +import org.hiero.sdk.TopicDeleteTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class TopicDeleteIntegrationTest { + @Test + @DisplayName("Can delete topic") + void canDeleteTopic() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new TopicCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setTopicMemo("[e2e::TopicCreateTransaction]") + .execute(testEnv.client); + + var topicId = Objects.requireNonNull(response.getReceipt(testEnv.client).topicId); + + new TopicDeleteTransaction() + .setTopicId(topicId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Cannot delete immutable topic") + void cannotDeleteImmutableTopic() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new TopicCreateTransaction().execute(testEnv.client); + + var topicId = Objects.requireNonNull(response.getReceipt(testEnv.client).topicId); + + assertThatExceptionOfType(ReceiptStatusException.class) + .isThrownBy(() -> { + new TopicDeleteTransaction() + .setTopicId(topicId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.UNAUTHORIZED.toString()); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TopicInfoIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TopicInfoIntegrationTest.java new file mode 100644 index 0000000000..41ba5d6ae5 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TopicInfoIntegrationTest.java @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Objects; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.MaxQueryPaymentExceededException; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.TopicCreateTransaction; +import org.hiero.sdk.TopicDeleteTransaction; +import org.hiero.sdk.TopicInfoQuery; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class TopicInfoIntegrationTest { + + @Test + @DisplayName("Can query topic info") + void canQueryTopicInfo() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new TopicCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setTopicMemo("[e2e::TopicCreateTransaction]") + .execute(testEnv.client); + + var topicId = Objects.requireNonNull(response.getReceipt(testEnv.client).topicId); + + var info = new TopicInfoQuery().setTopicId(topicId).execute(testEnv.client); + + assertThat(info.topicMemo).isEqualTo("[e2e::TopicCreateTransaction]"); + + new TopicDeleteTransaction() + .setTopicId(topicId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can get cost for topic info query") + void getCostQueryTopicInfo() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new TopicCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setTopicMemo("[e2e::TopicCreateTransaction]") + .execute(testEnv.client); + + var topicId = Objects.requireNonNull(response.getReceipt(testEnv.client).topicId); + + var infoQuery = new TopicInfoQuery().setTopicId(topicId); + + var cost = infoQuery.getCost(testEnv.client); + + assertThat(cost).isNotNull(); + + var info = infoQuery.execute(testEnv.client); + + assertThat(info.topicMemo).isEqualTo("[e2e::TopicCreateTransaction]"); + + new TopicDeleteTransaction() + .setTopicId(topicId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can get cost for topic info query") + void getCostBigMaxQueryTopicInfo() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new TopicCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setTopicMemo("[e2e::TopicCreateTransaction]") + .execute(testEnv.client); + + var topicId = Objects.requireNonNull(response.getReceipt(testEnv.client).topicId); + + var infoQuery = new TopicInfoQuery().setTopicId(topicId).setMaxQueryPayment(new Hbar(1000)); + + var cost = infoQuery.getCost(testEnv.client); + + assertThat(cost).isNotNull(); + + var info = infoQuery.execute(testEnv.client); + + assertThat(info.topicMemo).isEqualTo("[e2e::TopicCreateTransaction]"); + + new TopicDeleteTransaction() + .setTopicId(topicId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can get cost for topic info query") + void getCostSmallMaxQueryTopicInfo() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new TopicCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setTopicMemo("[e2e::TopicCreateTransaction]") + .execute(testEnv.client); + + var topicId = Objects.requireNonNull(response.getReceipt(testEnv.client).topicId); + + var infoQuery = new TopicInfoQuery().setTopicId(topicId).setMaxQueryPayment(Hbar.fromTinybars(1)); + + assertThatExceptionOfType(MaxQueryPaymentExceededException.class).isThrownBy(() -> { + infoQuery.execute(testEnv.client); + }); + + new TopicDeleteTransaction() + .setTopicId(topicId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can get cost for topic info query") + void getCostInsufficientTxFeeQueryTopicInfo() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new TopicCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setTopicMemo("[e2e::TopicCreateTransaction]") + .execute(testEnv.client); + + var topicId = Objects.requireNonNull(response.getReceipt(testEnv.client).topicId); + + var infoQuery = new TopicInfoQuery().setTopicId(topicId); + + var cost = infoQuery.getCost(testEnv.client); + + assertThat(cost).isNotNull(); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + infoQuery.setQueryPayment(Hbar.fromTinybars(1)).execute(testEnv.client); + }) + .satisfies(error -> assertThat(error.status.toString()).isEqualTo("INSUFFICIENT_TX_FEE")); + + new TopicDeleteTransaction() + .setTopicId(topicId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TopicMessageIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TopicMessageIntegrationTest.java new file mode 100644 index 0000000000..eaabe3ff29 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TopicMessageIntegrationTest.java @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.time.Instant; +import java.util.Objects; +import org.hiero.sdk.*; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class TopicMessageIntegrationTest { + @Test + @DisplayName("Can receive a topic message") + void canReceiveATopicMessage() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new TopicCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setTopicMemo("[e2e::TopicCreateTransaction]") + .execute(testEnv.client); + + var topicId = Objects.requireNonNull(response.getReceipt(testEnv.client).topicId); + + var info = new TopicInfoQuery().setTopicId(topicId).execute(testEnv.client); + + assertThat(info.topicId).isEqualTo(topicId); + assertThat(info.topicMemo).isEqualTo("[e2e::TopicCreateTransaction]"); + assertThat(info.sequenceNumber).isEqualTo(0); + assertThat(info.adminKey).isEqualTo(testEnv.operatorKey); + + Thread.sleep(3000); + + var receivedMessage = new boolean[] {false}; + var start = Instant.now(); + + var handle = new TopicMessageQuery() + .setTopicId(topicId) + .setStartTime(Instant.EPOCH) + .subscribe(testEnv.client, (message) -> { + receivedMessage[0] = + new String(message.contents, StandardCharsets.UTF_8).equals("Hello, from HCS!"); + }); + + Thread.sleep(3000); + + new TopicMessageSubmitTransaction() + .setTopicId(topicId) + .setMessage("Hello, from HCS!") + .execute(testEnv.client) + .getReceipt(testEnv.client); + + while (!receivedMessage[0]) { + if (Duration.between(start, Instant.now()).compareTo(Duration.ofSeconds(60)) > 0) { + throw new Exception("TopicMessage was not received in 60 seconds or less"); + } + + Thread.sleep(2000); + } + + new TopicDeleteTransaction() + .setTopicId(topicId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can receive a large topic message") + void canReceiveALargeTopicMessage() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + // Skip if using local node. + // Note: this check should be removed once the local node is supporting multiple nodes. + testEnv.assumeNotLocalNode(); + + var response = new TopicCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setTopicMemo("[e2e::TopicCreateTransaction]") + .execute(testEnv.client); + + var topicId = Objects.requireNonNull(response.getReceipt(testEnv.client).topicId); + + Thread.sleep(5000); + + var info = new TopicInfoQuery().setTopicId(topicId).execute(testEnv.client); + + assertThat(info.topicId).isEqualTo(topicId); + assertThat(info.topicMemo).isEqualTo("[e2e::TopicCreateTransaction]"); + assertThat(info.sequenceNumber).isEqualTo(0); + assertThat(info.adminKey).isEqualTo(testEnv.operatorKey); + + var receivedMessage = new boolean[] {false}; + var start = Instant.now(); + + var handle = new TopicMessageQuery() + .setTopicId(topicId) + .setStartTime(Instant.EPOCH) + .subscribe(testEnv.client, (message) -> { + receivedMessage[0] = + new String(message.contents, StandardCharsets.UTF_8).equals(Contents.BIG_CONTENTS); + }); + + new TopicMessageSubmitTransaction() + .setTopicId(topicId) + .setMessage(Contents.BIG_CONTENTS) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + while (!receivedMessage[0]) { + if (Duration.between(start, Instant.now()).compareTo(Duration.ofSeconds(60)) > 0) { + throw new Exception("TopicMessage was not received in 60 seconds or less"); + } + + Thread.sleep(1000); + } + + new TopicDeleteTransaction() + .setTopicId(topicId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TopicMessageSubmitIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TopicMessageSubmitIntegrationTest.java new file mode 100644 index 0000000000..43612a0378 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TopicMessageSubmitIntegrationTest.java @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatNoException; + +import java.util.Objects; +import org.bouncycastle.util.encoders.Hex; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.Status; +import org.hiero.sdk.TopicCreateTransaction; +import org.hiero.sdk.TopicDeleteTransaction; +import org.hiero.sdk.TopicInfoQuery; +import org.hiero.sdk.TopicMessageSubmitTransaction; +import org.hiero.sdk.Transaction; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class TopicMessageSubmitIntegrationTest { + @Test + @DisplayName("Can submit a topic message") + void canSubmitATopicMessage() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new TopicCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setTopicMemo("[e2e::TopicCreateTransaction]") + .execute(testEnv.client); + + var topicId = Objects.requireNonNull(response.getReceipt(testEnv.client).topicId); + + var info = new TopicInfoQuery().setTopicId(topicId).execute(testEnv.client); + + assertThat(info.topicId).isEqualTo(topicId); + assertThat(info.topicMemo).isEqualTo("[e2e::TopicCreateTransaction]"); + assertThat(info.sequenceNumber).isEqualTo(0); + assertThat(info.adminKey).isEqualTo(testEnv.operatorKey); + + new TopicMessageSubmitTransaction() + .setTopicId(topicId) + .setMessage("Hello, from HCS!") + .execute(testEnv.client) + .getReceipt(testEnv.client); + + info = new TopicInfoQuery().setTopicId(topicId).execute(testEnv.client); + + assertThat(info.topicId).isEqualTo(topicId); + assertThat(info.topicMemo).isEqualTo("[e2e::TopicCreateTransaction]"); + assertThat(info.sequenceNumber).isEqualTo(1); + assertThat(info.adminKey).isEqualTo(testEnv.operatorKey); + + new TopicDeleteTransaction() + .setTopicId(topicId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } + + @Test + @DisplayName("Can submit a large topic message") + void canSubmitALargeTopicMessage() { + // Skip if using PreviewNet + Assumptions.assumeTrue(!System.getProperty("HEDERA_NETWORK").equals("previewnet")); + + assertThatNoException().isThrownBy(() -> { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new TopicCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setTopicMemo("[e2e::TopicCreateTransaction]") + .execute(testEnv.client); + + var topicId = Objects.requireNonNull(response.getReceipt(testEnv.client).topicId); + + Thread.sleep(5000); + + var info = new TopicInfoQuery().setTopicId(topicId).execute(testEnv.client); + + assertThat(info.topicId).isEqualTo(topicId); + assertThat(info.topicMemo).isEqualTo("[e2e::TopicCreateTransaction]"); + assertThat(info.sequenceNumber).isEqualTo(0); + assertThat(info.adminKey).isEqualTo(testEnv.operatorKey); + + var responses = new TopicMessageSubmitTransaction() + .setTopicId(topicId) + .setMaxChunks(15) + .setMessage(Contents.BIG_CONTENTS) + .executeAll(testEnv.client); + + for (var resp : responses) { + resp.getReceipt(testEnv.client); + } + + info = new TopicInfoQuery().setTopicId(topicId).execute(testEnv.client); + + assertThat(info.topicId).isEqualTo(topicId); + assertThat(info.topicMemo).isEqualTo("[e2e::TopicCreateTransaction]"); + assertThat(info.sequenceNumber).isEqualTo(14); + assertThat(info.adminKey).isEqualTo(testEnv.operatorKey); + + new TopicDeleteTransaction() + .setTopicId(topicId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + }); + } + + @Test + @DisplayName("Cannot submit message when topic ID is not set") + void cannotSubmitMessageWhenTopicIDIsNotSet() { + // Skip if using PreviewNet + Assumptions.assumeTrue(!System.getProperty("HEDERA_NETWORK").equals("previewnet")); + + assertThatNoException().isThrownBy(() -> { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new TopicCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setTopicMemo("[e2e::TopicCreateTransaction]") + .execute(testEnv.client); + + var topicId = Objects.requireNonNull(response.getReceipt(testEnv.client).topicId); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TopicMessageSubmitTransaction() + .setMessage(Contents.BIG_CONTENTS) + .setMaxChunks(15) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_TOPIC_ID.toString()); + + new TopicDeleteTransaction() + .setTopicId(topicId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + }); + } + + @Test + @DisplayName("Cannot submit message when message is not set") + void cannotSubmitMessageWhenMessageIsNotSet() { + // Skip if using PreviewNet + Assumptions.assumeTrue(!System.getProperty("HEDERA_NETWORK").equals("previewnet")); + + assertThatNoException().isThrownBy(() -> { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new TopicCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setTopicMemo("[e2e::TopicCreateTransaction]") + .execute(testEnv.client); + + var topicId = Objects.requireNonNull(response.getReceipt(testEnv.client).topicId); + + assertThatExceptionOfType(PrecheckStatusException.class) + .isThrownBy(() -> { + new TopicMessageSubmitTransaction() + .setTopicId(topicId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + }) + .withMessageContaining(Status.INVALID_TOPIC_MESSAGE.toString()); + + new TopicDeleteTransaction() + .setTopicId(topicId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + }); + } + + @Test + @DisplayName("Hex Decode Regression Test") + @SuppressWarnings("UnusedVariable") + void decodeHexRegressionTest() throws Exception { + String binaryHex = + "2ac2010a580a130a0b08d38f8f880610a09be91512041899e11c120218041880c2d72f22020878da01330a0418a5a1201210303030303030313632373633373731351a190a130a0b08d38f8f880610a09be91512041899e11c1001180112660a640a20603edaec5d1c974c92cb5bee7b011310c3b84b13dc048424cd6ef146d6a0d4a41a40b6a08f310ee29923e5868aac074468b2bde05da95a806e2f4a4f452177f129ca0abae7831e595b5beaa1c947e2cb71201642bab33fece5184b04547afc40850a"; + byte[] transactionBytes = Hex.decode(binaryHex); + + var transaction = Objects.requireNonNull(Transaction.fromBytes(transactionBytes)); + + String idString = Objects.requireNonNull(transaction.getTransactionId()).toString(); + String transactionString = transaction.toString(); + } +} diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TopicUpdateIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TopicUpdateIntegrationTest.java new file mode 100644 index 0000000000..53b3d6f3c0 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TopicUpdateIntegrationTest.java @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Objects; +import org.hiero.sdk.TopicCreateTransaction; +import org.hiero.sdk.TopicDeleteTransaction; +import org.hiero.sdk.TopicInfoQuery; +import org.hiero.sdk.TopicUpdateTransaction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class TopicUpdateIntegrationTest { + @Test + @DisplayName("Can update topic") + void canUpdateTopic() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var response = new TopicCreateTransaction() + .setAdminKey(testEnv.operatorKey) + .setAutoRenewAccountId(testEnv.operatorId) + .setTopicMemo("[e2e::TopicCreateTransaction]") + .execute(testEnv.client); + + var topicId = Objects.requireNonNull(response.getReceipt(testEnv.client).topicId); + + new TopicUpdateTransaction() + .clearAutoRenewAccountId() + .setTopicMemo("hello") + .setTopicId(topicId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + + var topicInfo = new TopicInfoQuery().setTopicId(topicId).execute(testEnv.client); + + assertThat(topicInfo.topicMemo).isEqualTo("hello"); + assertThat(topicInfo.autoRenewAccountId).isNull(); + + new TopicDeleteTransaction() + .setTopicId(topicId) + .execute(testEnv.client) + .getReceipt(testEnv.client); + } + } +} diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TransactionIntegrationTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TransactionIntegrationTest.java similarity index 76% rename from sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TransactionIntegrationTest.java rename to sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TransactionIntegrationTest.java index ba175b5745..fef283dba0 100644 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/TransactionIntegrationTest.java +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TransactionIntegrationTest.java @@ -1,66 +1,47 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.sdk.test.integration; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatNoException; import com.google.protobuf.ByteString; -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.AccountDeleteTransaction; -import com.hedera.hashgraph.sdk.AccountId; -import com.hedera.hashgraph.sdk.FileAppendTransaction; -import com.hedera.hashgraph.sdk.FileContentsQuery; -import com.hedera.hashgraph.sdk.FileCreateTransaction; -import com.hedera.hashgraph.sdk.FileDeleteTransaction; -import com.hedera.hashgraph.sdk.FileInfoQuery; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.KeyList; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.TopicCreateTransaction; -import com.hedera.hashgraph.sdk.TopicDeleteTransaction; -import com.hedera.hashgraph.sdk.TopicInfoQuery; -import com.hedera.hashgraph.sdk.TopicMessageSubmitTransaction; -import com.hedera.hashgraph.sdk.Transaction; -import com.hedera.hashgraph.sdk.TransactionId; -import com.hedera.hashgraph.sdk.TransferTransaction; -import com.hedera.hashgraph.sdk.proto.AccountAmount; -import com.hedera.hashgraph.sdk.proto.AccountID; -import com.hedera.hashgraph.sdk.proto.CryptoTransferTransactionBody; -import com.hedera.hashgraph.sdk.proto.Duration; -import com.hedera.hashgraph.sdk.proto.SignatureMap; -import com.hedera.hashgraph.sdk.proto.SignaturePair; -import com.hedera.hashgraph.sdk.proto.SignedTransaction; -import com.hedera.hashgraph.sdk.proto.Timestamp; -import com.hedera.hashgraph.sdk.proto.TransactionBody; -import com.hedera.hashgraph.sdk.proto.TransactionID; -import com.hedera.hashgraph.sdk.proto.TransactionList; -import com.hedera.hashgraph.sdk.proto.TransferList; +import java.util.Arrays; import java.util.Objects; +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.AccountDeleteTransaction; +import org.hiero.sdk.AccountId; +import org.hiero.sdk.FileAppendTransaction; +import org.hiero.sdk.FileContentsQuery; +import org.hiero.sdk.FileCreateTransaction; +import org.hiero.sdk.FileDeleteTransaction; +import org.hiero.sdk.FileInfoQuery; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.KeyList; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.TopicCreateTransaction; +import org.hiero.sdk.TopicDeleteTransaction; +import org.hiero.sdk.TopicInfoQuery; +import org.hiero.sdk.TopicMessageSubmitTransaction; +import org.hiero.sdk.Transaction; +import org.hiero.sdk.TransactionId; +import org.hiero.sdk.TransferTransaction; +import org.hiero.sdk.proto.AccountAmount; +import org.hiero.sdk.proto.AccountID; +import org.hiero.sdk.proto.CryptoTransferTransactionBody; +import org.hiero.sdk.proto.Duration; +import org.hiero.sdk.proto.SignatureMap; +import org.hiero.sdk.proto.SignaturePair; +import org.hiero.sdk.proto.SignedTransaction; +import org.hiero.sdk.proto.Timestamp; +import org.hiero.sdk.proto.TransactionBody; +import org.hiero.sdk.proto.TransactionID; +import org.hiero.sdk.proto.TransactionList; +import org.hiero.sdk.proto.TransferList; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatNoException; - public class TransactionIntegrationTest { @Test @@ -71,9 +52,9 @@ void transactionHashInTransactionRecordIsEqualToTheDerivedTransactionHash() thro var key = PrivateKey.generateED25519(); var transaction = new AccountCreateTransaction() - .setKey(key) - .freezeWith(testEnv.client) - .signWithOperator(testEnv.client); + .setKey(key) + .freezeWith(testEnv.client) + .signWithOperator(testEnv.client); var expectedHash = transaction.getTransactionHashPerNode(); @@ -92,7 +73,6 @@ var record = response.getRecord(testEnv.client); assertThat(transactionId.getRecord(testEnv.client)).isNotNull(); assertThat(transactionId.getRecordAsync(testEnv.client).get()).isNotNull(); } - } /** @@ -107,22 +87,20 @@ void canSerializeDeserializeCompareFields() throws Exception { var adminKey = PrivateKey.generateECDSA(); var publicKey = adminKey.getPublicKey(); - var accountCreateTransaction = new AccountCreateTransaction() - .setKey(publicKey) - .setInitialBalance(new Hbar(1L)); + var accountCreateTransaction = + new AccountCreateTransaction().setKey(publicKey).setInitialBalance(new Hbar(1L)); var expectedNodeAccountIds = accountCreateTransaction.getNodeAccountIds(); var expectedBalance = new Hbar(1L); var transactionBytesSerialized = accountCreateTransaction.toBytes(); - AccountCreateTransaction accountCreateTransactionDeserialized = (AccountCreateTransaction) Transaction - .fromBytes(transactionBytesSerialized); + AccountCreateTransaction accountCreateTransactionDeserialized = + (AccountCreateTransaction) Transaction.fromBytes(transactionBytesSerialized); assertThat(expectedNodeAccountIds).isEqualTo(accountCreateTransactionDeserialized.getNodeAccountIds()); assertThat(expectedBalance).isEqualTo(accountCreateTransactionDeserialized.getInitialBalance()); - assertThatExceptionOfType(IllegalStateException.class).isThrownBy( - accountCreateTransactionDeserialized::getTransactionId); - + assertThatExceptionOfType(IllegalStateException.class) + .isThrownBy(accountCreateTransactionDeserialized::getTransactionId); } } @@ -131,7 +109,8 @@ void canSerializeDeserializeCompareFields() throws Exception { * @url https://hips.hedera.com/hip/hip-745 */ @Test - @DisplayName("incomplete transaction with node account ids can be serialized into bytes, deserialized and be equal to the original one") + @DisplayName( + "incomplete transaction with node account ids can be serialized into bytes, deserialized and be equal to the original one") void canSerializeWithNodeAccountIdsDeserializeCompareFields() throws Exception { try (var testEnv = new IntegrationTestEnv(1)) { @@ -149,15 +128,16 @@ void canSerializeWithNodeAccountIdsDeserializeCompareFields() throws Exception { var expectedBalance = new Hbar(1L); var transactionBytesSerialized = accountCreateTransaction.toBytes(); - AccountCreateTransaction accountCreateTransactionDeserialized = (AccountCreateTransaction) Transaction - .fromBytes(transactionBytesSerialized); + AccountCreateTransaction accountCreateTransactionDeserialized = + (AccountCreateTransaction) Transaction.fromBytes(transactionBytesSerialized); assertThat(expectedNodeAccountIds.size()) - .isEqualTo(accountCreateTransactionDeserialized.getNodeAccountIds().size()); + .isEqualTo(accountCreateTransactionDeserialized + .getNodeAccountIds() + .size()); assertThat(expectedBalance).isEqualTo(accountCreateTransactionDeserialized.getInitialBalance()); - assertThatExceptionOfType(IllegalStateException.class).isThrownBy( - accountCreateTransactionDeserialized::getTransactionId); - + assertThatExceptionOfType(IllegalStateException.class) + .isThrownBy(accountCreateTransactionDeserialized::getTransactionId); } } @@ -173,18 +153,15 @@ void canSerializeDeserializeAndExecuteIncompleteTransaction() throws Exception { var adminKey = PrivateKey.generateECDSA(); var publicKey = adminKey.getPublicKey(); - var accountCreateTransaction = new AccountCreateTransaction() - .setKey(publicKey) - .setInitialBalance(new Hbar(1L)); + var accountCreateTransaction = + new AccountCreateTransaction().setKey(publicKey).setInitialBalance(new Hbar(1L)); var transactionBytesSerialized = accountCreateTransaction.toBytes(); - AccountCreateTransaction accountCreateTransactionDeserialized = (AccountCreateTransaction) Transaction - .fromBytes( - transactionBytesSerialized); + AccountCreateTransaction accountCreateTransactionDeserialized = + (AccountCreateTransaction) Transaction.fromBytes(transactionBytesSerialized); - var txReceipt = accountCreateTransactionDeserialized - .execute(testEnv.client) - .getReceipt(testEnv.client); + var txReceipt = + accountCreateTransactionDeserialized.execute(testEnv.client).getReceipt(testEnv.client); new AccountDeleteTransaction() .setAccountId(txReceipt.accountId) @@ -192,7 +169,6 @@ void canSerializeDeserializeAndExecuteIncompleteTransaction() throws Exception { .freezeWith(testEnv.client) .sign(adminKey) .execute(testEnv.client); - } } @@ -216,13 +192,11 @@ void canSerializeDeserializeAndExecuteIncompleteTransactionWithNodeAccountIds() .setInitialBalance(new Hbar(1L)); var transactionBytesSerialized = accountCreateTransaction.toBytes(); - AccountCreateTransaction accountCreateTransactionDeserialized = (AccountCreateTransaction) Transaction - .fromBytes( - transactionBytesSerialized); + AccountCreateTransaction accountCreateTransactionDeserialized = + (AccountCreateTransaction) Transaction.fromBytes(transactionBytesSerialized); - var txReceipt = accountCreateTransactionDeserialized - .execute(testEnv.client) - .getReceipt(testEnv.client); + var txReceipt = + accountCreateTransactionDeserialized.execute(testEnv.client).getReceipt(testEnv.client); new AccountDeleteTransaction() .setAccountId(txReceipt.accountId) @@ -230,7 +204,6 @@ void canSerializeDeserializeAndExecuteIncompleteTransactionWithNodeAccountIds() .freezeWith(testEnv.client) .sign(adminKey) .execute(testEnv.client); - } } @@ -246,15 +219,14 @@ void canSerializeDeserializeEditExecuteCompareFields() throws Exception { var adminKey = PrivateKey.generateECDSA(); var publicKey = adminKey.getPublicKey(); - var accountCreateTransaction = new AccountCreateTransaction() - .setKey(publicKey); + var accountCreateTransaction = new AccountCreateTransaction().setKey(publicKey); var expectedBalance = new Hbar(1L); var nodeAccountIds = testEnv.client.getNetwork().values().stream().toList(); var transactionBytesSerialized = accountCreateTransaction.toBytes(); - AccountCreateTransaction accountCreateTransactionDeserialized = (AccountCreateTransaction) Transaction - .fromBytes(transactionBytesSerialized); + AccountCreateTransaction accountCreateTransactionDeserialized = + (AccountCreateTransaction) Transaction.fromBytes(transactionBytesSerialized); var txReceipt = accountCreateTransactionDeserialized .setInitialBalance(new Hbar(1L)) @@ -271,7 +243,6 @@ void canSerializeDeserializeEditExecuteCompareFields() throws Exception { .freezeWith(testEnv.client) .sign(adminKey) .execute(testEnv.client); - } } @@ -280,7 +251,8 @@ void canSerializeDeserializeEditExecuteCompareFields() throws Exception { * @url https://hips.hedera.com/hip/hip-745 */ @Test - @DisplayName("incomplete transaction with node account ids can be serialized into bytes, deserialized, edited and executed") + @DisplayName( + "incomplete transaction with node account ids can be serialized into bytes, deserialized, edited and executed") void canSerializeDeserializeEditExecuteCompareFieldsIncompleteTransactionWithNodeAccountIds() throws Exception { try (var testEnv = new IntegrationTestEnv(1)) { @@ -296,8 +268,8 @@ void canSerializeDeserializeEditExecuteCompareFieldsIncompleteTransactionWithNod var expectedBalance = new Hbar(1L); var transactionBytesSerialized = accountCreateTransaction.toBytes(); - AccountCreateTransaction accountCreateTransactionDeserialized = (AccountCreateTransaction) Transaction - .fromBytes(transactionBytesSerialized); + AccountCreateTransaction accountCreateTransactionDeserialized = + (AccountCreateTransaction) Transaction.fromBytes(transactionBytesSerialized); var txReceipt = accountCreateTransactionDeserialized .setInitialBalance(new Hbar(1L)) @@ -313,7 +285,6 @@ void canSerializeDeserializeEditExecuteCompareFieldsIncompleteTransactionWithNod .freezeWith(testEnv.client) .sign(adminKey) .execute(testEnv.client); - } } @@ -348,14 +319,14 @@ void canFreezeSignSerializeDeserializeReserializeAndExecute() throws Exception { .sign(adminKey); var transactionBytesSerialized = accountCreateTransaction.toBytes(); - AccountCreateTransaction accountCreateTransactionDeserialized = (AccountCreateTransaction) Transaction - .fromBytes(transactionBytesSerialized); + AccountCreateTransaction accountCreateTransactionDeserialized = + (AccountCreateTransaction) Transaction.fromBytes(transactionBytesSerialized); var transactionBytesReserialized = accountCreateTransactionDeserialized.toBytes(); assertThat(transactionBytesSerialized).isEqualTo(transactionBytesReserialized); - AccountCreateTransaction accountCreateTransactionReserialized = (AccountCreateTransaction) Transaction - .fromBytes(transactionBytesReserialized); + AccountCreateTransaction accountCreateTransactionReserialized = + (AccountCreateTransaction) Transaction.fromBytes(transactionBytesReserialized); var txResponse = accountCreateTransactionReserialized.execute(testEnv.client); @@ -367,7 +338,6 @@ void canFreezeSignSerializeDeserializeReserializeAndExecute() throws Exception { .freezeWith(testEnv.client) .sign(adminKey) .execute(testEnv.client); - } } @@ -405,10 +375,7 @@ var record = response.getRecord(testEnv.client); var deleteTransaction2 = Transaction.fromBytes(updateBytes); - deleteTransaction2 - .addSignature(key.getPublicKey(), sig1) - .execute(testEnv.client); - + deleteTransaction2.addSignature(key.getPublicKey(), sig1).execute(testEnv.client); } } @@ -417,7 +384,8 @@ var record = response.getRecord(testEnv.client); * @url https://hips.hedera.com/hip/hip-745 */ @Test - @DisplayName("file append chunked transaction can be frozen, signed, serialized into bytes, deserialized and be equal to the original one") + @DisplayName( + "file append chunked transaction can be frozen, signed, serialized into bytes, deserialized and be equal to the original one") void canFreezeSignSerializeDeserializeAndCompareFileAppendChunkedTransaction() throws Exception { try (var testEnv = new IntegrationTestEnv(1)) { @@ -432,9 +400,7 @@ void canFreezeSignSerializeDeserializeAndCompareFileAppendChunkedTransaction() t Thread.sleep(5000); - var info = new FileInfoQuery() - .setFileId(fileId) - .execute(testEnv.client); + var info = new FileInfoQuery().setFileId(fileId).execute(testEnv.client); assertThat(info.fileId).isEqualTo(fileId); assertThat(info.size).isEqualTo(28); @@ -450,12 +416,11 @@ void canFreezeSignSerializeDeserializeAndCompareFileAppendChunkedTransaction() t .sign(privateKey); var transactionBytesSerialized = fileAppendTransaction.toBytes(); - FileAppendTransaction fileAppendTransactionDeserialized = (FileAppendTransaction) Transaction - .fromBytes(transactionBytesSerialized); + FileAppendTransaction fileAppendTransactionDeserialized = + (FileAppendTransaction) Transaction.fromBytes(transactionBytesSerialized); var transactionBytesReserialized = fileAppendTransactionDeserialized.toBytes(); assertThat(transactionBytesSerialized).isEqualTo(transactionBytesReserialized); - } } @@ -464,7 +429,8 @@ void canFreezeSignSerializeDeserializeAndCompareFileAppendChunkedTransaction() t * @url https://hips.hedera.com/hip/hip-745 */ @Test - @DisplayName("incomplete file append chunked transaction can be serialized into bytes, deserialized, edited and executed") + @DisplayName( + "incomplete file append chunked transaction can be serialized into bytes, deserialized, edited and executed") void canSerializeDeserializeExecuteFileAppendChunkedTransaction() throws Exception { try (var testEnv = new IntegrationTestEnv(1)) { @@ -477,9 +443,7 @@ void canSerializeDeserializeExecuteFileAppendChunkedTransaction() throws Excepti Thread.sleep(5000); - var info = new FileInfoQuery() - .setFileId(fileId) - .execute(testEnv.client); + var info = new FileInfoQuery().setFileId(fileId).execute(testEnv.client); assertThat(info.fileId).isEqualTo(fileId); assertThat(info.size).isEqualTo(28); @@ -488,27 +452,20 @@ void canSerializeDeserializeExecuteFileAppendChunkedTransaction() throws Excepti assertThat(info.keys.getThreshold()).isNull(); assertThat(info.keys).isEqualTo(KeyList.of(testEnv.operatorKey)); - var fileAppendTransaction = new FileAppendTransaction() - .setFileId(fileId) - .setContents(Contents.BIG_CONTENTS); + var fileAppendTransaction = + new FileAppendTransaction().setFileId(fileId).setContents(Contents.BIG_CONTENTS); var transactionBytesSerialized = fileAppendTransaction.toBytes(); - FileAppendTransaction fileAppendTransactionDeserialized = (FileAppendTransaction) Transaction - .fromBytes(transactionBytesSerialized); + FileAppendTransaction fileAppendTransactionDeserialized = + (FileAppendTransaction) Transaction.fromBytes(transactionBytesSerialized); - fileAppendTransactionDeserialized - .execute(testEnv.client) - .getReceipt(testEnv.client); + fileAppendTransactionDeserialized.execute(testEnv.client).getReceipt(testEnv.client); - var contents = new FileContentsQuery() - .setFileId(fileId) - .execute(testEnv.client); + var contents = new FileContentsQuery().setFileId(fileId).execute(testEnv.client); assertThat(contents.toStringUtf8()).isEqualTo("[e2e::FileCreateTransaction]" + Contents.BIG_CONTENTS); - info = new FileInfoQuery() - .setFileId(fileId) - .execute(testEnv.client); + info = new FileInfoQuery().setFileId(fileId).execute(testEnv.client); assertThat(info.fileId).isEqualTo(fileId); assertThat(info.size).isEqualTo(13522); @@ -521,7 +478,6 @@ void canSerializeDeserializeExecuteFileAppendChunkedTransaction() throws Excepti .setFileId(fileId) .execute(testEnv.client) .getReceipt(testEnv.client); - } } @@ -530,7 +486,8 @@ void canSerializeDeserializeExecuteFileAppendChunkedTransaction() throws Excepti * @url https://hips.hedera.com/hip/hip-745 */ @Test - @DisplayName("incomplete file append chunked transaction with node account ids can be serialized into bytes, deserialized, edited and executed") + @DisplayName( + "incomplete file append chunked transaction with node account ids can be serialized into bytes, deserialized, edited and executed") void canSerializeDeserializeExecuteIncompleteFileAppendChunkedTransactionWithNodeAccountIds() throws Exception { try (var testEnv = new IntegrationTestEnv(1)) { @@ -545,9 +502,7 @@ void canSerializeDeserializeExecuteIncompleteFileAppendChunkedTransactionWithNod Thread.sleep(5000); - var info = new FileInfoQuery() - .setFileId(fileId) - .execute(testEnv.client); + var info = new FileInfoQuery().setFileId(fileId).execute(testEnv.client); assertThat(info.fileId).isEqualTo(fileId); assertThat(info.size).isEqualTo(28); @@ -562,23 +517,19 @@ void canSerializeDeserializeExecuteIncompleteFileAppendChunkedTransactionWithNod .setContents(Contents.BIG_CONTENTS); var transactionBytesSerialized = fileAppendTransaction.toBytes(); - FileAppendTransaction fileAppendTransactionDeserialized = (FileAppendTransaction) Transaction - .fromBytes(transactionBytesSerialized); + FileAppendTransaction fileAppendTransactionDeserialized = + (FileAppendTransaction) Transaction.fromBytes(transactionBytesSerialized); fileAppendTransactionDeserialized .setTransactionId(TransactionId.generate(testEnv.client.getOperatorAccountId())) .execute(testEnv.client) .getReceipt(testEnv.client); - var contents = new FileContentsQuery() - .setFileId(fileId) - .execute(testEnv.client); + var contents = new FileContentsQuery().setFileId(fileId).execute(testEnv.client); assertThat(contents.toStringUtf8()).isEqualTo("[e2e::FileCreateTransaction]" + Contents.BIG_CONTENTS); - info = new FileInfoQuery() - .setFileId(fileId) - .execute(testEnv.client); + info = new FileInfoQuery().setFileId(fileId).execute(testEnv.client); assertThat(info.fileId).isEqualTo(fileId); assertThat(info.size).isEqualTo(13522); @@ -591,7 +542,6 @@ void canSerializeDeserializeExecuteIncompleteFileAppendChunkedTransactionWithNod .setFileId(fileId) .execute(testEnv.client) .getReceipt(testEnv.client); - } } @@ -600,7 +550,8 @@ void canSerializeDeserializeExecuteIncompleteFileAppendChunkedTransactionWithNod * @url https://hips.hedera.com/hip/hip-745 */ @Test - @DisplayName("topic message submit chunked transaction can be frozen, signed, serialized into bytes, deserialized and be equal to the original one") + @DisplayName( + "topic message submit chunked transaction can be frozen, signed, serialized into bytes, deserialized and be equal to the original one") void canFreezeSignSerializeDeserializeAndCompareTopicMessageSubmitChunkedTransaction() throws Exception { try (var testEnv = new IntegrationTestEnv(1)) { @@ -615,9 +566,7 @@ void canFreezeSignSerializeDeserializeAndCompareTopicMessageSubmitChunkedTransac Thread.sleep(5000); - var info = new TopicInfoQuery() - .setTopicId(topicId) - .execute(testEnv.client); + var info = new TopicInfoQuery().setTopicId(topicId).execute(testEnv.client); assertThat(info.topicId).isEqualTo(topicId); assertThat(info.topicMemo).isEqualTo("[e2e::TopicCreateTransaction]"); @@ -632,8 +581,8 @@ void canFreezeSignSerializeDeserializeAndCompareTopicMessageSubmitChunkedTransac .sign(privateKey); var transactionBytesSerialized = topicMessageSubmitTransaction.toBytes(); - TopicMessageSubmitTransaction fileAppendTransactionDeserialized = (TopicMessageSubmitTransaction) Transaction - .fromBytes(transactionBytesSerialized); + TopicMessageSubmitTransaction fileAppendTransactionDeserialized = + (TopicMessageSubmitTransaction) Transaction.fromBytes(transactionBytesSerialized); var transactionBytesReserialized = fileAppendTransactionDeserialized.toBytes(); assertThat(transactionBytesSerialized).isEqualTo(transactionBytesReserialized); @@ -642,7 +591,6 @@ void canFreezeSignSerializeDeserializeAndCompareTopicMessageSubmitChunkedTransac .setTopicId(topicId) .execute(testEnv.client) .getReceipt(testEnv.client); - } } @@ -651,7 +599,8 @@ void canFreezeSignSerializeDeserializeAndCompareTopicMessageSubmitChunkedTransac * @url https://hips.hedera.com/hip/hip-745 */ @Test - @DisplayName("incomplete topic message submit chunked transaction can be serialized into bytes, deserialized, edited and executed") + @DisplayName( + "incomplete topic message submit chunked transaction can be serialized into bytes, deserialized, edited and executed") void canSerializeDeserializeExecuteIncompleteTopicMessageSubmitChunkedTransaction() throws Exception { try (var testEnv = new IntegrationTestEnv(1)) { @@ -664,9 +613,7 @@ void canSerializeDeserializeExecuteIncompleteTopicMessageSubmitChunkedTransactio Thread.sleep(5000); - var info = new TopicInfoQuery() - .setTopicId(topicId) - .execute(testEnv.client); + var info = new TopicInfoQuery().setTopicId(topicId).execute(testEnv.client); assertThat(info.topicId).isEqualTo(topicId); assertThat(info.topicMemo).isEqualTo("[e2e::TopicCreateTransaction]"); @@ -679,8 +626,8 @@ void canSerializeDeserializeExecuteIncompleteTopicMessageSubmitChunkedTransactio .setMessage(Contents.BIG_CONTENTS); var transactionBytesSerialized = topicMessageSubmitTransaction.toBytes(); - TopicMessageSubmitTransaction topicMessageSubmitTransactionDeserialized = (TopicMessageSubmitTransaction) Transaction - .fromBytes(transactionBytesSerialized); + TopicMessageSubmitTransaction topicMessageSubmitTransactionDeserialized = + (TopicMessageSubmitTransaction) Transaction.fromBytes(transactionBytesSerialized); var responses = topicMessageSubmitTransactionDeserialized.executeAll(testEnv.client); @@ -688,9 +635,7 @@ void canSerializeDeserializeExecuteIncompleteTopicMessageSubmitChunkedTransactio resp.getReceipt(testEnv.client); } - info = new TopicInfoQuery() - .setTopicId(topicId) - .execute(testEnv.client); + info = new TopicInfoQuery().setTopicId(topicId).execute(testEnv.client); assertThat(info.topicId).isEqualTo(topicId); assertThat(info.topicMemo).isEqualTo("[e2e::TopicCreateTransaction]"); @@ -701,7 +646,6 @@ void canSerializeDeserializeExecuteIncompleteTopicMessageSubmitChunkedTransactio .setTopicId(topicId) .execute(testEnv.client) .getReceipt(testEnv.client); - } } @@ -710,7 +654,8 @@ void canSerializeDeserializeExecuteIncompleteTopicMessageSubmitChunkedTransactio * @url https://hips.hedera.com/hip/hip-745 */ @Test - @DisplayName("incomplete topic message submit chunked transaction with node account ids can be serialized into bytes, deserialized, edited and executed") + @DisplayName( + "incomplete topic message submit chunked transaction with node account ids can be serialized into bytes, deserialized, edited and executed") void canSerializeDeserializeExecuteIncompleteTopicMessageSubmitChunkedTransactionWithNodeAccountIds() throws Exception { try (var testEnv = new IntegrationTestEnv(1)) { @@ -726,9 +671,7 @@ void canSerializeDeserializeExecuteIncompleteTopicMessageSubmitChunkedTransactio Thread.sleep(5000); - var info = new TopicInfoQuery() - .setTopicId(topicId) - .execute(testEnv.client); + var info = new TopicInfoQuery().setTopicId(topicId).execute(testEnv.client); assertThat(info.topicId).isEqualTo(topicId); assertThat(info.topicMemo).isEqualTo("[e2e::TopicCreateTransaction]"); @@ -742,8 +685,8 @@ void canSerializeDeserializeExecuteIncompleteTopicMessageSubmitChunkedTransactio .setMessage(Contents.BIG_CONTENTS); var transactionBytesSerialized = topicMessageSubmitTransaction.toBytes(); - TopicMessageSubmitTransaction topicMessageSubmitTransactionDeserialized = (TopicMessageSubmitTransaction) Transaction - .fromBytes(transactionBytesSerialized); + TopicMessageSubmitTransaction topicMessageSubmitTransactionDeserialized = + (TopicMessageSubmitTransaction) Transaction.fromBytes(transactionBytesSerialized); var responses = topicMessageSubmitTransactionDeserialized.executeAll(testEnv.client); @@ -751,9 +694,7 @@ void canSerializeDeserializeExecuteIncompleteTopicMessageSubmitChunkedTransactio resp.getReceipt(testEnv.client); } - info = new TopicInfoQuery() - .setTopicId(topicId) - .execute(testEnv.client); + info = new TopicInfoQuery().setTopicId(topicId).execute(testEnv.client); assertThat(info.topicId).isEqualTo(topicId); assertThat(info.topicMemo).isEqualTo("[e2e::TopicCreateTransaction]"); @@ -764,7 +705,6 @@ void canSerializeDeserializeExecuteIncompleteTopicMessageSubmitChunkedTransactio .setTopicId(topicId) .execute(testEnv.client) .getReceipt(testEnv.client); - } } @@ -798,32 +738,29 @@ void transactionFromToBytes2() { .build()) .setTransactionFee(200_000_000) .setTransactionValidDuration( - Duration.newBuilder() - .setSeconds(120) - .build()) + Duration.newBuilder().setSeconds(120).build()) .setGenerateRecord(false) .setMemo("") - .setCryptoTransfer( - CryptoTransferTransactionBody.newBuilder() - .setTransfers(TransferList.newBuilder() - .addAccountAmounts(AccountAmount.newBuilder() - .setAccountID(AccountID.newBuilder() - .setAccountNum(47439) - .setRealmNum(0) - .setShardNum(0) - .build()) - .setAmount(10) + .setCryptoTransfer(CryptoTransferTransactionBody.newBuilder() + .setTransfers(TransferList.newBuilder() + .addAccountAmounts(AccountAmount.newBuilder() + .setAccountID(AccountID.newBuilder() + .setAccountNum(47439) + .setRealmNum(0) + .setShardNum(0) .build()) - .addAccountAmounts(AccountAmount.newBuilder() - .setAccountID(AccountID.newBuilder() - .setAccountNum(542348) - .setRealmNum(0) - .setShardNum(0) - .build()) - .setAmount(-10) + .setAmount(10) + .build()) + .addAccountAmounts(AccountAmount.newBuilder() + .setAccountID(AccountID.newBuilder() + .setAccountNum(542348) + .setRealmNum(0) + .setShardNum(0) .build()) + .setAmount(-10) .build()) - .build()); + .build()) + .build()); var bodyBytes = transactionBodyBuilder.build().toByteString(); var key1 = PrivateKey.fromString( @@ -876,17 +813,20 @@ void transactionFromToBytes2() { var byts = signedBuilder.build().toByteString(); byts = TransactionList.newBuilder() - .addTransactionList(com.hedera.hashgraph.sdk.proto.Transaction.newBuilder() + .addTransactionList(org.hiero.sdk.proto.Transaction.newBuilder() .setSignedTransactionBytes(byts) .build()) - .build().toByteString(); + .build() + .toByteString(); var tx = (TransferTransaction) Transaction.fromBytes(byts.toByteArray()); try (var testEnv = new IntegrationTestEnv(1)) { - assertThat(tx.getHbarTransfers().get(new AccountId(542348)).toTinybars()).isEqualTo(-10); - assertThat(tx.getHbarTransfers().get(new AccountId(47439)).toTinybars()).isEqualTo(10); + assertThat(tx.getHbarTransfers().get(new AccountId(542348)).toTinybars()) + .isEqualTo(-10); + assertThat(tx.getHbarTransfers().get(new AccountId(47439)).toTinybars()) + .isEqualTo(10); assertThat(tx.getNodeAccountIds()).isNotNull(); assertThat(tx.getNodeAccountIds().size()).isEqualTo(1); @@ -907,7 +847,6 @@ void transactionFromToBytes2() { var resp = tx.execute(testEnv.client); resp.getReceipt(testEnv.client); - } }); } diff --git a/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TransactionResponseTest.java b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TransactionResponseTest.java new file mode 100644 index 0000000000..d92c0309d6 --- /dev/null +++ b/sdk/src/testIntegration/java/org/hiero/sdk/test/integration/TransactionResponseTest.java @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.sdk.test.integration; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.PrivateKey; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class TransactionResponseTest { + @Test + @DisplayName("transaction hash in transaction record is equal to the transaction response transaction hash") + void transactionHashInTransactionRecordIsEqualToTheTransactionResponseTransactionHash() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + + var key = PrivateKey.generateED25519(); + + var transaction = new AccountCreateTransaction().setKey(key).execute(testEnv.client); + + var record = transaction.getRecord(testEnv.client); + + assertThat(record.transactionHash.toByteArray()).containsExactly(transaction.transactionHash); + + var accountId = record.receipt.accountId; + assertThat(accountId).isNotNull(); + } + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 5cf6caf797..4435b73aa9 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,12 +1,12 @@ // SPDX-License-Identifier: Apache-2.0 plugins { id("org.hiero.gradle.build") version "0.1.1" } -rootProject.name = "hedera-sdk-java" +rootProject.name = "hiero-sdk-java" javaModules { - module("sdk") { group = "com.hedera.hashgraph" } - module("sdk-full") { group = "com.hedera.hashgraph" } - module("tck") { group = "com.hedera.hashgraph.sdk.tck" } + module("sdk") { group = "org.hiero" } + module("sdk-full") { group = "org.hiero" } + module("tck") { group = "org.hiero.sdk.tck" } } includeBuild("examples") diff --git a/tck/build.gradle.kts b/tck/build.gradle.kts index 551b22d894..8f4d066a9e 100644 --- a/tck/build.gradle.kts +++ b/tck/build.gradle.kts @@ -19,7 +19,7 @@ plugins { id("org.hiero.gradle.report.test-logger") } -description = "Hedera SDK TCK Server" +description = "Hiero SDK TCK Server" version = "0.0.1" @@ -63,6 +63,7 @@ dependencyAnalysis { onAny { severity("fail") exclude("com.google.protobuf:protobuf-javalite") + onUnusedDependencies { exclude(":sdk") } } } } diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/TckServer.java b/tck/src/main/java/com/hedera/hashgraph/tck/TckServer.java deleted file mode 100644 index cac9be0d8b..0000000000 --- a/tck/src/main/java/com/hedera/hashgraph/tck/TckServer.java +++ /dev/null @@ -1,30 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -public class TckServer { - public static void main(String[] args) { - SpringApplication.run(TckServer.class, args); - } -} diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/annotation/JSONRPC2Controller.java b/tck/src/main/java/com/hedera/hashgraph/tck/annotation/JSONRPC2Controller.java deleted file mode 100644 index 8b8fb407a5..0000000000 --- a/tck/src/main/java/com/hedera/hashgraph/tck/annotation/JSONRPC2Controller.java +++ /dev/null @@ -1,38 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.ResponseBody; - -/** - * Marks classes as HTTP handlers. - * In the context of this application these handlers - * should support the JSON-RPC spec - */ -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -@Controller -@ResponseBody -public @interface JSONRPC2Controller {} diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/annotation/JSONRPC2Method.java b/tck/src/main/java/com/hedera/hashgraph/tck/annotation/JSONRPC2Method.java deleted file mode 100644 index fdbee75da6..0000000000 --- a/tck/src/main/java/com/hedera/hashgraph/tck/annotation/JSONRPC2Method.java +++ /dev/null @@ -1,41 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Marks methods as JSON-RPC methods. - * Methods marked with this annotation will be registered - * as handlers for JSON-RPC requests with the specified method name. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface JSONRPC2Method { - /** - * Specifies the name of the JSON-RPC method. - * the params need to be of instance JSONRPC2Param because if not - will blow up at runtime - * @return The name of the JSON-RPC method. - */ - String value(); -} diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/annotation/JSONRPC2Service.java b/tck/src/main/java/com/hedera/hashgraph/tck/annotation/JSONRPC2Service.java deleted file mode 100644 index 19fe04535b..0000000000 --- a/tck/src/main/java/com/hedera/hashgraph/tck/annotation/JSONRPC2Service.java +++ /dev/null @@ -1,34 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import org.springframework.stereotype.Component; - -/** - * Marks classes as JSON-RPC services. - */ -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -@Component -public @interface JSONRPC2Service {} diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/config/BeanConfig.java b/tck/src/main/java/com/hedera/hashgraph/tck/config/BeanConfig.java deleted file mode 100644 index 8033d3009f..0000000000 --- a/tck/src/main/java/com/hedera/hashgraph/tck/config/BeanConfig.java +++ /dev/null @@ -1,32 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.config; - -import com.thetransactioncompany.jsonrpc2.server.Dispatcher; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class BeanConfig { - @Bean - public Dispatcher dispatcher() { - return new Dispatcher(); - } -} diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/config/WebConfig.java b/tck/src/main/java/com/hedera/hashgraph/tck/config/WebConfig.java deleted file mode 100644 index 1c5c9a082f..0000000000 --- a/tck/src/main/java/com/hedera/hashgraph/tck/config/WebConfig.java +++ /dev/null @@ -1,33 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.config; - -import com.hedera.hashgraph.tck.controller.JRPCInterceptor; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.InterceptorRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -@Configuration -public class WebConfig implements WebMvcConfigurer { - @Override - public void addInterceptors(InterceptorRegistry registry) { - registry.addInterceptor(new JRPCInterceptor()); - } -} diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/controller/JRPCController.java b/tck/src/main/java/com/hedera/hashgraph/tck/controller/JRPCController.java deleted file mode 100644 index 9ddaa6493d..0000000000 --- a/tck/src/main/java/com/hedera/hashgraph/tck/controller/JRPCController.java +++ /dev/null @@ -1,60 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.controller; - -import static com.hedera.hashgraph.tck.util.JSONRPC2ServiceScanner.registerServices; - -import com.hedera.hashgraph.tck.annotation.JSONRPC2Controller; -import com.thetransactioncompany.jsonrpc2.JSONRPC2Request; -import com.thetransactioncompany.jsonrpc2.server.Dispatcher; -import jakarta.servlet.http.HttpServletRequest; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.ApplicationContext; -import org.springframework.web.bind.annotation.PostMapping; - -@JSONRPC2Controller -public class JRPCController { - private final Logger logger = LoggerFactory.getLogger(JRPCController.class); - private final Dispatcher dispatcher; - - public JRPCController(final Dispatcher dispatcher, final ApplicationContext applicationContext) { - this.dispatcher = dispatcher; - registerServices(dispatcher, applicationContext); - } - - /** - * Endpoint to handle all incoming JSON-RPC requests - */ - @PostMapping("/") - public String handleJSONRPC2Request(final HttpServletRequest request) { - var req = (JSONRPC2Request) request.getAttribute("jsonrpcRequest"); - var resp = dispatcher.process(req, null); - - if (resp.getError() != null) { - String errorMessage = String.format( - "Error occurred processing JSON-RPC request: %s, Response error: %s", - req.toJSONString(), resp.getError().toString()); - logger.info(errorMessage); - } - - return resp.toJSONString(); - } -} diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/controller/JRPCInterceptor.java b/tck/src/main/java/com/hedera/hashgraph/tck/controller/JRPCInterceptor.java deleted file mode 100644 index a137216fe8..0000000000 --- a/tck/src/main/java/com/hedera/hashgraph/tck/controller/JRPCInterceptor.java +++ /dev/null @@ -1,63 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.controller; - -import com.thetransactioncompany.jsonrpc2.JSONRPC2ParseException; -import com.thetransactioncompany.jsonrpc2.JSONRPC2Request; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import java.io.BufferedReader; -import java.io.IOException; -import org.springframework.web.servlet.HandlerInterceptor; - -/** - * Interceptor class to map HttpServletRequest body to {@link JSONRPC2Request} - * before the request reaches the controller - */ -public class JRPCInterceptor implements HandlerInterceptor { - - @Override - public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) - throws Exception { - // Map HTTP Servlet Request to JSON-RPC Request - var jsonrpcRequest = mapToJSONRPC2Request(request); - - // Store the JSON-RPC request in request attribute for further processing - request.setAttribute("jsonrpcRequest", jsonrpcRequest); - - // Continue processing the request - return true; - } - - JSONRPC2Request mapToJSONRPC2Request(final HttpServletRequest httpRequest) - throws IOException, JSONRPC2ParseException { - // Read the JSON-RPC request from the HTTP request body - BufferedReader reader = httpRequest.getReader(); - StringBuilder requestBody = new StringBuilder(); - String line; - while ((line = reader.readLine()) != null) { - requestBody.append(line); - } - reader.close(); - - // Parse the JSON-RPC request - return JSONRPC2Request.parse(requestBody.toString()); - } -} diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/exception/InvalidJSONRPC2ParamsException.java b/tck/src/main/java/com/hedera/hashgraph/tck/exception/InvalidJSONRPC2ParamsException.java deleted file mode 100644 index b29fa49b4a..0000000000 --- a/tck/src/main/java/com/hedera/hashgraph/tck/exception/InvalidJSONRPC2ParamsException.java +++ /dev/null @@ -1,43 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.exception; - -/** - * Thrown when the server cannot parse the given parameters. - * This error should be thrown from the param parser - */ -public class InvalidJSONRPC2ParamsException extends Exception { - - public InvalidJSONRPC2ParamsException() { - super(); - } - - public InvalidJSONRPC2ParamsException(String message) { - super(message); - } - - public InvalidJSONRPC2ParamsException(String message, Throwable cause) { - super(message, cause); - } - - public InvalidJSONRPC2ParamsException(Throwable cause) { - super(cause); - } -} diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/exception/InvalidJSONRPC2RequestException.java b/tck/src/main/java/com/hedera/hashgraph/tck/exception/InvalidJSONRPC2RequestException.java deleted file mode 100644 index 39354c22b5..0000000000 --- a/tck/src/main/java/com/hedera/hashgraph/tck/exception/InvalidJSONRPC2RequestException.java +++ /dev/null @@ -1,30 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.exception; - -/** - * Thrown when the server cannot process the request - */ -public class InvalidJSONRPC2RequestException extends Exception { - - public InvalidJSONRPC2RequestException(String message) { - super(message); - } -} diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/methods/JSONRPC2Error.java b/tck/src/main/java/com/hedera/hashgraph/tck/methods/JSONRPC2Error.java deleted file mode 100644 index c66648afd9..0000000000 --- a/tck/src/main/java/com/hedera/hashgraph/tck/methods/JSONRPC2Error.java +++ /dev/null @@ -1,37 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.methods; - -import com.hedera.hashgraph.sdk.Status; - -/** - * Custom JSON-RPC error definitions - */ -public class JSONRPC2Error { - private JSONRPC2Error() { - // static utility class - } - - public static final int HEDERA_STATUS_CODE = -32001; - public static final com.thetransactioncompany.jsonrpc2.JSONRPC2Error HEDERA_ERROR = - new com.thetransactioncompany.jsonrpc2.JSONRPC2Error(HEDERA_STATUS_CODE, "Hedera error"); - - public record ErrorData(Status status, String message) {} -} diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/methods/JSONRPC2Param.java b/tck/src/main/java/com/hedera/hashgraph/tck/methods/JSONRPC2Param.java deleted file mode 100644 index c8954a8111..0000000000 --- a/tck/src/main/java/com/hedera/hashgraph/tck/methods/JSONRPC2Param.java +++ /dev/null @@ -1,37 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.methods; - -import java.util.Map; - -/** - * Abstract base class for JSON-RPC parameters. Every parameter POJO should extend this class - * and implement the {@link #parse} method. This method assists JSON-RPC services in creating - * parameters for their JSON-RPC methods from the request. - * - * IMPORTANT: - * all inheriting classes should include the following Lombok annotations: - * {@code @Getter}, {@code @AllArgsConstructor}, and {@code @NoArgsConstructor}. - * These annotations are needed for the instance creation via reflection. - * - */ -public abstract class JSONRPC2Param { - public abstract JSONRPC2Param parse(final Map jrpcParams) throws Exception; -} diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/KeyService.java b/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/KeyService.java deleted file mode 100644 index fae8f862df..0000000000 --- a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/KeyService.java +++ /dev/null @@ -1,148 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.methods.sdk; - -import static com.hedera.hashgraph.tck.util.KeyUtils.KeyType.*; -import static com.hedera.hashgraph.tck.util.KeyUtils.getKeyFromString; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.Key; -import com.hedera.hashgraph.sdk.KeyList; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.PublicKey; -import com.hedera.hashgraph.tck.annotation.JSONRPC2Method; -import com.hedera.hashgraph.tck.annotation.JSONRPC2Service; -import com.hedera.hashgraph.tck.exception.InvalidJSONRPC2RequestException; -import com.hedera.hashgraph.tck.methods.AbstractJSONRPC2Service; -import com.hedera.hashgraph.tck.methods.sdk.param.GenerateKeyParams; -import com.hedera.hashgraph.tck.methods.sdk.response.GenerateKeyResponse; -import org.bouncycastle.util.encoders.Hex; - -@JSONRPC2Service -public class KeyService extends AbstractJSONRPC2Service { - - @JSONRPC2Method("generateKey") - public GenerateKeyResponse generateKey(final GenerateKeyParams params) throws Exception { - // Make sure getFromKey() is only provided for ED25519_PUBLIC_KEY, ECDSA_SECP256k1_PUBLIC_KEY, or - // EVM_ADDRESS_KEY - if (params.getFromKey().isPresent() - && !params.getType().equals(ED25519_PUBLIC_KEY) - && !params.getType().equals(ECDSA_SECP256K1_PUBLIC_KEY) - && !params.getType().equals(EVM_ADDRESS_KEY)) { - throw new InvalidJSONRPC2RequestException( - "invalid parameters: fromKey should only be provided for ed25519PublicKey, ecdsaSecp256k1PublicKey, or evmAddress types."); - } - - // Make sure threshold is only provided for THRESHOLD_KEY_TYPE. - if (params.getThreshold().isPresent() && !params.getType().equals(THRESHOLD_KEY)) { - throw new InvalidJSONRPC2RequestException( - "invalid parameters: threshold should only be provided for thresholdKey types."); - } - - // Make sure keys is only provided for LIST_KEY_TYPE or THRESHOLD_KEY_TYPE - if (params.getKeys().isPresent() - && !params.getType().equals(LIST_KEY) - && !params.getType().equals(THRESHOLD_KEY)) { - throw new InvalidJSONRPC2RequestException( - "invalid parameters: keys should only be provided for keyList or thresholdKey types."); - } - - if ((params.getType().equals(THRESHOLD_KEY) || params.getType().equals(LIST_KEY)) - && params.getKeys().isEmpty()) { - throw new InvalidJSONRPC2RequestException( - "invalid request: keys list is required for generating a KeyList type."); - } - - if (params.getType().equals(THRESHOLD_KEY) && params.getThreshold().isEmpty()) { - throw new InvalidJSONRPC2RequestException( - "invalid request: threshold is required for generating a ThresholdKey type."); - } - - GenerateKeyResponse response = new GenerateKeyResponse(); - response.setKey(processKeyRecursively(params, response, false)); - return response; - } - - private String processKeyRecursively( - final GenerateKeyParams params, final GenerateKeyResponse response, boolean isList) - throws InvalidJSONRPC2RequestException, InvalidProtocolBufferException { - String privateKeyString; - PrivateKey privateKey; - switch (params.getType()) { - case ED25519_PRIVATE_KEY, ECDSA_SECP256K1_PRIVATE_KEY: - privateKeyString = params.getType().equals(ED25519_PRIVATE_KEY) - ? PrivateKey.generateED25519().toStringDER() - : PrivateKey.generateECDSA().toStringDER(); - if (isList) { - response.getPrivateKeys().add(privateKeyString); - } - - return privateKeyString; - - case ED25519_PUBLIC_KEY, ECDSA_SECP256K1_PUBLIC_KEY: - if (params.getFromKey().isPresent()) { - return PrivateKey.fromString(params.getFromKey().get()) - .getPublicKey() - .toStringDER(); - } - privateKey = params.getType().equals(ED25519_PUBLIC_KEY) - ? PrivateKey.generateED25519() - : PrivateKey.generateECDSA(); - if (isList) { - response.getPrivateKeys().add(privateKey.toStringDER()); - } - - return privateKey.getPublicKey().toStringDER(); - - case LIST_KEY, THRESHOLD_KEY: - KeyList keyList = new KeyList(); - params.getKeys().get().forEach(keyParams -> { - try { - keyList.add(getKeyFromString(processKeyRecursively(keyParams, response, true))); - } catch (Exception e) { - throw new IllegalArgumentException(e); - } - }); - - if (params.getType().equals(THRESHOLD_KEY)) { - keyList.setThreshold(params.getThreshold().get().intValue()); - } - - return Hex.toHexString(keyList.toBytes()); - - case EVM_ADDRESS_KEY: - if (params.getFromKey().isPresent()) { - Key hederaKey = getKeyFromString(params.getFromKey().get()); - if (hederaKey instanceof PrivateKey pk) { - return pk.getPublicKey().toEvmAddress().toString(); - } else if (hederaKey instanceof PublicKey pk) { - return pk.toEvmAddress().toString(); - } else { - throw new InvalidJSONRPC2RequestException( - "invalid parameters: fromKey for evmAddress is not ECDSAsecp256k1."); - } - } - return PrivateKey.generateECDSA().getPublicKey().toEvmAddress().toString(); - - default: - throw new InvalidJSONRPC2RequestException("invalid request: key type not recognized."); - } - } -} diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/SdkService.java b/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/SdkService.java deleted file mode 100644 index 70f1180212..0000000000 --- a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/SdkService.java +++ /dev/null @@ -1,80 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.methods.sdk; - -import com.hedera.hashgraph.sdk.AccountId; -import com.hedera.hashgraph.sdk.Client; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.tck.annotation.JSONRPC2Method; -import com.hedera.hashgraph.tck.annotation.JSONRPC2Service; -import com.hedera.hashgraph.tck.methods.AbstractJSONRPC2Service; -import com.hedera.hashgraph.tck.methods.sdk.param.SetupParams; -import com.hedera.hashgraph.tck.methods.sdk.response.SetupResponse; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Executors; - -/** - * SdkService for managing the {@link Client} setup and reset - */ -@JSONRPC2Service -public class SdkService extends AbstractJSONRPC2Service { - // this is shared state to all requests so there could be race conditions - // although the tck driver would not call these methods in such way - private Client client; - - @JSONRPC2Method("setup") - public SetupResponse setup(final SetupParams params) throws Exception { - var clientExecutor = Executors.newFixedThreadPool(16); - String clientType; - if (params.getNodeIp().isPresent() - && params.getNodeAccountId().isPresent() - && params.getMirrorNetworkIp().isPresent()) { - // Custom client setup - Map node = new HashMap<>(); - var nodeId = AccountId.fromString(params.getNodeAccountId().get()); - node.put(params.getNodeIp().get(), nodeId); - client = Client.forNetwork(node, clientExecutor); - clientType = "custom"; - client.setMirrorNetwork(List.of(params.getMirrorNetworkIp().get())); - } else { - // Default to testnet - client = Client.forTestnet(clientExecutor); - clientType = "testnet"; - } - - client.setOperator( - AccountId.fromString(params.getOperatorAccountId()), - PrivateKey.fromString(params.getOperatorPrivateKey())); - return new SetupResponse("Successfully setup " + clientType + " client."); - } - - @JSONRPC2Method("reset") - public SetupResponse reset() throws Exception { - client.close(); - client = null; - return new SetupResponse(""); - } - - public Client getClient() { - return this.client; - } -} diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/param/CustomFee.java b/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/param/CustomFee.java deleted file mode 100644 index 990fba3cf2..0000000000 --- a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/param/CustomFee.java +++ /dev/null @@ -1,126 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.methods.sdk.param; - -import com.hedera.hashgraph.tck.methods.JSONRPC2Param; -import java.util.Map; -import java.util.Optional; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import net.minidev.json.JSONObject; - -@Getter -@AllArgsConstructor -@NoArgsConstructor -public class CustomFee extends JSONRPC2Param { - - private String feeCollectorAccountId; - private Boolean feeCollectorsExempt; - private Optional fixedFee; - private Optional fractionalFee; - private Optional royaltyFee; - - @Override - public CustomFee parse(Map jrpcParams) throws Exception { - var feeCollectorAccountIdParsed = (String) jrpcParams.get("feeCollectorAccountId"); - var feeCollectorsExemptParsed = (Boolean) jrpcParams.get("feeCollectorsExempt"); - - Optional fixedFeeParsed = Optional.empty(); - if (jrpcParams.containsKey("fixedFee")) { - JSONObject jsonObject = (JSONObject) jrpcParams.get("fixedFee"); - fixedFeeParsed = Optional.of(FixedFee.parse(jsonObject)); - } - - Optional fractionalFeeParsed = Optional.empty(); - if (jrpcParams.containsKey("fractionalFee")) { - JSONObject jsonObject = (JSONObject) jrpcParams.get("fractionalFee"); - fractionalFeeParsed = Optional.of(FractionalFee.parse(jsonObject)); - } - - Optional royaltyFeeParsed = Optional.empty(); - if (jrpcParams.containsKey("royaltyFee")) { - JSONObject jsonObject = (JSONObject) jrpcParams.get("royaltyFee"); - royaltyFeeParsed = Optional.of(RoyaltyFee.parse(jsonObject)); - } - - return new CustomFee( - feeCollectorAccountIdParsed, - feeCollectorsExemptParsed, - fixedFeeParsed, - fractionalFeeParsed, - royaltyFeeParsed); - } - - @Getter - @AllArgsConstructor - @NoArgsConstructor - public static class FixedFee { - private String amount; - private Optional denominatingTokenId; - - public static FixedFee parse(Map jrpcParams) throws Exception { - var amountParsed = (String) jrpcParams.get("amount"); - var denominatingTokenIdParsed = Optional.ofNullable((String) jrpcParams.get("denominatingTokenId")); - return new FixedFee(amountParsed, denominatingTokenIdParsed); - } - } - - @Getter - @AllArgsConstructor - @NoArgsConstructor - public static class FractionalFee { - private String numerator; - private String denominator; - private String minimumAmount; - private String maximumAmount; - - public static FractionalFee parse(Map jrpcParams) throws Exception { - var numeratorParsed = (String) jrpcParams.get("numerator"); - var denominatorParsed = (String) jrpcParams.get("denominator"); - var minimumAmountParsed = (String) jrpcParams.get("minimumAmount"); - var maximumAmountParsed = (String) jrpcParams.get("maximumAmount"); - return new FractionalFee(numeratorParsed, denominatorParsed, minimumAmountParsed, maximumAmountParsed); - } - } - - @Getter - @AllArgsConstructor - @NoArgsConstructor - public static class RoyaltyFee { - private String numerator; - private String denominator; - private Optional fallbackFee; - - public static RoyaltyFee parse(Map jrpcParams) throws Exception { - var numeratorParsed = (String) jrpcParams.get("numerator"); - var denominatorParsed = (String) jrpcParams.get("denominator"); - - Optional fallbackFeeParsed = Optional.empty(); - if (jrpcParams.containsKey("fallbackFee")) { - JSONObject jsonObject = (JSONObject) jrpcParams.get("fallbackFee"); - fallbackFeeParsed = Optional.of(FixedFee.parse(jsonObject)); - } - - return new RoyaltyFee(numeratorParsed, denominatorParsed, fallbackFeeParsed); - } - } -} diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/param/GenerateKeyParams.java b/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/param/GenerateKeyParams.java deleted file mode 100644 index 9072e99423..0000000000 --- a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/param/GenerateKeyParams.java +++ /dev/null @@ -1,64 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.methods.sdk.param; - -import com.hedera.hashgraph.tck.methods.JSONRPC2Param; -import com.hedera.hashgraph.tck.util.KeyUtils.KeyType; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import net.minidev.json.JSONArray; -import net.minidev.json.JSONObject; - -@SuppressWarnings("OptionalUsedAsFieldOrParameterType") -@Getter -@AllArgsConstructor -@NoArgsConstructor -public class GenerateKeyParams extends JSONRPC2Param { - private KeyType type; - private Optional fromKey; - private Optional threshold; - private Optional> keys; - - @Override - public GenerateKeyParams parse(Map jrpcParams) throws Exception { - var parsedType = (String) jrpcParams.get("type"); - var parsedFromKey = Optional.ofNullable((String) jrpcParams.get("fromKey")); - var parsedThreshold = Optional.ofNullable((Long) jrpcParams.get("threshold")); - - Optional> parsedKeys = Optional.empty(); - if (jrpcParams.containsKey("keys")) { - JSONArray jsonArray = (JSONArray) jrpcParams.get("keys"); - List keyList = new ArrayList<>(); - for (Object o : jsonArray) { - JSONObject jsonObject = (JSONObject) o; - GenerateKeyParams keyParam = new GenerateKeyParams().parse(jsonObject); - keyList.add(keyParam); - } - parsedKeys = Optional.of(keyList); - } - - return new GenerateKeyParams(KeyType.fromString(parsedType), parsedFromKey, parsedThreshold, parsedKeys); - } -} diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/param/SetupParams.java b/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/param/SetupParams.java deleted file mode 100644 index 6687367ebb..0000000000 --- a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/param/SetupParams.java +++ /dev/null @@ -1,58 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.methods.sdk.param; - -import com.hedera.hashgraph.tck.methods.JSONRPC2Param; -import java.util.Map; -import java.util.Optional; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -/** - * SetupParams for SDK client - */ -@SuppressWarnings("OptionalUsedAsFieldOrParameterType") -@Getter -@AllArgsConstructor -@NoArgsConstructor -public class SetupParams extends JSONRPC2Param { - private String operatorAccountId; - private String operatorPrivateKey; - private Optional nodeIp; - private Optional nodeAccountId; - private Optional mirrorNetworkIp; - - @Override - public SetupParams parse(Map jrpcParams) throws ClassCastException { - var parsedOperatorAccountId = (String) jrpcParams.get("operatorAccountId"); - var parsedOperatorPrivateKey = (String) jrpcParams.get("operatorPrivateKey"); - var parsedNodeIp = Optional.ofNullable((String) jrpcParams.get("nodeIp")); - var parsedNodeAccountId = Optional.ofNullable((String) jrpcParams.get("nodeAccountId")); - var parsedMirrorNetworkIp = Optional.ofNullable((String) jrpcParams.get("mirrorNetworkIp")); - - return new SetupParams( - parsedOperatorAccountId, - parsedOperatorPrivateKey, - parsedNodeIp, - parsedNodeAccountId, - parsedMirrorNetworkIp); - } -} diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/param/TokenDeleteParams.java b/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/param/TokenDeleteParams.java deleted file mode 100644 index b65dd5d7e3..0000000000 --- a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/param/TokenDeleteParams.java +++ /dev/null @@ -1,52 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.methods.sdk.param; - -import com.hedera.hashgraph.tck.methods.JSONRPC2Param; -import java.util.Map; -import java.util.Optional; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import net.minidev.json.JSONObject; - -/** - * TokenDeleteParams for token delete method - */ -@Getter -@AllArgsConstructor -@NoArgsConstructor -public class TokenDeleteParams extends JSONRPC2Param { - private Optional tokenId; - private Optional commonTransactionParams; - - @Override - public JSONRPC2Param parse(Map jrpcParams) throws Exception { - var parsedTokenId = Optional.ofNullable((String) jrpcParams.get("tokenId")); - Optional parsedCommonTransactionParams = Optional.empty(); - if (jrpcParams.containsKey("commonTransactionParams")) { - JSONObject jsonObject = (JSONObject) jrpcParams.get("commonTransactionParams"); - parsedCommonTransactionParams = Optional.of(CommonTransactionParams.parse(jsonObject)); - } - - return new TokenDeleteParams(parsedTokenId, parsedCommonTransactionParams); - } -} diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/response/AccountResponse.java b/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/response/AccountResponse.java deleted file mode 100644 index 63e803b905..0000000000 --- a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/response/AccountResponse.java +++ /dev/null @@ -1,29 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.methods.sdk.response; - -import com.hedera.hashgraph.sdk.Status; -import lombok.Data; - -@Data -public class AccountResponse { - private final String accountId; - private final Status status; -} diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/response/GenerateKeyResponse.java b/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/response/GenerateKeyResponse.java deleted file mode 100644 index ca45309bcf..0000000000 --- a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/response/GenerateKeyResponse.java +++ /dev/null @@ -1,30 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.methods.sdk.response; - -import java.util.ArrayList; -import java.util.List; -import lombok.Data; - -@Data -public class GenerateKeyResponse { - private String key; - private List privateKeys = new ArrayList<>(); -} diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/response/SetupResponse.java b/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/response/SetupResponse.java deleted file mode 100644 index 987753c578..0000000000 --- a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/response/SetupResponse.java +++ /dev/null @@ -1,35 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.methods.sdk.response; - -import lombok.Data; - -@Data -public class SetupResponse { - private String message = ""; - private String status = ""; - - public SetupResponse(String message) { - if (message != null && !message.isEmpty()) { - this.message = message; - } - this.status = "SUCCESS"; - } -} diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/response/TokenResponse.java b/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/response/TokenResponse.java deleted file mode 100644 index 8c028f216b..0000000000 --- a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/response/TokenResponse.java +++ /dev/null @@ -1,30 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.methods.sdk.response; - -import com.hedera.hashgraph.sdk.Status; -import lombok.Data; - -@Data -public class TokenResponse { - private final String tokenId; - private final Status status; -} diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/util/JSONRPC2ServiceScanner.java b/tck/src/main/java/com/hedera/hashgraph/tck/util/JSONRPC2ServiceScanner.java deleted file mode 100644 index 151a5785d8..0000000000 --- a/tck/src/main/java/com/hedera/hashgraph/tck/util/JSONRPC2ServiceScanner.java +++ /dev/null @@ -1,44 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.util; - -import com.hedera.hashgraph.tck.annotation.JSONRPC2Service; -import com.thetransactioncompany.jsonrpc2.server.*; -import org.springframework.context.ApplicationContext; - -/** - * Utility class to register all {@link JSONRPC2Service} annotated classes - * to the JSON-RPC dispatcher - */ -public class JSONRPC2ServiceScanner { - private JSONRPC2ServiceScanner() { - // private constructor for utility class - } - - public static void registerServices(Dispatcher dispatcher, ApplicationContext context) { - String[] serviceNames = context.getBeanNamesForAnnotation(JSONRPC2Service.class); - if (serviceNames != null) { // NOSONAR - for (String serviceName : serviceNames) { - Object service = context.getBean(serviceName); - dispatcher.register((RequestHandler) service); - } - } - } -} diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/util/KeyUtils.java b/tck/src/main/java/com/hedera/hashgraph/tck/util/KeyUtils.java deleted file mode 100644 index 1de9b38ef0..0000000000 --- a/tck/src/main/java/com/hedera/hashgraph/tck/util/KeyUtils.java +++ /dev/null @@ -1,66 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.util; - -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.Key; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.PublicKey; - -public final class KeyUtils { - - public enum KeyType { - ED25519_PRIVATE_KEY("ed25519PrivateKey"), - ED25519_PUBLIC_KEY("ed25519PublicKey"), - ECDSA_SECP256K1_PRIVATE_KEY("ecdsaSecp256k1PrivateKey"), - ECDSA_SECP256K1_PUBLIC_KEY("ecdsaSecp256k1PublicKey"), - LIST_KEY("keyList"), - THRESHOLD_KEY("thresholdKey"), - EVM_ADDRESS_KEY("evmAddress"); - - private final String keyString; - - KeyType(String keyString) { - this.keyString = keyString; - } - - public static KeyType fromString(String keyString) { - for (KeyType type : KeyType.values()) { - if (type.keyString.equals(keyString)) { - return type; - } - } - throw new IllegalArgumentException("Unknown key type: " + keyString); - } - } - - public static Key getKeyFromString(String keyString) throws InvalidProtocolBufferException { - try { - return PublicKey.fromStringDER(keyString); - } catch (Exception e) { - try { - return PrivateKey.fromStringDER(keyString); - } catch (Exception ex) { - return Key.fromBytes(ByteString.fromHex(keyString).toByteArray()); - } - } - } -} diff --git a/tck/src/main/java/org/hiero/tck/TckServer.java b/tck/src/main/java/org/hiero/tck/TckServer.java new file mode 100644 index 0000000000..6dd4149882 --- /dev/null +++ b/tck/src/main/java/org/hiero/tck/TckServer.java @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class TckServer { + public static void main(String[] args) { + SpringApplication.run(TckServer.class, args); + } +} diff --git a/tck/src/main/java/org/hiero/tck/annotation/JSONRPC2Controller.java b/tck/src/main/java/org/hiero/tck/annotation/JSONRPC2Controller.java new file mode 100644 index 0000000000..1945409f5e --- /dev/null +++ b/tck/src/main/java/org/hiero/tck/annotation/JSONRPC2Controller.java @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * Marks classes as HTTP handlers. + * In the context of this application these handlers + * should support the JSON-RPC spec + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Controller +@ResponseBody +public @interface JSONRPC2Controller {} diff --git a/tck/src/main/java/org/hiero/tck/annotation/JSONRPC2Method.java b/tck/src/main/java/org/hiero/tck/annotation/JSONRPC2Method.java new file mode 100644 index 0000000000..f641b75501 --- /dev/null +++ b/tck/src/main/java/org/hiero/tck/annotation/JSONRPC2Method.java @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Marks methods as JSON-RPC methods. + * Methods marked with this annotation will be registered + * as handlers for JSON-RPC requests with the specified method name. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface JSONRPC2Method { + /** + * Specifies the name of the JSON-RPC method. + * the params need to be of instance JSONRPC2Param because if not - will blow up at runtime + * @return The name of the JSON-RPC method. + */ + String value(); +} diff --git a/tck/src/main/java/org/hiero/tck/annotation/JSONRPC2Service.java b/tck/src/main/java/org/hiero/tck/annotation/JSONRPC2Service.java new file mode 100644 index 0000000000..9af3eaa321 --- /dev/null +++ b/tck/src/main/java/org/hiero/tck/annotation/JSONRPC2Service.java @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.springframework.stereotype.Component; + +/** + * Marks classes as JSON-RPC services. + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Component +public @interface JSONRPC2Service {} diff --git a/tck/src/main/java/org/hiero/tck/config/BeanConfig.java b/tck/src/main/java/org/hiero/tck/config/BeanConfig.java new file mode 100644 index 0000000000..881ea4c52f --- /dev/null +++ b/tck/src/main/java/org/hiero/tck/config/BeanConfig.java @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.config; + +import com.thetransactioncompany.jsonrpc2.server.Dispatcher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class BeanConfig { + @Bean + public Dispatcher dispatcher() { + return new Dispatcher(); + } +} diff --git a/tck/src/main/java/org/hiero/tck/config/WebConfig.java b/tck/src/main/java/org/hiero/tck/config/WebConfig.java new file mode 100644 index 0000000000..53929487a0 --- /dev/null +++ b/tck/src/main/java/org/hiero/tck/config/WebConfig.java @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.config; + +import org.hiero.tck.controller.JRPCInterceptor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(new JRPCInterceptor()); + } +} diff --git a/tck/src/main/java/org/hiero/tck/controller/JRPCController.java b/tck/src/main/java/org/hiero/tck/controller/JRPCController.java new file mode 100644 index 0000000000..434f55c822 --- /dev/null +++ b/tck/src/main/java/org/hiero/tck/controller/JRPCController.java @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.controller; + +import com.thetransactioncompany.jsonrpc2.JSONRPC2Request; +import com.thetransactioncompany.jsonrpc2.server.Dispatcher; +import jakarta.servlet.http.HttpServletRequest; +import org.hiero.tck.annotation.JSONRPC2Controller; +import org.hiero.tck.util.JSONRPC2ServiceScanner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.web.bind.annotation.PostMapping; + +@JSONRPC2Controller +public class JRPCController { + private final Logger logger = LoggerFactory.getLogger(JRPCController.class); + private final Dispatcher dispatcher; + + public JRPCController(final Dispatcher dispatcher, final ApplicationContext applicationContext) { + this.dispatcher = dispatcher; + JSONRPC2ServiceScanner.registerServices(dispatcher, applicationContext); + } + + /** + * Endpoint to handle all incoming JSON-RPC requests + */ + @PostMapping("/") + public String handleJSONRPC2Request(final HttpServletRequest request) { + var req = (JSONRPC2Request) request.getAttribute("jsonrpcRequest"); + var resp = dispatcher.process(req, null); + + if (resp.getError() != null) { + String errorMessage = String.format( + "Error occurred processing JSON-RPC request: %s, Response error: %s", + req.toJSONString(), resp.getError().toString()); + logger.info(errorMessage); + } + + return resp.toJSONString(); + } +} diff --git a/tck/src/main/java/org/hiero/tck/controller/JRPCInterceptor.java b/tck/src/main/java/org/hiero/tck/controller/JRPCInterceptor.java new file mode 100644 index 0000000000..944e110895 --- /dev/null +++ b/tck/src/main/java/org/hiero/tck/controller/JRPCInterceptor.java @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.controller; + +import com.thetransactioncompany.jsonrpc2.JSONRPC2ParseException; +import com.thetransactioncompany.jsonrpc2.JSONRPC2Request; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.BufferedReader; +import java.io.IOException; +import org.springframework.web.servlet.HandlerInterceptor; + +/** + * Interceptor class to map HttpServletRequest body to {@link JSONRPC2Request} + * before the request reaches the controller + */ +public class JRPCInterceptor implements HandlerInterceptor { + + @Override + public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) + throws Exception { + // Map HTTP Servlet Request to JSON-RPC Request + var jsonrpcRequest = mapToJSONRPC2Request(request); + + // Store the JSON-RPC request in request attribute for further processing + request.setAttribute("jsonrpcRequest", jsonrpcRequest); + + // Continue processing the request + return true; + } + + JSONRPC2Request mapToJSONRPC2Request(final HttpServletRequest httpRequest) + throws IOException, JSONRPC2ParseException { + // Read the JSON-RPC request from the HTTP request body + BufferedReader reader = httpRequest.getReader(); + StringBuilder requestBody = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + requestBody.append(line); + } + reader.close(); + + // Parse the JSON-RPC request + return JSONRPC2Request.parse(requestBody.toString()); + } +} diff --git a/tck/src/main/java/org/hiero/tck/exception/InvalidJSONRPC2ParamsException.java b/tck/src/main/java/org/hiero/tck/exception/InvalidJSONRPC2ParamsException.java new file mode 100644 index 0000000000..8144e8e297 --- /dev/null +++ b/tck/src/main/java/org/hiero/tck/exception/InvalidJSONRPC2ParamsException.java @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.exception; + +/** + * Thrown when the server cannot parse the given parameters. + * This error should be thrown from the param parser + */ +public class InvalidJSONRPC2ParamsException extends Exception { + + public InvalidJSONRPC2ParamsException() { + super(); + } + + public InvalidJSONRPC2ParamsException(String message) { + super(message); + } + + public InvalidJSONRPC2ParamsException(String message, Throwable cause) { + super(message, cause); + } + + public InvalidJSONRPC2ParamsException(Throwable cause) { + super(cause); + } +} diff --git a/tck/src/main/java/org/hiero/tck/exception/InvalidJSONRPC2RequestException.java b/tck/src/main/java/org/hiero/tck/exception/InvalidJSONRPC2RequestException.java new file mode 100644 index 0000000000..46ad8d6a3d --- /dev/null +++ b/tck/src/main/java/org/hiero/tck/exception/InvalidJSONRPC2RequestException.java @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.exception; + +/** + * Thrown when the server cannot process the request + */ +public class InvalidJSONRPC2RequestException extends Exception { + + public InvalidJSONRPC2RequestException(String message) { + super(message); + } +} diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/methods/AbstractJSONRPC2Service.java b/tck/src/main/java/org/hiero/tck/methods/AbstractJSONRPC2Service.java similarity index 82% rename from tck/src/main/java/com/hedera/hashgraph/tck/methods/AbstractJSONRPC2Service.java rename to tck/src/main/java/org/hiero/tck/methods/AbstractJSONRPC2Service.java index fcdf713dce..6e805d4dd4 100644 --- a/tck/src/main/java/com/hedera/hashgraph/tck/methods/AbstractJSONRPC2Service.java +++ b/tck/src/main/java/org/hiero/tck/methods/AbstractJSONRPC2Service.java @@ -1,32 +1,8 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.methods; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.methods; -import static com.hedera.hashgraph.tck.methods.JSONRPC2Error.HEDERA_ERROR; +import static org.hiero.tck.methods.JSONRPC2Error.HEDERA_ERROR; -import com.hedera.hashgraph.sdk.PrecheckStatusException; -import com.hedera.hashgraph.sdk.ReceiptStatusException; -import com.hedera.hashgraph.tck.annotation.JSONRPC2Method; -import com.hedera.hashgraph.tck.exception.InvalidJSONRPC2ParamsException; -import com.hedera.hashgraph.tck.exception.InvalidJSONRPC2RequestException; -import com.hedera.hashgraph.tck.methods.JSONRPC2Error.ErrorData; import com.thetransactioncompany.jsonrpc2.JSONRPC2Error; import com.thetransactioncompany.jsonrpc2.JSONRPC2Request; import com.thetransactioncompany.jsonrpc2.JSONRPC2Response; @@ -38,6 +14,12 @@ import java.util.HashMap; import java.util.Map; import net.minidev.json.JSONObject; +import org.hiero.sdk.PrecheckStatusException; +import org.hiero.sdk.ReceiptStatusException; +import org.hiero.tck.annotation.JSONRPC2Method; +import org.hiero.tck.exception.InvalidJSONRPC2ParamsException; +import org.hiero.tck.exception.InvalidJSONRPC2RequestException; +import org.hiero.tck.methods.JSONRPC2Error.ErrorData; /** * Implements RequestHandler and overrides some of the Dispatcher logic, diff --git a/tck/src/main/java/org/hiero/tck/methods/JSONRPC2Error.java b/tck/src/main/java/org/hiero/tck/methods/JSONRPC2Error.java new file mode 100644 index 0000000000..416db69e37 --- /dev/null +++ b/tck/src/main/java/org/hiero/tck/methods/JSONRPC2Error.java @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.methods; + +import org.hiero.sdk.Status; + +/** + * Custom JSON-RPC error definitions + */ +public class JSONRPC2Error { + private JSONRPC2Error() { + // static utility class + } + + public static final int HEDERA_STATUS_CODE = -32001; + public static final com.thetransactioncompany.jsonrpc2.JSONRPC2Error HEDERA_ERROR = + new com.thetransactioncompany.jsonrpc2.JSONRPC2Error(HEDERA_STATUS_CODE, "Hedera error"); + + public record ErrorData(Status status, String message) {} +} diff --git a/tck/src/main/java/org/hiero/tck/methods/JSONRPC2Param.java b/tck/src/main/java/org/hiero/tck/methods/JSONRPC2Param.java new file mode 100644 index 0000000000..8738002ea2 --- /dev/null +++ b/tck/src/main/java/org/hiero/tck/methods/JSONRPC2Param.java @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.methods; + +import java.util.Map; + +/** + * Abstract base class for JSON-RPC parameters. Every parameter POJO should extend this class + * and implement the {@link #parse} method. This method assists JSON-RPC services in creating + * parameters for their JSON-RPC methods from the request. + * + * IMPORTANT: + * all inheriting classes should include the following Lombok annotations: + * {@code @Getter}, {@code @AllArgsConstructor}, and {@code @NoArgsConstructor}. + * These annotations are needed for the instance creation via reflection. + * + */ +public abstract class JSONRPC2Param { + public abstract JSONRPC2Param parse(final Map jrpcParams) throws Exception; +} diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/AccountService.java b/tck/src/main/java/org/hiero/tck/methods/sdk/AccountService.java similarity index 79% rename from tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/AccountService.java rename to tck/src/main/java/org/hiero/tck/methods/sdk/AccountService.java index 576ac8117a..0280b00ab3 100644 --- a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/AccountService.java +++ b/tck/src/main/java/org/hiero/tck/methods/sdk/AccountService.java @@ -1,43 +1,25 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.methods.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.methods.sdk; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.AccountCreateTransaction; -import com.hedera.hashgraph.sdk.AccountDeleteTransaction; -import com.hedera.hashgraph.sdk.AccountId; -import com.hedera.hashgraph.sdk.AccountUpdateTransaction; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.HbarUnit; -import com.hedera.hashgraph.sdk.Status; -import com.hedera.hashgraph.sdk.TransactionReceipt; -import com.hedera.hashgraph.tck.annotation.JSONRPC2Method; -import com.hedera.hashgraph.tck.annotation.JSONRPC2Service; -import com.hedera.hashgraph.tck.methods.AbstractJSONRPC2Service; -import com.hedera.hashgraph.tck.methods.sdk.param.AccountCreateParams; -import com.hedera.hashgraph.tck.methods.sdk.param.AccountDeleteParams; -import com.hedera.hashgraph.tck.methods.sdk.param.AccountUpdateParams; -import com.hedera.hashgraph.tck.methods.sdk.response.AccountResponse; -import com.hedera.hashgraph.tck.util.KeyUtils; import java.time.Duration; import java.time.Instant; +import org.hiero.sdk.AccountCreateTransaction; +import org.hiero.sdk.AccountDeleteTransaction; +import org.hiero.sdk.AccountId; +import org.hiero.sdk.AccountUpdateTransaction; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.HbarUnit; +import org.hiero.sdk.Status; +import org.hiero.sdk.TransactionReceipt; +import org.hiero.tck.annotation.JSONRPC2Method; +import org.hiero.tck.annotation.JSONRPC2Service; +import org.hiero.tck.methods.AbstractJSONRPC2Service; +import org.hiero.tck.methods.sdk.param.AccountCreateParams; +import org.hiero.tck.methods.sdk.param.AccountDeleteParams; +import org.hiero.tck.methods.sdk.param.AccountUpdateParams; +import org.hiero.tck.methods.sdk.response.AccountResponse; +import org.hiero.tck.util.KeyUtils; /** * AccountService for account related methods diff --git a/tck/src/main/java/org/hiero/tck/methods/sdk/KeyService.java b/tck/src/main/java/org/hiero/tck/methods/sdk/KeyService.java new file mode 100644 index 0000000000..584d8ea8f5 --- /dev/null +++ b/tck/src/main/java/org/hiero/tck/methods/sdk/KeyService.java @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.methods.sdk; + +import com.google.protobuf.InvalidProtocolBufferException; +import org.bouncycastle.util.encoders.Hex; +import org.hiero.sdk.Key; +import org.hiero.sdk.KeyList; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.PublicKey; +import org.hiero.tck.annotation.JSONRPC2Method; +import org.hiero.tck.annotation.JSONRPC2Service; +import org.hiero.tck.exception.InvalidJSONRPC2RequestException; +import org.hiero.tck.methods.AbstractJSONRPC2Service; +import org.hiero.tck.methods.sdk.param.GenerateKeyParams; +import org.hiero.tck.methods.sdk.response.GenerateKeyResponse; +import org.hiero.tck.util.KeyUtils; +import org.hiero.tck.util.KeyUtils.KeyType; + +@JSONRPC2Service +public class KeyService extends AbstractJSONRPC2Service { + + @JSONRPC2Method("generateKey") + public GenerateKeyResponse generateKey(final GenerateKeyParams params) throws Exception { + // Make sure getFromKey() is only provided for ED25519_PUBLIC_KEY, ECDSA_SECP256k1_PUBLIC_KEY, or + // EVM_ADDRESS_KEY + if (params.getFromKey().isPresent() + && !params.getType().equals(KeyType.ED25519_PUBLIC_KEY) + && !params.getType().equals(KeyType.ECDSA_SECP256K1_PUBLIC_KEY) + && !params.getType().equals(KeyType.EVM_ADDRESS_KEY)) { + throw new InvalidJSONRPC2RequestException( + "invalid parameters: fromKey should only be provided for ed25519PublicKey, ecdsaSecp256k1PublicKey, or evmAddress types."); + } + + // Make sure threshold is only provided for THRESHOLD_KEY_TYPE. + if (params.getThreshold().isPresent() && !params.getType().equals(KeyType.THRESHOLD_KEY)) { + throw new InvalidJSONRPC2RequestException( + "invalid parameters: threshold should only be provided for thresholdKey types."); + } + + // Make sure keys is only provided for LIST_KEY_TYPE or THRESHOLD_KEY_TYPE + if (params.getKeys().isPresent() + && !params.getType().equals(KeyType.LIST_KEY) + && !params.getType().equals(KeyType.THRESHOLD_KEY)) { + throw new InvalidJSONRPC2RequestException( + "invalid parameters: keys should only be provided for keyList or thresholdKey types."); + } + + if ((params.getType().equals(KeyType.THRESHOLD_KEY) || params.getType().equals(KeyType.LIST_KEY)) + && params.getKeys().isEmpty()) { + throw new InvalidJSONRPC2RequestException( + "invalid request: keys list is required for generating a KeyList type."); + } + + if (params.getType().equals(KeyType.THRESHOLD_KEY) + && params.getThreshold().isEmpty()) { + throw new InvalidJSONRPC2RequestException( + "invalid request: threshold is required for generating a ThresholdKey type."); + } + + GenerateKeyResponse response = new GenerateKeyResponse(); + response.setKey(processKeyRecursively(params, response, false)); + return response; + } + + private String processKeyRecursively( + final GenerateKeyParams params, final GenerateKeyResponse response, boolean isList) + throws InvalidJSONRPC2RequestException, InvalidProtocolBufferException { + String privateKeyString; + PrivateKey privateKey; + switch (params.getType()) { + case ED25519_PRIVATE_KEY, ECDSA_SECP256K1_PRIVATE_KEY: + privateKeyString = params.getType().equals(KeyType.ED25519_PRIVATE_KEY) + ? PrivateKey.generateED25519().toStringDER() + : PrivateKey.generateECDSA().toStringDER(); + if (isList) { + response.getPrivateKeys().add(privateKeyString); + } + + return privateKeyString; + + case ED25519_PUBLIC_KEY, ECDSA_SECP256K1_PUBLIC_KEY: + if (params.getFromKey().isPresent()) { + return PrivateKey.fromString(params.getFromKey().get()) + .getPublicKey() + .toStringDER(); + } + privateKey = params.getType().equals(KeyType.ED25519_PUBLIC_KEY) + ? PrivateKey.generateED25519() + : PrivateKey.generateECDSA(); + if (isList) { + response.getPrivateKeys().add(privateKey.toStringDER()); + } + + return privateKey.getPublicKey().toStringDER(); + + case LIST_KEY, THRESHOLD_KEY: + KeyList keyList = new KeyList(); + params.getKeys().get().forEach(keyParams -> { + try { + keyList.add(KeyUtils.getKeyFromString(processKeyRecursively(keyParams, response, true))); + } catch (Exception e) { + throw new IllegalArgumentException(e); + } + }); + + if (params.getType().equals(KeyType.THRESHOLD_KEY)) { + keyList.setThreshold(params.getThreshold().get().intValue()); + } + + return Hex.toHexString(keyList.toBytes()); + + case EVM_ADDRESS_KEY: + if (params.getFromKey().isPresent()) { + Key hederaKey = + KeyUtils.getKeyFromString(params.getFromKey().get()); + if (hederaKey instanceof PrivateKey pk) { + return pk.getPublicKey().toEvmAddress().toString(); + } else if (hederaKey instanceof PublicKey pk) { + return pk.toEvmAddress().toString(); + } else { + throw new InvalidJSONRPC2RequestException( + "invalid parameters: fromKey for evmAddress is not ECDSAsecp256k1."); + } + } + return PrivateKey.generateECDSA().getPublicKey().toEvmAddress().toString(); + + default: + throw new InvalidJSONRPC2RequestException("invalid request: key type not recognized."); + } + } +} diff --git a/tck/src/main/java/org/hiero/tck/methods/sdk/SdkService.java b/tck/src/main/java/org/hiero/tck/methods/sdk/SdkService.java new file mode 100644 index 0000000000..84f069a0d3 --- /dev/null +++ b/tck/src/main/java/org/hiero/tck/methods/sdk/SdkService.java @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.methods.sdk; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executors; +import org.hiero.sdk.AccountId; +import org.hiero.sdk.Client; +import org.hiero.sdk.PrivateKey; +import org.hiero.tck.annotation.JSONRPC2Method; +import org.hiero.tck.annotation.JSONRPC2Service; +import org.hiero.tck.methods.AbstractJSONRPC2Service; +import org.hiero.tck.methods.sdk.param.SetupParams; +import org.hiero.tck.methods.sdk.response.SetupResponse; + +/** + * SdkService for managing the {@link Client} setup and reset + */ +@JSONRPC2Service +public class SdkService extends AbstractJSONRPC2Service { + // this is shared state to all requests so there could be race conditions + // although the tck driver would not call these methods in such way + private Client client; + + @JSONRPC2Method("setup") + public SetupResponse setup(final SetupParams params) throws Exception { + var clientExecutor = Executors.newFixedThreadPool(16); + String clientType; + if (params.getNodeIp().isPresent() + && params.getNodeAccountId().isPresent() + && params.getMirrorNetworkIp().isPresent()) { + // Custom client setup + Map node = new HashMap<>(); + var nodeId = AccountId.fromString(params.getNodeAccountId().get()); + node.put(params.getNodeIp().get(), nodeId); + client = Client.forNetwork(node, clientExecutor); + clientType = "custom"; + client.setMirrorNetwork(List.of(params.getMirrorNetworkIp().get())); + } else { + // Default to testnet + client = Client.forTestnet(clientExecutor); + clientType = "testnet"; + } + + client.setOperator( + AccountId.fromString(params.getOperatorAccountId()), + PrivateKey.fromString(params.getOperatorPrivateKey())); + return new SetupResponse("Successfully setup " + clientType + " client."); + } + + @JSONRPC2Method("reset") + public SetupResponse reset() throws Exception { + client.close(); + client = null; + return new SetupResponse(""); + } + + public Client getClient() { + return this.client; + } +} diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/TokenService.java b/tck/src/main/java/org/hiero/tck/methods/sdk/TokenService.java similarity index 88% rename from tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/TokenService.java rename to tck/src/main/java/org/hiero/tck/methods/sdk/TokenService.java index 832678f27d..2a3e42763c 100644 --- a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/TokenService.java +++ b/tck/src/main/java/org/hiero/tck/methods/sdk/TokenService.java @@ -1,49 +1,30 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.methods.sdk; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.methods.sdk; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hashgraph.sdk.AccountId; -import com.hedera.hashgraph.sdk.CustomFixedFee; -import com.hedera.hashgraph.sdk.CustomFractionalFee; -import com.hedera.hashgraph.sdk.CustomRoyaltyFee; -import com.hedera.hashgraph.sdk.Status; -import com.hedera.hashgraph.sdk.TokenCreateTransaction; -import com.hedera.hashgraph.sdk.TokenDeleteTransaction; -import com.hedera.hashgraph.sdk.TokenId; -import com.hedera.hashgraph.sdk.TokenSupplyType; -import com.hedera.hashgraph.sdk.TokenType; -import com.hedera.hashgraph.sdk.TokenUpdateTransaction; -import com.hedera.hashgraph.sdk.TransactionReceipt; -import com.hedera.hashgraph.tck.annotation.JSONRPC2Method; -import com.hedera.hashgraph.tck.annotation.JSONRPC2Service; -import com.hedera.hashgraph.tck.methods.AbstractJSONRPC2Service; -import com.hedera.hashgraph.tck.methods.sdk.param.TokenCreateParams; -import com.hedera.hashgraph.tck.methods.sdk.param.TokenDeleteParams; -import com.hedera.hashgraph.tck.methods.sdk.param.TokenUpdateParams; -import com.hedera.hashgraph.tck.methods.sdk.response.TokenResponse; -import com.hedera.hashgraph.tck.util.KeyUtils; import java.time.Duration; import java.util.ArrayList; import java.util.List; +import org.hiero.sdk.AccountId; +import org.hiero.sdk.CustomFixedFee; +import org.hiero.sdk.CustomFractionalFee; +import org.hiero.sdk.CustomRoyaltyFee; +import org.hiero.sdk.Status; +import org.hiero.sdk.TokenCreateTransaction; +import org.hiero.sdk.TokenDeleteTransaction; +import org.hiero.sdk.TokenId; +import org.hiero.sdk.TokenSupplyType; +import org.hiero.sdk.TokenType; +import org.hiero.sdk.TokenUpdateTransaction; +import org.hiero.sdk.TransactionReceipt; +import org.hiero.tck.annotation.JSONRPC2Method; +import org.hiero.tck.annotation.JSONRPC2Service; +import org.hiero.tck.methods.AbstractJSONRPC2Service; +import org.hiero.tck.methods.sdk.param.TokenCreateParams; +import org.hiero.tck.methods.sdk.param.TokenDeleteParams; +import org.hiero.tck.methods.sdk.param.TokenUpdateParams; +import org.hiero.tck.methods.sdk.response.TokenResponse; +import org.hiero.tck.util.KeyUtils; /** * TokenService for token related methods @@ -173,7 +154,7 @@ public TokenResponse createToken(final TokenCreateParams params) throws Exceptio params.getMaxSupply().ifPresent(maxSupply -> tokenCreateTransaction.setMaxSupply(Long.valueOf(maxSupply))); params.getCustomFees().ifPresent(customFees -> { - List customFeeList = new ArrayList<>(); + List customFeeList = new ArrayList<>(); for (var customFee : customFees) { // set fixed fees customFee.getFixedFee().ifPresent(fixedFee -> { diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/param/AccountCreateParams.java b/tck/src/main/java/org/hiero/tck/methods/sdk/param/AccountCreateParams.java similarity index 79% rename from tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/param/AccountCreateParams.java rename to tck/src/main/java/org/hiero/tck/methods/sdk/param/AccountCreateParams.java index 89b2e5883e..b99c9c7a20 100644 --- a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/param/AccountCreateParams.java +++ b/tck/src/main/java/org/hiero/tck/methods/sdk/param/AccountCreateParams.java @@ -1,31 +1,13 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.methods.sdk.param; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.methods.sdk.param; -import com.hedera.hashgraph.tck.methods.JSONRPC2Param; import java.util.Map; import java.util.Optional; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import net.minidev.json.JSONObject; +import org.hiero.tck.methods.JSONRPC2Param; /** * AccountCreateParams for account create method diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/param/AccountDeleteParams.java b/tck/src/main/java/org/hiero/tck/methods/sdk/param/AccountDeleteParams.java similarity index 91% rename from tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/param/AccountDeleteParams.java rename to tck/src/main/java/org/hiero/tck/methods/sdk/param/AccountDeleteParams.java index 1c318ccafc..f3770f2d17 100644 --- a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/param/AccountDeleteParams.java +++ b/tck/src/main/java/org/hiero/tck/methods/sdk/param/AccountDeleteParams.java @@ -1,12 +1,13 @@ -package com.hedera.hashgraph.tck.methods.sdk.param; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.methods.sdk.param; -import com.hedera.hashgraph.tck.methods.JSONRPC2Param; import java.util.Map; import java.util.Optional; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import net.minidev.json.JSONObject; +import org.hiero.tck.methods.JSONRPC2Param; /** * AccountDeleteParams for account delete method diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/param/AccountUpdateParams.java b/tck/src/main/java/org/hiero/tck/methods/sdk/param/AccountUpdateParams.java similarity index 95% rename from tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/param/AccountUpdateParams.java rename to tck/src/main/java/org/hiero/tck/methods/sdk/param/AccountUpdateParams.java index e080c07448..381cd411a6 100644 --- a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/param/AccountUpdateParams.java +++ b/tck/src/main/java/org/hiero/tck/methods/sdk/param/AccountUpdateParams.java @@ -1,12 +1,13 @@ -package com.hedera.hashgraph.tck.methods.sdk.param; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.methods.sdk.param; -import com.hedera.hashgraph.tck.methods.JSONRPC2Param; import java.util.Map; import java.util.Optional; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import net.minidev.json.JSONObject; +import org.hiero.tck.methods.JSONRPC2Param; /** * AccountUpdateParams for account update method diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/param/CommonTransactionParams.java b/tck/src/main/java/org/hiero/tck/methods/sdk/param/CommonTransactionParams.java similarity index 75% rename from tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/param/CommonTransactionParams.java rename to tck/src/main/java/org/hiero/tck/methods/sdk/param/CommonTransactionParams.java index 28d9aec662..a9d005fab9 100644 --- a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/param/CommonTransactionParams.java +++ b/tck/src/main/java/org/hiero/tck/methods/sdk/param/CommonTransactionParams.java @@ -1,29 +1,6 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.methods.sdk.param; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.methods.sdk.param; -import com.hedera.hashgraph.sdk.Client; -import com.hedera.hashgraph.sdk.Hbar; -import com.hedera.hashgraph.sdk.PrivateKey; -import com.hedera.hashgraph.sdk.Transaction; -import com.hedera.hashgraph.sdk.TransactionId; import java.time.Duration; import java.util.List; import java.util.Map; @@ -33,6 +10,11 @@ import lombok.Getter; import lombok.NoArgsConstructor; import net.minidev.json.JSONArray; +import org.hiero.sdk.Client; +import org.hiero.sdk.Hbar; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.Transaction; +import org.hiero.sdk.TransactionId; /** * CommonTransactionParams diff --git a/tck/src/main/java/org/hiero/tck/methods/sdk/param/CustomFee.java b/tck/src/main/java/org/hiero/tck/methods/sdk/param/CustomFee.java new file mode 100644 index 0000000000..13ddae55b0 --- /dev/null +++ b/tck/src/main/java/org/hiero/tck/methods/sdk/param/CustomFee.java @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.methods.sdk.param; + +import java.util.Map; +import java.util.Optional; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import net.minidev.json.JSONObject; +import org.hiero.tck.methods.JSONRPC2Param; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +public class CustomFee extends JSONRPC2Param { + + private String feeCollectorAccountId; + private Boolean feeCollectorsExempt; + private Optional fixedFee; + private Optional fractionalFee; + private Optional royaltyFee; + + @Override + public CustomFee parse(Map jrpcParams) throws Exception { + var feeCollectorAccountIdParsed = (String) jrpcParams.get("feeCollectorAccountId"); + var feeCollectorsExemptParsed = (Boolean) jrpcParams.get("feeCollectorsExempt"); + + Optional fixedFeeParsed = Optional.empty(); + if (jrpcParams.containsKey("fixedFee")) { + JSONObject jsonObject = (JSONObject) jrpcParams.get("fixedFee"); + fixedFeeParsed = Optional.of(FixedFee.parse(jsonObject)); + } + + Optional fractionalFeeParsed = Optional.empty(); + if (jrpcParams.containsKey("fractionalFee")) { + JSONObject jsonObject = (JSONObject) jrpcParams.get("fractionalFee"); + fractionalFeeParsed = Optional.of(FractionalFee.parse(jsonObject)); + } + + Optional royaltyFeeParsed = Optional.empty(); + if (jrpcParams.containsKey("royaltyFee")) { + JSONObject jsonObject = (JSONObject) jrpcParams.get("royaltyFee"); + royaltyFeeParsed = Optional.of(RoyaltyFee.parse(jsonObject)); + } + + return new CustomFee( + feeCollectorAccountIdParsed, + feeCollectorsExemptParsed, + fixedFeeParsed, + fractionalFeeParsed, + royaltyFeeParsed); + } + + @Getter + @AllArgsConstructor + @NoArgsConstructor + public static class FixedFee { + private String amount; + private Optional denominatingTokenId; + + public static FixedFee parse(Map jrpcParams) throws Exception { + var amountParsed = (String) jrpcParams.get("amount"); + var denominatingTokenIdParsed = Optional.ofNullable((String) jrpcParams.get("denominatingTokenId")); + return new FixedFee(amountParsed, denominatingTokenIdParsed); + } + } + + @Getter + @AllArgsConstructor + @NoArgsConstructor + public static class FractionalFee { + private String numerator; + private String denominator; + private String minimumAmount; + private String maximumAmount; + + public static FractionalFee parse(Map jrpcParams) throws Exception { + var numeratorParsed = (String) jrpcParams.get("numerator"); + var denominatorParsed = (String) jrpcParams.get("denominator"); + var minimumAmountParsed = (String) jrpcParams.get("minimumAmount"); + var maximumAmountParsed = (String) jrpcParams.get("maximumAmount"); + return new FractionalFee(numeratorParsed, denominatorParsed, minimumAmountParsed, maximumAmountParsed); + } + } + + @Getter + @AllArgsConstructor + @NoArgsConstructor + public static class RoyaltyFee { + private String numerator; + private String denominator; + private Optional fallbackFee; + + public static RoyaltyFee parse(Map jrpcParams) throws Exception { + var numeratorParsed = (String) jrpcParams.get("numerator"); + var denominatorParsed = (String) jrpcParams.get("denominator"); + + Optional fallbackFeeParsed = Optional.empty(); + if (jrpcParams.containsKey("fallbackFee")) { + JSONObject jsonObject = (JSONObject) jrpcParams.get("fallbackFee"); + fallbackFeeParsed = Optional.of(FixedFee.parse(jsonObject)); + } + + return new RoyaltyFee(numeratorParsed, denominatorParsed, fallbackFeeParsed); + } + } +} diff --git a/tck/src/main/java/org/hiero/tck/methods/sdk/param/GenerateKeyParams.java b/tck/src/main/java/org/hiero/tck/methods/sdk/param/GenerateKeyParams.java new file mode 100644 index 0000000000..dce8179358 --- /dev/null +++ b/tck/src/main/java/org/hiero/tck/methods/sdk/param/GenerateKeyParams.java @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.methods.sdk.param; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import net.minidev.json.JSONArray; +import net.minidev.json.JSONObject; +import org.hiero.tck.methods.JSONRPC2Param; +import org.hiero.tck.util.KeyUtils.KeyType; + +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +@Getter +@AllArgsConstructor +@NoArgsConstructor +public class GenerateKeyParams extends JSONRPC2Param { + private KeyType type; + private Optional fromKey; + private Optional threshold; + private Optional> keys; + + @Override + public GenerateKeyParams parse(Map jrpcParams) throws Exception { + var parsedType = (String) jrpcParams.get("type"); + var parsedFromKey = Optional.ofNullable((String) jrpcParams.get("fromKey")); + var parsedThreshold = Optional.ofNullable((Long) jrpcParams.get("threshold")); + + Optional> parsedKeys = Optional.empty(); + if (jrpcParams.containsKey("keys")) { + JSONArray jsonArray = (JSONArray) jrpcParams.get("keys"); + List keyList = new ArrayList<>(); + for (Object o : jsonArray) { + JSONObject jsonObject = (JSONObject) o; + GenerateKeyParams keyParam = new GenerateKeyParams().parse(jsonObject); + keyList.add(keyParam); + } + parsedKeys = Optional.of(keyList); + } + + return new GenerateKeyParams(KeyType.fromString(parsedType), parsedFromKey, parsedThreshold, parsedKeys); + } +} diff --git a/tck/src/main/java/org/hiero/tck/methods/sdk/param/SetupParams.java b/tck/src/main/java/org/hiero/tck/methods/sdk/param/SetupParams.java new file mode 100644 index 0000000000..9d62568be8 --- /dev/null +++ b/tck/src/main/java/org/hiero/tck/methods/sdk/param/SetupParams.java @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.methods.sdk.param; + +import java.util.Map; +import java.util.Optional; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.hiero.tck.methods.JSONRPC2Param; + +/** + * SetupParams for SDK client + */ +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +@Getter +@AllArgsConstructor +@NoArgsConstructor +public class SetupParams extends JSONRPC2Param { + private String operatorAccountId; + private String operatorPrivateKey; + private Optional nodeIp; + private Optional nodeAccountId; + private Optional mirrorNetworkIp; + + @Override + public SetupParams parse(Map jrpcParams) throws ClassCastException { + var parsedOperatorAccountId = (String) jrpcParams.get("operatorAccountId"); + var parsedOperatorPrivateKey = (String) jrpcParams.get("operatorPrivateKey"); + var parsedNodeIp = Optional.ofNullable((String) jrpcParams.get("nodeIp")); + var parsedNodeAccountId = Optional.ofNullable((String) jrpcParams.get("nodeAccountId")); + var parsedMirrorNetworkIp = Optional.ofNullable((String) jrpcParams.get("mirrorNetworkIp")); + + return new SetupParams( + parsedOperatorAccountId, + parsedOperatorPrivateKey, + parsedNodeIp, + parsedNodeAccountId, + parsedMirrorNetworkIp); + } +} diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/param/TokenCreateParams.java b/tck/src/main/java/org/hiero/tck/methods/sdk/param/TokenCreateParams.java similarity index 87% rename from tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/param/TokenCreateParams.java rename to tck/src/main/java/org/hiero/tck/methods/sdk/param/TokenCreateParams.java index 49cbd450b7..c0025653a7 100644 --- a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/param/TokenCreateParams.java +++ b/tck/src/main/java/org/hiero/tck/methods/sdk/param/TokenCreateParams.java @@ -1,26 +1,6 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.methods.sdk.param; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.methods.sdk.param; -import com.hedera.hashgraph.tck.methods.JSONRPC2Param; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -30,6 +10,7 @@ import lombok.NoArgsConstructor; import net.minidev.json.JSONArray; import net.minidev.json.JSONObject; +import org.hiero.tck.methods.JSONRPC2Param; /** * TokenCreateParams for token create method diff --git a/tck/src/main/java/org/hiero/tck/methods/sdk/param/TokenDeleteParams.java b/tck/src/main/java/org/hiero/tck/methods/sdk/param/TokenDeleteParams.java new file mode 100644 index 0000000000..b8fc459bff --- /dev/null +++ b/tck/src/main/java/org/hiero/tck/methods/sdk/param/TokenDeleteParams.java @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.methods.sdk.param; + +import java.util.Map; +import java.util.Optional; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import net.minidev.json.JSONObject; +import org.hiero.tck.methods.JSONRPC2Param; + +/** + * TokenDeleteParams for token delete method + */ +@Getter +@AllArgsConstructor +@NoArgsConstructor +public class TokenDeleteParams extends JSONRPC2Param { + private Optional tokenId; + private Optional commonTransactionParams; + + @Override + public JSONRPC2Param parse(Map jrpcParams) throws Exception { + var parsedTokenId = Optional.ofNullable((String) jrpcParams.get("tokenId")); + Optional parsedCommonTransactionParams = Optional.empty(); + if (jrpcParams.containsKey("commonTransactionParams")) { + JSONObject jsonObject = (JSONObject) jrpcParams.get("commonTransactionParams"); + parsedCommonTransactionParams = Optional.of(CommonTransactionParams.parse(jsonObject)); + } + + return new TokenDeleteParams(parsedTokenId, parsedCommonTransactionParams); + } +} diff --git a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/param/TokenUpdateParams.java b/tck/src/main/java/org/hiero/tck/methods/sdk/param/TokenUpdateParams.java similarity index 83% rename from tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/param/TokenUpdateParams.java rename to tck/src/main/java/org/hiero/tck/methods/sdk/param/TokenUpdateParams.java index 01902fc8e5..745ff30e13 100644 --- a/tck/src/main/java/com/hedera/hashgraph/tck/methods/sdk/param/TokenUpdateParams.java +++ b/tck/src/main/java/org/hiero/tck/methods/sdk/param/TokenUpdateParams.java @@ -1,32 +1,13 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.methods.sdk.param; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.methods.sdk.param; -import com.hedera.hashgraph.tck.methods.JSONRPC2Param; import java.util.Map; import java.util.Optional; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import net.minidev.json.JSONObject; +import org.hiero.tck.methods.JSONRPC2Param; /** * TokenUpdateParams for token update method diff --git a/tck/src/main/java/org/hiero/tck/methods/sdk/response/AccountResponse.java b/tck/src/main/java/org/hiero/tck/methods/sdk/response/AccountResponse.java new file mode 100644 index 0000000000..b2eef2f95d --- /dev/null +++ b/tck/src/main/java/org/hiero/tck/methods/sdk/response/AccountResponse.java @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.methods.sdk.response; + +import lombok.Data; +import org.hiero.sdk.Status; + +@Data +public class AccountResponse { + private final String accountId; + private final Status status; +} diff --git a/tck/src/main/java/org/hiero/tck/methods/sdk/response/GenerateKeyResponse.java b/tck/src/main/java/org/hiero/tck/methods/sdk/response/GenerateKeyResponse.java new file mode 100644 index 0000000000..9098104605 --- /dev/null +++ b/tck/src/main/java/org/hiero/tck/methods/sdk/response/GenerateKeyResponse.java @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.methods.sdk.response; + +import java.util.ArrayList; +import java.util.List; +import lombok.Data; + +@Data +public class GenerateKeyResponse { + private String key; + private List privateKeys = new ArrayList<>(); +} diff --git a/tck/src/main/java/org/hiero/tck/methods/sdk/response/SetupResponse.java b/tck/src/main/java/org/hiero/tck/methods/sdk/response/SetupResponse.java new file mode 100644 index 0000000000..d0988aebdf --- /dev/null +++ b/tck/src/main/java/org/hiero/tck/methods/sdk/response/SetupResponse.java @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.methods.sdk.response; + +import lombok.Data; + +@Data +public class SetupResponse { + private String message = ""; + private String status = ""; + + public SetupResponse(String message) { + if (message != null && !message.isEmpty()) { + this.message = message; + } + this.status = "SUCCESS"; + } +} diff --git a/tck/src/main/java/org/hiero/tck/methods/sdk/response/TokenResponse.java b/tck/src/main/java/org/hiero/tck/methods/sdk/response/TokenResponse.java new file mode 100644 index 0000000000..6dfc12013f --- /dev/null +++ b/tck/src/main/java/org/hiero/tck/methods/sdk/response/TokenResponse.java @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.methods.sdk.response; + +import lombok.Data; +import org.hiero.sdk.Status; + +@Data +public class TokenResponse { + private final String tokenId; + private final Status status; +} diff --git a/tck/src/main/java/org/hiero/tck/util/JSONRPC2ServiceScanner.java b/tck/src/main/java/org/hiero/tck/util/JSONRPC2ServiceScanner.java new file mode 100644 index 0000000000..1d2585d299 --- /dev/null +++ b/tck/src/main/java/org/hiero/tck/util/JSONRPC2ServiceScanner.java @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.util; + +import com.thetransactioncompany.jsonrpc2.server.*; +import org.hiero.tck.annotation.JSONRPC2Service; +import org.springframework.context.ApplicationContext; + +/** + * Utility class to register all {@link JSONRPC2Service} annotated classes + * to the JSON-RPC dispatcher + */ +public class JSONRPC2ServiceScanner { + private JSONRPC2ServiceScanner() { + // private constructor for utility class + } + + public static void registerServices(Dispatcher dispatcher, ApplicationContext context) { + String[] serviceNames = context.getBeanNamesForAnnotation(JSONRPC2Service.class); + if (serviceNames != null) { // NOSONAR + for (String serviceName : serviceNames) { + Object service = context.getBean(serviceName); + dispatcher.register((RequestHandler) service); + } + } + } +} diff --git a/tck/src/main/java/org/hiero/tck/util/KeyUtils.java b/tck/src/main/java/org/hiero/tck/util/KeyUtils.java new file mode 100644 index 0000000000..1609a55786 --- /dev/null +++ b/tck/src/main/java/org/hiero/tck/util/KeyUtils.java @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.util; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import org.hiero.sdk.Key; +import org.hiero.sdk.PrivateKey; +import org.hiero.sdk.PublicKey; + +public final class KeyUtils { + + public enum KeyType { + ED25519_PRIVATE_KEY("ed25519PrivateKey"), + ED25519_PUBLIC_KEY("ed25519PublicKey"), + ECDSA_SECP256K1_PRIVATE_KEY("ecdsaSecp256k1PrivateKey"), + ECDSA_SECP256K1_PUBLIC_KEY("ecdsaSecp256k1PublicKey"), + LIST_KEY("keyList"), + THRESHOLD_KEY("thresholdKey"), + EVM_ADDRESS_KEY("evmAddress"); + + private final String keyString; + + KeyType(String keyString) { + this.keyString = keyString; + } + + public static KeyType fromString(String keyString) { + for (KeyType type : KeyType.values()) { + if (type.keyString.equals(keyString)) { + return type; + } + } + throw new IllegalArgumentException("Unknown key type: " + keyString); + } + } + + public static Key getKeyFromString(String keyString) throws InvalidProtocolBufferException { + try { + return PublicKey.fromStringDER(keyString); + } catch (Exception e) { + try { + return PrivateKey.fromStringDER(keyString); + } catch (Exception ex) { + return Key.fromBytes(ByteString.fromHex(keyString).toByteArray()); + } + } + } +} diff --git a/tck/src/test/java/com/hedera/hashgraph/tck/controller/JRPCInterceptorTest.java b/tck/src/test/java/com/hedera/hashgraph/tck/controller/JRPCInterceptorTest.java deleted file mode 100644 index acdb7b7282..0000000000 --- a/tck/src/test/java/com/hedera/hashgraph/tck/controller/JRPCInterceptorTest.java +++ /dev/null @@ -1,80 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.controller; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -import com.thetransactioncompany.jsonrpc2.JSONRPC2Request; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import java.io.BufferedReader; -import java.io.StringReader; -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 JRPCInterceptorTest { - - @Mock - HttpServletRequest request; - - @Mock - HttpServletResponse response; - - @Test - void testMapToJSONRPC2Request() throws Exception { - // given - String requestBody = - "{\"jsonrpc\": \"2.0\", \"method\": \"testMethod\", \"params\": {\"param1\": \"value1\"}, \"id\": 1}"; - BufferedReader reader = new BufferedReader(new StringReader(requestBody)); - when(request.getReader()).thenReturn(reader); - - JRPCInterceptor interceptor = new JRPCInterceptor(); - - // when - JSONRPC2Request jsonrpcRequest = interceptor.mapToJSONRPC2Request(request); - - // then - assertNotNull(jsonrpcRequest); - assertEquals("testMethod", jsonrpcRequest.getMethod()); - assertEquals(1L, jsonrpcRequest.getID()); - } - - @Test - void testPreHandle() throws Exception { - // given - String requestBody = - "{\"jsonrpc\": \"2.0\", \"method\": \"testMethod\", \"params\": {\"param1\": \"value1\"}, \"id\": 1}"; - BufferedReader reader = new BufferedReader(new StringReader(requestBody)); - when(request.getReader()).thenReturn(reader); - - JRPCInterceptor interceptor = new JRPCInterceptor(); - - // when - boolean result = interceptor.preHandle(request, response, null); - - // then - assertTrue(result); - verify(request).setAttribute(eq("jsonrpcRequest"), any(JSONRPC2Request.class)); - } -} diff --git a/tck/src/test/java/com/hedera/hashgraph/tck/methods/sdk/SdkServiceTest.java b/tck/src/test/java/com/hedera/hashgraph/tck/methods/sdk/SdkServiceTest.java deleted file mode 100644 index 4f01fa0868..0000000000 --- a/tck/src/test/java/com/hedera/hashgraph/tck/methods/sdk/SdkServiceTest.java +++ /dev/null @@ -1,73 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.methods.sdk; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import com.hedera.hashgraph.tck.methods.sdk.param.SetupParams; -import com.hedera.hashgraph.tck.methods.sdk.response.SetupResponse; -import java.util.Optional; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; - -@ExtendWith(MockitoExtension.class) -class SdkServiceTest { - - private SdkService sdkService = new SdkService(); - - @Test - void testSetup() throws Exception { - // Given - SetupParams params = new SetupParams( - "0.0.2", - "302e020100300506032b65700422042091132178e72057a1d7528025956fe39b0b847f200ab59b2fdd367017f3087137", - Optional.of("127.0.0.1:50211"), - Optional.of("0.0.3"), - Optional.of("http://127.0.0.1:5551")); - - // When - SetupResponse response = sdkService.setup(params); - - // Then - assertEquals("Successfully setup custom client.", response.getMessage()); - - response = sdkService.reset(); - - assertEquals("", response.getMessage()); - assertNull(sdkService.getClient()); - } - - @Test - void testSetupFail() { - // Given - SetupParams params = new SetupParams( - "operatorAccountId", - "operatorPrivateKey", - Optional.of("nodeIp"), - Optional.of("3asdf"), - Optional.of("127.0.0.1:50211")); - - // then - assertThrows(Exception.class, () -> sdkService.setup(params)); - } -} diff --git a/tck/src/test/java/com/hedera/hashgraph/tck/methods/sdk/param/CommonTransactionParamsTest.java b/tck/src/test/java/com/hedera/hashgraph/tck/methods/sdk/param/CommonTransactionParamsTest.java deleted file mode 100644 index af13bf44ef..0000000000 --- a/tck/src/test/java/com/hedera/hashgraph/tck/methods/sdk/param/CommonTransactionParamsTest.java +++ /dev/null @@ -1,58 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.methods.sdk.param; - -import static org.junit.jupiter.api.Assertions.*; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import net.minidev.json.JSONArray; -import org.junit.jupiter.api.Test; - -class CommonTransactionParamsTest { - @Test - void testCommonTransactionParamsParse() { - Map jrpcParams = new HashMap<>(); - jrpcParams.put("transactionId", "txId"); - jrpcParams.put("maxTransactionFee", 100L); - jrpcParams.put("validTransactionDuration", 120L); - jrpcParams.put("memo", "commonMemo"); - jrpcParams.put("regenerateTransactionId", true); - JSONArray signersArray = new JSONArray(); - signersArray.add( - "302e020100300506032b657004220420c1ed50ed4b024f5df25992d1fc4b8c5b4e3c3db63a5ff5fa05857f5b4b90f3bc"); - jrpcParams.put("signers", signersArray); - - CommonTransactionParams params = CommonTransactionParams.parse(jrpcParams); - - assertEquals(Optional.of("txId"), params.getTransactionId()); - assertEquals(Optional.of(100L), params.getMaxTransactionFee()); - assertEquals(Optional.of(120L), params.getValidTransactionDuration()); - assertEquals(Optional.of("commonMemo"), params.getMemo()); - assertEquals(Optional.of(true), params.getRegenerateTransactionId()); - assertEquals( - Optional.of( - List.of( - "302e020100300506032b657004220420c1ed50ed4b024f5df25992d1fc4b8c5b4e3c3db63a5ff5fa05857f5b4b90f3bc")), - params.getSigners()); - } -} diff --git a/tck/src/test/java/com/hedera/hashgraph/tck/methods/sdk/param/SetupParamsTest.java b/tck/src/test/java/com/hedera/hashgraph/tck/methods/sdk/param/SetupParamsTest.java deleted file mode 100644 index eefd98b26c..0000000000 --- a/tck/src/test/java/com/hedera/hashgraph/tck/methods/sdk/param/SetupParamsTest.java +++ /dev/null @@ -1,51 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.methods.sdk.param; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import org.junit.jupiter.api.Test; - -class SetupParamsTest { - - @Test - void testParse() { - // Given - Map jrpcParams = new HashMap<>(); - jrpcParams.put("operatorAccountId", "testAccountId"); - jrpcParams.put("operatorPrivateKey", "testPrivateKey"); - jrpcParams.put("nodeIp", "testNodeIp"); - jrpcParams.put("nodeAccountId", "testNodeAccountId"); - jrpcParams.put("mirrorNetworkIp", "testMirrorNetworkIp"); - - // When - SetupParams result = new SetupParams().parse(jrpcParams); - - // Then - assertEquals("testAccountId", result.getOperatorAccountId()); - assertEquals("testPrivateKey", result.getOperatorPrivateKey()); - assertEquals(Optional.of("testNodeIp"), result.getNodeIp()); - assertEquals(Optional.of("testNodeAccountId"), result.getNodeAccountId()); - assertEquals(Optional.of("testMirrorNetworkIp"), result.getMirrorNetworkIp()); - } -} diff --git a/tck/src/test/java/com/hedera/hashgraph/tck/methods/sdk/response/SetupResponseTest.java b/tck/src/test/java/com/hedera/hashgraph/tck/methods/sdk/response/SetupResponseTest.java deleted file mode 100644 index 5acdf20e75..0000000000 --- a/tck/src/test/java/com/hedera/hashgraph/tck/methods/sdk/response/SetupResponseTest.java +++ /dev/null @@ -1,70 +0,0 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.methods.sdk.response; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -import org.junit.jupiter.api.Test; - -class SetupResponseTest { - - @Test - void testConstructorWithMessage() { - // Given - String message = "Test message"; - - // When - SetupResponse setupResponse = new SetupResponse(message); - - // Then - assertNotNull(setupResponse); - assertEquals(message, setupResponse.getMessage()); - assertEquals("SUCCESS", setupResponse.getStatus()); - } - - @Test - void testConstructorWithNullMessage() { - // Given - String message = null; - - // When - SetupResponse setupResponse = new SetupResponse(message); - - // Then - assertNotNull(setupResponse); - assertEquals("", setupResponse.getMessage()); // message should default to empty string - assertEquals("SUCCESS", setupResponse.getStatus()); - } - - @Test - void testConstructorWithEmptyMessage() { - // Given - String message = ""; - - // When - SetupResponse setupResponse = new SetupResponse(message); - - // Then - assertNotNull(setupResponse); - assertEquals("", setupResponse.getMessage()); - assertEquals("SUCCESS", setupResponse.getStatus()); - } -} diff --git a/tck/src/test/java/com/hedera/hashgraph/tck/controller/JRPCControllerTest.java b/tck/src/test/java/org/hiero/tck/controller/JRPCControllerTest.java similarity index 75% rename from tck/src/test/java/com/hedera/hashgraph/tck/controller/JRPCControllerTest.java rename to tck/src/test/java/org/hiero/tck/controller/JRPCControllerTest.java index 7660478d96..2a0f9a31c2 100644 --- a/tck/src/test/java/com/hedera/hashgraph/tck/controller/JRPCControllerTest.java +++ b/tck/src/test/java/org/hiero/tck/controller/JRPCControllerTest.java @@ -1,23 +1,5 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.controller; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.controller; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; diff --git a/tck/src/test/java/org/hiero/tck/controller/JRPCInterceptorTest.java b/tck/src/test/java/org/hiero/tck/controller/JRPCInterceptorTest.java new file mode 100644 index 0000000000..9a2cc61f46 --- /dev/null +++ b/tck/src/test/java/org/hiero/tck/controller/JRPCInterceptorTest.java @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.controller; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import com.thetransactioncompany.jsonrpc2.JSONRPC2Request; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.BufferedReader; +import java.io.StringReader; +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 JRPCInterceptorTest { + + @Mock + HttpServletRequest request; + + @Mock + HttpServletResponse response; + + @Test + void testMapToJSONRPC2Request() throws Exception { + // given + String requestBody = + "{\"jsonrpc\": \"2.0\", \"method\": \"testMethod\", \"params\": {\"param1\": \"value1\"}, \"id\": 1}"; + BufferedReader reader = new BufferedReader(new StringReader(requestBody)); + when(request.getReader()).thenReturn(reader); + + JRPCInterceptor interceptor = new JRPCInterceptor(); + + // when + JSONRPC2Request jsonrpcRequest = interceptor.mapToJSONRPC2Request(request); + + // then + assertNotNull(jsonrpcRequest); + assertEquals("testMethod", jsonrpcRequest.getMethod()); + assertEquals(1L, jsonrpcRequest.getID()); + } + + @Test + void testPreHandle() throws Exception { + // given + String requestBody = + "{\"jsonrpc\": \"2.0\", \"method\": \"testMethod\", \"params\": {\"param1\": \"value1\"}, \"id\": 1}"; + BufferedReader reader = new BufferedReader(new StringReader(requestBody)); + when(request.getReader()).thenReturn(reader); + + JRPCInterceptor interceptor = new JRPCInterceptor(); + + // when + boolean result = interceptor.preHandle(request, response, null); + + // then + assertTrue(result); + verify(request).setAttribute(eq("jsonrpcRequest"), any(JSONRPC2Request.class)); + } +} diff --git a/tck/src/test/java/com/hedera/hashgraph/tck/methods/sdk/KeyServiceTest.java b/tck/src/test/java/org/hiero/tck/methods/sdk/KeyServiceTest.java similarity index 84% rename from tck/src/test/java/com/hedera/hashgraph/tck/methods/sdk/KeyServiceTest.java rename to tck/src/test/java/org/hiero/tck/methods/sdk/KeyServiceTest.java index eb9f3bdfd1..287796d045 100644 --- a/tck/src/test/java/com/hedera/hashgraph/tck/methods/sdk/KeyServiceTest.java +++ b/tck/src/test/java/org/hiero/tck/methods/sdk/KeyServiceTest.java @@ -1,32 +1,15 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.methods.sdk; - -import static com.hedera.hashgraph.tck.util.KeyUtils.KeyType.*; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.methods.sdk; + +import static org.hiero.tck.util.KeyUtils.KeyType.*; import static org.junit.jupiter.api.Assertions.*; -import com.hedera.hashgraph.tck.exception.InvalidJSONRPC2RequestException; -import com.hedera.hashgraph.tck.methods.sdk.param.GenerateKeyParams; -import com.hedera.hashgraph.tck.methods.sdk.response.GenerateKeyResponse; import java.util.Collections; import java.util.Optional; +import org.hiero.tck.exception.InvalidJSONRPC2RequestException; +import org.hiero.tck.methods.sdk.param.GenerateKeyParams; +import org.hiero.tck.methods.sdk.response.GenerateKeyResponse; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.function.Executable; @@ -41,7 +24,7 @@ void testGenerateKeyWithInvalidFromKey() { Executable executable = () -> keyService.generateKey(params); - assertThrows( + Assertions.assertThrows( InvalidJSONRPC2RequestException.class, executable, "invalid parameters: fromKey should only be provided for ed25519PublicKey, ecdsaSecp256k1PublicKey, or evmAddress types."); diff --git a/tck/src/test/java/org/hiero/tck/methods/sdk/SdkServiceTest.java b/tck/src/test/java/org/hiero/tck/methods/sdk/SdkServiceTest.java new file mode 100644 index 0000000000..38593b8b6f --- /dev/null +++ b/tck/src/test/java/org/hiero/tck/methods/sdk/SdkServiceTest.java @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.methods.sdk; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.Optional; +import org.hiero.tck.methods.sdk.param.SetupParams; +import org.hiero.tck.methods.sdk.response.SetupResponse; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class SdkServiceTest { + + private SdkService sdkService = new SdkService(); + + @Test + void testSetup() throws Exception { + // Given + SetupParams params = new SetupParams( + "0.0.2", + "302e020100300506032b65700422042091132178e72057a1d7528025956fe39b0b847f200ab59b2fdd367017f3087137", + Optional.of("127.0.0.1:50211"), + Optional.of("0.0.3"), + Optional.of("http://127.0.0.1:5551")); + + // When + SetupResponse response = sdkService.setup(params); + + // Then + assertEquals("Successfully setup custom client.", response.getMessage()); + + response = sdkService.reset(); + + assertEquals("", response.getMessage()); + assertNull(sdkService.getClient()); + } + + @Test + void testSetupFail() { + // Given + SetupParams params = new SetupParams( + "operatorAccountId", + "operatorPrivateKey", + Optional.of("nodeIp"), + Optional.of("3asdf"), + Optional.of("127.0.0.1:50211")); + + // then + assertThrows(Exception.class, () -> sdkService.setup(params)); + } +} diff --git a/tck/src/test/java/com/hedera/hashgraph/tck/methods/sdk/param/AccountCreateParamsTest.java b/tck/src/test/java/org/hiero/tck/methods/sdk/param/AccountCreateParamsTest.java similarity index 88% rename from tck/src/test/java/com/hedera/hashgraph/tck/methods/sdk/param/AccountCreateParamsTest.java rename to tck/src/test/java/org/hiero/tck/methods/sdk/param/AccountCreateParamsTest.java index 49c2ca2cd9..ee0d8eda00 100644 --- a/tck/src/test/java/com/hedera/hashgraph/tck/methods/sdk/param/AccountCreateParamsTest.java +++ b/tck/src/test/java/org/hiero/tck/methods/sdk/param/AccountCreateParamsTest.java @@ -1,23 +1,5 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.methods.sdk.param; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.methods.sdk.param; import static org.junit.jupiter.api.Assertions.*; diff --git a/tck/src/test/java/org/hiero/tck/methods/sdk/param/CommonTransactionParamsTest.java b/tck/src/test/java/org/hiero/tck/methods/sdk/param/CommonTransactionParamsTest.java new file mode 100644 index 0000000000..64528d99a2 --- /dev/null +++ b/tck/src/test/java/org/hiero/tck/methods/sdk/param/CommonTransactionParamsTest.java @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.methods.sdk.param; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import net.minidev.json.JSONArray; +import org.junit.jupiter.api.Test; + +class CommonTransactionParamsTest { + @Test + void testCommonTransactionParamsParse() { + Map jrpcParams = new HashMap<>(); + jrpcParams.put("transactionId", "txId"); + jrpcParams.put("maxTransactionFee", 100L); + jrpcParams.put("validTransactionDuration", 120L); + jrpcParams.put("memo", "commonMemo"); + jrpcParams.put("regenerateTransactionId", true); + JSONArray signersArray = new JSONArray(); + signersArray.add( + "302e020100300506032b657004220420c1ed50ed4b024f5df25992d1fc4b8c5b4e3c3db63a5ff5fa05857f5b4b90f3bc"); + jrpcParams.put("signers", signersArray); + + CommonTransactionParams params = CommonTransactionParams.parse(jrpcParams); + + assertEquals(Optional.of("txId"), params.getTransactionId()); + assertEquals(Optional.of(100L), params.getMaxTransactionFee()); + assertEquals(Optional.of(120L), params.getValidTransactionDuration()); + assertEquals(Optional.of("commonMemo"), params.getMemo()); + assertEquals(Optional.of(true), params.getRegenerateTransactionId()); + assertEquals( + Optional.of( + List.of( + "302e020100300506032b657004220420c1ed50ed4b024f5df25992d1fc4b8c5b4e3c3db63a5ff5fa05857f5b4b90f3bc")), + params.getSigners()); + } +} diff --git a/tck/src/test/java/com/hedera/hashgraph/tck/methods/sdk/param/GenerateKeyParamsTest.java b/tck/src/test/java/org/hiero/tck/methods/sdk/param/GenerateKeyParamsTest.java similarity index 85% rename from tck/src/test/java/com/hedera/hashgraph/tck/methods/sdk/param/GenerateKeyParamsTest.java rename to tck/src/test/java/org/hiero/tck/methods/sdk/param/GenerateKeyParamsTest.java index 0263fc022e..8a3776f391 100644 --- a/tck/src/test/java/com/hedera/hashgraph/tck/methods/sdk/param/GenerateKeyParamsTest.java +++ b/tck/src/test/java/org/hiero/tck/methods/sdk/param/GenerateKeyParamsTest.java @@ -1,32 +1,14 @@ -/*- - * - * Hedera Java SDK - * - * 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.hashgraph.tck.methods.sdk.param; +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.methods.sdk.param; import static org.junit.jupiter.api.Assertions.*; -import com.hedera.hashgraph.tck.util.KeyUtils.KeyType; import java.util.HashMap; import java.util.Map; import java.util.Optional; import net.minidev.json.JSONArray; import net.minidev.json.JSONObject; +import org.hiero.tck.util.KeyUtils.KeyType; import org.junit.jupiter.api.Test; class GenerateKeyParamsTest { diff --git a/tck/src/test/java/org/hiero/tck/methods/sdk/param/SetupParamsTest.java b/tck/src/test/java/org/hiero/tck/methods/sdk/param/SetupParamsTest.java new file mode 100644 index 0000000000..e56e6a0782 --- /dev/null +++ b/tck/src/test/java/org/hiero/tck/methods/sdk/param/SetupParamsTest.java @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.methods.sdk.param; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import org.junit.jupiter.api.Test; + +class SetupParamsTest { + + @Test + void testParse() { + // Given + Map jrpcParams = new HashMap<>(); + jrpcParams.put("operatorAccountId", "testAccountId"); + jrpcParams.put("operatorPrivateKey", "testPrivateKey"); + jrpcParams.put("nodeIp", "testNodeIp"); + jrpcParams.put("nodeAccountId", "testNodeAccountId"); + jrpcParams.put("mirrorNetworkIp", "testMirrorNetworkIp"); + + // When + SetupParams result = new SetupParams().parse(jrpcParams); + + // Then + assertEquals("testAccountId", result.getOperatorAccountId()); + assertEquals("testPrivateKey", result.getOperatorPrivateKey()); + assertEquals(Optional.of("testNodeIp"), result.getNodeIp()); + assertEquals(Optional.of("testNodeAccountId"), result.getNodeAccountId()); + assertEquals(Optional.of("testMirrorNetworkIp"), result.getMirrorNetworkIp()); + } +} diff --git a/tck/src/test/java/org/hiero/tck/methods/sdk/response/SetupResponseTest.java b/tck/src/test/java/org/hiero/tck/methods/sdk/response/SetupResponseTest.java new file mode 100644 index 0000000000..018ce388d8 --- /dev/null +++ b/tck/src/test/java/org/hiero/tck/methods/sdk/response/SetupResponseTest.java @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.hiero.tck.methods.sdk.response; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.jupiter.api.Test; + +class SetupResponseTest { + + @Test + void testConstructorWithMessage() { + // Given + String message = "Test message"; + + // When + SetupResponse setupResponse = new SetupResponse(message); + + // Then + assertNotNull(setupResponse); + assertEquals(message, setupResponse.getMessage()); + assertEquals("SUCCESS", setupResponse.getStatus()); + } + + @Test + void testConstructorWithNullMessage() { + // Given + String message = null; + + // When + SetupResponse setupResponse = new SetupResponse(message); + + // Then + assertNotNull(setupResponse); + assertEquals("", setupResponse.getMessage()); // message should default to empty string + assertEquals("SUCCESS", setupResponse.getStatus()); + } + + @Test + void testConstructorWithEmptyMessage() { + // Given + String message = ""; + + // When + SetupResponse setupResponse = new SetupResponse(message); + + // Then + assertNotNull(setupResponse); + assertEquals("", setupResponse.getMessage()); + assertEquals("SUCCESS", setupResponse.getStatus()); + } +}