From 885a971dbec66c1e856fe40b1f07a83930ebdcb2 Mon Sep 17 00:00:00 2001 From: Rob Walworth Date: Fri, 7 Jul 2023 14:49:39 -0500 Subject: [PATCH] Add TokenFreezeTransaction unit tests and integration tests Signed-off-by: Rob Walworth --- sdk/main/CMakeLists.txt | 1 + sdk/main/include/TokenFreezeTransaction.h | 146 +++++++++++ sdk/main/include/Transaction.h | 4 +- sdk/main/src/Executable.cc | 2 + sdk/main/src/TokenFreezeTransaction.cc | 106 ++++++++ sdk/main/src/Transaction.cc | 7 +- sdk/main/src/impl/Node.cc | 2 + sdk/tests/integration/CMakeLists.txt | 1 + .../TokenFreezeTransactionIntegrationTests.cc | 238 ++++++++++++++++++ sdk/tests/unit/CMakeLists.txt | 1 + .../unit/TokenFreezeTransactionUnitTests.cc | 111 ++++++++ sdk/tests/unit/TransactionTest.cc | 60 +++++ 12 files changed, 677 insertions(+), 2 deletions(-) create mode 100644 sdk/main/include/TokenFreezeTransaction.h create mode 100644 sdk/main/src/TokenFreezeTransaction.cc create mode 100644 sdk/tests/integration/TokenFreezeTransactionIntegrationTests.cc create mode 100644 sdk/tests/unit/TokenFreezeTransactionUnitTests.cc diff --git a/sdk/main/CMakeLists.txt b/sdk/main/CMakeLists.txt index e799434f..889db021 100644 --- a/sdk/main/CMakeLists.txt +++ b/sdk/main/CMakeLists.txt @@ -77,6 +77,7 @@ add_library(${PROJECT_NAME} STATIC src/TokenDeleteTransaction.cc src/TokenDissociateTransaction.cc src/TokenFeeScheduleUpdateTransaction.cc + src/TokenFreezeTransaction.cc src/TokenId.cc src/TokenInfo.cc src/TokenInfoQuery.cc diff --git a/sdk/main/include/TokenFreezeTransaction.h b/sdk/main/include/TokenFreezeTransaction.h new file mode 100644 index 00000000..85682fde --- /dev/null +++ b/sdk/main/include/TokenFreezeTransaction.h @@ -0,0 +1,146 @@ +/*- + * + * Hedera C++ SDK + * + * Copyright (C) 2020 - 2022 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#ifndef HEDERA_SDK_CPP_TOKEN_FREEZE_TRANSACTION_H_ +#define HEDERA_SDK_CPP_TOKEN_FREEZE_TRANSACTION_H_ + +#include "AccountId.h" +#include "TokenId.h" +#include "Transaction.h" + +namespace proto +{ +class TokenFreezeAccountTransactionBody; +class TransactionBody; +} + +namespace Hedera +{ +/** + * Freezes transfers of the specified token for the account. The transaction must be signed by the token's freeze key. + * + * - If the provided account is not found, the transaction will resolve to INVALID_ACCOUNT_ID. + * - If the provided account has been deleted, the transaction will resolve to ACCOUNT_DELETED. + * - If the provided token is not found, the transaction will resolve to INVALID_TOKEN_ID. + * - If the provided token has been deleted, the transaction will resolve to TOKEN_WAS_DELETED. + * - If an Association between the provided token and account is not found, the transaction will resolve to + * TOKEN_NOT_ASSOCIATED_TO_ACCOUNT. + * - If no Freeze Key is defined, the transaction will resolve to TOKEN_HAS_NO_FREEZE_KEY. + * + * Once executed the Account is marked as Frozen and will not be able to receive or send tokens unless unfrozen. The + * operation is idempotent. + * + * Transaction Signing Requirements: + * - Freeze key. + * - Transaction fee payer account key. + */ +class TokenFreezeTransaction : public Transaction +{ +public: + TokenFreezeTransaction() = default; + + /** + * Construct from a TransactionBody protobuf object. + * + * @param transactionBody The TransactionBody protobuf object from which to construct. + * @throws std::invalid_argument If the input TransactionBody does not represent a TokenFreeze transaction. + */ + explicit TokenFreezeTransaction(const proto::TransactionBody& transactionBody); + + /** + * Set the ID of the account to be frozen for the specified token. + * + * @param accountId The ID of the account to be frozen for the specified token. + * @return A reference to this TokenFreezeTransaction object with the newly-set account ID. + * @throws IllegalStateException If this TokenFreezeTransaction is frozen. + */ + TokenFreezeTransaction& setAccountId(const AccountId& accountId); + + /** + * Set the ID of the token to be frozen for the specified account. + * + * @param tokenId The ID of the token to be frozen for the specified account. + * @return A reference to this TokenFreezeTransaction object with the newly-set token ID. + * @throws IllegalStateException If this TokenFreezeTransaction is frozen. + */ + TokenFreezeTransaction& setTokenId(const TokenId& tokenId); + + /** + * Get the ID of the account to be frozen for the specified token. + * + * @return The ID of the account to be frozen for the specified token. + */ + [[nodiscard]] inline AccountId getAccountId() const { return mAccountId; } + + /** + * Get the ID of the token to be frozen for the specified account. + * + * @return The ID of the token to be frozen for the specified account. + */ + [[nodiscard]] inline TokenId getTokenId() const { return mTokenId; } + +private: + /** + * Derived from Executable. Construct a Transaction protobuf object from this TokenFreezeTransaction object. + * + * @param client The Client trying to construct this TokenFreezeTransaction. + * @param node The Node to which this TokenFreezeTransaction will be sent. This is unused. + * @return A Transaction protobuf object filled with this TokenFreezeTransaction object's data. + * @throws UninitializedException If the input client has no operator with which to sign this TokenFreezeTransaction. + */ + [[nodiscard]] proto::Transaction makeRequest(const Client& client, + const std::shared_ptr& /*node*/) const override; + + /** + * Derived from Executable. Submit this TokenFreezeTransaction to a Node. + * + * @param client The Client submitting this TokenFreezeTransaction. + * @param deadline The deadline for submitting this TokenFreezeTransaction. + * @param node Pointer to the Node to which this TokenFreezeTransaction should be submitted. + * @param response Pointer to the TransactionResponse protobuf object that gRPC should populate with the response + * information from the gRPC server. + * @return The gRPC status of the submission. + */ + [[nodiscard]] grpc::Status submitRequest(const Client& client, + const std::chrono::system_clock::time_point& deadline, + const std::shared_ptr& node, + proto::TransactionResponse* response) const override; + + /** + * Build a TokenFreezeAccountTransactionBody protobuf object from this TokenFreezeTransaction object. + * + * @return A pointer to a TokenFreezeAccountTransactionBody protobuf object filled with this TokenFreezeTransaction + * object's data. + */ + [[nodiscard]] proto::TokenFreezeAccountTransactionBody* build() const; + + /** + * The ID of the account to be frozen for the specified token. + */ + AccountId mAccountId; + + /** + * The ID of the token to be frozen for the specified account. + */ + TokenId mTokenId; +}; + +} // namespace Hedera + +#endif // HEDERA_SDK_CPP_TOKEN_FREEZE_TRANSACTION_H_ diff --git a/sdk/main/include/Transaction.h b/sdk/main/include/Transaction.h index d9b650ae..f206f8c2 100644 --- a/sdk/main/include/Transaction.h +++ b/sdk/main/include/Transaction.h @@ -56,6 +56,7 @@ class TokenCreateTransaction; class TokenDeleteTransaction; class TokenDissociateTransaction; class TokenFeeScheduleUpdateTransaction; +class TokenFreezeTransaction; class TokenMintTransaction; class TokenUpdateTransaction; class TokenWipeTransaction; @@ -138,7 +139,8 @@ class Transaction TokenWipeTransaction, TokenBurnTransaction, TokenDissociateTransaction, - TokenFeeScheduleUpdateTransaction>> + TokenFeeScheduleUpdateTransaction, + TokenFreezeTransaction>> fromBytes(const std::vector& bytes); /** diff --git a/sdk/main/src/Executable.cc b/sdk/main/src/Executable.cc index 0a464e7b..50f807b9 100644 --- a/sdk/main/src/Executable.cc +++ b/sdk/main/src/Executable.cc @@ -53,6 +53,7 @@ #include "TokenDeleteTransaction.h" #include "TokenDissociateTransaction.h" #include "TokenFeeScheduleUpdateTransaction.h" +#include "TokenFreezeTransaction.h" #include "TokenInfo.h" #include "TokenInfoQuery.h" #include "TokenMintTransaction.h" @@ -371,6 +372,7 @@ template class Executable; +template class Executable; template class Executable; template class Executable; template class Executable; diff --git a/sdk/main/src/TokenFreezeTransaction.cc b/sdk/main/src/TokenFreezeTransaction.cc new file mode 100644 index 00000000..235ed619 --- /dev/null +++ b/sdk/main/src/TokenFreezeTransaction.cc @@ -0,0 +1,106 @@ +/*- + * + * Hedera C++ SDK + * + * Copyright (C) 2020 - 2022 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include "TokenFreezeTransaction.h" +#include "impl/Node.h" + +#include +#include +#include +#include + +namespace Hedera +{ +//----- +TokenFreezeTransaction::TokenFreezeTransaction(const proto::TransactionBody& transactionBody) + : Transaction(transactionBody) +{ + if (!transactionBody.has_tokenfreeze()) + { + throw std::invalid_argument("Transaction body doesn't contain TokenFreeze data"); + } + + const proto::TokenFreezeAccountTransactionBody& body = transactionBody.tokenfreeze(); + + if (body.has_account()) + { + mAccountId = AccountId::fromProtobuf(body.account()); + } + + if (body.has_token()) + { + mTokenId = TokenId::fromProtobuf(body.token()); + } +} + +//----- +TokenFreezeTransaction& TokenFreezeTransaction::setAccountId(const AccountId& accountId) +{ + requireNotFrozen(); + mAccountId = accountId; + return *this; +} + +//----- +TokenFreezeTransaction& TokenFreezeTransaction::setTokenId(const TokenId& tokenId) +{ + requireNotFrozen(); + mTokenId = tokenId; + return *this; +} + +//----- +proto::Transaction TokenFreezeTransaction::makeRequest(const Client& client, + const std::shared_ptr&) const +{ + proto::TransactionBody transactionBody = generateTransactionBody(client); + transactionBody.set_allocated_tokenfreeze(build()); + + return signTransaction(transactionBody, client); +} + +//----- +grpc::Status TokenFreezeTransaction::submitRequest(const Client& client, + const std::chrono::system_clock::time_point& deadline, + const std::shared_ptr& node, + proto::TransactionResponse* response) const +{ + return node->submitTransaction( + proto::TransactionBody::DataCase::kTokenFreeze, makeRequest(client, node), deadline, response); +} + +//----- +proto::TokenFreezeAccountTransactionBody* TokenFreezeTransaction::build() const +{ + auto body = std::make_unique(); + + if (!(mAccountId == AccountId())) + { + body->set_allocated_account(mAccountId.toProtobuf().release()); + } + + if (!(mTokenId == TokenId())) + { + body->set_allocated_token(mTokenId.toProtobuf().release()); + } + + return body.release(); +} + +} // namespace Hedera diff --git a/sdk/main/src/Transaction.cc b/sdk/main/src/Transaction.cc index 9fdee2f3..e0533a69 100644 --- a/sdk/main/src/Transaction.cc +++ b/sdk/main/src/Transaction.cc @@ -42,6 +42,7 @@ #include "TokenDeleteTransaction.h" #include "TokenDissociateTransaction.h" #include "TokenFeeScheduleUpdateTransaction.h" +#include "TokenFreezeTransaction.h" #include "TokenMintTransaction.h" #include "TokenUpdateTransaction.h" #include "TokenWipeTransaction.h" @@ -89,7 +90,8 @@ std::pair> + TokenFeeScheduleUpdateTransaction, + TokenFreezeTransaction>> Transaction::fromBytes(const std::vector& bytes) { proto::TransactionBody txBody; @@ -179,6 +181,8 @@ Transaction::fromBytes(const std::vector& bytes) return { 22, TokenDissociateTransaction(txBody) }; case proto::TransactionBody::kTokenFeeScheduleUpdate: return { 23, TokenFeeScheduleUpdateTransaction(txBody) }; + case proto::TransactionBody::kTokenFreeze: + return { 24, TokenFreezeTransaction(txBody) }; default: throw std::invalid_argument("Type of transaction cannot be determined from input bytes"); } @@ -500,6 +504,7 @@ template class Transaction; template class Transaction; template class Transaction; template class Transaction; +template class Transaction; template class Transaction; template class Transaction; template class Transaction; diff --git a/sdk/main/src/impl/Node.cc b/sdk/main/src/impl/Node.cc index 2f6ee587..44c00ed1 100644 --- a/sdk/main/src/impl/Node.cc +++ b/sdk/main/src/impl/Node.cc @@ -204,6 +204,8 @@ grpc::Status Node::submitTransaction(proto::TransactionBody::DataCase funcEnum, return mTokenStub->dissociateTokens(&context, transaction, response); case proto::TransactionBody::DataCase::kTokenFeeScheduleUpdate: return mTokenStub->updateTokenFeeSchedule(&context, transaction, response); + case proto::TransactionBody::DataCase::kTokenFreeze: + return mTokenStub->freezeTokenAccount(&context, transaction, response); case proto::TransactionBody::DataCase::kTokenMint: return mTokenStub->mintToken(&context, transaction, response); case proto::TransactionBody::DataCase::kTokenUpdate: diff --git a/sdk/tests/integration/CMakeLists.txt b/sdk/tests/integration/CMakeLists.txt index dd445c19..cdc5f5b5 100644 --- a/sdk/tests/integration/CMakeLists.txt +++ b/sdk/tests/integration/CMakeLists.txt @@ -30,6 +30,7 @@ add_executable(${TEST_PROJECT_NAME} TokenDeleteTransactionIntegrationTests.cc TokenDissociateTransactionIntegrationTests.cc TokenFeeScheduleUpdateTransactionIntegrationTests.cc + TokenFreezeTransactionIntegrationTests.cc TokenInfoQueryIntegrationTests.cc TokenMintTransactionIntegrationTests.cc TokenUpdateTransactionIntegrationTests.cc diff --git a/sdk/tests/integration/TokenFreezeTransactionIntegrationTests.cc b/sdk/tests/integration/TokenFreezeTransactionIntegrationTests.cc new file mode 100644 index 00000000..5246234b --- /dev/null +++ b/sdk/tests/integration/TokenFreezeTransactionIntegrationTests.cc @@ -0,0 +1,238 @@ +/*- + * + * Hedera C++ SDK + * + * Copyright (C) 2020 - 2022 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include "AccountCreateTransaction.h" +#include "AccountDeleteTransaction.h" +#include "AccountId.h" +#include "BaseIntegrationTest.h" +#include "Client.h" +#include "ED25519PrivateKey.h" +#include "PrivateKey.h" +#include "TokenAssociateTransaction.h" +#include "TokenCreateTransaction.h" +#include "TokenDeleteTransaction.h" +#include "TokenFreezeTransaction.h" +#include "TransactionReceipt.h" +#include "TransactionRecord.h" +#include "TransactionResponse.h" +#include "TransferTransaction.h" +#include "exceptions/PrecheckStatusException.h" +#include "exceptions/ReceiptStatusException.h" + +#include + +using namespace Hedera; + +class TokenFreezeTransactionIntegrationTest : public BaseIntegrationTest +{ +}; + +//----- +TEST_F(TokenFreezeTransactionIntegrationTest, ExecuteTokenFreezeTransaction) +{ + // Given + std::shared_ptr operatorKey; + std::shared_ptr accountKey; + + ASSERT_NO_THROW( + operatorKey = ED25519PrivateKey::fromString( + "302e020100300506032b65700422042091132178e72057a1d7528025956fe39b0b847f200ab59b2fdd367017f3087137")); + ASSERT_NO_THROW(accountKey = ED25519PrivateKey::generatePrivateKey()); + + AccountId accountId; + ASSERT_NO_THROW(accountId = AccountCreateTransaction() + .setKey(accountKey.get()) + .setInitialBalance(Hbar(1LL)) + .execute(getTestClient()) + .getReceipt(getTestClient()) + .mAccountId.value()); + + TokenId tokenId; + ASSERT_NO_THROW(tokenId = TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setInitialSupply(100000ULL) + .setTreasuryAccountId(AccountId(2ULL)) + .setAdminKey(operatorKey) + .setFreezeKey(operatorKey) + .execute(getTestClient()) + .getReceipt(getTestClient()) + .mTokenId.value()); + + ASSERT_NO_THROW(const TransactionReceipt txReceipt = TokenAssociateTransaction() + .setAccountId(accountId) + .setTokenIds({ tokenId }) + .freezeWith(getTestClient()) + .sign(accountKey.get()) + .execute(getTestClient()) + .getReceipt(getTestClient())); + + // When + EXPECT_NO_THROW(const TransactionReceipt txReceipt = TokenFreezeTransaction() + .setAccountId(accountId) + .setTokenId(tokenId) + .freezeWith(getTestClient()) + .sign(accountKey.get()) + .execute(getTestClient()) + .getReceipt(getTestClient())); + + // Then + EXPECT_THROW(const TransactionReceipt txReceipt = TransferTransaction() + .addTokenTransfer(tokenId, AccountId(2ULL), -10LL) + .addTokenTransfer(tokenId, accountId, 10LL) + .execute(getTestClient()) + .getReceipt(getTestClient()), + ReceiptStatusException); // ACCOUNT_FROZEN_FOR_TOKEN + + // Clean up + ASSERT_NO_THROW(const TransactionReceipt txReceipt = AccountDeleteTransaction() + .setDeleteAccountId(accountId) + .setTransferAccountId(AccountId(2ULL)) + .freezeWith(getTestClient()) + .sign(accountKey.get()) + .execute(getTestClient()) + .getReceipt(getTestClient())); + ASSERT_NO_THROW(const TransactionReceipt txReceipt = + TokenDeleteTransaction().setTokenId(tokenId).execute(getTestClient()).getReceipt(getTestClient())); +} + +//----- +TEST_F(TokenFreezeTransactionIntegrationTest, CannotFreezeWithNoTokenId) +{ + // Given + std::shared_ptr operatorKey; + std::shared_ptr accountKey; + + ASSERT_NO_THROW( + operatorKey = ED25519PrivateKey::fromString( + "302e020100300506032b65700422042091132178e72057a1d7528025956fe39b0b847f200ab59b2fdd367017f3087137")); + ASSERT_NO_THROW(accountKey = ED25519PrivateKey::generatePrivateKey()); + + AccountId accountId; + ASSERT_NO_THROW(accountId = AccountCreateTransaction() + .setKey(accountKey.get()) + .setInitialBalance(Hbar(1LL)) + .execute(getTestClient()) + .getReceipt(getTestClient()) + .mAccountId.value()); + + // When / Then + EXPECT_THROW(const TransactionReceipt txReceipt = TokenFreezeTransaction() + .setAccountId(accountId) + .freezeWith(getTestClient()) + .sign(accountKey.get()) + .execute(getTestClient()) + .getReceipt(getTestClient()), + PrecheckStatusException); // INVALID_TOKEN_ID + + // Clean up + ASSERT_NO_THROW(const TransactionReceipt txReceipt = AccountDeleteTransaction() + .setDeleteAccountId(accountId) + .setTransferAccountId(AccountId(2ULL)) + .freezeWith(getTestClient()) + .sign(accountKey.get()) + .execute(getTestClient()) + .getReceipt(getTestClient())); +} + +//----- +TEST_F(TokenFreezeTransactionIntegrationTest, CannotFreezeWithNoAccountId) +{ + // Given + std::shared_ptr operatorKey; + + ASSERT_NO_THROW( + operatorKey = ED25519PrivateKey::fromString( + "302e020100300506032b65700422042091132178e72057a1d7528025956fe39b0b847f200ab59b2fdd367017f3087137")); + + TokenId tokenId; + ASSERT_NO_THROW(tokenId = TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setInitialSupply(100000ULL) + .setTreasuryAccountId(AccountId(2ULL)) + .setAdminKey(operatorKey) + .setFreezeKey(operatorKey) + .execute(getTestClient()) + .getReceipt(getTestClient()) + .mTokenId.value()); + + // When / Then + EXPECT_THROW(const TransactionReceipt txReceipt = + TokenFreezeTransaction().setTokenId(tokenId).execute(getTestClient()).getReceipt(getTestClient()), + PrecheckStatusException); // INVALID_ACCOUNT_ID + + // Clean up + ASSERT_NO_THROW(const TransactionReceipt txReceipt = + TokenDeleteTransaction().setTokenId(tokenId).execute(getTestClient()).getReceipt(getTestClient())); +} + +//----- +TEST_F(TokenFreezeTransactionIntegrationTest, CannotFreezeTokenOnAccountWithNoAssociation) +{ + // Given + std::shared_ptr operatorKey; + std::shared_ptr accountKey; + + ASSERT_NO_THROW( + operatorKey = ED25519PrivateKey::fromString( + "302e020100300506032b65700422042091132178e72057a1d7528025956fe39b0b847f200ab59b2fdd367017f3087137")); + ASSERT_NO_THROW(accountKey = ED25519PrivateKey::generatePrivateKey()); + + AccountId accountId; + ASSERT_NO_THROW(accountId = AccountCreateTransaction() + .setKey(accountKey.get()) + .setInitialBalance(Hbar(1LL)) + .execute(getTestClient()) + .getReceipt(getTestClient()) + .mAccountId.value()); + + TokenId tokenId; + ASSERT_NO_THROW(tokenId = TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setInitialSupply(100000ULL) + .setTreasuryAccountId(AccountId(2ULL)) + .setAdminKey(operatorKey) + .setFreezeKey(operatorKey) + .execute(getTestClient()) + .getReceipt(getTestClient()) + .mTokenId.value()); + + // When / Then + EXPECT_THROW(const TransactionReceipt txReceipt = TokenFreezeTransaction() + .setAccountId(accountId) + .setTokenId(tokenId) + .freezeWith(getTestClient()) + .sign(accountKey.get()) + .execute(getTestClient()) + .getReceipt(getTestClient()), + ReceiptStatusException); // TOKEN_NOT_ASSOCIATED_TO_ACCOUNT + + // Clean up + ASSERT_NO_THROW(const TransactionReceipt txReceipt = AccountDeleteTransaction() + .setDeleteAccountId(accountId) + .setTransferAccountId(AccountId(2ULL)) + .freezeWith(getTestClient()) + .sign(accountKey.get()) + .execute(getTestClient()) + .getReceipt(getTestClient())); + ASSERT_NO_THROW(const TransactionReceipt txReceipt = + TokenDeleteTransaction().setTokenId(tokenId).execute(getTestClient()).getReceipt(getTestClient())); +} diff --git a/sdk/tests/unit/CMakeLists.txt b/sdk/tests/unit/CMakeLists.txt index 466c0311..05469ca2 100644 --- a/sdk/tests/unit/CMakeLists.txt +++ b/sdk/tests/unit/CMakeLists.txt @@ -68,6 +68,7 @@ add_executable(${TEST_PROJECT_NAME} TokenDeleteTransactionTest.cc TokenDissociateTransactionUnitTests.cc TokenFeeScheduleUpdateTransactionUnitTests.cc + TokenFreezeTransactionUnitTests.cc TokenIdTest.cc TokenInfoQueryUnitTests.cc TokenInfoUnitTests.cc diff --git a/sdk/tests/unit/TokenFreezeTransactionUnitTests.cc b/sdk/tests/unit/TokenFreezeTransactionUnitTests.cc new file mode 100644 index 00000000..1c49744c --- /dev/null +++ b/sdk/tests/unit/TokenFreezeTransactionUnitTests.cc @@ -0,0 +1,111 @@ +/*- + * + * Hedera C++ SDK + * + * Copyright (C) 2020 - 2022 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include "AccountId.h" +#include "Client.h" +#include "ECDSAsecp256k1PrivateKey.h" +#include "TokenFreezeTransaction.h" +#include "exceptions/IllegalStateException.h" + +#include +#include + +using namespace Hedera; + +class TokenFreezeTransactionTest : public ::testing::Test +{ +protected: + void SetUp() override { mClient.setOperator(AccountId(), ECDSAsecp256k1PrivateKey::generatePrivateKey().get()); } + + [[nodiscard]] inline const Client& getTestClient() const { return mClient; } + [[nodiscard]] inline const AccountId& getTestAccountId() const { return mTestAccountId; } + [[nodiscard]] inline const TokenId& getTestTokenId() const { return mTestTokenId; } + +private: + Client mClient; + const AccountId mTestAccountId = AccountId(1ULL, 2ULL, 3ULL); + const TokenId mTestTokenId = TokenId(4ULL, 5ULL, 6ULL); +}; + +//----- +TEST_F(TokenFreezeTransactionTest, ConstructTokenFreezeTransactionFromTransactionBodyProtobuf) +{ + // Given + auto body = std::make_unique(); + body->set_allocated_account(getTestAccountId().toProtobuf().release()); + body->set_allocated_token(getTestTokenId().toProtobuf().release()); + + proto::TransactionBody txBody; + txBody.set_allocated_tokenfreeze(body.release()); + + // When + const TokenFreezeTransaction tokenFreezeTransaction(txBody); + + // Then + EXPECT_EQ(tokenFreezeTransaction.getAccountId(), getTestAccountId()); + EXPECT_EQ(tokenFreezeTransaction.getTokenId(), getTestTokenId()); +} + +//----- +TEST_F(TokenFreezeTransactionTest, GetSetAccountId) +{ + // Given + TokenFreezeTransaction transaction; + + // When + EXPECT_NO_THROW(transaction.setAccountId(getTestAccountId())); + + // Then + EXPECT_EQ(transaction.getAccountId(), getTestAccountId()); +} + +//----- +TEST_F(TokenFreezeTransactionTest, GetSetAccountIdFrozen) +{ + // Given + TokenFreezeTransaction transaction; + ASSERT_NO_THROW(transaction.freezeWith(getTestClient())); + + // When / Then + EXPECT_THROW(transaction.setAccountId(getTestAccountId()), IllegalStateException); +} + +//----- +TEST_F(TokenFreezeTransactionTest, GetSetTokenId) +{ + // Given + TokenFreezeTransaction transaction; + + // When + EXPECT_NO_THROW(transaction.setTokenId(getTestTokenId())); + + // Then + EXPECT_EQ(transaction.getTokenId(), getTestTokenId()); +} + +//----- +TEST_F(TokenFreezeTransactionTest, GetSetTokenIdFrozen) +{ + // Given + TokenFreezeTransaction transaction; + ASSERT_NO_THROW(transaction.freezeWith(getTestClient())); + + // When / Then + EXPECT_THROW(transaction.setTokenId(getTestTokenId()), IllegalStateException); +} diff --git a/sdk/tests/unit/TransactionTest.cc b/sdk/tests/unit/TransactionTest.cc index 4acbde86..934168bc 100644 --- a/sdk/tests/unit/TransactionTest.cc +++ b/sdk/tests/unit/TransactionTest.cc @@ -38,6 +38,7 @@ #include "TokenDeleteTransaction.h" #include "TokenDissociateTransaction.h" #include "TokenFeeScheduleUpdateTransaction.h" +#include "TokenFreezeTransaction.h" #include "TokenMintTransaction.h" #include "TokenUpdateTransaction.h" #include "TokenWipeTransaction.h" @@ -1469,3 +1470,62 @@ TEST_F(TransactionTest, TokenFeeScheduleUpdateTransactionFromTransactionBytes) ASSERT_EQ(index, 23); EXPECT_NO_THROW(const TokenFeeScheduleUpdateTransaction tokenFeeScheduleUpdateTransaction = std::get<23>(txVariant)); } + +//----- +TEST_F(TransactionTest, TokenFreezeTransactionFromTransactionBodyBytes) +{ + // Given + proto::TransactionBody txBody; + txBody.set_allocated_tokenfreeze(new proto::TokenFreezeAccountTransactionBody); + + // When + const auto [index, txVariant] = + Transaction::fromBytes(internal::Utilities::stringToByteVector(txBody.SerializeAsString())); + + // Then + ASSERT_EQ(index, 24); + EXPECT_NO_THROW(const TokenFreezeTransaction tokenFreezeTransaction = std::get<24>(txVariant)); +} + +//----- +TEST_F(TransactionTest, TokenFreezeTransactionFromSignedTransactionBytes) +{ + // Given + proto::TransactionBody txBody; + txBody.set_allocated_tokenfreeze(new proto::TokenFreezeAccountTransactionBody); + + proto::SignedTransaction signedTx; + signedTx.set_bodybytes(txBody.SerializeAsString()); + // SignatureMap not required + + // When + const auto [index, txVariant] = + Transaction::fromBytes(internal::Utilities::stringToByteVector(txBody.SerializeAsString())); + + // Then + ASSERT_EQ(index, 24); + EXPECT_NO_THROW(const TokenFreezeTransaction tokenFreezeTransaction = std::get<24>(txVariant)); +} + +//----- +TEST_F(TransactionTest, TokenFreezeTransactionFromTransactionBytes) +{ + // Given + proto::TransactionBody txBody; + txBody.set_allocated_tokenfreeze(new proto::TokenFreezeAccountTransactionBody); + + proto::SignedTransaction signedTx; + signedTx.set_bodybytes(txBody.SerializeAsString()); + // SignatureMap not required + + proto::Transaction tx; + tx.set_signedtransactionbytes(signedTx.SerializeAsString()); + + // When + const auto [index, txVariant] = + Transaction::fromBytes(internal::Utilities::stringToByteVector(txBody.SerializeAsString())); + + // Then + ASSERT_EQ(index, 24); + EXPECT_NO_THROW(const TokenFreezeTransaction tokenFreezeTransaction = std::get<24>(txVariant)); +}