diff --git a/src/tck/CMakeLists.txt b/src/tck/CMakeLists.txt index 19d043049..0aa57f769 100644 --- a/src/tck/CMakeLists.txt +++ b/src/tck/CMakeLists.txt @@ -10,6 +10,7 @@ add_executable(${TCK_SERVER_NAME} src/key/KeyService.cc src/sdk/SdkClient.cc src/token/TokenService.cc + src/topic/TopicService.cc src/main.cc src/TckServer.cc) diff --git a/src/tck/include/topic/TopicService.h b/src/tck/include/topic/TopicService.h new file mode 100644 index 000000000..38f3e2e15 --- /dev/null +++ b/src/tck/include/topic/TopicService.h @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: Apache-2.0 +#ifndef HIERO_TCK_CPP_TOPIC_SERVICE_H_ +#define HIERO_TCK_CPP_TOPIC_SERVICE_H_ + +#include + +namespace Hiero::TCK::TopicService +{ +/** + * Forward declarations. + */ +struct CreateTopicParams; +struct DeleteTopicParams; +struct GetTopicInfoQueryParams; +struct TopicMessageSubmitParams; + +/** + * Create a topic. + * + * @param params The parameters to use to create a topic. + * @return A JSON response containing the status of the topic creation and the new topic's ID. + */ +nlohmann::json createTopic(const CreateTopicParams& params); + +/** + * Delete a topic + * + * @param params The parameters to use to delete a topic. + * @return A json response containing the status of the deletion of the topic. + */ +nlohmann::json deleteTopic(const DeleteTopicParams& params); + +/** + * Get topic info. + * + * @param params The parameters to use to get info of a topic. + * @return A JSON response containing the status of the topic info. + */ +nlohmann::json getTopicInfo(const GetTopicInfoQueryParams& params); + +/** + * Submit a topic message. + * + * @param params The parameters to use to submit a topic message. + * @return A JSON response containing the status of the submission of the topic message. + */ +nlohmann::json submitTopicMessage(const TopicMessageSubmitParams& params); + +} // namespace Hiero::TCK::TopicService + +#endif // HIERO_TCK_CPP_TOPIC_SERVICE_H_ diff --git a/src/tck/include/topic/params/CreateTopicParams.h b/src/tck/include/topic/params/CreateTopicParams.h new file mode 100644 index 000000000..fb0227fcf --- /dev/null +++ b/src/tck/include/topic/params/CreateTopicParams.h @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: Apache-2.0 +#ifndef HIERO_TCK_CPP_CREATE_TOPIC_PARAMS_H_ +#define HIERO_TCK_CPP_CREATE_TOPIC_PARAMS_H_ + +#include "common/CommonTransactionParams.h" +#include "nlohmann/json.hpp" + +#include "token/CustomFeeSerializer.h" + +#include "json/JsonUtils.h" +#include +#include +#include + +namespace Hiero::TCK::TopicService +{ +/** + * Struct to hold the arguments for a `createTopic` JSON-RPC method call. + */ +struct CreateTopicParams +{ + /** + * Short publicly visible memo about the topic. No guarantee of uniqueness. (UTF-8 encoding max 100 bytes) + */ + std::optional mMemo; + + /** + * Access control for update/delete of the topic. DER-encoded hex string representation for private or public keys. + * Keylists and threshold keys are the hex of the serialized protobuf bytes. + */ + std::optional mAdminKey; + + /** + * Access control for submit message. DER-encoded hex string representation for private or public keys. Keylists and + * threshold keys are the hex of the serialized protobuf bytes. + */ + std::optional mSubmitKey; + + /** + * The amount of time to attempt to extend the topic's lifetime by automatically at the topic's expirationTime. Units + * of seconds. Min: 6999999 (≈30 days), Max: 8000001 (≈92 days) + */ + std::optional mAutoRenewPeriod; + + /** + * Optional account to be used at the topic's expirationTime to extend the life of the topic. Must sign transaction if + * specified. + */ + std::optional mAutoRenewAccount; + + /** + * A key that controls updates and deletions of topic fees. DER-encoded hex string representation for private or + * public keys. Keylists and threshold keys are the hex of the serialized protobuf bytes. + */ + std::optional mFeeScheduleKey; + + /** + * A list of keys that, if used to sign a message submission, allow the sender to bypass fees. DER-encoded hex string + * representation for private or public keys. + */ + std::optional> mFeeExemptKeys; + + /** + * A fee structure applied to message submissions for revenue generation. + */ + std::optional>> mCustomFees; + + /** + * Any parameters common to all transaction types. + */ + std::optional mCommonTxParams; +}; + +} // namespace Hiero::TCK::TopicService + +namespace nlohmann +{ +/** + * JSON serializer template specialization required to convert CreateTopicParams arguments properly. + */ +template<> +struct [[maybe_unused]] adl_serializer +{ + /** + * Convert a Json Object to a CreateTopicParams + * + * @param jsonFrom The json object which will fill the CreateTopicParams. + * @param params The CreateTopicParam to fill the json object. + */ + static void from_json(const json& jsonFrom, Hiero::TCK::TopicService::CreateTopicParams& params) + { + params.mMemo = Hiero::TCK::getOptionalJsonParameter(jsonFrom, "memo"); + + params.mAdminKey = Hiero::TCK::getOptionalJsonParameter(jsonFrom, "adminKey"); + + params.mSubmitKey = Hiero::TCK::getOptionalJsonParameter(jsonFrom, "submitKey"); + + params.mAutoRenewPeriod = Hiero::TCK::getOptionalJsonParameter(jsonFrom, "autoRenewPeriod"); + + params.mAutoRenewAccount = Hiero::TCK::getOptionalJsonParameter(jsonFrom, "autoRenewAccount"); + + params.mFeeScheduleKey = Hiero::TCK::getOptionalJsonParameter(jsonFrom, "feeScheduleKey"); + + params.mFeeExemptKeys = Hiero::TCK::getOptionalJsonParameter>(jsonFrom, "feeExemptKeys"); + + params.mCustomFees = + Hiero::TCK::getOptionalJsonParameter>>(jsonFrom, "customFees"); + + params.mCommonTxParams = + Hiero::TCK::getOptionalJsonParameter(jsonFrom, "commonTransactionParams"); + } +}; + +} // namespace nlohmann + +#endif // HIERO_TCK_CPP_CREATE_TOPIC_PARAMS_H_ diff --git a/src/tck/include/topic/params/DeleteTopicParams.h b/src/tck/include/topic/params/DeleteTopicParams.h new file mode 100644 index 000000000..8a5c52a09 --- /dev/null +++ b/src/tck/include/topic/params/DeleteTopicParams.h @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: Apache-2.0 +#ifndef HIERO_TCK_CPP_DELETE_TOPIC_PARAMS_H_ +#define HIERO_TCK_CPP_DELETE_TOPIC_PARAMS_H_ + +#include "common/CommonTransactionParams.h" +#include "nlohmann/json.hpp" + +#include "json/JsonUtils.h" +#include +#include + +namespace Hiero::TCK::TopicService +{ +/** + * Struct to hold the arguments for a `deleteTopic` Json-RPC method call. + */ +struct DeleteTopicParams +{ + /** + * The ID of the topic to delete. + */ + std::optional mTopicId; + + /** + * Any parameters common to all transaction types. + */ + std::optional mCommonTxParams; +}; + +} // namespace Hiero::TCK::TopicService + +namespace nlohmann +{ +/** + * JSON serializer template specialisation required to convert DeleteTopicParams argument properly. + */ +template<> +struct [[maybe_unused]] adl_serializer +{ + /** + * Convert a JSON object to a DeleteTopicParams. + * + * @param jsonFrom The JSON object with which to fill the DeleteTopicParams. + * @param params The DeleteTopicParams to fill with the JSON object. + */ + static void from_json(const json& jsonFrom, Hiero::TCK::TopicService::DeleteTopicParams& params) + { + params.mTopicId = Hiero::TCK::getOptionalJsonParameter(jsonFrom, "topicId"); + + params.mCommonTxParams = + Hiero::TCK::getOptionalJsonParameter(jsonFrom, "commonTransactionParams"); + } +}; + +} // namespace nlohmann + +#endif // HIERO_TCK_CPP_DELETE_TOPIC_PARAMS_H_ diff --git a/src/tck/include/topic/params/GetTopicInfoQueryParams.h b/src/tck/include/topic/params/GetTopicInfoQueryParams.h new file mode 100644 index 000000000..ea3ade739 --- /dev/null +++ b/src/tck/include/topic/params/GetTopicInfoQueryParams.h @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: Apache-2.0 +#ifndef HIERO_TCK_CPP_GET_TOPIC_INFO_QUERY_PARAMS_H_ +#define HIERO_TCK_CPP_GET_TOPIC_INFO_QUERY_PARAMS_H_ + +#include "nlohmann/json.hpp" + +#include "json/JsonUtils.h" +#include +#include + +namespace Hiero::TCK::TopicService +{ +/** + * Struct to hold the arguments for a `getTopicInfo` Json-RPC method call + */ +struct GetTopicInfoQueryParams +{ + /** + * The ID of the topic to query. + */ + std::optional mTopicId; + + /** + * Explicit payment amount for the query in tinybars. + */ + std::optional mQueryPayment; + + /** + * Maximum payment amount for the query in tinybars. + */ + std::optional mMaxQueryPayment; +}; + +} // namespace Hiero::TCK::TopicService + +namespace nlohmann +{ +/** + * JSON serializer template specialisation required to convert GetTopicInfoQueryParams arguments properly. + */ +template<> +struct [[maybe_unused]] adl_serializer +{ + /** + * Convert a Json Object to a GetTopicInfoQueryParams + * + * @param jsonFrom The json object which will fill the GetTopicInfoQueryParams. + * @param params The GetTopicInfoQueryParams to fill the json object. + */ + static void from_json(const json& jsonFrom, Hiero::TCK::TopicService::GetTopicInfoQueryParams& params) + { + params.mTopicId = Hiero::TCK::getOptionalJsonParameter(jsonFrom, "topicId"); + + params.mQueryPayment = Hiero::TCK::getOptionalJsonParameter(jsonFrom, "queryPayment"); + + params.mMaxQueryPayment = Hiero::TCK::getOptionalJsonParameter(jsonFrom, "maxQueryPayment"); + } +}; + +} // namespace nlohmann + +#endif // HIERO_TCK_CPP_GET_TOPIC_INFO_QUERY_PARAMS_H_ diff --git a/src/tck/include/topic/params/TopicMessageSubmitParams.h b/src/tck/include/topic/params/TopicMessageSubmitParams.h new file mode 100644 index 000000000..5422e8688 --- /dev/null +++ b/src/tck/include/topic/params/TopicMessageSubmitParams.h @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: Apache-2.0 +#ifndef HIERO_TCK_CPP_TOPIC_MESSAGE_SUBMIT_PARAMS_H_ +#define HIERO_TCK_CPP_TOPIC_MESSAGE_SUBMIT_PARAMS_H_ + +#include "common/CommonTransactionParams.h" +#include "nlohmann/json.hpp" + +#include "json/JsonUtils.h" +#include +#include + +namespace Hiero::TCK::TopicService +{ +/** + * Struct to hold the arguments for a `submitTopicMessage` JSON-RPC method call + */ +struct TopicMessageSubmitParams +{ + /** + * The ID of the topic to submit the message to. + */ + std::optional mTopicId; + + /** + * The message content to submit. UTF-8 encoding. Will be automatically chunked if the message exceeds the chunk size. + */ + std::optional mMessage; + + /** + * The maximum number of chunks the message can be split into. Default: 20. Used when message size exceeds chunk size. + */ + std::optional mMaxChunks; + + /** + * Any parameters common to all transaction types. + */ + std::optional mCommonTxParams; +}; + +} // namespace Hiero::TCK::TopicService + +namespace nlohmann +{ +/** + * Json serializer template specialisation required to convert TopicMessageSubmitParams argument properly + */ +template<> +struct [[maybe_unused]] adl_serializer +{ + /** + * Convert a Json Object to a TopicMessageSubmitParams + * + * @param jsonFrom The JSON object with which to fill the TopicMessageSubmitParams + * @param params the TopicMessageSubmitParams to fill with the JSON object. + */ + static void from_json(const json& jsonFrom, Hiero::TCK::TopicService::TopicMessageSubmitParams& params) + { + params.mTopicId = Hiero::TCK::getOptionalJsonParameter(jsonFrom, "topicId"); + + params.mMessage = Hiero::TCK::getOptionalJsonParameter(jsonFrom, "message"); + + params.mMaxChunks = Hiero::TCK::getOptionalJsonParameter(jsonFrom, "maxChunks"); + + params.mCommonTxParams = + Hiero::TCK::getOptionalJsonParameter(jsonFrom, "commonTransactionParams"); + } +}; + +} // namespace nlohmann + +#endif // HIERO_TCK_CPP_TOPIC_MESSAGE_SUBMIT_PARAMS_H_ diff --git a/src/tck/src/TckServer.cc b/src/tck/src/TckServer.cc index b66af7e02..1493cdc08 100644 --- a/src/tck/src/TckServer.cc +++ b/src/tck/src/TckServer.cc @@ -44,6 +44,11 @@ #include "token/params/UpdateTokenFeeScheduleParams.h" #include "token/params/UpdateTokenParams.h" #include "token/params/WipeTokenParams.h" +#include "topic/TopicService.h" +#include "topic/params/CreateTopicParams.h" +#include "topic/params/DeleteTopicParams.h" +#include "topic/params/GetTopicInfoQueryParams.h" +#include "topic/params/TopicMessageSubmitParams.h" #include "json/JsonUtils.h" namespace Hiero::TCK @@ -97,6 +102,12 @@ TckServer::TckServer(int port) mJsonRpcParser.addMethod("updateTokenFeeSchedule", getHandle(TokenService::updateTokenFeeSchedule)); mJsonRpcParser.addMethod("wipeToken", getHandle(TokenService::wipeToken)); + // Topic Service + mJsonRpcParser.addMethod("createTopic", getHandle(TopicService::createTopic)); + mJsonRpcParser.addMethod("deleteTopic", getHandle(TopicService::deleteTopic)); + mJsonRpcParser.addMethod("getTopicInfo", getHandle(TopicService::getTopicInfo)); + mJsonRpcParser.addMethod("submitTopicMessage", getHandle(TopicService::submitTopicMessage)); + // Add the FileService functions. mJsonRpcParser.addMethod("appendFile", getHandle(FileService::appendFile)); mJsonRpcParser.addMethod("createFile", getHandle(FileService::createFile)); @@ -225,5 +236,13 @@ template TckServer::MethodHandle TckServer::getHandle( nlohmann::json (*method)(const FileService::UpdateFileParams&)); +template TckServer::MethodHandle TckServer::getHandle( + nlohmann::json (*method)(const TopicService::CreateTopicParams&)); +template TckServer::MethodHandle TckServer::getHandle( + nlohmann::json (*method)(const TopicService::DeleteTopicParams&)); +template TckServer::MethodHandle TckServer::getHandle( + nlohmann::json (*method)(const TopicService::GetTopicInfoQueryParams&)); +template TckServer::MethodHandle TckServer::getHandle( + nlohmann::json (*method)(const TopicService::TopicMessageSubmitParams&)); } // namespace Hiero::TCK diff --git a/src/tck/src/topic/TopicService.cc b/src/tck/src/topic/TopicService.cc new file mode 100644 index 000000000..c6b3b2b76 --- /dev/null +++ b/src/tck/src/topic/TopicService.cc @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: Apache-2.0 +#include "topic/TopicService.h" +#include "common/CommonTransactionParams.h" +#include "key/KeyService.h" +#include "sdk/SdkClient.h" +#include "token/CustomFeeSerializer.h" +#include "topic/params/CreateTopicParams.h" +#include "topic/params/DeleteTopicParams.h" +#include "topic/params/GetTopicInfoQueryParams.h" +#include "topic/params/TopicMessageSubmitParams.h" +#include "json/JsonErrorType.h" +#include "json/JsonRpcException.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace Hiero::TCK::TopicService +{ +//----- +nlohmann::json createTopic(const CreateTopicParams& params) +{ + TopicCreateTransaction topicCreateTransaction; + topicCreateTransaction.setGrpcDeadline(SdkClient::DEFAULT_TCK_REQUEST_TIMEOUT); + + if (params.mMemo.has_value()) + { + topicCreateTransaction.setMemo(params.mMemo.value()); + } + + if (params.mAdminKey.has_value()) + { + topicCreateTransaction.setAdminKey(KeyService::getHieroKey(params.mAdminKey.value())); + } + + if (params.mSubmitKey.has_value()) + { + topicCreateTransaction.setSubmitKey(KeyService::getHieroKey(params.mSubmitKey.value())); + } + + if (params.mAutoRenewPeriod.has_value()) + { + topicCreateTransaction.setAutoRenewPeriod( + std::chrono::seconds(Hiero::internal::EntityIdHelper::getNum(params.mAutoRenewPeriod.value()))); + } + + if (params.mAutoRenewAccount.has_value()) + { + topicCreateTransaction.setAutoRenewAccountId(AccountId::fromString(params.mAutoRenewAccount.value())); + } + + if (params.mFeeScheduleKey.has_value()) + { + topicCreateTransaction.setFeeScheduleKey(KeyService::getHieroKey(params.mFeeScheduleKey.value())); + } + + if (params.mFeeExemptKeys.has_value()) + { + std::vector> feeExemptKeys; + for (const auto& keyStr : params.mFeeExemptKeys.value()) + { + feeExemptKeys.push_back(KeyService::getHieroKey(keyStr)); + } + topicCreateTransaction.setFeeExemptKeys(feeExemptKeys); + } + + if (params.mCustomFees.has_value()) + { + std::vector fixedFees; + for (const auto& fee : params.mCustomFees.value()) + { + if (CustomFixedFee* feePtr = dynamic_cast(fee.get())) + { + fixedFees.push_back(*feePtr); + } + } + topicCreateTransaction.setCustomFixedFees(fixedFees); + } + + if (params.mCommonTxParams.has_value()) + { + params.mCommonTxParams->fillOutTransaction(topicCreateTransaction, SdkClient::getClient()); + } + + const TransactionReceipt txReceipt = + topicCreateTransaction.execute(SdkClient::getClient()).getReceipt(SdkClient::getClient()); + + return { + {"topicId", txReceipt.mTopicId.value().toString()}, + { "status", gStatusToString.at(txReceipt.mStatus)} + }; +} + +//----- +nlohmann::json deleteTopic(const DeleteTopicParams& params) +{ + TopicDeleteTransaction topicDeleteTransaction; + topicDeleteTransaction.setGrpcDeadline(SdkClient::DEFAULT_TCK_REQUEST_TIMEOUT); + + if (params.mTopicId.has_value()) + { + topicDeleteTransaction.setTopicId(TopicId::fromString(params.mTopicId.value())); + } + + if (params.mCommonTxParams.has_value()) + { + params.mCommonTxParams->fillOutTransaction(topicDeleteTransaction, SdkClient::getClient()); + } + + return { + {"status", + gStatusToString.at( + topicDeleteTransaction.execute(SdkClient::getClient()).getReceipt(SdkClient::getClient()).mStatus)}, + }; +} + +//----- +nlohmann::json getTopicInfo(const GetTopicInfoQueryParams& params) +{ + TopicInfoQuery getTopicInfoQuery; + getTopicInfoQuery.setGrpcDeadline(SdkClient::DEFAULT_TCK_REQUEST_TIMEOUT); + + if (params.mTopicId.has_value()) + { + getTopicInfoQuery.setTopicId(TopicId::fromString(params.mTopicId.value())); + } + + if (params.mQueryPayment.has_value()) + { + getTopicInfoQuery.setQueryPayment(Hbar::fromTinybars(std::stoll(params.mQueryPayment.value()))); + } + + if (params.mMaxQueryPayment.has_value()) + { + getTopicInfoQuery.setMaxQueryPayment(Hbar::fromTinybars(std::stoll(params.mMaxQueryPayment.value()))); + } + + const TopicInfo info = getTopicInfoQuery.execute(SdkClient::getClient()); + + nlohmann::json response; + + response["topicId"] = info.mTopicId.toString(); + response["topicMemo"] = info.mMemo; + response["sequenceNumber"] = std::to_string(info.mSequenceNumber); + + auto expirySeconds = + std::chrono::duration_cast(info.mExpirationTime.time_since_epoch()).count(); + response["expirationTime"] = std::to_string(expirySeconds); + + if (info.mAutoRenewPeriod.has_value()) + { + response["autoRenewPeriod"] = + std::to_string(std::chrono::duration_cast(info.mAutoRenewPeriod.value()).count()); + } + + if (info.mAutoRenewAccountId.has_value()) + { + response["autoRenewAccountId"] = info.mAutoRenewAccountId.value().toString(); + } + + if (info.mAdminKey) + { + response["adminKey"] = internal::HexConverter::bytesToHex(info.mAdminKey->toBytes()); + } + + if (info.mSubmitKey) + { + response["submitKey"] = internal::HexConverter::bytesToHex(info.mSubmitKey->toBytes()); + } + + if (info.mFeeScheduleKey) + { + response["feeScheduleKey"] = internal::HexConverter::bytesToHex(info.mFeeScheduleKey->toBytes()); + } + + response["feeExemptKeys"] = nlohmann::json::array(); + for (const auto& key : info.mFeeExemptKeys) + { + response["feeExemptKeys"].push_back(internal::HexConverter::bytesToHex(key->toBytes())); + } + + response["customFees"] = nlohmann::json::array(); + for (const auto& fee : info.mCustomFixedFees) + { + std::shared_ptr feePtr = std::make_shared(fee); + response["customFees"].push_back(feePtr); + } + + response["ledgerId"] = info.mLedgerId.toString(); + + return response; +} + +//----- +nlohmann::json submitTopicMessage(const TopicMessageSubmitParams& params) +{ + TopicMessageSubmitTransaction topicMessageSubmitTransaction; + + topicMessageSubmitTransaction.setGrpcDeadline(SdkClient::DEFAULT_TCK_REQUEST_TIMEOUT); + + if (params.mTopicId.has_value()) + { + topicMessageSubmitTransaction.setTopicId(TopicId::fromString(params.mTopicId.value())); + } + + if (params.mMessage.has_value()) + { + topicMessageSubmitTransaction.setMessage(params.mMessage.value()); + } + + if (params.mMaxChunks.has_value()) + { + topicMessageSubmitTransaction.setMaxChunks(static_cast(params.mMaxChunks.value())); + } + + if (params.mCommonTxParams.has_value()) + { + params.mCommonTxParams->fillOutTransaction(topicMessageSubmitTransaction, SdkClient::getClient()); + } + + return { + {"status", + gStatusToString.at( + topicMessageSubmitTransaction.execute(SdkClient::getClient()).getReceipt(SdkClient::getClient()).mStatus)} + }; +} + +} // namespace Hiero::TCK::TopicService