diff --git a/src/Ethereum/RLP.cpp b/src/Ethereum/RLP.cpp index e549793fecf..2a4ca7680dd 100644 --- a/src/Ethereum/RLP.cpp +++ b/src/Ethereum/RLP.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2021 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -35,20 +35,6 @@ Data RLP::encodeList(const Data& encoded) noexcept { return result; } -Data RLP::encode(const Transaction& transaction) noexcept { - auto encoded = Data(); - append(encoded, encode(transaction.nonce)); - append(encoded, encode(transaction.gasPrice)); - append(encoded, encode(transaction.gasLimit)); - append(encoded, encode(transaction.to)); - append(encoded, encode(transaction.amount)); - append(encoded, encode(transaction.payload)); - append(encoded, encode(transaction.v)); - append(encoded, encode(transaction.r)); - append(encoded, encode(transaction.s)); - return encodeList(encoded); -} - Data RLP::encode(const Data& data) noexcept { if (data.size() == 1 && data[0] <= 0x7f) { // Fits in single byte, no header diff --git a/src/Ethereum/RLP.h b/src/Ethereum/RLP.h index d65a2d9aaa7..91b4807bc59 100644 --- a/src/Ethereum/RLP.h +++ b/src/Ethereum/RLP.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2021 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -49,9 +49,6 @@ struct RLP { static Data encode(const uint256_t& number) noexcept; - /// Encodes a transaction. - static Data encode(const Transaction& transaction) noexcept; - /// Wraps encoded data as a list. static Data encodeList(const Data& encoded) noexcept; diff --git a/src/Ethereum/Signer.cpp b/src/Ethereum/Signer.cpp index 609cd6704af..5aeb52fda86 100644 --- a/src/Ethereum/Signer.cpp +++ b/src/Ethereum/Signer.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2021 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -13,27 +13,28 @@ using namespace TW::Ethereum; Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { try { - auto signer = Signer(load(input.chain_id())); + uint256_t chainID = load(input.chain_id()); auto key = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); auto transaction = Signer::build(input); - signer.sign(key, transaction); + auto preHash = transaction->preHash(chainID); + auto signature = sign(key, chainID, preHash); auto output = Proto::SigningOutput(); - auto encoded = RLP::encode(transaction); + auto encoded = transaction->encoded(signature, chainID); output.set_encoded(encoded.data(), encoded.size()); - auto v = store(transaction.v); + auto v = store(signature.v); output.set_v(v.data(), v.size()); - auto r = store(transaction.r); + auto r = store(signature.r); output.set_r(r.data(), r.size()); - auto s = store(transaction.s); + auto s = store(signature.s); output.set_s(s.data(), s.size()); - output.set_data(transaction.payload.data(), transaction.payload.size()); + output.set_data(transaction->payload.data(), transaction->payload.size()); return output; } catch (std::exception&) { @@ -49,8 +50,7 @@ std::string Signer::signJSON(const std::string& json, const Data& key) { return hex(output.encoded()); } -std::tuple Signer::values(const uint256_t &chainID, - const Data& signature) noexcept { +Signature Signer::valuesRSV(const uint256_t& chainID, const Data& signature) noexcept { boost::multiprecision::uint256_t r, s, v; import_bits(r, signature.begin(), signature.begin() + 32); import_bits(s, signature.begin() + 32, signature.begin() + 64); @@ -64,13 +64,12 @@ std::tuple Signer::values(const uint256_t &chai } else { newV = v; } - return std::make_tuple(r, s, newV); + return Signature{r, s, newV}; } -std::tuple -Signer::sign(const uint256_t &chainID, const PrivateKey &privateKey, const Data& hash) noexcept { +Signature Signer::sign(const PrivateKey& privateKey, const uint256_t& chainID, const Data& hash) noexcept { auto signature = privateKey.sign(hash, TWCurveSECP256k1); - return values(chainID, signature); + return valuesRSV(chainID, signature); } // May throw @@ -85,31 +84,28 @@ Data addressStringToData(const std::string& asString) { return asData; } -Transaction Signer::build(const Proto::SigningInput &input) { +std::shared_ptr Signer::buildNonTyped(const Proto::SigningInput& input) { Data toAddress = addressStringToData(input.to_address()); uint256_t nonce = load(input.nonce()); uint256_t gasPrice = load(input.gas_price()); uint256_t gasLimit = load(input.gas_limit()); + assert(gasPrice != 0); switch (input.transaction().transaction_oneof_case()) { case Proto::Transaction::kTransfer: { - auto transaction = Transaction( - /* nonce: */ nonce, - /* gasPrice: */ gasPrice, - /* gasLimit: */ gasLimit, + auto transaction = TransactionNonTyped::buildNativeTransfer( + nonce, gasPrice, gasLimit, /* to: */ toAddress, /* amount: */ load(input.transaction().transfer().amount()), - /* optionalTransaction: */ Data(input.transaction().transfer().data().begin(), input.transaction().transfer().data().end())); + /* optional data: */ Data(input.transaction().transfer().data().begin(), input.transaction().transfer().data().end())); return transaction; } case Proto::Transaction::kErc20Transfer: { Data tokenToAddress = addressStringToData(input.transaction().erc20_transfer().to()); - auto transaction = Transaction::buildERC20Transfer( - /* nonce: */ nonce, - /* gasPrice: */ gasPrice, - /* gasLimit: */ gasLimit, + auto transaction = TransactionNonTyped::buildERC20Transfer( + nonce, gasPrice, gasLimit, /* tokenContract: */ toAddress, /* toAddress */ tokenToAddress, /* amount: */ load(input.transaction().erc20_transfer().amount())); @@ -119,10 +115,8 @@ Transaction Signer::build(const Proto::SigningInput &input) { case Proto::Transaction::kErc20Approve: { Data spenderAddress = addressStringToData(input.transaction().erc20_approve().spender()); - auto transaction = Transaction::buildERC20Approve( - /* nonce: */ nonce, - /* gasPrice: */ gasPrice, - /* gasLimit: */ gasLimit, + auto transaction = TransactionNonTyped::buildERC20Approve( + nonce, gasPrice, gasLimit, /* tokenContract: */ toAddress, /* toAddress */ spenderAddress, /* amount: */ load(input.transaction().erc20_approve().amount())); @@ -133,10 +127,8 @@ Transaction Signer::build(const Proto::SigningInput &input) { { Data tokenToAddress = addressStringToData(input.transaction().erc721_transfer().to()); Data tokenFromAddress = addressStringToData(input.transaction().erc721_transfer().from()); - auto transaction = Transaction::buildERC721Transfer( - /* nonce: */ nonce, - /* gasPrice: */ gasPrice, - /* gasLimit: */ gasLimit, + auto transaction = TransactionNonTyped::buildERC721Transfer( + nonce, gasPrice, gasLimit, /* tokenContract: */ toAddress, /* fromAddress: */ tokenFromAddress, /* toAddress */ tokenToAddress, @@ -148,10 +140,8 @@ Transaction Signer::build(const Proto::SigningInput &input) { { Data tokenToAddress = addressStringToData(input.transaction().erc1155_transfer().to()); Data tokenFromAddress = addressStringToData(input.transaction().erc1155_transfer().from()); - auto transaction = Transaction::buildERC1155Transfer( - /* nonce: */ nonce, - /* gasPrice: */ gasPrice, - /* gasLimit: */ gasLimit, + auto transaction = TransactionNonTyped::buildERC1155Transfer( + nonce, gasPrice, gasLimit, /* tokenContract: */ toAddress, /* fromAddress: */ tokenFromAddress, /* toAddress */ tokenToAddress, @@ -165,10 +155,8 @@ Transaction Signer::build(const Proto::SigningInput &input) { case Proto::Transaction::kContractGeneric: default: { - auto transaction = Transaction( - /* nonce: */ nonce, - /* gasPrice: */ gasPrice, - /* gasLimit: */ gasLimit, + auto transaction = TransactionNonTyped::buildNativeTransfer( + nonce, gasPrice, gasLimit, /* to: */ toAddress, /* amount: */ load(input.transaction().contract_generic().amount()), /* transaction: */ Data(input.transaction().contract_generic().data().begin(), input.transaction().contract_generic().data().end())); @@ -177,25 +165,7 @@ Transaction Signer::build(const Proto::SigningInput &input) { } } -void Signer::sign(const PrivateKey &privateKey, Transaction &transaction) const noexcept { - auto hash = this->hash(transaction); - auto tuple = Signer::sign(chainID, privateKey, hash); - - transaction.r = std::get<0>(tuple); - transaction.s = std::get<1>(tuple); - transaction.v = std::get<2>(tuple); -} - -Data Signer::hash(const Transaction &transaction) const noexcept { - auto encoded = Data(); - append(encoded, RLP::encode(transaction.nonce)); - append(encoded, RLP::encode(transaction.gasPrice)); - append(encoded, RLP::encode(transaction.gasLimit)); - append(encoded, RLP::encode(transaction.to)); - append(encoded, RLP::encode(transaction.amount)); - append(encoded, RLP::encode(transaction.payload)); - append(encoded, RLP::encode(chainID)); - append(encoded, RLP::encode(0)); - append(encoded, RLP::encode(0)); - return Hash::keccak256(RLP::encodeList(encoded)); +Signature Signer::sign(const PrivateKey& privateKey, const uint256_t& chainID, std::shared_ptr transaction) noexcept { + auto preHash = transaction->preHash(chainID); + return Signer::sign(privateKey, chainID, preHash); } diff --git a/src/Ethereum/Signer.h b/src/Ethereum/Signer.h index b041b64417f..c717f0651de 100644 --- a/src/Ethereum/Signer.h +++ b/src/Ethereum/Signer.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2021 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -16,8 +16,8 @@ #include #include -#include #include +#include namespace TW::Ethereum { @@ -30,33 +30,25 @@ class Signer { static std::string signJSON(const std::string& json, const Data& key); public: - uint256_t chainID; - - /// Initializes a signer with a chain identifier. - explicit Signer(uint256_t chainID) : chainID(std::move(chainID)) {} + Signer() = delete; /// Signs the given transaction. - void sign(const PrivateKey &privateKey, Transaction &transaction) const noexcept; + static Signature sign(const PrivateKey& privateKey, const uint256_t& chainID, std::shared_ptr transaction) noexcept; public: /// build Transaction from signing input - static Transaction build(const Proto::SigningInput &input); + static std::shared_ptr build(const Proto::SigningInput& input) { return buildNonTyped(input); } + static std::shared_ptr buildNonTyped(const Proto::SigningInput& input); /// Signs a hash with the given private key for the given chain identifier. /// /// @returns the r, s, and v values of the transaction signature - static std::tuple - sign(const uint256_t &chainID, const PrivateKey &privateKey, const Data& hash) noexcept; + static Signature sign(const PrivateKey& privateKey, const uint256_t& chainID, const Data& hash) noexcept; /// R, S, and V values for the given chain identifier and signature. /// /// @returns the r, s, and v values of the transaction signature - static std::tuple values(const uint256_t &chainID, - const Data& signature) noexcept; - - protected: - /// Computes the transaction hash. - Data hash(const Transaction &transaction) const noexcept; + static Signature valuesRSV(const uint256_t& chainID, const Data& signature) noexcept; }; } // namespace TW::Ethereum diff --git a/src/Ethereum/Transaction.cpp b/src/Ethereum/Transaction.cpp index 7b09ec38fe6..ff5359aaae6 100644 --- a/src/Ethereum/Transaction.cpp +++ b/src/Ethereum/Transaction.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2021 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -8,32 +8,72 @@ #include "ABI/Function.h" #include "ABI/ParamBase.h" #include "ABI/ParamAddress.h" +#include "RLP.h" using namespace TW::Ethereum::ABI; using namespace TW::Ethereum; using namespace TW; -Transaction Transaction::buildERC20Transfer(uint256_t nonce, uint256_t gasPrice, uint256_t gasLimit, - const Data& tokenContract, const Data& toAddress, uint256_t amount) { - return Transaction(nonce, gasPrice, gasLimit, tokenContract, 0, buildERC20TransferCall(toAddress, amount)); + +std::shared_ptr TransactionNonTyped::buildNativeTransfer(const uint256_t& nonce, + const uint256_t& gasPrice, const uint256_t& gasLimit, + const Data& toAddress, const uint256_t& amount, const Data& data) { + return std::make_unique(nonce, gasPrice, gasLimit, toAddress, amount, data); +} + +std::shared_ptr TransactionNonTyped::buildERC20Transfer(const uint256_t& nonce, + const uint256_t& gasPrice, const uint256_t& gasLimit, + const Data& tokenContract, const Data& toAddress, const uint256_t& amount) { + return std::make_unique(nonce, gasPrice, gasLimit, tokenContract, 0, buildERC20TransferCall(toAddress, amount)); +} + +std::shared_ptr TransactionNonTyped::buildERC20Approve(const uint256_t& nonce, + const uint256_t& gasPrice, const uint256_t& gasLimit, + const Data& tokenContract, const Data& spenderAddress, const uint256_t& amount) { + return std::make_unique(nonce, gasPrice, gasLimit, tokenContract, 0, buildERC20ApproveCall(spenderAddress, amount)); +} + +std::shared_ptr TransactionNonTyped::buildERC721Transfer(const uint256_t& nonce, + const uint256_t& gasPrice, const uint256_t& gasLimit, + const Data& tokenContract, const Data& from, const Data& to, const uint256_t& tokenId) { + return std::make_unique(nonce, gasPrice, gasLimit, tokenContract, 0, buildERC721TransferFromCall(from, to, tokenId)); } -Transaction Transaction::buildERC20Approve(uint256_t nonce, uint256_t gasPrice, uint256_t gasLimit, - const Data& tokenContract, const Data& spenderAddress, uint256_t amount) { - return Transaction(nonce, gasPrice, gasLimit, tokenContract, 0, buildERC20ApproveCall(spenderAddress, amount)); +std::shared_ptr TransactionNonTyped::buildERC1155Transfer(const uint256_t& nonce, + const uint256_t& gasPrice, const uint256_t& gasLimit, + const Data& tokenContract, const Data& from, const Data& to, const uint256_t& tokenId, const uint256_t& value, const Data& data) { + return std::make_unique(nonce, gasPrice, gasLimit, tokenContract, 0, buildERC1155TransferFromCall(from, to, tokenId, value, data)); } -Transaction Transaction::buildERC721Transfer(uint256_t nonce, uint256_t gasPrice, uint256_t gasLimit, - const Data& tokenContract, const Data& from, const Data& to, uint256_t tokenId) { - return Transaction(nonce, gasPrice, gasLimit, tokenContract, 0, buildERC721TransferFromCall(from, to, tokenId)); +Data TransactionNonTyped::preHash(const uint256_t chainID) const { + Data encoded; + append(encoded, RLP::encode(nonce)); + append(encoded, RLP::encode(gasPrice)); + append(encoded, RLP::encode(gasLimit)); + append(encoded, RLP::encode(to)); + append(encoded, RLP::encode(amount)); + append(encoded, RLP::encode(payload)); + append(encoded, RLP::encode(chainID)); + append(encoded, RLP::encode(0)); + append(encoded, RLP::encode(0)); + return Hash::keccak256(RLP::encodeList(encoded)); } -Transaction Transaction::buildERC1155Transfer(uint256_t nonce, uint256_t gasPrice, uint256_t gasLimit, - const Data& tokenContract, const Data& from, const Data& to, uint256_t tokenId, uint256_t value, const Data& data) { - return Transaction(nonce, gasPrice, gasLimit, tokenContract, 0, buildERC1155TransferFromCall(from, to, tokenId, value, data)); +Data TransactionNonTyped::encoded(const Signature& signature, const uint256_t chainID) const { + Data encoded; + append(encoded, RLP::encode(nonce)); + append(encoded, RLP::encode(gasPrice)); + append(encoded, RLP::encode(gasLimit)); + append(encoded, RLP::encode(to)); + append(encoded, RLP::encode(amount)); + append(encoded, RLP::encode(payload)); + append(encoded, RLP::encode(signature.v)); + append(encoded, RLP::encode(signature.r)); + append(encoded, RLP::encode(signature.s)); + return RLP::encodeList(encoded); } -Data Transaction::buildERC20TransferCall(const Data& to, uint256_t amount) { +Data TransactionNonTyped::buildERC20TransferCall(const Data& to, const uint256_t& amount) { auto func = Function("transfer", std::vector>{ std::make_shared(to), std::make_shared(amount) @@ -43,7 +83,7 @@ Data Transaction::buildERC20TransferCall(const Data& to, uint256_t amount) { return payload; } -Data Transaction::buildERC20ApproveCall(const Data& spender, uint256_t amount) { +Data TransactionNonTyped::buildERC20ApproveCall(const Data& spender, const uint256_t& amount) { auto func = Function("approve", std::vector>{ std::make_shared(spender), std::make_shared(amount) @@ -53,7 +93,7 @@ Data Transaction::buildERC20ApproveCall(const Data& spender, uint256_t amount) { return payload; } -Data Transaction::buildERC721TransferFromCall(const Data& from, const Data& to, uint256_t tokenId) { +Data TransactionNonTyped::buildERC721TransferFromCall(const Data& from, const Data& to, const uint256_t& tokenId) { auto func = Function("transferFrom", std::vector>{ std::make_shared(from), std::make_shared(to), @@ -64,7 +104,7 @@ Data Transaction::buildERC721TransferFromCall(const Data& from, const Data& to, return payload; } -Data Transaction::buildERC1155TransferFromCall(const Data& from, const Data& to, uint256_t tokenId, uint256_t value, const Data& data) { +Data TransactionNonTyped::buildERC1155TransferFromCall(const Data& from, const Data& to, const uint256_t& tokenId, const uint256_t& value, const Data& data) { auto func = Function("safeTransferFrom", std::vector>{ std::make_shared(from), std::make_shared(to), diff --git a/src/Ethereum/Transaction.h b/src/Ethereum/Transaction.h index 1f8bb516f88..218c38fcb7c 100644 --- a/src/Ethereum/Transaction.h +++ b/src/Ethereum/Transaction.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2021 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,54 +9,107 @@ #include "Address.h" #include "../uint256.h" +#include + namespace TW::Ethereum { -class Transaction { +// Transactions can be: +// - Non-typed (legacy, pre-EIP2718) transactions: +// -- simple ETH transfer +// -- others with payload, function call, e.g. ERC20 transfer +// - Typed transactions (enveloped, EIP2718), with specific type and transaction payload + +/// R-S-V Signature values +struct Signature { +public: + uint256_t r, s, v; +}; + +/// Base class for all transactions. +/// Non-typed and various typed transactions derive from this. +class TransactionBase { public: uint256_t nonce; + Data payload; + +public: + TransactionBase(const uint256_t& nonce, const Data& payload): nonce(nonce), payload(payload) {} + virtual ~TransactionBase() {} + // pre-sign hash of the tx, for signing + virtual Data preHash(const uint256_t chainID) const = 0; + // encoded tx (signed) + virtual Data encoded(const Signature& signature, const uint256_t chainID) const = 0; + +protected: + TransactionBase() {} +}; + +/// Original transaction format, with no explicit type, legacy as pre-EIP2718 +class TransactionNonTyped: public TransactionBase { +public: uint256_t gasPrice; uint256_t gasLimit; // Public key hash (Address.bytes) Data to; uint256_t amount; - Data payload; - - // Signature values - uint256_t v = uint256_t(); - uint256_t r = uint256_t(); - uint256_t s = uint256_t(); // Factory methods + // Create a native transfer transaction + static std::shared_ptr buildNativeTransfer(const uint256_t& nonce, + const uint256_t& gasPrice, const uint256_t& gasLimit, + const Data& toAddress, const uint256_t& amount, const Data& data = {}); + // Create an ERC20 token transfer transaction - static Transaction buildERC20Transfer(uint256_t nonce, uint256_t gasPrice, uint256_t gasLimit, - const Data& tokenContract, const Data& toAddress, uint256_t amount); + static std::shared_ptr buildERC20Transfer(const uint256_t& nonce, + const uint256_t& gasPrice, const uint256_t& gasLimit, + const Data& tokenContract, const Data& toAddress, const uint256_t& amount); // Create an ERC20 approve transaction - static Transaction buildERC20Approve(uint256_t nonce, uint256_t gasPrice, uint256_t gasLimit, - const Data& tokenContract, const Data& spenderAddress, uint256_t amount); + static std::shared_ptr buildERC20Approve(const uint256_t& nonce, + const uint256_t& gasPrice, const uint256_t& gasLimit, + const Data& tokenContract, const Data& spenderAddress, const uint256_t& amount); // Create an ERC721 NFT transfer transaction - static Transaction buildERC721Transfer(uint256_t nonce, uint256_t gasPrice, uint256_t gasLimit, - const Data& tokenContract, const Data& from, const Data& to, uint256_t tokenId); + static std::shared_ptr buildERC721Transfer(const uint256_t& nonce, + const uint256_t& gasPrice, const uint256_t& gasLimit, + const Data& tokenContract, const Data& from, const Data& to, const uint256_t& tokenId); // Create an ERC1155 NFT transfer transaction - static Transaction buildERC1155Transfer(uint256_t nonce, uint256_t gasPrice, uint256_t gasLimit, - const Data& tokenContract, const Data& from, const Data& to, uint256_t tokenId, uint256_t value, const Data& data); + static std::shared_ptr buildERC1155Transfer(const uint256_t& nonce, + const uint256_t& gasPrice, const uint256_t& gasLimit, + const Data& tokenContract, const Data& from, const Data& to, const uint256_t& tokenId, const uint256_t& value, const Data& data); // Helpers for building contract calls - static Data buildERC20TransferCall(const Data& to, uint256_t amount); - static Data buildERC20ApproveCall(const Data& spender, uint256_t amount); - static Data buildERC721TransferFromCall(const Data& from, const Data& to, uint256_t tokenId); - static Data buildERC1155TransferFromCall(const Data& from, const Data& to, uint256_t tokenId, uint256_t value, const Data& data); + static Data buildERC20TransferCall(const Data& to, const uint256_t& amount); + static Data buildERC20ApproveCall(const Data& spender, const uint256_t& amount); + static Data buildERC721TransferFromCall(const Data& from, const Data& to, const uint256_t& tokenId); + static Data buildERC1155TransferFromCall(const Data& from, const Data& to, const uint256_t& tokenId, const uint256_t& value, const Data& data); + + virtual Data preHash(const uint256_t chainID) const; + virtual Data encoded(const Signature& signature, const uint256_t chainID) const; public: - Transaction(uint256_t nonce, uint256_t gasPrice, uint256_t gasLimit, const Data& to, uint256_t amount, const Data& payload = {}) - : nonce(std::move(nonce)) + TransactionNonTyped(const uint256_t& nonce, + const uint256_t& gasPrice, const uint256_t& gasLimit, + const Data& to, const uint256_t& amount, const Data& payload = {}) + : TransactionBase(nonce, payload) , gasPrice(std::move(gasPrice)) , gasLimit(std::move(gasLimit)) , to(std::move(to)) - , amount(std::move(amount)) - , payload(std::move(payload)) {} + , amount(std::move(amount)) {} +}; + +enum TransactionType: uint8_t { + OptionalAccessList = 0x01, +}; + +/// Base class for various typed transactions. +class TransactionTyped: public TransactionBase { +public: + // transaction type + TransactionType type; + + TransactionTyped(TransactionType type): type(type) {} }; } // namespace TW::Ethereum diff --git a/tests/BinanceSmartChain/SignerTests.cpp b/tests/BinanceSmartChain/SignerTests.cpp index ff7e7637c4c..b3721eb7eec 100644 --- a/tests/BinanceSmartChain/SignerTests.cpp +++ b/tests/BinanceSmartChain/SignerTests.cpp @@ -23,17 +23,12 @@ namespace TW::Binance { using namespace TW::Ethereum; using namespace TW::Ethereum::ABI; -class SignerExposed : public Signer { -public: - SignerExposed(boost::multiprecision::uint256_t chainID) : Signer(chainID) {} - using Signer::hash; -}; TEST(BinanceSmartChain, SignNativeTransfer) { // https://explorer.binance.org/smart-testnet/tx/0x6da28164f7b3bc255d749c3ae562e2a742be54c12bf1858b014cc2fe5700684e auto toAddress = parse_hex("0x31BE00EB1fc8e14A696DBC72f746ec3e95f49683"); - auto transaction = Transaction( + auto transaction = TransactionNonTyped::buildNativeTransfer( /* nonce: */ 0, /* gasPrice: */ 20000000000, /* gasLimit: */ 21000, @@ -43,10 +38,10 @@ TEST(BinanceSmartChain, SignNativeTransfer) { // addr: 0xB9F5771C27664bF2282D98E09D7F50cEc7cB01a7 mnemonic: isolate dismiss ... cruel note auto privateKey = PrivateKey(parse_hex("4f96ed80e9a7555a6f74b3d658afdd9c756b0a40d4ca30c42c2039eb449bb904")); - auto signer = SignerExposed(97); - signer.sign(privateKey, transaction); + uint256_t chainID = 97; + auto signature = Signer::sign(privateKey, chainID, transaction); - auto encoded = RLP::encode(transaction); + auto encoded = transaction->encoded(signature, chainID); ASSERT_EQ(hex(encoded), "f86c808504a817c8008252089431be00eb1fc8e14a696dbc72f746ec3e95f49683872386f26fc100008081e5a057806b486844c5d0b7b5ce34b289f4e8776aa1fe24a3311cef5053995c51050ca07697aa0695de27da817625df0e7e4c64b0ab22d9df30aec92299a7b380be8db7"); } diff --git a/tests/Ethereum/SignerTests.cpp b/tests/Ethereum/SignerTests.cpp index e77bed75d9c..ca2b075befc 100644 --- a/tests/Ethereum/SignerTests.cpp +++ b/tests/Ethereum/SignerTests.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2021 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -15,30 +15,43 @@ namespace TW::Ethereum { using boost::multiprecision::uint256_t; -class SignerExposed : public Signer { -public: - SignerExposed(boost::multiprecision::uint256_t chainID) : Signer(chainID) {} - using Signer::hash; -}; -TEST(EthereumSigner, Hash) { +TEST(EthereumTransaction, encodeTransactionNonTyped) { + auto transaction = TransactionNonTyped::buildERC20Transfer( + /* nonce: */ 0, + /* gasPrice: */ 42000000000, // 0x09c7652400 + /* gasLimit: */ 78009, // 130B9 + /* tokenContract: */ parse_hex("0x6b175474e89094c44da98b954eedeac495271d0f"), // DAI + /* toAddress: */ parse_hex("0x5322b34c88ed0691971bf52a7047448f0f4efc84"), + /* amount: */ 2000000000000000000 + ); + uint256_t dummyChain = 0x34; + auto dummySignature = Signature{0, 0, 0}; + + auto preHash = transaction->preHash(dummyChain); + EXPECT_EQ(hex(preHash), "b3525019dc367d3ecac48905f9a95ff3550c25a24823db765f92cae2dec7ebfd"); + + auto encoded = transaction->encoded(dummySignature, dummyChain); + EXPECT_EQ(hex(encoded), "f86a808509c7652400830130b9946b175474e89094c44da98b954eedeac495271d0f80b844a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec80000808080"); +} + +TEST(EthereumSigner, PreHash) { auto address = parse_hex("0x3535353535353535353535353535353535353535"); - auto transaction = Transaction( + auto transaction = TransactionNonTyped::buildNativeTransfer( /* nonce: */ 9, /* gasPrice: */ 20000000000, /* gasLimit: */ 21000, /* to: */ address, /* amount: */ 1000000000000000000 ); - auto signer = SignerExposed(1); - auto hash = signer.hash(transaction); + auto preHash = transaction->preHash(1); - ASSERT_EQ(hex(hash), "daf5a779ae972f972197303d7b574746c7ef83eadac0f2791ad23db92e4c8e53"); + ASSERT_EQ(hex(preHash), "daf5a779ae972f972197303d7b574746c7ef83eadac0f2791ad23db92e4c8e53"); } TEST(EthereumSigner, Sign) { auto address = parse_hex("0x3535353535353535353535353535353535353535"); - auto transaction = Transaction( + auto transaction = TransactionNonTyped::buildNativeTransfer( /* nonce: */ 9, /* gasPrice: */ 20000000000, /* gasLimit: */ 21000, @@ -47,16 +60,15 @@ TEST(EthereumSigner, Sign) { ); auto key = PrivateKey(parse_hex("0x4646464646464646464646464646464646464646464646464646464646464646")); - auto signer = SignerExposed(1); - signer.sign(key, transaction); + auto signature = Signer::sign(key, 1, transaction); - ASSERT_EQ(transaction.v, 37); - ASSERT_EQ(transaction.r, uint256_t("18515461264373351373200002665853028612451056578545711640558177340181847433846")); - ASSERT_EQ(transaction.s, uint256_t("46948507304638947509940763649030358759909902576025900602547168820602576006531")); + ASSERT_EQ(signature.v, 37); + ASSERT_EQ(signature.r, uint256_t("18515461264373351373200002665853028612451056578545711640558177340181847433846")); + ASSERT_EQ(signature.s, uint256_t("46948507304638947509940763649030358759909902576025900602547168820602576006531")); } TEST(EthereumSigner, SignERC20Transfer) { - auto transaction = Transaction::buildERC20Transfer( + auto transaction = TransactionNonTyped::buildERC20Transfer( /* nonce: */ 0, /* gasPrice: */ 42000000000, // 0x09c7652400 /* gasLimit: */ 78009, // 130B9 @@ -66,12 +78,11 @@ TEST(EthereumSigner, SignERC20Transfer) { ); auto key = PrivateKey(parse_hex("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151")); - auto signer = SignerExposed(1); - signer.sign(key, transaction); + auto signature = Signer::sign(key, 1, transaction); - ASSERT_EQ(transaction.v, 37); - ASSERT_EQ(hex(TW::store(transaction.r)), "724c62ad4fbf47346b02de06e603e013f26f26b56fdc0be7ba3d6273401d98ce"); - ASSERT_EQ(hex(TW::store(transaction.s)), "032131cae15da7ddcda66963e8bef51ca0d9962bfef0547d3f02597a4a58c931"); + ASSERT_EQ(signature.v, 37); + ASSERT_EQ(hex(TW::store(signature.r)), "724c62ad4fbf47346b02de06e603e013f26f26b56fdc0be7ba3d6273401d98ce"); + ASSERT_EQ(hex(TW::store(signature.s)), "032131cae15da7ddcda66963e8bef51ca0d9962bfef0547d3f02597a4a58c931"); } } // namespace TW::Ethereum