Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Eth Transaction for EIP2718 #1354

Merged
merged 20 commits into from
Jul 7, 2021
Merged
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
16 changes: 1 addition & 15 deletions src/Ethereum/RLP.cpp
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand Down
5 changes: 1 addition & 4 deletions src/Ethereum/RLP.h
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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;

Expand Down
92 changes: 31 additions & 61 deletions src/Ethereum/Signer.cpp
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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&) {
Expand All @@ -49,8 +50,7 @@ std::string Signer::signJSON(const std::string& json, const Data& key) {
return hex(output.encoded());
}

std::tuple<uint256_t, uint256_t, uint256_t> 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);
Expand All @@ -64,13 +64,12 @@ std::tuple<uint256_t, uint256_t, uint256_t> Signer::values(const uint256_t &chai
} else {
newV = v;
}
return std::make_tuple(r, s, newV);
return Signature{r, s, newV};
}

std::tuple<uint256_t, uint256_t, uint256_t>
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
Expand All @@ -85,31 +84,28 @@ Data addressStringToData(const std::string& asString) {
return asData;
}

Transaction Signer::build(const Proto::SigningInput &input) {
std::shared_ptr<TransactionNonTyped> 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()));
Expand All @@ -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()));
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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()));
Expand All @@ -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<TransactionBase> transaction) noexcept {
auto preHash = transaction->preHash(chainID);
return Signer::sign(privateKey, chainID, preHash);
}
24 changes: 8 additions & 16 deletions src/Ethereum/Signer.h
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -16,8 +16,8 @@

#include <boost/multiprecision/cpp_int.hpp>
#include <cstdint>
#include <tuple>
#include <vector>
#include <memory>

namespace TW::Ethereum {

Expand All @@ -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<TransactionBase> transaction) noexcept;

public:
/// build Transaction from signing input
static Transaction build(const Proto::SigningInput &input);
static std::shared_ptr<TransactionBase> build(const Proto::SigningInput& input) { return buildNonTyped(input); }
static std::shared_ptr<TransactionNonTyped> 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<uint256_t, uint256_t, uint256_t>
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<uint256_t, uint256_t, uint256_t> 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
Expand Down
Loading