trustwallet / wallet-core
1
// Copyright © 2017-2020 Trust Wallet.
2
//
3
// This file is part of Trust. The full Trust copyright notice, including
4
// terms governing use, modification, and redistribution, is contained in the
5
// file LICENSE at the root of the source code distribution tree.
6

7
#include "Signer.h"
8
#include "HexCoding.h"
9
#include <google/protobuf/util/json_util.h>
10

11
using namespace TW;
12
using namespace TW::Ethereum;
13

14 1
Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept {
15
    try {
16 1
        auto signer = Signer(load(input.chain_id()));
17 1
        auto key = PrivateKey(Data(input.private_key().begin(), input.private_key().end()));
18 1
        auto transaction = Signer::build(input);
19

20 1
        signer.sign(key, transaction);
21

22 1
        auto output = Proto::SigningOutput();
23

24 1
        auto encoded = RLP::encode(transaction);
25 1
        output.set_encoded(encoded.data(), encoded.size());
26

27 1
        auto v = store(transaction.v);
28 1
        output.set_v(v.data(), v.size());
29

30 1
        auto r = store(transaction.r);
31 1
        output.set_r(r.data(), r.size());
32

33 1
        auto s = store(transaction.s);
34 1
        output.set_s(s.data(), s.size());
35

36 1
        output.set_data(transaction.payload.data(), transaction.payload.size());
37

38 1
        return output;
39 1
    } catch (std::exception&) {
40 1
        return Proto::SigningOutput();
41
    }
42
}
43

44 1
std::string Signer::signJSON(const std::string& json, const Data& key) {
45 1
    auto input = Proto::SigningInput();
46 1
    google::protobuf::util::JsonStringToMessage(json, &input);
47 1
    input.set_private_key(key.data(), key.size());
48 1
    auto output = Signer::sign(input);
49 1
    return hex(output.encoded());
50
}
51

52 1
std::tuple<uint256_t, uint256_t, uint256_t> Signer::values(const uint256_t &chainID,
53
                                                           const Data& signature) noexcept {
54 1
    boost::multiprecision::uint256_t r, s, v;
55 1
    import_bits(r, signature.begin(), signature.begin() + 32);
56 1
    import_bits(s, signature.begin() + 32, signature.begin() + 64);
57 1
    import_bits(v, signature.begin() + 64, signature.begin() + 65);
58 1
    v += 27;
59

60 1
    boost::multiprecision::uint256_t newV;
61 1
    if (chainID != 0) {
62 1
        import_bits(newV, signature.begin() + 64, signature.begin() + 65);
63 1
        newV += 35 + chainID + chainID;
64 1
    } else {
65 0
        newV = v;
66
    }
67 1
    return std::make_tuple(r, s, newV);
68
}
69

70
std::tuple<uint256_t, uint256_t, uint256_t>
71 1
Signer::sign(const uint256_t &chainID, const PrivateKey &privateKey, const Data& hash) noexcept {
72 1
    auto signature = privateKey.sign(hash, TWCurveSECP256k1);
73 1
    return values(chainID, signature);
74
}
75

76
// May throw
77 1
Data addressStringToData(const std::string& asString) {
78 1
    if (asString.empty()) {
79 1
        return {};
80
    }
81 1
    auto address = Address(asString);
82 1
    Data asData;
83 1
    asData.resize(20);
84 1
    std::copy(address.bytes.begin(), address.bytes.end(), asData.data());
85 1
    return asData;
86
}
87

88 1
Transaction Signer::build(const Proto::SigningInput &input) {
89 1
    Data toAddress = addressStringToData(input.to_address());
90 1
    uint256_t nonce = load(input.nonce());
91 1
    uint256_t gasPrice = load(input.gas_price());
92 1
    uint256_t gasLimit = load(input.gas_limit());
93 1
    switch (input.transaction().transaction_oneof_case()) {
94
        case Proto::Transaction::kTransfer:
95
            {
96 1
                auto transaction = Transaction(
97 1
                    /* nonce: */ nonce,
98 1
                    /* gasPrice: */ gasPrice,
99 1
                    /* gasLimit: */ gasLimit,
100
                    /* to: */ toAddress,
101 1
                    /* amount: */ load(input.transaction().transfer().amount()),
102 1
                    /* optionalTransaction: */ Data(input.transaction().transfer().data().begin(), input.transaction().transfer().data().end()));
103 1
                return transaction;
104
            }
105

106
        case Proto::Transaction::kErc20Transfer:
107
            {
108 1
                Data tokenToAddress = addressStringToData(input.transaction().erc20_transfer().to());
109 1
                auto transaction = Transaction::buildERC20Transfer(
110 1
                    /* nonce: */ nonce,
111 1
                    /* gasPrice: */ gasPrice,
112 1
                    /* gasLimit: */ gasLimit,
113
                    /* tokenContract: */ toAddress,
114
                    /* toAddress */ tokenToAddress,
115 1
                    /* amount: */ load(input.transaction().erc20_transfer().amount()));
116 1
                return transaction;
117
            }
118

119
        case Proto::Transaction::kErc20Approve:
120
            {
121 1
                Data spenderAddress = addressStringToData(input.transaction().erc20_approve().spender());
122 1
                auto transaction = Transaction::buildERC20Approve(
123 1
                    /* nonce: */ nonce,
124 1
                    /* gasPrice: */ gasPrice,
125 1
                    /* gasLimit: */ gasLimit,
126
                    /* tokenContract: */ toAddress,
127
                    /* toAddress */ spenderAddress,
128 1
                    /* amount: */ load(input.transaction().erc20_approve().amount()));
129 1
                return transaction;
130
            }
131

132
        case Proto::Transaction::kErc721Transfer:
133
            {
134 1
                Data tokenToAddress = addressStringToData(input.transaction().erc721_transfer().to());
135 1
                Data tokenFromAddress = addressStringToData(input.transaction().erc721_transfer().from());
136 1
                auto transaction = Transaction::buildERC721Transfer(
137 1
                    /* nonce: */ nonce,
138 1
                    /* gasPrice: */ gasPrice,
139 1
                    /* gasLimit: */ gasLimit,
140
                    /* tokenContract: */ toAddress,
141
                    /* fromAddress: */ tokenFromAddress,
142
                    /* toAddress */ tokenToAddress,
143 1
                    /* tokenId: */ load(input.transaction().erc721_transfer().token_id()));
144 1
                return transaction;
145
            }
146

147
        case Proto::Transaction::kErc1155Transfer:
148
            {
149 1
                Data tokenToAddress = addressStringToData(input.transaction().erc1155_transfer().to());
150 1
                Data tokenFromAddress = addressStringToData(input.transaction().erc1155_transfer().from());
151 1
                auto transaction = Transaction::buildERC1155Transfer(
152 1
                    /* nonce: */ nonce,
153 1
                    /* gasPrice: */ gasPrice,
154 1
                    /* gasLimit: */ gasLimit,
155
                    /* tokenContract: */ toAddress,
156
                    /* fromAddress: */ tokenFromAddress,
157
                    /* toAddress */ tokenToAddress,
158 1
                    /* tokenId: */ load(input.transaction().erc1155_transfer().token_id()),
159 1
                    /* value */ load(input.transaction().erc1155_transfer().value()),
160 1
                    /* data */ Data(input.transaction().erc1155_transfer().data().begin(), input.transaction().erc1155_transfer().data().end())
161
                );
162 1
                return transaction;
163
            }
164

165
        case Proto::Transaction::kContractGeneric:
166
        default:
167
            {
168 1
                auto transaction = Transaction(
169 1
                    /* nonce: */ nonce,
170 1
                    /* gasPrice: */ gasPrice,
171 1
                    /* gasLimit: */ gasLimit,
172
                    /* to: */ toAddress,
173 1
                    /* amount: */ load(input.transaction().contract_generic().amount()),
174 1
                    /* transaction: */ Data(input.transaction().contract_generic().data().begin(), input.transaction().contract_generic().data().end()));
175 1
                return transaction;
176
            }
177
    }
178
}
179

180 1
void Signer::sign(const PrivateKey &privateKey, Transaction &transaction) const noexcept {
181 1
    auto hash = this->hash(transaction);
182 1
    auto tuple = Signer::sign(chainID, privateKey, hash);
183

184 1
    transaction.r = std::get<0>(tuple);
185 1
    transaction.s = std::get<1>(tuple);
186 1
    transaction.v = std::get<2>(tuple);
187
}
188

189 1
Data Signer::hash(const Transaction &transaction) const noexcept {
190 1
    auto encoded = Data();
191 1
    append(encoded, RLP::encode(transaction.nonce));
192 1
    append(encoded, RLP::encode(transaction.gasPrice));
193 1
    append(encoded, RLP::encode(transaction.gasLimit));
194 1
    append(encoded, RLP::encode(transaction.to));
195 1
    append(encoded, RLP::encode(transaction.amount));
196 1
    append(encoded, RLP::encode(transaction.payload));
197 1
    append(encoded, RLP::encode(chainID));
198 1
    append(encoded, RLP::encode(0));
199 1
    append(encoded, RLP::encode(0));
200 1
    return Hash::keccak256(RLP::encodeList(encoded));
201
}

Read our documentation on viewing source code .

Loading