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 "TransactionBuilder.h"
8

9
#include "Actor.h"
10
#include "Encryption.h"
11
#include "NewFundsRequest.h"
12
#include "Signer.h"
13

14
#include "../HexCoding.h"
15

16
#include <nlohmann/json.hpp>
17

18
#include <ctime>
19

20
namespace TW::FIO {
21

22
using namespace TW;
23
using namespace std;
24
using json = nlohmann::json;
25

26

27
/// Internal helper
28 1
ChainParams getChainParams(const Proto::SigningInput& input) {
29 1
    return ChainParams{
30 1
        TW::data(input.chain_params().chain_id()),
31 1
        input.chain_params().head_block_number(),
32 1
        input.chain_params().ref_block_prefix()
33
    };
34
}
35

36 1
bool TransactionBuilder::expirySetDefaultIfNeeded(uint32_t& expiryTime) {
37 1
    if (expiryTime != 0) { return false; } // no change
38
    // fill based on current time 
39 1
    expiryTime = (uint32_t)time(nullptr) + ExpirySeconds;
40 1
    return true;
41
}
42

43 1
string TransactionBuilder::sign(Proto::SigningInput in) {
44 1
    PrivateKey privateKey(in.private_key());
45 1
    PublicKey publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1);
46 1
    Address owner(publicKey);
47
    
48 1
    string json;
49 1
    if (in.action().has_register_fio_address_message()) {
50 1
        const auto action = in.action().register_fio_address_message();
51 1
        json = TransactionBuilder::createRegisterFioAddress(owner, privateKey, 
52 1
            in.action().register_fio_address_message().fio_address(),
53 1
            getChainParams(in), action.fee(), in.tpid(), in.expiry());
54 1
    } else if (in.action().has_add_pub_address_message()) {
55 1
        const auto action = in.action().add_pub_address_message();
56
        // process addresses
57 1
        std::vector<std::pair<std::string, std::string>> addresses;
58 1
        for (int i = 0; i < action.public_addresses_size(); ++i) {
59 1
            addresses.push_back(std::make_pair(action.public_addresses(i).coin_symbol(), action.public_addresses(i).address()));
60
        }
61 1
        json = TransactionBuilder::createAddPubAddress(owner, privateKey,
62 1
            action.fio_address(), addresses, 
63 1
            getChainParams(in), action.fee(), in.tpid(), in.expiry());
64 1
    } else if (in.action().has_transfer_message()) {
65 1
        const auto action = in.action().transfer_message();
66 1
        json = TransactionBuilder::createTransfer(owner, privateKey,
67 1
            action.payee_public_key(), action.amount(),
68 1
            getChainParams(in), action.fee(), in.tpid(), in.expiry());
69 1
    } else if (in.action().has_renew_fio_address_message()) {
70 1
        const auto action = in.action().renew_fio_address_message();
71 1
        json = TransactionBuilder::createRenewFioAddress(owner, privateKey,
72 1
            action.fio_address(),
73 1
            getChainParams(in), action.fee(), in.tpid(), in.expiry());
74 1
    } else if (in.action().has_new_funds_request_message()) {
75 1
        const auto action = in.action().new_funds_request_message();
76 1
        const auto content = action.content();
77 1
        json = TransactionBuilder::createNewFundsRequest(owner, privateKey,
78 1
            action.payer_fio_name(), action.payer_fio_address(), action.payee_fio_name(), content.payee_public_address(),
79 1
            content.amount(), content.coin_symbol(), content.memo(), content.hash(), content.offline_url(),
80 1
            getChainParams(in), action.fee(), in.tpid(), in.expiry(), Data());
81
    }
82 1
    return json;
83
}
84

85 1
string TransactionBuilder::createRegisterFioAddress(const Address& address, const PrivateKey& privateKey, 
86
    const string& fioName,
87
    const ChainParams& chainParams, uint64_t fee, const string& walletTpId, uint32_t expiryTime) {
88

89 1
    const auto apiName = "regaddress";
90

91 1
    string actor = Actor::actor(address);
92 1
    RegisterFioAddressData raData(fioName, address.string(), fee, walletTpId, actor);
93 1
    Data serData;
94 1
    raData.serialize(serData);
95
    
96 1
    Action action;
97 1
    action.account = ContractAddress;
98 1
    action.name = apiName;
99 1
    action.actionDataSer = serData;
100 1
    action.auth.authArray.push_back(Authorization{actor, AuthrizationActive});
101

102 1
    Transaction tx;
103 1
    expirySetDefaultIfNeeded(expiryTime);
104 1
    tx.set(expiryTime, chainParams);
105 1
    tx.actions.push_back(action);
106 1
    Data serTx;
107 1
    tx.serialize(serTx);
108

109 1
    return signAdnBuildTx(chainParams.chainId, serTx, privateKey);
110
}
111

112 1
string TransactionBuilder::createAddPubAddress(const Address& address, const PrivateKey& privateKey, const string& fioName,
113
    const vector<pair<string, string>>& pubAddresses,
114
    const ChainParams& chainParams, uint64_t fee, const string& walletTpId, uint32_t expiryTime) {
115

116 1
    const auto apiName = "addaddress";
117

118 1
    string actor = Actor::actor(address);
119
    // convert addresses to add chainCode -- set it equal to coinSymbol
120 1
    vector<PublicAddress> pubAddresses2;
121 1
    for (const auto& a: pubAddresses) {
122 1
        pubAddresses2.push_back(PublicAddress{a.first, a.first, a.second});
123
    }
124 1
    AddPubAddressData aaData(fioName, pubAddresses2, fee, walletTpId, actor);
125 1
    Data serData;
126 1
    aaData.serialize(serData);
127
    
128 1
    Action action;
129 1
    action.account = ContractAddress;
130 1
    action.name = apiName;
131 1
    action.actionDataSer = serData;
132 1
    action.auth.authArray.push_back(Authorization{actor, AuthrizationActive});
133

134 1
    Transaction tx;
135 1
    expirySetDefaultIfNeeded(expiryTime);
136 1
    tx.set(expiryTime, chainParams);
137 1
    tx.actions.push_back(action);
138 1
    Data serTx;
139 1
    tx.serialize(serTx);
140

141 1
    return signAdnBuildTx(chainParams.chainId, serTx, privateKey);
142
}
143

144 1
string TransactionBuilder::createTransfer(const Address& address, const PrivateKey& privateKey, 
145
        const string& payeePublicKey, uint64_t amount,
146
        const ChainParams& chainParams, uint64_t fee, const string& walletTpId, uint32_t expiryTime) {
147

148 1
    const auto apiName = "trnsfiopubky";
149

150 1
    string actor = Actor::actor(address);
151 1
    TransferData ttData(payeePublicKey, amount, fee, walletTpId, actor);
152 1
    Data serData;
153 1
    ttData.serialize(serData);
154
    
155 1
    Action action;
156 1
    action.account = ContractToken;
157 1
    action.name = apiName;
158 1
    action.actionDataSer = serData;
159 1
    action.auth.authArray.push_back(Authorization{actor, AuthrizationActive});
160

161 1
    Transaction tx;
162 1
    expirySetDefaultIfNeeded(expiryTime);
163 1
    tx.set(expiryTime, chainParams);
164 1
    tx.actions.push_back(action);
165 1
    Data serTx;
166 1
    tx.serialize(serTx);
167

168 1
    return signAdnBuildTx(chainParams.chainId, serTx, privateKey);
169
}
170

171 1
string TransactionBuilder::createRenewFioAddress(const Address& address, const PrivateKey& privateKey, 
172
    const string& fioName,
173
    const ChainParams& chainParams, uint64_t fee, const string& walletTpId, uint32_t expiryTime) {
174

175 1
    const auto apiName = "renewaddress";
176

177 1
    string actor = Actor::actor(address);
178 1
    RenewFioAddressData raData(fioName, fee, walletTpId, actor);
179 1
    Data serData;
180 1
    raData.serialize(serData);
181
    
182 1
    Action action;
183 1
    action.account = ContractAddress;
184 1
    action.name = apiName;
185 1
    action.actionDataSer = serData;
186 1
    action.auth.authArray.push_back(Authorization{actor, AuthrizationActive});
187

188 1
    Transaction tx;
189 1
    expirySetDefaultIfNeeded(expiryTime);
190 1
    tx.set(expiryTime, chainParams);
191 1
    tx.actions.push_back(action);
192 1
    Data serTx;
193 1
    tx.serialize(serTx);
194

195 1
    return signAdnBuildTx(chainParams.chainId, serTx, privateKey);
196
}
197

198 1
string TransactionBuilder::createNewFundsRequest(const Address& address, const PrivateKey& privateKey,
199
        const string& payerFioName, const string& payerFioAddress, const string& payeeFioName, const string& payeePublicAddress, 
200
        const string& amount, const string& coinSymbol, const string& memo, const string& hash, const string& offlineUrl,
201
        const ChainParams& chainParams, uint64_t fee, const string& walletTpId, uint32_t expiryTime,
202
        const Data& iv) {
203

204 1
    const auto apiName = "newfundsreq";
205

206
    // use coinSymbol for chainCode as well
207 1
    NewFundsContent newFundsContent { payeePublicAddress, amount, coinSymbol, coinSymbol, memo, hash, offlineUrl };
208
    // serialize and encrypt
209 1
    Data serContent;
210 1
    newFundsContent.serialize(serContent);
211

212 1
    Address payerAddress(payerFioAddress);
213 1
    PublicKey payerPublicKey = payerAddress.publicKey();
214
    // encrypt and encode
215 1
    const string encodedEncryptedContent = Encryption::encode(Encryption::encrypt(privateKey, payerPublicKey, serContent, iv));
216

217 1
    string actor = Actor::actor(address);
218 1
    NewFundsRequestData nfData(payerFioName, payeeFioName, encodedEncryptedContent, fee, walletTpId, actor);
219 1
    Data serData;
220 1
    nfData.serialize(serData);
221
    
222 1
    Action action;
223 1
    action.account = ContractPayRequest;
224 1
    action.name = apiName;
225 1
    action.actionDataSer = serData;
226 1
    action.auth.authArray.push_back(Authorization{actor, AuthrizationActive});
227

228 1
    Transaction tx;
229 1
    expirySetDefaultIfNeeded(expiryTime);
230 1
    tx.set(expiryTime, chainParams);
231 1
    tx.actions.push_back(action);
232 1
    Data serTx;
233 1
    tx.serialize(serTx);
234

235 1
    return signAdnBuildTx(chainParams.chainId, serTx, privateKey);
236
}
237

238 1
string TransactionBuilder::signAdnBuildTx(const Data& chainId, const Data& packedTx, const PrivateKey& privateKey) {
239
    // create signature
240 1
    Data sigBuf(chainId);
241 1
    append(sigBuf, packedTx);
242 1
    append(sigBuf, TW::Data(32)); // context_free
243 1
    string signature = Signer::signatureToBsase58(Signer::signData(privateKey, sigBuf));
244

245
    // Build json
246 1
    json tx = {
247 1
        {"signatures", json::array({signature})},
248 1
        {"compression", "none"},
249 1
        {"packed_context_free_data", ""},
250 1
        {"packed_trx", hex(packedTx)}
251
    };
252 1
    return tx.dump();
253
}
254

255
} // namespace TW::FIO

Read our documentation on viewing source code .

Loading