Skip to content

Commit

Permalink
Merge branch 'main' into 00166-test-for-networking-configuration
Browse files Browse the repository at this point in the history
Signed-off-by: Deyan Zhekov <[email protected]>
  • Loading branch information
deyanzz authored Oct 25, 2023
2 parents 5eb7f73 + 63c8398 commit cbb0a03
Show file tree
Hide file tree
Showing 39 changed files with 5,302 additions and 5 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ find_package(absl CONFIG REQUIRED)
find_package(upb CONFIG REQUIRED)
find_package(Threads REQUIRED)
find_package(nlohmann_json CONFIG REQUIRED)
find_package(CURL CONFIG REQUIRED)

include(FetchContent)
include(SystemLibraries.cmake)
Expand Down
26 changes: 26 additions & 0 deletions sdk/examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,17 @@ set(SCHEDULE_IDENTICAL_TRANSACTION_EXAMPLE_NAME ${PROJECT_NAME}-schedule-identic
set(SCHEDULE_MULTI_SIG_TRANSACTION_EXAMPLE_NAME ${PROJECT_NAME}-schedule-multisig-transaction-example)
set(SCHEDULE_TRANSACTION_MULTI_SIG_TRANSACTION_EXAMPLE_NAME ${PROJECT_NAME}-schedule-transaction-multisig-threshold-example)
set(SCHEDULE_TRANSFER_EXAMPLE_NAME ${PROJECT_NAME}-schedule-transfer-example)
set(SIGN_TRANSACTION_EXAMPLE_NAME ${PROJECT_NAME}-sign-transaction-example)
set(SOLIDITY_PRECOMPILE_EXAMPLE_NAME ${PROJECT_NAME}-solidity-precompile-example)
set(STAKING_EXAMPLE_NAME ${PROJECT_NAME}-staking-example)
set(STAKING_WITH_UPDATE_EXAMPLE_NAME ${PROJECT_NAME}-staking-with-update-example)
set(TOPIC_WITH_ADMIN_KEY_EXAMPLE_NAME ${PROJECT_NAME}-topic-with-admin-key-example)
set(TRANSFER_CRYPTO_EXAMPLE_NAME ${PROJECT_NAME}-transfer-crypto-example)
set(TRANSFER_TOKENS_EXAMPLE_NAME ${PROJECT_NAME}-transfer-tokens-example)
set(TRANSFER_USING_EVM_ADDRESS_EXAMPLE_NAME ${PROJECT_NAME}-transfer-using-evm-address-example)
set(UPDATE_ACCOUNT_PUBLIC_KEY_EXAMPLE_NAME ${PROJECT_NAME}-update-account-public-key-example)
set(VALIDATE_CHECKSUM_EXAMPLE_NAME ${PROJECT_NAME}-validate-checksum-example)
set(ZERO_TOKEN_OPERATIONS_EXAMPLE_NAME ${PROJECT_NAME}-zero-token-operations-example)

add_executable(${ACCOUNT_ALIAS_EXAMPLE_NAME} AccountAliasExample.cc)
add_executable(${ACCOUNT_ALLOWANCE_EXAMPLE_NAME} AccountAllowanceExample.cc)
Expand Down Expand Up @@ -78,11 +84,17 @@ add_executable(${SCHEDULE_IDENTICAL_TRANSACTION_EXAMPLE_NAME} ScheduleIdenticalT
add_executable(${SCHEDULE_MULTI_SIG_TRANSACTION_EXAMPLE_NAME} ScheduleMultiSigTransactionExample.cc)
add_executable(${SCHEDULE_TRANSACTION_MULTI_SIG_TRANSACTION_EXAMPLE_NAME} ScheduleTransactionMultiSigThresholdExample.cc)
add_executable(${SCHEDULE_TRANSFER_EXAMPLE_NAME} ScheduleTransferExample.cc)
add_executable(${SIGN_TRANSACTION_EXAMPLE_NAME} SignTransactionExample.cc)
add_executable(${SOLIDITY_PRECOMPILE_EXAMPLE_NAME} SolidityPrecompileExample.cc)
add_executable(${STAKING_EXAMPLE_NAME} StakingExample.cc)
add_executable(${STAKING_WITH_UPDATE_EXAMPLE_NAME} StakingWithUpdateExample.cc)
add_executable(${TOPIC_WITH_ADMIN_KEY_EXAMPLE_NAME} TopicWithAdminKeyExample.cc)
add_executable(${TRANSFER_CRYPTO_EXAMPLE_NAME} TransferCryptoExample.cc)
add_executable(${TRANSFER_TOKENS_EXAMPLE_NAME} TransferTokensExample.cc)
add_executable(${TRANSFER_USING_EVM_ADDRESS_EXAMPLE_NAME} TransferUsingEvmAddressExample.cc)
add_executable(${UPDATE_ACCOUNT_PUBLIC_KEY_EXAMPLE_NAME} UpdateAccountPublicKeyExample.cc)
add_executable(${VALIDATE_CHECKSUM_EXAMPLE_NAME} ValidateChecksumExample.cc)
add_executable(${ZERO_TOKEN_OPERATIONS_EXAMPLE_NAME} ZeroTokenOperationsExample.cc)

file(COPY ${PROJECT_SOURCE_DIR}/addressbook/mainnet.pb
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE})
Expand All @@ -98,6 +110,8 @@ file(COPY ${PROJECT_SOURCE_DIR}/config/local_node.json
file(COPY ${PROJECT_SOURCE_DIR}/config/stateful.json
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE})

file(COPY precompile-example DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE})

# Install gRPC's roots.pem file for Windows with the examples so that it can be easily referenced.
if (WIN32)
file(COPY ${PROJECT_SOURCE_DIR}/vcpkg/packages/grpc_x64-windows/share/grpc/roots.pem
Expand Down Expand Up @@ -141,11 +155,17 @@ target_link_libraries(${SCHEDULE_IDENTICAL_TRANSACTION_EXAMPLE_NAME} PUBLIC ${PR
target_link_libraries(${SCHEDULE_MULTI_SIG_TRANSACTION_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
target_link_libraries(${SCHEDULE_TRANSACTION_MULTI_SIG_TRANSACTION_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
target_link_libraries(${SCHEDULE_TRANSFER_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
target_link_libraries(${SIGN_TRANSACTION_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
target_link_libraries(${SOLIDITY_PRECOMPILE_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
target_link_libraries(${STAKING_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
target_link_libraries(${STAKING_WITH_UPDATE_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
target_link_libraries(${TOPIC_WITH_ADMIN_KEY_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
target_link_libraries(${TRANSFER_CRYPTO_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
target_link_libraries(${TRANSFER_TOKENS_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
target_link_libraries(${TRANSFER_USING_EVM_ADDRESS_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
target_link_libraries(${UPDATE_ACCOUNT_PUBLIC_KEY_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
target_link_libraries(${VALIDATE_CHECKSUM_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
target_link_libraries(${ZERO_TOKEN_OPERATIONS_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})

install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE} DESTINATION examples FILES_MATCHING PATTERN *)

Expand Down Expand Up @@ -187,9 +207,15 @@ install(TARGETS
${SCHEDULE_MULTI_SIG_TRANSACTION_EXAMPLE_NAME}
${SCHEDULE_TRANSACTION_MULTI_SIG_TRANSACTION_EXAMPLE_NAME}
${SCHEDULE_TRANSFER_EXAMPLE_NAME}
${SIGN_TRANSACTION_EXAMPLE_NAME}
${SOLIDITY_PRECOMPILE_EXAMPLE_NAME}
${STAKING_EXAMPLE_NAME}
${STAKING_WITH_UPDATE_EXAMPLE_NAME}
${TOPIC_WITH_ADMIN_KEY_EXAMPLE_NAME}
${TRANSFER_CRYPTO_EXAMPLE_NAME}
${TRANSFER_TOKENS_EXAMPLE_NAME}
${TRANSFER_USING_EVM_ADDRESS_EXAMPLE_NAME}
${UPDATE_ACCOUNT_PUBLIC_KEY_EXAMPLE_NAME}
${VALIDATE_CHECKSUM_EXAMPLE_NAME}
${ZERO_TOKEN_OPERATIONS_EXAMPLE_NAME}
RUNTIME DESTINATION examples)
285 changes: 285 additions & 0 deletions sdk/examples/ContractHelper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
/*-
*
* 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.
*
*/
#ifndef HEDERA_SDK_CPP_EXAMPLES_CONTRACT_HELPER_H_
#define HEDERA_SDK_CPP_EXAMPLES_CONTRACT_HELPER_H_

#include "AccountId.h"
#include "Client.h"
#include "ContractCreateFlow.h"
#include "ContractExecuteTransaction.h"
#include "ContractFunctionParameters.h"
#include "ContractFunctionResult.h"
#include "ContractId.h"
#include "Hbar.h"
#include "PrivateKey.h"
#include "Status.h"
#include "TransactionId.h"
#include "TransactionReceipt.h"
#include "TransactionRecord.h"
#include "TransactionResponse.h"
#include "impl/HexConverter.h"

#include <fstream>
#include <functional>
#include <iostream>
#include <memory>
#include <nlohmann/json.hpp>
#include <proto/basic_types.pb.h>
#include <string_view>
#include <unordered_map>
#include <vector>

namespace Hedera
{
/**
* ContractHelper de-clutters SolidityPrecompileExample.
*
* When we instantiate a ContractHelper, we provide it with the JSON of a compiled solidity contract which is assumed to
* have functions named "step0()" through "stepN()".
*
* Each of these step functions is assumed to take no function parameters, and to return a Hedera ResponseCode which
* ought to be SUCCESS -- in other words, an int32 with value 22. See
* examples/precompile-example/HederaResponseCodes.sol.
*
* If a step takes function parameters, or if its ContractFunctionResult should be validated with a different method,
* the user can specify a supplier for a particular step with setParameterSupplier(stepIndex, parametersSupplier), and
* can specify an alternative validation method with setResultValidator(stepIndex, validateFunction)
*
* The contract is created on the Hedera network in the ContractHelper constructor, and when the user is ready to
* execute the step functions in the contract, they should call executeSteps(firstStepToExecute, lastStepToExecute).
*/
class ContractHelper
{
public:
/**
* Initialize this ContractHelper with the JSON file that contains the contract bytecode, the constructor parameters,
* and the Client to use to create the contract.
*
* @param filename The JSON file that contains the contract bytecode.
* @param constructorParameters The constructor parameters for the contract.
* @param client The Client to use the create the contract.
*/
ContractHelper(std::string_view filename,
const ContractFunctionParameters& constructorParameters,
const Client& client)
: mContractId(ContractCreateFlow()
.setBytecode(getBytecodeHex(filename))
.setMaxChunks(30U)
.setGas(8'000'000)
.setConstructorParameters(constructorParameters)
.execute(client)
.getReceipt(client)
.mContractId.value())
{
}

/**
* Add a result validator for a step.
*
* @param step The step the validator should take effect.
* @param func The validator function to run.
* @return A reference to this ContractHelper with the newly-added step result validator.
*/
ContractHelper& setResultValidatorForStep(int step, const std::function<bool(const ContractFunctionResult&)>& func)
{
mStepResultValidators.try_emplace(step, func);
return *this;
}

/**
* Add a parameter supplier for a step.
*
* @param step The step the parameter should be supplied.
* @param func The parameter supplier to run.
* @return A reference to this ContractHelper with the newly-added step parameter supplier.
*/
ContractHelper& setParameterSupplierForStep(int step, const std::function<ContractFunctionParameters(void)>& func)
{
mStepParameterSupplier.try_emplace(step, func);
return *this;
}

/**
* Add a payable amount for a step.
*
* @param step The step to which the payable amount should be added.
* @param amount The payable amount.
* @return A reference to this ContractHelper with the newly-added payable amount.
*/
ContractHelper& setPayableAmountForStep(int step, const Hbar& amount)
{
mStepPayableAmounts.try_emplace(step, amount);
return *this;
}

/**
* Add a signer for a step.
*
* @param step The step to which the signer should sign.
* @param key The PrivateKey with which to sign.
* @return A reference to this ContractHelper with the newly-added signer key.
*/
ContractHelper& addSignerForStep(int step, const std::shared_ptr<PrivateKey>& key)
{
mStepSigners[step].push_back(key);
return *this;
}

/**
* Add a fee payer for a step.
*
* @param step The step to which the fee payer should be added.
* @param accountId The ID of the account paying.
* @param key The private key of the fee payer account.
* @return A reference to this ContractHelper with the newly-added payable amount.
*/
ContractHelper& setFeePayerForStep(int step, const AccountId& accountId, const std::shared_ptr<PrivateKey>& key)
{
mStepFeePayers.try_emplace(step, accountId);
return addSignerForStep(step, key);
}

/**
* Execute all steps.
*
* @param first The first step to execute.
* @param last The last step to execute.
* @param client The Client to use to execute the steps.
*/
ContractHelper& executeSteps(int first, int last, const Client& client)
{
for (int step = first; step < last + 1; ++step)
{
std::cout << "Attempting to execute step " << step << std::endl;

ContractExecuteTransaction transaction =
ContractExecuteTransaction().setContractId(mContractId).setGas(10'000'000);

const bool hasPayableAmount = mStepPayableAmounts.find(step) != mStepPayableAmounts.end();
const bool hasParameters = mStepParameterSupplier.find(step) != mStepParameterSupplier.end();
const bool hasFeePayer = mStepFeePayers.find(step) != mStepFeePayers.end();
const bool hasSigners = mStepSigners.find(step) != mStepSigners.end();
const bool hasResultValidator = mStepResultValidators.find(step) != mStepResultValidators.end();

if (hasPayableAmount)
{
transaction.setPayableAmount(mStepPayableAmounts.at(step));
}

transaction.setFunction("step" + std::to_string(step),
hasParameters ? mStepParameterSupplier.at(step)() : ContractFunctionParameters());

if (hasFeePayer)
{
transaction.setTransactionId(TransactionId::generate(mStepFeePayers.at(step)));
}

transaction.freezeWith(&client);
if (hasSigners)
{
std::for_each(mStepSigners.at(step).cbegin(),
mStepSigners.at(step).cend(),
[&transaction](const std::shared_ptr<PrivateKey>& key) { transaction.sign(key); });
}

const TransactionRecord txRecord = transaction.execute(client).setValidateStatus(false).getRecord(client);

const ContractFunctionResult result = txRecord.mContractFunctionResult.value();
const std::function<bool(const ContractFunctionResult&)> validatorFunc =
hasResultValidator ? mStepResultValidators.at(step)
: [](const ContractFunctionResult& stepResult)
{
// Assume no custom validator means the function should return a success.
const Status status =
gProtobufResponseCodeToStatus.at(static_cast<proto::ResponseCodeEnum>(stepResult.getInt32(0)));
return status == Status::SUCCESS;
};

if (validatorFunc(result))
{
std::cout << "Step " << step
<< " completed and returned a valid result. TransactionId=" << txRecord.mTransactionID->toString()
<< std::endl;
}
else
{
std::cout << "Encountered invalid response status" << std::endl;
}
}

return *this;
}

private:
/**
* Get the hex-encoded bytecode in the input JSON file.
*
* @param file The JSON file from which to grab the bytecode.
* @return The bytecode encoded into a hex string.
*/
static std::string getBytecodeHex(std::string_view file)
{
std::ifstream infile(file.data());
nlohmann::json json = nlohmann::json::parse(infile);

if (json.contains("object") && json["object"].is_string())
{
return json["object"].get<std::string>();
}
else
{
return json["bytecode"].get<std::string>();
}
}

/**
* The ID of the created contract.
*/
ContractId mContractId;

/**
* Map of steps to the step's validator function.
*/
std::unordered_map<int, std::function<bool(const ContractFunctionResult&)>> mStepResultValidators;

/**
* Map of steps to the step's parameter supplier.
*/
std::unordered_map<int, std::function<ContractFunctionParameters(void)>> mStepParameterSupplier;

/**
* Map of steps to the step's payable amount.
*/
std::unordered_map<int, Hbar> mStepPayableAmounts;

/**
* Map of steps to the step's signers.
*/
std::unordered_map<int, std::vector<std::shared_ptr<PrivateKey>>> mStepSigners;

/**
* Map of steps to the ID of the fee payer account.
*/
std::unordered_map<int, AccountId> mStepFeePayers;
};

} // namespace Hedera

#endif // HEDERA_SDK_CPP_EXAMPLES_CONTRACT_HELPER_H_
Loading

0 comments on commit cbb0a03

Please sign in to comment.