From b1f887bbaae609fa2b43b620d4b0577ab57da06b Mon Sep 17 00:00:00 2001 From: Deyan Zhekov Date: Wed, 30 Aug 2023 22:54:07 +0300 Subject: [PATCH 1/3] 481: Example for `ContractNonceInfo` (#491) Signed-off-by: Deyan Zhekov --- sdk/examples/CMakeLists.txt | 4 + sdk/examples/ContractNoncesExample.cc | 133 ++++++++++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 sdk/examples/ContractNoncesExample.cc diff --git a/sdk/examples/CMakeLists.txt b/sdk/examples/CMakeLists.txt index 2113e9cf9..97d4bcae0 100644 --- a/sdk/examples/CMakeLists.txt +++ b/sdk/examples/CMakeLists.txt @@ -3,6 +3,7 @@ set(CONSENSUS_PUB_SUB_EXAMPLE_NAME ${PROJECT_NAME}-consensus-pub-sub-example) set(CREATE_ACCOUNT_EXAMPLE_NAME ${PROJECT_NAME}-create-account-example) set(CREATE_SIMPLE_CONTRACT_EXAMPLE_NAME ${PROJECT_NAME}-create-simple-contract-example) set(CREATE_STATEFUL_CONTRACT_EXAMPLE_NAME ${PROJECT_NAME}-create-stateful-contract-example) +set(CONTRACT_NONCES_EXAMPLE_NAME ${PROJECT_NAME}-contract-nonces-example) set(CUSTOM_FEES_EXAMPLE_NAME ${PROJECT_NAME}-custom-fees-example) set(DELETE_ACCOUNT_EXAMPLE_NAME ${PROJECT_NAME}-delete-account-example) set(DELETE_FILE_EXAMPLE_NAME ${PROJECT_NAME}-delete-file-example) @@ -27,6 +28,7 @@ add_executable(${CONSENSUS_PUB_SUB_EXAMPLE_NAME} ConsensusPubSubExample.cc) add_executable(${CREATE_ACCOUNT_EXAMPLE_NAME} CreateAccountExample.cc) add_executable(${CREATE_SIMPLE_CONTRACT_EXAMPLE_NAME} CreateSimpleContractExample.cc) add_executable(${CREATE_STATEFUL_CONTRACT_EXAMPLE_NAME} CreateStatefulContractExample.cc) +add_executable(${CONTRACT_NONCES_EXAMPLE_NAME} ContractNoncesExample.cc) add_executable(${CUSTOM_FEES_EXAMPLE_NAME} CustomFeesExample.cc) add_executable(${DELETE_ACCOUNT_EXAMPLE_NAME} DeleteAccountExample.cc) add_executable(${DELETE_FILE_EXAMPLE_NAME} DeleteFileExample.cc) @@ -69,6 +71,7 @@ target_link_libraries(${CONSENSUS_PUB_SUB_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME}) target_link_libraries(${CREATE_ACCOUNT_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME}) target_link_libraries(${CREATE_SIMPLE_CONTRACT_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME}) target_link_libraries(${CREATE_STATEFUL_CONTRACT_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME}) +target_link_libraries(${CONTRACT_NONCES_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME}) target_link_libraries(${CUSTOM_FEES_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME}) target_link_libraries(${DELETE_ACCOUNT_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME}) target_link_libraries(${DELETE_FILE_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME}) @@ -96,6 +99,7 @@ install(TARGETS ${CREATE_ACCOUNT_EXAMPLE_NAME} ${CREATE_SIMPLE_CONTRACT_EXAMPLE_NAME} ${CREATE_STATEFUL_CONTRACT_EXAMPLE_NAME} + ${CONTRACT_NONCES_EXAMPLE_NAME} ${CUSTOM_FEES_EXAMPLE_NAME} ${DELETE_ACCOUNT_EXAMPLE_NAME} ${DELETE_FILE_EXAMPLE_NAME} diff --git a/sdk/examples/ContractNoncesExample.cc b/sdk/examples/ContractNoncesExample.cc new file mode 100644 index 000000000..24190aae1 --- /dev/null +++ b/sdk/examples/ContractNoncesExample.cc @@ -0,0 +1,133 @@ +/*- + * + * Hedera C++ SDK + * + * Copyright (C) 2020 - 2023 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "Client.h" +#include "ContractCreateTransaction.h" +#include "ContractDeleteTransaction.h" +#include "ContractFunctionResult.h" +#include "ED25519PrivateKey.h" +#include "FileAppendTransaction.h" +#include "FileCreateTransaction.h" +#include "FileDeleteTransaction.h" +#include "FileId.h" +#include "FileInfo.h" +#include "FileInfoQuery.h" +#include "Status.h" +#include "TransactionReceipt.h" +#include "TransactionRecord.h" +#include "TransactionResponse.h" +#include "impl/Utilities.h" + +#include +#include +#include +#include + +using json = nlohmann::json; +using namespace Hedera; + +int main(int argc, char** argv) +{ + if (argc < 3) + { + std::cout << "Please input account ID and private key" << std::endl; + return 1; + } + + // Get a client for the Hedera testnet, and set the operator account ID and key such that all generated transactions + // will be paid for by this account and be signed by this key. + Client client = Client::forTestnet(); + const AccountId operatorId = AccountId::fromString(argv[1]); + std::unique_ptr operatorKey = ED25519PrivateKey::fromString(argv[2]); + const std::shared_ptr operatorPublicKey = operatorKey->getPublicKey(); + client.setOperator(operatorId, operatorKey.get()); + + // Contract's bytecode as hex + const std::string mBytecodeHexWithContractNonceInfo = + "6080604052348015600f57600080fd5b50604051601a90603b565b604051809103906000f0801580156035573d6000803e3d6000fd5" + "b50506047565b605c8061009483390190565b603f806100556000396000f3fe6080604052600080fdfea2646970667358221220a201" + "22cbad3457fedcc0600363d6e895f17048f5caa4afdab9e655123737567d64736f6c634300081200336080604052348015600f57600" + "080fd5b50603f80601d6000396000f3fe6080604052600080fdfea264697066735822122053dfd8835e3dc6fedfb8b4806460b9b716" + "3f8a7248bac510c6d6808d9da9d6d364736f6c63430008120033"; + + // Create the contract's bytecode file + TransactionReceipt txReceipt = + FileCreateTransaction() + .setKeys({ operatorPublicKey.get() }) + .setContents(internal::Utilities::stringToByteVector(mBytecodeHexWithContractNonceInfo)) + .setMaxTransactionFee(Hbar(2LL)) + .execute(client) + .getReceipt(client); + std::cout << "FileCreateTransaction execution completed with status: " << gStatusToString.at(txReceipt.mStatus) + << std::endl; + if (!txReceipt.mFileId.has_value()) + { + std::cout << "No file created!" << std::endl; + return 1; + } + + const FileId newFileId = txReceipt.mFileId.value(); + std::cout << "Contract bytecode file created with ID " << newFileId.toString() << std::endl; + + // Create the actual contract + TransactionResponse contractCreateTxResponse = ContractCreateTransaction() + .setAdminKey(operatorPublicKey.get()) + .setGas(100000ULL) + .setBytecodeFileId(newFileId) + .setMemo("[e2e::ContractADeploysContractBInConstructor]") + .execute(client); + + TransactionReceipt contractCreateTxReceipt = contractCreateTxResponse.getReceipt(client); + std::cout << "ContractCreateTransaction execution completed with status: " + << gStatusToString.at(contractCreateTxReceipt.mStatus) << std::endl; + if (!contractCreateTxReceipt.mContractId.has_value()) + { + std::cout << "No contract created!" << std::endl; + return 1; + } + + const ContractId contractId = contractCreateTxReceipt.mContractId.value(); + std::cout << "Smart contract created with ID " << contractId.toString() << std::endl; + + // Get contract's function results + ContractFunctionResult contractFunctionResult = + contractCreateTxResponse.getRecord(client).mContractFunctionResult.value(); + + std::cout << "Contract Nonces: " << std::endl; + for (auto it = contractFunctionResult.mContractNonces.begin(); it != contractFunctionResult.mContractNonces.end(); + ++it) + { + std::cout << "ContractId: " << (*it).mContractId.toString() << std::endl; + std::cout << "Nonce: " << (*it).mNonce << std::endl << std::endl; + } + + std::cout << std::endl << std::endl; + + // Now delete the contract + txReceipt = ContractDeleteTransaction() + .setContractId(contractId) + .setTransferAccountId(operatorId) + .setMaxTransactionFee(Hbar(1LL)) + .execute(client) + .getReceipt(client); + std::cout << "ContractDeleteTransaction execution completed with status: " << gStatusToString.at(txReceipt.mStatus) + << std::endl; + + return 0; +} From d805f9576db88bed39aaffd0a63f83dfaa3c6c2a Mon Sep 17 00:00:00 2001 From: Rob Walworth <110835868+rwalworth@users.noreply.github.com> Date: Thu, 31 Aug 2023 07:30:23 -0500 Subject: [PATCH 2/3] 486: Add `AccountAliasExample` (#487) Signed-off-by: Rob Walworth --- sdk/examples/AccountAliasExample.cc | 121 +++++++++++++++++++++ sdk/examples/CMakeLists.txt | 4 + sdk/main/include/ECDSAsecp256k1PublicKey.h | 7 ++ sdk/main/include/ED25519PublicKey.h | 7 ++ sdk/main/include/PublicKey.h | 34 +++++- sdk/main/src/AccountId.cc | 11 +- sdk/main/src/AccountInfo.cc | 9 +- sdk/main/src/ECDSAsecp256k1PublicKey.cc | 7 ++ sdk/main/src/ED25519PublicKey.cc | 7 ++ sdk/main/src/PublicKey.cc | 24 ++++ sdk/tests/unit/AccountIdTest.cc | 15 ++- sdk/tests/unit/AccountInfoTest.cc | 2 +- 12 files changed, 223 insertions(+), 25 deletions(-) create mode 100644 sdk/examples/AccountAliasExample.cc diff --git a/sdk/examples/AccountAliasExample.cc b/sdk/examples/AccountAliasExample.cc new file mode 100644 index 000000000..107c0b74f --- /dev/null +++ b/sdk/examples/AccountAliasExample.cc @@ -0,0 +1,121 @@ +/*- + * + * Hedera C++ SDK + * + * Copyright (C) 2020 - 2023 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "AccountBalance.h" +#include "AccountBalanceQuery.h" +#include "AccountInfo.h" +#include "AccountInfoQuery.h" +#include "Client.h" +#include "ED25519PrivateKey.h" +#include "PublicKey.h" +#include "Status.h" +#include "TransactionReceipt.h" +#include "TransactionResponse.h" +#include "TransferTransaction.h" + +#include + +using namespace Hedera; + +int main(int argc, char** argv) +{ + if (argc < 3) + { + std::cout << "Please input account ID and private key" << std::endl; + return 1; + } + + // Get a client for the Hedera testnet, and set the operator account ID and key such that all generated transactions + // will be paid for by this account and be signed by this key. + Client client = Client::forTestnet(); + const AccountId operatorAccountId = AccountId::fromString(argv[1]); + client.setOperator(operatorAccountId, ED25519PrivateKey::fromString(argv[2]).get()); + + /* + * Hedera supports a form of auto account creation. + * + * You can "create" an account by generating a private key, and then deriving the public key, without any need to + * interact with the Hedera network. The public key more or less acts as the user's account ID. This public key is an + * account's mAliasKey: a public key that aliases (or will eventually alias) to a Hedera account. + * + * An AccountId takes one of two forms: a normal AccountId with a null mAliasKey member takes the form 0.0.123, while + * an AccountId with a non-null mAliasKey member takes the form + * 0.0.302a300506032b6570032100114e6abc371b82dab5c15ea149f02d34a012087b163516dd70f44acafabf7777. + * Note the prefix of "0.0." indicating the shard and realm. Also note that the mAliasKey is stringified as a + * hex-encoded ASN1 DER representation of the key. + * + * An AccountId with a mAliasKey can be used just like a normal AccountId for the purposes of queries and + * transactions, however most queries and transactions involving such an AccountId won't work until Hbar has been + * transferred to the mAliasKey account. + * + * There is no record in the Hedera network of an account associated with a given mAliasKey until an amount of Hbar is + * transferred to the account. The moment that Hbar is transferred to that mAliasKey AccountId is the moment that that + * account actually begins to exist in the Hedera ledger. + */ + + // Generate a ED25519 private, public key pair + const std::shared_ptr privateKey = ED25519PrivateKey::generatePrivateKey(); + const std::shared_ptr publicKey = privateKey->getPublicKey(); + + std::cout << "Generated private key: " << privateKey->toStringDer() << std::endl; + std::cout << "Generated public key: " << publicKey->toStringDer() << std::endl; + + // Get an account ID from the generated public key (use default 0 for shared and realm). + const AccountId aliasAccountId = publicKey->toAccountId(); + + /* + * Note that no queries or transactions have taken place yet. This account "creation" process is entirely local. + * + * AccountId.fromString() can construct an AccountId with an alias key. It expects a string of the form 0.0.123 in the + * case of a normal AccountId, or of the form + * 0.0.302a300506032b6570032100114e6abc371b82dab5c15ea149f02d34a012087b163516dd70f44acafabf7777 + * in the case of an AccountId with an alias key. Note the prefix of "0.0." to indicate the shard and realm. + * + * If the shard and realm are known, you may use PublicKey.fromString().toAccountId() to construct the alias key + * AccountId. + * + * Now transfer some Hbar to the alias key account to officially create the account on the ledger. + */ + std::cout << "Transfer Hbar to alias account key: " + << gStatusToString.at(TransferTransaction() + .addHbarTransfer(operatorAccountId, Hbar(10LL).negated()) + .addHbarTransfer(aliasAccountId, Hbar(10LL)) + .execute(client) + .setValidateStatus(false) + .getReceipt(client) + .mStatus) + << std::endl; + + std::cout << "Balance of the created account: " + << AccountBalanceQuery().setAccountId(aliasAccountId).execute(client).getBalance().toTinybars() + << HbarUnit::TINYBAR().getSymbol() << std::endl; + + /* + * Note that once an account exists in the ledger, it is assigned a normal AccountId, which can be retrieved via an + * AccountInfoQuery. + * + * Users may continue to refer to the account by its alias key AccountId, but they may also now refer to it by its + * normal AccountId. + */ + const AccountInfo accountInfo = AccountInfoQuery().setAccountId(aliasAccountId).execute(client); + std::cout << "Created account ID: " << accountInfo.mAccountId.toString() << std::endl; + std::cout << "Created account alias: " << accountInfo.mPublicKeyAlias->toStringDer() << std::endl; + + return 0; +} diff --git a/sdk/examples/CMakeLists.txt b/sdk/examples/CMakeLists.txt index 97d4bcae0..9601aa15d 100644 --- a/sdk/examples/CMakeLists.txt +++ b/sdk/examples/CMakeLists.txt @@ -1,3 +1,4 @@ +set(ACCOUNT_ALIAS_EXAMPLE_NAME ${PROJECT_NAME}-account-alias-example) set(ACCOUNT_ALLOWANCE_EXAMPLE_NAME ${PROJECT_NAME}-account-allowance-example) set(CONSENSUS_PUB_SUB_EXAMPLE_NAME ${PROJECT_NAME}-consensus-pub-sub-example) set(CREATE_ACCOUNT_EXAMPLE_NAME ${PROJECT_NAME}-create-account-example) @@ -23,6 +24,7 @@ set(TRANSFER_CRYPTO_EXAMPLE_NAME ${PROJECT_NAME}-transfer-crypto-example) set(TRANSFER_TOKENS_EXAMPLE_NAME ${PROJECT_NAME}-transfer-tokens-example) set(UPDATE_ACCOUNT_PUBLIC_KEY_EXAMPLE_NAME ${PROJECT_NAME}-update-account-public-key-example) +add_executable(${ACCOUNT_ALIAS_EXAMPLE_NAME} AccountAliasExample.cc) add_executable(${ACCOUNT_ALLOWANCE_EXAMPLE_NAME} AccountAllowanceExample.cc) add_executable(${CONSENSUS_PUB_SUB_EXAMPLE_NAME} ConsensusPubSubExample.cc) add_executable(${CREATE_ACCOUNT_EXAMPLE_NAME} CreateAccountExample.cc) @@ -66,6 +68,7 @@ if (WIN32) DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}) endif () +target_link_libraries(${ACCOUNT_ALIAS_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME}) target_link_libraries(${ACCOUNT_ALLOWANCE_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME}) target_link_libraries(${CONSENSUS_PUB_SUB_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME}) target_link_libraries(${CREATE_ACCOUNT_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME}) @@ -94,6 +97,7 @@ target_link_libraries(${UPDATE_ACCOUNT_PUBLIC_KEY_EXAMPLE_NAME} PUBLIC ${PROJECT install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE} DESTINATION examples FILES_MATCHING PATTERN *) install(TARGETS + ${ACCOUNT_ALIAS_EXAMPLE_NAME} ${ACCOUNT_ALLOWANCE_EXAMPLE_NAME} ${CONSENSUS_PUB_SUB_EXAMPLE_NAME} ${CREATE_ACCOUNT_EXAMPLE_NAME} diff --git a/sdk/main/include/ECDSAsecp256k1PublicKey.h b/sdk/main/include/ECDSAsecp256k1PublicKey.h index 63de6bde2..59c7e408a 100644 --- a/sdk/main/include/ECDSAsecp256k1PublicKey.h +++ b/sdk/main/include/ECDSAsecp256k1PublicKey.h @@ -203,6 +203,13 @@ class ECDSAsecp256k1PublicKey : public PublicKey * @param key The wrapped OpenSSL key object from which to construct this ECDSAsecp256k1PublicKey. */ explicit ECDSAsecp256k1PublicKey(internal::OpenSSLUtils::EVP_PKEY&& key); + + /** + * Derived from PublicKey. Get a std::shared_ptr to this ECDSAsecp256k1PublicKey. + * + * @returns A pointer to this ECDSAsecp256k1PublicKey. + */ + [[nodiscard]] std::shared_ptr getShared() const override; }; } // namespace Hedera diff --git a/sdk/main/include/ED25519PublicKey.h b/sdk/main/include/ED25519PublicKey.h index 5d4b3214c..19e03596d 100644 --- a/sdk/main/include/ED25519PublicKey.h +++ b/sdk/main/include/ED25519PublicKey.h @@ -146,6 +146,13 @@ class ED25519PublicKey : public PublicKey * @param key The wrapped OpenSSL key object from which to construct this ED25519PublicKey. */ explicit ED25519PublicKey(internal::OpenSSLUtils::EVP_PKEY&& key); + + /** + * Derived from PublicKey. Get a std::shared_ptr to this ED25519PublicKey. + * + * @returns A pointer to this ED25519PublicKey. + */ + [[nodiscard]] std::shared_ptr getShared() const override; }; } // namespace Hedera diff --git a/sdk/main/include/PublicKey.h b/sdk/main/include/PublicKey.h index b1a75634d..797a1a9d1 100644 --- a/sdk/main/include/PublicKey.h +++ b/sdk/main/include/PublicKey.h @@ -38,12 +38,19 @@ namespace Hedera::internal::OpenSSLUtils class EVP_PKEY; } +namespace Hedera +{ +class AccountId; +} + namespace Hedera { /** * A generic class representing a public key. */ -class PublicKey : public Key +class PublicKey + : public Key + , public std::enable_shared_from_this { public: /** @@ -71,6 +78,15 @@ class PublicKey : public Key */ [[nodiscard]] static std::unique_ptr fromBytesDer(const std::vector& bytes); + /** + * Construct a PublicKey object from a byte array representing an alias. + * + * @param alias The bytes representing an alias. + * @return A pointer to a PublicKey representing the input alias bytes, or nullptr if the input alias byte array does + * not represent a PublicKey. + */ + [[nodiscard]] static std::unique_ptr fromAliasBytes(const std::vector& bytes); + /** * Verify that a signature was made by the PrivateKey which corresponds to this PublicKey. * @@ -109,6 +125,15 @@ class PublicKey : public Key */ [[nodiscard]] virtual std::vector toBytesRaw() const = 0; + /** + * Construct an AccountId object using this PublicKey as its alias. + * + * @param shard The shard of the AccountId. + * @param realm The realm of the AccountId. + * @return The constructed AccountId. + */ + [[nodiscard]] AccountId toAccountId(uint64_t shard = 0ULL, uint64_t realm = 0ULL) const; + protected: /** * Prevent public copying and moving to prevent slicing. Use the 'clone()' virtual method instead. @@ -133,6 +158,13 @@ class PublicKey : public Key [[nodiscard]] internal::OpenSSLUtils::EVP_PKEY getInternalKey() const; private: + /** + * Get a std::shared_ptr to this PublicKey. + * + * @returns A pointer to this PublicKey. + */ + [[nodiscard]] virtual std::shared_ptr getShared() const = 0; + /** * Implementation object used to hide implementation details and internal headers. */ diff --git a/sdk/main/src/AccountId.cc b/sdk/main/src/AccountId.cc index 2828414b6..3c64f0809 100644 --- a/sdk/main/src/AccountId.cc +++ b/sdk/main/src/AccountId.cc @@ -180,14 +180,7 @@ AccountId AccountId::fromProtobuf(const proto::AccountID& proto) } else { - try - { - accountId.mPublicKeyAlias = PublicKey::fromBytesDer(internal::Utilities::stringToByteVector(proto.alias())); - } - catch (const BadKeyException& ex) - { - std::cout << "Cannot decode AccountID protobuf alias: " << ex.what() << std::endl; - } + accountId.mPublicKeyAlias = PublicKey::fromAliasBytes(internal::Utilities::stringToByteVector(proto.alias())); } break; } @@ -211,7 +204,7 @@ std::unique_ptr AccountId::toProtobuf() const } else if (mPublicKeyAlias) { - proto->set_alias(internal::Utilities::byteVectorToString(mPublicKeyAlias->toBytesRaw())); + proto->set_alias(mPublicKeyAlias->toProtobufKey()->SerializeAsString()); } else if (mEvmAddressAlias) { diff --git a/sdk/main/src/AccountInfo.cc b/sdk/main/src/AccountInfo.cc index 7ed89fe09..c9cebd615 100644 --- a/sdk/main/src/AccountInfo.cc +++ b/sdk/main/src/AccountInfo.cc @@ -71,14 +71,7 @@ AccountInfo AccountInfo::fromProtobuf(const proto::CryptoGetInfoResponse_Account } else { - try - { - accountInfo.mPublicKeyAlias = PublicKey::fromBytesDer(internal::Utilities::stringToByteVector(proto.alias())); - } - catch (const BadKeyException& ex) - { - std::cout << "Cannot decode AccountID protobuf alias: " << ex.what() << std::endl; - } + accountInfo.mPublicKeyAlias = PublicKey::fromAliasBytes(internal::Utilities::stringToByteVector(proto.alias())); } } diff --git a/sdk/main/src/ECDSAsecp256k1PublicKey.cc b/sdk/main/src/ECDSAsecp256k1PublicKey.cc index a5bc5c490..23d24208a 100644 --- a/sdk/main/src/ECDSAsecp256k1PublicKey.cc +++ b/sdk/main/src/ECDSAsecp256k1PublicKey.cc @@ -438,4 +438,11 @@ ECDSAsecp256k1PublicKey::ECDSAsecp256k1PublicKey(internal::OpenSSLUtils::EVP_PKE { } +//----- +std::shared_ptr ECDSAsecp256k1PublicKey::getShared() const +{ + return std::const_pointer_cast( + std::dynamic_pointer_cast(shared_from_this())); +} + } // namespace Hedera diff --git a/sdk/main/src/ED25519PublicKey.cc b/sdk/main/src/ED25519PublicKey.cc index eef5759f4..f357f2b81 100644 --- a/sdk/main/src/ED25519PublicKey.cc +++ b/sdk/main/src/ED25519PublicKey.cc @@ -191,4 +191,11 @@ ED25519PublicKey::ED25519PublicKey(internal::OpenSSLUtils::EVP_PKEY&& key) { } +//----- +std::shared_ptr ED25519PublicKey::getShared() const +{ + return std::const_pointer_cast( + std::dynamic_pointer_cast(shared_from_this())); +} + } // namespace Hedera diff --git a/sdk/main/src/PublicKey.cc b/sdk/main/src/PublicKey.cc index 2348dd9c8..875d75356 100644 --- a/sdk/main/src/PublicKey.cc +++ b/sdk/main/src/PublicKey.cc @@ -18,6 +18,7 @@ * */ #include "PublicKey.h" +#include "AccountId.h" #include "ECDSAsecp256k1PublicKey.h" #include "ED25519PublicKey.h" #include "exceptions/BadKeyException.h" @@ -64,6 +65,29 @@ std::unique_ptr PublicKey::fromBytesDer(const std::vector& throw BadKeyException("Key type cannot be determined from input DER-encoded byte array"); } +//----- +std::unique_ptr PublicKey::fromAliasBytes(const std::vector& bytes) +{ + proto::Key protoKey; + protoKey.ParseFromArray(bytes.data(), static_cast(bytes.size())); + + if (std::unique_ptr key = Key::fromProtobuf(protoKey); auto* publicKey = dynamic_cast(key.get())) + { + key.release(); + return std::unique_ptr(publicKey); + } + else + { + return nullptr; + } +} + +//----- +AccountId PublicKey::toAccountId(uint64_t shard, uint64_t realm) const +{ + return AccountId(shard, realm, getShared()); +} + //----- PublicKey::PublicKey(internal::OpenSSLUtils::EVP_PKEY&& key) : mImpl(PublicKeyImpl()) diff --git a/sdk/tests/unit/AccountIdTest.cc b/sdk/tests/unit/AccountIdTest.cc index 8042d212b..5ed0252cd 100644 --- a/sdk/tests/unit/AccountIdTest.cc +++ b/sdk/tests/unit/AccountIdTest.cc @@ -418,13 +418,15 @@ TEST_F(AccountIdTest, ProtobufAccountId) EXPECT_EQ(protoAccountId->account_case(), proto::AccountID::AccountCase::kAlias); // Adjust protobuf fields - std::vector testBytes = ED25519PrivateKey::generatePrivateKey()->getPublicKey()->toBytesDer(); - protoAccountId->set_allocated_alias(new std::string(internal::Utilities::byteVectorToString(testBytes))); + std::unique_ptr key = ED25519PrivateKey::generatePrivateKey(); + std::vector testBytes = + internal::Utilities::stringToByteVector(key->getPublicKey()->toProtobufKey()->SerializeAsString()); + protoAccountId->set_alias(internal::Utilities::byteVectorToString(testBytes)); // Deserialize ED25519 alias accountId = AccountId::fromProtobuf(*protoAccountId); EXPECT_NE(accountId.getPublicKeyAlias(), nullptr); - EXPECT_EQ(accountId.getPublicKeyAlias()->toBytesDer(), testBytes); + EXPECT_EQ(accountId.getPublicKeyAlias()->toBytesDer(), key->getPublicKey()->toBytesDer()); // Serialize ECDSA alias accountId.setPublicKeyAlias(getTestEcdsaSecp256k1Alias()); @@ -432,13 +434,14 @@ TEST_F(AccountIdTest, ProtobufAccountId) EXPECT_EQ(protoAccountId->account_case(), proto::AccountID::AccountCase::kAlias); // Adjust protobuf fields - testBytes = ECDSAsecp256k1PrivateKey::generatePrivateKey()->getPublicKey()->toBytesDer(); - protoAccountId->set_allocated_alias(new std::string(internal::Utilities::byteVectorToString(testBytes))); + key = ECDSAsecp256k1PrivateKey::generatePrivateKey(); + testBytes = internal::Utilities::stringToByteVector(key->getPublicKey()->toProtobufKey()->SerializeAsString()); + protoAccountId->set_alias(internal::Utilities::byteVectorToString(testBytes)); // Deserialize ECDSA alias accountId = AccountId::fromProtobuf(*protoAccountId); EXPECT_NE(accountId.getPublicKeyAlias(), nullptr); - EXPECT_EQ(accountId.getPublicKeyAlias()->toBytesDer(), testBytes); + EXPECT_EQ(accountId.getPublicKeyAlias()->toBytesDer(), key->getPublicKey()->toBytesDer()); // Serialize EVM address accountId.setEvmAddressAlias(getTestEvmAddressAlias()); diff --git a/sdk/tests/unit/AccountInfoTest.cc b/sdk/tests/unit/AccountInfoTest.cc index 3d79e4314..a09e75904 100644 --- a/sdk/tests/unit/AccountInfoTest.cc +++ b/sdk/tests/unit/AccountInfoTest.cc @@ -107,7 +107,7 @@ TEST_F(AccountInfoTest, FromProtobuf) protoAccountInfo.set_memo(getTestMemo()); protoAccountInfo.set_ownednfts(static_cast(getTestOwnedNfts())); protoAccountInfo.set_max_automatic_token_associations(static_cast(getTestMaxAutomaticTokenAssociations())); - protoAccountInfo.set_alias(internal::Utilities::byteVectorToString(getTestPublicKeyAlias()->toBytesDer())); + protoAccountInfo.set_alias(getTestPublicKeyAlias()->toProtobufKey()->SerializeAsString()); protoAccountInfo.set_ledger_id(internal::Utilities::byteVectorToString(getTestLedgerId().toBytes())); protoAccountInfo.mutable_staking_info()->set_decline_reward(getTestDeclineReward()); From 6dbc6396ba814c24ce761a7847fed39c8f63642a Mon Sep 17 00:00:00 2001 From: Rob Walworth <110835868+rwalworth@users.noreply.github.com> Date: Fri, 1 Sep 2023 06:34:30 -0500 Subject: [PATCH 3/3] 479: Implement `NetworkVersionInfoQuery` (#483) Signed-off-by: Rob Walworth Co-authored-by: Deyan Zhekov --- sdk/main/CMakeLists.txt | 3 + sdk/main/include/NetworkVersionInfo.h | 95 ++++++++++++ sdk/main/include/NetworkVersionInfoQuery.h | 83 +++++++++++ sdk/main/include/SemanticVersion.h | 131 +++++++++++++++++ sdk/main/include/TransferTransaction.h | 1 + sdk/main/include/impl/Node.h | 6 + sdk/main/src/Executable.cc | 3 + sdk/main/src/NetworkVersionInfo.cc | 64 +++++++++ sdk/main/src/NetworkVersionInfoQuery.cc | 78 ++++++++++ sdk/main/src/Query.cc | 3 + sdk/main/src/SemanticVersion.cc | 94 ++++++++++++ sdk/main/src/impl/Node.cc | 24 ++-- sdk/tests/integration/CMakeLists.txt | 1 + ...NetworkVersionInfoQueryIntegrationTests.cc | 38 +++++ sdk/tests/unit/CMakeLists.txt | 2 + sdk/tests/unit/NetworkVersionInfoUnitTests.cc | 118 +++++++++++++++ sdk/tests/unit/SemanticVersionUnitTests.cc | 136 ++++++++++++++++++ 17 files changed, 872 insertions(+), 8 deletions(-) create mode 100644 sdk/main/include/NetworkVersionInfo.h create mode 100644 sdk/main/include/NetworkVersionInfoQuery.h create mode 100644 sdk/main/include/SemanticVersion.h create mode 100644 sdk/main/src/NetworkVersionInfo.cc create mode 100644 sdk/main/src/NetworkVersionInfoQuery.cc create mode 100644 sdk/main/src/SemanticVersion.cc create mode 100644 sdk/tests/integration/NetworkVersionInfoQueryIntegrationTests.cc create mode 100644 sdk/tests/unit/NetworkVersionInfoUnitTests.cc create mode 100644 sdk/tests/unit/SemanticVersionUnitTests.cc diff --git a/sdk/main/CMakeLists.txt b/sdk/main/CMakeLists.txt index 41e9060c5..f46745aad 100644 --- a/sdk/main/CMakeLists.txt +++ b/sdk/main/CMakeLists.txt @@ -66,6 +66,8 @@ add_library(${PROJECT_NAME} STATIC src/LedgerId.cc src/Mnemonic.cc src/MnemonicBIP39.cc + src/NetworkVersionInfo.cc + src/NetworkVersionInfoQuery.cc src/NftId.cc src/PrivateKey.cc src/PublicKey.cc @@ -76,6 +78,7 @@ add_library(${PROJECT_NAME} STATIC src/ScheduleInfo.cc src/ScheduleInfoQuery.cc src/ScheduleSignTransaction.cc + src/SemanticVersion.cc src/StakingInfo.cc src/Status.cc src/SubscriptionHandle.cc diff --git a/sdk/main/include/NetworkVersionInfo.h b/sdk/main/include/NetworkVersionInfo.h new file mode 100644 index 000000000..3a1e9ffc0 --- /dev/null +++ b/sdk/main/include/NetworkVersionInfo.h @@ -0,0 +1,95 @@ +/*- + * + * 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_NETWORK_VERSION_INFO_H_ +#define HEDERA_SDK_CPP_NETWORK_VERSION_INFO_H_ + +#include "SemanticVersion.h" + +#include +#include +#include + +namespace proto +{ +class NetworkGetVersionInfoResponse; +} + +namespace Hedera +{ +/** + * Contains information about the network's version. + */ +class NetworkVersionInfo +{ +public: + NetworkVersionInfo() = default; + + /** + * Construct from a protobuf schema SemanticVersion and a Hedera services SemanticVersion. + * + * @param hapi The SemanticVersion of the protobuf schema. + * @param hedera The SemanticVersion of the Hedera services. + */ + NetworkVersionInfo(const SemanticVersion& hapi, const SemanticVersion& hedera); + + /** + * Construct a NetworkVersionInfo object from a NetworkGetVersionInfoResponse protobuf object. + * + * @param proto The NetworkGetVersionInfoResponse protobuf object from which to construct a NetworkVersionInfo object. + * @return The constructed NetworkVersionInfo object. + */ + [[nodiscard]] static NetworkVersionInfo fromProtobuf(const proto::NetworkGetVersionInfoResponse& proto); + + /** + * Construct a NetworkVersionInfo object from a byte array. + * + * @param bytes The byte array from which to construct a NetworkVersionInfo object. + * @return The constructed NetworkVersionInfo object. + */ + [[nodiscard]] static NetworkVersionInfo fromBytes(const std::vector& bytes); + + /** + * Construct a NetworkGetVersionInfoResponse protobuf object from this NetworkVersionInfo object. + * + * @return A pointer to the created NetworkVersionInfo protobuf object. + */ + [[nodiscard]] std::unique_ptr toProtobuf() const; + + /** + * Construct a representative byte array from this NetworkVersionInfo object. + * + * @return The byte array representing this NetworkVersionInfo object. + */ + [[nodiscard]] std::vector toBytes() const; + + /** + * The version of the protobuf schema in use by the network. + */ + SemanticVersion mProtobufVersion; + + /** + * The version of the Hedera services in use by the network. + */ + SemanticVersion mServicesVersion; +}; + +} // namespace Hedera + +#endif // HEDERA_SDK_CPP_NETWORK_VERSION_INFO_H_ diff --git a/sdk/main/include/NetworkVersionInfoQuery.h b/sdk/main/include/NetworkVersionInfoQuery.h new file mode 100644 index 000000000..b8bbcbb21 --- /dev/null +++ b/sdk/main/include/NetworkVersionInfoQuery.h @@ -0,0 +1,83 @@ +/*- + * + * 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_NETWORK_VERSION_INFO_QUERY_H_ +#define HEDERA_SDK_CPP_NETWORK_VERSION_INFO_QUERY_H_ + +#include "Query.h" + +namespace Hedera +{ +class NetworkVersionInfo; +} + +namespace Hedera +{ +/** + * Get the deployed versions of Hedera Services and the HAPI proto in semantic version format. + */ +class NetworkVersionInfoQuery : public Query +{ +private: + /** + * Derived from Executable. Construct a Query protobuf object from this NetworkVersionInfoQuery object. + * + * @param client The Client trying to construct this NetworkVersionInfoQuery. + * @param node The Node to which this NetworkVersionInfoQuery will be sent. + * @return A Query protobuf object filled with this NetworkVersionInfoQuery object's data. + */ + [[nodiscard]] proto::Query makeRequest(const Client& client, + const std::shared_ptr& node) const override; + + /** + * Derived from Executable. Construct a NetworkVersionInfo object from a Response protobuf object. + * + * @param response The Response protobuf object from which to construct a NetworkVersionInfo object. + * @return A NetworkVersionInfo object filled with the Response protobuf object's data. + */ + [[nodiscard]] NetworkVersionInfo mapResponse(const proto::Response& response) const override; + + /** + * Derived from Executable. Get the status response code for a submitted NetworkVersionInfoQuery from a Response + * protobuf object. + * + * @param response The Response protobuf object from which to grab the NetworkVersionInfoQuery status response code. + * @return The NetworkVersionInfoQuery status response code of the input Response protobuf object. + */ + [[nodiscard]] Status mapResponseStatus(const proto::Response& response) const override; + + /** + * Derived from Executable. Submit this NetworkVersionInfoQuery to a Node. + * + * @param client The Client submitting this NetworkVersionInfoQuery. + * @param deadline The deadline for submitting this NetworkVersionInfoQuery. + * @param node Pointer to the Node to which this NetworkVersionInfoQuery should be submitted. + * @param response Pointer to the Response 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::Response* response) const override; +}; + +} // namespace Hedera + +#endif // HEDERA_SDK_CPP_NETWORK_VERSION_INFO_QUERY_H_ diff --git a/sdk/main/include/SemanticVersion.h b/sdk/main/include/SemanticVersion.h new file mode 100644 index 000000000..0d9533a2f --- /dev/null +++ b/sdk/main/include/SemanticVersion.h @@ -0,0 +1,131 @@ +/*- + * + * 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_SEMANTIC_VERSION_H_ +#define HEDERA_SDK_CPP_SEMANTIC_VERSION_H_ + +#include +#include +#include +#include +#include + +namespace proto +{ +class SemanticVersion; +} + +namespace Hedera +{ +/** + * 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. + */ +class SemanticVersion +{ +public: + SemanticVersion() = default; + + /** + * Construct from a major, minor, and patch number. + * + * @param major The major number. + * @param minor The minor number. + * @param patch The patch number. + * @param pre The pre-release version number. + * @param build The build metadata. + */ + SemanticVersion(int major, int minor, int patch, std::string_view pre = "", std::string_view build = ""); + + /** + * Compare this SemanticVersion to another SemanticVersion and determine if they represent the same semantic version. + * + * @param other The other SemanticVersion with which to compare this SemanticVersion. + * @return \c TRUE if this SemanticVersion is the same as the input SemanticVersion, otherwise \c FALSE. + */ + bool operator==(const SemanticVersion& other) const; + + /** + * Construct a SemanticVersion object from a SemanticVersion protobuf object. + * + * @param proto The SemanticVersion protobuf object from which to construct a SemanticVersion object. + * @return The constructed SemanticVersion object. + */ + [[nodiscard]] static SemanticVersion fromProtobuf(const proto::SemanticVersion& proto); + + /** + * Construct a SemanticVersion object from a byte array. + * + * @param bytes The byte array representing a SemanticVersion object. + * @return The constructed SemanticVersion object. + */ + [[nodiscard]] static SemanticVersion fromBytes(const std::vector& bytes); + + /** + * Construct a SemanticVersion protobuf object from this SemanticVersion object. + * + * @return A pointer to the created SemanticVersion protobuf object. + */ + [[nodiscard]] std::unique_ptr toProtobuf() const; + + /** + * Construct a representative byte array from this SemanticVersion object. + * + * @return The byte array representing this SemanticVersion object. + */ + [[nodiscard]] std::vector toBytes() const; + + /** + * Construct a string representation of this SemanticVersion object. + * + * @return The string representation of this SemanticVersion object. + */ + [[nodiscard]] std::string toString() const; + + /** + * Major number. Increases with incompatible API changes. + */ + int mMajor = 0; + + /** + * Minor number. Increases with backwards-compatible new functionality. + */ + int mMinor = 0; + + /** + * Patch number. Increases with backwards-compatible bug fixes. + */ + int mPatch = 0; + + /** + * Pre-release version. This may be denoted by appending a hyphen and a series of dot-separated identifiers. + */ + std::string mPre; + + /** + * Build metadata. This may be denoted by appending a plus sign and a series of dot-separated identifiers immediately + * following the patch or pre-release version. + */ + std::string mBuild; +}; + +} // namespace Hedera + +#endif // HEDERA_SDK_CPP_SEMANTIC_VERSION_H_ diff --git a/sdk/main/include/TransferTransaction.h b/sdk/main/include/TransferTransaction.h index 1384aa926..bc0d25a27 100644 --- a/sdk/main/include/TransferTransaction.h +++ b/sdk/main/include/TransferTransaction.h @@ -203,6 +203,7 @@ class TransferTransaction : public Transaction friend class ContractInfoQuery; friend class FileContentsQuery; friend class FileInfoQuery; + friend class NetworkVersionInfoQuery; friend class ScheduleInfoQuery; friend class TokenInfoQuery; friend class TokenNftInfoQuery; diff --git a/sdk/main/include/impl/Node.h b/sdk/main/include/impl/Node.h index 0a68dbb0b..316a908ee 100644 --- a/sdk/main/include/impl/Node.h +++ b/sdk/main/include/impl/Node.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -202,6 +203,11 @@ class Node */ std::unique_ptr mFreezeStub = nullptr; + /** + * Pointer to the gRPC stub used to communicate with the network service living on the remote node. + */ + std::unique_ptr mNetworkStub = nullptr; + /** * Pointer to the gRPC stub used to communicate with the schedule service living on the remote node. */ diff --git a/sdk/main/src/Executable.cc b/sdk/main/src/Executable.cc index 9a3588643..12fbcd353 100644 --- a/sdk/main/src/Executable.cc +++ b/sdk/main/src/Executable.cc @@ -48,6 +48,8 @@ #include "FileInfoQuery.h" #include "FileUpdateTransaction.h" #include "FreezeTransaction.h" +#include "NetworkVersionInfo.h" +#include "NetworkVersionInfoQuery.h" #include "ScheduleCreateTransaction.h" #include "ScheduleDeleteTransaction.h" #include "ScheduleInfo.h" @@ -378,6 +380,7 @@ template class Executable; template class Executable; template class Executable; +template class Executable; template class Executable + +namespace Hedera +{ +//----- +NetworkVersionInfo::NetworkVersionInfo(const SemanticVersion& hapi, const SemanticVersion& hedera) + : mProtobufVersion(hapi) + , mServicesVersion(hedera) +{ +} + +//----- +NetworkVersionInfo NetworkVersionInfo::fromProtobuf(const proto::NetworkGetVersionInfoResponse& proto) +{ + return NetworkVersionInfo(SemanticVersion::fromProtobuf(proto.hapiprotoversion()), + SemanticVersion::fromProtobuf(proto.hederaservicesversion())); +} + +//----- +NetworkVersionInfo NetworkVersionInfo::fromBytes(const std::vector& bytes) +{ + proto::NetworkGetVersionInfoResponse networkGetVersionInfoResponse; + networkGetVersionInfoResponse.ParseFromArray(bytes.data(), static_cast(bytes.size())); + return fromProtobuf(networkGetVersionInfoResponse); +} + +//----- +std::unique_ptr NetworkVersionInfo::toProtobuf() const +{ + auto proto = std::make_unique(); + proto->set_allocated_hapiprotoversion(mProtobufVersion.toProtobuf().release()); + proto->set_allocated_hederaservicesversion(mServicesVersion.toProtobuf().release()); + return proto; +} + +//----- +std::vector NetworkVersionInfo::toBytes() const +{ + return internal::Utilities::stringToByteVector(toProtobuf()->SerializeAsString()); +} + +} // namespace Hedera diff --git a/sdk/main/src/NetworkVersionInfoQuery.cc b/sdk/main/src/NetworkVersionInfoQuery.cc new file mode 100644 index 000000000..123a2d815 --- /dev/null +++ b/sdk/main/src/NetworkVersionInfoQuery.cc @@ -0,0 +1,78 @@ +/*- + * + * 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 "NetworkVersionInfoQuery.h" +#include "Client.h" +#include "NetworkVersionInfo.h" +#include "Status.h" +#include "TransferTransaction.h" +#include "impl/Node.h" + +#include +#include +#include +#include + +namespace Hedera +{ +//----- +proto::Query NetworkVersionInfoQuery::makeRequest(const Client& client, + const std::shared_ptr& node) const +{ + proto::Query query; + proto::NetworkGetVersionInfoQuery* getNetworkVersionInfoQuery = query.mutable_networkgetversioninfo(); + + proto::QueryHeader* header = getNetworkVersionInfoQuery->mutable_header(); + header->set_responsetype(proto::ANSWER_ONLY); + + TransferTransaction tx = TransferTransaction() + .setTransactionId(TransactionId::generate(*client.getOperatorAccountId())) + .setNodeAccountIds({ node->getAccountId() }) + .setMaxTransactionFee(Hbar(1LL)) + .addHbarTransfer(*client.getOperatorAccountId(), Hbar(-1LL)) + .addHbarTransfer(node->getAccountId(), Hbar(1LL)); + tx.onSelectNode(node); + header->set_allocated_payment(new proto::Transaction(tx.makeRequest(client, node))); + + return query; +} + +//----- +NetworkVersionInfo NetworkVersionInfoQuery::mapResponse(const proto::Response& response) const +{ + return NetworkVersionInfo::fromProtobuf(response.networkgetversioninfo()); +} + +//----- +Status NetworkVersionInfoQuery::mapResponseStatus(const proto::Response& response) const +{ + return gProtobufResponseCodeToStatus.at(response.networkgetversioninfo().header().nodetransactionprecheckcode()); +} + +//----- +grpc::Status NetworkVersionInfoQuery::submitRequest(const Client& client, + const std::chrono::system_clock::time_point& deadline, + const std::shared_ptr& node, + proto::Response* response) const +{ + return node->submitQuery( + proto::Query::QueryCase::kNetworkGetVersionInfo, makeRequest(client, node), deadline, response); +} + +} // namespace Hedera diff --git a/sdk/main/src/Query.cc b/sdk/main/src/Query.cc index 0d31d4b06..320af651c 100644 --- a/sdk/main/src/Query.cc +++ b/sdk/main/src/Query.cc @@ -29,6 +29,8 @@ #include "FileContentsQuery.h" #include "FileInfo.h" #include "FileInfoQuery.h" +#include "NetworkVersionInfo.h" +#include "NetworkVersionInfoQuery.h" #include "ScheduleInfo.h" #include "ScheduleInfoQuery.h" #include "TokenInfo.h" @@ -53,6 +55,7 @@ template class Query; template class Query; template class Query; template class Query; +template class Query; template class Query; template class Query; template class Query; diff --git a/sdk/main/src/SemanticVersion.cc b/sdk/main/src/SemanticVersion.cc new file mode 100644 index 000000000..b718c0b60 --- /dev/null +++ b/sdk/main/src/SemanticVersion.cc @@ -0,0 +1,94 @@ +/*- + * + * 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 "SemanticVersion.h" +#include "impl/Utilities.h" + +#include + +namespace Hedera +{ +//----- +SemanticVersion::SemanticVersion(int major, int minor, int patch, std::string_view pre, std::string_view build) + : mMajor(major) + , mMinor(minor) + , mPatch(patch) + , mPre(pre) + , mBuild(build) +{ +} + +//----- +bool SemanticVersion::operator==(const SemanticVersion& other) const +{ + return (mMajor == other.mMajor) && (mMinor == other.mMinor) && (mPatch == other.mPatch) && (mPre == other.mPre) && + (mBuild == other.mBuild); +} + +//----- +SemanticVersion SemanticVersion::fromProtobuf(const proto::SemanticVersion& proto) +{ + return { proto.major(), proto.minor(), proto.patch(), proto.pre(), proto.build() }; +} + +//----- +SemanticVersion SemanticVersion::fromBytes(const std::vector& bytes) +{ + proto::SemanticVersion semanticVersion; + semanticVersion.ParseFromArray(bytes.data(), static_cast(bytes.size())); + return fromProtobuf(semanticVersion); +} + +//----- +std::unique_ptr SemanticVersion::toProtobuf() const +{ + auto proto = std::make_unique(); + proto->set_major(mMajor); + proto->set_minor(mMinor); + proto->set_patch(mPatch); + proto->set_pre(mPre); + proto->set_build(mBuild); + return proto; +} + +//----- +std::vector SemanticVersion::toBytes() const +{ + return internal::Utilities::stringToByteVector(toProtobuf()->SerializeAsString()); +} + +//----- +std::string SemanticVersion::toString() const +{ + std::string str = std::to_string(mMajor) + '.' + std::to_string(mMinor) + '.' + std::to_string(mPatch); + + if (!mPre.empty()) + { + str += '-' + mPre; + } + + if (!mBuild.empty()) + { + str += '+' + mBuild; + } + + return str; +} + +} // namespace Hedera diff --git a/sdk/main/src/impl/Node.cc b/sdk/main/src/impl/Node.cc index 79e2ac23a..f9e844192 100644 --- a/sdk/main/src/impl/Node.cc +++ b/sdk/main/src/impl/Node.cc @@ -76,24 +76,29 @@ void Node::shutdown() mCryptoStub.reset(); } - if (mScheduleStub) + if (mFileStub) { - mScheduleStub.reset(); + mFileStub.reset(); } - if (mSmartContractStub) + if (mFreezeStub) { - mSmartContractStub.reset(); + mFreezeStub.reset(); } - if (mFileStub) + if (mNetworkStub) { - mFileStub.reset(); + mNetworkStub.reset(); } - if (mFreezeStub) + if (mScheduleStub) { - mFreezeStub.reset(); + mScheduleStub.reset(); + } + + if (mSmartContractStub) + { + mSmartContractStub.reset(); } if (mTokenStub) @@ -147,6 +152,8 @@ grpc::Status Node::submitQuery(proto::Query::QueryCase funcEnum, return mFileStub->getFileContent(&context, query, response); case proto::Query::QueryCase::kFileGetInfo: return mFileStub->getFileInfo(&context, query, response); + case proto::Query::QueryCase::kNetworkGetVersionInfo: + return mNetworkStub->getVersionInfo(&context, query, response); case proto::Query::QueryCase::kScheduleGetInfo: return mScheduleStub->getScheduleInfo(&context, query, response); case proto::Query::QueryCase::kTokenGetInfo: @@ -380,6 +387,7 @@ bool Node::initializeChannel(const std::chrono::system_clock::time_point& deadli mCryptoStub = proto::CryptoService::NewStub(mChannel); mFileStub = proto::FileService::NewStub(mChannel); mFreezeStub = proto::FreezeService::NewStub(mChannel); + mNetworkStub = proto::NetworkService::NewStub(mChannel); mScheduleStub = proto::ScheduleService::NewStub(mChannel); mSmartContractStub = proto::SmartContractService::NewStub(mChannel); mTokenStub = proto::TokenService::NewStub(mChannel); diff --git a/sdk/tests/integration/CMakeLists.txt b/sdk/tests/integration/CMakeLists.txt index ed470324f..a52e1cb9f 100644 --- a/sdk/tests/integration/CMakeLists.txt +++ b/sdk/tests/integration/CMakeLists.txt @@ -26,6 +26,7 @@ add_executable(${TEST_PROJECT_NAME} FileUpdateTransactionIntegrationTests.cc FreezeTransactionIntegrationTest.cc JSONIntegrationTest.cc + NetworkVersionInfoQueryIntegrationTests.cc ScheduleCreateTransactionIntegrationTests.cc ScheduleDeleteTransactionIntegrationTests.cc ScheduleInfoQueryIntegrationTests.cc diff --git a/sdk/tests/integration/NetworkVersionInfoQueryIntegrationTests.cc b/sdk/tests/integration/NetworkVersionInfoQueryIntegrationTests.cc new file mode 100644 index 000000000..48e017a91 --- /dev/null +++ b/sdk/tests/integration/NetworkVersionInfoQueryIntegrationTests.cc @@ -0,0 +1,38 @@ +/*- + * + * 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 "BaseIntegrationTest.h" +#include "NetworkVersionInfo.h" +#include "NetworkVersionInfoQuery.h" +#include "TransactionReceipt.h" + +#include + +using namespace Hedera; + +class NetworkVersionInfoQueryIntegrationTest : public BaseIntegrationTest +{ +}; + +//----- +TEST_F(NetworkVersionInfoQueryIntegrationTest, ExecuteNetworkVersionInfoQuery) +{ + // Given / When / Then + EXPECT_NO_THROW(const NetworkVersionInfo networkVersionInfo = NetworkVersionInfoQuery().execute(getTestClient())); +} diff --git a/sdk/tests/unit/CMakeLists.txt b/sdk/tests/unit/CMakeLists.txt index 086f95553..53e32d79f 100644 --- a/sdk/tests/unit/CMakeLists.txt +++ b/sdk/tests/unit/CMakeLists.txt @@ -59,6 +59,7 @@ add_executable(${TEST_PROJECT_NAME} HbarTransferTest.cc KeyListTest.cc LedgerIdTest.cc + NetworkVersionInfoUnitTests.cc NftIdTest.cc NodeAddressTest.cc ScheduleCreateTransactionUnitTests.cc @@ -67,6 +68,7 @@ add_executable(${TEST_PROJECT_NAME} ScheduleInfoQueryUnitTests.cc ScheduleInfoUnitTests.cc ScheduleSignTransactionUnitTests.cc + SemanticVersionUnitTests.cc StakingInfoTest.cc SystemDeleteTransactionUnitTests.cc SystemUndeleteTransactionUnitTests.cc diff --git a/sdk/tests/unit/NetworkVersionInfoUnitTests.cc b/sdk/tests/unit/NetworkVersionInfoUnitTests.cc new file mode 100644 index 000000000..a99715f1d --- /dev/null +++ b/sdk/tests/unit/NetworkVersionInfoUnitTests.cc @@ -0,0 +1,118 @@ +/*- + * + * 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 "NetworkVersionInfo.h" +#include "SemanticVersion.h" +#include "impl/Utilities.h" + +#include +#include + +using namespace Hedera; + +class NetworkVersionInfoTest : public ::testing::Test +{ +protected: + [[nodiscard]] inline const SemanticVersion& getTestHapiVersion() const { return mTestHapiVersion; } + [[nodiscard]] inline const SemanticVersion& getTestServicesVersion() const { return mTestServicesVersion; } + +private: + const SemanticVersion mTestHapiVersion = SemanticVersion(1, 2, 3); + const SemanticVersion mTestServicesVersion = SemanticVersion(4, 5, 6); +}; + +//----- +TEST_F(NetworkVersionInfoTest, ConstructWithValues) +{ + // Given / When + const NetworkVersionInfo networkVersionInfo(getTestHapiVersion(), getTestServicesVersion()); + + // Then + EXPECT_EQ(networkVersionInfo.mProtobufVersion, getTestHapiVersion()); + EXPECT_EQ(networkVersionInfo.mServicesVersion, getTestServicesVersion()); +} + +//----- +TEST_F(NetworkVersionInfoTest, FromProtobuf) +{ + // Given + proto::NetworkGetVersionInfoResponse networkGetVersionInfoResponse; + networkGetVersionInfoResponse.set_allocated_hapiprotoversion(getTestHapiVersion().toProtobuf().release()); + networkGetVersionInfoResponse.set_allocated_hederaservicesversion(getTestServicesVersion().toProtobuf().release()); + + // When + const NetworkVersionInfo networkVersionInfo = NetworkVersionInfo::fromProtobuf(networkGetVersionInfoResponse); + + // Then + EXPECT_EQ(networkVersionInfo.mProtobufVersion, getTestHapiVersion()); + EXPECT_EQ(networkVersionInfo.mServicesVersion, getTestServicesVersion()); +} + +//----- +TEST_F(NetworkVersionInfoTest, FromBytes) +{ + // Given + proto::NetworkGetVersionInfoResponse networkGetVersionInfoResponse; + networkGetVersionInfoResponse.set_allocated_hapiprotoversion(getTestHapiVersion().toProtobuf().release()); + networkGetVersionInfoResponse.set_allocated_hederaservicesversion(getTestServicesVersion().toProtobuf().release()); + + // When + const NetworkVersionInfo networkVersionInfo = NetworkVersionInfo::fromBytes( + internal::Utilities::stringToByteVector(networkGetVersionInfoResponse.SerializeAsString())); + + // Then + EXPECT_EQ(networkVersionInfo.mProtobufVersion, getTestHapiVersion()); + EXPECT_EQ(networkVersionInfo.mServicesVersion, getTestServicesVersion()); +} + +//----- +TEST_F(NetworkVersionInfoTest, ToProtobuf) +{ + // Given + const NetworkVersionInfo networkVersionInfo(getTestHapiVersion(), getTestServicesVersion()); + + // When + const std::unique_ptr protoNetworkVersionInfo = networkVersionInfo.toProtobuf(); + + // Then + EXPECT_EQ(protoNetworkVersionInfo->hapiprotoversion().major(), getTestHapiVersion().mMajor); + EXPECT_EQ(protoNetworkVersionInfo->hapiprotoversion().minor(), getTestHapiVersion().mMinor); + EXPECT_EQ(protoNetworkVersionInfo->hapiprotoversion().patch(), getTestHapiVersion().mPatch); + EXPECT_EQ(protoNetworkVersionInfo->hapiprotoversion().pre(), getTestHapiVersion().mPre); + EXPECT_EQ(protoNetworkVersionInfo->hapiprotoversion().build(), getTestHapiVersion().mBuild); + + EXPECT_EQ(protoNetworkVersionInfo->hederaservicesversion().major(), getTestServicesVersion().mMajor); + EXPECT_EQ(protoNetworkVersionInfo->hederaservicesversion().minor(), getTestServicesVersion().mMinor); + EXPECT_EQ(protoNetworkVersionInfo->hederaservicesversion().patch(), getTestServicesVersion().mPatch); + EXPECT_EQ(protoNetworkVersionInfo->hederaservicesversion().pre(), getTestServicesVersion().mPre); + EXPECT_EQ(protoNetworkVersionInfo->hederaservicesversion().build(), getTestServicesVersion().mBuild); +} + +//----- +TEST_F(NetworkVersionInfoTest, ToBytes) +{ + // Given + const NetworkVersionInfo networkVersionInfo(getTestHapiVersion(), getTestServicesVersion()); + + // When + const std::vector bytes = networkVersionInfo.toBytes(); + + // Then + EXPECT_EQ(bytes, internal::Utilities::stringToByteVector(networkVersionInfo.toProtobuf()->SerializeAsString())); +} diff --git a/sdk/tests/unit/SemanticVersionUnitTests.cc b/sdk/tests/unit/SemanticVersionUnitTests.cc new file mode 100644 index 000000000..e538df703 --- /dev/null +++ b/sdk/tests/unit/SemanticVersionUnitTests.cc @@ -0,0 +1,136 @@ +/*- + * + * 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 "SemanticVersion.h" +#include "impl/Utilities.h" + +#include +#include +#include + +using namespace Hedera; + +class SemanticVersionTest : public ::testing::Test +{ +protected: + [[nodiscard]] inline const int& getTestMajor() const { return mTestMajor; } + [[nodiscard]] inline const int& getTestMinor() const { return mTestMinor; } + [[nodiscard]] inline const int& getTestPatch() const { return mTestPatch; } + [[nodiscard]] inline const std::string& getTestPrerelease() const { return mTestPrerelease; } + [[nodiscard]] inline const std::string& getTestBuildMetadata() const { return mTestBuildMetadata; } + +private: + const int mTestMajor = 1; + const int mTestMinor = 2; + const int mTestPatch = 3; + const std::string mTestPrerelease = "test pre-release"; + const std::string mTestBuildMetadata = "test build metadata"; +}; + +//----- +TEST_F(SemanticVersionTest, ConstructWithValues) +{ + // Given / When + const SemanticVersion semanticVersion( + getTestMajor(), getTestMinor(), getTestPatch(), getTestPrerelease(), getTestBuildMetadata()); + + // Then + EXPECT_EQ(semanticVersion.mMajor, getTestMajor()); + EXPECT_EQ(semanticVersion.mMinor, getTestMinor()); + EXPECT_EQ(semanticVersion.mPatch, getTestPatch()); + EXPECT_EQ(semanticVersion.mPre, getTestPrerelease()); + EXPECT_EQ(semanticVersion.mBuild, getTestBuildMetadata()); +} + +//----- +TEST_F(SemanticVersionTest, FromProtobuf) +{ + // Given + proto::SemanticVersion protoSemanticVersion; + protoSemanticVersion.set_major(getTestMajor()); + protoSemanticVersion.set_minor(getTestMinor()); + protoSemanticVersion.set_patch(getTestPatch()); + protoSemanticVersion.set_pre(getTestPrerelease()); + protoSemanticVersion.set_build(getTestBuildMetadata()); + + // When + const SemanticVersion semanticVersion = SemanticVersion::fromProtobuf(protoSemanticVersion); + + // Then + EXPECT_EQ(semanticVersion.mMajor, getTestMajor()); + EXPECT_EQ(semanticVersion.mMinor, getTestMinor()); + EXPECT_EQ(semanticVersion.mPatch, getTestPatch()); + EXPECT_EQ(semanticVersion.mPre, getTestPrerelease()); + EXPECT_EQ(semanticVersion.mBuild, getTestBuildMetadata()); +} + +//----- +TEST_F(SemanticVersionTest, FromBytes) +{ + // Given + proto::SemanticVersion protoSemanticVersion; + protoSemanticVersion.set_major(getTestMajor()); + protoSemanticVersion.set_minor(getTestMinor()); + protoSemanticVersion.set_patch(getTestPatch()); + protoSemanticVersion.set_pre(getTestPrerelease()); + protoSemanticVersion.set_build(getTestBuildMetadata()); + + // When + const SemanticVersion semanticVersion = + SemanticVersion::fromBytes(internal::Utilities::stringToByteVector(protoSemanticVersion.SerializeAsString())); + + // Then + EXPECT_EQ(semanticVersion.mMajor, getTestMajor()); + EXPECT_EQ(semanticVersion.mMinor, getTestMinor()); + EXPECT_EQ(semanticVersion.mPatch, getTestPatch()); + EXPECT_EQ(semanticVersion.mPre, getTestPrerelease()); + EXPECT_EQ(semanticVersion.mBuild, getTestBuildMetadata()); +} + +//----- +TEST_F(SemanticVersionTest, ToProtobuf) +{ + // Given + const SemanticVersion semanticVersion( + getTestMajor(), getTestMinor(), getTestPatch(), getTestPrerelease(), getTestBuildMetadata()); + + // When + const std::unique_ptr protoSemanticVersion = semanticVersion.toProtobuf(); + + // Then + EXPECT_EQ(protoSemanticVersion->major(), getTestMajor()); + EXPECT_EQ(protoSemanticVersion->minor(), getTestMinor()); + EXPECT_EQ(protoSemanticVersion->patch(), getTestPatch()); + EXPECT_EQ(protoSemanticVersion->pre(), getTestPrerelease()); + EXPECT_EQ(protoSemanticVersion->build(), getTestBuildMetadata()); +} + +//----- +TEST_F(SemanticVersionTest, ToBytes) +{ + // Given + const SemanticVersion semanticVersion( + getTestMajor(), getTestMinor(), getTestPatch(), getTestPrerelease(), getTestBuildMetadata()); + + // When + const std::vector bytes = semanticVersion.toBytes(); + + // Then + EXPECT_EQ(bytes, internal::Utilities::stringToByteVector(semanticVersion.toProtobuf()->SerializeAsString())); +}