Electroneum
Loading...
Searching...
No Matches
wallet2.cpp
Go to the documentation of this file.
1// Copyrights(c) 2017-2021, The Electroneum Project
2// Copyrights(c) 2014-2019, The Monero Project
3//
4// All rights reserved.
5//
6// Redistribution and use in source and binary forms, with or without modification, are
7// permitted provided that the following conditions are met:
8//
9// 1. Redistributions of source code must retain the above copyright notice, this list of
10// conditions and the following disclaimer.
11//
12// 2. Redistributions in binary form must reproduce the above copyright notice, this list
13// of conditions and the following disclaimer in the documentation and/or other
14// materials provided with the distribution.
15//
16// 3. Neither the name of the copyright holder nor the names of its contributors may be
17// used to endorse or promote products derived from this software without specific
18// prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
21// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
28// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29//
30// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
31
32#include <numeric>
33#include <limits>
34#include <tuple>
35#include <boost/format.hpp>
36#include <boost/optional/optional.hpp>
37#include <boost/utility/value_init.hpp>
38#include <boost/algorithm/string/classification.hpp>
39#include <boost/algorithm/string/trim.hpp>
40#include <boost/algorithm/string/split.hpp>
41#include <boost/algorithm/string/join.hpp>
42#include <boost/asio/ip/address.hpp>
43#include <boost/range/adaptor/transformed.hpp>
44#include <boost/preprocessor/stringize.hpp>
45#include "include_base_utils.h"
47using namespace epee;
48
49#include "cryptonote_config.h"
50#include "wallet2.h"
53#include "misc_language.h"
55#include "multisig/multisig.h"
57#include "common/command_line.h"
58#include "common/threadpool.h"
59#include "profile_tools.h"
60#include "crypto/crypto.h"
65#include "common/i18n.h"
66#include "common/util.h"
68#include "rapidjson/document.h"
69#include "rapidjson/writer.h"
71#include "common/json_util.h"
72#include "memwipe.h"
73#include "common/base58.h"
74#include "common/combinator.h"
75#include "common/dns_utils.h"
76#include "common/notify.h"
77#include "common/perf_timer.h"
78#include "ringct/rctSigs.h"
79#include "ringdb.h"
82#include "net/socks_connect.h"
83
84extern "C"
85{
86#include "crypto/keccak.h"
87#include "crypto/crypto-ops.h"
88}
89using namespace std;
90using namespace crypto;
91using namespace cryptonote;
92
93#undef ELECTRONEUM_DEFAULT_LOG_CATEGORY
94#define ELECTRONEUM_DEFAULT_LOG_CATEGORY "wallet.wallet2"
95
96// used to choose when to stop adding outputs to a tx
97#define APPROXIMATE_INPUT_BYTES 80
98
99// used to target a given block weight (additional outputs may be added on top to build fee)
100#define TX_WEIGHT_TARGET(bytes) (bytes*2/3)
101
102// arbitrary, used to generate different hashes from the same input
103#define CHACHA8_KEY_TAIL 0x8c
104#define CACHE_KEY_TAIL 0x8d
105
106// Magic number 004 means these payloads are encrypted.
107#define UNSIGNED_TX_PREFIX "Electroneum unsigned tx set\004"
108#define SIGNED_TX_PREFIX "Electroneum signed tx set\004"
109#define MULTISIG_UNSIGNED_TX_PREFIX "Electroneum multisig unsigned tx set\001"
110
111#define RECENT_OUTPUT_RATIO (0.5) // 50% of outputs are from the recent zone
112#define RECENT_OUTPUT_DAYS (1.8) // last 1.8 day makes up the recent zone (taken from electroneumlink.pdf, Miller et al)
113#define RECENT_OUTPUT_ZONE ((time_t)(RECENT_OUTPUT_DAYS * 86400))
114#define RECENT_OUTPUT_BLOCKS (RECENT_OUTPUT_DAYS * 720)
115
116#define FEE_ESTIMATE_GRACE_BLOCKS 10 // estimate fee valid for that many blocks
117
118#define SECOND_OUTPUT_RELATEDNESS_THRESHOLD 0.0f
119
120#define SUBADDRESS_LOOKAHEAD_MAJOR 50
121#define SUBADDRESS_LOOKAHEAD_MINOR 200
122
123#define KEY_IMAGE_EXPORT_FILE_MAGIC "Electroneum key image export\002"
124
125#define MULTISIG_EXPORT_FILE_MAGIC "Electroneum multisig export\001"
126
127#define SEGREGATION_FORK_HEIGHT 99999999
128#define TESTNET_SEGREGATION_FORK_HEIGHT 99999999
129#define STAGENET_SEGREGATION_FORK_HEIGHT 99999999
130#define SEGREGATION_FORK_VICINITY 1500 /* blocks */
131
132#define FIRST_REFRESH_GRANULARITY 1024
133
134#define GAMMA_SHAPE 19.28
135#define GAMMA_SCALE (1/1.61)
136
137#define DEFAULT_MIN_OUTPUT_COUNT 5
138#define DEFAULT_MIN_OUTPUT_VALUE (2*COIN)
139
140#define OUTPUT_EXPORT_FILE_MAGIC "Electroneum output export\003"
141
142static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1";
143static const std::string MULTISIG_EXTRA_INFO_MAGIC = "MultisigxV1";
144
145namespace
146{
147 std::string get_default_ringdb_path()
148 {
149 boost::filesystem::path dir = tools::get_default_data_dir();
150 // remove .electroneum, replace with .shared-ringdb
151 dir = dir.remove_filename();
152 dir /= ".shared-ringdb";
153 return dir.string();
154 }
155
156 std::string pack_multisignature_keys(const std::string& prefix, const std::vector<crypto::public_key>& keys, const crypto::secret_key& signer_secret_key)
157 {
158 std::string data;
159 crypto::public_key signer;
160 CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(signer_secret_key, signer), "Failed to derive public spend key");
161 data += std::string((const char *)&signer, sizeof(crypto::public_key));
162
163 for (const auto &key: keys)
164 {
165 data += std::string((const char *)&key, sizeof(crypto::public_key));
166 }
167
168 data.resize(data.size() + sizeof(crypto::signature));
169
171 crypto::cn_fast_hash(data.data(), data.size() - sizeof(crypto::signature), hash);
172 crypto::signature &signature = *(crypto::signature*)&data[data.size() - sizeof(crypto::signature)];
173 crypto::generate_signature(hash, signer, signer_secret_key, signature);
174
175 return MULTISIG_EXTRA_INFO_MAGIC + tools::base58::encode(data);
176 }
177
178 std::vector<crypto::public_key> secret_keys_to_public_keys(const std::vector<crypto::secret_key>& keys)
179 {
180 std::vector<crypto::public_key> public_keys;
181 public_keys.reserve(keys.size());
182
183 std::transform(keys.begin(), keys.end(), std::back_inserter(public_keys), [] (const crypto::secret_key& k) -> crypto::public_key {
184 crypto::public_key p;
185 CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(k, p), "Failed to derive public spend key");
186 return p;
187 });
188
189 return public_keys;
190 }
191
192 bool keys_intersect(const std::unordered_set<crypto::public_key>& s1, const std::unordered_set<crypto::public_key>& s2)
193 {
194 if (s1.empty() || s2.empty())
195 return false;
196
197 for (const auto& e: s1)
198 {
199 if (s2.find(e) != s2.end())
200 return true;
201 }
202
203 return false;
204 }
205
206 void add_reason(std::string &reasons, const char *reason)
207 {
208 if (!reasons.empty())
209 reasons += ", ";
210 reasons += reason;
211 }
212
213 std::string get_text_reason(const cryptonote::COMMAND_RPC_SEND_RAW_TX::response &res)
214 {
215 std::string reason;
216 if (res.low_mixin)
217 add_reason(reason, "bad ring size");
218 if (res.double_spend)
219 add_reason(reason, "double spend");
220 if (res.invalid_input)
221 add_reason(reason, "invalid input");
222 if (res.invalid_output)
223 add_reason(reason, "invalid output");
224 if (res.too_big)
225 add_reason(reason, "too big");
226 if (res.overspend)
227 add_reason(reason, "overspend");
228 if (res.fee_too_low)
229 add_reason(reason, "fee too low");
230 if (res.not_rct)
231 add_reason(reason, "tx is not ringct");
232 if (res.sanity_check_failed)
233 add_reason(reason, "tx sanity check failed");
234 if (res.not_relayed)
235 add_reason(reason, "tx was not relayed");
236 return reason;
237 }
238}
239
240namespace
241{
242// Create on-demand to prevent static initialization order fiasco issues.
243struct options {
244 const command_line::arg_descriptor<std::string> daemon_address = {"daemon-address", tools::wallet2::tr("Use daemon instance at <host>:<port>"), ""};
245 const command_line::arg_descriptor<std::string> daemon_host = {"daemon-host", tools::wallet2::tr("Use daemon instance at host <arg> instead of localhost"), ""};
246 const command_line::arg_descriptor<std::string> proxy = {"proxy", tools::wallet2::tr("[<ip>:]<port> socks proxy to use for daemon connections"), {}, true};
247 const command_line::arg_descriptor<bool> trusted_daemon = {"trusted-daemon", tools::wallet2::tr("Enable commands which rely on a trusted daemon"), false};
248 const command_line::arg_descriptor<bool> untrusted_daemon = {"untrusted-daemon", tools::wallet2::tr("Disable commands which rely on a trusted daemon"), false};
249 const command_line::arg_descriptor<std::string> password = {"password", tools::wallet2::tr("Wallet password (For a wallet without a password use \"\")"), "", true};
250 const command_line::arg_descriptor<std::string> password_file = {"password-file", tools::wallet2::tr("Wallet password file"), "", true};
251 const command_line::arg_descriptor<int> daemon_port = {"daemon-port", tools::wallet2::tr("Use daemon instance at port <arg> instead of 26968"), 0};
252 const command_line::arg_descriptor<std::string> daemon_login = {"daemon-login", tools::wallet2::tr("Specify username[:password] for daemon RPC client"), "", true};
253 const command_line::arg_descriptor<std::string> daemon_ssl = {"daemon-ssl", tools::wallet2::tr("Enable SSL on daemon RPC connections: enabled|disabled|autodetect"), "autodetect"};
254 const command_line::arg_descriptor<std::string> daemon_ssl_private_key = {"daemon-ssl-private-key", tools::wallet2::tr("Path to a PEM format private key"), ""};
255 const command_line::arg_descriptor<std::string> daemon_ssl_certificate = {"daemon-ssl-certificate", tools::wallet2::tr("Path to a PEM format certificate"), ""};
256 const command_line::arg_descriptor<std::string> daemon_ssl_ca_certificates = {"daemon-ssl-ca-certificates", tools::wallet2::tr("Path to file containing concatenated PEM format certificate(s) to replace system CA(s).")};
257 const command_line::arg_descriptor<std::vector<std::string>> daemon_ssl_allowed_fingerprints = {"daemon-ssl-allowed-fingerprints", tools::wallet2::tr("List of valid fingerprints of allowed RPC servers")};
258 const command_line::arg_descriptor<bool> daemon_ssl_allow_any_cert = {"daemon-ssl-allow-any-cert", tools::wallet2::tr("Allow any SSL certificate from the daemon"), false};
259 const command_line::arg_descriptor<bool> daemon_ssl_allow_chained = {"daemon-ssl-allow-chained", tools::wallet2::tr("Allow user (via --daemon-ssl-ca-certificates) chain certificates"), false};
260 const command_line::arg_descriptor<bool> testnet = {"testnet", tools::wallet2::tr("For testnet. Daemon must also be launched with --testnet flag"), false};
261 const command_line::arg_descriptor<bool> stagenet = {"stagenet", tools::wallet2::tr("For stagenet. Daemon must also be launched with --stagenet flag"), false};
262 const command_line::arg_descriptor<uint64_t> fallback_to_pow_checkpoint_height = {"fallback-to-pow-checkpoint-height", tools::wallet2::tr("Warning: This is to set the height for a custom checkpoint in the event of PoW fallback. Do not use in normal circumstances. See docs for details "), 0, false};
263 const command_line::arg_descriptor<std::string> fallback_to_pow_checkpoint_hash = {"fallback-to-pow-checkpoint-hash", tools::wallet2::tr("Warning: This is to set the hash for a custom checkpoint in the event of PoW fallback. Do not use in normal circumstances. See docs for details "), "", false};
264 const command_line::arg_descriptor<std::string, false, true, 2> shared_ringdb_dir = {
265 "shared-ringdb-dir", tools::wallet2::tr("Set shared ring database path"),
266 get_default_ringdb_path(),
267 {{ &testnet, &stagenet }},
268 [](std::array<bool, 2> testnet_stagenet, bool defaulted, std::string val)->std::string {
269 if (testnet_stagenet[0])
270 return (boost::filesystem::path(val) / "testnet").string();
271 else if (testnet_stagenet[1])
272 return (boost::filesystem::path(val) / "stagenet").string();
273 return val;
274 }
275 };
276 const command_line::arg_descriptor<uint64_t> kdf_rounds = {"kdf-rounds", tools::wallet2::tr("Number of rounds for the key derivation function"), 1};
277 const command_line::arg_descriptor<std::string> hw_device = {"hw-device", tools::wallet2::tr("HW device to use"), ""};
278 const command_line::arg_descriptor<std::string> hw_device_derivation_path = {"hw-device-deriv-path", tools::wallet2::tr("HW device wallet derivation path (e.g., SLIP-10)"), ""};
279 const command_line::arg_descriptor<std::string> tx_notify = { "tx-notify" , "Run a program for each new incoming transaction, '%s' will be replaced by the transaction hash" , "" };
280 const command_line::arg_descriptor<bool> no_dns = {"no-dns", tools::wallet2::tr("Do not use DNS"), false};
281 const command_line::arg_descriptor<bool> offline = {"offline", tools::wallet2::tr("Do not connect to a daemon, nor use DNS"), false};
282
283 const command_line::arg_descriptor<bool> restricted = {"restricted-rpc", tools::wallet2::tr("Restricts to view-only commands"), false};
284 const command_line::arg_descriptor<std::string> data_dir = {"data-dir", tools::wallet2::tr("Path to blockchain db"), ""};
285};
286
287void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file, std::string &mms_file)
288{
289 keys_file = file_path;
290 wallet_file = file_path;
291 boost::system::error_code e;
292 if(string_tools::get_extension(keys_file) == "keys")
293 {//provided keys file name
294 wallet_file = string_tools::cut_off_extension(wallet_file);
295 }else
296 {//provided wallet file name
297 keys_file += ".keys";
298 }
299 mms_file = file_path + ".mms";
300}
301
302uint64_t calculate_fee(uint64_t fee_per_kb, size_t bytes, uint64_t fee_multiplier)
303{
304 uint64_t kB = (bytes + 1023) / 1024;
305 return kB * fee_per_kb * fee_multiplier;
306}
307
308uint64_t calculate_fee_from_weight(uint64_t base_fee, uint64_t weight, uint64_t fee_multiplier, uint64_t fee_quantization_mask)
309{
310 uint64_t fee = weight * base_fee * fee_multiplier;
311 fee = (fee + fee_quantization_mask - 1) / fee_quantization_mask * fee_quantization_mask;
312 return fee;
313}
314
315std::string get_weight_string(size_t weight)
316{
317 return std::to_string(weight) + " weight";
318}
319
320std::string get_weight_string(const cryptonote::transaction &tx, size_t blob_size)
321{
322 return get_weight_string(get_transaction_weight(tx, blob_size));
323}
324
325std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variables_map& vm, bool unattended, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
326{
327 namespace ip = boost::asio::ip;
328
329 const bool testnet = command_line::get_arg(vm, opts.testnet);
330 const bool stagenet = command_line::get_arg(vm, opts.stagenet);
331 const network_type nettype = testnet ? TESTNET : stagenet ? STAGENET : MAINNET;
332 const uint64_t kdf_rounds = command_line::get_arg(vm, opts.kdf_rounds);
333 THROW_WALLET_EXCEPTION_IF(kdf_rounds == 0, tools::error::wallet_internal_error, "KDF rounds must not be 0");
334
335 const bool use_proxy = command_line::has_arg(vm, opts.proxy);
336 auto daemon_address = command_line::get_arg(vm, opts.daemon_address);
337 auto daemon_host = command_line::get_arg(vm, opts.daemon_host);
338 auto daemon_port = command_line::get_arg(vm, opts.daemon_port);
339 auto device_name = command_line::get_arg(vm, opts.hw_device);
340 auto device_derivation_path = command_line::get_arg(vm, opts.hw_device_derivation_path);
341 auto daemon_ssl_private_key = command_line::get_arg(vm, opts.daemon_ssl_private_key);
342 auto daemon_ssl_certificate = command_line::get_arg(vm, opts.daemon_ssl_certificate);
343 auto daemon_ssl_ca_file = command_line::get_arg(vm, opts.daemon_ssl_ca_certificates);
344 auto daemon_ssl_allowed_fingerprints = command_line::get_arg(vm, opts.daemon_ssl_allowed_fingerprints);
345 auto daemon_ssl_allow_any_cert = command_line::get_arg(vm, opts.daemon_ssl_allow_any_cert);
346 auto daemon_ssl = command_line::get_arg(vm, opts.daemon_ssl);
347 auto fallback_to_pow_checkpoint_height = command_line::get_arg(vm, opts.fallback_to_pow_checkpoint_height);
348 auto fallback_to_pow_checkpoint_hash = command_line::get_arg(vm, opts.fallback_to_pow_checkpoint_hash);
349
350 // user specified CA file or fingeprints implies enabled SSL by default
352 if (command_line::get_arg(vm, opts.daemon_ssl_allow_any_cert))
354 else if (!daemon_ssl_ca_file.empty() || !daemon_ssl_allowed_fingerprints.empty())
355 {
356 std::vector<std::vector<uint8_t>> ssl_allowed_fingerprints{ daemon_ssl_allowed_fingerprints.size() };
357 std::transform(daemon_ssl_allowed_fingerprints.begin(), daemon_ssl_allowed_fingerprints.end(), ssl_allowed_fingerprints.begin(), epee::from_hex::vector);
358 for (const auto &fpr: ssl_allowed_fingerprints)
359 {
361 "SHA-256 fingerprint should be " BOOST_PP_STRINGIZE(SSL_FINGERPRINT_SIZE) " bytes long.");
362 }
363
364 ssl_options = epee::net_utils::ssl_options_t{
365 std::move(ssl_allowed_fingerprints), std::move(daemon_ssl_ca_file)
366 };
367
368 if (command_line::get_arg(vm, opts.daemon_ssl_allow_chained))
370 }
371
372 auto data_dir = command_line::get_arg(vm, opts.data_dir);
373
375 {
377 tools::wallet2::tr("Invalid argument for ") + std::string(opts.daemon_ssl.name));
378 }
379
381 std::move(daemon_ssl_private_key), std::move(daemon_ssl_certificate)
382 };
383
384 THROW_WALLET_EXCEPTION_IF(!daemon_address.empty() && !daemon_host.empty() && 0 != daemon_port,
385 tools::error::wallet_internal_error, tools::wallet2::tr("can't specify daemon host or port more than once"));
386
387 boost::optional<epee::net_utils::http::login> login{};
388 if (command_line::has_arg(vm, opts.daemon_login))
389 {
390 auto parsed = tools::login::parse(
391 command_line::get_arg(vm, opts.daemon_login), false, [password_prompter](bool verify) {
392 return password_prompter("Daemon client password", verify);
393 }
394 );
395 if (!parsed)
396 return nullptr;
397
398 login.emplace(std::move(parsed->username), std::move(parsed->password).password());
399 }
400
401 if (daemon_host.empty())
402 daemon_host = "localhost";
403
404 if (!daemon_port)
405 {
406 daemon_port = get_config(nettype).RPC_DEFAULT_PORT;
407 }
408
409 if (daemon_address.empty())
410 daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port);
411
412 {
413 const boost::string_ref real_daemon = boost::string_ref{daemon_address}.substr(0, daemon_address.rfind(':'));
414
415 /* If SSL or proxy is enabled, then a specific cert, CA or fingerprint must
416 be specified. This is specific to the wallet. */
417 const bool verification_required =
420
422 verification_required && !ssl_options.has_strong_verification(real_daemon),
424 tools::wallet2::tr("Enabling --") + std::string{use_proxy ? opts.proxy.name : opts.daemon_ssl.name} + tools::wallet2::tr(" requires --") +
425 opts.daemon_ssl_ca_certificates.name + tools::wallet2::tr(" or --") + opts.daemon_ssl_allowed_fingerprints.name + tools::wallet2::tr(" or use of a .onion/.i2p domain")
426 );
427 }
428
429 boost::asio::ip::tcp::endpoint proxy{};
430 if (use_proxy)
431 {
432 namespace ip = boost::asio::ip;
433
434 const auto proxy_address = command_line::get_arg(vm, opts.proxy);
435
436 boost::string_ref proxy_port{proxy_address};
437 boost::string_ref proxy_host = proxy_port.substr(0, proxy_port.rfind(":"));
438 if (proxy_port.size() == proxy_host.size())
439 proxy_host = "127.0.0.1";
440 else
441 proxy_port = proxy_port.substr(proxy_host.size() + 1);
442
443 uint16_t port_value = 0;
445 !epee::string_tools::get_xtype_from_string(port_value, std::string{proxy_port}),
447 std::string{"Invalid port specified for --"} + opts.proxy.name
448 );
449
450 boost::system::error_code error{};
451 proxy = ip::tcp::endpoint{ip::address::from_string(std::string{proxy_host}, error), port_value};
452 THROW_WALLET_EXCEPTION_IF(bool(error), tools::error::wallet_internal_error, std::string{"Invalid IP address specified for --"} + opts.proxy.name);
453 }
454
455 boost::optional<bool> trusted_daemon;
456 if (!command_line::is_arg_defaulted(vm, opts.trusted_daemon) || !command_line::is_arg_defaulted(vm, opts.untrusted_daemon))
457 trusted_daemon = command_line::get_arg(vm, opts.trusted_daemon) && !command_line::get_arg(vm, opts.untrusted_daemon);
458 THROW_WALLET_EXCEPTION_IF(!command_line::is_arg_defaulted(vm, opts.trusted_daemon) && !command_line::is_arg_defaulted(vm, opts.untrusted_daemon),
459 tools::error::wallet_internal_error, tools::wallet2::tr("--trusted-daemon and --untrusted-daemon are both seen, assuming untrusted"));
460
461 // set --trusted-daemon if local and not overridden
462 if (!trusted_daemon)
463 {
464 try
465 {
466 trusted_daemon = false;
468 {
469 MINFO(tools::wallet2::tr("Daemon is local, assuming trusted"));
470 trusted_daemon = true;
471 }
472 }
473 catch (const std::exception &e) { }
474 }
475
476 std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(nettype, kdf_rounds, unattended));
477 wallet->init(std::move(daemon_address), std::move(login), std::move(proxy), 0, *trusted_daemon, std::move(ssl_options), std::move(data_dir));
478 // Add the pow-fallback checkpoint if necessary
479 if(fallback_to_pow_checkpoint_hash != "" && fallback_to_pow_checkpoint_height != 0) {
480 wallet->add_checkpoint(fallback_to_pow_checkpoint_height, fallback_to_pow_checkpoint_hash);
481 }
482 boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir);
483 wallet->set_ring_database(ringdb_path.string());
484 wallet->get_message_store().set_options(vm);
485 wallet->device_name(device_name);
486 wallet->device_derivation_path(device_derivation_path);
487
488 if (command_line::get_arg(vm, opts.no_dns))
489 wallet->enable_dns(false);
490
491 if (command_line::get_arg(vm, opts.offline))
492 wallet->set_offline();
493
494 try
495 {
496 if (!command_line::is_arg_defaulted(vm, opts.tx_notify))
497 wallet->set_tx_notify(std::shared_ptr<tools::Notify>(new tools::Notify(command_line::get_arg(vm, opts.tx_notify).c_str())));
498 }
499 catch (const std::exception &e)
500 {
501 MERROR("Failed to parse tx notify spec: " << e.what());
502 }
503
504 return wallet;
505}
506
507boost::optional<tools::password_container> get_password(const boost::program_options::variables_map& vm, const options& opts, const std::function<boost::optional<tools::password_container>(const char*, bool)> &password_prompter, const bool verify)
508{
509 if (command_line::has_arg(vm, opts.password) && command_line::has_arg(vm, opts.password_file))
510 {
511 THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("can't specify more than one of --password and --password-file"));
512 }
513
514 if (command_line::has_arg(vm, opts.password))
515 {
516 return tools::password_container{command_line::get_arg(vm, opts.password)};
517 }
518
519 if (command_line::has_arg(vm, opts.password_file))
520 {
521 std::string password;
523 password);
524 THROW_WALLET_EXCEPTION_IF(!r, tools::error::wallet_internal_error, tools::wallet2::tr("the password file specified could not be read"));
525
526 // Remove line breaks the user might have inserted
527 boost::trim_right_if(password, boost::is_any_of("\r\n"));
528 return {tools::password_container{std::move(password)}};
529 }
530
531 THROW_WALLET_EXCEPTION_IF(!password_prompter, tools::error::wallet_internal_error, tools::wallet2::tr("no password specified; use --prompt-for-password to prompt for a password"));
532
533 return password_prompter(verify ? tools::wallet2::tr("Enter a new password for the wallet") : tools::wallet2::tr("Wallet password"), verify);
534}
535
536std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, bool unattended, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
537{
538 const bool testnet = command_line::get_arg(vm, opts.testnet);
539 const bool stagenet = command_line::get_arg(vm, opts.stagenet);
540 const network_type nettype = testnet ? TESTNET : stagenet ? STAGENET : MAINNET;
541
542 /* GET_FIELD_FROM_JSON_RETURN_ON_ERROR Is a generic macro that can return
543 false. Gcc will coerce this into unique_ptr(nullptr), but clang correctly
544 fails. This large wrapper is for the use of that macro */
545 std::unique_ptr<tools::wallet2> wallet;
546 epee::wipeable_string password;
547 const auto do_generate = [&]() -> bool {
548 std::string buf;
550 THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, std::string(tools::wallet2::tr("Failed to load file ")) + json_file);
551 return false;
552 }
553
554 rapidjson::Document json;
555 if (json.Parse(buf.c_str()).HasParseError()) {
557 return false;
558 }
559
560 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, version, unsigned, Uint, true, 0);
561 const int current_version = 1;
562 THROW_WALLET_EXCEPTION_IF(field_version > current_version, tools::error::wallet_internal_error,
563 ((boost::format(tools::wallet2::tr("Version %u too new, we can only grok up to %u")) % field_version % current_version)).str());
564
565 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, filename, std::string, String, true, std::string());
566
567 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, scan_from_height, uint64_t, Uint64, false, 0);
568 const bool recover = true;
569
570 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, password, std::string, String, false, std::string());
571
572 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, viewkey, std::string, String, false, std::string());
573 crypto::secret_key viewkey;
574 if (field_viewkey_found)
575 {
576 cryptonote::blobdata viewkey_data;
577 if(!epee::string_tools::parse_hexstr_to_binbuff(field_viewkey, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key))
578 {
580 }
581 viewkey = *reinterpret_cast<const crypto::secret_key*>(viewkey_data.data());
583 if (!crypto::secret_key_to_public_key(viewkey, pkey)) {
584 THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify view key secret key"));
585 }
586 }
587
588 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, spendkey, std::string, String, false, std::string());
590 if (field_spendkey_found)
591 {
592 cryptonote::blobdata spendkey_data;
593 if(!epee::string_tools::parse_hexstr_to_binbuff(field_spendkey, spendkey_data) || spendkey_data.size() != sizeof(crypto::secret_key))
594 {
595 THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to parse spend key secret key"));
596 }
597 spendkey = *reinterpret_cast<const crypto::secret_key*>(spendkey_data.data());
600 THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify spend key secret key"));
601 }
602 }
603
604 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, seed, std::string, String, false, std::string());
605 std::string old_language;
606 crypto::secret_key recovery_key;
607 bool restore_deterministic_wallet = false;
608 if (field_seed_found)
609 {
610 if (!crypto::ElectrumWords::words_to_bytes(field_seed, recovery_key, old_language))
611 {
612 THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("Electrum-style word list failed verification"));
613 }
614 restore_deterministic_wallet = true;
615
616 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, seed_passphrase, std::string, String, false, std::string());
617 if (field_seed_passphrase_found)
618 {
619 if (!field_seed_passphrase.empty())
620 recovery_key = cryptonote::decrypt_key(recovery_key, field_seed_passphrase);
621 }
622 }
623
624 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, address, std::string, String, false, std::string());
625
626 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, create_address_file, int, Int, false, false);
627 bool create_address_file = field_create_address_file;
628
629 // compatibility checks
630 if (!field_seed_found && !field_viewkey_found && !field_spendkey_found)
631 {
632 THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("At least one of either an Electrum-style word list, private view key, or private spend key must be specified"));
633 }
634 if (field_seed_found && (field_viewkey_found || field_spendkey_found))
635 {
636 THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("Both Electrum-style word list and private key(s) specified"));
637 }
638
639 // if an address was given, we check keys against it, and deduce the spend
640 // public key if it was not given
641 if (field_address_found)
642 {
644 if(!get_account_address_from_str(info, nettype, field_address))
645 {
647 }
648 if (field_viewkey_found)
649 {
651 if (!crypto::secret_key_to_public_key(viewkey, pkey)) {
652 THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify view key secret key"));
653 }
654 if (info.address.m_view_public_key != pkey) {
655 THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("view key does not match standard address"));
656 }
657 }
658 if (field_spendkey_found)
659 {
662 THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify spend key secret key"));
663 }
664 if (info.address.m_spend_public_key != pkey) {
665 THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("spend key does not match standard address"));
666 }
667 }
668 }
669
670 const bool deprecated_wallet = restore_deterministic_wallet && ((old_language == crypto::ElectrumWords::old_language_name) ||
673 tools::wallet2::tr("Cannot generate deprecated wallets from JSON"));
674
675 wallet.reset(make_basic(vm, unattended, opts, password_prompter).release());
676 wallet->set_refresh_from_block_height(field_scan_from_height);
677 wallet->explicit_refresh_from_block_height(field_scan_from_height_found);
678 if (!old_language.empty())
679 wallet->set_seed_language(old_language);
680
681 try
682 {
683 if (!field_seed.empty())
684 {
685 wallet->generate(field_filename, field_password, recovery_key, recover, false, create_address_file);
686 password = field_password;
687 }
688 else if (field_viewkey.empty() && !field_spendkey.empty())
689 {
690 wallet->generate(field_filename, field_password, spendkey, recover, false, create_address_file);
691 password = field_password;
692 }
693 else
694 {
696 if (!crypto::secret_key_to_public_key(viewkey, address.m_view_public_key)) {
697 THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify view key secret key"));
698 }
699
700 if (field_spendkey.empty())
701 {
702 // if we have an address but no spend key, we can deduce the spend public key
703 // from the address
704 if (field_address_found)
705 {
707 if(!get_account_address_from_str(info, nettype, field_address))
708 {
709 THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, std::string(tools::wallet2::tr("failed to parse address: ")) + field_address);
710 }
711 address.m_spend_public_key = info.address.m_spend_public_key;
712 }
713 else
714 {
715 THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("Address must be specified in order to create watch-only wallet"));
716 }
717 wallet->generate(field_filename, field_password, address, viewkey, create_address_file);
718 password = field_password;
719 }
720 else
721 {
722 if (!crypto::secret_key_to_public_key(spendkey, address.m_spend_public_key)) {
723 THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify spend key secret key"));
724 }
725 wallet->generate(field_filename, field_password, address, spendkey, viewkey, create_address_file);
726 password = field_password;
727 }
728 }
729 }
730 catch (const std::exception& e)
731 {
732 THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, std::string(tools::wallet2::tr("failed to generate new wallet: ")) + e.what());
733 }
734 return true;
735 };
736
737 if (do_generate())
738 {
739 return {std::move(wallet), tools::password_container(password)};
740 }
741 return {nullptr, tools::password_container{}};
742}
743
744std::string strjoin(const std::vector<size_t> &V, const char *sep)
745{
746 std::stringstream ss;
747 bool first = true;
748 for (const auto &v: V)
749 {
750 if (!first)
751 ss << sep;
752 ss << std::to_string(v);
753 first = false;
754 }
755 return ss.str();
756}
757
758static bool emplace_or_replace(std::unordered_multimap<crypto::hash, tools::wallet2::pool_payment_details> &container,
760{
761 auto range = container.equal_range(key);
762 for (auto i = range.first; i != range.second; ++i)
763 {
764 if (i->second.m_pd.m_tx_hash == pd.m_pd.m_tx_hash && i->second.m_pd.m_subaddr_index == pd.m_pd.m_subaddr_index)
765 {
766 i->second = pd;
767 return false;
768 }
769 }
770 container.emplace(key, pd);
771 return true;
772}
773
774void drop_from_short_history(std::list<crypto::hash> &short_chain_history, size_t N)
775{
776 std::list<crypto::hash>::iterator right;
777 // drop early N off, skipping the genesis block
778 if (short_chain_history.size() > N) {
779 right = short_chain_history.end();
780 std::advance(right,-1);
781 std::list<crypto::hash>::iterator left = right;
782 std::advance(left, -N);
783 short_chain_history.erase(left, right);
784 }
785}
786
787size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof)
788{
789 size_t size = 0;
790
791 // tx prefix
792
793 // first few bytes
794 size += 1 + 6;
795
796 // vin
797 size += n_inputs * (1+6+(mixin+1)*2+32);
798
799 // vout
800 size += n_outputs * (6+32);
801
802 // extra
803 size += extra_size;
804
805 // rct signatures
806
807 // type
808 size += 1;
809
810 // rangeSigs
811 if (bulletproof)
812 {
813 size_t log_padded_outputs = 0;
814 while ((1<<log_padded_outputs) < n_outputs)
815 ++log_padded_outputs;
816 size += (2 * (6 + log_padded_outputs) + 4 + 5) * 32 + 3;
817 }
818 else
819 size += (2*64*32+32+64*32) * n_outputs;
820
821 // MGs
822 size += n_inputs * (64 * (mixin+1) + 32);
823
824 // mixRing - not serialized, can be reconstructed
825 /* size += 2 * 32 * (mixin+1) * n_inputs; */
826
827 // pseudoOuts
828 size += 32 * n_inputs;
829 // ecdhInfo
830 size += 8 * n_outputs;
831 // outPk - only commitment is saved
832 size += 32 * n_outputs;
833 // txnFee
834 size += 4;
835
836 LOG_PRINT_L2("estimated " << (bulletproof ? "bulletproof" : "borromean") << " rct tx size for " << n_inputs << " inputs with ring size " << (mixin+1) << " and " << n_outputs << " outputs: " << size << " (" << ((32 * n_inputs/*+1*/) + 2 * 32 * (mixin+1) * n_inputs + 32 * n_outputs) << " saved)");
837 return size;
838}
839
840size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof)
841{
842 if (use_rct)
843 return estimate_rct_tx_size(n_inputs, mixin, n_outputs, extra_size, bulletproof);
844 else
845 return n_inputs * (mixin+1) * APPROXIMATE_INPUT_BYTES + extra_size;
846}
847
848uint64_t estimate_tx_weight(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof)
849{
850 size_t size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
851 if (use_rct && bulletproof && n_outputs > 2)
852 {
853 const uint64_t bp_base = 368;
854 size_t log_padded_outputs = 2;
855 while ((1<<log_padded_outputs) < n_outputs)
856 ++log_padded_outputs;
857 uint64_t nlr = 2 * (6 + log_padded_outputs);
858 const uint64_t bp_size = 32 * (9 + nlr);
859 const uint64_t bp_clawback = (bp_base * (1<<log_padded_outputs) - bp_size) * 4 / 5;
860 MDEBUG("clawback on size " << size << ": " << bp_clawback);
861 size += bp_clawback;
862 }
863 return size;
864}
865
866uint8_t get_bulletproof_fork()
867{
868 return 99;
869}
870
871uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask)
872{
873 if (use_per_byte_fee)
874 {
875 const size_t estimated_tx_weight = estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
876 return calculate_fee_from_weight(base_fee, estimated_tx_weight, fee_multiplier, fee_quantization_mask);
877 }
878 else
879 {
880 const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
881 return calculate_fee(base_fee, estimated_tx_size, fee_multiplier);
882 }
883}
884
885uint64_t calculate_fee(bool use_per_byte_fee, const cryptonote::transaction &tx, size_t blob_size, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask)
886{
887 if (use_per_byte_fee)
888 return calculate_fee_from_weight(base_fee, cryptonote::get_transaction_weight(tx, blob_size), fee_multiplier, fee_quantization_mask);
889 else
890 return calculate_fee(base_fee, blob_size, fee_multiplier);
891}
892
893bool get_short_payment_id(crypto::hash8 &payment_id8, const tools::wallet2::pending_tx &ptx, hw::device &hwdev)
894{
895 std::vector<tx_extra_field> tx_extra_fields;
896 parse_tx_extra(ptx.tx.extra, tx_extra_fields); // ok if partially parsed
897 cryptonote::tx_extra_nonce extra_nonce;
898 if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
899 {
900 if(get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
901 {
902 if (ptx.dests.empty())
903 {
904 MWARNING("Encrypted payment id found, but no destinations public key, cannot decrypt");
905 return false;
906 }
907 return hwdev.decrypt_payment_id(payment_id8, ptx.dests[0].addr.m_view_public_key, ptx.tx_key);
908 }
909 }
910 return false;
911}
912
913tools::wallet2::tx_construction_data get_construction_data_with_decrypted_short_payment_id(const tools::wallet2::pending_tx &ptx, hw::device &hwdev)
914{
916 crypto::hash8 payment_id8 = null_hash8;
917 if (get_short_payment_id(payment_id8, ptx, hwdev))
918 {
919 // Remove encrypted
921 // Add decrypted
922 std::string extra_nonce;
923 crypto::hash payment_id = null_hash;
924 memcpy(payment_id.data, payment_id8.data, 8); // convert short pid to regular
925 memset(payment_id.data + 8, 0, 24); // merely a sanity check
926
927 set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
928 THROW_WALLET_EXCEPTION_IF(!add_extra_nonce_to_tx_extra(construction_data.extra, extra_nonce),
929 tools::error::wallet_internal_error, "Failed to add decrypted payment id to tx extra");
930 LOG_PRINT_L1("Decrypted payment ID: " << payment_id);
931 }
932 return construction_data;
933}
934
935uint32_t get_subaddress_clamped_sum(uint32_t idx, uint32_t extra)
936{
937 static constexpr uint32_t uint32_max = std::numeric_limits<uint32_t>::max();
938 if (idx > uint32_max - extra)
939 return uint32_max;
940 return idx + extra;
941}
942
943static void setup_shim(hw::wallet_shim * shim, tools::wallet2 * wallet)
944{
945 shim->get_tx_pub_key_from_received_outs = std::bind(&tools::wallet2::get_tx_pub_key_from_received_outs, wallet, std::placeholders::_1);
946}
947
949{
951
952 // easy case if we have the whole tx
953 if (!entry.as_hex.empty() || (!entry.prunable_as_hex.empty() && !entry.pruned_as_hex.empty()))
954 {
955 CHECK_AND_ASSERT_MES(epee::string_tools::parse_hexstr_to_binbuff(entry.as_hex.empty() ? entry.pruned_as_hex + entry.prunable_as_hex : entry.as_hex, bd), false, "Failed to parse tx data");
956 CHECK_AND_ASSERT_MES(cryptonote::parse_and_validate_tx_from_blob(bd, tx), false, "Invalid tx data");
958 // if the hash was given, check it matches
959 CHECK_AND_ASSERT_MES(entry.tx_hash.empty() || epee::string_tools::pod_to_hex(tx_hash) == entry.tx_hash, false,
960 "Response claims a different hash than the data yields");
961 return true;
962 }
963 // case of a pruned tx with its prunable data hash
964 if (!entry.pruned_as_hex.empty() && !entry.prunable_hash.empty())
965 {
966 crypto::hash ph;
967 CHECK_AND_ASSERT_MES(epee::string_tools::hex_to_pod(entry.prunable_hash, ph), false, "Failed to parse prunable hash");
968 CHECK_AND_ASSERT_MES(epee::string_tools::parse_hexstr_to_binbuff(entry.pruned_as_hex, bd), false, "Failed to parse pruned data");
969 CHECK_AND_ASSERT_MES(parse_and_validate_tx_base_from_blob(bd, tx), false, "Invalid base tx data");
970 // only v2 txes can calculate their txid after pruned
971 if (bd[0] > 1)
972 {
974 }
975 else
976 {
977 // for v1, we trust the dameon
978 CHECK_AND_ASSERT_MES(epee::string_tools::hex_to_pod(entry.tx_hash, tx_hash), false, "Failed to parse tx hash");
979 }
980 return true;
981 }
982 return false;
983}
984
985 //-----------------------------------------------------------------
986} //namespace
987
988namespace tools
989{
990// for now, limit to 30 attempts. TODO: discuss a good number to limit to.
991const size_t MAX_SPLIT_ATTEMPTS = 30;
992
993constexpr const std::chrono::seconds wallet2::rpc_timeout;
994const char* wallet2::tr(const char* str) { return i18n_translate(str, "tools::wallet2"); }
995
996gamma_picker::gamma_picker(const std::vector<uint64_t> &rct_offsets, double shape, double scale):
997 rct_offsets(rct_offsets)
998{
999 gamma = std::gamma_distribution<double>(shape, scale);
1000 THROW_WALLET_EXCEPTION_IF(rct_offsets.size() <= ETN_DEFAULT_TX_SPENDABLE_AGE_V8, error::wallet_internal_error, "Bad offset calculation");
1001 const size_t blocks_in_a_year = 86400 * 365 / DIFFICULTY_TARGET_V6;
1002 const size_t blocks_to_consider = std::min<size_t>(rct_offsets.size(), blocks_in_a_year);
1003 const size_t outputs_to_consider = rct_offsets.back() - (blocks_to_consider < rct_offsets.size() ? rct_offsets[rct_offsets.size() - blocks_to_consider - 1] : 0);
1004 begin = rct_offsets.data();
1005 end = rct_offsets.data() + rct_offsets.size() - ETN_DEFAULT_TX_SPENDABLE_AGE_V8;
1006 num_rct_outputs = *(end - 1);
1007 THROW_WALLET_EXCEPTION_IF(num_rct_outputs == 0, error::wallet_internal_error, "No rct outputs");
1008 average_output_time = DIFFICULTY_TARGET_V6 * blocks_to_consider / outputs_to_consider; // this assumes constant target over the whole rct range
1009};
1010
1011gamma_picker::gamma_picker(const std::vector<uint64_t> &rct_offsets): gamma_picker(rct_offsets, GAMMA_SHAPE, GAMMA_SCALE) {}
1012
1014{
1015 double x = gamma(engine);
1016 x = exp(x);
1017 uint64_t output_index = x / average_output_time;
1018 if (output_index >= num_rct_outputs)
1019 return std::numeric_limits<uint64_t>::max(); // bad pick
1020 output_index = num_rct_outputs - 1 - output_index;
1021
1022 const uint64_t *it = std::lower_bound(begin, end, output_index);
1023 THROW_WALLET_EXCEPTION_IF(it == end, error::wallet_internal_error, "output_index not found");
1024 uint64_t index = std::distance(begin, it);
1025
1026 const uint64_t first_rct = index == 0 ? 0 : rct_offsets[index - 1];
1027 const uint64_t n_rct = rct_offsets[index] - first_rct;
1028 if (n_rct == 0)
1029 return std::numeric_limits<uint64_t>::max(); // bad pick
1030 MTRACE("Picking 1/" << n_rct << " in block " << index);
1031 return first_rct + crypto::rand_idx(n_rct);
1032};
1033
1034wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, const boost::optional<tools::password_container> &password):
1035 w(w),
1036 locked(password != boost::none)
1037{
1038 if (!locked || w.is_unattended() || w.ask_password() != tools::wallet2::AskPasswordToDecrypt || w.watch_only())
1039 {
1040 locked = false;
1041 return;
1042 }
1043 const epee::wipeable_string pass = password->password();
1044 w.generate_chacha_key_from_password(pass, key);
1045 w.decrypt_keys(key);
1046}
1047
1049 w(w),
1050 locked(locked)
1051{
1052 if (!locked)
1053 return;
1054 w.generate_chacha_key_from_password(password, key);
1055 w.decrypt_keys(key);
1056}
1057
1059{
1060 if (!locked)
1061 return;
1062 try { w.encrypt_keys(key); }
1063 catch (...)
1064 {
1065 MERROR("Failed to re-encrypt wallet keys");
1066 // do not propagate through dtor, we'd crash
1067 }
1068}
1069
1071{
1072 if (wallet)
1073 wallet->on_device_button_request(code);
1074}
1075
1077{
1078 if (wallet)
1079 wallet->on_device_button_pressed();
1080}
1081
1082boost::optional<epee::wipeable_string> wallet_device_callback::on_pin_request()
1083{
1084 if (wallet)
1085 return wallet->on_device_pin_request();
1086 return boost::none;
1087}
1088
1089boost::optional<epee::wipeable_string> wallet_device_callback::on_passphrase_request(bool on_device)
1090{
1091 if (wallet)
1092 return wallet->on_device_passphrase_request(on_device);
1093 return boost::none;
1094}
1095
1097{
1098 if (wallet)
1099 wallet->on_device_progress(event);
1100}
1101
1102wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
1103 m_multisig_rescan_info(NULL),
1104 m_multisig_rescan_k(NULL),
1105 m_upper_transaction_weight_limit(0),
1106 m_run(true),
1107 m_callback(0),
1108 m_trusted_daemon(false),
1109 m_nettype(nettype),
1110 m_multisig_rounds_passed(0),
1111 m_always_confirm_transfers(true),
1112 m_print_ring_members(false),
1113 m_store_tx_info(true),
1114 m_default_mixin(0),
1115 m_default_priority(0),
1116 m_refresh_type(RefreshOptimizeCoinbase),
1117 m_auto_refresh(true),
1118 m_first_refresh_done(false),
1119 m_refresh_from_block_height(0),
1120 m_explicit_refresh_from_block_height(true),
1121 m_confirm_missing_payment_id(true),
1122 m_confirm_non_default_ring_size(true),
1123 m_ask_password(AskPasswordOnAction),
1124 m_min_output_count(0),
1125 m_min_output_value(0),
1126 m_merge_destinations(false),
1127 m_confirm_backlog(true),
1128 m_confirm_backlog_threshold(0),
1129 m_confirm_export_overwrite(true),
1130 m_auto_low_priority(true),
1131 m_segregate_pre_fork_outputs(true),
1132 m_key_reuse_mitigation2(true),
1133 m_segregation_height(0),
1134 m_ignore_fractional_outputs(true),
1135 m_track_uses(false),
1136 m_setup_background_mining(BackgroundMiningMaybe),
1137 m_is_initialized(false),
1138 m_kdf_rounds(kdf_rounds),
1139 is_old_file_format(false),
1140 m_watch_only(false),
1141 m_multisig(false),
1142 m_multisig_threshold(0),
1143 m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex),
1144 m_account_public_address{crypto::null_pkey, crypto::null_pkey},
1145 m_subaddress_lookahead_major(SUBADDRESS_LOOKAHEAD_MAJOR),
1146 m_subaddress_lookahead_minor(SUBADDRESS_LOOKAHEAD_MINOR),
1147 m_light_wallet(false),
1148 m_light_wallet_scanned_block_height(0),
1149 m_light_wallet_blockchain_height(0),
1150 m_light_wallet_connected(false),
1151 m_light_wallet_balance(0),
1152 m_light_wallet_unlocked_balance(0),
1153 m_original_keys_available(false),
1154 m_message_store(),
1155 m_key_device_type(hw::device::device_type::SOFTWARE),
1156 m_ring_history_saved(false),
1157 m_ringdb(),
1158 m_last_block_reward(0),
1159 m_encrypt_keys_after_refresh(boost::none),
1160 m_unattended(unattended),
1161 m_devices_registered(false),
1162 m_device_last_key_image_sync(0),
1163 m_use_dns(true),
1164 m_offline(false),
1165 m_account_major_offset(0)
1166{
1167}
1168
1170{
1171}
1172
1173bool wallet2::has_testnet_option(const boost::program_options::variables_map& vm)
1174{
1175 return command_line::get_arg(vm, options().testnet);
1176}
1177
1178bool wallet2::has_stagenet_option(const boost::program_options::variables_map& vm)
1179{
1180 return command_line::get_arg(vm, options().stagenet);
1181}
1182
1183std::string wallet2::device_name_option(const boost::program_options::variables_map& vm)
1184{
1185 return command_line::get_arg(vm, options().hw_device);
1186}
1187
1188std::string wallet2::device_derivation_path_option(const boost::program_options::variables_map &vm)
1189{
1190 return command_line::get_arg(vm, options().hw_device_derivation_path);
1191}
1192
1193void wallet2::init_options(boost::program_options::options_description& desc_params)
1194{
1195 const options opts{};
1196 command_line::add_arg(desc_params, opts.daemon_address);
1197 command_line::add_arg(desc_params, opts.daemon_host);
1198 command_line::add_arg(desc_params, opts.proxy);
1199 command_line::add_arg(desc_params, opts.trusted_daemon);
1200 command_line::add_arg(desc_params, opts.untrusted_daemon);
1201 command_line::add_arg(desc_params, opts.password);
1202 command_line::add_arg(desc_params, opts.password_file);
1203 command_line::add_arg(desc_params, opts.daemon_port);
1204 command_line::add_arg(desc_params, opts.daemon_login);
1205 command_line::add_arg(desc_params, opts.daemon_ssl);
1206 command_line::add_arg(desc_params, opts.daemon_ssl_private_key);
1207 command_line::add_arg(desc_params, opts.daemon_ssl_certificate);
1208 command_line::add_arg(desc_params, opts.daemon_ssl_ca_certificates);
1209 command_line::add_arg(desc_params, opts.daemon_ssl_allowed_fingerprints);
1210 command_line::add_arg(desc_params, opts.daemon_ssl_allow_any_cert);
1211 command_line::add_arg(desc_params, opts.daemon_ssl_allow_chained);
1212 command_line::add_arg(desc_params, opts.testnet);
1213 command_line::add_arg(desc_params, opts.stagenet);
1214 command_line::add_arg(desc_params, opts.shared_ringdb_dir);
1215 command_line::add_arg(desc_params, opts.kdf_rounds);
1217 command_line::add_arg(desc_params, opts.hw_device);
1218 command_line::add_arg(desc_params, opts.hw_device_derivation_path);
1219 command_line::add_arg(desc_params, opts.tx_notify);
1220 command_line::add_arg(desc_params, opts.no_dns);
1221 command_line::add_arg(desc_params, opts.offline);
1222 command_line::add_arg(desc_params, opts.data_dir);
1223 command_line::add_arg(desc_params, opts.fallback_to_pow_checkpoint_height);
1224 command_line::add_arg(desc_params, opts.fallback_to_pow_checkpoint_hash);
1225}
1226
1227std::pair<std::unique_ptr<wallet2>, tools::password_container> wallet2::make_from_json(const boost::program_options::variables_map& vm, bool unattended, const std::string& json_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
1228{
1229 const options opts{};
1230 return generate_from_json(json_file, vm, unattended, opts, password_prompter);
1231}
1232
1233std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file(
1234 const boost::program_options::variables_map& vm, bool unattended, const std::string& wallet_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
1235{
1236 const options opts{};
1237 auto pwd = get_password(vm, opts, password_prompter, false);
1238 if (!pwd)
1239 {
1240 return {nullptr, password_container{}};
1241 }
1242 auto wallet = make_basic(vm, unattended, opts, password_prompter);
1243 if (wallet && !wallet_file.empty())
1244 {
1245 wallet->load(wallet_file, pwd->password());
1246 }
1247 return {std::move(wallet), std::move(*pwd)};
1248}
1249
1250std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_new(const boost::program_options::variables_map& vm, bool unattended, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter)
1251{
1252 const options opts{};
1253 auto pwd = get_password(vm, opts, password_prompter, true);
1254 if (!pwd)
1255 {
1256 return {nullptr, password_container{}};
1257 }
1258 return {make_basic(vm, unattended, opts, password_prompter), std::move(*pwd)};
1259}
1260
1261std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::variables_map& vm, bool unattended, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
1262{
1263 const options opts{};
1264 return make_basic(vm, unattended, opts, password_prompter);
1265}
1266
1267//----------------------------------------------------------------------------------------------------
1268bool wallet2::set_daemon(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, bool trusted_daemon, epee::net_utils::ssl_options_t ssl_options)
1269{
1270 boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex);
1271
1272 if(m_http_client.is_connected())
1273 m_http_client.disconnect();
1274 m_daemon_address = std::move(daemon_address);
1275 m_daemon_login = std::move(daemon_login);
1276 m_trusted_daemon = trusted_daemon;
1277
1278 MINFO("setting daemon to " << get_daemon_address());
1279 return m_http_client.set_server(get_daemon_address(), get_daemon_login(), std::move(ssl_options));
1280}
1281//----------------------------------------------------------------------------------------------------
1282bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, boost::asio::ip::tcp::endpoint proxy, uint64_t upper_transaction_weight_limit, bool trusted_daemon, epee::net_utils::ssl_options_t ssl_options, std::string blockchain_db_path)
1283{
1284 m_checkpoints.init_default_checkpoints(m_nettype);
1285 m_is_initialized = true;
1286 m_upper_transaction_weight_limit = upper_transaction_weight_limit;
1287
1288 if (proxy != boost::asio::ip::tcp::endpoint{})
1289 m_http_client.set_connector(net::socks::connector{std::move(proxy)});
1290 return set_daemon(daemon_address, daemon_login, trusted_daemon, std::move(ssl_options));
1291}
1292//----------------------------------------------------------------------------------------------------
1294{
1295 crypto::secret_key second;
1296 keccak((uint8_t *)&get_account().get_keys().m_spend_secret_key, sizeof(crypto::secret_key), (uint8_t *)&second, sizeof(crypto::secret_key));
1297 sc_reduce32((uint8_t *)&second);
1298 return memcmp(second.data,get_account().get_keys().m_view_secret_key.data, sizeof(crypto::secret_key)) == 0;
1299}
1300//----------------------------------------------------------------------------------------------------
1301bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeable_string &passphrase) const
1302{
1303 bool keys_deterministic = is_deterministic();
1304 if (!keys_deterministic)
1305 {
1306 std::cout << "This is not a deterministic wallet" << std::endl;
1307 return false;
1308 }
1309 if (seed_language.empty())
1310 {
1311 std::cout << "seed_language not set" << std::endl;
1312 return false;
1313 }
1314
1316 if (!passphrase.empty())
1317 key = cryptonote::encrypt_key(key, passphrase);
1318 if (!crypto::ElectrumWords::bytes_to_words(key, electrum_words, seed_language))
1319 {
1320 std::cout << "Failed to create seed from key for language: " << seed_language << std::endl;
1321 return false;
1322 }
1323
1324 return true;
1325}
1326//----------------------------------------------------------------------------------------------------
1327bool wallet2::get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase, bool raw) const
1328{
1329 bool ready;
1330 uint32_t threshold, total;
1331 if (!multisig(&ready, &threshold, &total))
1332 {
1333 std::cout << "This is not a multisig wallet" << std::endl;
1334 return false;
1335 }
1336 if (!ready)
1337 {
1338 std::cout << "This multisig wallet is not yet finalized" << std::endl;
1339 return false;
1340 }
1341 if (!raw && seed_language.empty())
1342 {
1343 std::cout << "seed_language not set" << std::endl;
1344 return false;
1345 }
1346
1347 crypto::secret_key skey;
1348 crypto::public_key pkey;
1349 const account_keys &keys = get_account().get_keys();
1351 data.append((const char*)&threshold, sizeof(uint32_t));
1352 data.append((const char*)&total, sizeof(uint32_t));
1353 skey = keys.m_spend_secret_key;
1354 data.append((const char*)&skey, sizeof(skey));
1356 data.append((const char*)&pkey, sizeof(pkey));
1357 skey = keys.m_view_secret_key;
1358 data.append((const char*)&skey, sizeof(skey));
1360 data.append((const char*)&pkey, sizeof(pkey));
1361 for (const auto &skey: keys.m_multisig_keys)
1362 data.append((const char*)&skey, sizeof(skey));
1363 for (const auto &signer: m_multisig_signers)
1364 data.append((const char*)&signer, sizeof(signer));
1365
1366 if (!passphrase.empty())
1367 {
1369 crypto::cn_slow_hash(passphrase.data(), passphrase.size(), (crypto::hash&)key);
1370 sc_reduce32((unsigned char*)key.data);
1371 data = encrypt(data, key, true);
1372 }
1373
1374 if (raw)
1375 {
1376 seed = epee::to_hex::wipeable_string({(const unsigned char*)data.data(), data.size()});
1377 }
1378 else
1379 {
1380 if (!crypto::ElectrumWords::bytes_to_words(data.data(), data.size(), seed, seed_language))
1381 {
1382 std::cout << "Failed to encode seed";
1383 return false;
1384 }
1385 }
1386
1387 return true;
1388}
1389//----------------------------------------------------------------------------------------------------
1391{
1392 bool r = true;
1393 hw::device &hwdev = lookup_device(m_device_name);
1394 hwdev.set_name(m_device_name);
1395 hwdev.set_network_type(m_nettype);
1396 hwdev.set_derivation_path(m_device_derivation_path);
1397 hwdev.set_callback(get_device_callback());
1398 r = hwdev.init();
1399 if (!r){
1400 MERROR("Could not init device");
1401 return false;
1402 }
1403
1404 r = hwdev.connect();
1405 if (!r){
1406 MERROR("Could not connect to the device");
1407 return false;
1408 }
1409
1410 m_account.set_device(hwdev);
1411 return true;
1412}
1413//----------------------------------------------------------------------------------------------------
1417const std::string &wallet2::get_seed_language() const
1418{
1419 return seed_language;
1420}
1421
1425void wallet2::set_seed_language(const std::string &language)
1426{
1427 seed_language = language;
1428}
1429//----------------------------------------------------------------------------------------------------
1431{
1432 hw::device &hwdev = m_account.get_device();
1433 cryptonote::subaddress_index index2 = {index.major + (index.major != 0 ? m_account_major_offset : 0), index.minor};
1434 return hwdev.get_subaddress(m_account.get_keys(), index2);
1435}
1436//----------------------------------------------------------------------------------------------------
1437boost::optional<cryptonote::subaddress_index> wallet2::get_subaddress_index(const cryptonote::account_public_address& address) const
1438{
1439 auto index = m_subaddresses.find(address.m_spend_public_key);
1440 if (index == m_subaddresses.end())
1441 return boost::none;
1442 return index->second;
1443}
1444//----------------------------------------------------------------------------------------------------
1446{
1447 hw::device &hwdev = m_account.get_device();
1448 cryptonote::subaddress_index index2 = {index.major + (index.major != 0 ? m_account_major_offset : 0), index.minor};
1449 return hwdev.get_subaddress_spend_public_key(m_account.get_keys(), index2);
1450}
1451//----------------------------------------------------------------------------------------------------
1457//----------------------------------------------------------------------------------------------------
1458std::string wallet2::get_integrated_address_as_str(const crypto::hash8& payment_id) const
1459{
1460 return cryptonote::get_account_integrated_address_as_str(m_nettype, get_address(), payment_id);
1461}
1462//----------------------------------------------------------------------------------------------------
1463void wallet2::add_subaddress_account(const std::string& label, const bool update_account_tags)
1464{
1466 expand_subaddresses({index_major, 0}, update_account_tags);
1467 m_subaddress_labels[index_major][0] = label;
1468}
1469//----------------------------------------------------------------------------------------------------
1470void wallet2::add_subaddress(uint32_t index_major, const std::string& label)
1471{
1472 THROW_WALLET_EXCEPTION_IF(index_major >= m_subaddress_labels.size(), error::account_index_outofbound);
1473 uint32_t index_minor = (uint32_t)get_num_subaddresses(index_major);
1474 expand_subaddresses({index_major, index_minor});
1475 m_subaddress_labels[index_major][index_minor] = label;
1476}
1477//----------------------------------------------------------------------------------------------------
1478void wallet2::expand_subaddresses(const cryptonote::subaddress_index& index, const bool update_account_tags)
1479{
1480 hw::device &hwdev = m_account.get_device();
1481 if (m_subaddress_labels.size() <= index.major)
1482 {
1483 // add new accounts
1485 const uint32_t major_end = get_subaddress_clamped_sum(index.major, m_subaddress_lookahead_major);
1486 for (index2.major = m_subaddress_labels.size(); index2.major < major_end; ++index2.major)
1487 {
1488 const uint32_t end = get_subaddress_clamped_sum((index2.major == index.major ? index.minor : 0), m_subaddress_lookahead_minor);
1489 const std::vector<crypto::public_key> pkeys = hwdev.get_subaddress_spend_public_keys(m_account.get_keys(), index2.major + (index2.major != 0 ? m_account_major_offset : 0), 0, end);
1490 for (index2.minor = 0; index2.minor < end; ++index2.minor)
1491 {
1492 const crypto::public_key &D = pkeys[index2.minor];
1493 m_subaddresses[D] = index2;
1494 }
1495 }
1496 m_subaddress_labels.resize(index.major + 1, {"Untitled account"});
1497 m_subaddress_labels[index.major].resize(index.minor + 1);
1498
1499 if(update_account_tags)
1501 }
1502 else if (m_subaddress_labels[index.major].size() <= index.minor)
1503 {
1504 // add new subaddresses
1505 const uint32_t end = get_subaddress_clamped_sum(index.minor, m_subaddress_lookahead_minor);
1506 const uint32_t begin = m_subaddress_labels[index.major].size();
1507 cryptonote::subaddress_index index2 = {index.major, begin};
1508 const std::vector<crypto::public_key> pkeys = hwdev.get_subaddress_spend_public_keys(m_account.get_keys(), index2.major + (index2.major != 0 ? m_account_major_offset : 0), index2.minor, end);
1509 for (; index2.minor < end; ++index2.minor)
1510 {
1511 const crypto::public_key &D = pkeys[index2.minor - begin];
1512 m_subaddresses[D] = index2;
1513 }
1514 m_subaddress_labels[index.major].resize(index.minor + 1);
1515 }
1516}
1517//----------------------------------------------------------------------------------------------------
1519{
1520 if (index.major >= m_subaddress_labels.size() || index.minor >= m_subaddress_labels[index.major].size())
1521 {
1522 MERROR("Subaddress label doesn't exist");
1523 return "";
1524 }
1525 return m_subaddress_labels[index.major][index.minor];
1526}
1527//----------------------------------------------------------------------------------------------------
1528void wallet2::set_subaddress_label(const cryptonote::subaddress_index& index, const std::string &label)
1529{
1530 THROW_WALLET_EXCEPTION_IF(index.major >= m_subaddress_labels.size(), error::account_index_outofbound);
1531 THROW_WALLET_EXCEPTION_IF(index.minor >= m_subaddress_labels[index.major].size(), error::address_index_outofbound);
1532 m_subaddress_labels[index.major][index.minor] = label;
1533}
1534//----------------------------------------------------------------------------------------------------
1535void wallet2::set_subaddress_lookahead(size_t major, size_t minor)
1536{
1537 THROW_WALLET_EXCEPTION_IF(major > 0xffffffff, error::wallet_internal_error, "Subaddress major lookahead is too large");
1538 THROW_WALLET_EXCEPTION_IF(minor > 0xffffffff, error::wallet_internal_error, "Subaddress minor lookahead is too large");
1539 m_subaddress_lookahead_major = major;
1540 m_subaddress_lookahead_minor = minor;
1541}
1542//----------------------------------------------------------------------------------------------------
1547{
1548 return is_old_file_format;
1549}
1550//----------------------------------------------------------------------------------------------------
1551void wallet2::set_spent(size_t idx, uint64_t height, bool public_out)
1552{
1553 transfer_details &td = m_transfers[idx];
1554
1555 if(public_out){
1556 LOG_PRINT_L2("Setting SPENT at "
1557 << height << ": chainstate index " << td.m_txid << ":" << td.m_internal_output_index
1558 << ", amount " << print_etn(td.m_amount));
1559 }else{
1560 LOG_PRINT_L2("Setting SPENT at "
1561 << height << ": ki " << td.m_key_image
1562 << ", amount " << print_etn(td.m_amount));
1563 }
1564
1565 td.m_spent = true;
1566 td.m_spent_height = height;
1567}
1568//----------------------------------------------------------------------------------------------------
1569void wallet2::set_unspent(size_t idx, bool public_out)
1570{
1571 transfer_details &td = m_transfers[idx];
1572
1573 if(public_out){
1574 LOG_PRINT_L2("Setting UNSPENT: chainstate index "
1575 << td.m_txid << ":"<< td.m_internal_output_index << ", amount " << print_etn(td.m_amount));
1576 }else{
1577 LOG_PRINT_L2("Setting UNSPENT: ki " << td.m_key_image << ", amount " << print_etn(td.m_amount));
1578 }
1579
1580 td.m_spent = false;
1581 td.m_spent_height = 0;
1582}
1583//----------------------------------------------------------------------------------------------------
1584void wallet2::freeze(size_t idx)
1585{
1586 CHECK_AND_ASSERT_THROW_MES(idx < m_transfers.size(), "Invalid transfer_details index");
1587 transfer_details &td = m_transfers[idx];
1588 td.m_frozen = true;
1589}
1590//----------------------------------------------------------------------------------------------------
1591void wallet2::thaw(size_t idx)
1592{
1593 CHECK_AND_ASSERT_THROW_MES(idx < m_transfers.size(), "Invalid transfer_details index");
1594 transfer_details &td = m_transfers[idx];
1595 td.m_frozen = false;
1596}
1597//----------------------------------------------------------------------------------------------------
1598bool wallet2::frozen(size_t idx) const
1599{
1600 CHECK_AND_ASSERT_THROW_MES(idx < m_transfers.size(), "Invalid transfer_details index");
1601 const transfer_details &td = m_transfers[idx];
1602 return td.m_frozen;
1603}
1604//----------------------------------------------------------------------------------------------------
1606{
1608}
1609//----------------------------------------------------------------------------------------------------
1611{
1613}
1614//----------------------------------------------------------------------------------------------------
1616{
1617 return frozen(get_transfer_details(ki));
1618}
1619//----------------------------------------------------------------------------------------------------
1621{
1622 for (size_t idx = 0; idx < m_transfers.size(); ++idx)
1623 {
1624 const transfer_details &td = m_transfers[idx];
1625 if (td.m_key_image_known && td.m_key_image == ki)
1626 return idx;
1627 }
1628 CHECK_AND_ASSERT_THROW_MES(false, "Key image not found");
1629}
1630//----------------------------------------------------------------------------------------------------
1632{
1633 return td.m_frozen;
1634}
1635//----------------------------------------------------------------------------------------------------
1636void wallet2::check_acc_out_precomp(const tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, tx_scan_info_t &tx_scan_info) const
1637{
1638 hw::device &hwdev = m_account.get_device();
1639 boost::unique_lock<hw::device> hwdev_lock (hwdev);
1641 if (o.target.type() != typeid(txout_to_key) && o.target.type() != typeid(txout_to_key_public))
1642 {
1643 tx_scan_info.error = true;
1644 LOG_ERROR("wrong type id in transaction out");
1645 return;
1646 }
1647 if(o.target.type() != typeid(txout_to_key_public)) {
1648 tx_scan_info.received = is_out_to_acc_precomp(m_subaddresses, boost::get<txout_to_key>(o.target).key, derivation,
1649 additional_derivations, i, hwdev);
1650 }else{
1651 //only assign subaddress recipient if view key also matches too as we now spend with combined keys (a+b) and we wont be
1652 // doing key image related checks later to check if we can really spend the out (ie checking view key match by proxy)
1653 auto out_address = boost::get<txout_to_key_public>(o.target).address;
1654 auto receive_info = cryptonote::is_out_to_acc_precomp_public(m_subaddresses, out_address);
1655 tx_scan_info.received =
1656 (receive_info == boost::none) ?
1657 (receive_info) :
1658 get_subaddress(receive_info->index).m_view_public_key == out_address.m_view_public_key ?
1659 receive_info : boost::none; //todo: refactor with function pointers
1660 }
1661 if(tx_scan_info.received)
1662 {
1663 tx_scan_info.etn_transfered = o.amount; // may be 0 for ringct outputs
1664 }
1665 else
1666 {
1667 tx_scan_info.etn_transfered = 0;
1668 }
1669 tx_scan_info.error = false;
1670}
1671//----------------------------------------------------------------------------------------------------
1672void wallet2::check_acc_out_precomp(const tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info) const
1673{
1674 // if(we're not pointing at a pre cached data member || we're attempting to process a receive entry before it's populated from cache thread)
1675 if (!is_out_data || i >= is_out_data->received.size())
1676 return check_acc_out_precomp(o, derivation, additional_derivations, i, tx_scan_info);
1677
1678 tx_scan_info.received = is_out_data->received[i];
1679 if(tx_scan_info.received)
1680 {
1681 tx_scan_info.etn_transfered = o.amount; // may be 0 for ringct outputs
1682 }
1683 else
1684 {
1685 tx_scan_info.etn_transfered = 0;
1686 }
1687 tx_scan_info.error = false;
1688}
1689//----------------------------------------------------------------------------------------------------
1690void wallet2::check_acc_out_precomp_once(const tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info, bool &already_seen) const
1691{
1692 tx_scan_info.received = boost::none;
1693 if (already_seen)
1694 return;
1695 check_acc_out_precomp(o, derivation, additional_derivations, i, is_out_data, tx_scan_info);
1696 if (tx_scan_info.received)
1697 already_seen = true;
1698}
1699//----------------------------------------------------------------------------------------------------
1700static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &derivation, unsigned int i, rct::key & mask, hw::device &hwdev)
1701{
1702 crypto::secret_key scalar1;
1703 hwdev.derivation_to_scalar(derivation, i, scalar1);
1704 try
1705 {
1706 switch (rv.type)
1707 {
1708 case rct::RCTTypeSimple:
1711 return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask, hwdev);
1712 case rct::RCTTypeFull:
1713 return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask, hwdev);
1714 default:
1715 LOG_ERROR("Unsupported rct type: " << rv.type);
1716 return 0;
1717 }
1718 }
1719 catch (const std::exception &e)
1720 {
1721 LOG_ERROR("Failed to decode input " << i);
1722 return 0;
1723 }
1724}
1725//----------------------------------------------------------------------------------------------------
1726void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_etn_got_in_outs, std::vector<size_t> &outs, bool pool)
1727{
1728 THROW_WALLET_EXCEPTION_IF(i >= tx.vout.size(), error::wallet_internal_error, "Invalid vout index");
1729
1730 // if keys are encrypted, ask for password
1731 if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only && !m_multisig_rescan_k)
1732 {
1733 static critical_section password_lock;
1734 CRITICAL_REGION_LOCAL(password_lock);
1735 if (!m_encrypt_keys_after_refresh)
1736 {
1737 boost::optional<epee::wipeable_string> pwd = m_callback->on_get_password(pool ? "output found in pool" : "output received");
1738 THROW_WALLET_EXCEPTION_IF(!pwd, error::password_needed, tr("Password is needed to compute key image for incoming etn"));
1739 THROW_WALLET_EXCEPTION_IF(!verify_password(*pwd), error::password_needed, tr("Invalid password: password is needed to compute key image for incoming etn"));
1740 decrypt_keys(*pwd);
1741 m_encrypt_keys_after_refresh = *pwd;
1742 }
1743 }
1744
1745 if (m_multisig)
1746 {
1747 tx_scan_info.in_ephemeral.pub = boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key;
1748 tx_scan_info.in_ephemeral.sec = crypto::null_skey;
1749 tx_scan_info.ki = rct::rct2ki(rct::zero());
1750 }
1751 else if (tx.version == 1)
1752 { //calculate the key image as if we were going to spend this output
1753 bool r = cryptonote::generate_key_image_helper_precomp(m_account.get_keys(), boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key, tx_scan_info.received->derivation, i, tx_scan_info.received->index, tx_scan_info.in_ephemeral, tx_scan_info.ki, m_account.get_device(), m_account_major_offset);
1754 THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
1755 THROW_WALLET_EXCEPTION_IF(tx_scan_info.in_ephemeral.pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key,
1756 error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
1757 }
1758
1759 THROW_WALLET_EXCEPTION_IF(std::find(outs.begin(), outs.end(), i) != outs.end(), error::wallet_internal_error, "Same output cannot be added twice");
1760 //we should never enter this unless it's an rct output
1761 if (tx_scan_info.etn_transfered == 0 && !miner_tx)
1762 {
1763 tx_scan_info.etn_transfered = tools::decodeRct(tx.rct_signatures, tx_scan_info.received->derivation, i, tx_scan_info.mask, m_account.get_device());
1764 }
1765 if (tx_scan_info.etn_transfered == 0)
1766 {
1767 MERROR("Invalid output amount, skipping");
1768 tx_scan_info.error = true;
1769 return;
1770 }
1771 outs.push_back(i);
1772 THROW_WALLET_EXCEPTION_IF(tx_etn_got_in_outs[tx_scan_info.received->index] >= std::numeric_limits<uint64_t>::max() - tx_scan_info.etn_transfered,
1773 error::wallet_internal_error, "Overflow in received amounts");
1774 //index here is a subaddress index (major & minor)
1775 tx_etn_got_in_outs[tx_scan_info.received->index] += tx_scan_info.etn_transfered;
1776 tx_scan_info.amount = tx_scan_info.etn_transfered;
1777 ++num_vouts_received;
1778}
1779//----------------------------------------------------------------------------------------------------
1780void wallet2::cache_tx_data(const cryptonote::transaction& tx, const crypto::hash &txid, tx_cache_data &tx_cache_data) const
1781{
1783 {
1784 // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key
1785 LOG_PRINT_L0("Transaction extra has unsupported format: " << txid);
1786 if (tx_cache_data.tx_extra_fields.empty())
1787 return;
1788 }
1789
1790
1791 // Don't try to extract tx public key if tx has no ouputs
1792 const bool is_miner = tx.vin.size() == 1 && tx.vin[0].type() == typeid(cryptonote::txin_gen);
1793 if (!is_miner || m_refresh_type != RefreshType::RefreshNoCoinbase)
1794 {
1795 const size_t rec_size = is_miner && m_refresh_type == RefreshType::RefreshOptimizeCoinbase ? 1 : tx.vout.size();
1796 if (!tx.vout.empty())
1797 {
1798 const std::vector<boost::optional<cryptonote::subaddress_receive_info>> rec(rec_size, boost::none);
1799 if(tx.version > 1){
1800 tx_cache_data.public_outs.push_back({crypto::null_pkey, {}, rec});
1801 return;
1802 }
1803
1804 // if tx.vout is not empty, we loop through all tx pubkeys
1805 tx_extra_pub_key pub_key_field;
1806 size_t pk_index = 0;
1807 while (find_tx_extra_field_by_type(tx_cache_data.tx_extra_fields, pub_key_field, pk_index++))
1808 tx_cache_data.primary.push_back({pub_key_field.pub_key, {}, rec});
1809
1810 // additional tx pubkeys and derivations for multi-destination transfers involving one or more subaddresses
1811 tx_extra_additional_pub_keys additional_tx_pub_keys;
1812 if (find_tx_extra_field_by_type(tx_cache_data.tx_extra_fields, additional_tx_pub_keys))
1813 {
1814 for (size_t i = 0; i < additional_tx_pub_keys.data.size(); ++i)
1815 tx_cache_data.additional.push_back({additional_tx_pub_keys.data[i], {}, {}});
1816 }
1817 }
1818 }
1819}
1820//----------------------------------------------------------------------------------------------------
1821void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen, bool nonexistent_utxo_seen, const tx_cache_data &tx_cache_data, std::pair<std::map<std::pair<uint64_t, uint64_t>, size_t>, std::map<std::pair<std::array<char, 32>, size_t>, size_t>> *output_tracker_cache)
1822{
1823 PERF_TIMER(process_new_transaction);
1824 // In this function, tx (probably) only contains the base information
1825 // (that is, the prunable stuff may or may not be included)
1826 if (!miner_tx && !pool)
1827 process_unconfirmed(txid, tx, height);
1828 std::unordered_map<cryptonote::subaddress_index, uint64_t> tx_etn_got_in_outs; // per receiving subaddress index
1829 crypto::public_key tx_pub_key = null_pkey;
1830 bool notify = false;
1831
1832 std::vector<tx_extra_field> local_tx_extra_fields;
1833 if (tx_cache_data.tx_extra_fields.empty())
1834 {
1835 if(!parse_tx_extra(tx.extra, local_tx_extra_fields))
1836 {
1837 // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key
1838 LOG_PRINT_L0("Transaction extra has unsupported format: " << txid);
1839 }
1840 }
1841 const std::vector<tx_extra_field> &tx_extra_fields = tx_cache_data.tx_extra_fields.empty() ? local_tx_extra_fields : tx_cache_data.tx_extra_fields;
1842
1843 // Don't try to extract tx public key if tx has no ouputs
1844 size_t pk_index = 0; //this is an index for searching for pubkeys
1845 std::vector<tx_scan_info_t> tx_scan_info(tx.vout.size());
1846 std::deque<bool> output_found(tx.vout.size(), false);
1847 uint64_t total_received_1 = 0;
1848
1849 while (!tx.vout.empty()) {
1850 std::vector<size_t> outs;
1851
1852 // if tx.vout is not empty, we loop through all tx pubkeys
1853 tx_extra_pub_key pub_key_field;
1854 if (tx.version == 1) {
1855 if (!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, pk_index++)) {
1856 if (pk_index > 1) // we should find we hit this afer one iteration for v1 transactions
1857 break;
1858 LOG_PRINT_L0("Public key wasn't found in the transaction extra. Skipping transaction " << txid);
1859 if (0 != m_callback)
1860 m_callback->on_skip_transaction(height, txid, tx);
1861 break;
1862 }
1863 if (!tx_cache_data.primary.empty()) {
1864 THROW_WALLET_EXCEPTION_IF(tx_cache_data.primary.size() < pk_index ||
1865 pub_key_field.pub_key != tx_cache_data.primary[pk_index - 1].pkey,
1866 error::wallet_internal_error, "tx_cache_data is out of sync");
1867 }
1868 }
1869 int num_vouts_received = 0;
1870 tx_pub_key = pub_key_field.pub_key;
1873 const cryptonote::account_keys &keys = m_account.get_keys();
1874 crypto::key_derivation derivation;
1875
1876 std::vector<crypto::key_derivation> additional_derivations;
1877 tx_extra_additional_pub_keys additional_tx_pub_keys;
1878
1879 const wallet2::is_out_data *is_out_data_ptr = NULL; //will point to pre-cached tx data if data is available
1880
1881 if (tx.version == 1) {
1882 // THIS IF/ELSE IS PURELY PROCESSING DERIVATIONS (DIFFIE H SHARED SECRETS aR1....aRN) FOR V1 TX
1883 if (tx_cache_data.primary.empty()) {
1884 hw::device &hwdev = m_account.get_device();
1885 boost::unique_lock<hw::device> hwdev_lock(hwdev);
1886 hw::reset_mode rst(hwdev);
1887
1889 if (!hwdev.generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation)) {
1890 MWARNING("Failed to generate key derivation from tx pubkey in " << txid << ", skipping");
1891 static_assert(sizeof(derivation) == sizeof(rct::key),
1892 "Mismatched sizes of key_derivation and rct::key");
1893 memcpy(&derivation, rct::identity().bytes, sizeof(derivation));
1894 }
1895
1896 if (pk_index == 1) {
1897 // additional tx pubkeys and derivations for multi-destination transfers involving one or more subaddresses
1898 if (find_tx_extra_field_by_type(tx_extra_fields, additional_tx_pub_keys)) {
1899 for (size_t i = 0; i < additional_tx_pub_keys.data.size(); ++i) {
1900 additional_derivations.push_back({});
1901 if (!hwdev.generate_key_derivation(additional_tx_pub_keys.data[i],
1902 keys.m_view_secret_key,
1903 additional_derivations.back())) {
1904 MWARNING("Failed to generate key derivation from additional tx pubkey in " << txid
1905 << ", skipping");
1906 memcpy(&additional_derivations.back(), rct::identity().bytes,
1907 sizeof(crypto::key_derivation));
1908 }
1909 }
1910 }
1911 }
1912 } else {
1913 THROW_WALLET_EXCEPTION_IF(pk_index - 1 >= tx_cache_data.primary.size(),
1914 error::wallet_internal_error, "pk_index out of range of tx_cache_data");
1915 is_out_data_ptr = &tx_cache_data.primary[pk_index - 1];
1916 derivation = tx_cache_data.primary[pk_index - 1].derivation;
1917 if (pk_index == 1) {
1918 for (size_t n = 0; n < tx_cache_data.additional.size(); ++n) {
1919 additional_tx_pub_keys.data.push_back(tx_cache_data.additional[n].pkey);
1920 additional_derivations.push_back(tx_cache_data.additional[n].derivation);
1921 }
1922 }
1923 }
1924 }
1925 // END OF DERIVATIONS PROCESSING (V1 ONLY)
1926 // NOW WE BEGIN TO CHECK THE OUTS //
1927
1928 //if prior precomp have built the cache, then set is out_data_ptr. Otherwise later thread (check_acc_out_precomp) will find the info itself
1929 if (tx.version > 1 && !tx_cache_data.public_outs.empty()) {
1930 is_out_data_ptr = &tx_cache_data.public_outs[0];
1931 }
1932
1933 // IGNORE COINBASE
1934 if (miner_tx && m_refresh_type == RefreshNoCoinbase) {
1935 // assume coinbase isn't for us
1936 }
1937 // PROCESS COINBASE
1938 else if (miner_tx && m_refresh_type == RefreshOptimizeCoinbase) {
1939 //put amount in the scan info this time and check output it correct type... before (process_parsed_blocks/geniod) we only precomputed whether we owned an output or not.
1940 //both tx_scan_info and output_found are populated INSIDE the precomp function only
1941 check_acc_out_precomp_once(tx.vout[0], derivation, additional_derivations, 0, is_out_data_ptr,
1942 tx_scan_info[0], output_found[0]); //is the miner tx ours?
1943 THROW_WALLET_EXCEPTION_IF(tx_scan_info[0].error, error::acc_outs_lookup_error, tx, tx_pub_key,
1944 m_account.get_keys());
1945
1946 // this assumes that the miner tx pays a single address
1947 if (tx_scan_info[0].received) {
1948 // process the other outs from that miner tx. the first one was already checked
1949 for (size_t i = 1; i < tx.vout.size(); ++i) {
1950 tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp_once, this, std::cref(tx.vout[i]),
1951 std::cref(derivation), std::cref(additional_derivations), i,
1952 std::cref(is_out_data_ptr), std::ref(tx_scan_info[i]),
1953 std::ref(output_found[i])), true);
1954 }
1955 waiter.wait(&tpool);
1956 // then scan all outputs from 0
1957 hw::device &hwdev = m_account.get_device();
1958 boost::unique_lock<hw::device> hwdev_lock(hwdev);
1960 for (size_t i = 0; i < tx.vout.size(); ++i) {
1961 THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key,
1962 m_account.get_keys());
1963 if (tx_scan_info[i].received) //at this point we are only scanning the entried that we marked as received in precomp
1964 {
1965 if (tx.version == 1) {
1966 hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key,
1967 additional_tx_pub_keys.data, derivation, additional_derivations);
1968 }
1969 scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_etn_got_in_outs,
1970 outs, pool);
1971 }
1972 }
1973 }
1974 }
1975 // PROCESS SINGLE NON COINBASE TX (IF THEY EXIST) WITH THREADS IF THERE IS MORE THAN ONE OUT AND MULTITHREADING IS PERMITTED
1976 else if (tx.vout.size() > 1 && tools::threadpool::getInstance().get_max_concurrency() > 1 && !is_out_data_ptr) {
1977 for (size_t i = 0; i < tx.vout.size(); ++i) {
1978 tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp_once, this, std::cref(tx.vout[i]),
1979 std::cref(derivation), std::cref(additional_derivations), i,
1980 std::cref(is_out_data_ptr), std::ref(tx_scan_info[i]),
1981 std::ref(output_found[i])), true);
1982 }
1983 waiter.wait(&tpool);
1984
1985 hw::device &hwdev = m_account.get_device();
1986 boost::unique_lock<hw::device> hwdev_lock(hwdev);
1988 for (size_t i = 0; i < tx.vout.size(); ++i) {
1989 THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key,
1990 m_account.get_keys());
1991 if (tx_scan_info[i].received) //at this point we are only scanning the entried that we marked as received in precomp
1992 {
1993 // todo: 4.0.0.0 ledger code only
1994 if (tx.version == 1) {
1995 hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key,
1996 additional_tx_pub_keys.data,
1997 derivation, additional_derivations);
1998 }
1999 scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_etn_got_in_outs,
2000 outs, pool);
2001 }
2002 }
2003 }
2004 // IF ONLY ONE OUT OR MULTITHREADING ISN'T ENABLED, PROCESS SINGLE TX NORMALLY
2005 else {
2006 for (size_t i = 0; i < tx.vout.size(); ++i) {
2007 check_acc_out_precomp_once(tx.vout[i], derivation, additional_derivations, i, is_out_data_ptr,
2008 tx_scan_info[i], output_found[i]);
2009 THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key,
2010 m_account.get_keys());
2011 if (tx_scan_info[i].received) //at this point we are only scanning the entried that we marked as received in precomp
2012 {
2013 hw::device &hwdev = m_account.get_device();
2014 boost::unique_lock<hw::device> hwdev_lock(hwdev);
2016 if (tx.version == 1) {
2017 hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key,
2018 additional_tx_pub_keys.data,
2019 derivation, additional_derivations);
2020 }
2021 scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_etn_got_in_outs,
2022 outs, pool);
2023 }
2024 }
2025 }
2026 if (tx.version == 1){
2027 if (!outs.empty() && num_vouts_received > 0) { // we will loop over outs below, which is just the index
2028 //good news - got etn! take care about it
2029 //usually we have only one transfer for user in transaction
2030 if (!pool) {
2031 THROW_WALLET_EXCEPTION_IF(tx.vout.size() != o_indices.size(), error::wallet_internal_error,
2032 "transactions outputs size=" + std::to_string(tx.vout.size()) +
2033 " not match with daemon response size=" + std::to_string(o_indices.size()));
2034 }
2035
2036 for (const size_t o: outs) {
2037 THROW_WALLET_EXCEPTION_IF(tx.vout.size() <= o, error::wallet_internal_error,
2038 "wrong out in transaction: internal index=" +
2039 std::to_string(o) + ", total_outs=" + std::to_string(tx.vout.size()));
2040
2041 auto kit = m_pub_keys.find(tx_scan_info[o].in_ephemeral.pub);
2042 //stealth address already exists in a transfer entry or we have more pubkeys than transfers for some unkown reason
2043 THROW_WALLET_EXCEPTION_IF(kit != m_pub_keys.end() && kit->second >= m_transfers.size(),
2044 error::wallet_internal_error,
2045 std::string("Unexpected transfer index from public key: ")
2046 + "got " +
2047 (kit == m_pub_keys.end() ? "<none>" : boost::lexical_cast<std::string>(
2048 kit->second))
2049 + ", m_transfers.size() is " +
2050 boost::lexical_cast<std::string>(m_transfers.size()));
2051 if (kit == m_pub_keys.end()) { //typical
2052 uint64_t amount = tx.vout[o].amount ? tx.vout[o].amount : tx_scan_info[o].amount;
2053 if (!pool) {
2054 m_transfers.push_back(boost::value_initialized<transfer_details>());
2055 transfer_details &td = m_transfers.back();
2056 td.m_block_height = height;
2057 td.m_internal_output_index = o;
2058 td.m_global_output_index = o_indices[o];
2059 td.m_tx = (const cryptonote::transaction_prefix &) tx;
2060 td.m_txid = txid;
2061 td.m_key_image = tx_scan_info[o].ki;
2062 td.m_key_image_known = !m_watch_only && !m_multisig;
2063 if (!td.m_key_image_known) {
2064 // we might have cold signed, and have a mapping to key images
2065 std::unordered_map<crypto::public_key, crypto::key_image>::const_iterator i = m_cold_key_images.find(
2066 tx_scan_info[o].in_ephemeral.pub);
2067 if (i != m_cold_key_images.end()) {
2068 td.m_key_image = i->second;
2069 td.m_key_image_known = true;
2070 }
2071 }
2072 if (m_watch_only) {
2073 // for view wallets, that flag means "we want to request it"
2074 td.m_key_image_request = true;
2075 } else {
2076 td.m_key_image_request = false;
2077 }
2078 td.m_key_image_partial = m_multisig;
2079 td.m_amount = amount;
2080 td.m_pk_index = pk_index - 1;
2081 td.m_subaddr_index = tx_scan_info[o].received->index;
2082 expand_subaddresses(tx_scan_info[o].received->index);
2083
2084 //TODO: Public
2085 td.m_mask = rct::identity();
2086 td.m_rct = false;
2087
2088 td.m_frozen = false;
2089 set_unspent(m_transfers.size() - 1);
2090 if (td.m_key_image_known)
2091 m_key_images[td.m_key_image] = m_transfers.size() - 1;
2092 m_pub_keys[tx_scan_info[o].in_ephemeral.pub] = m_transfers.size() - 1;
2093 if (output_tracker_cache)
2094 (*output_tracker_cache).first[std::make_pair(tx.vout[o].amount, td.m_global_output_index)] =
2095 m_transfers.size() - 1;
2096 if (m_multisig) {
2097 THROW_WALLET_EXCEPTION_IF(!m_multisig_rescan_k && m_multisig_rescan_info,
2098 error::wallet_internal_error, "NULL m_multisig_rescan_k");
2099 if (m_multisig_rescan_info &&
2100 m_multisig_rescan_info->front().size() >= m_transfers.size())
2101 update_multisig_rescan_info(*m_multisig_rescan_k, *m_multisig_rescan_info,
2102 m_transfers.size() - 1);
2103 }
2104 LOG_PRINT_L0("Received ETN: " << print_etn(td.amount()) << ", with tx: " << txid);
2105 if (0 != m_callback)
2106 m_callback->on_etn_received(height, txid, tx, td.m_amount, td.m_subaddr_index,
2107 td.m_tx.unlock_time);
2108 }
2109 total_received_1 += amount;
2110 notify = true;
2111 } else if (m_transfers[kit->second].m_spent ||
2112 // if weve seen this stealth before, check if it's spent or if the amount is larger or equal than the one we scanned for
2113 m_transfers[kit->second].amount() >= tx_scan_info[o].amount) {
2114 LOG_ERROR("Public key " << epee::string_tools::pod_to_hex(kit->first)
2115 << " from received " << print_etn(tx_scan_info[o].amount)
2116 << " output already exists with "
2117 << (m_transfers[kit->second].m_spent ? "spent" : "unspent") << " "
2118 << print_etn(m_transfers[kit->second].amount()) << " in tx "
2119 << m_transfers[kit->second].m_txid << ", received output ignored");
2121 tx_etn_got_in_outs[tx_scan_info[o].received->index] < tx_scan_info[o].amount,
2122 error::wallet_internal_error, "Unexpected values of new and old outputs");
2123 tx_etn_got_in_outs[tx_scan_info[o].received->index] -= tx_scan_info[o].amount;
2124 } else { //otherwise, we might still have this stealth on file, but we found an out to the same stealth with a greater amount during scan.... therefore swap with file version
2125 LOG_ERROR("Public key " << epee::string_tools::pod_to_hex(kit->first)
2126 << " from received " << print_etn(tx_scan_info[o].amount)
2127 << " output already exists with "
2128 << print_etn(m_transfers[kit->second].amount())
2129 << ", replacing with new output");
2130 // The new larger output replaced a previous smaller one
2132 tx_etn_got_in_outs[tx_scan_info[o].received->index] < tx_scan_info[o].amount,
2133 error::wallet_internal_error, "Unexpected values of new and old outputs");
2134 THROW_WALLET_EXCEPTION_IF(m_transfers[kit->second].amount() > tx_scan_info[o].amount,
2135 error::wallet_internal_error,
2136 "Unexpected values of new and old outputs");
2137 tx_etn_got_in_outs[tx_scan_info[o].received->index] -= m_transfers[kit->second].amount();
2138
2139 uint64_t amount = tx.vout[o].amount ? tx.vout[o].amount : tx_scan_info[o].amount;
2140 uint64_t extra_amount = amount - m_transfers[kit->second].amount();
2141 if (!pool) {
2142 transfer_details &td = m_transfers[kit->second];
2143 td.m_block_height = height;
2144 td.m_internal_output_index = o;
2145 td.m_global_output_index = o_indices[o];
2146 td.m_tx = (const cryptonote::transaction_prefix &) tx;
2147 td.m_txid = txid;
2148 td.m_amount = amount;
2149 td.m_pk_index = pk_index - 1;
2150 td.m_subaddr_index = tx_scan_info[o].received->index;
2151 expand_subaddresses(tx_scan_info[o].received->index);
2152
2153 //TODO: Public
2154 td.m_mask = rct::identity();
2155 td.m_rct = false;
2156
2157 if (output_tracker_cache)
2158 (*output_tracker_cache).first[std::make_pair(tx.vout[o].amount,
2159 td.m_global_output_index)] = kit->second;
2160 if (m_multisig) {
2161 THROW_WALLET_EXCEPTION_IF(!m_multisig_rescan_k && m_multisig_rescan_info,
2162 error::wallet_internal_error, "NULL m_multisig_rescan_k");
2163 if (m_multisig_rescan_info &&
2164 m_multisig_rescan_info->front().size() >= m_transfers.size())
2165 update_multisig_rescan_info(*m_multisig_rescan_k, *m_multisig_rescan_info,
2166 m_transfers.size() - 1);
2167 }
2168 THROW_WALLET_EXCEPTION_IF(td.get_public_key() != tx_scan_info[o].in_ephemeral.pub,
2169 error::wallet_internal_error, "Inconsistent public keys");
2170 THROW_WALLET_EXCEPTION_IF(td.m_spent, error::wallet_internal_error,
2171 "Inconsistent spent status");
2172
2173 LOG_PRINT_L1("Received ETN: " << print_etn(td.amount()) << ", with tx: " << txid);
2174 if (0 != m_callback)
2175 m_callback->on_etn_received(height, txid, tx, td.m_amount, td.m_subaddr_index,
2176 td.m_tx.unlock_time);
2177 }
2178 total_received_1 += extra_amount;
2179 notify = true;
2180 }
2181 }
2182 }
2183 } //end of v1 tx outs processing
2184 else { //process v2+ tx outs
2185 if (!outs.empty() && num_vouts_received > 0) { // we will loop over outs below, which is just the index
2186 //good news - got etn! take care about it
2187 //usually we have only one transfer for user in transaction
2188 for (const size_t o: outs) {
2189 THROW_WALLET_EXCEPTION_IF(tx.vout.size() <= o, error::wallet_internal_error,
2190 "wrong out in transaction: internal index=" +
2191 std::to_string(o) + ", total_outs=" + std::to_string(tx.vout.size()));
2192
2193 auto kit = m_chainstate_indexes.find(std::make_pair(txid, o));
2194 // Chainstate index already exists in a transfer entry or we have more chainstate indexes than transfers for some unkown reason
2195 THROW_WALLET_EXCEPTION_IF(kit != m_chainstate_indexes.end() && kit->second >= m_transfers.size(),
2196 error::wallet_internal_error,
2197 std::string("Unexpected transfer index from chainstate index: ")
2198 + "got " +
2199 (kit == m_chainstate_indexes.end() ? "<none>" : boost::lexical_cast<std::string>(
2200 kit->second))
2201 + ", m_transfers.size() is " +
2202 boost::lexical_cast<std::string>(m_transfers.size()));
2203 if (kit == m_chainstate_indexes.end()) { //typical
2204 uint64_t amount = tx.vout[o].amount ? tx.vout[o].amount : tx_scan_info[o].amount;
2205 if (!pool) {
2206 m_transfers.push_back(boost::value_initialized<transfer_details>());
2207 transfer_details &td = m_transfers.back();
2208 td.m_block_height = height;
2209 td.m_internal_output_index = o;
2210 td.m_global_output_index = std::numeric_limits<uint64_t>::max();
2211 td.m_tx = (const cryptonote::transaction_prefix &) tx;
2212 td.m_txid = txid;
2213 td.m_key_image = boost::value_initialized<crypto::key_image>();
2214 td.m_key_image_known = false;
2215 td.m_key_image_partial = false;
2216 td.m_amount = amount;
2217 td.m_pk_index = pk_index - 1;
2218 td.m_subaddr_index = tx_scan_info[o].received->index;
2219 expand_subaddresses(tx_scan_info[o].received->index);
2220 td.m_mask = rct::identity();
2221 td.m_rct = false;
2222 td.m_frozen = false;
2223 set_unspent(m_transfers.size() - 1, true);
2224 m_chainstate_indexes[std::make_pair(txid, o)] = m_transfers.size() - 1;
2225 if (output_tracker_cache) {
2226 std::array<char, 32> transaction_id;
2227 std::copy(std::begin(td.m_txid.data), std::end(td.m_txid.data), transaction_id.begin());
2228 (*output_tracker_cache).second[std::make_pair(transaction_id, td.m_internal_output_index)] =
2229 m_transfers.size() - 1;
2230 }
2231 if (m_multisig) {
2232 THROW_WALLET_EXCEPTION_IF(!m_multisig_rescan_k && m_multisig_rescan_info,
2233 error::wallet_internal_error, "NULL m_multisig_rescan_k");
2234 if (m_multisig_rescan_info &&
2235 m_multisig_rescan_info->front().size() >= m_transfers.size())
2236 update_multisig_rescan_info(*m_multisig_rescan_k, *m_multisig_rescan_info,
2237 m_transfers.size() - 1);
2238 }
2239 LOG_PRINT_L0("Received ETN: " << print_etn(td.amount()) << ", with tx: " << txid);
2240 if (0 != m_callback)
2241 m_callback->on_etn_received(height, txid, tx, td.m_amount, td.m_subaddr_index,
2242 td.m_tx.unlock_time);
2243 }
2244 total_received_1 += amount;
2245 notify = true;
2246 } else if (m_transfers[kit->second].m_spent ||
2247 // if weve seen this chainstate index before, check if it's spent or if the amount is larger or equal than the one we scanned for
2248 m_transfers[kit->second].amount() >= tx_scan_info[o].amount) {
2249 LOG_ERROR("Chainstate index " << epee::string_tools::pod_to_hex(kit->first.first)
2250 << ":" << kit->first.second
2251 << " from received " << print_etn(tx_scan_info[o].amount)
2252 << " output already exists with "
2253 << (m_transfers[kit->second].m_spent ? "spent" : "unspent") << " "
2254 << print_etn(m_transfers[kit->second].amount()) << " in tx "
2255 << m_transfers[kit->second].m_txid << ", received output ignored");
2257 tx_etn_got_in_outs[tx_scan_info[o].received->index] < tx_scan_info[o].amount,
2258 error::wallet_internal_error, "Unexpected values of new and old outputs");
2259 tx_etn_got_in_outs[tx_scan_info[o].received->index] -= tx_scan_info[o].amount;
2260 } else { //otherwise, we might still have this chainstate index on file, but we found an out to the same stealth with a greater amount during scan.... therefore swap with file version
2261 LOG_ERROR("Chainstate index " << epee::string_tools::pod_to_hex(kit->first.first)
2262 << ":" << kit->first.second
2263 << " from received " << print_etn(tx_scan_info[o].amount)
2264 << " output already exists with "
2265 << print_etn(m_transfers[kit->second].amount())
2266 << ", replacing with new output");
2267 // The new larger output replaced a previous smaller one
2269 tx_etn_got_in_outs[tx_scan_info[o].received->index] < tx_scan_info[o].amount,
2270 error::wallet_internal_error, "Unexpected values of new and old outputs");
2271 THROW_WALLET_EXCEPTION_IF(m_transfers[kit->second].amount() > tx_scan_info[o].amount,
2272 error::wallet_internal_error,
2273 "Unexpected values of new and old outputs");
2274 tx_etn_got_in_outs[tx_scan_info[o].received->index] -= m_transfers[kit->second].amount();
2275
2276 uint64_t amount = tx.vout[o].amount ? tx.vout[o].amount : tx_scan_info[o].amount;
2277 uint64_t extra_amount = amount - m_transfers[kit->second].amount();
2278 if (!pool) {
2279 transfer_details &td = m_transfers[kit->second];
2280 td.m_block_height = height;
2281 td.m_internal_output_index = o;
2282 td.m_global_output_index = std::numeric_limits<uint64_t>::max();
2283 td.m_tx = (const cryptonote::transaction_prefix &) tx;
2284 td.m_txid = txid;
2285 td.m_amount = amount;
2286 td.m_pk_index = pk_index - 1;
2287 td.m_subaddr_index = tx_scan_info[o].received->index;
2288 expand_subaddresses(tx_scan_info[o].received->index);
2289 td.m_mask = rct::identity();
2290 td.m_rct = false;
2291
2292 if (output_tracker_cache) {
2293 std::array<char, 32> transaction_id;
2294 std::copy(std::begin(td.m_txid.data), std::end(td.m_txid.data), transaction_id.begin());
2295 (*output_tracker_cache).second[std::make_pair(transaction_id,
2296 td.m_internal_output_index)] = kit->second;
2297 }
2298 if (m_multisig) {
2299 THROW_WALLET_EXCEPTION_IF(!m_multisig_rescan_k && m_multisig_rescan_info,
2300 error::wallet_internal_error, "NULL m_multisig_rescan_k");
2301 if (m_multisig_rescan_info &&
2302 m_multisig_rescan_info->front().size() >= m_transfers.size())
2303 update_multisig_rescan_info(*m_multisig_rescan_k, *m_multisig_rescan_info,
2304 m_transfers.size() - 1);
2305 }
2306 THROW_WALLET_EXCEPTION_IF(td.get_chainstate_index() != std::make_pair(txid, o),
2307 error::wallet_internal_error, "Inconsistent public keys");
2308 THROW_WALLET_EXCEPTION_IF(td.m_spent, error::wallet_internal_error,
2309 "Inconsistent spent status");
2310
2311 LOG_PRINT_L1("Received ETN: " << print_etn(td.amount()) << ", with tx: " << txid);
2312 if (0 != m_callback)
2313 m_callback->on_etn_received(height, txid, tx, td.m_amount, td.m_subaddr_index,
2314 td.m_tx.unlock_time);
2315 }
2316 total_received_1 += extra_amount;
2317 notify = true;
2318 }
2319 }
2320 }
2321 break; // we don't have to iterate again for v2+ outs becuase there aren't multiple pubkeys (see pk index)
2322 } //end of v2+ outs processing
2323 } // end of all outs processing
2324
2325 uint64_t tx_etn_spent_in_ins = 0;
2326 // The line below is equivalent to "boost::optional<uint32_t> subaddr_account;", but avoids the GCC warning: ‘*((void*)& subaddr_account +4)’ may be used uninitialized in this function
2327 // It's a GCC bug with boost::optional, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47679
2328 auto subaddr_account ([]()->boost::optional<uint32_t> {return boost::none;}());
2329 std::set<uint32_t> subaddr_indices;
2330 // check all outputs for spending (compare key images)
2331 for(auto& in: tx.vin)
2332 {
2333 if(tx.version < 3) { // we still use old txin_to_key for migration transactions (v2)
2334 if (in.type() != typeid(cryptonote::txin_to_key))
2335 continue;
2336 const cryptonote::txin_to_key &in_to_key = boost::get<cryptonote::txin_to_key>(in);
2337 auto it = m_key_images.find(in_to_key.k_image);
2338 if (it != m_key_images.end()) //these are UNspent key images
2339 {
2340 transfer_details &td = m_transfers[it->second];
2341 uint64_t amount = in_to_key.amount;
2342 if (amount > 0) {
2343 if (amount != td.amount()) {
2344 MERROR("Inconsistent amount in tx input: got " << print_etn(amount) <<
2345 ", expected " << print_etn(td.amount()));
2346 // this means:
2347 // 1) the same output pub key was used as destination multiple times,
2348 // 2) the wallet set the highest amount among them to transfer_details::m_amount, and
2349 // 3) the wallet somehow spent that output with an amount smaller than the above amount, causing inconsistency
2350 td.m_amount = amount;
2351 }
2352 } else {
2353 amount = td.amount();
2354 }
2355 tx_etn_spent_in_ins += amount;
2356 if (subaddr_account && *subaddr_account != td.m_subaddr_index.major)
2357 LOG_ERROR(
2358 "spent funds are from different subaddress accounts; count of incoming/outgoing payments will be incorrect");
2359 subaddr_account = td.m_subaddr_index.major;
2360 subaddr_indices.insert(td.m_subaddr_index.minor);
2361 if (!pool) {
2362 LOG_PRINT_L1("Spent ETN: " << print_etn(amount) << ", with tx: " << txid);
2363 set_spent(it->second, height);
2364 if (0 != m_callback)
2365 m_callback->on_etn_spent(height, txid, tx, amount, tx, td.m_subaddr_index);
2366 }
2367 }
2368
2369 if (!pool && m_track_uses) {
2370 PERF_TIMER(track_uses);
2371 const uint64_t amount = in_to_key.amount; //amount to check against transfer details
2372 std::vector<uint64_t> offsets = cryptonote::relative_output_offsets_to_absolute(
2373 in_to_key.key_offsets); //todo: 4.0.0.0
2374 if (output_tracker_cache) {
2375 for (uint64_t offset: offsets) {
2376 const std::map<std::pair<uint64_t, uint64_t>, size_t>::const_iterator i = output_tracker_cache->first.find(
2377 std::make_pair(amount, offset));
2378 if (i != output_tracker_cache->first.end()) {
2379 size_t idx = i->second;
2380 THROW_WALLET_EXCEPTION_IF(idx >= m_transfers.size(), error::wallet_internal_error,
2381 "Output tracker cache index out of range");
2382 m_transfers[idx].m_uses.push_back(std::make_pair(height, txid));
2383 }
2384 }
2385 } else {
2386 //essentially the long way of doing it without a cache - loop over all m_transfers to find a match
2387 for (transfer_details &td: m_transfers) {
2388 if (amount != td.m_amount) //need to check against the amounts in the list of transfer details to find the matching amount and global out index
2389 continue;
2390 for (uint64_t offset: offsets)
2391 if (offset == td.m_global_output_index)
2392 td.m_uses.push_back(std::make_pair(height, txid));
2393 }
2394 }
2395 }
2396 }else{ // Public inputs (v3)
2397 if (in.type() != typeid(cryptonote::txin_to_key_public))
2398 continue;
2399 const cryptonote::txin_to_key_public &in_to_key_public = boost::get<cryptonote::txin_to_key_public>(in);
2400 auto it = m_chainstate_indexes.find(std::make_pair(in_to_key_public.tx_hash, in_to_key_public.relative_offset));
2401 if (it != m_chainstate_indexes.end()) //these are UNspent chainstate indexes
2402 {
2403 transfer_details &td = m_transfers[it->second];
2404 uint64_t amount = in_to_key_public.amount; // here we're just grabbing the amount of the input from m_transfers
2405 if (amount > 0) {
2406 if (amount != td.amount()) {
2407 MERROR("Inconsistent amount in tx input: got " << print_etn(amount) <<
2408 ", expected " << print_etn(td.amount()));
2409 // this means:
2410 // 1) the same chainstate index was used as destination multiple times,
2411 // 2) the wallet set the highest amount among them to transfer_details::m_amount, and
2412 // 3) the wallet somehow spent that output with an amount smaller than the above amount, causing inconsistency
2413 td.m_amount = amount;
2414 }
2415 } else {
2416 amount = td.amount();
2417 }
2418 tx_etn_spent_in_ins += amount;
2419 if (subaddr_account && *subaddr_account != td.m_subaddr_index.major)
2420 LOG_ERROR(
2421 "spent funds are from different subaddress accounts; count of incoming/outgoing payments will be incorrect");
2422 subaddr_account = td.m_subaddr_index.major;
2423 subaddr_indices.insert(td.m_subaddr_index.minor);
2424 if (!pool) {
2425 LOG_PRINT_L1("Spent ETN: " << print_etn(amount) << ", with tx: " << txid);
2426 set_spent(it->second, height, true);
2427 if (0 != m_callback)
2428 m_callback->on_etn_spent(height, txid, tx, amount, tx, td.m_subaddr_index);
2429 }
2430 }
2431
2432 if (!pool && m_track_uses) {
2433 PERF_TIMER(track_uses);
2434 const uint64_t amount = in_to_key_public.amount;
2435 if (output_tracker_cache) {
2436 std::array<char, 32> transaction_id;
2437 std::copy(std::begin(in_to_key_public.tx_hash.data), std::end(in_to_key_public.tx_hash.data), transaction_id.begin());
2438 const std::map<std::pair<std::array<char, 32>, size_t>, size_t>::const_iterator i = output_tracker_cache->second.find(
2439 std::make_pair(transaction_id, in_to_key_public.relative_offset));
2440 if (i != output_tracker_cache->second.end()) {
2441 size_t idx = i->second;
2442 THROW_WALLET_EXCEPTION_IF(idx >= m_transfers.size(), error::wallet_internal_error,
2443 "Output tracker cache index out of range");
2444 m_transfers[idx].m_uses.push_back(std::make_pair(height, txid));
2445 }
2446 } else
2447 for (transfer_details &td: m_transfers) {
2448 if (in_to_key_public.tx_hash != td.m_txid)
2449 continue;
2450 if (in_to_key_public.relative_offset == td.m_internal_output_index)
2451 td.m_uses.push_back(std::make_pair(height, txid));
2452 }
2453 }
2454 }
2455 }
2456
2457 uint64_t fee;
2458 if(miner_tx){
2459 fee = 0;
2460 }else{
2461 uint64_t tx_etn_total_inputs;
2462 get_inputs_etn_amount(tx, tx_etn_total_inputs);
2463 fee = tx_etn_total_inputs - get_outs_etn_amount(tx); //it doesn't matter if we personally spent ins, fee is alwasys total ins minus total outs
2464 }
2465
2466 if (tx_etn_spent_in_ins > 0 && !pool)
2467 {
2468 // only used for v1. refactor later
2469 uint64_t self_received = std::accumulate<decltype(tx_etn_got_in_outs.begin()), uint64_t>(tx_etn_got_in_outs.begin(), tx_etn_got_in_outs.end(), 0,
2470 [&subaddr_account] (uint64_t acc, const std::pair<cryptonote::subaddress_index, uint64_t>& p)
2471 {
2472 return acc + (p.first.major == *subaddr_account ? p.second : 0);
2473 });
2474 process_outgoing(txid, tx, height, ts, tx_etn_spent_in_ins, self_received, *subaddr_account, subaddr_indices);
2475 // if sending to yourself at the same subaddress account, set the outgoing payment amount to 0 so that it's less confusing
2476 if (tx_etn_spent_in_ins == self_received + fee)
2477 {
2478 auto i = m_confirmed_txs.find(txid);
2479 THROW_WALLET_EXCEPTION_IF(i == m_confirmed_txs.end(), error::wallet_internal_error,
2480 "confirmed tx wasn't found: " + string_tools::pod_to_hex(txid));
2481 if(tx.version == 1)
2482 i->second.m_change = self_received;
2483 }
2484 }
2485
2486 // remove change sent to the spending subaddress account from the list of received funds
2487 uint64_t sub_change = 0;
2488 for (auto i = tx_etn_got_in_outs.begin(); i != tx_etn_got_in_outs.end();)
2489 {
2490 if (subaddr_account && i->first.major == *subaddr_account)
2491 {
2492 sub_change += i->second;
2493 i = tx_etn_got_in_outs.erase(i);
2494 }
2495 else
2496 ++i;
2497 }
2498
2499 // create payment_details for each incoming transfer to a subaddress index
2500 if (tx_etn_got_in_outs.size() > 0)
2501 {
2502 tx_extra_nonce extra_nonce;
2503 crypto::hash payment_id = null_hash;
2504 if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
2505 {
2506 crypto::hash8 payment_id8 = null_hash8;
2507 if(get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
2508 {
2509 // We got a payment ID to go with this tx
2510 LOG_PRINT_L2("Found encrypted payment ID: " << payment_id8);
2511 MINFO("Consider using subaddresses instead of encrypted payment IDs");
2512 if (tx_pub_key != null_pkey)
2513 {
2514 if (!m_account.get_device().decrypt_payment_id(payment_id8, tx_pub_key, m_account.get_keys().m_view_secret_key))
2515 {
2516 LOG_PRINT_L0("Failed to decrypt payment ID: " << payment_id8);
2517 }
2518 else
2519 {
2520 // put the 64 bit decrypted payment id in the first 8 bytes
2521 memcpy(payment_id.data, payment_id8.data, 8);
2522 // rest is already 0, but guard against code changes above
2523 memset(payment_id.data + 8, 0, 24);
2524 LOG_PRINT_L2(" payment ID: " << payment_id);
2525 }
2526 }
2527 else
2528 {
2529 LOG_PRINT_L1("No public key found in tx, unable to decrypt payment id");
2530 }
2531 }
2532 else if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
2533 {
2534 LOG_PRINT_L2("Found unencrypted payment ID: " << payment_id);
2535 MWARNING("Found unencrypted payment ID: these are bad for privacy, consider using subaddresses instead");
2536 }
2537 }
2538
2539 uint64_t total_received_2 = sub_change;
2540 for (const auto& i : tx_etn_got_in_outs)
2541 total_received_2 += i.second;
2542 if (total_received_1 != total_received_2)
2543 {
2544 const el::Level level = el::Level::Warning;
2545 MCLOG_RED(level, "global", "**********************************************************************");
2546 MCLOG_RED(level, "global", "Consistency failure in amounts received");
2547 MCLOG_RED(level, "global", "Check transaction " << txid);
2548 MCLOG_RED(level, "global", "**********************************************************************");
2549 exit(1);
2550 return;
2551 }
2552
2553 bool all_same = true;
2554 for (const auto& i : tx_etn_got_in_outs)
2555 {
2556 payment_details payment;
2557 payment.m_tx_hash = txid;
2558 payment.m_fee = fee;
2559 payment.m_amount = i.second;
2560 payment.m_block_height = height;
2561 payment.m_unlock_time = tx.unlock_time;
2562 payment.m_timestamp = ts;
2563 payment.m_coinbase = miner_tx;
2564 payment.m_subaddr_index = i.first;
2565 if (pool) {
2566 if (emplace_or_replace(m_unconfirmed_payments, payment_id, pool_payment_details{payment, double_spend_seen, nonexistent_utxo_seen}))
2567 all_same = false;
2568 if (0 != m_callback)
2569 m_callback->on_unconfirmed_etn_received(height, txid, tx, payment.m_amount, payment.m_subaddr_index);
2570 }
2571 else
2572 m_payments.emplace(payment_id, payment);
2573 LOG_PRINT_L2("Payment found in " << (pool ? "pool" : "block") << ": " << payment_id << " / " << payment.m_tx_hash << " / " << payment.m_amount);
2574 }
2575
2576 // if it's a pool tx and we already had it, don't notify again
2577 if (pool && all_same)
2578 notify = false;
2579 }
2580
2581 if (notify)
2582 {
2583 std::shared_ptr<tools::Notify> tx_notify = m_tx_notify;
2584 if (tx_notify)
2585 tx_notify->notify("%s", epee::string_tools::pod_to_hex(txid).c_str(), NULL);
2586 }
2587}
2588//----------------------------------------------------------------------------------------------------
2589void wallet2::process_unconfirmed(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height)
2590{
2591 if (m_unconfirmed_txs.empty())
2592 return;
2593
2594 auto unconf_it = m_unconfirmed_txs.find(txid);
2595 if(unconf_it != m_unconfirmed_txs.end()) {
2596 if (store_tx_info()) {
2597 try {
2598 m_confirmed_txs.insert(std::make_pair(txid, confirmed_transfer_details(unconf_it->second, height)));
2599 }
2600 catch (...) {
2601 // can fail if the tx has unexpected input types
2602 LOG_PRINT_L0("Failed to add outgoing transaction to confirmed transaction map");
2603 }
2604 }
2605 m_unconfirmed_txs.erase(unconf_it);
2606 }
2607}
2608//----------------------------------------------------------------------------------------------------
2609void wallet2::process_outgoing(const crypto::hash &txid, const cryptonote::transaction &tx, uint64_t height, uint64_t ts, uint64_t spent, uint64_t received, uint32_t subaddr_account, const std::set<uint32_t>& subaddr_indices)
2610{
2611 std::pair<std::unordered_map<crypto::hash, confirmed_transfer_details>::iterator, bool> entry = m_confirmed_txs.insert(std::make_pair(txid, confirmed_transfer_details()));
2612 // fill with the info we know, some info might already be there
2613 if (entry.second)
2614 {
2615 // this case will happen if the tx is from our outputs, but was sent by another
2616 // wallet (eg, we're a cold wallet and the hot wallet sent it). For RCT transactions,
2617 // we only see 0 input amounts, so have to deduce amount out from other parameters.
2618 entry.first->second.m_amount_in = spent;
2619 entry.first->second.m_amount_out = get_outs_etn_amount(tx);
2620 if(tx.version == 1)
2621 entry.first->second.m_change = received;
2622
2623 std::vector<tx_extra_field> tx_extra_fields;
2624 parse_tx_extra(tx.extra, tx_extra_fields); // ok if partially parsed
2625 tx_extra_nonce extra_nonce;
2626 if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
2627 {
2628 // we do not care about failure here
2629 get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, entry.first->second.m_payment_id);
2630 }
2631 entry.first->second.m_subaddr_account = subaddr_account;
2632 entry.first->second.m_subaddr_indices = subaddr_indices;
2633 }
2634
2635 entry.first->second.m_rings.clear();
2636 for (const auto &in: tx.vin)
2637 {
2638 if (in.type() != typeid(cryptonote::txin_to_key))
2639 continue;
2640 const auto &txin = boost::get<cryptonote::txin_to_key>(in);
2641 entry.first->second.m_rings.push_back(std::make_pair(txin.k_image, txin.key_offsets));
2642 }
2643 entry.first->second.m_block_height = height;
2644 entry.first->second.m_timestamp = ts;
2645 entry.first->second.m_unlock_time = tx.unlock_time;
2646 entry.first->second.m_is_migration = tx.version == 2;
2647
2648 // is tx going to the portal address? check the first output's dest...
2649 if(tx.version == 3){ //
2650 cryptonote::account_public_address dest_address = boost::get<cryptonote::txout_to_key_public>(tx.vout[0].target).address; //
2651 std::string portal_address_viewkey_hex_str;
2652 std::string portal_address_spendkey_hex_str;
2653 bool is_portal_address;
2654 if(nettype() == MAINNET){
2655 is_portal_address = epee::string_tools::pod_to_hex(dest_address.m_spend_public_key) == "8ce0f34fd37c7f7d07c44024eb5b3cdf275d1b3e75c3464b808dce532e861137" && epee::string_tools::pod_to_hex(dest_address.m_view_public_key) == "2b95a2eb2c62253c57e82b082b850bbf22a1a7829aaea09c7c1511c1cced4375";
2656 }else{
2657 is_portal_address = epee::string_tools::pod_to_hex(dest_address.m_spend_public_key) == "5bd0c0e25eee6133850edd2b255ed9e3d6bb99fd5f08b7b5cf7f2618ad6ff2a3" && epee::string_tools::pod_to_hex(dest_address.m_view_public_key) == "5866666666666666666666666666666666666666666666666666666666666666";
2658 }
2659
2660
2661 entry.first->second.m_is_sc_migration = is_portal_address;
2662 }
2663
2664
2665 if(tx.version > 1){
2666 // grab the input owner keys/address by using the subaddr indicies used for the transaction
2667 std::vector<account_public_address> input_addresses;
2668 for (auto minor_index : subaddr_indices) {
2669 cryptonote::subaddress_index index{subaddr_account, minor_index};
2670 input_addresses.push_back(get_subaddress(index));
2671 }
2672
2673 //build list of potential change outputs - NB if *ALL* outs go to input addresses, then we DON'T conside them change; the transaction is a looped sweep.
2674 // If one or more outs do not go to an input address, we consider ALL other outputs as change outputs
2675 std::unordered_set<uint32_t> change_indexes;
2676 for (size_t i = 0; i < tx.vout.size(); ++i) {
2677 for (auto input_address : input_addresses) {
2678 if (boost::get<txout_to_key_public>(tx.vout[i].target).address == input_address) {
2679 change_indexes.insert(i);
2680 continue;
2681 }
2682 }
2683 }
2684
2685 // if this is true we have a sweep tx so clear all change out indexes
2686 if (change_indexes.size() == tx.vout.size()) {
2687 change_indexes.clear();
2688 }
2689
2690 int64_t total_change = 0;
2691 for (auto &change_index : change_indexes)
2692 total_change += tx.vout[change_index].amount;
2693 entry.first->second.m_change = total_change;
2694
2695 // For V2+ tx, we can repopulate tx destinations in the wallet cache during a rescan by simply reading them from the transactions
2696 //todo: optimise
2697 if (entry.first->second.m_dests.empty()) {
2698
2699 // fill destinations
2700 for (size_t i = 0; i < tx.vout.size(); ++i) {
2701 if (change_indexes.find(i) == change_indexes.end()) { // only include non-change outs as dests
2702 auto output = boost::get<txout_to_key_public>(tx.vout[i].target); // grab output from the tx
2703 //predicate for comparison later on
2704 auto pred = [output](const tx_destination_entry &destination) {
2705 return destination.addr == output.address;
2706 };
2707
2708 //search our working list of destinations in entry, and either add output amount to the
2709 // running total in the case of a match, or add a new destination otherwise
2710 auto dest_ptr = std::find_if(std::begin(entry.first->second.m_dests),
2711 std::end(entry.first->second.m_dests), pred);
2712 if (dest_ptr != std::end(entry.first->second.m_dests)) {
2713 dest_ptr->amount += tx.vout[i].amount;
2714 } else {
2715 entry.first->second.m_dests.push_back(tx_destination_entry(
2716 tx.vout[i].amount,
2717 output.address,
2718 output.m_address_prefix ==
2719 get_config(this->m_nettype).CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX ? true : false
2720 ));
2721 }
2722 }
2723 }
2724 }
2725 }
2726 add_rings(tx);
2727}
2728//----------------------------------------------------------------------------------------------------
2729bool wallet2::should_skip_block(const cryptonote::block &b, uint64_t height) const
2730{
2731 // seeking only for blocks that are not older then the wallet creation time plus 1 day. 1 day is for possible user incorrect time setup
2732 return !(b.timestamp + 60*60*24 > m_account.get_createtime() && height >= m_refresh_from_block_height);
2733}
2734//----------------------------------------------------------------------------------------------------
2735void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, const std::vector<tx_cache_data> &tx_cache_data, size_t tx_cache_data_offset, std::pair<std::map<std::pair<uint64_t, uint64_t>, size_t>, std::map<std::pair<std::array<char, 32>, size_t>, size_t>> *output_tracker_cache)
2736{
2737 if(b.major_version < 10) {
2738 THROW_WALLET_EXCEPTION_IF(bche.txs.size() + 1 != parsed_block.o_indices.indices.size(),
2739 error::wallet_internal_error,
2740 "block transactions=" + std::to_string(bche.txs.size()) +
2741 " not match with daemon response size=" +
2742 std::to_string(parsed_block.o_indices.indices.size()));
2743 }
2744
2745 //handle transactions from new block
2746
2747 //optimization: seeking only for blocks that are not older then the wallet creation time plus 1 day. 1 day is for possible user incorrect time setup
2748 if (!should_skip_block(b, height))
2749 {
2750 //FIRST PROCESS THE MINER TX FOR THE NEW BLOCK
2751 TIME_MEASURE_START(miner_tx_handle_time);
2752 if (m_refresh_type != RefreshNoCoinbase)
2753 process_new_transaction(get_transaction_hash(b.miner_tx), b.miner_tx, parsed_block.o_indices.indices[0].indices, height, b.timestamp, true, false, false, false, tx_cache_data[tx_cache_data_offset], output_tracker_cache);
2754 ++tx_cache_data_offset;
2755 TIME_MEASURE_FINISH(miner_tx_handle_time);
2756 //NOW THE OTHER TRANSACTIONS
2757 TIME_MEASURE_START(txs_handle_time);
2758 THROW_WALLET_EXCEPTION_IF(bche.txs.size() != b.tx_hashes.size(), error::wallet_internal_error, "Wrong amount of transactions for block");
2759 THROW_WALLET_EXCEPTION_IF(bche.txs.size() != parsed_block.txes.size(), error::wallet_internal_error, "Wrong amount of transactions for block");
2760 for (size_t idx = 0; idx < b.tx_hashes.size(); ++idx)
2761 {
2762 process_new_transaction(b.tx_hashes[idx], parsed_block.txes[idx], parsed_block.o_indices.indices[idx+1].indices, height, b.timestamp, false, false, false, false, tx_cache_data[tx_cache_data_offset++], output_tracker_cache);
2763 }
2764 TIME_MEASURE_FINISH(txs_handle_time);
2765 m_last_block_reward = cryptonote::get_outs_etn_amount(b.miner_tx);
2766 LOG_PRINT_L2("Processed block: " << bl_id << ", height " << height << ", " << miner_tx_handle_time + txs_handle_time << "(" << miner_tx_handle_time << "/" << txs_handle_time <<")ms");
2767 }else
2768 {
2769 if (!(height % 128))
2770 LOG_PRINT_L2( "Skipped block by timestamp, height: " << height << ", block time " << b.timestamp << ", account time " << m_account.get_createtime());
2771 }
2772 m_blockchain.push_back(bl_id);
2773
2774 if (0 != m_callback)
2775 m_callback->on_new_block(height, b);
2776}
2777//----------------------------------------------------------------------------------------------------
2778void wallet2::get_short_chain_history(std::list<crypto::hash>& ids, uint64_t granularity) const
2779{
2780 size_t i = 0;
2781 size_t current_multiplier = 1;
2782 size_t blockchain_size = std::max((size_t)(m_blockchain.size() / granularity * granularity), m_blockchain.offset());
2783 size_t sz = blockchain_size - m_blockchain.offset();
2784 if(!sz)
2785 {
2786 ids.push_back(m_blockchain.genesis());
2787 return;
2788 }
2789 size_t current_back_offset = 1;
2790 bool base_included = false;
2791 while(current_back_offset < sz)
2792 {
2793 ids.push_back(m_blockchain[m_blockchain.offset() + sz-current_back_offset]);
2794 if(sz-current_back_offset == 0)
2795 base_included = true;
2796 if(i < 10)
2797 {
2798 ++current_back_offset;
2799 }else
2800 {
2801 current_back_offset += current_multiplier *= 2;
2802 }
2803 ++i;
2804 }
2805 if(!base_included)
2806 ids.push_back(m_blockchain[m_blockchain.offset()]);
2807 if(m_blockchain.offset())
2808 ids.push_back(m_blockchain.genesis());
2809}
2810//----------------------------------------------------------------------------------------------------
2811void wallet2::parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const
2812{
2814}
2815//----------------------------------------------------------------------------------------------------
2816void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices)
2817{
2820 req.block_ids = short_chain_history;
2821
2822 req.prune = true;
2823 req.start_height = start_height;
2824 req.no_miner_tx = m_refresh_type == RefreshNoCoinbase;
2825 m_daemon_rpc_mutex.lock();
2826
2827 bool r = invoke_http_bin("/getblocks.bin", req, res, rpc_timeout);
2828
2829 m_daemon_rpc_mutex.unlock();
2830 THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getblocks.bin");
2831 THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getblocks.bin");
2832 THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_blocks_error, get_rpc_status(res.status));
2833 THROW_WALLET_EXCEPTION_IF(res.blocks.size() != res.output_indices.size(), error::wallet_internal_error,
2834 "mismatched blocks (" + boost::lexical_cast<std::string>(res.blocks.size()) + ") and output_indices (" +
2835 boost::lexical_cast<std::string>(res.output_indices.size()) + ") sizes from daemon");
2836
2837 blocks_start_height = res.start_height;
2838 blocks = std::move(res.blocks);
2839 o_indices = std::move(res.output_indices);
2840}
2841
2842//------------------------------------------------------------------------------------------------------------------------------
2843cryptonote::blobdata wallet2::get_pruned_tx_blob(const cryptonote::blobdata &blobdata)
2844{
2846
2848 {
2849 MERROR("Failed to parse and validate tx from blob");
2850 return blobdata;
2851 }
2852
2853 std::stringstream ss;
2854 binary_archive<true> ba(ss);
2855 bool r = tx.serialize_base(ba);
2856 CHECK_AND_ASSERT_MES(r, blobdata, "Failed to serialize rct signatures base");
2857 return ss.str();
2858}
2859//----------------------------------------------------------------------------------------------------
2860void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<crypto::hash> &hashes)
2861{
2864 req.block_ids = short_chain_history;
2865
2866 req.start_height = start_height;
2867 m_daemon_rpc_mutex.lock();
2868
2869 bool r = invoke_http_bin("/gethashes.bin", req, res, rpc_timeout);
2870
2871 m_daemon_rpc_mutex.unlock();
2872 THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gethashes.bin");
2873 THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gethashes.bin");
2874 THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_hashes_error, get_rpc_status(res.status));
2875
2876 blocks_start_height = res.start_height;
2877 hashes = std::move(res.m_block_ids);
2878}
2879
2880//----------------------------------------------------------------------------------------------------
2881void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cryptonote::block_complete_entry> &blocks, const std::vector<parsed_block> &parsed_blocks, uint64_t& blocks_added, std::pair<std::map<std::pair<uint64_t, uint64_t>, size_t>, std::map<std::pair<std::array<char, 32>, size_t>, size_t>> *output_tracker_cache)
2882{
2883 size_t current_index = start_height;
2884 blocks_added = 0;
2885
2886 THROW_WALLET_EXCEPTION_IF(blocks.size() != parsed_blocks.size(), error::wallet_internal_error, "size mismatch");
2887 THROW_WALLET_EXCEPTION_IF(!m_blockchain.is_in_bounds(current_index), error::out_of_hashchain_bounds_error);
2888
2891
2892 //this is the total number of tx in the batch of blocks
2893 size_t num_txes = 0;
2894 std::vector<tx_cache_data> tx_cache_data;
2895 for (size_t i = 0; i < blocks.size(); ++i)
2896 num_txes += 1 + parsed_blocks[i].txes.size();
2897 tx_cache_data.resize(num_txes);
2898 size_t txidx = 0;
2899 for (size_t i = 0; i < blocks.size(); ++i)
2900 {
2901 THROW_WALLET_EXCEPTION_IF(parsed_blocks[i].txes.size() != parsed_blocks[i].block.tx_hashes.size(),
2902 error::wallet_internal_error, "Mismatched parsed_blocks[i].txes.size() and parsed_blocks[i].block.tx_hashes.size()");
2903
2904 //ONLY CACHE TX PUBLIC KEYS R0....Rn FOR V1 TX (that's if we don't want to skip the blocks anyway)
2905 if (should_skip_block(parsed_blocks[i].block, start_height + i)){
2906 txidx += 1 + parsed_blocks[i].block.tx_hashes.size();
2907 continue;
2908 }else{
2909 if (m_refresh_type != RefreshNoCoinbase) // we're caching pubkeys only
2910 tpool.submit(&waiter, [&, i, txidx]() {
2911 cache_tx_data(parsed_blocks[i].block.miner_tx, get_transaction_hash(parsed_blocks[i].block.miner_tx),
2912 tx_cache_data[txidx]);
2913 });
2914 ++txidx;
2915 for (size_t idx = 0; idx < parsed_blocks[i].txes.size(); ++idx) {
2916 tpool.submit(&waiter, [&, i, idx, txidx]() {
2917 cache_tx_data(parsed_blocks[i].txes[idx], parsed_blocks[i].block.tx_hashes[idx], tx_cache_data[txidx]);
2918 });
2919 ++txidx;
2920 }
2921 }
2922 }
2923 THROW_WALLET_EXCEPTION_IF(txidx != num_txes, error::wallet_internal_error, "txidx does not match tx_cache_data size");
2924 waiter.wait(&tpool);
2925
2926 hw::device &hwdev = m_account.get_device();
2927 hw::reset_mode rst(hwdev);
2929 const cryptonote::account_keys &keys = m_account.get_keys();
2930
2931 auto gender = [&](wallet2::is_out_data &iod) {
2932 if (!hwdev.generate_key_derivation(iod.pkey, keys.m_view_secret_key, iod.derivation))
2933 {
2934 MWARNING("Failed to generate key derivation from tx pubkey, skipping");
2935 static_assert(sizeof(iod.derivation) == sizeof(rct::key), "Mismatched sizes of key_derivation and rct::key");
2936 memcpy(&iod.derivation, rct::identity().bytes, sizeof(iod.derivation));
2937 }
2938 };
2939
2940 //call gender above in a thread
2941 for (size_t i = 0; i < tx_cache_data.size(); ++i) {
2942 if (tx_cache_data[i].empty())
2943 continue;
2944 if(!tx_cache_data[i].public_only()){ // no need to thread key derivations for public outs
2945 tpool.submit(&waiter, [&hwdev, &gender, &tx_cache_data, i]() {
2946 auto &slot = tx_cache_data[i];
2947 boost::unique_lock<hw::device> hwdev_lock(hwdev);
2948 for (auto &iod: slot.primary)
2949 gender(iod);
2950 for (auto &iod: slot.additional)
2951 gender(iod);
2952 }, true);
2953 }
2954 }
2955
2956 waiter.wait(&tpool);
2957
2958 auto geniod = [&](const cryptonote::transaction &tx, size_t n_vouts, size_t txidx) {
2959 for (size_t k = 0; k < n_vouts; ++k)
2960 {
2961 const auto &o = tx.vout[k];
2962 if (o.target.type() == typeid(cryptonote::txout_to_key))
2963 {
2964 std::vector<crypto::key_derivation> additional_derivations;
2965 additional_derivations.reserve(tx_cache_data[txidx].additional.size());
2966 for (const auto &iod: tx_cache_data[txidx].additional)
2967 additional_derivations.push_back(iod.derivation);
2968 const auto &key = boost::get<txout_to_key>(o.target).key;
2969 for (size_t l = 0; l < tx_cache_data[txidx].primary.size(); ++l) // afaik this loop over l is for additional *primary* tx pubkeys (accidentally?) put in the extra
2970 {
2971 //NB this doesn't mean that all of the outs were received with the primary txpubkey R, it was just a convention to push this number of
2972 // empty rec's to tx_cache_data[txidx].primary in the tx_cache thread before (just in case they were)
2973 THROW_WALLET_EXCEPTION_IF(tx_cache_data[txidx].primary[l].received.size() != n_vouts,
2974 error::wallet_internal_error, "Unexpected received array size");
2975 // if we find that an out belongs to us, mark as received in the cache
2976 tx_cache_data[txidx].primary[l].received[k] = is_out_to_acc_precomp(m_subaddresses, key, tx_cache_data[txidx].primary[l].derivation, additional_derivations, k, hwdev);
2977 additional_derivations.clear();
2978 }
2979 }else if(o.target.type() == typeid(cryptonote::txout_to_key_public)){ // this is the equivalent of our ownership precomp
2980 const auto etn_address = boost::get<txout_to_key_public>(o.target).address;
2981 THROW_WALLET_EXCEPTION_IF(tx_cache_data[txidx].public_outs[0].received.size() != n_vouts,
2982 error::wallet_internal_error, "Unexpected received array size");
2983 THROW_WALLET_EXCEPTION_IF(tx_cache_data[txidx].public_outs.size() != 1,
2984 error::wallet_internal_error, "Unexpected received vector size");
2985 // no loop over l required for public outputs ^
2986
2987 //only assign subaddress recipient if view key also matches too as we now spend with combined keys (a+b) and we wont be
2988 // doing key image related checks later to check if we can really spend the out (ie checking view key match by proxy)
2989 auto receive_info = cryptonote::is_out_to_acc_precomp_public(m_subaddresses, etn_address);
2990 tx_cache_data[txidx].public_outs[0].received[k] =
2991 (receive_info == boost::none) ?
2992 (receive_info) :
2993 get_subaddress(receive_info->index).m_view_public_key == etn_address.m_view_public_key ?
2994 receive_info : boost::none; //todo: refactor with function pointers
2995
2996 }
2997 }
2998 };
2999
3000 //we reset txidx from above ready to precompute ownership.
3001 txidx = 0;
3002 for (size_t i = 0; i < blocks.size(); ++i)
3003 {
3004 if (should_skip_block(parsed_blocks[i].block, start_height + i))
3005 {
3006 txidx += 1 + parsed_blocks[i].block.tx_hashes.size();
3007 continue;
3008 }
3009
3010 //Run geniod on a thread which is a proxy for is_out_to_acc_precomp which precomputes ownership of outputs
3011 if (m_refresh_type != RefreshType::RefreshNoCoinbase)
3012 {
3013 THROW_WALLET_EXCEPTION_IF(txidx >= tx_cache_data.size(), error::wallet_internal_error, "txidx out of range");
3014 const size_t n_vouts = m_refresh_type == RefreshType::RefreshOptimizeCoinbase ? 1 : parsed_blocks[i].block.miner_tx.vout.size();
3015 tpool.submit(&waiter, [&, i, n_vouts, txidx](){ geniod(parsed_blocks[i].block.miner_tx, n_vouts, txidx); }, true);
3016 }
3017 ++txidx;
3018 for (size_t j = 0; j < parsed_blocks[i].txes.size(); ++j)
3019 {
3020 THROW_WALLET_EXCEPTION_IF(txidx >= tx_cache_data.size(), error::wallet_internal_error, "txidx out of range");
3021 tpool.submit(&waiter, [&, i, j, txidx](){ geniod(parsed_blocks[i].txes[j], parsed_blocks[i].txes[j].vout.size(), txidx); }, true);
3022 ++txidx;
3023 }
3024 }
3025 THROW_WALLET_EXCEPTION_IF(txidx != tx_cache_data.size(), error::wallet_internal_error, "txidx did not reach expected value");
3026 waiter.wait(&tpool);
3028
3029 size_t tx_cache_data_offset = 0;
3030 for (size_t i = 0; i < blocks.size(); ++i)
3031 {
3032 const crypto::hash &bl_id = parsed_blocks[i].hash;
3033 const cryptonote::block &bl = parsed_blocks[i].block;
3034
3035 if(current_index >= m_blockchain.size())
3036 {
3037 process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset, output_tracker_cache);
3038 ++blocks_added;
3039 }
3040 else if(bl_id != m_blockchain[current_index])
3041 {
3042 //split detected here !!!
3043 THROW_WALLET_EXCEPTION_IF(current_index == start_height, error::wallet_internal_error,
3044 "wrong daemon response: split starts from the first block in response " + string_tools::pod_to_hex(bl_id) +
3045 " (height " + std::to_string(start_height) + "), local block id at this height: " +
3046 string_tools::pod_to_hex(m_blockchain[current_index]));
3047
3048 detach_blockchain(current_index, output_tracker_cache);
3049 process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset, output_tracker_cache);
3050 }
3051 else
3052 {
3053 LOG_PRINT_L2("Block is already in blockchain: " << string_tools::pod_to_hex(bl_id));
3054 }
3055 ++current_index;
3056 tx_cache_data_offset += 1 + parsed_blocks[i].txes.size();
3057 }
3058}
3059//----------------------------------------------------------------------------------------------------
3060void wallet2::refresh(bool trusted_daemon)
3061{
3062 uint64_t blocks_fetched = 0;
3063 refresh(trusted_daemon, 0, blocks_fetched);
3064}
3065//----------------------------------------------------------------------------------------------------
3066void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched)
3067{
3068 bool received_etn = false;
3069 refresh(trusted_daemon, start_height, blocks_fetched, received_etn);
3070}
3071//----------------------------------------------------------------------------------------------------
3072void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, const std::vector<cryptonote::block_complete_entry> &prev_blocks, const std::vector<parsed_block> &prev_parsed_blocks, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<parsed_block> &parsed_blocks, bool &error)
3073{
3074 error = false;
3075
3076 try
3077 {
3078 drop_from_short_history(short_chain_history, 3);
3079
3080 THROW_WALLET_EXCEPTION_IF(prev_blocks.size() != prev_parsed_blocks.size(), error::wallet_internal_error, "size mismatch");
3081
3082 // prepend the last 3 blocks, should be enough to guard against a block or two's reorg
3083 auto s = std::next(prev_parsed_blocks.rbegin(), std::min((size_t)3, prev_parsed_blocks.size())).base();
3084 for (; s != prev_parsed_blocks.end(); ++s)
3085 {
3086 short_chain_history.push_front(s->hash);
3087 }
3088
3089 // pull the new blocks
3090 std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> o_indices;
3091 pull_blocks(start_height, blocks_start_height, short_chain_history, blocks, o_indices);
3092 THROW_WALLET_EXCEPTION_IF(blocks.size() != o_indices.size(), error::wallet_internal_error, "Mismatched sizes of blocks and o_indices");
3093
3096 parsed_blocks.resize(blocks.size());
3097 for (size_t i = 0; i < blocks.size(); ++i)
3098 {
3099 tpool.submit(&waiter, boost::bind(&wallet2::parse_block_round, this, std::cref(blocks[i].block),
3100 std::ref(parsed_blocks[i].block), std::ref(parsed_blocks[i].hash), std::ref(parsed_blocks[i].error)), true);
3101 }
3102 waiter.wait(&tpool);
3103 for (size_t i = 0; i < blocks.size(); ++i)
3104 {
3105 if (parsed_blocks[i].error)
3106 {
3107 error = true;
3108 break;
3109 }
3110 parsed_blocks[i].o_indices = std::move(o_indices[i]);
3111 }
3112
3113 boost::mutex error_lock;
3114 for (size_t i = 0; i < blocks.size(); ++i)
3115 {
3116 parsed_blocks[i].txes.resize(blocks[i].txs.size());
3117 for (size_t j = 0; j < blocks[i].txs.size(); ++j)
3118 {
3119 tpool.submit(&waiter, [&, i, j](){
3120 if (!parse_and_validate_tx_base_from_blob(blocks[i].txs[j], parsed_blocks[i].txes[j]))
3121 {
3122 boost::unique_lock<boost::mutex> lock(error_lock);
3123 error = true;
3124 }
3125 }, true);
3126 }
3127 }
3128 waiter.wait(&tpool);
3129 }
3130 catch(...)
3131 {
3132 error = true;
3133 }
3134}
3135
3136void wallet2::remove_obsolete_pool_txs(const std::vector<crypto::hash> &tx_hashes)
3137{
3138 // remove pool txes to us that aren't in the pool anymore
3139 std::unordered_multimap<crypto::hash, wallet2::pool_payment_details>::iterator uit = m_unconfirmed_payments.begin();
3140 while (uit != m_unconfirmed_payments.end())
3141 {
3142 const crypto::hash &txid = uit->second.m_pd.m_tx_hash;
3143 bool found = false;
3144 for (const auto &it2: tx_hashes)
3145 {
3146 if (it2 == txid)
3147 {
3148 found = true;
3149 break;
3150 }
3151 }
3152 auto pit = uit++;
3153 if (!found)
3154 {
3155 MDEBUG("Removing " << txid << " from unconfirmed payments, not found in pool");
3156 m_unconfirmed_payments.erase(pit);
3157 if (0 != m_callback)
3158 m_callback->on_pool_tx_removed(txid);
3159 }
3160 }
3161}
3162
3163//----------------------------------------------------------------------------------------------------
3164void wallet2::update_pool_state(bool refreshed)
3165{
3166 MTRACE("update_pool_state start");
3167
3168 auto keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this]() {
3169 if (m_encrypt_keys_after_refresh)
3170 {
3171 encrypt_keys(*m_encrypt_keys_after_refresh);
3172 m_encrypt_keys_after_refresh = boost::none;
3173 }
3174 });
3175
3176 // get the pool state
3179 m_daemon_rpc_mutex.lock();
3180 bool r = invoke_http_json("/get_transaction_pool_hashes.bin", req, res, rpc_timeout);
3181 m_daemon_rpc_mutex.unlock();
3182 THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_transaction_pool_hashes.bin");
3183 THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_transaction_pool_hashes.bin");
3185 MTRACE("update_pool_state got pool");
3186
3187 // remove any pending tx that's not in the pool
3188 std::unordered_map<crypto::hash, wallet2::unconfirmed_transfer_details>::iterator it = m_unconfirmed_txs.begin();
3189 while (it != m_unconfirmed_txs.end())
3190 {
3191 const crypto::hash &txid = it->first;
3192 bool found = false;
3193 for (const auto &it2: res.tx_hashes)
3194 {
3195 if (it2 == txid)
3196 {
3197 found = true;
3198 break;
3199 }
3200 }
3201 auto pit = it++;
3202 if (!found)
3203 {
3204 // we want to avoid a false positive when we ask for the pool just after
3205 // a tx is removed from the pool due to being found in a new block, but
3206 // just before the block is visible by refresh. So we keep a boolean, so
3207 // that the first time we don't see the tx, we set that boolean, and only
3208 // delete it the second time it is checked (but only when refreshed, so
3209 // we're sure we've seen the blockchain state first)
3210 if (pit->second.m_state == wallet2::unconfirmed_transfer_details::pending)
3211 {
3212 LOG_PRINT_L1("Pending txid " << txid << " not in pool, marking as not in pool");
3214 }
3215 else if (pit->second.m_state == wallet2::unconfirmed_transfer_details::pending_not_in_pool && refreshed)
3216 {
3217 LOG_PRINT_L1("Pending txid " << txid << " not in pool, marking as failed");
3219
3220 // the inputs aren't spent anymore, since the tx failed
3221 remove_rings(pit->second.m_tx);
3222 for (size_t vini = 0; vini < pit->second.m_tx.vin.size(); ++vini)
3223 {
3224 if (pit->second.m_tx.vin[vini].type() == typeid(txin_to_key))
3225 {
3226 txin_to_key &tx_in_to_key = boost::get<txin_to_key>(pit->second.m_tx.vin[vini]);
3227 for (size_t i = 0; i < m_transfers.size(); ++i)
3228 {
3229 const transfer_details &td = m_transfers[i];
3230 if (td.m_key_image == tx_in_to_key.k_image)
3231 {
3232 LOG_PRINT_L1("Resetting spent status for output " << vini << ": " << td.m_key_image);
3233 set_unspent(i);
3234 break;
3235 }
3236 }
3237 }
3238 }
3239 }
3240 }
3241 }
3242 MTRACE("update_pool_state done first loop");
3243
3244 // remove pool txes to us that aren't in the pool anymore
3245 // but only if we just refreshed, so that the tx can go in
3246 // the in transfers list instead (or nowhere if it just
3247 // disappeared without being mined)
3248 if (refreshed)
3249 remove_obsolete_pool_txs(res.tx_hashes);
3250
3251 MTRACE("update_pool_state done second loop");
3252
3253 // gather txids of new pool txes to us
3254 std::vector<std::pair<crypto::hash, bool>> txids;
3255 for (const auto &txid: res.tx_hashes)
3256 {
3257 bool txid_found_in_up = false;
3258 for (const auto &up: m_unconfirmed_payments)
3259 {
3260 if (up.second.m_pd.m_tx_hash == txid)
3261 {
3262 txid_found_in_up = true;
3263 break;
3264 }
3265 }
3266 if (m_scanned_pool_txs[0].find(txid) != m_scanned_pool_txs[0].end() || m_scanned_pool_txs[1].find(txid) != m_scanned_pool_txs[1].end())
3267 {
3268 // if it's for us, we want to keep track of whether we saw a double spend, so don't bail out
3269 if (!txid_found_in_up)
3270 {
3271 LOG_PRINT_L2("Already seen " << txid << ", and not for us, skipped");
3272 continue;
3273 }
3274 }
3275 if (!txid_found_in_up)
3276 {
3277 LOG_PRINT_L1("Found new pool tx: " << txid);
3278 bool found = false;
3279 for (const auto &i: m_unconfirmed_txs)
3280 {
3281 if (i.first == txid)
3282 {
3283 found = true;
3284 // if this is a payment to yourself at a different subaddress account, don't skip it
3285 // so that you can see the incoming pool tx with 'show_transfers' on that receiving subaddress account
3286 const unconfirmed_transfer_details& utd = i.second;
3287 for (const auto& dst : utd.m_dests)
3288 {
3289 auto subaddr_index = m_subaddresses.find(dst.addr.m_spend_public_key);
3290 if (subaddr_index != m_subaddresses.end() && subaddr_index->second.major != utd.m_subaddr_account)
3291 {
3292 found = false;
3293 break;
3294 }
3295 }
3296 break;
3297 }
3298 }
3299 if (!found)
3300 {
3301 // not one of those we sent ourselves
3302 txids.push_back({txid, false});
3303 }
3304 else
3305 {
3306 LOG_PRINT_L1("We sent that one");
3307 }
3308 }
3309 else
3310 {
3311 LOG_PRINT_L1("Already saw that one, it's for us");
3312 txids.push_back({txid, true});
3313 }
3314 }
3315
3316 // get those txes
3317 if (!txids.empty())
3318 {
3321 for (const auto &p: txids)
3322 req.txs_hashes.push_back(epee::string_tools::pod_to_hex(p.first));
3323 MDEBUG("asking for " << txids.size() << " transactions");
3324 req.decode_as_json = false;
3325 req.prune = true;
3326 m_daemon_rpc_mutex.lock();
3327 bool r = invoke_http_json("/gettransactions", req, res, rpc_timeout);
3328 m_daemon_rpc_mutex.unlock();
3329 MDEBUG("Got " << r << " and " << res.status);
3330 if (r && res.status == CORE_RPC_STATUS_OK)
3331 {
3332 if (res.txs.size() == txids.size())
3333 {
3334 for (const auto &tx_entry: res.txs)
3335 {
3336 if (tx_entry.in_pool)
3337 {
3340 crypto::hash tx_hash;
3341
3342 if (get_pruned_tx(tx_entry, tx, tx_hash))
3343 {
3344 const std::vector<std::pair<crypto::hash, bool>>::const_iterator i = std::find_if(txids.begin(), txids.end(),
3345 [tx_hash](const std::pair<crypto::hash, bool> &e) { return e.first == tx_hash; });
3346 if (i != txids.end())
3347 {
3348 process_new_transaction(tx_hash, tx, std::vector<uint64_t>(), 0, time(NULL), false, true, tx_entry.double_spend_seen, false, {});
3349 m_scanned_pool_txs[0].insert(tx_hash);
3350 if (m_scanned_pool_txs[0].size() > 5000)
3351 {
3352 std::swap(m_scanned_pool_txs[0], m_scanned_pool_txs[1]);
3353 m_scanned_pool_txs[0].clear();
3354 }
3355 }
3356 else
3357 {
3358 MERROR("Got txid " << tx_hash << " which we did not ask for");
3359 }
3360 }
3361 else
3362 {
3363 LOG_PRINT_L0("Failed to parse transaction from daemon");
3364 }
3365 }
3366 else
3367 {
3368 LOG_PRINT_L1("Transaction from daemon was in pool, but is no more");
3369 }
3370 }
3371 }
3372 else
3373 {
3374 LOG_PRINT_L0("Expected " << txids.size() << " tx(es), got " << res.txs.size());
3375 }
3376 }
3377 else
3378 {
3379 LOG_PRINT_L0("Error calling gettransactions daemon RPC: r " << r << ", status " << get_rpc_status(res.status));
3380 }
3381 }
3382 MTRACE("update_pool_state end");
3383}
3384
3385//----------------------------------------------------------------------------------------------------
3386void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, bool force)
3387{
3388 std::vector<crypto::hash> hashes;
3389
3390 const uint64_t checkpoint_height = m_checkpoints.get_max_height();
3391 if ((stop_height > checkpoint_height && m_blockchain.size()-1 < checkpoint_height) && !force)
3392 {
3393 // we will drop all these, so don't bother getting them
3394 uint64_t missing_blocks = m_checkpoints.get_max_height() - m_blockchain.size();
3395 while (missing_blocks-- > 0)
3396 m_blockchain.push_back(crypto::null_hash); // maybe a bit suboptimal, but deque won't do huge reallocs like vector
3397 m_blockchain.push_back(m_checkpoints.get_points().at(checkpoint_height));
3398 m_blockchain.trim(checkpoint_height);
3399 short_chain_history.clear();
3400 get_short_chain_history(short_chain_history);
3401 }
3402
3403 size_t current_index = m_blockchain.size();
3404 while(m_run.load(std::memory_order_relaxed) && current_index < stop_height)
3405 {
3406 pull_hashes(0, blocks_start_height, short_chain_history, hashes);
3407 if (hashes.size() <= 3)
3408 return;
3409 if (blocks_start_height < m_blockchain.offset())
3410 {
3411 MERROR("Blocks start before blockchain offset: " << blocks_start_height << " " << m_blockchain.offset());
3412 return;
3413 }
3414 if (hashes.size() + current_index < stop_height) {
3415 drop_from_short_history(short_chain_history, 3);
3416 std::vector<crypto::hash>::iterator right = hashes.end();
3417 // prepend 3 more
3418 for (int i = 0; i<3; i++) {
3419 right--;
3420 short_chain_history.push_front(*right);
3421 }
3422 }
3423 current_index = blocks_start_height;
3424 for(auto& bl_id: hashes)
3425 {
3426 if(current_index >= m_blockchain.size())
3427 {
3428 if (!(current_index % 1024))
3429 LOG_PRINT_L2( "Skipped block by height: " << current_index);
3430 m_blockchain.push_back(bl_id);
3431
3432 if (0 != m_callback)
3433 { // FIXME: this isn't right, but simplewallet just logs that we got a block.
3434 cryptonote::block dummy;
3435 m_callback->on_new_block(current_index, dummy);
3436 }
3437 }
3438 else if(bl_id != m_blockchain[current_index])
3439 {
3440 //split detected here !!!
3441 return;
3442 }
3443 ++current_index;
3444 if (current_index >= stop_height)
3445 return;
3446 }
3447 }
3448}
3449
3450
3451bool wallet2::add_address_book_row(const cryptonote::account_public_address &address, const crypto::hash &payment_id, const std::string &description, bool is_subaddress)
3452{
3454 a.m_address = address;
3455 a.m_payment_id = payment_id;
3456 a.m_description = description;
3457 a.m_is_subaddress = is_subaddress;
3458
3459 auto old_size = m_address_book.size();
3460 m_address_book.push_back(a);
3461 if(m_address_book.size() == old_size+1)
3462 return true;
3463 return false;
3464}
3465
3466bool wallet2::delete_address_book_row(std::size_t row_id) {
3467 if(m_address_book.size() <= row_id)
3468 return false;
3469
3470 m_address_book.erase(m_address_book.begin()+row_id);
3471
3472 return true;
3473}
3474
3475//----------------------------------------------------------------------------------------------------
3476std::shared_ptr<std::pair<std::map<std::pair<uint64_t, uint64_t>, size_t>, std::map<std::pair<std::array<char, 32>, size_t>, size_t>>> wallet2::create_output_tracker_cache() const
3477{ // output tracker cache at the pointed-to address is a map where the key is a pair of <output amount, global out index>
3478 // and the value is the m_transfers index. Essentially, this is a cache of output unique identifier against it's location in m_transfers (if it exists there)
3479 std::shared_ptr<std::pair<std::map<std::pair<uint64_t, uint64_t>, size_t>, std::map<std::pair<std::array<char,32>, size_t>, size_t>>> cache{new std::pair<std::map<std::pair<uint64_t, uint64_t>, size_t>, std::map<std::pair<std::array<char,32>, size_t>, size_t>>()};
3480 for (size_t i = 0; i < m_transfers.size(); ++i)
3481 {
3482 const transfer_details &td = m_transfers[i];
3483 //amount, global out index
3484 (*cache).first[std::make_pair(td.is_rct() ? 0 : td.amount(), td.m_global_output_index)] = i;
3485 //txid, relative out index
3486 std::array<char, 32> transaction_id;
3487 std::copy(std::begin(td.m_txid.data), std::end(td.m_txid.data), transaction_id.begin());
3488 (*cache).second[std::make_pair(transaction_id, td.m_internal_output_index)] = i;
3489 }
3490 return cache;
3491}
3492//----------------------------------------------------------------------------------------------------
3493void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched, bool& received_etn, bool check_pool) {
3494 if (m_offline) {
3495 blocks_fetched = 0;
3496 received_etn = 0;
3497 return;
3498 }
3499
3500 if (m_light_wallet) {
3501
3502 // MyMonero get_address_info needs to be called occasionally to trigger wallet sync.
3503 // This call is not really needed for other purposes and can be removed if mymonero changes their backend.
3505
3506 // Get basic info
3508 // Last stored block height
3509 uint64_t prev_height = m_light_wallet_blockchain_height;
3510 // Update lw heights
3511 m_light_wallet_scanned_block_height = res.scanned_block_height;
3512 m_light_wallet_blockchain_height = res.blockchain_height;
3513 // If new height - call new_block callback
3514 if (m_light_wallet_blockchain_height != prev_height) {
3515 MDEBUG("new block since last time!");
3516 m_callback->on_lw_new_block(m_light_wallet_blockchain_height - 1);
3517 }
3518 m_light_wallet_connected = true;
3519 MDEBUG("lw scanned block height: " << m_light_wallet_scanned_block_height);
3520 MDEBUG("lw blockchain height: " << m_light_wallet_blockchain_height);
3521 MDEBUG(m_light_wallet_blockchain_height - m_light_wallet_scanned_block_height << " blocks behind");
3522 // TODO: add wallet created block info
3523
3525 } else
3526 m_light_wallet_connected = false;
3527
3528 // Lighwallet refresh done
3529 return;
3530 }
3531 received_etn = false;
3532 blocks_fetched = 0;
3533 uint64_t added_blocks = 0;
3534 size_t try_count = 0;
3535 crypto::hash last_tx_hash_id = m_transfers.size() ? m_transfers.back().m_txid : null_hash;
3536 std::list<crypto::hash> short_chain_history;
3539 uint64_t blocks_start_height;
3540 std::vector<cryptonote::block_complete_entry> blocks;
3541 std::vector<parsed_block> parsed_blocks;
3542 bool refreshed = false;
3543 std::shared_ptr<std::pair<std::map<std::pair<uint64_t, uint64_t>, size_t>, std::map<std::pair<std::array<char, 32>, size_t>, size_t>>> output_tracker_cache; //this is where the only usage of output_tracker cache begins
3544 hw::device &hwdev = m_account.get_device();
3545
3546 // pull the first set of blocks
3547 get_short_chain_history(short_chain_history,
3548 (m_first_refresh_done || trusted_daemon) ? 1 : FIRST_REFRESH_GRANULARITY);
3549 m_run.store(true, std::memory_order_relaxed);
3550 if (start_height > m_blockchain.size() || m_refresh_from_block_height > m_blockchain.size()) {
3551 if (!start_height)
3552 start_height = m_refresh_from_block_height;
3553 // we can shortcut by only pulling hashes up to the start_height
3554 fast_refresh(start_height, blocks_start_height, short_chain_history);
3555 // regenerate the history now that we've got a full set of hashes
3556 short_chain_history.clear();
3557 get_short_chain_history(short_chain_history,
3558 (m_first_refresh_done || trusted_daemon) ? 1 : FIRST_REFRESH_GRANULARITY);
3559 start_height = 0;
3560 // and then fall through to regular refresh processing
3561 }
3562
3563 // If stop() is called during fast refresh we don't need to continue
3564 if (!m_run.load(std::memory_order_relaxed))
3565 return;
3566 // always reset start_height to 0 to force short_chain_ history to be used on
3567 // subsequent pulls in this refresh.
3568 start_height = 0;
3569
3570 auto keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this]() {
3571 if (m_encrypt_keys_after_refresh) {
3572 encrypt_keys(*m_encrypt_keys_after_refresh);
3573 m_encrypt_keys_after_refresh = boost::none;
3574 }
3575 });
3576
3577 auto scope_exit_handler_hwdev = epee::misc_utils::create_scope_leave_handler(
3578 [&]() { hwdev.computing_key_images(false); });
3579 bool first = true;
3580 while (m_run.load(std::memory_order_relaxed)) {
3581 uint64_t next_blocks_start_height;
3582 std::vector<cryptonote::block_complete_entry> next_blocks;
3583 std::vector<parsed_block> next_parsed_blocks;
3584 bool error;
3585 try {
3586 // pull the next set of blocks while we're processing the current one
3587 error = false;
3588 next_blocks.clear();
3589 next_parsed_blocks.clear();
3590 added_blocks = 0;
3591 if (!first && blocks.empty()) {
3592 refreshed = false;
3593 break;
3594 }
3595 tpool.submit(&waiter, [&] {
3596 pull_and_parse_next_blocks(start_height, next_blocks_start_height, short_chain_history, blocks,
3597 parsed_blocks, next_blocks, next_parsed_blocks, error);
3598 });
3599
3600 if (!first) {
3601 try {
3602 process_parsed_blocks(blocks_start_height, blocks, parsed_blocks, added_blocks,
3603 output_tracker_cache.get());
3604 }
3606 MINFO("Daemon claims next refresh block is out of hash chain bounds, resetting hash chain");
3607 uint64_t stop_height = m_blockchain.offset();
3608 std::vector<crypto::hash> tip(m_blockchain.size() - m_blockchain.offset());
3609 for (size_t i = m_blockchain.offset(); i < m_blockchain.size(); ++i)
3610 tip[i - m_blockchain.offset()] = m_blockchain[i];
3612 generate_genesis(b);
3613 m_blockchain.clear();
3614 m_blockchain.push_back(get_block_hash(b));
3615 short_chain_history.clear();
3616 get_short_chain_history(short_chain_history);
3617 fast_refresh(stop_height, blocks_start_height, short_chain_history, true);
3619 (m_blockchain.size() == stop_height || (m_blockchain.size() == 1 && stop_height == 0)
3620 ? false : true), error::wallet_internal_error, "Unexpected hashchain size");
3621 THROW_WALLET_EXCEPTION_IF(m_blockchain.offset() != 0, error::wallet_internal_error,
3622 "Unexpected hashchain offset");
3623 for (const auto &h: tip)
3624 m_blockchain.push_back(h);
3625 short_chain_history.clear();
3626 get_short_chain_history(short_chain_history);
3627 start_height = stop_height;
3628 throw std::runtime_error(""); // loop again
3629 }
3630 catch (const std::exception &e) {
3631 MERROR("Error parsing blocks: " << e.what());
3632 error = true;
3633 }
3634 blocks_fetched += added_blocks;
3635 }
3636 waiter.wait(&tpool);
3637 if (!first && blocks_start_height == next_blocks_start_height) {
3638 m_node_rpc_proxy.set_height(m_blockchain.size());
3639 refreshed = true;
3640 break;
3641 }
3642
3643 first = false;
3644
3645 // handle error from async fetching thread
3646 if (error) {
3647 throw std::runtime_error("proxy exception in refresh thread");
3648 }
3649
3650 // if we've got at least 10 blocks to refresh, assume we're starting
3651 // a long refresh, and setup a tracking output cache if we need to
3652 // We hit create_output_tracker_cache before doing processing our blocks in process_parsed_blocks above( see 'first' variable)
3653 if (m_track_uses && (!output_tracker_cache ||
3654 (output_tracker_cache->first.empty() && output_tracker_cache->second.empty())) &&
3655 next_blocks.size() >= 10)
3656 output_tracker_cache = create_output_tracker_cache();
3657
3658 // switch to the new blocks from the daemon
3659 blocks_start_height = next_blocks_start_height;
3660 blocks = std::move(next_blocks);
3661 parsed_blocks = std::move(next_parsed_blocks);
3662 }
3663 catch (const tools::error::password_needed &) {
3664 blocks_fetched += added_blocks;
3665 waiter.wait(&tpool);
3666 throw;
3667 }
3668 catch (const std::exception &) {
3669 blocks_fetched += added_blocks;
3670 waiter.wait(&tpool);
3671 if (try_count < 3) {
3672 LOG_PRINT_L1("Another try pull_blocks (try_count=" << try_count << ")...");
3673 first = true;
3674 start_height = 0;
3675 blocks.clear();
3676 parsed_blocks.clear();
3677 short_chain_history.clear();
3678 get_short_chain_history(short_chain_history, 1);
3679 ++try_count;
3680 } else {
3681 LOG_ERROR("pull_blocks failed, try_count=" << try_count);
3682 throw;
3683 }
3684 }
3685 }
3686 if (last_tx_hash_id != (m_transfers.size() ? m_transfers.back().m_txid : null_hash))
3687 received_etn = true;
3688
3689 try {
3690 // If stop() is called we don't need to check pending transactions
3691 if (check_pool && m_run.load(std::memory_order_relaxed))
3692 update_pool_state(refreshed);
3693 }
3694 catch (...) {
3695 LOG_PRINT_L1("Failed to check pending transactions");
3696 }
3697
3698 m_first_refresh_done = true;
3699
3700 LOG_PRINT_L1("Refresh done, blocks received: " << blocks_fetched << ", pre v10 balance (all accounts): "
3701 << print_etn(balance_all(false)) << ", unlocked: "
3703 << ", post v10 balance (all accounts): "
3704 << print_etn(balance_all(true)) << ", unlocked: "
3706
3707 //get the testnet bridge address - should be same as mainnet because of our netbyte being erroneously set to the same thing when Electroneum was first created
3708 // cryptonote::account_public_address bridge_public_address;
3709 // std::string portal_address_viewkey_hex_str = "5866666666666666666666666666666666666666666666666666666666666666"; //private view is just 0100000000000000000000000000000000000000000000000000000000000000
3710 // std::string portal_address_spendkey_hex_str = "5bd0c0e25eee6133850edd2b255ed9e3d6bb99fd5f08b7b5cf7f2618ad6ff2a3"; //private view is just 0100000000000000000000000000000000000000000000000000000000000000
3711 // epee::string_tools::hex_to_pod(portal_address_viewkey_hex_str, bridge_public_address.m_view_public_key);
3712 // epee::string_tools::hex_to_pod(portal_address_spendkey_hex_str, bridge_public_address.m_spend_public_key);
3713 // std::string bridge_address = cryptonote::get_account_address_as_str(this->nettype(), false, bridge_public_address); //OK
3714
3715 //generate the coinbase burn address. spendkey is "9511fabcb699b4f9dffc1779713d0dd7eb1ca56ba5b8ab8d3253a0a6ccf736b3", address "etnkCys4uGhSi9h48ajL9vBDJTcn2s2ttXtXq3SXWPAbiMHNhHitu5fJ8QgRfFWTzmJ8QgRfFWTzmJ8QgRfFWTzm4t51HTfCtK"
3716 //cryptonote::account_public_address coinbase_burn_address;
3717 //crypto::hash h;
3718 //crypto::ec_point point;
3719 //epee::string_tools::hex_to_pod("714c8d8eeee5243e7f266e5210f76f58b8b1d6330cedfbc4eda6d5947b212012", h); // genesis hash hex ---> hash type
3720 //crypto::hash_to_point(h, point); // generate curve point (burn address spendkey) deterministically in such a way that we can't recover the private key
3721 //crypto::public_key coinbase_burn_address_spendkey;
3722 //std::copy(std::begin(point.data), std::end(point.data), std::begin(coinbase_burn_address_spendkey.data)); // serialise point to pubkey type
3723 //std::string coinbase_burn_address_spendkey_hex_str = epee::string_tools::pod_to_hex(coinbase_burn_address_spendkey); // for testing only. pub spend =
3724 //std::string coinbase_burn_address_viewkey_hex_str = "5866666666666666666666666666666666666666666666666666666666666666"; //private view is just 0100000000000000000000000000000000000000000000000000000000000000
3725 //coinbase_burn_address.m_spend_public_key = coinbase_burn_address_spendkey;
3726 //epee::string_tools::hex_to_pod(coinbase_burn_address_viewkey_hex_str, coinbase_burn_address.m_view_public_key);
3727 //std::string coinbase_burn_address_str = cryptonote::get_account_address_as_str(this->nettype(), false, coinbase_burn_address); //OK
3728
3729
3730 try {
3731 // V9-->V10 PUBLIC MIGRATIONS
3732 // check that the local blockchain height is at least the v10 fork height + 5 blocks (so we know we don't need to scan for any more v1 outputs and they have all have 5 confs)
3733 //todo: write function for wallet that gets the b.major version for a given *local* blockchain height, to save hardcoding heights.
3734 uint64_t migration_minheight = this->nettype() == TESTNET ? 1086402 + 5 : 1175315 + 5;
3735 if (this->get_blockchain_current_height() > migration_minheight && this->unlocked_balance_all(false) != 0) {
3737 "You are now on the transparent version of Electroneum and so we're giving you the chance to migrate your funds via a sweep transaction back to your address.\n Don't worry, this migration is completely free of charge. Please follow the prompts to continue.");
3738 std::map<uint32_t, std::map<uint32_t, std::pair<uint64_t, uint64_t>>> unlocked_balance_per_subaddress_per_account; // map of: account index ----> (subaddress index, pair(u-balance, unlock time))
3739 // for each account, grab all of the subaddress info (index, (balance, unlock))
3740 for (uint32_t account_index = 0; account_index < this->get_num_subaddress_accounts(); ++account_index) {
3741 unlocked_balance_per_subaddress_per_account[account_index] = this->unlocked_balance_per_subaddress(
3742 account_index, false);
3743 }
3744 for (uint32_t i = 0; i < this->get_num_subaddress_accounts(); i++) {
3746 index.major = i;
3747 for (auto subaddress: unlocked_balance_per_subaddress_per_account[i]) {
3748 index.minor = subaddress.first;
3749
3750 if (subaddress.second.first != 0 &&
3751 subaddress.second.second == 0/*is there a fully unlocked nonzero balance /sanity check*/) {
3752 cryptonote::account_public_address address = get_subaddress(index); // BRIDGE PORTAL ADDRESS
3753 std::set<uint32_t> subaddress_source{index.minor};
3754 std::vector<wallet2::pending_tx> ptx_vector = this->create_transactions_all(0,
3755 address /*dest address*/,
3756 index.major !=
3757 0 ||
3758 index.minor !=
3759 0 /*is dest a subaddress*/,
3760 1 /*one output only*/,
3761 0 /* don't mix*/,
3762 0 /*default unlock time*/,
3763 4 /*highest priority*/,
3764 vector<uint8_t>() /*empty tx extra */,
3765 index.major /*account index*/,
3766 subaddress_source /*source subaddr index*/,
3767 true /*migrate*/);
3768 this->commit_tx(ptx_vector);
3769 }
3770 }
3771 }
3772 LOG_PRINT_L0("Migration to the public version of the blockchain has completed. Please use the command show_transfers (CLI Wallet) or get_transfers (RPC Wallet) to see the details of your migration transactions.");
3773 }
3774
3775 } catch(...) {
3776 THROW_WALLET_EXCEPTION(error::wallet_internal_error, "V9 (Privatised)-->V10 (Public) wallet migration failed.");
3777 }
3778
3779 try {
3780 // V10 Migration to Electroneum Smart Chain
3782 std::string portal_address_viewkey_hex_str;
3783 std::string portal_address_spendkey_hex_str;
3784 if(m_nettype == MAINNET){
3785 portal_address_viewkey_hex_str = "2b95a2eb2c62253c57e82b082b850bbf22a1a7829aaea09c7c1511c1cced4375";
3786 portal_address_spendkey_hex_str = "8ce0f34fd37c7f7d07c44024eb5b3cdf275d1b3e75c3464b808dce532e861137";
3787 }else{
3788 portal_address_viewkey_hex_str = "5866666666666666666666666666666666666666666666666666666666666666"; //private view is just 0100000000000000000000000000000000000000000000000000000000000000
3789 portal_address_spendkey_hex_str = "5bd0c0e25eee6133850edd2b255ed9e3d6bb99fd5f08b7b5cf7f2618ad6ff2a3"; //
3790 }
3791
3792 bool portal_wallet = //if the portal address wallet ever needs opening, don't allow it to sweep to itself
3793 epee::string_tools::pod_to_hex(get_address().m_spend_public_key) ==
3794 portal_address_spendkey_hex_str &&
3795 epee::string_tools::pod_to_hex(get_address().m_view_public_key) == portal_address_viewkey_hex_str;
3796
3797 epee::string_tools::hex_to_pod(portal_address_spendkey_hex_str, portal_address.m_spend_public_key);
3798 epee::string_tools::hex_to_pod(portal_address_viewkey_hex_str, portal_address.m_view_public_key);
3799
3800 // ONLY do migration transactions after the fork block
3801 uint64_t smartchain_migration_minheight = this->nettype() == MAINNET ? 1811310 : 1455270;
3802 if (this->get_blockchain_current_height() > smartchain_migration_minheight) {
3803 // check that unlocked balance = unlocked balance as a best-effort to ensure that we're not migrating the funds whilst more are in transit/confirming
3804 if ((!portal_wallet) && (this->balance_all(true) != 0) &&
3805 (this->unlocked_balance_all(true) == this->balance_all(true))) {
3806 std::cout << std::endl << "You are beginning your token migration over to the Electroneum Smart Chain." << std::endl;
3807 std::cout << "This transaction is feeless. For further information, please read our documentation over at https:///developer.electroneum.com/migration-to-smart-chain/overview" << std::endl;
3808 std::map<uint32_t, std::map<uint32_t, std::pair<uint64_t, uint64_t>>> unlocked_balance_per_subaddress_per_account; // map of: account index ----> (subaddress index, pair(u-balance, unlock time))
3809 // for each account, grab all of the subaddress info (index, (balance, unlock))
3810 for (uint32_t account_index = 0;
3811 account_index < this->get_num_subaddress_accounts(); ++account_index) {
3812 unlocked_balance_per_subaddress_per_account[account_index] = this->unlocked_balance_per_subaddress(
3813 account_index, true);
3814 }
3815 for (uint32_t i = 0; i < this->get_num_subaddress_accounts(); i++) {
3817 index.major = i;
3818 for (auto subaddress: unlocked_balance_per_subaddress_per_account[i]) {
3819 index.minor = subaddress.first;
3820
3821 if (subaddress.second.first != 0 &&
3822 subaddress.second.second ==
3823 0/*is there a fully unlocked nonzero balance /sanity check*/) {
3824 std::set<uint32_t> subaddress_source{index.minor};
3825 std::vector<wallet2::pending_tx> ptx_vector = this->create_transactions_all(0,
3826 portal_address /*dest address (portal address for bridge)*/,
3827 0 /*is dest a subaddress*/,
3828 1 /*one output only*/, //???????
3829 0 /* don't mix*/,
3830 0 /*default unlock time*/,
3831 1 /*priority - set low in case they don't have fees for high priority but do for low priority*/,
3832 vector<uint8_t>() /*empty tx extra */,
3833 index.major /*account index*/,
3834 subaddress_source /*source subaddr index*/,
3835 false /*migrate to transparent chain*/);
3836 this->commit_tx(ptx_vector);
3837 }
3838 }
3839 }
3840 std::cout << std::endl;
3841 std::cout << "Migration to Smart Chain portal address completed. Please use the command show_transfers (CLI Wallet) or get_transfers (RPC Wallet) to see the details of your Smart Chain migration transactions." << std::endl;
3842 std::cout << "Please note that the entire migration process is not instant and your funds may take some time to show up in the Smart Chain." << std::endl;
3843 std::cout << "You can find your SmartChain address using the \"spendkey\" command in the CLI wallet." << std::endl;
3844 }
3845 }
3846 } catch(...) {
3847 THROW_WALLET_EXCEPTION(error::wallet_internal_error, "V10 Smart Chain migration failed.");
3848 }
3849}
3850//----------------------------------------------------------------------------------------------------
3851bool wallet2::refresh(bool trusted_daemon, uint64_t & blocks_fetched, bool& received_etn, bool& ok)
3852{
3853 try
3854 {
3855 refresh(trusted_daemon, 0, blocks_fetched, received_etn);
3856 ok = true;
3857 }
3858 catch (...)
3859 {
3860 ok = false;
3861 }
3862 return ok;
3863}
3864//----------------------------------------------------------------------------------------------------
3865bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector<uint64_t> &distribution)
3866{
3867 uint32_t rpc_version;
3868 boost::optional<std::string> result = m_node_rpc_proxy.get_rpc_version(rpc_version);
3869 // no error
3870 if (!!result)
3871 {
3872 // empty string -> not connection
3875 if (*result != CORE_RPC_STATUS_OK)
3876 {
3877 MDEBUG("Cannot determine daemon RPC version, not requesting rct distribution");
3878 return false;
3879 }
3880 }
3881 else
3882 {
3883 if (rpc_version >= MAKE_CORE_RPC_VERSION(1, 19))
3884 {
3885 MDEBUG("Daemon is recent enough, requesting rct distribution");
3886 }
3887 else
3888 {
3889 MDEBUG("Daemon is too old, not requesting rct distribution");
3890 return false;
3891 }
3892 }
3893
3896 req.amounts.push_back(0);
3897 req.from_height = 0;
3898 req.cumulative = false;
3899 req.binary = true;
3900 req.compress = true;
3901 m_daemon_rpc_mutex.lock();
3902 bool r = invoke_http_bin("/get_output_distribution.bin", req, res, rpc_timeout);
3903 m_daemon_rpc_mutex.unlock();
3904 if (!r)
3905 {
3906 MWARNING("Failed to request output distribution: no connection to daemon");
3907 return false;
3908 }
3909 if (res.status == CORE_RPC_STATUS_BUSY)
3910 {
3911 MWARNING("Failed to request output distribution: daemon is busy");
3912 return false;
3913 }
3914 if (res.status != CORE_RPC_STATUS_OK)
3915 {
3916 MWARNING("Failed to request output distribution: " << res.status);
3917 return false;
3918 }
3919 if (res.distributions.size() != 1)
3920 {
3921 MWARNING("Failed to request output distribution: not the expected single result");
3922 return false;
3923 }
3924 if (res.distributions[0].amount != 0)
3925 {
3926 MWARNING("Failed to request output distribution: results are not for amount 0");
3927 return false;
3928 }
3929 for (size_t i = 1; i < res.distributions[0].data.distribution.size(); ++i)
3930 res.distributions[0].data.distribution[i] += res.distributions[0].data.distribution[i-1];
3931 start_height = res.distributions[0].data.start_height;
3932 distribution = std::move(res.distributions[0].data.distribution);
3933 return true;
3934}
3935//----------------------------------------------------------------------------------------------------
3936void wallet2::detach_blockchain(uint64_t height, std::pair<std::map<std::pair<uint64_t, uint64_t>, size_t>, std::map<std::pair<std::array<char, 32>, size_t>, size_t>> *output_tracker_cache)
3937{
3938 LOG_PRINT_L0("Detaching blockchain on height " << height);
3939
3940 // size 1 2 3 4 5 6 7 8 9
3941 // block 0 1 2 3 4 5 6 7 8
3942 // C
3943 THROW_WALLET_EXCEPTION_IF(height < m_blockchain.offset() && m_blockchain.size() > m_blockchain.offset(),
3944 error::wallet_internal_error, "Daemon claims reorg below last checkpoint");
3945
3946 size_t transfers_detached = 0;
3947
3948 for (size_t i = 0; i < m_transfers.size(); ++i)
3949 {
3950 wallet2::transfer_details &td = m_transfers[i];
3951 if (td.m_spent && td.m_spent_height >= height)
3952 {
3953 if(td.m_tx.version == 1){ // we're resetting chainstate indexes for ver > 1
3954 LOG_PRINT_L1("Resetting spent/frozen status for output " << i << ": " << td.m_key_image);
3955 }else{
3956 LOG_PRINT_L1("Resetting spent/frozen status for output "
3957 << i << ": " << "chainstate index " << td.m_txid <<": " << td.m_internal_output_index);
3958 }
3959 set_unspent(i);
3960 thaw(i);
3961 }
3962 }
3963
3964 for (transfer_details &td: m_transfers)
3965 {
3966 while (!td.m_uses.empty() && td.m_uses.back().first >= height)
3967 td.m_uses.pop_back();
3968 }
3969
3970 if (output_tracker_cache) {
3971 output_tracker_cache->first.clear();
3972 output_tracker_cache->second.clear();
3973 }
3974
3975 auto it = std::find_if(m_transfers.begin(), m_transfers.end(), [&](const transfer_details& td){return td.m_block_height >= height;});
3976 size_t i_start = it - m_transfers.begin();
3977
3978 for(size_t i = i_start; i!= m_transfers.size();i++)
3979 {
3980 if (!m_transfers[i].m_key_image_known || m_transfers[i].m_key_image_partial)
3981 continue;
3982 auto it_ki = m_key_images.find(m_transfers[i].m_key_image);
3983 THROW_WALLET_EXCEPTION_IF(it_ki == m_key_images.end(), error::wallet_internal_error, "key image not found: index " + std::to_string(i) + ", ki " + epee::string_tools::pod_to_hex(m_transfers[i].m_key_image) + ", " + std::to_string(m_key_images.size()) + " key images known");
3984 m_key_images.erase(it_ki);
3985 }
3986
3987 for(size_t i = i_start; i!= m_transfers.size();i++)
3988 {
3989 auto it_pk = m_pub_keys.find(m_transfers[i].get_public_key());
3990 THROW_WALLET_EXCEPTION_IF(it_pk == m_pub_keys.end(), error::wallet_internal_error, "public key not found");
3991 m_pub_keys.erase(it_pk);
3992 }
3993
3994 for(size_t i = i_start; i!= m_transfers.size();i++)
3995 {
3996 auto it_pk = m_chainstate_indexes.find(m_transfers[i].get_chainstate_index());
3997 if(m_transfers[i].m_tx.version > 1) {
3998 THROW_WALLET_EXCEPTION_IF(it_pk == m_chainstate_indexes.end(), error::wallet_internal_error,
3999 "chainstate index not found");
4000 m_chainstate_indexes.erase(it_pk);
4001 }else{
4002 continue;
4003 }
4004 }
4005
4006
4007 m_transfers.erase(it, m_transfers.end());
4008
4009 size_t blocks_detached = m_blockchain.size() - height;
4010 m_blockchain.crop(height);
4011
4012 for (auto it = m_payments.begin(); it != m_payments.end(); )
4013 {
4014 if(height <= it->second.m_block_height)
4015 it = m_payments.erase(it);
4016 else
4017 ++it;
4018 }
4019
4020 for (auto it = m_confirmed_txs.begin(); it != m_confirmed_txs.end(); )
4021 {
4022 if(height <= it->second.m_block_height)
4023 it = m_confirmed_txs.erase(it);
4024 else
4025 ++it;
4026 }
4027
4028 LOG_PRINT_L0("Detached blockchain on height " << height << ", transfers detached " << transfers_detached << ", blocks detached " << blocks_detached);
4029}
4030//----------------------------------------------------------------------------------------------------
4032{
4033 m_is_initialized=false;
4035 m_account.deinit();
4036 return true;
4037}
4038//----------------------------------------------------------------------------------------------------
4039bool wallet2::clear()
4040{
4041 m_blockchain.clear();
4042 m_transfers.clear();
4043 m_key_images.clear();
4044 m_pub_keys.clear();
4045 m_chainstate_indexes.clear();
4046 m_unconfirmed_txs.clear();
4047 m_payments.clear();
4048 m_tx_keys.clear();
4049 m_additional_tx_keys.clear();
4050 m_confirmed_txs.clear();
4051 m_unconfirmed_payments.clear();
4052 m_scanned_pool_txs[0].clear();
4053 m_scanned_pool_txs[1].clear();
4054 m_address_book.clear();
4055 m_subaddresses.clear();
4056 m_subaddress_labels.clear();
4057 m_multisig_rounds_passed = 0;
4058 m_device_last_key_image_sync = 0;
4059 return true;
4060}
4061//----------------------------------------------------------------------------------------------------
4062void wallet2::clear_soft(bool keep_key_images)
4063{
4064 m_blockchain.clear();
4065 m_transfers.clear();
4066 if (!keep_key_images)
4067 m_key_images.clear();
4068 m_pub_keys.clear();
4069 m_chainstate_indexes.clear();
4070 m_unconfirmed_txs.clear();
4071 m_payments.clear();
4072 m_confirmed_txs.clear();
4073 m_unconfirmed_payments.clear();
4074 m_scanned_pool_txs[0].clear();
4075 m_scanned_pool_txs[1].clear();
4076
4078 generate_genesis(b);
4079 m_blockchain.push_back(get_block_hash(b));
4080 m_last_block_reward = cryptonote::get_outs_etn_amount(b.miner_tx);
4081}
4082
4090bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable_string& password, bool watch_only)
4091{
4092 std::string account_data;
4093 std::string multisig_signers;
4094 std::string multisig_derivations;
4095 cryptonote::account_base account = m_account;
4096
4097 crypto::chacha_key key;
4098 crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
4099
4100 if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
4101 {
4102 account.encrypt_viewkey(key);
4103 account.decrypt_keys(key);
4104 }
4105
4106 if (watch_only)
4107 account.forget_spend_key();
4108
4109 account.encrypt_keys(key);
4110
4111 bool r = epee::serialization::store_t_to_binary(account, account_data);
4112 CHECK_AND_ASSERT_MES(r, false, "failed to serialize wallet keys");
4113 wallet2::keys_file_data keys_file_data = boost::value_initialized<wallet2::keys_file_data>();
4114
4115 // Create a JSON object with "key_data" and "seed_language" as keys.
4116 rapidjson::Document json;
4117 json.SetObject();
4118 rapidjson::Value value(rapidjson::kStringType);
4119 value.SetString(account_data.c_str(), account_data.length());
4120 json.AddMember("key_data", value, json.GetAllocator());
4121 if (!seed_language.empty())
4122 {
4123 value.SetString(seed_language.c_str(), seed_language.length());
4124 json.AddMember("seed_language", value, json.GetAllocator());
4125 }
4126
4127 rapidjson::Value value2(rapidjson::kNumberType);
4128
4129 value2.SetInt(m_key_device_type);
4130 json.AddMember("key_on_device", value2, json.GetAllocator());
4131
4132 value2.SetInt(watch_only ? 1 :0); // WTF ? JSON has different true and false types, and not boolean ??
4133 json.AddMember("watch_only", value2, json.GetAllocator());
4134
4135 value2.SetInt(m_multisig ? 1 :0);
4136 json.AddMember("multisig", value2, json.GetAllocator());
4137
4138 value2.SetUint(m_multisig_threshold);
4139 json.AddMember("multisig_threshold", value2, json.GetAllocator());
4140
4141 if (m_multisig)
4142 {
4143 bool r = ::serialization::dump_binary(m_multisig_signers, multisig_signers);
4144 CHECK_AND_ASSERT_MES(r, false, "failed to serialize wallet multisig signers");
4145 value.SetString(multisig_signers.c_str(), multisig_signers.length());
4146 json.AddMember("multisig_signers", value, json.GetAllocator());
4147
4148 r = ::serialization::dump_binary(m_multisig_derivations, multisig_derivations);
4149 CHECK_AND_ASSERT_MES(r, false, "failed to serialize wallet multisig derivations");
4150 value.SetString(multisig_derivations.c_str(), multisig_derivations.length());
4151 json.AddMember("multisig_derivations", value, json.GetAllocator());
4152
4153 value2.SetUint(m_multisig_rounds_passed);
4154 json.AddMember("multisig_rounds_passed", value2, json.GetAllocator());
4155 }
4156
4157 value2.SetInt(m_always_confirm_transfers ? 1 :0);
4158 json.AddMember("always_confirm_transfers", value2, json.GetAllocator());
4159
4160 value2.SetInt(m_print_ring_members ? 1 :0);
4161 json.AddMember("print_ring_members", value2, json.GetAllocator());
4162
4163 value2.SetInt(m_store_tx_info ? 1 :0);
4164 json.AddMember("store_tx_info", value2, json.GetAllocator());
4165
4166 value2.SetUint(m_default_mixin);
4167 json.AddMember("default_mixin", value2, json.GetAllocator());
4168
4169 value2.SetUint(m_default_priority);
4170 json.AddMember("default_priority", value2, json.GetAllocator());
4171
4172 value2.SetInt(m_auto_refresh ? 1 :0);
4173 json.AddMember("auto_refresh", value2, json.GetAllocator());
4174
4175 value2.SetInt(m_refresh_type);
4176 json.AddMember("refresh_type", value2, json.GetAllocator());
4177
4178 value2.SetUint64(m_refresh_from_block_height);
4179 json.AddMember("refresh_height", value2, json.GetAllocator());
4180
4181 value2.SetInt(m_confirm_missing_payment_id ? 1 :0);
4182 json.AddMember("confirm_missing_payment_id", value2, json.GetAllocator());
4183
4184 value2.SetInt(m_confirm_non_default_ring_size ? 1 :0);
4185 json.AddMember("confirm_non_default_ring_size", value2, json.GetAllocator());
4186
4187 value2.SetInt(m_ask_password);
4188 json.AddMember("ask_password", value2, json.GetAllocator());
4189
4190 value2.SetUint(m_min_output_count);
4191 json.AddMember("min_output_count", value2, json.GetAllocator());
4192
4193 value2.SetUint64(m_min_output_value);
4194 json.AddMember("min_output_value", value2, json.GetAllocator());
4195
4197 json.AddMember("default_decimal_point", value2, json.GetAllocator());
4198
4199 value2.SetInt(m_merge_destinations ? 1 :0);
4200 json.AddMember("merge_destinations", value2, json.GetAllocator());
4201
4202 value2.SetInt(m_confirm_backlog ? 1 :0);
4203 json.AddMember("confirm_backlog", value2, json.GetAllocator());
4204
4205 value2.SetUint(m_confirm_backlog_threshold);
4206 json.AddMember("confirm_backlog_threshold", value2, json.GetAllocator());
4207
4208 value2.SetInt(m_confirm_export_overwrite ? 1 :0);
4209 json.AddMember("confirm_export_overwrite", value2, json.GetAllocator());
4210
4211 value2.SetInt(m_auto_low_priority ? 1 : 0);
4212 json.AddMember("auto_low_priority", value2, json.GetAllocator());
4213
4214 value2.SetUint(m_nettype);
4215 json.AddMember("nettype", value2, json.GetAllocator());
4216
4217 value2.SetInt(m_segregate_pre_fork_outputs ? 1 : 0);
4218 json.AddMember("segregate_pre_fork_outputs", value2, json.GetAllocator());
4219
4220 value2.SetInt(m_key_reuse_mitigation2 ? 1 : 0);
4221 json.AddMember("key_reuse_mitigation2", value2, json.GetAllocator());
4222
4223 value2.SetUint(m_segregation_height);
4224 json.AddMember("segregation_height", value2, json.GetAllocator());
4225
4226 value2.SetInt(m_ignore_fractional_outputs ? 1 : 0);
4227 json.AddMember("ignore_fractional_outputs", value2, json.GetAllocator());
4228
4229 value2.SetInt(m_track_uses ? 1 : 0);
4230 json.AddMember("track_uses", value2, json.GetAllocator());
4231
4232 value2.SetInt(m_setup_background_mining);
4233 json.AddMember("setup_background_mining", value2, json.GetAllocator());
4234
4235 value2.SetUint(m_subaddress_lookahead_major);
4236 json.AddMember("subaddress_lookahead_major", value2, json.GetAllocator());
4237
4238 value2.SetUint(m_subaddress_lookahead_minor);
4239 json.AddMember("subaddress_lookahead_minor", value2, json.GetAllocator());
4240
4241 value2.SetInt(m_original_keys_available ? 1 : 0);
4242 json.AddMember("original_keys_available", value2, json.GetAllocator());
4243
4244 value2.SetUint(1);
4245 json.AddMember("encrypted_secret_keys", value2, json.GetAllocator());
4246
4247 value.SetString(m_device_name.c_str(), m_device_name.size());
4248 json.AddMember("device_name", value, json.GetAllocator());
4249
4250 value.SetString(m_device_derivation_path.c_str(), m_device_derivation_path.size());
4251 json.AddMember("device_derivation_path", value, json.GetAllocator());
4252
4253 value2.SetUint(m_account_major_offset);
4254 json.AddMember("account_major_offset", value2, json.GetAllocator());
4255
4256 std::string original_address;
4257 std::string original_view_secret_key;
4258 if (m_original_keys_available)
4259 {
4260 original_address = get_account_address_as_str(m_nettype, false, m_original_address);
4261 value.SetString(original_address.c_str(), original_address.length());
4262 json.AddMember("original_address", value, json.GetAllocator());
4263 original_view_secret_key = epee::string_tools::pod_to_hex(m_original_view_secret_key);
4264 value.SetString(original_view_secret_key.c_str(), original_view_secret_key.length());
4265 json.AddMember("original_view_secret_key", value, json.GetAllocator());
4266 }
4267
4268 // Serialize the JSON object
4269 rapidjson::StringBuffer buffer;
4270 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
4271 json.Accept(writer);
4272 account_data = buffer.GetString();
4273
4274 // Encrypt the entire JSON object.
4275 std::string cipher;
4276 cipher.resize(account_data.size());
4277 keys_file_data.iv = crypto::rand<crypto::chacha_iv>();
4278 crypto::chacha20(account_data.data(), account_data.size(), key, keys_file_data.iv, &cipher[0]);
4279 keys_file_data.account_data = cipher;
4280
4281 std::string tmp_file_name = keys_file_name + ".new";
4282 std::string buf;
4283 r = ::serialization::dump_binary(keys_file_data, buf);
4284 r = r && epee::file_io_utils::save_string_to_file(tmp_file_name, buf);
4285 CHECK_AND_ASSERT_MES(r, false, "failed to generate wallet keys file " << tmp_file_name);
4286
4287 unlock_keys_file();
4288 std::error_code e = tools::replace_file(tmp_file_name, keys_file_name);
4289 lock_keys_file();
4290
4291 if (e) {
4292 boost::filesystem::remove(tmp_file_name);
4293 LOG_ERROR("failed to update wallet keys file " << keys_file_name);
4294 return false;
4295 }
4296
4297 return true;
4298}
4299//----------------------------------------------------------------------------------------------------
4300void wallet2::setup_keys(const epee::wipeable_string &password)
4301{
4302 crypto::chacha_key key;
4303 crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
4304
4305 // re-encrypt, but keep viewkey unencrypted
4306 if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
4307 {
4308 m_account.encrypt_keys(key);
4309 m_account.decrypt_viewkey(key);
4310 }
4311
4312 static_assert(HASH_SIZE == sizeof(crypto::chacha_key), "Mismatched sizes of hash and chacha key");
4314 memcpy(cache_key_data.data(), &key, HASH_SIZE);
4315 cache_key_data[HASH_SIZE] = CACHE_KEY_TAIL;
4316 cn_fast_hash(cache_key_data.data(), HASH_SIZE+1, (crypto::hash&)m_cache_key);
4317 get_ringdb_key();
4318}
4319//----------------------------------------------------------------------------------------------------
4320void wallet2::change_password(const std::string &filename, const epee::wipeable_string &original_password, const epee::wipeable_string &new_password)
4321{
4322 if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
4323 decrypt_keys(original_password);
4324 setup_keys(new_password);
4325 rewrite(filename, new_password);
4326 if (!filename.empty())
4327 store();
4328}
4329//----------------------------------------------------------------------------------------------------
4335bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_string& password)
4336{
4337 rapidjson::Document json;
4338 wallet2::keys_file_data keys_file_data;
4339 std::string buf;
4340 bool encrypted_secret_keys = false;
4341 bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf);
4343
4344 // Decrypt the contents
4345 r = ::serialization::parse_binary(buf, keys_file_data);
4346 THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + keys_file_name + '\"');
4347 crypto::chacha_key key;
4348 crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
4349 std::string account_data;
4350 account_data.resize(keys_file_data.account_data.size());
4351 crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
4352 if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject())
4353 crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
4354
4355 // The contents should be JSON if the wallet follows the new format.
4356 if (json.Parse(account_data.c_str()).HasParseError())
4357 {
4358 is_old_file_format = true;
4359 m_watch_only = false;
4360 m_multisig = false;
4361 m_multisig_threshold = 0;
4362 m_multisig_signers.clear();
4363 m_multisig_rounds_passed = 0;
4364 m_multisig_derivations.clear();
4365 m_always_confirm_transfers = true;
4366 m_print_ring_members = false;
4367 m_store_tx_info = true;
4368 m_default_mixin = 0;
4369 m_default_priority = 0;
4370 m_auto_refresh = true;
4371 m_refresh_type = RefreshType::RefreshDefault;
4372 m_refresh_from_block_height = 0;
4373 m_confirm_missing_payment_id = true;
4374 m_confirm_non_default_ring_size = true;
4375 m_ask_password = AskPasswordOnAction;
4377 m_min_output_count = 0;
4378 m_min_output_value = 0;
4379 m_merge_destinations = false;
4380 m_confirm_backlog = true;
4381 m_confirm_backlog_threshold = 0;
4382 m_confirm_export_overwrite = true;
4383 m_auto_low_priority = true;
4384 m_segregate_pre_fork_outputs = true;
4385 m_key_reuse_mitigation2 = true;
4386 m_segregation_height = 0;
4387 m_ignore_fractional_outputs = true;
4388 m_track_uses = false;
4389 m_setup_background_mining = BackgroundMiningMaybe;
4390 m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR;
4391 m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR;
4392 m_original_keys_available = false;
4393 m_device_name = "";
4394 m_device_derivation_path = "";
4395 m_key_device_type = hw::device::device_type::SOFTWARE;
4396 m_account_major_offset = 0;
4397 encrypted_secret_keys = false;
4398 }
4399 else if(json.IsObject())
4400 {
4401 if (!json.HasMember("key_data"))
4402 {
4403 LOG_ERROR("Field key_data not found in JSON");
4404 return false;
4405 }
4406 if (!json["key_data"].IsString())
4407 {
4408 LOG_ERROR("Field key_data found in JSON, but not String");
4409 return false;
4410 }
4411 const char *field_key_data = json["key_data"].GetString();
4412 account_data = std::string(field_key_data, field_key_data + json["key_data"].GetStringLength());
4413
4414 if (json.HasMember("key_on_device"))
4415 {
4417 m_key_device_type = static_cast<hw::device::device_type>(field_key_on_device);
4418 }
4419
4420 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, seed_language, std::string, String, false, std::string());
4421 if (field_seed_language_found)
4422 {
4423 set_seed_language(field_seed_language);
4424 }
4425 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, watch_only, int, Int, false, false);
4426 m_watch_only = field_watch_only;
4427 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, multisig, int, Int, false, false);
4428 m_multisig = field_multisig;
4429 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, multisig_threshold, unsigned int, Uint, m_multisig, 0);
4430 m_multisig_threshold = field_multisig_threshold;
4431 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, multisig_rounds_passed, unsigned int, Uint, false, 0);
4432 m_multisig_rounds_passed = field_multisig_rounds_passed;
4433 if (m_multisig)
4434 {
4435 if (!json.HasMember("multisig_signers"))
4436 {
4437 LOG_ERROR("Field multisig_signers not found in JSON");
4438 return false;
4439 }
4440 if (!json["multisig_signers"].IsString())
4441 {
4442 LOG_ERROR("Field multisig_signers found in JSON, but not String");
4443 return false;
4444 }
4445 const char *field_multisig_signers = json["multisig_signers"].GetString();
4446 std::string multisig_signers = std::string(field_multisig_signers, field_multisig_signers + json["multisig_signers"].GetStringLength());
4447 r = ::serialization::parse_binary(multisig_signers, m_multisig_signers);
4448 if (!r)
4449 {
4450 LOG_ERROR("Field multisig_signers found in JSON, but failed to parse");
4451 return false;
4452 }
4453
4454 //previous version of multisig does not have this field
4455 if (json.HasMember("multisig_derivations"))
4456 {
4457 if (!json["multisig_derivations"].IsString())
4458 {
4459 LOG_ERROR("Field multisig_derivations found in JSON, but not String");
4460 return false;
4461 }
4462 const char *field_multisig_derivations = json["multisig_derivations"].GetString();
4463 std::string multisig_derivations = std::string(field_multisig_derivations, field_multisig_derivations + json["multisig_derivations"].GetStringLength());
4464 r = ::serialization::parse_binary(multisig_derivations, m_multisig_derivations);
4465 if (!r)
4466 {
4467 LOG_ERROR("Field multisig_derivations found in JSON, but failed to parse");
4468 return false;
4469 }
4470 }
4471 }
4472 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, always_confirm_transfers, int, Int, false, true);
4473 m_always_confirm_transfers = field_always_confirm_transfers;
4474 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, print_ring_members, int, Int, false, true);
4475 m_print_ring_members = field_print_ring_members;
4476 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_keys, int, Int, false, true);
4477 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_info, int, Int, false, true);
4478 m_store_tx_info = ((field_store_tx_keys != 0) || (field_store_tx_info != 0));
4479 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_mixin, unsigned int, Uint, false, 0);
4480 m_default_mixin = field_default_mixin;
4481 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_priority, unsigned int, Uint, false, 0);
4482 if (field_default_priority_found)
4483 {
4484 m_default_priority = field_default_priority;
4485 }
4486 else
4487 {
4488 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_fee_multiplier, unsigned int, Uint, false, 0);
4489 if (field_default_fee_multiplier_found)
4490 m_default_priority = field_default_fee_multiplier;
4491 else
4492 m_default_priority = 0;
4493 }
4494 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, auto_refresh, int, Int, false, true);
4495 m_auto_refresh = field_auto_refresh;
4496 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, refresh_type, int, Int, false, RefreshType::RefreshDefault);
4497 m_refresh_type = RefreshType::RefreshDefault;
4498 if (field_refresh_type_found)
4499 {
4500 if (field_refresh_type == RefreshFull || field_refresh_type == RefreshOptimizeCoinbase || field_refresh_type == RefreshNoCoinbase)
4501 m_refresh_type = (RefreshType)field_refresh_type;
4502 else
4503 LOG_PRINT_L0("Unknown refresh-type value (" << field_refresh_type << "), using default");
4504 }
4505 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, refresh_height, uint64_t, Uint64, false, 0);
4506 m_refresh_from_block_height = field_refresh_height;
4507 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_missing_payment_id, int, Int, false, true);
4508 m_confirm_missing_payment_id = field_confirm_missing_payment_id;
4509 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_non_default_ring_size, int, Int, false, true);
4510 m_confirm_non_default_ring_size = field_confirm_non_default_ring_size;
4511 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ask_password, AskPasswordType, Int, false, AskPasswordToDecrypt);
4512 m_ask_password = field_ask_password;
4513 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_decimal_point, int, Int, false, CRYPTONOTE_DISPLAY_DECIMAL_POINT);
4514 cryptonote::set_default_decimal_point(field_default_decimal_point);
4515 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, min_output_count, uint32_t, Uint, false, 0);
4516 m_min_output_count = field_min_output_count;
4517 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, min_output_value, uint64_t, Uint64, false, 0);
4518 m_min_output_value = field_min_output_value;
4519 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, merge_destinations, int, Int, false, false);
4520 m_merge_destinations = field_merge_destinations;
4521 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_backlog, int, Int, false, true);
4522 m_confirm_backlog = field_confirm_backlog;
4523 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_backlog_threshold, uint32_t, Uint, false, 0);
4524 m_confirm_backlog_threshold = field_confirm_backlog_threshold;
4525 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_export_overwrite, int, Int, false, true);
4526 m_confirm_export_overwrite = field_confirm_export_overwrite;
4527 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, auto_low_priority, int, Int, false, true);
4528 m_auto_low_priority = field_auto_low_priority;
4529 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, nettype, uint8_t, Uint, false, static_cast<uint8_t>(m_nettype));
4530 // The network type given in the program argument is inconsistent with the network type saved in the wallet
4531 THROW_WALLET_EXCEPTION_IF(static_cast<uint8_t>(m_nettype) != field_nettype, error::wallet_internal_error,
4532 (boost::format("%s wallet cannot be opened as %s wallet")
4533 % (field_nettype == 0 ? "Mainnet" : field_nettype == 1 ? "Testnet" : "Stagenet")
4534 % (m_nettype == MAINNET ? "mainnet" : m_nettype == TESTNET ? "testnet" : "stagenet")).str());
4535 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, segregate_pre_fork_outputs, int, Int, false, true);
4536 m_segregate_pre_fork_outputs = field_segregate_pre_fork_outputs;
4537 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, key_reuse_mitigation2, int, Int, false, true);
4538 m_key_reuse_mitigation2 = field_key_reuse_mitigation2;
4539 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, segregation_height, int, Uint, false, 0);
4540 m_segregation_height = field_segregation_height;
4541 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ignore_fractional_outputs, int, Int, false, true);
4542 m_ignore_fractional_outputs = field_ignore_fractional_outputs;
4543 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, track_uses, int, Int, false, false);
4544 m_track_uses = field_track_uses;
4545 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, setup_background_mining, BackgroundMiningSetupType, Int, false, BackgroundMiningMaybe);
4546 m_setup_background_mining = field_setup_background_mining;
4547 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_major, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MAJOR);
4548 m_subaddress_lookahead_major = field_subaddress_lookahead_major;
4549 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_minor, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MINOR);
4550 m_subaddress_lookahead_minor = field_subaddress_lookahead_minor;
4551
4552 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, encrypted_secret_keys, uint32_t, Uint, false, false);
4553 encrypted_secret_keys = field_encrypted_secret_keys;
4554
4555 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, account_major_offset, uint32_t, Uint, false, 0);
4556 m_account_major_offset = field_account_major_offset;
4557
4558 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, device_name, std::string, String, false, std::string());
4559 if (m_device_name.empty())
4560 {
4561 if (field_device_name_found)
4562 {
4563 m_device_name = field_device_name;
4564 }
4565 else
4566 {
4567 m_device_name = m_key_device_type == hw::device::device_type::LEDGER ? "Ledger" : "default";
4568 }
4569 }
4570
4571 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, device_derivation_path, std::string, String, false, std::string());
4572 m_device_derivation_path = field_device_derivation_path;
4573
4574 if (json.HasMember("original_keys_available"))
4575 {
4576 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, original_keys_available, int, Int, false, false);
4577 m_original_keys_available = field_original_keys_available;
4578 if (m_original_keys_available)
4579 {
4580 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, original_address, std::string, String, true, std::string());
4582 bool ok = get_account_address_from_str(info, m_nettype, field_original_address);
4583 if (!ok)
4584 {
4585 LOG_ERROR("Failed to parse original_address from JSON");
4586 return false;
4587 }
4588 m_original_address = info.address;
4589 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, original_view_secret_key, std::string, String, true, std::string());
4590 ok = epee::string_tools::hex_to_pod(field_original_view_secret_key, m_original_view_secret_key);
4591 if (!ok)
4592 {
4593 LOG_ERROR("Failed to parse original_view_secret_key from JSON");
4594 return false;
4595 }
4596 }
4597 }
4598 else
4599 {
4600 m_original_keys_available = false;
4601 }
4602 }
4603 else
4604 {
4605 THROW_WALLET_EXCEPTION(error::wallet_internal_error, "invalid password");
4606 return false;
4607 }
4608
4609 r = epee::serialization::load_t_from_binary(m_account, account_data);
4610 THROW_WALLET_EXCEPTION_IF(!r, error::invalid_password);
4611 if (m_key_device_type == hw::device::device_type::LEDGER || m_key_device_type == hw::device::device_type::TREZOR) {
4612 LOG_PRINT_L0("Account on device. Initing device...");
4613 hw::device &hwdev = lookup_device(m_device_name);
4614 THROW_WALLET_EXCEPTION_IF(!hwdev.set_name(m_device_name), error::wallet_internal_error, "Could not set device name " + m_device_name);
4615 hwdev.set_network_type(m_nettype);
4616 hwdev.set_derivation_path(m_device_derivation_path);
4617 hwdev.set_callback(get_device_callback());
4618 THROW_WALLET_EXCEPTION_IF(!hwdev.init(), error::wallet_internal_error, "Could not initialize the device " + m_device_name);
4619 THROW_WALLET_EXCEPTION_IF(!hwdev.connect(), error::wallet_internal_error, "Could not connect to the device " + m_device_name);
4620 m_account.set_device(hwdev);
4621
4622 account_public_address device_account_public_address;
4623 THROW_WALLET_EXCEPTION_IF(!hwdev.get_public_address(device_account_public_address), error::wallet_internal_error, "Cannot get a device address");
4624 THROW_WALLET_EXCEPTION_IF(device_account_public_address != m_account.get_keys().m_account_address, error::wallet_internal_error, "Device wallet does not match wallet address. "
4625 "Device address: " + cryptonote::get_account_address_as_str(m_nettype, false, device_account_public_address) +
4626 ", wallet address: " + m_account.get_public_address_str(m_nettype));
4627 LOG_PRINT_L0("Device inited...");
4628 } else if (key_on_device()) {
4629 THROW_WALLET_EXCEPTION(error::wallet_internal_error, "hardware device not supported");
4630 }
4631
4632 if (r)
4633 {
4634 if (encrypted_secret_keys)
4635 {
4636 m_account.decrypt_keys(key);
4637 }
4638 else
4639 {
4640 // rewrite with encrypted keys, ignore errors
4641 if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
4642 encrypt_keys(key);
4643 bool saved_ret = store_keys(keys_file_name, password, m_watch_only);
4644 if (!saved_ret)
4645 {
4646 // just moan a bit, but not fatal
4647 MERROR("Error saving keys file with encrypted keys, not fatal");
4648 }
4649 if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
4650 decrypt_keys(key);
4651 m_keys_file_locker.reset();
4652 }
4653 }
4654 const cryptonote::account_keys& keys = m_account.get_keys();
4655 hw::device &hwdev = m_account.get_device();
4657 if(!m_watch_only && !m_multisig && hwdev.device_protocol() != hw::device::PROTOCOL_COLD)
4659 THROW_WALLET_EXCEPTION_IF(!r, error::invalid_password);
4660
4661 if (r)
4662 setup_keys(password);
4663
4664 return true;
4665}
4666
4678{
4679 // this temporary unlocking is necessary for Windows (otherwise the file couldn't be loaded).
4681 bool r = verify_password(m_keys_file, password, m_account.get_device().device_protocol() == hw::device::PROTOCOL_COLD || m_watch_only || m_multisig, m_account.get_device(), m_kdf_rounds);
4683 return r;
4684}
4685
4699bool wallet2::verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds)
4700{
4701 rapidjson::Document json;
4703 std::string buf;
4704 bool encrypted_secret_keys = false;
4705 bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf);
4707
4708 // Decrypt the contents
4710 THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + keys_file_name + '\"');
4711 crypto::chacha_key key;
4712 crypto::generate_chacha_key(password.data(), password.size(), key, kdf_rounds);
4713 std::string account_data;
4714 account_data.resize(keys_file_data.account_data.size());
4715 crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
4716 if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject())
4717 crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
4718
4719 // The contents should be JSON if the wallet follows the new format.
4720 if (json.Parse(account_data.c_str()).HasParseError())
4721 {
4722 // old format before JSON wallet key file format
4723 }
4724 else
4725 {
4726 account_data = std::string(json["key_data"].GetString(), json["key_data"].GetString() +
4727 json["key_data"].GetStringLength());
4728 GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, encrypted_secret_keys, uint32_t, Uint, false, false);
4729 encrypted_secret_keys = field_encrypted_secret_keys;
4730 }
4731
4732 cryptonote::account_base account_data_check;
4733
4734 r = epee::serialization::load_t_from_binary(account_data_check, account_data);
4735
4736 if (encrypted_secret_keys)
4737 account_data_check.decrypt_keys(key);
4738
4739 const cryptonote::account_keys& keys = account_data_check.get_keys();
4741 if(!no_spend_key)
4743 return r;
4744}
4745
4746void wallet2::encrypt_keys(const crypto::chacha_key &key)
4747{
4748 m_account.encrypt_keys(key);
4749 m_account.decrypt_viewkey(key);
4750}
4751
4752void wallet2::decrypt_keys(const crypto::chacha_key &key)
4753{
4754 m_account.encrypt_viewkey(key);
4755 m_account.decrypt_keys(key);
4756}
4757
4759{
4760 crypto::chacha_key key;
4761 crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
4763}
4764
4766{
4767 crypto::chacha_key key;
4768 crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
4770}
4771
4772void wallet2::setup_new_blockchain()
4773{
4775 generate_genesis(b);
4776 m_blockchain.push_back(get_block_hash(b));
4777 m_last_block_reward = cryptonote::get_outs_etn_amount(b.miner_tx);
4778 add_subaddress_account(tr("Primary account"));
4779}
4780
4781void wallet2::create_keys_file(const std::string &wallet_, bool watch_only, const epee::wipeable_string &password, bool create_address_file)
4782{
4783 if (!wallet_.empty())
4784 {
4785 bool r = store_keys(m_keys_file, password, watch_only);
4786 THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
4787
4788 if (create_address_file)
4789 {
4790 r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
4791 if(!r) MERROR("String with address text not saved");
4792 }
4793 }
4794}
4795
4796
4807bool wallet2::query_device(hw::device::device_type& device_type, const std::string& keys_file_name, const epee::wipeable_string& password, uint64_t kdf_rounds)
4808{
4809 rapidjson::Document json;
4811 std::string buf;
4812 bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf);
4814
4815 // Decrypt the contents
4817 THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + keys_file_name + '\"');
4818 crypto::chacha_key key;
4819 crypto::generate_chacha_key(password.data(), password.size(), key, kdf_rounds);
4820 std::string account_data;
4821 account_data.resize(keys_file_data.account_data.size());
4822 crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
4823 if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject())
4824 crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
4825
4827 // The contents should be JSON if the wallet follows the new format.
4828 if (json.Parse(account_data.c_str()).HasParseError())
4829 {
4830 // old format before JSON wallet key file format
4831 }
4832 else
4833 {
4834 account_data = std::string(json["key_data"].GetString(), json["key_data"].GetString() +
4835 json["key_data"].GetStringLength());
4836
4837 if (json.HasMember("key_on_device"))
4838 {
4840 device_type = static_cast<hw::device::device_type>(field_key_on_device);
4841 }
4842 }
4843
4844 cryptonote::account_base account_data_check;
4845
4846 r = epee::serialization::load_t_from_binary(account_data_check, account_data);
4847 if (!r) return false;
4848 return true;
4849}
4850
4851void wallet2::init_type(hw::device::device_type device_type)
4852{
4853 m_account_public_address = m_account.get_keys().m_account_address;
4854 m_watch_only = false;
4855 m_multisig = false;
4856 m_multisig_threshold = 0;
4857 m_multisig_signers.clear();
4858 m_original_keys_available = false;
4859 m_key_device_type = device_type;
4860}
4861
4869void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password,
4870 const epee::wipeable_string& multisig_data, bool create_address_file)
4871{
4872 clear();
4873 prepare_file_names(wallet_);
4874
4875 if (!wallet_.empty())
4876 {
4877 boost::system::error_code ignored_ec;
4878 THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, m_wallet_file);
4879 THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
4880 }
4881
4882 m_account.generate(rct::rct2sk(rct::zero()), true, false);
4883
4885 size_t offset = 0;
4886 uint32_t threshold = *(uint32_t*)(multisig_data.data() + offset);
4887 offset += sizeof(uint32_t);
4888 uint32_t total = *(uint32_t*)(multisig_data.data() + offset);
4889 offset += sizeof(uint32_t);
4892 const size_t n_multisig_keys = total == threshold ? 1 : threshold;
4893 THROW_WALLET_EXCEPTION_IF(multisig_data.size() != 8 + 32 * (4 + n_multisig_keys + total), error::invalid_multisig_seed);
4894
4895 std::vector<crypto::secret_key> multisig_keys;
4896 std::vector<crypto::public_key> multisig_signers;
4897 crypto::secret_key spend_secret_key = *(crypto::secret_key*)(multisig_data.data() + offset);
4898 offset += sizeof(crypto::secret_key);
4899 crypto::public_key spend_public_key = *(crypto::public_key*)(multisig_data.data() + offset);
4900 offset += sizeof(crypto::public_key);
4901 crypto::secret_key view_secret_key = *(crypto::secret_key*)(multisig_data.data() + offset);
4902 offset += sizeof(crypto::secret_key);
4903 crypto::public_key view_public_key = *(crypto::public_key*)(multisig_data.data() + offset);
4904 offset += sizeof(crypto::public_key);
4905 for (size_t n = 0; n < n_multisig_keys; ++n)
4906 {
4907 multisig_keys.push_back(*(crypto::secret_key*)(multisig_data.data() + offset));
4908 offset += sizeof(crypto::secret_key);
4909 }
4910 for (size_t n = 0; n < total; ++n)
4911 {
4912 multisig_signers.push_back(*(crypto::public_key*)(multisig_data.data() + offset));
4913 offset += sizeof(crypto::public_key);
4914 }
4915
4916 crypto::public_key calculated_view_public_key;
4917 THROW_WALLET_EXCEPTION_IF(!crypto::secret_key_to_public_key(view_secret_key, calculated_view_public_key), error::invalid_multisig_seed);
4918 THROW_WALLET_EXCEPTION_IF(view_public_key != calculated_view_public_key, error::invalid_multisig_seed);
4919 crypto::public_key local_signer;
4921 THROW_WALLET_EXCEPTION_IF(std::find(multisig_signers.begin(), multisig_signers.end(), local_signer) == multisig_signers.end(), error::invalid_multisig_seed);
4922 rct::key skey = rct::zero();
4923 for (const auto &msk: multisig_keys)
4924 sc_add(skey.bytes, skey.bytes, rct::sk2rct(msk).bytes);
4925 THROW_WALLET_EXCEPTION_IF(!(rct::rct2sk(skey) == spend_secret_key), error::invalid_multisig_seed);
4926 memwipe(&skey, sizeof(rct::key));
4927
4928 m_account.make_multisig(view_secret_key, spend_secret_key, spend_public_key, multisig_keys);
4929 m_account.finalize_multisig(spend_public_key);
4930
4931 // Not possible to restore a multisig wallet that is able to activate the MMS
4932 // (because the original keys are not (yet) part of the restore info), so
4933 // keep m_original_keys_available to false
4935 m_multisig = true;
4936 m_multisig_threshold = threshold;
4937 m_multisig_signers = multisig_signers;
4938 setup_keys(password);
4939
4940 create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file);
4941 setup_new_blockchain();
4942
4943 if (!wallet_.empty())
4944 store();
4945}
4946
4957crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password,
4958 const crypto::secret_key& recovery_param, bool recover, bool two_random, bool create_address_file)
4959{
4960 clear();
4961 prepare_file_names(wallet_);
4962
4963 if (!wallet_.empty())
4964 {
4965 boost::system::error_code ignored_ec;
4966 THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, m_wallet_file);
4967 THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
4968 }
4969
4970 crypto::secret_key retval = m_account.generate(recovery_param, recover, two_random);
4971
4973 setup_keys(password);
4974
4975 // calculate a starting refresh height
4976 if(m_refresh_from_block_height == 0 && !recover){
4977 m_refresh_from_block_height = estimate_blockchain_height();
4978 }
4979
4980 create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file);
4981
4982 setup_new_blockchain();
4983
4984 if (!wallet_.empty())
4985 store();
4986
4987 return retval;
4988}
4989
4991 {
4992 // -1 month for fluctuations in block time and machine date/time setup.
4993 // avg seconds per block
4994 const int seconds_per_block = DIFFICULTY_TARGET_V6;
4995 // ~num blocks per month
4996 const uint64_t blocks_per_month = 60*60*24*30/seconds_per_block;
4997
4998 // try asking the daemon first
4999 std::string err;
5000 uint64_t height = 0;
5001
5002 // we get the max of approximated height and local height.
5003 // approximated height is the least of daemon target height
5004 // (the max of what the other daemons are claiming is their
5005 // height) and the theoretical height based on the local
5006 // clock. This will be wrong only if both the local clock
5007 // is bad *and* a peer daemon claims a highest height than
5008 // the real chain.
5009 // local height is the height the local daemon is currently
5010 // synced to, it will be lower than the real chain height if
5011 // the daemon is currently syncing.
5012 // If we use the approximate height we subtract one month as
5013 // a safety margin.
5015 uint64_t target_height = get_daemon_blockchain_target_height(err);
5016 if (err.empty()) {
5017 if (target_height < height)
5018 height = target_height;
5019 } else {
5020 // if we couldn't talk to the daemon, check safety margin.
5021 if (height > blocks_per_month)
5022 height -= blocks_per_month;
5023 else
5024 height = 0;
5025 }
5026 uint64_t local_height = get_daemon_blockchain_height(err);
5027 if (err.empty() && local_height > height)
5028 height = local_height;
5029 return height;
5030 }
5031
5040void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password,
5042 const crypto::secret_key& viewkey, bool create_address_file)
5043{
5044 clear();
5045 prepare_file_names(wallet_);
5046
5047 if (!wallet_.empty())
5048 {
5049 boost::system::error_code ignored_ec;
5050 THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, m_wallet_file);
5051 THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
5052 }
5053
5054 m_account.create_from_viewkey(account_public_address, viewkey);
5056 m_watch_only = true;
5057 m_account_public_address = account_public_address;
5058 setup_keys(password);
5059
5060 create_keys_file(wallet_, true, password, m_nettype != MAINNET || create_address_file);
5061
5062 setup_new_blockchain();
5063
5064 if (!wallet_.empty())
5065 store();
5066}
5067
5077void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password,
5079 const crypto::secret_key& spendkey, const crypto::secret_key& viewkey, bool create_address_file)
5080{
5081 clear();
5082 prepare_file_names(wallet_);
5083
5084 if (!wallet_.empty())
5085 {
5086 boost::system::error_code ignored_ec;
5087 THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, m_wallet_file);
5088 THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
5089 }
5090
5091 m_account.create_from_keys(account_public_address, spendkey, viewkey);
5093 m_account_public_address = account_public_address;
5094 setup_keys(password);
5095
5096 create_keys_file(wallet_, false, password, create_address_file);
5097
5098 setup_new_blockchain();
5099
5100 if (!wallet_.empty())
5101 store();
5102}
5103
5110void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& password, const std::string &device_name, bool create_address_file)
5111{
5112 clear();
5113 prepare_file_names(wallet_);
5114
5115 boost::system::error_code ignored_ec;
5116 if (!wallet_.empty()) {
5117 THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, m_wallet_file);
5118 THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
5119 }
5120
5121 auto &hwdev = lookup_device(device_name);
5122 hwdev.set_name(device_name);
5123 hwdev.set_network_type(m_nettype);
5124 hwdev.set_derivation_path(m_device_derivation_path);
5125 hwdev.set_callback(get_device_callback());
5126
5127 m_account.create_from_device(hwdev);
5128 init_type(m_account.get_device().get_type());
5129 setup_keys(password);
5130 m_device_name = device_name;
5131
5132 create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file);
5133 if (m_subaddress_lookahead_major == SUBADDRESS_LOOKAHEAD_MAJOR && m_subaddress_lookahead_minor == SUBADDRESS_LOOKAHEAD_MINOR)
5134 {
5135 // the default lookahead setting (50:200) is clearly too much for hardware wallet
5136 m_subaddress_lookahead_major = 5;
5137 m_subaddress_lookahead_minor = 20;
5138 }
5139 setup_new_blockchain();
5140 if (!wallet_.empty()) {
5141 store();
5142 }
5143}
5144
5146 const std::vector<crypto::secret_key> &view_keys,
5147 const std::vector<crypto::public_key> &spend_keys,
5149{
5150 CHECK_AND_ASSERT_THROW_MES(!view_keys.empty(), "empty view keys");
5151 CHECK_AND_ASSERT_THROW_MES(view_keys.size() == spend_keys.size(), "Mismatched view/spend key sizes");
5152 CHECK_AND_ASSERT_THROW_MES(threshold > 1 && threshold <= spend_keys.size() + 1, "Invalid threshold");
5153
5154 std::string extra_multisig_info;
5155 std::vector<crypto::secret_key> multisig_keys;
5156 rct::key spend_pkey = rct::identity();
5157 rct::key spend_skey;
5158 std::vector<crypto::public_key> multisig_signers;
5159
5160 // decrypt keys
5162 if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
5163 {
5164 crypto::chacha_key chacha_key;
5165 crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds);
5166 m_account.encrypt_viewkey(chacha_key);
5167 m_account.decrypt_keys(chacha_key);
5168 keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this, chacha_key]() { m_account.encrypt_keys(chacha_key); m_account.decrypt_viewkey(chacha_key); });
5169 }
5170
5171 // In common multisig scheme there are 4 types of key exchange rounds:
5172 // 1. First round is exchange of view secret keys and public spend keys.
5173 // 2. Middle round is exchange of derivations: Ki = b * Mj, where b - spend secret key,
5174 // M - public multisig key (in first round it equals to public spend key), K - new public multisig key.
5175 // 3. Secret spend establishment round sets your secret multisig keys as follows: kl = H(Ml), where M - is *your* public multisig key,
5176 // k - secret multisig key used to sign transactions. k and M are sets of keys, of course.
5177 // And secret spend key as the sum of all participant's secret multisig keys
5178 // 4. Last round establishes multisig wallet's public spend key. Participants exchange their public multisig keys
5179 // and calculate common spend public key as sum of all unique participants' public multisig keys.
5180 // Note that N/N scheme has only first round. N-1/N has 2 rounds: first and last. Common M/N has all 4 rounds.
5181
5182 // IMPORTANT: wallet's public spend key is not equal to secret_spend_key * G!
5183 // Wallet's public spend key is the sum of unique public multisig keys of all participants.
5184 // secret_spend_key * G = public signer key
5185
5186 if (threshold == spend_keys.size() + 1)
5187 {
5188 // In N / N case we only need to do one round and calculate secret multisig keys and new secret spend key
5189 MINFO("Creating spend key...");
5190
5191 // Calculates all multisig keys and spend key
5192 cryptonote::generate_multisig_N_N(get_account().get_keys(), spend_keys, multisig_keys, spend_skey, spend_pkey);
5193
5194 // Our signer key is b * G, where b is secret spend key.
5195 multisig_signers = spend_keys;
5196 multisig_signers.push_back(get_multisig_signer_public_key(get_account().get_keys().m_spend_secret_key));
5197 }
5198 else
5199 {
5200 // We just got public spend keys of all participants and deriving multisig keys (set of Mi = b * Bi).
5201 // note that derivations are public keys as DH exchange suppose it to be
5202 auto derivations = cryptonote::generate_multisig_derivations(get_account().get_keys(), spend_keys);
5203
5204 spend_pkey = rct::identity();
5205 multisig_signers = std::vector<crypto::public_key>(spend_keys.size() + 1, crypto::null_pkey);
5206
5207 if (threshold == spend_keys.size())
5208 {
5209 // N - 1 / N case
5210
5211 // We need an extra step, so we package all the composite public keys
5212 // we know about, and make a signed string out of them
5213 MINFO("Creating spend key...");
5214
5215 // Calculating set of our secret multisig keys as follows: mi = H(Mi),
5216 // where mi - secret multisig key, Mi - others' participants public multisig key
5217 multisig_keys = cryptonote::calculate_multisig_keys(derivations);
5218
5219 // calculating current participant's spend secret key as sum of all secret multisig keys for current participant.
5220 // IMPORTANT: participant's secret spend key is not an entire wallet's secret spend!
5221 // Entire wallet's secret spend is sum of all unique secret multisig keys
5222 // among all of participants and is not held by anyone!
5223 spend_skey = rct::sk2rct(cryptonote::calculate_multisig_signer_key(multisig_keys));
5224
5225 // Preparing data for the last round to calculate common public spend key. The data contains public multisig keys.
5226 extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, secret_keys_to_public_keys(multisig_keys), rct::rct2sk(spend_skey));
5227 }
5228 else
5229 {
5230 // M / N case
5231 MINFO("Preparing keys for next exchange round...");
5232
5233 // Preparing data for middle round - packing new public multisig keys to exchage with others.
5234 extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, derivations, m_account.get_keys().m_spend_secret_key);
5235 spend_skey = rct::sk2rct(m_account.get_keys().m_spend_secret_key);
5236
5237 // Need to store middle keys to be able to proceed in case of wallet shutdown.
5238 m_multisig_derivations = derivations;
5239 }
5240 }
5241
5242 if (!m_original_keys_available)
5243 {
5244 // Save the original i.e. non-multisig keys so the MMS can continue to use them to encrypt and decrypt messages
5245 // (making a wallet multisig overwrites those keys, see account_base::make_multisig)
5246 m_original_address = m_account.get_keys().m_account_address;
5247 m_original_view_secret_key = m_account.get_keys().m_view_secret_key;
5248 m_original_keys_available = true;
5249 }
5250
5251 clear();
5252 MINFO("Creating view key...");
5253 crypto::secret_key view_skey = cryptonote::generate_multisig_view_secret_key(get_account().get_keys().m_view_secret_key, view_keys);
5254
5255 MINFO("Creating multisig address...");
5256 CHECK_AND_ASSERT_THROW_MES(m_account.make_multisig(view_skey, rct::rct2sk(spend_skey), rct::rct2pk(spend_pkey), multisig_keys),
5257 "Failed to create multisig wallet due to bad keys");
5258 memwipe(&spend_skey, sizeof(rct::key));
5259
5261 m_original_keys_available = true;
5262 m_multisig = true;
5263 m_multisig_threshold = threshold;
5264 m_multisig_signers = multisig_signers;
5265 ++m_multisig_rounds_passed;
5266
5267 // re-encrypt keys
5268 keys_reencryptor = epee::misc_utils::auto_scope_leave_caller();
5269
5270 create_keys_file(m_wallet_file, false, password, boost::filesystem::exists(m_wallet_file + ".address.txt"));
5271
5272 setup_new_blockchain();
5273
5274 if (!m_wallet_file.empty())
5275 store();
5276
5277 return extra_multisig_info;
5278}
5279
5281 const std::vector<std::string> &info)
5282{
5284 error::wallet_internal_error, "Empty multisig info");
5285
5286 if (info[0].substr(0, MULTISIG_EXTRA_INFO_MAGIC.size()) != MULTISIG_EXTRA_INFO_MAGIC)
5287 {
5289 error::wallet_internal_error, "Unsupported info string");
5290 }
5291
5292 std::vector<crypto::public_key> signers;
5293 std::unordered_set<crypto::public_key> pkeys;
5294
5295 THROW_WALLET_EXCEPTION_IF(!unpack_extra_multisig_info(info, signers, pkeys),
5296 error::wallet_internal_error, "Bad extra multisig info");
5297
5298 return exchange_multisig_keys(password, pkeys, signers);
5299}
5300
5302 std::unordered_set<crypto::public_key> derivations,
5303 std::vector<crypto::public_key> signers)
5304{
5305 CHECK_AND_ASSERT_THROW_MES(!derivations.empty(), "empty pkeys");
5306 CHECK_AND_ASSERT_THROW_MES(!signers.empty(), "empty signers");
5307
5308 bool ready = false;
5309 CHECK_AND_ASSERT_THROW_MES(multisig(&ready), "The wallet is not multisig");
5310 CHECK_AND_ASSERT_THROW_MES(!ready, "Multisig wallet creation process has already been finished");
5311
5312 // keys are decrypted
5314 if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
5315 {
5316 crypto::chacha_key chacha_key;
5317 crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds);
5318 m_account.encrypt_viewkey(chacha_key);
5319 m_account.decrypt_keys(chacha_key);
5320 keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this, chacha_key]() { m_account.encrypt_keys(chacha_key); m_account.decrypt_viewkey(chacha_key); });
5321 }
5322
5323 if (m_multisig_rounds_passed == multisig_rounds_required(m_multisig_signers.size(), m_multisig_threshold) - 1)
5324 {
5325 // the last round is passed and we have to calculate spend public key
5326 // add ours if not included
5328
5329 if (std::find(signers.begin(), signers.end(), local_signer) == signers.end())
5330 {
5331 signers.push_back(local_signer);
5332 for (const auto &msk: get_account().get_multisig_keys())
5333 {
5334 derivations.insert(rct::rct2pk(rct::scalarmultBase(rct::sk2rct(msk))));
5335 }
5336 }
5337
5338 CHECK_AND_ASSERT_THROW_MES(signers.size() == m_multisig_signers.size(), "Bad signers size");
5339
5340 // Summing all of unique public multisig keys to calculate common public spend key
5341 crypto::public_key spend_public_key = cryptonote::generate_multisig_M_N_spend_public_key(std::vector<crypto::public_key>(derivations.begin(), derivations.end()));
5342 m_account_public_address.m_spend_public_key = spend_public_key;
5343 m_account.finalize_multisig(spend_public_key);
5344
5345 m_multisig_signers = signers;
5346 std::sort(m_multisig_signers.begin(), m_multisig_signers.end(), [](const crypto::public_key &e0, const crypto::public_key &e1){ return memcmp(&e0, &e1, sizeof(e0)); });
5347
5348 ++m_multisig_rounds_passed;
5349 m_multisig_derivations.clear();
5350
5351 // keys are encrypted again
5352 keys_reencryptor = epee::misc_utils::auto_scope_leave_caller();
5353
5354 if (!m_wallet_file.empty())
5355 {
5356 bool r = store_keys(m_keys_file, password, false);
5358
5359 if (boost::filesystem::exists(m_wallet_file + ".address.txt"))
5360 {
5361 r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
5362 if(!r) MERROR("String with address text not saved");
5363 }
5364 }
5365
5366 m_subaddresses.clear();
5367 m_subaddress_labels.clear();
5368 add_subaddress_account(tr("Primary account"));
5369
5370 if (!m_wallet_file.empty())
5371 store();
5372
5373 return {};
5374 }
5375
5376 // Below are either middle or secret spend key establishment rounds
5377
5378 for (const auto& key: m_multisig_derivations)
5379 derivations.erase(key);
5380
5381 // Deriving multisig keys (set of Mi = b * Bi) according to DH from other participants' multisig keys.
5382 auto new_derivations = cryptonote::generate_multisig_derivations(get_account().get_keys(), std::vector<crypto::public_key>(derivations.begin(), derivations.end()));
5383
5384 std::string extra_multisig_info;
5385 if (m_multisig_rounds_passed == multisig_rounds_required(m_multisig_signers.size(), m_multisig_threshold) - 2) // next round is last
5386 {
5387 // Next round is last therefore we are performing secret spend establishment round as described above.
5388 MINFO("Creating spend key...");
5389
5390 // Calculating our secret multisig keys by hashing our public multisig keys.
5391 auto multisig_keys = cryptonote::calculate_multisig_keys(std::vector<crypto::public_key>(new_derivations.begin(), new_derivations.end()));
5392 // And summing it to get personal secret spend key
5394
5395 m_account.make_multisig(m_account.get_keys().m_view_secret_key, spend_skey, rct::rct2pk(rct::identity()), multisig_keys);
5396
5397 // Packing public multisig keys to exchange with others and calculate common public spend key in the last round
5398 extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, secret_keys_to_public_keys(multisig_keys), spend_skey);
5399 }
5400 else
5401 {
5402 // This is just middle round
5403 MINFO("Preparing keys for next exchange round...");
5404 extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, new_derivations, m_account.get_keys().m_spend_secret_key);
5405 m_multisig_derivations = new_derivations;
5406 }
5407
5408 ++m_multisig_rounds_passed;
5409
5410 create_keys_file(m_wallet_file, false, password, boost::filesystem::exists(m_wallet_file + ".address.txt"));
5411 return extra_multisig_info;
5412}
5413
5414void wallet2::unpack_multisig_info(const std::vector<std::string>& info,
5415 std::vector<crypto::public_key> &public_keys,
5416 std::vector<crypto::secret_key> &secret_keys) const
5417{
5418 // parse all multisig info
5419 public_keys.resize(info.size());
5420 secret_keys.resize(info.size());
5421 for (size_t i = 0; i < info.size(); ++i)
5422 {
5423 THROW_WALLET_EXCEPTION_IF(!verify_multisig_info(info[i], secret_keys[i], public_keys[i]),
5424 error::wallet_internal_error, "Bad multisig info: " + info[i]);
5425 }
5426
5427 // remove duplicates
5428 for (size_t i = 0; i < secret_keys.size(); ++i)
5429 {
5430 for (size_t j = i + 1; j < secret_keys.size(); ++j)
5431 {
5432 if (rct::sk2rct(secret_keys[i]) == rct::sk2rct(secret_keys[j]))
5433 {
5434 MDEBUG("Duplicate key found, ignoring");
5435 secret_keys[j] = secret_keys.back();
5436 public_keys[j] = public_keys.back();
5437 secret_keys.pop_back();
5438 public_keys.pop_back();
5439 --j;
5440 }
5441 }
5442 }
5443
5444 // people may include their own, weed it out
5445 const crypto::secret_key local_skey = cryptonote::get_multisig_blinded_secret_key(get_account().get_keys().m_view_secret_key);
5446 const crypto::public_key local_pkey = get_multisig_signer_public_key(get_account().get_keys().m_spend_secret_key);
5447 for (size_t i = 0; i < secret_keys.size(); ++i)
5448 {
5449 if (secret_keys[i] == local_skey)
5450 {
5451 MDEBUG("Local key is present, ignoring");
5452 secret_keys[i] = secret_keys.back();
5453 public_keys[i] = public_keys.back();
5454 secret_keys.pop_back();
5455 public_keys.pop_back();
5456 --i;
5457 }
5458 else
5459 {
5460 THROW_WALLET_EXCEPTION_IF(public_keys[i] == local_pkey, error::wallet_internal_error,
5461 "Found local spend public key, but not local view secret key - something very weird");
5462 }
5463 }
5464}
5465
5467 const std::vector<std::string> &info,
5469{
5470 std::vector<crypto::secret_key> secret_keys(info.size());
5471 std::vector<crypto::public_key> public_keys(info.size());
5472 unpack_multisig_info(info, public_keys, secret_keys);
5473 return make_multisig(password, secret_keys, public_keys, threshold);
5474}
5475
5476bool wallet2::finalize_multisig(const epee::wipeable_string &password, const std::unordered_set<crypto::public_key> &pkeys, std::vector<crypto::public_key> signers)
5477{
5478 bool ready;
5479 uint32_t threshold, total;
5480 if (!multisig(&ready, &threshold, &total))
5481 {
5482 MERROR("This is not a multisig wallet");
5483 return false;
5484 }
5485 if (ready)
5486 {
5487 MERROR("This multisig wallet is already finalized");
5488 return false;
5489 }
5490 if (threshold + 1 != total)
5491 {
5492 MERROR("finalize_multisig should only be used for N-1/N wallets, use exchange_multisig_keys instead");
5493 return false;
5494 }
5495 exchange_multisig_keys(password, pkeys, signers);
5496 return true;
5497}
5498
5499bool wallet2::unpack_extra_multisig_info(const std::vector<std::string>& info,
5500 std::vector<crypto::public_key> &signers,
5501 std::unordered_set<crypto::public_key> &pkeys) const
5502{
5503 // parse all multisig info
5504 signers.resize(info.size(), crypto::null_pkey);
5505 for (size_t i = 0; i < info.size(); ++i)
5506 {
5507 if (!verify_extra_multisig_info(info[i], pkeys, signers[i]))
5508 {
5509 return false;
5510 }
5511 }
5512
5513 return true;
5514}
5515
5516bool wallet2::finalize_multisig(const epee::wipeable_string &password, const std::vector<std::string> &info)
5517{
5518 std::unordered_set<crypto::public_key> public_keys;
5519 std::vector<crypto::public_key> signers;
5520 if (!unpack_extra_multisig_info(info, signers, public_keys))
5521 {
5522 MERROR("Bad multisig info");
5523 return false;
5524 }
5525
5526 return finalize_multisig(password, public_keys, signers);
5527}
5528
5530{
5531 // It's a signed package of private view key and public spend key
5532 const crypto::secret_key skey = cryptonote::get_multisig_blinded_secret_key(get_account().get_keys().m_view_secret_key);
5533 const crypto::public_key pkey = get_multisig_signer_public_key(get_account().get_keys().m_spend_secret_key);
5535
5536 std::string data;
5537 data += std::string((const char *)&skey, sizeof(crypto::secret_key));
5538 data += std::string((const char *)&pkey, sizeof(crypto::public_key));
5539
5540 data.resize(data.size() + sizeof(crypto::signature));
5541 crypto::cn_fast_hash(data.data(), data.size() - sizeof(signature), hash);
5542 crypto::signature &signature = *(crypto::signature*)&data[data.size() - sizeof(crypto::signature)];
5544
5545 return std::string("MultisigV1") + tools::base58::encode(data);
5546}
5547
5549{
5550 const size_t header_len = strlen("MultisigV1");
5551 if (data.size() < header_len || data.substr(0, header_len) != "MultisigV1")
5552 {
5553 MERROR("Multisig info header check error");
5554 return false;
5555 }
5556 std::string decoded;
5557 if (!tools::base58::decode(data.substr(header_len), decoded))
5558 {
5559 MERROR("Multisig info decoding error");
5560 return false;
5561 }
5562 if (decoded.size() != sizeof(crypto::secret_key) + sizeof(crypto::public_key) + sizeof(crypto::signature))
5563 {
5564 MERROR("Multisig info is corrupt");
5565 return false;
5566 }
5567
5568 size_t offset = 0;
5569 skey = *(const crypto::secret_key*)(decoded.data() + offset);
5570 offset += sizeof(skey);
5571 pkey = *(const crypto::public_key*)(decoded.data() + offset);
5572 offset += sizeof(pkey);
5573 const crypto::signature &signature = *(const crypto::signature*)(decoded.data() + offset);
5574
5576 crypto::cn_fast_hash(decoded.data(), decoded.size() - sizeof(signature), hash);
5578 {
5579 MERROR("Multisig info signature is invalid");
5580 return false;
5581 }
5582
5583 return true;
5584}
5585
5586bool wallet2::verify_extra_multisig_info(const std::string &data, std::unordered_set<crypto::public_key> &pkeys, crypto::public_key &signer)
5587{
5588 if (data.size() < MULTISIG_EXTRA_INFO_MAGIC.size() || data.substr(0, MULTISIG_EXTRA_INFO_MAGIC.size()) != MULTISIG_EXTRA_INFO_MAGIC)
5589 {
5590 MERROR("Multisig info header check error");
5591 return false;
5592 }
5593 std::string decoded;
5594 if (!tools::base58::decode(data.substr(MULTISIG_EXTRA_INFO_MAGIC.size()), decoded))
5595 {
5596 MERROR("Multisig info decoding error");
5597 return false;
5598 }
5599 if (decoded.size() < sizeof(crypto::public_key) + sizeof(crypto::signature))
5600 {
5601 MERROR("Multisig info is corrupt");
5602 return false;
5603 }
5604 if ((decoded.size() - (sizeof(crypto::public_key) + sizeof(crypto::signature))) % sizeof(crypto::public_key))
5605 {
5606 MERROR("Multisig info is corrupt");
5607 return false;
5608 }
5609
5610 const size_t n_keys = (decoded.size() - (sizeof(crypto::public_key) + sizeof(crypto::signature))) / sizeof(crypto::public_key);
5611 size_t offset = 0;
5612 signer = *(const crypto::public_key*)(decoded.data() + offset);
5613 offset += sizeof(signer);
5614 const crypto::signature &signature = *(const crypto::signature*)(decoded.data() + offset + n_keys * sizeof(crypto::public_key));
5615
5617 crypto::cn_fast_hash(decoded.data(), decoded.size() - sizeof(signature), hash);
5618 if (!crypto::check_signature(hash, signer, signature))
5619 {
5620 MERROR("Multisig info signature is invalid");
5621 return false;
5622 }
5623
5624 for (size_t n = 0; n < n_keys; ++n)
5625 {
5626 crypto::public_key mspk = *(const crypto::public_key*)(decoded.data() + offset);
5627 pkeys.insert(mspk);
5628 offset += sizeof(mspk);
5629 }
5630
5631 return true;
5632}
5633
5634bool wallet2::multisig(bool *ready, uint32_t *threshold, uint32_t *total) const
5635{
5636 if (!m_multisig)
5637 return false;
5638 if (threshold)
5639 *threshold = m_multisig_threshold;
5640 if (total)
5641 *total = m_multisig_signers.size();
5642 if (ready)
5643 *ready = !(get_account().get_keys().m_account_address.m_spend_public_key == rct::rct2pk(rct::identity()));
5644 return true;
5645}
5646
5648{
5649 if (!m_multisig)
5650 return false;
5651 for (const auto &td: m_transfers)
5652 if (td.m_key_image_partial && td.m_tx.version == 1)
5653 return true;
5654 return false;
5655}
5656
5658{
5659 for (const auto &td: m_transfers)
5660 if (!td.m_key_image_known && td.m_tx.version == 1)
5661 return true;
5662 return false;
5663}
5664
5670void wallet2::rewrite(const std::string& wallet_name, const epee::wipeable_string& password)
5671{
5672 if (wallet_name.empty())
5673 return;
5674 prepare_file_names(wallet_name);
5675 boost::system::error_code ignored_ec;
5676 THROW_WALLET_EXCEPTION_IF(!boost::filesystem::exists(m_keys_file, ignored_ec), error::file_not_found, m_keys_file);
5677 bool r = store_keys(m_keys_file, password, m_watch_only);
5679}
5680
5686void wallet2::write_watch_only_wallet(const std::string& wallet_name, const epee::wipeable_string& password, std::string &new_keys_filename)
5687{
5688 prepare_file_names(wallet_name);
5689 boost::system::error_code ignored_ec;
5690 new_keys_filename = m_wallet_file + "-watchonly.keys";
5691 bool watch_only_keys_file_exists = boost::filesystem::exists(new_keys_filename, ignored_ec);
5692 THROW_WALLET_EXCEPTION_IF(watch_only_keys_file_exists, error::file_save_error, new_keys_filename);
5693 bool r = store_keys(new_keys_filename, password, true);
5694 THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, new_keys_filename);
5695}
5696//----------------------------------------------------------------------------------------------------
5697void wallet2::wallet_exists(const std::string& file_path, bool& keys_file_exists, bool& wallet_file_exists)
5698{
5699 std::string keys_file, wallet_file, mms_file;
5700 do_prepare_file_names(file_path, keys_file, wallet_file, mms_file);
5701
5702 boost::system::error_code ignore;
5703 keys_file_exists = boost::filesystem::exists(keys_file, ignore);
5704 wallet_file_exists = boost::filesystem::exists(wallet_file, ignore);
5705}
5706//----------------------------------------------------------------------------------------------------
5707bool wallet2::wallet_valid_path_format(const std::string& file_path)
5708{
5709 return !file_path.empty();
5710}
5711//----------------------------------------------------------------------------------------------------
5712bool wallet2::parse_long_payment_id(const std::string& payment_id_str, crypto::hash& payment_id)
5713{
5714 cryptonote::blobdata payment_id_data;
5715 if(!epee::string_tools::parse_hexstr_to_binbuff(payment_id_str, payment_id_data))
5716 return false;
5717
5718 if(sizeof(crypto::hash) != payment_id_data.size())
5719 return false;
5720
5721 payment_id = *reinterpret_cast<const crypto::hash*>(payment_id_data.data());
5722 return true;
5723}
5724//----------------------------------------------------------------------------------------------------
5725bool wallet2::parse_short_payment_id(const std::string& payment_id_str, crypto::hash8& payment_id)
5726{
5727 cryptonote::blobdata payment_id_data;
5728 if(!epee::string_tools::parse_hexstr_to_binbuff(payment_id_str, payment_id_data))
5729 return false;
5730
5731 if(sizeof(crypto::hash8) != payment_id_data.size())
5732 return false;
5733
5734 payment_id = *reinterpret_cast<const crypto::hash8*>(payment_id_data.data());
5735 return true;
5736}
5737//----------------------------------------------------------------------------------------------------
5738bool wallet2::parse_payment_id(const std::string& payment_id_str, crypto::hash& payment_id)
5739{
5740 if (parse_long_payment_id(payment_id_str, payment_id))
5741 return true;
5742 crypto::hash8 payment_id8;
5743 if (parse_short_payment_id(payment_id_str, payment_id8))
5744 {
5745 memcpy(payment_id.data, payment_id8.data, 8);
5746 memset(payment_id.data + 8, 0, 24);
5747 return true;
5748 }
5749 return false;
5750}
5751//----------------------------------------------------------------------------------------------------
5752bool wallet2::prepare_file_names(const std::string& file_path)
5753{
5754 do_prepare_file_names(file_path, m_keys_file, m_wallet_file, m_mms_file);
5755 return true;
5756}
5757//----------------------------------------------------------------------------------------------------
5759{
5761
5762 if (m_offline)
5763 {
5764 if (version)
5765 *version = 0;
5766 if (ssl)
5767 *ssl = false;
5768 return false;
5769 }
5770
5771 // TODO: Add light wallet version check.
5772 if(m_light_wallet) {
5773 if (version)
5774 *version = 0;
5775 if (ssl)
5776 *ssl = m_light_wallet_connected; // light wallet is always SSL
5777 return m_light_wallet_connected;
5778 }
5779
5780 {
5781 boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex);
5782 if(!m_http_client.is_connected(ssl))
5783 {
5784 m_node_rpc_proxy.invalidate();
5785 if (!m_http_client.connect(std::chrono::milliseconds(timeout)))
5786 return false;
5787 if(!m_http_client.is_connected(ssl))
5788 return false;
5789 }
5790 }
5791
5792 if (version)
5793 {
5796 bool r = invoke_http_json_rpc("/json_rpc", "get_version", req_t, resp_t);
5797 if(!r) {
5798 *version = 0;
5799 return false;
5800 }
5801 if (resp_t.status != CORE_RPC_STATUS_OK)
5802 *version = 0;
5803 else
5804 *version = resp_t.version;
5805 }
5806
5807 return true;
5808}
5809//----------------------------------------------------------------------------------------------------
5810void wallet2::set_offline(bool offline)
5811{
5812 m_offline = offline;
5813 m_http_client.set_auto_connect(!offline);
5814 if (offline)
5815 {
5816 boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex);
5817 if(m_http_client.is_connected())
5818 m_http_client.disconnect();
5819 }
5820}
5821//----------------------------------------------------------------------------------------------------
5822bool wallet2::generate_chacha_key_from_secret_keys(crypto::chacha_key &key) const
5823{
5824 hw::device &hwdev = m_account.get_device();
5825 return hwdev.generate_chacha_key(m_account.get_keys(), key, m_kdf_rounds);
5826}
5827//----------------------------------------------------------------------------------------------------
5828void wallet2::generate_chacha_key_from_password(const epee::wipeable_string &pass, crypto::chacha_key &key) const
5829{
5830 crypto::generate_chacha_key(pass.data(), pass.size(), key, m_kdf_rounds);
5831}
5832//----------------------------------------------------------------------------------------------------
5833void wallet2::load(const std::string& wallet_, const epee::wipeable_string& password)
5834{
5835 clear();
5836 prepare_file_names(wallet_);
5837
5838 boost::system::error_code e;
5839 bool exists = boost::filesystem::exists(m_keys_file, e);
5840 THROW_WALLET_EXCEPTION_IF(e || !exists, error::file_not_found, m_keys_file);
5842 THROW_WALLET_EXCEPTION_IF(!is_keys_file_locked(), error::wallet_internal_error, "internal error: \"" + m_keys_file + "\" is opened by another wallet program");
5843
5844 // this temporary unlocking is necessary for Windows (otherwise the file couldn't be loaded).
5846 if (!load_keys(m_keys_file, password))
5847 {
5849 }
5850 LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str(m_nettype));
5852
5853 wallet_keys_unlocker unlocker(*this, m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only, password);
5854
5855 //keys loaded ok!
5856 //try to load wallet file. but even if we failed, it is not big problem
5857 if(!boost::filesystem::exists(m_wallet_file, e) || e)
5858 {
5859 LOG_PRINT_L0("file not found: " << m_wallet_file << ", starting with empty blockchain");
5860 m_account_public_address = m_account.get_keys().m_account_address;
5861 }
5862 else
5863 {
5865 std::string buf;
5866 bool r = epee::file_io_utils::load_file_to_string(m_wallet_file, buf, std::numeric_limits<size_t>::max());
5868
5869 // try to read it as an encrypted cache
5870 try
5871 {
5872 LOG_PRINT_L1("Trying to decrypt cache data");
5873
5875 THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + m_wallet_file + '\"');
5876 std::string cache_data;
5877 cache_data.resize(cache_file_data.cache_data.size());
5878 crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), m_cache_key, cache_file_data.iv, &cache_data[0]);
5879
5880 try {
5881 std::stringstream iss;
5882 iss << cache_data;
5884 ar >> *this;
5885 }
5886 catch(...)
5887 {
5888 // try with previous scheme: direct from keys
5889 crypto::chacha_key key;
5890 generate_chacha_key_from_secret_keys(key);
5891 crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cache_data[0]);
5892 try {
5893 std::stringstream iss;
5894 iss << cache_data;
5896 ar >> *this;
5897 }
5898 catch (...)
5899 {
5900 crypto::chacha8(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cache_data[0]);
5901 try
5902 {
5903 std::stringstream iss;
5904 iss << cache_data;
5906 ar >> *this;
5907 }
5908 catch (...)
5909 {
5910 LOG_PRINT_L0("Failed to open portable binary, trying unportable");
5911 boost::filesystem::copy_file(m_wallet_file, m_wallet_file + ".unportable", boost::filesystem::copy_options::overwrite_existing);
5912 std::stringstream iss;
5913 iss.str("");
5914 iss << cache_data;
5915 boost::archive::binary_iarchive ar(iss);
5916 ar >> *this;
5917 }
5918 }
5919 }
5920 }
5921 catch (...)
5922 {
5923 LOG_PRINT_L1("Failed to load encrypted cache, trying unencrypted");
5924 try {
5925 std::stringstream iss;
5926 iss << buf;
5928 ar >> *this;
5929 }
5930 catch (...)
5931 {
5932 LOG_PRINT_L0("Failed to open portable binary, trying unportable");
5933 boost::filesystem::copy_file(m_wallet_file, m_wallet_file + ".unportable", boost::filesystem::copy_options::overwrite_existing);
5934 std::stringstream iss;
5935 iss.str("");
5936 iss << buf;
5937 boost::archive::binary_iarchive ar(iss);
5938 ar >> *this;
5939 }
5940 }
5942 m_account_public_address.m_spend_public_key != m_account.get_keys().m_account_address.m_spend_public_key ||
5943 m_account_public_address.m_view_public_key != m_account.get_keys().m_account_address.m_view_public_key,
5944 error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file);
5945 }
5946
5947 cryptonote::block genesis;
5948 generate_genesis(genesis);
5949 crypto::hash genesis_hash = get_block_hash(genesis);
5950
5951 if (m_blockchain.empty())
5952 {
5953 m_blockchain.push_back(genesis_hash);
5954 m_last_block_reward = cryptonote::get_outs_etn_amount(genesis.miner_tx);
5955 }
5956 else
5957 {
5958 check_genesis(genesis_hash);
5959 }
5960
5961 trim_hashchain();
5962
5963 if (get_num_subaddress_accounts() == 0)
5964 add_subaddress_account(tr("Primary account"));
5965
5966 try
5967 {
5968 find_and_save_rings(false);
5969 }
5970 catch (const std::exception &e)
5971 {
5972 MERROR("Failed to save rings, will try again next time");
5973 }
5974
5975 try
5976 {
5977 m_message_store.read_from_file(get_multisig_wallet_state(), m_mms_file);
5978 }
5979 catch (const std::exception &e)
5980 {
5981 MERROR("Failed to initialize MMS, it will be unusable");
5982 }
5983}
5984//----------------------------------------------------------------------------------------------------
5985void wallet2::trim_hashchain()
5986{
5987 uint64_t height = m_checkpoints.get_max_height();
5988
5989 for (const transfer_details &td: m_transfers)
5990 if (td.m_block_height < height)
5991 height = td.m_block_height;
5992
5993 if (!m_blockchain.empty() && m_blockchain.size() == m_blockchain.offset())
5994 {
5995 MINFO("Fixing empty hashchain");
5998 m_daemon_rpc_mutex.lock();
5999 req.height = m_blockchain.size() - 1;
6000 bool r = invoke_http_json_rpc("/json_rpc", "getblockheaderbyheight", req, res, rpc_timeout);
6001 m_daemon_rpc_mutex.unlock();
6002 if (r && res.status == CORE_RPC_STATUS_OK)
6003 {
6005 epee::string_tools::hex_to_pod(res.block_header.hash, hash);
6006 m_blockchain.refill(hash);
6007 }
6008 else
6009 {
6010 MERROR("Failed to request block header from daemon, hash chain may be unable to sync till the wallet is loaded with a usable daemon");
6011 }
6012 }
6013 if (height > 0 && m_blockchain.size() > height)
6014 {
6015 --height;
6016 MDEBUG("trimming to " << height << ", offset " << m_blockchain.offset());
6017 m_blockchain.trim(height);
6018 }
6019}
6020//----------------------------------------------------------------------------------------------------
6021void wallet2::check_genesis(const crypto::hash& genesis_hash) const {
6022 std::string what("Genesis block mismatch. You probably use wallet without testnet (or stagenet) flag with blockchain from test (or stage) network or vice versa");
6023
6024 THROW_WALLET_EXCEPTION_IF(genesis_hash != m_blockchain.genesis(), error::wallet_internal_error, what);
6025}
6026//----------------------------------------------------------------------------------------------------
6027std::string wallet2::path() const
6028{
6029 return m_wallet_file;
6030}
6031//----------------------------------------------------------------------------------------------------
6033{
6034 if (!m_wallet_file.empty())
6036}
6037//----------------------------------------------------------------------------------------------------
6038void wallet2::store_to(const std::string &path, const epee::wipeable_string &password)
6039{
6040 trim_hashchain();
6041
6042 // if file is the same, we do:
6043 // 1. save wallet to the *.new file
6044 // 2. remove old wallet file
6045 // 3. rename *.new to wallet_name
6046
6047 // handle if we want just store wallet state to current files (ex store() replacement);
6048 bool same_file = true;
6049 if (!path.empty())
6050 {
6051 std::string canonical_path = boost::filesystem::canonical(m_wallet_file).string();
6052 size_t pos = canonical_path.find(path);
6053 same_file = pos != std::string::npos;
6054 }
6055
6056
6057 if (!same_file)
6058 {
6059 // check if we want to store to directory which doesn't exists yet
6060 boost::filesystem::path parent_path = boost::filesystem::path(path).parent_path();
6061
6062 // if path is not exists, try to create it
6063 if (!parent_path.empty() && !boost::filesystem::exists(parent_path))
6064 {
6065 boost::system::error_code ec;
6066 if (!boost::filesystem::create_directories(parent_path, ec))
6067 {
6068 throw std::logic_error(ec.message());
6069 }
6070 }
6071 }
6072 // preparing wallet data
6073 std::stringstream oss;
6075 ar << *this;
6076
6077 wallet2::cache_file_data cache_file_data = boost::value_initialized<wallet2::cache_file_data>();
6078 cache_file_data.cache_data = oss.str();
6079 std::string cipher;
6080 cipher.resize(cache_file_data.cache_data.size());
6082 crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), m_cache_key, cache_file_data.iv, &cipher[0]);
6083 cache_file_data.cache_data = cipher;
6084
6085 const std::string new_file = same_file ? m_wallet_file + ".new" : path;
6086 const std::string old_file = m_wallet_file;
6087 const std::string old_keys_file = m_keys_file;
6088 const std::string old_address_file = m_wallet_file + ".address.txt";
6089 const std::string old_mms_file = m_mms_file;
6090
6091 // save keys to the new file
6092 // if we here, main wallet file is saved and we only need to save keys and address files
6093 if (!same_file) {
6094 prepare_file_names(path);
6095 bool r = store_keys(m_keys_file, password, false);
6097 if (boost::filesystem::exists(old_address_file))
6098 {
6099 // save address to the new file
6100 const std::string address_file = m_wallet_file + ".address.txt";
6101 r = file_io_utils::save_string_to_file(address_file, m_account.get_public_address_str(m_nettype));
6103 }
6104 // remove old wallet file
6105 r = boost::filesystem::remove(old_file);
6106 if (!r) {
6107 LOG_ERROR("error removing file: " << old_file);
6108 }
6109 // remove old keys file
6110 r = boost::filesystem::remove(old_keys_file);
6111 if (!r) {
6112 LOG_ERROR("error removing file: " << old_keys_file);
6113 }
6114 // remove old address file
6115 r = boost::filesystem::remove(old_address_file);
6116 if (!r) {
6117 LOG_ERROR("error removing file: " << old_address_file);
6118 }
6119 // remove old message store file
6120 if (boost::filesystem::exists(old_mms_file))
6121 {
6122 r = boost::filesystem::remove(old_mms_file);
6123 if (!r) {
6124 LOG_ERROR("error removing file: " << old_mms_file);
6125 }
6126 }
6127 } else {
6128 // save to new file
6129#ifdef WIN32
6130 // On Windows avoid using std::ofstream which does not work with UTF-8 filenames
6131 // The price to pay is temporary higher memory consumption for string stream + binary archive
6132 std::ostringstream oss;
6133 binary_archive<true> oar(oss);
6134 bool success = ::serialization::serialize(oar, cache_file_data);
6135 if (success) {
6136 success = epee::file_io_utils::save_string_to_file(new_file, oss.str());
6137 }
6139#else
6140 std::ofstream ostr;
6141 ostr.open(new_file, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);
6142 binary_archive<true> oar(ostr);
6143 bool success = ::serialization::serialize(oar, cache_file_data);
6144 ostr.close();
6145 THROW_WALLET_EXCEPTION_IF(!success || !ostr.good(), error::file_save_error, new_file);
6146#endif
6147
6148 // here we have "*.new" file, we need to rename it to be without ".new"
6149 std::error_code e = tools::replace_file(new_file, m_wallet_file);
6151 }
6152
6153 if (m_message_store.get_active())
6154 {
6155 // While the "m_message_store" object of course always exist, a file for the message
6156 // store should only exist if the MMS is really active
6157 m_message_store.write_to_file(get_multisig_wallet_state(), m_mms_file);
6158 }
6159
6160}
6161//----------------------------------------------------------------------------------------------------
6162uint64_t wallet2::balance(uint32_t index_major, bool public_blockchain) const
6163{
6164 uint64_t amount = 0;
6165 if(m_light_wallet)
6166 return m_light_wallet_unlocked_balance;
6167 for (const auto& i : balance_per_subaddress(index_major, public_blockchain))
6168 amount += i.second;
6169 return amount;
6170}
6171//----------------------------------------------------------------------------------------------------
6172uint64_t wallet2::unlocked_balance(uint32_t index_major, bool public_blockchain, uint64_t *blocks_to_unlock) const
6173{
6174 uint64_t amount = 0;
6175 if (blocks_to_unlock)
6176 *blocks_to_unlock = 0;
6177 if(m_light_wallet)
6178 return m_light_wallet_balance;
6179 for (const auto& i : unlocked_balance_per_subaddress(index_major, public_blockchain))
6180 {
6181 amount += i.second.first;
6182 if (blocks_to_unlock && i.second.second > *blocks_to_unlock)
6183 *blocks_to_unlock = i.second.second;
6184 }
6185 return amount;
6186}
6187//----------------------------------------------------------------------------------------------------
6188std::map<uint32_t, uint64_t> wallet2::balance_per_subaddress(uint32_t index_major, bool public_blockchain) const
6189{
6190 std::map<uint32_t, uint64_t> amount_per_subaddr;
6191 for (const auto& td: m_transfers)
6192 {
6193 if((public_blockchain && td.m_tx.version == 1) || (!public_blockchain && td.m_tx.version > 1))
6194 continue;
6195
6196 if (td.m_subaddr_index.major == index_major && !td.m_spent && !td.m_frozen)
6197 {
6198 auto found = amount_per_subaddr.find(td.m_subaddr_index.minor);
6199 if (found == amount_per_subaddr.end())
6200 amount_per_subaddr[td.m_subaddr_index.minor] = td.amount();
6201 else
6202 found->second += td.amount();
6203 }
6204 }
6205 for (const auto& utx: m_unconfirmed_txs) {
6206 if ((public_blockchain && utx.second.m_tx.version == 1) || (!public_blockchain && utx.second.m_tx.version > 1))
6207 continue;
6208
6209 if (utx.second.m_state != wallet2::unconfirmed_transfer_details::failed) {
6210 //HANDLE LOOPBACK OUTS INCLUDING CHANGE
6211 if(utx.second.m_tx.version > 1){
6212 for (const cryptonote::tx_out &out : utx.second.m_tx.vout) {
6213 if (out.target.type() == typeid(txout_to_key_public)) {
6214 // check whether this out is to one of our subaddresses
6215 auto target = boost::get<cryptonote::txout_to_key_public>(out.target);
6216 auto subaddr_found = m_subaddresses.find(target.address.m_spend_public_key);
6217 // if this out is to us
6218 // and the view key part of the destination matches that of our our subaddress,
6219 // and the major index of this subaddress corresponds to the current account we're getting balance for,
6220 // then add amount to balance
6221 if (subaddr_found != m_subaddresses.end() && get_subaddress(subaddr_found->second).m_view_public_key == target.address.m_view_public_key && subaddr_found->second.major == index_major) {
6222 auto found = amount_per_subaddr.find(subaddr_found->second.minor);
6223 if (found == amount_per_subaddr.end())
6224 amount_per_subaddr[subaddr_found->second.minor] = out.amount;
6225 else
6226 found->second += out.amount;
6227 } else {
6228 continue;
6229 }
6230 }
6231 }
6232 }
6233 // CHANGE HANDLING FOR V1 TX
6234 // (NB LOOPBACK OUTS APART FROM CHANGE AREN'T FACTORED INTO BAL WHILST TX IS UNCONFIRMED
6235 // and there is no need to fix this (monero) issue as we've migrated to a transparent chain)
6236 if (utx.second.m_tx.version == 1 && utx.second.m_subaddr_account == index_major) {
6237 // all changes go to 0-th subaddress (in the current subaddress account)
6238 auto found = amount_per_subaddr.find(0);
6239 if (found == amount_per_subaddr.end())
6240 amount_per_subaddr[0] = utx.second.m_change;
6241 else
6242 found->second += utx.second.m_change;
6243 }
6244 }
6245 }
6246 return amount_per_subaddr;
6247}
6248//----------------------------------------------------------------------------------------------------
6249std::map<uint32_t, std::pair<uint64_t, uint64_t>> wallet2::unlocked_balance_per_subaddress(uint32_t index_major, bool public_blockchain) const
6250{
6251 std::map<uint32_t, std::pair<uint64_t, uint64_t>> amount_per_subaddr; //map of subaddr minor index : <amount,unlock t>
6252 const uint64_t blockchain_height = get_blockchain_current_height();
6253 //Figure out amount & blocks_to_unlock for the major subaddress index for each transfer
6254 for(const transfer_details& td: m_transfers)
6255 {
6256 if((public_blockchain && td.m_tx.version == 1) || (!public_blockchain && td.m_tx.version > 1))
6257 continue;
6258
6259 if(td.m_subaddr_index.major == index_major && !td.m_spent && !td.m_frozen)
6260 {
6261 uint64_t amount = 0, blocks_to_unlock = 0;
6262 if (is_transfer_unlocked(td))
6263 {
6264 amount = td.amount();
6265 blocks_to_unlock = 0;
6266 }
6267 else
6268 {
6269
6270 uint64_t v8height = m_nettype == TESTNET ? 446674 : 589169;
6271 uint16_t UNLOCK_WINDOW = td.m_block_height > v8height ? ETN_DEFAULT_TX_SPENDABLE_AGE_V8 : CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE;
6272 uint64_t unlock_height = td.m_block_height + std::max<uint64_t>(UNLOCK_WINDOW, CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS);
6273 if (td.m_tx.unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER && td.m_tx.unlock_time > unlock_height)
6274 unlock_height = td.m_tx.unlock_time;
6275 blocks_to_unlock = unlock_height > blockchain_height ? unlock_height - blockchain_height : 0;
6276 amount = 0;
6277 }
6278 auto found = amount_per_subaddr.find(td.m_subaddr_index.minor);
6279 // if we don't have this subaddress index (key) in our map, create a new entry, otherwise just add a new pair
6280 if (found == amount_per_subaddr.end())
6281 amount_per_subaddr[td.m_subaddr_index.minor] = std::make_pair(amount, blocks_to_unlock);
6282 else
6283 {
6284 found->second.first += amount;
6285 found->second.second = std::max(found->second.second, blocks_to_unlock);
6286 }
6287 }
6288 }
6289 return amount_per_subaddr;
6290}
6291//----------------------------------------------------------------------------------------------------
6292uint64_t wallet2::balance_all(bool public_blockchain) const
6293{
6294 uint64_t r = 0;
6295 for (uint32_t index_major = 0; index_major < get_num_subaddress_accounts(); ++index_major)
6296 r += balance(index_major, public_blockchain);
6297 return r;
6298}
6299//----------------------------------------------------------------------------------------------------
6300uint64_t wallet2::unlocked_balance_all(bool public_blockchain, uint64_t *blocks_to_unlock) const
6301{
6302 uint64_t r = 0;
6303 if (blocks_to_unlock)
6304 *blocks_to_unlock = 0;
6305 for (uint32_t index_major = 0; index_major < get_num_subaddress_accounts(); ++index_major)
6306 {
6307 uint64_t local_blocks_to_unlock;
6308 r += unlocked_balance(index_major, public_blockchain ,blocks_to_unlock ? &local_blocks_to_unlock : NULL);
6309 if (blocks_to_unlock)
6310 *blocks_to_unlock = std::max(*blocks_to_unlock, local_blocks_to_unlock);
6311 }
6312 return r;
6313}
6314//----------------------------------------------------------------------------------------------------
6316{
6317 incoming_transfers = m_transfers;
6318}
6319//----------------------------------------------------------------------------------------------------
6320void wallet2::get_payments(const crypto::hash& payment_id, std::list<wallet2::payment_details>& payments, uint64_t min_height, const boost::optional<uint32_t>& subaddr_account, const std::set<uint32_t>& subaddr_indices) const
6321{
6322 auto range = m_payments.equal_range(payment_id);
6323 std::for_each(range.first, range.second, [&payments, &min_height, &subaddr_account, &subaddr_indices](const payment_container::value_type& x) {
6324 if (min_height < x.second.m_block_height &&
6325 (!subaddr_account || *subaddr_account == x.second.m_subaddr_index.major) &&
6326 (subaddr_indices.empty() || subaddr_indices.count(x.second.m_subaddr_index.minor) == 1))
6327 {
6328 payments.push_back(x.second);
6329 }
6330 });
6331}
6332//----------------------------------------------------------------------------------------------------
6333void wallet2::get_payments(std::list<std::pair<crypto::hash,wallet2::payment_details>>& payments, uint64_t min_height, uint64_t max_height, const boost::optional<uint32_t>& subaddr_account, const std::set<uint32_t>& subaddr_indices) const
6334{
6335 auto range = std::make_pair(m_payments.begin(), m_payments.end());
6336 std::for_each(range.first, range.second, [&payments, &min_height, &max_height, &subaddr_account, &subaddr_indices](const payment_container::value_type& x) {
6337 if (min_height < x.second.m_block_height && max_height >= x.second.m_block_height &&
6338 (!subaddr_account || *subaddr_account == x.second.m_subaddr_index.major) &&
6339 (subaddr_indices.empty() || subaddr_indices.count(x.second.m_subaddr_index.minor) == 1))
6340 {
6341 payments.push_back(x);
6342 }
6343 });
6344}
6345//----------------------------------------------------------------------------------------------------
6346void wallet2::get_payments_out(std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>>& confirmed_payments,
6347 uint64_t min_height, uint64_t max_height, const boost::optional<uint32_t>& subaddr_account, const std::set<uint32_t>& subaddr_indices) const
6348{
6349 for (auto i = m_confirmed_txs.begin(); i != m_confirmed_txs.end(); ++i) {
6350 if (i->second.m_block_height <= min_height || i->second.m_block_height > max_height)
6351 continue;
6352 if (subaddr_account && *subaddr_account != i->second.m_subaddr_account)
6353 continue;
6354 if (!subaddr_indices.empty() && std::count_if(i->second.m_subaddr_indices.begin(), i->second.m_subaddr_indices.end(), [&subaddr_indices](uint32_t index) { return subaddr_indices.count(index) == 1; }) == 0)
6355 continue;
6356 if (i->second.m_is_migration) //avoid as processed by separate function
6357 continue;
6358 if(i->second.m_is_sc_migration) //avoid as processed by separate function
6359 continue;
6360 confirmed_payments.push_back(*i);
6361 }
6362}//----------------------------------------------------------------------------------------------------
6363void wallet2::get_payments_out_migration(std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>>& confirmed_payments,
6364 uint64_t min_height, uint64_t max_height, const boost::optional<uint32_t>& subaddr_account, const std::set<uint32_t>& subaddr_indices) const
6365{
6366 for (auto i = m_confirmed_txs.begin(); i != m_confirmed_txs.end(); ++i) {
6367 if (i->second.m_block_height <= min_height || i->second.m_block_height > max_height)
6368 continue;
6369 if (subaddr_account && *subaddr_account != i->second.m_subaddr_account)
6370 continue;
6371 if (!subaddr_indices.empty() && std::count_if(i->second.m_subaddr_indices.begin(), i->second.m_subaddr_indices.end(), [&subaddr_indices](uint32_t index) { return subaddr_indices.count(index) == 1; }) == 0)
6372 continue;
6373 if (!i->second.m_is_migration)
6374 continue;
6375
6376 confirmed_payments.push_back(*i);
6377 }
6378}
6379//----------------------------------------------------------------------------------------------------
6380void wallet2::get_payments_out_sc_migration(std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>>& confirmed_payments,
6381 uint64_t min_height, uint64_t max_height, const boost::optional<uint32_t>& subaddr_account, const std::set<uint32_t>& subaddr_indices) const{
6382
6383 for (auto i = m_confirmed_txs.begin(); i != m_confirmed_txs.end(); ++i) {
6384 if (i->second.m_block_height <= min_height || i->second.m_block_height > max_height)
6385 continue;
6386 if (subaddr_account && *subaddr_account != i->second.m_subaddr_account)
6387 continue;
6388 if (!subaddr_indices.empty() && std::count_if(i->second.m_subaddr_indices.begin(), i->second.m_subaddr_indices.end(), [&subaddr_indices](uint32_t index) { return subaddr_indices.count(index) == 1; }) == 0)
6389 continue;
6390 if (!i->second.m_is_sc_migration)
6391 continue;
6392 confirmed_payments.push_back(*i);
6393 }
6394}
6395//----------------------------------------------------------------------------------------------------
6396void wallet2::get_unconfirmed_payments_out(std::list<std::pair<crypto::hash,wallet2::unconfirmed_transfer_details>>& unconfirmed_payments, const boost::optional<uint32_t>& subaddr_account, const std::set<uint32_t>& subaddr_indices) const
6397{
6398 for (auto i = m_unconfirmed_txs.begin(); i != m_unconfirmed_txs.end(); ++i) {
6399 if (subaddr_account && *subaddr_account != i->second.m_subaddr_account)
6400 continue;
6401 if (!subaddr_indices.empty() && std::count_if(i->second.m_subaddr_indices.begin(), i->second.m_subaddr_indices.end(), [&subaddr_indices](uint32_t index) { return subaddr_indices.count(index) == 1; }) == 0)
6402 continue;
6403 unconfirmed_payments.push_back(*i);
6404 }
6405}
6406//----------------------------------------------------------------------------------------------------
6407
6408void wallet2::get_unconfirmed_payments(std::list<std::pair<crypto::hash,wallet2::pool_payment_details>>& unconfirmed_payments, const boost::optional<uint32_t>& subaddr_account, const std::set<uint32_t>& subaddr_indices) const
6409{
6410 for (auto i = m_unconfirmed_payments.begin(); i != m_unconfirmed_payments.end(); ++i) {
6411 if ((!subaddr_account || *subaddr_account == i->second.m_pd.m_subaddr_index.major) &&
6412 (subaddr_indices.empty() || subaddr_indices.count(i->second.m_pd.m_subaddr_index.minor) == 1))
6413 unconfirmed_payments.push_back(*i);
6414 }
6415}
6416//----------------------------------------------------------------------------------------------------
6418{
6419 // This is RPC call that can take a long time if there are many outputs,
6420 // so we call it several times, in stripes, so we don't time out spuriously
6421
6422 // M_TRANSFERS is a container of OUTPUTS and NOT a container of entire transfers!
6423
6424 // The logic for dealing with publicised (v8 hf) outputs is as follows:
6425 // 1. Check the output block height in m_transfers. If it's >= v8 hard fork height, the output must be a public one,
6426 // 2. m_transfers contains the tx hash and relative out index for each output which uniquely determine
6427 // 'chainstate UTXOs' in the blockchain database; if a chainstate UTXO is present in the DB, the output is truly
6428 // unspent. However nonexistence of the UTXO in the db doesn't mean it's spent, only that it doesn't exist. So we
6429 // must first check (by some means) that the output did exist. Use the tx input db.
6430 // 3. Call the daemon for this output and ask of the spent status.
6431 // 4. Set the correct spent status of the output in m_transfers
6432
6433 std::vector<int> spent_status;
6434 spent_status.reserve(m_transfers.size());
6435 const size_t chunk_size = 1000;
6436 for (size_t start_offset = 0; start_offset < m_transfers.size(); start_offset += chunk_size)
6437 {
6438 const size_t n_outputs = std::min<size_t>(chunk_size, m_transfers.size() - start_offset); // 1000 or less if we dont have 1000
6439 MDEBUG("Calling is_key_image_spent on " << start_offset << " - " << (start_offset + n_outputs - 1) << ", out of " << m_transfers.size());
6442 for (size_t n = start_offset; n < start_offset + n_outputs; ++n) //loop over key images for the outputs in m_transfers and put the key image in the request
6443 req.key_images.push_back(string_tools::pod_to_hex(m_transfers[n].m_key_image));
6444 m_daemon_rpc_mutex.lock();
6445 bool r = invoke_http_json("/is_key_image_spent", req, daemon_resp, rpc_timeout); //fire off the check command
6446 m_daemon_rpc_mutex.unlock();
6448 THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "is_key_image_spent");
6449 THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::is_key_image_spent_error, get_rpc_status(daemon_resp.status));
6450 THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != n_outputs, error::wallet_internal_error,
6451 "daemon returned wrong response for is_key_image_spent, wrong amounts count = " +
6452 std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(n_outputs));
6453 std::copy(daemon_resp.spent_status.begin(), daemon_resp.spent_status.end(), std::back_inserter(spent_status));
6454 }
6455
6456
6457 // urgent code update so just duplicate code above for public outputs
6458 uint64_t v8height = m_nettype == TESTNET ? 446674 : 589169;
6459 for (size_t start_offset = 0; start_offset < m_transfers.size(); start_offset += chunk_size)
6460 {
6461 const size_t n_outputs = std::min<size_t>(chunk_size, m_transfers.size() - start_offset);
6462 MDEBUG("Preparing is_public_output_spent request for outputs " << start_offset << " - " << (start_offset + n_outputs - 1) << ", out of " << m_transfers.size());
6463
6466
6467 // Prepare the request for public outputs only for m_transfers after v8 height
6468 for (size_t k = start_offset; k < start_offset + n_outputs; ++k) {
6469 if (m_transfers[k].m_block_height >= v8height) {
6470 public_output pub_out;
6471 pub_out.txid = epee::string_tools::pod_to_hex(m_transfers[k].m_txid);
6472 pub_out.relative_out_index = static_cast<uint64_t>(m_transfers[k].m_internal_output_index);
6473 req.public_outputs.push_back(pub_out);
6474 }
6475 }
6476
6477 if(req.public_outputs.size() == 0){
6478 MDEBUG("No public outs found in the range: " << start_offset << " - " << (start_offset + n_outputs - 1) << ", out of " << m_transfers.size() << ", skipping chunk");
6479 continue;
6480 }
6481 // We always call the daemon, but the request may be empty if no outputs meet the criteria
6482 m_daemon_rpc_mutex.lock();
6483 bool r = invoke_http_json("/is_public_output_spent", req, daemon_resp, rpc_timeout);
6484 m_daemon_rpc_mutex.unlock();
6485 THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "is_public_output_spent");
6486 THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "is_public_output_spent");
6487 THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::is_public_output_spent_error, get_rpc_status(daemon_resp.status));
6488 THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != req.public_outputs.size(), error::wallet_internal_error,
6489 "daemon returned wrong response for is_public_output_spent, wrong amount count = " +
6490 std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(req.public_outputs.size()));
6491
6492 // Update spent_status only for outputs that were included in the request. do this by iterating through m_transfers and if it's >= v8 height
6493 // then set the corresponding spent status for the same index. use a request index like so because not always does n == request index
6494 // because not all outputs are public outputs
6495 size_t request_index = 0;
6496 for (size_t k = start_offset; k < start_offset + n_outputs; ++k) {
6497 if (m_transfers[k].m_block_height >= v8height) {
6498 spent_status[k] = daemon_resp.spent_status[request_index++];
6499 }
6500 }
6501 }
6502
6503 // update spent status in m_transfers
6504 // spent_status[i] guide:
6505 // UNSPENT = 0,
6506 // SPENT_IN_BLOCKCHAIN = 1,
6507 // SPENT_IN_POOL = 2,
6508 for (size_t i = 0; i < m_transfers.size(); ++i)
6509 {
6510 transfer_details& td = m_transfers[i];
6511 // a view wallet may not know about key images. only skip in this case IF it isn't a public output
6512 if (!(m_transfers[i].m_block_height >= v8height) && (!td.m_key_image_known || td.m_key_image_partial)) //we will hit this for all public outs, so modify here
6513 continue;
6514
6515 if (td.m_spent != (spent_status[i] != SPENT_STATUS::UNSPENT)) // if output in m_transfers is unspent and the daemon says spent or the other way round, handle either which way
6516 {
6517 if (td.m_spent) // given parent if statement, spent means we need to change to unspent
6518 {
6519 if(!(m_transfers[i].m_block_height >= v8height)){
6520 LOG_PRINT_L0("Marking output " << i << "(" << td.m_key_image << ") as unspent, it was marked as spent");
6521 } else{
6522 LOG_PRINT_L0("Marking public output " << i << " (txid: " << td.m_txid << ", index: " << td.m_internal_output_index << ") as unspent, it was marked as spent");
6523 }
6524 set_unspent(i);
6525 td.m_spent_height = 0;
6526 }
6527 else // given parent if statement, unspent means we need to change to spent
6528 {
6529 if (!(m_transfers[i].m_block_height >= v8height)) {
6530 LOG_PRINT_L0("Marking output " << i << " (key image: " << epee::string_tools::pod_to_hex(td.m_key_image) << ") as spent, it was marked as unspent");
6531 } else {
6532 LOG_PRINT_L0("Not marking output " << i << " (txid: " << epee::string_tools::pod_to_hex(td.m_txid) << ", index: " << td.m_internal_output_index << ") as spent since block height " << td.m_block_height << " is below the threshold of " << v8height);
6533 }
6534 set_spent(i, td.m_spent_height);
6535 }
6536 }
6537 }
6538}
6539//----------------------------------------------------------------------------------------------------
6540void wallet2::rescan_blockchain(bool hard, bool refresh, bool keep_key_images)
6541{
6542 CHECK_AND_ASSERT_THROW_MES(!hard || !keep_key_images, "Cannot preserve key images on hard rescan");
6543 const size_t transfers_cnt = m_transfers.size();
6544 crypto::hash transfers_hash{};
6545
6546 if(hard)
6547 {
6548 clear();
6549 setup_new_blockchain();
6550 }
6551 else
6552 {
6553 if (keep_key_images && refresh)
6554 hash_m_transfers((int64_t) transfers_cnt, transfers_hash);
6555 clear_soft(keep_key_images);
6556 }
6557
6558 if (refresh)
6559 this->refresh(false);
6560
6561 if (refresh && keep_key_images)
6562 finish_rescan_bc_keep_key_images(transfers_cnt, transfers_hash);
6563}
6564//----------------------------------------------------------------------------------------------------
6569//----------------------------------------------------------------------------------------------------
6570bool wallet2::is_transfer_unlocked(uint64_t unlock_time, uint64_t block_height) const
6571{
6572 if(!is_tx_spendtime_unlocked(unlock_time, block_height))
6573 return false;
6574
6575 uint64_t v8height = m_nettype == TESTNET ? 446674 : 589169;
6576 uint16_t UNLOCK_WINDOW = block_height > v8height ? ETN_DEFAULT_TX_SPENDABLE_AGE_V8 : CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE;
6577
6578 if(block_height + UNLOCK_WINDOW > get_blockchain_current_height())
6579 return false;
6580
6581 return true;
6582}
6583//----------------------------------------------------------------------------------------------------
6584bool wallet2::is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_height) const
6585{
6586 if(unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER)
6587 {
6588 //interpret as block index
6590 return true;
6591 else
6592 return false;
6593 }else
6594 {
6595 //interpret as time
6596 uint64_t current_time = static_cast<uint64_t>(time(NULL));
6597 // XXX: this needs to be fast, so we'd need to get the starting heights
6598 // from the daemon to be correct once voting kicks in
6599 uint64_t v6height = m_nettype == TESTNET ? 190059 : 307499;
6601 if(current_time + leeway >= unlock_time)
6602 return true;
6603 else
6604 return false;
6605 }
6606 return false;
6607}
6608//----------------------------------------------------------------------------------------------------
6609namespace
6610{
6611 template<typename T>
6612 T pop_index(std::vector<T>& vec, size_t idx)
6613 {
6614 CHECK_AND_ASSERT_MES(!vec.empty(), T(), "Vector must be non-empty");
6615 CHECK_AND_ASSERT_MES(idx < vec.size(), T(), "idx out of bounds");
6616
6617 T res = vec[idx];
6618 if (idx + 1 != vec.size())
6619 {
6620 vec[idx] = vec.back();
6621 }
6622 vec.resize(vec.size() - 1);
6623
6624 return res;
6625 }
6626
6627 template<typename T>
6628 T pop_random_value(std::vector<T>& vec)
6629 {
6630 CHECK_AND_ASSERT_MES(!vec.empty(), T(), "Vector must be non-empty");
6631
6632 size_t idx = crypto::rand_idx(vec.size());
6633 return pop_index (vec, idx);
6634 }
6635
6636 template<typename T>
6637 T pop_back(std::vector<T>& vec)
6638 {
6639 CHECK_AND_ASSERT_MES(!vec.empty(), T(), "Vector must be non-empty");
6640
6641 T res = vec.back();
6642 vec.pop_back();
6643 return res;
6644 }
6645
6646 template<typename T>
6647 void pop_if_present(std::vector<T>& vec, T e)
6648 {
6649 for (size_t i = 0; i < vec.size(); ++i)
6650 {
6651 if (e == vec[i])
6652 {
6653 pop_index (vec, i);
6654 return;
6655 }
6656 }
6657 }
6658}
6659//----------------------------------------------------------------------------------------------------
6660// This returns a handwavy estimation of how much two outputs are related
6661// If they're from the same tx, then they're fully related. From close block
6662// heights, they're kinda related. The actual values don't matter, just
6663// their ordering, but it could become more murky if we add scores later.
6664float wallet2::get_output_relatedness(const transfer_details &td0, const transfer_details &td1) const
6665{
6666 int dh;
6667
6668 // expensive test, and same tx will fall onto the same block height below
6669 if (td0.m_txid == td1.m_txid)
6670 return 1.0f;
6671
6672 // same block height -> possibly tx burst, or same tx (since above is disabled)
6673 dh = td0.m_block_height > td1.m_block_height ? td0.m_block_height - td1.m_block_height : td1.m_block_height - td0.m_block_height;
6674 if (dh == 0)
6675 return 0.9f;
6676
6677 // adjacent blocks -> possibly tx burst
6678 if (dh == 1)
6679 return 0.8f;
6680
6681 // could extract the payment id, and compare them, but this is a bit expensive too
6682
6683 // similar block heights
6684 if (dh < 10)
6685 return 0.2f;
6686
6687 // don't think these are particularly related
6688 return 0.0f;
6689}
6690//----------------------------------------------------------------------------------------------------
6691size_t wallet2::pop_best_value_from(const transfer_container &transfers, std::vector<size_t> &unused_indices, const std::vector<size_t>& selected_transfers, bool smallest) const
6692{
6693 std::vector<size_t> candidates;
6694 float best_relatedness = 1.0f;
6695 for (size_t n = 0; n < unused_indices.size(); ++n)
6696 {
6697 const transfer_details &candidate = transfers[unused_indices[n]];
6698 float relatedness = 0.0f;
6699 for (std::vector<size_t>::const_iterator i = selected_transfers.begin(); i != selected_transfers.end(); ++i)
6700 {
6701 float r = get_output_relatedness(candidate, transfers[*i]);
6702 if (r > relatedness)
6703 {
6704 relatedness = r;
6705 if (relatedness == 1.0f)
6706 break;
6707 }
6708 }
6709
6710 if (relatedness < best_relatedness)
6711 {
6712 best_relatedness = relatedness;
6713 candidates.clear();
6714 }
6715
6716 if (relatedness == best_relatedness)
6717 candidates.push_back(n);
6718 }
6719
6720 // we have all the least related outputs in candidates, so we can pick either
6721 // the smallest, or a random one, depending on request
6722 size_t idx;
6723 if (smallest)
6724 {
6725 idx = 0;
6726 for (size_t n = 0; n < candidates.size(); ++n)
6727 {
6728 const transfer_details &td = transfers[unused_indices[candidates[n]]];
6729 if (td.amount() < transfers[unused_indices[candidates[idx]]].amount())
6730 idx = n;
6731 }
6732 }
6733 else
6734 {
6735 idx = crypto::rand_idx(candidates.size());
6736 }
6737 return pop_index (unused_indices, candidates[idx]);
6738}
6739//----------------------------------------------------------------------------------------------------
6740size_t wallet2::pop_best_value(std::vector<size_t> &unused_indices, const std::vector<size_t>& selected_transfers, bool smallest) const
6741{
6742 return pop_best_value_from(m_transfers, unused_indices, selected_transfers, smallest);
6743}
6744//----------------------------------------------------------------------------------------------------
6745// Select random input sources for transaction.
6746// returns:
6747// direct return: amount of etn found
6748// modified reference: selected_transfers, a list of iterators/indices of input sources
6749uint64_t wallet2::select_transfers(uint64_t needed_etn, std::vector<size_t> unused_transfers_indices, std::vector<size_t>& selected_transfers) const
6750{
6751 uint64_t found_etn = 0;
6752 selected_transfers.reserve(unused_transfers_indices.size());
6753 while (found_etn < needed_etn && !unused_transfers_indices.empty())
6754 {
6755 size_t idx = pop_best_value(unused_transfers_indices, selected_transfers);
6756
6757 const transfer_container::const_iterator it = m_transfers.begin() + idx;
6758 selected_transfers.push_back(idx);
6759 found_etn += it->amount();
6760 }
6761
6762 return found_etn;
6763}
6764//----------------------------------------------------------------------------------------------------
6765void wallet2::add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t amount_in, const std::vector<cryptonote::tx_destination_entry> &dests, const crypto::hash &payment_id, uint64_t change_amount, uint32_t subaddr_account, const std::set<uint32_t>& subaddr_indices)
6766{
6767 unconfirmed_transfer_details& utd = m_unconfirmed_txs[cryptonote::get_transaction_hash(tx)];
6768 utd.m_amount_in = amount_in;
6769 utd.m_amount_out = 0;
6770
6771 if(tx.version == 1){
6772 for (const auto &d: dests)
6773 utd.m_amount_out += d.amount;
6774 utd.m_amount_out += change_amount; // dests does not contain change
6775 utd.m_change = change_amount;
6776 utd.m_dests = dests;
6777 } else {
6778 // grab the input owner keys/address by using the subaddr indicies used for the transaction
6779 std::vector<account_public_address> input_addresses;
6780 for (auto minor_index : subaddr_indices) {
6781 cryptonote::subaddress_index index{subaddr_account, minor_index};
6782 input_addresses.push_back(get_subaddress(index));
6783 }
6784
6785 //build list of potential change outputs - NB if *ALL* outs go to input addresses, then we DON'T conside them change; the transaction is a looped sweep.
6786 // If one or more outs do not go to an input address, we consider ALL other outputs as change outputs
6787 std::unordered_set<uint32_t> change_indexes;
6788 for (size_t i = 0; i < tx.vout.size(); ++i) {
6789 for (auto input_address : input_addresses) {
6790 if (boost::get<txout_to_key_public>(tx.vout[i].target).address == input_address) {
6791 change_indexes.insert(i);
6792 continue;
6793 }
6794 }
6795 }
6796
6797 // if this is true we have a sweep tx so clear all change out indexes
6798 if (change_indexes.size() == tx.vout.size()) {
6799 change_indexes.clear();
6800 }
6801
6802 int64_t total_change = 0;
6803 for (auto &change_index : change_indexes)
6804 total_change += tx.vout[change_index].amount;
6805 utd.m_change = total_change;
6806
6807 //todo: optimise & refactor
6808 // fill destinations
6809 for (size_t i = 0; i < tx.vout.size(); ++i) {
6810 if (change_indexes.find(i) == change_indexes.end()) { // only include non-change outs as dests
6811 auto output = boost::get<txout_to_key_public>(tx.vout[i].target); // grab output from the tx
6812 //predicate for comparison later on
6813 auto pred = [output](const tx_destination_entry &destination) {
6814 return destination.addr == output.address;
6815 };
6816
6817 //search our working list of destinations in entry, and either add output amount to the
6818 // running total in the case of a match, or add a new destination otherwise
6819 auto dest_ptr = std::find_if(std::begin(utd.m_dests),
6820 std::end(utd.m_dests), pred);
6821 if (dest_ptr != std::end(utd.m_dests)) {
6822 dest_ptr->amount += tx.vout[i].amount;
6823 } else {
6824 utd.m_dests.push_back(tx_destination_entry(
6825 tx.vout[i].amount,
6826 output.address,
6827 output.m_address_prefix ==
6828 get_config(this->m_nettype).CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX ? true : false
6829 ));
6830 }
6831 }
6832 }
6833 //amount out is the sum of destinations and change (if loopback tx change = 0 so this still checks out)
6834 for (const auto &d: utd.m_dests)
6835 utd.m_amount_out += d.amount;
6836 utd.m_amount_out += total_change;
6837 }
6838
6839 utd.m_tx = (const cryptonote::transaction_prefix&)tx;
6840 utd.m_sent_time = time(NULL);
6841 utd.m_payment_id = payment_id;
6842 utd.m_state = wallet2::unconfirmed_transfer_details::pending;
6843 utd.m_timestamp = time(NULL);
6844 utd.m_subaddr_account = subaddr_account;
6845 utd.m_subaddr_indices = subaddr_indices;
6846 for (const auto &in: tx.vin)
6847 {
6848 if (in.type() != typeid(cryptonote::txin_to_key))
6849 continue;
6850 const auto &txin = boost::get<cryptonote::txin_to_key>(in);
6851 utd.m_rings.push_back(std::make_pair(txin.k_image, txin.key_offsets));
6852 }
6853}
6854
6855//----------------------------------------------------------------------------------------------------
6856crypto::hash wallet2::get_payment_id(const pending_tx &ptx) const
6857{
6858 std::vector<tx_extra_field> tx_extra_fields;
6859 parse_tx_extra(ptx.tx.extra, tx_extra_fields); // ok if partially parsed
6860 tx_extra_nonce extra_nonce;
6861 crypto::hash payment_id = null_hash;
6862 if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
6863 {
6864 crypto::hash8 payment_id8 = null_hash8;
6865 if(get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
6866 {
6867 if (ptx.dests.empty())
6868 {
6869 MWARNING("Encrypted payment id found, but no destinations public key, cannot decrypt");
6870 return crypto::null_hash;
6871 }
6872 if (m_account.get_device().decrypt_payment_id(payment_id8, ptx.dests[0].addr.m_view_public_key, ptx.tx_key))
6873 {
6874 memcpy(payment_id.data, payment_id8.data, 8);
6875 }
6876 }
6877 else if (!get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
6878 {
6879 payment_id = crypto::null_hash;
6880 }
6881 }
6882 return payment_id;
6883}
6884
6885//----------------------------------------------------------------------------------------------------
6886// take a pending tx and actually send it to the daemon
6888{
6889 using namespace cryptonote;
6890
6891 if(m_light_wallet)
6892 {
6895 oreq.address = get_account().get_public_address_str(m_nettype);
6896 oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
6898 m_daemon_rpc_mutex.lock();
6899 bool r = invoke_http_json("/submit_raw_tx", oreq, ores, rpc_timeout, "POST");
6900 m_daemon_rpc_mutex.unlock();
6902 // MyMonero and OpenMonero use different status strings
6903 THROW_WALLET_EXCEPTION_IF(ores.status != "OK" && ores.status != "success" , error::tx_rejected, ptx.tx, get_rpc_status(ores.status), ores.error);
6904 }
6905 else
6906 {
6907 // Normal submit
6910 req.do_not_relay = false;
6911 req.do_sanity_checks = true;
6912 COMMAND_RPC_SEND_RAW_TX::response daemon_send_resp;
6913 m_daemon_rpc_mutex.lock();
6914 bool r = invoke_http_json("/sendrawtransaction", req, daemon_send_resp, rpc_timeout);
6915 m_daemon_rpc_mutex.unlock();
6917 THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "sendrawtransaction");
6918 THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status != CORE_RPC_STATUS_OK, error::tx_rejected, ptx.tx, get_rpc_status(daemon_send_resp.status), get_text_reason(daemon_send_resp));
6919 // sanity checks
6920 for (size_t idx: ptx.selected_transfers)
6921 {
6923 "Bad output index in selected transfers: " + boost::lexical_cast<std::string>(idx));
6924 }
6925 }
6926 crypto::hash txid;
6927
6928 txid = get_transaction_hash(ptx.tx);
6929 crypto::hash payment_id = crypto::null_hash;
6930 std::vector<cryptonote::tx_destination_entry> dests;
6931 uint64_t amount_in = 0;
6932 if (store_tx_info())
6933 {
6934 payment_id = get_payment_id(ptx);
6935 dests = ptx.dests;
6936 for(size_t idx: ptx.selected_transfers)
6937 amount_in += m_transfers[idx].amount();
6938 }
6939 add_unconfirmed_tx(ptx.tx, amount_in, dests, payment_id, ptx.change_dts.amount, ptx.construction_data.subaddr_account, ptx.construction_data.subaddr_indices);
6940 if (store_tx_info())
6941 {
6942 m_tx_keys.insert(std::make_pair(txid, ptx.tx_key));
6943 m_additional_tx_keys.insert(std::make_pair(txid, ptx.additional_tx_keys));
6944 }
6945
6946 LOG_PRINT_L2("transaction " << txid << " generated ok and sent to daemon, key_images: [" << ptx.key_images << "]");
6947
6948 for(size_t idx: ptx.selected_transfers)
6949 {
6950 set_spent(idx, 0);
6951 }
6952
6953 // tx generated, get rid of used k values
6954 for (size_t idx: ptx.selected_transfers)
6955 m_transfers[idx].m_multisig_k.clear();
6956
6957 //fee includes dust if dust policy specified it.
6958 LOG_PRINT_L1("Transaction successfully sent. <" << txid << ">" << ENDL
6959 << "Commission: " << print_etn(ptx.fee) << " (dust sent to dust addr: " << print_etn((ptx.dust_added_to_fee ? 0 : ptx.dust)) << ")" << ENDL
6960 << "Pre V10 Balance: " << print_etn(balance(ptx.construction_data.subaddr_account, false)) << ENDL
6961 << "Pre V10 Unlocked Balance: " << print_etn(unlocked_balance(ptx.construction_data.subaddr_account, false)) << ENDL
6962 << "Public Chain Balance: " << print_etn(balance(ptx.construction_data.subaddr_account, true)) << ENDL
6963 << "Public Chain Unlocked Balance: " << print_etn(unlocked_balance(ptx.construction_data.subaddr_account, true)) << ENDL
6964 << "Please, wait for confirmation for your balance to be unlocked.");
6965}
6966
6967void wallet2::commit_tx(std::vector<pending_tx>& ptx_vector)
6968{
6969 for (auto & ptx : ptx_vector)
6970 {
6971 commit_tx(ptx);
6972 }
6973}
6974//----------------------------------------------------------------------------------------------------
6975bool wallet2::save_tx(const std::vector<pending_tx>& ptx_vector, const std::string &filename) const
6976{
6977 LOG_PRINT_L0("saving " << ptx_vector.size() << " transactions");
6978 std::string ciphertext = dump_tx_to_str(ptx_vector);
6979 if (ciphertext.empty())
6980 return false;
6981 return epee::file_io_utils::save_string_to_file(filename, ciphertext);
6982}
6983//----------------------------------------------------------------------------------------------------
6984std::string wallet2::dump_tx_to_str(const std::vector<pending_tx> &ptx_vector) const
6985{
6986 LOG_PRINT_L0("saving " << ptx_vector.size() << " transactions");
6987 unsigned_tx_set txs;
6988 for (auto &tx: ptx_vector)
6989 {
6990 // Short payment id is encrypted with tx_key.
6991 // Since sign_tx() generates new tx_keys and encrypts the payment id, we need to save the decrypted payment ID
6992 // Save tx construction_data to unsigned_tx_set
6993 txs.txes.push_back(get_construction_data_with_decrypted_short_payment_id(tx, m_account.get_device()));
6994 }
6995
6996 txs.transfers = export_outputs();
6997 // save as binary
6998 std::ostringstream oss;
7000 try
7001 {
7002 ar << txs;
7003 }
7004 catch (...)
7005 {
7006 return std::string();
7007 }
7008 LOG_PRINT_L2("Saving unsigned tx data: " << oss.str());
7009 std::string ciphertext = encrypt_with_view_secret_key(oss.str());
7010
7011 return std::string(UNSIGNED_TX_PREFIX) + ciphertext;
7012}
7013//----------------------------------------------------------------------------------------------------
7014bool wallet2::load_unsigned_tx(const std::string &unsigned_filename, unsigned_tx_set &exported_txs) const
7015{
7016 std::string s;
7017 boost::system::error_code errcode;
7018
7019 if (!boost::filesystem::exists(unsigned_filename, errcode))
7020 {
7021 LOG_PRINT_L0("File " << unsigned_filename << " does not exist: " << errcode);
7022 return false;
7023 }
7024 if (!epee::file_io_utils::load_file_to_string(unsigned_filename.c_str(), s))
7025 {
7026 LOG_PRINT_L0("Failed to load from " << unsigned_filename);
7027 return false;
7028 }
7029
7030 return parse_unsigned_tx_from_str(s, exported_txs);
7031}
7032//----------------------------------------------------------------------------------------------------
7033bool wallet2::parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const
7034{
7035 std::string s = unsigned_tx_st;
7036 const size_t magiclen = strlen(UNSIGNED_TX_PREFIX) - 1;
7037 if (strncmp(s.c_str(), UNSIGNED_TX_PREFIX, magiclen))
7038 {
7039 LOG_PRINT_L0("Bad magic from unsigned tx");
7040 return false;
7041 }
7042 s = s.substr(magiclen);
7043 const char version = s[0];
7044 s = s.substr(1);
7045 if (version == '\003')
7046 {
7047 try
7048 {
7049 std::istringstream iss(s);
7051 ar >> exported_txs;
7052 }
7053 catch (const std::exception &e)
7054 {
7055 LOG_PRINT_L0("Failed to parse data from unsigned tx: " << e.what());
7056 return false;
7057 }
7058 catch (...)
7059 {
7060 LOG_PRINT_L0("Failed to parse data from unsigned tx");
7061 return false;
7062 }
7063 }
7064 else if (version == '\004')
7065 {
7066 try
7067 {
7069 try
7070 {
7071 std::istringstream iss(s);
7073 ar >> exported_txs;
7074 }
7075 catch (const std::exception &e)
7076 {
7077 LOG_PRINT_L0("Failed to parse decrypted data from unsigned tx: " << e.what());
7078 return false;
7079 }
7080 }
7081 catch (const std::exception &e)
7082 {
7083 LOG_PRINT_L0("Failed to decrypt unsigned tx: " << e.what());
7084 return false;
7085 }
7086 catch(...)
7087 {
7088 LOG_PRINT_L0("Failed to parse decrypted data from unsigned tx");
7089 return false;
7090 }
7091 }
7092 else
7093 {
7094 LOG_PRINT_L0("Unsupported version in unsigned tx");
7095 return false;
7096 }
7097 LOG_PRINT_L1("Loaded tx unsigned data from binary: " << exported_txs.txes.size() << " transactions");
7098
7099 return true;
7100}
7101//----------------------------------------------------------------------------------------------------
7102bool wallet2::sign_tx(const std::string &unsigned_filename, const std::string &signed_filename, std::vector<wallet2::pending_tx> &txs, std::function<bool(const unsigned_tx_set&)> accept_func, bool export_raw)
7103{
7104 unsigned_tx_set exported_txs;
7105 if(!load_unsigned_tx(unsigned_filename, exported_txs))
7106 return false;
7107
7108 if (accept_func && !accept_func(exported_txs))
7109 {
7110 LOG_PRINT_L1("Transactions rejected by callback");
7111 return false;
7112 }
7113 return sign_tx(exported_txs, signed_filename, txs, export_raw);
7114}
7115//----------------------------------------------------------------------------------------------------
7116bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pending_tx> &txs, signed_tx_set &signed_txes)
7117{
7118 import_outputs(exported_txs.transfers);
7119
7120 // sign the transactions
7121 for (size_t n = 0; n < exported_txs.txes.size(); ++n)
7122 {
7123 tools::wallet2::tx_construction_data &sd = exported_txs.txes[n];
7125 LOG_PRINT_L1(" " << (n+1) << ": " << sd.sources.size() << " inputs, ring size " << sd.sources[0].outputs.size());
7126 signed_txes.ptx.push_back(pending_tx());
7127 tools::wallet2::pending_tx &ptx = signed_txes.ptx.back();
7128 rct::RCTConfig rct_config = sd.rct_config;
7129 crypto::secret_key tx_key;
7130 std::vector<crypto::secret_key> additional_tx_keys;
7131 rct::multisig_out msout;
7132
7133 // NB no early blocks this time for v3 transactions, as nobody should be sending v3 transactions before their v2 (migration) tx have confirmed. V2 confirmations always take place at, or after, the fork block.
7134 // todo: 4.0.0.0 Migrate vs send regular tx.
7136 bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sd.sources, sd.splitted_dsts, sd.change_dts.addr, sd.extra, ptx.tx, sd.unlock_time, tx_key, additional_tx_keys, sd.use_rct, rct_config, m_multisig ? &msout : NULL, m_account_major_offset, this->m_nettype);
7138 // we don't test tx size, because we don't know the current limit, due to not having a blockchain,
7139 // and it's a bit pointless to fail there anyway, since it'd be a (good) guess only. We sign anyway,
7140 // and if we really go over limit, the daemon will reject when it gets submitted. Chances are it's
7141 // OK anyway since it was generated in the first place, and rerolling should be within a few bytes.
7142
7143 // normally, the tx keys are saved in commit_tx, when the tx is actually sent to the daemon.
7144 // we can't do that here since the tx will be sent from the compromised wallet, which we don't want
7145 // to see that info, so we save it here
7146 if (store_tx_info())
7147 {
7148 const crypto::hash txid = get_transaction_hash(ptx.tx);
7149 m_tx_keys.insert(std::make_pair(txid, tx_key));
7150 m_additional_tx_keys.insert(std::make_pair(txid, additional_tx_keys));
7151 }
7152
7153 std::string key_images;
7154
7155 if(ptx.tx.version < 3) {
7156 bool all_are_txin_to_key = std::all_of(ptx.tx.vin.begin(), ptx.tx.vin.end(), [&](const txin_v &s_e) -> bool {
7157 CHECKED_GET_SPECIFIC_VARIANT(s_e, const txin_to_key, in, false);
7158 key_images += boost::to_string(in.k_image) + " ";
7159 return true;
7160 });
7161 THROW_WALLET_EXCEPTION_IF(!all_are_txin_to_key, error::unexpected_txin_type, ptx.tx);
7162 }else{
7163 bool all_are_txin_to_key_public = std::all_of(ptx.tx.vin.begin(), ptx.tx.vin.end(), [&](const txin_v &s_e) -> bool {
7164 CHECKED_GET_SPECIFIC_VARIANT(s_e, const txin_to_key_public, in, false);
7165 return true;
7166 });
7167 THROW_WALLET_EXCEPTION_IF(!all_are_txin_to_key_public, error::unexpected_txin_type, ptx.tx);
7168 }
7169 if(ptx.tx.version > 1) {
7170 bool all_are_txout_to_key_public = std::all_of(ptx.tx.vout.begin(), ptx.tx.vout.end(), [&](const tx_out &s_e) -> bool {
7171 CHECKED_GET_SPECIFIC_VARIANT(s_e.target, const txout_to_key_public, in, false);
7172 return true;
7173 });
7174 THROW_WALLET_EXCEPTION_IF(!all_are_txout_to_key_public, error::unexpected_txout_type, ptx.tx);
7175 }
7176 ptx.key_images = key_images;
7177 ptx.fee = 0;
7178 for (const auto &i: sd.sources) ptx.fee += i.amount;
7179 for (const auto &i: sd.splitted_dsts) ptx.fee -= i.amount;
7180 ptx.dust = 0;
7181 ptx.dust_added_to_fee = false;
7182 ptx.change_dts = sd.change_dts;
7184 ptx.tx_key = rct::rct2sk(rct::identity()); // don't send it back to the untrusted view wallet
7185 ptx.dests = sd.dests;
7186 ptx.construction_data = sd;
7187
7188 txs.push_back(ptx);
7189
7190 // add tx keys only to ptx
7191 txs.back().tx_key = tx_key;
7192 txs.back().additional_tx_keys = additional_tx_keys;
7193 }
7194
7195 // add key image mapping for these txes
7196 const account_keys &keys = get_account().get_keys();
7197 hw::device &hwdev = m_account.get_device();
7198 for (size_t n = 0; n < exported_txs.txes.size(); ++n)
7199 {
7200 const cryptonote::transaction &tx = signed_txes.ptx[n].tx;
7201
7202 crypto::key_derivation derivation;
7203 std::vector<crypto::key_derivation> additional_derivations;
7204
7205 // compute public keys from out secret keys
7206 crypto::public_key tx_pub_key;
7207 crypto::secret_key_to_public_key(txs[n].tx_key, tx_pub_key);
7208 std::vector<crypto::public_key> additional_tx_pub_keys;
7209 for (const crypto::secret_key &skey: txs[n].additional_tx_keys)
7210 {
7211 additional_tx_pub_keys.resize(additional_tx_pub_keys.size() + 1);
7212 crypto::secret_key_to_public_key(skey, additional_tx_pub_keys.back());
7213 }
7214
7215 // compute derivations
7217 if (!hwdev.generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation))
7218 {
7219 MWARNING("Failed to generate key derivation from tx pubkey in " << cryptonote::get_transaction_hash(tx) << ", skipping");
7220 static_assert(sizeof(derivation) == sizeof(rct::key), "Mismatched sizes of key_derivation and rct::key");
7221 memcpy(&derivation, rct::identity().bytes, sizeof(derivation));
7222 }
7223 for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
7224 {
7225 additional_derivations.push_back({});
7226 if (!hwdev.generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back()))
7227 {
7228 MWARNING("Failed to generate key derivation from additional tx pubkey in " << cryptonote::get_transaction_hash(tx) << ", skipping");
7229 memcpy(&additional_derivations.back(), rct::identity().bytes, sizeof(crypto::key_derivation));
7230 }
7231 }
7232
7233 for (size_t i = 0; i < tx.vout.size(); ++i)
7234 {
7235 if (tx.vout[i].target.type() != typeid(cryptonote::txout_to_key))
7236 continue;
7237 const cryptonote::txout_to_key &out = boost::get<cryptonote::txout_to_key>(tx.vout[i].target);
7238 // if this output is back to this wallet, we can calculate its key image already
7239 if (!is_out_to_acc_precomp(m_subaddresses, out.key, derivation, additional_derivations, i, hwdev))
7240 continue;
7242 cryptonote::keypair in_ephemeral;
7243 if (generate_key_image_helper(keys, m_subaddresses, out.key, tx_pub_key, additional_tx_pub_keys, i, in_ephemeral, ki, hwdev))
7244 signed_txes.tx_key_images[out.key] = ki;
7245 else
7246 MERROR("Failed to calculate key image");
7247 }
7248 }
7249
7250 // add key images
7251 signed_txes.key_images.resize(m_transfers.size());
7252 for (size_t i = 0; i < m_transfers.size(); ++i)
7253 {
7254 if (!m_transfers[i].m_key_image_known || m_transfers[i].m_key_image_partial)
7255 LOG_PRINT_L0("WARNING: key image not known in signing wallet at index " << i);
7256 signed_txes.key_images[i] = m_transfers[i].m_key_image;
7257 }
7258
7259 return true;
7260}
7261//----------------------------------------------------------------------------------------------------
7262bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_filename, std::vector<wallet2::pending_tx> &txs, bool export_raw)
7263{
7264 // sign the transactions
7265 signed_tx_set signed_txes;
7266 std::string ciphertext = sign_tx_dump_to_str(exported_txs, txs, signed_txes);
7267 if (ciphertext.empty())
7268 {
7269 LOG_PRINT_L0("Failed to sign unsigned_tx_set");
7270 return false;
7271 }
7272
7273 if (!epee::file_io_utils::save_string_to_file(signed_filename, ciphertext))
7274 {
7275 LOG_PRINT_L0("Failed to save file to " << signed_filename);
7276 return false;
7277 }
7278 // export signed raw tx without encryption
7279 if (export_raw)
7280 {
7281 for (size_t i = 0; i < signed_txes.ptx.size(); ++i)
7282 {
7283 std::string tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(signed_txes.ptx[i].tx));
7284 std::string raw_filename = signed_filename + "_raw" + (signed_txes.ptx.size() == 1 ? "" : ("_" + std::to_string(i)));
7285 if (!epee::file_io_utils::save_string_to_file(raw_filename, tx_as_hex))
7286 {
7287 LOG_PRINT_L0("Failed to save file to " << raw_filename);
7288 return false;
7289 }
7290 }
7291 }
7292 return true;
7293}
7294//----------------------------------------------------------------------------------------------------
7295std::string wallet2::sign_tx_dump_to_str(unsigned_tx_set &exported_txs, std::vector<wallet2::pending_tx> &ptx, signed_tx_set &signed_txes)
7296{
7297 // sign the transactions
7298 bool r = sign_tx(exported_txs, ptx, signed_txes);
7299 if (!r)
7300 {
7301 LOG_PRINT_L0("Failed to sign unsigned_tx_set");
7302 return std::string();
7303 }
7304
7305 // save as binary
7306 std::ostringstream oss;
7308 try
7309 {
7310 ar << signed_txes;
7311 }
7312 catch(...)
7313 {
7314 return std::string();
7315 }
7316 LOG_PRINT_L3("Saving signed tx data (with encryption): " << oss.str());
7317 std::string ciphertext = encrypt_with_view_secret_key(oss.str());
7318 return std::string(SIGNED_TX_PREFIX) + ciphertext;
7319}
7320//----------------------------------------------------------------------------------------------------
7321bool wallet2::load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func)
7322{
7323 std::string s;
7324 boost::system::error_code errcode;
7325 signed_tx_set signed_txs;
7326
7327 if (!boost::filesystem::exists(signed_filename, errcode))
7328 {
7329 LOG_PRINT_L0("File " << signed_filename << " does not exist: " << errcode);
7330 return false;
7331 }
7332
7333 if (!epee::file_io_utils::load_file_to_string(signed_filename.c_str(), s))
7334 {
7335 LOG_PRINT_L0("Failed to load from " << signed_filename);
7336 return false;
7337 }
7338
7339 return parse_tx_from_str(s, ptx, accept_func);
7340}
7341//----------------------------------------------------------------------------------------------------
7342bool wallet2::parse_tx_from_str(const std::string &signed_tx_st, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set &)> accept_func)
7343{
7344 std::string s = signed_tx_st;
7345 boost::system::error_code errcode;
7346 signed_tx_set signed_txs;
7347
7348 const size_t magiclen = strlen(SIGNED_TX_PREFIX) - 1;
7349 if (strncmp(s.c_str(), SIGNED_TX_PREFIX, magiclen))
7350 {
7351 LOG_PRINT_L0("Bad magic from signed transaction");
7352 return false;
7353 }
7354 s = s.substr(magiclen);
7355 const char version = s[0];
7356 s = s.substr(1);
7357 if (version == '\003')
7358 {
7359 try
7360 {
7361 std::istringstream iss(s);
7363 ar >> signed_txs;
7364 }
7365 catch (const std::exception &e)
7366 {
7367 LOG_PRINT_L0("Failed to parse data from signed transaction: " << e.what());
7368 return false;
7369 }
7370 catch(...)
7371 {
7372 LOG_PRINT_L0("Failed to parse data from signed transaction");
7373 return false;
7374 }
7375 }
7376 else if (version == '\004')
7377 {
7378 try
7379 {
7381 try
7382 {
7383 std::istringstream iss(s);
7385 ar >> signed_txs;
7386 }
7387 catch (const std::exception &e)
7388 {
7389 LOG_PRINT_L0("Failed to parse decrypted data from signed transaction: " << e.what());
7390 return false;
7391 }
7392 }
7393 catch (const std::exception &e)
7394 {
7395 LOG_PRINT_L0("Failed to decrypt signed transaction: " << e.what());
7396 return false;
7397 }
7398 catch(...)
7399 {
7400 LOG_PRINT_L0("Failed to decrypt signed transaction");
7401 return false;
7402 }
7403 }
7404 else
7405 {
7406 LOG_PRINT_L0("Unsupported version in signed transaction");
7407 return false;
7408 }
7409 LOG_PRINT_L0("Loaded signed tx data from binary: " << signed_txs.ptx.size() << " transactions");
7410 for (auto &c_ptx: signed_txs.ptx) LOG_PRINT_L0(cryptonote::obj_to_json_str(c_ptx.tx));
7411
7412 if (accept_func && !accept_func(signed_txs))
7413 {
7414 LOG_PRINT_L1("Transactions rejected by callback");
7415 return false;
7416 }
7417
7418 // import key images
7419 bool r = import_key_images(signed_txs.key_images);
7420 if (!r) return false;
7421
7422 // remember key images for this tx, for when we get those txes from the blockchain
7423 for (const auto &e: signed_txs.tx_key_images)
7424 m_cold_key_images.insert(e);
7425
7426 ptx = signed_txs.ptx;
7427
7428 return true;
7429}
7430//----------------------------------------------------------------------------------------------------
7432{
7433 LOG_PRINT_L0("saving " << txs.m_ptx.size() << " multisig transactions");
7434
7435 // txes generated, get rid of used k values
7436 for (size_t n = 0; n < txs.m_ptx.size(); ++n)
7437 for (size_t idx: txs.m_ptx[n].construction_data.selected_transfers)
7438 m_transfers[idx].m_multisig_k.clear();
7439
7440 // zero out some data we don't want to share
7441 for (auto &ptx: txs.m_ptx)
7442 {
7443 for (auto &e: ptx.construction_data.sources)
7444 e.multisig_kLRki.k = rct::zero();
7445 }
7446
7447 for (auto &ptx: txs.m_ptx)
7448 {
7449 // Get decrypted payment id from pending_tx
7450 ptx.construction_data = get_construction_data_with_decrypted_short_payment_id(ptx, m_account.get_device());
7451 }
7452
7453 // save as binary
7454 std::ostringstream oss;
7456 try
7457 {
7458 ar << txs;
7459 }
7460 catch (...)
7461 {
7462 return std::string();
7463 }
7464 LOG_PRINT_L2("Saving multisig unsigned tx data: " << oss.str());
7465 std::string ciphertext = encrypt_with_view_secret_key(oss.str());
7466 return std::string(MULTISIG_UNSIGNED_TX_PREFIX) + ciphertext;
7467}
7468//----------------------------------------------------------------------------------------------------
7469bool wallet2::save_multisig_tx(const multisig_tx_set &txs, const std::string &filename)
7470{
7471 std::string ciphertext = save_multisig_tx(txs);
7472 if (ciphertext.empty())
7473 return false;
7474 return epee::file_io_utils::save_string_to_file(filename, ciphertext);
7475}
7476//----------------------------------------------------------------------------------------------------
7477wallet2::multisig_tx_set wallet2::make_multisig_tx_set(const std::vector<pending_tx>& ptx_vector) const
7478{
7479 multisig_tx_set txs;
7480 txs.m_ptx = ptx_vector;
7481
7482 for (const auto &msk: get_account().get_multisig_keys())
7483 {
7485 for (auto &ptx: txs.m_ptx) for (auto &sig: ptx.multisig_sigs) sig.signing_keys.insert(pkey);
7486 }
7487
7489 return txs;
7490}
7491
7492std::string wallet2::save_multisig_tx(const std::vector<pending_tx>& ptx_vector)
7493{
7494 return save_multisig_tx(make_multisig_tx_set(ptx_vector));
7495}
7496//----------------------------------------------------------------------------------------------------
7497bool wallet2::save_multisig_tx(const std::vector<pending_tx>& ptx_vector, const std::string &filename)
7498{
7499 std::string ciphertext = save_multisig_tx(ptx_vector);
7500 if (ciphertext.empty())
7501 return false;
7502 return epee::file_io_utils::save_string_to_file(filename, ciphertext);
7503}
7504//----------------------------------------------------------------------------------------------------
7505bool wallet2::parse_multisig_tx_from_str(std::string multisig_tx_st, multisig_tx_set &exported_txs) const
7506{
7507 const size_t magiclen = strlen(MULTISIG_UNSIGNED_TX_PREFIX);
7508 if (strncmp(multisig_tx_st.c_str(), MULTISIG_UNSIGNED_TX_PREFIX, magiclen))
7509 {
7510 LOG_PRINT_L0("Bad magic from multisig tx data");
7511 return false;
7512 }
7513 try
7514 {
7515 multisig_tx_st = decrypt_with_view_secret_key(std::string(multisig_tx_st, magiclen));
7516 }
7517 catch (const std::exception &e)
7518 {
7519 LOG_PRINT_L0("Failed to decrypt multisig tx data: " << e.what());
7520 return false;
7521 }
7522 try
7523 {
7524 std::istringstream iss(multisig_tx_st);
7526 ar >> exported_txs;
7527 }
7528 catch (...)
7529 {
7530 LOG_PRINT_L0("Failed to parse multisig tx data");
7531 return false;
7532 }
7533
7534 // sanity checks
7535 for (const auto &ptx: exported_txs.m_ptx)
7536 {
7537 CHECK_AND_ASSERT_MES(ptx.selected_transfers.size() == ptx.tx.vin.size(), false, "Mismatched selected_transfers/vin sizes");
7538 for (size_t idx: ptx.selected_transfers)
7539 CHECK_AND_ASSERT_MES(idx < m_transfers.size(), false, "Transfer index out of range");
7540 CHECK_AND_ASSERT_MES(ptx.construction_data.selected_transfers.size() == ptx.tx.vin.size(), false, "Mismatched cd selected_transfers/vin sizes");
7541 for (size_t idx: ptx.construction_data.selected_transfers)
7542 CHECK_AND_ASSERT_MES(idx < m_transfers.size(), false, "Transfer index out of range");
7543 CHECK_AND_ASSERT_MES(ptx.construction_data.sources.size() == ptx.tx.vin.size(), false, "Mismatched sources/vin sizes");
7544 }
7545
7546 return true;
7547}
7548//----------------------------------------------------------------------------------------------------
7549bool wallet2::load_multisig_tx(cryptonote::blobdata s, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func)
7550{
7551 if(!parse_multisig_tx_from_str(s, exported_txs))
7552 {
7553 LOG_PRINT_L0("Failed to parse multisig transaction from string");
7554 return false;
7555 }
7556
7557 LOG_PRINT_L1("Loaded multisig tx unsigned data from binary: " << exported_txs.m_ptx.size() << " transactions");
7558 for (auto &ptx: exported_txs.m_ptx) LOG_PRINT_L0(cryptonote::obj_to_json_str(ptx.tx));
7559
7560 if (accept_func && !accept_func(exported_txs))
7561 {
7562 LOG_PRINT_L1("Transactions rejected by callback");
7563 return false;
7564 }
7565
7566 const bool is_signed = exported_txs.m_signers.size() >= m_multisig_threshold;
7567 if (is_signed)
7568 {
7569 for (const auto &ptx: exported_txs.m_ptx)
7570 {
7571 const crypto::hash txid = get_transaction_hash(ptx.tx);
7572 if (store_tx_info())
7573 {
7574 m_tx_keys.insert(std::make_pair(txid, ptx.tx_key));
7575 m_additional_tx_keys.insert(std::make_pair(txid, ptx.additional_tx_keys));
7576 }
7577 }
7578 }
7579
7580 return true;
7581}
7582//----------------------------------------------------------------------------------------------------
7583bool wallet2::load_multisig_tx_from_file(const std::string &filename, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func)
7584{
7585 std::string s;
7586 boost::system::error_code errcode;
7587
7588 if (!boost::filesystem::exists(filename, errcode))
7589 {
7590 LOG_PRINT_L0("File " << filename << " does not exist: " << errcode);
7591 return false;
7592 }
7593 if (!epee::file_io_utils::load_file_to_string(filename.c_str(), s))
7594 {
7595 LOG_PRINT_L0("Failed to load from " << filename);
7596 return false;
7597 }
7598
7599 if (!load_multisig_tx(s, exported_txs, accept_func))
7600 {
7601 LOG_PRINT_L0("Failed to parse multisig tx data from " << filename);
7602 return false;
7603 }
7604 return true;
7605}
7606//----------------------------------------------------------------------------------------------------
7607bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto::hash> &txids)
7608{
7609 THROW_WALLET_EXCEPTION_IF(exported_txs.m_ptx.empty(), error::wallet_internal_error, "No tx found");
7610
7612
7613 THROW_WALLET_EXCEPTION_IF(exported_txs.m_signers.find(local_signer) != exported_txs.m_signers.end(),
7614 error::wallet_internal_error, "Transaction already signed by this private key");
7615 THROW_WALLET_EXCEPTION_IF(exported_txs.m_signers.size() > m_multisig_threshold,
7616 error::wallet_internal_error, "Transaction was signed by too many signers");
7617 THROW_WALLET_EXCEPTION_IF(exported_txs.m_signers.size() == m_multisig_threshold,
7618 error::wallet_internal_error, "Transaction is already fully signed");
7619
7620 txids.clear();
7621
7622 // sign the transactions
7623 for (size_t n = 0; n < exported_txs.m_ptx.size(); ++n)
7624 {
7625 tools::wallet2::pending_tx &ptx = exported_txs.m_ptx[n];
7626 THROW_WALLET_EXCEPTION_IF(ptx.multisig_sigs.empty(), error::wallet_internal_error, "No signatures found in multisig tx");
7628 LOG_PRINT_L1(" " << (n+1) << ": " << sd.sources.size() << " inputs, mixin " << (sd.sources[0].outputs.size()-1) <<
7629 ", signed by " << exported_txs.m_signers.size() << "/" << m_multisig_threshold);
7631 rct::multisig_out msout = ptx.multisig_sigs.front().msout;
7632 auto sources = sd.sources;
7633 rct::RCTConfig rct_config = sd.rct_config;
7634 bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources, sd.splitted_dsts, ptx.change_dts.addr, sd.extra, tx, sd.unlock_time, ptx.tx_key, ptx.additional_tx_keys, sd.use_rct, rct_config, &msout, false, 0 /*===default value*/, this->m_nettype);
7636
7638 error::wallet_internal_error, "Transaction prefix does not match data");
7639
7640 // Tests passed, sign
7641 std::vector<unsigned int> indices;
7642 for (const auto &source: sources)
7643 indices.push_back(source.real_output);
7644
7645 for (auto &sig: ptx.multisig_sigs)
7646 {
7647 if (sig.ignore.find(local_signer) == sig.ignore.end())
7648 {
7649 ptx.tx.rct_signatures = sig.sigs;
7650
7651 rct::keyV k;
7652 for (size_t idx: sd.selected_transfers)
7653 k.push_back(get_multisig_k(idx, sig.used_L));
7654
7655 rct::key skey = rct::zero();
7656 for (const auto &msk: get_account().get_multisig_keys())
7657 {
7659
7660 if (sig.signing_keys.find(pmsk) == sig.signing_keys.end())
7661 {
7662 sc_add(skey.bytes, skey.bytes, rct::sk2rct(msk).bytes);
7663 sig.signing_keys.insert(pmsk);
7664 }
7665 }
7666 THROW_WALLET_EXCEPTION_IF(!rct::signMultisig(ptx.tx.rct_signatures, indices, k, sig.msout, skey),
7667 error::wallet_internal_error, "Failed signing, transaction likely malformed");
7668
7669 sig.sigs = ptx.tx.rct_signatures;
7670 }
7671 }
7672
7673 const bool is_last = exported_txs.m_signers.size() + 1 >= m_multisig_threshold;
7674 if (is_last)
7675 {
7676 // when the last signature on a multisig tx is made, we select the right
7677 // signature to plug into the final tx
7678 bool found = false;
7679 for (const auto &sig: ptx.multisig_sigs)
7680 {
7681 if (sig.ignore.find(local_signer) == sig.ignore.end() && !keys_intersect(sig.ignore, exported_txs.m_signers))
7682 {
7683 THROW_WALLET_EXCEPTION_IF(found, error::wallet_internal_error, "More than one transaction is final");
7684 ptx.tx.rct_signatures = sig.sigs;
7685 found = true;
7686 }
7687 }
7689 "Final signed transaction not found: this transaction was likely made without our export data, so we cannot sign it");
7690 const crypto::hash txid = get_transaction_hash(ptx.tx);
7691 if (store_tx_info())
7692 {
7693 m_tx_keys.insert(std::make_pair(txid, ptx.tx_key));
7694 m_additional_tx_keys.insert(std::make_pair(txid, ptx.additional_tx_keys));
7695 }
7696 txids.push_back(txid);
7697 }
7698 }
7699
7700 // txes generated, get rid of used k values
7701 for (size_t n = 0; n < exported_txs.m_ptx.size(); ++n)
7702 for (size_t idx: exported_txs.m_ptx[n].construction_data.selected_transfers)
7703 m_transfers[idx].m_multisig_k.clear();
7704
7705 exported_txs.m_signers.insert(get_multisig_signer_public_key());
7706
7707 return true;
7708}
7709//----------------------------------------------------------------------------------------------------
7710bool wallet2::sign_multisig_tx_to_file(multisig_tx_set &exported_txs, const std::string &filename, std::vector<crypto::hash> &txids)
7711{
7712 bool r = sign_multisig_tx(exported_txs, txids);
7713 if (!r)
7714 return false;
7715 return save_multisig_tx(exported_txs, filename);
7716}
7717//----------------------------------------------------------------------------------------------------
7718bool wallet2::sign_multisig_tx_from_file(const std::string &filename, std::vector<crypto::hash> &txids, std::function<bool(const multisig_tx_set&)> accept_func)
7719{
7720 multisig_tx_set exported_txs;
7721 if(!load_multisig_tx_from_file(filename, exported_txs))
7722 return false;
7723
7724 if (accept_func && !accept_func(exported_txs))
7725 {
7726 LOG_PRINT_L1("Transactions rejected by callback");
7727 return false;
7728 }
7729 return sign_multisig_tx_to_file(exported_txs, filename, txids);
7730}
7731//----------------------------------------------------------------------------------------------------
7732uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm) const
7733{
7734 static const struct
7735 {
7736 size_t count;
7737 uint64_t multipliers[4];
7738 }
7739 multipliers[] =
7740 {
7741 { 3, {1, 2, 3} },
7742 { 3, {1, 20, 166} },
7743 { 4, {1, 4, 20, 166} },
7744 { 4, {1, 2, 4, 8} },
7745 };
7746
7747 if (fee_algorithm == -1)
7748 fee_algorithm = get_fee_algorithm();
7749
7750 // 0 -> default (here, x1 till fee algorithm 2, x4 from it)
7751 if (priority == 0)
7752 priority = m_default_priority;
7753 if (priority == 0)
7754 {
7755 if (fee_algorithm == 2)
7756 priority = 2;
7757 else
7758 priority = 1;
7759 }
7760
7761 THROW_WALLET_EXCEPTION_IF(fee_algorithm < 0 || fee_algorithm > 4, error::invalid_priority);
7762
7763 // 1 to 3/4 are allowed as priorities
7764 const uint32_t max_priority = multipliers[fee_algorithm].count;
7765 if (priority >= 1 && priority <= max_priority)
7766 {
7767 return multipliers[fee_algorithm].multipliers[priority-1];
7768 }
7769
7771 return 1;
7772}
7773//----------------------------------------------------------------------------------------------------
7774uint64_t wallet2::get_dynamic_base_fee_estimate() const
7775{
7776 uint64_t fee;
7777 boost::optional<std::string> result = m_node_rpc_proxy.get_dynamic_base_fee_estimate(FEE_ESTIMATE_GRACE_BLOCKS, fee);
7778 if (!result)
7779 return fee;
7780 const uint64_t base_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE) ? FEE_PER_BYTE : use_fork_rules(HF_VERSION_ZERO_FEE) ? FEE_PER_KB_V11 : FEE_PER_KB_V6;
7781 LOG_PRINT_L1("Failed to query base fee, using " << print_etn(base_fee));
7782 return base_fee;
7783}
7784//----------------------------------------------------------------------------------------------------
7786{
7787 if(m_light_wallet)
7788 {
7790 return m_light_wallet_per_kb_fee / 1024;
7791 else
7792 return m_light_wallet_per_kb_fee;
7793 }
7794 bool use_dyn_fee = use_fork_rules(HF_VERSION_DYNAMIC_FEE, -720 * 1);
7795 if (!use_dyn_fee){
7797 return FEE_PER_KB_V11;
7798 } else{
7799 return FEE_PER_KB_V6;
7800 }
7801 }
7802
7803
7804 return get_dynamic_base_fee_estimate(); //this never gets hit for any version before 100
7805}
7806//----------------------------------------------------------------------------------------------------
7808{
7809 if(m_light_wallet)
7810 {
7811 return 1; // TODO
7812 }
7813 bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
7814 if (!use_per_byte_fee)
7815 return 1;
7816
7817 uint64_t fee_quantization_mask;
7818 boost::optional<std::string> result = m_node_rpc_proxy.get_fee_quantization_mask(fee_quantization_mask);
7819 if (result)
7820 return 1;
7821 return fee_quantization_mask;
7822}
7823//----------------------------------------------------------------------------------------------------
7825{
7826 // changes at v3, v5, v8
7827 if (use_fork_rules(6, 0))
7828 return 3;
7829 if (use_fork_rules(5, 0))
7830 return 2;
7831 if (use_fork_rules(3, -720 * 14))
7832 return 1;
7833 return 0;
7834}
7835//------------------------------------------------------------------------------------------------------------------------------
7837{
7839 return 11;
7841 return 7;
7843 return 5;
7845 return 1;
7847 return 3;
7848 return 0;
7849}
7850//------------------------------------------------------------------------------------------------------------------------------
7852{
7854 return 11;
7856 return 1;
7857 return 0;
7858}
7859//------------------------------------------------------------------------------------------------------------------------------
7861{
7862 const uint64_t min_ring_size = get_min_ring_size();
7863 if (mixin + 1 < min_ring_size)
7864 {
7865 MWARNING("Requested ring size " << (mixin + 1) << " too low, using " << min_ring_size);
7866 mixin = min_ring_size-1;
7867 }
7868 const uint64_t max_ring_size = get_max_ring_size();
7869 if (max_ring_size && mixin + 1 > max_ring_size)
7870 {
7871 MWARNING("Requested ring size " << (mixin + 1) << " too high, using " << max_ring_size);
7872 mixin = max_ring_size-1;
7873 }
7874 return mixin;
7875}
7876//----------------------------------------------------------------------------------------------------
7878{
7879 // just return 1 for normal priority for aurelius instead of being concerned with backlog and adjusting priority because fees are 0 for everyone
7880 return 1;
7881
7882 if (priority == 0 && m_default_priority == 0 && auto_low_priority())
7883 {
7884 try
7885 {
7886 // check if there's a backlog in the tx pool
7887 const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
7888 const uint64_t base_fee = get_base_fee();
7889 const uint64_t fee_multiplier = get_fee_multiplier(1);
7890 const double fee_level = fee_multiplier * base_fee * (use_per_byte_fee ? 1 : (12/(double)13 / (double)1024));
7891 const std::vector<std::pair<uint64_t, uint64_t>> blocks = estimate_backlog({std::make_pair(fee_level, fee_level)});
7892 if (blocks.size() != 1)
7893 {
7894 MERROR("Bad estimated backlog array size");
7895 return priority;
7896 }
7897 else if (blocks[0].first > 0)
7898 {
7899 MINFO("We don't use the low priority because there's a backlog in the tx pool.");
7900 return priority;
7901 }
7902
7903 // get the current full reward zone
7904 uint64_t block_weight_limit = 0;
7905 const auto result = m_node_rpc_proxy.get_block_weight_limit(block_weight_limit);
7906 throw_on_rpc_response_error(result, "get_info");
7907 const uint64_t full_reward_zone = block_weight_limit / 2;
7908
7909 // get the last N block headers and sum the block sizes
7910 const size_t N = 10;
7911 if (m_blockchain.size() < N)
7912 {
7913 MERROR("The blockchain is too short");
7914 return priority;
7915 }
7918 m_daemon_rpc_mutex.lock();
7919 getbh_req.start_height = m_blockchain.size() - N;
7920 getbh_req.end_height = m_blockchain.size() - 1;
7921 bool r = invoke_http_json_rpc("/json_rpc", "getblockheadersrange", getbh_req, getbh_res, rpc_timeout);
7922 m_daemon_rpc_mutex.unlock();
7923 THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getblockheadersrange");
7924 THROW_WALLET_EXCEPTION_IF(getbh_res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getblockheadersrange");
7925 THROW_WALLET_EXCEPTION_IF(getbh_res.status != CORE_RPC_STATUS_OK, error::get_blocks_error, get_rpc_status(getbh_res.status));
7926 if (getbh_res.headers.size() != N)
7927 {
7928 MERROR("Bad blockheaders size");
7929 return priority;
7930 }
7931 size_t block_weight_sum = 0;
7932 for (const cryptonote::block_header_response &i : getbh_res.headers)
7933 {
7934 block_weight_sum += i.block_weight;
7935 }
7936
7937 // estimate how 'full' the last N blocks are
7938 const size_t P = 100 * block_weight_sum / (N * full_reward_zone);
7939 MINFO((boost::format("The last %d blocks fill roughly %d%% of the full reward zone.") % N % P).str());
7940 if (P > 80)
7941 {
7942 MINFO("We don't use the low priority because recent blocks are quite full.");
7943 return priority;
7944 }
7945 MINFO("We'll use the low priority because probably it's safe to do so.");
7946 return 1;
7947 }
7948 catch (const std::exception &e)
7949 {
7950 MERROR(e.what());
7951 }
7952 }
7953 return priority;
7954}
7955//----------------------------------------------------------------------------------------------------
7956bool wallet2::set_ring_database(const std::string &filename)
7957{
7958 m_ring_database = filename;
7959 MINFO("ringdb path set to " << filename);
7960 m_ringdb.reset();
7961 if (!m_ring_database.empty())
7962 {
7963 try
7964 {
7966 generate_genesis(b);
7967 m_ringdb.reset(new tools::ringdb(m_ring_database, epee::string_tools::pod_to_hex(get_block_hash(b))));
7968 }
7969 catch (const std::exception &e)
7970 {
7971 MERROR("Failed to initialize ringdb: " << e.what());
7972 m_ring_database = "";
7973 return false;
7974 }
7975 }
7976 return true;
7977}
7978
7979crypto::chacha_key wallet2::get_ringdb_key()
7980{
7981 if (!m_ringdb_key)
7982 {
7983 MINFO("caching ringdb key");
7984 crypto::chacha_key key;
7985 generate_chacha_key_from_secret_keys(key);
7986 m_ringdb_key = key;
7987 }
7988 return *m_ringdb_key;
7989}
7990
7991void wallet2::register_devices(){
7993}
7994
7995hw::device& wallet2::lookup_device(const std::string & device_descriptor){
7996 if (!m_devices_registered){
7997 m_devices_registered = true;
7998 register_devices();
7999 }
8000
8001 return hw::get_device(device_descriptor);
8002}
8003
8004bool wallet2::add_rings(const crypto::chacha_key &key, const cryptonote::transaction_prefix &tx)
8005{
8006 if (!m_ringdb)
8007 return false;
8008 try { return m_ringdb->add_rings(key, tx); }
8009 catch (const std::exception &e) { return false; }
8010}
8011
8012bool wallet2::add_rings(const cryptonote::transaction_prefix &tx)
8013{
8014 try { return add_rings(get_ringdb_key(), tx); }
8015 catch (const std::exception &e) { return false; }
8016}
8017
8018bool wallet2::remove_rings(const cryptonote::transaction_prefix &tx)
8019{
8020 if (!m_ringdb)
8021 return false;
8022 try { return m_ringdb->remove_rings(get_ringdb_key(), tx); }
8023 catch (const std::exception &e) { return false; }
8024}
8025
8026bool wallet2::get_ring(const crypto::chacha_key &key, const crypto::key_image &key_image, std::vector<uint64_t> &outs)
8027{
8028 if (!m_ringdb)
8029 return false;
8030 try { return m_ringdb->get_ring(key, key_image, outs); }
8031 catch (const std::exception &e) { return false; }
8032}
8033
8034bool wallet2::get_rings(const crypto::hash &txid, std::vector<std::pair<crypto::key_image, std::vector<uint64_t>>> &outs)
8035{
8036 for (auto i: m_confirmed_txs)
8037 {
8038 if (txid == i.first)
8039 {
8040 for (const auto &x: i.second.m_rings)
8041 outs.push_back({x.first, cryptonote::relative_output_offsets_to_absolute(x.second)});
8042 return true;
8043 }
8044 }
8045 for (auto i: m_unconfirmed_txs)
8046 {
8047 if (txid == i.first)
8048 {
8049 for (const auto &x: i.second.m_rings)
8050 outs.push_back({x.first, cryptonote::relative_output_offsets_to_absolute(x.second)});
8051 return true;
8052 }
8053 }
8054 return false;
8055}
8056
8057bool wallet2::get_ring(const crypto::key_image &key_image, std::vector<uint64_t> &outs)
8058{
8059 try { return get_ring(get_ringdb_key(), key_image, outs); }
8060 catch (const std::exception &e) { return false; }
8061}
8062
8063bool wallet2::set_ring(const crypto::key_image &key_image, const std::vector<uint64_t> &outs, bool relative)
8064{
8065 if (!m_ringdb)
8066 return false;
8067
8068 try { return m_ringdb->set_ring(get_ringdb_key(), key_image, outs, relative); }
8069 catch (const std::exception &e) { return false; }
8070}
8071
8072bool wallet2::unset_ring(const std::vector<crypto::key_image> &key_images)
8073{
8074 if (!m_ringdb)
8075 return false;
8076
8077 try { return m_ringdb->remove_rings(get_ringdb_key(), key_images); }
8078 catch (const std::exception &e) { return false; }
8079}
8080
8082{
8083 if (!m_ringdb)
8084 return false;
8085
8088 req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
8089 req.decode_as_json = false;
8090 req.prune = true;
8091 m_daemon_rpc_mutex.lock();
8092 bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout);
8093 m_daemon_rpc_mutex.unlock();
8094 THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to get transaction from daemon");
8095 if (res.txs.empty())
8096 return false;
8097 THROW_WALLET_EXCEPTION_IF(res.txs.size(), error::wallet_internal_error, "Failed to get transaction from daemon");
8098
8100 crypto::hash tx_hash;
8101 if (!get_pruned_tx(res.txs.front(), tx, tx_hash))
8102 return false;
8103 THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "Failed to get the right transaction from daemon");
8104
8105 try { return m_ringdb->remove_rings(get_ringdb_key(), tx); }
8106 catch (const std::exception &e) { return false; }
8107}
8108
8110{
8111 if (!force && m_ring_history_saved)
8112 return true;
8113 if (!m_ringdb)
8114 return false;
8115
8118
8119 MDEBUG("Finding and saving rings...");
8120
8121 // get payments we made
8122 std::vector<crypto::hash> txs_hashes;
8123 std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>> payments;
8124 get_payments_out(payments, 0, std::numeric_limits<uint64_t>::max(), boost::none, std::set<uint32_t>());
8125 for (const std::pair<crypto::hash,wallet2::confirmed_transfer_details> &entry: payments)
8126 {
8127 const crypto::hash &txid = entry.first;
8128 txs_hashes.push_back(txid);
8129 }
8130
8131 MDEBUG("Found " << std::to_string(txs_hashes.size()) << " transactions");
8132
8133 // get those transactions from the daemon
8134 auto it = txs_hashes.begin();
8135 static const size_t SLICE_SIZE = 200;
8136 for (size_t slice = 0; slice < txs_hashes.size(); slice += SLICE_SIZE)
8137 {
8138 req.decode_as_json = false;
8139 req.prune = true;
8140 req.txs_hashes.clear();
8141 size_t ntxes = slice + SLICE_SIZE > txs_hashes.size() ? txs_hashes.size() - slice : SLICE_SIZE;
8142 for (size_t s = slice; s < slice + ntxes; ++s)
8143 req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txs_hashes[s]));
8144 bool r;
8145 {
8146 const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
8147 r = invoke_http_json("/gettransactions", req, res, rpc_timeout);
8148 }
8152 THROW_WALLET_EXCEPTION_IF(res.txs.size() != req.txs_hashes.size(), error::wallet_internal_error,
8153 "daemon returned wrong response for gettransactions, wrong txs count = " +
8154 std::to_string(res.txs.size()) + ", expected " + std::to_string(req.txs_hashes.size()));
8155
8156 MDEBUG("Scanning " << res.txs.size() << " transactions");
8157 THROW_WALLET_EXCEPTION_IF(slice + res.txs.size() > txs_hashes.size(), error::wallet_internal_error, "Unexpected tx array size");
8158 for (size_t i = 0; i < res.txs.size(); ++i, ++it)
8159 {
8160 const auto &tx_info = res.txs[i];
8162 crypto::hash tx_hash;
8164 "Failed to get transaction from daemon");
8165 THROW_WALLET_EXCEPTION_IF(!(tx_hash == *it), error::wallet_internal_error, "Wrong txid received");
8166 THROW_WALLET_EXCEPTION_IF(!add_rings(get_ringdb_key(), tx), error::wallet_internal_error, "Failed to save ring");
8167 }
8168 }
8169
8170 MINFO("Found and saved rings for " << txs_hashes.size() << " transactions");
8171 m_ring_history_saved = true;
8172 return true;
8173}
8174
8175bool wallet2::blackball_output(const std::pair<uint64_t, uint64_t> &output)
8176{
8177 if (!m_ringdb)
8178 return false;
8179 try { return m_ringdb->blackball(output); }
8180 catch (const std::exception &e) { return false; }
8181}
8182
8183bool wallet2::set_blackballed_outputs(const std::vector<std::pair<uint64_t, uint64_t>> &outputs, bool add)
8184{
8185 if (!m_ringdb)
8186 return false;
8187 try
8188 {
8189 bool ret = true;
8190 if (!add)
8191 ret &= m_ringdb->clear_blackballs();
8192 ret &= m_ringdb->blackball(outputs);
8193 return ret;
8194 }
8195 catch (const std::exception &e) { return false; }
8196}
8197
8198bool wallet2::unblackball_output(const std::pair<uint64_t, uint64_t> &output)
8199{
8200 if (!m_ringdb)
8201 return false;
8202 try { return m_ringdb->unblackball(output); }
8203 catch (const std::exception &e) { return false; }
8204}
8205
8206bool wallet2::is_output_blackballed(const std::pair<uint64_t, uint64_t> &output) const
8207{
8208 if (!m_ringdb)
8209 return false;
8210 try { return m_ringdb->blackballed(output); }
8211 catch (const std::exception &e) { return false; }
8212}
8213
8215{
8216 if (m_keys_file_locker)
8217 {
8218 MDEBUG(m_keys_file << " is already locked.");
8219 return false;
8220 }
8221 m_keys_file_locker.reset(new tools::file_locker(m_keys_file));
8222 return true;
8223}
8224
8226{
8227 if (!m_keys_file_locker)
8228 {
8229 MDEBUG(m_keys_file << " is already unlocked.");
8230 return false;
8231 }
8232 m_keys_file_locker.reset();
8233 return true;
8234}
8235
8237{
8238 return m_keys_file_locker->locked();
8239}
8240
8241bool wallet2::tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& output_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const
8242{
8243 if (!unlocked) // don't add locked outs
8244 return false;
8245 if (global_index == real_index) // don't re-add real one
8246 return false;
8247 auto item = std::make_tuple(global_index, output_public_key, mask);
8248 CHECK_AND_ASSERT_MES(!outs.empty(), false, "internal error: outs is empty");
8249 if (std::find(outs.back().begin(), outs.back().end(), item) != outs.back().end()) // don't add duplicates
8250 return false;
8251 // check the keys are valid
8252 if (!rct::isInMainSubgroup(rct::pk2rct(output_public_key)))
8253 {
8254 MWARNING("Key " << output_public_key << " at index " << global_index << " is not in the main subgroup");
8255 return false;
8256 }
8257 if (!rct::isInMainSubgroup(mask))
8258 {
8259 MWARNING("Commitment " << mask << " at index " << global_index << " is not in the main subgroup");
8260 return false;
8261 }
8262// if (is_output_blackballed(output_public_key)) // don't add blackballed outputs
8263// return false;
8264 outs.back().push_back(item);
8265 return true;
8266}
8267
8268void wallet2::light_wallet_get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count) {
8269
8270 MDEBUG("LIGHTWALLET - Getting random outs");
8271
8274
8275 size_t light_wallet_requested_outputs_count = (size_t)((fake_outputs_count + 1) * 1.5 + 1);
8276
8277 // Amounts to ask for
8278 // MyMonero api handle amounts and fees as strings
8279 for(size_t idx: selected_transfers) {
8280 const uint64_t ask_amount = m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount();
8281 std::ostringstream amount_ss;
8282 amount_ss << ask_amount;
8283 oreq.amounts.push_back(amount_ss.str());
8284 }
8285
8286 oreq.count = light_wallet_requested_outputs_count;
8287 m_daemon_rpc_mutex.lock();
8288 bool r = invoke_http_json("/get_random_outs", oreq, ores, rpc_timeout, "POST");
8289 m_daemon_rpc_mutex.unlock();
8291 THROW_WALLET_EXCEPTION_IF(ores.amount_outs.empty() , error::wallet_internal_error, "No outputs received from light wallet node. Error: " + ores.Error);
8292
8293 // Check if we got enough outputs for each amount
8294 for(auto& out: ores.amount_outs) {
8295 const uint64_t out_amount = boost::lexical_cast<uint64_t>(out.amount);
8296 THROW_WALLET_EXCEPTION_IF(out.outputs.size() < light_wallet_requested_outputs_count , error::wallet_internal_error, "Not enough outputs for amount: " + boost::lexical_cast<std::string>(out.amount));
8297 MDEBUG(out.outputs.size() << " outputs for amount "+ boost::lexical_cast<std::string>(out.amount) + " received from light wallet node");
8298 }
8299
8300 MDEBUG("selected transfers size: " << selected_transfers.size());
8301
8302 for(size_t idx: selected_transfers)
8303 {
8304 // Create new index
8305 outs.push_back(std::vector<get_outs_entry>());
8306 outs.back().reserve(fake_outputs_count + 1);
8307
8308 // add real output first
8309 const transfer_details &td = m_transfers[idx];
8310 const uint64_t amount = td.is_rct() ? 0 : td.amount();
8311 outs.back().push_back(std::make_tuple(td.m_global_output_index, td.get_public_key(), rct::commit(td.amount(), td.m_mask)));
8312 MDEBUG("added real output " << string_tools::pod_to_hex(td.get_public_key()));
8313
8314 // Even if the lightwallet server returns random outputs, we pick them randomly.
8315 std::vector<size_t> order;
8316 order.resize(light_wallet_requested_outputs_count);
8317 for (size_t n = 0; n < order.size(); ++n)
8318 order[n] = n;
8319 std::shuffle(order.begin(), order.end(), std::default_random_engine(crypto::rand<unsigned>()));
8320
8321
8322 LOG_PRINT_L2("Looking for " << (fake_outputs_count+1) << " outputs with amounts " << print_etn(td.is_rct() ? 0 : td.amount()));
8323 MDEBUG("OUTS SIZE: " << outs.back().size());
8324 for (size_t o = 0; o < light_wallet_requested_outputs_count && outs.back().size() < fake_outputs_count + 1; ++o)
8325 {
8326 // Random pick
8327 size_t i = order[o];
8328
8329 // Find which random output key to use
8330 bool found_amount = false;
8331 size_t amount_key;
8332 for(amount_key = 0; amount_key < ores.amount_outs.size(); ++amount_key)
8333 {
8334 if(boost::lexical_cast<uint64_t>(ores.amount_outs[amount_key].amount) == amount) {
8335 found_amount = true;
8336 break;
8337 }
8338 }
8339 THROW_WALLET_EXCEPTION_IF(!found_amount , error::wallet_internal_error, "Outputs for amount " + boost::lexical_cast<std::string>(ores.amount_outs[amount_key].amount) + " not found" );
8340
8341 LOG_PRINT_L2("Index " << i << "/" << light_wallet_requested_outputs_count << ": idx " << ores.amount_outs[amount_key].outputs[i].global_index << " (real " << td.m_global_output_index << "), unlocked " << "(always in light)" << ", key " << ores.amount_outs[0].outputs[i].public_key);
8342
8343 // Convert light wallet string data to proper data structures
8344 crypto::public_key tx_public_key;
8345 rct::key mask = AUTO_VAL_INIT(mask); // decrypted mask - not used here
8346 rct::key rct_commit = AUTO_VAL_INIT(rct_commit);
8347 THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, ores.amount_outs[amount_key].outputs[i].public_key), error::wallet_internal_error, "Invalid public_key");
8348 string_tools::hex_to_pod(ores.amount_outs[amount_key].outputs[i].public_key, tx_public_key);
8349 const uint64_t global_index = ores.amount_outs[amount_key].outputs[i].global_index;
8350 if(!light_wallet_parse_rct_str(ores.amount_outs[amount_key].outputs[i].rct, tx_public_key, 0, mask, rct_commit, false))
8351 rct_commit = rct::zeroCommit(td.amount());
8352
8353 if (tx_add_fake_output(outs, global_index, tx_public_key, rct_commit, td.m_global_output_index, true)) {
8354 MDEBUG("added fake output " << ores.amount_outs[amount_key].outputs[i].public_key);
8355 MDEBUG("index " << global_index);
8356 }
8357 }
8358
8359 THROW_WALLET_EXCEPTION_IF(outs.back().size() < fake_outputs_count + 1 , error::wallet_internal_error, "Not enough fake outputs found" );
8360
8361 // Real output is the first. Shuffle outputs
8362 MTRACE(outs.back().size() << " outputs added. Sorting outputs by index:");
8363 std::sort(outs.back().begin(), outs.back().end(), [](const get_outs_entry &a, const get_outs_entry &b) { return std::get<0>(a) < std::get<0>(b); });
8364
8365 // Print output order
8366 for(auto added_out: outs.back())
8367 MTRACE(std::get<0>(added_out));
8368
8369 }
8370}
8371
8372void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, const uint8_t tx_version)
8373{
8374 LOG_PRINT_L2("fake_outputs_count: " << fake_outputs_count);
8375 outs.clear();
8376
8377 if(m_light_wallet && fake_outputs_count > 0) {
8378 light_wallet_get_outs(outs, selected_transfers, fake_outputs_count);
8379 return;
8380 }
8381
8382 if (fake_outputs_count > 0) // zero for electroneum, so skip a lot of code
8383 {
8384 uint64_t segregation_fork_height = get_segregation_fork_height();
8385 // check whether we're shortly after the fork
8387 boost::optional<std::string> result = m_node_rpc_proxy.get_height(height);
8388 throw_on_rpc_response_error(result, "get_info");
8389 bool is_shortly_after_segregation_fork = height >= segregation_fork_height && height < segregation_fork_height + SEGREGATION_FORK_VICINITY;
8390 bool is_after_segregation_fork = height >= segregation_fork_height;
8391
8392 uint64_t v8height = m_nettype == TESTNET ? 446674 : 589169;
8395
8396 // if we have at least one rct out, get the distribution, or fall back to the previous system
8397 uint64_t rct_start_height;
8398 std::vector<uint64_t> rct_offsets;
8399 bool has_rct = false;
8400 uint64_t max_rct_index = 0;
8401 for (size_t idx: selected_transfers)
8402 if (m_transfers[idx].is_rct())
8403 {
8404 has_rct = true;
8405 max_rct_index = std::max(max_rct_index, m_transfers[idx].m_global_output_index);
8406 }
8407 const bool has_rct_distribution = has_rct && get_rct_distribution(rct_start_height, rct_offsets);
8408 if (has_rct_distribution)
8409 {
8410 // check we're clear enough of rct start, to avoid corner cases below
8411 THROW_WALLET_EXCEPTION_IF(rct_offsets.size() <= TX_SPENDABLE_AGE,
8412 error::get_output_distribution, "Not enough rct outputs");
8413 THROW_WALLET_EXCEPTION_IF(rct_offsets.back() <= max_rct_index,
8414 error::get_output_distribution, "Daemon reports suspicious number of rct outputs");
8415 }
8416
8417 // get histogram for the amounts we need
8420 // request histogram for all outputs, except 0 if we have the rct distribution
8421 for(size_t idx: selected_transfers)
8422 if (!m_transfers[idx].is_rct() || !has_rct_distribution)
8423 req_t.amounts.push_back(m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount());
8424 if (!req_t.amounts.empty())
8425 {
8426 std::sort(req_t.amounts.begin(), req_t.amounts.end());
8427 auto end = std::unique(req_t.amounts.begin(), req_t.amounts.end());
8428 req_t.amounts.resize(std::distance(req_t.amounts.begin(), end));
8429 req_t.unlocked = true;
8430 req_t.recent_cutoff = time(NULL) - RECENT_OUTPUT_ZONE;
8431 m_daemon_rpc_mutex.lock();
8432 bool r = invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, rpc_timeout);
8433 m_daemon_rpc_mutex.unlock();
8434 THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected");
8435 THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
8436 THROW_WALLET_EXCEPTION_IF(resp_t.status != CORE_RPC_STATUS_OK, error::get_histogram_error, get_rpc_status(resp_t.status));
8437 }
8438
8439 // if we want to segregate fake outs pre or post fork, get distribution
8440 std::unordered_map<uint64_t, std::pair<uint64_t, uint64_t>> segregation_limit;
8441 if (is_after_segregation_fork && (m_segregate_pre_fork_outputs || m_key_reuse_mitigation2))
8442 {
8445 for(size_t idx: selected_transfers)
8446 req_t.amounts.push_back(m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount());
8447 std::sort(req_t.amounts.begin(), req_t.amounts.end());
8448 auto end = std::unique(req_t.amounts.begin(), req_t.amounts.end());
8449 req_t.amounts.resize(std::distance(req_t.amounts.begin(), end));
8450 req_t.from_height = std::max<uint64_t>(segregation_fork_height, RECENT_OUTPUT_BLOCKS) - RECENT_OUTPUT_BLOCKS;
8451 req_t.to_height = segregation_fork_height + 1;
8452 req_t.cumulative = true;
8453 req_t.binary = true;
8454 m_daemon_rpc_mutex.lock();
8455 bool r = invoke_http_json_rpc("/json_rpc", "get_output_distribution", req_t, resp_t, rpc_timeout * 1000);
8456 m_daemon_rpc_mutex.unlock();
8457 THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected");
8458 THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_distribution");
8459 THROW_WALLET_EXCEPTION_IF(resp_t.status != CORE_RPC_STATUS_OK, error::get_output_distribution, get_rpc_status(resp_t.status));
8460
8461 // check we got all data
8462 for(size_t idx: selected_transfers)
8463 {
8464 const uint64_t amount = m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount();
8465 bool found = false;
8466 for (const auto &d: resp_t.distributions)
8467 {
8468 if (d.amount == amount)
8469 {
8470 THROW_WALLET_EXCEPTION_IF(d.data.start_height > segregation_fork_height, error::get_output_distribution, "Distribution start_height too high");
8471 THROW_WALLET_EXCEPTION_IF(segregation_fork_height - d.data.start_height >= d.data.distribution.size(), error::get_output_distribution, "Distribution size too small");
8472 THROW_WALLET_EXCEPTION_IF(segregation_fork_height - RECENT_OUTPUT_BLOCKS - d.data.start_height >= d.data.distribution.size(), error::get_output_distribution, "Distribution size too small");
8473 THROW_WALLET_EXCEPTION_IF(segregation_fork_height <= RECENT_OUTPUT_BLOCKS, error::wallet_internal_error, "Fork height too low");
8474 THROW_WALLET_EXCEPTION_IF(segregation_fork_height - RECENT_OUTPUT_BLOCKS < d.data.start_height, error::get_output_distribution, "Bad start height");
8475 uint64_t till_fork = d.data.distribution[segregation_fork_height - d.data.start_height];
8476 uint64_t recent = till_fork - d.data.distribution[segregation_fork_height - RECENT_OUTPUT_BLOCKS - d.data.start_height];
8477 segregation_limit[amount] = std::make_pair(till_fork, recent);
8478 found = true;
8479 break;
8480 }
8481 }
8482 THROW_WALLET_EXCEPTION_IF(!found, error::get_output_distribution, "Requested amount not found in response");
8483 }
8484 }
8485
8486 // we ask for more, to have spares if some outputs are still locked
8487 size_t base_requested_outputs_count = (size_t)((fake_outputs_count + 1) * 1.5 + 1);
8488 LOG_PRINT_L2("base_requested_outputs_count: " << base_requested_outputs_count);
8489
8490 // generate output indices to request
8492 COMMAND_RPC_GET_OUTPUTS_BIN::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
8493
8494 std::unique_ptr<gamma_picker> gamma;
8495 if (has_rct_distribution)
8496 gamma.reset(new gamma_picker(rct_offsets));
8497
8498 size_t num_selected_transfers = 0;
8499 for(size_t idx: selected_transfers)
8500 {
8501 ++num_selected_transfers;
8502 const transfer_details &td = m_transfers[idx];
8503 const uint64_t amount = td.is_rct() ? 0 : td.amount();
8504 std::unordered_set<uint64_t> seen_indices;
8505 // request more for rct in base recent (locked) coinbases are picked, since they're locked for longer
8506 size_t requested_outputs_count = base_requested_outputs_count + (td.is_rct() ? MINED_ETN_SPENDABLE_AGE - TX_SPENDABLE_AGE : 0);
8507 size_t start = req.outputs.size();
8508 bool use_histogram = amount != 0 || !has_rct_distribution;
8509
8510 const bool output_is_pre_fork = td.m_block_height < segregation_fork_height;
8511 uint64_t num_outs = 0, num_recent_outs = 0;
8512 uint64_t num_post_fork_outs = 0;
8513 float pre_fork_num_out_ratio = 0.0f;
8514 float post_fork_num_out_ratio = 0.0f;
8515
8516 if (is_after_segregation_fork && m_segregate_pre_fork_outputs && output_is_pre_fork)
8517 {
8518 num_outs = segregation_limit[amount].first;
8519 num_recent_outs = segregation_limit[amount].second;
8520 }
8521 else
8522 {
8523 // if there are just enough outputs to mix with, use all of them.
8524 // Eventually this should become impossible.
8525 for (const auto &he: resp_t.histogram)
8526 {
8527 if (he.amount == amount)
8528 {
8529 LOG_PRINT_L2("Found " << print_etn(amount) << ": " << he.total_instances << " total, "
8530 << he.unlocked_instances << " unlocked, " << he.recent_instances << " recent");
8531 num_outs = he.unlocked_instances;
8532 num_recent_outs = he.recent_instances;
8533 break;
8534 }
8535 }
8536 if (is_after_segregation_fork && m_key_reuse_mitigation2)
8537 {
8538 if (output_is_pre_fork)
8539 {
8540 if (is_shortly_after_segregation_fork)
8541 {
8542 pre_fork_num_out_ratio = 33.4/100.0f * (1.0f - RECENT_OUTPUT_RATIO);
8543 }
8544 else
8545 {
8546 pre_fork_num_out_ratio = 33.4/100.0f * (1.0f - RECENT_OUTPUT_RATIO);
8547 post_fork_num_out_ratio = 33.4/100.0f * (1.0f - RECENT_OUTPUT_RATIO);
8548 }
8549 }
8550 else
8551 {
8552 if (is_shortly_after_segregation_fork)
8553 {
8554 }
8555 else
8556 {
8557 post_fork_num_out_ratio = 67.8/100.0f * (1.0f - RECENT_OUTPUT_RATIO);
8558 }
8559 }
8560 }
8561 num_post_fork_outs = num_outs - segregation_limit[amount].first;
8562 }
8563
8564 if (use_histogram)
8565 {
8566 LOG_PRINT_L1("" << num_outs << " unlocked outputs of size " << print_etn(amount));
8567 THROW_WALLET_EXCEPTION_IF(num_outs == 0, error::wallet_internal_error,
8568 "histogram reports no unlocked outputs for " + boost::lexical_cast<std::string>(amount) + ", not even ours");
8569 THROW_WALLET_EXCEPTION_IF(num_recent_outs > num_outs, error::wallet_internal_error,
8570 "histogram reports more recent outs than outs for " + boost::lexical_cast<std::string>(amount));
8571 }
8572 else
8573 {
8574 // the base offset of the first rct output in the first unlocked block (or the one to be if there's none)
8575 num_outs = rct_offsets[rct_offsets.size() - TX_SPENDABLE_AGE];
8576 LOG_PRINT_L1("" << num_outs << " unlocked rct outputs");
8577 THROW_WALLET_EXCEPTION_IF(num_outs == 0, error::wallet_internal_error,
8578 "histogram reports no unlocked rct outputs, not even ours");
8579 }
8580
8581 // how many fake outs to draw on a pre-fork distribution
8582 size_t pre_fork_outputs_count = requested_outputs_count * pre_fork_num_out_ratio;
8583 size_t post_fork_outputs_count = requested_outputs_count * post_fork_num_out_ratio;
8584 // how many fake outs to draw otherwise
8585 size_t normal_output_count = requested_outputs_count - pre_fork_outputs_count - post_fork_outputs_count;
8586
8587 size_t recent_outputs_count = 0;
8588 if (use_histogram)
8589 {
8590 // X% of those outs are to be taken from recent outputs
8591 recent_outputs_count = normal_output_count * RECENT_OUTPUT_RATIO;
8592 if (recent_outputs_count == 0)
8593 recent_outputs_count = 1; // ensure we have at least one, if possible
8594 if (recent_outputs_count > num_recent_outs)
8595 recent_outputs_count = num_recent_outs;
8596 if (td.m_global_output_index >= num_outs - num_recent_outs && recent_outputs_count > 0)
8597 --recent_outputs_count; // if the real out is recent, pick one less recent fake out
8598 }
8599 LOG_PRINT_L1("Fake output makeup: " << requested_outputs_count << " requested: " << recent_outputs_count << " recent, " <<
8600 pre_fork_outputs_count << " pre-fork, " << post_fork_outputs_count << " post-fork, " <<
8601 (requested_outputs_count - recent_outputs_count - pre_fork_outputs_count - post_fork_outputs_count) << " full-chain");
8602
8603 uint64_t num_found = 0;
8604
8605 // if we have a known ring, use it
8606 if (td.m_key_image_known && !td.m_key_image_partial)
8607 {
8608 std::vector<uint64_t> ring;
8609 if (get_ring(get_ringdb_key(), td.m_key_image, ring))
8610 {
8611 MINFO("This output has a known ring, reusing (size " << ring.size() << ")");
8612 THROW_WALLET_EXCEPTION_IF(ring.size() > fake_outputs_count + 1, error::wallet_internal_error,
8613 "An output in this transaction was previously spent on another chain with ring size " +
8614 std::to_string(ring.size()) + ", it cannot be spent now with ring size " +
8615 std::to_string(fake_outputs_count + 1) + " as it is smaller: use a higher ring size");
8616 bool own_found = false;
8617 for (const auto &out: ring)
8618 {
8619 MINFO("Ring has output " << out);
8620 if (out < num_outs)
8621 {
8622 MINFO("Using it");
8623 req.outputs.push_back({amount, out});
8624 ++num_found;
8625 seen_indices.emplace(out);
8626 if (out == td.m_global_output_index)
8627 {
8628 MINFO("This is the real output");
8629 own_found = true;
8630 }
8631 }
8632 else
8633 {
8634 MINFO("Ignoring output " << out << ", too recent");
8635 }
8636 }
8637 THROW_WALLET_EXCEPTION_IF(!own_found, error::wallet_internal_error,
8638 "Known ring does not include the spent output: " + std::to_string(td.m_global_output_index));
8639 }
8640 }
8641
8642 if (num_outs <= requested_outputs_count)
8643 {
8644 for (uint64_t i = 0; i < num_outs; i++)
8645 req.outputs.push_back({amount, i});
8646 // duplicate to make up shortfall: this will be caught after the RPC call,
8647 // so we can also output the amounts for which we can't reach the required
8648 // mixin after checking the actual unlockedness
8649 for (uint64_t i = num_outs; i < requested_outputs_count; ++i)
8650 req.outputs.push_back({amount, num_outs - 1});
8651 }
8652 else
8653 {
8654 // start with real one
8655 if (num_found == 0)
8656 {
8657 num_found = 1;
8658 seen_indices.emplace(td.m_global_output_index);
8659 req.outputs.push_back({amount, td.m_global_output_index});
8660 LOG_PRINT_L1("Selecting real output: " << td.m_global_output_index << " for " << print_etn(amount));
8661 }
8662
8663 std::unordered_map<const char*, std::set<uint64_t>> picks;
8664
8665 // while we still need more mixins
8666 uint64_t num_usable_outs = num_outs;
8667 bool allow_blackballed = false;
8668 MDEBUG("Starting gamma picking with " << num_outs << ", num_usable_outs " << num_usable_outs
8669 << ", requested_outputs_count " << requested_outputs_count);
8670 while (num_found < requested_outputs_count)
8671 {
8672 // if we've gone through every possible output, we've gotten all we can
8673 if (seen_indices.size() == num_usable_outs)
8674 {
8675 // there is a first pass which rejects blackballed outputs, then a second pass
8676 // which allows them if we don't have enough non blackballed outputs to reach
8677 // the required amount of outputs (since consensus does not care about blackballed
8678 // outputs, we still need to reach the minimum ring size)
8679 if (allow_blackballed)
8680 break;
8681 MINFO("Not enough output not marked as spent, we'll allow outputs marked as spent");
8682 allow_blackballed = true;
8683 num_usable_outs = num_outs;
8684 }
8685
8686 // get a random output index from the DB. If we've already seen it,
8687 // return to the top of the loop and try again, otherwise add it to the
8688 // list of output indices we've seen.
8689
8690 uint64_t i;
8691 const char *type = "";
8692 if (amount == 0 && has_rct_distribution)
8693 {
8694 THROW_WALLET_EXCEPTION_IF(!gamma, error::wallet_internal_error, "No gamma picker");
8695 // gamma distribution
8696 if (num_found -1 < recent_outputs_count + pre_fork_outputs_count)
8697 {
8698 do i = gamma->pick(); while (i >= segregation_limit[amount].first);
8699 type = "pre-fork gamma";
8700 }
8701 else if (num_found -1 < recent_outputs_count + pre_fork_outputs_count + post_fork_outputs_count)
8702 {
8703 do i = gamma->pick(); while (i < segregation_limit[amount].first || i >= num_outs);
8704 type = "post-fork gamma";
8705 }
8706 else
8707 {
8708 do i = gamma->pick(); while (i >= num_outs);
8709 type = "gamma";
8710 }
8711 }
8712 else if (num_found - 1 < recent_outputs_count) // -1 to account for the real one we seeded with
8713 {
8714 // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
8715 uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53);
8716 double frac = std::sqrt((double)r / ((uint64_t)1 << 53));
8717 i = (uint64_t)(frac*num_recent_outs) + num_outs - num_recent_outs;
8718 // just in case rounding up to 1 occurs after calc
8719 if (i == num_outs)
8720 --i;
8721 type = "recent";
8722 }
8723 else if (num_found -1 < recent_outputs_count + pre_fork_outputs_count)
8724 {
8725 // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
8726 uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53);
8727 double frac = std::sqrt((double)r / ((uint64_t)1 << 53));
8728 i = (uint64_t)(frac*segregation_limit[amount].first);
8729 // just in case rounding up to 1 occurs after calc
8730 if (i == num_outs)
8731 --i;
8732 type = " pre-fork";
8733 }
8734 else if (num_found -1 < recent_outputs_count + pre_fork_outputs_count + post_fork_outputs_count)
8735 {
8736 // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
8737 uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53);
8738 double frac = std::sqrt((double)r / ((uint64_t)1 << 53));
8739 i = (uint64_t)(frac*num_post_fork_outs) + segregation_limit[amount].first;
8740 // just in case rounding up to 1 occurs after calc
8741 if (i == num_post_fork_outs+segregation_limit[amount].first)
8742 --i;
8743 type = "post-fork";
8744 }
8745 else
8746 {
8747 // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
8748 uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53);
8749 double frac = std::sqrt((double)r / ((uint64_t)1 << 53));
8750 i = (uint64_t)(frac*num_outs);
8751 // just in case rounding up to 1 occurs after calc
8752 if (i == num_outs)
8753 --i;
8754 type = "triangular";
8755 }
8756
8757 if (seen_indices.count(i))
8758 continue;
8759 if (!allow_blackballed && is_output_blackballed(std::make_pair(amount, i))) // don't add blackballed outputs
8760 {
8761 --num_usable_outs;
8762 continue;
8763 }
8764 seen_indices.emplace(i);
8765
8766 picks[type].insert(i);
8767 req.outputs.push_back({amount, i});
8768 ++num_found;
8769 MDEBUG("picked " << i << ", " << num_found << " now picked");
8770 }
8771
8772 for (const auto &pick: picks)
8773 MDEBUG("picking " << pick.first << " outputs: " <<
8774 boost::join(pick.second | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " "));
8775
8776 // if we had enough unusable outputs, we might fall off here and still
8777 // have too few outputs, so we stuff with one to keep counts good, and
8778 // we'll error out later
8779 while (num_found < requested_outputs_count)
8780 {
8781 req.outputs.push_back({amount, 0});
8782 ++num_found;
8783 }
8784 }
8785
8786 // sort the subsection, to ensure the daemon doesn't know which output is ours
8787 std::sort(req.outputs.begin() + start, req.outputs.end(),
8788 [](const get_outputs_out &a, const get_outputs_out &b) { return a.index < b.index; });
8789 }
8790
8791 if (ELPP->vRegistry()->allowed(el::Level::Debug, ELECTRONEUM_DEFAULT_LOG_CATEGORY))
8792 {
8793 std::map<uint64_t, std::set<uint64_t>> outs;
8794 for (const auto &i: req.outputs)
8795 outs[i.amount].insert(i.index);
8796 for (const auto &o: outs)
8797 MDEBUG("asking for outputs with amount " << print_etn(o.first) << ": " <<
8798 boost::join(o.second | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " "));
8799 }
8800
8801 // get the keys for those
8802 req.get_txid = false;
8803 m_daemon_rpc_mutex.lock();
8804 bool r = invoke_http_bin("/get_outs.bin", req, daemon_resp, rpc_timeout);
8805 m_daemon_rpc_mutex.unlock();
8806 THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin");
8807 THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin");
8808 THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_outs_error, get_rpc_status(daemon_resp.status));
8809 THROW_WALLET_EXCEPTION_IF(daemon_resp.outs.size() != req.outputs.size(), error::wallet_internal_error,
8810 "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
8811 std::to_string(daemon_resp.outs.size()) + ", expected " + std::to_string(req.outputs.size()));
8812
8813 std::unordered_map<uint64_t, uint64_t> scanty_outs;
8814 size_t base = 0;
8815 outs.reserve(num_selected_transfers);
8816 for(size_t idx: selected_transfers)
8817 {
8818 const transfer_details &td = m_transfers[idx];
8819 size_t requested_outputs_count = base_requested_outputs_count + (td.is_rct() ? MINED_ETN_SPENDABLE_AGE - TX_SPENDABLE_AGE : 0);
8820 outs.push_back(std::vector<get_outs_entry>());
8821 outs.back().reserve(fake_outputs_count + 1);
8822 const rct::key mask = td.is_rct() ? rct::commit(td.amount(), td.m_mask) : rct::zeroCommit(td.amount());
8823
8824 uint64_t num_outs = 0;
8825 const uint64_t amount = td.is_rct() ? 0 : td.amount();
8826 const bool output_is_pre_fork = td.m_block_height < segregation_fork_height;
8827 if (is_after_segregation_fork && m_segregate_pre_fork_outputs && output_is_pre_fork)
8828 num_outs = segregation_limit[amount].first;
8829 else for (const auto &he: resp_t.histogram)
8830 {
8831 if (he.amount == amount)
8832 {
8833 num_outs = he.unlocked_instances;
8834 break;
8835 }
8836 }
8837 bool use_histogram = amount != 0 || !has_rct_distribution;
8838 if (!use_histogram)
8839 num_outs = rct_offsets[rct_offsets.size() - TX_SPENDABLE_AGE];
8840
8841 // make sure the real outputs we asked for are really included, along
8842 // with the correct key and mask: this guards against an active attack
8843 // where the node sends dummy data for all outputs, and we then send
8844 // the real one, which the node can then tell from the fake outputs,
8845 // as it has different data than the dummy data it had sent earlier
8846 bool real_out_found = false;
8847 for (size_t n = 0; n < requested_outputs_count; ++n)
8848 {
8849 size_t i = base + n;
8850 if (req.outputs[i].index == td.m_global_output_index)
8851 if (daemon_resp.outs[i].key == boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key)
8852 if (daemon_resp.outs[i].mask == mask)
8853 real_out_found = true;
8854 }
8855 THROW_WALLET_EXCEPTION_IF(!real_out_found, error::wallet_internal_error,
8856 "Daemon response did not include the requested real output");
8857
8858 // pick real out first (it will be sorted when done)
8859 outs.back().push_back(std::make_tuple(td.m_global_output_index, boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key, mask));
8860
8861 // then pick outs from an existing ring, if any
8862 if (td.m_key_image_known && !td.m_key_image_partial)
8863 {
8864 std::vector<uint64_t> ring;
8865 if (get_ring(get_ringdb_key(), td.m_key_image, ring))
8866 {
8867 for (uint64_t out: ring)
8868 {
8869 if (out < num_outs)
8870 {
8871 if (out != td.m_global_output_index)
8872 {
8873 bool found = false;
8874 for (size_t o = 0; o < requested_outputs_count; ++o)
8875 {
8876 size_t i = base + o;
8877 if (req.outputs[i].index == out)
8878 {
8879 LOG_PRINT_L2("Index " << i << "/" << requested_outputs_count << ": idx " << req.outputs[i].index << " (real " << td.m_global_output_index << "), unlocked " << daemon_resp.outs[i].unlocked << ", key " << daemon_resp.outs[i].key << " (from existing ring)");
8880 tx_add_fake_output(outs, req.outputs[i].index, daemon_resp.outs[i].key, daemon_resp.outs[i].mask, td.m_global_output_index, daemon_resp.outs[i].unlocked);
8881 found = true;
8882 break;
8883 }
8884 }
8885 THROW_WALLET_EXCEPTION_IF(!found, error::wallet_internal_error, "Falied to find existing ring output in daemon out data");
8886 }
8887 }
8888 }
8889 }
8890 }
8891
8892 // then pick others in random order till we reach the required number
8893 // since we use an equiprobable pick here, we don't upset the triangular distribution
8894 std::vector<size_t> order;
8895 order.resize(requested_outputs_count);
8896 for (size_t n = 0; n < order.size(); ++n)
8897 order[n] = n;
8898 std::shuffle(order.begin(), order.end(), std::default_random_engine(crypto::rand<unsigned>()));
8899
8900 LOG_PRINT_L2("Looking for " << (fake_outputs_count+1) << " outputs of size " << print_etn(td.is_rct() ? 0 : td.amount()));
8901 for (size_t o = 0; o < requested_outputs_count && outs.back().size() < fake_outputs_count + 1; ++o)
8902 {
8903 size_t i = base + order[o];
8904 LOG_PRINT_L2("Index " << i << "/" << requested_outputs_count << ": idx " << req.outputs[i].index << " (real " << td.m_global_output_index << "), unlocked " << daemon_resp.outs[i].unlocked << ", key " << daemon_resp.outs[i].key);
8905 tx_add_fake_output(outs, req.outputs[i].index, daemon_resp.outs[i].key, daemon_resp.outs[i].mask, td.m_global_output_index, daemon_resp.outs[i].unlocked);
8906 }
8907 if (outs.back().size() < fake_outputs_count + 1)
8908 {
8909 scanty_outs[td.is_rct() ? 0 : td.amount()] = outs.back().size();
8910 }
8911 else
8912 {
8913 // sort the subsection, so any spares are reset in order
8914 std::sort(outs.back().begin(), outs.back().end(), [](const get_outs_entry &a, const get_outs_entry &b) { return std::get<0>(a) < std::get<0>(b); });
8915 }
8916 base += requested_outputs_count;
8917 }
8918 THROW_WALLET_EXCEPTION_IF(!scanty_outs.empty(), error::not_enough_outs_to_mix, scanty_outs, fake_outputs_count);
8919 }
8920 else //no fake outs => start reading here
8921 {
8922 if(tx_version < 3) {
8923 for (size_t idx: selected_transfers) {
8924 const transfer_details &td = m_transfers[idx];
8925 std::vector<get_outs_entry> v;
8926 const rct::key mask = td.is_rct() ? rct::commit(td.amount(), td.m_mask) : rct::zeroCommit(td.amount());
8927 v.push_back(std::make_tuple(td.m_global_output_index, td.get_public_key(),
8928 mask)); // get pub key is where our error is (wrong get)
8929 outs.push_back(v);
8930 }
8931 }
8932 }
8933
8934 if(tx_version < 3) {
8935 // save those outs in the ringdb for reuse
8936 for (size_t i = 0; i < selected_transfers.size(); ++i) {
8937 const size_t idx = selected_transfers[i];
8938 THROW_WALLET_EXCEPTION_IF(idx >= m_transfers.size(), error::wallet_internal_error,
8939 "selected_transfers entry out of range");
8940 const transfer_details &td = m_transfers[idx];
8941 std::vector<uint64_t> ring;
8942 ring.reserve(outs[i].size());
8943 for (const auto &e: outs[i])
8944 ring.push_back(std::get<0>(e));
8945 if (!set_ring(td.m_key_image, ring, false))//
8946 MERROR("Failed to set ring for " << td.m_key_image);
8947 }
8948 }
8949}
8950
8951template<typename T>
8952void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
8953 std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
8954 uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx)
8955{
8956 using namespace cryptonote;
8957 // throw if attempting a transaction with no destinations
8959
8960 THROW_WALLET_EXCEPTION_IF(m_multisig, error::wallet_internal_error, "Multisig wallets cannot spend non rct outputs");
8961
8962 uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit();
8963 uint64_t needed_etn = fee;
8964 LOG_PRINT_L2("transfer: starting with fee " << print_etn (needed_etn));
8965
8966 // calculate total amount being sent to all destinations
8967 // throw if total amount overflows uint64_t
8968 for(auto& dt: dsts)
8969 {
8971 needed_etn += dt.amount;
8972 LOG_PRINT_L2("transfer: adding " << print_etn(dt.amount) << ", for a total of " << print_etn (needed_etn));
8973 THROW_WALLET_EXCEPTION_IF(needed_etn < dt.amount, error::tx_sum_overflow, dsts, fee, m_nettype);
8974 }
8975
8976 uint64_t found_etn = 0;
8977 for(size_t idx: selected_transfers)
8978 {
8979 found_etn += m_transfers[idx].amount();
8980 }
8981
8982 LOG_PRINT_L2("wanted " << print_etn(needed_etn) << ", found " << print_etn(found_etn) << ", fee " << print_etn(fee));
8983 THROW_WALLET_EXCEPTION_IF(found_etn < needed_etn, error::not_enough_unlocked_etn, found_etn, needed_etn - fee, fee);
8984
8985 uint32_t subaddr_account = m_transfers[*selected_transfers.begin()].m_subaddr_index.major;
8986 for (auto i = ++selected_transfers.begin(); i != selected_transfers.end(); ++i)
8987 THROW_WALLET_EXCEPTION_IF(subaddr_account != m_transfers[*i].m_subaddr_index.major, error::wallet_internal_error, "the tx uses funds from multiple accounts");
8988
8989 if (outs.empty())
8990 get_outs(outs, selected_transfers, fake_outputs_count, tx.version); // may throw
8991
8992 //prepare inputs
8993 LOG_PRINT_L2("preparing outputs");
8994 typedef cryptonote::tx_source_entry::output_entry tx_output_entry;
8995 size_t i = 0, out_index = 0;
8996 std::vector<cryptonote::tx_source_entry> sources;
8997 for(size_t idx: selected_transfers)
8998 {
8999 sources.resize(sources.size()+1);
9000 cryptonote::tx_source_entry& src = sources.back();
9001 const transfer_details& td = m_transfers[idx];
9002 src.amount = td.amount();
9003 src.rct = td.is_rct();
9004 if(tx.version < 3) {
9005 //paste keys (fake and real)
9006 // adding pairs of global index & stealth address to our vector of source outs (needed forold ins only)
9007 for (size_t n = 0; n < fake_outputs_count + 1; ++n) {
9008 tx_output_entry oe;
9009 oe.first = std::get<0>(outs[out_index][n]);
9010 oe.second.dest = rct::pk2rct(std::get<1>(outs[out_index][n]));
9011 oe.second.mask = std::get<2>(outs[out_index][n]);
9012
9013 src.outputs.push_back(oe);
9014 ++i;
9015 }
9016
9017 //paste real transaction to the random index
9018 auto it_to_replace = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const tx_output_entry &a) {
9019 return a.first == td.m_global_output_index;
9020 });
9022 "real output not found");
9023
9024 tx_output_entry real_oe;
9025 real_oe.first = td.m_global_output_index;
9026 real_oe.second.dest = rct::pk2rct(
9027 boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key);
9028 real_oe.second.mask = rct::commit(td.amount(), td.m_mask);
9029 *it_to_replace = real_oe;
9032 src.real_output = it_to_replace - src.outputs.begin();
9034 }
9035 src.real_output_in_tx_index = td.m_internal_output_index; // these two are all we really need for v3 sources
9036 src.tx_hash = td.m_txid;
9039 ++out_index;
9040 }
9041 LOG_PRINT_L2("outputs prepared");
9042
9043 cryptonote::tx_destination_entry change_dts = AUTO_VAL_INIT(change_dts);
9044 if (needed_etn < found_etn)
9045 {
9046 // send change to the first input's address for v3+ tx
9047 uint32_t change_subaddress_minor = tx.version > 2 ? sources.front().subaddr_index.minor : 0;
9048 change_dts.addr = get_subaddress({subaddr_account, change_subaddress_minor});
9049 change_dts.is_subaddress = (subaddr_account != 0 || change_subaddress_minor != 0);
9050 change_dts.amount = found_etn - needed_etn;
9051 }
9052
9053 std::vector<cryptonote::tx_destination_entry> splitted_dsts, dust_dsts;
9054 uint64_t dust = 0;
9055 destination_split_strategy(dsts, change_dts, dust_policy.dust_threshold, splitted_dsts, dust_dsts);
9056 for(auto& d: dust_dsts) {
9057 THROW_WALLET_EXCEPTION_IF(dust_policy.dust_threshold < d.amount, error::wallet_internal_error, "invalid dust value: dust = " +
9058 std::to_string(d.amount) + ", dust_threshold = " + std::to_string(dust_policy.dust_threshold));
9059 }
9060 for(auto& d: dust_dsts) {
9061 if (!dust_policy.add_to_fee)
9062 splitted_dsts.push_back(cryptonote::tx_destination_entry(d.amount, dust_policy.addr_for_dust, d.is_subaddress));
9063 dust += d.amount;
9064 }
9065
9066 crypto::secret_key tx_key;
9067 std::vector<crypto::secret_key> additional_tx_keys;
9068 rct::multisig_out msout;
9069
9070 LOG_PRINT_L2("constructing tx");
9071 bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, {}, m_multisig ? &msout : NULL, m_account_major_offset, this->m_nettype);
9072 LOG_PRINT_L2("constructed tx, r="<<r);
9073 THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype);
9074 THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit);
9075
9076 std::string key_images;
9077 bool are_ins_correct_type = tx.version >= 3 ?
9078 std::all_of(tx.vin.begin(), tx.vin.end(), [&](const txin_v& s_e) -> bool
9079 {
9080 CHECKED_GET_SPECIFIC_VARIANT(s_e, const txin_to_key_public, in, false);
9081 return true;
9082 })
9083 :
9084 std::all_of(tx.vin.begin(), tx.vin.end(), [&](const txin_v& s_e) -> bool
9085 {
9086 CHECKED_GET_SPECIFIC_VARIANT(s_e, const txin_to_key, in, false);
9087 key_images += boost::to_string(in.k_image) + " ";
9088 return true;
9089 });
9090
9091 THROW_WALLET_EXCEPTION_IF(!are_ins_correct_type, error::unexpected_txin_type, tx);
9092
9093
9094 bool dust_sent_elsewhere = (dust_policy.addr_for_dust.m_view_public_key != change_dts.addr.m_view_public_key
9095 || dust_policy.addr_for_dust.m_spend_public_key != change_dts.addr.m_spend_public_key);
9096
9097 if (dust_policy.add_to_fee || dust_sent_elsewhere) change_dts.amount -= dust;
9098
9099 ptx.key_images = key_images;
9100 ptx.fee = (dust_policy.add_to_fee ? fee+dust : fee);
9101 ptx.dust = ((dust_policy.add_to_fee || dust_sent_elsewhere) ? dust : 0);
9102 ptx.dust_added_to_fee = dust_policy.add_to_fee;
9103 ptx.tx = tx;
9104 ptx.change_dts = change_dts;
9105 ptx.selected_transfers = selected_transfers;
9106 ptx.tx_key = tx_key;
9107 ptx.additional_tx_keys = additional_tx_keys;
9108 ptx.dests = dsts;
9109 ptx.construction_data.sources = sources;
9110 ptx.construction_data.change_dts = change_dts;
9111 ptx.construction_data.splitted_dsts = splitted_dsts;
9112 ptx.construction_data.selected_transfers = selected_transfers;
9113 ptx.construction_data.extra = tx.extra;
9114 ptx.construction_data.unlock_time = unlock_time;
9115 ptx.construction_data.use_rct = false;
9117 ptx.construction_data.dests = dsts;
9118 // record which subaddress indices are being used as inputs
9119 ptx.construction_data.subaddr_account = subaddr_account;
9121 for (size_t idx: selected_transfers)
9122 ptx.construction_data.subaddr_indices.insert(m_transfers[idx].m_subaddr_index.minor);
9123 LOG_PRINT_L2("transfer_selected done");
9124}
9125
9126std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_etn, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices) const
9127{
9128 std::vector<size_t> picks;
9129 float current_output_relatdness = 1.0f;
9130
9131 LOG_PRINT_L2("pick_preferred_rct_inputs: needed_etn " << print_etn(needed_etn));
9132
9133 // try to find a rct input of enough size
9134 for (size_t i = 0; i < m_transfers.size(); ++i)
9135 {
9136 const transfer_details& td = m_transfers[i];
9137 if (!td.m_spent && !td.m_frozen && td.is_rct() && td.amount() >= needed_etn && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
9138 {
9139 LOG_PRINT_L2("We can use " << i << " alone: " << print_etn(td.amount()));
9140 picks.push_back(i);
9141 return picks;
9142 }
9143 }
9144
9145 // then try to find two outputs
9146 // this could be made better by picking one of the outputs to be a small one, since those
9147 // are less useful since often below the needed etn, so if one can be used in a pair,
9148 // it gets rid of it for the future
9149 for (size_t i = 0; i < m_transfers.size(); ++i)
9150 {
9151 const transfer_details& td = m_transfers[i];
9152 if (!td.m_spent && !td.m_frozen && !td.m_key_image_partial && td.is_rct() && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
9153 {
9154 LOG_PRINT_L2("Considering input " << i << ", " << print_etn(td.amount()));
9155 for (size_t j = i + 1; j < m_transfers.size(); ++j)
9156 {
9157 const transfer_details& td2 = m_transfers[j];
9158 if (!td2.m_spent && !td2.m_frozen && !td.m_key_image_partial && td2.is_rct() && td.amount() + td2.amount() >= needed_etn && is_transfer_unlocked(td2) && td2.m_subaddr_index == td.m_subaddr_index)
9159 {
9160 // update our picks if those outputs are less related than any we
9161 // already found. If the same, don't update, and oldest suitable outputs
9162 // will be used in preference.
9163 float relatedness = get_output_relatedness(td, td2);
9164 LOG_PRINT_L2(" with input " << j << ", " << print_etn(td2.amount()) << ", relatedness " << relatedness);
9165 if (relatedness < current_output_relatdness)
9166 {
9167 // reset the current picks with those, and return them directly
9168 // if they're unrelated. If they are related, we'll end up returning
9169 // them if we find nothing better
9170 picks.clear();
9171 picks.push_back(i);
9172 picks.push_back(j);
9173 LOG_PRINT_L0("we could use " << i << " and " << j);
9174 if (relatedness == 0.0f)
9175 return picks;
9176 current_output_relatdness = relatedness;
9177 }
9178 }
9179 }
9180 }
9181 }
9182
9183 return picks;
9184}
9185
9186bool wallet2::should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const
9187{
9188 if (!use_rct)
9189 return false;
9190 if (n_transfers > 1)
9191 return false;
9192 if (unused_dust_indices.empty() && unused_transfers_indices.empty())
9193 return false;
9194 // we want at least one free rct output to avoid a corner case where
9195 // we'd choose a non rct output which doesn't have enough "siblings"
9196 // value-wise on the chain, and thus can't be mixed
9197 bool found = false;
9198 for (auto i: unused_dust_indices)
9199 {
9200 if (m_transfers[i].is_rct())
9201 {
9202 found = true;
9203 break;
9204 }
9205 }
9206 if (!found) for (auto i: unused_transfers_indices)
9207 {
9208 if (m_transfers[i].is_rct())
9209 {
9210 found = true;
9211 break;
9212 }
9213 }
9214 if (!found)
9215 return false;
9216 return true;
9217}
9218
9219std::vector<size_t> wallet2::get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const
9220{
9221 std::vector<size_t> indices;
9222 for (size_t n: unused_dust_indices)
9223 if (m_transfers[n].is_rct())
9224 indices.push_back(n);
9225 for (size_t n: unused_transfers_indices)
9226 if (m_transfers[n].is_rct())
9227 indices.push_back(n);
9228 return indices;
9229}
9230
9231static uint32_t get_count_above(const std::vector<wallet2::transfer_details> &transfers, const std::vector<size_t> &indices, uint64_t threshold)
9232{
9233 uint32_t count = 0;
9234 for (size_t idx: indices)
9235 if (transfers[idx].amount() >= threshold)
9236 ++count;
9237 return count;
9238}
9239
9240bool wallet2::light_wallet_login(bool &new_address)
9241{
9242 MDEBUG("Light wallet login request");
9243 m_light_wallet_connected = false;
9246 request.address = get_account().get_public_address_str(m_nettype);
9247 request.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
9248 // Always create account if it doesn't exist.
9249 request.create_account = true;
9250 m_daemon_rpc_mutex.lock();
9251 bool connected = invoke_http_json("/login", request, response, rpc_timeout, "POST");
9252 m_daemon_rpc_mutex.unlock();
9253 // MyMonero doesn't send any status message. OpenMonero does.
9254 m_light_wallet_connected = connected && (response.status.empty() || response.status == "success");
9255 new_address = response.new_address;
9256 MDEBUG("Status: " << response.status);
9257 MDEBUG("Reason: " << response.reason);
9258 MDEBUG("New wallet: " << response.new_address);
9259 if(m_light_wallet_connected)
9260 {
9261 // Clear old data on successful login.
9262 // m_transfers.clear();
9263 // m_payments.clear();
9264 // m_unconfirmed_payments.clear();
9265 }
9266 return m_light_wallet_connected;
9267}
9268
9270{
9271 MDEBUG("Light wallet import wallet request");
9273 oreq.address = get_account().get_public_address_str(m_nettype);
9274 oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
9275 m_daemon_rpc_mutex.lock();
9276 bool r = invoke_http_json("/import_wallet_request", oreq, response, rpc_timeout, "POST");
9277 m_daemon_rpc_mutex.unlock();
9278 THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "import_wallet_request");
9279
9280
9281 return true;
9282}
9283
9285{
9286 MDEBUG("Getting unspent outs");
9287
9290
9291 oreq.amount = "0";
9292 oreq.address = get_account().get_public_address_str(m_nettype);
9293 oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
9294 // openMonero specific
9295 oreq.dust_threshold = boost::lexical_cast<std::string>(::config::DEFAULT_DUST_THRESHOLD);
9296 // below are required by openMonero api - but are not used.
9297 oreq.mixin = 0;
9298 oreq.use_dust = true;
9299
9300
9301 m_daemon_rpc_mutex.lock();
9302 bool r = invoke_http_json("/get_unspent_outs", oreq, ores, rpc_timeout, "POST");
9303 m_daemon_rpc_mutex.unlock();
9305 THROW_WALLET_EXCEPTION_IF(ores.status == "error", error::wallet_internal_error, ores.reason);
9306
9307 m_light_wallet_per_kb_fee = ores.per_kb_fee;
9308
9309 std::unordered_map<crypto::hash,bool> transfers_txs;
9310 for(const auto &t: m_transfers)
9311 transfers_txs.emplace(t.m_txid,t.m_spent);
9312
9313 MDEBUG("FOUND " << ores.outputs.size() <<" outputs");
9314
9315 // return if no outputs found
9316 if(ores.outputs.empty())
9317 return;
9318
9319 // Clear old outputs
9320 m_transfers.clear();
9321
9322 for (const auto &o: ores.outputs) {
9323 bool spent = false;
9324 bool add_transfer = true;
9325 crypto::key_image unspent_key_image;
9326 crypto::public_key tx_public_key = AUTO_VAL_INIT(tx_public_key);
9327 THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, o.tx_pub_key), error::wallet_internal_error, "Invalid tx_pub_key field");
9328 string_tools::hex_to_pod(o.tx_pub_key, tx_public_key);
9329
9330 for (const std::string &ski: o.spend_key_images) {
9331 spent = false;
9332
9333 // Check if key image is ours
9335 string_tools::hex_to_pod(ski, unspent_key_image);
9336 if(light_wallet_key_image_is_ours(unspent_key_image, tx_public_key, o.index)){
9337 MTRACE("Output " << o.public_key << " is spent. Key image: " << ski);
9338 spent = true;
9339 break;
9340 } {
9341 MTRACE("Unspent output found. " << o.public_key);
9342 }
9343 }
9344
9345 // Check if tx already exists in m_transfers.
9346 crypto::hash txid;
9347 crypto::public_key tx_pub_key;
9350 THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, o.public_key), error::wallet_internal_error, "Invalid public_key field");
9351 THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, o.tx_pub_key), error::wallet_internal_error, "Invalid tx_pub_key field");
9352 string_tools::hex_to_pod(o.tx_hash, txid);
9353 string_tools::hex_to_pod(o.public_key, public_key);
9354 string_tools::hex_to_pod(o.tx_pub_key, tx_pub_key);
9355
9356 for(auto &t: m_transfers){
9357 if(t.get_public_key() == public_key) {
9358 t.m_spent = spent;
9359 add_transfer = false;
9360 break;
9361 }
9362 }
9363
9364 if(!add_transfer)
9365 continue;
9366
9367 m_transfers.push_back(boost::value_initialized<transfer_details>());
9368 transfer_details& td = m_transfers.back();
9369
9370 td.m_block_height = o.height;
9371 td.m_global_output_index = o.global_index;
9372 td.m_txid = txid;
9373
9374 // Add to extra
9375 add_tx_pub_key_to_extra(td.m_tx, tx_pub_key);
9376
9377 td.m_key_image = unspent_key_image;
9378 td.m_key_image_known = !m_watch_only && !m_multisig;
9379 td.m_key_image_request = false;
9380 td.m_key_image_partial = m_multisig;
9381 td.m_amount = o.amount;
9382 td.m_pk_index = 0;
9383 td.m_internal_output_index = o.index;
9384 td.m_spent = spent;
9385 td.m_frozen = false;
9386
9387 tx_out txout;
9389 txout.amount = td.m_amount;
9390
9391 td.m_tx.vout.resize(td.m_internal_output_index + 1);
9392 td.m_tx.vout[td.m_internal_output_index] = txout;
9393
9394 // Add unlock time and coinbase bool got from get_address_txs api call
9395 std::unordered_map<crypto::hash,address_tx>::const_iterator found = m_light_wallet_address_txs.find(txid);
9396 THROW_WALLET_EXCEPTION_IF(found == m_light_wallet_address_txs.end(), error::wallet_internal_error, "Lightwallet: tx not found in m_light_wallet_address_txs");
9397 bool miner_tx = found->second.m_coinbase;
9398 td.m_tx.unlock_time = found->second.m_unlock_time;
9399
9400 if (!o.rct.empty())
9401 {
9402 // Coinbase tx's
9403 if(miner_tx)
9404 {
9405 td.m_mask = rct::identity();
9406 }
9407 else
9408 {
9409 // rct txs
9410 // decrypt rct mask, calculate commit hash and compare against blockchain commit hash
9411 rct::key rct_commit;
9412 light_wallet_parse_rct_str(o.rct, tx_pub_key, td.m_internal_output_index, td.m_mask, rct_commit, true);
9413 bool valid_commit = (rct_commit == rct::commit(td.amount(), td.m_mask));
9414 if(!valid_commit)
9415 {
9416 MDEBUG("output index: " << o.global_index);
9417 MDEBUG("mask: " + string_tools::pod_to_hex(td.m_mask));
9418 MDEBUG("calculated commit: " + string_tools::pod_to_hex(rct::commit(td.amount(), td.m_mask)));
9419 MDEBUG("expected commit: " + string_tools::pod_to_hex(rct_commit));
9420 MDEBUG("amount: " << td.amount());
9421 }
9422 THROW_WALLET_EXCEPTION_IF(!valid_commit, error::wallet_internal_error, "Lightwallet: rct commit hash mismatch!");
9423 }
9424 td.m_rct = true;
9425 }
9426 else
9427 {
9428 td.m_mask = rct::identity();
9429 td.m_rct = false;
9430 }
9431 if(!spent)
9432 set_unspent(m_transfers.size()-1);
9433 m_key_images[td.m_key_image] = m_transfers.size()-1;
9434 m_pub_keys[td.get_public_key()] = m_transfers.size()-1;
9435 }
9436}
9437
9439{
9440 MTRACE(__FUNCTION__);
9441
9443
9444 request.address = get_account().get_public_address_str(m_nettype);
9445 request.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
9446 m_daemon_rpc_mutex.lock();
9447 bool r = invoke_http_json("/get_address_info", request, response, rpc_timeout, "POST");
9448 m_daemon_rpc_mutex.unlock();
9450 // TODO: Validate result
9451 return true;
9452}
9453
9455{
9456 MDEBUG("Refreshing light wallet");
9457
9460
9461 ireq.address = get_account().get_public_address_str(m_nettype);
9462 ireq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
9463 m_daemon_rpc_mutex.lock();
9464 bool r = invoke_http_json("/get_address_txs", ireq, ires, rpc_timeout, "POST");
9465 m_daemon_rpc_mutex.unlock();
9467 //OpenMonero sends status=success, Mymonero doesn't.
9468 THROW_WALLET_EXCEPTION_IF((!ires.status.empty() && ires.status != "success"), error::no_connection_to_daemon, "get_address_txs");
9469
9470
9471 // Abort if no transactions
9472 if(ires.transactions.empty())
9473 return;
9474
9475 // Create searchable vectors
9476 std::vector<crypto::hash> payments_txs;
9477 for(const auto &p: m_payments)
9478 payments_txs.push_back(p.second.m_tx_hash);
9479 std::vector<crypto::hash> unconfirmed_payments_txs;
9480 for(const auto &up: m_unconfirmed_payments)
9481 unconfirmed_payments_txs.push_back(up.second.m_pd.m_tx_hash);
9482
9483 // for balance calculation
9484 uint64_t wallet_total_sent = 0;
9485 // txs in pool
9486 std::vector<crypto::hash> pool_txs;
9487
9488 for (const auto &t: ires.transactions) {
9489 const uint64_t total_received = t.total_received;
9490 uint64_t total_sent = t.total_sent;
9491
9492 // Check key images - subtract fake outputs from total_sent
9493 for(const auto &so: t.spent_outputs)
9494 {
9495 crypto::public_key tx_public_key;
9497 THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, so.tx_pub_key), error::wallet_internal_error, "Invalid tx_pub_key field");
9498 THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, so.key_image), error::wallet_internal_error, "Invalid key_image field");
9499 string_tools::hex_to_pod(so.tx_pub_key, tx_public_key);
9500 string_tools::hex_to_pod(so.key_image, key_image);
9501
9502 if(!light_wallet_key_image_is_ours(key_image, tx_public_key, so.out_index)) {
9503 THROW_WALLET_EXCEPTION_IF(so.amount > t.total_sent, error::wallet_internal_error, "Lightwallet: total sent is negative!");
9504 total_sent -= so.amount;
9505 }
9506 }
9507
9508 // Do not add tx if empty.
9509 if(total_sent == 0 && total_received == 0)
9510 continue;
9511
9512 crypto::hash payment_id = null_hash;
9513 crypto::hash tx_hash;
9514
9515 THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, t.payment_id), error::wallet_internal_error, "Invalid payment_id field");
9517 string_tools::hex_to_pod(t.payment_id, payment_id);
9518 string_tools::hex_to_pod(t.hash, tx_hash);
9519
9520 // lightwallet specific info
9521 bool incoming = (total_received > total_sent);
9523 address_tx.m_tx_hash = tx_hash;
9524 address_tx.m_incoming = incoming;
9525 address_tx.m_amount = incoming ? total_received - total_sent : total_sent - total_received;
9526 address_tx.m_fee = 0; // TODO
9527 address_tx.m_block_height = t.height;
9528 address_tx.m_unlock_time = t.unlock_time;
9529 address_tx.m_timestamp = t.timestamp;
9530 address_tx.m_coinbase = t.coinbase;
9531 address_tx.m_mempool = t.mempool;
9532 m_light_wallet_address_txs.emplace(tx_hash,address_tx);
9533
9534 // populate data needed for history (m_payments, m_unconfirmed_payments, m_confirmed_txs)
9535 // INCOMING transfers
9536 if(total_received > total_sent) {
9537 payment_details payment;
9538 payment.m_tx_hash = tx_hash;
9539 payment.m_amount = total_received - total_sent;
9540 payment.m_fee = 0; // TODO
9541 payment.m_block_height = t.height;
9542 payment.m_unlock_time = t.unlock_time;
9543 payment.m_timestamp = t.timestamp;
9544 payment.m_coinbase = t.coinbase;
9545
9546 if (t.mempool) {
9547 if (std::find(unconfirmed_payments_txs.begin(), unconfirmed_payments_txs.end(), tx_hash) == unconfirmed_payments_txs.end()) {
9548 pool_txs.push_back(tx_hash);
9549 // assume false as we don't get that info from the light wallet server
9550 crypto::hash payment_id;
9552 error::wallet_internal_error, "Failed to parse payment id");
9553 emplace_or_replace(m_unconfirmed_payments, payment_id, pool_payment_details{payment, false, false});
9554 if (0 != m_callback) {
9555 m_callback->on_lw_unconfirmed_etn_received(t.height, payment.m_tx_hash, payment.m_amount);
9556 }
9557 }
9558 } else {
9559 if (std::find(payments_txs.begin(), payments_txs.end(), tx_hash) == payments_txs.end()) {
9560 m_payments.emplace(tx_hash, payment);
9561 if (0 != m_callback) {
9562 m_callback->on_lw_etn_received(t.height, payment.m_tx_hash, payment.m_amount);
9563 }
9564 }
9565 }
9566 // Outgoing transfers
9567 } else {
9568 uint64_t amount_sent = total_sent - total_received;
9569 cryptonote::transaction dummy_tx; // not used by light wallet
9570 // increase wallet total sent
9571 wallet_total_sent += total_sent;
9572 if (t.mempool)
9573 {
9574 // Handled by add_unconfirmed_tx in commit_tx
9575 // If sent from another wallet instance we need to add it
9576 if(m_unconfirmed_txs.find(tx_hash) == m_unconfirmed_txs.end())
9577 {
9579 utd.m_amount_in = amount_sent;
9580 utd.m_amount_out = amount_sent;
9581 utd.m_change = 0;
9582 utd.m_payment_id = payment_id;
9583 utd.m_timestamp = t.timestamp;
9585 m_unconfirmed_txs.emplace(tx_hash,utd);
9586 }
9587 }
9588 else
9589 {
9590 // Only add if new
9591 auto confirmed_tx = m_confirmed_txs.find(tx_hash);
9592 if(confirmed_tx == m_confirmed_txs.end()) {
9593 // tx is added to m_unconfirmed_txs - move to confirmed
9594 if(m_unconfirmed_txs.find(tx_hash) != m_unconfirmed_txs.end())
9595 {
9596 process_unconfirmed(tx_hash, dummy_tx, t.height);
9597 }
9598 // Tx sent by another wallet instance
9599 else
9600 {
9602 ctd.m_amount_in = amount_sent;
9603 ctd.m_amount_out = amount_sent;
9604 ctd.m_change = 0;
9605 ctd.m_payment_id = payment_id;
9606 ctd.m_block_height = t.height;
9607 ctd.m_timestamp = t.timestamp;
9608 m_confirmed_txs.emplace(tx_hash,ctd);
9609 }
9610 if (0 != m_callback)
9611 {
9612 m_callback->on_lw_etn_spent(t.height, tx_hash, amount_sent);
9613 }
9614 }
9615 // If not new - check the amount and update if necessary.
9616 // when sending a tx to same wallet the receiving amount has to be credited
9617 else
9618 {
9619 if(confirmed_tx->second.m_amount_in != amount_sent || confirmed_tx->second.m_amount_out != amount_sent)
9620 {
9621 MDEBUG("Adjusting amount sent/received for tx: <" + t.hash + ">. Is tx sent to own wallet? " << print_etn(amount_sent) << " != " << print_etn(confirmed_tx->second.m_amount_in));
9622 confirmed_tx->second.m_amount_in = amount_sent;
9623 confirmed_tx->second.m_amount_out = amount_sent;
9624 confirmed_tx->second.m_change = 0;
9625 }
9626 }
9627 }
9628 }
9629 }
9630 // TODO: purge old unconfirmed_txs
9631 remove_obsolete_pool_txs(pool_txs);
9632
9633 // Calculate wallet balance
9634 m_light_wallet_balance = ires.total_received-wallet_total_sent;
9635 // MyMonero doesn't send unlocked balance
9636 if(ires.total_received_unlocked > 0)
9637 m_light_wallet_unlocked_balance = ires.total_received_unlocked-wallet_total_sent;
9638 else
9639 m_light_wallet_unlocked_balance = m_light_wallet_balance;
9640}
9641
9642bool wallet2::light_wallet_parse_rct_str(const std::string& rct_string, const crypto::public_key& tx_pub_key, uint64_t internal_output_index, rct::key& decrypted_mask, rct::key& rct_commit, bool decrypt) const
9643{
9644 // rct string is empty if output is non RCT
9645 if (rct_string.empty())
9646 return false;
9647 // rct_string is a string with length 64+64+64 (<rct commit> + <encrypted mask> + <rct amount>)
9648 rct::key encrypted_mask;
9649 std::string rct_commit_str = rct_string.substr(0,64);
9650 std::string encrypted_mask_str = rct_string.substr(64,64);
9651 THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, rct_commit_str), error::wallet_internal_error, "Invalid rct commit hash: " + rct_commit_str);
9652 THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, encrypted_mask_str), error::wallet_internal_error, "Invalid rct mask: " + encrypted_mask_str);
9653 string_tools::hex_to_pod(rct_commit_str, rct_commit);
9654 string_tools::hex_to_pod(encrypted_mask_str, encrypted_mask);
9655 if (decrypt) {
9656 // Decrypt the mask
9657 crypto::key_derivation derivation;
9658 bool r = generate_key_derivation(tx_pub_key, get_account().get_keys().m_view_secret_key, derivation);
9659 THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation");
9660 crypto::secret_key scalar;
9661 crypto::derivation_to_scalar(derivation, internal_output_index, scalar);
9662 sc_sub(decrypted_mask.bytes,encrypted_mask.bytes,rct::hash_to_scalar(rct::sk2rct(scalar)).bytes);
9663 }
9664 return true;
9665}
9666
9668{
9669 // Lookup key image from cache
9670 std::map<uint64_t, crypto::key_image> index_keyimage_map;
9671 std::unordered_map<crypto::public_key, std::map<uint64_t, crypto::key_image> >::const_iterator found_pub_key = m_key_image_cache.find(tx_public_key);
9672 if(found_pub_key != m_key_image_cache.end()) {
9673 // pub key found. key image for index cached?
9674 index_keyimage_map = found_pub_key->second;
9675 std::map<uint64_t,crypto::key_image>::const_iterator index_found = index_keyimage_map.find(out_index);
9676 if(index_found != index_keyimage_map.end())
9677 return key_image == index_found->second;
9678 }
9679
9680 // Not in cache - calculate key image
9681 crypto::key_image calculated_key_image;
9682 cryptonote::keypair in_ephemeral;
9683
9684 // Subaddresses aren't supported in mymonero/openmonero yet. Roll out the original scheme:
9685 // compute D = a*R
9686 // compute P = Hs(D || i)*G + B
9687 // compute x = Hs(D || i) + b (and check if P==x*G)
9688 // compute I = x*Hp(P)
9689 const account_keys& ack = get_account().get_keys();
9690 crypto::key_derivation derivation;
9691 bool r = crypto::generate_key_derivation(tx_public_key, ack.m_view_secret_key, derivation);
9692 CHECK_AND_ASSERT_MES(r, false, "failed to generate_key_derivation(" << tx_public_key << ", " << ack.m_view_secret_key << ")");
9693
9694 r = crypto::derive_public_key(derivation, out_index, ack.m_account_address.m_spend_public_key, in_ephemeral.pub);
9695 CHECK_AND_ASSERT_MES(r, false, "failed to derive_public_key (" << derivation << ", " << out_index << ", " << ack.m_account_address.m_spend_public_key << ")");
9696
9697 crypto::derive_secret_key(derivation, out_index, ack.m_spend_secret_key, in_ephemeral.sec);
9698 crypto::public_key out_pkey_test;
9699 r = crypto::secret_key_to_public_key(in_ephemeral.sec, out_pkey_test);
9700 CHECK_AND_ASSERT_MES(r, false, "failed to secret_key_to_public_key(" << in_ephemeral.sec << ")");
9701 CHECK_AND_ASSERT_MES(in_ephemeral.pub == out_pkey_test, false, "derived secret key doesn't match derived public key");
9702
9703 crypto::generate_key_image(in_ephemeral.pub, in_ephemeral.sec, calculated_key_image);
9704
9705 index_keyimage_map.emplace(out_index, calculated_key_image);
9706 m_key_image_cache.emplace(tx_public_key, index_keyimage_map);
9707 return key_image == calculated_key_image;
9708}
9709
9710// Another implementation of transaction creation that is hopefully better
9711// While there is anything left to pay, it goes through random outputs and tries
9712// to fill the next destination/amount. If it fully fills it, it will use the
9713// remainder to try to fill the next one as well.
9714// The tx size if roughly estimated as a linear function of only inputs, and a
9715// new tx will be created when that size goes above a given fraction of the
9716// max tx size. At that point, more outputs may be added if the fee cannot be
9717// satisfied.
9718// If the next output in the next tx would go to the same destination (ie, we
9719// cut off at a tx boundary in the middle of paying a given destination), the
9720// fee will be carved out of the current input if possible, to avoid having to
9721// add another output just for the fee and getting change.
9722// This system allows for sending (almost) the entire balance, since it does
9723// not generate spurious change in all txes, thus decreasing the instantaneous
9724// usable balance.
9725std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices)
9726{
9727 //ensure device is let in NONE mode in any case
9728 hw::device &hwdev = m_account.get_device();
9729 boost::unique_lock<hw::device> hwdev_lock (hwdev);
9730 hw::reset_mode rst(hwdev);
9731
9732 //destinations in the full etn-address format
9733 auto original_dsts = dsts;
9734
9735 if(m_light_wallet) {
9736 // Populate m_transfers
9738 }
9739
9740 uint8_t tx_version = this->public_transactions_required() ? 3 : 1;
9741 //vector of pairs of <subaddr minor index : vector<transfer indexes for that subaddr index inside m_transfers>>
9742 std::vector<std::pair<uint32_t, std::vector<size_t>>> unused_transfers_indices_per_subaddr;
9743 std::vector<std::pair<uint32_t, std::vector<size_t>>> unused_dust_indices_per_subaddr;
9744 uint64_t needed_etn;
9745 uint64_t accumulated_fee, accumulated_outputs, accumulated_change;
9746 struct TX {
9747 std::vector<size_t> selected_transfers;
9748 std::vector<cryptonote::tx_destination_entry> dsts;
9750 pending_tx ptx;
9751 size_t weight;
9752 uint64_t needed_fee;
9753 std::vector<std::vector<tools::wallet2::get_outs_entry>> outs;
9754
9755 TX() : weight(0), needed_fee(0) {}
9756
9757 void add(const cryptonote::tx_destination_entry &de, uint64_t amount, unsigned int original_output_index, bool merge_destinations) {
9759 {
9760 std::vector<cryptonote::tx_destination_entry>::iterator i;
9761 i = std::find_if(dsts.begin(), dsts.end(), [&](const cryptonote::tx_destination_entry &d) { return !memcmp (&d.addr, &de.addr, sizeof(de.addr)); });
9762 if (i == dsts.end())
9763 {
9764 dsts.push_back(de);
9765 i = dsts.end() - 1;
9766 i->amount = 0;
9767 }
9768 i->amount += amount;
9769 }
9770 else
9771 {
9772 THROW_WALLET_EXCEPTION_IF(original_output_index > dsts.size(), error::wallet_internal_error,
9773 std::string("original_output_index too large: ") + std::to_string(original_output_index) + " > " + std::to_string(dsts.size()));
9774 if (original_output_index == dsts.size())
9775 {
9776 dsts.push_back(de);
9777 dsts.back().amount = 0;
9778 }
9779 THROW_WALLET_EXCEPTION_IF(memcmp(&dsts[original_output_index].addr, &de.addr, sizeof(de.addr)), error::wallet_internal_error, "Mismatched destination address");
9780 dsts[original_output_index].amount += amount;
9781 }
9782 }
9783 };
9784 std::vector<TX> txes;
9785 bool adding_fee; // true if new outputs go towards fee, rather than destinations
9786
9787 uint64_t needed_fee, available_for_fee = 0;
9788 uint64_t upper_transaction_weight_limit;
9789 uint64_t extra_bytes = extra.size();
9790 switch(hwdev.get_type()){
9791
9792 // Normal Software Limit
9793 case 0 : upper_transaction_weight_limit = get_upper_transaction_weight_limit(); break;
9794
9795 // Ledger NanoS: ~3.3kB of RAM for app variables. Give a bit of buffer (300) for other variables on device
9796 // and subtract the size of the extra.
9797 // because in the Ledger app this still lives on the stack at the same time the entire prefix does.
9798 // This is a rough rule of thumb estimate... The logic can be updated at a later stage.
9799 // Right now we just need to make we don't fail to build any tx (and split the tx to avoid this happening)
9800 case 1 : upper_transaction_weight_limit = 3000 - extra_bytes; break;
9801
9802 //Trezor limit set at the same as Ledger for the time being.
9803 case 2 : upper_transaction_weight_limit = 3000 - extra_bytes; break;
9804 //Future hw devices
9805 }
9806 const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
9807 const bool use_rct = use_fork_rules(HF_VERSION_ENABLE_RCT, 0);
9808 const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
9809 const rct::RCTConfig rct_config {
9811 bulletproof ? (use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1) : 0
9812 };
9813
9814 const uint64_t base_fee = get_base_fee();
9815 const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
9816 const uint64_t fee_quantization_mask = get_fee_quantization_mask();
9817
9818 // throw if attempting a transaction with no destinations
9820
9821 // calculate total amount being sent to all destinations
9822 // throw if total amount overflows uint64_t
9823 needed_etn = 0;
9824 for(auto& dt: dsts)
9825 {
9827 needed_etn += dt.amount;
9828 LOG_PRINT_L2("transfer: adding " << print_etn(dt.amount) << ", for a total of " << print_etn (needed_etn));
9829 THROW_WALLET_EXCEPTION_IF(needed_etn < dt.amount, error::tx_sum_overflow, dsts, 0, m_nettype);
9830 }
9831
9832 // throw if attempting a transaction with no etn
9834
9835 std::map<uint32_t, std::pair<uint64_t, uint64_t>> unlocked_balance_per_subaddr = unlocked_balance_per_subaddress(subaddr_account, tx_version >= 3);
9836 std::map<uint32_t, uint64_t> balance_per_subaddr = balance_per_subaddress(subaddr_account, tx_version >= 3);
9837
9838 if (subaddr_indices.empty()) // "index=<N1>[,<N2>,...]" wasn't specified -> use all the indices with non-zero unlocked balance
9839 {
9840 for (const auto& i : balance_per_subaddr)
9841 subaddr_indices.insert(i.first);
9842 }
9843
9844 // early out if we know we can't make it anyway
9845 // we could also check for being within FEE_PER_KB, but if the fee calculation
9846 // ever changes, this might be missed, so let this go through
9847 uint64_t min_fee = (fee_multiplier * base_fee * estimate_tx_size(use_rct, 1, fake_outs_count, 2, extra.size(), bulletproof));
9848 //Whilst we're still dealing with fee/kb:
9849 if (!use_per_byte_fee){
9850 min_fee /= 1000;
9851 if(min_fee == 0){min_fee += 10;}
9852 }
9853 uint64_t balance_subtotal = 0;
9854 uint64_t unlocked_balance_subtotal = 0;
9855 for (uint32_t index_minor : subaddr_indices)
9856 {
9857 balance_subtotal += balance_per_subaddr[index_minor];
9858 unlocked_balance_subtotal += unlocked_balance_per_subaddr[index_minor].first;
9859 }
9860 THROW_WALLET_EXCEPTION_IF(needed_etn + min_fee > balance_subtotal, error::not_enough_etn,
9861 balance_subtotal, needed_etn, 0);
9862 // first check overall balance is enough, then unlocked one, so we throw distinct exceptions
9863 THROW_WALLET_EXCEPTION_IF(needed_etn + min_fee > unlocked_balance_subtotal, error::not_enough_unlocked_etn,
9864 unlocked_balance_subtotal, needed_etn, 0);
9865
9866 for (uint32_t i : subaddr_indices)
9867 LOG_PRINT_L2("Candidate subaddress index for spending: " << i);
9868
9869 // determine threshold for fractional amount
9870 const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof);
9871 const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof);
9872 THROW_WALLET_EXCEPTION_IF(tx_weight_one_ring > tx_weight_two_rings, error::wallet_internal_error, "Estimated tx weight with 1 input is larger than with 2 inputs!");
9873 const size_t tx_weight_per_ring = tx_weight_two_rings - tx_weight_one_ring;
9874 const uint64_t fractional_threshold = (fee_multiplier * base_fee * tx_weight_per_ring) / (use_per_byte_fee ? 1 : 1024);
9875
9876 // gather all dust and non-dust outputs belonging to specified subaddresses
9877 size_t num_nondust_outputs = 0;
9878 size_t num_dust_outputs = 0;
9879 for (size_t i = 0; i < m_transfers.size(); ++i)
9880 {
9881 const transfer_details& td = m_transfers[i];
9882 if (m_ignore_fractional_outputs && td.amount() < fractional_threshold)
9883 {
9884 MDEBUG("Ignoring output " << i << " of amount " << print_etn(td.amount()) << " which is below threshold " << print_etn(fractional_threshold));
9885 continue;
9886 }
9887 if (!td.m_spent && !td.m_frozen && (!td.m_key_image_partial || td.m_tx.version > 1) && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
9888 {
9889 if((tx_version < 3 && td.m_tx.version > 1) || (tx_version >= 3 && td.m_tx.version == 1))
9890 continue;
9891
9892 const uint32_t index_minor = td.m_subaddr_index.minor;
9893 auto find_predicate = [&index_minor](const std::pair<uint32_t, std::vector<size_t>>& x) { return x.first == index_minor; };
9894 if ((td.is_rct()) || is_valid_decomposed_amount(td.amount()))
9895 {
9896 auto found = std::find_if(unused_transfers_indices_per_subaddr.begin(), unused_transfers_indices_per_subaddr.end(), find_predicate);
9897 if (found == unused_transfers_indices_per_subaddr.end())
9898 {
9899 unused_transfers_indices_per_subaddr.push_back({index_minor, {i}});
9900 }
9901 else
9902 {
9903 found->second.push_back(i);
9904 }
9905 ++num_nondust_outputs;
9906 }
9907 else
9908 {
9909 auto found = std::find_if(unused_dust_indices_per_subaddr.begin(), unused_dust_indices_per_subaddr.end(), find_predicate);
9910 if (found == unused_dust_indices_per_subaddr.end())
9911 {
9912 unused_dust_indices_per_subaddr.push_back({index_minor, {i}});
9913 }
9914 else
9915 {
9916 found->second.push_back(i);
9917 }
9918 ++num_dust_outputs;
9919 }
9920 }
9921 }
9922
9923 // sort output indices
9924 {
9925 auto sort_predicate = [&unlocked_balance_per_subaddr] (const std::pair<uint32_t, std::vector<size_t>>& x, const std::pair<uint32_t, std::vector<size_t>>& y)
9926 {
9927 return unlocked_balance_per_subaddr[x.first].first > unlocked_balance_per_subaddr[y.first].first;
9928 };
9929 std::sort(unused_transfers_indices_per_subaddr.begin(), unused_transfers_indices_per_subaddr.end(), sort_predicate);
9930 std::sort(unused_dust_indices_per_subaddr.begin(), unused_dust_indices_per_subaddr.end(), sort_predicate);
9931 }
9932
9933 LOG_PRINT_L2("Starting with " << num_nondust_outputs << " non-dust outputs and " << num_dust_outputs << " dust outputs");
9934
9935 if (unused_dust_indices_per_subaddr.empty() && unused_transfers_indices_per_subaddr.empty())
9936 return std::vector<wallet2::pending_tx>();
9937
9938 // if empty, put dummy entry so that the front can be referenced later in the loop
9939 if (unused_dust_indices_per_subaddr.empty())
9940 unused_dust_indices_per_subaddr.push_back({});
9941 if (unused_transfers_indices_per_subaddr.empty())
9942 unused_transfers_indices_per_subaddr.push_back({});
9943
9944 // start with an empty tx
9945 txes.push_back(TX());
9946 accumulated_fee = 0;
9947 accumulated_outputs = 0;
9948 accumulated_change = 0;
9949 adding_fee = false;
9950 needed_fee = 0;
9951 std::vector<std::vector<tools::wallet2::get_outs_entry>> outs;
9952
9953 // for rct, since we don't see the amounts, we will try to make all transactions
9954 // look the same, with 1 or 2 inputs, and 2 outputs. One input is preferable, as
9955 // this prevents linking to another by provenance analysis, but two is ok if we
9956 // try to pick outputs not from the same block. We will get two outputs, one for
9957 // the destination, and one for change.
9958 LOG_PRINT_L2("checking preferred");
9959 std::vector<size_t> preferred_inputs;
9960 uint64_t rct_outs_needed = 2 * (fake_outs_count + 1);
9961 rct_outs_needed += 100; // some fudge factor since we don't know how many are locked
9962 if (use_rct)
9963 {
9964 // this is used to build a tx that's 1 or 2 inputs, and 2 outputs, which
9965 // will get us a known fee.
9966 uint64_t estimated_fee = estimate_fee(use_per_byte_fee, use_rct, 2, fake_outs_count, 2, extra.size(), bulletproof, base_fee, fee_multiplier, fee_quantization_mask);
9967 preferred_inputs = pick_preferred_rct_inputs(needed_etn + estimated_fee, subaddr_account, subaddr_indices);
9968 if (!preferred_inputs.empty())
9969 {
9970 string s;
9971 for (auto i: preferred_inputs) s += boost::lexical_cast<std::string>(i) + " (" + print_etn(m_transfers[i].amount()) + ") ";
9972 LOG_PRINT_L1("Found preferred rct inputs for rct tx: " << s);
9973
9974 // bring the list of available outputs stored by the same subaddress index to the front of the list
9975 uint32_t index_minor = m_transfers[preferred_inputs[0]].m_subaddr_index.minor;
9976 for (size_t i = 1; i < unused_transfers_indices_per_subaddr.size(); ++i)
9977 {
9978 if (unused_transfers_indices_per_subaddr[i].first == index_minor)
9979 {
9980 std::swap(unused_transfers_indices_per_subaddr[0], unused_transfers_indices_per_subaddr[i]);
9981 break;
9982 }
9983 }
9984 for (size_t i = 1; i < unused_dust_indices_per_subaddr.size(); ++i)
9985 {
9986 if (unused_dust_indices_per_subaddr[i].first == index_minor)
9987 {
9988 std::swap(unused_dust_indices_per_subaddr[0], unused_dust_indices_per_subaddr[i]);
9989 break;
9990 }
9991 }
9992 }
9993 }
9994 LOG_PRINT_L2("done checking preferred");
9995
9996 // while:
9997 // - we have something to send
9998 // - or we need to gather more fee
9999 // - or we have just one input in that tx, which is rct (to try and make all/most rct txes 2/2)
10000 unsigned int original_output_index = 0;
10001 std::vector<size_t>* unused_transfers_indices = &unused_transfers_indices_per_subaddr[0].second;
10002 std::vector<size_t>* unused_dust_indices = &unused_dust_indices_per_subaddr[0].second;
10003
10005 while ((!dsts.empty() && dsts[0].amount > 0) || adding_fee || !preferred_inputs.empty() || should_pick_a_second_output(use_rct, txes.back().selected_transfers.size(), *unused_transfers_indices, *unused_dust_indices)) {
10006 TX &tx = txes.back();
10007
10008 LOG_PRINT_L2("Start of loop with " << unused_transfers_indices->size() << " " << unused_dust_indices->size() << ", tx.dsts.size() " << tx.dsts.size());
10009 LOG_PRINT_L2("unused_transfers_indices: " << strjoin(*unused_transfers_indices, " "));
10010 LOG_PRINT_L2("unused_dust_indices: " << strjoin(*unused_dust_indices, " "));
10011 LOG_PRINT_L2("dsts size " << dsts.size() << ", first " << (dsts.empty() ? "-" : cryptonote::print_etn(dsts[0].amount)));
10012 LOG_PRINT_L2("adding_fee " << adding_fee << ", use_rct " << use_rct);
10013
10014 // if we need to spend etn and don't have any left, we fail
10015 if (unused_dust_indices->empty() && unused_transfers_indices->empty()) {
10016 LOG_PRINT_L2("No more outputs to choose from");
10017 THROW_WALLET_EXCEPTION_IF(1, error::tx_not_possible, unlocked_balance(subaddr_account, tx_version >= 3), needed_etn, accumulated_fee + needed_fee);
10018 }
10019
10020 // get a random unspent output and use it to pay part (or all) of the current destination (and maybe next one, etc)
10021 // This could be more clever, but maybe at the cost of making probabilistic inferences easier
10022 size_t idx;
10023 if (!preferred_inputs.empty()) {
10024 idx = pop_back(preferred_inputs);
10025 pop_if_present(*unused_transfers_indices, idx);
10026 pop_if_present(*unused_dust_indices, idx);
10027 } else if ((dsts.empty() || dsts[0].amount == 0) && !adding_fee) {
10028 // the "make rct txes 2/2" case - we pick a small value output to "clean up" the wallet too
10029 std::vector<size_t> indices = get_only_rct(*unused_dust_indices, *unused_transfers_indices);
10030 idx = pop_best_value(indices, tx.selected_transfers, true);
10031
10032 // we might not want to add it if it's a large output and we don't have many left
10033 uint64_t min_output_value = m_min_output_value;
10034 uint32_t min_output_count = m_min_output_count;
10035 if (min_output_value == 0 && min_output_count == 0)
10036 {
10037 min_output_value = DEFAULT_MIN_OUTPUT_VALUE;
10038 min_output_count = DEFAULT_MIN_OUTPUT_COUNT;
10039 }
10040 if (m_transfers[idx].amount() >= min_output_value) {
10041 if (get_count_above(m_transfers, *unused_transfers_indices, min_output_value) < min_output_count) {
10042 LOG_PRINT_L2("Second output was not strictly needed, and we're running out of outputs above " << print_etn(min_output_value) << ", not adding");
10043 break;
10044 }
10045 }
10046
10047 // since we're trying to add a second output which is not strictly needed,
10048 // we only add it if it's unrelated enough to the first one
10049 float relatedness = get_output_relatedness(m_transfers[idx], m_transfers[tx.selected_transfers.front()]);
10050 if (relatedness > SECOND_OUTPUT_RELATEDNESS_THRESHOLD)
10051 {
10052 LOG_PRINT_L2("Second output was not strictly needed, and relatedness " << relatedness << ", not adding");
10053 break;
10054 }
10055 pop_if_present(*unused_transfers_indices, idx);
10056 pop_if_present(*unused_dust_indices, idx);
10057 } else
10058 idx = pop_best_value(unused_transfers_indices->empty() ? *unused_dust_indices : *unused_transfers_indices, tx.selected_transfers);
10059
10060 const transfer_details &td = m_transfers[idx];
10061 LOG_PRINT_L2("Picking output " << idx << ", amount " << print_etn(td.amount()) << ", ki " << td.m_key_image);
10062
10063 // add this output to the list to spend
10064 tx.selected_transfers.push_back(idx);
10065 uint64_t available_amount = td.amount();
10066 accumulated_outputs += available_amount;
10067
10068 // clear any fake outs we'd already gathered, since we'll need a new set
10069 outs.clear();
10070
10071 if (adding_fee)
10072 {
10073 LOG_PRINT_L2("We need more fee, adding it to fee");
10074 available_for_fee += available_amount;
10075 }
10076 else
10077 {
10078 while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof) < TX_WEIGHT_TARGET(upper_transaction_weight_limit))
10079 {
10080 // we can fully pay that destination
10081 LOG_PRINT_L2("We can fully pay " << get_account_address_as_str(m_nettype, dsts[0].is_subaddress, dsts[0].addr) <<
10082 " for " << print_etn(dsts[0].amount));
10083 tx.add(dsts[0], dsts[0].amount, original_output_index, m_merge_destinations);
10084 available_amount -= dsts[0].amount;
10085 dsts[0].amount = 0;
10086 pop_index(dsts, 0);
10087 ++original_output_index;
10088 }
10089
10090 if (available_amount > 0 && !dsts.empty() && estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof) < TX_WEIGHT_TARGET(upper_transaction_weight_limit)) {
10091 // we can partially fill that destination
10092 LOG_PRINT_L2("We can partially pay " << get_account_address_as_str(m_nettype, dsts[0].is_subaddress, dsts[0].addr) <<
10093 " for " << print_etn(available_amount) << "/" << print_etn(dsts[0].amount));
10094 tx.add(dsts[0], available_amount, original_output_index, m_merge_destinations);
10095 dsts[0].amount -= available_amount;
10096 available_amount = 0;
10097 }
10098 }
10099
10100 // here, check if we need to sent tx and start a new one
10101 LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit "
10102 << upper_transaction_weight_limit);
10103 bool try_tx = false;
10104 // if we have preferred picks, but haven't yet used all of them, continue
10105 if (preferred_inputs.empty())
10106 {
10107 if (adding_fee)
10108 {
10109 /* might not actually be enough if adding this output bumps size to next kB, but we need to try */
10110 try_tx = available_for_fee >= needed_fee;
10111 }
10112 else
10113 {
10114 const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof);
10115 try_tx = dsts.empty() || (estimated_rct_tx_weight >= TX_WEIGHT_TARGET(upper_transaction_weight_limit));
10116 THROW_WALLET_EXCEPTION_IF(try_tx && tx.dsts.empty(), error::tx_too_big, estimated_rct_tx_weight, upper_transaction_weight_limit);
10117 }
10118 }
10119
10120 if (try_tx) {
10122 test_tx.version = tx_version;
10123 pending_tx test_ptx;
10124
10125 needed_fee = estimate_fee(use_per_byte_fee, use_rct ,tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, base_fee, fee_multiplier, fee_quantization_mask);
10126
10127 uint64_t inputs = 0, outputs = needed_fee;
10128 for (size_t idx: tx.selected_transfers) inputs += m_transfers[idx].amount();
10129 for (const auto &o: tx.dsts) outputs += o.amount;
10130
10131 if (inputs < outputs)
10132 {
10133 LOG_PRINT_L2("We don't have enough for the basic fee, switching to adding_fee");
10134 adding_fee = true;
10135 goto skip_tx;
10136 }
10137
10138 LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " outputs and " <<
10139 tx.selected_transfers.size() << " inputs");
10140
10141 transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
10143 auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
10144 needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
10145 available_for_fee = test_ptx.fee + test_ptx.change_dts.amount + (!test_ptx.dust_added_to_fee ? test_ptx.dust : 0);
10146 LOG_PRINT_L2("Made a " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_etn(available_for_fee) << " available for fee (" <<
10147 print_etn(needed_fee) << " needed)");
10148
10149 if (needed_fee > available_for_fee && !dsts.empty() && dsts[0].amount > 0)
10150 {
10151 // we don't have enough for the fee, but we've only partially paid the current address,
10152 // so we can take the fee from the paid amount, since we'll have to make another tx anyway
10153 std::vector<cryptonote::tx_destination_entry>::iterator i;
10154 i = std::find_if(tx.dsts.begin(), tx.dsts.end(),
10155 [&](const cryptonote::tx_destination_entry &d) { return !memcmp (&d.addr, &dsts[0].addr, sizeof(dsts[0].addr)); });
10156 THROW_WALLET_EXCEPTION_IF(i == tx.dsts.end(), error::wallet_internal_error, "paid address not found in outputs");
10157 if (i->amount > needed_fee)
10158 {
10159 uint64_t new_paid_amount = i->amount /*+ test_ptx.fee*/ - needed_fee;
10160 LOG_PRINT_L2("Adjusting amount paid to " << get_account_address_as_str(m_nettype, i->is_subaddress, i->addr) << " from " <<
10161 print_etn(i->amount) << " to " << print_etn(new_paid_amount) << " to accommodate " <<
10162 print_etn(needed_fee) << " fee");
10163 dsts[0].amount += i->amount - new_paid_amount;
10164 i->amount = new_paid_amount;
10165 test_ptx.fee = needed_fee;
10166 available_for_fee = needed_fee;
10167 }
10168 }
10169
10170 if (needed_fee > available_for_fee)
10171 {
10172 LOG_PRINT_L2("We could not make a tx, switching to fee accumulation");
10173
10174 adding_fee = true;
10175 }
10176 else
10177 {
10178 LOG_PRINT_L2("We made a tx, adjusting fee and saving it, we need " << print_etn(needed_fee) << " and we have " << print_etn(test_ptx.fee));
10179 while (needed_fee > test_ptx.fee) {
10180 transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
10182 txBlob = t_serializable_object_to_blob(test_ptx.tx);
10183 needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
10184 LOG_PRINT_L2("Made an attempt at a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_etn(test_ptx.fee) <<
10185 " fee and " << print_etn(test_ptx.change_dts.amount) << " change");
10186 }
10187
10188 LOG_PRINT_L2("Made a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_etn(test_ptx.fee) <<
10189 " fee and " << print_etn(test_ptx.change_dts.amount) << " change");
10190
10191 tx.tx = test_tx;
10192 tx.ptx = test_ptx;
10193 tx.weight = get_transaction_weight(test_tx, txBlob.size());
10194 tx.outs = outs;
10195 tx.needed_fee = test_ptx.fee;
10196 accumulated_fee += test_ptx.fee;
10197 accumulated_change += test_ptx.change_dts.amount;
10198 adding_fee = false;
10199 if (!dsts.empty())
10200 {
10201 LOG_PRINT_L2("We have more to pay, starting another tx");
10202 txes.push_back(TX());
10203 original_output_index = 0;
10204 }
10205 }
10206 }
10207
10208skip_tx:
10209 // if unused_*_indices is empty while unused_*_indices_per_subaddr has multiple elements, and if we still have something to pay,
10210 // pop front of unused_*_indices_per_subaddr and have unused_*_indices point to the front of unused_*_indices_per_subaddr
10211 if ((!dsts.empty() && dsts[0].amount > 0) || adding_fee)
10212 {
10213 if (unused_transfers_indices->empty() && unused_transfers_indices_per_subaddr.size() > 1)
10214 {
10215 unused_transfers_indices_per_subaddr.erase(unused_transfers_indices_per_subaddr.begin());
10216 unused_transfers_indices = &unused_transfers_indices_per_subaddr[0].second;
10217 }
10218 if (unused_dust_indices->empty() && unused_dust_indices_per_subaddr.size() > 1)
10219 {
10220 unused_dust_indices_per_subaddr.erase(unused_dust_indices_per_subaddr.begin());
10221 unused_dust_indices = &unused_dust_indices_per_subaddr[0].second;
10222 }
10223 }
10224 }
10225
10226 if (adding_fee)
10227 {
10228 LOG_PRINT_L1("We ran out of outputs while trying to gather final fee");
10229 THROW_WALLET_EXCEPTION_IF(1, error::tx_not_possible, unlocked_balance(subaddr_account, tx_version >= 3), needed_etn, accumulated_fee + needed_fee);
10230 }
10231
10232 LOG_PRINT_L1("Done creating " << txes.size() << " transactions, " << print_etn(accumulated_fee) <<
10233 " total fee, " << print_etn(accumulated_change) << " total change");
10234
10236 for (std::vector<TX>::iterator i = txes.begin(); i != txes.end(); ++i)
10237 {
10238 TX &tx = *i;
10240 test_tx.version = tx_version;
10241 pending_tx test_ptx;
10242 transfer_selected(tx.dsts,
10243 tx.selected_transfers,
10244 fake_outs_count,
10245 tx.outs,
10246 unlock_time,
10247 tx.needed_fee,
10248 extra,
10251 test_tx,
10252 test_ptx);
10253
10254 auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
10255 tx.tx = test_tx;
10256 tx.ptx = test_ptx;
10257 tx.weight = get_transaction_weight(test_tx, txBlob.size());
10258 }
10259
10260 std::vector<wallet2::pending_tx> ptx_vector;
10261 for (std::vector<TX>::iterator i = txes.begin(); i != txes.end(); ++i)
10262 {
10263 TX &tx = *i;
10264 uint64_t tx_etn = 0;
10265 for (size_t idx: tx.selected_transfers)
10266 tx_etn += m_transfers[idx].amount();
10267 LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() <<
10268 " " << get_transaction_hash(tx.ptx.tx) << ": " << get_weight_string(tx.weight) << ", sending " << print_etn(tx_etn) << " in " << tx.selected_transfers.size() <<
10269 " outputs to " << tx.dsts.size() << " destination(s), including " <<
10270 print_etn(tx.ptx.fee) << " fee, " << print_etn(tx.ptx.change_dts.amount) << " change");
10271 ptx_vector.push_back(tx.ptx);
10272 }
10273
10274 THROW_WALLET_EXCEPTION_IF(!sanity_check(ptx_vector, original_dsts), error::wallet_internal_error, "Created transaction(s) failed sanity check");
10275
10276 // if we made it this far, we're OK to actually send the transactions
10277 return ptx_vector;
10278}
10279
10280bool wallet2::sanity_check(const std::vector<wallet2::pending_tx> &ptx_vector,
10281 std::vector<cryptonote::tx_destination_entry> dsts) const {
10282 MDEBUG("sanity_check: " << ptx_vector.size() << " txes, " << dsts.size() << " destinations");
10283
10284 hw::device &hwdev = m_account.get_device();
10285
10286 THROW_WALLET_EXCEPTION_IF(ptx_vector.empty(), error::wallet_internal_error, "No transactions");
10287
10288 if (std::all_of(ptx_vector.begin(), ptx_vector.end(), [](const pending_tx &ptx) { return ptx.tx.version == 1; })) { // we only need do tx proofs for v1
10289 // check every party in there does receive at least the required amount
10290 std::unordered_map<account_public_address, std::pair<uint64_t, bool>> required;
10291 for (const auto &d: dsts) {
10292 required[d.addr].first += d.amount;
10293 required[d.addr].second = d.is_subaddress;
10294 }
10295
10296 // add change
10297 uint64_t change = 0;
10298 for (const auto &ptx: ptx_vector) {
10299 for (size_t idx: ptx.selected_transfers) //1:add the amount you're spending
10300 change += m_transfers[idx].amount();
10301 change -= ptx.fee; //2: take off the fee
10302 }
10303 for (const auto &r: required)
10304 change -= r.second.first; // 3: subtract the destination required amount
10305 MDEBUG("Adding " << cryptonote::print_etn(change) << " expected change");
10306
10307 // for all txes that have actual change, check change is coming back to the sending wallet
10308 for (const pending_tx &ptx: ptx_vector) {
10309 if (ptx.change_dts.amount == 0)
10310 continue;
10312 m_subaddresses.find(ptx.change_dts.addr.m_spend_public_key) == m_subaddresses.end(),
10313 error::wallet_internal_error, "Change address is not ours");
10314 required[ptx.change_dts.addr].first += ptx.change_dts.amount;
10315 required[ptx.change_dts.addr].second = ptx.change_dts.is_subaddress;
10316 }
10317
10318
10319 for (const auto &r: required) {
10320 const account_public_address &address = r.first;
10321 const crypto::public_key &view_pkey = address.m_view_public_key;
10322
10323 uint64_t total_received = 0;
10324
10325 for (const auto &ptx: ptx_vector) {
10326 uint64_t received = 0;
10327 try {
10328 std::string proof = get_tx_proof(ptx.tx, ptx.tx_key, ptx.additional_tx_keys, address,
10329 r.second.second,
10330 "automatic-sanity-check");
10331 check_tx_proof(ptx.tx, address, r.second.second, "automatic-sanity-check", proof, received);
10332 }
10333 catch (const std::exception &e) { received = 0; }
10334 total_received += received;
10335 }
10336
10337 std::stringstream ss;
10338 ss << "Total received by "
10339 << cryptonote::get_account_address_as_str(m_nettype, r.second.second, address)
10340 << ": "
10341 << cryptonote::print_etn(total_received) << ", expected " << cryptonote::print_etn(r.second.first);
10342 MDEBUG(ss.str());
10343 THROW_WALLET_EXCEPTION_IF(total_received < r.second.first, error::wallet_internal_error, ss.str());
10344 }
10345 } // end of v1 sanity check
10346 else {
10347 // for all txes that have actual change, check change is coming back to the sending wallet
10348 for (const pending_tx &ptx: ptx_vector) {
10349 if (ptx.change_dts.amount == 0)
10350 continue;
10352 m_subaddresses.find(ptx.change_dts.addr.m_spend_public_key) == m_subaddresses.end(),
10353 error::wallet_internal_error, "Change address is not ours");
10354 }
10355 }
10356 return true;
10357}
10358
10359std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const bool migrate)
10360{
10361 std::vector<size_t> unused_transfers_indices;
10362 std::vector<size_t> unused_dust_indices;
10363 const bool use_rct = use_fork_rules(4, 0);
10364 uint8_t tx_version = public_transactions_required() ? (migrate ? 2 : 3) : 1; //public migration **NOT** SC migration. SC migration tx are just vanilla v3 tx.
10365 THROW_WALLET_EXCEPTION_IF(unlocked_balance(subaddr_account, tx_version >=3) == 0, error::wallet_internal_error, "No unlocked balance in the entire wallet");
10366 std::map<uint32_t, std::pair<std::vector<size_t>, std::vector<size_t>>> unused_transfer_dust_indices_per_subaddr;
10367
10368 // gather all dust ***and*** non-dust outputs of specified subaddress (if any) and below specified threshold (if any)
10369 bool fund_found = false;
10370 for (size_t i = 0; i < m_transfers.size(); ++i)
10371 {
10372 const transfer_details& td = m_transfers[i];
10373
10374 if((tx_version < 3 && td.m_tx.version > 1) || (tx_version >= 3 && td.m_tx.version == 1))
10375 continue;
10376
10377 if (!td.m_spent && !td.m_frozen && (!td.m_key_image_partial || tx_version >= 3) && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && (subaddr_indices.empty() || subaddr_indices.count(td.m_subaddr_index.minor) == 1))
10378 {
10379 fund_found = true;
10380 if (below == 0 || td.amount() < below)
10381 {
10382 if ((td.is_rct()) || is_valid_decomposed_amount(td.amount()))
10383 unused_transfer_dust_indices_per_subaddr[td.m_subaddr_index.minor].first.push_back(i);
10384 else
10385 unused_transfer_dust_indices_per_subaddr[td.m_subaddr_index.minor].second.push_back(i);
10386 }
10387 }
10388 }
10389 THROW_WALLET_EXCEPTION_IF(!fund_found, error::wallet_internal_error, "No unlocked balance in the specified subaddress(es)");
10390 THROW_WALLET_EXCEPTION_IF(unused_transfer_dust_indices_per_subaddr.empty(), error::wallet_internal_error, "The smallest amount found is not below the specified threshold");
10391
10392 if (subaddr_indices.empty())
10393 {
10394 // in case subaddress index wasn't specified, choose non-empty subaddress randomly (with index=0 being chosen last)
10395 if (unused_transfer_dust_indices_per_subaddr.count(0) == 1 && unused_transfer_dust_indices_per_subaddr.size() > 1)
10396 unused_transfer_dust_indices_per_subaddr.erase(0);
10397 auto i = unused_transfer_dust_indices_per_subaddr.begin();
10398 std::advance(i, crypto::rand_idx(unused_transfer_dust_indices_per_subaddr.size()));
10399 unused_transfers_indices = i->second.first;
10400 unused_dust_indices = i->second.second;
10401 LOG_PRINT_L2("Spending from subaddress index " << i->first);
10402 }
10403 else
10404 {
10405 for (const auto& p : unused_transfer_dust_indices_per_subaddr)
10406 {
10407 unused_transfers_indices.insert(unused_transfers_indices.end(), p.second.first.begin(), p.second.first.end());
10408 unused_dust_indices.insert(unused_dust_indices.end(), p.second.second.begin(), p.second.second.end());
10409 LOG_PRINT_L2("Spending from subaddress index " << p.first);
10410 }
10411 }
10412
10413 return create_transactions_from(address, is_subaddress, outputs, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra, tx_version);
10414}
10415
10416std::vector<wallet2::pending_tx> wallet2::create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra)
10417{
10418 std::vector<size_t> unused_transfers_indices;
10419 std::vector<size_t> unused_dust_indices;
10420 uint8_t tx_version = 1; //todo: 4.0.0.0 leave it as this for now until we update it for sending by chainstate index
10421 const bool use_rct = use_fork_rules(4, 0);
10422 // find output with the given key image (
10423 for (size_t i = 0; i < m_transfers.size(); ++i)
10424 {
10425 const transfer_details& td = m_transfers[i];
10426
10427 if (td.m_key_image_known && td.m_tx.version == 1 && td.m_key_image == ki && !td.m_spent && !td.m_frozen && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td))
10428 {
10429 if (td.is_rct() || is_valid_decomposed_amount(td.amount()))
10430 unused_transfers_indices.push_back(i);
10431 else
10432 unused_dust_indices.push_back(i);
10433 break;
10434 }
10435 }
10436 return create_transactions_from(address, is_subaddress, outputs, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra, tx_version);
10437}
10438
10439std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, const uint8_t tx_version)
10440{
10441 //ensure device is let in NONE mode in any case
10442 hw::device &hwdev = m_account.get_device();
10443 boost::unique_lock<hw::device> hwdev_lock (hwdev);
10444 hw::reset_mode rst(hwdev);
10445
10446 uint64_t accumulated_fee, accumulated_outputs, accumulated_change;
10447 struct TX {
10448 std::vector<size_t> selected_transfers;
10449 std::vector<cryptonote::tx_destination_entry> dsts;
10451 pending_tx ptx;
10452 size_t weight;
10453 uint64_t needed_fee;
10454 std::vector<std::vector<get_outs_entry>> outs;
10455
10456 TX() : weight(0), needed_fee(0) {}
10457 };
10458 std::vector<TX> txes;
10459 uint64_t needed_fee, available_for_fee = 0;
10460 uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit(); //max size of a tx - usually ~ half the block size
10461 std::vector<std::vector<get_outs_entry>> outs;
10462
10463 const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE);
10464 const bool use_rct = fake_outs_count > 0 && use_fork_rules(4, 0);
10465 const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
10466 const rct::RCTConfig rct_config {
10468 bulletproof ? (use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1) : 0,
10469 };
10470 const uint64_t base_fee = get_base_fee();
10471 const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
10472 const uint64_t fee_quantization_mask = get_fee_quantization_mask();
10473
10474 LOG_PRINT_L2("Starting with " << unused_transfers_indices.size() << " non-dust outputs and " << unused_dust_indices.size() << " dust outputs");
10475
10476 if (unused_dust_indices.empty() && unused_transfers_indices.empty())
10477 return std::vector<wallet2::pending_tx>();
10478
10479 // start with an empty tx
10480 txes.push_back(TX());
10481 accumulated_fee = 0;
10482 accumulated_outputs = 0;
10483 accumulated_change = 0;
10484 needed_fee = 0;
10485
10486 // while we have something to send
10488 while (!unused_dust_indices.empty() || !unused_transfers_indices.empty()) {
10489 TX &tx = txes.back();
10490
10491 // get a random unspent output and use it to pay next chunk. We try to alternate
10492 // dust and non dust to ensure we never get with only dust, from which we might
10493 // get a tx that can't pay for itself
10494 uint64_t fee_dust_threshold;
10496 {
10497 const uint64_t estimated_tx_weight_with_one_extra_output = estimate_tx_weight(use_rct, tx.selected_transfers.size() + 1, fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof);
10498 fee_dust_threshold = calculate_fee_from_weight(base_fee, estimated_tx_weight_with_one_extra_output, fee_multiplier, fee_quantization_mask);
10499 }
10500 else
10501 {
10502 fee_dust_threshold = base_fee * fee_multiplier * (upper_transaction_weight_limit + 1023) / 1024;
10503 }
10504
10505 size_t idx =
10506 unused_transfers_indices.empty()
10507 ? pop_best_value(unused_dust_indices, tx.selected_transfers)
10508 : unused_dust_indices.empty()
10509 ? pop_best_value(unused_transfers_indices, tx.selected_transfers)
10510 : ((tx.selected_transfers.size() & 1) || accumulated_outputs > fee_dust_threshold)
10511 ? pop_best_value(unused_dust_indices, tx.selected_transfers)
10512 : pop_best_value(unused_transfers_indices, tx.selected_transfers);
10513
10514 const transfer_details &td = m_transfers[idx];
10515 LOG_PRINT_L2("Picking output " << idx << ", amount " << print_etn(td.amount()));
10516
10517 // add this output to the list to spend
10518 tx.selected_transfers.push_back(idx);
10519 uint64_t available_amount = td.amount();
10520 accumulated_outputs += available_amount;
10521
10522 // clear any fake outs we'd already gathered, since we'll need a new set
10523 outs.clear();
10524
10525 // here, check if we need to sent tx and start a new one
10526 LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit "
10527 << upper_transaction_weight_limit);
10528 const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 2, extra.size(), bulletproof);
10529 bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || ( estimated_rct_tx_weight >= TX_WEIGHT_TARGET(upper_transaction_weight_limit));
10530
10531 if (try_tx) {
10533 test_tx.version = tx_version;
10534 pending_tx test_ptx;
10535
10536 needed_fee = tx_version == 2 ? 0 : estimate_fee(use_per_byte_fee, use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, base_fee, fee_multiplier, fee_quantization_mask);
10537
10538 // add N - 1 outputs for correct initial fee estimation
10539 for (size_t i = 0; i < ((outputs > 1) ? outputs - 1 : outputs); ++i)
10540 tx.dsts.push_back(tx_destination_entry(1, address, is_subaddress));
10541
10542 LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " destinations and " <<
10543 tx.selected_transfers.size() << " outputs");
10544
10545 transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
10547
10548 auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
10549 needed_fee = tx_version == 2 ? 0 : calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
10550 available_for_fee = test_ptx.fee + test_ptx.change_dts.amount;
10551 for (auto &dt: test_ptx.dests)
10552 available_for_fee += dt.amount;
10553 LOG_PRINT_L2("Made a " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_etn(available_for_fee) << " available for fee (" <<
10554 print_etn(needed_fee) << " needed)");
10555
10556 // add last output, missed for fee estimation
10557 if (outputs > 1)
10558 tx.dsts.push_back(tx_destination_entry(1, address, is_subaddress));
10559
10560 THROW_WALLET_EXCEPTION_IF(needed_fee > available_for_fee, error::wallet_internal_error, "Transaction cannot pay for itself");
10561
10562 do {
10563 LOG_PRINT_L2("We made a tx, adjusting fee and saving it");
10564 // distribute total transferred amount between outputs
10565 uint64_t amount_transferred = available_for_fee - needed_fee; //shouuld be zero for migration transactions
10566 uint64_t dt_amount = amount_transferred / outputs;
10567 // residue is distributed as one atomic unit per output until it reaches zero
10568 uint64_t residue = amount_transferred % outputs;
10569 for (auto &dt: tx.dsts)
10570 {
10571 uint64_t dt_residue = 0;
10572 if (residue > 0)
10573 {
10574 dt_residue = 1;
10575 residue -= 1;
10576 }
10577 dt.amount = dt_amount + dt_residue;
10578 }
10579 transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
10581 txBlob = t_serializable_object_to_blob(test_ptx.tx);
10582 needed_fee = tx_version == 2 ? 0 : calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
10583 LOG_PRINT_L2("Made an attempt at a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_etn(test_ptx.fee) <<
10584 " fee and " << print_etn(test_ptx.change_dts.amount) << " change");
10585 } while (needed_fee > test_ptx.fee);
10586
10587 LOG_PRINT_L2("Made a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_etn(test_ptx.fee) <<
10588 " fee and " << print_etn(test_ptx.change_dts.amount) << " change");
10589
10590 tx.tx = test_tx;
10591 tx.ptx = test_ptx;
10592 tx.weight = get_transaction_weight(test_tx, txBlob.size());
10593 tx.outs = outs;
10594 tx.needed_fee = test_ptx.fee;
10595 accumulated_fee += test_ptx.fee;
10596 accumulated_change += test_ptx.change_dts.amount;
10597 if (!unused_transfers_indices.empty() || !unused_dust_indices.empty())
10598 {
10599 LOG_PRINT_L2("We have more to pay, starting another tx");
10600 txes.push_back(TX());
10601 }
10602 }
10603 }
10604
10605 LOG_PRINT_L1("Done creating " << txes.size() << " transactions, " << print_etn(accumulated_fee) <<
10606 " total fee, " << print_etn(accumulated_change) << " total change");
10607
10609 for (std::vector<TX>::iterator i = txes.begin(); i != txes.end(); ++i)
10610 {
10611 TX &tx = *i;
10613 test_tx.version = tx_version;
10614 pending_tx test_ptx;
10615 transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra,
10617
10618 auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
10619 tx.tx = test_tx;
10620 tx.ptx = test_ptx;
10621 tx.weight = get_transaction_weight(test_tx, txBlob.size());
10622 }
10623
10624 std::vector<wallet2::pending_tx> ptx_vector;
10625 for (std::vector<TX>::iterator i = txes.begin(); i != txes.end(); ++i)
10626 {
10627 TX &tx = *i;
10628 uint64_t tx_etn = 0;
10629 for (size_t idx: tx.selected_transfers)
10630 tx_etn += m_transfers[idx].amount();
10631 LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() <<
10632 " " << get_transaction_hash(tx.ptx.tx) << ": " << get_weight_string(tx.weight) << ", sending " << print_etn(tx_etn) << " in " << tx.selected_transfers.size() <<
10633 " outputs to " << tx.dsts.size() << " destination(s), including " <<
10634 print_etn(tx.ptx.fee) << " fee, " << print_etn(tx.ptx.change_dts.amount) << " change");
10635 ptx_vector.push_back(tx.ptx);
10636 }
10637
10638 uint64_t a = 0;
10639 for (const TX &tx: txes)
10640 {
10641 for (size_t idx: tx.selected_transfers)
10642 {
10643 a += m_transfers[idx].amount();
10644 }
10645 a -= tx.ptx.fee;
10646 }
10647 std::vector<cryptonote::tx_destination_entry> synthetic_dsts(1, cryptonote::tx_destination_entry("", a, address, is_subaddress));
10648 THROW_WALLET_EXCEPTION_IF(!sanity_check(ptx_vector, synthetic_dsts), error::wallet_internal_error, "Created transaction(s) failed sanity check");
10649
10650 // if we made it this far, we're OK to actually send the transactions
10651 return ptx_vector;
10652}
10653
10654//----------------------------------------------------------------------------------------------------
10655void wallet2::cold_tx_aux_import(const std::vector<pending_tx> & ptx, const std::vector<std::string> & tx_device_aux)
10656{
10657 CHECK_AND_ASSERT_THROW_MES(ptx.size() == tx_device_aux.size(), "TX aux has invalid size");
10658 for (size_t i = 0; i < ptx.size(); ++i){
10659 crypto::hash txid;
10660 txid = get_transaction_hash(ptx[i].tx);
10661 set_tx_device_aux(txid, tx_device_aux[i]);
10662 }
10663}
10664//----------------------------------------------------------------------------------------------------
10665void wallet2::cold_sign_tx(const std::vector<pending_tx>& ptx_vector, signed_tx_set &exported_txs, std::vector<cryptonote::address_parse_info> &dsts_info, std::vector<std::string> & tx_device_aux)
10666{
10667 auto & hwdev = get_account().get_device();
10668 if (!hwdev.has_tx_cold_sign()){
10669 throw std::invalid_argument("Device does not support cold sign protocol");
10670 }
10671
10672 unsigned_tx_set txs;
10673 for (auto &tx: ptx_vector)
10674 {
10675 txs.txes.push_back(get_construction_data_with_decrypted_short_payment_id(tx, m_account.get_device()));
10676 }
10677 txs.transfers = std::make_pair(0, m_transfers);
10678
10679 auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev);
10680 CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface");
10681
10682 hw::tx_aux_data aux_data;
10683 hw::wallet_shim wallet_shim;
10684 setup_shim(&wallet_shim, this);
10685 aux_data.tx_recipients = dsts_info;
10686 aux_data.bp_version = use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1;
10687 dev_cold->tx_sign(&wallet_shim, txs, exported_txs, aux_data);
10688 tx_device_aux = aux_data.tx_device_aux;
10689
10690 MDEBUG("Signed tx data from hw: " << exported_txs.ptx.size() << " transactions");
10691 for (auto &c_ptx: exported_txs.ptx) LOG_PRINT_L0(cryptonote::obj_to_json_str(c_ptx.tx));
10692}
10693//----------------------------------------------------------------------------------------------------
10695 auto & hwdev = get_account().get_device();
10696 CHECK_AND_ASSERT_THROW_MES(hwdev.has_ki_cold_sync(), "Device does not support cold ki sync protocol");
10697
10698 auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev);
10699 CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface");
10700
10701 std::vector<std::pair<crypto::key_image, crypto::signature>> ski;
10702 hw::wallet_shim wallet_shim;
10703 setup_shim(&wallet_shim, this);
10704
10705 dev_cold->ki_sync(&wallet_shim, m_transfers, ski);
10706
10707 // Call COMMAND_RPC_IS_KEY_IMAGE_SPENT only if daemon is trusted.
10708 uint64_t import_res = import_key_images(ski, 0, spent, unspent, is_trusted_daemon());
10709 m_device_last_key_image_sync = time(NULL);
10710
10711 return import_res;
10712}
10713//----------------------------------------------------------------------------------------------------
10715{
10716 boost::optional<std::string> result = m_node_rpc_proxy.get_earliest_height(version, earliest_height);
10717 throw_on_rpc_response_error(result, "get_hard_fork_info");
10718}
10719//----------------------------------------------------------------------------------------------------
10721{
10722 // TODO: How to get fork rule info from light wallet node?
10723 if(m_light_wallet)
10724 return true;
10725 uint64_t height, earliest_height;
10726 boost::optional<std::string> result = m_node_rpc_proxy.get_height(height);
10727 throw_on_rpc_response_error(result, "get_info");
10728 result = m_node_rpc_proxy.get_earliest_height(version, earliest_height);
10729 throw_on_rpc_response_error(result, "get_hard_fork_info");
10730
10731 bool close_enough = (int64_t)height >= (int64_t)earliest_height - early_blocks && earliest_height != std::numeric_limits<uint64_t>::max() && version <= CURRENT_HARDFORK_VERSION; // start using the rules that many blocks beforehand
10732 if (close_enough)
10733 LOG_PRINT_L2("Using v" << (unsigned)version << " rules");
10734 else
10735 LOG_PRINT_L2("Not using v" << (unsigned)version << " rules");
10736 return close_enough;
10737}
10738//----------------------------------------------------------------------------------------------------
10739uint64_t wallet2::get_upper_transaction_weight_limit() const
10740{
10741 if (m_upper_transaction_weight_limit > 0)
10742 return m_upper_transaction_weight_limit;
10743 uint64_t full_reward_zone = use_fork_rules(10, 10) ?
10744 CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V10 : use_fork_rules(8, 10) ?
10745 CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V8 : use_fork_rules(5, 10) ?
10746 CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : use_fork_rules(2, 10) ?
10748
10749 if (use_fork_rules(8, 10))
10750 return full_reward_zone / 2 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
10751 else
10752 return full_reward_zone - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
10753}
10754//----------------------------------------------------------------------------------------------------
10755std::vector<size_t> wallet2::select_available_outputs(const std::function<bool(const transfer_details &td)> &f) const
10756{
10757 std::vector<size_t> outputs;
10758 size_t n = 0;
10759 for (transfer_container::const_iterator i = m_transfers.begin(); i != m_transfers.end(); ++i, ++n)
10760 {
10761 if (i->m_spent)
10762 continue;
10763 if (i->m_frozen)
10764 continue;
10765 if (i->m_key_image_partial)
10766 continue;
10767 if (!is_transfer_unlocked(*i))
10768 continue;
10769 if (f(*i))
10770 outputs.push_back(n);
10771 }
10772 return outputs;
10773}
10774//----------------------------------------------------------------------------------------------------
10775std::vector<uint64_t> wallet2::get_unspent_amounts_vector() const
10776{
10777 std::set<uint64_t> set;
10778 for (const auto &td: m_transfers)
10779 {
10780 if (!td.m_spent && !td.m_frozen)
10781 set.insert(td.is_rct() ? 0 : td.amount());
10782 }
10783 std::vector<uint64_t> vector;
10784 vector.reserve(set.size());
10785 for (const auto &i: set)
10786 {
10787 vector.push_back(i);
10788 }
10789 return vector;
10790}
10791//----------------------------------------------------------------------------------------------------
10792std::vector<size_t> wallet2::select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct)
10793{
10796 m_daemon_rpc_mutex.lock();
10797 if (is_trusted_daemon())
10798 req_t.amounts = get_unspent_amounts_vector();
10799 req_t.min_count = count;
10800 req_t.max_count = 0;
10801 req_t.unlocked = unlocked;
10802 req_t.recent_cutoff = 0;
10803 bool r = invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, rpc_timeout);
10804 m_daemon_rpc_mutex.unlock();
10805 THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "select_available_outputs_from_histogram");
10806 THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
10808
10809 std::set<uint64_t> mixable;
10810 for (const auto &i: resp_t.histogram)
10811 {
10812 mixable.insert(i.amount);
10813 }
10814
10815 return select_available_outputs([mixable, atleast, allow_rct](const transfer_details &td) {
10816 if (!allow_rct && td.is_rct())
10817 return false;
10818 const uint64_t amount = td.is_rct() ? 0 : td.amount();
10819 if (atleast) {
10820 if (mixable.find(amount) != mixable.end())
10821 return true;
10822 }
10823 else {
10824 if (mixable.find(amount) == mixable.end())
10825 return true;
10826 }
10827 return false;
10828 });
10829}
10830//----------------------------------------------------------------------------------------------------
10832{
10835 m_daemon_rpc_mutex.lock();
10836 req_t.amounts.push_back(0);
10837 req_t.min_count = 0;
10838 req_t.max_count = 0;
10839 req_t.unlocked = true;
10840 req_t.recent_cutoff = 0;
10841 bool r = invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, rpc_timeout);
10842 m_daemon_rpc_mutex.unlock();
10844 THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
10846 THROW_WALLET_EXCEPTION_IF(resp_t.histogram.size() != 1, error::get_histogram_error, "Expected exactly one response");
10847 THROW_WALLET_EXCEPTION_IF(resp_t.histogram[0].amount != 0, error::get_histogram_error, "Expected 0 amount");
10848
10849 return resp_t.histogram[0].total_instances;
10850}
10851//----------------------------------------------------------------------------------------------------
10853{
10854 THROW_WALLET_EXCEPTION_IF(idx >= m_transfers.size(), error::wallet_internal_error, "Bad transfer index");
10855 return m_transfers[idx];
10856}
10857//----------------------------------------------------------------------------------------------------
10859{
10860 // request all outputs with less instances than the min ring size
10861 return select_available_outputs_from_histogram(get_min_ring_size(), false, true, false);
10862}
10863//----------------------------------------------------------------------------------------------------
10865{
10866 // request all outputs with at least as many instances as the min ring size
10868}
10869//----------------------------------------------------------------------------------------------------
10870std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions()
10871{
10872 // From hard fork 1, we don't consider small amounts to be dust anymore
10873 const bool hf1_rules = use_fork_rules(2, 10); // first hard fork has version 2
10874 tx_dust_policy dust_policy(hf1_rules ? 0 : ::config::DEFAULT_DUST_THRESHOLD);
10875
10876 uint8_t tx_version = this->public_transactions_required() ? 3 : 1;
10877 const uint64_t base_fee = get_base_fee();
10878
10879 // may throw
10880 std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs();
10881 size_t num_dust_outputs = unmixable_outputs.size();
10882
10883 if (num_dust_outputs == 0)
10884 {
10885 return std::vector<wallet2::pending_tx>();
10886 }
10887
10888 // split in "dust" and "non dust" to make it easier to select outputs
10889 std::vector<size_t> unmixable_transfer_outputs, unmixable_dust_outputs;
10890 for (auto n: unmixable_outputs)
10891 {
10892 if (m_transfers[n].amount() < base_fee)
10893 unmixable_dust_outputs.push_back(n);
10894 else
10895 unmixable_transfer_outputs.push_back(n);
10896 }
10897
10898 return create_transactions_from(m_account_public_address, false, 1, unmixable_transfer_outputs, unmixable_dust_outputs, 0 /*fake_outs_count */, 0 /* unlock_time */, 1 /*priority */, std::vector<uint8_t>(), tx_version);
10899}
10900//----------------------------------------------------------------------------------------------------
10902{
10903 // may throw
10904 std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs();
10905 for (size_t idx : unmixable_outputs)
10906 {
10907 freeze(idx);
10908 }
10909}
10910
10911bool wallet2::get_tx_key_cached(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const
10912{
10913 additional_tx_keys.clear();
10914 const std::unordered_map<crypto::hash, crypto::secret_key>::const_iterator i = m_tx_keys.find(txid);
10915 if (i == m_tx_keys.end())
10916 return false;
10917 tx_key = i->second;
10918 const auto j = m_additional_tx_keys.find(txid);
10919 if (j != m_additional_tx_keys.end())
10920 additional_tx_keys = j->second;
10921 return true;
10922}
10923//----------------------------------------------------------------------------------------------------
10924bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys)
10925{
10926 bool r = get_tx_key_cached(txid, tx_key, additional_tx_keys);
10927 if (r)
10928 {
10929 return true;
10930 }
10931
10932 auto & hwdev = get_account().get_device();
10933
10934 // So far only Cold protocol devices are supported.
10936 {
10937 return false;
10938 }
10939
10940 const auto tx_data_it = m_tx_device.find(txid);
10941 if (tx_data_it == m_tx_device.end())
10942 {
10943 MDEBUG("Aux data not found for txid: " << txid);
10944 return false;
10945 }
10946
10947 auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev);
10948 CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface");
10949 if (!dev_cold->is_get_tx_key_supported())
10950 {
10951 MDEBUG("get_tx_key not supported by the device");
10952 return false;
10953 }
10954
10956 dev_cold->load_tx_key_data(tx_key_data, tx_data_it->second);
10957
10958 // Load missing tx prefix hash
10959 if (tx_key_data.tx_prefix_hash.empty())
10960 {
10963 req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
10964 req.decode_as_json = false;
10965 req.prune = true;
10966 m_daemon_rpc_mutex.lock();
10967 bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout);
10968 m_daemon_rpc_mutex.unlock();
10969 THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
10970 error::wallet_internal_error, "Failed to get transaction from daemon");
10971
10973 crypto::hash tx_hash{};
10974 cryptonote::blobdata tx_data;
10975 crypto::hash tx_prefix_hash{};
10976 ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
10977 THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
10978 THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx, tx_hash, tx_prefix_hash),
10979 error::wallet_internal_error, "Failed to validate transaction from daemon");
10981 "Failed to get the right transaction from daemon");
10982
10983 tx_key_data.tx_prefix_hash = std::string(tx_prefix_hash.data, 32);
10984 }
10985
10986 std::vector<crypto::secret_key> tx_keys;
10987 dev_cold->get_tx_key(tx_keys, tx_key_data, m_account.get_keys().m_view_secret_key);
10988 if (tx_keys.empty())
10989 {
10990 return false;
10991 }
10992
10993 tx_key = tx_keys[0];
10994 tx_keys.erase(tx_keys.begin());
10995 additional_tx_keys = tx_keys;
10996
10997 return true;
10998}
10999//----------------------------------------------------------------------------------------------------
11000void wallet2::set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys)
11001{
11002 // fetch tx from daemon and check if secret keys agree with corresponding public keys
11004 req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
11005 req.decode_as_json = false;
11006 req.prune = true;
11008 bool r;
11009 {
11010 const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
11011 r = invoke_http_json("/gettransactions", req, res, rpc_timeout);
11012 }
11017 "daemon returned wrong response for gettransactions, wrong txs count = " +
11018 std::to_string(res.txs.size()) + ", expected 1");
11020 crypto::hash tx_hash;
11021 THROW_WALLET_EXCEPTION_IF(!get_pruned_tx(res.txs[0], tx, tx_hash), error::wallet_internal_error,
11022 "Failed to get transaction from daemon");
11023 THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "txid mismatch");
11024 std::vector<tx_extra_field> tx_extra_fields;
11025 THROW_WALLET_EXCEPTION_IF(!parse_tx_extra(tx.extra, tx_extra_fields), error::wallet_internal_error, "Transaction extra has unsupported format");
11026 tx_extra_pub_key pub_key_field;
11027 bool found = false;
11028 size_t index = 0;
11029 while (find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, index++))
11030 {
11031 crypto::public_key calculated_pub_key;
11032 crypto::secret_key_to_public_key(tx_key, calculated_pub_key);
11033 if (calculated_pub_key == pub_key_field.pub_key)
11034 {
11035 found = true;
11036 break;
11037 }
11038 }
11039 THROW_WALLET_EXCEPTION_IF(!found, error::wallet_internal_error, "Given tx secret key doesn't agree with the tx public key in the blockchain");
11040 tx_extra_additional_pub_keys additional_tx_pub_keys;
11041 find_tx_extra_field_by_type(tx_extra_fields, additional_tx_pub_keys);
11042 THROW_WALLET_EXCEPTION_IF(additional_tx_keys.size() != additional_tx_pub_keys.data.size(), error::wallet_internal_error, "The number of additional tx secret keys doesn't agree with the number of additional tx public keys in the blockchain" );
11043 m_tx_keys.insert(std::make_pair(txid, tx_key));
11044 m_additional_tx_keys.insert(std::make_pair(txid, additional_tx_keys));
11045}
11046//----------------------------------------------------------------------------------------------------
11047std::string wallet2::get_spend_proof(const crypto::hash &txid, const std::string &message)
11048{
11050 "get_spend_proof requires spend secret key and is not available for a watch-only wallet");
11051
11052 // fetch tx from daemon
11054 req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
11055 req.decode_as_json = false;
11056 req.prune = true;
11058 bool r;
11059 {
11060 const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
11061 r = invoke_http_json("/gettransactions", req, res, rpc_timeout);
11062 }
11067 "daemon returned wrong response for gettransactions, wrong txs count = " +
11068 std::to_string(res.txs.size()) + ", expected 1");
11069
11071 crypto::hash tx_hash;
11072 THROW_WALLET_EXCEPTION_IF(!get_pruned_tx(res.txs[0], tx, tx_hash), error::wallet_internal_error, "Failed to get tx from daemon");
11073
11074 std::vector<std::vector<crypto::signature>> signatures;
11075
11076 // get signature prefix hash
11077 std::string sig_prefix_data((const char*)&txid, sizeof(crypto::hash));
11078 sig_prefix_data += message;
11079 crypto::hash sig_prefix_hash;
11080 crypto::cn_fast_hash(sig_prefix_data.data(), sig_prefix_data.size(), sig_prefix_hash);
11081
11082 for(size_t i = 0; i < tx.vin.size(); ++i)
11083 {
11084 const txin_to_key* const in_key = boost::get<txin_to_key>(std::addressof(tx.vin[i]));
11085 if (in_key == nullptr)
11086 continue;
11087
11088 // check if the key image belongs to us
11089 const auto found = m_key_images.find(in_key->k_image);
11090 if(found == m_key_images.end())
11091 {
11092 THROW_WALLET_EXCEPTION_IF(i > 0, error::wallet_internal_error, "subset of key images belong to us, very weird!");
11093 THROW_WALLET_EXCEPTION_IF(true, error::wallet_internal_error, "This tx wasn't generated by this wallet!");
11094 }
11095
11096 // derive the real output keypair
11097 const transfer_details& in_td = m_transfers[found->second];
11098 const txout_to_key* const in_tx_out_pkey = boost::get<txout_to_key>(std::addressof(in_td.m_tx.vout[in_td.m_internal_output_index].target));
11099 THROW_WALLET_EXCEPTION_IF(in_tx_out_pkey == nullptr, error::wallet_internal_error, "Output is not txout_to_key");
11100 const crypto::public_key in_tx_pub_key = get_tx_pub_key_from_extra(in_td.m_tx, in_td.m_pk_index);
11101 const std::vector<crypto::public_key> in_additionakl_tx_pub_keys = get_additional_tx_pub_keys_from_extra(in_td.m_tx);
11102 keypair in_ephemeral;
11103 crypto::key_image in_img;
11104 THROW_WALLET_EXCEPTION_IF(!generate_key_image_helper(m_account.get_keys(), m_subaddresses, in_tx_out_pkey->key, in_tx_pub_key, in_additionakl_tx_pub_keys, in_td.m_internal_output_index, in_ephemeral, in_img, m_account.get_device(), m_account_major_offset),
11105 error::wallet_internal_error, "failed to generate key image");
11106 THROW_WALLET_EXCEPTION_IF(in_key->k_image != in_img, error::wallet_internal_error, "key image mismatch");
11107
11108 // get output pubkeys in the ring
11109 const std::vector<uint64_t> absolute_offsets = cryptonote::relative_output_offsets_to_absolute(in_key->key_offsets);
11110 const size_t ring_size = in_key->key_offsets.size();
11111 THROW_WALLET_EXCEPTION_IF(absolute_offsets.size() != ring_size, error::wallet_internal_error, "absolute offsets size is wrong");
11113 req.outputs.resize(ring_size);
11114 for (size_t j = 0; j < ring_size; ++j)
11115 {
11116 req.outputs[j].amount = in_key->amount;
11117 req.outputs[j].index = absolute_offsets[j];
11118 }
11120 bool r;
11121 {
11122 const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
11123 r = invoke_http_bin("/get_outs.bin", req, res, rpc_timeout);
11124 }
11129 "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
11130 std::to_string(res.outs.size()) + ", expected " + std::to_string(ring_size));
11131
11132 // copy pubkey pointers
11133 std::vector<const crypto::public_key *> p_output_keys;
11134 for (const COMMAND_RPC_GET_OUTPUTS_BIN::outkey &out : res.outs)
11135 p_output_keys.push_back(&out.key);
11136
11137 // figure out real output index and secret key
11138 size_t sec_index = -1;
11139 for (size_t j = 0; j < ring_size; ++j)
11140 {
11141 if (res.outs[j].key == in_ephemeral.pub)
11142 {
11143 sec_index = j;
11144 break;
11145 }
11146 }
11147 THROW_WALLET_EXCEPTION_IF(sec_index >= ring_size, error::wallet_internal_error, "secret index not found");
11148
11149 // generate ring sig for this input
11150 signatures.push_back(std::vector<crypto::signature>());
11151 std::vector<crypto::signature>& sigs = signatures.back();
11152 sigs.resize(in_key->key_offsets.size());
11153 crypto::generate_ring_signature(sig_prefix_hash, in_key->k_image, p_output_keys, in_ephemeral.sec, sec_index, sigs.data());
11154 }
11155
11156 std::string sig_str = "SpendProofV1";
11157 for (const std::vector<crypto::signature>& ring_sig : signatures)
11158 for (const crypto::signature& sig : ring_sig)
11159 sig_str += tools::base58::encode(std::string((const char *)&sig, sizeof(crypto::signature)));
11160 return sig_str;
11161}
11162//----------------------------------------------------------------------------------------------------
11163bool wallet2::check_spend_proof(const crypto::hash &txid, const std::string &message, const std::string &sig_str)
11164{
11165 const std::string header = "SpendProofV1";
11166 const size_t header_len = header.size();
11167 THROW_WALLET_EXCEPTION_IF(sig_str.size() < header_len || sig_str.substr(0, header_len) != header, error::wallet_internal_error,
11168 "Signature header check error");
11169
11170 // fetch tx from daemon
11172 req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
11173 req.decode_as_json = false;
11174 req.prune = true;
11176 bool r;
11177 {
11178 const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
11179 r = invoke_http_json("/gettransactions", req, res, rpc_timeout);
11180 }
11185 "daemon returned wrong response for gettransactions, wrong txs count = " +
11186 std::to_string(res.txs.size()) + ", expected 1");
11187
11189 crypto::hash tx_hash;
11190 THROW_WALLET_EXCEPTION_IF(!get_pruned_tx(res.txs[0], tx, tx_hash), error::wallet_internal_error, "failed to get tx from daemon");
11191
11192 // check signature size
11193 size_t num_sigs = 0;
11194 for(size_t i = 0; i < tx.vin.size(); ++i)
11195 {
11196 const txin_to_key* const in_key = boost::get<txin_to_key>(std::addressof(tx.vin[i]));
11197 if (in_key != nullptr)
11198 num_sigs += in_key->key_offsets.size();
11199 }
11200 std::vector<std::vector<crypto::signature>> signatures = { std::vector<crypto::signature>(1) };
11201 const size_t sig_len = tools::base58::encode(std::string((const char *)&signatures[0][0], sizeof(crypto::signature))).size();
11202 if( sig_str.size() != header_len + num_sigs * sig_len ) {
11203 return false;
11204 }
11205
11206 // decode base58
11207 signatures.clear();
11208 size_t offset = header_len;
11209 for(size_t i = 0; i < tx.vin.size(); ++i)
11210 {
11211 const txin_to_key* const in_key = boost::get<txin_to_key>(std::addressof(tx.vin[i]));
11212 if (in_key == nullptr)
11213 continue;
11214 signatures.resize(signatures.size() + 1);
11215 signatures.back().resize(in_key->key_offsets.size());
11216 for (size_t j = 0; j < in_key->key_offsets.size(); ++j)
11217 {
11218 std::string sig_decoded;
11219 THROW_WALLET_EXCEPTION_IF(!tools::base58::decode(sig_str.substr(offset, sig_len), sig_decoded), error::wallet_internal_error, "Signature decoding error");
11220 THROW_WALLET_EXCEPTION_IF(sizeof(crypto::signature) != sig_decoded.size(), error::wallet_internal_error, "Signature decoding error");
11221 memcpy(&signatures.back()[j], sig_decoded.data(), sizeof(crypto::signature));
11222 offset += sig_len;
11223 }
11224 }
11225
11226 // get signature prefix hash
11227 std::string sig_prefix_data((const char*)&txid, sizeof(crypto::hash));
11228 sig_prefix_data += message;
11229 crypto::hash sig_prefix_hash;
11230 crypto::cn_fast_hash(sig_prefix_data.data(), sig_prefix_data.size(), sig_prefix_hash);
11231
11232 std::vector<std::vector<crypto::signature>>::const_iterator sig_iter = signatures.cbegin();
11233 for(size_t i = 0; i < tx.vin.size(); ++i)
11234 {
11235 const txin_to_key* const in_key = boost::get<txin_to_key>(std::addressof(tx.vin[i]));
11236 if (in_key == nullptr)
11237 continue;
11238
11239 // get output pubkeys in the ring
11241 const std::vector<uint64_t> absolute_offsets = cryptonote::relative_output_offsets_to_absolute(in_key->key_offsets);
11242 req.outputs.resize(absolute_offsets.size());
11243 for (size_t j = 0; j < absolute_offsets.size(); ++j)
11244 {
11245 req.outputs[j].amount = in_key->amount;
11246 req.outputs[j].index = absolute_offsets[j];
11247 }
11249 bool r;
11250 {
11251 const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
11252 r = invoke_http_bin("/get_outs.bin", req, res, rpc_timeout);
11253 }
11257 THROW_WALLET_EXCEPTION_IF(res.outs.size() != req.outputs.size(), error::wallet_internal_error,
11258 "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
11259 std::to_string(res.outs.size()) + ", expected " + std::to_string(req.outputs.size()));
11260
11261 // copy pointers
11262 std::vector<const crypto::public_key *> p_output_keys;
11263 for (const COMMAND_RPC_GET_OUTPUTS_BIN::outkey &out : res.outs)
11264 p_output_keys.push_back(&out.key);
11265
11266 // check this ring
11267 if (!crypto::check_ring_signature(sig_prefix_hash, in_key->k_image, p_output_keys, sig_iter->data()))
11268 return false;
11269 ++sig_iter;
11270 }
11271 THROW_WALLET_EXCEPTION_IF(sig_iter != signatures.cend(), error::wallet_internal_error, "Signature iterator didn't reach the end");
11272 return true;
11273}
11274//----------------------------------------------------------------------------------------------------
11275
11276void wallet2::check_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations)
11277{
11278 crypto::key_derivation derivation;
11280 "Failed to generate key derivation from supplied parameters");
11281
11282 std::vector<crypto::key_derivation> additional_derivations;
11283 additional_derivations.resize(additional_tx_keys.size());
11284 for (size_t i = 0; i < additional_tx_keys.size(); ++i)
11285 THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(address.m_view_public_key, additional_tx_keys[i], additional_derivations[i]), error::wallet_internal_error,
11286 "Failed to generate key derivation from supplied parameters");
11287
11288 check_tx_key_helper(txid, derivation, additional_derivations, address, received, in_pool, confirmations);
11289}
11290
11291void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, const cryptonote::account_public_address &address, uint64_t &received) const
11292{
11293 received = 0;
11294
11295 for (size_t n = 0; n < tx.vout.size(); ++n)
11296 {
11297 const cryptonote::txout_to_key* const out_key = boost::get<cryptonote::txout_to_key>(std::addressof(tx.vout[n].target));
11298 if (!out_key)
11299 continue;
11300
11301 crypto::public_key derived_out_key;
11302 bool r = crypto::derive_public_key(derivation, n, address.m_spend_public_key, derived_out_key);
11303 THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to derive public key");
11304 bool found = out_key->key == derived_out_key;
11305 crypto::key_derivation found_derivation = derivation;
11306 if (!found && !additional_derivations.empty())
11307 {
11308 r = crypto::derive_public_key(additional_derivations[n], n, address.m_spend_public_key, derived_out_key);
11309 THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to derive public key");
11310 found = out_key->key == derived_out_key;
11311 found_derivation = additional_derivations[n];
11312 }
11313
11314 if (found)
11315 {
11316 uint64_t amount;
11317 amount = tx.vout[n].amount;
11318 received += amount;
11319 }
11320 }
11321}
11322
11323void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations)
11324{
11327 req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
11328 req.decode_as_json = false;
11329 req.prune = true;
11330 m_daemon_rpc_mutex.lock();
11331 bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout);
11332 m_daemon_rpc_mutex.unlock();
11333 THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
11334 error::wallet_internal_error, "Failed to get transaction from daemon");
11335
11337 crypto::hash tx_hash;
11338 if (res.txs.size() == 1)
11339 {
11340 ok = get_pruned_tx(res.txs.front(), tx, tx_hash);
11341 THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
11342 }
11343 else
11344 {
11345 cryptonote::blobdata tx_data;
11346 ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
11347 THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
11349 error::wallet_internal_error, "Failed to validate transaction from daemon");
11351 }
11352
11354 "Failed to get the right transaction from daemon");
11355 THROW_WALLET_EXCEPTION_IF(!additional_derivations.empty() && additional_derivations.size() != tx.vout.size(), error::wallet_internal_error,
11356 "The size of additional derivations is wrong");
11357
11358 check_tx_key_helper(tx, derivation, additional_derivations, address, received);
11359
11360 in_pool = res.txs.front().in_pool;
11361 confirmations = 0;
11362 if (!in_pool)
11363 {
11364 std::string err;
11365 uint64_t bc_height = get_daemon_blockchain_height(err);
11366 if (err.empty())
11367 confirmations = bc_height - res.txs.front().block_height;
11368 }
11369}
11370
11371std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message)
11372{
11373 // fetch tx pubkey from the daemon
11376 req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
11377 req.decode_as_json = false;
11378 req.prune = true;
11379 m_daemon_rpc_mutex.lock();
11380 bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout);
11381 m_daemon_rpc_mutex.unlock();
11382 THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
11383 error::wallet_internal_error, "Failed to get transaction from daemon");
11384
11386 crypto::hash tx_hash;
11387 if (res.txs.size() == 1)
11388 {
11389 ok = get_pruned_tx(res.txs.front(), tx, tx_hash);
11390 THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
11391 }
11392 else
11393 {
11394 cryptonote::blobdata tx_data;
11395 ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
11396 THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
11398 error::wallet_internal_error, "Failed to validate transaction from daemon");
11400 }
11401
11402 CHECK_AND_ASSERT_THROW_MES(tx.version == 1, "Tx proofs are for v1 transactions only");
11403 THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "Failed to get the right transaction from daemon");
11404
11405 // determine if the address is found in the subaddress hash table (i.e. whether the proof is outbound or inbound)
11407 std::vector<crypto::secret_key> additional_tx_keys;
11408 const bool is_out = m_subaddresses.count(address.m_spend_public_key) == 0;
11409 if (is_out)
11410 {
11411 THROW_WALLET_EXCEPTION_IF(!get_tx_key(txid, tx_key, additional_tx_keys), error::wallet_internal_error, "Tx secret key wasn't found in the wallet file.");
11412 }
11413
11414 return get_tx_proof(tx, tx_key, additional_tx_keys, address, is_subaddress, message);
11415}
11416
11417std::string wallet2::get_tx_proof(const cryptonote::transaction &tx, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message) const
11418{
11419 hw::device &hwdev = m_account.get_device();
11420 rct::key aP;
11421 // determine if the address is found in the subaddress hash table (i.e. whether the proof is outbound or inbound)
11422 const bool is_out = m_subaddresses.count(address.m_spend_public_key) == 0;
11423
11425 std::string prefix_data((const char*)&txid, sizeof(crypto::hash));
11426 prefix_data += message;
11427 crypto::hash prefix_hash;
11428 crypto::cn_fast_hash(prefix_data.data(), prefix_data.size(), prefix_hash);
11429
11430 std::vector<crypto::public_key> shared_secret;
11431 std::vector<crypto::signature> sig;
11432 std::string sig_str;
11433 if (is_out)
11434 {
11435 const size_t num_sigs = 1 + additional_tx_keys.size();
11436 shared_secret.resize(num_sigs);
11437 sig.resize(num_sigs);
11438
11439 hwdev.scalarmultKey(aP, rct::pk2rct(address.m_view_public_key), rct::sk2rct(tx_key));
11440 shared_secret[0] = rct::rct2pk(aP);
11441 crypto::public_key tx_pub_key;
11442 if (is_subaddress)
11443 {
11444 hwdev.scalarmultKey(aP, rct::pk2rct(address.m_spend_public_key), rct::sk2rct(tx_key));
11445 tx_pub_key = rct2pk(aP);
11446 hwdev.generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[0], tx_key, sig[0]);
11447 }
11448 else
11449 {
11450 hwdev.secret_key_to_public_key(tx_key, tx_pub_key);
11451 hwdev.generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, boost::none, shared_secret[0], tx_key, sig[0]);
11452 }
11453 for (size_t i = 1; i < num_sigs; ++i)
11454 {
11455 hwdev.scalarmultKey(aP, rct::pk2rct(address.m_view_public_key), rct::sk2rct(additional_tx_keys[i - 1]));
11456 shared_secret[i] = rct::rct2pk(aP);
11457 if (is_subaddress)
11458 {
11459 hwdev.scalarmultKey(aP, rct::pk2rct(address.m_spend_public_key), rct::sk2rct(additional_tx_keys[i - 1]));
11460 tx_pub_key = rct2pk(aP);
11461 hwdev.generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[i], additional_tx_keys[i - 1], sig[i]);
11462 }
11463 else
11464 {
11465 hwdev.secret_key_to_public_key(additional_tx_keys[i - 1], tx_pub_key);
11466 hwdev.generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, boost::none, shared_secret[i], additional_tx_keys[i - 1], sig[i]);
11467 }
11468 }
11469 sig_str = std::string("OutProofV1");
11470 }
11471 else
11472 {
11474 THROW_WALLET_EXCEPTION_IF(tx_pub_key == null_pkey, error::wallet_internal_error, "Tx pubkey was not found");
11475
11476 std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(tx);
11477 const size_t num_sigs = 1 + additional_tx_pub_keys.size();
11478 shared_secret.resize(num_sigs);
11479 sig.resize(num_sigs);
11480
11481 const crypto::secret_key& a = m_account.get_keys().m_view_secret_key;
11482 hwdev.scalarmultKey(aP, rct::pk2rct(tx_pub_key), rct::sk2rct(a));
11483 shared_secret[0] = rct2pk(aP);
11484 if (is_subaddress)
11485 {
11486 hwdev.generate_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, address.m_spend_public_key, shared_secret[0], a, sig[0]);
11487 }
11488 else
11489 {
11490 hwdev.generate_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, boost::none, shared_secret[0], a, sig[0]);
11491 }
11492 for (size_t i = 1; i < num_sigs; ++i)
11493 {
11494 hwdev.scalarmultKey(aP,rct::pk2rct(additional_tx_pub_keys[i - 1]), rct::sk2rct(a));
11495 shared_secret[i] = rct2pk(aP);
11496 if (is_subaddress)
11497 {
11498 hwdev.generate_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[i - 1], address.m_spend_public_key, shared_secret[i], a, sig[i]);
11499 }
11500 else
11501 {
11502 hwdev.generate_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[i - 1], boost::none, shared_secret[i], a, sig[i]);
11503 }
11504 }
11505 sig_str = std::string("InProofV1");
11506 }
11507 const size_t num_sigs = shared_secret.size();
11508
11509 // check if this address actually received any funds
11510 crypto::key_derivation derivation;
11511 THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(shared_secret[0], rct::rct2sk(rct::I), derivation), error::wallet_internal_error, "Failed to generate key derivation");
11512 std::vector<crypto::key_derivation> additional_derivations(num_sigs - 1);
11513 for (size_t i = 1; i < num_sigs; ++i)
11514 THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(shared_secret[i], rct::rct2sk(rct::I), additional_derivations[i - 1]), error::wallet_internal_error, "Failed to generate key derivation");
11515 uint64_t received;
11516 check_tx_key_helper(tx, derivation, additional_derivations, address, received);
11517 THROW_WALLET_EXCEPTION_IF(!received, error::wallet_internal_error, tr("No funds received in this tx."));
11518
11519 // concatenate all signature strings
11520 for (size_t i = 0; i < num_sigs; ++i)
11521 sig_str +=
11522 tools::base58::encode(std::string((const char *)&shared_secret[i], sizeof(crypto::public_key))) +
11523 tools::base58::encode(std::string((const char *)&sig[i], sizeof(crypto::signature)));
11524 return sig_str;
11525}
11526
11527bool wallet2::check_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message, const std::string &sig_str, uint64_t &received, bool &in_pool, uint64_t &confirmations)
11528{
11529 // fetch tx pubkey from the daemon
11532 req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
11533 req.decode_as_json = false;
11534 req.prune = true;
11535 m_daemon_rpc_mutex.lock();
11536 bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout);
11537 m_daemon_rpc_mutex.unlock();
11538 THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
11539 error::wallet_internal_error, "Failed to get transaction from daemon");
11540
11542 crypto::hash tx_hash;
11543 if (res.txs.size() == 1)
11544 {
11545 ok = get_pruned_tx(res.txs.front(), tx, tx_hash);
11546 THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
11547 }
11548 else
11549 {
11550 cryptonote::blobdata tx_data;
11551 ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
11552 THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
11554 error::wallet_internal_error, "Failed to validate transaction from daemon");
11556 }
11557
11558 THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "Failed to get the right transaction from daemon");
11559
11560 if (!check_tx_proof(tx, address, is_subaddress, message, sig_str, received))
11561 return false;
11562
11563 in_pool = res.txs.front().in_pool;
11564 confirmations = 0;
11565 if (!in_pool)
11566 {
11567 std::string err;
11568 uint64_t bc_height = get_daemon_blockchain_height(err);
11569 if (err.empty())
11570 confirmations = bc_height - res.txs.front().block_height;
11571 }
11572
11573 return true;
11574}
11575
11576bool wallet2::check_tx_proof(const cryptonote::transaction &tx, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message, const std::string &sig_str, uint64_t &received) const
11577{
11578 CHECK_AND_ASSERT_THROW_MES(tx.version == 1, "Tx proofs are for v1 transactions only");
11579 const bool is_out = sig_str.substr(0, 3) == "Out";
11580 const std::string header = is_out ? "OutProofV1" : "InProofV1";
11581 const size_t header_len = header.size();
11582 THROW_WALLET_EXCEPTION_IF(sig_str.size() < header_len || sig_str.substr(0, header_len) != header, error::wallet_internal_error,
11583 "Signature header check error");
11584
11585 // decode base58
11586 std::vector<crypto::public_key> shared_secret(1);
11587 std::vector<crypto::signature> sig(1);
11588 const size_t pk_len = tools::base58::encode(std::string((const char *)&shared_secret[0], sizeof(crypto::public_key))).size();
11589 const size_t sig_len = tools::base58::encode(std::string((const char *)&sig[0], sizeof(crypto::signature))).size();
11590 const size_t num_sigs = (sig_str.size() - header_len) / (pk_len + sig_len);
11591 THROW_WALLET_EXCEPTION_IF(sig_str.size() != header_len + num_sigs * (pk_len + sig_len), error::wallet_internal_error,
11592 "Wrong signature size");
11593 shared_secret.resize(num_sigs);
11594 sig.resize(num_sigs);
11595 for (size_t i = 0; i < num_sigs; ++i)
11596 {
11597 std::string pk_decoded;
11598 std::string sig_decoded;
11599 const size_t offset = header_len + i * (pk_len + sig_len);
11600 THROW_WALLET_EXCEPTION_IF(!tools::base58::decode(sig_str.substr(offset, pk_len), pk_decoded), error::wallet_internal_error,
11601 "Signature decoding error");
11602 THROW_WALLET_EXCEPTION_IF(!tools::base58::decode(sig_str.substr(offset + pk_len, sig_len), sig_decoded), error::wallet_internal_error,
11603 "Signature decoding error");
11604 THROW_WALLET_EXCEPTION_IF(sizeof(crypto::public_key) != pk_decoded.size() || sizeof(crypto::signature) != sig_decoded.size(), error::wallet_internal_error,
11605 "Signature decoding error");
11606 memcpy(&shared_secret[i], pk_decoded.data(), sizeof(crypto::public_key));
11607 memcpy(&sig[i], sig_decoded.data(), sizeof(crypto::signature));
11608 }
11609
11611 THROW_WALLET_EXCEPTION_IF(tx_pub_key == null_pkey, error::wallet_internal_error, "Tx pubkey was not found");
11612
11613 std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(tx);
11614 THROW_WALLET_EXCEPTION_IF(additional_tx_pub_keys.size() + 1 != num_sigs, error::wallet_internal_error, "Signature size mismatch with additional tx pubkeys");
11615
11617 std::string prefix_data((const char*)&txid, sizeof(crypto::hash));
11618 prefix_data += message;
11619 crypto::hash prefix_hash;
11620 crypto::cn_fast_hash(prefix_data.data(), prefix_data.size(), prefix_hash);
11621
11622 // check signature
11623 std::vector<int> good_signature(num_sigs, 0);
11624 if (is_out)
11625 {
11626 good_signature[0] = is_subaddress ?
11627 crypto::check_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[0], sig[0]) :
11628 crypto::check_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, boost::none, shared_secret[0], sig[0]);
11629
11630 for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
11631 {
11632 good_signature[i + 1] = is_subaddress ?
11633 crypto::check_tx_proof(prefix_hash, additional_tx_pub_keys[i], address.m_view_public_key, address.m_spend_public_key, shared_secret[i + 1], sig[i + 1]) :
11634 crypto::check_tx_proof(prefix_hash, additional_tx_pub_keys[i], address.m_view_public_key, boost::none, shared_secret[i + 1], sig[i + 1]);
11635 }
11636 }
11637 else
11638 {
11639 good_signature[0] = is_subaddress ?
11640 crypto::check_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, address.m_spend_public_key, shared_secret[0], sig[0]) :
11641 crypto::check_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, boost::none, shared_secret[0], sig[0]);
11642
11643 for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
11644 {
11645 good_signature[i + 1] = is_subaddress ?
11646 crypto::check_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[i], address.m_spend_public_key, shared_secret[i + 1], sig[i + 1]) :
11647 crypto::check_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[i], boost::none, shared_secret[i + 1], sig[i + 1]);
11648 }
11649 }
11650
11651 if (std::any_of(good_signature.begin(), good_signature.end(), [](int i) { return i > 0; }))
11652 {
11653 // obtain key derivation by multiplying scalar 1 to the shared secret
11654 crypto::key_derivation derivation;
11655 if (good_signature[0])
11656 THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(shared_secret[0], rct::rct2sk(rct::I), derivation), error::wallet_internal_error, "Failed to generate key derivation");
11657
11658 std::vector<crypto::key_derivation> additional_derivations(num_sigs - 1);
11659 for (size_t i = 1; i < num_sigs; ++i)
11660 if (good_signature[i])
11661 THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(shared_secret[i], rct::rct2sk(rct::I), additional_derivations[i - 1]), error::wallet_internal_error, "Failed to generate key derivation");
11662
11663 check_tx_key_helper(tx, derivation, additional_derivations, address, received);
11664 return true;
11665 }
11666 return false;
11667}
11668
11669std::string wallet2::get_reserve_proof(const boost::optional<std::pair<uint32_t, uint64_t>> &account_minreserve, const std::string &message)
11670{
11671 THROW_WALLET_EXCEPTION_IF(m_watch_only || m_multisig, error::wallet_internal_error, "Reserve proof can only be generated by a full wallet");
11673 THROW_WALLET_EXCEPTION_IF(account_minreserve && balance(account_minreserve->first, false) < account_minreserve->second, error::wallet_internal_error,
11674 "Not enough balance in this account for the requested minimum reserve amount");
11675
11676 // determine which outputs to include in the proof
11677 std::vector<size_t> selected_transfers;
11678 for (size_t i = 0; i < m_transfers.size(); ++i)
11679 {
11680 const transfer_details &td = m_transfers[i];
11681 if (!td.m_spent && !td.m_frozen && (!account_minreserve || account_minreserve->first == td.m_subaddr_index.major))
11682 selected_transfers.push_back(i);
11683 }
11684
11685 if (account_minreserve)
11686 {
11687 THROW_WALLET_EXCEPTION_IF(account_minreserve->second == 0, error::wallet_internal_error, "Proved amount must be greater than 0");
11688 // minimize the number of outputs included in the proof, by only picking the N largest outputs that can cover the requested min reserve amount
11689 std::sort(selected_transfers.begin(), selected_transfers.end(), [&](const size_t a, const size_t b)
11690 { return m_transfers[a].amount() > m_transfers[b].amount(); });
11691 while (selected_transfers.size() >= 2 && m_transfers[selected_transfers[1]].amount() >= account_minreserve->second)
11692 selected_transfers.erase(selected_transfers.begin());
11693 size_t sz = 0;
11694 uint64_t total = 0;
11695 while (total < account_minreserve->second)
11696 {
11697 total += m_transfers[selected_transfers[sz]].amount();
11698 ++sz;
11699 }
11700 selected_transfers.resize(sz);
11701 }
11702
11703 // compute signature prefix hash
11704 std::string prefix_data = message;
11705 prefix_data.append((const char*)&m_account.get_keys().m_account_address, sizeof(cryptonote::account_public_address));
11706 for (size_t i = 0; i < selected_transfers.size(); ++i)
11707 {
11708 prefix_data.append((const char*)&m_transfers[selected_transfers[i]].m_key_image, sizeof(crypto::key_image));
11709 }
11710 crypto::hash prefix_hash;
11711 crypto::cn_fast_hash(prefix_data.data(), prefix_data.size(), prefix_hash);
11712
11713 // generate proof entries
11714 std::vector<reserve_proof_entry> proofs(selected_transfers.size());
11715 std::unordered_set<cryptonote::subaddress_index> subaddr_indices = { {0,0} };
11716 for (size_t i = 0; i < selected_transfers.size(); ++i)
11717 {
11718 transfer_details &td = m_transfers[selected_transfers[i]];
11719 reserve_proof_entry& proof = proofs[i];
11720 proof.txid = td.m_txid;
11722 proof.key_image = td.m_key_image;
11723 cryptonote::subaddress_index index2 = {td.m_subaddr_index.major + (td.m_subaddr_index.major != 0 ? m_account_major_offset : 0), td.m_subaddr_index.minor};
11724 td.m_subaddr_index = index2;
11725 subaddr_indices.insert(td.m_subaddr_index);
11726
11727 // get tx pub key
11729 THROW_WALLET_EXCEPTION_IF(tx_pub_key == crypto::null_pkey, error::wallet_internal_error, "The tx public key isn't found");
11730 const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx);
11731
11732 // determine which tx pub key was used for deriving the output key
11733 const crypto::public_key *tx_pub_key_used = &tx_pub_key;
11734 for (int i = 0; i < 2; ++i)
11735 {
11736 proof.shared_secret = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(*tx_pub_key_used), rct::sk2rct(m_account.get_keys().m_view_secret_key)));
11737 crypto::key_derivation derivation;
11738 THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(proof.shared_secret, rct::rct2sk(rct::I), derivation),
11739 error::wallet_internal_error, "Failed to generate key derivation");
11740 crypto::public_key subaddress_spendkey;
11741 THROW_WALLET_EXCEPTION_IF(!derive_subaddress_public_key(td.get_public_key(), derivation, proof.index_in_tx, subaddress_spendkey),
11742 error::wallet_internal_error, "Failed to derive subaddress public key");
11743 if (m_subaddresses.count(subaddress_spendkey) == 1)
11744 break;
11745 THROW_WALLET_EXCEPTION_IF(additional_tx_pub_keys.empty(), error::wallet_internal_error,
11746 "Normal tx pub key doesn't derive the expected output, while the additional tx pub keys are empty");
11748 "Neither normal tx pub key nor additional tx pub key derive the expected output key");
11749 tx_pub_key_used = &additional_tx_pub_keys[proof.index_in_tx];
11750 }
11751
11752 // generate signature for shared secret
11753 crypto::generate_tx_proof(prefix_hash, m_account.get_keys().m_account_address.m_view_public_key, *tx_pub_key_used, boost::none, proof.shared_secret, m_account.get_keys().m_view_secret_key, proof.shared_secret_sig);
11754
11755 // derive ephemeral secret key
11757 cryptonote::keypair ephemeral;
11758 const bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, td.get_public_key(), tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, ephemeral, ki, m_account.get_device(), m_account_major_offset);
11759 THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
11760 THROW_WALLET_EXCEPTION_IF(ephemeral.pub != td.get_public_key(), error::wallet_internal_error, "Derived public key doesn't agree with the stored one");
11761
11762 // generate signature for key image
11763 const std::vector<const crypto::public_key*> pubs = { &ephemeral.pub };
11764 crypto::generate_ring_signature(prefix_hash, td.m_key_image, &pubs[0], 1, ephemeral.sec, 0, &proof.key_image_sig);
11765 }
11766
11767 // collect all subaddress spend keys that received those outputs and generate their signatures
11768 std::unordered_map<crypto::public_key, crypto::signature> subaddr_spendkeys;
11769 for (const cryptonote::subaddress_index &index : subaddr_indices)
11770 {
11771 crypto::secret_key subaddr_spend_skey = m_account.get_keys().m_spend_secret_key;
11772 if (!index.is_zero())
11773 {
11774 crypto::secret_key m = m_account.get_device().get_subaddress_secret_key(m_account.get_keys().m_view_secret_key, index);
11775 crypto::secret_key tmp = subaddr_spend_skey;
11776 sc_add((unsigned char*)&subaddr_spend_skey, (unsigned char*)&m, (unsigned char*)&tmp);
11777 }
11778 crypto::public_key subaddr_spend_pkey;
11779 secret_key_to_public_key(subaddr_spend_skey, subaddr_spend_pkey);
11780 crypto::generate_signature(prefix_hash, subaddr_spend_pkey, subaddr_spend_skey, subaddr_spendkeys[subaddr_spend_pkey]);
11781 }
11782
11783 // serialize & encode
11784 std::ostringstream oss;
11786 ar << proofs << subaddr_spendkeys;
11787 return "ReserveProofV1" + tools::base58::encode(oss.str());
11788}
11789
11790bool wallet2::check_reserve_proof(const cryptonote::account_public_address &address, const std::string &message, const std::string &sig_str, uint64_t &total, uint64_t &spent)
11791{
11792 uint32_t rpc_version;
11793 THROW_WALLET_EXCEPTION_IF(!check_connection(&rpc_version), error::wallet_internal_error, "Failed to connect to daemon: " + get_daemon_address());
11794 THROW_WALLET_EXCEPTION_IF(rpc_version < MAKE_CORE_RPC_VERSION(1, 0), error::wallet_internal_error, "Daemon RPC version is too old");
11795
11796 static constexpr char header[] = "ReserveProofV1";
11797 THROW_WALLET_EXCEPTION_IF(!boost::string_ref{sig_str}.starts_with(header), error::wallet_internal_error,
11798 "Signature header check error");
11799
11800 std::string sig_decoded;
11801 THROW_WALLET_EXCEPTION_IF(!tools::base58::decode(sig_str.substr(std::strlen(header)), sig_decoded), error::wallet_internal_error,
11802 "Signature decoding error");
11803
11804 std::istringstream iss(sig_decoded);
11806 std::vector<reserve_proof_entry> proofs;
11807 std::unordered_map<crypto::public_key, crypto::signature> subaddr_spendkeys;
11808 ar >> proofs >> subaddr_spendkeys;
11809
11810 THROW_WALLET_EXCEPTION_IF(subaddr_spendkeys.count(address.m_spend_public_key) == 0, error::wallet_internal_error,
11811 "The given address isn't found in the proof");
11812
11813 // compute signature prefix hash
11814 std::string prefix_data = message;
11815 prefix_data.append((const char*)&address, sizeof(cryptonote::account_public_address));
11816 for (size_t i = 0; i < proofs.size(); ++i)
11817 {
11818 prefix_data.append((const char*)&proofs[i].key_image, sizeof(crypto::key_image));
11819 }
11820 crypto::hash prefix_hash;
11821 crypto::cn_fast_hash(prefix_data.data(), prefix_data.size(), prefix_hash);
11822
11823 // fetch txes from daemon
11826 for (size_t i = 0; i < proofs.size(); ++i)
11827 gettx_req.txs_hashes.push_back(epee::string_tools::pod_to_hex(proofs[i].txid));
11828 gettx_req.decode_as_json = false;
11829 gettx_req.prune = true;
11830 m_daemon_rpc_mutex.lock();
11831 bool ok = invoke_http_json("/gettransactions", gettx_req, gettx_res, rpc_timeout);
11832 m_daemon_rpc_mutex.unlock();
11833 THROW_WALLET_EXCEPTION_IF(!ok || gettx_res.txs.size() != proofs.size(),
11834 error::wallet_internal_error, "Failed to get transaction from daemon");
11835
11836 // check spent status
11839 for (size_t i = 0; i < proofs.size(); ++i)
11840 kispent_req.key_images.push_back(epee::string_tools::pod_to_hex(proofs[i].key_image));
11841 m_daemon_rpc_mutex.lock();
11842 ok = invoke_http_json("/is_key_image_spent", kispent_req, kispent_res, rpc_timeout);
11843 m_daemon_rpc_mutex.unlock();
11844 THROW_WALLET_EXCEPTION_IF(!ok || kispent_res.spent_status.size() != proofs.size(),
11845 error::wallet_internal_error, "Failed to get key image spent status from daemon");
11846
11847 total = spent = 0;
11848 for (size_t i = 0; i < proofs.size(); ++i)
11849 {
11850 const reserve_proof_entry& proof = proofs[i];
11851 THROW_WALLET_EXCEPTION_IF(gettx_res.txs[i].in_pool, error::wallet_internal_error, "Tx is unconfirmed");
11852
11854 crypto::hash tx_hash;
11855 ok = get_pruned_tx(gettx_res.txs[i], tx, tx_hash);
11856 THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
11857
11858 THROW_WALLET_EXCEPTION_IF(tx_hash != proof.txid, error::wallet_internal_error, "Failed to get the right transaction from daemon");
11859
11860 THROW_WALLET_EXCEPTION_IF(proof.index_in_tx >= tx.vout.size(), error::wallet_internal_error, "index_in_tx is out of bound");
11861
11862 const cryptonote::txout_to_key* const out_key = boost::get<cryptonote::txout_to_key>(std::addressof(tx.vout[proof.index_in_tx].target));
11863 THROW_WALLET_EXCEPTION_IF(!out_key, error::wallet_internal_error, "Output key wasn't found")
11864
11865 // get tx pub key
11866 const crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx);
11867 THROW_WALLET_EXCEPTION_IF(tx_pub_key == crypto::null_pkey, error::wallet_internal_error, "The tx public key isn't found");
11868 const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(tx);
11869
11870 // check singature for shared secret
11871 ok = crypto::check_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, boost::none, proof.shared_secret, proof.shared_secret_sig);
11872 if (!ok && additional_tx_pub_keys.size() == tx.vout.size())
11873 ok = crypto::check_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[proof.index_in_tx], boost::none, proof.shared_secret, proof.shared_secret_sig);
11874 if (!ok)
11875 return false;
11876
11877 // check signature for key image
11878 const std::vector<const crypto::public_key*> pubs = { &out_key->key };
11879 ok = crypto::check_ring_signature(prefix_hash, proof.key_image, &pubs[0], 1, &proof.key_image_sig);
11880 if (!ok)
11881 return false;
11882
11883 // check if the address really received the fund
11884 crypto::key_derivation derivation;
11885 THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(proof.shared_secret, rct::rct2sk(rct::I), derivation), error::wallet_internal_error, "Failed to generate key derivation");
11886 crypto::public_key subaddr_spendkey;
11887 crypto::derive_subaddress_public_key(out_key->key, derivation, proof.index_in_tx, subaddr_spendkey);
11888 THROW_WALLET_EXCEPTION_IF(subaddr_spendkeys.count(subaddr_spendkey) == 0, error::wallet_internal_error,
11889 "The address doesn't seem to have received the fund");
11890
11891 // check amount
11892 uint64_t amount = tx.vout[proof.index_in_tx].amount;
11893 if (amount == 0)
11894 {
11895 // decode rct
11896 crypto::secret_key shared_secret;
11897 crypto::derivation_to_scalar(derivation, proof.index_in_tx, shared_secret);
11898 rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[proof.index_in_tx];
11899 rct::ecdhDecode(ecdh_info, rct::sk2rct(shared_secret), tx.rct_signatures.type == rct::RCTTypeBulletproof2);
11900 amount = rct::h2d(ecdh_info.amount);
11901 }
11902 total += amount;
11903 if (kispent_res.spent_status[i])
11904 spent += amount;
11905 }
11906
11907 // check signatures for all subaddress spend keys
11908 for (const auto &i : subaddr_spendkeys)
11909 {
11910 if (!crypto::check_signature(prefix_hash, i.first, i.second))
11911 return false;
11912 }
11913 return true;
11914}
11915
11916std::string wallet2::get_wallet_file() const
11917{
11918 return m_wallet_file;
11919}
11920
11921std::string wallet2::get_keys_file() const
11922{
11923 return m_keys_file;
11924}
11925
11927{
11928 return m_daemon_address;
11929}
11930
11932{
11934
11935 boost::optional<std::string> result = m_node_rpc_proxy.get_height(height);
11936 if (result)
11937 {
11938 if (m_trusted_daemon)
11939 err = *result;
11940 else
11941 err = "daemon error";
11942 return 0;
11943 }
11944
11945 err = "";
11946 return height;
11947}
11948
11950{
11951 err = "";
11952 uint64_t target_height = 0;
11953 const auto result = m_node_rpc_proxy.get_target_height(target_height);
11954 if (result && *result != CORE_RPC_STATUS_OK)
11955 {
11956 if (m_trusted_daemon)
11957 err = *result;
11958 else
11959 err = "daemon error";
11960 return 0;
11961 }
11962 return target_height;
11963}
11964
11966{
11967 // time of v2 fork
11968 const time_t fork_time = m_nettype == TESTNET ? 1341378000 : m_nettype == STAGENET ? 1521000000 : 1538815057;
11969 // v2 fork block
11970 const uint64_t fork_block = m_nettype == TESTNET ? 190060 : m_nettype == STAGENET ? 32000 : 307500;
11971 // avg seconds per block
11972 const int seconds_per_block = DIFFICULTY_TARGET_V6;
11973 // Calculated blockchain height
11974 uint64_t approx_blockchain_height = fork_block + (time(NULL) - fork_time)/seconds_per_block;
11975 // testnet got some huge rollbacks, so the estimation is way off
11976 static const uint64_t approximate_testnet_rolled_back_blocks = 303967;
11977 if (m_nettype == TESTNET && approx_blockchain_height > approximate_testnet_rolled_back_blocks)
11978 approx_blockchain_height -= approximate_testnet_rolled_back_blocks;
11979 // estiamte blocks from v6
11980 if(m_nettype == MAINNET) {
11981 approx_blockchain_height += 82000;
11982 }
11983 LOG_PRINT_L2("Calculated blockchain height: " << approx_blockchain_height);
11984 return approx_blockchain_height;
11985}
11986
11987void wallet2::set_tx_note(const crypto::hash &txid, const std::string &note)
11988{
11989 m_tx_notes[txid] = note;
11990}
11991
11992std::string wallet2::get_tx_note(const crypto::hash &txid) const
11993{
11994 std::unordered_map<crypto::hash, std::string>::const_iterator i = m_tx_notes.find(txid);
11995 if (i == m_tx_notes.end())
11996 return std::string();
11997 return i->second;
11998}
11999
12000void wallet2::set_tx_device_aux(const crypto::hash &txid, const std::string &aux)
12001{
12002 m_tx_device[txid] = aux;
12003}
12004
12005std::string wallet2::get_tx_device_aux(const crypto::hash &txid) const
12006{
12007 std::unordered_map<crypto::hash, std::string>::const_iterator i = m_tx_device.find(txid);
12008 if (i == m_tx_device.end())
12009 return std::string();
12010 return i->second;
12011}
12012
12013void wallet2::set_attribute(const std::string &key, const std::string &value)
12014{
12015 m_attributes[key] = value;
12016}
12017
12018std::string wallet2::get_attribute(const std::string &key) const
12019{
12020 std::unordered_map<std::string, std::string>::const_iterator i = m_attributes.find(key);
12021 if (i == m_attributes.end())
12022 return std::string();
12023 return i->second;
12024}
12025
12026void wallet2::set_description(const std::string &description)
12027{
12029}
12030
12031std::string wallet2::get_description() const
12032{
12034}
12035
12036const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& wallet2::get_account_tags()
12037{
12038 // ensure consistency
12039 if (m_account_tags.second.size() != get_num_subaddress_accounts())
12040 m_account_tags.second.resize(get_num_subaddress_accounts(), "");
12041 for (const std::string& tag : m_account_tags.second)
12042 {
12043 if (!tag.empty() && m_account_tags.first.count(tag) == 0)
12044 m_account_tags.first.insert({tag, ""});
12045 }
12046 for (auto i = m_account_tags.first.begin(); i != m_account_tags.first.end(); )
12047 {
12048 if (std::find(m_account_tags.second.begin(), m_account_tags.second.end(), i->first) == m_account_tags.second.end())
12049 i = m_account_tags.first.erase(i);
12050 else
12051 ++i;
12052 }
12053 return m_account_tags;
12054}
12055
12056void wallet2::set_account_tag(const std::set<uint32_t> &account_indices, const std::string& tag)
12057{
12058 for (uint32_t account_index : account_indices)
12059 {
12060 THROW_WALLET_EXCEPTION_IF(account_index >= get_num_subaddress_accounts(), error::wallet_internal_error, "Account index out of bound");
12061 if (m_account_tags.second[account_index] == tag)
12062 MDEBUG("This tag is already assigned to this account");
12063 else
12064 m_account_tags.second[account_index] = tag;
12065 }
12067}
12068
12069void wallet2::set_account_tag_description(const std::string& tag, const std::string& description)
12070{
12071 THROW_WALLET_EXCEPTION_IF(tag.empty(), error::wallet_internal_error, "Tag must not be empty");
12072 THROW_WALLET_EXCEPTION_IF(m_account_tags.first.count(tag) == 0, error::wallet_internal_error, "Tag is unregistered");
12073 m_account_tags.first[tag] = description;
12074}
12075
12076std::string wallet2::sign(const std::string &data) const
12077{
12079 crypto::cn_fast_hash(data.data(), data.size(), hash);
12080 const cryptonote::account_keys &keys = m_account.get_keys();
12083 return std::string("SigV1") + tools::base58::encode(std::string((const char *)&signature, sizeof(signature)));
12084}
12085
12086bool wallet2::verify(const std::string &data, const cryptonote::account_public_address &address, const std::string &signature) const
12087{
12088 const size_t header_len = strlen("SigV1");
12089 if (signature.size() < header_len || signature.substr(0, header_len) != "SigV1") {
12090 LOG_PRINT_L0("Signature header check error");
12091 return false;
12092 }
12094 crypto::cn_fast_hash(data.data(), data.size(), hash);
12095 std::string decoded;
12096 if (!tools::base58::decode(signature.substr(header_len), decoded)) {
12097 LOG_PRINT_L0("Signature decoding error");
12098 return false;
12099 }
12101 if (sizeof(s) != decoded.size()) {
12102 LOG_PRINT_L0("Signature decoding error");
12103 return false;
12104 }
12105 memcpy(&s, decoded.data(), sizeof(s));
12106 return crypto::check_signature(hash, address.m_spend_public_key, s);
12107}
12108
12109std::string wallet2::sign_multisig_participant(const std::string& data) const
12110{
12111 CHECK_AND_ASSERT_THROW_MES(m_multisig, "Wallet is not multisig");
12112
12114 crypto::cn_fast_hash(data.data(), data.size(), hash);
12115 const cryptonote::account_keys &keys = m_account.get_keys();
12118 return MULTISIG_SIGNATURE_MAGIC + tools::base58::encode(std::string((const char *)&signature, sizeof(signature)));
12119}
12120
12121bool wallet2::verify_with_public_key(const std::string &data, const crypto::public_key &public_key, const std::string &signature) const
12122{
12123 if (signature.size() < MULTISIG_SIGNATURE_MAGIC.size() || signature.substr(0, MULTISIG_SIGNATURE_MAGIC.size()) != MULTISIG_SIGNATURE_MAGIC) {
12124 MERROR("Signature header check error");
12125 return false;
12126 }
12128 crypto::cn_fast_hash(data.data(), data.size(), hash);
12129 std::string decoded;
12130 if (!tools::base58::decode(signature.substr(MULTISIG_SIGNATURE_MAGIC.size()), decoded)) {
12131 MERROR("Signature decoding error");
12132 return false;
12133 }
12135 if (sizeof(s) != decoded.size()) {
12136 MERROR("Signature decoding error");
12137 return false;
12138 }
12139 memcpy(&s, decoded.data(), sizeof(s));
12141}
12142//----------------------------------------------------------------------------------------------------
12144{
12145 std::vector<tx_extra_field> tx_extra_fields;
12146 if(!parse_tx_extra(td.m_tx.extra, tx_extra_fields))
12147 {
12148 // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key
12149 }
12150
12151 // Due to a previous bug, there might be more than one tx pubkey in extra, one being
12152 // the result of a previously discarded signature.
12153 // For speed, since scanning for outputs is a slow process, we check whether extra
12154 // contains more than one pubkey. If not, the first one is returned. If yes, they're
12155 // checked for whether they yield at least one output
12156 tx_extra_pub_key pub_key_field;
12158 "Public key wasn't found in the transaction extra");
12159 const crypto::public_key tx_pub_key = pub_key_field.pub_key;
12160 bool two_found = find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, 1);
12161 if (!two_found) {
12162 // easy case, just one found
12163 return tx_pub_key;
12164 }
12165
12166 // more than one, loop and search
12167 const cryptonote::account_keys& keys = m_account.get_keys();
12168 size_t pk_index = 0;
12169 hw::device &hwdev = m_account.get_device();
12170
12171 while (find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, pk_index++)) {
12172 const crypto::public_key tx_pub_key = pub_key_field.pub_key;
12173 crypto::key_derivation derivation;
12174 bool r = hwdev.generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation);
12175 THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation");
12176
12177 for (size_t i = 0; i < td.m_tx.vout.size(); ++i)
12178 {
12179 tx_scan_info_t tx_scan_info;
12180 check_acc_out_precomp(td.m_tx.vout[i], derivation, {}, i, tx_scan_info);
12181 if (!tx_scan_info.error && tx_scan_info.received)
12182 return tx_pub_key;
12183 }
12184 }
12185
12186 // we found no key yielding an output, but it might be in the additional
12187 // tx pub keys only, which we do not need to check, so return the first one
12188 return tx_pub_key;
12189}
12190
12191bool wallet2::export_key_images(const std::string &filename) const
12192{
12194 std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> ski = export_key_images();
12195 std::string magic(KEY_IMAGE_EXPORT_FILE_MAGIC, strlen(KEY_IMAGE_EXPORT_FILE_MAGIC));
12196 const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
12197 const uint32_t offset = ski.first;
12198
12199 std::string data;
12200 data.reserve(4 + ski.second.size() * (sizeof(crypto::key_image) + sizeof(crypto::signature)) + 2 * sizeof(crypto::public_key));
12201 data.resize(4);
12202 data[0] = offset & 0xff;
12203 data[1] = (offset >> 8) & 0xff;
12204 data[2] = (offset >> 16) & 0xff;
12205 data[3] = (offset >> 24) & 0xff;
12206 data += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key));
12207 data += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key));
12208 for (const auto &i: ski.second)
12209 {
12210 data += std::string((const char *)&i.first, sizeof(crypto::key_image));
12211 data += std::string((const char *)&i.second, sizeof(crypto::signature));
12212 }
12213
12214 // encrypt data, keep magic plaintext
12215 PERF_TIMER(export_key_images_encrypt);
12216 std::string ciphertext = encrypt_with_view_secret_key(data);
12217 return epee::file_io_utils::save_string_to_file(filename, magic + ciphertext);
12218}
12219
12220//----------------------------------------------------------------------------------------------------
12221std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> wallet2::export_key_images(bool all) const
12222{
12223 PERF_TIMER(export_key_images_raw);
12224 std::vector<std::pair<crypto::key_image, crypto::signature>> ski;
12225
12226 size_t offset = 0;
12227 if (!all)
12228 {
12229 while (offset < m_transfers.size() && !m_transfers[offset].m_key_image_request)
12230 ++offset;
12231 }
12232
12233 ski.reserve(m_transfers.size() - offset);
12234 for (size_t n = offset; n < m_transfers.size(); ++n)
12235 {
12236 const transfer_details &td = m_transfers[n];
12237
12238 // get ephemeral public key
12241 "Output is not txout_to_key");
12242 const cryptonote::txout_to_key &o = boost::get<const cryptonote::txout_to_key>(out.target);
12243 const crypto::public_key pkey = o.key;
12244
12245 // get tx pub key
12246 std::vector<tx_extra_field> tx_extra_fields;
12247 if(!parse_tx_extra(td.m_tx.extra, tx_extra_fields))
12248 {
12249 // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key
12250 }
12251
12253 const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx);
12254
12255 // generate ephemeral secret key
12257 cryptonote::keypair in_ephemeral;
12258 bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, pkey, tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, in_ephemeral, ki, m_account.get_device(), m_account_major_offset);
12259 THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
12260
12262 error::wallet_internal_error, "key_image generated not matched with cached key image");
12263 THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != pkey,
12264 error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
12265
12266 // sign the key image with the output secret key
12268 std::vector<const crypto::public_key*> key_ptrs;
12269 key_ptrs.push_back(&pkey);
12270
12271 crypto::generate_ring_signature((const crypto::hash&)td.m_key_image, td.m_key_image, key_ptrs, in_ephemeral.sec, 0, &signature);
12272
12273 ski.push_back(std::make_pair(td.m_key_image, signature));
12274 }
12275 return std::make_pair(offset, ski);
12276}
12277
12278uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent)
12279{
12280 PERF_TIMER(import_key_images_fsu);
12281 std::string data;
12282 bool r = epee::file_io_utils::load_file_to_string(filename, data);
12283
12284 THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, std::string(tr("failed to read file ")) + filename);
12285
12286 const size_t magiclen = strlen(KEY_IMAGE_EXPORT_FILE_MAGIC);
12287 if (data.size() < magiclen || memcmp(data.data(), KEY_IMAGE_EXPORT_FILE_MAGIC, magiclen))
12288 {
12289 THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Bad key image export file magic in ") + filename);
12290 }
12291
12292 try
12293 {
12294 PERF_TIMER(import_key_images_decrypt);
12295 data = decrypt_with_view_secret_key(std::string(data, magiclen));
12296 }
12297 catch (const std::exception &e)
12298 {
12299 THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to decrypt ") + filename + ": " + e.what());
12300 }
12301
12302 const size_t headerlen = 4 + 2 * sizeof(crypto::public_key);
12303 THROW_WALLET_EXCEPTION_IF(data.size() < headerlen, error::wallet_internal_error, std::string("Bad data size from file ") + filename);
12304 const uint32_t offset = (uint8_t)data[0] | (((uint8_t)data[1]) << 8) | (((uint8_t)data[2]) << 16) | (((uint8_t)data[3]) << 24);
12305 const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[4];
12306 const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[4 + sizeof(crypto::public_key)];
12307 const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
12308 if (public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key)
12309 {
12310 THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string( "Key images from ") + filename + " are for a different account");
12311 }
12312 THROW_WALLET_EXCEPTION_IF(offset > m_transfers.size(), error::wallet_internal_error, "Offset larger than known outputs");
12313
12314 const size_t record_size = sizeof(crypto::key_image) + sizeof(crypto::signature);
12315 THROW_WALLET_EXCEPTION_IF((data.size() - headerlen) % record_size,
12316 error::wallet_internal_error, std::string("Bad data size from file ") + filename);
12317 size_t nki = (data.size() - headerlen) / record_size;
12318
12319 std::vector<std::pair<crypto::key_image, crypto::signature>> ski;
12320 ski.reserve(nki);
12321 for (size_t n = 0; n < nki; ++n)
12322 {
12323 crypto::key_image key_image = *reinterpret_cast<const crypto::key_image*>(&data[headerlen + n * record_size]);
12324 crypto::signature signature = *reinterpret_cast<const crypto::signature*>(&data[headerlen + n * record_size + sizeof(crypto::key_image)]);
12325
12326 ski.push_back(std::make_pair(key_image, signature));
12327 }
12328
12329 return import_key_images(ski, offset, spent, unspent);
12330}
12331
12332//----------------------------------------------------------------------------------------------------
12333uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_image, crypto::signature>> &signed_key_images, size_t offset, uint64_t &spent, uint64_t &unspent, bool check_spent)
12334{
12335 PERF_TIMER(import_key_images_lots);
12338
12339 THROW_WALLET_EXCEPTION_IF(offset > m_transfers.size(), error::wallet_internal_error, "Offset larger than known outputs");
12340 THROW_WALLET_EXCEPTION_IF(signed_key_images.size() > m_transfers.size() - offset, error::wallet_internal_error,
12341 "The blockchain is out of date compared to the signed key images");
12342
12343 if (signed_key_images.empty() && offset == 0)
12344 {
12345 spent = 0;
12346 unspent = 0;
12347 return 0;
12348 }
12349
12350 req.key_images.reserve(signed_key_images.size());
12351
12352 PERF_TIMER_START(import_key_images_A);
12353 for (size_t n = 0; n < signed_key_images.size(); ++n)
12354 {
12355 const transfer_details &td = m_transfers[n + offset];
12356 const crypto::key_image &key_image = signed_key_images[n].first;
12357 const crypto::signature &signature = signed_key_images[n].second;
12358
12359 // get ephemeral public key
12362 "Non txout_to_key output found");
12363 const cryptonote::txout_to_key &o = boost::get<cryptonote::txout_to_key>(out.target);
12364 const crypto::public_key pkey = o.key;
12365
12366 if (!td.m_key_image_known || !(key_image == td.m_key_image))
12367 {
12368 std::vector<const crypto::public_key*> pkeys;
12369 pkeys.push_back(&pkey);
12371 error::wallet_internal_error, "Key image out of validity domain: input " + boost::lexical_cast<std::string>(n + offset) + "/"
12372 + boost::lexical_cast<std::string>(signed_key_images.size()) + ", key image " + epee::string_tools::pod_to_hex(key_image));
12373
12375 error::signature_check_failed, boost::lexical_cast<std::string>(n + offset) + "/"
12376 + boost::lexical_cast<std::string>(signed_key_images.size()) + ", key image " + epee::string_tools::pod_to_hex(key_image)
12377 + ", signature " + epee::string_tools::pod_to_hex(signature) + ", pubkey " + epee::string_tools::pod_to_hex(*pkeys[0]));
12378 }
12379 req.key_images.push_back(epee::string_tools::pod_to_hex(key_image));
12380 }
12381 PERF_TIMER_STOP(import_key_images_A);
12382
12383 PERF_TIMER_START(import_key_images_B);
12384 for (size_t n = 0; n < signed_key_images.size(); ++n)
12385 {
12386 m_transfers[n + offset].m_key_image = signed_key_images[n].first;
12387 m_key_images[m_transfers[n + offset].m_key_image] = n + offset;
12388 m_transfers[n + offset].m_key_image_known = true;
12389 m_transfers[n + offset].m_key_image_request = false;
12390 m_transfers[n + offset].m_key_image_partial = false;
12391 }
12392 PERF_TIMER_STOP(import_key_images_B);
12393
12394 if(check_spent)
12395 {
12396 PERF_TIMER(import_key_images_RPC);
12397 m_daemon_rpc_mutex.lock();
12398 bool r = invoke_http_json("/is_key_image_spent", req, daemon_resp, rpc_timeout);
12399 m_daemon_rpc_mutex.unlock();
12401 THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "is_key_image_spent");
12402 THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::is_key_image_spent_error, daemon_resp.status);
12403 THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != signed_key_images.size(), error::wallet_internal_error,
12404 "daemon returned wrong response for is_key_image_spent, wrong amounts count = " +
12405 std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(signed_key_images.size()));
12406 for (size_t n = 0; n < daemon_resp.spent_status.size(); ++n)
12407 {
12408 transfer_details &td = m_transfers[n + offset];
12409 td.m_spent = daemon_resp.spent_status[n] != SPENT_STATUS::UNSPENT;
12410 }
12411 }
12412 spent = 0;
12413 unspent = 0;
12414 std::unordered_set<crypto::hash> spent_txids; // For each spent key image, search for a tx in m_transfers that uses it as input.
12415 std::vector<size_t> swept_transfers; // If such a spending tx wasn't found in m_transfers, this means the spending tx
12416 // was created by sweep_all, so we can't know the spent height and other detailed info.
12417 std::unordered_map<crypto::key_image, crypto::hash> spent_key_images;
12418
12419 PERF_TIMER_START(import_key_images_C);
12420 for (const transfer_details &td: m_transfers)
12421 {
12422 for (const cryptonote::txin_v& in : td.m_tx.vin)
12423 {
12424 if (in.type() == typeid(cryptonote::txin_to_key))
12425 spent_key_images.insert(std::make_pair(boost::get<cryptonote::txin_to_key>(in).k_image, td.m_txid));
12426 }
12427 }
12428 PERF_TIMER_STOP(import_key_images_C);
12429
12430 // accumulate outputs before the updated data
12431 for(size_t i = 0; i < offset; ++i)
12432 {
12433 const transfer_details &td = m_transfers[i];
12434 if (td.m_frozen)
12435 continue;
12436 uint64_t amount = td.amount();
12437 if (td.m_spent)
12438 spent += amount;
12439 else
12440 unspent += amount;
12441 }
12442
12443 PERF_TIMER_START(import_key_images_D);
12444 for(size_t i = 0; i < signed_key_images.size(); ++i)
12445 {
12446 const transfer_details &td = m_transfers[i + offset];
12447 if (td.m_frozen)
12448 continue;
12449 uint64_t amount = td.amount();
12450 if (td.m_spent)
12451 spent += amount;
12452 else
12453 unspent += amount;
12454 LOG_PRINT_L2("Transfer " << i << ": " << print_etn(amount) << " (" << td.m_global_output_index << "): "
12455 << (td.m_spent ? "spent" : "unspent") << " (key image " << req.key_images[i] << ")");
12456
12457 if (i < daemon_resp.spent_status.size() && daemon_resp.spent_status[i] == SPENT_STATUS::SPENT_IN_BLOCKCHAIN)
12458 {
12459 const std::unordered_map<crypto::key_image, crypto::hash>::const_iterator skii = spent_key_images.find(td.m_key_image);
12460 if (skii == spent_key_images.end())
12461 swept_transfers.push_back(i);
12462 else
12463 spent_txids.insert(skii->second);
12464 }
12465 }
12466 PERF_TIMER_STOP(import_key_images_D);
12467
12468 MDEBUG("Total: " << print_etn(spent) << " spent, " << print_etn(unspent) << " unspent");
12469
12470 if (check_spent)
12471 {
12472 // query outgoing txes
12475 gettxs_req.decode_as_json = false;
12476 gettxs_req.prune = true;
12477 gettxs_req.txs_hashes.reserve(spent_txids.size());
12478 for (const crypto::hash& spent_txid : spent_txids)
12479 gettxs_req.txs_hashes.push_back(epee::string_tools::pod_to_hex(spent_txid));
12480
12481
12482 PERF_TIMER_START(import_key_images_E);
12483 m_daemon_rpc_mutex.lock();
12484 bool r = invoke_http_json("/gettransactions", gettxs_req, gettxs_res, rpc_timeout);
12485 m_daemon_rpc_mutex.unlock();
12487 THROW_WALLET_EXCEPTION_IF(gettxs_res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions");
12488 THROW_WALLET_EXCEPTION_IF(gettxs_res.txs.size() != spent_txids.size(), error::wallet_internal_error,
12489 "daemon returned wrong response for gettransactions, wrong count = " + std::to_string(gettxs_res.txs.size()) + ", expected " + std::to_string(spent_txids.size()));
12490 PERF_TIMER_STOP(import_key_images_E);
12491
12492 // process each outgoing tx
12493 PERF_TIMER_START(import_key_images_F);
12494 auto spent_txid = spent_txids.begin();
12495 hw::device &hwdev = m_account.get_device();
12496 auto it = spent_txids.begin();
12497 for (const COMMAND_RPC_GET_TRANSACTIONS::entry& e : gettxs_res.txs)
12498 {
12499 THROW_WALLET_EXCEPTION_IF(e.in_pool, error::wallet_internal_error, "spent tx isn't supposed to be in txpool");
12500
12501 cryptonote::transaction spent_tx;
12502 crypto::hash spnet_txid_parsed;
12503 THROW_WALLET_EXCEPTION_IF(!get_pruned_tx(e, spent_tx, spnet_txid_parsed), error::wallet_internal_error, "Failed to get tx from daemon");
12504 THROW_WALLET_EXCEPTION_IF(!(spnet_txid_parsed == *it), error::wallet_internal_error, "parsed txid mismatch");
12505 ++it;
12506
12507 // get received (change) amount
12508 uint64_t tx_etn_got_in_outs = 0;
12509 const cryptonote::account_keys& keys = m_account.get_keys();
12510 const crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(spent_tx);
12511 crypto::key_derivation derivation;
12512 bool r = hwdev.generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation);
12513 THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation");
12514 const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(spent_tx);
12515 std::vector<crypto::key_derivation> additional_derivations;
12516 for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
12517 {
12518 additional_derivations.push_back({});
12519 r = hwdev.generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back());
12520 THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation");
12521 }
12522 size_t output_index = 0;
12523 bool miner_tx = cryptonote::is_coinbase(spent_tx);
12524 for (const cryptonote::tx_out& out : spent_tx.vout)
12525 {
12526 tx_scan_info_t tx_scan_info;
12527 check_acc_out_precomp(out, derivation, additional_derivations, output_index, tx_scan_info);
12528 THROW_WALLET_EXCEPTION_IF(tx_scan_info.error, error::wallet_internal_error, "check_acc_out_precomp failed");
12529 if (tx_scan_info.received)
12530 {
12531 if (tx_scan_info.etn_transfered == 0 && !miner_tx)
12532 {
12533 rct::key mask;
12534 tx_scan_info.etn_transfered = tools::decodeRct(spent_tx.rct_signatures, tx_scan_info.received->derivation, output_index, mask, hwdev);
12535 }
12536 THROW_WALLET_EXCEPTION_IF(tx_etn_got_in_outs >= std::numeric_limits<uint64_t>::max() - tx_scan_info.etn_transfered,
12537 error::wallet_internal_error, "Overflow in received amounts");
12538 tx_etn_got_in_outs += tx_scan_info.etn_transfered;
12539 }
12540 ++output_index;
12541 }
12542
12543 // get spent amount
12544 uint64_t tx_etn_spent_in_ins = 0;
12545 uint32_t subaddr_account = (uint32_t)-1;
12546 std::set<uint32_t> subaddr_indices;
12547 for (const cryptonote::txin_v& in : spent_tx.vin)
12548 {
12549 if (in.type() != typeid(cryptonote::txin_to_key))
12550 continue;
12551 auto it = m_key_images.find(boost::get<cryptonote::txin_to_key>(in).k_image);
12552 if (it != m_key_images.end())
12553 {
12554 THROW_WALLET_EXCEPTION_IF(it->second >= m_transfers.size(), error::wallet_internal_error, std::string("Key images cache contains illegal transfer offset: ") + std::to_string(it->second) + std::string(" m_transfers.size() = ") + std::to_string(m_transfers.size()));
12555 const transfer_details& td = m_transfers[it->second];
12556 uint64_t amount = boost::get<cryptonote::txin_to_key>(in).amount;
12557 if (amount > 0)
12558 {
12560 std::string("Inconsistent amount in tx input: got ") + print_etn(amount) +
12561 std::string(", expected ") + print_etn(td.amount()));
12562 }
12563 amount = td.amount();
12564 tx_etn_spent_in_ins += amount;
12565
12566 LOG_PRINT_L0("Spent ETN: " << print_etn(amount) << ", with tx: " << *spent_txid);
12567 set_spent(it->second, e.block_height);
12568 if (m_callback)
12569 m_callback->on_etn_spent(e.block_height, *spent_txid, spent_tx, amount, spent_tx, td.m_subaddr_index);
12570 if (subaddr_account != (uint32_t)-1 && subaddr_account != td.m_subaddr_index.major)
12571 LOG_PRINT_L0("WARNING: This tx spends outputs received by different subaddress accounts, which isn't supposed to happen");
12572 subaddr_account = td.m_subaddr_index.major;
12573 subaddr_indices.insert(td.m_subaddr_index.minor);
12574 }
12575 }
12576
12577 // create outgoing payment
12578 process_outgoing(*spent_txid, spent_tx, e.block_height, e.block_timestamp, tx_etn_spent_in_ins, tx_etn_got_in_outs, subaddr_account, subaddr_indices);
12579
12580 // erase corresponding incoming payment
12581 for (auto j = m_payments.begin(); j != m_payments.end(); ++j)
12582 {
12583 if (j->second.m_tx_hash == *spent_txid)
12584 {
12585 m_payments.erase(j);
12586 break;
12587 }
12588 }
12589
12590 ++spent_txid;
12591 }
12592 PERF_TIMER_STOP(import_key_images_F);
12593
12594 PERF_TIMER_START(import_key_images_G);
12595 for (size_t n : swept_transfers)
12596 {
12597 const transfer_details& td = m_transfers[n];
12599 pd.m_change = (uint64_t)-1; // change is unknown
12600 pd.m_amount_in = pd.m_amount_out = td.amount(); // fee is unknown
12601 pd.m_block_height = 0; // spent block height is unknown
12602 pd.m_is_migration = td.m_tx.version == 2;
12603 if(td.m_tx.version == 3){
12604 cryptonote::account_public_address dest_address = boost::get<cryptonote::txout_to_key_public>(td.m_tx.vout[0].target).address;
12605 bool is_portal_address;
12606 if(m_nettype == MAINNET){
12607 is_portal_address = epee::string_tools::pod_to_hex(dest_address.m_spend_public_key) == "8ce0f34fd37c7f7d07c44024eb5b3cdf275d1b3e75c3464b808dce532e861137" && epee::string_tools::pod_to_hex(dest_address.m_view_public_key) == "2b95a2eb2c62253c57e82b082b850bbf22a1a7829aaea09c7c1511c1cced4375";
12608 }else{
12609 is_portal_address = epee::string_tools::pod_to_hex(dest_address.m_spend_public_key) == "5bd0c0e25eee6133850edd2b255ed9e3d6bb99fd5f08b7b5cf7f2618ad6ff2a3" && epee::string_tools::pod_to_hex(dest_address.m_view_public_key) == "5866666666666666666666666666666666666666666666666666666666666666";
12610 }
12611 pd.m_is_sc_migration = is_portal_address;
12612 }
12613 const crypto::hash &spent_txid = crypto::null_hash; // spent txid is unknown
12614 m_confirmed_txs.insert(std::make_pair(spent_txid, pd));
12615 }
12616 PERF_TIMER_STOP(import_key_images_G);
12617 }
12618
12619 // this can be 0 if we do not know the height
12620 return m_transfers[signed_key_images.size() + offset - 1].m_block_height;
12621}
12622
12623bool wallet2::import_key_images(std::vector<crypto::key_image> key_images, size_t offset, boost::optional<std::unordered_set<size_t>> selected_transfers)
12624{
12625 if (key_images.size() + offset > m_transfers.size())
12626 {
12627 LOG_PRINT_L1("More key images returned that we know outputs for");
12628 return false;
12629 }
12630 for (size_t ki_idx = 0; ki_idx < key_images.size(); ++ki_idx)
12631 {
12632 const size_t transfer_idx = ki_idx + offset;
12633 if (selected_transfers && selected_transfers.get().find(transfer_idx) == selected_transfers.get().end())
12634 continue;
12635
12636 transfer_details &td = m_transfers[transfer_idx];
12637 if (td.m_key_image_known && !td.m_key_image_partial && td.m_key_image != key_images[ki_idx])
12638 LOG_PRINT_L0("WARNING: imported key image differs from previously known key image at index " << ki_idx << ": trusting imported one");
12639 td.m_key_image = key_images[ki_idx];
12640 m_key_images[td.m_key_image] = transfer_idx;
12641 td.m_key_image_known = true;
12642 td.m_key_image_request = false;
12643 td.m_key_image_partial = false;
12644 m_pub_keys[td.get_public_key()] = transfer_idx;
12645 }
12646
12647 return true;
12648}
12649
12650bool wallet2::import_key_images(signed_tx_set & signed_tx, size_t offset, bool only_selected_transfers)
12651{
12652 std::unordered_set<size_t> selected_transfers;
12653 if (only_selected_transfers)
12654 {
12655 for (const pending_tx & ptx : signed_tx.ptx)
12656 {
12657 for (const size_t s: ptx.selected_transfers)
12658 selected_transfers.insert(s);
12659 }
12660 }
12661
12662 return import_key_images(signed_tx.key_images, offset, only_selected_transfers ? boost::make_optional(selected_transfers) : boost::none);
12663}
12664
12666{
12667 payment_container payments;
12668 for (auto const &p : m_payments)
12669 {
12670 payments.emplace(p);
12671 }
12672 return payments;
12673}
12675{
12676 m_payments.clear();
12677 for (auto const &p : payments)
12678 {
12679 m_payments.emplace(p);
12680 }
12681}
12682void wallet2::import_payments_out(const std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>> &confirmed_payments)
12683{
12684 m_confirmed_txs.clear();
12685 for (auto const &p : confirmed_payments)
12686 {
12687 m_confirmed_txs.emplace(p);
12688 }
12689}
12690
12691std::tuple<size_t,crypto::hash,std::vector<crypto::hash>> wallet2::export_blockchain() const
12692{
12693 std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> bc;
12694 std::get<0>(bc) = m_blockchain.offset();
12695 std::get<1>(bc) = m_blockchain.empty() ? crypto::null_hash: m_blockchain.genesis();
12696 for (size_t n = m_blockchain.offset(); n < m_blockchain.size(); ++n)
12697 {
12698 std::get<2>(bc).push_back(m_blockchain[n]);
12699 }
12700 return bc;
12701}
12702
12703void wallet2::import_blockchain(const std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> &bc)
12704{
12705 m_blockchain.clear();
12706 if (std::get<0>(bc))
12707 {
12708 for (size_t n = std::get<0>(bc); n > 0; --n)
12709 m_blockchain.push_back(std::get<1>(bc));
12710 m_blockchain.trim(std::get<0>(bc));
12711 }
12712 for (auto const &b : std::get<2>(bc))
12713 {
12714 m_blockchain.push_back(b);
12715 }
12716 cryptonote::block genesis;
12717 generate_genesis(genesis);
12718 crypto::hash genesis_hash = get_block_hash(genesis);
12719 check_genesis(genesis_hash);
12720 m_last_block_reward = cryptonote::get_outs_etn_amount(genesis.miner_tx);
12721}
12722//----------------------------------------------------------------------------------------------------
12723std::pair<size_t, std::vector<tools::wallet2::transfer_details>> wallet2::export_outputs(bool all) const
12724{
12726 std::vector<tools::wallet2::transfer_details> outs;
12727
12728 size_t offset = 0;
12729 if (!all)
12730 while (offset < m_transfers.size() && (m_transfers[offset].m_key_image_known && !m_transfers[offset].m_key_image_request))
12731 ++offset;
12732
12733 outs.reserve(m_transfers.size() - offset);
12734 for (size_t n = offset; n < m_transfers.size(); ++n)
12735 {
12736 const transfer_details &td = m_transfers[n];
12737
12738 outs.push_back(td);
12739 }
12740
12741 return std::make_pair(offset, outs);
12742}
12743//----------------------------------------------------------------------------------------------------
12744std::string wallet2::export_outputs_to_str(bool all) const
12745{
12747
12748 std::stringstream oss;
12750 const auto& outputs = export_outputs(all);
12751 ar << outputs;
12752
12753 std::string magic(OUTPUT_EXPORT_FILE_MAGIC, strlen(OUTPUT_EXPORT_FILE_MAGIC));
12754 const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
12755 std::string header;
12756 header += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key));
12757 header += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key));
12758 PERF_TIMER(export_outputs_encryption);
12759 std::string ciphertext = encrypt_with_view_secret_key(header + oss.str());
12760 return magic + ciphertext;
12761}
12762//----------------------------------------------------------------------------------------------------
12763size_t wallet2::import_outputs(const std::pair<size_t, std::vector<tools::wallet2::transfer_details>> &outputs)
12764{
12766
12767 THROW_WALLET_EXCEPTION_IF(outputs.first > m_transfers.size(), error::wallet_internal_error,
12768 "Imported outputs omit more outputs that we know of");
12769
12770 const size_t offset = outputs.first;
12771 const size_t original_size = m_transfers.size();
12772 m_transfers.resize(offset + outputs.second.size());
12773 for (size_t i = 0; i < offset; ++i)
12774 m_transfers[i].m_key_image_request = false;
12775 for (size_t i = 0; i < outputs.second.size(); ++i)
12776 {
12777 transfer_details td = outputs.second[i];
12778
12779 // skip those we've already imported, or which have different data
12780 if (i + offset < original_size)
12781 {
12782 // compare the data used to create the key image below
12783 const transfer_details &org_td = m_transfers[i + offset];
12784 if (!org_td.m_key_image_known)
12785 goto process;
12786#define CMPF(f) if (!(td.f == org_td.f)) goto process
12787 CMPF(m_txid);
12788 CMPF(m_key_image);
12789 CMPF(m_internal_output_index);
12790#undef CMPF
12792 goto process;
12793
12794 // copy anyway, since the comparison does not include ancillary fields which may have changed
12795 m_transfers[i + offset] = std::move(td);
12796 continue;
12797 }
12798
12799process:
12800
12801 // the hot wallet wouldn't have known about key images (except if we already exported them)
12802 cryptonote::keypair in_ephemeral;
12803
12804 THROW_WALLET_EXCEPTION_IF(td.m_tx.vout.empty(), error::wallet_internal_error, "tx with no outputs at index " + boost::lexical_cast<std::string>(i + offset));
12806 const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx);
12807
12809 error::wallet_internal_error, "Unsupported output type");
12810 const crypto::public_key& out_key = boost::get<cryptonote::txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key;
12811 bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, out_key, tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, in_ephemeral, td.m_key_image, m_account.get_device(), m_account_major_offset);
12812 THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
12814 td.m_key_image_known = true;
12815 td.m_key_image_request = true;
12816 td.m_key_image_partial = false;
12817 THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != out_key,
12818 error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key at index " + boost::lexical_cast<std::string>(i + offset));
12819
12820 m_key_images[td.m_key_image] = i + offset;
12821 m_pub_keys[td.get_public_key()] = i + offset;
12822 m_transfers[i + offset] = std::move(td);
12823 }
12824
12825 return m_transfers.size();
12826}
12827//----------------------------------------------------------------------------------------------------
12828size_t wallet2::import_outputs_from_str(const std::string &outputs_st)
12829{
12831 std::string data = outputs_st;
12832 const size_t magiclen = strlen(OUTPUT_EXPORT_FILE_MAGIC);
12833 if (data.size() < magiclen || memcmp(data.data(), OUTPUT_EXPORT_FILE_MAGIC, magiclen))
12834 {
12835 THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Bad magic from outputs"));
12836 }
12837
12838 try
12839 {
12840 PERF_TIMER(import_outputs_decrypt);
12841 data = decrypt_with_view_secret_key(std::string(data, magiclen));
12842 }
12843 catch (const std::exception &e)
12844 {
12845 THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to decrypt outputs: ") + e.what());
12846 }
12847
12848 const size_t headerlen = 2 * sizeof(crypto::public_key);
12849 if (data.size() < headerlen)
12850 {
12851 THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Bad data size for outputs"));
12852 }
12853 const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[0];
12854 const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[sizeof(crypto::public_key)];
12855 const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
12856 if (public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key)
12857 {
12858 THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Outputs from are for a different account"));
12859 }
12860
12861 size_t imported_outputs = 0;
12862 try
12863 {
12864 std::string body(data, headerlen);
12865 std::stringstream iss;
12866 iss << body;
12867 std::pair<size_t, std::vector<tools::wallet2::transfer_details>> outputs;
12868 try
12869 {
12871 ar >> outputs;
12872 }
12873 catch (...)
12874 {
12875 iss.str("");
12876 iss << body;
12877 boost::archive::binary_iarchive ar(iss);
12878 ar >> outputs;
12879 }
12880
12881 imported_outputs = import_outputs(outputs);
12882 }
12883 catch (const std::exception &e)
12884 {
12885 THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to import outputs") + e.what());
12886 }
12887
12888 return imported_outputs;
12889}
12890//----------------------------------------------------------------------------------------------------
12897//----------------------------------------------------------------------------------------------------
12899{
12900 CHECK_AND_ASSERT_THROW_MES(m_multisig, "Wallet is not multisig");
12901 crypto::public_key signer;
12902 CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(get_account().get_keys().m_spend_secret_key, signer), "Failed to generate signer public key");
12903 return signer;
12904}
12905//----------------------------------------------------------------------------------------------------
12907{
12908 CHECK_AND_ASSERT_THROW_MES(m_multisig, "Wallet is not multisig");
12909 crypto::public_key pkey;
12910 CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(msk, pkey), "Failed to derive public key");
12911 return pkey;
12912}
12913//----------------------------------------------------------------------------------------------------
12915{
12916 CHECK_AND_ASSERT_THROW_MES(m_multisig, "Wallet is not multisig");
12917 CHECK_AND_ASSERT_THROW_MES(idx < get_account().get_multisig_keys().size(), "Multisig signing key index out of range");
12918 return get_multisig_signing_public_key(get_account().get_multisig_keys()[idx]);
12919}
12920//----------------------------------------------------------------------------------------------------
12921rct::key wallet2::get_multisig_k(size_t idx, const std::unordered_set<rct::key> &used_L) const
12922{
12923 CHECK_AND_ASSERT_THROW_MES(m_multisig, "Wallet is not multisig");
12924 CHECK_AND_ASSERT_THROW_MES(idx < m_transfers.size(), "idx out of range");
12925 for (const auto &k: m_transfers[idx].m_multisig_k)
12926 {
12927 rct::key L;
12928 rct::scalarmultBase(L, k);
12929 if (used_L.find(L) != used_L.end())
12930 return k;
12931 }
12933 return rct::zero();
12934}
12935//----------------------------------------------------------------------------------------------------
12936rct::multisig_kLRki wallet2::get_multisig_kLRki(size_t n, const rct::key &k) const
12937{
12938 CHECK_AND_ASSERT_THROW_MES(n < m_transfers.size(), "Bad m_transfers index");
12939 rct::multisig_kLRki kLRki;
12940 kLRki.k = k;
12941 cryptonote::generate_multisig_LR(m_transfers[n].get_public_key(), rct::rct2sk(kLRki.k), (crypto::public_key&)kLRki.L, (crypto::public_key&)kLRki.R);
12942 kLRki.ki = rct::ki2rct(m_transfers[n].m_key_image);
12943 return kLRki;
12944}
12945//----------------------------------------------------------------------------------------------------
12946rct::multisig_kLRki wallet2::get_multisig_composite_kLRki(size_t n, const std::unordered_set<crypto::public_key> &ignore_set, std::unordered_set<rct::key> &used_L, std::unordered_set<rct::key> &new_used_L) const
12947{
12948 CHECK_AND_ASSERT_THROW_MES(n < m_transfers.size(), "Bad transfer index");
12949
12950 const transfer_details &td = m_transfers[n];
12951 rct::multisig_kLRki kLRki = get_multisig_kLRki(n, rct::skGen());
12952
12953 // pick a L/R pair from every other participant but one
12954 size_t n_signers_used = 1;
12955 for (const auto &p: m_transfers[n].m_multisig_info)
12956 {
12957 if (ignore_set.find(p.m_signer) != ignore_set.end())
12958 continue;
12959
12960 for (const auto &lr: p.m_LR)
12961 {
12962 if (used_L.find(lr.m_L) != used_L.end())
12963 continue;
12964 used_L.insert(lr.m_L);
12965 new_used_L.insert(lr.m_L);
12966 rct::addKeys(kLRki.L, kLRki.L, lr.m_L);
12967 rct::addKeys(kLRki.R, kLRki.R, lr.m_R);
12968 ++n_signers_used;
12969 break;
12970 }
12971 }
12972 CHECK_AND_ASSERT_THROW_MES(n_signers_used >= m_multisig_threshold, "LR not found for enough participants");
12973
12974 return kLRki;
12975}
12976//----------------------------------------------------------------------------------------------------
12977crypto::key_image wallet2::get_multisig_composite_key_image(size_t n) const
12978{
12979 CHECK_AND_ASSERT_THROW_MES(n < m_transfers.size(), "Bad output index");
12980
12981 const transfer_details &td = m_transfers[n];
12982 const crypto::public_key tx_key = get_tx_pub_key_from_received_outs(td);
12983 const std::vector<crypto::public_key> additional_tx_keys = cryptonote::get_additional_tx_pub_keys_from_extra(td.m_tx);
12985 std::vector<crypto::key_image> pkis;
12986 for (const auto &info: td.m_multisig_info)
12987 for (const auto &pki: info.m_partial_key_images)
12988 pkis.push_back(pki);
12989 bool r = cryptonote::generate_multisig_composite_key_image(get_account().get_keys(), m_subaddresses, td.get_public_key(), tx_key, additional_tx_keys, td.m_internal_output_index, pkis, ki);
12990 THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
12991 return ki;
12992}
12993//----------------------------------------------------------------------------------------------------
12995{
12996 std::vector<tools::wallet2::multisig_info> info;
12997
12999
13000 info.resize(m_transfers.size());
13001 for (size_t n = 0; n < m_transfers.size(); ++n)
13002 {
13003 transfer_details &td = m_transfers[n];
13005 td.m_multisig_k.clear();
13006 info[n].m_LR.clear();
13007 info[n].m_partial_key_images.clear();
13008
13009 for (size_t m = 0; m < get_account().get_multisig_keys().size(); ++m)
13010 {
13011 // we want to export the partial key image, not the full one, so we can't use td.m_key_image
13012 bool r = generate_multisig_key_image(get_account().get_keys(), m, td.get_public_key(), ki);
13013 CHECK_AND_ASSERT_THROW_MES(r, "Failed to generate key image");
13014 info[n].m_partial_key_images.push_back(ki);
13015 }
13016
13017 // Wallet tries to create as many transactions as many signers combinations. We calculate the maximum number here as follows:
13018 // if we have 2/4 wallet with signers: A, B, C, D and A is a transaction creator it will need to pick up 1 signer from 3 wallets left.
13019 // That means counting combinations for excluding 2-of-3 wallets (k = total signers count - threshold, n = total signers count - 1).
13020 size_t nlr = tools::combinations_count(m_multisig_signers.size() - m_multisig_threshold, m_multisig_signers.size() - 1);
13021 for (size_t m = 0; m < nlr; ++m)
13022 {
13023 td.m_multisig_k.push_back(rct::skGen());
13024 const rct::multisig_kLRki kLRki = get_multisig_kLRki(n, td.m_multisig_k.back());
13025 info[n].m_LR.push_back({kLRki.L, kLRki.R});
13026 }
13027
13028 info[n].m_signer = signer;
13029 }
13030
13031 std::stringstream oss;
13033 ar << info;
13034
13035 const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
13036 std::string header;
13037 header += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key));
13038 header += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key));
13039 header += std::string((const char *)&signer, sizeof(crypto::public_key));
13040 std::string ciphertext = encrypt_with_view_secret_key(header + oss.str());
13041
13042 return MULTISIG_EXPORT_FILE_MAGIC + ciphertext;
13043}
13044//----------------------------------------------------------------------------------------------------
13045void wallet2::update_multisig_rescan_info(const std::vector<std::vector<rct::key>> &multisig_k, const std::vector<std::vector<tools::wallet2::multisig_info>> &info, size_t n)
13046{
13047 CHECK_AND_ASSERT_THROW_MES(n < m_transfers.size(), "Bad index in update_multisig_info");
13048 CHECK_AND_ASSERT_THROW_MES(multisig_k.size() >= m_transfers.size(), "Mismatched sizes of multisig_k and info");
13049
13050 MDEBUG("update_multisig_rescan_info: updating index " << n);
13051 transfer_details &td = m_transfers[n];
13052 td.m_multisig_info.clear();
13053 for (const auto &pi: info)
13054 {
13055 CHECK_AND_ASSERT_THROW_MES(n < pi.size(), "Bad pi size");
13056 td.m_multisig_info.push_back(pi[n]);
13057 }
13058 m_key_images.erase(td.m_key_image);
13059 td.m_key_image = get_multisig_composite_key_image(n);
13060 td.m_key_image_known = true;
13061 td.m_key_image_request = false;
13062 td.m_key_image_partial = false;
13063 td.m_multisig_k = multisig_k[n];
13064 m_key_images[td.m_key_image] = n;
13065}
13066//----------------------------------------------------------------------------------------------------
13067size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs)
13068{
13069 CHECK_AND_ASSERT_THROW_MES(m_multisig, "Wallet is not multisig");
13070
13071 std::vector<std::vector<tools::wallet2::multisig_info>> info;
13072 std::unordered_set<crypto::public_key> seen;
13073 for (cryptonote::blobdata &data: blobs)
13074 {
13075 const size_t magiclen = strlen(MULTISIG_EXPORT_FILE_MAGIC);
13076 THROW_WALLET_EXCEPTION_IF(data.size() < magiclen || memcmp(data.data(), MULTISIG_EXPORT_FILE_MAGIC, magiclen),
13077 error::wallet_internal_error, "Bad multisig info file magic in ");
13078
13079 data = decrypt_with_view_secret_key(std::string(data, magiclen));
13080
13081 const size_t headerlen = 3 * sizeof(crypto::public_key);
13082 THROW_WALLET_EXCEPTION_IF(data.size() < headerlen, error::wallet_internal_error, "Bad data size");
13083
13084 const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[0];
13085 const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[sizeof(crypto::public_key)];
13086 const crypto::public_key &signer = *(const crypto::public_key*)&data[2*sizeof(crypto::public_key)];
13087 const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
13088 THROW_WALLET_EXCEPTION_IF(public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key,
13089 error::wallet_internal_error, "Multisig info is for a different account");
13090 if (get_multisig_signer_public_key() == signer)
13091 {
13092 MINFO("Multisig info from this wallet ignored");
13093 continue;
13094 }
13095 if (seen.find(signer) != seen.end())
13096 {
13097 MINFO("Duplicate multisig info ignored");
13098 continue;
13099 }
13100 seen.insert(signer);
13101
13102 std::string body(data, headerlen);
13103 std::istringstream iss(body);
13104 std::vector<tools::wallet2::multisig_info> i;
13106 ar >> i;
13107 MINFO(boost::format("%u outputs found") % boost::lexical_cast<std::string>(i.size()));
13108 info.push_back(std::move(i));
13109 }
13110
13111 CHECK_AND_ASSERT_THROW_MES(info.size() + 1 <= m_multisig_signers.size() && info.size() + 1 >= m_multisig_threshold, "Wrong number of multisig sources");
13112
13113 std::vector<std::vector<rct::key>> k;
13114 k.reserve(m_transfers.size());
13115 for (const auto &td: m_transfers)
13116 k.push_back(td.m_multisig_k);
13117
13118 // how many outputs we're going to update
13119 size_t n_outputs = m_transfers.size();
13120 for (const auto &pi: info)
13121 if (pi.size() < n_outputs)
13122 n_outputs = pi.size();
13123
13124 if (n_outputs == 0)
13125 return 0;
13126
13127 // check signers are consistent
13128 for (const auto &pi: info)
13129 {
13130 CHECK_AND_ASSERT_THROW_MES(std::find(m_multisig_signers.begin(), m_multisig_signers.end(), pi[0].m_signer) != m_multisig_signers.end(),
13131 "Signer is not a member of this multisig wallet");
13132 for (size_t n = 1; n < n_outputs; ++n)
13133 CHECK_AND_ASSERT_THROW_MES(pi[n].m_signer == pi[0].m_signer, "Mismatched signers in imported multisig info");
13134 }
13135
13136 // trim data we don't have info for from all participants
13137 for (auto &pi: info)
13138 pi.resize(n_outputs);
13139
13140 // sort by signer
13141 if (!info.empty() && !info.front().empty())
13142 {
13143 std::sort(info.begin(), info.end(), [](const std::vector<tools::wallet2::multisig_info> &i0, const std::vector<tools::wallet2::multisig_info> &i1){ return memcmp(&i0[0].m_signer, &i1[0].m_signer, sizeof(i0[0].m_signer)); });
13144 }
13145
13146 // first pass to determine where to detach the blockchain
13147 for (size_t n = 0; n < n_outputs; ++n)
13148 {
13149 const transfer_details &td = m_transfers[n];
13150 if (!td.m_key_image_partial)
13151 continue;
13152 MINFO("Multisig info importing from block height " << td.m_block_height);
13153 detach_blockchain(td.m_block_height);
13154 break;
13155 }
13156
13157 for (size_t n = 0; n < n_outputs && n < m_transfers.size(); ++n)
13158 {
13159 update_multisig_rescan_info(k, info, n);
13160 }
13161
13162 m_multisig_rescan_k = &k;
13163 m_multisig_rescan_info = &info;
13164 try
13165 {
13166
13167 refresh(false);
13168 }
13169 catch (...)
13170 {
13171 m_multisig_rescan_info = NULL;
13172 m_multisig_rescan_k = NULL;
13173 throw;
13174 }
13175 m_multisig_rescan_info = NULL;
13176 m_multisig_rescan_k = NULL;
13177
13178 return n_outputs;
13179}
13180//----------------------------------------------------------------------------------------------------
13181std::string wallet2::encrypt(const char *plaintext, size_t len, const crypto::secret_key &skey, bool authenticated) const
13182{
13183 crypto::chacha_key key;
13184 crypto::generate_chacha_key(&skey, sizeof(skey), key, m_kdf_rounds);
13185 std::string ciphertext;
13186 crypto::chacha_iv iv = crypto::rand<crypto::chacha_iv>();
13187 ciphertext.resize(len + sizeof(iv) + (authenticated ? sizeof(crypto::signature) : 0));
13188 crypto::chacha20(plaintext, len, key, iv, &ciphertext[sizeof(iv)]);
13189 memcpy(&ciphertext[0], &iv, sizeof(iv));
13190 if (authenticated)
13191 {
13193 crypto::cn_fast_hash(ciphertext.data(), ciphertext.size() - sizeof(signature), hash);
13194 crypto::public_key pkey;
13196 crypto::signature &signature = *(crypto::signature*)&ciphertext[ciphertext.size() - sizeof(crypto::signature)];
13198 }
13199 return ciphertext;
13200}
13201//----------------------------------------------------------------------------------------------------
13202std::string wallet2::encrypt(const epee::span<char> &plaintext, const crypto::secret_key &skey, bool authenticated) const
13203{
13204 return encrypt(plaintext.data(), plaintext.size(), skey, authenticated);
13205}
13206//----------------------------------------------------------------------------------------------------
13207std::string wallet2::encrypt(const std::string &plaintext, const crypto::secret_key &skey, bool authenticated) const
13208{
13209 return encrypt(plaintext.data(), plaintext.size(), skey, authenticated);
13210}
13211//----------------------------------------------------------------------------------------------------
13212std::string wallet2::encrypt(const epee::wipeable_string &plaintext, const crypto::secret_key &skey, bool authenticated) const
13213{
13214 return encrypt(plaintext.data(), plaintext.size(), skey, authenticated);
13215}
13216//----------------------------------------------------------------------------------------------------
13217std::string wallet2::encrypt_with_view_secret_key(const std::string &plaintext, bool authenticated) const
13218{
13219 return encrypt(plaintext, get_account().get_keys().m_view_secret_key, authenticated);
13220}
13221//----------------------------------------------------------------------------------------------------
13222template<typename T>
13223T wallet2::decrypt(const std::string &ciphertext, const crypto::secret_key &skey, bool authenticated) const
13224{
13225 const size_t prefix_size = sizeof(chacha_iv) + (authenticated ? sizeof(crypto::signature) : 0);
13226 THROW_WALLET_EXCEPTION_IF(ciphertext.size() < prefix_size,
13227 error::wallet_internal_error, "Unexpected ciphertext size");
13228
13229 crypto::chacha_key key;
13230 crypto::generate_chacha_key(&skey, sizeof(skey), key, m_kdf_rounds);
13231 const crypto::chacha_iv &iv = *(const crypto::chacha_iv*)&ciphertext[0];
13232 if (authenticated)
13233 {
13235 crypto::cn_fast_hash(ciphertext.data(), ciphertext.size() - sizeof(signature), hash);
13236 crypto::public_key pkey;
13238 const crypto::signature &signature = *(const crypto::signature*)&ciphertext[ciphertext.size() - sizeof(crypto::signature)];
13240 error::wallet_internal_error, "Failed to authenticate ciphertext");
13241 }
13242 std::unique_ptr<char[]> buffer{new char[ciphertext.size() - prefix_size]};
13243 auto wiper = epee::misc_utils::create_scope_leave_handler([&]() { memwipe(buffer.get(), ciphertext.size() - prefix_size); });
13244 crypto::chacha20(ciphertext.data() + sizeof(iv), ciphertext.size() - prefix_size, key, iv, buffer.get());
13245 return T(buffer.get(), ciphertext.size() - prefix_size);
13246}
13247//----------------------------------------------------------------------------------------------------
13248template epee::wipeable_string wallet2::decrypt(const std::string &ciphertext, const crypto::secret_key &skey, bool authenticated) const;
13249//----------------------------------------------------------------------------------------------------
13250std::string wallet2::decrypt_with_view_secret_key(const std::string &ciphertext, bool authenticated) const
13251{
13252 return decrypt(ciphertext, get_account().get_keys().m_view_secret_key, authenticated);
13253}
13254//----------------------------------------------------------------------------------------------------
13255std::string wallet2::make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error) const
13256{
13259 {
13260 error = std::string("wrong address: ") + address;
13261 return std::string();
13262 }
13263
13264 // we want only one payment id
13265 if (info.has_payment_id && !payment_id.empty())
13266 {
13267 error = "A single payment id is allowed";
13268 return std::string();
13269 }
13270
13271 if (!payment_id.empty())
13272 {
13273 crypto::hash pid32;
13274 crypto::hash8 pid8;
13275 if (!wallet2::parse_long_payment_id(payment_id, pid32) && !parse_short_payment_id(payment_id, pid8))
13276 {
13277 error = "Invalid payment id";
13278 return std::string();
13279 }
13280 }
13281
13282 std::string uri = "electroneum:" + address;
13283 unsigned int n_fields = 0;
13284
13285 if (!payment_id.empty())
13286 {
13287 uri += (n_fields++ ? "&" : "?") + std::string("tx_payment_id=") + payment_id;
13288 }
13289
13290 if (amount > 0)
13291 {
13292 // URI encoded amount is in decimal units, not atomic units
13293 uri += (n_fields++ ? "&" : "?") + std::string("tx_amount=") + cryptonote::print_etn(amount);
13294 }
13295
13296 if (!recipient_name.empty())
13297 {
13298 uri += (n_fields++ ? "&" : "?") + std::string("recipient_name=") + epee::net_utils::conver_to_url_format(recipient_name);
13299 }
13300
13301 if (!tx_description.empty())
13302 {
13303 uri += (n_fields++ ? "&" : "?") + std::string("tx_description=") + epee::net_utils::conver_to_url_format(tx_description);
13304 }
13305
13306 return uri;
13307}
13308//----------------------------------------------------------------------------------------------------
13309bool wallet2::parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error)
13310{
13311 if (uri.substr(0, 12) != "electroneum:")
13312 {
13313 error = std::string("URI has wrong scheme (expected \"electroneum:\"): ") + uri;
13314 return false;
13315 }
13316
13317 std::string remainder = uri.substr(12);
13318 const char *ptr = strchr(remainder.c_str(), '?');
13319 address = ptr ? remainder.substr(0, ptr-remainder.c_str()) : remainder;
13320
13323 {
13324 error = std::string("URI has wrong address: ") + address;
13325 return false;
13326 }
13327 if (!strchr(remainder.c_str(), '?'))
13328 return true;
13329
13330 std::vector<std::string> arguments;
13331 std::string body = remainder.substr(address.size() + 1);
13332 if (body.empty())
13333 return true;
13334 boost::split(arguments, body, boost::is_any_of("&"));
13335 std::set<std::string> have_arg;
13336 for (const auto &arg: arguments)
13337 {
13338 std::vector<std::string> kv;
13339 boost::split(kv, arg, boost::is_any_of("="));
13340 if (kv.size() != 2)
13341 {
13342 error = std::string("URI has wrong parameter: ") + arg;
13343 return false;
13344 }
13345 if (have_arg.find(kv[0]) != have_arg.end())
13346 {
13347 error = std::string("URI has more than one instance of " + kv[0]);
13348 return false;
13349 }
13350 have_arg.insert(kv[0]);
13351
13352 if (kv[0] == "tx_amount")
13353 {
13354 amount = 0;
13355 if (!cryptonote::parse_amount(amount, kv[1]))
13356 {
13357 error = std::string("URI has invalid amount: ") + kv[1];
13358 return false;
13359 }
13360 }
13361 else if (kv[0] == "tx_payment_id")
13362 {
13363 if (info.has_payment_id)
13364 {
13365 error = "Separate payment id given with an integrated address";
13366 return false;
13367 }
13371 {
13372 error = "Invalid payment id: " + kv[1];
13373 return false;
13374 }
13375 payment_id = kv[1];
13376 }
13377 else if (kv[0] == "recipient_name")
13378 {
13379 recipient_name = epee::net_utils::convert_from_url_format(kv[1]);
13380 }
13381 else if (kv[0] == "tx_description")
13382 {
13383 tx_description = epee::net_utils::convert_from_url_format(kv[1]);
13384 }
13385 else
13386 {
13387 unknown_parameters.push_back(arg);
13388 }
13389 }
13390 return true;
13391}
13392//----------------------------------------------------------------------------------------------------
13394{
13397 {
13398 throw std::runtime_error("failed to connect to daemon: " + get_daemon_address());
13399 }
13400 if (version < MAKE_CORE_RPC_VERSION(1, 6))
13401 {
13402 throw std::runtime_error("this function requires RPC version 1.6 or higher");
13403 }
13404 std::tm date = { 0, 0, 0, 0, 0, 0, 0, 0 };
13405 date.tm_year = year - 1900;
13406 date.tm_mon = month - 1;
13407 date.tm_mday = day;
13408 if (date.tm_mon < 0 || 11 < date.tm_mon || date.tm_mday < 1 || 31 < date.tm_mday)
13409 {
13410 throw std::runtime_error("month or day out of range");
13411 }
13412 uint64_t timestamp_target = std::mktime(&date);
13413 std::string err;
13414 uint64_t height_min = 0;
13415 uint64_t height_max = get_daemon_blockchain_height(err) - 1;
13416 if (!err.empty())
13417 {
13418 throw std::runtime_error("failed to get blockchain height");
13419 }
13420 while (true)
13421 {
13424 uint64_t height_mid = (height_min + height_max) / 2;
13425 req.heights =
13426 {
13427 height_min,
13428 height_mid,
13429 height_max
13430 };
13431 bool r = invoke_http_bin("/getblocks_by_height.bin", req, res, rpc_timeout);
13432 if (!r || res.status != CORE_RPC_STATUS_OK)
13433 {
13434 std::ostringstream oss;
13435 oss << "failed to get blocks by heights: ";
13436 for (auto height : req.heights)
13437 oss << height << ' ';
13438 oss << endl << "reason: ";
13439 if (!r)
13440 oss << "possibly lost connection to daemon";
13441 else if (res.status == CORE_RPC_STATUS_BUSY)
13442 oss << "daemon is busy";
13443 else
13444 oss << get_rpc_status(res.status);
13445 throw std::runtime_error(oss.str());
13446 }
13447 cryptonote::block blk_min, blk_mid, blk_max;
13448 if (res.blocks.size() < 3) throw std::runtime_error("Not enough blocks returned from daemon");
13449 if (!parse_and_validate_block_from_blob(res.blocks[0].block, blk_min)) throw std::runtime_error("failed to parse blob at height " + std::to_string(height_min));
13450 if (!parse_and_validate_block_from_blob(res.blocks[1].block, blk_mid)) throw std::runtime_error("failed to parse blob at height " + std::to_string(height_mid));
13451 if (!parse_and_validate_block_from_blob(res.blocks[2].block, blk_max)) throw std::runtime_error("failed to parse blob at height " + std::to_string(height_max));
13452 uint64_t timestamp_min = blk_min.timestamp;
13453 uint64_t timestamp_mid = blk_mid.timestamp;
13454 uint64_t timestamp_max = blk_max.timestamp;
13455 if (!(timestamp_min <= timestamp_mid && timestamp_mid <= timestamp_max))
13456 {
13457 // the timestamps are not in the chronological order.
13458 // assuming they're sufficiently close to each other, simply return the smallest height
13459 return std::min({height_min, height_mid, height_max});
13460 }
13461 if (timestamp_target > timestamp_max)
13462 {
13463 throw std::runtime_error("specified date is in the future");
13464 }
13465 if (timestamp_target <= timestamp_min + 2 * 24 * 60 * 60) // two days of "buffer" period
13466 {
13467 return height_min;
13468 }
13469 if (timestamp_target <= timestamp_mid)
13470 height_max = height_mid;
13471 else
13472 height_min = height_mid;
13473 if (height_max - height_min <= 2 * 24 * 30) // don't divide the height range finer than two days
13474 {
13475 return height_min;
13476 }
13477 }
13478}
13479//----------------------------------------------------------------------------------------------------
13481{
13483 boost::optional<std::string> result = m_node_rpc_proxy.get_target_height(height);
13484 if (result && *result != CORE_RPC_STATUS_OK)
13485 return false;
13487}
13488//----------------------------------------------------------------------------------------------------
13489std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(const std::vector<std::pair<double, double>> &fee_levels)
13490{
13491 for (const auto &fee_level: fee_levels)
13492 {
13493 THROW_WALLET_EXCEPTION_IF(fee_level.first == 0.0, error::wallet_internal_error, "Invalid 0 fee");
13494 THROW_WALLET_EXCEPTION_IF(fee_level.second == 0.0, error::wallet_internal_error, "Invalid 0 fee");
13495 }
13496
13497 // get txpool backlog
13500 m_daemon_rpc_mutex.lock();
13501 bool r = invoke_http_json_rpc("/json_rpc", "get_txpool_backlog", req, res, rpc_timeout);
13502 m_daemon_rpc_mutex.unlock();
13503 THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "Failed to connect to daemon");
13504 THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_txpool_backlog");
13506
13507 uint64_t block_weight_limit = 0;
13508 const auto result = m_node_rpc_proxy.get_block_weight_limit(block_weight_limit);
13509 throw_on_rpc_response_error(result, "get_info");
13510 uint64_t full_reward_zone = block_weight_limit / 2;
13511 THROW_WALLET_EXCEPTION_IF(full_reward_zone == 0, error::wallet_internal_error, "Invalid block weight limit from daemon");
13512
13513 std::vector<std::pair<uint64_t, uint64_t>> blocks;
13514 for (const auto &fee_level: fee_levels)
13515 {
13516 const double our_fee_byte_min = fee_level.first;
13517 const double our_fee_byte_max = fee_level.second;
13518 uint64_t priority_weight_min = 0, priority_weight_max = 0;
13519 for (const auto &i: res.backlog)
13520 {
13521 if (i.weight == 0)
13522 {
13523 MWARNING("Got 0 weight tx from txpool, ignored");
13524 continue;
13525 }
13526 double this_fee_byte = i.fee / (double)i.weight;
13527 if (this_fee_byte >= our_fee_byte_min)
13528 priority_weight_min += i.weight;
13529 if (this_fee_byte >= our_fee_byte_max)
13530 priority_weight_max += i.weight;
13531 }
13532
13533 uint64_t nblocks_min = priority_weight_min / full_reward_zone;
13534 uint64_t nblocks_max = priority_weight_max / full_reward_zone;
13535 MDEBUG("estimate_backlog: priority_weight " << priority_weight_min << " - " << priority_weight_max << " for "
13536 << our_fee_byte_min << " - " << our_fee_byte_max << " piconero byte fee, "
13537 << nblocks_min << " - " << nblocks_max << " blocks at block weight " << full_reward_zone);
13538 blocks.push_back(std::make_pair(nblocks_min, nblocks_max));
13539 }
13540 return blocks;
13541}
13542//----------------------------------------------------------------------------------------------------
13543std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(uint64_t min_tx_weight, uint64_t max_tx_weight, const std::vector<uint64_t> &fees)
13544{
13545 THROW_WALLET_EXCEPTION_IF(min_tx_weight == 0, error::wallet_internal_error, "Invalid 0 fee");
13546 THROW_WALLET_EXCEPTION_IF(max_tx_weight == 0, error::wallet_internal_error, "Invalid 0 fee");
13547 for (uint64_t fee: fees)
13548 {
13549 THROW_WALLET_EXCEPTION_IF(fee == 0, error::wallet_internal_error, "Invalid 0 fee");
13550 }
13551 std::vector<std::pair<double, double>> fee_levels;
13552 for (uint64_t fee: fees)
13553 {
13554 double our_fee_byte_min = fee / (double)min_tx_weight, our_fee_byte_max = fee / (double)max_tx_weight;
13555 fee_levels.emplace_back(our_fee_byte_min, our_fee_byte_max);
13556 }
13557 return estimate_backlog(fee_levels);
13558}
13559//----------------------------------------------------------------------------------------------------
13560uint64_t wallet2::get_segregation_fork_height() const
13561{
13562 if (m_nettype == TESTNET)
13564 if (m_nettype == STAGENET)
13566 THROW_WALLET_EXCEPTION_IF(m_nettype != MAINNET, tools::error::wallet_internal_error, "Invalid network type");
13567
13568 if (m_segregation_height > 0)
13569 return m_segregation_height;
13570
13571 if (m_use_dns && !m_offline)
13572 {
13573 // All four ElectroneumPulse domains have DNSSEC on and valid
13574 static const std::vector<std::string> dns_urls = {
13575 "segheights.electroneumpulse.org",
13576 "segheights.electroneumpulse.net",
13577 "segheights.electroneumpulse.co",
13578 "segheights.electroneumpulse.se"
13579 };
13580
13581 const uint64_t current_height = get_blockchain_current_height();
13582 uint64_t best_diff = std::numeric_limits<uint64_t>::max(), best_height = 0;
13583 std::vector<std::string> records;
13584 if (tools::dns_utils::load_txt_records_from_dns(records, dns_urls, "heights"))
13585 {
13586 for (const auto& record : records)
13587 {
13588 std::vector<std::string> fields;
13589 boost::split(fields, record, boost::is_any_of(":"));
13590 if (fields.size() != 2)
13591 continue;
13594 continue;
13595
13596 MINFO("Found segregation height via DNS: " << fields[0] << " fork height at " << height);
13597 uint64_t diff = height > current_height ? height - current_height : current_height - height;
13598 if (diff < best_diff)
13599 {
13600 best_diff = diff;
13601 best_height = height;
13602 }
13603 }
13604 if (best_height)
13605 return best_height;
13606 }
13607 }
13609}
13610//----------------------------------------------------------------------------------------------------
13611void wallet2::generate_genesis(cryptonote::block& b) const {
13612 cryptonote::generate_genesis_block(b, get_config(m_nettype).GENESIS_TX, get_config(m_nettype).GENESIS_NONCE);
13613}
13614//----------------------------------------------------------------------------------------------------
13616{
13618 state.nettype = m_nettype;
13619 state.multisig = multisig(&state.multisig_is_ready);
13620 state.has_multisig_partial_key_images = has_multisig_partial_key_images();
13621 state.multisig_rounds_passed = m_multisig_rounds_passed;
13622 state.num_transfer_details = m_transfers.size();
13623 if (state.multisig)
13624 {
13625 THROW_WALLET_EXCEPTION_IF(!m_original_keys_available, error::wallet_internal_error, "MMS use not possible because own original Electroneum address not available");
13626 state.address = m_original_address;
13627 state.view_secret_key = m_original_view_secret_key;
13628 }
13629 else
13630 {
13631 state.address = m_account.get_keys().m_account_address;
13632 state.view_secret_key = m_account.get_keys().m_view_secret_key;
13633 }
13634 state.mms_file=m_mms_file;
13635 return state;
13636}
13637//----------------------------------------------------------------------------------------------------
13638wallet_device_callback * wallet2::get_device_callback()
13639{
13640 if (!m_device_callback){
13641 m_device_callback.reset(new wallet_device_callback(this));
13642 }
13643 return m_device_callback.get();
13644}//----------------------------------------------------------------------------------------------------
13645void wallet2::on_device_button_request(uint64_t code)
13646{
13647 if (nullptr != m_callback)
13648 m_callback->on_device_button_request(code);
13649}
13650//----------------------------------------------------------------------------------------------------
13651void wallet2::on_device_button_pressed()
13652{
13653 if (nullptr != m_callback)
13654 m_callback->on_device_button_pressed();
13655}
13656//----------------------------------------------------------------------------------------------------
13657boost::optional<epee::wipeable_string> wallet2::on_device_pin_request()
13658{
13659 if (nullptr != m_callback)
13660 return m_callback->on_device_pin_request();
13661 return boost::none;
13662}
13663//----------------------------------------------------------------------------------------------------
13664boost::optional<epee::wipeable_string> wallet2::on_device_passphrase_request(bool on_device)
13665{
13666 if (nullptr != m_callback)
13667 return m_callback->on_device_passphrase_request(on_device);
13668 return boost::none;
13669}
13670//----------------------------------------------------------------------------------------------------
13671void wallet2::on_device_progress(const hw::device_progress& event)
13672{
13673 if (nullptr != m_callback)
13674 m_callback->on_device_progress(event);
13675}
13676//----------------------------------------------------------------------------------------------------
13677std::string wallet2::get_rpc_status(const std::string &s) const
13678{
13679 if (m_trusted_daemon)
13680 return s;
13681 return "<error>";
13682}
13683//----------------------------------------------------------------------------------------------------
13684void wallet2::throw_on_rpc_response_error(const boost::optional<std::string> &status, const char *method) const
13685{
13686 // no error
13687 if (!status)
13688 return;
13689
13690 MERROR("RPC error: " << method << ": status " << *status);
13691
13692 // empty string -> not connection
13694
13696 THROW_WALLET_EXCEPTION_IF(*status != CORE_RPC_STATUS_OK, tools::error::wallet_generic_rpc_error, method, m_trusted_daemon ? *status : "daemon error");
13697}
13698//----------------------------------------------------------------------------------------------------
13700{
13703 keccak_update(&state, (const uint8_t *) transfer.m_txid.data, sizeof(transfer.m_txid.data));
13704 keccak_update(&state, (const uint8_t *) transfer.m_internal_output_index, sizeof(transfer.m_internal_output_index));
13705 keccak_update(&state, (const uint8_t *) transfer.m_global_output_index, sizeof(transfer.m_global_output_index));
13706 keccak_update(&state, (const uint8_t *) transfer.m_amount, sizeof(transfer.m_amount));
13707 keccak_finish(&state, (uint8_t *) hash.data);
13708}
13709//----------------------------------------------------------------------------------------------------
13711{
13712 CHECK_AND_ASSERT_THROW_MES(transfer_height > (int64_t)m_transfers.size(), "Hash height is greater than number of transfers");
13713
13715 crypto::hash tmp_hash{};
13716 uint64_t current_height = 0;
13717
13719 for(const transfer_details & transfer : m_transfers){
13720 if (transfer_height >= 0 && current_height >= (uint64_t)transfer_height){
13721 break;
13722 }
13723
13724 hash_m_transfer(transfer, tmp_hash);
13725 keccak_update(&state, (const uint8_t *) transfer.m_block_height, sizeof(transfer.m_block_height));
13726 keccak_update(&state, (const uint8_t *) tmp_hash.data, sizeof(tmp_hash.data));
13727 current_height += 1;
13728 }
13729
13730 keccak_finish(&state, (uint8_t *) hash.data);
13731 return current_height;
13732}
13733//----------------------------------------------------------------------------------------------------
13735{
13736 // Compute hash of m_transfers, if differs there had to be BC reorg.
13737 crypto::hash new_transfers_hash{};
13738 hash_m_transfers((int64_t) transfer_height, new_transfers_hash);
13739
13740 if (new_transfers_hash != hash)
13741 {
13742 // Soft-Reset to avoid inconsistency in case of BC reorg.
13743 clear_soft(false); // keep_key_images works only with soft reset.
13744 THROW_WALLET_EXCEPTION_IF(true, error::wallet_internal_error, "Transfers changed during rescan, soft or hard rescan is needed");
13745 }
13746
13747 // Restore key images in m_transfers from m_key_images
13748 for(auto it = m_key_images.begin(); it != m_key_images.end(); it++)
13749 {
13750 THROW_WALLET_EXCEPTION_IF(it->second >= m_transfers.size(), error::wallet_internal_error, "Key images cache contains illegal transfer offset");
13751 m_transfers[it->second].m_key_image = it->first;
13752 m_transfers[it->second].m_key_image_known = true;
13753 }
13754}
13755//----------------------------------------------------------------------------------------------------
13757{
13758 return m_http_client.get_bytes_sent();
13759}
13760//----------------------------------------------------------------------------------------------------
13762{
13763 return m_http_client.get_bytes_received();
13764}
13765
13767 m_checkpoints.add_checkpoint(height, hash);
13768}
13769}
else if(0==res)
uint64_t height
uint8_t version
time_t time
uint8_t threshold
cryptonote::account_public_address get_address(const var_addr_t &inp)
Definition chaingen.cpp:665
std::tuple< uint64_t, crypto::public_key, rct::key > get_outs_entry
Definition chaingen.h:352
void encrypt_viewkey(const crypto::chacha_key &key)
Definition account.h:106
void encrypt_keys(const crypto::chacha_key &key)
Definition account.h:104
void decrypt_keys(const crypto::chacha_key &key)
Definition account.h:105
const account_keys & get_keys() const
Definition account.cpp:264
hw::device & get_device() const
Definition account.h:91
ssl_verification_t verification
Definition net_ssl.h:82
bool has_strong_verification(boost::string_ref host) const noexcept
\retrurn True if host can be verified using this configuration WITHOUT system "root" CAs.
Definition net_ssl.cpp:402
ssl_authentication_t auth
Definition net_ssl.h:80
Non-owning sequence of data. Does not deep copy.
Definition span.h:57
constexpr std::size_t size() const noexcept
Definition span.h:111
constexpr pointer data() const noexcept
Definition span.h:110
const char * data() const noexcept
void append(const char *ptr, size_t len)
bool empty() const noexcept
size_t size() const noexcept
virtual bool has_tx_cold_sign(void) const
Definition device.hpp:244
virtual bool connect(void)=0
virtual void generate_tx_proof(const crypto::hash &prefix_hash, const crypto::public_key &R, const crypto::public_key &A, const boost::optional< crypto::public_key > &B, const crypto::public_key &D, const crypto::secret_key &r, crypto::signature &sig)=0
virtual bool conceal_derivation(crypto::key_derivation &derivation, const crypto::public_key &tx_pub_key, const std::vector< crypto::public_key > &additional_tx_pub_keys, const crypto::key_derivation &main_derivation, const std::vector< crypto::key_derivation > &additional_derivations)=0
virtual bool init(void)=0
virtual void set_network_type(cryptonote::network_type network_type)
Definition device.hpp:248
virtual device_type get_type() const =0
virtual bool derivation_to_scalar(const crypto::key_derivation &derivation, const size_t output_index, crypto::ec_scalar &res)=0
virtual bool has_ki_cold_sync(void) const
Definition device.hpp:243
@ TRANSACTION_CREATE_FAKE
Definition device.hpp:101
@ TRANSACTION_CREATE_REAL
Definition device.hpp:100
@ TRANSACTION_PARSE
Definition device.hpp:102
virtual crypto::public_key get_subaddress_spend_public_key(const cryptonote::account_keys &keys, const cryptonote::subaddress_index &index)=0
virtual bool set_mode(device_mode mode)
Definition device.hpp:130
virtual void set_callback(i_device_callback *callback)
Definition device.hpp:136
virtual bool generate_key_derivation(const crypto::public_key &pub, const crypto::secret_key &sec, crypto::key_derivation &derivation)=0
virtual bool set_name(const std::string &name)=0
virtual void set_derivation_path(const std::string &derivation_path)
Definition device.hpp:137
virtual bool secret_key_to_public_key(const crypto::secret_key &sec, crypto::public_key &pub)=0
virtual std::vector< crypto::public_key > get_subaddress_spend_public_keys(const cryptonote::account_keys &keys, uint32_t account, uint32_t begin, uint32_t end)=0
bool decrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key)
Definition device.hpp:210
@ PROTOCOL_COLD
Definition device.hpp:115
virtual bool generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key, uint64_t kdf_rounds)=0
virtual bool verify_keys(const crypto::secret_key &secret_key, const crypto::public_key &public_key)=0
virtual void computing_key_images(bool started)
Definition device.hpp:247
virtual device_protocol_t device_protocol() const
Definition device.hpp:135
virtual cryptonote::account_public_address get_subaddress(const cryptonote::account_keys &keys, const cryptonote::subaddress_index &index)=0
virtual bool get_public_address(cryptonote::account_public_address &pubkey)=0
virtual bool scalarmultKey(rct::key &aP, const rct::key &P, const rct::key &a)=0
std::vector< std::string > tx_device_aux
boost::optional< int > bp_version
std::vector< cryptonote::address_parse_info > tx_recipients
static void init_options(boost::program_options::options_description &desc_params)
gamma_picker(const std::vector< uint64_t > &rct_offsets)
Definition wallet2.cpp:1011
void wait(threadpool *tpool)
A global thread pool.
Definition threadpool.h:44
void submit(waiter *waiter, std::function< void()> f, bool leaf=false)
static threadpool & getInstance()
Definition threadpool.h:46
bool get_multisig_seed(epee::wipeable_string &seed, const epee::wipeable_string &passphrase=std::string(), bool raw=true) const
Definition wallet2.cpp:1327
void expand_subaddresses(const cryptonote::subaddress_index &index, const bool udpate_account_tags=true)
Definition wallet2.cpp:1478
uint64_t get_max_ring_size() const
Definition wallet2.cpp:7851
std::string exchange_multisig_keys(const epee::wipeable_string &password, const std::vector< std::string > &info)
Definition wallet2.cpp:5280
bool add_address_book_row(const cryptonote::account_public_address &address, const crypto::hash &payment_id, const std::string &description, bool is_subaddress)
Definition wallet2.cpp:3451
bool sanity_check(const std::vector< wallet2::pending_tx > &ptx_vector, std::vector< cryptonote::tx_destination_entry > dsts) const
bool unlock_keys_file()
Definition wallet2.cpp:8225
bool parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const
Definition wallet2.cpp:7033
static const char * tr(const char *str)
Definition wallet2.cpp:994
static bool has_testnet_option(const boost::program_options::variables_map &vm)
Definition wallet2.cpp:1173
bool set_daemon(std::string daemon_address="http://localhost:8080", boost::optional< epee::net_utils::http::login > daemon_login=boost::none, bool trusted_daemon=true, epee::net_utils::ssl_options_t ssl_options=epee::net_utils::ssl_support_t::e_ssl_support_autodetect)
Definition wallet2.cpp:1268
cryptonote::blobdata export_multisig()
bool is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_height) const
Definition wallet2.cpp:6584
bool load_unsigned_tx(const std::string &unsigned_filename, unsigned_tx_set &exported_txs) const
Definition wallet2.cpp:7014
size_t import_outputs(const std::pair< size_t, std::vector< tools::wallet2::transfer_details > > &outputs)
crypto::public_key get_multisig_signing_public_key(size_t idx) const
std::vector< wallet2::pending_tx > create_transactions_2(std::vector< cryptonote::tx_destination_entry > dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector< uint8_t > &extra, uint32_t subaddr_account, std::set< uint32_t > subaddr_indices)
Definition wallet2.cpp:9725
uint64_t get_base_fee() const
Definition wallet2.cpp:7785
bool sign_multisig_tx_to_file(multisig_tx_set &exported_txs, const std::string &filename, std::vector< crypto::hash > &txids)
Definition wallet2.cpp:7710
void light_wallet_get_address_txs()
Definition wallet2.cpp:9454
size_t import_multisig(std::vector< cryptonote::blobdata > info)
void load(const std::string &wallet, const epee::wipeable_string &password)
Definition wallet2.cpp:5833
uint64_t balance_all(bool public_blockchain) const
Definition wallet2.cpp:6292
crypto::public_key get_multisig_signer_public_key(const crypto::secret_key &spend_skey) const
bool invoke_http_json(const boost::string_ref uri, const t_request &req, t_response &res, std::chrono::milliseconds timeout=std::chrono::seconds(15), const boost::string_ref http_method="GET")
Definition wallet2.h:1319
std::string export_outputs_to_str(bool all=false) const
std::string encrypt(const char *plaintext, size_t len, const crypto::secret_key &skey, bool authenticated=true) const
uint64_t get_num_rct_outputs()
void rescan_blockchain(bool hard, bool refresh=true, bool keep_key_images=false)
Definition wallet2.cpp:6540
uint64_t cold_key_image_sync(uint64_t &spent, uint64_t &unspent)
const boost::optional< epee::net_utils::http::login > & get_daemon_login() const
Definition wallet2.h:1165
void add_checkpoint(uint64_t height, std::string hash)
std::vector< wallet2::pending_tx > create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector< uint8_t > &extra)
static std::pair< std::unique_ptr< wallet2 >, password_container > make_new(const boost::program_options::variables_map &vm, bool unattended, const std::function< boost::optional< password_container >(const char *, bool)> &password_prompter)
Uses stdin and stdout. Returns a wallet2 and password for wallet with no file if no errors.
Definition wallet2.cpp:1250
void refresh(bool trusted_daemon)
Definition wallet2.cpp:3060
void set_tx_note(const crypto::hash &txid, const std::string &note)
std::vector< size_t > select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct)
bool invoke_http_bin(const boost::string_ref uri, const t_request &req, t_response &res, std::chrono::milliseconds timeout=std::chrono::seconds(15), const boost::string_ref http_method="GET")
Definition wallet2.h:1326
void set_tx_device_aux(const crypto::hash &txid, const std::string &aux)
void freeze(size_t idx)
Definition wallet2.cpp:1584
static bool parse_payment_id(const std::string &payment_id_str, crypto::hash &payment_id)
Definition wallet2.cpp:5738
bool load_multisig_tx_from_file(const std::string &filename, multisig_tx_set &exported_txs, std::function< bool(const multisig_tx_set &)> accept_func=NULL)
Definition wallet2.cpp:7583
std::string make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error) const
void merge_destinations(bool merge)
Definition wallet2.h:1085
void store_to(const std::string &path, const epee::wipeable_string &password)
store_to Stores wallet to another file(s), deleting old ones
Definition wallet2.cpp:6038
wallet2(cryptonote::network_type nettype=cryptonote::MAINNET, uint64_t kdf_rounds=1, bool unattended=false)
Definition wallet2.cpp:1102
void remove_obsolete_pool_txs(const std::vector< crypto::hash > &tx_hashes)
Definition wallet2.cpp:3136
std::vector< size_t > select_available_outputs(const std::function< bool(const transfer_details &td)> &f) const
const transfer_details & get_transfer_details(size_t idx) const
uint64_t get_daemon_blockchain_height(std::string &err) const
std::vector< pending_tx > create_unmixable_sweep_transactions()
crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const
bool delete_address_book_row(std::size_t row_id)
Definition wallet2.cpp:3466
void check_tx_key_helper(const crypto::hash &txid, const crypto::key_derivation &derivation, const std::vector< crypto::key_derivation > &additional_derivations, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations)
bool load_tx(const std::string &signed_filename, std::vector< tools::wallet2::pending_tx > &ptx, std::function< bool(const signed_tx_set &)> accept_func=NULL)
Definition wallet2.cpp:7321
void add_subaddress(uint32_t index_major, const std::string &label)
Definition wallet2.cpp:1470
bool check_connection(uint32_t *version=NULL, bool *ssl=NULL, uint32_t timeout=200000)
Definition wallet2.cpp:5758
void decrypt_keys(const crypto::chacha_key &key)
Definition wallet2.cpp:4752
bool is_synced() const
bool light_wallet_import_wallet_request(tools::COMMAND_RPC_IMPORT_WALLET_REQUEST::response &response)
Definition wallet2.cpp:9269
void update_pool_state(bool refreshed=false)
Definition wallet2.cpp:3164
std::string get_daemon_address() const
void get_payments_out_sc_migration(std::list< std::pair< crypto::hash, wallet2::confirmed_transfer_details > > &confirmed_payments, uint64_t min_height, uint64_t max_height=(uint64_t) -1, const boost::optional< uint32_t > &subaddr_account=boost::none, const std::set< uint32_t > &subaddr_indices={}) const
Definition wallet2.cpp:6380
static std::string device_derivation_path_option(const boost::program_options::variables_map &vm)
Definition wallet2.cpp:1188
bool get_seed(epee::wipeable_string &electrum_words, const epee::wipeable_string &passphrase=epee::wipeable_string()) const
Definition wallet2.cpp:1301
std::pair< size_t, std::vector< tools::wallet2::transfer_details > > export_outputs(bool all=false) const
std::vector< size_t > select_available_mixable_outputs()
std::tuple< uint64_t, crypto::public_key, rct::key > get_outs_entry
Definition wallet2.h:562
bool get_rings(const crypto::hash &txid, std::vector< std::pair< crypto::key_image, std::vector< uint64_t > > > &outs)
Definition wallet2.cpp:8034
void restore(const std::string &wallet_, const epee::wipeable_string &password, const std::string &device_name, bool create_address_file=false)
Restore a wallet hold by an HW.
Definition wallet2.cpp:5110
std::string sign(const std::string &data) const
size_t get_num_subaddress_accounts() const
Definition wallet2.h:801
bool light_wallet_parse_rct_str(const std::string &rct_string, const crypto::public_key &tx_pub_key, uint64_t internal_output_index, rct::key &decrypted_mask, rct::key &rct_commit, bool decrypt) const
Definition wallet2.cpp:9642
bool load_multisig_tx(cryptonote::blobdata blob, multisig_tx_set &exported_txs, std::function< bool(const multisig_tx_set &)> accept_func=NULL)
Definition wallet2.cpp:7549
void light_wallet_get_unspent_outs()
Definition wallet2.cpp:9284
bool parse_tx_from_str(const std::string &signed_tx_st, std::vector< tools::wallet2::pending_tx > &ptx, std::function< bool(const signed_tx_set &)> accept_func)
Definition wallet2.cpp:7342
static bool query_device(hw::device::device_type &device_type, const std::string &keys_file_name, const epee::wipeable_string &password, uint64_t kdf_rounds=1)
determine the key storage for the specified wallet file
Definition wallet2.cpp:4807
std::map< uint32_t, uint64_t > balance_per_subaddress(uint32_t subaddr_index_major, bool public_blockchain=false) const
Definition wallet2.cpp:6188
std::string get_attribute(const std::string &key) const
bool is_keys_file_locked() const
Definition wallet2.cpp:8236
void add_subaddress_account(const std::string &label, const bool update_account_tags=true)
Definition wallet2.cpp:1463
void hash_m_transfer(const transfer_details &transfer, crypto::hash &hash) const
bool sign_multisig_tx(multisig_tx_set &exported_txs, std::vector< crypto::hash > &txids)
Definition wallet2.cpp:7607
void cold_sign_tx(const std::vector< pending_tx > &ptx_vector, signed_tx_set &exported_txs, std::vector< cryptonote::address_parse_info > &dsts_info, std::vector< std::string > &tx_device_aux)
void set_description(const std::string &description)
@ AskPasswordToDecrypt
Definition wallet2.h:228
@ AskPasswordOnAction
Definition wallet2.h:227
cryptonote::account_base & get_account()
Definition wallet2.h:734
static std::pair< std::unique_ptr< wallet2 >, password_container > make_from_json(const boost::program_options::variables_map &vm, bool unattended, const std::string &json_file, const std::function< boost::optional< password_container >(const char *, bool)> &password_prompter)
Uses stdin and stdout. Returns a wallet2 if no errors.
Definition wallet2.cpp:1227
std::string get_subaddress_label(const cryptonote::subaddress_index &index) const
Definition wallet2.cpp:1518
void get_unconfirmed_payments_out(std::list< std::pair< crypto::hash, wallet2::unconfirmed_transfer_details > > &unconfirmed_payments, const boost::optional< uint32_t > &subaddr_account=boost::none, const std::set< uint32_t > &subaddr_indices={}) const
Definition wallet2.cpp:6396
bool verify(const std::string &data, const cryptonote::account_public_address &address, const std::string &signature) const
static std::string device_name_option(const boost::program_options::variables_map &vm)
Definition wallet2.cpp:1183
uint64_t get_bytes_sent() const
void set_subaddress_label(const cryptonote::subaddress_index &index, const std::string &label)
Definition wallet2.cpp:1528
std::vector< wallet2::pending_tx > create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector< uint8_t > &extra, uint32_t subaddr_account, std::set< uint32_t > subaddr_indices, const bool migrate=false)
uint64_t get_min_ring_size() const
Definition wallet2.cpp:7836
uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm=-1) const
Definition wallet2.cpp:7732
int get_fee_algorithm() const
Definition wallet2.cpp:7824
bool init(std::string daemon_address="http://localhost:8080", boost::optional< epee::net_utils::http::login > daemon_login=boost::none, boost::asio::ip::tcp::endpoint proxy={}, uint64_t upper_transaction_weight_limit=0, bool trusted_daemon=true, epee::net_utils::ssl_options_t ssl_options=epee::net_utils::ssl_support_t::e_ssl_support_autodetect, std::string blockchain_db_path="")
Definition wallet2.cpp:1282
void get_payments_out_migration(std::list< std::pair< crypto::hash, wallet2::confirmed_transfer_details > > &confirmed_payments, uint64_t min_height, uint64_t max_height=(uint64_t) -1, const boost::optional< uint32_t > &subaddr_account=boost::none, const std::set< uint32_t > &subaddr_indices={}) const
Definition wallet2.cpp:6363
void get_payments(const crypto::hash &payment_id, std::list< wallet2::payment_details > &payments, uint64_t min_height=0, const boost::optional< uint32_t > &subaddr_account=boost::none, const std::set< uint32_t > &subaddr_indices={}) const
Definition wallet2.cpp:6320
payment_container export_payments() const
bool use_fork_rules(uint8_t version, int64_t early_blocks=0) const
bool reconnect_device()
Definition wallet2.cpp:1390
void get_payments_out(std::list< std::pair< crypto::hash, wallet2::confirmed_transfer_details > > &confirmed_payments, uint64_t min_height, uint64_t max_height=(uint64_t) -1, const boost::optional< uint32_t > &subaddr_account=boost::none, const std::set< uint32_t > &subaddr_indices={}) const
Definition wallet2.cpp:6346
void thaw(size_t idx)
Definition wallet2.cpp:1591
bool has_multisig_partial_key_images() const
Definition wallet2.cpp:5647
std::vector< std::pair< uint64_t, uint64_t > > estimate_backlog(const std::vector< std::pair< double, double > > &fee_levels)
void get_transfers(wallet2::transfer_container &incoming_transfers) const
Definition wallet2.cpp:6315
std::string get_tx_device_aux(const crypto::hash &txid) const
bool light_wallet_get_address_info(tools::COMMAND_RPC_GET_ADDRESS_INFO::response &response)
Definition wallet2.cpp:9438
uint64_t get_blockchain_height_by_date(uint16_t year, uint8_t month, uint8_t day)
static bool verify_multisig_info(const std::string &data, crypto::secret_key &skey, crypto::public_key &pkey)
Definition wallet2.cpp:5548
uint64_t hash_m_transfers(int64_t transfer_height, crypto::hash &hash) const
std::vector< transfer_details > transfer_container
Definition wallet2.h:449
size_t import_outputs_from_str(const std::string &outputs_st)
void change_password(const std::string &filename, const epee::wipeable_string &original_password, const epee::wipeable_string &new_password)
Definition wallet2.cpp:4320
void import_blockchain(const std::tuple< size_t, crypto::hash, std::vector< crypto::hash > > &bc)
std::string sign_multisig_participant(const std::string &data) const
sign_multisig_participant signs given message with the multisig public signer key
void check_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector< crypto::secret_key > &additional_tx_keys, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations)
bool set_ring(const crypto::key_image &key_image, const std::vector< uint64_t > &outs, bool relative)
Definition wallet2.cpp:8063
std::string make_multisig(const epee::wipeable_string &password, const std::vector< std::string > &info, uint32_t threshold)
Creates a multisig wallet.
Definition wallet2.cpp:5466
bool lock_keys_file()
Definition wallet2.cpp:8214
@ RefreshOptimizeCoinbase
Definition wallet2.h:220
void set_seed_language(const std::string &language)
Sets the seed language.
Definition wallet2.cpp:1425
static std::unique_ptr< wallet2 > make_dummy(const boost::program_options::variables_map &vm, bool unattended, const std::function< boost::optional< password_container >(const char *, bool)> &password_prompter)
Just parses variables.
Definition wallet2.cpp:1261
const char *const ATTRIBUTE_DESCRIPTION
Definition wallet2.h:1309
std::string get_description() const
uint64_t get_bytes_received() const
void set_attribute(const std::string &key, const std::string &value)
void import_payments(const payment_container &payments)
bool unblackball_output(const std::pair< uint64_t, uint64_t > &output)
Definition wallet2.cpp:8198
void encrypt_keys(const crypto::chacha_key &key)
Definition wallet2.cpp:4746
bool get_ring(const crypto::key_image &key_image, std::vector< uint64_t > &outs)
Definition wallet2.cpp:8057
const std::pair< std::map< std::string, std::string >, std::vector< std::string > > & get_account_tags()
Get the list of registered account tags.
bool sign_tx(const std::string &unsigned_filename, const std::string &signed_filename, std::vector< wallet2::pending_tx > &ptx, std::function< bool(const unsigned_tx_set &)> accept_func=NULL, bool export_raw=false)
mms::multisig_wallet_state get_multisig_wallet_state() const
uint64_t get_daemon_blockchain_target_height(std::string &err)
static std::pair< std::unique_ptr< wallet2 >, password_container > make_from_file(const boost::program_options::variables_map &vm, bool unattended, const std::string &wallet_file, const std::function< boost::optional< password_container >(const char *, bool)> &password_prompter)
Uses stdin and stdout. Returns a wallet2 and password for wallet_file if no errors.
Definition wallet2.cpp:1233
void set_subaddress_lookahead(size_t major, size_t minor)
Definition wallet2.cpp:1535
uint64_t get_fee_quantization_mask() const
Definition wallet2.cpp:7807
boost::optional< cryptonote::subaddress_index > get_subaddress_index(const cryptonote::account_public_address &address) const
Definition wallet2.cpp:1437
bool check_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message, const std::string &sig_str, uint64_t &received, bool &in_pool, uint64_t &confirmations)
bool finalize_multisig(const epee::wipeable_string &password, const std::vector< std::string > &info)
Finalizes creation of a multisig wallet.
Definition wallet2.cpp:5516
std::string get_integrated_address_as_str(const crypto::hash8 &payment_id) const
Definition wallet2.cpp:1458
void get_unconfirmed_payments(std::list< std::pair< crypto::hash, wallet2::pool_payment_details > > &unconfirmed_payments, const boost::optional< uint32_t > &subaddr_account=boost::none, const std::set< uint32_t > &subaddr_indices={}) const
Definition wallet2.cpp:6408
bool save_tx(const std::vector< pending_tx > &ptx_vector, const std::string &filename) const
Definition wallet2.cpp:6975
std::string decrypt_with_view_secret_key(const std::string &ciphertext, bool authenticated=true) const
std::string get_spend_proof(const crypto::hash &txid, const std::string &message)
void set_account_tag(const std::set< uint32_t > &account_indices, const std::string &tag)
Set a tag to the given accounts.
size_t get_num_subaddresses(uint32_t index_major) const
Definition wallet2.h:802
uint64_t get_blockchain_current_height() const
Definition wallet2.h:898
void set_account_tag_description(const std::string &tag, const std::string &description)
Set the label of the given tag.
std::string get_wallet_file() const
bool is_deterministic() const
Checks if deterministic wallet.
Definition wallet2.cpp:1293
static bool parse_long_payment_id(const std::string &payment_id_str, crypto::hash &payment_id)
Definition wallet2.cpp:5712
bool store_tx_info() const
Definition wallet2.h:1069
void transfer_selected(const std::vector< cryptonote::tx_destination_entry > &dsts, const std::vector< size_t > &selected_transfers, size_t fake_outputs_count, std::vector< std::vector< tools::wallet2::get_outs_entry > > &outs, uint64_t unlock_time, uint64_t fee, const std::vector< uint8_t > &extra, T destination_split_strategy, const tx_dust_policy &dust_policy, cryptonote::transaction &tx, pending_tx &ptx)
Definition wallet2.cpp:8952
bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector< std::string > &unknown_parameters, std::string &error)
std::vector< size_t > select_available_unmixable_outputs()
T decrypt(const std::string &ciphertext, const crypto::secret_key &skey, bool authenticated=true) const
bool blackball_output(const std::pair< uint64_t, uint64_t > &output)
Definition wallet2.cpp:8175
bool sign_multisig_tx_from_file(const std::string &filename, std::vector< crypto::hash > &txids, std::function< bool(const multisig_tx_set &)> accept_func)
Definition wallet2.cpp:7718
bool multisig(bool *ready=NULL, uint32_t *threshold=NULL, uint32_t *total=NULL) const
Definition wallet2.cpp:5634
uint64_t unlocked_balance(uint32_t subaddr_index_major, bool public_blockchain, uint64_t *blocks_to_unlock=NULL) const
Definition wallet2.cpp:6172
std::vector< wallet2::pending_tx > create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector< size_t > unused_transfers_indices, std::vector< size_t > unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector< uint8_t > &extra, const uint8_t tx_version)
bool verify_with_public_key(const std::string &data, const crypto::public_key &public_key, const std::string &signature) const
verify_with_public_key verifies message was signed with given public key
uint32_t adjust_priority(uint32_t priority)
Definition wallet2.cpp:7877
std::string save_multisig_tx(multisig_tx_set txs)
Definition wallet2.cpp:7431
void generate(const std::string &wallet_, const epee::wipeable_string &password, const epee::wipeable_string &multisig_data, bool create_address_file=false)
Generates a wallet or restores one.
Definition wallet2.cpp:4869
bool has_unknown_key_images() const
Definition wallet2.cpp:5657
friend class wallet_keys_unlocker
Definition wallet2.h:213
bool set_blackballed_outputs(const std::vector< std::pair< uint64_t, uint64_t > > &outputs, bool add=false)
Definition wallet2.cpp:8183
static bool parse_short_payment_id(const std::string &payment_id_str, crypto::hash8 &payment_id)
Definition wallet2.cpp:5725
@ BackgroundMiningMaybe
Definition wallet2.h:232
bool check_reserve_proof(const cryptonote::account_public_address &address, const std::string &message, const std::string &sig_str, uint64_t &total, uint64_t &spent)
Verifies a proof of reserve.
bool key_on_device() const
Definition wallet2.h:830
std::tuple< size_t, crypto::hash, std::vector< crypto::hash > > export_blockchain() const
cryptonote::account_public_address get_subaddress(const cryptonote::subaddress_index &index) const
Definition wallet2.cpp:1430
void set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector< crypto::secret_key > &additional_tx_keys)
void get_hard_fork_info(uint8_t version, uint64_t &earliest_height) const
uint64_t import_key_images(const std::vector< std::pair< crypto::key_image, crypto::signature > > &signed_key_images, size_t offset, uint64_t &spent, uint64_t &unspent, bool check_spent=true)
bool check_spend_proof(const crypto::hash &txid, const std::string &message, const std::string &sig_str)
multisig_tx_set make_multisig_tx_set(const std::vector< pending_tx > &ptx_vector) const
Definition wallet2.cpp:7477
bool is_output_blackballed(const std::pair< uint64_t, uint64_t > &output) const
Definition wallet2.cpp:8206
void rewrite(const std::string &wallet_name, const epee::wipeable_string &password)
Rewrites to the wallet file for wallet upgrade (doesn't generate key, assumes it's already there).
Definition wallet2.cpp:5670
bool light_wallet_key_image_is_ours(const crypto::key_image &key_image, const crypto::public_key &tx_public_key, uint64_t out_index)
Definition wallet2.cpp:9667
bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector< crypto::secret_key > &additional_tx_keys)
static bool has_stagenet_option(const boost::program_options::variables_map &vm)
Definition wallet2.cpp:1178
size_t pop_best_value_from(const transfer_container &transfers, std::vector< size_t > &unused_dust_indices, const std::vector< size_t > &selected_transfers, bool smallest=false) const
Definition wallet2.cpp:6691
bool is_trusted_daemon() const
Definition wallet2.h:765
bool unset_ring(const std::vector< crypto::key_image > &key_images)
Definition wallet2.cpp:8072
uint64_t get_approximate_blockchain_height() const
Calculates the approximate blockchain height from current date/time.
std::map< uint32_t, std::pair< uint64_t, uint64_t > > unlocked_balance_per_subaddress(uint32_t subaddr_index_major, bool public_blockchain=false) const
Definition wallet2.cpp:6249
bool set_ring_database(const std::string &filename)
Definition wallet2.cpp:7956
bool get_tx_key_cached(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector< crypto::secret_key > &additional_tx_keys) const
std::string get_subaddress_as_str(const cryptonote::subaddress_index &index) const
Definition wallet2.cpp:1452
const std::string & get_seed_language() const
Gets the seed language.
Definition wallet2.cpp:1417
void import_payments_out(const std::list< std::pair< crypto::hash, wallet2::confirmed_transfer_details > > &confirmed_payments)
bool export_key_images(const std::string &filename) const
void write_watch_only_wallet(const std::string &wallet_name, const epee::wipeable_string &password, std::string &new_keys_filename)
Writes to a file named based on the normal wallet (doesn't generate key, assumes it's already there).
Definition wallet2.cpp:5686
static bool verify_extra_multisig_info(const std::string &data, std::unordered_set< crypto::public_key > &pkeys, crypto::public_key &signer)
Definition wallet2.cpp:5586
std::string dump_tx_to_str(const std::vector< pending_tx > &ptx_vector) const
Definition wallet2.cpp:6984
bool light_wallet_login(bool &new_address)
Definition wallet2.cpp:9240
std::string encrypt_with_view_secret_key(const std::string &plaintext, bool authenticated=true) const
bool auto_low_priority() const
Definition wallet2.h:1093
bool parse_multisig_tx_from_str(std::string multisig_tx_st, multisig_tx_set &exported_txs) const
Definition wallet2.cpp:7505
bool find_and_save_rings(bool force=true)
Definition wallet2.cpp:8109
uint64_t unlocked_balance_all(bool public_blockchain, uint64_t *blocks_to_unlock=NULL) const
Definition wallet2.cpp:6300
bool public_transactions_required() const
Definition wallet2.h:900
std::unordered_multimap< crypto::hash, payment_details > payment_container
Definition wallet2.h:450
static bool wallet_valid_path_format(const std::string &file_path)
Definition wallet2.cpp:5707
bool frozen(size_t idx) const
Definition wallet2.cpp:1598
bool is_transfer_unlocked(const transfer_details &td) const
Definition wallet2.cpp:6565
void cold_tx_aux_import(const std::vector< pending_tx > &ptx, const std::vector< std::string > &tx_device_aux)
uint64_t adjust_mixin(uint64_t mixin) const
Definition wallet2.cpp:7860
static constexpr const std::chrono::seconds rpc_timeout
Definition wallet2.h:216
uint64_t balance(uint32_t subaddr_index_major, bool public_blockchain) const
Definition wallet2.cpp:6162
void commit_tx(pending_tx &ptx_vector)
Definition wallet2.cpp:6887
static void init_options(boost::program_options::options_description &desc_params)
Definition wallet2.cpp:1193
bool is_deprecated() const
Tells if the wallet file is deprecated.
Definition wallet2.cpp:1546
std::string get_reserve_proof(const boost::optional< std::pair< uint32_t, uint64_t > > &account_minreserve, const std::string &message)
Generates a proof that proves the reserve of unspent funds.
void light_wallet_get_outs(std::vector< std::vector< get_outs_entry > > &outs, const std::vector< size_t > &selected_transfers, size_t fake_outputs_count)
Definition wallet2.cpp:8268
std::string get_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message)
std::string sign_tx_dump_to_str(unsigned_tx_set &exported_txs, std::vector< wallet2::pending_tx > &ptx, signed_tx_set &signed_txes)
Definition wallet2.cpp:7295
void discard_unmixable_outputs()
void finish_rescan_bc_keep_key_images(uint64_t transfer_height, const crypto::hash &hash)
std::string get_tx_note(const crypto::hash &txid) const
std::string get_keys_file() const
std::string path() const
Definition wallet2.cpp:6027
static void wallet_exists(const std::string &file_path, bool &keys_file_exists, bool &wallet_file_exists)
Check if wallet keys and bin files exist.
Definition wallet2.cpp:5697
cryptonote::network_type nettype() const
Definition wallet2.h:824
size_t pop_best_value(std::vector< size_t > &unused_dust_indices, const std::vector< size_t > &selected_transfers, bool smallest=false) const
Definition wallet2.cpp:6740
void rescan_spent()
Definition wallet2.cpp:6417
uint64_t estimate_blockchain_height()
Definition wallet2.cpp:4990
bool invoke_http_json_rpc(const boost::string_ref uri, const std::string &method_name, const t_request &req, t_response &res, std::chrono::milliseconds timeout=std::chrono::seconds(15), const boost::string_ref http_method="GET", const std::string &req_id="0")
Definition wallet2.h:1333
cryptonote::account_public_address get_address() const
Definition wallet2.h:793
crypto::public_key get_subaddress_spend_public_key(const cryptonote::subaddress_index &index) const
Definition wallet2.cpp:1445
std::string get_multisig_info() const
Definition wallet2.cpp:5529
const std::string & device_name() const
Definition wallet2.h:1109
static bool verify_password(const std::string &keys_file_name, const epee::wipeable_string &password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds)
verify password for specified wallet keys file.
Definition wallet2.cpp:4699
void set_offline(bool offline=true)
Definition wallet2.cpp:5810
boost::optional< epee::wipeable_string > on_passphrase_request(bool on_device) override
Definition wallet2.cpp:1089
void on_button_pressed() override
Definition wallet2.cpp:1076
void on_button_request(uint64_t code=0) override
Definition wallet2.cpp:1070
void on_progress(const hw::device_progress &event) override
Definition wallet2.cpp:1096
boost::optional< epee::wipeable_string > on_pin_request() override
Definition wallet2.cpp:1082
wallet_keys_unlocker(wallet2 &w, const boost::optional< tools::password_container > &password)
Definition wallet2.cpp:1034
#define tr(x)
#define MAKE_CORE_RPC_VERSION(major, minor)
#define CORE_RPC_STATUS_OK
#define CORE_RPC_STATUS_BUSY
void sc_reduce32(unsigned char *)
#define HF_VERSION_ENABLE_RCT
#define CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE
#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5
#define FEE_PER_KB_V11
#define FEE_PER_BYTE
#define HF_VERSION_MIN_MIXIN_6
#define ETN_DEFAULT_TX_SPENDABLE_AGE_V8
#define HF_VERSION_DYNAMIC_FEE
#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2
#define FEE_PER_KB_V6
#define HF_VERSION_MIN_MIXIN_2
#define HF_VERSION_SMALLER_BP
#define HF_VERSION_ENFORCE_0_DECOY_TXS
#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1
#define CURRENT_HARDFORK_VERSION
#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS
#define DIFFICULTY_TARGET_V6
#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V8
#define HF_VERSION_MAX_RING_11
#define CRYPTONOTE_DISPLAY_DECIMAL_POINT
#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V6
#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V10
#define ETN_MINED_ETN_UNLOCK_WINDOW_V8
#define CRYPTONOTE_MAX_BLOCK_NUMBER
#define HF_VERSION_MIN_MIXIN_10
#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS
#define CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE
#define CRYPTONOTE_MINED_ETN_UNLOCK_WINDOW
#define HF_VERSION_PER_BYTE_FEE
#define HF_VERSION_ZERO_FEE
#define HF_VERSION_MIN_MIXIN_4
std::string message("Message requiring signing")
#define ELPP
Mnemonic seed generation and wallet restoration from them.
void * memcpy(void *a, const void *b, size_t c)
@ HASH_SIZE
Definition hash-ops.h:78
const char * res
const char * key
const char * i18n_translate(const char *s, const std::string &context)
Definition i18n.cpp:323
#define GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, name, type, jtype, mandatory, def)
Definition json_util.h:32
void keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen)
void keccak_finish(KECCAK_CTX *ctx, uint8_t *md)
void keccak_update(KECCAK_CTX *ctx, const uint8_t *in, size_t inlen)
void keccak_init(KECCAK_CTX *ctx)
void * memwipe(void *src, size_t n)
#define AUTO_VAL_INIT(v)
#define MERROR(x)
Definition misc_log_ex.h:73
#define LOG_PRINT_L3(x)
#define ELECTRONEUM_DEFAULT_LOG_CATEGORY
Definition misc_log_ex.h:36
#define MWARNING(x)
Definition misc_log_ex.h:74
#define MDEBUG(x)
Definition misc_log_ex.h:76
#define MCLOG_RED(level, cat, x)
Definition misc_log_ex.h:58
#define ENDL
#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message)
#define LOG_PRINT_L1(x)
#define LOG_ERROR(x)
Definition misc_log_ex.h:98
#define CHECK_AND_ASSERT_THROW_MES(expr, message)
#define MTRACE(x)
Definition misc_log_ex.h:77
#define MINFO(x)
Definition misc_log_ex.h:75
#define LOG_PRINT_L2(x)
#define LOG_PRINT_L0(x)
Definition misc_log_ex.h:99
void add_arg(boost::program_options::options_description &description, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg, bool unique=true)
bool is_arg_defaulted(const boost::program_options::variables_map &vm, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg)
std::enable_if<!std::is_same< T, bool >::value, bool >::type has_arg(const boost::program_options::variables_map &vm, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg)
T get_arg(const boost::program_options::variables_map &vm, const arg_descriptor< T, false, true > &arg)
uint64_t const DEFAULT_DUST_THRESHOLD
bool words_to_bytes(const epee::wipeable_string &words, epee::wipeable_string &dst, size_t len, bool duplicate, std::string &language_name)
Converts seed words to bytes (secret key).
bool get_is_old_style_seed(const epee::wipeable_string &seed)
Tells if the seed passed is an old style seed or not.
const std::string old_language_name
bool bytes_to_words(const char *src, size_t len, epee::wipeable_string &words, const std::string &language_name)
Converts bytes (secret key) to seed words.
crypto namespace.
Definition crypto.cpp:58
const crypto::public_key null_pkey
Definition crypto.cpp:72
void generate_signature(const hash &prefix_hash, const public_key &pub, const secret_key &sec, signature &sig)
Definition crypto.h:292
std::enable_if< std::is_pod< T >::value, T >::type rand()
Definition crypto.h:216
void generate_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional< public_key > &B, const public_key &D, const secret_key &r, signature &sig)
Definition crypto.h:311
POD_CLASS signature
Definition crypto.h:108
const crypto::secret_key null_skey
Definition crypto.cpp:73
epee::mlocked< tools::scrubbed< ec_scalar > > secret_key
Definition crypto.h:82
void cn_fast_hash(const void *data, size_t length, char *hash)
bool derive_subaddress_public_key(const public_key &out_key, const key_derivation &derivation, std::size_t output_index, public_key &result)
Definition crypto.h:286
POD_CLASS hash8
Definition hash.h:53
POD_CLASS key_derivation
Definition crypto.h:101
void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, uint64_t height)
void derive_secret_key(const key_derivation &derivation, std::size_t output_index, const secret_key &base, secret_key &derived_key)
Definition crypto.h:282
bool generate_key_derivation(const public_key &key1, const secret_key &key2, key_derivation &derivation)
Definition crypto.h:272
void generate_ring_signature(const hash &prefix_hash, const key_image &image, const public_key *const *pubs, std::size_t pubs_count, const secret_key &sec, std::size_t sec_index, signature *sig)
Definition crypto.h:327
POD_CLASS public_key
Definition crypto.h:79
bool derive_public_key(const key_derivation &derivation, std::size_t output_index, const public_key &base, public_key &derived_key)
Definition crypto.h:275
void rand(size_t N, uint8_t *bytes)
Definition crypto.h:209
POD_CLASS key_image
Definition crypto.h:105
bool check_signature(const hash &prefix_hash, const public_key &pub, const signature &sig)
Definition crypto.h:295
void sc_add(unsigned char *, const unsigned char *, const unsigned char *)
bool secret_key_to_public_key(const secret_key &sec, public_key &pub)
Definition crypto.h:262
std::enable_if< std::is_unsigned< T >::value, T >::type rand_idx(T sz)
Definition crypto.h:244
bool check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional< public_key > &B, const public_key &D, const signature &sig)
Definition crypto.h:314
POD_CLASS hash
Definition hash.h:50
void sc_sub(unsigned char *, const unsigned char *, const unsigned char *)
void generate_key_image(const public_key &pub, const secret_key &sec, key_image &image)
Definition crypto.h:324
void derivation_to_scalar(const key_derivation &derivation, size_t output_index, ec_scalar &res)
Definition crypto.h:279
bool check_ring_signature(const hash &prefix_hash, const key_image &image, const public_key *const *pubs, std::size_t pubs_count, const signature *sig)
Definition crypto.h:333
Holds cryptonote related classes and helpers.
Definition ban.cpp:40
crypto::secret_key calculate_multisig_signer_key(const std::vector< crypto::secret_key > &multisig_keys)
Definition multisig.cpp:100
std::string obj_to_json_str(T &obj)
bool construct_tx_and_get_tx_key(const account_keys &sender_account_keys, const std::unordered_map< crypto::public_key, subaddress_index > &subaddresses, std::vector< tx_source_entry > &sources, std::vector< tx_destination_entry > &destinations, const boost::optional< cryptonote::account_public_address > &change_addr, const std::vector< uint8_t > &extra, transaction &tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector< crypto::secret_key > &additional_tx_keys, bool rct, const rct::RCTConfig &rct_config, rct::multisig_out *msout, const uint32_t account_major_offset, const cryptonote::network_type nettype)
crypto::public_key generate_multisig_M_N_spend_public_key(const std::vector< crypto::public_key > &pkeys)
generate_multisig_M_N_spend_public_key calculates multisig wallet's spend public key by summing all o...
Definition multisig.cpp:132
bool is_valid_decomposed_amount(uint64_t amount)
bool generate_key_image_helper(const account_keys &ack, const std::unordered_map< crypto::public_key, subaddress_index > &subaddresses, const crypto::public_key &out_key, const crypto::public_key &tx_public_key, const std::vector< crypto::public_key > &additional_tx_public_keys, size_t real_output_index, keypair &in_ephemeral, crypto::key_image &ki, hw::device &hwdev, const uint32_t account_major_offset)
uint64_t get_outs_etn_amount(const transaction &tx)
bool get_encrypted_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash8 &payment_id)
uint32_t multisig_rounds_required(uint32_t participants, uint32_t threshold)
Definition multisig.cpp:181
boost::variant< txin_gen, txin_to_script, txin_to_scripthash, txin_to_key, txin_to_key_public > txin_v
std::vector< crypto::public_key > get_additional_tx_pub_keys_from_extra(const std::vector< uint8_t > &tx_extra)
bool get_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash &payment_id)
void set_payment_id_to_tx_extra_nonce(blobdata &extra_nonce, const crypto::hash &payment_id)
void get_transaction_prefix_hash(const transaction_prefix &tx, crypto::hash &h)
unsigned int get_default_decimal_point()
crypto::secret_key get_multisig_blinded_secret_key(const crypto::secret_key &key)
Definition multisig.cpp:47
bool generate_multisig_composite_key_image(const account_keys &keys, const std::unordered_map< crypto::public_key, subaddress_index > &subaddresses, const crypto::public_key &out_key, const crypto::public_key &tx_public_key, const std::vector< crypto::public_key > &additional_tx_public_keys, size_t real_output_index, const std::vector< crypto::key_image > &pkis, crypto::key_image &ki)
Definition multisig.cpp:156
std::vector< uint64_t > relative_output_offsets_to_absolute(const std::vector< uint64_t > &off)
bool get_block_hash(const block &b, crypto::hash &res)
bool get_account_address_from_str(address_parse_info &info, network_type nettype, std::string const &str)
bool parse_and_validate_block_from_blob(const blobdata &b_blob, block &b, crypto::hash *block_hash)
bool generate_multisig_key_image(const account_keys &keys, size_t multisig_key_index, const crypto::public_key &out_key, crypto::key_image &ki)
Definition multisig.cpp:142
crypto::secret_key encrypt_key(crypto::secret_key key, const epee::wipeable_string &passphrase)
crypto::public_key get_tx_pub_key_from_extra(const std::vector< uint8_t > &tx_extra, size_t pk_index)
boost::optional< subaddress_receive_info > is_out_to_acc_precomp(const std::unordered_map< crypto::public_key, subaddress_index > &subaddresses, const crypto::public_key &out_key, const crypto::key_derivation &derivation, const std::vector< crypto::key_derivation > &additional_derivations, size_t output_index, hw::device &hwdev)
std::string get_account_address_as_str(network_type nettype, bool subaddress, account_public_address const &adr)
bool add_tx_pub_key_to_extra(transaction &tx, const crypto::public_key &tx_pub_key)
crypto::hash get_transaction_hash(const transaction &t)
bool find_tx_extra_field_by_type(const std::vector< tx_extra_field > &tx_extra_fields, T &field, size_t index=0)
bool is_coinbase(const transaction &tx)
crypto::secret_key decrypt_key(crypto::secret_key key, const epee::wipeable_string &passphrase)
bool construct_tx_with_tx_key(const account_keys &sender_account_keys, const std::unordered_map< crypto::public_key, subaddress_index > &subaddresses, std::vector< tx_source_entry > &sources, std::vector< tx_destination_entry > &destinations, const boost::optional< cryptonote::account_public_address > &change_addr, const std::vector< uint8_t > &extra, transaction &tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector< crypto::secret_key > &additional_tx_keys, bool rct, const rct::RCTConfig &rct_config, rct::multisig_out *msout, bool shuffle_outs, const uint32_t account_major_offset, const cryptonote::network_type nettype)
void set_default_decimal_point(unsigned int decimal_point)
blobdata tx_to_blob(const transaction &tx)
bool parse_tx_extra(const std::vector< uint8_t > &tx_extra, std::vector< tx_extra_field > &tx_extra_fields)
std::string blobdata
bool parse_amount(uint64_t &amount, const std::string &str_amount_)
bool parse_and_validate_tx_from_blob(const blobdata &tx_blob, transaction &tx)
void generate_multisig_LR(const crypto::public_key pkey, const crypto::secret_key &k, crypto::public_key &L, crypto::public_key &R)
Definition multisig.cpp:150
bool get_inputs_etn_amount(const transaction &tx, uint64_t &etn)
std::vector< crypto::public_key > generate_multisig_derivations(const account_keys &keys, const std::vector< crypto::public_key > &derivations)
generate_multisig_derivations performs common DH key derivation. Each middle round in M/N scheme is D...
Definition multisig.cpp:87
bool remove_field_from_tx_extra(std::vector< uint8_t > &tx_extra, const std::type_info &type)
void generate_multisig_N_N(const account_keys &keys, const std::vector< crypto::public_key > &spend_keys, std::vector< crypto::secret_key > &multisig_keys, rct::key &spend_skey, rct::key &spend_pkey)
Definition multisig.cpp:58
std::string print_etn(uint64_t amount, unsigned int decimal_point)
crypto::hash get_pruned_transaction_hash(const transaction &t, const crypto::hash &pruned_data_hash)
std::vector< crypto::secret_key > calculate_multisig_keys(const std::vector< crypto::public_key > &derivations)
calculate_multisig_keys. Calculates secret multisig keys from others' participants ones as follows: m...
Definition multisig.cpp:111
boost::optional< subaddress_receive_info > is_out_to_acc_precomp_public(const std::unordered_map< crypto::public_key, subaddress_index > &subaddresses, const cryptonote::account_public_address output_address)
bool generate_genesis_block(block &bl, std::string const &genesis_tx, uint32_t nonce)
std::string get_account_integrated_address_as_str(network_type nettype, account_public_address const &adr, crypto::hash8 const &payment_id)
const config_t & get_config(network_type nettype)
bool generate_key_image_helper_precomp(const account_keys &ack, const crypto::public_key &out_key, const crypto::key_derivation &recv_derivation, size_t real_output_index, const subaddress_index &received_index, keypair &in_ephemeral, crypto::key_image &ki, hw::device &hwdev, const uint32_t account_major_offset)
bool parse_and_validate_tx_base_from_blob(const blobdata &tx_blob, transaction &tx)
bool t_serializable_object_to_blob(const t_object &to, blobdata &b_blob)
crypto::secret_key generate_multisig_view_secret_key(const crypto::secret_key &skey, const std::vector< crypto::secret_key > &skeys)
Definition multisig.cpp:124
uint64_t get_transaction_weight(const transaction &tx, size_t blob_size)
bool add_extra_nonce_to_tx_extra(std::vector< uint8_t > &tx_extra, const blobdata &extra_nonce)
Level
Represents enumeration for severity level used to determine level of logging.
@ Warning
Useful when application has potentially harmful situtaions.
@ Debug
Informational events most useful for developers to debug application.
bool load_file_to_string(const std::string &path_to_file, std::string &target_str, size_t max_size=1000000000)
bool save_string_to_file(const std::string &path_to_file, const std::string &str)
boost::shared_ptr< call_befor_die_base > auto_scope_leave_caller
auto_scope_leave_caller create_scope_leave_handler(t_scope_leave_handler f)
bool invoke_http_json_rpc(const boost::string_ref uri, std::string method_name, const t_request &out_struct, t_response &result_struct, t_transport &transport, std::chrono::milliseconds timeout=std::chrono::seconds(15), const boost::string_ref http_method="GET", const std::string &req_id="0")
@ user_certificates
Verify peer via specific (non-chain) certificate(s) only.
Definition net_ssl.h:56
@ none
Do not verify peer.
Definition net_ssl.h:54
@ user_ca
Verify peer via specific (possibly chain) certificate(s) only.
Definition net_ssl.h:57
bool ssl_support_from_string(ssl_support_t &ssl, boost::string_ref s)
Definition net_ssl.cpp:516
bool invoke_http_bin(const boost::string_ref uri, const t_request &out_struct, t_response &result_struct, t_transport &transport, std::chrono::milliseconds timeout=std::chrono::seconds(15), const boost::string_ref method="GET")
bool load_t_from_binary(t_struct &out, const epee::span< const uint8_t > binary_buff)
bool store_t_to_binary(t_struct &str_in, std::string &binary_buff, size_t indent=0)
bool parse_hexstr_to_binbuff(const epee::span< const char > s, epee::span< char > &res)
std::string cut_off_extension(const std::string &str)
std::string pod_to_hex(const t_pod_type &s)
std::string get_extension(const std::string &str)
bool hex_to_pod(const std::string &hex_str, t_pod_type &s)
PUSH_WARNINGS bool get_xtype_from_string(OUT XType &val, const std::string &str_id)
bool validate_hex(uint64_t length, const std::string &str)
std::string buff_to_hex_nodelimer(const std::string &src)
void register_all()
Definition device.cpp:38
device & get_device(const std::string &device_descriptor)
Definition device.cpp:95
mdb_size_t count(MDB_cursor *cur)
error
Tracks LMDB error codes.
Definition error.h:45
void scalarmultBase(key &aG, const key &a)
Definition rctOps.cpp:350
key curveOrder()
Definition rctOps.h:76
void hash_to_scalar(key &hash, const void *data, const std::size_t l)
Definition rctOps.cpp:536
etn_amount decodeRctSimple(const rctSig &rv, const key &sk, unsigned int i, key &mask, hw::device &hwdev)
Definition rctSigs.cpp:1180
bool isInMainSubgroup(const key &A)
Definition rctOps.cpp:412
std::vector< key > keyV
Definition rctTypes.h:88
etn_amount decodeRct(const rctSig &rv, const key &sk, unsigned int i, key &mask, hw::device &hwdev)
Definition rctSigs.cpp:1150
key zeroCommit(etn_amount amount)
Definition rctOps.cpp:322
void scalarmultKey(key &aP, const key &P, const key &a)
Definition rctOps.cpp:368
etn_amount h2d(const key &test)
Definition rctTypes.cpp:161
@ RCTTypeFull
Definition rctTypes.h:230
@ RCTTypeSimple
Definition rctTypes.h:231
@ RCTTypeBulletproof2
Definition rctTypes.h:233
@ RCTTypeBulletproof
Definition rctTypes.h:232
void addKeys(key &AB, const key &A, const key &B)
Definition rctOps.cpp:420
bool signMultisig(rctSig &rv, const std::vector< unsigned int > &indices, const keyV &k, const multisig_out &msout, const key &secret_key)
Definition rctSigs.cpp:1210
key zero()
Definition rctOps.h:70
void ecdhDecode(ecdhTuple &masked, const key &sharedSec, bool v2)
Definition rctOps.cpp:712
@ RangeProofBorromean
Definition rctTypes.h:235
@ RangeProofPaddedBulletproof
Definition rctTypes.h:235
void skGen(key &sk)
Definition rctOps.cpp:253
key commit(etn_amount amount, const key &mask)
Definition rctOps.cpp:336
key identity()
Definition rctOps.h:73
bool parse_binary(const std::string &blob, T &v)
bool dump_binary(T &v, std::string &blob)
bool serialize(Archive &ar, T &v)
STL namespace.
bool decode(const std::string &enc, std::string &data)
Definition base58.cpp:196
std::string encode(const std::string &data)
Definition base58.cpp:173
void print_source_entry(const cryptonote::tx_source_entry &src)
Definition wallet2.h:2137
void digit_split_strategy(const std::vector< cryptonote::tx_destination_entry > &dsts, const cryptonote::tx_destination_entry &change_dst, uint64_t dust_threshold, std::vector< cryptonote::tx_destination_entry > &splitted_dsts, std::vector< cryptonote::tx_destination_entry > &dust_dsts)
Definition wallet2.h:2098
bool load_txt_records_from_dns(std::vector< std::string > &good_records, const std::vector< std::string > &dns_urls, std::string type)
file_error_base< file_save_error_message_index > file_save_error
file_error_base< file_not_found_message_index > file_not_found
file_error_base< file_exists_message_index > file_exists
failed_rpc_request< refresh_error, get_blocks_error_message_index > get_blocks_error
file_error_base< file_read_error_message_index > file_read_error
Various Tools.
Definition tools.cpp:31
const size_t MAX_SPLIT_ATTEMPTS
Definition wallet2.cpp:991
std::error_code replace_file(const std::string &old_name, const std::string &new_name)
std::rename wrapper for nix and something strange for windows.
Definition util.cpp:648
bool is_local_address(const std::string &address)
Definition util.cpp:874
uint64_t combinations_count(uint32_t k, uint32_t n)
std::string get_default_data_dir()
Returns the default data directory.
Definition util.cpp:600
boost::filesystem::path data_dir
Definition main.cpp:50
#define SSL_FINGERPRINT_SIZE
Definition net_ssl.h:40
#define PERF_TIMER_START(name)
Definition perf_timer.h:85
#define PERF_TIMER(name)
Definition perf_timer.h:82
#define PERF_TIMER_STOP(name)
Definition perf_timer.h:86
const GenericPointer< typename T::ValueType > T2 value
Definition pointer.h:1225
const CharType(& source)[N]
Definition pointer.h:1147
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
Definition pointer.h:1124
#define TIME_MEASURE_FINISH(var_name)
#define TIME_MEASURE_START(var_name)
#define UNSIGNED_TX_PREFIX
#define SIGNED_TX_PREFIX
#define OUTPUT_EXPORT_FILE_MAGIC
const char * buf
#define true
#define false
CXA_THROW_INFO_T * info
unsigned short uint16_t
Definition stdint.h:125
signed __int64 int64_t
Definition stdint.h:135
unsigned int uint32_t
Definition stdint.h:126
unsigned char uint8_t
Definition stdint.h:124
unsigned __int64 uint64_t
Definition stdint.h:136
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
crypto::secret_key m_view_secret_key
Definition account.h:45
crypto::secret_key m_spend_secret_key
Definition account.h:44
std::vector< crypto::secret_key > m_multisig_keys
Definition account.h:46
account_public_address m_account_address
Definition account.h:43
std::vector< blobdata > txs
std::vector< crypto::hash > tx_hashes
uint16_t const RPC_DEFAULT_PORT
crypto::secret_key sec
crypto::public_key pub
bool is_subaddress
uint64_t amount
account_public_address addr
std::vector< crypto::public_key > data
Definition tx_extra.h:170
crypto::public_key pub_key
Definition tx_extra.h:102
txout_target_v target
std::vector< crypto::public_key > real_out_additional_tx_keys
crypto::public_key real_out_tx_key
uint64_t amount
rct::multisig_kLRki multisig_kLRki
bool rct
size_t real_output
crypto::hash tx_hash
subaddress_index subaddr_index
std::vector< output_entry > outputs
std::pair< uint64_t, rct::ctkey > output_entry
size_t real_output_in_tx_index
crypto::key_image k_image
std::vector< uint64_t > key_offsets
static std::vector< uint8_t > vector(boost::string_ref src)
Definition hex.cpp:88
static epee::wipeable_string wipeable_string(const span< const std::uint8_t > src)
Definition hex.cpp:69
boost::function< crypto::public_key(const tools::wallet2::transfer_details &td)> get_tx_pub_key_from_received_outs
Primarily for use with epee::net_utils::http_client.
unsigned char bytes[32]
Definition rctTypes.h:86
std::vector< ecdhTuple > ecdhInfo
Definition rctTypes.h:246
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
static boost::optional< login > parse(std::string &&userpass, bool verify, const std::function< boost::optional< password_container >(bool)> &prompt)
Definition password.cpp:268
cryptonote::account_public_address addr_for_dust
Definition wallet2.h:166
uint64_t dust_threshold
Definition wallet2.h:164
std::vector< boost::optional< cryptonote::subaddress_receive_info > > received
Definition wallet2.h:577
std::unordered_set< crypto::public_key > m_signers
Definition wallet2.h:513
std::vector< pending_tx > m_ptx
Definition wallet2.h:512
cryptonote::subaddress_index m_subaddr_index
Definition wallet2.h:362
tx_construction_data construction_data
Definition wallet2.h:477
std::vector< size_t > selected_transfers
Definition wallet2.h:470
crypto::secret_key tx_key
Definition wallet2.h:472
std::vector< cryptonote::tx_destination_entry > dests
Definition wallet2.h:474
std::vector< multisig_sig > multisig_sigs
Definition wallet2.h:475
cryptonote::transaction tx
Definition wallet2.h:466
std::vector< crypto::secret_key > additional_tx_keys
Definition wallet2.h:473
cryptonote::tx_destination_entry change_dts
Definition wallet2.h:469
Definition wallet2.h:553
crypto::public_key shared_secret
Definition wallet2.h:556
crypto::signature key_image_sig
Definition wallet2.h:559
crypto::signature shared_secret_sig
Definition wallet2.h:558
uint64_t index_in_tx
Definition wallet2.h:555
crypto::key_image key_image
Definition wallet2.h:557
crypto::hash txid
Definition wallet2.h:554
std::unordered_map< crypto::public_key, crypto::key_image > tx_key_images
Definition wallet2.h:507
std::vector< crypto::key_image > key_images
Definition wallet2.h:506
std::vector< pending_tx > ptx
Definition wallet2.h:505
const crypto::public_key & get_public_key() const
Definition wallet2.h:326
crypto::key_image m_key_image
Definition wallet2.h:311
cryptonote::subaddress_index m_subaddr_index
Definition wallet2.h:318
cryptonote::transaction_prefix m_tx
Definition wallet2.h:304
std::vector< rct::key > m_multisig_k
Definition wallet2.h:320
std::vector< cryptonote::tx_extra_field > tx_extra_fields
Definition wallet2.h:593
std::vector< is_out_data > primary
Definition wallet2.h:589
std::vector< is_out_data > public_outs
Definition wallet2.h:596
std::vector< is_out_data > additional
Definition wallet2.h:590
std::vector< uint8_t > extra
Definition wallet2.h:426
std::vector< cryptonote::tx_source_entry > sources
Definition wallet2.h:422
std::vector< cryptonote::tx_destination_entry > splitted_dsts
Definition wallet2.h:424
std::vector< cryptonote::tx_destination_entry > dests
Definition wallet2.h:430
std::vector< size_t > selected_transfers
Definition wallet2.h:425
std::set< uint32_t > subaddr_indices
Definition wallet2.h:432
cryptonote::tx_destination_entry change_dts
Definition wallet2.h:423
boost::optional< cryptonote::subaddress_receive_info > received
Definition wallet2.h:296
enum tools::wallet2::unconfirmed_transfer_details::@320170243027143365014242201170062371014272002365 m_state
std::vector< cryptonote::tx_destination_entry > m_dests
Definition wallet2.h:385
std::vector< tx_construction_data > txes
Definition wallet2.h:499
std::pair< size_t, wallet2::transfer_container > transfers
Definition wallet2.h:500
#define CRITICAL_REGION_LOCAL(x)
Definition syncobj.h:228
struct hash_func hashes[]
const char * address
Definition multisig.cpp:37
const char * spendkey
Definition multisig.cpp:38
string daemon_address
Definition transfers.cpp:42
bool get_short_payment_id(crypto::hash8 &payment_id8, const tools::wallet2::pending_tx &ptx, hw::device &hwdev)
#define TX_WEIGHT_TARGET(bytes)
Definition wallet2.cpp:100
#define GAMMA_SCALE
Definition wallet2.cpp:135
#define KEY_IMAGE_EXPORT_FILE_MAGIC
Definition wallet2.cpp:123
#define TESTNET_SEGREGATION_FORK_HEIGHT
Definition wallet2.cpp:128
#define MULTISIG_EXPORT_FILE_MAGIC
Definition wallet2.cpp:125
#define FEE_ESTIMATE_GRACE_BLOCKS
Definition wallet2.cpp:116
#define STAGENET_SEGREGATION_FORK_HEIGHT
Definition wallet2.cpp:129
#define SEGREGATION_FORK_VICINITY
Definition wallet2.cpp:130
#define FIRST_REFRESH_GRANULARITY
Definition wallet2.cpp:132
#define DEFAULT_MIN_OUTPUT_VALUE
Definition wallet2.cpp:138
#define SEGREGATION_FORK_HEIGHT
Definition wallet2.cpp:127
#define RECENT_OUTPUT_BLOCKS
Definition wallet2.cpp:114
#define MULTISIG_UNSIGNED_TX_PREFIX
Definition wallet2.cpp:109
#define SUBADDRESS_LOOKAHEAD_MAJOR
Definition wallet2.cpp:120
#define APPROXIMATE_INPUT_BYTES
Definition wallet2.cpp:97
#define SECOND_OUTPUT_RELATEDNESS_THRESHOLD
Definition wallet2.cpp:118
#define SUBADDRESS_LOOKAHEAD_MINOR
Definition wallet2.cpp:121
#define DEFAULT_MIN_OUTPUT_COUNT
Definition wallet2.cpp:137
#define RECENT_OUTPUT_RATIO
Definition wallet2.cpp:111
#define CACHE_KEY_TAIL
Definition wallet2.cpp:104
#define GAMMA_SHAPE
Definition wallet2.cpp:134
#define RECENT_OUTPUT_ZONE
Definition wallet2.cpp:113
#define CMPF(f)
#define THROW_WALLET_EXCEPTION_IF(cond, err_type,...)
#define THROW_WALLET_EXCEPTION(err_type,...)
#define T(x)