Skip to content

Commit

Permalink
Refactor Eth Transaction for EIP2718 (#1354)
Browse files Browse the repository at this point in the history
* Add new fields to Ethereum proto.

* Legacy transaction building.

* Typescript test update

* TS test fix

* Compiler warning fix (ios)

* Legacy and Enveloped transactions.

* lSigner.
Refactor Transaction

* AccessList generation test (wip)

* Cleanup, small refactor

* Signer build change, make it more generic.

* Copyright year

* SignatureRSV rename.

* Signer class is static now, no chainID memeber.

* Renames, Legacy

* Minor touches

* Rename, hash

* Minor reorg in sign()

Co-authored-by: Catenocrypt <[email protected]>
  • Loading branch information
optout21 and Catenocrypt authored Jul 7, 2021
1 parent cdc4e18 commit a03acbc
Show file tree
Hide file tree
Showing 8 changed files with 213 additions and 169 deletions.
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

0 comments on commit a03acbc

Please sign in to comment.