Skip to content

Commit

Permalink
feat(accountUpdateTransaction): Implement JSON-RPC method endpoint fo…
Browse files Browse the repository at this point in the history
…r `AccountUpdateTransaction` (#738)

Signed-off-by: Rob Walworth <[email protected]>
Co-authored-by: gsstoykov <[email protected]>
  • Loading branch information
rwalworth and gsstoykov authored Jul 30, 2024
1 parent 8791d83 commit 710e1b6
Show file tree
Hide file tree
Showing 12 changed files with 186 additions and 32 deletions.
1 change: 0 additions & 1 deletion src/sdk/main/include/AccountUpdateTransaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ class AccountUpdateTransaction : public Transaction<AccountUpdateTransaction>
* @param memo The desired new memo for the account.
* @return A reference to this AccountUpdateTransaction object with the newly-set memo.
* @throws IllegalStateException If this AccountUpdateTransaction is frozen.
* @throws std::length_error If the memo is more than 100 characters.
*/
AccountUpdateTransaction& setAccountMemo(std::string_view memo);

Expand Down
22 changes: 16 additions & 6 deletions src/sdk/main/include/KeyList.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ namespace proto
{
class Key;
class KeyList;
class ThresholdKey;
}

namespace Hedera
Expand All @@ -55,7 +56,16 @@ class KeyList : public Key
* @return The created KeyList object.
* @throws BadKeyException If a key in the KeyList protobuf is unable to be created.
*/
[[nodiscard]] static KeyList fromProtobuf(const proto::KeyList& proto, int threshold = -1);
[[nodiscard]] static KeyList fromProtobuf(const proto::KeyList& proto);

/**
* Construct a KeyList object from a ThresholdKey protobuf object.
*
* @param proto The ThresholdKey protobuf object from which to create a KeyList object.
* @return The created KeyList object.
* @throws BadKeyException If a key in the KeyList protobuf is unable to be created.
*/
[[nodiscard]] static KeyList fromProtobuf(const proto::ThresholdKey& proto);

/**
* Construct a KeyList object from a list of Keys.
Expand All @@ -71,7 +81,7 @@ class KeyList : public Key
* @param threshold The number of Keys in the KeyList that must sign.
* @param The created Keylist object.
*/
[[nodiscard]] static KeyList withThreshold(int threshold);
[[nodiscard]] static KeyList withThreshold(uint32_t threshold);

/**
* Derived from Key. Create a clone of this KeyList object.
Expand Down Expand Up @@ -117,14 +127,14 @@ class KeyList : public Key
* @param threshold The threshold for this KeyList.
* @return A reference to this KeyList with the newly-set threshold.
*/
KeyList& setThreshold(int threshold);
KeyList& setThreshold(uint32_t threshold);

/**
* Get the threshold for this KeyList.
*
* @return The threshold number of Keys that must sign.
*/
[[nodiscard]] inline int getThreshold() const { return mThreshold; }
[[nodiscard]] inline uint32_t getThreshold() const { return mThreshold; }

/**
* Get the number of keys in this KeyList.
Expand Down Expand Up @@ -174,9 +184,9 @@ class KeyList : public Key
std::vector<std::shared_ptr<Key>> mKeys;

/**
* The threshold number of keys that must sign a transaction. -1 means all keys must sign.
* The threshold number of keys that must sign a transaction. 0 means all keys must sign.
*/
int mThreshold = -1;
uint32_t mThreshold = 0;
};

} // namespace Hedera
Expand Down
6 changes: 3 additions & 3 deletions src/sdk/main/src/AccountCreateTransaction.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ namespace Hedera
AccountCreateTransaction::AccountCreateTransaction()
: Transaction<AccountCreateTransaction>()
{
setDefaultMaxTransactionFee(Hbar(5LL));
setDefaultMaxTransactionFee(Hbar(10000LL));
}

//-----
AccountCreateTransaction::AccountCreateTransaction(const proto::TransactionBody& transactionBody)
: Transaction<AccountCreateTransaction>(transactionBody)
{
setDefaultMaxTransactionFee(Hbar(5LL));
setDefaultMaxTransactionFee(Hbar(10000LL));
initFromSourceTransactionBody();
}

Expand All @@ -51,7 +51,7 @@ AccountCreateTransaction::AccountCreateTransaction(
const std::map<TransactionId, std::map<AccountId, proto::Transaction>>& transactions)
: Transaction<AccountCreateTransaction>(transactions)
{
setDefaultMaxTransactionFee(Hbar(5LL));
setDefaultMaxTransactionFee(Hbar(10000LL));
initFromSourceTransactionBody();
}

Expand Down
5 changes: 0 additions & 5 deletions src/sdk/main/src/AccountUpdateTransaction.cc
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,6 @@ AccountUpdateTransaction& AccountUpdateTransaction::setAccountMemo(std::string_v
{
requireNotFrozen();

if (memo.size() > 100)
{
throw std::length_error("Account memo is too large. Must be smaller than 100 bytes");
}

mAccountMemo = memo;
return *this;
}
Expand Down
2 changes: 2 additions & 0 deletions src/sdk/main/src/Key.cc
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ std::unique_ptr<Key> Key::fromProtobuf(const proto::Key& key)
return ECDSAsecp256k1PublicKey::fromBytes(internal::Utilities::stringToByteVector(key.ecdsa_secp256k1()));
case proto::Key::KeyCase::kKeyList:
return std::make_unique<KeyList>(KeyList::fromProtobuf(key.keylist()));
case proto::Key::KeyCase::kThresholdKey:
return std::make_unique<KeyList>(KeyList::fromProtobuf(key.thresholdkey()));
default:
throw std::invalid_argument("Key protobuf case not recognized");
}
Expand Down
21 changes: 17 additions & 4 deletions src/sdk/main/src/KeyList.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,9 @@
namespace Hedera
{
//-----
KeyList KeyList::fromProtobuf(const proto::KeyList& proto, int threshold)
KeyList KeyList::fromProtobuf(const proto::KeyList& proto)
{
KeyList keyList;
keyList.mThreshold = threshold;

for (int i = 0; i < proto.keys_size(); ++i)
{
Expand All @@ -41,6 +40,20 @@ KeyList KeyList::fromProtobuf(const proto::KeyList& proto, int threshold)
return keyList;
}

//-----
KeyList KeyList::fromProtobuf(const proto::ThresholdKey& proto)
{
KeyList keyList;
keyList.mThreshold = proto.threshold();

for (int i = 0; i < proto.keys().keys_size(); ++i)
{
keyList.mKeys.emplace_back(Key::fromProtobuf(proto.keys().keys(i)).release());
}

return keyList;
}

//-----
KeyList KeyList::of(const std::vector<std::shared_ptr<Key>>& keys)
{
Expand All @@ -50,7 +63,7 @@ KeyList KeyList::of(const std::vector<std::shared_ptr<Key>>& keys)
}

//-----
KeyList KeyList::withThreshold(int threshold)
KeyList KeyList::withThreshold(uint32_t threshold)
{
KeyList keyList;
keyList.mThreshold = threshold;
Expand Down Expand Up @@ -118,7 +131,7 @@ std::ostream& operator<<(std::ostream& os, const KeyList& list)
}

//-----
KeyList& KeyList::setThreshold(int threshold)
KeyList& KeyList::setThreshold(uint32_t threshold)
{
mThreshold = threshold;
return *this;
Expand Down
10 changes: 0 additions & 10 deletions src/sdk/tests/unit/AccountUpdateTransactionUnitTests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -280,16 +280,6 @@ TEST_F(AccountUpdateTransactionUnitTests, SetAccountMemo)
EXPECT_EQ(transaction.getAccountMemo(), getTestAccountMemo());
}

//-----
TEST_F(AccountUpdateTransactionUnitTests, SetAccountMemoTooLarge)
{
// Given
AccountUpdateTransaction transaction;

// When / Then
EXPECT_THROW(transaction.setAccountMemo(std::string(101, 'a')), std::length_error);
}

//-----
TEST_F(AccountUpdateTransactionUnitTests, SetAccountMemoFrozen)
{
Expand Down
28 changes: 27 additions & 1 deletion src/sdk/tests/unit/KeyListUnitTests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class KeyListUnitTests : public ::testing::Test
[[nodiscard]] inline const std::shared_ptr<PublicKey>& getTestPublicKey1() const { return mTestPublicKey1; }
[[nodiscard]] inline const std::shared_ptr<PublicKey>& getTestPublicKey2() const { return mTestPublicKey2; }
[[nodiscard]] inline const std::shared_ptr<PublicKey>& getTestPublicKey3() const { return mTestPublicKey3; }
[[nodiscard]] inline uint32_t getTestThreshold() const { return mTestThreshold; }

private:
const std::shared_ptr<PublicKey> mTestPublicKey1 =
Expand All @@ -48,10 +49,11 @@ class KeyListUnitTests : public ::testing::Test
ED25519PrivateKey::fromString(
"302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e12")
->getPublicKey();
const uint32_t mTestThreshold = 2U;
};

//-----
TEST_F(KeyListUnitTests, FromProtobuf)
TEST_F(KeyListUnitTests, FromProtobufKeyList)
{
// Given
proto::KeyList protoKeyList;
Expand All @@ -69,6 +71,30 @@ TEST_F(KeyListUnitTests, FromProtobuf)
EXPECT_TRUE(keyList.contains(getTestPublicKey3().get()));
}

//-----
TEST_F(KeyListUnitTests, FromProtobufThresholdKey)
{
// Given
proto::ThresholdKey protoThresholdKey;
protoThresholdKey.set_threshold(getTestThreshold());
protoThresholdKey.mutable_keys()->add_keys()->set_ed25519(
internal::Utilities::byteVectorToString(getTestPublicKey1()->toBytesDer()));
protoThresholdKey.mutable_keys()->add_keys()->set_ed25519(
internal::Utilities::byteVectorToString(getTestPublicKey2()->toBytesDer()));
protoThresholdKey.mutable_keys()->add_keys()->set_ed25519(
internal::Utilities::byteVectorToString(getTestPublicKey3()->toBytesDer()));

// When
KeyList keyList;
EXPECT_NO_THROW(keyList = KeyList::fromProtobuf(protoThresholdKey));

// Then
EXPECT_EQ(keyList.getThreshold(), getTestThreshold());
EXPECT_TRUE(keyList.contains(getTestPublicKey1().get()));
EXPECT_TRUE(keyList.contains(getTestPublicKey2().get()));
EXPECT_TRUE(keyList.contains(getTestPublicKey3().get()));
}

//-----
TEST_F(KeyListUnitTests, Of)
{
Expand Down
4 changes: 2 additions & 2 deletions src/tck/include/KeyHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ struct KeyRequest
const std::optional<std::vector<KeyRequest>>& keys);

/**
* The type of Key to generate.
*The type of Key to generate.
*/
KeyType mType;

Expand All @@ -109,7 +109,7 @@ struct KeyRequest
};

/**
* Generate a Key from a key hex string. The string must be either the DER-encoding of an ED25519 or ECDSAsecp256k1
* Generate a Key from a key hex string.The string must be either the DER - encoding of an ED25519 or ECDSAsecp256k1
* private or public key, or the serialized Key protobuf of a KeyList or ThresholdKey.
*
* @param key The hex string from which to get the Key.
Expand Down
27 changes: 27 additions & 0 deletions src/tck/include/SdkClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,33 @@ nlohmann::json setup(const std::string& operatorAccountId,
const std::optional<std::string>& nodeIp,
const std::optional<std::string>& nodeAccountId,
const std::optional<std::string>& mirrorNetworkIp);
/**
* Update an account.
*
* @param accountId The ID of the account to update.
* @param key The desired key for the account.
* @param autoRenewPeriod The new desired amount of time in seconds to renew the account.
* @param expirationTime The new desired time for the account to expire.
* @param receiverSignatureRequired Should the account now require a receiver signature?
* @param memo The new desired memo for the account.
* @param maxAutoTokenAssociations The new desired maximum number of automatic token associations for the account.
* @param stakedAccountId The ID of the new desired account to which the account should stake.
* @param stakedNodeId The ID of the new desired node to which the account should stake.
* @param declineStakingReward Should the account now decline staking rewards?
* @param commonTxParams Any parameters common to all transaction types.
* @return A JSON response containing the created account ID and the status of the account creation.
*/
nlohmann::json updateAccount(const std::optional<std::string>& accountId,
const std::optional<std::string>& key,
const std::optional<int64_t>& autoRenewPeriod,
const std::optional<int64_t>& expirationTime,
const std::optional<bool>& receiverSignatureRequired,
const std::optional<std::string>& memo,
const std::optional<int32_t>& maxAutoTokenAssociations,
const std::optional<std::string>& stakedAccountId,
const std::optional<int64_t>& stakedNodeId,
const std::optional<bool>& declineStakingReward,
const std::optional<CommonTransactionParams>& commonTxParams);

} // namespace Hedera::TCK::SdkClient

Expand Down
79 changes: 79 additions & 0 deletions src/tck/src/SdkClient.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "SdkClient.h"
#include "AccountCreateTransaction.h"
#include "AccountId.h"
#include "AccountUpdateTransaction.h"
#include "Client.h"
#include "EvmAddress.h"
#include "HbarUnit.h"
Expand Down Expand Up @@ -179,4 +180,82 @@ nlohmann::json SdkClient::setup(const std::string& operatorAccountId,
};
}

//-----
nlohmann::json SdkClient::updateAccount(const std::optional<std::string>& accountId,
const std::optional<std::string>& key,
const std::optional<int64_t>& autoRenewPeriod,
const std::optional<int64_t>& expirationTime,
const std::optional<bool>& receiverSignatureRequired,
const std::optional<std::string>& memo,
const std::optional<int32_t>& maxAutoTokenAssociations,
const std::optional<std::string>& stakedAccountId,
const std::optional<int64_t>& stakedNodeId,
const std::optional<bool>& declineStakingReward,
const std::optional<CommonTransactionParams>& commonTxParams)
{
AccountUpdateTransaction accountUpdateTransaction;
accountUpdateTransaction.setGrpcDeadline(std::chrono::seconds(DEFAULT_TCK_REQUEST_TIMEOUT));

if (accountId.has_value())
{
accountUpdateTransaction.setAccountId(AccountId::fromString(accountId.value()));
}

if (key.has_value())
{
accountUpdateTransaction.setKey(getHederaKey(key.value()));
}

if (autoRenewPeriod.has_value())
{
accountUpdateTransaction.setAutoRenewPeriod(std::chrono::seconds(autoRenewPeriod.value()));
}

if (expirationTime.has_value())
{
accountUpdateTransaction.setExpirationTime(std::chrono::system_clock::from_time_t(0) +
std::chrono::seconds(expirationTime.value()));
}

if (receiverSignatureRequired.has_value())
{
accountUpdateTransaction.setReceiverSignatureRequired(receiverSignatureRequired.value());
}

if (memo.has_value())
{
accountUpdateTransaction.setAccountMemo(memo.value());
}

if (maxAutoTokenAssociations.has_value())
{
accountUpdateTransaction.setMaxAutomaticTokenAssociations(maxAutoTokenAssociations.value());
}

if (stakedAccountId.has_value())
{
accountUpdateTransaction.setStakedAccountId(AccountId::fromString(stakedAccountId.value()));
}

if (stakedNodeId.has_value())
{
accountUpdateTransaction.setStakedNodeId(stakedNodeId.value());
}

if (declineStakingReward.has_value())
{
accountUpdateTransaction.setDeclineStakingReward(declineStakingReward.value());
}

if (commonTxParams.has_value())
{
commonTxParams->fillOutTransaction(accountUpdateTransaction, mClient);
}

const TransactionReceipt txReceipt = accountUpdateTransaction.execute(mClient).getReceipt(mClient);
return {
{"status", gStatusToString.at(txReceipt.mStatus)}
};
}

} // namespace Hedera::TCK::SdkClient
Loading

0 comments on commit 710e1b6

Please sign in to comment.