From cfbde7b4497e1f2e24c2360295c5052ddc5862c2 Mon Sep 17 00:00:00 2001 From: hewig <360470+hewigovens@users.noreply.github.com> Date: Mon, 9 Dec 2019 06:22:54 +0800 Subject: [PATCH] [Cosmos] Add MsgWithdrawDelegationRewardsAll (#763) * Add MsgWithdrawDelegationRewardsAll * renaming type prefixes --- include/TrustWalletCore/TWCosmosProto.h | 1 + src/Cosmos/Serialization.cpp | 75 +++++++++++++++---------- src/Cosmos/Serialization.h | 17 +++--- src/Cosmos/Signer.cpp | 20 +++++-- src/proto/Cosmos.proto | 10 +++- tests/Cosmos/StakingTests.cpp | 35 +++++++++++- tools/tests | 1 - 7 files changed, 113 insertions(+), 46 deletions(-) diff --git a/include/TrustWalletCore/TWCosmosProto.h b/include/TrustWalletCore/TWCosmosProto.h index b87c5705d88..a5513445c4e 100644 --- a/include/TrustWalletCore/TWCosmosProto.h +++ b/include/TrustWalletCore/TWCosmosProto.h @@ -14,6 +14,7 @@ typedef TWData *_Nonnull TW_Cosmos_Proto_SendCoinsMessage; typedef TWData *_Nonnull TW_Cosmos_Proto_StakeMessage; typedef TWData *_Nonnull TW_Cosmos_Proto_ReStakeMessage; typedef TWData *_Nonnull TW_Cosmos_Proto_WithdrawStakeRewardMessage; +typedef TWData *_Nonnull TW_Cosmos_Proto_WithdrawStakeRewardsAllMessage; typedef TWData *_Nonnull TW_Cosmos_Proto_Signature; typedef TWData *_Nonnull TW_Cosmos_Proto_Transaction; typedef TWData *_Nonnull TW_Cosmos_Proto_SigningInput; diff --git a/src/Cosmos/Serialization.cpp b/src/Cosmos/Serialization.cpp index 1dedb73e5fb..cc42857d85e 100644 --- a/src/Cosmos/Serialization.cpp +++ b/src/Cosmos/Serialization.cpp @@ -16,12 +16,13 @@ using namespace TW::Cosmos::Proto; using json = nlohmann::json; using string = std::string; -const string AMINO_PREFIX_SEND_COIN_MESSAGE = "cosmos-sdk/MsgSend"; -const string AMINO_PREFIX_STAKE_MESSAGE = "cosmos-sdk/MsgDelegate"; -const string AMINO_PREFIX_UNSTAKE_MESSAGE = "cosmos-sdk/MsgUndelegate"; -const string AMINO_PREFIX_RESTAKE_MESSAGE = "cosmos-sdk/MsgBeginRedelegate"; -const string AMINO_PREFIX_WITHDRAW_STAKE_MESSAGE = "cosmos-sdk/MsgWithdrawDelegationReward"; -const string AMINO_PREFIX_PUBLIC_KEY = "tendermint/PubKeySecp256k1"; +const string TYPE_PREFIX_MSG_SEND = "cosmos-sdk/MsgSend"; +const string TYPE_PREFIX_MSG_DELEGATE = "cosmos-sdk/MsgDelegate"; +const string TYPE_PREFIX_MSG_UNDELEGATE = "cosmos-sdk/MsgUndelegate"; +const string TYPE_PREFIX_MSG_REDELEGATE = "cosmos-sdk/MsgBeginRedelegate"; +const string TYPE_PREFIX_MSG_WITHDRAW_REWARD = "cosmos-sdk/MsgWithdrawDelegationReward"; +const string TYPE_PREFIX_MSG_WITHDRAW_REWARDS_ALL = "cosmos-sdk/MsgWithdrawDelegationRewardsAll"; +const string TYPE_PREFIX_PUBLIC_KEY = "tendermint/PubKeySecp256k1"; static json broadcastJSON(json& jsonObj) { json jsonMsgWrapper; @@ -66,7 +67,7 @@ static json feeJSON(const Fee& fee) { return jsonFee; } -static json sendCoinsMessageJSON(json& amounts, const string& from_address, const string& to_address, const string& type_prefix) { +static json sendCoinsMessage(json& amounts, const string& from_address, const string& to_address, const string& type_prefix) { json jsonMsg; jsonMsg["amount"] = amounts; @@ -76,7 +77,7 @@ static json sendCoinsMessageJSON(json& amounts, const string& from_address, cons return wrapperJSON(type_prefix, jsonMsg); } -static json stakeMessageJSON(json& amount, const string& delegator_address, const string& validator_address, const string& type_prefix) { +static json stakeMessage(json& amount, const string& delegator_address, const string& validator_address, const string& type_prefix) { json jsonMsg; jsonMsg["amount"] = amount; @@ -86,7 +87,7 @@ static json stakeMessageJSON(json& amount, const string& delegator_address, cons return wrapperJSON(type_prefix, jsonMsg); } -static json restakeMessageJSON(json& amount, const string& delegator_address, const string& validator_src_address, const string& validator_dst_address, const string& type_prefix) { +static json restakeMessage(json& amount, const string& delegator_address, const string& validator_src_address, const string& validator_dst_address, const string& type_prefix) { json jsonMsg; jsonMsg["amount"] = amount; @@ -97,7 +98,7 @@ static json restakeMessageJSON(json& amount, const string& delegator_address, co return wrapperJSON(type_prefix, jsonMsg); } -static json withdrawStakeRewardMessageJSON(const string& delegator_address, const string& validator_address, const string& type_prefix) { +static json withdrawStakeRewardMessage(const string& delegator_address, const string& validator_address, const string& type_prefix) { json jsonMsg; jsonMsg["delegator_address"] = delegator_address; @@ -106,48 +107,62 @@ static json withdrawStakeRewardMessageJSON(const string& delegator_address, cons return wrapperJSON(type_prefix, jsonMsg); } -static json sendCoinsMessageJSON(const SendCoinsMessage& message) { +static json withdrawStakeRewardsAllMessage(const string& delegator_address, const string& type_prefix) { + json jsonMsg; + + jsonMsg["delegator_address"] = delegator_address; + + return wrapperJSON(type_prefix, jsonMsg); +} + +static json sendCoinsMessage(const SendCoinsMessage& message) { json jsonAmounts = json::array(); for (auto& amount : message.amounts()) { jsonAmounts.push_back(amountJSON(std::to_string(amount.amount()), amount.denom())); } - return sendCoinsMessageJSON(jsonAmounts, message.from_address(), message.to_address(), message.type_prefix()); + return sendCoinsMessage(jsonAmounts, message.from_address(), message.to_address(), message.type_prefix()); } -static json stakeMessageJSON(const StakeMessage& message) { +static json stakeMessage(const StakeMessage& message) { auto amount = message.amount(); json jsonAmount = amountJSON(std::to_string(amount.amount()), amount.denom()); - return stakeMessageJSON(jsonAmount, message.delegator_address(), message.validator_address(), message.type_prefix()); + return stakeMessage(jsonAmount, message.delegator_address(), message.validator_address(), message.type_prefix()); } -static json restakeMessageJSON(const ReStakeMessage& message) { +static json restakeMessage(const ReStakeMessage& message) { auto amount = message.amount(); json jsonAmount = amountJSON(std::to_string(amount.amount()), amount.denom()); - return restakeMessageJSON(jsonAmount, message.delegator_address(), message.validator_src_address(), + return restakeMessage(jsonAmount, message.delegator_address(), message.validator_src_address(), message.validator_dst_address(), message.type_prefix()); } -static json withdrawStakeRewardMessageJSON(const WithdrawStakeRewardMessage& message) { - return withdrawStakeRewardMessageJSON(message.delegator_address(), message.validator_address(), message.type_prefix()); +static json withdrawStakeRewardMessage(const WithdrawStakeRewardMessage& message) { + return withdrawStakeRewardMessage(message.delegator_address(), message.validator_address(), message.type_prefix()); +} + +static json withdrawStakeRewardsAllMessage(const WithdrawStakeRewardsAllMessage& message) { + return withdrawStakeRewardsAllMessage(message.delegator_address(), message.type_prefix()); } static json messageJSON(const SigningInput& input) { if (input.has_send_coins_message()) { - return sendCoinsMessageJSON(input.send_coins_message()); + return sendCoinsMessage(input.send_coins_message()); } else if (input.has_stake_message()) { - return stakeMessageJSON(input.stake_message()); + return stakeMessage(input.stake_message()); } else if (input.has_unstake_message()) { - return stakeMessageJSON(input.unstake_message()); + return stakeMessage(input.unstake_message()); } else if (input.has_restake_message()) { - return restakeMessageJSON(input.restake_message()); + return restakeMessage(input.restake_message()); } else if (input.has_withdraw_stake_reward_message()) { - return withdrawStakeRewardMessageJSON(input.withdraw_stake_reward_message()); + return withdrawStakeRewardMessage(input.withdraw_stake_reward_message()); + } else if (input.has_withdraw_stake_rewards_all_message()) { + return withdrawStakeRewardsAllMessage(input.withdraw_stake_rewards_all_message()); } return nullptr; @@ -155,15 +170,17 @@ static json messageJSON(const SigningInput& input) { static json messageJSON(const Transaction& transaction) { if (transaction.has_send_coins_message()) { - return sendCoinsMessageJSON(transaction.send_coins_message()); + return sendCoinsMessage(transaction.send_coins_message()); } else if (transaction.has_stake_message()) { - return stakeMessageJSON(transaction.stake_message()); + return stakeMessage(transaction.stake_message()); } else if (transaction.has_unstake_message()) { - return stakeMessageJSON(transaction.unstake_message()); + return stakeMessage(transaction.unstake_message()); } else if (transaction.has_restake_message()) { - return restakeMessageJSON(transaction.restake_message()); + return restakeMessage(transaction.restake_message()); } else if (transaction.has_withdraw_stake_reward_message()) { - return withdrawStakeRewardMessageJSON(transaction.withdraw_stake_reward_message()); + return withdrawStakeRewardMessage(transaction.withdraw_stake_reward_message()); + } else if (transaction.has_withdraw_stake_rewards_all_message()) { + return withdrawStakeRewardsAllMessage(transaction.withdraw_stake_rewards_all_message()); } return nullptr; @@ -172,7 +189,7 @@ static json messageJSON(const Transaction& transaction) { static json signatureJSON(const Signature& signature) { json jsonSignature; - jsonSignature["pub_key"]["type"] = AMINO_PREFIX_PUBLIC_KEY; + jsonSignature["pub_key"]["type"] = TYPE_PREFIX_PUBLIC_KEY; jsonSignature["pub_key"]["value"] = Base64::encode(Data(signature.public_key().begin(), signature.public_key().end())); jsonSignature["signature"] = Base64::encode(Data(signature.signature().begin(), signature.signature().end())); diff --git a/src/Cosmos/Serialization.h b/src/Cosmos/Serialization.h index cd85fee95c4..4c31c6e15e3 100644 --- a/src/Cosmos/Serialization.h +++ b/src/Cosmos/Serialization.h @@ -9,16 +9,19 @@ #include "../proto/Cosmos.pb.h" #include -extern const std::string AMINO_PREFIX_SEND_COIN_MESSAGE; -extern const std::string AMINO_PREFIX_STAKE_MESSAGE; -extern const std::string AMINO_PREFIX_UNSTAKE_MESSAGE; -extern const std::string AMINO_PREFIX_RESTAKE_MESSAGE; -extern const std::string AMINO_PREFIX_WITHDRAW_STAKE_MESSAGE; -extern const std::string AMINO_PREFIX_PUBLIC_KEY; +using string = std::string; + +extern const string TYPE_PREFIX_MSG_SEND; +extern const string TYPE_PREFIX_MSG_DELEGATE; +extern const string TYPE_PREFIX_MSG_UNDELEGATE; +extern const string TYPE_PREFIX_MSG_REDELEGATE; +extern const string TYPE_PREFIX_MSG_WITHDRAW_REWARD; +extern const string TYPE_PREFIX_MSG_WITHDRAW_REWARDS_ALL; +extern const string TYPE_PREFIX_PUBLIC_KEY; namespace TW::Cosmos { nlohmann::json signaturePreimageJSON(const Proto::SigningInput& input); -nlohmann::json transactionJSON(const Proto::Transaction& transaction, const std::string& type_prefix); +nlohmann::json transactionJSON(const Proto::Transaction& transaction, const string& type_prefix); } // namespace diff --git a/src/Cosmos/Signer.cpp b/src/Cosmos/Signer.cpp index 4c982a50a9c..8d7247adb5f 100644 --- a/src/Cosmos/Signer.cpp +++ b/src/Cosmos/Signer.cpp @@ -21,39 +21,45 @@ using json = nlohmann::json; Signer::Signer(Proto::SigningInput&& input) { if (input.type_prefix().empty()) { - input.set_type_prefix(AMINO_PREFIX_SEND_COIN_MESSAGE); + input.set_type_prefix(TYPE_PREFIX_MSG_SEND); } if (input.has_send_coins_message()) { auto message = input.send_coins_message(); if (message.type_prefix().empty()) { - message.set_type_prefix(AMINO_PREFIX_SEND_COIN_MESSAGE); + message.set_type_prefix(TYPE_PREFIX_MSG_SEND); } *input.mutable_send_coins_message() = message; } else if (input.has_stake_message()) { auto message = input.stake_message(); if (message.type_prefix().empty()) { - message.set_type_prefix(AMINO_PREFIX_STAKE_MESSAGE); + message.set_type_prefix(TYPE_PREFIX_MSG_DELEGATE); } *input.mutable_stake_message() = message; } else if(input.has_unstake_message()) { auto message = input.unstake_message(); if (message.type_prefix().empty()) { - message.set_type_prefix(AMINO_PREFIX_UNSTAKE_MESSAGE); + message.set_type_prefix(TYPE_PREFIX_MSG_UNDELEGATE); } *input.mutable_unstake_message() = message; } else if(input.has_withdraw_stake_reward_message()) { auto message = input.withdraw_stake_reward_message(); if (message.type_prefix().empty()) { - message.set_type_prefix(AMINO_PREFIX_WITHDRAW_STAKE_MESSAGE); + message.set_type_prefix(TYPE_PREFIX_MSG_WITHDRAW_REWARD); } *input.mutable_withdraw_stake_reward_message() = message; } else if(input.has_restake_message()) { auto message = input.restake_message(); if (message.type_prefix().empty()) { - message.set_type_prefix(AMINO_PREFIX_RESTAKE_MESSAGE); + message.set_type_prefix(TYPE_PREFIX_MSG_REDELEGATE); } *input.mutable_restake_message() = message; + } else if (input.has_withdraw_stake_rewards_all_message()) { + auto message = input.withdraw_stake_rewards_all_message(); + if (message.type_prefix().empty()) { + message.set_type_prefix(TYPE_PREFIX_MSG_WITHDRAW_REWARDS_ALL); + } + *input.mutable_withdraw_stake_rewards_all_message() = message; } this->input = input; @@ -91,6 +97,8 @@ json Signer::buildTransactionJSON(const Data& signature) const { *transaction.mutable_restake_message() = input.restake_message(); } else if (input.has_withdraw_stake_reward_message()) { *transaction.mutable_withdraw_stake_reward_message() = input.withdraw_stake_reward_message(); + } else if (input.has_withdraw_stake_rewards_all_message()) { + *transaction.mutable_withdraw_stake_rewards_all_message() = input.withdraw_stake_rewards_all_message(); } *transaction.mutable_signature() = sig; diff --git a/src/proto/Cosmos.proto b/src/proto/Cosmos.proto index 7dec3bb00ce..662ea6f7ff1 100644 --- a/src/proto/Cosmos.proto +++ b/src/proto/Cosmos.proto @@ -51,6 +51,12 @@ message WithdrawStakeRewardMessage { string type_prefix = 3; } +message WithdrawStakeRewardsAllMessage { + string delegator_address = 1; + // cosmos-sdk/MsgWithdrawDelegationRewardsAll + string type_prefix = 2; +} + // Signature message Signature { bytes public_key = 1; @@ -69,6 +75,7 @@ message Transaction { StakeMessage unstake_message = 6; ReStakeMessage restake_message = 7; WithdrawStakeRewardMessage withdraw_stake_reward_message = 8; + WithdrawStakeRewardsAllMessage withdraw_stake_rewards_all_message = 9; } } @@ -88,9 +95,10 @@ message SigningInput { StakeMessage unstake_message = 9; ReStakeMessage restake_message = 10; WithdrawStakeRewardMessage withdraw_stake_reward_message = 11; + WithdrawStakeRewardsAllMessage withdraw_stake_rewards_all_message = 12; } // default is cosmos-sdk/MsgSend - string type_prefix = 12; + string type_prefix = 20; } // Transaction signing output. diff --git a/tests/Cosmos/StakingTests.cpp b/tests/Cosmos/StakingTests.cpp index dc98e653e80..3ec6bb0e368 100644 --- a/tests/Cosmos/StakingTests.cpp +++ b/tests/Cosmos/StakingTests.cpp @@ -15,7 +15,8 @@ #include #include -namespace TW::Cosmos { +using namespace TW; +using namespace TW::Cosmos; TEST(CosmosStaking, Staking) { auto input = Proto::SigningInput(); @@ -159,6 +160,36 @@ TEST(CosmosStaking, Withdraw) { ASSERT_EQ("{\"mode\":\"block\",\"tx\":{\"fee\":{\"amount\":[{\"amount\":\"1018\",\"denom\":\"muon\"}],\"gas\":\"101721\"},\"memo\":\"\",\"msg\":[{\"type\":\"cosmos-sdk/MsgWithdrawDelegationReward\",\"value\":{\"delegator_address\":\"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02\",\"validator_address\":\"cosmosvaloper1zkupr83hrzkn3up5elktzcq3tuft8nxsmwdqgp\"}}],\"signatures\":[{\"pub_key\":{\"type\":\"tendermint/PubKeySecp256k1\",\"value\":\"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F\"},\"signature\":\"VG8NZzVvavlM+1qyK5dOSZwzEj8sLCkvTw5kh44Oco9GQxBf13FVC+s/I3HwiICqo4+o8jNMEDp3nx2C0tuY1g==\"}],\"type\":\"cosmos-sdk/MsgSend\"}}", output.json()); ASSERT_EQ(hex(output.signature()), "546f0d67356f6af94cfb5ab22b974e499c33123f2c2c292f4f0e64878e0e728f4643105fd771550beb3f2371f08880aaa38fa8f2334c103a779f1d82d2db98d6"); - } +} + +TEST(CosmosStaking, WithdrawAll) { + auto input = Proto::SigningInput(); + input.set_account_number(1037); + input.set_chain_id("gaia-13003"); + input.set_memo(""); + input.set_sequence(7); + + auto& message = *input.mutable_withdraw_stake_rewards_all_message(); + message.set_delegator_address("cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02"); + + auto &fee = *input.mutable_fee(); + fee.set_gas(101721); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("muon"); + amountOfFee->set_amount(1018); + + auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto signer = Cosmos::Signer(std::move(input)); + auto signature = signer.sign(); + auto signatureInBase64 = Base64::encode(signature); + + ASSERT_EQ("ImvsgnfbjebxzeBCUPeOcMoOJWMV3IhWM1apV20WiS4K11iA50fe0uXr4Xf/RTxUDXTm56cne/OjOr77BG99Aw==", signatureInBase64); + + auto output = signer.build(); + + ASSERT_EQ("{\"mode\":\"block\",\"tx\":{\"fee\":{\"amount\":[{\"amount\":\"1018\",\"denom\":\"muon\"}],\"gas\":\"101721\"},\"memo\":\"\",\"msg\":[{\"type\":\"cosmos-sdk/MsgWithdrawDelegationRewardsAll\",\"value\":{\"delegator_address\":\"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02\"}}],\"signatures\":[{\"pub_key\":{\"type\":\"tendermint/PubKeySecp256k1\",\"value\":\"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F\"},\"signature\":\"ImvsgnfbjebxzeBCUPeOcMoOJWMV3IhWM1apV20WiS4K11iA50fe0uXr4Xf/RTxUDXTm56cne/OjOr77BG99Aw==\"}],\"type\":\"cosmos-sdk/MsgSend\"}}", output.json()); + ASSERT_EQ(hex(output.signature()), "226bec8277db8de6f1cde04250f78e70ca0e256315dc88563356a9576d16892e0ad75880e747ded2e5ebe177ff453c540d74e6e7a7277bf3a33abefb046f7d03"); } diff --git a/tools/tests b/tools/tests index 467de545a1d..1662d1c3e47 100755 --- a/tools/tests +++ b/tools/tests @@ -14,4 +14,3 @@ build/tests/tests tests tools/ios-test tools/android-test -tools/js-test