Skip to content

Commit

Permalink
Cosmos unstake (#565)
Browse files Browse the repository at this point in the history
* Add messages for cosmos unstake

* Added tests to unstake and withdraw rewards
  • Loading branch information
leoneparise authored and vikmeup committed Aug 2, 2019
1 parent 2b615ca commit a3fa33d
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 11 deletions.
1 change: 1 addition & 0 deletions include/TrustWalletCore/TWCosmosProto.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ typedef TWData *_Nonnull TW_Cosmos_Proto_Amount;
typedef TWData *_Nonnull TW_Cosmos_Proto_Fee;
typedef TWData *_Nonnull TW_Cosmos_Proto_SendCoinsMessage;
typedef TWData *_Nonnull TW_Cosmos_Proto_StakeMessage;
typedef TWData *_Nonnull TW_Cosmos_Proto_WithdrawStakeRewardMessage;
typedef TWData *_Nonnull TW_Cosmos_Proto_Signature;
typedef TWData *_Nonnull TW_Cosmos_Proto_Transaction;
typedef TWData *_Nonnull TW_Cosmos_Proto_SigningInput;
Expand Down
24 changes: 24 additions & 0 deletions src/Cosmos/Serialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ using json = nlohmann::json;

const std::string AMINO_PREFIX_SEND_COIN_MESSAGE = "cosmos-sdk/MsgSend";
const std::string AMINO_PREFIX_STAKE_MESSAGE = "cosmos-sdk/MsgDelegate";
const std::string AMINO_PREFIX_UNSTAKE_MESSAGE = "cosmos-sdk/MsgUndelegate";
const std::string AMINO_PREFIX_WITHDRAW_STAKE_MESSAGE = "cosmos-sdk/MsgWithdrawDelegationReward";
const std::string AMINO_PREFIX_TRANSACTION = "auth/StdTx";
const std::string AMINO_PREFIX_PUBLIC_KEY = "tendermint/PubKeySecp256k1";

Expand Down Expand Up @@ -83,6 +85,15 @@ json stakeMessageJSON(json& amount, std::string delegator_address, std::string v
return wrapperJSON(type_prefix, jsonMsg);
}

json withdrawStakeRewardMessageJSON(std::string delegator_address, std::string validator_address, std::string type_prefix) {
json jsonMsg;

jsonMsg["delegator_address"] = delegator_address;
jsonMsg["validator_address"] = validator_address;

return wrapperJSON(type_prefix, jsonMsg);
}

json sendCoinsMessageJSON(const SendCoinsMessage& message) {
json jsonAmounts = json::array();

Expand All @@ -93,18 +104,27 @@ json sendCoinsMessageJSON(const SendCoinsMessage& message) {
return sendCoinsMessageJSON(jsonAmounts, message.from_address(), message.to_address(), message.type_prefix());
}


json stakeMessageJSON(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());
}

json withdrawStakeRewardMessageJSON(const WithdrawStakeRewardMessage& message) {
return withdrawStakeRewardMessageJSON(message.delegator_address(), message.validator_address(), message.type_prefix());
}

json messageJSON(const SigningInput& input) {
if (input.has_send_coins_message()) {
return sendCoinsMessageJSON(input.send_coins_message());
} else if (input.has_stake_message()) {
return stakeMessageJSON(input.stake_message());
} else if (input.has_unstake_message()) {
return stakeMessageJSON(input.unstake_message());
} else if (input.has_withdraw_stake_reward_message()) {
return withdrawStakeRewardMessageJSON(input.withdraw_stake_reward_message());
}

return nullptr;
Expand All @@ -115,6 +135,10 @@ json messageJSON(const Transaction& transaction) {
return sendCoinsMessageJSON(transaction.send_coins_message());
} else if (transaction.has_stake_message()) {
return stakeMessageJSON(transaction.stake_message());
} else if (transaction.has_unstake_message()) {
return stakeMessageJSON(transaction.unstake_message());
} else if (transaction.has_withdraw_stake_reward_message()) {
return withdrawStakeRewardMessageJSON(transaction.withdraw_stake_reward_message());
}

return nullptr;
Expand Down
2 changes: 2 additions & 0 deletions src/Cosmos/Serialization.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
extern const std::string AMINO_PREFIX_SEND_COIN_MESSAGE;
extern const std::string AMINO_PREFIX_STAKE_MESSAGE;
extern const std::string AMINO_PREFIX_TRANSACTION;
extern const std::string AMINO_PREFIX_UNSTAKE_MESSAGE;
extern const std::string AMINO_PREFIX_WITHDRAW_STAKE_MESSAGE;
extern const std::string AMINO_PREFIX_PUBLIC_KEY;

namespace TW::Cosmos {
Expand Down
32 changes: 24 additions & 8 deletions src/Cosmos/Signer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,29 @@ Signer::Signer(Proto::SigningInput&& input) {
}

if (input.has_send_coins_message()) {
auto coin_message = input.send_coins_message();
if (coin_message.type_prefix().empty()) {
coin_message.set_type_prefix(AMINO_PREFIX_SEND_COIN_MESSAGE);
auto message = input.send_coins_message();
if (message.type_prefix().empty()) {
message.set_type_prefix(AMINO_PREFIX_SEND_COIN_MESSAGE);
}
*input.mutable_send_coins_message() = coin_message;
*input.mutable_send_coins_message() = message;
} else if (input.has_stake_message()) {
auto stake_message = input.stake_message();
if (stake_message.type_prefix().empty()) {
stake_message.set_type_prefix(AMINO_PREFIX_STAKE_MESSAGE);
auto message = input.stake_message();
if (message.type_prefix().empty()) {
message.set_type_prefix(AMINO_PREFIX_STAKE_MESSAGE);
}
*input.mutable_stake_message() = stake_message;
*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);
}
*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);
}
*input.mutable_withdraw_stake_reward_message() = message;
}
this->input = input;
}
Expand Down Expand Up @@ -74,6 +86,10 @@ json Signer::buildTransactionJSON(const Data& signature) const {
*transaction.mutable_send_coins_message() = input.send_coins_message();
} else if (input.has_stake_message()) {
*transaction.mutable_stake_message() = input.stake_message();
} else if (input.has_unstake_message()) {
*transaction.mutable_unstake_message() = input.unstake_message();
} else if (input.has_withdraw_stake_reward_message()) {
*transaction.mutable_withdraw_stake_reward_message() = input.withdraw_stake_reward_message();
}

*transaction.mutable_signature() = sig;
Expand Down
18 changes: 15 additions & 3 deletions src/proto/Cosmos.proto
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,18 @@ message StakeMessage {
string delegator_address = 1;
string validator_address = 2;
Amount amount = 3;
// default is cosmos-sdk/MsgDelegate
// cosmos-sdk/MsgDelegate to stake and cosmos-sdk/MsgUndelegate to unstake
string type_prefix = 4;
}

// Message for staking.
message WithdrawStakeRewardMessage {
string delegator_address = 1;
string validator_address = 2;
// default is cosmos-sdk/MsgWithdrawDelegationReward
string type_prefix = 3;
}

// Signature
message Signature {
bytes public_key = 1;
Expand All @@ -48,7 +56,9 @@ message Transaction {
oneof message_oneof {
SendCoinsMessage send_coins_message = 4;
StakeMessage stake_message = 5;
}
StakeMessage unstake_message = 6;
WithdrawStakeRewardMessage withdraw_stake_reward_message = 7;
}
}

// Input data necessary to create a signed order.
Expand All @@ -64,9 +74,11 @@ message SigningInput {
oneof message_oneof {
SendCoinsMessage send_coins_message = 7;
StakeMessage stake_message = 8;
StakeMessage unstake_message = 9;
WithdrawStakeRewardMessage withdraw_stake_reward_message = 10;
}
// default is cosmos-sdk/MsgSend
string type_prefix = 9;
string type_prefix = 11;
}

// Transaction signing output.
Expand Down
79 changes: 79 additions & 0 deletions tests/Cosmos/StakingTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,83 @@ TEST(CosmosStaking, Staking) {
*/
}

TEST(CosmosStaking, Unstaking) {
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_unstake_message();
message.set_delegator_address("cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02");
message.set_validator_address("cosmosvaloper1zkupr83hrzkn3up5elktzcq3tuft8nxsmwdqgp");
auto& amountOfTx = *message.mutable_amount();
amountOfTx.set_denom("muon");
amountOfTx.set_amount(10);

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 = signer.signInBase64();

ASSERT_EQ("j4WpUVohGIHa6/s0bCvuyjq1wtQGqbOtQCz92qPQjisTN44Tz++Ozx1lAP6F0M4+eTA03XerqQ8hZCeAfL/3nw==", signatureInBase64);

auto output = signer.build();

ASSERT_EQ("{\"tx\":{\"fee\":{\"amount\":[{\"amount\":\"1018\",\"denom\":\"muon\"}],\"gas\":\"101721\"},\"memo\":\"\",\"msg\":[{\"type\":\"cosmos-sdk/MsgUndelegate\",\"value\":{\"amount\":{\"amount\":\"10\",\"denom\":\"muon\"},\"delegator_address\":\"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02\",\"validator_address\":\"cosmosvaloper1zkupr83hrzkn3up5elktzcq3tuft8nxsmwdqgp\"}}],\"signatures\":[{\"pub_key\":{\"type\":\"tendermint/PubKeySecp256k1\",\"value\":\"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F\"},\"signature\":\"j4WpUVohGIHa6/s0bCvuyjq1wtQGqbOtQCz92qPQjisTN44Tz++Ozx1lAP6F0M4+eTA03XerqQ8hZCeAfL/3nw==\"}],\"type\":\"cosmos-sdk/MsgSend\"}}", output.json());

ASSERT_EQ("a1627478a563666565a266616d6f756e7481a266616d6f756e7464313031386564656e6f6d646d756f6e6367617366313031373231646d656d6f60636d736781a264747970657818636f736d6f732d73646b2f4d7367556e64656c65676174656576616c7565a366616d6f756e74a266616d6f756e746231306564656e6f6d646d756f6e7164656c656761746f725f61646472657373782d636f736d6f733168736b366a727979716a6668703564686335357463396a74636b7967783065706836646430327176616c696461746f725f616464726573737834636f736d6f7376616c6f706572317a6b757072383368727a6b6e33757035656c6b747a63713374756674386e78736d77647167706a7369676e61747572657381a2677075625f6b6579a26474797065781a74656e6465726d696e742f5075624b6579536563703235366b316576616c7565782c416c636f6273507a66544e56653775714141736e6445724a416a71706c6e79756461474230662b522b703346697369676e617475726578586a34577055566f6847494861362f733062437675796a71317774514771624f7451437a39327150516a6973544e3434547a2b2b4f7a78316c41503646304d342b6554413033586572715138685a436541664c2f336e773d3d647479706572636f736d6f732d73646b2f4d736753656e64", hex(output.encoded()));

/*
the sample tx on testnet
https://hubble.figment.network/chains/gaia-13003/blocks/125922/transactions/AAE5E18516DC8B0EF864F91B9531AB63B2248E3FC9058B6A330AE79EF1B4120A?format=json
*/
}

TEST(CosmosStaking, Withdraw) {
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_reward_message();
message.set_delegator_address("cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02");
message.set_validator_address("cosmosvaloper1zkupr83hrzkn3up5elktzcq3tuft8nxsmwdqgp");

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 = signer.signInBase64();

ASSERT_EQ("VG8NZzVvavlM+1qyK5dOSZwzEj8sLCkvTw5kh44Oco9GQxBf13FVC+s/I3HwiICqo4+o8jNMEDp3nx2C0tuY1g==", signatureInBase64);

auto output = signer.build();

ASSERT_EQ("{\"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("a1627478a563666565a266616d6f756e7481a266616d6f756e7464313031386564656e6f6d646d756f6e6367617366313031373231646d656d6f60636d736781a264747970657826636f736d6f732d73646b2f4d7367576974686472617744656c65676174696f6e5265776172646576616c7565a27164656c656761746f725f61646472657373782d636f736d6f733168736b366a727979716a6668703564686335357463396a74636b7967783065706836646430327176616c696461746f725f616464726573737834636f736d6f7376616c6f706572317a6b757072383368727a6b6e33757035656c6b747a63713374756674386e78736d77647167706a7369676e61747572657381a2677075625f6b6579a26474797065781a74656e6465726d696e742f5075624b6579536563703235366b316576616c7565782c416c636f6273507a66544e56653775714141736e6445724a416a71706c6e79756461474230662b522b703346697369676e617475726578585647384e5a7a567661766c4d2b3171794b35644f535a777a456a38734c436b765477356b6834344f636f39475178426631334656432b732f49334877694943716f342b6f386a4e4d454470336e7832433074755931673d3d647479706572636f736d6f732d73646b2f4d736753656e64", hex(output.encoded()));

/*
the sample tx on testnet
https://hubble.figment.network/chains/gaia-13003/blocks/125922/transactions/AAE5E18516DC8B0EF864F91B9531AB63B2248E3FC9058B6A330AE79EF1B4120A?format=json
*/
}

}

0 comments on commit a3fa33d

Please sign in to comment.