Skip to content
Open
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
103 changes: 67 additions & 36 deletions src/Tron/Signer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// Copyright © 2017 Trust Wallet.

#include "Signer.h"
#include "Address.h"

#include "Protobuf/TronInternal.pb.h"

Expand All @@ -14,19 +15,32 @@
#include <nlohmann/json.hpp>
#include <cassert>
#include <chrono>
#include <stdexcept>

namespace TW::Tron {

const std::string TRANSFER_TOKEN_FUNCTION = "0xa9059cbb";

/// Decode a Base58Check-encoded Tron address, throwing on invalid input.
static Data decodeAddress(const std::string& addr) {
auto decoded = Base58::decodeCheck(addr);
if (decoded.empty() || decoded.size() != Address::size) {
throw std::invalid_argument("Invalid Tron address: " + addr);
}
if (decoded[0] != Address::prefix) {
throw std::invalid_argument("Invalid Tron address: " + addr);
}
return decoded;
}

/// Converts an external TransferContract to an internal one used for signing.
protocol::TransferContract to_internal(const Proto::TransferContract& transfer) {
auto internal = protocol::TransferContract();

const auto ownerAddress = Base58::decodeCheck(transfer.owner_address());
const auto ownerAddress = decodeAddress(transfer.owner_address());
internal.set_owner_address(ownerAddress.data(), ownerAddress.size());

const auto toAddress = Base58::decodeCheck(transfer.to_address());
const auto toAddress = decodeAddress(transfer.to_address());
internal.set_to_address(toAddress.data(), toAddress.size());

internal.set_amount(transfer.amount());
Expand All @@ -41,10 +55,10 @@ protocol::TransferAssetContract to_internal(const Proto::TransferAssetContract&

internal.set_asset_name(transfer.asset_name());

const auto ownerAddress = Base58::decodeCheck(transfer.owner_address());
const auto ownerAddress = decodeAddress(transfer.owner_address());
internal.set_owner_address(ownerAddress.data(), ownerAddress.size());

const auto toAddress = Base58::decodeCheck(transfer.to_address());
const auto toAddress = decodeAddress(transfer.to_address());
internal.set_to_address(toAddress.data(), toAddress.size());

internal.set_amount(transfer.amount());
Expand All @@ -55,8 +69,8 @@ protocol::TransferAssetContract to_internal(const Proto::TransferAssetContract&
protocol::FreezeBalanceContract to_internal(const Proto::FreezeBalanceContract& freezeContract) {
auto internal = protocol::FreezeBalanceContract();
auto resource = protocol::ResourceCode();
const auto ownerAddress = Base58::decodeCheck(freezeContract.owner_address());
const auto receiverAddress = Base58::decodeCheck(freezeContract.receiver_address());
const auto ownerAddress = decodeAddress(freezeContract.owner_address());
const auto receiverAddress = decodeAddress(freezeContract.receiver_address());

protocol::ResourceCode_Parse(freezeContract.resource(), &resource);

Expand All @@ -72,7 +86,7 @@ protocol::FreezeBalanceContract to_internal(const Proto::FreezeBalanceContract&
protocol::FreezeBalanceV2Contract to_internal(const Proto::FreezeBalanceV2Contract& freezeContract) {
auto internal = protocol::FreezeBalanceV2Contract();
auto resource = protocol::ResourceCode();
const auto ownerAddress = Base58::decodeCheck(freezeContract.owner_address());
const auto ownerAddress = decodeAddress(freezeContract.owner_address());

protocol::ResourceCode_Parse(freezeContract.resource(), &resource);

Expand All @@ -86,8 +100,8 @@ protocol::FreezeBalanceV2Contract to_internal(const Proto::FreezeBalanceV2Contra
protocol::UnfreezeBalanceContract to_internal(const Proto::UnfreezeBalanceContract& unfreezeContract) {
auto internal = protocol::UnfreezeBalanceContract();
auto resource = protocol::ResourceCode();
const auto ownerAddress = Base58::decodeCheck(unfreezeContract.owner_address());
const auto receiverAddress = Base58::decodeCheck(unfreezeContract.receiver_address());
const auto ownerAddress = decodeAddress(unfreezeContract.owner_address());
const auto receiverAddress = decodeAddress(unfreezeContract.receiver_address());

protocol::ResourceCode_Parse(unfreezeContract.resource(), &resource);

Expand All @@ -101,7 +115,7 @@ protocol::UnfreezeBalanceContract to_internal(const Proto::UnfreezeBalanceContra
protocol::UnfreezeBalanceV2Contract to_internal(const Proto::UnfreezeBalanceV2Contract& unfreezeContract) {
auto internal = protocol::UnfreezeBalanceV2Contract();
auto resource = protocol::ResourceCode();
const auto ownerAddress = Base58::decodeCheck(unfreezeContract.owner_address());
const auto ownerAddress = decodeAddress(unfreezeContract.owner_address());

protocol::ResourceCode_Parse(unfreezeContract.resource(), &resource);

Expand All @@ -115,8 +129,8 @@ protocol::UnfreezeBalanceV2Contract to_internal(const Proto::UnfreezeBalanceV2Co
protocol::DelegateResourceContract to_internal(const Proto::DelegateResourceContract& delegateContract) {
auto internal = protocol::DelegateResourceContract();
auto resource = protocol::ResourceCode();
const auto ownerAddress = Base58::decodeCheck(delegateContract.owner_address());
const auto receiverAddress = Base58::decodeCheck(delegateContract.receiver_address());
const auto ownerAddress = decodeAddress(delegateContract.owner_address());
const auto receiverAddress = decodeAddress(delegateContract.receiver_address());

protocol::ResourceCode_Parse(delegateContract.resource(), &resource);

Expand All @@ -132,8 +146,8 @@ protocol::DelegateResourceContract to_internal(const Proto::DelegateResourceCont
protocol::UnDelegateResourceContract to_internal(const Proto::UnDelegateResourceContract& undelegateContract) {
auto internal = protocol::UnDelegateResourceContract();
auto resource = protocol::ResourceCode();
const auto ownerAddress = Base58::decodeCheck(undelegateContract.owner_address());
const auto receiverAddress = Base58::decodeCheck(undelegateContract.receiver_address());
const auto ownerAddress = decodeAddress(undelegateContract.owner_address());
const auto receiverAddress = decodeAddress(undelegateContract.receiver_address());

protocol::ResourceCode_Parse(undelegateContract.resource(), &resource);

Expand All @@ -147,14 +161,14 @@ protocol::UnDelegateResourceContract to_internal(const Proto::UnDelegateResource

protocol::WithdrawExpireUnfreezeContract to_internal(const Proto::WithdrawExpireUnfreezeContract& withdrawExpireUnfreezeContract) {
auto internal = protocol::WithdrawExpireUnfreezeContract();
const auto ownerAddress = Base58::decodeCheck(withdrawExpireUnfreezeContract.owner_address());
const auto ownerAddress = decodeAddress(withdrawExpireUnfreezeContract.owner_address());
internal.set_owner_address(ownerAddress.data(), ownerAddress.size());
return internal;
}

protocol::UnfreezeAssetContract to_internal(const Proto::UnfreezeAssetContract& unfreezeContract) {
auto internal = protocol::UnfreezeAssetContract();
const auto ownerAddress = Base58::decodeCheck(unfreezeContract.owner_address());
const auto ownerAddress = decodeAddress(unfreezeContract.owner_address());

internal.set_owner_address(ownerAddress.data(), ownerAddress.size());

Expand All @@ -163,13 +177,13 @@ protocol::UnfreezeAssetContract to_internal(const Proto::UnfreezeAssetContract&

protocol::VoteAssetContract to_internal(const Proto::VoteAssetContract& voteContract) {
auto internal = protocol::VoteAssetContract();
const auto ownerAddress = Base58::decodeCheck(voteContract.owner_address());
const auto ownerAddress = decodeAddress(voteContract.owner_address());

internal.set_owner_address(ownerAddress.data(), ownerAddress.size());
internal.set_support(voteContract.support());
internal.set_count(voteContract.count());
for (int i = 0; i < voteContract.vote_address_size(); i++) {
auto voteAddress = Base58::decodeCheck(voteContract.vote_address(i));
auto voteAddress = decodeAddress(voteContract.vote_address(i));
internal.add_vote_address(voteAddress.data(), voteAddress.size());
}

Expand All @@ -178,12 +192,12 @@ protocol::VoteAssetContract to_internal(const Proto::VoteAssetContract& voteCont

protocol::VoteWitnessContract to_internal(const Proto::VoteWitnessContract& voteContract) {
auto internal = protocol::VoteWitnessContract();
const auto ownerAddress = Base58::decodeCheck(voteContract.owner_address());
const auto ownerAddress = decodeAddress(voteContract.owner_address());

internal.set_owner_address(ownerAddress.data(), ownerAddress.size());
internal.set_support(voteContract.support());
for (int i = 0; i < voteContract.votes_size(); i++) {
auto voteAddress = Base58::decodeCheck(voteContract.votes(i).vote_address());
auto voteAddress = decodeAddress(voteContract.votes(i).vote_address());
auto* vote = internal.add_votes();

vote->set_vote_address(voteAddress.data(), voteAddress.size());
Expand All @@ -195,7 +209,7 @@ protocol::VoteWitnessContract to_internal(const Proto::VoteWitnessContract& vote

protocol::WithdrawBalanceContract to_internal(const Proto::WithdrawBalanceContract& withdrawContract) {
auto internal = protocol::WithdrawBalanceContract();
const auto ownerAddress = Base58::decodeCheck(withdrawContract.owner_address());
const auto ownerAddress = decodeAddress(withdrawContract.owner_address());

internal.set_owner_address(ownerAddress.data(), ownerAddress.size());

Expand All @@ -204,8 +218,8 @@ protocol::WithdrawBalanceContract to_internal(const Proto::WithdrawBalanceContra

protocol::TriggerSmartContract to_internal(const Proto::TriggerSmartContract& triggerSmartContract) {
auto internal = protocol::TriggerSmartContract();
const auto ownerAddress = Base58::decodeCheck(triggerSmartContract.owner_address());
const auto contractAddress = Base58::decodeCheck(triggerSmartContract.contract_address());
const auto ownerAddress = decodeAddress(triggerSmartContract.owner_address());
const auto contractAddress = decodeAddress(triggerSmartContract.contract_address());

internal.set_owner_address(ownerAddress.data(), ownerAddress.size());
internal.set_contract_address(contractAddress.data(), contractAddress.size());
Expand All @@ -218,7 +232,7 @@ protocol::TriggerSmartContract to_internal(const Proto::TriggerSmartContract& tr
}

protocol::TriggerSmartContract to_internal(const Proto::TransferTRC20Contract& transferTrc20Contract) {
auto toAddress = Base58::decodeCheck(transferTrc20Contract.to_address());
auto toAddress = decodeAddress(transferTrc20Contract.to_address());
// amount is 256 bits, big endian
Data amount = data(transferTrc20Contract.amount());

Expand Down Expand Up @@ -268,7 +282,7 @@ void setBlockReference(const Proto::Transaction& transaction, protocol::Transact
internal.mutable_raw_data()->set_ref_block_bytes(heightData.data() + heightData.size() - 2, 2);
}

protocol::Transaction buildTransaction(const Proto::SigningInput& input) noexcept {
protocol::Transaction buildTransaction(const Proto::SigningInput& input) {
auto tx = protocol::Transaction();

if (input.transaction().has_transfer()) {
Expand Down Expand Up @@ -438,7 +452,15 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) {
}

auto output = Proto::SigningOutput();
auto tx = buildTransaction(input);

protocol::Transaction tx;
try {
tx = buildTransaction(input);
} catch (const std::invalid_argument& e) {
output.set_error(Common::Proto::Error_invalid_address);
output.set_error_message(e.what());
return output;
}

// Get default timestamp and expiration
const uint64_t now = duration_cast<std::chrono::milliseconds>(
Expand Down Expand Up @@ -493,15 +515,20 @@ Proto::SigningOutput Signer::compile(const Data& signature) const {
return output;
}
}
auto preImage = signaturePreimage();
auto hash = Hash::sha256(preImage);
auto transaction = buildTransaction(input);
const auto json = transactionJSON(transaction, hash, signature).dump();
output.set_json(json.data(), json.size());
output.set_ref_block_bytes(transaction.raw_data().ref_block_bytes());
output.set_ref_block_hash(transaction.raw_data().ref_block_hash());
output.set_id(hash.data(), hash.size());
output.set_signature(signature.data(), signature.size());
try {
auto preImage = signaturePreimage();
auto hash = Hash::sha256(preImage);
auto transaction = buildTransaction(input);
const auto json = transactionJSON(transaction, hash, signature).dump();
output.set_json(json.data(), json.size());
output.set_ref_block_bytes(transaction.raw_data().ref_block_bytes());
output.set_ref_block_hash(transaction.raw_data().ref_block_hash());
output.set_id(hash.data(), hash.size());
output.set_signature(signature.data(), signature.size());
} catch (const std::invalid_argument& e) {
output.set_error(Common::Proto::Error_invalid_address);
output.set_error_message(e.what());
}
return output;
}

Expand All @@ -520,7 +547,11 @@ Data Signer::signaturePreimage() const {
return {};
}
}
return serializeTxRawData(buildTransaction(input));
try {
return serializeTxRawData(buildTransaction(input));
} catch (const std::invalid_argument&) {
return {};
}
}

Data Signer::signaturePreimageHash() const {
Expand Down
73 changes: 73 additions & 0 deletions tests/chains/Tron/SignerTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -581,4 +581,77 @@ TEST(TronSigner, SignTransferTrc20Contract) {
ASSERT_EQ(hex(output.signature()), "bec790877b3a008640781e3948b070740b1f6023c29ecb3f7b5835433c13fc5835e5cad3bd44360ff2ddad5ed7dc9d7dee6878f90e86a40355b7697f5954b88c01");
}

TEST(TronSigner, SignTransferInvalidToAddress) {
auto input = Proto::SigningInput();
auto& transaction = *input.mutable_transaction();
auto& transfer = *transaction.mutable_transfer();
transfer.set_owner_address("TJRyWwFs9wTFGZg3JbrVriFbNfCug5tDeC");
transfer.set_to_address("INVALID_NOT_BASE58");
transfer.set_amount(100);

transaction.set_timestamp(1539295479000);
auto& blockHeader = *transaction.mutable_block_header();
blockHeader.set_timestamp(1539295479000);
blockHeader.set_number(3111739);
blockHeader.set_version(3);

const auto privateKey = PrivateKey(parse_hex("2d8f68944bdbfbc0769542fba8fc2d2a3de67393334471624364c7006da2aa54"));
input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size());

const auto output = Signer::sign(input);
EXPECT_EQ(output.error(), Common::Proto::Error_invalid_address);
EXPECT_TRUE(output.id().empty());
EXPECT_TRUE(output.signature().empty());
}

TEST(TronSigner, SignTransferTrc20InvalidToAddress) {
auto input = Proto::SigningInput();
auto& transaction = *input.mutable_transaction();
auto& transfer_contract = *transaction.mutable_transfer_trc20_contract();
transfer_contract.set_owner_address("TJRyWwFs9wTFGZg3JbrVriFbNfCug5tDeC");
transfer_contract.set_contract_address("THTR75o8xXAgCTQqpiot2AFRAjvW1tSbVV");
transfer_contract.set_to_address("TW1dU4L3eNm7Lw8WvieLKEHpXWAussRG9X"); // typo: last char changed
Data amount = store(uint256_t(1000));
transfer_contract.set_amount(std::string(amount.begin(), amount.end()));

transaction.set_timestamp(1539295479000);
auto& blockHeader = *transaction.mutable_block_header();
blockHeader.set_timestamp(1539295479000);
blockHeader.set_number(3111739);
blockHeader.set_version(3);

const auto privateKey = PrivateKey(parse_hex("2d8f68944bdbfbc0769542fba8fc2d2a3de67393334471624364c7006da2aa54"));
input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size());

const auto output = Signer::sign(input);
EXPECT_EQ(output.error(), Common::Proto::Error_invalid_address);
EXPECT_TRUE(output.id().empty());
EXPECT_TRUE(output.signature().empty());
}

TEST(TronSigner, SignTransferTrc20InvalidContractAddress) {
auto input = Proto::SigningInput();
auto& transaction = *input.mutable_transaction();
auto& transfer_contract = *transaction.mutable_transfer_trc20_contract();
transfer_contract.set_owner_address("TJRyWwFs9wTFGZg3JbrVriFbNfCug5tDeC");
transfer_contract.set_contract_address("0x1234567890abcdef"); // hex address, wrong network
transfer_contract.set_to_address("TW1dU4L3eNm7Lw8WvieLKEHpXWAussRG9Z");
Data amount = store(uint256_t(1000));
transfer_contract.set_amount(std::string(amount.begin(), amount.end()));

transaction.set_timestamp(1539295479000);
auto& blockHeader = *transaction.mutable_block_header();
blockHeader.set_timestamp(1539295479000);
blockHeader.set_number(3111739);
blockHeader.set_version(3);

const auto privateKey = PrivateKey(parse_hex("2d8f68944bdbfbc0769542fba8fc2d2a3de67393334471624364c7006da2aa54"));
input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size());

const auto output = Signer::sign(input);
EXPECT_EQ(output.error(), Common::Proto::Error_invalid_address);
EXPECT_TRUE(output.id().empty());
EXPECT_TRUE(output.signature().empty());
}

} // namespace TW::Tron
Loading