Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add AutoCreateAccountTransferTransactionExample #495

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 126 additions & 0 deletions sdk/examples/AutoCreateAccountTransferTransactionExample.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*-
*
* 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 "AccountId.h"
#include "Client.h"
#include "ECDSAsecp256k1PrivateKey.h"
#include "ECDSAsecp256k1PublicKey.h"
#include "ED25519PrivateKey.h"
#include "EvmAddress.h"
#include "Hbar.h"
#include "TransactionReceipt.h"
#include "TransactionReceiptQuery.h"
#include "TransactionResponse.h"
#include "TransferTransaction.h"

#include <iostream>
#include <memory>

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;
}

const AccountId operatorAccountId = AccountId::fromString(argv[1]);
const std::shared_ptr<ED25519PrivateKey> operatorPrivateKey = ED25519PrivateKey::fromString(argv[2]);

/**
* Auto-create a new account using a public-address via a `TransferTransaction`. Reference: [HIP-583 Expand alias
* support in CryptoCreate & CryptoTransfer Transactions](https://hips.hedera.com/hip/hip-583)
*
* - Create an ECSDA private key.
* - Extract the ECDSA public key.
* - Extract the Ethereum public address.
* - Use the `TransferTransaction`.
* - Populate the `FromAddress` with the sender Hedera account ID.
* - Populate the `ToAddress` with Ethereum public address.
* - Note: Can transfer from public address to public address in the `TransferTransaction` for complete accounts.
* Transfers from hollow accounts will not work because the hollow account does not have a public key
* assigned to authorize transfers out of the account.
* - Sign the `TransferTransaction` transaction using an existing Hedera account and key paying for the transaction
* fee.
* - The `AccountCreateTransaction` is executed as a child transaction triggered by the `TransferTransaction`.
* - The Hedera account that was created has a public address the user specified in the TransferTransaction ToAddress.
* - Will not have a public key at this stage.
* - Cannot do anything besides receive tokens or hbars.
* - The alias property of the account does not have the public address.
* - Referred to as a hollow account.
* - To get the new account ID ask for the child receipts or child records for the parent transaction ID of the
* `TransferTransaction`.
* - Get the `AccountInfo` and verify the account is a hollow account with the supplied public address (may need to
* verify with mirror node API).
* - To enhance the hollow account to have a public key the hollow account needs to be specified as a transaction fee
* payer in a HAPI transaction.
* - Create a HAPI transaction and assign the new hollow account as the transaction fee payer.
* - Sign with the private key that corresponds to the public key on the hollow account.
* - Get the `AccountInfo` for the account and return the public key on the account to show it is a complete account.
*/

// 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();
client.setOperator(operatorAccountId, operatorPrivateKey.get());

/**
* Step 1: Create an ECSDA private key.
*/
const std::unique_ptr<ECDSAsecp256k1PrivateKey> privateKey = ECDSAsecp256k1PrivateKey::generatePrivateKey();

/**
* Step 2: Extract the ECDSA public key.
*/
const std::shared_ptr<ECDSAsecp256k1PublicKey> publicKey =
std::dynamic_pointer_cast<ECDSAsecp256k1PublicKey>(privateKey->getPublicKey());

/**
* Step 3: Extract the Ethereum public address.
*/
const EvmAddress evmAddress = publicKey->toEvmAddress();

/**
* Step 4: Use the `TransferTransaction` and set the EVM address field to the Ethereum public address
*/
TransferTransaction transferTransaction = TransferTransaction()
.addHbarTransfer(operatorAccountId, Hbar(10LL).negated())
.addHbarTransfer(AccountId::fromEvmAddress(evmAddress), Hbar(10LL))
.freezeWith(&client);

/**
* Step 5: Sign the `TransferTransaction` transaction using an existing Hedera account and key paying for the
* transaction fee.
*/
const TransactionResponse response = transferTransaction.execute(client);

/**
* Step 6: To get the new account ID, ask for the child receipts or child records for the parent transaction ID of the
* `TransferTransaction` (the `AccountCreateTransaction` is executed as a child transaction triggered by the
* `TransferTransaction`).
*/
const TransactionReceipt receipt =
TransactionReceiptQuery().setTransactionId(response.getTransactionId()).setIncludeChildren(true).execute(client);

const AccountId newAccountId = receipt.mChildren.at(0).mAccountId.value();

return 0;
}
4 changes: 4 additions & 0 deletions sdk/examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ set(ACCOUNT_ALIAS_EXAMPLE_NAME ${PROJECT_NAME}-account-alias-example)
set(ACCOUNT_ALLOWANCE_EXAMPLE_NAME ${PROJECT_NAME}-account-allowance-example)
set(ACCOUNT_CREATE_WITH_HTS_EXAMPLE_NAME ${PROJECT_NAME}-account-create-with-hts-example)
set(ACCOUNT_CREATION_WAYS_EXAMPLE_NAME ${PROJECT_NAME}-account-creation-ways-example)
set(AUTO_CREATE_ACCOUNT_TRANSFER_TRANSACTION_EXAMPLE_NAME ${PROJECT_NAME}-auto-create-account-transfer-transaction-example)
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)
Expand Down Expand Up @@ -30,6 +31,7 @@ add_executable(${ACCOUNT_ALIAS_EXAMPLE_NAME} AccountAliasExample.cc)
add_executable(${ACCOUNT_ALLOWANCE_EXAMPLE_NAME} AccountAllowanceExample.cc)
add_executable(${ACCOUNT_CREATE_WITH_HTS_EXAMPLE_NAME} AccountCreateWithHtsExample.cc)
add_executable(${ACCOUNT_CREATION_WAYS_EXAMPLE_NAME} AccountCreationWaysExample.cc)
add_executable(${AUTO_CREATE_ACCOUNT_TRANSFER_TRANSACTION_EXAMPLE_NAME} AutoCreateAccountTransferTransactionExample.cc)
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)
Expand Down Expand Up @@ -76,6 +78,7 @@ target_link_libraries(${ACCOUNT_ALIAS_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
target_link_libraries(${ACCOUNT_ALLOWANCE_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
target_link_libraries(${ACCOUNT_CREATE_WITH_HTS_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
target_link_libraries(${ACCOUNT_CREATION_WAYS_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
target_link_libraries(${AUTO_CREATE_ACCOUNT_TRANSFER_TRANSACTION_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})
target_link_libraries(${CREATE_SIMPLE_CONTRACT_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
Expand Down Expand Up @@ -107,6 +110,7 @@ install(TARGETS
${ACCOUNT_ALLOWANCE_EXAMPLE_NAME}
${ACCOUNT_CREATE_WITH_HTS_EXAMPLE_NAME}
${ACCOUNT_CREATION_WAYS_EXAMPLE_NAME}
${AUTO_CREATE_ACCOUNT_TRANSFER_TRANSACTION_EXAMPLE_NAME}
${CONSENSUS_PUB_SUB_EXAMPLE_NAME}
${CREATE_ACCOUNT_EXAMPLE_NAME}
${CREATE_SIMPLE_CONTRACT_EXAMPLE_NAME}
Expand Down
31 changes: 29 additions & 2 deletions sdk/main/include/TransactionReceipt.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

namespace proto
{
class TransactionGetReceiptResponse;
class TransactionReceipt;
}

Expand All @@ -48,13 +49,23 @@ namespace Hedera
class TransactionReceipt
{
public:
/**
* Construct a TransactionReceipt object from a TransactionGetReceiptResponse protobuf object.
*
* @param proto The TransactionGetReceiptResponse protobuf object from which to construct a TransactionReceipt object.
* @return The constructed TransactionReceipt object.
*/
[[nodiscard]] static TransactionReceipt fromProtobuf(const proto::TransactionGetReceiptResponse& proto);

/**
* Construct a TransactionReceipt object from a TransactionReceipt protobuf object.
*
* @param proto The TransactionReceipt protobuf object from which to construct an TransactionReceipt object.
* @param proto The TransactionReceipt protobuf object from which to construct a TransactionReceipt object.
* @param transactionId The ID of the transaction to which the constructed TransactionReceipt will correspond.
* @return The constructed TransactionReceipt object.
*/
[[nodiscard]] static TransactionReceipt fromProtobuf(const proto::TransactionReceipt& proto);
[[nodiscard]] static TransactionReceipt fromProtobuf(const proto::TransactionReceipt& proto,
const TransactionId& transactionId = TransactionId());

/**
* Validate the status and throw if it is not a Status::SUCCESS.
Expand All @@ -63,6 +74,11 @@ class TransactionReceipt
*/
void validateStatus() const;

/**
* The ID of the transaction to which this TransactionReceipt corresponds.
*/
TransactionId mTransactionId;

/**
* The consensus status of the transaction; is UNKNOWN if consensus has not been reached, or if the associated
* transaction did not have a valid payer signature.
Expand Down Expand Up @@ -181,6 +197,17 @@ class TransactionReceipt
* newly-created NFTs.
*/
std::vector<uint64_t> mSerialNumbers;

/**
* The receipts of processing all transactions with the given ID, in consensus time order.
*/
std::vector<TransactionReceipt> mDuplicates;

/**
* The receipts (if any) of all child transactions spawned by the transaction with the given top-level id, in
* consensus order. Always empty if the top-level status is UNKNOWN.
*/
std::vector<TransactionReceipt> mChildren;
};

} // namespace Hedera
Expand Down
45 changes: 45 additions & 0 deletions sdk/main/include/TransactionReceiptQuery.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,25 @@ class TransactionReceiptQuery : public Query<TransactionReceiptQuery, Transactio
*/
TransactionReceiptQuery& setTransactionId(const TransactionId& transactionId);

/**
* Set the child transaction retrieval policy for this TransactionReceiptQuery.
*
* @param children \c TRUE if this TransactionReceiptQuery should get the receipts of any child transactions,
* otherwise \c FALSE.
* @return A reference to this TransactionReceiptQuery object with the newly-set child transaction retrieval policy.
*/
TransactionReceiptQuery& setIncludeChildren(bool children);

/**
* Set the duplicate transaction retrieval policy for this TransactionReceiptQuery.
*
* @param duplicates \c TRUE if this TransactionReceiptQuery should get the receipts of any duplicate transactions,
* otherwise \c FALSE.
* @return A reference to this TransactionReceiptQuery object with the newly-set duplicate transaction retrieval
* policy.
*/
TransactionReceiptQuery& setIncludeDuplicates(bool duplicates);

/**
* Get the ID of the transaction of which this query is currently configured to get the receipt.
*
Expand All @@ -57,6 +76,22 @@ class TransactionReceiptQuery : public Query<TransactionReceiptQuery, Transactio
*/
[[nodiscard]] inline std::optional<TransactionId> getTransactionId() const { return mTransactionId; }

/**
* Get the child transaction retrieval policy for this TransactionReceiptQuery.
*
* @return \c TRUE if this TransactionReceiptQuery is currently configured to get the receipts of any child
* transactions, otherwise \c FALSE.
*/
[[nodiscard]] inline bool getIncludeChildren() const { return mIncludeChildren; }

/**
* Get the duplicate transaction retrieval policy for this TransactionReceiptQuery.
*
* @return \c TRUE if this TransactionReceiptQuery is currently configured to get the receipts of any duplicate
* transactions, otherwise \c FALSE.
*/
[[nodiscard]] inline bool getIncludeDuplicates() const { return mIncludeDuplicates; }

private:
/**
* Derived from Executable. Construct a Query protobuf object from this TransactionReceiptQuery object.
Expand Down Expand Up @@ -118,6 +153,16 @@ class TransactionReceiptQuery : public Query<TransactionReceiptQuery, Transactio
* The ID of the transaction of which this query should get the receipt.
*/
std::optional<TransactionId> mTransactionId;

/**
* Should the receipts of any children transactions be retrieved as well?
*/
bool mIncludeChildren = false;

/**
* Should the receipts of any duplicates transactions be retrieved as well?
*/
bool mIncludeDuplicates = false;
};

} // namespace Hedera
Expand Down
23 changes: 22 additions & 1 deletion sdk/main/src/TransactionReceipt.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,35 @@
#include "exceptions/ReceiptStatusException.h"
#include "impl/Utilities.h"

#include <proto/transaction_get_receipt.pb.h>
#include <proto/transaction_receipt.pb.h>

namespace Hedera
{
//-----
TransactionReceipt TransactionReceipt::fromProtobuf(const proto::TransactionReceipt& proto)
TransactionReceipt TransactionReceipt::fromProtobuf(const proto::TransactionGetReceiptResponse& proto)
{
TransactionReceipt receipt = TransactionReceipt::fromProtobuf(proto.receipt());

for (int i = 0; i < proto.duplicatetransactionreceipts_size(); ++i)
{
receipt.mDuplicates.push_back(TransactionReceipt::fromProtobuf(proto.duplicatetransactionreceipts(i)));
}

for (int i = 0; i < proto.child_transaction_receipts_size(); ++i)
{
receipt.mChildren.push_back(TransactionReceipt::fromProtobuf(proto.child_transaction_receipts(i)));
}

return receipt;
}

//-----
TransactionReceipt TransactionReceipt::fromProtobuf(const proto::TransactionReceipt& proto,
const TransactionId& transactionId)
{
TransactionReceipt receipt;
receipt.mTransactionId = transactionId;
receipt.mStatus = gProtobufResponseCodeToStatus.at(proto.status());

if (proto.has_accountid())
Expand Down
18 changes: 17 additions & 1 deletion sdk/main/src/TransactionReceiptQuery.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,20 @@ TransactionReceiptQuery& TransactionReceiptQuery::setTransactionId(const Transac
return *this;
}

//-----
TransactionReceiptQuery& TransactionReceiptQuery::setIncludeChildren(bool children)
{
mIncludeChildren = children;
return *this;
}

//-----
TransactionReceiptQuery& TransactionReceiptQuery::setIncludeDuplicates(bool duplicates)
{
mIncludeDuplicates = duplicates;
return *this;
}

//-----
proto::Query TransactionReceiptQuery::makeRequest(const Client&, const std::shared_ptr<internal::Node>&) const
{
Expand All @@ -47,14 +61,16 @@ proto::Query TransactionReceiptQuery::makeRequest(const Client&, const std::shar

// This is a free query, so no payment required
getTransactionReceiptQuery->set_allocated_transactionid(mTransactionId->toProtobuf().release());
getTransactionReceiptQuery->set_include_child_receipts(mIncludeChildren);
getTransactionReceiptQuery->set_includeduplicates(mIncludeDuplicates);

return query;
}

//-----
TransactionReceipt TransactionReceiptQuery::mapResponse(const proto::Response& response) const
{
return TransactionReceipt::fromProtobuf(response.transactiongetreceipt().receipt());
return TransactionReceipt::fromProtobuf(response.transactiongetreceipt());
}

//-----
Expand Down
17 changes: 11 additions & 6 deletions sdk/main/src/TransactionRecord.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,17 @@ TransactionRecord TransactionRecord::fromProtobuf(const proto::TransactionRecord
{
TransactionRecord transactionRecord;

if (proto.has_receipt())
if (proto.has_transactionid())
{
transactionRecord.mTransactionID = TransactionId::fromProtobuf(proto.transactionid());

if (proto.has_receipt())
{
transactionRecord.mReceipt =
TransactionReceipt::fromProtobuf(proto.receipt(), transactionRecord.mTransactionID.value());
}
}
else
{
transactionRecord.mReceipt = TransactionReceipt::fromProtobuf(proto.receipt());
}
Expand All @@ -42,11 +52,6 @@ TransactionRecord TransactionRecord::fromProtobuf(const proto::TransactionRecord
transactionRecord.mConsensusTimestamp = internal::TimestampConverter::fromProtobuf(proto.consensustimestamp());
}

if (proto.has_transactionid())
{
transactionRecord.mTransactionID = TransactionId::fromProtobuf(proto.transactionid());
}

transactionRecord.mMemo = proto.memo();
transactionRecord.mTransactionFee = proto.transactionfee();

Expand Down
Loading