Electroneum
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"
46 using namespace epee;
47 
48 #include "cryptonote_config.h"
49 #include "wallet2.h"
52 #include "misc_language.h"
54 #include "multisig/multisig.h"
56 #include "common/command_line.h"
57 #include "common/threadpool.h"
58 #include "profile_tools.h"
59 #include "crypto/crypto.h"
61 #include "serialization/string.h"
64 #include "common/i18n.h"
65 #include "common/util.h"
67 #include "rapidjson/document.h"
68 #include "rapidjson/writer.h"
69 #include "rapidjson/stringbuffer.h"
70 #include "common/json_util.h"
71 #include "memwipe.h"
72 #include "common/base58.h"
73 #include "common/combinator.h"
74 #include "common/dns_utils.h"
75 #include "common/notify.h"
76 #include "common/perf_timer.h"
77 #include "ringct/rctSigs.h"
78 #include "ringdb.h"
79 #include "device/device_cold.hpp"
81 #include "net/socks_connect.h"
82 
83 extern "C"
84 {
85 #include "crypto/keccak.h"
86 #include "crypto/crypto-ops.h"
87 }
88 using namespace std;
89 using namespace crypto;
90 using namespace cryptonote;
91 
92 #undef ELECTRONEUM_DEFAULT_LOG_CATEGORY
93 #define ELECTRONEUM_DEFAULT_LOG_CATEGORY "wallet.wallet2"
94 
95 // used to choose when to stop adding outputs to a tx
96 #define APPROXIMATE_INPUT_BYTES 80
97 
98 // used to target a given block weight (additional outputs may be added on top to build fee)
99 #define TX_WEIGHT_TARGET(bytes) (bytes*2/3)
100 
101 // arbitrary, used to generate different hashes from the same input
102 #define CHACHA8_KEY_TAIL 0x8c
103 #define CACHE_KEY_TAIL 0x8d
104 
105 // Magic number 004 means these payloads are encrypted.
106 #define UNSIGNED_TX_PREFIX "Electroneum unsigned tx set\004"
107 #define SIGNED_TX_PREFIX "Electroneum signed tx set\004"
108 #define MULTISIG_UNSIGNED_TX_PREFIX "Electroneum multisig unsigned tx set\001"
109 
110 #define RECENT_OUTPUT_RATIO (0.5) // 50% of outputs are from the recent zone
111 #define RECENT_OUTPUT_DAYS (1.8) // last 1.8 day makes up the recent zone (taken from electroneumlink.pdf, Miller et al)
112 #define RECENT_OUTPUT_ZONE ((time_t)(RECENT_OUTPUT_DAYS * 86400))
113 #define RECENT_OUTPUT_BLOCKS (RECENT_OUTPUT_DAYS * 720)
114 
115 #define FEE_ESTIMATE_GRACE_BLOCKS 10 // estimate fee valid for that many blocks
116 
117 #define SECOND_OUTPUT_RELATEDNESS_THRESHOLD 0.0f
118 
119 #define SUBADDRESS_LOOKAHEAD_MAJOR 50
120 #define SUBADDRESS_LOOKAHEAD_MINOR 200
121 
122 #define KEY_IMAGE_EXPORT_FILE_MAGIC "Electroneum key image export\002"
123 
124 #define MULTISIG_EXPORT_FILE_MAGIC "Electroneum multisig export\001"
125 
126 #define SEGREGATION_FORK_HEIGHT 99999999
127 #define TESTNET_SEGREGATION_FORK_HEIGHT 99999999
128 #define STAGENET_SEGREGATION_FORK_HEIGHT 99999999
129 #define SEGREGATION_FORK_VICINITY 1500 /* blocks */
130 
131 #define FIRST_REFRESH_GRANULARITY 1024
132 
133 #define GAMMA_SHAPE 19.28
134 #define GAMMA_SCALE (1/1.61)
135 
136 #define DEFAULT_MIN_OUTPUT_COUNT 5
137 #define DEFAULT_MIN_OUTPUT_VALUE (2*COIN)
138 
139 #define OUTPUT_EXPORT_FILE_MAGIC "Electroneum output export\003"
140 
141 static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1";
142 static const std::string MULTISIG_EXTRA_INFO_MAGIC = "MultisigxV1";
143 
144 namespace
145 {
146  std::string get_default_ringdb_path()
147  {
148  boost::filesystem::path dir = tools::get_default_data_dir();
149  // remove .electroneum, replace with .shared-ringdb
150  dir = dir.remove_filename();
151  dir /= ".shared-ringdb";
152  return dir.string();
153  }
154 
155  std::string pack_multisignature_keys(const std::string& prefix, const std::vector<crypto::public_key>& keys, const crypto::secret_key& signer_secret_key)
156  {
157  std::string data;
158  crypto::public_key signer;
159  CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(signer_secret_key, signer), "Failed to derive public spend key");
160  data += std::string((const char *)&signer, sizeof(crypto::public_key));
161 
162  for (const auto &key: keys)
163  {
164  data += std::string((const char *)&key, sizeof(crypto::public_key));
165  }
166 
167  data.resize(data.size() + sizeof(crypto::signature));
168 
170  crypto::cn_fast_hash(data.data(), data.size() - sizeof(crypto::signature), hash);
171  crypto::signature &signature = *(crypto::signature*)&data[data.size() - sizeof(crypto::signature)];
172  crypto::generate_signature(hash, signer, signer_secret_key, signature);
173 
174  return MULTISIG_EXTRA_INFO_MAGIC + tools::base58::encode(data);
175  }
176 
177  std::vector<crypto::public_key> secret_keys_to_public_keys(const std::vector<crypto::secret_key>& keys)
178  {
179  std::vector<crypto::public_key> public_keys;
180  public_keys.reserve(keys.size());
181 
182  std::transform(keys.begin(), keys.end(), std::back_inserter(public_keys), [] (const crypto::secret_key& k) -> crypto::public_key {
183  crypto::public_key p;
184  CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(k, p), "Failed to derive public spend key");
185  return p;
186  });
187 
188  return public_keys;
189  }
190 
191  bool keys_intersect(const std::unordered_set<crypto::public_key>& s1, const std::unordered_set<crypto::public_key>& s2)
192  {
193  if (s1.empty() || s2.empty())
194  return false;
195 
196  for (const auto& e: s1)
197  {
198  if (s2.find(e) != s2.end())
199  return true;
200  }
201 
202  return false;
203  }
204 
205  void add_reason(std::string &reasons, const char *reason)
206  {
207  if (!reasons.empty())
208  reasons += ", ";
209  reasons += reason;
210  }
211 
213  {
214  std::string reason;
215  if (res.low_mixin)
216  add_reason(reason, "bad ring size");
217  if (res.double_spend)
218  add_reason(reason, "double spend");
219  if (res.invalid_input)
220  add_reason(reason, "invalid input");
221  if (res.invalid_output)
222  add_reason(reason, "invalid output");
223  if (res.too_big)
224  add_reason(reason, "too big");
225  if (res.overspend)
226  add_reason(reason, "overspend");
227  if (res.fee_too_low)
228  add_reason(reason, "fee too low");
229  if (res.not_rct)
230  add_reason(reason, "tx is not ringct");
231  if (res.sanity_check_failed)
232  add_reason(reason, "tx sanity check failed");
233  if (res.not_relayed)
234  add_reason(reason, "tx was not relayed");
235  return reason;
236  }
237 }
238 
239 namespace
240 {
241 // Create on-demand to prevent static initialization order fiasco issues.
242 struct options {
243  const command_line::arg_descriptor<std::string> daemon_address = {"daemon-address", tools::wallet2::tr("Use daemon instance at <host>:<port>"), ""};
244  const command_line::arg_descriptor<std::string> daemon_host = {"daemon-host", tools::wallet2::tr("Use daemon instance at host <arg> instead of localhost"), ""};
245  const command_line::arg_descriptor<std::string> proxy = {"proxy", tools::wallet2::tr("[<ip>:]<port> socks proxy to use for daemon connections"), {}, true};
246  const command_line::arg_descriptor<bool> trusted_daemon = {"trusted-daemon", tools::wallet2::tr("Enable commands which rely on a trusted daemon"), false};
247  const command_line::arg_descriptor<bool> untrusted_daemon = {"untrusted-daemon", tools::wallet2::tr("Disable commands which rely on a trusted daemon"), false};
248  const command_line::arg_descriptor<std::string> password = {"password", tools::wallet2::tr("Wallet password (For a wallet without a password use \"\")"), "", true};
249  const command_line::arg_descriptor<std::string> password_file = {"password-file", tools::wallet2::tr("Wallet password file"), "", true};
250  const command_line::arg_descriptor<int> daemon_port = {"daemon-port", tools::wallet2::tr("Use daemon instance at port <arg> instead of 26968"), 0};
251  const command_line::arg_descriptor<std::string> daemon_login = {"daemon-login", tools::wallet2::tr("Specify username[:password] for daemon RPC client"), "", true};
252  const command_line::arg_descriptor<std::string> daemon_ssl = {"daemon-ssl", tools::wallet2::tr("Enable SSL on daemon RPC connections: enabled|disabled|autodetect"), "autodetect"};
253  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"), ""};
254  const command_line::arg_descriptor<std::string> daemon_ssl_certificate = {"daemon-ssl-certificate", tools::wallet2::tr("Path to a PEM format certificate"), ""};
255  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).")};
256  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")};
257  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};
258  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};
259  const command_line::arg_descriptor<bool> testnet = {"testnet", tools::wallet2::tr("For testnet. Daemon must also be launched with --testnet flag"), false};
260  const command_line::arg_descriptor<bool> stagenet = {"stagenet", tools::wallet2::tr("For stagenet. Daemon must also be launched with --stagenet flag"), false};
261  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};
262  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  "shared-ringdb-dir", tools::wallet2::tr("Set shared ring database path"),
265  get_default_ringdb_path(),
266  {{ &testnet, &stagenet }},
267  [](std::array<bool, 2> testnet_stagenet, bool defaulted, std::string val)->std::string {
268  if (testnet_stagenet[0])
269  return (boost::filesystem::path(val) / "testnet").string();
270  else if (testnet_stagenet[1])
271  return (boost::filesystem::path(val) / "stagenet").string();
272  return val;
273  }
274  };
275  const command_line::arg_descriptor<uint64_t> kdf_rounds = {"kdf-rounds", tools::wallet2::tr("Number of rounds for the key derivation function"), 1};
276  const command_line::arg_descriptor<std::string> hw_device = {"hw-device", tools::wallet2::tr("HW device to use"), ""};
277  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)"), ""};
278  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" , "" };
279  const command_line::arg_descriptor<bool> no_dns = {"no-dns", tools::wallet2::tr("Do not use DNS"), false};
280  const command_line::arg_descriptor<bool> offline = {"offline", tools::wallet2::tr("Do not connect to a daemon, nor use DNS"), false};
281 
282  const command_line::arg_descriptor<bool> restricted = {"restricted-rpc", tools::wallet2::tr("Restricts to view-only commands"), false};
283  const command_line::arg_descriptor<std::string> data_dir = {"data-dir", tools::wallet2::tr("Path to blockchain db"), ""};
284 };
285 
286 void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file, std::string &mms_file)
287 {
288  keys_file = file_path;
289  wallet_file = file_path;
290  boost::system::error_code e;
291  if(string_tools::get_extension(keys_file) == "keys")
292  {//provided keys file name
293  wallet_file = string_tools::cut_off_extension(wallet_file);
294  }else
295  {//provided wallet file name
296  keys_file += ".keys";
297  }
298  mms_file = file_path + ".mms";
299 }
300 
301 uint64_t calculate_fee(uint64_t fee_per_kb, size_t bytes, uint64_t fee_multiplier)
302 {
303  uint64_t kB = (bytes + 1023) / 1024;
304  return kB * fee_per_kb * fee_multiplier;
305 }
306 
307 uint64_t calculate_fee_from_weight(uint64_t base_fee, uint64_t weight, uint64_t fee_multiplier, uint64_t fee_quantization_mask)
308 {
309  uint64_t fee = weight * base_fee * fee_multiplier;
310  fee = (fee + fee_quantization_mask - 1) / fee_quantization_mask * fee_quantization_mask;
311  return fee;
312 }
313 
314 std::string get_weight_string(size_t weight)
315 {
316  return std::to_string(weight) + " weight";
317 }
318 
319 std::string get_weight_string(const cryptonote::transaction &tx, size_t blob_size)
320 {
321  return get_weight_string(get_transaction_weight(tx, blob_size));
322 }
323 
324 std::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)
325 {
326  namespace ip = boost::asio::ip;
327 
328  const bool testnet = command_line::get_arg(vm, opts.testnet);
329  const bool stagenet = command_line::get_arg(vm, opts.stagenet);
330  const network_type nettype = testnet ? TESTNET : stagenet ? STAGENET : MAINNET;
331  const uint64_t kdf_rounds = command_line::get_arg(vm, opts.kdf_rounds);
332  THROW_WALLET_EXCEPTION_IF(kdf_rounds == 0, tools::error::wallet_internal_error, "KDF rounds must not be 0");
333 
334  const bool use_proxy = command_line::has_arg(vm, opts.proxy);
335  auto daemon_address = command_line::get_arg(vm, opts.daemon_address);
336  auto daemon_host = command_line::get_arg(vm, opts.daemon_host);
337  auto daemon_port = command_line::get_arg(vm, opts.daemon_port);
338  auto device_name = command_line::get_arg(vm, opts.hw_device);
339  auto device_derivation_path = command_line::get_arg(vm, opts.hw_device_derivation_path);
340  auto daemon_ssl_private_key = command_line::get_arg(vm, opts.daemon_ssl_private_key);
341  auto daemon_ssl_certificate = command_line::get_arg(vm, opts.daemon_ssl_certificate);
342  auto daemon_ssl_ca_file = command_line::get_arg(vm, opts.daemon_ssl_ca_certificates);
343  auto daemon_ssl_allowed_fingerprints = command_line::get_arg(vm, opts.daemon_ssl_allowed_fingerprints);
344  auto daemon_ssl_allow_any_cert = command_line::get_arg(vm, opts.daemon_ssl_allow_any_cert);
345  auto daemon_ssl = command_line::get_arg(vm, opts.daemon_ssl);
346  auto fallback_to_pow_checkpoint_height = command_line::get_arg(vm, opts.fallback_to_pow_checkpoint_height);
347  auto fallback_to_pow_checkpoint_hash = command_line::get_arg(vm, opts.fallback_to_pow_checkpoint_hash);
348 
349  // user specified CA file or fingeprints implies enabled SSL by default
351  if (command_line::get_arg(vm, opts.daemon_ssl_allow_any_cert))
353  else if (!daemon_ssl_ca_file.empty() || !daemon_ssl_allowed_fingerprints.empty())
354  {
355  std::vector<std::vector<uint8_t>> ssl_allowed_fingerprints{ daemon_ssl_allowed_fingerprints.size() };
356  std::transform(daemon_ssl_allowed_fingerprints.begin(), daemon_ssl_allowed_fingerprints.end(), ssl_allowed_fingerprints.begin(), epee::from_hex::vector);
357  for (const auto &fpr: ssl_allowed_fingerprints)
358  {
360  "SHA-256 fingerprint should be " BOOST_PP_STRINGIZE(SSL_FINGERPRINT_SIZE) " bytes long.");
361  }
362 
363  ssl_options = epee::net_utils::ssl_options_t{
364  std::move(ssl_allowed_fingerprints), std::move(daemon_ssl_ca_file)
365  };
366 
367  if (command_line::get_arg(vm, opts.daemon_ssl_allow_chained))
369  }
370 
371  auto data_dir = command_line::get_arg(vm, opts.data_dir);
372 
374  {
376  tools::wallet2::tr("Invalid argument for ") + std::string(opts.daemon_ssl.name));
377  }
378 
380  std::move(daemon_ssl_private_key), std::move(daemon_ssl_certificate)
381  };
382 
383  THROW_WALLET_EXCEPTION_IF(!daemon_address.empty() && !daemon_host.empty() && 0 != daemon_port,
384  tools::error::wallet_internal_error, tools::wallet2::tr("can't specify daemon host or port more than once"));
385 
386  boost::optional<epee::net_utils::http::login> login{};
387  if (command_line::has_arg(vm, opts.daemon_login))
388  {
389  auto parsed = tools::login::parse(
390  command_line::get_arg(vm, opts.daemon_login), false, [password_prompter](bool verify) {
391  return password_prompter("Daemon client password", verify);
392  }
393  );
394  if (!parsed)
395  return nullptr;
396 
397  login.emplace(std::move(parsed->username), std::move(parsed->password).password());
398  }
399 
400  if (daemon_host.empty())
401  daemon_host = "localhost";
402 
403  if (!daemon_port)
404  {
405  daemon_port = get_config(nettype).RPC_DEFAULT_PORT;
406  }
407 
408  if (daemon_address.empty())
409  daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port);
410 
411  {
412  const boost::string_ref real_daemon = boost::string_ref{daemon_address}.substr(0, daemon_address.rfind(':'));
413 
414  /* If SSL or proxy is enabled, then a specific cert, CA or fingerprint must
415  be specified. This is specific to the wallet. */
416  const bool verification_required =
419 
421  verification_required && !ssl_options.has_strong_verification(real_daemon),
423  tools::wallet2::tr("Enabling --") + std::string{use_proxy ? opts.proxy.name : opts.daemon_ssl.name} + tools::wallet2::tr(" requires --") +
424  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")
425  );
426  }
427 
428  boost::asio::ip::tcp::endpoint proxy{};
429  if (use_proxy)
430  {
431  namespace ip = boost::asio::ip;
432 
433  const auto proxy_address = command_line::get_arg(vm, opts.proxy);
434 
435  boost::string_ref proxy_port{proxy_address};
436  boost::string_ref proxy_host = proxy_port.substr(0, proxy_port.rfind(":"));
437  if (proxy_port.size() == proxy_host.size())
438  proxy_host = "127.0.0.1";
439  else
440  proxy_port = proxy_port.substr(proxy_host.size() + 1);
441 
442  uint16_t port_value = 0;
444  !epee::string_tools::get_xtype_from_string(port_value, std::string{proxy_port}),
446  std::string{"Invalid port specified for --"} + opts.proxy.name
447  );
448 
449  boost::system::error_code error{};
450  proxy = ip::tcp::endpoint{ip::address::from_string(std::string{proxy_host}, error), port_value};
451  THROW_WALLET_EXCEPTION_IF(bool(error), tools::error::wallet_internal_error, std::string{"Invalid IP address specified for --"} + opts.proxy.name);
452  }
453 
454  boost::optional<bool> trusted_daemon;
455  if (!command_line::is_arg_defaulted(vm, opts.trusted_daemon) || !command_line::is_arg_defaulted(vm, opts.untrusted_daemon))
456  trusted_daemon = command_line::get_arg(vm, opts.trusted_daemon) && !command_line::get_arg(vm, opts.untrusted_daemon);
457  THROW_WALLET_EXCEPTION_IF(!command_line::is_arg_defaulted(vm, opts.trusted_daemon) && !command_line::is_arg_defaulted(vm, opts.untrusted_daemon),
458  tools::error::wallet_internal_error, tools::wallet2::tr("--trusted-daemon and --untrusted-daemon are both seen, assuming untrusted"));
459 
460  // set --trusted-daemon if local and not overridden
461  if (!trusted_daemon)
462  {
463  try
464  {
465  trusted_daemon = false;
467  {
468  MINFO(tools::wallet2::tr("Daemon is local, assuming trusted"));
469  trusted_daemon = true;
470  }
471  }
472  catch (const std::exception &e) { }
473  }
474 
475  std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(nettype, kdf_rounds, unattended));
476  wallet->init(std::move(daemon_address), std::move(login), std::move(proxy), 0, *trusted_daemon, std::move(ssl_options), std::move(data_dir));
477  // Add the pow-fallback checkpoint if necessary
478  if(fallback_to_pow_checkpoint_hash != "" && fallback_to_pow_checkpoint_height != 0) {
479  wallet->add_checkpoint(fallback_to_pow_checkpoint_height, fallback_to_pow_checkpoint_hash);
480  }
481  boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir);
482  wallet->set_ring_database(ringdb_path.string());
483  wallet->get_message_store().set_options(vm);
484  wallet->device_name(device_name);
485  wallet->device_derivation_path(device_derivation_path);
486 
487  if (command_line::get_arg(vm, opts.no_dns))
488  wallet->enable_dns(false);
489 
490  if (command_line::get_arg(vm, opts.offline))
491  wallet->set_offline();
492 
493  try
494  {
495  if (!command_line::is_arg_defaulted(vm, opts.tx_notify))
496  wallet->set_tx_notify(std::shared_ptr<tools::Notify>(new tools::Notify(command_line::get_arg(vm, opts.tx_notify).c_str())));
497  }
498  catch (const std::exception &e)
499  {
500  MERROR("Failed to parse tx notify spec: " << e.what());
501  }
502 
503  return wallet;
504 }
505 
506 boost::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)
507 {
508  if (command_line::has_arg(vm, opts.password) && command_line::has_arg(vm, opts.password_file))
509  {
510  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("can't specify more than one of --password and --password-file"));
511  }
512 
513  if (command_line::has_arg(vm, opts.password))
514  {
515  return tools::password_container{command_line::get_arg(vm, opts.password)};
516  }
517 
518  if (command_line::has_arg(vm, opts.password_file))
519  {
520  std::string password;
521  bool r = epee::file_io_utils::load_file_to_string(command_line::get_arg(vm, opts.password_file),
522  password);
523  THROW_WALLET_EXCEPTION_IF(!r, tools::error::wallet_internal_error, tools::wallet2::tr("the password file specified could not be read"));
524 
525  // Remove line breaks the user might have inserted
526  boost::trim_right_if(password, boost::is_any_of("\r\n"));
527  return {tools::password_container{std::move(password)}};
528  }
529 
530  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"));
531 
532  return password_prompter(verify ? tools::wallet2::tr("Enter a new password for the wallet") : tools::wallet2::tr("Wallet password"), verify);
533 }
534 
535 std::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)
536 {
537  const bool testnet = command_line::get_arg(vm, opts.testnet);
538  const bool stagenet = command_line::get_arg(vm, opts.stagenet);
539  const network_type nettype = testnet ? TESTNET : stagenet ? STAGENET : MAINNET;
540 
541  /* GET_FIELD_FROM_JSON_RETURN_ON_ERROR Is a generic macro that can return
542  false. Gcc will coerce this into unique_ptr(nullptr), but clang correctly
543  fails. This large wrapper is for the use of that macro */
544  std::unique_ptr<tools::wallet2> wallet;
545  epee::wipeable_string password;
546  const auto do_generate = [&]() -> bool {
550  return false;
551  }
552 
554  if (json.Parse(buf.c_str()).HasParseError()) {
556  return false;
557  }
558 
559  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, version, unsigned, Uint, true, 0);
560  const int current_version = 1;
561  THROW_WALLET_EXCEPTION_IF(field_version > current_version, tools::error::wallet_internal_error,
562  ((boost::format(tools::wallet2::tr("Version %u too new, we can only grok up to %u")) % field_version % current_version)).str());
563 
565 
566  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, scan_from_height, uint64_t, Uint64, false, 0);
567  const bool recover = true;
568 
569  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, password, std::string, String, false, std::string());
570 
572  crypto::secret_key viewkey;
573  if (field_viewkey_found)
574  {
575  cryptonote::blobdata viewkey_data;
576  if(!epee::string_tools::parse_hexstr_to_binbuff(field_viewkey, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key))
577  {
578  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to parse view key secret key"));
579  }
580  viewkey = *reinterpret_cast<const crypto::secret_key*>(viewkey_data.data());
581  crypto::public_key pkey;
582  if (!crypto::secret_key_to_public_key(viewkey, pkey)) {
583  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify view key secret key"));
584  }
585  }
586 
589  if (field_spendkey_found)
590  {
591  cryptonote::blobdata spendkey_data;
592  if(!epee::string_tools::parse_hexstr_to_binbuff(field_spendkey, spendkey_data) || spendkey_data.size() != sizeof(crypto::secret_key))
593  {
594  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to parse spend key secret key"));
595  }
596  spendkey = *reinterpret_cast<const crypto::secret_key*>(spendkey_data.data());
597  crypto::public_key pkey;
599  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify spend key secret key"));
600  }
601  }
602 
604  std::string old_language;
605  crypto::secret_key recovery_key;
606  bool restore_deterministic_wallet = false;
607  if (field_seed_found)
608  {
609  if (!crypto::ElectrumWords::words_to_bytes(field_seed, recovery_key, old_language))
610  {
611  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("Electrum-style word list failed verification"));
612  }
613  restore_deterministic_wallet = true;
614 
615  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, seed_passphrase, std::string, String, false, std::string());
616  if (field_seed_passphrase_found)
617  {
618  if (!field_seed_passphrase.empty())
619  recovery_key = cryptonote::decrypt_key(recovery_key, field_seed_passphrase);
620  }
621  }
622 
624 
625  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, create_address_file, int, Int, false, false);
626  bool create_address_file = field_create_address_file;
627 
628  // compatibility checks
629  if (!field_seed_found && !field_viewkey_found && !field_spendkey_found)
630  {
631  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"));
632  }
633  if (field_seed_found && (field_viewkey_found || field_spendkey_found))
634  {
635  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("Both Electrum-style word list and private key(s) specified"));
636  }
637 
638  // if an address was given, we check keys against it, and deduce the spend
639  // public key if it was not given
640  if (field_address_found)
641  {
643  if(!get_account_address_from_str(info, nettype, field_address))
644  {
646  }
647  if (field_viewkey_found)
648  {
649  crypto::public_key pkey;
650  if (!crypto::secret_key_to_public_key(viewkey, pkey)) {
651  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify view key secret key"));
652  }
653  if (info.address.m_view_public_key != pkey) {
654  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("view key does not match standard address"));
655  }
656  }
657  if (field_spendkey_found)
658  {
659  crypto::public_key pkey;
661  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify spend key secret key"));
662  }
663  if (info.address.m_spend_public_key != pkey) {
664  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("spend key does not match standard address"));
665  }
666  }
667  }
668 
669  const bool deprecated_wallet = restore_deterministic_wallet && ((old_language == crypto::ElectrumWords::old_language_name) ||
672  tools::wallet2::tr("Cannot generate deprecated wallets from JSON"));
673 
674  wallet.reset(make_basic(vm, unattended, opts, password_prompter).release());
675  wallet->set_refresh_from_block_height(field_scan_from_height);
676  wallet->explicit_refresh_from_block_height(field_scan_from_height_found);
677  if (!old_language.empty())
678  wallet->set_seed_language(old_language);
679 
680  try
681  {
682  if (!field_seed.empty())
683  {
684  wallet->generate(field_filename, field_password, recovery_key, recover, false, create_address_file);
685  password = field_password;
686  }
687  else if (field_viewkey.empty() && !field_spendkey.empty())
688  {
689  wallet->generate(field_filename, field_password, spendkey, recover, false, create_address_file);
690  password = field_password;
691  }
692  else
693  {
695  if (!crypto::secret_key_to_public_key(viewkey, address.m_view_public_key)) {
696  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify view key secret key"));
697  }
698 
699  if (field_spendkey.empty())
700  {
701  // if we have an address but no spend key, we can deduce the spend public key
702  // from the address
703  if (field_address_found)
704  {
706  if(!get_account_address_from_str(info, nettype, field_address))
707  {
708  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, std::string(tools::wallet2::tr("failed to parse address: ")) + field_address);
709  }
710  address.m_spend_public_key = info.address.m_spend_public_key;
711  }
712  else
713  {
714  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("Address must be specified in order to create watch-only wallet"));
715  }
716  wallet->generate(field_filename, field_password, address, viewkey, create_address_file);
717  password = field_password;
718  }
719  else
720  {
721  if (!crypto::secret_key_to_public_key(spendkey, address.m_spend_public_key)) {
722  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify spend key secret key"));
723  }
724  wallet->generate(field_filename, field_password, address, spendkey, viewkey, create_address_file);
725  password = field_password;
726  }
727  }
728  }
729  catch (const std::exception& e)
730  {
731  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, std::string(tools::wallet2::tr("failed to generate new wallet: ")) + e.what());
732  }
733  return true;
734  };
735 
736  if (do_generate())
737  {
738  return {std::move(wallet), tools::password_container(password)};
739  }
740  return {nullptr, tools::password_container{}};
741 }
742 
743 std::string strjoin(const std::vector<size_t> &V, const char *sep)
744 {
745  std::stringstream ss;
746  bool first = true;
747  for (const auto &v: V)
748  {
749  if (!first)
750  ss << sep;
751  ss << std::to_string(v);
752  first = false;
753  }
754  return ss.str();
755 }
756 
757 static bool emplace_or_replace(std::unordered_multimap<crypto::hash, tools::wallet2::pool_payment_details> &container,
759 {
760  auto range = container.equal_range(key);
761  for (auto i = range.first; i != range.second; ++i)
762  {
763  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)
764  {
765  i->second = pd;
766  return false;
767  }
768  }
769  container.emplace(key, pd);
770  return true;
771 }
772 
773 void drop_from_short_history(std::list<crypto::hash> &short_chain_history, size_t N)
774 {
775  std::list<crypto::hash>::iterator right;
776  // drop early N off, skipping the genesis block
777  if (short_chain_history.size() > N) {
778  right = short_chain_history.end();
779  std::advance(right,-1);
780  std::list<crypto::hash>::iterator left = right;
781  std::advance(left, -N);
782  short_chain_history.erase(left, right);
783  }
784 }
785 
786 size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof)
787 {
788  size_t size = 0;
789 
790  // tx prefix
791 
792  // first few bytes
793  size += 1 + 6;
794 
795  // vin
796  size += n_inputs * (1+6+(mixin+1)*2+32);
797 
798  // vout
799  size += n_outputs * (6+32);
800 
801  // extra
802  size += extra_size;
803 
804  // rct signatures
805 
806  // type
807  size += 1;
808 
809  // rangeSigs
810  if (bulletproof)
811  {
812  size_t log_padded_outputs = 0;
813  while ((1<<log_padded_outputs) < n_outputs)
814  ++log_padded_outputs;
815  size += (2 * (6 + log_padded_outputs) + 4 + 5) * 32 + 3;
816  }
817  else
818  size += (2*64*32+32+64*32) * n_outputs;
819 
820  // MGs
821  size += n_inputs * (64 * (mixin+1) + 32);
822 
823  // mixRing - not serialized, can be reconstructed
824  /* size += 2 * 32 * (mixin+1) * n_inputs; */
825 
826  // pseudoOuts
827  size += 32 * n_inputs;
828  // ecdhInfo
829  size += 8 * n_outputs;
830  // outPk - only commitment is saved
831  size += 32 * n_outputs;
832  // txnFee
833  size += 4;
834 
835  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)");
836  return size;
837 }
838 
839 size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof)
840 {
841  if (use_rct)
842  return estimate_rct_tx_size(n_inputs, mixin, n_outputs, extra_size, bulletproof);
843  else
844  return n_inputs * (mixin+1) * APPROXIMATE_INPUT_BYTES + extra_size;
845 }
846 
847 uint64_t estimate_tx_weight(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof)
848 {
849  size_t size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
850  if (use_rct && bulletproof && n_outputs > 2)
851  {
852  const uint64_t bp_base = 368;
853  size_t log_padded_outputs = 2;
854  while ((1<<log_padded_outputs) < n_outputs)
855  ++log_padded_outputs;
856  uint64_t nlr = 2 * (6 + log_padded_outputs);
857  const uint64_t bp_size = 32 * (9 + nlr);
858  const uint64_t bp_clawback = (bp_base * (1<<log_padded_outputs) - bp_size) * 4 / 5;
859  MDEBUG("clawback on size " << size << ": " << bp_clawback);
860  size += bp_clawback;
861  }
862  return size;
863 }
864 
865 uint8_t get_bulletproof_fork()
866 {
867  return 99;
868 }
869 
870 uint64_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)
871 {
872  if (use_per_byte_fee)
873  {
874  const size_t estimated_tx_weight = estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
875  return calculate_fee_from_weight(base_fee, estimated_tx_weight, fee_multiplier, fee_quantization_mask);
876  }
877  else
878  {
879  const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
880  return calculate_fee(base_fee, estimated_tx_size, fee_multiplier);
881  }
882 }
883 
884 uint64_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)
885 {
886  if (use_per_byte_fee)
887  return calculate_fee_from_weight(base_fee, cryptonote::get_transaction_weight(tx, blob_size), fee_multiplier, fee_quantization_mask);
888  else
889  return calculate_fee(base_fee, blob_size, fee_multiplier);
890 }
891 
892 bool get_short_payment_id(crypto::hash8 &payment_id8, const tools::wallet2::pending_tx &ptx, hw::device &hwdev)
893 {
894  std::vector<tx_extra_field> tx_extra_fields;
895  parse_tx_extra(ptx.tx.extra, tx_extra_fields); // ok if partially parsed
896  cryptonote::tx_extra_nonce extra_nonce;
897  if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
898  {
899  if(get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
900  {
901  if (ptx.dests.empty())
902  {
903  MWARNING("Encrypted payment id found, but no destinations public key, cannot decrypt");
904  return false;
905  }
906  return hwdev.decrypt_payment_id(payment_id8, ptx.dests[0].addr.m_view_public_key, ptx.tx_key);
907  }
908  }
909  return false;
910 }
911 
912 tools::wallet2::tx_construction_data get_construction_data_with_decrypted_short_payment_id(const tools::wallet2::pending_tx &ptx, hw::device &hwdev)
913 {
915  crypto::hash8 payment_id8 = null_hash8;
916  if (get_short_payment_id(payment_id8, ptx, hwdev))
917  {
918  // Remove encrypted
919  remove_field_from_tx_extra(construction_data.extra, typeid(cryptonote::tx_extra_nonce));
920  // Add decrypted
921  std::string extra_nonce;
922  crypto::hash payment_id = null_hash;
923  memcpy(payment_id.data, payment_id8.data, 8); // convert short pid to regular
924  memset(payment_id.data + 8, 0, 24); // merely a sanity check
925 
926  set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
927  THROW_WALLET_EXCEPTION_IF(!add_extra_nonce_to_tx_extra(construction_data.extra, extra_nonce),
928  tools::error::wallet_internal_error, "Failed to add decrypted payment id to tx extra");
929  LOG_PRINT_L1("Decrypted payment ID: " << payment_id);
930  }
931  return construction_data;
932 }
933 
934 uint32_t get_subaddress_clamped_sum(uint32_t idx, uint32_t extra)
935 {
936  static constexpr uint32_t uint32_max = std::numeric_limits<uint32_t>::max();
937  if (idx > uint32_max - extra)
938  return uint32_max;
939  return idx + extra;
940 }
941 
942 static void setup_shim(hw::wallet_shim * shim, tools::wallet2 * wallet)
943 {
944  shim->get_tx_pub_key_from_received_outs = std::bind(&tools::wallet2::get_tx_pub_key_from_received_outs, wallet, std::placeholders::_1);
945 }
946 
947 bool get_pruned_tx(const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::entry &entry, cryptonote::transaction &tx, crypto::hash &tx_hash)
948 {
950 
951  // easy case if we have the whole tx
952  if (!entry.as_hex.empty() || (!entry.prunable_as_hex.empty() && !entry.pruned_as_hex.empty()))
953  {
954  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");
955  CHECK_AND_ASSERT_MES(cryptonote::parse_and_validate_tx_from_blob(bd, tx), false, "Invalid tx data");
956  tx_hash = cryptonote::get_transaction_hash(tx);
957  // if the hash was given, check it matches
958  CHECK_AND_ASSERT_MES(entry.tx_hash.empty() || epee::string_tools::pod_to_hex(tx_hash) == entry.tx_hash, false,
959  "Response claims a different hash than the data yields");
960  return true;
961  }
962  // case of a pruned tx with its prunable data hash
963  if (!entry.pruned_as_hex.empty() && !entry.prunable_hash.empty())
964  {
965  crypto::hash ph;
966  CHECK_AND_ASSERT_MES(epee::string_tools::hex_to_pod(entry.prunable_hash, ph), false, "Failed to parse prunable hash");
967  CHECK_AND_ASSERT_MES(epee::string_tools::parse_hexstr_to_binbuff(entry.pruned_as_hex, bd), false, "Failed to parse pruned data");
968  CHECK_AND_ASSERT_MES(parse_and_validate_tx_base_from_blob(bd, tx), false, "Invalid base tx data");
969  // only v2 txes can calculate their txid after pruned
970  if (bd[0] > 1)
971  {
972  tx_hash = cryptonote::get_pruned_transaction_hash(tx, ph);
973  }
974  else
975  {
976  // for v1, we trust the dameon
977  CHECK_AND_ASSERT_MES(epee::string_tools::hex_to_pod(entry.tx_hash, tx_hash), false, "Failed to parse tx hash");
978  }
979  return true;
980  }
981  return false;
982 }
983 
984  //-----------------------------------------------------------------
985 } //namespace
986 
987 namespace tools
988 {
989 // for now, limit to 30 attempts. TODO: discuss a good number to limit to.
990 const size_t MAX_SPLIT_ATTEMPTS = 30;
991 
992 constexpr const std::chrono::seconds wallet2::rpc_timeout;
993 const char* wallet2::tr(const char* str) { return i18n_translate(str, "tools::wallet2"); }
994 
995 gamma_picker::gamma_picker(const std::vector<uint64_t> &rct_offsets, double shape, double scale):
996  rct_offsets(rct_offsets)
997 {
998  gamma = std::gamma_distribution<double>(shape, scale);
999  THROW_WALLET_EXCEPTION_IF(rct_offsets.size() <= ETN_DEFAULT_TX_SPENDABLE_AGE_V8, error::wallet_internal_error, "Bad offset calculation");
1000  const size_t blocks_in_a_year = 86400 * 365 / DIFFICULTY_TARGET_V6;
1001  const size_t blocks_to_consider = std::min<size_t>(rct_offsets.size(), blocks_in_a_year);
1002  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);
1003  begin = rct_offsets.data();
1004  end = rct_offsets.data() + rct_offsets.size() - ETN_DEFAULT_TX_SPENDABLE_AGE_V8;
1005  num_rct_outputs = *(end - 1);
1006  THROW_WALLET_EXCEPTION_IF(num_rct_outputs == 0, error::wallet_internal_error, "No rct outputs");
1007  average_output_time = DIFFICULTY_TARGET_V6 * blocks_to_consider / outputs_to_consider; // this assumes constant target over the whole rct range
1008 };
1009 
1010 gamma_picker::gamma_picker(const std::vector<uint64_t> &rct_offsets): gamma_picker(rct_offsets, GAMMA_SHAPE, GAMMA_SCALE) {}
1011 
1013 {
1014  double x = gamma(engine);
1015  x = exp(x);
1016  uint64_t output_index = x / average_output_time;
1017  if (output_index >= num_rct_outputs)
1018  return std::numeric_limits<uint64_t>::max(); // bad pick
1019  output_index = num_rct_outputs - 1 - output_index;
1020 
1021  const uint64_t *it = std::lower_bound(begin, end, output_index);
1022  THROW_WALLET_EXCEPTION_IF(it == end, error::wallet_internal_error, "output_index not found");
1023  uint64_t index = std::distance(begin, it);
1024 
1025  const uint64_t first_rct = index == 0 ? 0 : rct_offsets[index - 1];
1026  const uint64_t n_rct = rct_offsets[index] - first_rct;
1027  if (n_rct == 0)
1028  return std::numeric_limits<uint64_t>::max(); // bad pick
1029  MTRACE("Picking 1/" << n_rct << " in block " << index);
1030  return first_rct + crypto::rand_idx(n_rct);
1031 };
1032 
1033 wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, const boost::optional<tools::password_container> &password):
1034  w(w),
1035  locked(password != boost::none)
1036 {
1037  if (!locked || w.is_unattended() || w.ask_password() != tools::wallet2::AskPasswordToDecrypt || w.watch_only())
1038  {
1039  locked = false;
1040  return;
1041  }
1042  const epee::wipeable_string pass = password->password();
1043  w.generate_chacha_key_from_password(pass, key);
1044  w.decrypt_keys(key);
1045 }
1046 
1048  w(w),
1049  locked(locked)
1050 {
1051  if (!locked)
1052  return;
1053  w.generate_chacha_key_from_password(password, key);
1054  w.decrypt_keys(key);
1055 }
1056 
1058 {
1059  if (!locked)
1060  return;
1061  try { w.encrypt_keys(key); }
1062  catch (...)
1063  {
1064  MERROR("Failed to re-encrypt wallet keys");
1065  // do not propagate through dtor, we'd crash
1066  }
1067 }
1068 
1070 {
1071  if (wallet)
1072  wallet->on_device_button_request(code);
1073 }
1074 
1076 {
1077  if (wallet)
1078  wallet->on_device_button_pressed();
1079 }
1080 
1081 boost::optional<epee::wipeable_string> wallet_device_callback::on_pin_request()
1082 {
1083  if (wallet)
1084  return wallet->on_device_pin_request();
1085  return boost::none;
1086 }
1087 
1088 boost::optional<epee::wipeable_string> wallet_device_callback::on_passphrase_request(bool on_device)
1089 {
1090  if (wallet)
1091  return wallet->on_device_passphrase_request(on_device);
1092  return boost::none;
1093 }
1094 
1096 {
1097  if (wallet)
1098  wallet->on_device_progress(event);
1099 }
1100 
1101 wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
1102  m_multisig_rescan_info(NULL),
1103  m_multisig_rescan_k(NULL),
1104  m_upper_transaction_weight_limit(0),
1105  m_run(true),
1106  m_callback(0),
1107  m_trusted_daemon(false),
1108  m_nettype(nettype),
1109  m_multisig_rounds_passed(0),
1110  m_always_confirm_transfers(true),
1111  m_print_ring_members(false),
1112  m_store_tx_info(true),
1113  m_default_mixin(0),
1114  m_default_priority(0),
1115  m_refresh_type(RefreshOptimizeCoinbase),
1116  m_auto_refresh(true),
1117  m_first_refresh_done(false),
1118  m_refresh_from_block_height(0),
1119  m_explicit_refresh_from_block_height(true),
1120  m_confirm_missing_payment_id(true),
1121  m_confirm_non_default_ring_size(true),
1122  m_ask_password(AskPasswordOnAction),
1123  m_min_output_count(0),
1124  m_min_output_value(0),
1125  m_merge_destinations(false),
1126  m_confirm_backlog(true),
1127  m_confirm_backlog_threshold(0),
1128  m_confirm_export_overwrite(true),
1129  m_auto_low_priority(true),
1130  m_segregate_pre_fork_outputs(true),
1131  m_key_reuse_mitigation2(true),
1132  m_segregation_height(0),
1133  m_ignore_fractional_outputs(true),
1134  m_track_uses(false),
1135  m_setup_background_mining(BackgroundMiningMaybe),
1136  m_is_initialized(false),
1137  m_kdf_rounds(kdf_rounds),
1138  is_old_file_format(false),
1139  m_watch_only(false),
1140  m_multisig(false),
1141  m_multisig_threshold(0),
1142  m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex),
1143  m_account_public_address{crypto::null_pkey, crypto::null_pkey},
1144  m_subaddress_lookahead_major(SUBADDRESS_LOOKAHEAD_MAJOR),
1145  m_subaddress_lookahead_minor(SUBADDRESS_LOOKAHEAD_MINOR),
1146  m_light_wallet(false),
1147  m_light_wallet_scanned_block_height(0),
1148  m_light_wallet_blockchain_height(0),
1149  m_light_wallet_connected(false),
1150  m_light_wallet_balance(0),
1151  m_light_wallet_unlocked_balance(0),
1152  m_original_keys_available(false),
1153  m_message_store(),
1154  m_key_device_type(hw::device::device_type::SOFTWARE),
1155  m_ring_history_saved(false),
1156  m_ringdb(),
1157  m_last_block_reward(0),
1158  m_encrypt_keys_after_refresh(boost::none),
1159  m_unattended(unattended),
1160  m_devices_registered(false),
1161  m_device_last_key_image_sync(0),
1162  m_use_dns(true),
1163  m_offline(false),
1164  m_account_major_offset(0)
1165 {
1166 }
1167 
1169 {
1170 }
1171 
1172 bool wallet2::has_testnet_option(const boost::program_options::variables_map& vm)
1173 {
1174  return command_line::get_arg(vm, options().testnet);
1175 }
1176 
1177 bool wallet2::has_stagenet_option(const boost::program_options::variables_map& vm)
1178 {
1179  return command_line::get_arg(vm, options().stagenet);
1180 }
1181 
1182 std::string wallet2::device_name_option(const boost::program_options::variables_map& vm)
1183 {
1184  return command_line::get_arg(vm, options().hw_device);
1185 }
1186 
1187 std::string wallet2::device_derivation_path_option(const boost::program_options::variables_map &vm)
1188 {
1189  return command_line::get_arg(vm, options().hw_device_derivation_path);
1190 }
1191 
1192 void wallet2::init_options(boost::program_options::options_description& desc_params)
1193 {
1194  const options opts{};
1195  command_line::add_arg(desc_params, opts.daemon_address);
1196  command_line::add_arg(desc_params, opts.daemon_host);
1197  command_line::add_arg(desc_params, opts.proxy);
1198  command_line::add_arg(desc_params, opts.trusted_daemon);
1199  command_line::add_arg(desc_params, opts.untrusted_daemon);
1200  command_line::add_arg(desc_params, opts.password);
1201  command_line::add_arg(desc_params, opts.password_file);
1202  command_line::add_arg(desc_params, opts.daemon_port);
1203  command_line::add_arg(desc_params, opts.daemon_login);
1204  command_line::add_arg(desc_params, opts.daemon_ssl);
1205  command_line::add_arg(desc_params, opts.daemon_ssl_private_key);
1206  command_line::add_arg(desc_params, opts.daemon_ssl_certificate);
1207  command_line::add_arg(desc_params, opts.daemon_ssl_ca_certificates);
1208  command_line::add_arg(desc_params, opts.daemon_ssl_allowed_fingerprints);
1209  command_line::add_arg(desc_params, opts.daemon_ssl_allow_any_cert);
1210  command_line::add_arg(desc_params, opts.daemon_ssl_allow_chained);
1211  command_line::add_arg(desc_params, opts.testnet);
1212  command_line::add_arg(desc_params, opts.stagenet);
1213  command_line::add_arg(desc_params, opts.shared_ringdb_dir);
1214  command_line::add_arg(desc_params, opts.kdf_rounds);
1215  mms::message_store::init_options(desc_params);
1216  command_line::add_arg(desc_params, opts.hw_device);
1217  command_line::add_arg(desc_params, opts.hw_device_derivation_path);
1218  command_line::add_arg(desc_params, opts.tx_notify);
1219  command_line::add_arg(desc_params, opts.no_dns);
1220  command_line::add_arg(desc_params, opts.offline);
1221  command_line::add_arg(desc_params, opts.data_dir);
1222  command_line::add_arg(desc_params, opts.fallback_to_pow_checkpoint_height);
1223  command_line::add_arg(desc_params, opts.fallback_to_pow_checkpoint_hash);
1224 }
1225 
1226 std::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)
1227 {
1228  const options opts{};
1229  return generate_from_json(json_file, vm, unattended, opts, password_prompter);
1230 }
1231 
1232 std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file(
1233  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)
1234 {
1235  const options opts{};
1236  auto pwd = get_password(vm, opts, password_prompter, false);
1237  if (!pwd)
1238  {
1239  return {nullptr, password_container{}};
1240  }
1241  auto wallet = make_basic(vm, unattended, opts, password_prompter);
1242  if (wallet && !wallet_file.empty())
1243  {
1244  wallet->load(wallet_file, pwd->password());
1245  }
1246  return {std::move(wallet), std::move(*pwd)};
1247 }
1248 
1249 std::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)
1250 {
1251  const options opts{};
1252  auto pwd = get_password(vm, opts, password_prompter, true);
1253  if (!pwd)
1254  {
1255  return {nullptr, password_container{}};
1256  }
1257  return {make_basic(vm, unattended, opts, password_prompter), std::move(*pwd)};
1258 }
1259 
1260 std::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)
1261 {
1262  const options opts{};
1263  return make_basic(vm, unattended, opts, password_prompter);
1264 }
1265 
1266 //----------------------------------------------------------------------------------------------------
1267 bool 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)
1268 {
1269  boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex);
1270 
1271  if(m_http_client.is_connected())
1272  m_http_client.disconnect();
1273  m_daemon_address = std::move(daemon_address);
1274  m_daemon_login = std::move(daemon_login);
1275  m_trusted_daemon = trusted_daemon;
1276 
1277  MINFO("setting daemon to " << get_daemon_address());
1278  return m_http_client.set_server(get_daemon_address(), get_daemon_login(), std::move(ssl_options));
1279 }
1280 //----------------------------------------------------------------------------------------------------
1281 bool 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)
1282 {
1283  m_checkpoints.init_default_checkpoints(m_nettype);
1284  m_is_initialized = true;
1285  m_upper_transaction_weight_limit = upper_transaction_weight_limit;
1286 
1287  if (proxy != boost::asio::ip::tcp::endpoint{})
1288  m_http_client.set_connector(net::socks::connector{std::move(proxy)});
1289  return set_daemon(daemon_address, daemon_login, trusted_daemon, std::move(ssl_options));
1290 }
1291 //----------------------------------------------------------------------------------------------------
1293 {
1294  crypto::secret_key second;
1295  keccak((uint8_t *)&get_account().get_keys().m_spend_secret_key, sizeof(crypto::secret_key), (uint8_t *)&second, sizeof(crypto::secret_key));
1296  sc_reduce32((uint8_t *)&second);
1297  return memcmp(second.data,get_account().get_keys().m_view_secret_key.data, sizeof(crypto::secret_key)) == 0;
1298 }
1299 //----------------------------------------------------------------------------------------------------
1300 bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeable_string &passphrase) const
1301 {
1302  bool keys_deterministic = is_deterministic();
1303  if (!keys_deterministic)
1304  {
1305  std::cout << "This is not a deterministic wallet" << std::endl;
1306  return false;
1307  }
1308  if (seed_language.empty())
1309  {
1310  std::cout << "seed_language not set" << std::endl;
1311  return false;
1312  }
1313 
1315  if (!passphrase.empty())
1316  key = cryptonote::encrypt_key(key, passphrase);
1317  if (!crypto::ElectrumWords::bytes_to_words(key, electrum_words, seed_language))
1318  {
1319  std::cout << "Failed to create seed from key for language: " << seed_language << std::endl;
1320  return false;
1321  }
1322 
1323  return true;
1324 }
1325 //----------------------------------------------------------------------------------------------------
1326 bool wallet2::get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase, bool raw) const
1327 {
1328  bool ready;
1329  uint32_t threshold, total;
1330  if (!multisig(&ready, &threshold, &total))
1331  {
1332  std::cout << "This is not a multisig wallet" << std::endl;
1333  return false;
1334  }
1335  if (!ready)
1336  {
1337  std::cout << "This multisig wallet is not yet finalized" << std::endl;
1338  return false;
1339  }
1340  if (!raw && seed_language.empty())
1341  {
1342  std::cout << "seed_language not set" << std::endl;
1343  return false;
1344  }
1345 
1346  crypto::secret_key skey;
1347  crypto::public_key pkey;
1348  const account_keys &keys = get_account().get_keys();
1349  epee::wipeable_string data;
1350  data.append((const char*)&threshold, sizeof(uint32_t));
1351  data.append((const char*)&total, sizeof(uint32_t));
1352  skey = keys.m_spend_secret_key;
1353  data.append((const char*)&skey, sizeof(skey));
1355  data.append((const char*)&pkey, sizeof(pkey));
1356  skey = keys.m_view_secret_key;
1357  data.append((const char*)&skey, sizeof(skey));
1359  data.append((const char*)&pkey, sizeof(pkey));
1360  for (const auto &skey: keys.m_multisig_keys)
1361  data.append((const char*)&skey, sizeof(skey));
1362  for (const auto &signer: m_multisig_signers)
1363  data.append((const char*)&signer, sizeof(signer));
1364 
1365  if (!passphrase.empty())
1366  {
1368  crypto::cn_slow_hash(passphrase.data(), passphrase.size(), (crypto::hash&)key);
1369  sc_reduce32((unsigned char*)key.data);
1370  data = encrypt(data, key, true);
1371  }
1372 
1373  if (raw)
1374  {
1375  seed = epee::to_hex::wipeable_string({(const unsigned char*)data.data(), data.size()});
1376  }
1377  else
1378  {
1379  if (!crypto::ElectrumWords::bytes_to_words(data.data(), data.size(), seed, seed_language))
1380  {
1381  std::cout << "Failed to encode seed";
1382  return false;
1383  }
1384  }
1385 
1386  return true;
1387 }
1388 //----------------------------------------------------------------------------------------------------
1390 {
1391  bool r = true;
1392  hw::device &hwdev = lookup_device(m_device_name);
1393  hwdev.set_name(m_device_name);
1394  hwdev.set_network_type(m_nettype);
1395  hwdev.set_derivation_path(m_device_derivation_path);
1396  hwdev.set_callback(get_device_callback());
1397  r = hwdev.init();
1398  if (!r){
1399  MERROR("Could not init device");
1400  return false;
1401  }
1402 
1403  r = hwdev.connect();
1404  if (!r){
1405  MERROR("Could not connect to the device");
1406  return false;
1407  }
1408 
1409  m_account.set_device(hwdev);
1410  return true;
1411 }
1412 //----------------------------------------------------------------------------------------------------
1417 {
1418  return seed_language;
1419 }
1425 {
1426  seed_language = language;
1427 }
1428 //----------------------------------------------------------------------------------------------------
1430 {
1431  hw::device &hwdev = m_account.get_device();
1432  cryptonote::subaddress_index index2 = {index.major + (index.major != 0 ? m_account_major_offset : 0), index.minor};
1433  return hwdev.get_subaddress(m_account.get_keys(), index2);
1434 }
1435 //----------------------------------------------------------------------------------------------------
1436 boost::optional<cryptonote::subaddress_index> wallet2::get_subaddress_index(const cryptonote::account_public_address& address) const
1437 {
1438  auto index = m_subaddresses.find(address.m_spend_public_key);
1439  if (index == m_subaddresses.end())
1440  return boost::none;
1441  return index->second;
1442 }
1443 //----------------------------------------------------------------------------------------------------
1445 {
1446  hw::device &hwdev = m_account.get_device();
1447  cryptonote::subaddress_index index2 = {index.major + (index.major != 0 ? m_account_major_offset : 0), index.minor};
1448  return hwdev.get_subaddress_spend_public_key(m_account.get_keys(), index2);
1449 }
1450 //----------------------------------------------------------------------------------------------------
1452 {
1454  return cryptonote::get_account_address_as_str(m_nettype, !index.is_zero(), address);
1455 }
1456 //----------------------------------------------------------------------------------------------------
1458 {
1459  return cryptonote::get_account_integrated_address_as_str(m_nettype, get_address(), payment_id);
1460 }
1461 //----------------------------------------------------------------------------------------------------
1462 void wallet2::add_subaddress_account(const std::string& label, const bool update_account_tags)
1463 {
1465  expand_subaddresses({index_major, 0}, update_account_tags);
1466  m_subaddress_labels[index_major][0] = label;
1467 }
1468 //----------------------------------------------------------------------------------------------------
1469 void wallet2::add_subaddress(uint32_t index_major, const std::string& label)
1470 {
1471  THROW_WALLET_EXCEPTION_IF(index_major >= m_subaddress_labels.size(), error::account_index_outofbound);
1472  uint32_t index_minor = (uint32_t)get_num_subaddresses(index_major);
1473  expand_subaddresses({index_major, index_minor});
1474  m_subaddress_labels[index_major][index_minor] = label;
1475 }
1476 //----------------------------------------------------------------------------------------------------
1477 void wallet2::expand_subaddresses(const cryptonote::subaddress_index& index, const bool update_account_tags)
1478 {
1479  hw::device &hwdev = m_account.get_device();
1480  if (m_subaddress_labels.size() <= index.major)
1481  {
1482  // add new accounts
1484  const uint32_t major_end = get_subaddress_clamped_sum(index.major, m_subaddress_lookahead_major);
1485  for (index2.major = m_subaddress_labels.size(); index2.major < major_end; ++index2.major)
1486  {
1487  const uint32_t end = get_subaddress_clamped_sum((index2.major == index.major ? index.minor : 0), m_subaddress_lookahead_minor);
1488  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);
1489  for (index2.minor = 0; index2.minor < end; ++index2.minor)
1490  {
1491  const crypto::public_key &D = pkeys[index2.minor];
1492  m_subaddresses[D] = index2;
1493  }
1494  }
1495  m_subaddress_labels.resize(index.major + 1, {"Untitled account"});
1496  m_subaddress_labels[index.major].resize(index.minor + 1);
1497 
1498  if(update_account_tags)
1499  get_account_tags();
1500  }
1501  else if (m_subaddress_labels[index.major].size() <= index.minor)
1502  {
1503  // add new subaddresses
1504  const uint32_t end = get_subaddress_clamped_sum(index.minor, m_subaddress_lookahead_minor);
1505  const uint32_t begin = m_subaddress_labels[index.major].size();
1506  cryptonote::subaddress_index index2 = {index.major, begin};
1507  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);
1508  for (; index2.minor < end; ++index2.minor)
1509  {
1510  const crypto::public_key &D = pkeys[index2.minor - begin];
1511  m_subaddresses[D] = index2;
1512  }
1513  m_subaddress_labels[index.major].resize(index.minor + 1);
1514  }
1515 }
1516 //----------------------------------------------------------------------------------------------------
1518 {
1519  if (index.major >= m_subaddress_labels.size() || index.minor >= m_subaddress_labels[index.major].size())
1520  {
1521  MERROR("Subaddress label doesn't exist");
1522  return "";
1523  }
1524  return m_subaddress_labels[index.major][index.minor];
1525 }
1526 //----------------------------------------------------------------------------------------------------
1528 {
1529  THROW_WALLET_EXCEPTION_IF(index.major >= m_subaddress_labels.size(), error::account_index_outofbound);
1530  THROW_WALLET_EXCEPTION_IF(index.minor >= m_subaddress_labels[index.major].size(), error::address_index_outofbound);
1531  m_subaddress_labels[index.major][index.minor] = label;
1532 }
1533 //----------------------------------------------------------------------------------------------------
1534 void wallet2::set_subaddress_lookahead(size_t major, size_t minor)
1535 {
1536  THROW_WALLET_EXCEPTION_IF(major > 0xffffffff, error::wallet_internal_error, "Subaddress major lookahead is too large");
1537  THROW_WALLET_EXCEPTION_IF(minor > 0xffffffff, error::wallet_internal_error, "Subaddress minor lookahead is too large");
1538  m_subaddress_lookahead_major = major;
1539  m_subaddress_lookahead_minor = minor;
1540 }
1541 //----------------------------------------------------------------------------------------------------
1546 {
1547  return is_old_file_format;
1548 }
1549 //----------------------------------------------------------------------------------------------------
1550 void wallet2::set_spent(size_t idx, uint64_t height, bool public_out)
1551 {
1552  transfer_details &td = m_transfers[idx];
1553 
1554  if(public_out){
1555  LOG_PRINT_L2("Setting SPENT at "
1556  << height << ": chainstate index " << td.m_txid << ":" << td.m_internal_output_index
1557  << ", amount " << print_etn(td.m_amount));
1558  }else{
1559  LOG_PRINT_L2("Setting SPENT at "
1560  << height << ": ki " << td.m_key_image
1561  << ", amount " << print_etn(td.m_amount));
1562  }
1563 
1564  td.m_spent = true;
1565  td.m_spent_height = height;
1566 }
1567 //----------------------------------------------------------------------------------------------------
1568 void wallet2::set_unspent(size_t idx, bool public_out)
1569 {
1570  transfer_details &td = m_transfers[idx];
1571 
1572  if(public_out){
1573  LOG_PRINT_L2("Setting UNSPENT: chainstate index "
1574  << td.m_txid << ":"<< td.m_internal_output_index << ", amount " << print_etn(td.m_amount));
1575  }else{
1576  LOG_PRINT_L2("Setting UNSPENT: ki " << td.m_key_image << ", amount " << print_etn(td.m_amount));
1577  }
1578 
1579  td.m_spent = false;
1580  td.m_spent_height = 0;
1581 }
1582 //----------------------------------------------------------------------------------------------------
1583 void wallet2::freeze(size_t idx)
1584 {
1585  CHECK_AND_ASSERT_THROW_MES(idx < m_transfers.size(), "Invalid transfer_details index");
1586  transfer_details &td = m_transfers[idx];
1587  td.m_frozen = true;
1588 }
1589 //----------------------------------------------------------------------------------------------------
1590 void wallet2::thaw(size_t idx)
1591 {
1592  CHECK_AND_ASSERT_THROW_MES(idx < m_transfers.size(), "Invalid transfer_details index");
1593  transfer_details &td = m_transfers[idx];
1594  td.m_frozen = false;
1595 }
1596 //----------------------------------------------------------------------------------------------------
1597 bool wallet2::frozen(size_t idx) const
1598 {
1599  CHECK_AND_ASSERT_THROW_MES(idx < m_transfers.size(), "Invalid transfer_details index");
1600  const transfer_details &td = m_transfers[idx];
1601  return td.m_frozen;
1602 }
1603 //----------------------------------------------------------------------------------------------------
1605 {
1607 }
1608 //----------------------------------------------------------------------------------------------------
1610 {
1612 }
1613 //----------------------------------------------------------------------------------------------------
1614 bool wallet2::frozen(const crypto::key_image &ki) const
1615 {
1616  return frozen(get_transfer_details(ki));
1617 }
1618 //----------------------------------------------------------------------------------------------------
1619 size_t wallet2::get_transfer_details(const crypto::key_image &ki) const
1620 {
1621  for (size_t idx = 0; idx < m_transfers.size(); ++idx)
1622  {
1623  const transfer_details &td = m_transfers[idx];
1624  if (td.m_key_image_known && td.m_key_image == ki)
1625  return idx;
1626  }
1627  CHECK_AND_ASSERT_THROW_MES(false, "Key image not found");
1628 }
1629 //----------------------------------------------------------------------------------------------------
1630 bool wallet2::frozen(const transfer_details &td) const
1631 {
1632  return td.m_frozen;
1633 }
1634 //----------------------------------------------------------------------------------------------------
1635 void 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
1636 {
1637  hw::device &hwdev = m_account.get_device();
1638  boost::unique_lock<hw::device> hwdev_lock (hwdev);
1640  if (o.target.type() != typeid(txout_to_key) && o.target.type() != typeid(txout_to_key_public))
1641  {
1642  tx_scan_info.error = true;
1643  LOG_ERROR("wrong type id in transaction out");
1644  return;
1645  }
1646  if(o.target.type() != typeid(txout_to_key_public)) {
1647  tx_scan_info.received = is_out_to_acc_precomp(m_subaddresses, boost::get<txout_to_key>(o.target).key, derivation,
1648  additional_derivations, i, hwdev);
1649  }else{
1650  //only assign subaddress recipient if view key also matches too as we now spend with combined keys (a+b) and we wont be
1651  // doing key image related checks later to check if we can really spend the out (ie checking view key match by proxy)
1652  auto out_address = boost::get<txout_to_key_public>(o.target).address;
1653  auto receive_info = cryptonote::is_out_to_acc_precomp_public(m_subaddresses, out_address);
1654  tx_scan_info.received =
1655  (receive_info == boost::none) ?
1656  (receive_info) :
1657  get_subaddress(receive_info->index).m_view_public_key == out_address.m_view_public_key ?
1658  receive_info : boost::none; //todo: refactor with function pointers
1659  }
1660  if(tx_scan_info.received)
1661  {
1662  tx_scan_info.etn_transfered = o.amount; // may be 0 for ringct outputs
1663  }
1664  else
1665  {
1666  tx_scan_info.etn_transfered = 0;
1667  }
1668  tx_scan_info.error = false;
1669 }
1670 //----------------------------------------------------------------------------------------------------
1671 void 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
1672 {
1673  // 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)
1674  if (!is_out_data || i >= is_out_data->received.size())
1675  return check_acc_out_precomp(o, derivation, additional_derivations, i, tx_scan_info);
1676 
1677  tx_scan_info.received = is_out_data->received[i];
1678  if(tx_scan_info.received)
1679  {
1680  tx_scan_info.etn_transfered = o.amount; // may be 0 for ringct outputs
1681  }
1682  else
1683  {
1684  tx_scan_info.etn_transfered = 0;
1685  }
1686  tx_scan_info.error = false;
1687 }
1688 //----------------------------------------------------------------------------------------------------
1689 void 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
1690 {
1691  tx_scan_info.received = boost::none;
1692  if (already_seen)
1693  return;
1694  check_acc_out_precomp(o, derivation, additional_derivations, i, is_out_data, tx_scan_info);
1695  if (tx_scan_info.received)
1696  already_seen = true;
1697 }
1698 //----------------------------------------------------------------------------------------------------
1699 static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &derivation, unsigned int i, rct::key & mask, hw::device &hwdev)
1700 {
1701  crypto::secret_key scalar1;
1702  hwdev.derivation_to_scalar(derivation, i, scalar1);
1703  try
1704  {
1705  switch (rv.type)
1706  {
1707  case rct::RCTTypeSimple:
1710  return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask, hwdev);
1711  case rct::RCTTypeFull:
1712  return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask, hwdev);
1713  default:
1714  LOG_ERROR("Unsupported rct type: " << rv.type);
1715  return 0;
1716  }
1717  }
1718  catch (const std::exception &e)
1719  {
1720  LOG_ERROR("Failed to decode input " << i);
1721  return 0;
1722  }
1723 }
1724 //----------------------------------------------------------------------------------------------------
1725 void 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)
1726 {
1727  THROW_WALLET_EXCEPTION_IF(i >= tx.vout.size(), error::wallet_internal_error, "Invalid vout index");
1728 
1729  // if keys are encrypted, ask for password
1730  if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only && !m_multisig_rescan_k)
1731  {
1732  static critical_section password_lock;
1733  CRITICAL_REGION_LOCAL(password_lock);
1734  if (!m_encrypt_keys_after_refresh)
1735  {
1736  boost::optional<epee::wipeable_string> pwd = m_callback->on_get_password(pool ? "output found in pool" : "output received");
1737  THROW_WALLET_EXCEPTION_IF(!pwd, error::password_needed, tr("Password is needed to compute key image for incoming etn"));
1738  THROW_WALLET_EXCEPTION_IF(!verify_password(*pwd), error::password_needed, tr("Invalid password: password is needed to compute key image for incoming etn"));
1739  decrypt_keys(*pwd);
1740  m_encrypt_keys_after_refresh = *pwd;
1741  }
1742  }
1743 
1744  if (m_multisig)
1745  {
1746  tx_scan_info.in_ephemeral.pub = boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key;
1747  tx_scan_info.in_ephemeral.sec = crypto::null_skey;
1748  tx_scan_info.ki = rct::rct2ki(rct::zero());
1749  }
1750  else if (tx.version == 1)
1751  { //calculate the key image as if we were going to spend this output
1752  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);
1753  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
1754  THROW_WALLET_EXCEPTION_IF(tx_scan_info.in_ephemeral.pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key,
1755  error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
1756  }
1757 
1758  THROW_WALLET_EXCEPTION_IF(std::find(outs.begin(), outs.end(), i) != outs.end(), error::wallet_internal_error, "Same output cannot be added twice");
1759  //we should never enter this unless it's an rct output
1760  if (tx_scan_info.etn_transfered == 0 && !miner_tx)
1761  {
1762  tx_scan_info.etn_transfered = tools::decodeRct(tx.rct_signatures, tx_scan_info.received->derivation, i, tx_scan_info.mask, m_account.get_device());
1763  }
1764  if (tx_scan_info.etn_transfered == 0)
1765  {
1766  MERROR("Invalid output amount, skipping");
1767  tx_scan_info.error = true;
1768  return;
1769  }
1770  outs.push_back(i);
1771  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,
1772  error::wallet_internal_error, "Overflow in received amounts");
1773  //index here is a subaddress index (major & minor)
1774  tx_etn_got_in_outs[tx_scan_info.received->index] += tx_scan_info.etn_transfered;
1775  tx_scan_info.amount = tx_scan_info.etn_transfered;
1776  ++num_vouts_received;
1777 }
1778 //----------------------------------------------------------------------------------------------------
1779 void wallet2::cache_tx_data(const cryptonote::transaction& tx, const crypto::hash &txid, tx_cache_data &tx_cache_data) const
1780 {
1781  if(!parse_tx_extra(tx.extra, tx_cache_data.tx_extra_fields))
1782  {
1783  // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key
1784  LOG_PRINT_L0("Transaction extra has unsupported format: " << txid);
1785  if (tx_cache_data.tx_extra_fields.empty())
1786  return;
1787  }
1788 
1789 
1790  // Don't try to extract tx public key if tx has no ouputs
1791  const bool is_miner = tx.vin.size() == 1 && tx.vin[0].type() == typeid(cryptonote::txin_gen);
1792  if (!is_miner || m_refresh_type != RefreshType::RefreshNoCoinbase)
1793  {
1794  const size_t rec_size = is_miner && m_refresh_type == RefreshType::RefreshOptimizeCoinbase ? 1 : tx.vout.size();
1795  if (!tx.vout.empty())
1796  {
1797  const std::vector<boost::optional<cryptonote::subaddress_receive_info>> rec(rec_size, boost::none);
1798  if(tx.version > 1){
1799  tx_cache_data.public_outs.push_back({crypto::null_pkey, {}, rec});
1800  return;
1801  }
1802 
1803  // if tx.vout is not empty, we loop through all tx pubkeys
1804  tx_extra_pub_key pub_key_field;
1805  size_t pk_index = 0;
1806  while (find_tx_extra_field_by_type(tx_cache_data.tx_extra_fields, pub_key_field, pk_index++))
1807  tx_cache_data.primary.push_back({pub_key_field.pub_key, {}, rec});
1808 
1809  // additional tx pubkeys and derivations for multi-destination transfers involving one or more subaddresses
1810  tx_extra_additional_pub_keys additional_tx_pub_keys;
1811  if (find_tx_extra_field_by_type(tx_cache_data.tx_extra_fields, additional_tx_pub_keys))
1812  {
1813  for (size_t i = 0; i < additional_tx_pub_keys.data.size(); ++i)
1814  tx_cache_data.additional.push_back({additional_tx_pub_keys.data[i], {}, {}});
1815  }
1816  }
1817  }
1818 }
1819 //----------------------------------------------------------------------------------------------------
1820 void 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)
1821 {
1822  PERF_TIMER(process_new_transaction);
1823  // In this function, tx (probably) only contains the base information
1824  // (that is, the prunable stuff may or may not be included)
1825  if (!miner_tx && !pool)
1826  process_unconfirmed(txid, tx, height);
1827  std::unordered_map<cryptonote::subaddress_index, uint64_t> tx_etn_got_in_outs; // per receiving subaddress index
1828  crypto::public_key tx_pub_key = null_pkey;
1829  bool notify = false;
1830 
1831  std::vector<tx_extra_field> local_tx_extra_fields;
1832  if (tx_cache_data.tx_extra_fields.empty())
1833  {
1834  if(!parse_tx_extra(tx.extra, local_tx_extra_fields))
1835  {
1836  // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key
1837  LOG_PRINT_L0("Transaction extra has unsupported format: " << txid);
1838  }
1839  }
1840  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;
1841 
1842  // Don't try to extract tx public key if tx has no ouputs
1843  size_t pk_index = 0; //this is an index for searching for pubkeys
1844  std::vector<tx_scan_info_t> tx_scan_info(tx.vout.size());
1845  std::deque<bool> output_found(tx.vout.size(), false);
1846  uint64_t total_received_1 = 0;
1847 
1848  while (!tx.vout.empty()) {
1849  std::vector<size_t> outs;
1850 
1851  // if tx.vout is not empty, we loop through all tx pubkeys
1852  tx_extra_pub_key pub_key_field;
1853  if (tx.version == 1) {
1854  if (!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, pk_index++)) {
1855  if (pk_index > 1) // we should find we hit this afer one iteration for v1 transactions
1856  break;
1857  LOG_PRINT_L0("Public key wasn't found in the transaction extra. Skipping transaction " << txid);
1858  if (0 != m_callback)
1859  m_callback->on_skip_transaction(height, txid, tx);
1860  break;
1861  }
1862  if (!tx_cache_data.primary.empty()) {
1863  THROW_WALLET_EXCEPTION_IF(tx_cache_data.primary.size() < pk_index ||
1864  pub_key_field.pub_key != tx_cache_data.primary[pk_index - 1].pkey,
1865  error::wallet_internal_error, "tx_cache_data is out of sync");
1866  }
1867  }
1868  int num_vouts_received = 0;
1869  tx_pub_key = pub_key_field.pub_key;
1872  const cryptonote::account_keys &keys = m_account.get_keys();
1873  crypto::key_derivation derivation;
1874 
1875  std::vector<crypto::key_derivation> additional_derivations;
1876  tx_extra_additional_pub_keys additional_tx_pub_keys;
1877 
1878  const wallet2::is_out_data *is_out_data_ptr = NULL; //will point to pre-cached tx data if data is available
1879 
1880  if (tx.version == 1) {
1881  // THIS IF/ELSE IS PURELY PROCESSING DERIVATIONS (DIFFIE H SHARED SECRETS aR1....aRN) FOR V1 TX
1882  if (tx_cache_data.primary.empty()) {
1883  hw::device &hwdev = m_account.get_device();
1884  boost::unique_lock<hw::device> hwdev_lock(hwdev);
1885  hw::reset_mode rst(hwdev);
1886 
1888  if (!hwdev.generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation)) {
1889  MWARNING("Failed to generate key derivation from tx pubkey in " << txid << ", skipping");
1890  static_assert(sizeof(derivation) == sizeof(rct::key),
1891  "Mismatched sizes of key_derivation and rct::key");
1892  memcpy(&derivation, rct::identity().bytes, sizeof(derivation));
1893  }
1894 
1895  if (pk_index == 1) {
1896  // additional tx pubkeys and derivations for multi-destination transfers involving one or more subaddresses
1897  if (find_tx_extra_field_by_type(tx_extra_fields, additional_tx_pub_keys)) {
1898  for (size_t i = 0; i < additional_tx_pub_keys.data.size(); ++i) {
1899  additional_derivations.push_back({});
1900  if (!hwdev.generate_key_derivation(additional_tx_pub_keys.data[i],
1901  keys.m_view_secret_key,
1902  additional_derivations.back())) {
1903  MWARNING("Failed to generate key derivation from additional tx pubkey in " << txid
1904  << ", skipping");
1905  memcpy(&additional_derivations.back(), rct::identity().bytes,
1906  sizeof(crypto::key_derivation));
1907  }
1908  }
1909  }
1910  }
1911  } else {
1912  THROW_WALLET_EXCEPTION_IF(pk_index - 1 >= tx_cache_data.primary.size(),
1913  error::wallet_internal_error, "pk_index out of range of tx_cache_data");
1914  is_out_data_ptr = &tx_cache_data.primary[pk_index - 1];
1915  derivation = tx_cache_data.primary[pk_index - 1].derivation;
1916  if (pk_index == 1) {
1917  for (size_t n = 0; n < tx_cache_data.additional.size(); ++n) {
1918  additional_tx_pub_keys.data.push_back(tx_cache_data.additional[n].pkey);
1919  additional_derivations.push_back(tx_cache_data.additional[n].derivation);
1920  }
1921  }
1922  }
1923  }
1924  // END OF DERIVATIONS PROCESSING (V1 ONLY)
1925  // NOW WE BEGIN TO CHECK THE OUTS //
1926 
1927  //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
1928  if (tx.version > 1 && !tx_cache_data.public_outs.empty()) {
1929  is_out_data_ptr = &tx_cache_data.public_outs[0];
1930  }
1931 
1932  // IGNORE COINBASE
1933  if (miner_tx && m_refresh_type == RefreshNoCoinbase) {
1934  // assume coinbase isn't for us
1935  }
1936  // PROCESS COINBASE
1937  else if (miner_tx && m_refresh_type == RefreshOptimizeCoinbase) {
1938  //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.
1939  //both tx_scan_info and output_found are populated INSIDE the precomp function only
1940  check_acc_out_precomp_once(tx.vout[0], derivation, additional_derivations, 0, is_out_data_ptr,
1941  tx_scan_info[0], output_found[0]); //is the miner tx ours?
1942  THROW_WALLET_EXCEPTION_IF(tx_scan_info[0].error, error::acc_outs_lookup_error, tx, tx_pub_key,
1943  m_account.get_keys());
1944 
1945  // this assumes that the miner tx pays a single address
1946  if (tx_scan_info[0].received) {
1947  // process the other outs from that miner tx. the first one was already checked
1948  for (size_t i = 1; i < tx.vout.size(); ++i) {
1949  tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp_once, this, std::cref(tx.vout[i]),
1950  std::cref(derivation), std::cref(additional_derivations), i,
1951  std::cref(is_out_data_ptr), std::ref(tx_scan_info[i]),
1952  std::ref(output_found[i])), true);
1953  }
1954  waiter.wait(&tpool);
1955  // then scan all outputs from 0
1956  hw::device &hwdev = m_account.get_device();
1957  boost::unique_lock<hw::device> hwdev_lock(hwdev);
1958  hwdev.set_mode(hw::device::NONE);
1959  for (size_t i = 0; i < tx.vout.size(); ++i) {
1960  THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key,
1961  m_account.get_keys());
1962  if (tx_scan_info[i].received) //at this point we are only scanning the entried that we marked as received in precomp
1963  {
1964  if (tx.version == 1) {
1965  hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key,
1966  additional_tx_pub_keys.data, derivation, additional_derivations);
1967  }
1968  scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_etn_got_in_outs,
1969  outs, pool);
1970  }
1971  }
1972  }
1973  }
1974  // PROCESS SINGLE NON COINBASE TX (IF THEY EXIST) WITH THREADS IF THERE IS MORE THAN ONE OUT AND MULTITHREADING IS PERMITTED
1975  else if (tx.vout.size() > 1 && tools::threadpool::getInstance().get_max_concurrency() > 1 && !is_out_data_ptr) {
1976  for (size_t i = 0; i < tx.vout.size(); ++i) {
1977  tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp_once, this, std::cref(tx.vout[i]),
1978  std::cref(derivation), std::cref(additional_derivations), i,
1979  std::cref(is_out_data_ptr), std::ref(tx_scan_info[i]),
1980  std::ref(output_found[i])), true);
1981  }
1982  waiter.wait(&tpool);
1983 
1984  hw::device &hwdev = m_account.get_device();
1985  boost::unique_lock<hw::device> hwdev_lock(hwdev);
1986  hwdev.set_mode(hw::device::NONE);
1987  for (size_t i = 0; i < tx.vout.size(); ++i) {
1988  THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key,
1989  m_account.get_keys());
1990  if (tx_scan_info[i].received) //at this point we are only scanning the entried that we marked as received in precomp
1991  {
1992  // todo: 4.0.0.0 ledger code only
1993  if (tx.version == 1) {
1994  hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key,
1995  additional_tx_pub_keys.data,
1996  derivation, additional_derivations);
1997  }
1998  scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_etn_got_in_outs,
1999  outs, pool);
2000  }
2001  }
2002  }
2003  // IF ONLY ONE OUT OR MULTITHREADING ISN'T ENABLED, PROCESS SINGLE TX NORMALLY
2004  else {
2005  for (size_t i = 0; i < tx.vout.size(); ++i) {
2006  check_acc_out_precomp_once(tx.vout[i], derivation, additional_derivations, i, is_out_data_ptr,
2007  tx_scan_info[i], output_found[i]);
2008  THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key,
2009  m_account.get_keys());
2010  if (tx_scan_info[i].received) //at this point we are only scanning the entried that we marked as received in precomp
2011  {
2012  hw::device &hwdev = m_account.get_device();
2013  boost::unique_lock<hw::device> hwdev_lock(hwdev);
2014  hwdev.set_mode(hw::device::NONE);
2015  if (tx.version == 1) {
2016  hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key,
2017  additional_tx_pub_keys.data,
2018  derivation, additional_derivations);
2019  }
2020  scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_etn_got_in_outs,
2021  outs, pool);
2022  }
2023  }
2024  }
2025  if (tx.version == 1){
2026  if (!outs.empty() && num_vouts_received > 0) { // we will loop over outs below, which is just the index
2027  //good news - got etn! take care about it
2028  //usually we have only one transfer for user in transaction
2029  if (!pool) {
2030  THROW_WALLET_EXCEPTION_IF(tx.vout.size() != o_indices.size(), error::wallet_internal_error,
2031  "transactions outputs size=" + std::to_string(tx.vout.size()) +
2032  " not match with daemon response size=" + std::to_string(o_indices.size()));
2033  }
2034 
2035  for (const size_t o: outs) {
2036  THROW_WALLET_EXCEPTION_IF(tx.vout.size() <= o, error::wallet_internal_error,
2037  "wrong out in transaction: internal index=" +
2038  std::to_string(o) + ", total_outs=" + std::to_string(tx.vout.size()));
2039 
2040  auto kit = m_pub_keys.find(tx_scan_info[o].in_ephemeral.pub);
2041  //stealth address already exists in a transfer entry or we have more pubkeys than transfers for some unkown reason
2042  THROW_WALLET_EXCEPTION_IF(kit != m_pub_keys.end() && kit->second >= m_transfers.size(),
2043  error::wallet_internal_error,
2044  std::string("Unexpected transfer index from public key: ")
2045  + "got " +
2046  (kit == m_pub_keys.end() ? "<none>" : boost::lexical_cast<std::string>(
2047  kit->second))
2048  + ", m_transfers.size() is " +
2049  boost::lexical_cast<std::string>(m_transfers.size()));
2050  if (kit == m_pub_keys.end()) { //typical
2051  uint64_t amount = tx.vout[o].amount ? tx.vout[o].amount : tx_scan_info[o].amount;
2052  if (!pool) {
2053  m_transfers.push_back(boost::value_initialized<transfer_details>());
2054  transfer_details &td = m_transfers.back();
2055  td.m_block_height = height;
2056  td.m_internal_output_index = o;
2057  td.m_global_output_index = o_indices[o];
2058  td.m_tx = (const cryptonote::transaction_prefix &) tx;
2059  td.m_txid = txid;
2060  td.m_key_image = tx_scan_info[o].ki;
2061  td.m_key_image_known = !m_watch_only && !m_multisig;
2062  if (!td.m_key_image_known) {
2063  // we might have cold signed, and have a mapping to key images
2064  std::unordered_map<crypto::public_key, crypto::key_image>::const_iterator i = m_cold_key_images.find(
2065  tx_scan_info[o].in_ephemeral.pub);
2066  if (i != m_cold_key_images.end()) {
2067  td.m_key_image = i->second;
2068  td.m_key_image_known = true;
2069  }
2070  }
2071  if (m_watch_only) {
2072  // for view wallets, that flag means "we want to request it"
2073  td.m_key_image_request = true;
2074  } else {
2075  td.m_key_image_request = false;
2076  }
2077  td.m_key_image_partial = m_multisig;
2078  td.m_amount = amount;
2079  td.m_pk_index = pk_index - 1;
2080  td.m_subaddr_index = tx_scan_info[o].received->index;
2081  expand_subaddresses(tx_scan_info[o].received->index);
2082 
2083  //TODO: Public
2084  td.m_mask = rct::identity();
2085  td.m_rct = false;
2086 
2087  td.m_frozen = false;
2088  set_unspent(m_transfers.size() - 1);
2089  if (td.m_key_image_known)
2090  m_key_images[td.m_key_image] = m_transfers.size() - 1;
2091  m_pub_keys[tx_scan_info[o].in_ephemeral.pub] = m_transfers.size() - 1;
2092  if (output_tracker_cache)
2093  (*output_tracker_cache).first[std::make_pair(tx.vout[o].amount, td.m_global_output_index)] =
2094  m_transfers.size() - 1;
2095  if (m_multisig) {
2096  THROW_WALLET_EXCEPTION_IF(!m_multisig_rescan_k && m_multisig_rescan_info,
2097  error::wallet_internal_error, "NULL m_multisig_rescan_k");
2098  if (m_multisig_rescan_info &&
2099  m_multisig_rescan_info->front().size() >= m_transfers.size())
2100  update_multisig_rescan_info(*m_multisig_rescan_k, *m_multisig_rescan_info,
2101  m_transfers.size() - 1);
2102  }
2103  LOG_PRINT_L0("Received ETN: " << print_etn(td.amount()) << ", with tx: " << txid);
2104  if (0 != m_callback)
2105  m_callback->on_etn_received(height, txid, tx, td.m_amount, td.m_subaddr_index,
2106  td.m_tx.unlock_time);
2107  }
2108  total_received_1 += amount;
2109  notify = true;
2110  } else if (m_transfers[kit->second].m_spent ||
2111  // 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
2112  m_transfers[kit->second].amount() >= tx_scan_info[o].amount) {
2113  LOG_ERROR("Public key " << epee::string_tools::pod_to_hex(kit->first)
2114  << " from received " << print_etn(tx_scan_info[o].amount)
2115  << " output already exists with "
2116  << (m_transfers[kit->second].m_spent ? "spent" : "unspent") << " "
2117  << print_etn(m_transfers[kit->second].amount()) << " in tx "
2118  << m_transfers[kit->second].m_txid << ", received output ignored");
2120  tx_etn_got_in_outs[tx_scan_info[o].received->index] < tx_scan_info[o].amount,
2121  error::wallet_internal_error, "Unexpected values of new and old outputs");
2122  tx_etn_got_in_outs[tx_scan_info[o].received->index] -= tx_scan_info[o].amount;
2123  } 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
2124  LOG_ERROR("Public key " << epee::string_tools::pod_to_hex(kit->first)
2125  << " from received " << print_etn(tx_scan_info[o].amount)
2126  << " output already exists with "
2127  << print_etn(m_transfers[kit->second].amount())
2128  << ", replacing with new output");
2129  // The new larger output replaced a previous smaller one
2131  tx_etn_got_in_outs[tx_scan_info[o].received->index] < tx_scan_info[o].amount,
2132  error::wallet_internal_error, "Unexpected values of new and old outputs");
2133  THROW_WALLET_EXCEPTION_IF(m_transfers[kit->second].amount() > tx_scan_info[o].amount,
2134  error::wallet_internal_error,
2135  "Unexpected values of new and old outputs");
2136  tx_etn_got_in_outs[tx_scan_info[o].received->index] -= m_transfers[kit->second].amount();
2137 
2138  uint64_t amount = tx.vout[o].amount ? tx.vout[o].amount : tx_scan_info[o].amount;
2139  uint64_t extra_amount = amount - m_transfers[kit->second].amount();
2140  if (!pool) {
2141  transfer_details &td = m_transfers[kit->second];
2142  td.m_block_height = height;
2143  td.m_internal_output_index = o;
2144  td.m_global_output_index = o_indices[o];
2145  td.m_tx = (const cryptonote::transaction_prefix &) tx;
2146  td.m_txid = txid;
2147  td.m_amount = amount;
2148  td.m_pk_index = pk_index - 1;
2149  td.m_subaddr_index = tx_scan_info[o].received->index;
2150  expand_subaddresses(tx_scan_info[o].received->index);
2151 
2152  //TODO: Public
2153  td.m_mask = rct::identity();
2154  td.m_rct = false;
2155 
2156  if (output_tracker_cache)
2157  (*output_tracker_cache).first[std::make_pair(tx.vout[o].amount,
2158  td.m_global_output_index)] = kit->second;
2159  if (m_multisig) {
2160  THROW_WALLET_EXCEPTION_IF(!m_multisig_rescan_k && m_multisig_rescan_info,
2161  error::wallet_internal_error, "NULL m_multisig_rescan_k");
2162  if (m_multisig_rescan_info &&
2163  m_multisig_rescan_info->front().size() >= m_transfers.size())
2164  update_multisig_rescan_info(*m_multisig_rescan_k, *m_multisig_rescan_info,
2165  m_transfers.size() - 1);
2166  }
2167  THROW_WALLET_EXCEPTION_IF(td.get_public_key() != tx_scan_info[o].in_ephemeral.pub,
2168  error::wallet_internal_error, "Inconsistent public keys");
2169  THROW_WALLET_EXCEPTION_IF(td.m_spent, error::wallet_internal_error,
2170  "Inconsistent spent status");
2171 
2172  LOG_PRINT_L1("Received ETN: " << print_etn(td.amount()) << ", with tx: " << txid);
2173  if (0 != m_callback)
2174  m_callback->on_etn_received(height, txid, tx, td.m_amount, td.m_subaddr_index,
2175  td.m_tx.unlock_time);
2176  }
2177  total_received_1 += extra_amount;
2178  notify = true;
2179  }
2180  }
2181  }
2182  } //end of v1 tx outs processing
2183  else { //process v2+ tx outs
2184  if (!outs.empty() && num_vouts_received > 0) { // we will loop over outs below, which is just the index
2185  //good news - got etn! take care about it
2186  //usually we have only one transfer for user in transaction
2187  for (const size_t o: outs) {
2188  THROW_WALLET_EXCEPTION_IF(tx.vout.size() <= o, error::wallet_internal_error,
2189  "wrong out in transaction: internal index=" +
2190  std::to_string(o) + ", total_outs=" + std::to_string(tx.vout.size()));
2191 
2192  auto kit = m_chainstate_indexes.find(std::make_pair(txid, o));
2193  // Chainstate index already exists in a transfer entry or we have more chainstate indexes than transfers for some unkown reason
2194  THROW_WALLET_EXCEPTION_IF(kit != m_chainstate_indexes.end() && kit->second >= m_transfers.size(),
2195  error::wallet_internal_error,
2196  std::string("Unexpected transfer index from chainstate index: ")
2197  + "got " +
2198  (kit == m_chainstate_indexes.end() ? "<none>" : boost::lexical_cast<std::string>(
2199  kit->second))
2200  + ", m_transfers.size() is " +
2201  boost::lexical_cast<std::string>(m_transfers.size()));
2202  if (kit == m_chainstate_indexes.end()) { //typical
2203  uint64_t amount = tx.vout[o].amount ? tx.vout[o].amount : tx_scan_info[o].amount;
2204  if (!pool) {
2205  m_transfers.push_back(boost::value_initialized<transfer_details>());
2206  transfer_details &td = m_transfers.back();
2207  td.m_block_height = height;
2208  td.m_internal_output_index = o;
2209  td.m_global_output_index = std::numeric_limits<uint64_t>::max();
2210  td.m_tx = (const cryptonote::transaction_prefix &) tx;
2211  td.m_txid = txid;
2212  td.m_key_image = boost::value_initialized<crypto::key_image>();
2213  td.m_key_image_known = false;
2214  td.m_key_image_partial = false;
2215  td.m_amount = amount;
2216  td.m_pk_index = pk_index - 1;
2217  td.m_subaddr_index = tx_scan_info[o].received->index;
2218  expand_subaddresses(tx_scan_info[o].received->index);
2219  td.m_mask = rct::identity();
2220  td.m_rct = false;
2221  td.m_frozen = false;
2222  set_unspent(m_transfers.size() - 1, true);
2223  m_chainstate_indexes[std::make_pair(txid, o)] = m_transfers.size() - 1;
2224  if (output_tracker_cache) {
2225  std::array<char, 32> transaction_id;
2226  std::copy(std::begin(td.m_txid.data), std::end(td.m_txid.data), transaction_id.begin());
2227  (*output_tracker_cache).second[std::make_pair(transaction_id, td.m_internal_output_index)] =
2228  m_transfers.size() - 1;
2229  }
2230  if (m_multisig) {
2231  THROW_WALLET_EXCEPTION_IF(!m_multisig_rescan_k && m_multisig_rescan_info,
2232  error::wallet_internal_error, "NULL m_multisig_rescan_k");
2233  if (m_multisig_rescan_info &&
2234  m_multisig_rescan_info->front().size() >= m_transfers.size())
2235  update_multisig_rescan_info(*m_multisig_rescan_k, *m_multisig_rescan_info,
2236  m_transfers.size() - 1);
2237  }
2238  LOG_PRINT_L0("Received ETN: " << print_etn(td.amount()) << ", with tx: " << txid);
2239  if (0 != m_callback)
2240  m_callback->on_etn_received(height, txid, tx, td.m_amount, td.m_subaddr_index,
2241  td.m_tx.unlock_time);
2242  }
2243  total_received_1 += amount;
2244  notify = true;
2245  } else if (m_transfers[kit->second].m_spent ||
2246  // 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
2247  m_transfers[kit->second].amount() >= tx_scan_info[o].amount) {
2248  LOG_ERROR("Chainstate index " << epee::string_tools::pod_to_hex(kit->first.first)
2249  << ":" << kit->first.second
2250  << " from received " << print_etn(tx_scan_info[o].amount)
2251  << " output already exists with "
2252  << (m_transfers[kit->second].m_spent ? "spent" : "unspent") << " "
2253  << print_etn(m_transfers[kit->second].amount()) << " in tx "
2254  << m_transfers[kit->second].m_txid << ", received output ignored");
2256  tx_etn_got_in_outs[tx_scan_info[o].received->index] < tx_scan_info[o].amount,
2257  error::wallet_internal_error, "Unexpected values of new and old outputs");
2258  tx_etn_got_in_outs[tx_scan_info[o].received->index] -= tx_scan_info[o].amount;
2259  } 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
2260  LOG_ERROR("Chainstate index " << epee::string_tools::pod_to_hex(kit->first.first)
2261  << ":" << kit->first.second
2262  << " from received " << print_etn(tx_scan_info[o].amount)
2263  << " output already exists with "
2264  << print_etn(m_transfers[kit->second].amount())
2265  << ", replacing with new output");
2266  // The new larger output replaced a previous smaller one
2268  tx_etn_got_in_outs[tx_scan_info[o].received->index] < tx_scan_info[o].amount,
2269  error::wallet_internal_error, "Unexpected values of new and old outputs");
2270  THROW_WALLET_EXCEPTION_IF(m_transfers[kit->second].amount() > tx_scan_info[o].amount,
2271  error::wallet_internal_error,
2272  "Unexpected values of new and old outputs");
2273  tx_etn_got_in_outs[tx_scan_info[o].received->index] -= m_transfers[kit->second].amount();
2274 
2275  uint64_t amount = tx.vout[o].amount ? tx.vout[o].amount : tx_scan_info[o].amount;
2276  uint64_t extra_amount = amount - m_transfers[kit->second].amount();
2277  if (!pool) {
2278  transfer_details &td = m_transfers[kit->second];
2279  td.m_block_height = height;
2280  td.m_internal_output_index = o;
2281  td.m_global_output_index = std::numeric_limits<uint64_t>::max();
2282  td.m_tx = (const cryptonote::transaction_prefix &) tx;
2283  td.m_txid = txid;
2284  td.m_amount = amount;
2285  td.m_pk_index = pk_index - 1;
2286  td.m_subaddr_index = tx_scan_info[o].received->index;
2287  expand_subaddresses(tx_scan_info[o].received->index);
2288  td.m_mask = rct::identity();
2289  td.m_rct = false;
2290 
2291  if (output_tracker_cache) {
2292  std::array<char, 32> transaction_id;
2293  std::copy(std::begin(td.m_txid.data), std::end(td.m_txid.data), transaction_id.begin());
2294  (*output_tracker_cache).second[std::make_pair(transaction_id,
2295  td.m_internal_output_index)] = kit->second;
2296  }
2297  if (m_multisig) {
2298  THROW_WALLET_EXCEPTION_IF(!m_multisig_rescan_k && m_multisig_rescan_info,
2299  error::wallet_internal_error, "NULL m_multisig_rescan_k");
2300  if (m_multisig_rescan_info &&
2301  m_multisig_rescan_info->front().size() >= m_transfers.size())
2302  update_multisig_rescan_info(*m_multisig_rescan_k, *m_multisig_rescan_info,
2303  m_transfers.size() - 1);
2304  }
2305  THROW_WALLET_EXCEPTION_IF(td.get_chainstate_index() != std::make_pair(txid, o),
2306  error::wallet_internal_error, "Inconsistent public keys");
2307  THROW_WALLET_EXCEPTION_IF(td.m_spent, error::wallet_internal_error,
2308  "Inconsistent spent status");
2309 
2310  LOG_PRINT_L1("Received ETN: " << print_etn(td.amount()) << ", with tx: " << txid);
2311  if (0 != m_callback)
2312  m_callback->on_etn_received(height, txid, tx, td.m_amount, td.m_subaddr_index,
2313  td.m_tx.unlock_time);
2314  }
2315  total_received_1 += extra_amount;
2316  notify = true;
2317  }
2318  }
2319  }
2320  break; // we don't have to iterate again for v2+ outs becuase there aren't multiple pubkeys (see pk index)
2321  } //end of v2+ outs processing
2322  } // end of all outs processing
2323 
2324  uint64_t tx_etn_spent_in_ins = 0;
2325  // 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
2326  // It's a GCC bug with boost::optional, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47679
2327  auto subaddr_account ([]()->boost::optional<uint32_t> {return boost::none;}());
2328  std::set<uint32_t> subaddr_indices;
2329  // check all outputs for spending (compare key images)
2330  for(auto& in: tx.vin)
2331  {
2332  if(tx.version < 3) { // we still use old txin_to_key for migration transactions (v2)
2333  if (in.type() != typeid(cryptonote::txin_to_key))
2334  continue;
2335  const cryptonote::txin_to_key &in_to_key = boost::get<cryptonote::txin_to_key>(in);
2336  auto it = m_key_images.find(in_to_key.k_image);
2337  if (it != m_key_images.end()) //these are UNspent key images
2338  {
2339  transfer_details &td = m_transfers[it->second];
2340  uint64_t amount = in_to_key.amount;
2341  if (amount > 0) {
2342  if (amount != td.amount()) {
2343  MERROR("Inconsistent amount in tx input: got " << print_etn(amount) <<
2344  ", expected " << print_etn(td.amount()));
2345  // this means:
2346  // 1) the same output pub key was used as destination multiple times,
2347  // 2) the wallet set the highest amount among them to transfer_details::m_amount, and
2348  // 3) the wallet somehow spent that output with an amount smaller than the above amount, causing inconsistency
2349  td.m_amount = amount;
2350  }
2351  } else {
2352  amount = td.amount();
2353  }
2354  tx_etn_spent_in_ins += amount;
2355  if (subaddr_account && *subaddr_account != td.m_subaddr_index.major)
2356  LOG_ERROR(
2357  "spent funds are from different subaddress accounts; count of incoming/outgoing payments will be incorrect");
2358  subaddr_account = td.m_subaddr_index.major;
2359  subaddr_indices.insert(td.m_subaddr_index.minor);
2360  if (!pool) {
2361  LOG_PRINT_L1("Spent ETN: " << print_etn(amount) << ", with tx: " << txid);
2362  set_spent(it->second, height);
2363  if (0 != m_callback)
2364  m_callback->on_etn_spent(height, txid, tx, amount, tx, td.m_subaddr_index);
2365  }
2366  }
2367 
2368  if (!pool && m_track_uses) {
2369  PERF_TIMER(track_uses);
2370  const uint64_t amount = in_to_key.amount; //amount to check against transfer details
2371  std::vector<uint64_t> offsets = cryptonote::relative_output_offsets_to_absolute(
2372  in_to_key.key_offsets); //todo: 4.0.0.0
2373  if (output_tracker_cache) {
2374  for (uint64_t offset: offsets) {
2375  const std::map<std::pair<uint64_t, uint64_t>, size_t>::const_iterator i = output_tracker_cache->first.find(
2376  std::make_pair(amount, offset));
2377  if (i != output_tracker_cache->first.end()) {
2378  size_t idx = i->second;
2379  THROW_WALLET_EXCEPTION_IF(idx >= m_transfers.size(), error::wallet_internal_error,
2380  "Output tracker cache index out of range");
2381  m_transfers[idx].m_uses.push_back(std::make_pair(height, txid));
2382  }
2383  }
2384  } else {
2385  //essentially the long way of doing it without a cache - loop over all m_transfers to find a match
2386  for (transfer_details &td: m_transfers) {
2387  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
2388  continue;
2389  for (uint64_t offset: offsets)
2390  if (offset == td.m_global_output_index)
2391  td.m_uses.push_back(std::make_pair(height, txid));
2392  }
2393  }
2394  }
2395  }else{ // Public inputs (v3)
2396  if (in.type() != typeid(cryptonote::txin_to_key_public))
2397  continue;
2398  const cryptonote::txin_to_key_public &in_to_key_public = boost::get<cryptonote::txin_to_key_public>(in);
2399  auto it = m_chainstate_indexes.find(std::make_pair(in_to_key_public.tx_hash, in_to_key_public.relative_offset));
2400  if (it != m_chainstate_indexes.end()) //these are UNspent chainstate indexes
2401  {
2402  transfer_details &td = m_transfers[it->second];
2403  uint64_t amount = in_to_key_public.amount; // here we're just grabbing the amount of the input from m_transfers
2404  if (amount > 0) {
2405  if (amount != td.amount()) {
2406  MERROR("Inconsistent amount in tx input: got " << print_etn(amount) <<
2407  ", expected " << print_etn(td.amount()));
2408  // this means:
2409  // 1) the same chainstate index was used as destination multiple times,
2410  // 2) the wallet set the highest amount among them to transfer_details::m_amount, and
2411  // 3) the wallet somehow spent that output with an amount smaller than the above amount, causing inconsistency
2412  td.m_amount = amount;
2413  }
2414  } else {
2415  amount = td.amount();
2416  }
2417  tx_etn_spent_in_ins += amount;
2418  if (subaddr_account && *subaddr_account != td.m_subaddr_index.major)
2419  LOG_ERROR(
2420  "spent funds are from different subaddress accounts; count of incoming/outgoing payments will be incorrect");
2421  subaddr_account = td.m_subaddr_index.major;
2422  subaddr_indices.insert(td.m_subaddr_index.minor);
2423  if (!pool) {
2424  LOG_PRINT_L1("Spent ETN: " << print_etn(amount) << ", with tx: " << txid);
2425  set_spent(it->second, height, true);
2426  if (0 != m_callback)
2427  m_callback->on_etn_spent(height, txid, tx, amount, tx, td.m_subaddr_index);
2428  }
2429  }
2430 
2431  if (!pool && m_track_uses) {
2432  PERF_TIMER(track_uses);
2433  const uint64_t amount = in_to_key_public.amount;
2434  if (output_tracker_cache) {
2435  std::array<char, 32> transaction_id;
2436  std::copy(std::begin(in_to_key_public.tx_hash.data), std::end(in_to_key_public.tx_hash.data), transaction_id.begin());
2437  const std::map<std::pair<std::array<char, 32>, size_t>, size_t>::const_iterator i = output_tracker_cache->second.find(
2438  std::make_pair(transaction_id, in_to_key_public.relative_offset));
2439  if (i != output_tracker_cache->second.end()) {
2440  size_t idx = i->second;
2441  THROW_WALLET_EXCEPTION_IF(idx >= m_transfers.size(), error::wallet_internal_error,
2442  "Output tracker cache index out of range");
2443  m_transfers[idx].m_uses.push_back(std::make_pair(height, txid));
2444  }
2445  } else
2446  for (transfer_details &td: m_transfers) {
2447  if (in_to_key_public.tx_hash != td.m_txid)
2448  continue;
2449  if (in_to_key_public.relative_offset == td.m_internal_output_index)
2450  td.m_uses.push_back(std::make_pair(height, txid));
2451  }
2452  }
2453  }
2454  }
2455 
2456  uint64_t fee;
2457  if(miner_tx){
2458  fee = 0;
2459  }else{
2460  uint64_t tx_etn_total_inputs;
2461  get_inputs_etn_amount(tx, tx_etn_total_inputs);
2462  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
2463  }
2464 
2465  if (tx_etn_spent_in_ins > 0 && !pool)
2466  {
2467  // only used for v1. refactor later
2468  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,
2469  [&subaddr_account] (uint64_t acc, const std::pair<cryptonote::subaddress_index, uint64_t>& p)
2470  {
2471  return acc + (p.first.major == *subaddr_account ? p.second : 0);
2472  });
2473  process_outgoing(txid, tx, height, ts, tx_etn_spent_in_ins, self_received, *subaddr_account, subaddr_indices);
2474  // if sending to yourself at the same subaddress account, set the outgoing payment amount to 0 so that it's less confusing
2475  if (tx_etn_spent_in_ins == self_received + fee)
2476  {
2477  auto i = m_confirmed_txs.find(txid);
2478  THROW_WALLET_EXCEPTION_IF(i == m_confirmed_txs.end(), error::wallet_internal_error,
2479  "confirmed tx wasn't found: " + string_tools::pod_to_hex(txid));
2480  if(tx.version == 1)
2481  i->second.m_change = self_received;
2482  }
2483  }
2484 
2485  // remove change sent to the spending subaddress account from the list of received funds
2486  uint64_t sub_change = 0;
2487  for (auto i = tx_etn_got_in_outs.begin(); i != tx_etn_got_in_outs.end();)
2488  {
2489  if (subaddr_account && i->first.major == *subaddr_account)
2490  {
2491  sub_change += i->second;
2492  i = tx_etn_got_in_outs.erase(i);
2493  }
2494  else
2495  ++i;
2496  }
2497 
2498  // create payment_details for each incoming transfer to a subaddress index
2499  if (tx_etn_got_in_outs.size() > 0)
2500  {
2501  tx_extra_nonce extra_nonce;
2502  crypto::hash payment_id = null_hash;
2503  if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
2504  {
2505  crypto::hash8 payment_id8 = null_hash8;
2506  if(get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
2507  {
2508  // We got a payment ID to go with this tx
2509  LOG_PRINT_L2("Found encrypted payment ID: " << payment_id8);
2510  MINFO("Consider using subaddresses instead of encrypted payment IDs");
2511  if (tx_pub_key != null_pkey)
2512  {
2513  if (!m_account.get_device().decrypt_payment_id(payment_id8, tx_pub_key, m_account.get_keys().m_view_secret_key))
2514  {
2515  LOG_PRINT_L0("Failed to decrypt payment ID: " << payment_id8);
2516  }
2517  else
2518  {
2519  // put the 64 bit decrypted payment id in the first 8 bytes
2520  memcpy(payment_id.data, payment_id8.data, 8);
2521  // rest is already 0, but guard against code changes above
2522  memset(payment_id.data + 8, 0, 24);
2523  LOG_PRINT_L2(" payment ID: " << payment_id);
2524  }
2525  }
2526  else
2527  {
2528  LOG_PRINT_L1("No public key found in tx, unable to decrypt payment id");
2529  }
2530  }
2531  else if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
2532  {
2533  LOG_PRINT_L2("Found unencrypted payment ID: " << payment_id);
2534  MWARNING("Found unencrypted payment ID: these are bad for privacy, consider using subaddresses instead");
2535  }
2536  }
2537 
2538  uint64_t total_received_2 = sub_change;
2539  for (const auto& i : tx_etn_got_in_outs)
2540  total_received_2 += i.second;
2541  if (total_received_1 != total_received_2)
2542  {
2543  const el::Level level = el::Level::Warning;
2544  MCLOG_RED(level, "global", "**********************************************************************");
2545  MCLOG_RED(level, "global", "Consistency failure in amounts received");
2546  MCLOG_RED(level, "global", "Check transaction " << txid);
2547  MCLOG_RED(level, "global", "**********************************************************************");
2548  exit(1);
2549  return;
2550  }
2551 
2552  bool all_same = true;
2553  for (const auto& i : tx_etn_got_in_outs)
2554  {
2555  payment_details payment;
2556  payment.m_tx_hash = txid;
2557  payment.m_fee = fee;
2558  payment.m_amount = i.second;
2559  payment.m_block_height = height;
2560  payment.m_unlock_time = tx.unlock_time;
2561  payment.m_timestamp = ts;
2562  payment.m_coinbase = miner_tx;
2563  payment.m_subaddr_index = i.first;
2564  if (pool) {
2565  if (emplace_or_replace(m_unconfirmed_payments, payment_id, pool_payment_details{payment, double_spend_seen, nonexistent_utxo_seen}))
2566  all_same = false;
2567  if (0 != m_callback)
2568  m_callback->on_unconfirmed_etn_received(height, txid, tx, payment.m_amount, payment.m_subaddr_index);
2569  }
2570  else
2571  m_payments.emplace(payment_id, payment);
2572  LOG_PRINT_L2("Payment found in " << (pool ? "pool" : "block") << ": " << payment_id << " / " << payment.m_tx_hash << " / " << payment.m_amount);
2573  }
2574 
2575  // if it's a pool tx and we already had it, don't notify again
2576  if (pool && all_same)
2577  notify = false;
2578  }
2579 
2580  if (notify)
2581  {
2582  std::shared_ptr<tools::Notify> tx_notify = m_tx_notify;
2583  if (tx_notify)
2584  tx_notify->notify("%s", epee::string_tools::pod_to_hex(txid).c_str(), NULL);
2585  }
2586 }
2587 //----------------------------------------------------------------------------------------------------
2588 void wallet2::process_unconfirmed(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height)
2589 {
2590  if (m_unconfirmed_txs.empty())
2591  return;
2592 
2593  auto unconf_it = m_unconfirmed_txs.find(txid);
2594  if(unconf_it != m_unconfirmed_txs.end()) {
2595  if (store_tx_info()) {
2596  try {
2597  m_confirmed_txs.insert(std::make_pair(txid, confirmed_transfer_details(unconf_it->second, height)));
2598  }
2599  catch (...) {
2600  // can fail if the tx has unexpected input types
2601  LOG_PRINT_L0("Failed to add outgoing transaction to confirmed transaction map");
2602  }
2603  }
2604  m_unconfirmed_txs.erase(unconf_it);
2605  }
2606 }
2607 //----------------------------------------------------------------------------------------------------
2608 void 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)
2609 {
2610  std::pair<std::unordered_map<crypto::hash, confirmed_transfer_details>::iterator, bool> entry = m_confirmed_txs.insert(std::make_pair(txid, confirmed_transfer_details()));
2611  // fill with the info we know, some info might already be there
2612  if (entry.second)
2613  {
2614  // this case will happen if the tx is from our outputs, but was sent by another
2615  // wallet (eg, we're a cold wallet and the hot wallet sent it). For RCT transactions,
2616  // we only see 0 input amounts, so have to deduce amount out from other parameters.
2617  entry.first->second.m_amount_in = spent;
2618  entry.first->second.m_amount_out = get_outs_etn_amount(tx);
2619  if(tx.version == 1)
2620  entry.first->second.m_change = received;
2621 
2622  std::vector<tx_extra_field> tx_extra_fields;
2623  parse_tx_extra(tx.extra, tx_extra_fields); // ok if partially parsed
2624  tx_extra_nonce extra_nonce;
2625  if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
2626  {
2627  // we do not care about failure here
2628  get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, entry.first->second.m_payment_id);
2629  }
2630  entry.first->second.m_subaddr_account = subaddr_account;
2631  entry.first->second.m_subaddr_indices = subaddr_indices;
2632  }
2633 
2634  entry.first->second.m_rings.clear();
2635  for (const auto &in: tx.vin)
2636  {
2637  if (in.type() != typeid(cryptonote::txin_to_key))
2638  continue;
2639  const auto &txin = boost::get<cryptonote::txin_to_key>(in);
2640  entry.first->second.m_rings.push_back(std::make_pair(txin.k_image, txin.key_offsets));
2641  }
2642  entry.first->second.m_block_height = height;
2643  entry.first->second.m_timestamp = ts;
2644  entry.first->second.m_unlock_time = tx.unlock_time;
2645  entry.first->second.m_is_migration = tx.version == 2;
2646 
2647 
2648  if(tx.version > 1){
2649  // grab the input owner keys/address by using the subaddr indicies used for the transaction
2650  std::vector<account_public_address> input_addresses;
2651  for (auto minor_index : subaddr_indices) {
2652  cryptonote::subaddress_index index{subaddr_account, minor_index};
2653  input_addresses.push_back(get_subaddress(index));
2654  }
2655 
2656  //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.
2657  // If one or more outs do not go to an input address, we consider ALL other outputs as change outputs
2658  std::unordered_set<uint32_t> change_indexes;
2659  for (size_t i = 0; i < tx.vout.size(); ++i) {
2660  for (auto input_address : input_addresses) {
2661  if (boost::get<txout_to_key_public>(tx.vout[i].target).address == input_address) {
2662  change_indexes.insert(i);
2663  continue;
2664  }
2665  }
2666  }
2667 
2668  // if this is true we have a sweep tx so clear all change out indexes
2669  if (change_indexes.size() == tx.vout.size()) {
2670  change_indexes.clear();
2671  }
2672 
2673  int64_t total_change = 0;
2674  for (auto &change_index : change_indexes)
2675  total_change += tx.vout[change_index].amount;
2676  entry.first->second.m_change = total_change;
2677 
2678  // For V2+ tx, we can repopulate tx destinations in the wallet cache during a rescan by simply reading them from the transactions
2679  //todo: optimise
2680  if (entry.first->second.m_dests.empty()) {
2681 
2682  // fill destinations
2683  for (size_t i = 0; i < tx.vout.size(); ++i) {
2684  if (change_indexes.find(i) == change_indexes.end()) { // only include non-change outs as dests
2685  auto output = boost::get<txout_to_key_public>(tx.vout[i].target); // grab output from the tx
2686  //predicate for comparison later on
2687  auto pred = [output](const tx_destination_entry &destination) {
2688  return destination.addr == output.address;
2689  };
2690 
2691  //search our working list of destinations in entry, and either add output amount to the
2692  // running total in the case of a match, or add a new destination otherwise
2693  auto dest_ptr = std::find_if(std::begin(entry.first->second.m_dests),
2694  std::end(entry.first->second.m_dests), pred);
2695  if (dest_ptr != std::end(entry.first->second.m_dests)) {
2696  dest_ptr->amount += tx.vout[i].amount;
2697  } else {
2698  entry.first->second.m_dests.push_back(tx_destination_entry(
2699  tx.vout[i].amount,
2700  output.address,
2701  output.m_address_prefix ==
2702  get_config(this->m_nettype).CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX ? true : false
2703  ));
2704  }
2705  }
2706  }
2707  }
2708  }
2709  add_rings(tx);
2710 }
2711 //----------------------------------------------------------------------------------------------------
2712 bool wallet2::should_skip_block(const cryptonote::block &b, uint64_t height) const
2713 {
2714  // 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
2715  return !(b.timestamp + 60*60*24 > m_account.get_createtime() && height >= m_refresh_from_block_height);
2716 }
2717 //----------------------------------------------------------------------------------------------------
2718 void 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)
2719 {
2720  if(b.major_version < 10) {
2721  THROW_WALLET_EXCEPTION_IF(bche.txs.size() + 1 != parsed_block.o_indices.indices.size(),
2722  error::wallet_internal_error,
2723  "block transactions=" + std::to_string(bche.txs.size()) +
2724  " not match with daemon response size=" +
2725  std::to_string(parsed_block.o_indices.indices.size()));
2726  }
2727 
2728  //handle transactions from new block
2729 
2730  //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
2731  if (!should_skip_block(b, height))
2732  {
2733  //FIRST PROCESS THE MINER TX FOR THE NEW BLOCK
2734  TIME_MEASURE_START(miner_tx_handle_time);
2735  if (m_refresh_type != RefreshNoCoinbase)
2736  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);
2737  ++tx_cache_data_offset;
2738  TIME_MEASURE_FINISH(miner_tx_handle_time);
2739  //NOW THE OTHER TRANSACTIONS
2740  TIME_MEASURE_START(txs_handle_time);
2741  THROW_WALLET_EXCEPTION_IF(bche.txs.size() != b.tx_hashes.size(), error::wallet_internal_error, "Wrong amount of transactions for block");
2742  THROW_WALLET_EXCEPTION_IF(bche.txs.size() != parsed_block.txes.size(), error::wallet_internal_error, "Wrong amount of transactions for block");
2743  for (size_t idx = 0; idx < b.tx_hashes.size(); ++idx)
2744  {
2745  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);
2746  }
2747  TIME_MEASURE_FINISH(txs_handle_time);
2748  m_last_block_reward = cryptonote::get_outs_etn_amount(b.miner_tx);
2749  LOG_PRINT_L2("Processed block: " << bl_id << ", height " << height << ", " << miner_tx_handle_time + txs_handle_time << "(" << miner_tx_handle_time << "/" << txs_handle_time <<")ms");
2750  }else
2751  {
2752  if (!(height % 128))
2753  LOG_PRINT_L2( "Skipped block by timestamp, height: " << height << ", block time " << b.timestamp << ", account time " << m_account.get_createtime());
2754  }
2755  m_blockchain.push_back(bl_id);
2756 
2757  if (0 != m_callback)
2758  m_callback->on_new_block(height, b);
2759 }
2760 //----------------------------------------------------------------------------------------------------
2761 void wallet2::get_short_chain_history(std::list<crypto::hash>& ids, uint64_t granularity) const
2762 {
2763  size_t i = 0;
2764  size_t current_multiplier = 1;
2765  size_t blockchain_size = std::max((size_t)(m_blockchain.size() / granularity * granularity), m_blockchain.offset());
2766  size_t sz = blockchain_size - m_blockchain.offset();
2767  if(!sz)
2768  {
2769  ids.push_back(m_blockchain.genesis());
2770  return;
2771  }
2772  size_t current_back_offset = 1;
2773  bool base_included = false;
2774  while(current_back_offset < sz)
2775  {
2776  ids.push_back(m_blockchain[m_blockchain.offset() + sz-current_back_offset]);
2777  if(sz-current_back_offset == 0)
2778  base_included = true;
2779  if(i < 10)
2780  {
2781  ++current_back_offset;
2782  }else
2783  {
2784  current_back_offset += current_multiplier *= 2;
2785  }
2786  ++i;
2787  }
2788  if(!base_included)
2789  ids.push_back(m_blockchain[m_blockchain.offset()]);
2790  if(m_blockchain.offset())
2791  ids.push_back(m_blockchain.genesis());
2792 }
2793 //----------------------------------------------------------------------------------------------------
2794 void wallet2::parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const
2795 {
2797 }
2798 //----------------------------------------------------------------------------------------------------
2799 void 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)
2800 {
2803  req.block_ids = short_chain_history;
2804 
2805  req.prune = true;
2806  req.start_height = start_height;
2807  req.no_miner_tx = m_refresh_type == RefreshNoCoinbase;
2808  m_daemon_rpc_mutex.lock();
2809 
2810  bool r = invoke_http_bin("/getblocks.bin", req, res, rpc_timeout);
2811 
2812  m_daemon_rpc_mutex.unlock();
2813  THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getblocks.bin");
2814  THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getblocks.bin");
2815  THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_blocks_error, get_rpc_status(res.status));
2816  THROW_WALLET_EXCEPTION_IF(res.blocks.size() != res.output_indices.size(), error::wallet_internal_error,
2817  "mismatched blocks (" + boost::lexical_cast<std::string>(res.blocks.size()) + ") and output_indices (" +
2818  boost::lexical_cast<std::string>(res.output_indices.size()) + ") sizes from daemon");
2819 
2820  blocks_start_height = res.start_height;
2821  blocks = std::move(res.blocks);
2822  o_indices = std::move(res.output_indices);
2823 }
2824 
2825 //------------------------------------------------------------------------------------------------------------------------------
2826 cryptonote::blobdata wallet2::get_pruned_tx_blob(const cryptonote::blobdata &blobdata)
2827 {
2829 
2831  {
2832  MERROR("Failed to parse and validate tx from blob");
2833  return blobdata;
2834  }
2835 
2836  std::stringstream ss;
2837  binary_archive<true> ba(ss);
2838  bool r = tx.serialize_base(ba);
2839  CHECK_AND_ASSERT_MES(r, blobdata, "Failed to serialize rct signatures base");
2840  return ss.str();
2841 }
2842 //----------------------------------------------------------------------------------------------------
2843 void 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)
2844 {
2847  req.block_ids = short_chain_history;
2848 
2849  req.start_height = start_height;
2850  m_daemon_rpc_mutex.lock();
2851 
2852  bool r = invoke_http_bin("/gethashes.bin", req, res, rpc_timeout);
2853 
2854  m_daemon_rpc_mutex.unlock();
2855  THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gethashes.bin");
2856  THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gethashes.bin");
2857  THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_hashes_error, get_rpc_status(res.status));
2858 
2859  blocks_start_height = res.start_height;
2860  hashes = std::move(res.m_block_ids);
2861 }
2862 
2863 //----------------------------------------------------------------------------------------------------
2864 void 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)
2865 {
2866  size_t current_index = start_height;
2867  blocks_added = 0;
2868 
2869  THROW_WALLET_EXCEPTION_IF(blocks.size() != parsed_blocks.size(), error::wallet_internal_error, "size mismatch");
2870  THROW_WALLET_EXCEPTION_IF(!m_blockchain.is_in_bounds(current_index), error::out_of_hashchain_bounds_error);
2871 
2874 
2875  //this is the total number of tx in the batch of blocks
2876  size_t num_txes = 0;
2877  std::vector<tx_cache_data> tx_cache_data;
2878  for (size_t i = 0; i < blocks.size(); ++i)
2879  num_txes += 1 + parsed_blocks[i].txes.size();
2880  tx_cache_data.resize(num_txes);
2881  size_t txidx = 0;
2882  for (size_t i = 0; i < blocks.size(); ++i)
2883  {
2884  THROW_WALLET_EXCEPTION_IF(parsed_blocks[i].txes.size() != parsed_blocks[i].block.tx_hashes.size(),
2885  error::wallet_internal_error, "Mismatched parsed_blocks[i].txes.size() and parsed_blocks[i].block.tx_hashes.size()");
2886 
2887  //ONLY CACHE TX PUBLIC KEYS R0....Rn FOR V1 TX (that's if we don't want to skip the blocks anyway)
2888  if (should_skip_block(parsed_blocks[i].block, start_height + i)){
2889  txidx += 1 + parsed_blocks[i].block.tx_hashes.size();
2890  continue;
2891  }else{
2892  if (m_refresh_type != RefreshNoCoinbase) // we're caching pubkeys only
2893  tpool.submit(&waiter, [&, i, txidx]() {
2894  cache_tx_data(parsed_blocks[i].block.miner_tx, get_transaction_hash(parsed_blocks[i].block.miner_tx),
2895  tx_cache_data[txidx]);
2896  });
2897  ++txidx;
2898  for (size_t idx = 0; idx < parsed_blocks[i].txes.size(); ++idx) {
2899  tpool.submit(&waiter, [&, i, idx, txidx]() {
2900  cache_tx_data(parsed_blocks[i].txes[idx], parsed_blocks[i].block.tx_hashes[idx], tx_cache_data[txidx]);
2901  });
2902  ++txidx;
2903  }
2904  }
2905  }
2906  THROW_WALLET_EXCEPTION_IF(txidx != num_txes, error::wallet_internal_error, "txidx does not match tx_cache_data size");
2907  waiter.wait(&tpool);
2908 
2909  hw::device &hwdev = m_account.get_device();
2910  hw::reset_mode rst(hwdev);
2912  const cryptonote::account_keys &keys = m_account.get_keys();
2913 
2914  auto gender = [&](wallet2::is_out_data &iod) {
2915  if (!hwdev.generate_key_derivation(iod.pkey, keys.m_view_secret_key, iod.derivation))
2916  {
2917  MWARNING("Failed to generate key derivation from tx pubkey, skipping");
2918  static_assert(sizeof(iod.derivation) == sizeof(rct::key), "Mismatched sizes of key_derivation and rct::key");
2919  memcpy(&iod.derivation, rct::identity().bytes, sizeof(iod.derivation));
2920  }
2921  };
2922 
2923  //call gender above in a thread
2924  for (size_t i = 0; i < tx_cache_data.size(); ++i) {
2925  if (tx_cache_data[i].empty())
2926  continue;
2927  if(!tx_cache_data[i].public_only()){ // no need to thread key derivations for public outs
2928  tpool.submit(&waiter, [&hwdev, &gender, &tx_cache_data, i]() {
2929  auto &slot = tx_cache_data[i];
2930  boost::unique_lock<hw::device> hwdev_lock(hwdev);
2931  for (auto &iod: slot.primary)
2932  gender(iod);
2933  for (auto &iod: slot.additional)
2934  gender(iod);
2935  }, true);
2936  }
2937  }
2938 
2939  waiter.wait(&tpool);
2940 
2941  auto geniod = [&](const cryptonote::transaction &tx, size_t n_vouts, size_t txidx) {
2942  for (size_t k = 0; k < n_vouts; ++k)
2943  {
2944  const auto &o = tx.vout[k];
2945  if (o.target.type() == typeid(cryptonote::txout_to_key))
2946  {
2947  std::vector<crypto::key_derivation> additional_derivations;
2948  additional_derivations.reserve(tx_cache_data[txidx].additional.size());
2949  for (const auto &iod: tx_cache_data[txidx].additional)
2950  additional_derivations.push_back(iod.derivation);
2951  const auto &key = boost::get<txout_to_key>(o.target).key;
2952  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
2953  {
2954  //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
2955  // empty rec's to tx_cache_data[txidx].primary in the tx_cache thread before (just in case they were)
2956  THROW_WALLET_EXCEPTION_IF(tx_cache_data[txidx].primary[l].received.size() != n_vouts,
2957  error::wallet_internal_error, "Unexpected received array size");
2958  // if we find that an out belongs to us, mark as received in the cache
2959  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);
2960  additional_derivations.clear();
2961  }
2962  }else if(o.target.type() == typeid(cryptonote::txout_to_key_public)){ // this is the equivalent of our ownership precomp
2963  const auto etn_address = boost::get<txout_to_key_public>(o.target).address;
2964  THROW_WALLET_EXCEPTION_IF(tx_cache_data[txidx].public_outs[0].received.size() != n_vouts,
2965  error::wallet_internal_error, "Unexpected received array size");
2966  THROW_WALLET_EXCEPTION_IF(tx_cache_data[txidx].public_outs.size() != 1,
2967  error::wallet_internal_error, "Unexpected received vector size");
2968  // no loop over l required for public outputs ^
2969 
2970  //only assign subaddress recipient if view key also matches too as we now spend with combined keys (a+b) and we wont be
2971  // doing key image related checks later to check if we can really spend the out (ie checking view key match by proxy)
2972  auto receive_info = cryptonote::is_out_to_acc_precomp_public(m_subaddresses, etn_address);
2973  tx_cache_data[txidx].public_outs[0].received[k] =
2974  (receive_info == boost::none) ?
2975  (receive_info) :
2976  get_subaddress(receive_info->index).m_view_public_key == etn_address.m_view_public_key ?
2977  receive_info : boost::none; //todo: refactor with function pointers
2978 
2979  }
2980  }
2981  };
2982 
2983  //we reset txidx from above ready to precompute ownership.
2984  txidx = 0;
2985  for (size_t i = 0; i < blocks.size(); ++i)
2986  {
2987  if (should_skip_block(parsed_blocks[i].block, start_height + i))
2988  {
2989  txidx += 1 + parsed_blocks[i].block.tx_hashes.size();
2990  continue;
2991  }
2992 
2993  //Run geniod on a thread which is a proxy for is_out_to_acc_precomp which precomputes ownership of outputs
2994  if (m_refresh_type != RefreshType::RefreshNoCoinbase)
2995  {
2996  THROW_WALLET_EXCEPTION_IF(txidx >= tx_cache_data.size(), error::wallet_internal_error, "txidx out of range");
2997  const size_t n_vouts = m_refresh_type == RefreshType::RefreshOptimizeCoinbase ? 1 : parsed_blocks[i].block.miner_tx.vout.size();
2998  tpool.submit(&waiter, [&, i, n_vouts, txidx](){ geniod(parsed_blocks[i].block.miner_tx, n_vouts, txidx); }, true);
2999  }
3000  ++txidx;
3001  for (size_t j = 0; j < parsed_blocks[i].txes.size(); ++j)
3002  {
3003  THROW_WALLET_EXCEPTION_IF(txidx >= tx_cache_data.size(), error::wallet_internal_error, "txidx out of range");
3004  tpool.submit(&waiter, [&, i, j, txidx](){ geniod(parsed_blocks[i].txes[j], parsed_blocks[i].txes[j].vout.size(), txidx); }, true);
3005  ++txidx;
3006  }
3007  }
3008  THROW_WALLET_EXCEPTION_IF(txidx != tx_cache_data.size(), error::wallet_internal_error, "txidx did not reach expected value");
3009  waiter.wait(&tpool);
3010  hwdev.set_mode(hw::device::NONE);
3011 
3012  size_t tx_cache_data_offset = 0;
3013  for (size_t i = 0; i < blocks.size(); ++i)
3014  {
3015  const crypto::hash &bl_id = parsed_blocks[i].hash;
3016  const cryptonote::block &bl = parsed_blocks[i].block;
3017 
3018  if(current_index >= m_blockchain.size())
3019  {
3020  process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset, output_tracker_cache);
3021  ++blocks_added;
3022  }
3023  else if(bl_id != m_blockchain[current_index])
3024  {
3025  //split detected here !!!
3026  THROW_WALLET_EXCEPTION_IF(current_index == start_height, error::wallet_internal_error,
3027  "wrong daemon response: split starts from the first block in response " + string_tools::pod_to_hex(bl_id) +
3028  " (height " + std::to_string(start_height) + "), local block id at this height: " +
3029  string_tools::pod_to_hex(m_blockchain[current_index]));
3030 
3031  detach_blockchain(current_index, output_tracker_cache);
3032  process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset, output_tracker_cache);
3033  }
3034  else
3035  {
3036  LOG_PRINT_L2("Block is already in blockchain: " << string_tools::pod_to_hex(bl_id));
3037  }
3038  ++current_index;
3039  tx_cache_data_offset += 1 + parsed_blocks[i].txes.size();
3040  }
3041 }
3042 //----------------------------------------------------------------------------------------------------
3043 void wallet2::refresh(bool trusted_daemon)
3044 {
3045  uint64_t blocks_fetched = 0;
3046  refresh(trusted_daemon, 0, blocks_fetched);
3047 }
3048 //----------------------------------------------------------------------------------------------------
3049 void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched)
3050 {
3051  bool received_etn = false;
3052  refresh(trusted_daemon, start_height, blocks_fetched, received_etn);
3053 }
3054 //----------------------------------------------------------------------------------------------------
3055 void 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)
3056 {
3057  error = false;
3058 
3059  try
3060  {
3061  drop_from_short_history(short_chain_history, 3);
3062 
3063  THROW_WALLET_EXCEPTION_IF(prev_blocks.size() != prev_parsed_blocks.size(), error::wallet_internal_error, "size mismatch");
3064 
3065  // prepend the last 3 blocks, should be enough to guard against a block or two's reorg
3066  auto s = std::next(prev_parsed_blocks.rbegin(), std::min((size_t)3, prev_parsed_blocks.size())).base();
3067  for (; s != prev_parsed_blocks.end(); ++s)
3068  {
3069  short_chain_history.push_front(s->hash);
3070  }
3071 
3072  // pull the new blocks
3073  std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> o_indices;
3074  pull_blocks(start_height, blocks_start_height, short_chain_history, blocks, o_indices);
3075  THROW_WALLET_EXCEPTION_IF(blocks.size() != o_indices.size(), error::wallet_internal_error, "Mismatched sizes of blocks and o_indices");
3076 
3079  parsed_blocks.resize(blocks.size());
3080  for (size_t i = 0; i < blocks.size(); ++i)
3081  {
3082  tpool.submit(&waiter, boost::bind(&wallet2::parse_block_round, this, std::cref(blocks[i].block),
3083  std::ref(parsed_blocks[i].block), std::ref(parsed_blocks[i].hash), std::ref(parsed_blocks[i].error)), true);
3084  }
3085  waiter.wait(&tpool);
3086  for (size_t i = 0; i < blocks.size(); ++i)
3087  {
3088  if (parsed_blocks[i].error)
3089  {
3090  error = true;
3091  break;
3092  }
3093  parsed_blocks[i].o_indices = std::move(o_indices[i]);
3094  }
3095 
3096  boost::mutex error_lock;
3097  for (size_t i = 0; i < blocks.size(); ++i)
3098  {
3099  parsed_blocks[i].txes.resize(blocks[i].txs.size());
3100  for (size_t j = 0; j < blocks[i].txs.size(); ++j)
3101  {
3102  tpool.submit(&waiter, [&, i, j](){
3103  if (!parse_and_validate_tx_base_from_blob(blocks[i].txs[j], parsed_blocks[i].txes[j]))
3104  {
3105  boost::unique_lock<boost::mutex> lock(error_lock);
3106  error = true;
3107  }
3108  }, true);
3109  }
3110  }
3111  waiter.wait(&tpool);
3112  }
3113  catch(...)
3114  {
3115  error = true;
3116  }
3117 }
3118 
3119 void wallet2::remove_obsolete_pool_txs(const std::vector<crypto::hash> &tx_hashes)
3120 {
3121  // remove pool txes to us that aren't in the pool anymore
3122  std::unordered_multimap<crypto::hash, wallet2::pool_payment_details>::iterator uit = m_unconfirmed_payments.begin();
3123  while (uit != m_unconfirmed_payments.end())
3124  {
3125  const crypto::hash &txid = uit->second.m_pd.m_tx_hash;
3126  bool found = false;
3127  for (const auto &it2: tx_hashes)
3128  {
3129  if (it2 == txid)
3130  {
3131  found = true;
3132  break;
3133  }
3134  }
3135  auto pit = uit++;
3136  if (!found)
3137  {
3138  MDEBUG("Removing " << txid << " from unconfirmed payments, not found in pool");
3139  m_unconfirmed_payments.erase(pit);
3140  if (0 != m_callback)
3141  m_callback->on_pool_tx_removed(txid);
3142  }
3143  }
3144 }
3145 
3146 //----------------------------------------------------------------------------------------------------
3147 void wallet2::update_pool_state(bool refreshed)
3148 {
3149  MTRACE("update_pool_state start");
3150 
3151  auto keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this]() {
3152  if (m_encrypt_keys_after_refresh)
3153  {
3154  encrypt_keys(*m_encrypt_keys_after_refresh);
3155  m_encrypt_keys_after_refresh = boost::none;
3156  }
3157  });
3158 
3159  // get the pool state
3162  m_daemon_rpc_mutex.lock();
3163  bool r = invoke_http_json("/get_transaction_pool_hashes.bin", req, res, rpc_timeout);
3164  m_daemon_rpc_mutex.unlock();
3165  THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_transaction_pool_hashes.bin");
3166  THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_transaction_pool_hashes.bin");
3168  MTRACE("update_pool_state got pool");
3169 
3170  // remove any pending tx that's not in the pool
3171  std::unordered_map<crypto::hash, wallet2::unconfirmed_transfer_details>::iterator it = m_unconfirmed_txs.begin();
3172  while (it != m_unconfirmed_txs.end())
3173  {
3174  const crypto::hash &txid = it->first;
3175  bool found = false;
3176  for (const auto &it2: res.tx_hashes)
3177  {
3178  if (it2 == txid)
3179  {
3180  found = true;
3181  break;
3182  }
3183  }
3184  auto pit = it++;
3185  if (!found)
3186  {
3187  // we want to avoid a false positive when we ask for the pool just after
3188  // a tx is removed from the pool due to being found in a new block, but
3189  // just before the block is visible by refresh. So we keep a boolean, so
3190  // that the first time we don't see the tx, we set that boolean, and only
3191  // delete it the second time it is checked (but only when refreshed, so
3192  // we're sure we've seen the blockchain state first)
3193  if (pit->second.m_state == wallet2::unconfirmed_transfer_details::pending)
3194  {
3195  LOG_PRINT_L1("Pending txid " << txid << " not in pool, marking as not in pool");
3196  pit->second.m_state = wallet2::unconfirmed_transfer_details::pending_not_in_pool;
3197  }
3198  else if (pit->second.m_state == wallet2::unconfirmed_transfer_details::pending_not_in_pool && refreshed)
3199  {
3200  LOG_PRINT_L1("Pending txid " << txid << " not in pool, marking as failed");
3201  pit->second.m_state = wallet2::unconfirmed_transfer_details::failed;
3202 
3203  // the inputs aren't spent anymore, since the tx failed
3204  remove_rings(pit->second.m_tx);
3205  for (size_t vini = 0; vini < pit->second.m_tx.vin.size(); ++vini)
3206  {
3207  if (pit->second.m_tx.vin[vini].type() == typeid(txin_to_key))
3208  {
3209  txin_to_key &tx_in_to_key = boost::get<txin_to_key>(pit->second.m_tx.vin[vini]);
3210  for (size_t i = 0; i < m_transfers.size(); ++i)
3211  {
3212  const transfer_details &td = m_transfers[i];
3213  if (td.m_key_image == tx_in_to_key.k_image)
3214  {
3215  LOG_PRINT_L1("Resetting spent status for output " << vini << ": " << td.m_key_image);
3216  set_unspent(i);
3217  break;
3218  }
3219  }
3220  }
3221  }
3222  }
3223  }
3224  }
3225  MTRACE("update_pool_state done first loop");
3226 
3227  // remove pool txes to us that aren't in the pool anymore
3228  // but only if we just refreshed, so that the tx can go in
3229  // the in transfers list instead (or nowhere if it just
3230  // disappeared without being mined)
3231  if (refreshed)
3232  remove_obsolete_pool_txs(res.tx_hashes);
3233 
3234  MTRACE("update_pool_state done second loop");
3235 
3236  // gather txids of new pool txes to us
3237  std::vector<std::pair<crypto::hash, bool>> txids;
3238  for (const auto &txid: res.tx_hashes)
3239  {
3240  bool txid_found_in_up = false;
3241  for (const auto &up: m_unconfirmed_payments)
3242  {
3243  if (up.second.m_pd.m_tx_hash == txid)
3244  {
3245  txid_found_in_up = true;
3246  break;
3247  }
3248  }
3249  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())
3250  {
3251  // if it's for us, we want to keep track of whether we saw a double spend, so don't bail out
3252  if (!txid_found_in_up)
3253  {
3254  LOG_PRINT_L2("Already seen " << txid << ", and not for us, skipped");
3255  continue;
3256  }
3257  }
3258  if (!txid_found_in_up)
3259  {
3260  LOG_PRINT_L1("Found new pool tx: " << txid);
3261  bool found = false;
3262  for (const auto &i: m_unconfirmed_txs)
3263  {
3264  if (i.first == txid)
3265  {
3266  found = true;
3267  // if this is a payment to yourself at a different subaddress account, don't skip it
3268  // so that you can see the incoming pool tx with 'show_transfers' on that receiving subaddress account
3269  const unconfirmed_transfer_details& utd = i.second;
3270  for (const auto& dst : utd.m_dests)
3271  {
3272  auto subaddr_index = m_subaddresses.find(dst.addr.m_spend_public_key);
3273  if (subaddr_index != m_subaddresses.end() && subaddr_index->second.major != utd.m_subaddr_account)
3274  {
3275  found = false;
3276  break;
3277  }
3278  }
3279  break;
3280  }
3281  }
3282  if (!found)
3283  {
3284  // not one of those we sent ourselves
3285  txids.push_back({txid, false});
3286  }
3287  else
3288  {
3289  LOG_PRINT_L1("We sent that one");
3290  }
3291  }
3292  else
3293  {
3294  LOG_PRINT_L1("Already saw that one, it's for us");
3295  txids.push_back({txid, true});
3296  }
3297  }
3298 
3299  // get those txes
3300  if (!txids.empty())
3301  {
3304  for (const auto &p: txids)
3305  req.txs_hashes.push_back(epee::string_tools::pod_to_hex(p.first));
3306  MDEBUG("asking for " << txids.size() << " transactions");
3307  req.decode_as_json = false;
3308  req.prune = true;
3309  m_daemon_rpc_mutex.lock();
3310  bool r = invoke_http_json("/gettransactions", req, res, rpc_timeout);
3311  m_daemon_rpc_mutex.unlock();
3312  MDEBUG("Got " << r << " and " << res.status);
3313  if (r && res.status == CORE_RPC_STATUS_OK)
3314  {
3315  if (res.txs.size() == txids.size())
3316  {
3317  for (const auto &tx_entry: res.txs)
3318  {
3319  if (tx_entry.in_pool)
3320  {
3323  crypto::hash tx_hash;
3324 
3325  if (get_pruned_tx(tx_entry, tx, tx_hash))
3326  {
3327  const std::vector<std::pair<crypto::hash, bool>>::const_iterator i = std::find_if(txids.begin(), txids.end(),
3328  [tx_hash](const std::pair<crypto::hash, bool> &e) { return e.first == tx_hash; });
3329  if (i != txids.end())
3330  {
3331  process_new_transaction(tx_hash, tx, std::vector<uint64_t>(), 0, time(NULL), false, true, tx_entry.double_spend_seen, false, {});
3332  m_scanned_pool_txs[0].insert(tx_hash);
3333  if (m_scanned_pool_txs[0].size() > 5000)
3334  {
3335  std::swap(m_scanned_pool_txs[0], m_scanned_pool_txs[1]);
3336  m_scanned_pool_txs[0].clear();
3337  }
3338  }
3339  else
3340  {
3341  MERROR("Got txid " << tx_hash << " which we did not ask for");
3342  }
3343  }
3344  else
3345  {
3346  LOG_PRINT_L0("Failed to parse transaction from daemon");
3347  }
3348  }
3349  else
3350  {
3351  LOG_PRINT_L1("Transaction from daemon was in pool, but is no more");
3352  }
3353  }
3354  }
3355  else
3356  {
3357  LOG_PRINT_L0("Expected " << txids.size() << " tx(es), got " << res.txs.size());
3358  }
3359  }
3360  else
3361  {
3362  LOG_PRINT_L0("Error calling gettransactions daemon RPC: r " << r << ", status " << get_rpc_status(res.status));
3363  }
3364  }
3365  MTRACE("update_pool_state end");
3366 }
3367 
3368 //----------------------------------------------------------------------------------------------------
3369 void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, bool force)
3370 {
3371  std::vector<crypto::hash> hashes;
3372 
3373  const uint64_t checkpoint_height = m_checkpoints.get_max_height();
3374  if ((stop_height > checkpoint_height && m_blockchain.size()-1 < checkpoint_height) && !force)
3375  {
3376  // we will drop all these, so don't bother getting them
3377  uint64_t missing_blocks = m_checkpoints.get_max_height() - m_blockchain.size();
3378  while (missing_blocks-- > 0)
3379  m_blockchain.push_back(crypto::null_hash); // maybe a bit suboptimal, but deque won't do huge reallocs like vector
3380  m_blockchain.push_back(m_checkpoints.get_points().at(checkpoint_height));
3381  m_blockchain.trim(checkpoint_height);
3382  short_chain_history.clear();
3383  get_short_chain_history(short_chain_history);
3384  }
3385 
3386  size_t current_index = m_blockchain.size();
3387  while(m_run.load(std::memory_order_relaxed) && current_index < stop_height)
3388  {
3389  pull_hashes(0, blocks_start_height, short_chain_history, hashes);
3390  if (hashes.size() <= 3)
3391  return;
3392  if (blocks_start_height < m_blockchain.offset())
3393  {
3394  MERROR("Blocks start before blockchain offset: " << blocks_start_height << " " << m_blockchain.offset());
3395  return;
3396  }
3397  if (hashes.size() + current_index < stop_height) {
3398  drop_from_short_history(short_chain_history, 3);
3399  std::vector<crypto::hash>::iterator right = hashes.end();
3400  // prepend 3 more
3401  for (int i = 0; i<3; i++) {
3402  right--;
3403  short_chain_history.push_front(*right);
3404  }
3405  }
3406  current_index = blocks_start_height;
3407  for(auto& bl_id: hashes)
3408  {
3409  if(current_index >= m_blockchain.size())
3410  {
3411  if (!(current_index % 1024))
3412  LOG_PRINT_L2( "Skipped block by height: " << current_index);
3413  m_blockchain.push_back(bl_id);
3414 
3415  if (0 != m_callback)
3416  { // FIXME: this isn't right, but simplewallet just logs that we got a block.
3417  cryptonote::block dummy;
3418  m_callback->on_new_block(current_index, dummy);
3419  }
3420  }
3421  else if(bl_id != m_blockchain[current_index])
3422  {
3423  //split detected here !!!
3424  return;
3425  }
3426  ++current_index;
3427  if (current_index >= stop_height)
3428  return;
3429  }
3430  }
3431 }
3432 
3433 
3434 bool wallet2::add_address_book_row(const cryptonote::account_public_address &address, const crypto::hash &payment_id, const std::string &description, bool is_subaddress)
3435 {
3437  a.m_address = address;
3438  a.m_payment_id = payment_id;
3439  a.m_description = description;
3440  a.m_is_subaddress = is_subaddress;
3441 
3442  auto old_size = m_address_book.size();
3443  m_address_book.push_back(a);
3444  if(m_address_book.size() == old_size+1)
3445  return true;
3446  return false;
3447 }
3448 
3449 bool wallet2::delete_address_book_row(std::size_t row_id) {
3450  if(m_address_book.size() <= row_id)
3451  return false;
3452 
3453  m_address_book.erase(m_address_book.begin()+row_id);
3454 
3455  return true;
3456 }
3457 
3458 //----------------------------------------------------------------------------------------------------
3459 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>>> wallet2::create_output_tracker_cache() const
3460 { // output tracker cache at the pointed-to address is a map where the key is a pair of <output amount, global out index>
3461  // 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)
3462  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>>()};
3463  for (size_t i = 0; i < m_transfers.size(); ++i)
3464  {
3465  const transfer_details &td = m_transfers[i];
3466  //amount, global out index
3467  (*cache).first[std::make_pair(td.is_rct() ? 0 : td.amount(), td.m_global_output_index)] = i;
3468  //txid, relative out index
3469  std::array<char, 32> transaction_id;
3470  std::copy(std::begin(td.m_txid.data), std::end(td.m_txid.data), transaction_id.begin());
3471  (*cache).second[std::make_pair(transaction_id, td.m_internal_output_index)] = i;
3472  }
3473  return cache;
3474 }
3475 //----------------------------------------------------------------------------------------------------
3476 void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched, bool& received_etn, bool check_pool)
3477 {
3478  if (m_offline)
3479  {
3480  blocks_fetched = 0;
3481  received_etn = 0;
3482  return;
3483  }
3484 
3485  if(m_light_wallet) {
3486 
3487  // MyMonero get_address_info needs to be called occasionally to trigger wallet sync.
3488  // This call is not really needed for other purposes and can be removed if mymonero changes their backend.
3490 
3491  // Get basic info
3492  if(light_wallet_get_address_info(res)) {
3493  // Last stored block height
3494  uint64_t prev_height = m_light_wallet_blockchain_height;
3495  // Update lw heights
3496  m_light_wallet_scanned_block_height = res.scanned_block_height;
3497  m_light_wallet_blockchain_height = res.blockchain_height;
3498  // If new height - call new_block callback
3499  if(m_light_wallet_blockchain_height != prev_height)
3500  {
3501  MDEBUG("new block since last time!");
3502  m_callback->on_lw_new_block(m_light_wallet_blockchain_height - 1);
3503  }
3504  m_light_wallet_connected = true;
3505  MDEBUG("lw scanned block height: " << m_light_wallet_scanned_block_height);
3506  MDEBUG("lw blockchain height: " << m_light_wallet_blockchain_height);
3507  MDEBUG(m_light_wallet_blockchain_height-m_light_wallet_scanned_block_height << " blocks behind");
3508  // TODO: add wallet created block info
3509 
3510  light_wallet_get_address_txs();
3511  } else
3512  m_light_wallet_connected = false;
3513 
3514  // Lighwallet refresh done
3515  return;
3516  }
3517  received_etn = false;
3518  blocks_fetched = 0;
3519  uint64_t added_blocks = 0;
3520  size_t try_count = 0;
3521  crypto::hash last_tx_hash_id = m_transfers.size() ? m_transfers.back().m_txid : null_hash;
3522  std::list<crypto::hash> short_chain_history;
3525  uint64_t blocks_start_height;
3526  std::vector<cryptonote::block_complete_entry> blocks;
3527  std::vector<parsed_block> parsed_blocks;
3528  bool refreshed = false;
3529  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
3530  hw::device &hwdev = m_account.get_device();
3531 
3532  // pull the first set of blocks
3533  get_short_chain_history(short_chain_history, (m_first_refresh_done || trusted_daemon) ? 1 : FIRST_REFRESH_GRANULARITY);
3534  m_run.store(true, std::memory_order_relaxed);
3535  if (start_height > m_blockchain.size() || m_refresh_from_block_height > m_blockchain.size()) {
3536  if (!start_height)
3537  start_height = m_refresh_from_block_height;
3538  // we can shortcut by only pulling hashes up to the start_height
3539  fast_refresh(start_height, blocks_start_height, short_chain_history);
3540  // regenerate the history now that we've got a full set of hashes
3541  short_chain_history.clear();
3542  get_short_chain_history(short_chain_history, (m_first_refresh_done || trusted_daemon) ? 1 : FIRST_REFRESH_GRANULARITY);
3543  start_height = 0;
3544  // and then fall through to regular refresh processing
3545  }
3546 
3547  // If stop() is called during fast refresh we don't need to continue
3548  if(!m_run.load(std::memory_order_relaxed))
3549  return;
3550  // always reset start_height to 0 to force short_chain_ history to be used on
3551  // subsequent pulls in this refresh.
3552  start_height = 0;
3553 
3554  auto keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this]() {
3555  if (m_encrypt_keys_after_refresh)
3556  {
3557  encrypt_keys(*m_encrypt_keys_after_refresh);
3558  m_encrypt_keys_after_refresh = boost::none;
3559  }
3560  });
3561 
3562  auto scope_exit_handler_hwdev = epee::misc_utils::create_scope_leave_handler([&](){hwdev.computing_key_images(false);});
3563  bool first = true;
3564  while(m_run.load(std::memory_order_relaxed))
3565  {
3566  uint64_t next_blocks_start_height;
3567  std::vector<cryptonote::block_complete_entry> next_blocks;
3568  std::vector<parsed_block> next_parsed_blocks;
3569  bool error;
3570  try
3571  {
3572  // pull the next set of blocks while we're processing the current one
3573  error = false;
3574  next_blocks.clear();
3575  next_parsed_blocks.clear();
3576  added_blocks = 0;
3577  if (!first && blocks.empty())
3578  {
3579  refreshed = false;
3580  break;
3581  }
3582  tpool.submit(&waiter, [&]{pull_and_parse_next_blocks(start_height, next_blocks_start_height, short_chain_history, blocks, parsed_blocks, next_blocks, next_parsed_blocks, error);});
3583 
3584  if (!first)
3585  {
3586  try
3587  {
3588  process_parsed_blocks(blocks_start_height, blocks, parsed_blocks, added_blocks, output_tracker_cache.get());
3589  }
3591  {
3592  MINFO("Daemon claims next refresh block is out of hash chain bounds, resetting hash chain");
3593  uint64_t stop_height = m_blockchain.offset();
3594  std::vector<crypto::hash> tip(m_blockchain.size() - m_blockchain.offset());
3595  for (size_t i = m_blockchain.offset(); i < m_blockchain.size(); ++i)
3596  tip[i - m_blockchain.offset()] = m_blockchain[i];
3598  generate_genesis(b);
3599  m_blockchain.clear();
3600  m_blockchain.push_back(get_block_hash(b));
3601  short_chain_history.clear();
3602  get_short_chain_history(short_chain_history);
3603  fast_refresh(stop_height, blocks_start_height, short_chain_history, true);
3604  THROW_WALLET_EXCEPTION_IF((m_blockchain.size() == stop_height || (m_blockchain.size() == 1 && stop_height == 0) ? false : true), error::wallet_internal_error, "Unexpected hashchain size");
3605  THROW_WALLET_EXCEPTION_IF(m_blockchain.offset() != 0, error::wallet_internal_error, "Unexpected hashchain offset");
3606  for (const auto &h: tip)
3607  m_blockchain.push_back(h);
3608  short_chain_history.clear();
3609  get_short_chain_history(short_chain_history);
3610  start_height = stop_height;
3611  throw std::runtime_error(""); // loop again
3612  }
3613  catch (const std::exception &e)
3614  {
3615  MERROR("Error parsing blocks: " << e.what());
3616  error = true;
3617  }
3618  blocks_fetched += added_blocks;
3619  }
3620  waiter.wait(&tpool);
3621  if(!first && blocks_start_height == next_blocks_start_height)
3622  {
3623  m_node_rpc_proxy.set_height(m_blockchain.size());
3624  refreshed = true;
3625  break;
3626  }
3627 
3628  first = false;
3629 
3630  // handle error from async fetching thread
3631  if (error)
3632  {
3633  throw std::runtime_error("proxy exception in refresh thread");
3634  }
3635 
3636  // if we've got at least 10 blocks to refresh, assume we're starting
3637  // a long refresh, and setup a tracking output cache if we need to
3638  // We hit create_output_tracker_cache before doing processing our blocks in process_parsed_blocks above( see 'first' variable)
3639  if (m_track_uses && (!output_tracker_cache || (output_tracker_cache->first.empty() && output_tracker_cache->second.empty())) && next_blocks.size() >= 10)
3640  output_tracker_cache = create_output_tracker_cache();
3641 
3642  // switch to the new blocks from the daemon
3643  blocks_start_height = next_blocks_start_height;
3644  blocks = std::move(next_blocks);
3645  parsed_blocks = std::move(next_parsed_blocks);
3646  }
3647  catch (const tools::error::password_needed&)
3648  {
3649  blocks_fetched += added_blocks;
3650  waiter.wait(&tpool);
3651  throw;
3652  }
3653  catch (const std::exception&)
3654  {
3655  blocks_fetched += added_blocks;
3656  waiter.wait(&tpool);
3657  if(try_count < 3)
3658  {
3659  LOG_PRINT_L1("Another try pull_blocks (try_count=" << try_count << ")...");
3660  first = true;
3661  start_height = 0;
3662  blocks.clear();
3663  parsed_blocks.clear();
3664  short_chain_history.clear();
3665  get_short_chain_history(short_chain_history, 1);
3666  ++try_count;
3667  }
3668  else
3669  {
3670  LOG_ERROR("pull_blocks failed, try_count=" << try_count);
3671  throw;
3672  }
3673  }
3674  }
3675  if(last_tx_hash_id != (m_transfers.size() ? m_transfers.back().m_txid : null_hash))
3676  received_etn = true;
3677 
3678  try
3679  {
3680  // If stop() is called we don't need to check pending transactions
3681  if (check_pool && m_run.load(std::memory_order_relaxed))
3682  update_pool_state(refreshed);
3683  }
3684  catch (...)
3685  {
3686  LOG_PRINT_L1("Failed to check pending transactions");
3687  }
3688 
3689  m_first_refresh_done = true;
3690 
3691  LOG_PRINT_L1("Refresh done, blocks received: " << blocks_fetched << ", pre v10 balance (all accounts): " << print_etn(balance_all(false)) << ", unlocked: " << print_etn(unlocked_balance_all(false)) << ", post v10 balance (all accounts): " << print_etn(balance_all(true)) << ", unlocked: " << print_etn(unlocked_balance_all(true)));
3692 
3693  try {
3694  // V9-->V10 PUBLIC MIGRATIONS
3695  // 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)
3696  //todo: write function for wallet that gets the b.major version for a given *local* blockchain height, to save hardcoding heights.
3697  uint64_t migration_minheight = this->nettype() == TESTNET ? 1165235 + 5 : 1175315 + 5;
3698  if (this->get_blockchain_current_height() > migration_minheight && this->unlocked_balance_all(false) != 0) {
3699  LOG_PRINT_L0("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.");
3700  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))
3701  // for each account, grab all of the subaddress info (index, (balance, unlock))
3702  for (uint32_t account_index = 0; account_index < this->get_num_subaddress_accounts(); ++account_index) {
3703  unlocked_balance_per_subaddress_per_account[account_index] = this->unlocked_balance_per_subaddress(
3704  account_index, false);
3705  }
3706  for (uint32_t i = 0; i < this->get_num_subaddress_accounts(); i++) {
3708  index.major = i;
3709  for (auto subaddress: unlocked_balance_per_subaddress_per_account[i]) {
3710  index.minor = subaddress.first;
3711 
3712  if (subaddress.second.first != 0 &&
3713  subaddress.second.second == 0/*is there a fully unlocked nonzero balance /sanity check*/) {
3714  cryptonote::account_public_address address = get_subaddress(index);
3715  std::set <uint32_t> subaddress_source{index.minor};
3716  std::vector <wallet2::pending_tx> ptx_vector = this->create_transactions_all(0,
3717  address /*dest address*/,
3718  index.major != 0 ||
3719  index.minor !=
3720  0 /*is dest a subaddress*/,
3721  1 /*one output only*/,
3722  0 /* don't mix*/,
3723  0 /*default unlock time*/,
3724  4 /*highest priority*/,
3725  vector<uint8_t>() /*empty tx extra */,
3726  index.major /*account index*/,
3727  subaddress_source /*source subaddr index*/,
3728  true /*migrate*/);
3729  this->commit_tx(ptx_vector);
3730  }
3731  }
3732  }
3733  LOG_PRINT_L0("Migration completed.");
3734  }
3735  }
3736  catch(...){
3737  THROW_WALLET_EXCEPTION(error::wallet_internal_error, "Overall migration failed but some balances may have migrated ok. Please restart the wallet and try again and contact Electroneum if the issue persists.");
3738  }
3739 }
3740 //----------------------------------------------------------------------------------------------------
3741 bool wallet2::refresh(bool trusted_daemon, uint64_t & blocks_fetched, bool& received_etn, bool& ok)
3742 {
3743  try
3744  {
3745  refresh(trusted_daemon, 0, blocks_fetched, received_etn);
3746  ok = true;
3747  }
3748  catch (...)
3749  {
3750  ok = false;
3751  }
3752  return ok;
3753 }
3754 //----------------------------------------------------------------------------------------------------
3755 bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector<uint64_t> &distribution)
3756 {
3757  uint32_t rpc_version;
3758  boost::optional<std::string> result = m_node_rpc_proxy.get_rpc_version(rpc_version);
3759  // no error
3760  if (!!result)
3761  {
3762  // empty string -> not connection
3763  THROW_WALLET_EXCEPTION_IF(result->empty(), tools::error::no_connection_to_daemon, "getversion");
3765  if (*result != CORE_RPC_STATUS_OK)
3766  {
3767  MDEBUG("Cannot determine daemon RPC version, not requesting rct distribution");
3768  return false;
3769  }
3770  }
3771  else
3772  {
3773  if (rpc_version >= MAKE_CORE_RPC_VERSION(1, 19))
3774  {
3775  MDEBUG("Daemon is recent enough, requesting rct distribution");
3776  }
3777  else
3778  {
3779  MDEBUG("Daemon is too old, not requesting rct distribution");
3780  return false;
3781  }
3782  }
3783 
3786  req.amounts.push_back(0);
3787  req.from_height = 0;
3788  req.cumulative = false;
3789  req.binary = true;
3790  req.compress = true;
3791  m_daemon_rpc_mutex.lock();
3792  bool r = invoke_http_bin("/get_output_distribution.bin", req, res, rpc_timeout);
3793  m_daemon_rpc_mutex.unlock();
3794  if (!r)
3795  {
3796  MWARNING("Failed to request output distribution: no connection to daemon");
3797  return false;
3798  }
3799  if (res.status == CORE_RPC_STATUS_BUSY)
3800  {
3801  MWARNING("Failed to request output distribution: daemon is busy");
3802  return false;
3803  }
3804  if (res.status != CORE_RPC_STATUS_OK)
3805  {
3806  MWARNING("Failed to request output distribution: " << res.status);
3807  return false;
3808  }
3809  if (res.distributions.size() != 1)
3810  {
3811  MWARNING("Failed to request output distribution: not the expected single result");
3812  return false;
3813  }
3814  if (res.distributions[0].amount != 0)
3815  {
3816  MWARNING("Failed to request output distribution: results are not for amount 0");
3817  return false;
3818  }
3819  for (size_t i = 1; i < res.distributions[0].data.distribution.size(); ++i)
3820  res.distributions[0].data.distribution[i] += res.distributions[0].data.distribution[i-1];
3821  start_height = res.distributions[0].data.start_height;
3822  distribution = std::move(res.distributions[0].data.distribution);
3823  return true;
3824 }
3825 //----------------------------------------------------------------------------------------------------
3826 void 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)
3827 {
3828  LOG_PRINT_L0("Detaching blockchain on height " << height);
3829 
3830  // size 1 2 3 4 5 6 7 8 9
3831  // block 0 1 2 3 4 5 6 7 8
3832  // C
3833  THROW_WALLET_EXCEPTION_IF(height < m_blockchain.offset() && m_blockchain.size() > m_blockchain.offset(),
3834  error::wallet_internal_error, "Daemon claims reorg below last checkpoint");
3835 
3836  size_t transfers_detached = 0;
3837 
3838  for (size_t i = 0; i < m_transfers.size(); ++i)
3839  {
3840  wallet2::transfer_details &td = m_transfers[i];
3841  if (td.m_spent && td.m_spent_height >= height)
3842  {
3843  if(td.m_tx.version == 1){ // we're resetting chainstate indexes for ver > 1
3844  LOG_PRINT_L1("Resetting spent/frozen status for output " << i << ": " << td.m_key_image);
3845  }else{
3846  LOG_PRINT_L1("Resetting spent/frozen status for output "
3847  << i << ": " << "chainstate index " << td.m_txid <<": " << td.m_internal_output_index);
3848  }
3849  set_unspent(i);
3850  thaw(i);
3851  }
3852  }
3853 
3854  for (transfer_details &td: m_transfers)
3855  {
3856  while (!td.m_uses.empty() && td.m_uses.back().first >= height)
3857  td.m_uses.pop_back();
3858  }
3859 
3860  if (output_tracker_cache) {
3861  output_tracker_cache->first.clear();
3862  output_tracker_cache->second.clear();
3863  }
3864 
3865  auto it = std::find_if(m_transfers.begin(), m_transfers.end(), [&](const transfer_details& td){return td.m_block_height >= height;});
3866  size_t i_start = it - m_transfers.begin();
3867 
3868  for(size_t i = i_start; i!= m_transfers.size();i++)
3869  {
3870  if (!m_transfers[i].m_key_image_known || m_transfers[i].m_key_image_partial)
3871  continue;
3872  auto it_ki = m_key_images.find(m_transfers[i].m_key_image);
3873  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");
3874  m_key_images.erase(it_ki);
3875  }
3876 
3877  for(size_t i = i_start; i!= m_transfers.size();i++)
3878  {
3879  auto it_pk = m_pub_keys.find(m_transfers[i].get_public_key());
3880  THROW_WALLET_EXCEPTION_IF(it_pk == m_pub_keys.end(), error::wallet_internal_error, "public key not found");
3881  m_pub_keys.erase(it_pk);
3882  }
3883 
3884  for(size_t i = i_start; i!= m_transfers.size();i++)
3885  {
3886  auto it_pk = m_chainstate_indexes.find(m_transfers[i].get_chainstate_index());
3887  if(m_transfers[i].m_tx.version > 1) {
3888  THROW_WALLET_EXCEPTION_IF(it_pk == m_chainstate_indexes.end(), error::wallet_internal_error,
3889  "chainstate index not found");
3890  m_chainstate_indexes.erase(it_pk);
3891  }else{
3892  continue;
3893  }
3894  }
3895 
3896 
3897  m_transfers.erase(it, m_transfers.end());
3898 
3899  size_t blocks_detached = m_blockchain.size() - height;
3900  m_blockchain.crop(height);
3901 
3902  for (auto it = m_payments.begin(); it != m_payments.end(); )
3903  {
3904  if(height <= it->second.m_block_height)
3905  it = m_payments.erase(it);
3906  else
3907  ++it;
3908  }
3909 
3910  for (auto it = m_confirmed_txs.begin(); it != m_confirmed_txs.end(); )
3911  {
3912  if(height <= it->second.m_block_height)
3913  it = m_confirmed_txs.erase(it);
3914  else
3915  ++it;
3916  }
3917 
3918  LOG_PRINT_L0("Detached blockchain on height " << height << ", transfers detached " << transfers_detached << ", blocks detached " << blocks_detached);
3919 }
3920 //----------------------------------------------------------------------------------------------------
3922 {
3923  m_is_initialized=false;
3924  unlock_keys_file();
3925  m_account.deinit();
3926  return true;
3927 }
3928 //----------------------------------------------------------------------------------------------------
3929 bool wallet2::clear()
3930 {
3931  m_blockchain.clear();
3932  m_transfers.clear();
3933  m_key_images.clear();
3934  m_pub_keys.clear();
3935  m_chainstate_indexes.clear();
3936  m_unconfirmed_txs.clear();
3937  m_payments.clear();
3938  m_tx_keys.clear();
3939  m_additional_tx_keys.clear();
3940  m_confirmed_txs.clear();
3941  m_unconfirmed_payments.clear();
3942  m_scanned_pool_txs[0].clear();
3943  m_scanned_pool_txs[1].clear();
3944  m_address_book.clear();
3945  m_subaddresses.clear();
3946  m_subaddress_labels.clear();
3947  m_multisig_rounds_passed = 0;
3948  m_device_last_key_image_sync = 0;
3949  return true;
3950 }
3951 //----------------------------------------------------------------------------------------------------
3952 void wallet2::clear_soft(bool keep_key_images)
3953 {
3954  m_blockchain.clear();
3955  m_transfers.clear();
3956  if (!keep_key_images)
3957  m_key_images.clear();
3958  m_pub_keys.clear();
3959  m_chainstate_indexes.clear();
3960  m_unconfirmed_txs.clear();
3961  m_payments.clear();
3962  m_confirmed_txs.clear();
3963  m_unconfirmed_payments.clear();
3964  m_scanned_pool_txs[0].clear();
3965  m_scanned_pool_txs[1].clear();
3966 
3968  generate_genesis(b);
3969  m_blockchain.push_back(get_block_hash(b));
3970  m_last_block_reward = cryptonote::get_outs_etn_amount(b.miner_tx);
3971 }
3972 
3980 bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable_string& password, bool watch_only)
3981 {
3982  std::string account_data;
3983  std::string multisig_signers;
3984  std::string multisig_derivations;
3985  cryptonote::account_base account = m_account;
3986 
3987  crypto::chacha_key key;
3988  crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
3989 
3990  if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
3991  {
3992  account.encrypt_viewkey(key);
3993  account.decrypt_keys(key);
3994  }
3995 
3996  if (watch_only)
3997  account.forget_spend_key();
3998 
3999  account.encrypt_keys(key);
4000 
4001  bool r = epee::serialization::store_t_to_binary(account, account_data);
4002  CHECK_AND_ASSERT_MES(r, false, "failed to serialize wallet keys");
4003  wallet2::keys_file_data keys_file_data = boost::value_initialized<wallet2::keys_file_data>();
4004 
4005  // Create a JSON object with "key_data" and "seed_language" as keys.
4007  json.SetObject();
4009  value.SetString(account_data.c_str(), account_data.length());
4010  json.AddMember("key_data", value, json.GetAllocator());
4011  if (!seed_language.empty())
4012  {
4013  value.SetString(seed_language.c_str(), seed_language.length());
4014  json.AddMember("seed_language", value, json.GetAllocator());
4015  }
4016 
4018 
4019  value2.SetInt(m_key_device_type);
4020  json.AddMember("key_on_device", value2, json.GetAllocator());
4021 
4022  value2.SetInt(watch_only ? 1 :0); // WTF ? JSON has different true and false types, and not boolean ??
4023  json.AddMember("watch_only", value2, json.GetAllocator());
4024 
4025  value2.SetInt(m_multisig ? 1 :0);
4026  json.AddMember("multisig", value2, json.GetAllocator());
4027 
4028  value2.SetUint(m_multisig_threshold);
4029  json.AddMember("multisig_threshold", value2, json.GetAllocator());
4030 
4031  if (m_multisig)
4032  {
4033  bool r = ::serialization::dump_binary(m_multisig_signers, multisig_signers);
4034  CHECK_AND_ASSERT_MES(r, false, "failed to serialize wallet multisig signers");
4035  value.SetString(multisig_signers.c_str(), multisig_signers.length());
4036  json.AddMember("multisig_signers", value, json.GetAllocator());
4037 
4038  r = ::serialization::dump_binary(m_multisig_derivations, multisig_derivations);
4039  CHECK_AND_ASSERT_MES(r, false, "failed to serialize wallet multisig derivations");
4040  value.SetString(multisig_derivations.c_str(), multisig_derivations.length());
4041  json.AddMember("multisig_derivations", value, json.GetAllocator());
4042 
4043  value2.SetUint(m_multisig_rounds_passed);
4044  json.AddMember("multisig_rounds_passed", value2, json.GetAllocator());
4045  }
4046 
4047  value2.SetInt(m_always_confirm_transfers ? 1 :0);
4048  json.AddMember("always_confirm_transfers", value2, json.GetAllocator());
4049 
4050  value2.SetInt(m_print_ring_members ? 1 :0);
4051  json.AddMember("print_ring_members", value2, json.GetAllocator());
4052 
4053  value2.SetInt(m_store_tx_info ? 1 :0);
4054  json.AddMember("store_tx_info", value2, json.GetAllocator());
4055 
4056  value2.SetUint(m_default_mixin);
4057  json.AddMember("default_mixin", value2, json.GetAllocator());
4058 
4059  value2.SetUint(m_default_priority);
4060  json.AddMember("default_priority", value2, json.GetAllocator());
4061 
4062  value2.SetInt(m_auto_refresh ? 1 :0);
4063  json.AddMember("auto_refresh", value2, json.GetAllocator());
4064 
4065  value2.SetInt(m_refresh_type);
4066  json.AddMember("refresh_type", value2, json.GetAllocator());
4067 
4068  value2.SetUint64(m_refresh_from_block_height);
4069  json.AddMember("refresh_height", value2, json.GetAllocator());
4070 
4071  value2.SetInt(m_confirm_missing_payment_id ? 1 :0);
4072  json.AddMember("confirm_missing_payment_id", value2, json.GetAllocator());
4073 
4074  value2.SetInt(m_confirm_non_default_ring_size ? 1 :0);
4075  json.AddMember("confirm_non_default_ring_size", value2, json.GetAllocator());
4076 
4077  value2.SetInt(m_ask_password);
4078  json.AddMember("ask_password", value2, json.GetAllocator());
4079 
4080  value2.SetUint(m_min_output_count);
4081  json.AddMember("min_output_count", value2, json.GetAllocator());
4082 
4083  value2.SetUint64(m_min_output_value);
4084  json.AddMember("min_output_value", value2, json.GetAllocator());
4085 
4086  value2.SetInt(cryptonote::get_default_decimal_point());
4087  json.AddMember("default_decimal_point", value2, json.GetAllocator());
4088 
4089  value2.SetInt(m_merge_destinations ? 1 :0);
4090  json.AddMember("merge_destinations", value2, json.GetAllocator());
4091 
4092  value2.SetInt(m_confirm_backlog ? 1 :0);
4093  json.AddMember("confirm_backlog", value2, json.GetAllocator());
4094 
4095  value2.SetUint(m_confirm_backlog_threshold);
4096  json.AddMember("confirm_backlog_threshold", value2, json.GetAllocator());
4097 
4098  value2.SetInt(m_confirm_export_overwrite ? 1 :0);
4099  json.AddMember("confirm_export_overwrite", value2, json.GetAllocator());
4100 
4101  value2.SetInt(m_auto_low_priority ? 1 : 0);
4102  json.AddMember("auto_low_priority", value2, json.GetAllocator());
4103 
4104  value2.SetUint(m_nettype);
4105  json.AddMember("nettype", value2, json.GetAllocator());
4106 
4107  value2.SetInt(m_segregate_pre_fork_outputs ? 1 : 0);
4108  json.AddMember("segregate_pre_fork_outputs", value2, json.GetAllocator());
4109 
4110  value2.SetInt(m_key_reuse_mitigation2 ? 1 : 0);
4111  json.AddMember("key_reuse_mitigation2", value2, json.GetAllocator());
4112 
4113  value2.SetUint(m_segregation_height);
4114  json.AddMember("segregation_height", value2, json.GetAllocator());
4115 
4116  value2.SetInt(m_ignore_fractional_outputs ? 1 : 0);
4117  json.AddMember("ignore_fractional_outputs", value2, json.GetAllocator());
4118 
4119  value2.SetInt(m_track_uses ? 1 : 0);
4120  json.AddMember("track_uses", value2, json.GetAllocator());
4121 
4122  value2.SetInt(m_setup_background_mining);
4123  json.AddMember("setup_background_mining", value2, json.GetAllocator());
4124 
4125  value2.SetUint(m_subaddress_lookahead_major);
4126  json.AddMember("subaddress_lookahead_major", value2, json.GetAllocator());
4127 
4128  value2.SetUint(m_subaddress_lookahead_minor);
4129  json.AddMember("subaddress_lookahead_minor", value2, json.GetAllocator());
4130 
4131  value2.SetInt(m_original_keys_available ? 1 : 0);
4132  json.AddMember("original_keys_available", value2, json.GetAllocator());
4133 
4134  value2.SetUint(1);
4135  json.AddMember("encrypted_secret_keys", value2, json.GetAllocator());
4136 
4137  value.SetString(m_device_name.c_str(), m_device_name.size());
4138  json.AddMember("device_name", value, json.GetAllocator());
4139 
4140  value.SetString(m_device_derivation_path.c_str(), m_device_derivation_path.size());
4141  json.AddMember("device_derivation_path", value, json.GetAllocator());
4142 
4143  value2.SetUint(m_account_major_offset);
4144  json.AddMember("account_major_offset", value2, json.GetAllocator());
4145 
4146  std::string original_address;
4147  std::string original_view_secret_key;
4148  if (m_original_keys_available)
4149  {
4150  original_address = get_account_address_as_str(m_nettype, false, m_original_address);
4151  value.SetString(original_address.c_str(), original_address.length());
4152  json.AddMember("original_address", value, json.GetAllocator());
4153  original_view_secret_key = epee::string_tools::pod_to_hex(m_original_view_secret_key);
4154  value.SetString(original_view_secret_key.c_str(), original_view_secret_key.length());
4155  json.AddMember("original_view_secret_key", value, json.GetAllocator());
4156  }
4157 
4158  // Serialize the JSON object
4159  rapidjson::StringBuffer buffer;
4160  rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
4161  json.Accept(writer);
4162  account_data = buffer.GetString();
4163 
4164  // Encrypt the entire JSON object.
4165  std::string cipher;
4166  cipher.resize(account_data.size());
4167  keys_file_data.iv = crypto::rand<crypto::chacha_iv>();
4168  crypto::chacha20(account_data.data(), account_data.size(), key, keys_file_data.iv, &cipher[0]);
4169  keys_file_data.account_data = cipher;
4170 
4171  std::string tmp_file_name = keys_file_name + ".new";
4172  std::string buf;
4173  r = ::serialization::dump_binary(keys_file_data, buf);
4174  r = r && epee::file_io_utils::save_string_to_file(tmp_file_name, buf);
4175  CHECK_AND_ASSERT_MES(r, false, "failed to generate wallet keys file " << tmp_file_name);
4176 
4177  unlock_keys_file();
4178  std::error_code e = tools::replace_file(tmp_file_name, keys_file_name);
4179  lock_keys_file();
4180 
4181  if (e) {
4182  boost::filesystem::remove(tmp_file_name);
4183  LOG_ERROR("failed to update wallet keys file " << keys_file_name);
4184  return false;
4185  }
4186 
4187  return true;
4188 }
4189 //----------------------------------------------------------------------------------------------------
4190 void wallet2::setup_keys(const epee::wipeable_string &password)
4191 {
4192  crypto::chacha_key key;
4193  crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
4194 
4195  // re-encrypt, but keep viewkey unencrypted
4196  if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
4197  {
4198  m_account.encrypt_keys(key);
4199  m_account.decrypt_viewkey(key);
4200  }
4201 
4202  static_assert(HASH_SIZE == sizeof(crypto::chacha_key), "Mismatched sizes of hash and chacha key");
4204  memcpy(cache_key_data.data(), &key, HASH_SIZE);
4205  cache_key_data[HASH_SIZE] = CACHE_KEY_TAIL;
4206  cn_fast_hash(cache_key_data.data(), HASH_SIZE+1, (crypto::hash&)m_cache_key);
4207  get_ringdb_key();
4208 }
4209 //----------------------------------------------------------------------------------------------------
4210 void wallet2::change_password(const std::string &filename, const epee::wipeable_string &original_password, const epee::wipeable_string &new_password)
4211 {
4212  if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
4213  decrypt_keys(original_password);
4214  setup_keys(new_password);
4215  rewrite(filename, new_password);
4216  if (!filename.empty())
4217  store();
4218 }
4219 //----------------------------------------------------------------------------------------------------
4225 bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_string& password)
4226 {
4228  wallet2::keys_file_data keys_file_data;
4229  std::string buf;
4230  bool encrypted_secret_keys = false;
4231  bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf);
4233 
4234  // Decrypt the contents
4235  r = ::serialization::parse_binary(buf, keys_file_data);
4236  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + keys_file_name + '\"');
4237  crypto::chacha_key key;
4238  crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
4239  std::string account_data;
4240  account_data.resize(keys_file_data.account_data.size());
4241  crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
4242  if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject())
4243  crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
4244 
4245  // The contents should be JSON if the wallet follows the new format.
4246  if (json.Parse(account_data.c_str()).HasParseError())
4247  {
4248  is_old_file_format = true;
4249  m_watch_only = false;
4250  m_multisig = false;
4251  m_multisig_threshold = 0;
4252  m_multisig_signers.clear();
4253  m_multisig_rounds_passed = 0;
4254  m_multisig_derivations.clear();
4255  m_always_confirm_transfers = true;
4256  m_print_ring_members = false;
4257  m_store_tx_info = true;
4258  m_default_mixin = 0;
4259  m_default_priority = 0;
4260  m_auto_refresh = true;
4261  m_refresh_type = RefreshType::RefreshDefault;
4262  m_refresh_from_block_height = 0;
4263  m_confirm_missing_payment_id = true;
4264  m_confirm_non_default_ring_size = true;
4265  m_ask_password = AskPasswordOnAction;
4267  m_min_output_count = 0;
4268  m_min_output_value = 0;
4269  m_merge_destinations = false;
4270  m_confirm_backlog = true;
4271  m_confirm_backlog_threshold = 0;
4272  m_confirm_export_overwrite = true;
4273  m_auto_low_priority = true;
4274  m_segregate_pre_fork_outputs = true;
4275  m_key_reuse_mitigation2 = true;
4276  m_segregation_height = 0;
4277  m_ignore_fractional_outputs = true;
4278  m_track_uses = false;
4279  m_setup_background_mining = BackgroundMiningMaybe;
4280  m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR;
4281  m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR;
4282  m_original_keys_available = false;
4283  m_device_name = "";
4284  m_device_derivation_path = "";
4285  m_key_device_type = hw::device::device_type::SOFTWARE;
4286  m_account_major_offset = 0;
4287  encrypted_secret_keys = false;
4288  }
4289  else if(json.IsObject())
4290  {
4291  if (!json.HasMember("key_data"))
4292  {
4293  LOG_ERROR("Field key_data not found in JSON");
4294  return false;
4295  }
4296  if (!json["key_data"].IsString())
4297  {
4298  LOG_ERROR("Field key_data found in JSON, but not String");
4299  return false;
4300  }
4301  const char *field_key_data = json["key_data"].GetString();
4302  account_data = std::string(field_key_data, field_key_data + json["key_data"].GetStringLength());
4303 
4304  if (json.HasMember("key_on_device"))
4305  {
4306  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, key_on_device, int, Int, false, hw::device::device_type::SOFTWARE);
4307  m_key_device_type = static_cast<hw::device::device_type>(field_key_on_device);
4308  }
4309 
4310  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, seed_language, std::string, String, false, std::string());
4311  if (field_seed_language_found)
4312  {
4313  set_seed_language(field_seed_language);
4314  }
4315  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, watch_only, int, Int, false, false);
4316  m_watch_only = field_watch_only;
4317  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, multisig, int, Int, false, false);
4318  m_multisig = field_multisig;
4319  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, multisig_threshold, unsigned int, Uint, m_multisig, 0);
4320  m_multisig_threshold = field_multisig_threshold;
4321  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, multisig_rounds_passed, unsigned int, Uint, false, 0);
4322  m_multisig_rounds_passed = field_multisig_rounds_passed;
4323  if (m_multisig)
4324  {
4325  if (!json.HasMember("multisig_signers"))
4326  {
4327  LOG_ERROR("Field multisig_signers not found in JSON");
4328  return false;
4329  }
4330  if (!json["multisig_signers"].IsString())
4331  {
4332  LOG_ERROR("Field multisig_signers found in JSON, but not String");
4333  return false;
4334  }
4335  const char *field_multisig_signers = json["multisig_signers"].GetString();
4336  std::string multisig_signers = std::string(field_multisig_signers, field_multisig_signers + json["multisig_signers"].GetStringLength());
4337  r = ::serialization::parse_binary(multisig_signers, m_multisig_signers);
4338  if (!r)
4339  {
4340  LOG_ERROR("Field multisig_signers found in JSON, but failed to parse");
4341  return false;
4342  }
4343 
4344  //previous version of multisig does not have this field
4345  if (json.HasMember("multisig_derivations"))
4346  {
4347  if (!json["multisig_derivations"].IsString())
4348  {
4349  LOG_ERROR("Field multisig_derivations found in JSON, but not String");
4350  return false;
4351  }
4352  const char *field_multisig_derivations = json["multisig_derivations"].GetString();
4353  std::string multisig_derivations = std::string(field_multisig_derivations, field_multisig_derivations + json["multisig_derivations"].GetStringLength());
4354  r = ::serialization::parse_binary(multisig_derivations, m_multisig_derivations);
4355  if (!r)
4356  {
4357  LOG_ERROR("Field multisig_derivations found in JSON, but failed to parse");
4358  return false;
4359  }
4360  }
4361  }
4362  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, always_confirm_transfers, int, Int, false, true);
4363  m_always_confirm_transfers = field_always_confirm_transfers;
4364  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, print_ring_members, int, Int, false, true);
4365  m_print_ring_members = field_print_ring_members;
4366  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_keys, int, Int, false, true);
4367  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_info, int, Int, false, true);
4368  m_store_tx_info = ((field_store_tx_keys != 0) || (field_store_tx_info != 0));
4369  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_mixin, unsigned int, Uint, false, 0);
4370  m_default_mixin = field_default_mixin;
4371  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_priority, unsigned int, Uint, false, 0);
4372  if (field_default_priority_found)
4373  {
4374  m_default_priority = field_default_priority;
4375  }
4376  else
4377  {
4378  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_fee_multiplier, unsigned int, Uint, false, 0);
4379  if (field_default_fee_multiplier_found)
4380  m_default_priority = field_default_fee_multiplier;
4381  else
4382  m_default_priority = 0;
4383  }
4384  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, auto_refresh, int, Int, false, true);
4385  m_auto_refresh = field_auto_refresh;
4386  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, refresh_type, int, Int, false, RefreshType::RefreshDefault);
4387  m_refresh_type = RefreshType::RefreshDefault;
4388  if (field_refresh_type_found)
4389  {
4390  if (field_refresh_type == RefreshFull || field_refresh_type == RefreshOptimizeCoinbase || field_refresh_type == RefreshNoCoinbase)
4391  m_refresh_type = (RefreshType)field_refresh_type;
4392  else
4393  LOG_PRINT_L0("Unknown refresh-type value (" << field_refresh_type << "), using default");
4394  }
4395  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, refresh_height, uint64_t, Uint64, false, 0);
4396  m_refresh_from_block_height = field_refresh_height;
4397  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_missing_payment_id, int, Int, false, true);
4398  m_confirm_missing_payment_id = field_confirm_missing_payment_id;
4399  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_non_default_ring_size, int, Int, false, true);
4400  m_confirm_non_default_ring_size = field_confirm_non_default_ring_size;
4401  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ask_password, AskPasswordType, Int, false, AskPasswordToDecrypt);
4402  m_ask_password = field_ask_password;
4403  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_decimal_point, int, Int, false, CRYPTONOTE_DISPLAY_DECIMAL_POINT);
4404  cryptonote::set_default_decimal_point(field_default_decimal_point);
4405  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, min_output_count, uint32_t, Uint, false, 0);
4406  m_min_output_count = field_min_output_count;
4407  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, min_output_value, uint64_t, Uint64, false, 0);
4408  m_min_output_value = field_min_output_value;
4409  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, merge_destinations, int, Int, false, false);
4410  m_merge_destinations = field_merge_destinations;
4411  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_backlog, int, Int, false, true);
4412  m_confirm_backlog = field_confirm_backlog;
4413  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_backlog_threshold, uint32_t, Uint, false, 0);
4414  m_confirm_backlog_threshold = field_confirm_backlog_threshold;
4415  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_export_overwrite, int, Int, false, true);
4416  m_confirm_export_overwrite = field_confirm_export_overwrite;
4417  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, auto_low_priority, int, Int, false, true);
4418  m_auto_low_priority = field_auto_low_priority;
4419  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, nettype, uint8_t, Uint, false, static_cast<uint8_t>(m_nettype));
4420  // The network type given in the program argument is inconsistent with the network type saved in the wallet
4421  THROW_WALLET_EXCEPTION_IF(static_cast<uint8_t>(m_nettype) != field_nettype, error::wallet_internal_error,
4422  (boost::format("%s wallet cannot be opened as %s wallet")
4423  % (field_nettype == 0 ? "Mainnet" : field_nettype == 1 ? "Testnet" : "Stagenet")
4424  % (m_nettype == MAINNET ? "mainnet" : m_nettype == TESTNET ? "testnet" : "stagenet")).str());
4425  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, segregate_pre_fork_outputs, int, Int, false, true);
4426  m_segregate_pre_fork_outputs = field_segregate_pre_fork_outputs;
4427  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, key_reuse_mitigation2, int, Int, false, true);
4428  m_key_reuse_mitigation2 = field_key_reuse_mitigation2;
4429  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, segregation_height, int, Uint, false, 0);
4430  m_segregation_height = field_segregation_height;
4431  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ignore_fractional_outputs, int, Int, false, true);
4432  m_ignore_fractional_outputs = field_ignore_fractional_outputs;
4433  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, track_uses, int, Int, false, false);
4434  m_track_uses = field_track_uses;
4435  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, setup_background_mining, BackgroundMiningSetupType, Int, false, BackgroundMiningMaybe);
4436  m_setup_background_mining = field_setup_background_mining;
4437  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_major, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MAJOR);
4438  m_subaddress_lookahead_major = field_subaddress_lookahead_major;
4439  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_minor, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MINOR);
4440  m_subaddress_lookahead_minor = field_subaddress_lookahead_minor;
4441 
4442  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, encrypted_secret_keys, uint32_t, Uint, false, false);
4443  encrypted_secret_keys = field_encrypted_secret_keys;
4444 
4445  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, account_major_offset, uint32_t, Uint, false, 0);
4446  m_account_major_offset = field_account_major_offset;
4447 
4448  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, device_name, std::string, String, false, std::string());
4449  if (m_device_name.empty())
4450  {
4451  if (field_device_name_found)
4452  {
4453  m_device_name = field_device_name;
4454  }
4455  else
4456  {
4457  m_device_name = m_key_device_type == hw::device::device_type::LEDGER ? "Ledger" : "default";
4458  }
4459  }
4460 
4461  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, device_derivation_path, std::string, String, false, std::string());
4462  m_device_derivation_path = field_device_derivation_path;
4463 
4464  if (json.HasMember("original_keys_available"))
4465  {
4466  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, original_keys_available, int, Int, false, false);
4467  m_original_keys_available = field_original_keys_available;
4468  if (m_original_keys_available)
4469  {
4470  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, original_address, std::string, String, true, std::string());
4472  bool ok = get_account_address_from_str(info, m_nettype, field_original_address);
4473  if (!ok)
4474  {
4475  LOG_ERROR("Failed to parse original_address from JSON");
4476  return false;
4477  }
4478  m_original_address = info.address;
4479  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, original_view_secret_key, std::string, String, true, std::string());
4480  ok = epee::string_tools::hex_to_pod(field_original_view_secret_key, m_original_view_secret_key);
4481  if (!ok)
4482  {
4483  LOG_ERROR("Failed to parse original_view_secret_key from JSON");
4484  return false;
4485  }
4486  }
4487  }
4488  else
4489  {
4490  m_original_keys_available = false;
4491  }
4492  }
4493  else
4494  {
4495  THROW_WALLET_EXCEPTION(error::wallet_internal_error, "invalid password");
4496  return false;
4497  }
4498 
4499  r = epee::serialization::load_t_from_binary(m_account, account_data);
4500  THROW_WALLET_EXCEPTION_IF(!r, error::invalid_password);
4501  if (m_key_device_type == hw::device::device_type::LEDGER || m_key_device_type == hw::device::device_type::TREZOR) {
4502  LOG_PRINT_L0("Account on device. Initing device...");
4503  hw::device &hwdev = lookup_device(m_device_name);
4504  THROW_WALLET_EXCEPTION_IF(!hwdev.set_name(m_device_name), error::wallet_internal_error, "Could not set device name " + m_device_name);
4505  hwdev.set_network_type(m_nettype);
4506  hwdev.set_derivation_path(m_device_derivation_path);
4507  hwdev.set_callback(get_device_callback());
4508  THROW_WALLET_EXCEPTION_IF(!hwdev.init(), error::wallet_internal_error, "Could not initialize the device " + m_device_name);
4509  THROW_WALLET_EXCEPTION_IF(!hwdev.connect(), error::wallet_internal_error, "Could not connect to the device " + m_device_name);
4510  m_account.set_device(hwdev);
4511 
4512  account_public_address device_account_public_address;
4513  THROW_WALLET_EXCEPTION_IF(!hwdev.get_public_address(device_account_public_address), error::wallet_internal_error, "Cannot get a device address");
4514  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. "
4515  "Device address: " + cryptonote::get_account_address_as_str(m_nettype, false, device_account_public_address) +
4516  ", wallet address: " + m_account.get_public_address_str(m_nettype));
4517  LOG_PRINT_L0("Device inited...");
4518  } else if (key_on_device()) {
4519  THROW_WALLET_EXCEPTION(error::wallet_internal_error, "hardware device not supported");
4520  }
4521 
4522  if (r)
4523  {
4524  if (encrypted_secret_keys)
4525  {
4526  m_account.decrypt_keys(key);
4527  }
4528  else
4529  {
4530  // rewrite with encrypted keys, ignore errors
4531  if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
4532  encrypt_keys(key);
4533  bool saved_ret = store_keys(keys_file_name, password, m_watch_only);
4534  if (!saved_ret)
4535  {
4536  // just moan a bit, but not fatal
4537  MERROR("Error saving keys file with encrypted keys, not fatal");
4538  }
4539  if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
4540  decrypt_keys(key);
4541  m_keys_file_locker.reset();
4542  }
4543  }
4544  const cryptonote::account_keys& keys = m_account.get_keys();
4545  hw::device &hwdev = m_account.get_device();
4547  if(!m_watch_only && !m_multisig && hwdev.device_protocol() != hw::device::PROTOCOL_COLD)
4549  THROW_WALLET_EXCEPTION_IF(!r, error::invalid_password);
4550 
4551  if (r)
4552  setup_keys(password);
4553 
4554  return true;
4555 }
4556 
4567 bool wallet2::verify_password(const epee::wipeable_string& password)
4568 {
4569  // this temporary unlocking is necessary for Windows (otherwise the file couldn't be loaded).
4570  unlock_keys_file();
4571  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);
4572  lock_keys_file();
4573  return r;
4574 }
4575 
4589 bool 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)
4590 {
4593  std::string buf;
4594  bool encrypted_secret_keys = false;
4595  bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf);
4597 
4598  // Decrypt the contents
4600  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + keys_file_name + '\"');
4601  crypto::chacha_key key;
4602  crypto::generate_chacha_key(password.data(), password.size(), key, kdf_rounds);
4603  std::string account_data;
4604  account_data.resize(keys_file_data.account_data.size());
4606  if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject())
4608 
4609  // The contents should be JSON if the wallet follows the new format.
4610  if (json.Parse(account_data.c_str()).HasParseError())
4611  {
4612  // old format before JSON wallet key file format
4613  }
4614  else
4615  {
4616  account_data = std::string(json["key_data"].GetString(), json["key_data"].GetString() +
4617  json["key_data"].GetStringLength());
4618  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, encrypted_secret_keys, uint32_t, Uint, false, false);
4619  encrypted_secret_keys = field_encrypted_secret_keys;
4620  }
4621 
4622  cryptonote::account_base account_data_check;
4623 
4624  r = epee::serialization::load_t_from_binary(account_data_check, account_data);
4625 
4626  if (encrypted_secret_keys)
4627  account_data_check.decrypt_keys(key);
4628 
4629  const cryptonote::account_keys& keys = account_data_check.get_keys();
4631  if(!no_spend_key)
4633  return r;
4634 }
4635 
4636 void wallet2::encrypt_keys(const crypto::chacha_key &key)
4637 {
4638  m_account.encrypt_keys(key);
4639  m_account.decrypt_viewkey(key);
4640 }
4641 
4642 void wallet2::decrypt_keys(const crypto::chacha_key &key)
4643 {
4644  m_account.encrypt_viewkey(key);
4645  m_account.decrypt_keys(key);
4646 }
4647 
4648 void wallet2::encrypt_keys(const epee::wipeable_string &password)
4649 {
4650  crypto::chacha_key key;
4651  crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
4652  encrypt_keys(key);
4653 }
4654 
4655 void wallet2::decrypt_keys(const epee::wipeable_string &password)
4656 {
4657  crypto::chacha_key key;
4658  crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
4659  decrypt_keys(key);
4660 }
4661 
4662 void wallet2::setup_new_blockchain()
4663 {
4665  generate_genesis(b);
4666  m_blockchain.push_back(get_block_hash(b));
4667  m_last_block_reward = cryptonote::get_outs_etn_amount(b.miner_tx);
4668  add_subaddress_account(tr("Primary account"));
4669 }
4670 
4671 void wallet2::create_keys_file(const std::string &wallet_, bool watch_only, const epee::wipeable_string &password, bool create_address_file)
4672 {
4673  if (!wallet_.empty())
4674  {
4675  bool r = store_keys(m_keys_file, password, watch_only);
4677 
4678  if (create_address_file)
4679  {
4680  r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
4681  if(!r) MERROR("String with address text not saved");
4682  }
4683  }
4684 }
4685 
4686 
4697 bool wallet2::query_device(hw::device::device_type& device_type, const std::string& keys_file_name, const epee::wipeable_string& password, uint64_t kdf_rounds)
4698 {
4701  std::string buf;
4702  bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf);
4704 
4705  // Decrypt the contents
4707  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + keys_file_name + '\"');
4708  crypto::chacha_key key;
4709  crypto::generate_chacha_key(password.data(), password.size(), key, kdf_rounds);
4710  std::string account_data;
4711  account_data.resize(keys_file_data.account_data.size());
4713  if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject())
4715 
4716  device_type = hw::device::device_type::SOFTWARE;
4717  // The contents should be JSON if the wallet follows the new format.
4718  if (json.Parse(account_data.c_str()).HasParseError())
4719  {
4720  // old format before JSON wallet key file format
4721  }
4722  else
4723  {
4724  account_data = std::string(json["key_data"].GetString(), json["key_data"].GetString() +
4725  json["key_data"].GetStringLength());
4726 
4727  if (json.HasMember("key_on_device"))
4728  {
4729  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, key_on_device, int, Int, false, hw::device::device_type::SOFTWARE);
4730  device_type = static_cast<hw::device::device_type>(field_key_on_device);
4731  }
4732  }
4733 
4734  cryptonote::account_base account_data_check;
4735 
4736  r = epee::serialization::load_t_from_binary(account_data_check, account_data);
4737  if (!r) return false;
4738  return true;
4739 }
4740 
4741 void wallet2::init_type(hw::device::device_type device_type)
4742 {
4743  m_account_public_address = m_account.get_keys().m_account_address;
4744  m_watch_only = false;
4745  m_multisig = false;
4746  m_multisig_threshold = 0;
4747  m_multisig_signers.clear();
4748  m_original_keys_available = false;
4749  m_key_device_type = device_type;
4750 }
4751 
4759 void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password,
4760  const epee::wipeable_string& multisig_data, bool create_address_file)
4761 {
4762  clear();
4763  prepare_file_names(wallet_);
4764 
4765  if (!wallet_.empty())
4766  {
4767  boost::system::error_code ignored_ec;
4768  THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, m_wallet_file);
4769  THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
4770  }
4771 
4772  m_account.generate(rct::rct2sk(rct::zero()), true, false);
4773 
4775  size_t offset = 0;
4776  uint32_t threshold = *(uint32_t*)(multisig_data.data() + offset);
4777  offset += sizeof(uint32_t);
4778  uint32_t total = *(uint32_t*)(multisig_data.data() + offset);
4779  offset += sizeof(uint32_t);
4782  const size_t n_multisig_keys = total == threshold ? 1 : threshold;
4783  THROW_WALLET_EXCEPTION_IF(multisig_data.size() != 8 + 32 * (4 + n_multisig_keys + total), error::invalid_multisig_seed);
4784 
4785  std::vector<crypto::secret_key> multisig_keys;
4786  std::vector<crypto::public_key> multisig_signers;
4787  crypto::secret_key spend_secret_key = *(crypto::secret_key*)(multisig_data.data() + offset);
4788  offset += sizeof(crypto::secret_key);
4789  crypto::public_key spend_public_key = *(crypto::public_key*)(multisig_data.data() + offset);
4790  offset += sizeof(crypto::public_key);
4791  crypto::secret_key view_secret_key = *(crypto::secret_key*)(multisig_data.data() + offset);
4792  offset += sizeof(crypto::secret_key);
4793  crypto::public_key view_public_key = *(crypto::public_key*)(multisig_data.data() + offset);
4794  offset += sizeof(crypto::public_key);
4795  for (size_t n = 0; n < n_multisig_keys; ++n)
4796  {
4797  multisig_keys.push_back(*(crypto::secret_key*)(multisig_data.data() + offset));
4798  offset += sizeof(crypto::secret_key);
4799  }
4800  for (size_t n = 0; n < total; ++n)
4801  {
4802  multisig_signers.push_back(*(crypto::public_key*)(multisig_data.data() + offset));
4803  offset += sizeof(crypto::public_key);
4804  }
4805 
4806  crypto::public_key calculated_view_public_key;
4807  THROW_WALLET_EXCEPTION_IF(!crypto::secret_key_to_public_key(view_secret_key, calculated_view_public_key), error::invalid_multisig_seed);
4808  THROW_WALLET_EXCEPTION_IF(view_public_key != calculated_view_public_key, error::invalid_multisig_seed);
4809  crypto::public_key local_signer;
4811  THROW_WALLET_EXCEPTION_IF(std::find(multisig_signers.begin(), multisig_signers.end(), local_signer) == multisig_signers.end(), error::invalid_multisig_seed);
4812  rct::key skey = rct::zero();
4813  for (const auto &msk: multisig_keys)
4814  sc_add(skey.bytes, skey.bytes, rct::sk2rct(msk).bytes);
4815  THROW_WALLET_EXCEPTION_IF(!(rct::rct2sk(skey) == spend_secret_key), error::invalid_multisig_seed);
4816  memwipe(&skey, sizeof(rct::key));
4817 
4818  m_account.make_multisig(view_secret_key, spend_secret_key, spend_public_key, multisig_keys);
4819  m_account.finalize_multisig(spend_public_key);
4820 
4821  // Not possible to restore a multisig wallet that is able to activate the MMS
4822  // (because the original keys are not (yet) part of the restore info), so
4823  // keep m_original_keys_available to false
4824  init_type(hw::device::device_type::SOFTWARE);
4825  m_multisig = true;
4826  m_multisig_threshold = threshold;
4827  m_multisig_signers = multisig_signers;
4828  setup_keys(password);
4829 
4830  create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file);
4831  setup_new_blockchain();
4832 
4833  if (!wallet_.empty())
4834  store();
4835 }
4836 
4847 crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password,
4848  const crypto::secret_key& recovery_param, bool recover, bool two_random, bool create_address_file)
4849 {
4850  clear();
4851  prepare_file_names(wallet_);
4852 
4853  if (!wallet_.empty())
4854  {
4855  boost::system::error_code ignored_ec;
4856  THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, m_wallet_file);
4857  THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
4858  }
4859 
4860  crypto::secret_key retval = m_account.generate(recovery_param, recover, two_random);
4861 
4862  init_type(hw::device::device_type::SOFTWARE);
4863  setup_keys(password);
4864 
4865  // calculate a starting refresh height
4866  if(m_refresh_from_block_height == 0 && !recover){
4867  m_refresh_from_block_height = estimate_blockchain_height();
4868  }
4869 
4870  create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file);
4871 
4872  setup_new_blockchain();
4873 
4874  if (!wallet_.empty())
4875  store();
4876 
4877  return retval;
4878 }
4879 
4880  uint64_t wallet2::estimate_blockchain_height()
4881  {
4882  // -1 month for fluctuations in block time and machine date/time setup.
4883  // avg seconds per block
4884  const int seconds_per_block = DIFFICULTY_TARGET_V6;
4885  // ~num blocks per month
4886  const uint64_t blocks_per_month = 60*60*24*30/seconds_per_block;
4887 
4888  // try asking the daemon first
4889  std::string err;
4890  uint64_t height = 0;
4891 
4892  // we get the max of approximated height and local height.
4893  // approximated height is the least of daemon target height
4894  // (the max of what the other daemons are claiming is their
4895  // height) and the theoretical height based on the local
4896  // clock. This will be wrong only if both the local clock
4897  // is bad *and* a peer daemon claims a highest height than
4898  // the real chain.
4899  // local height is the height the local daemon is currently
4900  // synced to, it will be lower than the real chain height if
4901  // the daemon is currently syncing.
4902  // If we use the approximate height we subtract one month as
4903  // a safety margin.
4904  height = get_approximate_blockchain_height();
4905  uint64_t target_height = get_daemon_blockchain_target_height(err);
4906  if (err.empty()) {
4907  if (target_height < height)
4908  height = target_height;
4909  } else {
4910  // if we couldn't talk to the daemon, check safety margin.
4911  if (height > blocks_per_month)
4912  height -= blocks_per_month;
4913  else
4914  height = 0;
4915  }
4916  uint64_t local_height = get_daemon_blockchain_height(err);
4917  if (err.empty() && local_height > height)
4918  height = local_height;
4919  return height;
4920  }
4921 
4930 void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password,
4932  const crypto::secret_key& viewkey, bool create_address_file)
4933 {
4934  clear();
4935  prepare_file_names(wallet_);
4936 
4937  if (!wallet_.empty())
4938  {
4939  boost::system::error_code ignored_ec;
4940  THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, m_wallet_file);
4941  THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
4942  }
4943 
4944  m_account.create_from_viewkey(account_public_address, viewkey);
4945  init_type(hw::device::device_type::SOFTWARE);
4946  m_watch_only = true;
4947  m_account_public_address = account_public_address;
4948  setup_keys(password);
4949 
4950  create_keys_file(wallet_, true, password, m_nettype != MAINNET || create_address_file);
4951 
4952  setup_new_blockchain();
4953 
4954  if (!wallet_.empty())
4955  store();
4956 }
4957 
4967 void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password,
4969  const crypto::secret_key& spendkey, const crypto::secret_key& viewkey, bool create_address_file)
4970 {
4971  clear();
4972  prepare_file_names(wallet_);
4973 
4974  if (!wallet_.empty())
4975  {
4976  boost::system::error_code ignored_ec;
4977  THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, m_wallet_file);
4978  THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
4979  }
4980 
4981  m_account.create_from_keys(account_public_address, spendkey, viewkey);
4982  init_type(hw::device::device_type::SOFTWARE);
4983  m_account_public_address = account_public_address;
4984  setup_keys(password);
4985 
4986  create_keys_file(wallet_, false, password, create_address_file);
4987 
4988  setup_new_blockchain();
4989 
4990  if (!wallet_.empty())
4991  store();
4992 }
4993 
5000 void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& password, const std::string &device_name, bool create_address_file)
5001 {
5002  clear();
5003  prepare_file_names(wallet_);
5004 
5005  boost::system::error_code ignored_ec;
5006  if (!wallet_.empty()) {
5007  THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, m_wallet_file);
5008  THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
5009  }
5010 
5011  auto &hwdev = lookup_device(device_name);
5012  hwdev.set_name(device_name);
5013  hwdev.set_network_type(m_nettype);
5014  hwdev.set_derivation_path(m_device_derivation_path);
5015  hwdev.set_callback(get_device_callback());
5016 
5017  m_account.create_from_device(hwdev);
5018  init_type(m_account.get_device().get_type());
5019  setup_keys(password);
5020  m_device_name = device_name;
5021 
5022  create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file);
5023  if (m_subaddress_lookahead_major == SUBADDRESS_LOOKAHEAD_MAJOR && m_subaddress_lookahead_minor == SUBADDRESS_LOOKAHEAD_MINOR)
5024  {
5025  // the default lookahead setting (50:200) is clearly too much for hardware wallet
5026  m_subaddress_lookahead_major = 5;
5027  m_subaddress_lookahead_minor = 20;
5028  }
5029  setup_new_blockchain();
5030  if (!wallet_.empty()) {
5031  store();
5032  }
5033 }
5034 
5035 std::string wallet2::make_multisig(const epee::wipeable_string &password,
5036  const std::vector<crypto::secret_key> &view_keys,
5037  const std::vector<crypto::public_key> &spend_keys,
5039 {
5040  CHECK_AND_ASSERT_THROW_MES(!view_keys.empty(), "empty view keys");
5041  CHECK_AND_ASSERT_THROW_MES(view_keys.size() == spend_keys.size(), "Mismatched view/spend key sizes");
5042  CHECK_AND_ASSERT_THROW_MES(threshold > 1 && threshold <= spend_keys.size() + 1, "Invalid threshold");
5043 
5044  std::string extra_multisig_info;
5045  std::vector<crypto::secret_key> multisig_keys;
5046  rct::key spend_pkey = rct::identity();
5047  rct::key spend_skey;
5048  std::vector<crypto::public_key> multisig_signers;
5049 
5050  // decrypt keys
5052  if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
5053  {
5054  crypto::chacha_key chacha_key;
5055  crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds);
5056  m_account.encrypt_viewkey(chacha_key);
5057  m_account.decrypt_keys(chacha_key);
5058  keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this, chacha_key]() { m_account.encrypt_keys(chacha_key); m_account.decrypt_viewkey(chacha_key); });
5059  }
5060 
5061  // In common multisig scheme there are 4 types of key exchange rounds:
5062  // 1. First round is exchange of view secret keys and public spend keys.
5063  // 2. Middle round is exchange of derivations: Ki = b * Mj, where b - spend secret key,
5064  // M - public multisig key (in first round it equals to public spend key), K - new public multisig key.
5065  // 3. Secret spend establishment round sets your secret multisig keys as follows: kl = H(Ml), where M - is *your* public multisig key,
5066  // k - secret multisig key used to sign transactions. k and M are sets of keys, of course.
5067  // And secret spend key as the sum of all participant's secret multisig keys
5068  // 4. Last round establishes multisig wallet's public spend key. Participants exchange their public multisig keys
5069  // and calculate common spend public key as sum of all unique participants' public multisig keys.
5070  // 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.
5071 
5072  // IMPORTANT: wallet's public spend key is not equal to secret_spend_key * G!
5073  // Wallet's public spend key is the sum of unique public multisig keys of all participants.
5074  // secret_spend_key * G = public signer key
5075 
5076  if (threshold == spend_keys.size() + 1)
5077  {
5078  // In N / N case we only need to do one round and calculate secret multisig keys and new secret spend key
5079  MINFO("Creating spend key...");
5080 
5081  // Calculates all multisig keys and spend key
5082  cryptonote::generate_multisig_N_N(get_account().get_keys(), spend_keys, multisig_keys, spend_skey, spend_pkey);
5083 
5084  // Our signer key is b * G, where b is secret spend key.
5085  multisig_signers = spend_keys;
5086  multisig_signers.push_back(get_multisig_signer_public_key(get_account().get_keys().m_spend_secret_key));
5087  }
5088  else
5089  {
5090  // We just got public spend keys of all participants and deriving multisig keys (set of Mi = b * Bi).
5091  // note that derivations are public keys as DH exchange suppose it to be
5092  auto derivations = cryptonote::generate_multisig_derivations(get_account().get_keys(), spend_keys);
5093 
5094  spend_pkey = rct::identity();
5095  multisig_signers = std::vector<crypto::public_key>(spend_keys.size() + 1, crypto::null_pkey);
5096 
5097  if (threshold == spend_keys.size())
5098  {
5099  // N - 1 / N case
5100 
5101  // We need an extra step, so we package all the composite public keys
5102  // we know about, and make a signed string out of them
5103  MINFO("Creating spend key...");
5104 
5105  // Calculating set of our secret multisig keys as follows: mi = H(Mi),
5106  // where mi - secret multisig key, Mi - others' participants public multisig key
5107  multisig_keys = cryptonote::calculate_multisig_keys(derivations);
5108 
5109  // calculating current participant's spend secret key as sum of all secret multisig keys for current participant.
5110  // IMPORTANT: participant's secret spend key is not an entire wallet's secret spend!
5111  // Entire wallet's secret spend is sum of all unique secret multisig keys
5112  // among all of participants and is not held by anyone!
5113  spend_skey = rct::sk2rct(cryptonote::calculate_multisig_signer_key(multisig_keys));
5114 
5115  // Preparing data for the last round to calculate common public spend key. The data contains public multisig keys.
5116  extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, secret_keys_to_public_keys(multisig_keys), rct::rct2sk(spend_skey));
5117  }
5118  else
5119  {
5120  // M / N case
5121  MINFO("Preparing keys for next exchange round...");
5122 
5123  // Preparing data for middle round - packing new public multisig keys to exchage with others.
5124  extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, derivations, m_account.get_keys().m_spend_secret_key);
5125  spend_skey = rct::sk2rct(m_account.get_keys().m_spend_secret_key);
5126 
5127  // Need to store middle keys to be able to proceed in case of wallet shutdown.
5128  m_multisig_derivations = derivations;
5129  }
5130  }
5131 
5132  if (!m_original_keys_available)
5133  {
5134  // Save the original i.e. non-multisig keys so the MMS can continue to use them to encrypt and decrypt messages
5135  // (making a wallet multisig overwrites those keys, see account_base::make_multisig)
5136  m_original_address = m_account.get_keys().m_account_address;
5137  m_original_view_secret_key = m_account.get_keys().m_view_secret_key;
5138  m_original_keys_available = true;
5139  }
5140 
5141  clear();
5142  MINFO("Creating view key...");
5143  crypto::secret_key view_skey = cryptonote::generate_multisig_view_secret_key(get_account().get_keys().m_view_secret_key, view_keys);
5144 
5145  MINFO("Creating multisig address...");
5146  CHECK_AND_ASSERT_THROW_MES(m_account.make_multisig(view_skey, rct::rct2sk(spend_skey), rct::rct2pk(spend_pkey), multisig_keys),
5147  "Failed to create multisig wallet due to bad keys");
5148  memwipe(&spend_skey, sizeof(rct::key));
5149 
5150  init_type(hw::device::device_type::SOFTWARE);
5151  m_original_keys_available = true;
5152  m_multisig = true;
5153  m_multisig_threshold = threshold;
5154  m_multisig_signers = multisig_signers;
5155  ++m_multisig_rounds_passed;
5156 
5157  // re-encrypt keys
5158  keys_reencryptor = epee::misc_utils::auto_scope_leave_caller();
5159 
5160  create_keys_file(m_wallet_file, false, password, boost::filesystem::exists(m_wallet_file + ".address.txt"));
5161 
5162  setup_new_blockchain();
5163 
5164  if (!m_wallet_file.empty())
5165  store();
5166 
5167  return extra_multisig_info;
5168 }
5169 
5170 std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &password,
5171  const std::vector<std::string> &info)
5172 {
5174  error::wallet_internal_error, "Empty multisig info");
5175 
5176  if (info[0].substr(0, MULTISIG_EXTRA_INFO_MAGIC.size()) != MULTISIG_EXTRA_INFO_MAGIC)
5177  {
5179  error::wallet_internal_error, "Unsupported info string");
5180  }
5181 
5182  std::vector<crypto::public_key> signers;
5183  std::unordered_set<crypto::public_key> pkeys;
5184 
5185  THROW_WALLET_EXCEPTION_IF(!unpack_extra_multisig_info(info, signers, pkeys),
5186  error::wallet_internal_error, "Bad extra multisig info");
5187 
5188  return exchange_multisig_keys(password, pkeys, signers);
5189 }
5190 
5191 std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &password,
5192  std::unordered_set<crypto::public_key> derivations,
5193  std::vector<crypto::public_key> signers)
5194 {
5195  CHECK_AND_ASSERT_THROW_MES(!derivations.empty(), "empty pkeys");
5196  CHECK_AND_ASSERT_THROW_MES(!signers.empty(), "empty signers");
5197 
5198  bool ready = false;
5199  CHECK_AND_ASSERT_THROW_MES(multisig(&ready), "The wallet is not multisig");
5200  CHECK_AND_ASSERT_THROW_MES(!ready, "Multisig wallet creation process has already been finished");
5201 
5202  // keys are decrypted
5204  if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
5205  {
5206  crypto::chacha_key chacha_key;
5207  crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds);
5208  m_account.encrypt_viewkey(chacha_key);
5209  m_account.decrypt_keys(chacha_key);
5210  keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this, chacha_key]() { m_account.encrypt_keys(chacha_key); m_account.decrypt_viewkey(chacha_key); });
5211  }
5212 
5213  if (m_multisig_rounds_passed == multisig_rounds_required(m_multisig_signers.size(), m_multisig_threshold) - 1)
5214  {
5215  // the last round is passed and we have to calculate spend public key
5216  // add ours if not included
5217  crypto::public_key local_signer = get_multisig_signer_public_key();
5218 
5219  if (std::find(signers.begin(), signers.end(), local_signer) == signers.end())
5220  {
5221  signers.push_back(local_signer);
5222  for (const auto &msk: get_account().get_multisig_keys())
5223  {
5224  derivations.insert(rct::rct2pk(rct::scalarmultBase(rct::sk2rct(msk))));
5225  }
5226  }
5227 
5228  CHECK_AND_ASSERT_THROW_MES(signers.size() == m_multisig_signers.size(), "Bad signers size");
5229 
5230  // Summing all of unique public multisig keys to calculate common public spend key
5231  crypto::public_key spend_public_key = cryptonote::generate_multisig_M_N_spend_public_key(std::vector<crypto::public_key>(derivations.begin(), derivations.end()));
5232  m_account_public_address.m_spend_public_key = spend_public_key;
5233  m_account.finalize_multisig(spend_public_key);
5234 
5235  m_multisig_signers = signers;
5236  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)); });
5237 
5238  ++m_multisig_rounds_passed;
5239  m_multisig_derivations.clear();
5240 
5241  // keys are encrypted again
5242  keys_reencryptor = epee::misc_utils::auto_scope_leave_caller();
5243 
5244  if (!m_wallet_file.empty())
5245  {
5246  bool r = store_keys(m_keys_file, password, false);
5248 
5249  if (boost::filesystem::exists(m_wallet_file + ".address.txt"))
5250  {
5251  r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
5252  if(!r) MERROR("String with address text not saved");
5253  }
5254  }
5255 
5256  m_subaddresses.clear();
5257  m_subaddress_labels.clear();
5258  add_subaddress_account(tr("Primary account"));
5259 
5260  if (!m_wallet_file.empty())
5261  store();
5262 
5263  return {};
5264  }
5265 
5266  // Below are either middle or secret spend key establishment rounds
5267 
5268  for (const auto& key: m_multisig_derivations)
5269  derivations.erase(key);
5270 
5271  // Deriving multisig keys (set of Mi = b * Bi) according to DH from other participants' multisig keys.
5272  auto new_derivations = cryptonote::generate_multisig_derivations(get_account().get_keys(), std::vector<crypto::public_key>(derivations.begin(), derivations.end()));
5273 
5274  std::string extra_multisig_info;
5275  if (m_multisig_rounds_passed == multisig_rounds_required(m_multisig_signers.size(), m_multisig_threshold) - 2) // next round is last
5276  {
5277  // Next round is last therefore we are performing secret spend establishment round as described above.
5278  MINFO("Creating spend key...");
5279 
5280  // Calculating our secret multisig keys by hashing our public multisig keys.
5281  auto multisig_keys = cryptonote::calculate_multisig_keys(std::vector<crypto::public_key>(new_derivations.begin(), new_derivations.end()));
5282  // And summing it to get personal secret spend key
5284 
5285  m_account.make_multisig(m_account.get_keys().m_view_secret_key, spend_skey, rct::rct2pk(rct::identity()), multisig_keys);
5286 
5287  // Packing public multisig keys to exchange with others and calculate common public spend key in the last round
5288  extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, secret_keys_to_public_keys(multisig_keys), spend_skey);
5289  }
5290  else
5291  {
5292  // This is just middle round
5293  MINFO("Preparing keys for next exchange round...");
5294  extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, new_derivations, m_account.get_keys().m_spend_secret_key);
5295  m_multisig_derivations = new_derivations;
5296  }
5297 
5298  ++m_multisig_rounds_passed;
5299 
5300  create_keys_file(m_wallet_file, false, password, boost::filesystem::exists(m_wallet_file + ".address.txt"));
5301  return extra_multisig_info;
5302 }
5303 
5304 void wallet2::unpack_multisig_info(const std::vector<std::string>& info,
5305  std::vector<crypto::public_key> &public_keys,
5306  std::vector<crypto::secret_key> &secret_keys) const
5307 {
5308  // parse all multisig info
5309  public_keys.resize(info.size());
5310  secret_keys.resize(info.size());
5311  for (size_t i = 0; i < info.size(); ++i)
5312  {
5313  THROW_WALLET_EXCEPTION_IF(!verify_multisig_info(info[i], secret_keys[i], public_keys[i]),
5314  error::wallet_internal_error, "Bad multisig info: " + info[i]);
5315  }
5316 
5317  // remove duplicates
5318  for (size_t i = 0; i < secret_keys.size(); ++i)
5319  {
5320  for (size_t j = i + 1; j < secret_keys.size(); ++j)
5321  {
5322  if (rct::sk2rct(secret_keys[i]) == rct::sk2rct(secret_keys[j]))
5323  {
5324  MDEBUG("Duplicate key found, ignoring");
5325  secret_keys[j] = secret_keys.back();
5326  public_keys[j] = public_keys.back();
5327  secret_keys.pop_back();
5328  public_keys.pop_back();
5329  --j;
5330  }
5331  }
5332  }
5333 
5334  // people may include their own, weed it out
5335  const crypto::secret_key local_skey = cryptonote::get_multisig_blinded_secret_key(get_account().get_keys().m_view_secret_key);
5336  const crypto::public_key local_pkey = get_multisig_signer_public_key(get_account().get_keys().m_spend_secret_key);
5337  for (size_t i = 0; i < secret_keys.size(); ++i)
5338  {
5339  if (secret_keys[i] == local_skey)
5340  {
5341  MDEBUG("Local key is present, ignoring");
5342  secret_keys[i] = secret_keys.back();
5343  public_keys[i] = public_keys.back();
5344  secret_keys.pop_back();
5345  public_keys.pop_back();
5346  --i;
5347  }
5348  else
5349  {
5350  THROW_WALLET_EXCEPTION_IF(public_keys[i] == local_pkey, error::wallet_internal_error,
5351  "Found local spend public key, but not local view secret key - something very weird");
5352  }
5353  }
5354 }
5355 
5356 std::string wallet2::make_multisig(const epee::wipeable_string &password,
5357  const std::vector<std::string> &info,
5359 {
5360  std::vector<crypto::secret_key> secret_keys(info.size());
5361  std::vector<crypto::public_key> public_keys(info.size());
5362  unpack_multisig_info(info, public_keys, secret_keys);
5363  return make_multisig(password, secret_keys, public_keys, threshold);
5364 }
5365 
5366 bool wallet2::finalize_multisig(const epee::wipeable_string &password, const std::unordered_set<crypto::public_key> &pkeys, std::vector<crypto::public_key> signers)
5367 {
5368  bool ready;
5369  uint32_t threshold, total;
5370  if (!multisig(&ready, &threshold, &total))
5371  {
5372  MERROR("This is not a multisig wallet");
5373  return false;
5374  }
5375  if (ready)
5376  {
5377  MERROR("This multisig wallet is already finalized");
5378  return false;
5379  }
5380  if (threshold + 1 != total)
5381  {
5382  MERROR("finalize_multisig should only be used for N-1/N wallets, use exchange_multisig_keys instead");
5383  return false;
5384  }
5385  exchange_multisig_keys(password, pkeys, signers);
5386  return true;
5387 }
5388 
5389 bool wallet2::unpack_extra_multisig_info(const std::vector<std::string>& info,
5390  std::vector<crypto::public_key> &signers,
5391  std::unordered_set<crypto::public_key> &pkeys) const
5392 {
5393  // parse all multisig info
5394  signers.resize(info.size(), crypto::null_pkey);
5395  for (size_t i = 0; i < info.size(); ++i)
5396  {
5397  if (!verify_extra_multisig_info(info[i], pkeys, signers[i]))
5398  {
5399  return false;
5400  }
5401  }
5402 
5403  return true;
5404 }
5405 
5406 bool wallet2::finalize_multisig(const epee::wipeable_string &password, const std::vector<std::string> &info)
5407 {
5408  std::unordered_set<crypto::public_key> public_keys;
5409  std::vector<crypto::public_key> signers;
5410  if (!unpack_extra_multisig_info(info, signers, public_keys))
5411  {
5412  MERROR("Bad multisig info");
5413  return false;
5414  }
5415 
5416  return finalize_multisig(password, public_keys, signers);
5417 }
5418 
5419 std::string wallet2::get_multisig_info() const
5420 {
5421  // It's a signed package of private view key and public spend key
5422  const crypto::secret_key skey = cryptonote::get_multisig_blinded_secret_key(get_account().get_keys().m_view_secret_key);
5423  const crypto::public_key pkey = get_multisig_signer_public_key(get_account().get_keys().m_spend_secret_key);
5425 
5426  std::string data;
5427  data += std::string((const char *)&skey, sizeof(crypto::secret_key));
5428  data += std::string((const char *)&pkey, sizeof(crypto::public_key));
5429 
5430  data.resize(data.size() + sizeof(crypto::signature));
5431  crypto::cn_fast_hash(data.data(), data.size() - sizeof(signature), hash);
5432  crypto::signature &signature = *(crypto::signature*)&data[data.size() - sizeof(crypto::signature)];
5433  crypto::generate_signature(hash, pkey, get_multisig_blinded_secret_key(get_account().get_keys().m_spend_secret_key), signature);
5434 
5435  return std::string("MultisigV1") + tools::base58::encode(data);
5436 }
5437 
5438 bool wallet2::verify_multisig_info(const std::string &data, crypto::secret_key &skey, crypto::public_key &pkey)
5439 {
5440  const size_t header_len = strlen("MultisigV1");
5441  if (data.size() < header_len || data.substr(0, header_len) != "MultisigV1")
5442  {
5443  MERROR("Multisig info header check error");
5444  return false;
5445  }
5446  std::string decoded;
5447  if (!tools::base58::decode(data.substr(header_len), decoded))
5448  {
5449  MERROR("Multisig info decoding error");
5450  return false;
5451  }
5452  if (decoded.size() != sizeof(crypto::secret_key) + sizeof(crypto::public_key) + sizeof(crypto::signature))
5453  {
5454  MERROR("Multisig info is corrupt");
5455  return false;
5456  }
5457 
5458  size_t offset = 0;
5459  skey = *(const crypto::secret_key*)(decoded.data() + offset);
5460  offset += sizeof(skey);
5461  pkey = *(const crypto::public_key*)(decoded.data() + offset);
5462  offset += sizeof(pkey);
5463  const crypto::signature &signature = *(const crypto::signature*)(decoded.data() + offset);
5464 
5466  crypto::cn_fast_hash(decoded.data(), decoded.size() - sizeof(signature), hash);
5467  if (!crypto::check_signature(hash, pkey, signature))
5468  {
5469  MERROR("Multisig info signature is invalid");
5470  return false;
5471  }
5472 
5473  return true;
5474 }
5475 
5476 bool wallet2::verify_extra_multisig_info(const std::string &data, std::unordered_set<crypto::public_key> &pkeys, crypto::public_key &signer)
5477 {
5478  if (data.size() < MULTISIG_EXTRA_INFO_MAGIC.size() || data.substr(0, MULTISIG_EXTRA_INFO_MAGIC.size()) != MULTISIG_EXTRA_INFO_MAGIC)
5479  {
5480  MERROR("Multisig info header check error");
5481  return false;
5482  }
5483  std::string decoded;
5484  if (!tools::base58::decode(data.substr(MULTISIG_EXTRA_INFO_MAGIC.size()), decoded))
5485  {
5486  MERROR("Multisig info decoding error");
5487  return false;
5488  }
5489  if (decoded.size() < sizeof(crypto::public_key) + sizeof(crypto::signature))
5490  {
5491  MERROR("Multisig info is corrupt");
5492  return false;
5493  }
5494  if ((decoded.size() - (sizeof(crypto::public_key) + sizeof(crypto::signature))) % sizeof(crypto::public_key))
5495  {
5496  MERROR("Multisig info is corrupt");
5497  return false;
5498  }
5499 
5500  const size_t n_keys = (decoded.size() - (sizeof(crypto::public_key) + sizeof(crypto::signature))) / sizeof(crypto::public_key);
5501  size_t offset = 0;
5502  signer = *(const crypto::public_key*)(decoded.data() + offset);
5503  offset += sizeof(signer);
5504  const crypto::signature &signature = *(const crypto::signature*)(decoded.data() + offset + n_keys * sizeof(crypto::public_key));
5505 
5507  crypto::cn_fast_hash(decoded.data(), decoded.size() - sizeof(signature), hash);
5508  if (!crypto::check_signature(hash, signer, signature))
5509  {
5510  MERROR("Multisig info signature is invalid");
5511  return false;
5512  }
5513 
5514  for (size_t n = 0; n < n_keys; ++n)
5515  {
5516  crypto::public_key mspk = *(const crypto::public_key*)(decoded.data() + offset);
5517  pkeys.insert(mspk);
5518  offset += sizeof(mspk);
5519  }
5520 
5521  return true;
5522 }
5523 
5524 bool wallet2::multisig(bool *ready, uint32_t *threshold, uint32_t *total) const
5525 {
5526  if (!m_multisig)
5527  return false;
5528  if (threshold)
5529  *threshold = m_multisig_threshold;
5530  if (total)
5531  *total = m_multisig_signers.size();
5532  if (ready)
5533  *ready = !(get_account().get_keys().m_account_address.m_spend_public_key == rct::rct2pk(rct::identity()));
5534  return true;
5535 }
5536 
5537 bool wallet2::has_multisig_partial_key_images() const
5538 {
5539  if (!m_multisig)
5540  return false;
5541  for (const auto &td: m_transfers)
5542  if (td.m_key_image_partial && td.m_tx.version == 1)
5543  return true;
5544  return false;
5545 }
5546 
5547 bool wallet2::has_unknown_key_images() const
5548 {
5549  for (const auto &td: m_transfers)
5550  if (!td.m_key_image_known && td.m_tx.version == 1)
5551  return true;
5552  return false;
5553 }
5554 
5560 void wallet2::rewrite(const std::string& wallet_name, const epee::wipeable_string& password)
5561 {
5562  if (wallet_name.empty())
5563  return;
5564  prepare_file_names(wallet_name);
5565  boost::system::error_code ignored_ec;
5566  THROW_WALLET_EXCEPTION_IF(!boost::filesystem::exists(m_keys_file, ignored_ec), error::file_not_found, m_keys_file);
5567  bool r = store_keys(m_keys_file, password, m_watch_only);
5569 }
5576 void wallet2::write_watch_only_wallet(const std::string& wallet_name, const epee::wipeable_string& password, std::string &new_keys_filename)
5577 {
5578  prepare_file_names(wallet_name);
5579  boost::system::error_code ignored_ec;
5580  new_keys_filename = m_wallet_file + "-watchonly.keys";
5581  bool watch_only_keys_file_exists = boost::filesystem::exists(new_keys_filename, ignored_ec);
5582  THROW_WALLET_EXCEPTION_IF(watch_only_keys_file_exists, error::file_save_error, new_keys_filename);
5583  bool r = store_keys(new_keys_filename, password, true);
5584  THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, new_keys_filename);
5585 }
5586 //----------------------------------------------------------------------------------------------------
5587 void wallet2::wallet_exists(const std::string& file_path, bool& keys_file_exists, bool& wallet_file_exists)
5588 {
5589  std::string keys_file, wallet_file, mms_file;
5590  do_prepare_file_names(file_path, keys_file, wallet_file, mms_file);
5591 
5592  boost::system::error_code ignore;
5593  keys_file_exists = boost::filesystem::exists(keys_file, ignore);
5594  wallet_file_exists = boost::filesystem::exists(wallet_file, ignore);
5595 }
5596 //----------------------------------------------------------------------------------------------------
5597 bool wallet2::wallet_valid_path_format(const std::string& file_path)
5598 {
5599  return !file_path.empty();
5600 }
5601 //----------------------------------------------------------------------------------------------------
5602 bool wallet2::parse_long_payment_id(const std::string& payment_id_str, crypto::hash& payment_id)
5603 {
5604  cryptonote::blobdata payment_id_data;
5605  if(!epee::string_tools::parse_hexstr_to_binbuff(payment_id_str, payment_id_data))
5606  return false;
5607 
5608  if(sizeof(crypto::hash) != payment_id_data.size())
5609  return false;
5610 
5611  payment_id = *reinterpret_cast<const crypto::hash*>(payment_id_data.data());
5612  return true;
5613 }
5614 //----------------------------------------------------------------------------------------------------
5615 bool wallet2::parse_short_payment_id(const std::string& payment_id_str, crypto::hash8& payment_id)
5616 {
5617  cryptonote::blobdata payment_id_data;
5618  if(!epee::string_tools::parse_hexstr_to_binbuff(payment_id_str, payment_id_data))
5619  return false;
5620 
5621  if(sizeof(crypto::hash8) != payment_id_data.size())
5622  return false;
5623 
5624  payment_id = *reinterpret_cast<const crypto::hash8*>(payment_id_data.data());
5625  return true;
5626 }
5627 //----------------------------------------------------------------------------------------------------
5628 bool wallet2::parse_payment_id(const std::string& payment_id_str, crypto::hash& payment_id)
5629 {
5630  if (parse_long_payment_id(payment_id_str, payment_id))
5631  return true;
5632  crypto::hash8 payment_id8;
5633  if (parse_short_payment_id(payment_id_str, payment_id8))
5634  {
5635  memcpy(payment_id.data, payment_id8.data, 8);
5636  memset(payment_id.data + 8, 0, 24);
5637  return true;
5638  }
5639  return false;
5640 }
5641 //----------------------------------------------------------------------------------------------------
5642 bool wallet2::prepare_file_names(const std::string& file_path)
5643 {
5644  do_prepare_file_names(file_path, m_keys_file, m_wallet_file, m_mms_file);
5645  return true;
5646 }
5647 //----------------------------------------------------------------------------------------------------
5648 bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout)
5649 {
5651 
5652  if (m_offline)
5653  {
5654  if (version)
5655  *version = 0;
5656  if (ssl)
5657  *ssl = false;
5658  return false;
5659  }
5660 
5661  // TODO: Add light wallet version check.
5662  if(m_light_wallet) {
5663  if (version)
5664  *version = 0;
5665  if (ssl)
5666  *ssl = m_light_wallet_connected; // light wallet is always SSL
5667  return m_light_wallet_connected;
5668  }
5669 
5670  {
5671  boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex);
5672  if(!m_http_client.is_connected(ssl))
5673  {
5674  m_node_rpc_proxy.invalidate();
5675  if (!m_http_client.connect(std::chrono::milliseconds(timeout)))
5676  return false;
5677  if(!m_http_client.is_connected(ssl))
5678  return false;
5679  }
5680  }
5681 
5682  if (version)
5683  {
5686  bool r = invoke_http_json_rpc("/json_rpc", "get_version", req_t, resp_t);
5687  if(!r) {
5688  *version = 0;
5689  return false;
5690  }
5691  if (resp_t.status != CORE_RPC_STATUS_OK)
5692  *version = 0;
5693  else
5694  *version = resp_t.version;
5695  }
5696 
5697  return true;
5698 }
5699 //----------------------------------------------------------------------------------------------------
5700 void wallet2::set_offline(bool offline)
5701 {
5702  m_offline = offline;
5703  m_http_client.set_auto_connect(!offline);
5704  if (offline)
5705  {
5706  boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex);
5707  if(m_http_client.is_connected())
5708  m_http_client.disconnect();
5709  }
5710 }
5711 //----------------------------------------------------------------------------------------------------
5712 bool wallet2::generate_chacha_key_from_secret_keys(crypto::chacha_key &key) const
5713 {
5714  hw::device &hwdev = m_account.get_device();
5715  return hwdev.generate_chacha_key(m_account.get_keys(), key, m_kdf_rounds);
5716 }
5717 //----------------------------------------------------------------------------------------------------
5718 void wallet2::generate_chacha_key_from_password(const epee::wipeable_string &pass, crypto::chacha_key &key) const
5719 {
5720  crypto::generate_chacha_key(pass.data(), pass.size(), key, m_kdf_rounds);
5721 }
5722 //----------------------------------------------------------------------------------------------------
5723 void wallet2::load(const std::string& wallet_, const epee::wipeable_string& password)
5724 {
5725  clear();
5726  prepare_file_names(wallet_);
5727 
5728  boost::system::error_code e;
5729  bool exists = boost::filesystem::exists(m_keys_file, e);
5730  THROW_WALLET_EXCEPTION_IF(e || !exists, error::file_not_found, m_keys_file);
5731  lock_keys_file();
5732  THROW_WALLET_EXCEPTION_IF(!is_keys_file_locked(), error::wallet_internal_error, "internal error: \"" + m_keys_file + "\" is opened by another wallet program");
5733 
5734  // this temporary unlocking is necessary for Windows (otherwise the file couldn't be loaded).
5735  unlock_keys_file();
5736  if (!load_keys(m_keys_file, password))
5737  {
5739  }
5740  LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str(m_nettype));
5741  lock_keys_file();
5742 
5743  wallet_keys_unlocker unlocker(*this, m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only, password);
5744 
5745  //keys loaded ok!
5746  //try to load wallet file. but even if we failed, it is not big problem
5747  if(!boost::filesystem::exists(m_wallet_file, e) || e)
5748  {
5749  LOG_PRINT_L0("file not found: " << m_wallet_file << ", starting with empty blockchain");
5750  m_account_public_address = m_account.get_keys().m_account_address;
5751  }
5752  else
5753  {
5755  std::string buf;
5756  bool r = epee::file_io_utils::load_file_to_string(m_wallet_file, buf, std::numeric_limits<size_t>::max());
5758 
5759  // try to read it as an encrypted cache
5760  try
5761  {
5762  LOG_PRINT_L1("Trying to decrypt cache data");
5763 
5765  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + m_wallet_file + '\"');
5766  std::string cache_data;
5767  cache_data.resize(cache_file_data.cache_data.size());
5768  crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), m_cache_key, cache_file_data.iv, &cache_data[0]);
5769 
5770  try {
5771  std::stringstream iss;
5772  iss << cache_data;
5774  ar >> *this;
5775  }
5776  catch(...)
5777  {
5778  // try with previous scheme: direct from keys
5779  crypto::chacha_key key;
5780  generate_chacha_key_from_secret_keys(key);
5782  try {
5783  std::stringstream iss;
5784  iss << cache_data;
5786  ar >> *this;
5787  }
5788  catch (...)
5789  {
5791  try
5792  {
5793  std::stringstream iss;
5794  iss << cache_data;
5796  ar >> *this;
5797  }
5798  catch (...)
5799  {
5800  LOG_PRINT_L0("Failed to open portable binary, trying unportable");
5801  boost::filesystem::copy_file(m_wallet_file, m_wallet_file + ".unportable", boost::filesystem::copy_option::overwrite_if_exists);
5802  std::stringstream iss;
5803  iss.str("");
5804  iss << cache_data;
5805  boost::archive::binary_iarchive ar(iss);
5806  ar >> *this;
5807  }
5808  }
5809  }
5810  }
5811  catch (...)
5812  {
5813  LOG_PRINT_L1("Failed to load encrypted cache, trying unencrypted");
5814  try {
5815  std::stringstream iss;
5816  iss << buf;
5818  ar >> *this;
5819  }
5820  catch (...)
5821  {
5822  LOG_PRINT_L0("Failed to open portable binary, trying unportable");
5823  boost::filesystem::copy_file(m_wallet_file, m_wallet_file + ".unportable", boost::filesystem::copy_option::overwrite_if_exists);
5824  std::stringstream iss;
5825  iss.str("");
5826  iss << buf;
5827  boost::archive::binary_iarchive ar(iss);
5828  ar >> *this;
5829  }
5830  }
5832  m_account_public_address.m_spend_public_key != m_account.get_keys().m_account_address.m_spend_public_key ||
5833  m_account_public_address.m_view_public_key != m_account.get_keys().m_account_address.m_view_public_key,
5834  error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file);
5835  }
5836 
5837  cryptonote::block genesis;
5838  generate_genesis(genesis);
5839  crypto::hash genesis_hash = get_block_hash(genesis);
5840 
5841  if (m_blockchain.empty())
5842  {
5843  m_blockchain.push_back(genesis_hash);
5844  m_last_block_reward = cryptonote::get_outs_etn_amount(genesis.miner_tx);
5845  }
5846  else
5847  {
5848  check_genesis(genesis_hash);
5849  }
5850 
5851  trim_hashchain();
5852 
5853  if (get_num_subaddress_accounts() == 0)
5854  add_subaddress_account(tr("Primary account"));
5855 
5856  try
5857  {
5858  find_and_save_rings(false);
5859  }
5860  catch (const std::exception &e)
5861  {
5862  MERROR("Failed to save rings, will try again next time");
5863  }
5864 
5865  try
5866  {
5867  m_message_store.read_from_file(get_multisig_wallet_state(), m_mms_file);
5868  }
5869  catch (const std::exception &e)
5870  {
5871  MERROR("Failed to initialize MMS, it will be unusable");
5872  }
5873 }
5874 //----------------------------------------------------------------------------------------------------
5875 void wallet2::trim_hashchain()
5876 {
5877  uint64_t height = m_checkpoints.get_max_height();
5878 
5879  for (const transfer_details &td: m_transfers)
5880  if (td.m_block_height < height)
5881  height = td.m_block_height;
5882 
5883  if (!m_blockchain.empty() && m_blockchain.size() == m_blockchain.offset())
5884  {
5885  MINFO("Fixing empty hashchain");
5888  m_daemon_rpc_mutex.lock();
5889  req.height = m_blockchain.size() - 1;
5890  bool r = invoke_http_json_rpc("/json_rpc", "getblockheaderbyheight", req, res, rpc_timeout);
5891  m_daemon_rpc_mutex.unlock();
5892  if (r && res.status == CORE_RPC_STATUS_OK)
5893  {
5895  epee::string_tools::hex_to_pod(res.block_header.hash, hash);
5896  m_blockchain.refill(hash);
5897  }
5898  else
5899  {
5900  MERROR("Failed to request block header from daemon, hash chain may be unable to sync till the wallet is loaded with a usable daemon");
5901  }
5902  }
5903  if (height > 0 && m_blockchain.size() > height)
5904  {
5905  --height;
5906  MDEBUG("trimming to " << height << ", offset " << m_blockchain.offset());
5907  m_blockchain.trim(height);
5908  }
5909 }
5910 //----------------------------------------------------------------------------------------------------
5911 void wallet2::check_genesis(const crypto::hash& genesis_hash) const {
5912  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");
5913 
5914  THROW_WALLET_EXCEPTION_IF(genesis_hash != m_blockchain.genesis(), error::wallet_internal_error, what);
5915 }
5916 //----------------------------------------------------------------------------------------------------
5917 std::string wallet2::path() const
5918 {
5919  return m_wallet_file;
5920 }
5921 //----------------------------------------------------------------------------------------------------
5922 void wallet2::store()
5923 {
5924  if (!m_wallet_file.empty())
5925  store_to("", epee::wipeable_string());
5926 }
5927 //----------------------------------------------------------------------------------------------------
5928 void wallet2::store_to(const std::string &path, const epee::wipeable_string &password)
5929 {
5930  trim_hashchain();
5931 
5932  // if file is the same, we do:
5933  // 1. save wallet to the *.new file
5934  // 2. remove old wallet file
5935  // 3. rename *.new to wallet_name
5936 
5937  // handle if we want just store wallet state to current files (ex store() replacement);
5938  bool same_file = true;
5939  if (!path.empty())
5940  {
5941  std::string canonical_path = boost::filesystem::canonical(m_wallet_file).string();
5942  size_t pos = canonical_path.find(path);
5943  same_file = pos != std::string::npos;
5944  }
5945 
5946 
5947  if (!same_file)
5948  {
5949  // check if we want to store to directory which doesn't exists yet
5950  boost::filesystem::path parent_path = boost::filesystem::path(path).parent_path();
5951 
5952  // if path is not exists, try to create it
5953  if (!parent_path.empty() && !boost::filesystem::exists(parent_path))
5954  {
5955  boost::system::error_code ec;
5956  if (!boost::filesystem::create_directories(parent_path, ec))
5957  {
5958  throw std::logic_error(ec.message());
5959  }
5960  }
5961  }
5962  // preparing wallet data
5963  std::stringstream oss;
5965  ar << *this;
5966 
5967  wallet2::cache_file_data cache_file_data = boost::value_initialized<wallet2::cache_file_data>();
5968  cache_file_data.cache_data = oss.str();
5969  std::string cipher;
5970  cipher.resize(cache_file_data.cache_data.size());
5971  cache_file_data.iv = crypto::rand<crypto::chacha_iv>();
5972  crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), m_cache_key, cache_file_data.iv, &cipher[0]);
5973  cache_file_data.cache_data = cipher;
5974 
5975  const std::string new_file = same_file ? m_wallet_file + ".new" : path;
5976  const std::string old_file = m_wallet_file;
5977  const std::string old_keys_file = m_keys_file;
5978  const std::string old_address_file = m_wallet_file + ".address.txt";
5979  const std::string old_mms_file = m_mms_file;
5980 
5981  // save keys to the new file
5982  // if we here, main wallet file is saved and we only need to save keys and address files
5983  if (!same_file) {
5984  prepare_file_names(path);
5985  bool r = store_keys(m_keys_file, password, false);
5987  if (boost::filesystem::exists(old_address_file))
5988  {
5989  // save address to the new file
5990  const std::string address_file = m_wallet_file + ".address.txt";
5991  r = file_io_utils::save_string_to_file(address_file, m_account.get_public_address_str(m_nettype));
5993  }
5994  // remove old wallet file
5995  r = boost::filesystem::remove(old_file);
5996  if (!r) {
5997  LOG_ERROR("error removing file: " << old_file);
5998  }
5999  // remove old keys file
6000  r = boost::filesystem::remove(old_keys_file);
6001  if (!r) {
6002  LOG_ERROR("error removing file: " << old_keys_file);
6003  }
6004  // remove old address file
6005  r = boost::filesystem::remove(old_address_file);
6006  if (!r) {
6007  LOG_ERROR("error removing file: " << old_address_file);
6008  }
6009  // remove old message store file
6010  if (boost::filesystem::exists(old_mms_file))
6011  {
6012  r = boost::filesystem::remove(old_mms_file);
6013  if (!r) {
6014  LOG_ERROR("error removing file: " << old_mms_file);
6015  }
6016  }
6017  } else {
6018  // save to new file
6019 #ifdef WIN32
6020  // On Windows avoid using std::ofstream which does not work with UTF-8 filenames
6021  // The price to pay is temporary higher memory consumption for string stream + binary archive
6022  std::ostringstream oss;
6023  binary_archive<true> oar(oss);
6025  if (success) {
6026  success = epee::file_io_utils::save_string_to_file(new_file, oss.str());
6027  }
6029 #else
6030  std::ofstream ostr;
6031  ostr.open(new_file, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);
6032  binary_archive<true> oar(ostr);
6034  ostr.close();
6035  THROW_WALLET_EXCEPTION_IF(!success || !ostr.good(), error::file_save_error, new_file);
6036 #endif
6037 
6038  // here we have "*.new" file, we need to rename it to be without ".new"
6039  std::error_code e = tools::replace_file(new_file, m_wallet_file);
6040  THROW_WALLET_EXCEPTION_IF(e, error::file_save_error, m_wallet_file, e);
6041  }
6042 
6043  if (m_message_store.get_active())
6044  {
6045  // While the "m_message_store" object of course always exist, a file for the message
6046  // store should only exist if the MMS is really active
6047  m_message_store.write_to_file(get_multisig_wallet_state(), m_mms_file);
6048  }
6049 
6050 }
6051 //----------------------------------------------------------------------------------------------------
6052 uint64_t wallet2::balance(uint32_t index_major, bool public_blockchain) const
6053 {
6054  uint64_t amount = 0;
6055  if(m_light_wallet)
6056  return m_light_wallet_unlocked_balance;
6057  for (const auto& i : balance_per_subaddress(index_major, public_blockchain))
6058  amount += i.second;
6059  return amount;
6060 }
6061 //----------------------------------------------------------------------------------------------------
6062 uint64_t wallet2::unlocked_balance(uint32_t index_major, bool public_blockchain, uint64_t *blocks_to_unlock) const
6063 {
6064  uint64_t amount = 0;
6065  if (blocks_to_unlock)
6066  *blocks_to_unlock = 0;
6067  if(m_light_wallet)
6068  return m_light_wallet_balance;
6069  for (const auto& i : unlocked_balance_per_subaddress(index_major, public_blockchain))
6070  {
6071  amount += i.second.first;
6072  if (blocks_to_unlock && i.second.second > *blocks_to_unlock)
6073  *blocks_to_unlock = i.second.second;
6074  }
6075  return amount;
6076 }
6077 //----------------------------------------------------------------------------------------------------
6078 std::map<uint32_t, uint64_t> wallet2::balance_per_subaddress(uint32_t index_major, bool public_blockchain) const
6079 {
6080  std::map<uint32_t, uint64_t> amount_per_subaddr;
6081  for (const auto& td: m_transfers)
6082  {
6083  if((public_blockchain && td.m_tx.version == 1) || (!public_blockchain && td.m_tx.version > 1))
6084  continue;
6085 
6086  if (td.m_subaddr_index.major == index_major && !td.m_spent && !td.m_frozen)
6087  {
6088  auto found = amount_per_subaddr.find(td.m_subaddr_index.minor);
6089  if (found == amount_per_subaddr.end())
6090  amount_per_subaddr[td.m_subaddr_index.minor] = td.amount();
6091  else
6092  found->second += td.amount();
6093  }
6094  }
6095  for (const auto& utx: m_unconfirmed_txs) {
6096  if ((public_blockchain && utx.second.m_tx.version == 1) || (!public_blockchain && utx.second.m_tx.version > 1))
6097  continue;
6098 
6099  if (utx.second.m_state != wallet2::unconfirmed_transfer_details::failed) {
6100  //HANDLE LOOPBACK OUTS INCLUDING CHANGE
6101  if(utx.second.m_tx.version > 1){
6102  for (const cryptonote::tx_out &out : utx.second.m_tx.vout) {
6103  if (out.target.type() == typeid(txout_to_key_public)) {
6104  // check whether this out is to one of our subaddresses
6105  auto target = boost::get<cryptonote::txout_to_key_public>(out.target);
6106  auto subaddr_found = m_subaddresses.find(target.address.m_spend_public_key);
6107  // if this out is to us
6108  // and the view key part of the destination matches that of our our subaddress,
6109  // and the major index of this subaddress corresponds to the current account we're getting balance for,
6110  // then add amount to balance
6111  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) {
6112  auto found = amount_per_subaddr.find(subaddr_found->second.minor);
6113  if (found == amount_per_subaddr.end())
6114  amount_per_subaddr[subaddr_found->second.minor] = out.amount;
6115  else
6116  found->second += out.amount;
6117  } else {
6118  continue;
6119  }
6120  }
6121  }
6122  }
6123  // CHANGE HANDLING FOR V1 TX
6124  // (NB LOOPBACK OUTS APART FROM CHANGE AREN'T FACTORED INTO BAL WHILST TX IS UNCONFIRMED
6125  // and there is no need to fix this (monero) issue as we've migrated to a transparent chain)
6126  if (utx.second.m_tx.version == 1 && utx.second.m_subaddr_account == index_major) {
6127  // all changes go to 0-th subaddress (in the current subaddress account)
6128  auto found = amount_per_subaddr.find(0);
6129  if (found == amount_per_subaddr.end())
6130  amount_per_subaddr[0] = utx.second.m_change;
6131  else
6132  found->second += utx.second.m_change;
6133  }
6134  }
6135  }
6136  return amount_per_subaddr;
6137 }
6138 //----------------------------------------------------------------------------------------------------
6139 std::map<uint32_t, std::pair<uint64_t, uint64_t>> wallet2::unlocked_balance_per_subaddress(uint32_t index_major, bool public_blockchain) const
6140 {
6141  std::map<uint32_t, std::pair<uint64_t, uint64_t>> amount_per_subaddr; //map of subaddr minor index : <amount,unlock t>
6142  const uint64_t blockchain_height = get_blockchain_current_height();
6143  //Figure out amount & blocks_to_unlock for the major subaddress index for each transfer
6144  for(const transfer_details& td: m_transfers)
6145  {
6146  if((public_blockchain && td.m_tx.version == 1) || (!public_blockchain && td.m_tx.version > 1))
6147  continue;
6148 
6149  if(td.m_subaddr_index.major == index_major && !td.m_spent && !td.m_frozen)
6150  {
6151  uint64_t amount = 0, blocks_to_unlock = 0;
6152  if (is_transfer_unlocked(td))
6153  {
6154  amount = td.amount();
6155  blocks_to_unlock = 0;
6156  }
6157  else
6158  {
6159 
6160  uint64_t v8height = m_nettype == TESTNET ? 446674 : 589169;
6161  uint16_t UNLOCK_WINDOW = td.m_block_height > v8height ? ETN_DEFAULT_TX_SPENDABLE_AGE_V8 : CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE;
6162  uint64_t unlock_height = td.m_block_height + std::max<uint64_t>(UNLOCK_WINDOW, CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS);
6163  if (td.m_tx.unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER && td.m_tx.unlock_time > unlock_height)
6164  unlock_height = td.m_tx.unlock_time;
6165  blocks_to_unlock = unlock_height > blockchain_height ? unlock_height - blockchain_height : 0;
6166  amount = 0;
6167  }
6168  auto found = amount_per_subaddr.find(td.m_subaddr_index.minor);
6169  // if we don't have this subaddress index (key) in our map, create a new entry, otherwise just add a new pair
6170  if (found == amount_per_subaddr.end())
6171  amount_per_subaddr[td.m_subaddr_index.minor] = std::make_pair(amount, blocks_to_unlock);
6172  else
6173  {
6174  found->second.first += amount;
6175  found->second.second = std::max(found->second.second, blocks_to_unlock);
6176  }
6177  }
6178  }
6179  return amount_per_subaddr;
6180 }
6181 //----------------------------------------------------------------------------------------------------
6182 uint64_t wallet2::balance_all(bool public_blockchain) const
6183 {
6184  uint64_t r = 0;
6185  for (uint32_t index_major = 0; index_major < get_num_subaddress_accounts(); ++index_major)
6186  r += balance(index_major, public_blockchain);
6187  return r;
6188 }
6189 //----------------------------------------------------------------------------------------------------
6190 uint64_t wallet2::unlocked_balance_all(bool public_blockchain, uint64_t *blocks_to_unlock) const
6191 {
6192  uint64_t r = 0;
6193  if (blocks_to_unlock)
6194  *blocks_to_unlock = 0;
6195  for (uint32_t index_major = 0; index_major < get_num_subaddress_accounts(); ++index_major)
6196  {
6197  uint64_t local_blocks_to_unlock;
6198  r += unlocked_balance(index_major, public_blockchain ,blocks_to_unlock ? &local_blocks_to_unlock : NULL);
6199  if (blocks_to_unlock)
6200  *blocks_to_unlock = std::max(*blocks_to_unlock, local_blocks_to_unlock);
6201  }
6202  return r;
6203 }
6204 //----------------------------------------------------------------------------------------------------
6205 void wallet2::get_transfers(wallet2::transfer_container& incoming_transfers) const
6206 {
6207  incoming_transfers = m_transfers;
6208 }
6209 //----------------------------------------------------------------------------------------------------
6210 void 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
6211 {
6212  auto range = m_payments.equal_range(payment_id);
6213  std::for_each(range.first, range.second, [&payments, &min_height, &subaddr_account, &subaddr_indices](const payment_container::value_type& x) {
6214  if (min_height < x.second.m_block_height &&
6215  (!subaddr_account || *subaddr_account == x.second.m_subaddr_index.major) &&
6216  (subaddr_indices.empty() || subaddr_indices.count(x.second.m_subaddr_index.minor) == 1))
6217  {
6218  payments.push_back(x.second);
6219  }
6220  });
6221 }
6222 //----------------------------------------------------------------------------------------------------
6223 void 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
6224 {
6225  auto range = std::make_pair(m_payments.begin(), m_payments.end());
6226  std::for_each(range.first, range.second, [&payments, &min_height, &max_height, &subaddr_account, &subaddr_indices](const payment_container::value_type& x) {
6227  if (min_height < x.second.m_block_height && max_height >= x.second.m_block_height &&
6228  (!subaddr_account || *subaddr_account == x.second.m_subaddr_index.major) &&
6229  (subaddr_indices.empty() || subaddr_indices.count(x.second.m_subaddr_index.minor) == 1))
6230  {
6231  payments.push_back(x);
6232  }
6233  });
6234 }
6235 //----------------------------------------------------------------------------------------------------
6236 void wallet2::get_payments_out(std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>>& confirmed_payments,
6237  uint64_t min_height, uint64_t max_height, const boost::optional<uint32_t>& subaddr_account, const std::set<uint32_t>& subaddr_indices) const
6238 {
6239  for (auto i = m_confirmed_txs.begin(); i != m_confirmed_txs.end(); ++i) {
6240  if (i->second.m_block_height <= min_height || i->second.m_block_height > max_height)
6241  continue;
6242  if (subaddr_account && *subaddr_account != i->second.m_subaddr_account)
6243  continue;
6244  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)
6245  continue;
6246  if (i->second.m_is_migration)
6247  continue;
6248  confirmed_payments.push_back(*i);
6249  }
6250 }//----------------------------------------------------------------------------------------------------
6251 void wallet2::get_payments_out_migration(std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>>& confirmed_payments,
6252  uint64_t min_height, uint64_t max_height, const boost::optional<uint32_t>& subaddr_account, const std::set<uint32_t>& subaddr_indices) const
6253 {
6254  for (auto i = m_confirmed_txs.begin(); i != m_confirmed_txs.end(); ++i) {
6255  if (i->second.m_block_height <= min_height || i->second.m_block_height > max_height)
6256  continue;
6257  if (subaddr_account && *subaddr_account != i->second.m_subaddr_account)
6258  continue;
6259  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)
6260  continue;
6261  if (!i->second.m_is_migration)
6262  continue;
6263  confirmed_payments.push_back(*i);
6264  }
6265 }
6266 //----------------------------------------------------------------------------------------------------
6267 void 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
6268 {
6269  for (auto i = m_unconfirmed_txs.begin(); i != m_unconfirmed_txs.end(); ++i) {
6270  if (subaddr_account && *subaddr_account != i->second.m_subaddr_account)
6271  continue;
6272  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)
6273  continue;
6274  unconfirmed_payments.push_back(*i);
6275  }
6276 }
6277 //----------------------------------------------------------------------------------------------------
6278 void 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
6279 {
6280  for (auto i = m_unconfirmed_payments.begin(); i != m_unconfirmed_payments.end(); ++i) {
6281  if ((!subaddr_account || *subaddr_account == i->second.m_pd.m_subaddr_index.major) &&
6282  (subaddr_indices.empty() || subaddr_indices.count(i->second.m_pd.m_subaddr_index.minor) == 1))
6283  unconfirmed_payments.push_back(*i);
6284  }
6285 }
6286 //----------------------------------------------------------------------------------------------------
6287 void wallet2::rescan_spent()
6288 {
6289  // This is RPC call that can take a long time if there are many outputs,
6290  // so we call it several times, in stripes, so we don't time out spuriously
6291  std::vector<int> spent_status;
6292  spent_status.reserve(m_transfers.size());
6293  const size_t chunk_size = 1000;
6294  for (size_t start_offset = 0; start_offset < m_transfers.size(); start_offset += chunk_size)
6295  {
6296  const size_t n_outputs = std::min<size_t>(chunk_size, m_transfers.size() - start_offset);
6297  MDEBUG("Calling is_key_image_spent on " << start_offset << " - " << (start_offset + n_outputs - 1) << ", out of " << m_transfers.size());
6299  COMMAND_RPC_IS_KEY_IMAGE_SPENT::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
6300  for (size_t n = start_offset; n < start_offset + n_outputs; ++n)
6301  req.key_images.push_back(string_tools::pod_to_hex(m_transfers[n].m_key_image));
6302  m_daemon_rpc_mutex.lock();
6303  bool r = invoke_http_json("/is_key_image_spent", req, daemon_resp, rpc_timeout);
6304  m_daemon_rpc_mutex.unlock();
6305  THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "is_key_image_spent");
6306  THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "is_key_image_spent");
6307  THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::is_key_image_spent_error, get_rpc_status(daemon_resp.status));
6308  THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != n_outputs, error::wallet_internal_error,
6309  "daemon returned wrong response for is_key_image_spent, wrong amounts count = " +
6310  std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(n_outputs));
6311  std::copy(daemon_resp.spent_status.begin(), daemon_resp.spent_status.end(), std::back_inserter(spent_status));
6312  }
6313 
6314  // update spent status
6315  for (size_t i = 0; i < m_transfers.size(); ++i)
6316  {
6317  transfer_details& td = m_transfers[i];
6318  // a view wallet may not know about key images
6319  if (!td.m_key_image_known || td.m_key_image_partial)
6320  continue;
6321  if (td.m_spent != (spent_status[i] != COMMAND_RPC_IS_KEY_IMAGE_SPENT::UNSPENT))
6322  {
6323  if (td.m_spent)
6324  {
6325  LOG_PRINT_L0("Marking output " << i << "(" << td.m_key_image << ") as unspent, it was marked as spent");
6326  set_unspent(i);
6327  td.m_spent_height = 0;
6328  }
6329  else
6330  {
6331  LOG_PRINT_L0("Marking output " << i << "(" << td.m_key_image << ") as spent, it was marked as unspent");
6332  set_spent(i, td.m_spent_height);
6333  // unknown height, if this gets reorged, it might still be missed
6334  }
6335  }
6336  }
6337 }
6338 //----------------------------------------------------------------------------------------------------
6339 void wallet2::rescan_blockchain(bool hard, bool refresh, bool keep_key_images)
6340 {
6341  CHECK_AND_ASSERT_THROW_MES(!hard || !keep_key_images, "Cannot preserve key images on hard rescan");
6342  const size_t transfers_cnt = m_transfers.size();
6343  crypto::hash transfers_hash{};
6344 
6345  if(hard)
6346  {
6347  clear();
6348  setup_new_blockchain();
6349  }
6350  else
6351  {
6352  if (keep_key_images && refresh)
6353  hash_m_transfers((int64_t) transfers_cnt, transfers_hash);
6354  clear_soft(keep_key_images);
6355  }
6356 
6357  if (refresh)
6358  this->refresh(false);
6359 
6360  if (refresh && keep_key_images)
6361  finish_rescan_bc_keep_key_images(transfers_cnt, transfers_hash);
6362 }
6363 //----------------------------------------------------------------------------------------------------
6364 bool wallet2::is_transfer_unlocked(const transfer_details& td) const
6365 {
6366  return is_transfer_unlocked(td.m_tx.unlock_time, td.m_block_height);
6367 }
6368 //----------------------------------------------------------------------------------------------------
6369 bool wallet2::is_transfer_unlocked(uint64_t unlock_time, uint64_t block_height) const
6370 {
6371  if(!is_tx_spendtime_unlocked(unlock_time, block_height))
6372  return false;
6373 
6374  uint64_t v8height = m_nettype == TESTNET ? 446674 : 589169;
6375  uint16_t UNLOCK_WINDOW = block_height > v8height ? ETN_DEFAULT_TX_SPENDABLE_AGE_V8 : CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE;
6376 
6377  if(block_height + UNLOCK_WINDOW > get_blockchain_current_height())
6378  return false;
6379 
6380  return true;
6381 }
6382 //----------------------------------------------------------------------------------------------------
6383 bool wallet2::is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_height) const
6384 {
6385  if(unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER)
6386  {
6387  //interpret as block index
6388  if(get_blockchain_current_height()-1 + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS >= unlock_time)
6389  return true;
6390  else
6391  return false;
6392  }else
6393  {
6394  //interpret as time
6395  uint64_t current_time = static_cast<uint64_t>(time(NULL));
6396  // XXX: this needs to be fast, so we'd need to get the starting heights
6397  // from the daemon to be correct once voting kicks in
6398  uint64_t v6height = m_nettype == TESTNET ? 190059 : 307499;
6400  if(current_time + leeway >= unlock_time)
6401  return true;
6402  else
6403  return false;
6404  }
6405  return false;
6406 }
6407 //----------------------------------------------------------------------------------------------------
6408 namespace
6409 {
6410  template<typename T>
6411  T pop_index(std::vector<T>& vec, size_t idx)
6412  {
6413  CHECK_AND_ASSERT_MES(!vec.empty(), T(), "Vector must be non-empty");
6414  CHECK_AND_ASSERT_MES(idx < vec.size(), T(), "idx out of bounds");
6415 
6416  T res = vec[idx];
6417  if (idx + 1 != vec.size())
6418  {
6419  vec[idx] = vec.back();
6420  }
6421  vec.resize(vec.size() - 1);
6422 
6423  return res;
6424  }
6425 
6426  template<typename T>
6427  T pop_random_value(std::vector<T>& vec)
6428  {
6429  CHECK_AND_ASSERT_MES(!vec.empty(), T(), "Vector must be non-empty");
6430 
6431  size_t idx = crypto::rand_idx(vec.size());
6432  return pop_index (vec, idx);
6433  }
6434 
6435  template<typename T>
6436  T pop_back(std::vector<T>& vec)
6437  {
6438  CHECK_AND_ASSERT_MES(!vec.empty(), T(), "Vector must be non-empty");
6439 
6440  T res = vec.back();
6441  vec.pop_back();
6442  return res;
6443  }
6444 
6445  template<typename T>
6446  void pop_if_present(std::vector<T>& vec, T e)
6447  {
6448  for (size_t i = 0; i < vec.size(); ++i)
6449  {
6450  if (e == vec[i])
6451  {
6452  pop_index (vec, i);
6453  return;
6454  }
6455  }
6456  }
6457 }
6458 //----------------------------------------------------------------------------------------------------
6459 // This returns a handwavy estimation of how much two outputs are related
6460 // If they're from the same tx, then they're fully related. From close block
6461 // heights, they're kinda related. The actual values don't matter, just
6462 // their ordering, but it could become more murky if we add scores later.
6463 float wallet2::get_output_relatedness(const transfer_details &td0, const transfer_details &td1) const
6464 {
6465  int dh;
6466 
6467  // expensive test, and same tx will fall onto the same block height below
6468  if (td0.m_txid == td1.m_txid)
6469  return 1.0f;
6470 
6471  // same block height -> possibly tx burst, or same tx (since above is disabled)
6472  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;
6473  if (dh == 0)
6474  return 0.9f;
6475 
6476  // adjacent blocks -> possibly tx burst
6477  if (dh == 1)
6478  return 0.8f;
6479 
6480  // could extract the payment id, and compare them, but this is a bit expensive too
6481 
6482  // similar block heights
6483  if (dh < 10)
6484  return 0.2f;
6485 
6486  // don't think these are particularly related
6487  return 0.0f;
6488 }
6489 //----------------------------------------------------------------------------------------------------
6490 size_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
6491 {
6492  std::vector<size_t> candidates;
6493  float best_relatedness = 1.0f;
6494  for (size_t n = 0; n < unused_indices.size(); ++n)
6495  {
6496  const transfer_details &candidate = transfers[unused_indices[n]];
6497  float relatedness = 0.0f;
6498  for (std::vector<size_t>::const_iterator i = selected_transfers.begin(); i != selected_transfers.end(); ++i)
6499  {
6500  float r = get_output_relatedness(candidate, transfers[*i]);
6501  if (r > relatedness)
6502  {
6503  relatedness = r;
6504  if (relatedness == 1.0f)
6505  break;
6506  }
6507  }
6508 
6509  if (relatedness < best_relatedness)
6510  {
6511  best_relatedness = relatedness;
6512  candidates.clear();
6513  }
6514 
6515  if (relatedness == best_relatedness)
6516  candidates.push_back(n);
6517  }
6518 
6519  // we have all the least related outputs in candidates, so we can pick either
6520  // the smallest, or a random one, depending on request
6521  size_t idx;
6522  if (smallest)
6523  {
6524  idx = 0;
6525  for (size_t n = 0; n < candidates.size(); ++n)
6526  {
6527  const transfer_details &td = transfers[unused_indices[candidates[n]]];
6528  if (td.amount() < transfers[unused_indices[candidates[idx]]].amount())
6529  idx = n;
6530  }
6531  }
6532  else
6533  {
6534  idx = crypto::rand_idx(candidates.size());
6535  }
6536  return pop_index (unused_indices, candidates[idx]);
6537 }
6538 //----------------------------------------------------------------------------------------------------
6539 size_t wallet2::pop_best_value(std::vector<size_t> &unused_indices, const std::vector<size_t>& selected_transfers, bool smallest) const
6540 {
6541  return pop_best_value_from(m_transfers, unused_indices, selected_transfers, smallest);
6542 }
6543 //----------------------------------------------------------------------------------------------------
6544 // Select random input sources for transaction.
6545 // returns:
6546 // direct return: amount of etn found
6547 // modified reference: selected_transfers, a list of iterators/indices of input sources
6548 uint64_t wallet2::select_transfers(uint64_t needed_etn, std::vector<size_t> unused_transfers_indices, std::vector<size_t>& selected_transfers) const
6549 {
6550  uint64_t found_etn = 0;
6551  selected_transfers.reserve(unused_transfers_indices.size());
6552  while (found_etn < needed_etn && !unused_transfers_indices.empty())
6553  {
6554  size_t idx = pop_best_value(unused_transfers_indices, selected_transfers);
6555 
6556  const transfer_container::const_iterator it = m_transfers.begin() + idx;
6557  selected_transfers.push_back(idx);
6558  found_etn += it->amount();
6559  }
6560 
6561  return found_etn;
6562 }
6563 //----------------------------------------------------------------------------------------------------
6564 void 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)
6565 {
6566  unconfirmed_transfer_details& utd = m_unconfirmed_txs[cryptonote::get_transaction_hash(tx)];
6567  utd.m_amount_in = amount_in;
6568  utd.m_amount_out = 0;
6569 
6570  if(tx.version == 1){
6571  for (const auto &d: dests)
6572  utd.m_amount_out += d.amount;
6573  utd.m_amount_out += change_amount; // dests does not contain change
6574  utd.m_change = change_amount;
6575  utd.m_dests = dests;
6576  } else {
6577  // grab the input owner keys/address by using the subaddr indicies used for the transaction
6578  std::vector<account_public_address> input_addresses;
6579  for (auto minor_index : subaddr_indices) {
6580  cryptonote::subaddress_index index{subaddr_account, minor_index};
6581  input_addresses.push_back(get_subaddress(index));
6582  }
6583 
6584  //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.
6585  // If one or more outs do not go to an input address, we consider ALL other outputs as change outputs
6586  std::unordered_set<uint32_t> change_indexes;
6587  for (size_t i = 0; i < tx.vout.size(); ++i) {
6588  for (auto input_address : input_addresses) {
6589  if (boost::get<txout_to_key_public>(tx.vout[i].target).address == input_address) {
6590  change_indexes.insert(i);
6591  continue;
6592  }
6593  }
6594  }
6595 
6596  // if this is true we have a sweep tx so clear all change out indexes
6597  if (change_indexes.size() == tx.vout.size()) {
6598  change_indexes.clear();
6599  }
6600 
6601  int64_t total_change = 0;
6602  for (auto &change_index : change_indexes)
6603  total_change += tx.vout[change_index].amount;
6604  utd.m_change = total_change;
6605 
6606  //todo: optimise & refactor
6607  // fill destinations
6608  for (size_t i = 0; i < tx.vout.size(); ++i) {
6609  if (change_indexes.find(i) == change_indexes.end()) { // only include non-change outs as dests
6610  auto output = boost::get<txout_to_key_public>(tx.vout[i].target); // grab output from the tx
6611  //predicate for comparison later on
6612  auto pred = [output](const tx_destination_entry &destination) {
6613  return destination.addr == output.address;
6614  };
6615 
6616  //search our working list of destinations in entry, and either add output amount to the
6617  // running total in the case of a match, or add a new destination otherwise
6618  auto dest_ptr = std::find_if(std::begin(utd.m_dests),
6619  std::end(utd.m_dests), pred);
6620  if (dest_ptr != std::end(utd.m_dests)) {
6621  dest_ptr->amount += tx.vout[i].amount;
6622  } else {
6623  utd.m_dests.push_back(tx_destination_entry(
6624  tx.vout[i].amount,
6625  output.address,
6626  output.m_address_prefix ==
6627  get_config(this->m_nettype).CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX ? true : false
6628  ));
6629  }
6630  }
6631  }
6632  //amount out is the sum of destinations and change (if loopback tx change = 0 so this still checks out)
6633  for (const auto &d: utd.m_dests)
6634  utd.m_amount_out += d.amount;
6635  utd.m_amount_out += total_change;
6636  }
6637 
6638  utd.m_tx = (const cryptonote::transaction_prefix&)tx;
6639  utd.m_sent_time = time(NULL);
6640  utd.m_payment_id = payment_id;
6641  utd.m_state = wallet2::unconfirmed_transfer_details::pending;
6642  utd.m_timestamp = time(NULL);
6643  utd.m_subaddr_account = subaddr_account;
6644  utd.m_subaddr_indices = subaddr_indices;
6645  for (const auto &in: tx.vin)
6646  {
6647  if (in.type() != typeid(cryptonote::txin_to_key))
6648  continue;
6649  const auto &txin = boost::get<cryptonote::txin_to_key>(in);
6650  utd.m_rings.push_back(std::make_pair(txin.k_image, txin.key_offsets));
6651  }
6652 }
6653 
6654 //----------------------------------------------------------------------------------------------------
6655 crypto::hash wallet2::get_payment_id(const pending_tx &ptx) const
6656 {
6657  std::vector<tx_extra_field> tx_extra_fields;
6658  parse_tx_extra(ptx.tx.extra, tx_extra_fields); // ok if partially parsed
6659  tx_extra_nonce extra_nonce;
6660  crypto::hash payment_id = null_hash;
6661  if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
6662  {
6663  crypto::hash8 payment_id8 = null_hash8;
6664  if(get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
6665  {
6666  if (ptx.dests.empty())
6667  {
6668  MWARNING("Encrypted payment id found, but no destinations public key, cannot decrypt");
6669  return crypto::null_hash;
6670  }
6671  if (m_account.get_device().decrypt_payment_id(payment_id8, ptx.dests[0].addr.m_view_public_key, ptx.tx_key))
6672  {
6673  memcpy(payment_id.data, payment_id8.data, 8);
6674  }
6675  }
6676  else if (!get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
6677  {
6678  payment_id = crypto::null_hash;
6679  }
6680  }
6681  return payment_id;
6682 }
6683 
6684 //----------------------------------------------------------------------------------------------------
6685 // take a pending tx and actually send it to the daemon
6686 void wallet2::commit_tx(pending_tx& ptx)
6687 {
6688  using namespace cryptonote;
6689 
6690  if(m_light_wallet)
6691  {
6694  oreq.address = get_account().get_public_address_str(m_nettype);
6695  oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
6697  m_daemon_rpc_mutex.lock();
6698  bool r = invoke_http_json("/submit_raw_tx", oreq, ores, rpc_timeout, "POST");
6699  m_daemon_rpc_mutex.unlock();
6701  // MyMonero and OpenMonero use different status strings
6702  THROW_WALLET_EXCEPTION_IF(ores.status != "OK" && ores.status != "success" , error::tx_rejected, ptx.tx, get_rpc_status(ores.status), ores.error);
6703  }
6704  else
6705  {
6706  // Normal submit
6709  req.do_not_relay = false;
6710  req.do_sanity_checks = true;
6711  COMMAND_RPC_SEND_RAW_TX::response daemon_send_resp;
6712  m_daemon_rpc_mutex.lock();
6713  bool r = invoke_http_json("/sendrawtransaction", req, daemon_send_resp, rpc_timeout);
6714  m_daemon_rpc_mutex.unlock();
6715  THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "sendrawtransaction");
6716  THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "sendrawtransaction");
6717  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));
6718  // sanity checks
6719  for (size_t idx: ptx.selected_transfers)
6720  {
6721  THROW_WALLET_EXCEPTION_IF(idx >= m_transfers.size(), error::wallet_internal_error,
6722  "Bad output index in selected transfers: " + boost::lexical_cast<std::string>(idx));
6723  }
6724  }
6725  crypto::hash txid;
6726 
6727  txid = get_transaction_hash(ptx.tx);
6728  crypto::hash payment_id = crypto::null_hash;
6729  std::vector<cryptonote::tx_destination_entry> dests;
6730  uint64_t amount_in = 0;
6731  if (store_tx_info())
6732  {
6733  payment_id = get_payment_id(ptx);
6734  dests = ptx.dests;
6735  for(size_t idx: ptx.selected_transfers)
6736  amount_in += m_transfers[idx].amount();
6737  }
6738  add_unconfirmed_tx(ptx.tx, amount_in, dests, payment_id, ptx.change_dts.amount, ptx.construction_data.subaddr_account, ptx.construction_data.subaddr_indices);
6739  if (store_tx_info())
6740  {
6741  m_tx_keys.insert(std::make_pair(txid, ptx.tx_key));
6742  m_additional_tx_keys.insert(std::make_pair(txid, ptx.additional_tx_keys));
6743  }
6744 
6745  LOG_PRINT_L2("transaction " << txid << " generated ok and sent to daemon, key_images: [" << ptx.key_images << "]");
6746 
6747  for(size_t idx: ptx.selected_transfers)
6748  {
6749  set_spent(idx, 0);
6750  }
6751 
6752  // tx generated, get rid of used k values
6753  for (size_t idx: ptx.selected_transfers)
6754  m_transfers[idx].m_multisig_k.clear();
6755 
6756  //fee includes dust if dust policy specified it.
6757  LOG_PRINT_L1("Transaction successfully sent. <" << txid << ">" << ENDL
6758  << "Commission: " << print_etn(ptx.fee) << " (dust sent to dust addr: " << print_etn((ptx.dust_added_to_fee ? 0 : ptx.dust)) << ")" << ENDL
6759  << "Pre V10 Balance: " << print_etn(balance(ptx.construction_data.subaddr_account, false)) << ENDL
6760  << "Pre V10 Unlocked Balance: " << print_etn(unlocked_balance(ptx.construction_data.subaddr_account, false)) << ENDL
6761  << "Public Chain Balance: " << print_etn(balance(ptx.construction_data.subaddr_account, true)) << ENDL
6762  << "Public Chain Unlocked Balance: " << print_etn(unlocked_balance(ptx.construction_data.subaddr_account, true)) << ENDL
6763  << "Please, wait for confirmation for your balance to be unlocked.");
6764 }
6765 
6766 void wallet2::commit_tx(std::vector<pending_tx>& ptx_vector)
6767 {
6768  for (auto & ptx : ptx_vector)
6769  {
6770  commit_tx(ptx);
6771  }
6772 }
6773 //----------------------------------------------------------------------------------------------------
6774 bool wallet2::save_tx(const std::vector<pending_tx>& ptx_vector, const std::string &filename) const
6775 {
6776  LOG_PRINT_L0("saving " << ptx_vector.size() << " transactions");
6777  std::string ciphertext = dump_tx_to_str(ptx_vector);
6778  if (ciphertext.empty())
6779  return false;
6780  return epee::file_io_utils::save_string_to_file(filename, ciphertext);
6781 }
6782 //----------------------------------------------------------------------------------------------------
6783 std::string wallet2::dump_tx_to_str(const std::vector<pending_tx> &ptx_vector) const
6784 {
6785  LOG_PRINT_L0("saving " << ptx_vector.size() << " transactions");
6786  unsigned_tx_set txs;
6787  for (auto &tx: ptx_vector)
6788  {
6789  // Short payment id is encrypted with tx_key.
6790  // Since sign_tx() generates new tx_keys and encrypts the payment id, we need to save the decrypted payment ID
6791  // Save tx construction_data to unsigned_tx_set
6792  txs.txes.push_back(get_construction_data_with_decrypted_short_payment_id(tx, m_account.get_device()));
6793  }
6794 
6795  txs.transfers = export_outputs();
6796  // save as binary
6797  std::ostringstream oss;
6799  try
6800  {
6801  ar << txs;
6802  }
6803  catch (...)
6804  {
6805  return std::string();
6806  }
6807  LOG_PRINT_L2("Saving unsigned tx data: " << oss.str());
6808  std::string ciphertext = encrypt_with_view_secret_key(oss.str());
6809 
6810  return std::string(UNSIGNED_TX_PREFIX) + ciphertext;
6811 }
6812 //----------------------------------------------------------------------------------------------------
6813 bool wallet2::load_unsigned_tx(const std::string &unsigned_filename, unsigned_tx_set &exported_txs) const
6814 {
6815  std::string s;
6816  boost::system::error_code errcode;
6817 
6818  if (!boost::filesystem::exists(unsigned_filename, errcode))
6819  {
6820  LOG_PRINT_L0("File " << unsigned_filename << " does not exist: " << errcode);
6821  return false;
6822  }
6823  if (!epee::file_io_utils::load_file_to_string(unsigned_filename.c_str(), s))
6824  {
6825  LOG_PRINT_L0("Failed to load from " << unsigned_filename);
6826  return false;
6827  }
6828 
6829  return parse_unsigned_tx_from_str(s, exported_txs);
6830 }
6831 //----------------------------------------------------------------------------------------------------
6832 bool wallet2::parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const
6833 {
6834  std::string s = unsigned_tx_st;
6835  const size_t magiclen = strlen(UNSIGNED_TX_PREFIX) - 1;
6836  if (strncmp(s.c_str(), UNSIGNED_TX_PREFIX, magiclen))
6837  {
6838  LOG_PRINT_L0("Bad magic from unsigned tx");
6839  return false;
6840  }
6841  s = s.substr(magiclen);
6842  const char version = s[0];
6843  s = s.substr(1);
6844  if (version == '\003')
6845  {
6846  try
6847  {
6848  std::istringstream iss(s);
6850  ar >> exported_txs;
6851  }
6852  catch (const std::exception &e)
6853  {
6854  LOG_PRINT_L0("Failed to parse data from unsigned tx: " << e.what());
6855  return false;
6856  }
6857  catch (...)
6858  {
6859  LOG_PRINT_L0("Failed to parse data from unsigned tx");
6860  return false;
6861  }
6862  }
6863  else if (version == '\004')
6864  {
6865  try
6866  {
6867  s = decrypt_with_view_secret_key(s);
6868  try
6869  {
6870  std::istringstream iss(s);
6872  ar >> exported_txs;
6873  }
6874  catch (const std::exception &e)
6875  {
6876  LOG_PRINT_L0("Failed to parse decrypted data from unsigned tx: " << e.what());
6877  return false;
6878  }
6879  }
6880  catch (const std::exception &e)
6881  {
6882  LOG_PRINT_L0("Failed to decrypt unsigned tx: " << e.what());
6883  return false;
6884  }
6885  catch(...)
6886  {
6887  LOG_PRINT_L0("Failed to parse decrypted data from unsigned tx");
6888  return false;
6889  }
6890  }
6891  else
6892  {
6893  LOG_PRINT_L0("Unsupported version in unsigned tx");
6894  return false;
6895  }
6896  LOG_PRINT_L1("Loaded tx unsigned data from binary: " << exported_txs.txes.size() << " transactions");
6897 
6898  return true;
6899 }
6900 //----------------------------------------------------------------------------------------------------
6901 bool 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)
6902 {
6903  unsigned_tx_set exported_txs;
6904  if(!load_unsigned_tx(unsigned_filename, exported_txs))
6905  return false;
6906 
6907  if (accept_func && !accept_func(exported_txs))
6908  {
6909  LOG_PRINT_L1("Transactions rejected by callback");
6910  return false;
6911  }
6912  return sign_tx(exported_txs, signed_filename, txs, export_raw);
6913 }
6914 //----------------------------------------------------------------------------------------------------
6915 bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pending_tx> &txs, signed_tx_set &signed_txes)
6916 {
6917  import_outputs(exported_txs.transfers);
6918 
6919  // sign the transactions
6920  for (size_t n = 0; n < exported_txs.txes.size(); ++n)
6921  {
6922  tools::wallet2::tx_construction_data &sd = exported_txs.txes[n];
6923  THROW_WALLET_EXCEPTION_IF(sd.sources.empty(), error::wallet_internal_error, "Empty sources");
6924  LOG_PRINT_L1(" " << (n+1) << ": " << sd.sources.size() << " inputs, ring size " << sd.sources[0].outputs.size());
6925  signed_txes.ptx.push_back(pending_tx());
6926  tools::wallet2::pending_tx &ptx = signed_txes.ptx.back();
6927  rct::RCTConfig rct_config = sd.rct_config;
6928  crypto::secret_key tx_key;
6929  std::vector<crypto::secret_key> additional_tx_keys;
6930  rct::multisig_out msout;
6931 
6932  // 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.
6933  // todo: 4.0.0.0 Migrate vs send regular tx.
6934  uint16_t hard_fork_version = use_fork_rules(CURRENT_HARDFORK_VERSION, 0) ? CURRENT_HARDFORK_VERSION : (CURRENT_HARDFORK_VERSION - 1);
6935  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);
6937  // we don't test tx size, because we don't know the current limit, due to not having a blockchain,
6938  // and it's a bit pointless to fail there anyway, since it'd be a (good) guess only. We sign anyway,
6939  // and if we really go over limit, the daemon will reject when it gets submitted. Chances are it's
6940  // OK anyway since it was generated in the first place, and rerolling should be within a few bytes.
6941 
6942  // normally, the tx keys are saved in commit_tx, when the tx is actually sent to the daemon.
6943  // we can't do that here since the tx will be sent from the compromised wallet, which we don't want
6944  // to see that info, so we save it here
6945  if (store_tx_info())
6946  {
6947  const crypto::hash txid = get_transaction_hash(ptx.tx);
6948  m_tx_keys.insert(std::make_pair(txid, tx_key));
6949  m_additional_tx_keys.insert(std::make_pair(txid, additional_tx_keys));
6950  }
6951 
6952  std::string key_images;
6953 
6954  if(ptx.tx.version < 3) {
6955  bool all_are_txin_to_key = std::all_of(ptx.tx.vin.begin(), ptx.tx.vin.end(), [&](const txin_v &s_e) -> bool {
6956  CHECKED_GET_SPECIFIC_VARIANT(s_e, const txin_to_key, in, false);
6957  key_images += boost::to_string(in.k_image) + " ";
6958  return true;
6959  });
6960  THROW_WALLET_EXCEPTION_IF(!all_are_txin_to_key, error::unexpected_txin_type, ptx.tx);
6961  }else{
6962  bool all_are_txin_to_key_public = std::all_of(ptx.tx.vin.begin(), ptx.tx.vin.end(), [&](const txin_v &s_e) -> bool {
6963  CHECKED_GET_SPECIFIC_VARIANT(s_e, const txin_to_key_public, in, false);
6964  return true;
6965  });
6966  THROW_WALLET_EXCEPTION_IF(!all_are_txin_to_key_public, error::unexpected_txin_type, ptx.tx);
6967  }
6968  if(ptx.tx.version > 1) {
6969  bool all_are_txout_to_key_public = std::all_of(ptx.tx.vout.begin(), ptx.tx.vout.end(), [&](const tx_out &s_e) -> bool {
6970  CHECKED_GET_SPECIFIC_VARIANT(s_e.target, const txout_to_key_public, in, false);
6971  return true;
6972  });
6973  THROW_WALLET_EXCEPTION_IF(!all_are_txout_to_key_public, error::unexpected_txout_type, ptx.tx);
6974  }
6975  ptx.key_images = key_images;
6976  ptx.fee = 0;
6977  for (const auto &i: sd.sources) ptx.fee += i.amount;
6978  for (const auto &i: sd.splitted_dsts) ptx.fee -= i.amount;
6979  ptx.dust = 0;
6980  ptx.dust_added_to_fee = false;
6981  ptx.change_dts = sd.change_dts;
6983  ptx.tx_key = rct::rct2sk(rct::identity()); // don't send it back to the untrusted view wallet
6984  ptx.dests = sd.dests;
6985  ptx.construction_data = sd;
6986 
6987  txs.push_back(ptx);
6988 
6989  // add tx keys only to ptx
6990  txs.back().tx_key = tx_key;
6991  txs.back().additional_tx_keys = additional_tx_keys;
6992  }
6993 
6994  // add key image mapping for these txes
6995  const account_keys &keys = get_account().get_keys();
6996  hw::device &hwdev = m_account.get_device();
6997  for (size_t n = 0; n < exported_txs.txes.size(); ++n)
6998  {
6999  const cryptonote::transaction &tx = signed_txes.ptx[n].tx;
7000 
7001  crypto::key_derivation derivation;
7002  std::vector<crypto::key_derivation> additional_derivations;
7003 
7004  // compute public keys from out secret keys
7005  crypto::public_key tx_pub_key;
7006  crypto::secret_key_to_public_key(txs[n].tx_key, tx_pub_key);
7007  std::vector<crypto::public_key> additional_tx_pub_keys;
7008  for (const crypto::secret_key &skey: txs[n].additional_tx_keys)
7009  {
7010  additional_tx_pub_keys.resize(additional_tx_pub_keys.size() + 1);
7011  crypto::secret_key_to_public_key(skey, additional_tx_pub_keys.back());
7012  }
7013 
7014  // compute derivations
7016  if (!hwdev.generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation))
7017  {
7018  MWARNING("Failed to generate key derivation from tx pubkey in " << cryptonote::get_transaction_hash(tx) << ", skipping");
7019  static_assert(sizeof(derivation) == sizeof(rct::key), "Mismatched sizes of key_derivation and rct::key");
7020  memcpy(&derivation, rct::identity().bytes, sizeof(derivation));
7021  }
7022  for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
7023  {
7024  additional_derivations.push_back({});
7025  if (!hwdev.generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back()))
7026  {
7027  MWARNING("Failed to generate key derivation from additional tx pubkey in " << cryptonote::get_transaction_hash(tx) << ", skipping");
7028  memcpy(&additional_derivations.back(), rct::identity().bytes, sizeof(crypto::key_derivation));
7029  }
7030  }
7031 
7032  for (size_t i = 0; i < tx.vout.size(); ++i)
7033  {
7034  if (tx.vout[i].target.type() != typeid(cryptonote::txout_to_key))
7035  continue;
7036  const cryptonote::txout_to_key &out = boost::get<cryptonote::txout_to_key>(tx.vout[i].target);
7037  // if this output is back to this wallet, we can calculate its key image already
7038  if (!is_out_to_acc_precomp(m_subaddresses, out.key, derivation, additional_derivations, i, hwdev))
7039  continue;
7040  crypto::key_image ki;
7041  cryptonote::keypair in_ephemeral;
7042  if (generate_key_image_helper(keys, m_subaddresses, out.key, tx_pub_key, additional_tx_pub_keys, i, in_ephemeral, ki, hwdev))
7043  signed_txes.tx_key_images[out.key] = ki;
7044  else
7045  MERROR("Failed to calculate key image");
7046  }
7047  }
7048 
7049  // add key images
7050  signed_txes.key_images.resize(m_transfers.size());
7051  for (size_t i = 0; i < m_transfers.size(); ++i)
7052  {
7053  if (!m_transfers[i].m_key_image_known || m_transfers[i].m_key_image_partial)
7054  LOG_PRINT_L0("WARNING: key image not known in signing wallet at index " << i);
7055  signed_txes.key_images[i] = m_transfers[i].m_key_image;
7056  }
7057 
7058  return true;
7059 }
7060 //----------------------------------------------------------------------------------------------------
7061 bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_filename, std::vector<wallet2::pending_tx> &txs, bool export_raw)
7062 {
7063  // sign the transactions
7064  signed_tx_set signed_txes;
7065  std::string ciphertext = sign_tx_dump_to_str(exported_txs, txs, signed_txes);
7066  if (ciphertext.empty())
7067  {
7068  LOG_PRINT_L0("Failed to sign unsigned_tx_set");
7069  return false;
7070  }
7071 
7072  if (!epee::file_io_utils::save_string_to_file(signed_filename, ciphertext))
7073  {
7074  LOG_PRINT_L0("Failed to save file to " << signed_filename);
7075  return false;
7076  }
7077  // export signed raw tx without encryption
7078  if (export_raw)
7079  {
7080  for (size_t i = 0; i < signed_txes.ptx.size(); ++i)
7081  {
7082  std::string tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(signed_txes.ptx[i].tx));
7083  std::string raw_filename = signed_filename + "_raw" + (signed_txes.ptx.size() == 1 ? "" : ("_" + std::to_string(i)));
7084  if (!epee::file_io_utils::save_string_to_file(raw_filename, tx_as_hex))
7085  {
7086  LOG_PRINT_L0("Failed to save file to " << raw_filename);
7087  return false;
7088  }
7089  }
7090  }
7091  return true;
7092 }
7093 //----------------------------------------------------------------------------------------------------
7094 std::string wallet2::sign_tx_dump_to_str(unsigned_tx_set &exported_txs, std::vector<wallet2::pending_tx> &ptx, signed_tx_set &signed_txes)
7095 {
7096  // sign the transactions
7097  bool r = sign_tx(exported_txs, ptx, signed_txes);
7098  if (!r)
7099  {
7100  LOG_PRINT_L0("Failed to sign unsigned_tx_set");
7101  return std::string();
7102  }
7103 
7104  // save as binary
7105  std::ostringstream oss;
7107  try
7108  {
7109  ar << signed_txes;
7110  }
7111  catch(...)
7112  {
7113  return std::string();
7114  }
7115  LOG_PRINT_L3("Saving signed tx data (with encryption): " << oss.str());
7116  std::string ciphertext = encrypt_with_view_secret_key(oss.str());
7117  return std::string(SIGNED_TX_PREFIX) + ciphertext;
7118 }
7119 //----------------------------------------------------------------------------------------------------
7120 bool wallet2::load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func)
7121 {
7122  std::string s;
7123  boost::system::error_code errcode;
7124  signed_tx_set signed_txs;
7125 
7126  if (!boost::filesystem::exists(signed_filename, errcode))
7127  {
7128  LOG_PRINT_L0("File " << signed_filename << " does not exist: " << errcode);
7129  return false;
7130  }
7131 
7132  if (!epee::file_io_utils::load_file_to_string(signed_filename.c_str(), s))
7133  {
7134  LOG_PRINT_L0("Failed to load from " << signed_filename);
7135  return false;
7136  }
7137 
7138  return parse_tx_from_str(s, ptx, accept_func);
7139 }
7140 //----------------------------------------------------------------------------------------------------
7141 bool 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)
7142 {
7143  std::string s = signed_tx_st;
7144  boost::system::error_code errcode;
7145  signed_tx_set signed_txs;
7146 
7147  const size_t magiclen = strlen(SIGNED_TX_PREFIX) - 1;
7148  if (strncmp(s.c_str(), SIGNED_TX_PREFIX, magiclen))
7149  {
7150  LOG_PRINT_L0("Bad magic from signed transaction");
7151  return false;
7152  }
7153  s = s.substr(magiclen);
7154  const char version = s[0];
7155  s = s.substr(1);
7156  if (version == '\003')
7157  {
7158  try
7159  {
7160  std::istringstream iss(s);
7162  ar >> signed_txs;
7163  }
7164  catch (const std::exception &e)
7165  {
7166  LOG_PRINT_L0("Failed to parse data from signed transaction: " << e.what());
7167  return false;
7168  }
7169  catch(...)
7170  {
7171  LOG_PRINT_L0("Failed to parse data from signed transaction");
7172  return false;
7173  }
7174  }
7175  else if (version == '\004')
7176  {
7177  try
7178  {
7179  s = decrypt_with_view_secret_key(s);
7180  try
7181  {
7182  std::istringstream iss(s);
7184  ar >> signed_txs;
7185  }
7186  catch (const std::exception &e)
7187  {
7188  LOG_PRINT_L0("Failed to parse decrypted data from signed transaction: " << e.what());
7189  return false;
7190  }
7191  }
7192  catch (const std::exception &e)
7193  {
7194  LOG_PRINT_L0("Failed to decrypt signed transaction: " << e.what());
7195  return false;
7196  }
7197  catch(...)
7198  {
7199  LOG_PRINT_L0("Failed to decrypt signed transaction");
7200  return false;
7201  }
7202  }
7203  else
7204  {
7205  LOG_PRINT_L0("Unsupported version in signed transaction");
7206  return false;
7207  }
7208  LOG_PRINT_L0("Loaded signed tx data from binary: " << signed_txs.ptx.size() << " transactions");
7209  for (auto &c_ptx: signed_txs.ptx) LOG_PRINT_L0(cryptonote::obj_to_json_str(c_ptx.tx));
7210 
7211  if (accept_func && !accept_func(signed_txs))
7212  {
7213  LOG_PRINT_L1("Transactions rejected by callback");
7214  return false;
7215  }
7216 
7217  // import key images
7218  bool r = import_key_images(signed_txs.key_images);
7219  if (!r) return false;
7220 
7221  // remember key images for this tx, for when we get those txes from the blockchain
7222  for (const auto &e: signed_txs.tx_key_images)
7223  m_cold_key_images.insert(e);
7224 
7225  ptx = signed_txs.ptx;
7226 
7227  return true;
7228 }
7229 //----------------------------------------------------------------------------------------------------
7230 std::string wallet2::save_multisig_tx(multisig_tx_set txs)
7231 {
7232  LOG_PRINT_L0("saving " << txs.m_ptx.size() << " multisig transactions");
7233 
7234  // txes generated, get rid of used k values
7235  for (size_t n = 0; n < txs.m_ptx.size(); ++n)
7236  for (size_t idx: txs.m_ptx[n].construction_data.selected_transfers)
7237  m_transfers[idx].m_multisig_k.clear();
7238 
7239  // zero out some data we don't want to share
7240  for (auto &ptx: txs.m_ptx)
7241  {
7242  for (auto &e: ptx.construction_data.sources)
7243  e.multisig_kLRki.k = rct::zero();
7244  }
7245 
7246  for (auto &ptx: txs.m_ptx)
7247  {
7248  // Get decrypted payment id from pending_tx
7249  ptx.construction_data = get_construction_data_with_decrypted_short_payment_id(ptx, m_account.get_device());
7250  }
7251 
7252  // save as binary
7253  std::ostringstream oss;
7255  try
7256  {
7257  ar << txs;
7258  }
7259  catch (...)
7260  {
7261  return std::string();
7262  }
7263  LOG_PRINT_L2("Saving multisig unsigned tx data: " << oss.str());
7264  std::string ciphertext = encrypt_with_view_secret_key(oss.str());
7265  return std::string(MULTISIG_UNSIGNED_TX_PREFIX) + ciphertext;
7266 }
7267 //----------------------------------------------------------------------------------------------------
7268 bool wallet2::save_multisig_tx(const multisig_tx_set &txs, const std::string &filename)
7269 {
7270  std::string ciphertext = save_multisig_tx(txs);
7271  if (ciphertext.empty())
7272  return false;
7273  return epee::file_io_utils::save_string_to_file(filename, ciphertext);
7274 }
7275 //----------------------------------------------------------------------------------------------------
7276 wallet2::multisig_tx_set wallet2::make_multisig_tx_set(const std::vector<pending_tx>& ptx_vector) const
7277 {
7278  multisig_tx_set txs;
7279  txs.m_ptx = ptx_vector;
7280 
7281  for (const auto &msk: get_account().get_multisig_keys())
7282  {
7283  crypto::public_key pkey = get_multisig_signing_public_key(msk);
7284  for (auto &ptx: txs.m_ptx) for (auto &sig: ptx.multisig_sigs) sig.signing_keys.insert(pkey);
7285  }
7286 
7287  txs.m_signers.insert(get_multisig_signer_public_key());
7288  return txs;
7289 }
7290 
7291 std::string wallet2::save_multisig_tx(const std::vector<pending_tx>& ptx_vector)
7292 {
7293  return save_multisig_tx(make_multisig_tx_set(ptx_vector));
7294 }
7295 //----------------------------------------------------------------------------------------------------
7296 bool wallet2::save_multisig_tx(const std::vector<pending_tx>& ptx_vector, const std::string &filename)
7297 {
7298  std::string ciphertext = save_multisig_tx(ptx_vector);
7299  if (ciphertext.empty())
7300  return false;
7301  return epee::file_io_utils::save_string_to_file(filename, ciphertext);
7302 }
7303 //----------------------------------------------------------------------------------------------------
7304 bool wallet2::parse_multisig_tx_from_str(std::string multisig_tx_st, multisig_tx_set &exported_txs) const
7305 {
7306  const size_t magiclen = strlen(MULTISIG_UNSIGNED_TX_PREFIX);
7307  if (strncmp(multisig_tx_st.c_str(), MULTISIG_UNSIGNED_TX_PREFIX, magiclen))
7308  {
7309  LOG_PRINT_L0("Bad magic from multisig tx data");
7310  return false;
7311  }
7312  try
7313  {
7314  multisig_tx_st = decrypt_with_view_secret_key(std::string(multisig_tx_st, magiclen));
7315  }
7316  catch (const std::exception &e)
7317  {
7318  LOG_PRINT_L0("Failed to decrypt multisig tx data: " << e.what());
7319  return false;
7320  }
7321  try
7322  {
7323  std::istringstream iss(multisig_tx_st);
7325  ar >> exported_txs;
7326  }
7327  catch (...)
7328  {
7329  LOG_PRINT_L0("Failed to parse multisig tx data");
7330  return false;
7331  }
7332 
7333  // sanity checks
7334  for (const auto &ptx: exported_txs.m_ptx)
7335  {
7336  CHECK_AND_ASSERT_MES(ptx.selected_transfers.size() == ptx.tx.vin.size(), false, "Mismatched selected_transfers/vin sizes");
7337  for (size_t idx: ptx.selected_transfers)
7338  CHECK_AND_ASSERT_MES(idx < m_transfers.size(), false, "Transfer index out of range");
7339  CHECK_AND_ASSERT_MES(ptx.construction_data.selected_transfers.size() == ptx.tx.vin.size(), false, "Mismatched cd selected_transfers/vin sizes");
7340  for (size_t idx: ptx.construction_data.selected_transfers)
7341  CHECK_AND_ASSERT_MES(idx < m_transfers.size(), false, "Transfer index out of range");
7342  CHECK_AND_ASSERT_MES(ptx.construction_data.sources.size() == ptx.tx.vin.size(), false, "Mismatched sources/vin sizes");
7343  }
7344 
7345  return true;
7346 }
7347 //----------------------------------------------------------------------------------------------------
7348 bool wallet2::load_multisig_tx(cryptonote::blobdata s, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func)
7349 {
7350  if(!parse_multisig_tx_from_str(s, exported_txs))
7351  {
7352  LOG_PRINT_L0("Failed to parse multisig transaction from string");
7353  return false;
7354  }
7355 
7356  LOG_PRINT_L1("Loaded multisig tx unsigned data from binary: " << exported_txs.m_ptx.size() << " transactions");
7357  for (auto &ptx: exported_txs.m_ptx) LOG_PRINT_L0(cryptonote::obj_to_json_str(ptx.tx));
7358 
7359  if (accept_func && !accept_func(exported_txs))
7360  {
7361  LOG_PRINT_L1("Transactions rejected by callback");
7362  return false;
7363  }
7364 
7365  const bool is_signed = exported_txs.m_signers.size() >= m_multisig_threshold;
7366  if (is_signed)
7367  {
7368  for (const auto &ptx: exported_txs.m_ptx)
7369  {
7370  const crypto::hash txid = get_transaction_hash(ptx.tx);
7371  if (store_tx_info())
7372  {
7373  m_tx_keys.insert(std::make_pair(txid, ptx.tx_key));
7374  m_additional_tx_keys.insert(std::make_pair(txid, ptx.additional_tx_keys));
7375  }
7376  }
7377  }
7378 
7379  return true;
7380 }
7381 //----------------------------------------------------------------------------------------------------
7382 bool wallet2::load_multisig_tx_from_file(const std::string &filename, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func)
7383 {
7384  std::string s;
7385  boost::system::error_code errcode;
7386 
7387  if (!boost::filesystem::exists(filename, errcode))
7388  {
7389  LOG_PRINT_L0("File " << filename << " does not exist: " << errcode);
7390  return false;
7391  }
7392  if (!epee::file_io_utils::load_file_to_string(filename.c_str(), s))
7393  {
7394  LOG_PRINT_L0("Failed to load from " << filename);
7395  return false;
7396  }
7397 
7398  if (!load_multisig_tx(s, exported_txs, accept_func))
7399  {
7400  LOG_PRINT_L0("Failed to parse multisig tx data from " << filename);
7401  return false;
7402  }
7403  return true;
7404 }
7405 //----------------------------------------------------------------------------------------------------
7406 bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto::hash> &txids)
7407 {
7408  THROW_WALLET_EXCEPTION_IF(exported_txs.m_ptx.empty(), error::wallet_internal_error, "No tx found");
7409 
7410  const crypto::public_key local_signer = get_multisig_signer_public_key();
7411 
7412  THROW_WALLET_EXCEPTION_IF(exported_txs.m_signers.find(local_signer) != exported_txs.m_signers.end(),
7413  error::wallet_internal_error, "Transaction already signed by this private key");
7414  THROW_WALLET_EXCEPTION_IF(exported_txs.m_signers.size() > m_multisig_threshold,
7415  error::wallet_internal_error, "Transaction was signed by too many signers");
7416  THROW_WALLET_EXCEPTION_IF(exported_txs.m_signers.size() == m_multisig_threshold,
7417  error::wallet_internal_error, "Transaction is already fully signed");
7418 
7419  txids.clear();
7420 
7421  // sign the transactions
7422  for (size_t n = 0; n < exported_txs.m_ptx.size(); ++n)
7423  {
7424  tools::wallet2::pending_tx &ptx = exported_txs.m_ptx[n];
7425  THROW_WALLET_EXCEPTION_IF(ptx.multisig_sigs.empty(), error::wallet_internal_error, "No signatures found in multisig tx");
7427  LOG_PRINT_L1(" " << (n+1) << ": " << sd.sources.size() << " inputs, mixin " << (sd.sources[0].outputs.size()-1) <<
7428  ", signed by " << exported_txs.m_signers.size() << "/" << m_multisig_threshold);
7430  rct::multisig_out msout = ptx.multisig_sigs.front().msout;
7431  auto sources = sd.sources;
7432  rct::RCTConfig rct_config = sd.rct_config;
7433  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);
7435 
7437  error::wallet_internal_error, "Transaction prefix does not match data");
7438 
7439  // Tests passed, sign
7440  std::vector<unsigned int> indices;
7441  for (const auto &source: sources)
7442  indices.push_back(source.real_output);
7443 
7444  for (auto &sig: ptx.multisig_sigs)
7445  {
7446  if (sig.ignore.find(local_signer) == sig.ignore.end())
7447  {
7448  ptx.tx.rct_signatures = sig.sigs;
7449 
7450  rct::keyV k;
7451  for (size_t idx: sd.selected_transfers)
7452  k.push_back(get_multisig_k(idx, sig.used_L));
7453 
7454  rct::key skey = rct::zero();
7455  for (const auto &msk: get_account().get_multisig_keys())
7456  {
7457  crypto::public_key pmsk = get_multisig_signing_public_key(msk);
7458 
7459  if (sig.signing_keys.find(pmsk) == sig.signing_keys.end())
7460  {
7461  sc_add(skey.bytes, skey.bytes, rct::sk2rct(msk).bytes);
7462  sig.signing_keys.insert(pmsk);
7463  }
7464  }
7465  THROW_WALLET_EXCEPTION_IF(!rct::signMultisig(ptx.tx.rct_signatures, indices, k, sig.msout, skey),
7466  error::wallet_internal_error, "Failed signing, transaction likely malformed");
7467 
7468  sig.sigs = ptx.tx.rct_signatures;
7469  }
7470  }
7471 
7472  const bool is_last = exported_txs.m_signers.size() + 1 >= m_multisig_threshold;
7473  if (is_last)
7474  {
7475  // when the last signature on a multisig tx is made, we select the right
7476  // signature to plug into the final tx
7477  bool found = false;
7478  for (const auto &sig: ptx.multisig_sigs)
7479  {
7480  if (sig.ignore.find(local_signer) == sig.ignore.end() && !keys_intersect(sig.ignore, exported_txs.m_signers))
7481  {
7482  THROW_WALLET_EXCEPTION_IF(found, error::wallet_internal_error, "More than one transaction is final");
7483  ptx.tx.rct_signatures = sig.sigs;
7484  found = true;
7485  }
7486  }
7488  "Final signed transaction not found: this transaction was likely made without our export data, so we cannot sign it");
7489  const crypto::hash txid = get_transaction_hash(ptx.tx);
7490  if (store_tx_info())
7491  {
7492  m_tx_keys.insert(std::make_pair(txid, ptx.tx_key));
7493  m_additional_tx_keys.insert(std::make_pair(txid, ptx.additional_tx_keys));
7494  }
7495  txids.push_back(txid);
7496  }
7497  }
7498 
7499  // txes generated, get rid of used k values
7500  for (size_t n = 0; n < exported_txs.m_ptx.size(); ++n)
7501  for (size_t idx: exported_txs.m_ptx[n].construction_data.selected_transfers)
7502  m_transfers[idx].m_multisig_k.clear();
7503 
7504  exported_txs.m_signers.insert(get_multisig_signer_public_key());
7505 
7506  return true;
7507 }
7508 //----------------------------------------------------------------------------------------------------
7509 bool wallet2::sign_multisig_tx_to_file(multisig_tx_set &exported_txs, const std::string &filename, std::vector<crypto::hash> &txids)
7510 {
7511  bool r = sign_multisig_tx(exported_txs, txids);
7512  if (!r)
7513  return false;
7514  return save_multisig_tx(exported_txs, filename);
7515 }
7516 //----------------------------------------------------------------------------------------------------
7517 bool wallet2::sign_multisig_tx_from_file(const std::string &filename, std::vector<crypto::hash> &txids, std::function<bool(const multisig_tx_set&)> accept_func)
7518 {
7519  multisig_tx_set exported_txs;
7520  if(!load_multisig_tx_from_file(filename, exported_txs))
7521  return false;
7522 
7523  if (accept_func && !accept_func(exported_txs))
7524  {
7525  LOG_PRINT_L1("Transactions rejected by callback");
7526  return false;
7527  }
7528  return sign_multisig_tx_to_file(exported_txs, filename, txids);
7529 }
7530 //----------------------------------------------------------------------------------------------------
7531 uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm) const
7532 {
7533  static const struct
7534  {
7535  size_t count;
7536  uint64_t multipliers[4];
7537  }
7538  multipliers[] =
7539  {
7540  { 3, {1, 2, 3} },
7541  { 3, {1, 20, 166} },
7542  { 4, {1, 4, 20, 166} },
7543  { 4, {1, 2, 4, 8} },
7544  };
7545 
7546  if (fee_algorithm == -1)
7547  fee_algorithm = get_fee_algorithm();
7548 
7549  // 0 -> default (here, x1 till fee algorithm 2, x4 from it)
7550  if (priority == 0)
7551  priority = m_default_priority;
7552  if (priority == 0)
7553  {
7554  if (fee_algorithm == 2)
7555  priority = 2;
7556  else
7557  priority = 1;
7558  }
7559 
7560  THROW_WALLET_EXCEPTION_IF(fee_algorithm < 0 || fee_algorithm > 4, error::invalid_priority);
7561 
7562  // 1 to 3/4 are allowed as priorities
7563  const uint32_t max_priority = multipliers[fee_algorithm].count;
7564  if (priority >= 1 && priority <= max_priority)
7565  {
7566  return multipliers[fee_algorithm].multipliers[priority-1];
7567  }
7568 
7570  return 1;
7571 }
7572 //----------------------------------------------------------------------------------------------------
7573 uint64_t wallet2::get_dynamic_base_fee_estimate() const
7574 {
7575  uint64_t fee;
7576  boost::optional<std::string> result = m_node_rpc_proxy.get_dynamic_base_fee_estimate(FEE_ESTIMATE_GRACE_BLOCKS, fee);
7577  if (!result)
7578  return fee;
7579  const uint64_t base_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE) ? FEE_PER_BYTE : FEE_PER_KB_V6;
7580  LOG_PRINT_L1("Failed to query base fee, using " << print_etn(base_fee));
7581  return base_fee;
7582 }
7583 //----------------------------------------------------------------------------------------------------
7584 uint64_t wallet2::get_base_fee() const
7585 {
7586  if(m_light_wallet)
7587  {
7588  if (use_fork_rules(HF_VERSION_PER_BYTE_FEE))
7589  return m_light_wallet_per_kb_fee / 1024;
7590  else
7591  return m_light_wallet_per_kb_fee;
7592  }
7593  bool use_dyn_fee = use_fork_rules(HF_VERSION_DYNAMIC_FEE, -720 * 1);
7594  if (!use_dyn_fee)
7595  return FEE_PER_KB_V6;
7596 
7597  return get_dynamic_base_fee_estimate();
7598 }
7599 //----------------------------------------------------------------------------------------------------
7600 uint64_t wallet2::get_fee_quantization_mask() const
7601 {
7602  if(m_light_wallet)
7603  {
7604  return 1; // TODO
7605  }
7606  bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
7607  if (!use_per_byte_fee)
7608  return 1;
7609 
7610  uint64_t fee_quantization_mask;
7611  boost::optional<std::string> result = m_node_rpc_proxy.get_fee_quantization_mask(fee_quantization_mask);
7612  if (result)
7613  return 1;
7614  return fee_quantization_mask;
7615 }
7616 //----------------------------------------------------------------------------------------------------
7617 int wallet2::get_fee_algorithm() const
7618 {
7619  // changes at v3, v5, v8
7620  if (use_fork_rules(6, 0))
7621  return 3;
7622  if (use_fork_rules(5, 0))
7623  return 2;
7624  if (use_fork_rules(3, -720 * 14))
7625  return 1;
7626  return 0;
7627 }
7628 //------------------------------------------------------------------------------------------------------------------------------
7629 uint64_t wallet2::get_min_ring_size() const
7630 {
7631  if (use_fork_rules(HF_VERSION_MIN_MIXIN_10, 10))
7632  return 11;
7633  if (use_fork_rules(HF_VERSION_MIN_MIXIN_6, 10))
7634  return 7;
7635  if (use_fork_rules(HF_VERSION_MIN_MIXIN_4, 10))
7636  return 5;
7637  if (use_fork_rules(HF_VERSION_ENFORCE_0_DECOY_TXS, 10))
7638  return 1;
7639  if (use_fork_rules(HF_VERSION_MIN_MIXIN_2, 10))
7640  return 3;
7641  return 0;
7642 }
7643 //------------------------------------------------------------------------------------------------------------------------------
7644 uint64_t wallet2::get_max_ring_size() const
7645 {
7646  if (use_fork_rules(HF_VERSION_MAX_RING_11, 10))
7647  return 11;
7648  if (use_fork_rules(HF_VERSION_ENFORCE_0_DECOY_TXS, 10))
7649  return 1;
7650  return 0;
7651 }
7652 //------------------------------------------------------------------------------------------------------------------------------
7653 uint64_t wallet2::adjust_mixin(uint64_t mixin) const
7654 {
7655  const uint64_t min_ring_size = get_min_ring_size();
7656  if (mixin + 1 < min_ring_size)
7657  {
7658  MWARNING("Requested ring size " << (mixin + 1) << " too low, using " << min_ring_size);
7659  mixin = min_ring_size-1;
7660  }
7661  const uint64_t max_ring_size = get_max_ring_size();
7662  if (max_ring_size && mixin + 1 > max_ring_size)
7663  {
7664  MWARNING("Requested ring size " << (mixin + 1) << " too high, using " << max_ring_size);
7665  mixin = max_ring_size-1;
7666  }
7667  return mixin;
7668 }
7669 //----------------------------------------------------------------------------------------------------
7670 uint32_t wallet2::adjust_priority(uint32_t priority)
7671 {
7672  if (priority == 0 && m_default_priority == 0 && auto_low_priority())
7673  {
7674  try
7675  {
7676  // check if there's a backlog in the tx pool
7677  const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
7678  const uint64_t base_fee = get_base_fee();
7679  const uint64_t fee_multiplier = get_fee_multiplier(1);
7680  const double fee_level = fee_multiplier * base_fee * (use_per_byte_fee ? 1 : (12/(double)13 / (double)1024));
7681  const std::vector<std::pair<uint64_t, uint64_t>> blocks = estimate_backlog({std::make_pair(fee_level, fee_level)});
7682  if (blocks.size() != 1)
7683  {
7684  MERROR("Bad estimated backlog array size");
7685  return priority;
7686  }
7687  else if (blocks[0].first > 0)
7688  {
7689  MINFO("We don't use the low priority because there's a backlog in the tx pool.");
7690  return priority;
7691  }
7692 
7693  // get the current full reward zone
7694  uint64_t block_weight_limit = 0;
7695  const auto result = m_node_rpc_proxy.get_block_weight_limit(block_weight_limit);
7696  throw_on_rpc_response_error(result, "get_info");
7697  const uint64_t full_reward_zone = block_weight_limit / 2;
7698 
7699  // get the last N block headers and sum the block sizes
7700  const size_t N = 10;
7701  if (m_blockchain.size() < N)
7702  {
7703  MERROR("The blockchain is too short");
7704  return priority;
7705  }
7708  m_daemon_rpc_mutex.lock();
7709  getbh_req.start_height = m_blockchain.size() - N;
7710  getbh_req.end_height = m_blockchain.size() - 1;
7711  bool r = invoke_http_json_rpc("/json_rpc", "getblockheadersrange", getbh_req, getbh_res, rpc_timeout);
7712  m_daemon_rpc_mutex.unlock();
7713  THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getblockheadersrange");
7714  THROW_WALLET_EXCEPTION_IF(getbh_res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getblockheadersrange");
7715  THROW_WALLET_EXCEPTION_IF(getbh_res.status != CORE_RPC_STATUS_OK, error::get_blocks_error, get_rpc_status(getbh_res.status));
7716  if (getbh_res.headers.size() != N)
7717  {
7718  MERROR("Bad blockheaders size");
7719  return priority;
7720  }
7721  size_t block_weight_sum = 0;
7722  for (const cryptonote::block_header_response &i : getbh_res.headers)
7723  {
7724  block_weight_sum += i.block_weight;
7725  }
7726 
7727  // estimate how 'full' the last N blocks are
7728  const size_t P = 100 * block_weight_sum / (N * full_reward_zone);
7729  MINFO((boost::format("The last %d blocks fill roughly %d%% of the full reward zone.") % N % P).str());
7730  if (P > 80)
7731  {
7732  MINFO("We don't use the low priority because recent blocks are quite full.");
7733  return priority;
7734  }
7735  MINFO("We'll use the low priority because probably it's safe to do so.");
7736  return 1;
7737  }
7738  catch (const std::exception &e)
7739  {
7740  MERROR(e.what());
7741  }
7742  }
7743  return priority;
7744 }
7745 //----------------------------------------------------------------------------------------------------
7746 bool wallet2::set_ring_database(const std::string &filename)
7747 {
7748  m_ring_database = filename;
7749  MINFO("ringdb path set to " << filename);
7750  m_ringdb.reset();
7751  if (!m_ring_database.empty())
7752  {
7753  try
7754  {
7756  generate_genesis(b);
7757  m_ringdb.reset(new tools::ringdb(m_ring_database, epee::string_tools::pod_to_hex(get_block_hash(b))));
7758  }
7759  catch (const std::exception &e)
7760  {
7761  MERROR("Failed to initialize ringdb: " << e.what());
7762  m_ring_database = "";
7763  return false;
7764  }
7765  }
7766  return true;
7767 }
7768 
7769 crypto::chacha_key wallet2::get_ringdb_key()
7770 {
7771  if (!m_ringdb_key)
7772  {
7773  MINFO("caching ringdb key");
7774  crypto::chacha_key key;
7775  generate_chacha_key_from_secret_keys(key);
7776  m_ringdb_key = key;
7777  }
7778  return *m_ringdb_key;
7779 }
7780 
7781 void wallet2::register_devices(){
7783 }
7784 
7785 hw::device& wallet2::lookup_device(const std::string & device_descriptor){
7786  if (!m_devices_registered){
7787  m_devices_registered = true;
7788  register_devices();
7789  }
7790 
7791  return hw::get_device(device_descriptor);
7792 }
7793 
7794 bool wallet2::add_rings(const crypto::chacha_key &key, const cryptonote::transaction_prefix &tx)
7795 {
7796  if (!m_ringdb)
7797  return false;
7798  try { return m_ringdb->add_rings(key, tx); }
7799  catch (const std::exception &e) { return false; }
7800 }
7801 
7802 bool wallet2::add_rings(const cryptonote::transaction_prefix &tx)
7803 {
7804  try { return add_rings(get_ringdb_key(), tx); }
7805  catch (const std::exception &e) { return false; }
7806 }
7807 
7808 bool wallet2::remove_rings(const cryptonote::transaction_prefix &tx)
7809 {
7810  if (!m_ringdb)
7811  return false;
7812  try { return m_ringdb->remove_rings(get_ringdb_key(), tx); }
7813  catch (const std::exception &e) { return false; }
7814 }
7815 
7816 bool wallet2::get_ring(const crypto::chacha_key &key, const crypto::key_image &key_image, std::vector<uint64_t> &outs)
7817 {
7818  if (!m_ringdb)
7819  return false;
7820  try { return m_ringdb->get_ring(key, key_image, outs); }
7821  catch (const std::exception &e) { return false; }
7822 }
7823 
7824 bool wallet2::get_rings(const crypto::hash &txid, std::vector<std::pair<crypto::key_image, std::vector<uint64_t>>> &outs)
7825 {
7826  for (auto i: m_confirmed_txs)
7827  {
7828  if (txid == i.first)
7829  {
7830  for (const auto &x: i.second.m_rings)
7831  outs.push_back({x.first, cryptonote::relative_output_offsets_to_absolute(x.second)});
7832  return true;
7833  }
7834  }
7835  for (auto i: m_unconfirmed_txs)
7836  {
7837  if (txid == i.first)
7838  {
7839  for (const auto &x: i.second.m_rings)
7840  outs.push_back({x.first, cryptonote::relative_output_offsets_to_absolute(x.second)});
7841  return true;
7842  }
7843  }
7844  return false;
7845 }
7846 
7847 bool wallet2::get_ring(const crypto::key_image &key_image, std::vector<uint64_t> &outs)
7848 {
7849  try { return get_ring(get_ringdb_key(), key_image, outs); }
7850  catch (const std::exception &e) { return false; }
7851 }
7852 
7853 bool wallet2::set_ring(const crypto::key_image &key_image, const std::vector<uint64_t> &outs, bool relative)
7854 {
7855  if (!m_ringdb)
7856  return false;
7857 
7858  try { return m_ringdb->set_ring(get_ringdb_key(), key_image, outs, relative); }
7859  catch (const std::exception &e) { return false; }
7860 }
7861 
7862 bool wallet2::unset_ring(const std::vector<crypto::key_image> &key_images)
7863 {
7864  if (!m_ringdb)
7865  return false;
7866 
7867  try { return m_ringdb->remove_rings(get_ringdb_key(), key_images); }
7868  catch (const std::exception &e) { return false; }
7869 }
7870 
7871 bool wallet2::unset_ring(const crypto::hash &txid)
7872 {
7873  if (!m_ringdb)
7874  return false;
7875 
7878  req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
7879  req.decode_as_json = false;
7880  req.prune = true;
7881  m_daemon_rpc_mutex.lock();
7882  bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout);
7883  m_daemon_rpc_mutex.unlock();
7884  THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to get transaction from daemon");
7885  if (res.txs.empty())
7886  return false;
7887  THROW_WALLET_EXCEPTION_IF(res.txs.size(), error::wallet_internal_error, "Failed to get transaction from daemon");
7888 
7890  crypto::hash tx_hash;
7891  if (!get_pruned_tx(res.txs.front(), tx, tx_hash))
7892  return false;
7893  THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "Failed to get the right transaction from daemon");
7894 
7895  try { return m_ringdb->remove_rings(get_ringdb_key(), tx); }
7896  catch (const std::exception &e) { return false; }
7897 }
7898 
7899 bool wallet2::find_and_save_rings(bool force)
7900 {
7901  if (!force && m_ring_history_saved)
7902  return true;
7903  if (!m_ringdb)
7904  return false;
7905 
7908 
7909  MDEBUG("Finding and saving rings...");
7910 
7911  // get payments we made
7912  std::vector<crypto::hash> txs_hashes;
7913  std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>> payments;
7914  get_payments_out(payments, 0, std::numeric_limits<uint64_t>::max(), boost::none, std::set<uint32_t>());
7915  for (const std::pair<crypto::hash,wallet2::confirmed_transfer_details> &entry: payments)
7916  {
7917  const crypto::hash &txid = entry.first;
7918  txs_hashes.push_back(txid);
7919  }
7920 
7921  MDEBUG("Found " << std::to_string(txs_hashes.size()) << " transactions");
7922 
7923  // get those transactions from the daemon
7924  auto it = txs_hashes.begin();
7925  static const size_t SLICE_SIZE = 200;
7926  for (size_t slice = 0; slice < txs_hashes.size(); slice += SLICE_SIZE)
7927  {
7928  req.decode_as_json = false;
7929  req.prune = true;
7930  req.txs_hashes.clear();
7931  size_t ntxes = slice + SLICE_SIZE > txs_hashes.size() ? txs_hashes.size() - slice : SLICE_SIZE;
7932  for (size_t s = slice; s < slice + ntxes; ++s)
7933  req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txs_hashes[s]));
7934  bool r;
7935  {
7936  const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
7937  r = invoke_http_json("/gettransactions", req, res, rpc_timeout);
7938  }
7942  THROW_WALLET_EXCEPTION_IF(res.txs.size() != req.txs_hashes.size(), error::wallet_internal_error,
7943  "daemon returned wrong response for gettransactions, wrong txs count = " +
7944  std::to_string(res.txs.size()) + ", expected " + std::to_string(req.txs_hashes.size()));
7945 
7946  MDEBUG("Scanning " << res.txs.size() << " transactions");
7947  THROW_WALLET_EXCEPTION_IF(slice + res.txs.size() > txs_hashes.size(), error::wallet_internal_error, "Unexpected tx array size");
7948  for (size_t i = 0; i < res.txs.size(); ++i, ++it)
7949  {
7950  const auto &tx_info = res.txs[i];
7952  crypto::hash tx_hash;
7953  THROW_WALLET_EXCEPTION_IF(!get_pruned_tx(tx_info, tx, tx_hash), error::wallet_internal_error,
7954  "Failed to get transaction from daemon");
7955  THROW_WALLET_EXCEPTION_IF(!(tx_hash == *it), error::wallet_internal_error, "Wrong txid received");
7956  THROW_WALLET_EXCEPTION_IF(!add_rings(get_ringdb_key(), tx), error::wallet_internal_error, "Failed to save ring");
7957  }
7958  }
7959 
7960  MINFO("Found and saved rings for " << txs_hashes.size() << " transactions");
7961  m_ring_history_saved = true;
7962  return true;
7963 }
7964 
7965 bool wallet2::blackball_output(const std::pair<uint64_t, uint64_t> &output)
7966 {
7967  if (!m_ringdb)
7968  return false;
7969  try { return m_ringdb->blackball(output); }
7970  catch (const std::exception &e) { return false; }
7971 }
7972 
7973 bool wallet2::set_blackballed_outputs(const std::vector<std::pair<uint64_t, uint64_t>> &outputs, bool add)
7974 {
7975  if (!m_ringdb)
7976  return false;
7977  try
7978  {
7979  bool ret = true;
7980  if (!add)
7981  ret &= m_ringdb->clear_blackballs();
7982  ret &= m_ringdb->blackball(outputs);
7983  return ret;
7984  }
7985  catch (const std::exception &e) { return false; }
7986 }
7987 
7988 bool wallet2::unblackball_output(const std::pair<uint64_t, uint64_t> &output)
7989 {
7990  if (!m_ringdb)
7991  return false;
7992  try { return m_ringdb->unblackball(output); }
7993  catch (const std::exception &e) { return false; }
7994 }
7995 
7996 bool wallet2::is_output_blackballed(const std::pair<uint64_t, uint64_t> &output) const
7997 {
7998  if (!m_ringdb)
7999  return false;
8000  try { return m_ringdb->blackballed(output); }
8001  catch (const std::exception &e) { return false; }
8002 }
8003 
8004 bool wallet2::lock_keys_file()
8005 {
8006  if (m_keys_file_locker)
8007  {
8008  MDEBUG(m_keys_file << " is already locked.");
8009  return false;
8010  }
8011  m_keys_file_locker.reset(new tools::file_locker(m_keys_file));
8012  return true;
8013 }
8014 
8015 bool wallet2::unlock_keys_file()
8016 {
8017  if (!m_keys_file_locker)
8018  {
8019  MDEBUG(m_keys_file << " is already unlocked.");
8020  return false;
8021  }
8022  m_keys_file_locker.reset();
8023  return true;
8024 }
8025 
8026 bool wallet2::is_keys_file_locked() const
8027 {
8028  return m_keys_file_locker->locked();
8029 }
8030 
8031 bool 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
8032 {
8033  if (!unlocked) // don't add locked outs
8034  return false;
8035  if (global_index == real_index) // don't re-add real one
8036  return false;
8037  auto item = std::make_tuple(global_index, output_public_key, mask);
8038  CHECK_AND_ASSERT_MES(!outs.empty(), false, "internal error: outs is empty");
8039  if (std::find(outs.back().begin(), outs.back().end(), item) != outs.back().end()) // don't add duplicates
8040  return false;
8041  // check the keys are valid
8042  if (!rct::isInMainSubgroup(rct::pk2rct(output_public_key)))
8043  {
8044  MWARNING("Key " << output_public_key << " at index " << global_index << " is not in the main subgroup");
8045  return false;
8046  }
8047  if (!rct::isInMainSubgroup(mask))
8048  {
8049  MWARNING("Commitment " << mask << " at index " << global_index << " is not in the main subgroup");
8050  return false;
8051  }
8052 // if (is_output_blackballed(output_public_key)) // don't add blackballed outputs
8053 // return false;
8054  outs.back().push_back(item);
8055  return true;
8056 }
8057 
8058 void 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) {
8059 
8060  MDEBUG("LIGHTWALLET - Getting random outs");
8061 
8064 
8065  size_t light_wallet_requested_outputs_count = (size_t)((fake_outputs_count + 1) * 1.5 + 1);
8066 
8067  // Amounts to ask for
8068  // MyMonero api handle amounts and fees as strings
8069  for(size_t idx: selected_transfers) {
8070  const uint64_t ask_amount = m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount();
8071  std::ostringstream amount_ss;
8072  amount_ss << ask_amount;
8073  oreq.amounts.push_back(amount_ss.str());
8074  }
8075 
8076  oreq.count = light_wallet_requested_outputs_count;
8077  m_daemon_rpc_mutex.lock();
8078  bool r = invoke_http_json("/get_random_outs", oreq, ores, rpc_timeout, "POST");
8079  m_daemon_rpc_mutex.unlock();
8081  THROW_WALLET_EXCEPTION_IF(ores.amount_outs.empty() , error::wallet_internal_error, "No outputs received from light wallet node. Error: " + ores.Error);
8082 
8083  // Check if we got enough outputs for each amount
8084  for(auto& out: ores.amount_outs) {
8085  const uint64_t out_amount = boost::lexical_cast<uint64_t>(out.amount);
8086  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));
8087  MDEBUG(out.outputs.size() << " outputs for amount "+ boost::lexical_cast<std::string>(out.amount) + " received from light wallet node");
8088  }
8089 
8090  MDEBUG("selected transfers size: " << selected_transfers.size());
8091 
8092  for(size_t idx: selected_transfers)
8093  {
8094  // Create new index
8095  outs.push_back(std::vector<get_outs_entry>());
8096  outs.back().reserve(fake_outputs_count + 1);
8097 
8098  // add real output first
8099  const transfer_details &td = m_transfers[idx];
8100  const uint64_t amount = td.is_rct() ? 0 : td.amount();
8101  outs.back().push_back(std::make_tuple(td.m_global_output_index, td.get_public_key(), rct::commit(td.amount(), td.m_mask)));
8102  MDEBUG("added real output " << string_tools::pod_to_hex(td.get_public_key()));
8103 
8104  // Even if the lightwallet server returns random outputs, we pick them randomly.
8105  std::vector<size_t> order;
8106  order.resize(light_wallet_requested_outputs_count);
8107  for (size_t n = 0; n < order.size(); ++n)
8108  order[n] = n;
8109  std::shuffle(order.begin(), order.end(), std::default_random_engine(crypto::rand<unsigned>()));
8110 
8111 
8112  LOG_PRINT_L2("Looking for " << (fake_outputs_count+1) << " outputs with amounts " << print_etn(td.is_rct() ? 0 : td.amount()));
8113  MDEBUG("OUTS SIZE: " << outs.back().size());
8114  for (size_t o = 0; o < light_wallet_requested_outputs_count && outs.back().size() < fake_outputs_count + 1; ++o)
8115  {
8116  // Random pick
8117  size_t i = order[o];
8118 
8119  // Find which random output key to use
8120  bool found_amount = false;
8121  size_t amount_key;
8122  for(amount_key = 0; amount_key < ores.amount_outs.size(); ++amount_key)
8123  {
8124  if(boost::lexical_cast<uint64_t>(ores.amount_outs[amount_key].amount) == amount) {
8125  found_amount = true;
8126  break;
8127  }
8128  }
8129  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" );
8130 
8131  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);
8132 
8133  // Convert light wallet string data to proper data structures
8134  crypto::public_key tx_public_key;
8135  rct::key mask = AUTO_VAL_INIT(mask); // decrypted mask - not used here
8136  rct::key rct_commit = AUTO_VAL_INIT(rct_commit);
8137  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");
8138  string_tools::hex_to_pod(ores.amount_outs[amount_key].outputs[i].public_key, tx_public_key);
8139  const uint64_t global_index = ores.amount_outs[amount_key].outputs[i].global_index;
8140  if(!light_wallet_parse_rct_str(ores.amount_outs[amount_key].outputs[i].rct, tx_public_key, 0, mask, rct_commit, false))
8141  rct_commit = rct::zeroCommit(td.amount());
8142 
8143  if (tx_add_fake_output(outs, global_index, tx_public_key, rct_commit, td.m_global_output_index, true)) {
8144  MDEBUG("added fake output " << ores.amount_outs[amount_key].outputs[i].public_key);
8145  MDEBUG("index " << global_index);
8146  }
8147  }
8148 
8149  THROW_WALLET_EXCEPTION_IF(outs.back().size() < fake_outputs_count + 1 , error::wallet_internal_error, "Not enough fake outputs found" );
8150 
8151  // Real output is the first. Shuffle outputs
8152  MTRACE(outs.back().size() << " outputs added. Sorting outputs by index:");
8153  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); });
8154 
8155  // Print output order
8156  for(auto added_out: outs.back())
8157  MTRACE(std::get<0>(added_out));
8158 
8159  }
8160 }
8161 
8162 void 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)
8163 {
8164  LOG_PRINT_L2("fake_outputs_count: " << fake_outputs_count);
8165  outs.clear();
8166 
8167  if(m_light_wallet && fake_outputs_count > 0) {
8168  light_wallet_get_outs(outs, selected_transfers, fake_outputs_count);
8169  return;
8170  }
8171 
8172  if (fake_outputs_count > 0) // zero for electroneum, so skip a lot of code
8173  {
8174  uint64_t segregation_fork_height = get_segregation_fork_height();
8175  // check whether we're shortly after the fork
8176  uint64_t height;
8177  boost::optional<std::string> result = m_node_rpc_proxy.get_height(height);
8178  throw_on_rpc_response_error(result, "get_info");
8179  bool is_shortly_after_segregation_fork = height >= segregation_fork_height && height < segregation_fork_height + SEGREGATION_FORK_VICINITY;
8180  bool is_after_segregation_fork = height >= segregation_fork_height;
8181 
8182  uint64_t v8height = m_nettype == TESTNET ? 446674 : 589169;
8184  uint16_t MINED_ETN_SPENDABLE_AGE = height > v8height ? ETN_MINED_ETN_UNLOCK_WINDOW_V8 : CRYPTONOTE_MINED_ETN_UNLOCK_WINDOW;
8185 
8186  // if we have at least one rct out, get the distribution, or fall back to the previous system
8187  uint64_t rct_start_height;
8188  std::vector<uint64_t> rct_offsets;
8189  bool has_rct = false;
8190  uint64_t max_rct_index = 0;
8191  for (size_t idx: selected_transfers)
8192  if (m_transfers[idx].is_rct())
8193  {
8194  has_rct = true;
8195  max_rct_index = std::max(max_rct_index, m_transfers[idx].m_global_output_index);
8196  }
8197  const bool has_rct_distribution = has_rct && get_rct_distribution(rct_start_height, rct_offsets);
8198  if (has_rct_distribution)
8199  {
8200  // check we're clear enough of rct start, to avoid corner cases below
8201  THROW_WALLET_EXCEPTION_IF(rct_offsets.size() <= TX_SPENDABLE_AGE,
8202  error::get_output_distribution, "Not enough rct outputs");
8203  THROW_WALLET_EXCEPTION_IF(rct_offsets.back() <= max_rct_index,
8204  error::get_output_distribution, "Daemon reports suspicious number of rct outputs");
8205  }
8206 
8207  // get histogram for the amounts we need
8210  // request histogram for all outputs, except 0 if we have the rct distribution
8211  for(size_t idx: selected_transfers)
8212  if (!m_transfers[idx].is_rct() || !has_rct_distribution)
8213  req_t.amounts.push_back(m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount());
8214  if (!req_t.amounts.empty())
8215  {
8216  std::sort(req_t.amounts.begin(), req_t.amounts.end());
8217  auto end = std::unique(req_t.amounts.begin(), req_t.amounts.end());
8218  req_t.amounts.resize(std::distance(req_t.amounts.begin(), end));
8219  req_t.unlocked = true;
8220  req_t.recent_cutoff = time(NULL) - RECENT_OUTPUT_ZONE;
8221  m_daemon_rpc_mutex.lock();
8222  bool r = invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, rpc_timeout);
8223  m_daemon_rpc_mutex.unlock();
8224  THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected");
8225  THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
8226  THROW_WALLET_EXCEPTION_IF(resp_t.status != CORE_RPC_STATUS_OK, error::get_histogram_error, get_rpc_status(resp_t.status));
8227  }
8228 
8229  // if we want to segregate fake outs pre or post fork, get distribution
8230  std::unordered_map<uint64_t, std::pair<uint64_t, uint64_t>> segregation_limit;
8231  if (is_after_segregation_fork && (m_segregate_pre_fork_outputs || m_key_reuse_mitigation2))
8232  {
8235  for(size_t idx: selected_transfers)
8236  req_t.amounts.push_back(m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount());
8237  std::sort(req_t.amounts.begin(), req_t.amounts.end());
8238  auto end = std::unique(req_t.amounts.begin(), req_t.amounts.end());
8239  req_t.amounts.resize(std::distance(req_t.amounts.begin(), end));
8240  req_t.from_height = std::max<uint64_t>(segregation_fork_height, RECENT_OUTPUT_BLOCKS) - RECENT_OUTPUT_BLOCKS;
8241  req_t.to_height = segregation_fork_height + 1;
8242  req_t.cumulative = true;
8243  req_t.binary = true;
8244  m_daemon_rpc_mutex.lock();
8245  bool r = invoke_http_json_rpc("/json_rpc", "get_output_distribution", req_t, resp_t, rpc_timeout * 1000);
8246  m_daemon_rpc_mutex.unlock();
8247  THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected");
8248  THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_distribution");
8249  THROW_WALLET_EXCEPTION_IF(resp_t.status != CORE_RPC_STATUS_OK, error::get_output_distribution, get_rpc_status(resp_t.status));
8250 
8251  // check we got all data
8252  for(size_t idx: selected_transfers)
8253  {
8254  const uint64_t amount = m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount();
8255  bool found = false;
8256  for (const auto &d: resp_t.distributions)
8257  {
8258  if (d.amount == amount)
8259  {
8260  THROW_WALLET_EXCEPTION_IF(d.data.start_height > segregation_fork_height, error::get_output_distribution, "Distribution start_height too high");
8261  THROW_WALLET_EXCEPTION_IF(segregation_fork_height - d.data.start_height >= d.data.distribution.size(), error::get_output_distribution, "Distribution size too small");
8262  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");
8263  THROW_WALLET_EXCEPTION_IF(segregation_fork_height <= RECENT_OUTPUT_BLOCKS, error::wallet_internal_error, "Fork height too low");
8264  THROW_WALLET_EXCEPTION_IF(segregation_fork_height - RECENT_OUTPUT_BLOCKS < d.data.start_height, error::get_output_distribution, "Bad start height");
8265  uint64_t till_fork = d.data.distribution[segregation_fork_height - d.data.start_height];
8266  uint64_t recent = till_fork - d.data.distribution[segregation_fork_height - RECENT_OUTPUT_BLOCKS - d.data.start_height];
8267  segregation_limit[amount] = std::make_pair(till_fork, recent);
8268  found = true;
8269  break;
8270  }
8271  }
8272  THROW_WALLET_EXCEPTION_IF(!found, error::get_output_distribution, "Requested amount not found in response");
8273  }
8274  }
8275 
8276  // we ask for more, to have spares if some outputs are still locked
8277  size_t base_requested_outputs_count = (size_t)((fake_outputs_count + 1) * 1.5 + 1);
8278  LOG_PRINT_L2("base_requested_outputs_count: " << base_requested_outputs_count);
8279 
8280  // generate output indices to request
8282  COMMAND_RPC_GET_OUTPUTS_BIN::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
8283 
8284  std::unique_ptr<gamma_picker> gamma;
8285  if (has_rct_distribution)
8286  gamma.reset(new gamma_picker(rct_offsets));
8287 
8288  size_t num_selected_transfers = 0;
8289  for(size_t idx: selected_transfers)
8290  {
8291  ++num_selected_transfers;
8292  const transfer_details &td = m_transfers[idx];
8293  const uint64_t amount = td.is_rct() ? 0 : td.amount();
8294  std::unordered_set<uint64_t> seen_indices;
8295  // request more for rct in base recent (locked) coinbases are picked, since they're locked for longer
8296  size_t requested_outputs_count = base_requested_outputs_count + (td.is_rct() ? MINED_ETN_SPENDABLE_AGE - TX_SPENDABLE_AGE : 0);
8297  size_t start = req.outputs.size();
8298  bool use_histogram = amount != 0 || !has_rct_distribution;
8299 
8300  const bool output_is_pre_fork = td.m_block_height < segregation_fork_height;
8301  uint64_t num_outs = 0, num_recent_outs = 0;
8302  uint64_t num_post_fork_outs = 0;
8303  float pre_fork_num_out_ratio = 0.0f;
8304  float post_fork_num_out_ratio = 0.0f;
8305 
8306  if (is_after_segregation_fork && m_segregate_pre_fork_outputs && output_is_pre_fork)
8307  {
8308  num_outs = segregation_limit[amount].first;
8309  num_recent_outs = segregation_limit[amount].second;
8310  }
8311  else
8312  {
8313  // if there are just enough outputs to mix with, use all of them.
8314  // Eventually this should become impossible.
8315  for (const auto &he: resp_t.histogram)
8316  {
8317  if (he.amount == amount)
8318  {
8319  LOG_PRINT_L2("Found " << print_etn(amount) << ": " << he.total_instances << " total, "
8320  << he.unlocked_instances << " unlocked, " << he.recent_instances << " recent");
8321  num_outs = he.unlocked_instances;
8322  num_recent_outs = he.recent_instances;
8323  break;
8324  }
8325  }
8326  if (is_after_segregation_fork && m_key_reuse_mitigation2)
8327  {
8328  if (output_is_pre_fork)
8329  {
8330  if (is_shortly_after_segregation_fork)
8331  {
8332  pre_fork_num_out_ratio = 33.4/100.0f * (1.0f - RECENT_OUTPUT_RATIO);
8333  }
8334  else
8335  {
8336  pre_fork_num_out_ratio = 33.4/100.0f * (1.0f - RECENT_OUTPUT_RATIO);
8337  post_fork_num_out_ratio = 33.4/100.0f * (1.0f - RECENT_OUTPUT_RATIO);
8338  }
8339  }
8340  else
8341  {
8342  if (is_shortly_after_segregation_fork)
8343  {
8344  }
8345  else
8346  {
8347  post_fork_num_out_ratio = 67.8/100.0f * (1.0f - RECENT_OUTPUT_RATIO);
8348  }
8349  }
8350  }
8351  num_post_fork_outs = num_outs - segregation_limit[amount].first;
8352  }
8353 
8354  if (use_histogram)
8355  {
8356  LOG_PRINT_L1("" << num_outs << " unlocked outputs of size " << print_etn(amount));
8357  THROW_WALLET_EXCEPTION_IF(num_outs == 0, error::wallet_internal_error,
8358  "histogram reports no unlocked outputs for " + boost::lexical_cast<std::string>(amount) + ", not even ours");
8359  THROW_WALLET_EXCEPTION_IF(num_recent_outs > num_outs, error::wallet_internal_error,
8360  "histogram reports more recent outs than outs for " + boost::lexical_cast<std::string>(amount));
8361  }
8362  else
8363  {
8364  // the base offset of the first rct output in the first unlocked block (or the one to be if there's none)
8365  num_outs = rct_offsets[rct_offsets.size() - TX_SPENDABLE_AGE];
8366  LOG_PRINT_L1("" << num_outs << " unlocked rct outputs");
8367  THROW_WALLET_EXCEPTION_IF(num_outs == 0, error::wallet_internal_error,
8368  "histogram reports no unlocked rct outputs, not even ours");
8369  }
8370 
8371  // how many fake outs to draw on a pre-fork distribution
8372  size_t pre_fork_outputs_count = requested_outputs_count * pre_fork_num_out_ratio;
8373  size_t post_fork_outputs_count = requested_outputs_count * post_fork_num_out_ratio;
8374  // how many fake outs to draw otherwise
8375  size_t normal_output_count = requested_outputs_count - pre_fork_outputs_count - post_fork_outputs_count;
8376 
8377  size_t recent_outputs_count = 0;
8378  if (use_histogram)
8379  {
8380  // X% of those outs are to be taken from recent outputs
8381  recent_outputs_count = normal_output_count * RECENT_OUTPUT_RATIO;
8382  if (recent_outputs_count == 0)
8383  recent_outputs_count = 1; // ensure we have at least one, if possible
8384  if (recent_outputs_count > num_recent_outs)
8385  recent_outputs_count = num_recent_outs;
8386  if (td.m_global_output_index >= num_outs - num_recent_outs && recent_outputs_count > 0)
8387  --recent_outputs_count; // if the real out is recent, pick one less recent fake out
8388  }
8389  LOG_PRINT_L1("Fake output makeup: " << requested_outputs_count << " requested: " << recent_outputs_count << " recent, " <<
8390  pre_fork_outputs_count << " pre-fork, " << post_fork_outputs_count << " post-fork, " <<
8391  (requested_outputs_count - recent_outputs_count - pre_fork_outputs_count - post_fork_outputs_count) << " full-chain");
8392 
8393  uint64_t num_found = 0;
8394 
8395  // if we have a known ring, use it
8396  if (td.m_key_image_known && !td.m_key_image_partial)
8397  {
8398  std::vector<uint64_t> ring;
8399  if (get_ring(get_ringdb_key(), td.m_key_image, ring))
8400  {
8401  MINFO("This output has a known ring, reusing (size " << ring.size() << ")");
8402  THROW_WALLET_EXCEPTION_IF(ring.size() > fake_outputs_count + 1, error::wallet_internal_error,
8403  "An output in this transaction was previously spent on another chain with ring size " +
8404  std::to_string(ring.size()) + ", it cannot be spent now with ring size " +
8405  std::to_string(fake_outputs_count + 1) + " as it is smaller: use a higher ring size");
8406  bool own_found = false;
8407  for (const auto &out: ring)
8408  {
8409  MINFO("Ring has output " << out);
8410  if (out < num_outs)
8411  {
8412  MINFO("Using it");
8413  req.outputs.push_back({amount, out});
8414  ++num_found;
8415  seen_indices.emplace(out);
8416  if (out == td.m_global_output_index)
8417  {
8418  MINFO("This is the real output");
8419  own_found = true;
8420  }
8421  }
8422  else
8423  {
8424  MINFO("Ignoring output " << out << ", too recent");
8425  }
8426  }
8427  THROW_WALLET_EXCEPTION_IF(!own_found, error::wallet_internal_error,
8428  "Known ring does not include the spent output: " + std::to_string(td.m_global_output_index));
8429  }
8430  }
8431 
8432  if (num_outs <= requested_outputs_count)
8433  {
8434  for (uint64_t i = 0; i < num_outs; i++)
8435  req.outputs.push_back({amount, i});
8436  // duplicate to make up shortfall: this will be caught after the RPC call,
8437  // so we can also output the amounts for which we can't reach the required
8438  // mixin after checking the actual unlockedness
8439  for (uint64_t i = num_outs; i < requested_outputs_count; ++i)
8440  req.outputs.push_back({amount, num_outs - 1});
8441  }
8442  else
8443  {
8444  // start with real one
8445  if (num_found == 0)
8446  {
8447  num_found = 1;
8448  seen_indices.emplace(td.m_global_output_index);
8449  req.outputs.push_back({amount, td.m_global_output_index});
8450  LOG_PRINT_L1("Selecting real output: " << td.m_global_output_index << " for " << print_etn(amount));
8451  }
8452 
8453  std::unordered_map<const char*, std::set<uint64_t>> picks;
8454 
8455  // while we still need more mixins
8456  uint64_t num_usable_outs = num_outs;
8457  bool allow_blackballed = false;
8458  MDEBUG("Starting gamma picking with " << num_outs << ", num_usable_outs " << num_usable_outs
8459  << ", requested_outputs_count " << requested_outputs_count);
8460  while (num_found < requested_outputs_count)
8461  {
8462  // if we've gone through every possible output, we've gotten all we can
8463  if (seen_indices.size() == num_usable_outs)
8464  {
8465  // there is a first pass which rejects blackballed outputs, then a second pass
8466  // which allows them if we don't have enough non blackballed outputs to reach
8467  // the required amount of outputs (since consensus does not care about blackballed
8468  // outputs, we still need to reach the minimum ring size)
8469  if (allow_blackballed)
8470  break;
8471  MINFO("Not enough output not marked as spent, we'll allow outputs marked as spent");
8472  allow_blackballed = true;
8473  num_usable_outs = num_outs;
8474  }
8475 
8476  // get a random output index from the DB. If we've already seen it,
8477  // return to the top of the loop and try again, otherwise add it to the
8478  // list of output indices we've seen.
8479 
8480  uint64_t i;
8481  const char *type = "";
8482  if (amount == 0 && has_rct_distribution)
8483  {
8484  THROW_WALLET_EXCEPTION_IF(!gamma, error::wallet_internal_error, "No gamma picker");
8485  // gamma distribution
8486  if (num_found -1 < recent_outputs_count + pre_fork_outputs_count)
8487  {
8488  do i = gamma->pick(); while (i >= segregation_limit[amount].first);
8489  type = "pre-fork gamma";
8490  }
8491  else if (num_found -1 < recent_outputs_count + pre_fork_outputs_count + post_fork_outputs_count)
8492  {
8493  do i = gamma->pick(); while (i < segregation_limit[amount].first || i >= num_outs);
8494  type = "post-fork gamma";
8495  }
8496  else
8497  {
8498  do i = gamma->pick(); while (i >= num_outs);
8499  type = "gamma";
8500  }
8501  }
8502  else if (num_found - 1 < recent_outputs_count) // -1 to account for the real one we seeded with
8503  {
8504  // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
8505  uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53);
8506  double frac = std::sqrt((double)r / ((uint64_t)1 << 53));
8507  i = (uint64_t)(frac*num_recent_outs) + num_outs - num_recent_outs;
8508  // just in case rounding up to 1 occurs after calc
8509  if (i == num_outs)
8510  --i;
8511  type = "recent";
8512  }
8513  else if (num_found -1 < recent_outputs_count + pre_fork_outputs_count)
8514  {
8515  // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
8516  uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53);
8517  double frac = std::sqrt((double)r / ((uint64_t)1 << 53));
8518  i = (uint64_t)(frac*segregation_limit[amount].first);
8519  // just in case rounding up to 1 occurs after calc
8520  if (i == num_outs)
8521  --i;
8522  type = " pre-fork";
8523  }
8524  else if (num_found -1 < recent_outputs_count + pre_fork_outputs_count + post_fork_outputs_count)
8525  {
8526  // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
8527  uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53);
8528  double frac = std::sqrt((double)r / ((uint64_t)1 << 53));
8529  i = (uint64_t)(frac*num_post_fork_outs) + segregation_limit[amount].first;
8530  // just in case rounding up to 1 occurs after calc
8531  if (i == num_post_fork_outs+segregation_limit[amount].first)
8532  --i;
8533  type = "post-fork";
8534  }
8535  else
8536  {
8537  // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
8538  uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53);
8539  double frac = std::sqrt((double)r / ((uint64_t)1 << 53));
8540  i = (uint64_t)(frac*num_outs);
8541  // just in case rounding up to 1 occurs after calc
8542  if (i == num_outs)
8543  --i;
8544  type = "triangular";
8545  }
8546 
8547  if (seen_indices.count(i))
8548  continue;
8549  if (!allow_blackballed && is_output_blackballed(std::make_pair(amount, i))) // don't add blackballed outputs
8550  {
8551  --num_usable_outs;
8552  continue;
8553  }
8554  seen_indices.emplace(i);
8555 
8556  picks[type].insert(i);
8557  req.outputs.push_back({amount, i});
8558  ++num_found;
8559  MDEBUG("picked " << i << ", " << num_found << " now picked");
8560  }
8561 
8562  for (const auto &pick: picks)
8563  MDEBUG("picking " << pick.first << " outputs: " <<
8564  boost::join(pick.second | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " "));
8565 
8566  // if we had enough unusable outputs, we might fall off here and still
8567  // have too few outputs, so we stuff with one to keep counts good, and
8568  // we'll error out later
8569  while (num_found < requested_outputs_count)
8570  {
8571  req.outputs.push_back({amount, 0});
8572  ++num_found;
8573  }
8574  }
8575 
8576  // sort the subsection, to ensure the daemon doesn't know which output is ours
8577  std::sort(req.outputs.begin() + start, req.outputs.end(),
8578  [](const get_outputs_out &a, const get_outputs_out &b) { return a.index < b.index; });
8579  }
8580 
8581  if (ELPP->vRegistry()->allowed(el::Level::Debug, ELECTRONEUM_DEFAULT_LOG_CATEGORY))
8582  {
8583  std::map<uint64_t, std::set<uint64_t>> outs;
8584  for (const auto &i: req.outputs)
8585  outs[i.amount].insert(i.index);
8586  for (const auto &o: outs)
8587  MDEBUG("asking for outputs with amount " << print_etn(o.first) << ": " <<
8588  boost::join(o.second | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " "));
8589  }
8590 
8591  // get the keys for those
8592  req.get_txid = false;
8593  m_daemon_rpc_mutex.lock();
8594  bool r = invoke_http_bin("/get_outs.bin", req, daemon_resp, rpc_timeout);
8595  m_daemon_rpc_mutex.unlock();
8596  THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin");
8597  THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin");
8598  THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_outs_error, get_rpc_status(daemon_resp.status));
8599  THROW_WALLET_EXCEPTION_IF(daemon_resp.outs.size() != req.outputs.size(), error::wallet_internal_error,
8600  "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
8601  std::to_string(daemon_resp.outs.size()) + ", expected " + std::to_string(req.outputs.size()));
8602 
8603  std::unordered_map<uint64_t, uint64_t> scanty_outs;
8604  size_t base = 0;
8605  outs.reserve(num_selected_transfers);
8606  for(size_t idx: selected_transfers)
8607  {
8608  const transfer_details &td = m_transfers[idx];
8609  size_t requested_outputs_count = base_requested_outputs_count + (td.is_rct() ? MINED_ETN_SPENDABLE_AGE - TX_SPENDABLE_AGE : 0);
8610  outs.push_back(std::vector<get_outs_entry>());
8611  outs.back().reserve(fake_outputs_count + 1);
8612  const rct::key mask = td.is_rct() ? rct::commit(td.amount(), td.m_mask) : rct::zeroCommit(td.amount());
8613 
8614  uint64_t num_outs = 0;
8615  const uint64_t amount = td.is_rct() ? 0 : td.amount();
8616  const bool output_is_pre_fork = td.m_block_height < segregation_fork_height;
8617  if (is_after_segregation_fork && m_segregate_pre_fork_outputs && output_is_pre_fork)
8618  num_outs = segregation_limit[amount].first;
8619  else for (const auto &he: resp_t.histogram)
8620  {
8621  if (he.amount == amount)
8622  {
8623  num_outs = he.unlocked_instances;
8624  break;
8625  }
8626  }
8627  bool use_histogram = amount != 0 || !has_rct_distribution;
8628  if (!use_histogram)
8629  num_outs = rct_offsets[rct_offsets.size() - TX_SPENDABLE_AGE];
8630 
8631  // make sure the real outputs we asked for are really included, along
8632  // with the correct key and mask: this guards against an active attack
8633  // where the node sends dummy data for all outputs, and we then send
8634  // the real one, which the node can then tell from the fake outputs,
8635  // as it has different data than the dummy data it had sent earlier
8636  bool real_out_found = false;
8637  for (size_t n = 0; n < requested_outputs_count; ++n)
8638  {
8639  size_t i = base + n;
8640  if (req.outputs[i].index == td.m_global_output_index)
8641  if (daemon_resp.outs[i].key == boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key)
8642  if (daemon_resp.outs[i].mask == mask)
8643  real_out_found = true;
8644  }
8645  THROW_WALLET_EXCEPTION_IF(!real_out_found, error::wallet_internal_error,
8646  "Daemon response did not include the requested real output");
8647 
8648  // pick real out first (it will be sorted when done)
8649  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));
8650 
8651  // then pick outs from an existing ring, if any
8652  if (td.m_key_image_known && !td.m_key_image_partial)
8653  {
8654  std::vector<uint64_t> ring;
8655  if (get_ring(get_ringdb_key(), td.m_key_image, ring))
8656  {
8657  for (uint64_t out: ring)
8658  {
8659  if (out < num_outs)
8660  {
8661  if (out != td.m_global_output_index)
8662  {
8663  bool found = false;
8664  for (size_t o = 0; o < requested_outputs_count; ++o)
8665  {
8666  size_t i = base + o;
8667  if (req.outputs[i].index == out)
8668  {
8669  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)");
8670  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);
8671  found = true;
8672  break;
8673  }
8674  }
8675  THROW_WALLET_EXCEPTION_IF(!found, error::wallet_internal_error, "Falied to find existing ring output in daemon out data");
8676  }
8677  }
8678  }
8679  }
8680  }
8681 
8682  // then pick others in random order till we reach the required number
8683  // since we use an equiprobable pick here, we don't upset the triangular distribution
8684  std::vector<size_t> order;
8685  order.resize(requested_outputs_count);
8686  for (size_t n = 0; n < order.size(); ++n)
8687  order[n] = n;
8688  std::shuffle(order.begin(), order.end(), std::default_random_engine(crypto::rand<unsigned>()));
8689 
8690  LOG_PRINT_L2("Looking for " << (fake_outputs_count+1) << " outputs of size " << print_etn(td.is_rct() ? 0 : td.amount()));
8691  for (size_t o = 0; o < requested_outputs_count && outs.back().size() < fake_outputs_count + 1; ++o)
8692  {
8693  size_t i = base + order[o];
8694  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);
8695  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);
8696  }
8697  if (outs.back().size() < fake_outputs_count + 1)
8698  {
8699  scanty_outs[td.is_rct() ? 0 : td.amount()] = outs.back().size();
8700  }
8701  else
8702  {
8703  // sort the subsection, so any spares are reset in order
8704  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); });
8705  }
8706  base += requested_outputs_count;
8707  }
8708  THROW_WALLET_EXCEPTION_IF(!scanty_outs.empty(), error::not_enough_outs_to_mix, scanty_outs, fake_outputs_count);
8709  }
8710  else //no fake outs => start reading here
8711  {
8712  if(tx_version < 3) {
8713  for (size_t idx: selected_transfers) {
8714  const transfer_details &td = m_transfers[idx];
8715  std::vector<get_outs_entry> v;
8716  const rct::key mask = td.is_rct() ? rct::commit(td.amount(), td.m_mask) : rct::zeroCommit(td.amount());
8717  v.push_back(std::make_tuple(td.m_global_output_index, td.get_public_key(),
8718  mask)); // get pub key is where our error is (wrong get)
8719  outs.push_back(v);
8720  }
8721  }
8722  }
8723 
8724  if(tx_version < 3) {
8725  // save those outs in the ringdb for reuse
8726  for (size_t i = 0; i < selected_transfers.size(); ++i) {
8727  const size_t idx = selected_transfers[i];
8728  THROW_WALLET_EXCEPTION_IF(idx >= m_transfers.size(), error::wallet_internal_error,
8729  "selected_transfers entry out of range");
8730  const transfer_details &td = m_transfers[idx];
8731  std::vector<uint64_t> ring;
8732  ring.reserve(outs[i].size());
8733  for (const auto &e: outs[i])
8734  ring.push_back(std::get<0>(e));
8735  if (!set_ring(td.m_key_image, ring, false))//
8736  MERROR("Failed to set ring for " << td.m_key_image);
8737  }
8738  }
8739 }
8740 
8741 template<typename T>
8742 void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
8743  std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
8744  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)
8745 {
8746  using namespace cryptonote;
8747  // throw if attempting a transaction with no destinations
8749 
8750  THROW_WALLET_EXCEPTION_IF(m_multisig, error::wallet_internal_error, "Multisig wallets cannot spend non rct outputs");
8751 
8752  uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit();
8753  uint64_t needed_etn = fee;
8754  LOG_PRINT_L2("transfer: starting with fee " << print_etn (needed_etn));
8755 
8756  // calculate total amount being sent to all destinations
8757  // throw if total amount overflows uint64_t
8758  for(auto& dt: dsts)
8759  {
8761  needed_etn += dt.amount;
8762  LOG_PRINT_L2("transfer: adding " << print_etn(dt.amount) << ", for a total of " << print_etn (needed_etn));
8763  THROW_WALLET_EXCEPTION_IF(needed_etn < dt.amount, error::tx_sum_overflow, dsts, fee, m_nettype);
8764  }
8765 
8766  uint64_t found_etn = 0;
8767  for(size_t idx: selected_transfers)
8768  {
8769  found_etn += m_transfers[idx].amount();
8770  }
8771 
8772  LOG_PRINT_L2("wanted " << print_etn(needed_etn) << ", found " << print_etn(found_etn) << ", fee " << print_etn(fee));
8773  THROW_WALLET_EXCEPTION_IF(found_etn < needed_etn, error::not_enough_unlocked_etn, found_etn, needed_etn - fee, fee);
8774 
8775  uint32_t subaddr_account = m_transfers[*selected_transfers.begin()].m_subaddr_index.major;
8776  for (auto i = ++selected_transfers.begin(); i != selected_transfers.end(); ++i)
8777  THROW_WALLET_EXCEPTION_IF(subaddr_account != m_transfers[*i].m_subaddr_index.major, error::wallet_internal_error, "the tx uses funds from multiple accounts");
8778 
8779  if (outs.empty())
8780  get_outs(outs, selected_transfers, fake_outputs_count, tx.version); // may throw
8781 
8782  //prepare inputs
8783  LOG_PRINT_L2("preparing outputs");
8784  typedef cryptonote::tx_source_entry::output_entry tx_output_entry;
8785  size_t i = 0, out_index = 0;
8786  std::vector<cryptonote::tx_source_entry> sources;
8787  for(size_t idx: selected_transfers)
8788  {
8789  sources.resize(sources.size()+1);
8790  cryptonote::tx_source_entry& src = sources.back();
8791  const transfer_details& td = m_transfers[idx];
8792  src.amount = td.amount();
8793  src.rct = td.is_rct();
8794  if(tx.version < 3) {
8795  //paste keys (fake and real)
8796  // adding pairs of global index & stealth address to our vector of source outs (needed forold ins only)
8797  for (size_t n = 0; n < fake_outputs_count + 1; ++n) {
8798  tx_output_entry oe;
8799  oe.first = std::get<0>(outs[out_index][n]);
8800  oe.second.dest = rct::pk2rct(std::get<1>(outs[out_index][n]));
8801  oe.second.mask = std::get<2>(outs[out_index][n]);
8802 
8803  src.outputs.push_back(oe);
8804  ++i;
8805  }
8806 
8807  //paste real transaction to the random index
8808  auto it_to_replace = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const tx_output_entry &a) {
8809  return a.first == td.m_global_output_index;
8810  });
8812  "real output not found");
8813 
8814  tx_output_entry real_oe;
8815  real_oe.first = td.m_global_output_index;
8816  real_oe.second.dest = rct::pk2rct(
8817  boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key);
8818  real_oe.second.mask = rct::commit(td.amount(), td.m_mask);
8819  *it_to_replace = real_oe;
8822  src.real_output = it_to_replace - src.outputs.begin();
8824  }
8825  src.real_output_in_tx_index = td.m_internal_output_index; // these two are all we really need for v3 sources
8826  src.tx_hash = td.m_txid;
8827  src.subaddr_index = td.m_subaddr_index;
8829  ++out_index;
8830  }
8831  LOG_PRINT_L2("outputs prepared");
8832 
8833  cryptonote::tx_destination_entry change_dts = AUTO_VAL_INIT(change_dts);
8834  if (needed_etn < found_etn)
8835  {
8836  // send change to the first input's address for v3+ tx
8837  uint32_t change_subaddress_minor = tx.version > 2 ? sources.front().subaddr_index.minor : 0;
8838  change_dts.addr = get_subaddress({subaddr_account, change_subaddress_minor});
8839  change_dts.is_subaddress = (subaddr_account != 0 || change_subaddress_minor != 0);
8840  change_dts.amount = found_etn - needed_etn;
8841  }
8842 
8843  std::vector<cryptonote::tx_destination_entry> splitted_dsts, dust_dsts;
8844  uint64_t dust = 0;
8845  destination_split_strategy(dsts, change_dts, dust_policy.dust_threshold, splitted_dsts, dust_dsts);
8846  for(auto& d: dust_dsts) {
8847  THROW_WALLET_EXCEPTION_IF(dust_policy.dust_threshold < d.amount, error::wallet_internal_error, "invalid dust value: dust = " +
8848  std::to_string(d.amount) + ", dust_threshold = " + std::to_string(dust_policy.dust_threshold));
8849  }
8850  for(auto& d: dust_dsts) {
8851  if (!dust_policy.add_to_fee)
8852  splitted_dsts.push_back(cryptonote::tx_destination_entry(d.amount, dust_policy.addr_for_dust, d.is_subaddress));
8853  dust += d.amount;
8854  }
8855 
8856  crypto::secret_key tx_key;
8857  std::vector<crypto::secret_key> additional_tx_keys;
8858  rct::multisig_out msout;
8859 
8860  LOG_PRINT_L2("constructing tx");
8861  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);
8862  LOG_PRINT_L2("constructed tx, r="<<r);
8863  THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype);
8864  THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit);
8865 
8866  std::string key_images;
8867  bool are_ins_correct_type = tx.version >= 3 ?
8868  std::all_of(tx.vin.begin(), tx.vin.end(), [&](const txin_v& s_e) -> bool
8869  {
8870  CHECKED_GET_SPECIFIC_VARIANT(s_e, const txin_to_key_public, in, false);
8871  return true;
8872  })
8873  :
8874  std::all_of(tx.vin.begin(), tx.vin.end(), [&](const txin_v& s_e) -> bool
8875  {
8876  CHECKED_GET_SPECIFIC_VARIANT(s_e, const txin_to_key, in, false);
8877  key_images += boost::to_string(in.k_image) + " ";
8878  return true;
8879  });
8880 
8881  THROW_WALLET_EXCEPTION_IF(!are_ins_correct_type, error::unexpected_txin_type, tx);
8882 
8883 
8884  bool dust_sent_elsewhere = (dust_policy.addr_for_dust.m_view_public_key != change_dts.addr.m_view_public_key
8885  || dust_policy.addr_for_dust.m_spend_public_key != change_dts.addr.m_spend_public_key);
8886 
8887  if (dust_policy.add_to_fee || dust_sent_elsewhere) change_dts.amount -= dust;
8888 
8889  ptx.key_images = key_images;
8890  ptx.fee = (dust_policy.add_to_fee ? fee+dust : fee);
8891  ptx.dust = ((dust_policy.add_to_fee || dust_sent_elsewhere) ? dust : 0);
8892  ptx.dust_added_to_fee = dust_policy.add_to_fee;
8893  ptx.tx = tx;
8894  ptx.change_dts = change_dts;
8895  ptx.selected_transfers = selected_transfers;
8896  ptx.tx_key = tx_key;
8897  ptx.additional_tx_keys = additional_tx_keys;
8898  ptx.dests = dsts;
8899  ptx.construction_data.sources = sources;
8900  ptx.construction_data.change_dts = change_dts;
8901  ptx.construction_data.splitted_dsts = splitted_dsts;
8902  ptx.construction_data.selected_transfers = selected_transfers;
8903  ptx.construction_data.extra = tx.extra;
8904  ptx.construction_data.unlock_time = unlock_time;
8905  ptx.construction_data.use_rct = false;
8907  ptx.construction_data.dests = dsts;
8908  // record which subaddress indices are being used as inputs
8909  ptx.construction_data.subaddr_account = subaddr_account;
8910  ptx.construction_data.subaddr_indices.clear();
8911  for (size_t idx: selected_transfers)
8912  ptx.construction_data.subaddr_indices.insert(m_transfers[idx].m_subaddr_index.minor);
8913  LOG_PRINT_L2("transfer_selected done");
8914 }
8915 
8916 std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_etn, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices) const
8917 {
8918  std::vector<size_t> picks;
8919  float current_output_relatdness = 1.0f;
8920 
8921  LOG_PRINT_L2("pick_preferred_rct_inputs: needed_etn " << print_etn(needed_etn));
8922 
8923  // try to find a rct input of enough size
8924  for (size_t i = 0; i < m_transfers.size(); ++i)
8925  {
8926  const transfer_details& td = m_transfers[i];
8927  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)
8928  {
8929  LOG_PRINT_L2("We can use " << i << " alone: " << print_etn(td.amount()));
8930  picks.push_back(i);
8931  return picks;
8932  }
8933  }
8934 
8935  // then try to find two outputs
8936  // this could be made better by picking one of the outputs to be a small one, since those
8937  // are less useful since often below the needed etn, so if one can be used in a pair,
8938  // it gets rid of it for the future
8939  for (size_t i = 0; i < m_transfers.size(); ++i)
8940  {
8941  const transfer_details& td = m_transfers[i];
8942  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)
8943  {
8944  LOG_PRINT_L2("Considering input " << i << ", " << print_etn(td.amount()));
8945  for (size_t j = i + 1; j < m_transfers.size(); ++j)
8946  {
8947  const transfer_details& td2 = m_transfers[j];
8948  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)
8949  {
8950  // update our picks if those outputs are less related than any we
8951  // already found. If the same, don't update, and oldest suitable outputs
8952  // will be used in preference.
8953  float relatedness = get_output_relatedness(td, td2);
8954  LOG_PRINT_L2(" with input " << j << ", " << print_etn(td2.amount()) << ", relatedness " << relatedness);
8955  if (relatedness < current_output_relatdness)
8956  {
8957  // reset the current picks with those, and return them directly
8958  // if they're unrelated. If they are related, we'll end up returning
8959  // them if we find nothing better
8960  picks.clear();
8961  picks.push_back(i);
8962  picks.push_back(j);
8963  LOG_PRINT_L0("we could use " << i << " and " << j);
8964  if (relatedness == 0.0f)
8965  return picks;
8966  current_output_relatdness = relatedness;
8967  }
8968  }
8969  }
8970  }
8971  }
8972 
8973  return picks;
8974 }
8975 
8976 bool 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
8977 {
8978  if (!use_rct)
8979  return false;
8980  if (n_transfers > 1)
8981  return false;
8982  if (unused_dust_indices.empty() && unused_transfers_indices.empty())
8983  return false;
8984  // we want at least one free rct output to avoid a corner case where
8985  // we'd choose a non rct output which doesn't have enough "siblings"
8986  // value-wise on the chain, and thus can't be mixed
8987  bool found = false;
8988  for (auto i: unused_dust_indices)
8989  {
8990  if (m_transfers[i].is_rct())
8991  {
8992  found = true;
8993  break;
8994  }
8995  }
8996  if (!found) for (auto i: unused_transfers_indices)
8997  {
8998  if (m_transfers[i].is_rct())
8999  {
9000  found = true;
9001  break;
9002  }
9003  }
9004  if (!found)
9005  return false;
9006  return true;
9007 }
9008 
9009 std::vector<size_t> wallet2::get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const
9010 {
9011  std::vector<size_t> indices;
9012  for (size_t n: unused_dust_indices)
9013  if (m_transfers[n].is_rct())
9014  indices.push_back(n);
9015  for (size_t n: unused_transfers_indices)
9016  if (m_transfers[n].is_rct())
9017  indices.push_back(n);
9018  return indices;
9019 }
9020 
9021 static uint32_t get_count_above(const std::vector<wallet2::transfer_details> &transfers, const std::vector<size_t> &indices, uint64_t threshold)
9022 {
9023  uint32_t count = 0;
9024  for (size_t idx: indices)
9025  if (transfers[idx].amount() >= threshold)
9026  ++count;
9027  return count;
9028 }
9029 
9030 bool wallet2::light_wallet_login(bool &new_address)
9031 {
9032  MDEBUG("Light wallet login request");
9033  m_light_wallet_connected = false;
9036  request.address = get_account().get_public_address_str(m_nettype);
9037  request.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
9038  // Always create account if it doesn't exist.
9039  request.create_account = true;
9040  m_daemon_rpc_mutex.lock();
9041  bool connected = invoke_http_json("/login", request, response, rpc_timeout, "POST");
9042  m_daemon_rpc_mutex.unlock();
9043  // MyMonero doesn't send any status message. OpenMonero does.
9044  m_light_wallet_connected = connected && (response.status.empty() || response.status == "success");
9045  new_address = response.new_address;
9046  MDEBUG("Status: " << response.status);
9047  MDEBUG("Reason: " << response.reason);
9048  MDEBUG("New wallet: " << response.new_address);
9049  if(m_light_wallet_connected)
9050  {
9051  // Clear old data on successful login.
9052  // m_transfers.clear();
9053  // m_payments.clear();
9054  // m_unconfirmed_payments.clear();
9055  }
9056  return m_light_wallet_connected;
9057 }
9058 
9059 bool wallet2::light_wallet_import_wallet_request(tools::COMMAND_RPC_IMPORT_WALLET_REQUEST::response &response)
9060 {
9061  MDEBUG("Light wallet import wallet request");
9063  oreq.address = get_account().get_public_address_str(m_nettype);
9064  oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
9065  m_daemon_rpc_mutex.lock();
9066  bool r = invoke_http_json("/import_wallet_request", oreq, response, rpc_timeout, "POST");
9067  m_daemon_rpc_mutex.unlock();
9068  THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "import_wallet_request");
9069 
9070 
9071  return true;
9072 }
9073 
9074 void wallet2::light_wallet_get_unspent_outs()
9075 {
9076  MDEBUG("Getting unspent outs");
9077 
9080 
9081  oreq.amount = "0";
9082  oreq.address = get_account().get_public_address_str(m_nettype);
9083  oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
9084  // openMonero specific
9085  oreq.dust_threshold = boost::lexical_cast<std::string>(::config::DEFAULT_DUST_THRESHOLD);
9086  // below are required by openMonero api - but are not used.
9087  oreq.mixin = 0;
9088  oreq.use_dust = true;
9089 
9090 
9091  m_daemon_rpc_mutex.lock();
9092  bool r = invoke_http_json("/get_unspent_outs", oreq, ores, rpc_timeout, "POST");
9093  m_daemon_rpc_mutex.unlock();
9095  THROW_WALLET_EXCEPTION_IF(ores.status == "error", error::wallet_internal_error, ores.reason);
9096 
9097  m_light_wallet_per_kb_fee = ores.per_kb_fee;
9098 
9099  std::unordered_map<crypto::hash,bool> transfers_txs;
9100  for(const auto &t: m_transfers)
9101  transfers_txs.emplace(t.m_txid,t.m_spent);
9102 
9103  MDEBUG("FOUND " << ores.outputs.size() <<" outputs");
9104 
9105  // return if no outputs found
9106  if(ores.outputs.empty())
9107  return;
9108 
9109  // Clear old outputs
9110  m_transfers.clear();
9111 
9112  for (const auto &o: ores.outputs) {
9113  bool spent = false;
9114  bool add_transfer = true;
9115  crypto::key_image unspent_key_image;
9116  crypto::public_key tx_public_key = AUTO_VAL_INIT(tx_public_key);
9117  THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, o.tx_pub_key), error::wallet_internal_error, "Invalid tx_pub_key field");
9118  string_tools::hex_to_pod(o.tx_pub_key, tx_public_key);
9119 
9120  for (const std::string &ski: o.spend_key_images) {
9121  spent = false;
9122 
9123  // Check if key image is ours
9125  string_tools::hex_to_pod(ski, unspent_key_image);
9126  if(light_wallet_key_image_is_ours(unspent_key_image, tx_public_key, o.index)){
9127  MTRACE("Output " << o.public_key << " is spent. Key image: " << ski);
9128  spent = true;
9129  break;
9130  } {
9131  MTRACE("Unspent output found. " << o.public_key);
9132  }
9133  }
9134 
9135  // Check if tx already exists in m_transfers.
9136  crypto::hash txid;
9137  crypto::public_key tx_pub_key;
9139  THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, o.tx_hash), error::wallet_internal_error, "Invalid tx_hash field");
9140  THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, o.public_key), error::wallet_internal_error, "Invalid public_key field");
9141  THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, o.tx_pub_key), error::wallet_internal_error, "Invalid tx_pub_key field");
9142  string_tools::hex_to_pod(o.tx_hash, txid);
9143  string_tools::hex_to_pod(o.public_key, public_key);
9144  string_tools::hex_to_pod(o.tx_pub_key, tx_pub_key);
9145 
9146  for(auto &t: m_transfers){
9147  if(t.get_public_key() == public_key) {
9148  t.m_spent = spent;
9149  add_transfer = false;
9150  break;
9151  }
9152  }
9153 
9154  if(!add_transfer)
9155  continue;
9156 
9157  m_transfers.push_back(boost::value_initialized<transfer_details>());
9158  transfer_details& td = m_transfers.back();
9159 
9160  td.m_block_height = o.height;
9161  td.m_global_output_index = o.global_index;
9162  td.m_txid = txid;
9163 
9164  // Add to extra
9165  add_tx_pub_key_to_extra(td.m_tx, tx_pub_key);
9166 
9167  td.m_key_image = unspent_key_image;
9168  td.m_key_image_known = !m_watch_only && !m_multisig;
9169  td.m_key_image_request = false;
9170  td.m_key_image_partial = m_multisig;
9171  td.m_amount = o.amount;
9172  td.m_pk_index = 0;
9173  td.m_internal_output_index = o.index;
9174  td.m_spent = spent;
9175  td.m_frozen = false;
9176 
9177  tx_out txout;
9178  txout.target = txout_to_key(public_key);
9179  txout.amount = td.m_amount;
9180 
9181  td.m_tx.vout.resize(td.m_internal_output_index + 1);
9182  td.m_tx.vout[td.m_internal_output_index] = txout;
9183 
9184  // Add unlock time and coinbase bool got from get_address_txs api call
9185  std::unordered_map<crypto::hash,address_tx>::const_iterator found = m_light_wallet_address_txs.find(txid);
9186  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");
9187  bool miner_tx = found->second.m_coinbase;
9188  td.m_tx.unlock_time = found->second.m_unlock_time;
9189 
9190  if (!o.rct.empty())
9191  {
9192  // Coinbase tx's
9193  if(miner_tx)
9194  {
9195  td.m_mask = rct::identity();
9196  }
9197  else
9198  {
9199  // rct txs
9200  // decrypt rct mask, calculate commit hash and compare against blockchain commit hash
9201  rct::key rct_commit;
9202  light_wallet_parse_rct_str(o.rct, tx_pub_key, td.m_internal_output_index, td.m_mask, rct_commit, true);
9203  bool valid_commit = (rct_commit == rct::commit(td.amount(), td.m_mask));
9204  if(!valid_commit)
9205  {
9206  MDEBUG("output index: " << o.global_index);
9207  MDEBUG("mask: " + string_tools::pod_to_hex(td.m_mask));
9208  MDEBUG("calculated commit: " + string_tools::pod_to_hex(rct::commit(td.amount(), td.m_mask)));
9209  MDEBUG("expected commit: " + string_tools::pod_to_hex(rct_commit));
9210  MDEBUG("amount: " << td.amount());
9211  }
9212  THROW_WALLET_EXCEPTION_IF(!valid_commit, error::wallet_internal_error, "Lightwallet: rct commit hash mismatch!");
9213  }
9214  td.m_rct = true;
9215  }
9216  else
9217  {
9218  td.m_mask = rct::identity();
9219  td.m_rct = false;
9220  }
9221  if(!spent)
9222  set_unspent(m_transfers.size()-1);
9223  m_key_images[td.m_key_image] = m_transfers.size()-1;
9224  m_pub_keys[td.get_public_key()] = m_transfers.size()-1;
9225  }
9226 }
9227 
9228 bool wallet2::light_wallet_get_address_info(tools::COMMAND_RPC_GET_ADDRESS_INFO::response &response)
9229 {
9230  MTRACE(__FUNCTION__);
9231 
9233 
9234  request.address = get_account().get_public_address_str(m_nettype);
9235  request.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
9236  m_daemon_rpc_mutex.lock();
9237  bool r = invoke_http_json("/get_address_info", request, response, rpc_timeout, "POST");
9238  m_daemon_rpc_mutex.unlock();
9240  // TODO: Validate result
9241  return true;
9242 }
9243 
9244 void wallet2::light_wallet_get_address_txs()
9245 {
9246  MDEBUG("Refreshing light wallet");
9247 
9250 
9251  ireq.address = get_account().get_public_address_str(m_nettype);
9252  ireq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
9253  m_daemon_rpc_mutex.lock();
9254  bool r = invoke_http_json("/get_address_txs", ireq, ires, rpc_timeout, "POST");
9255  m_daemon_rpc_mutex.unlock();
9257  //OpenMonero sends status=success, Mymonero doesn't.
9258  THROW_WALLET_EXCEPTION_IF((!ires.status.empty() && ires.status != "success"), error::no_connection_to_daemon, "get_address_txs");
9259 
9260 
9261  // Abort if no transactions
9262  if(ires.transactions.empty())
9263  return;
9264 
9265  // Create searchable vectors
9266  std::vector<crypto::hash> payments_txs;
9267  for(const auto &p: m_payments)
9268  payments_txs.push_back(p.second.m_tx_hash);
9269  std::vector<crypto::hash> unconfirmed_payments_txs;
9270  for(const auto &up: m_unconfirmed_payments)
9271  unconfirmed_payments_txs.push_back(up.second.m_pd.m_tx_hash);
9272 
9273  // for balance calculation
9274  uint64_t wallet_total_sent = 0;
9275  // txs in pool
9276  std::vector<crypto::hash> pool_txs;
9277 
9278  for (const auto &t: ires.transactions) {
9279  const uint64_t total_received = t.total_received;
9280  uint64_t total_sent = t.total_sent;
9281 
9282  // Check key images - subtract fake outputs from total_sent
9283  for(const auto &so: t.spent_outputs)
9284  {
9285  crypto::public_key tx_public_key;
9287  THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, so.tx_pub_key), error::wallet_internal_error, "Invalid tx_pub_key field");
9288  THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, so.key_image), error::wallet_internal_error, "Invalid key_image field");
9289  string_tools::hex_to_pod(so.tx_pub_key, tx_public_key);
9290  string_tools::hex_to_pod(so.key_image, key_image);
9291 
9292  if(!light_wallet_key_image_is_ours(key_image, tx_public_key, so.out_index)) {
9293  THROW_WALLET_EXCEPTION_IF(so.amount > t.total_sent, error::wallet_internal_error, "Lightwallet: total sent is negative!");
9294  total_sent -= so.amount;
9295  }
9296  }
9297 
9298  // Do not add tx if empty.
9299  if(total_sent == 0 && total_received == 0)
9300  continue;
9301 
9302  crypto::hash payment_id = null_hash;
9303  crypto::hash tx_hash;
9304 
9305  THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, t.payment_id), error::wallet_internal_error, "Invalid payment_id field");
9307  string_tools::hex_to_pod(t.payment_id, payment_id);
9308  string_tools::hex_to_pod(t.hash, tx_hash);
9309 
9310  // lightwallet specific info
9311  bool incoming = (total_received > total_sent);
9313  address_tx.m_tx_hash = tx_hash;
9314  address_tx.m_incoming = incoming;
9315  address_tx.m_amount = incoming ? total_received - total_sent : total_sent - total_received;
9316  address_tx.m_fee = 0; // TODO
9317  address_tx.m_block_height = t.height;
9318  address_tx.m_unlock_time = t.unlock_time;
9319  address_tx.m_timestamp = t.timestamp;
9320  address_tx.m_coinbase = t.coinbase;
9321  address_tx.m_mempool = t.mempool;
9322  m_light_wallet_address_txs.emplace(tx_hash,address_tx);
9323 
9324  // populate data needed for history (m_payments, m_unconfirmed_payments, m_confirmed_txs)
9325  // INCOMING transfers
9326  if(total_received > total_sent) {
9327  payment_details payment;
9328  payment.m_tx_hash = tx_hash;
9329  payment.m_amount = total_received - total_sent;
9330  payment.m_fee = 0; // TODO
9331  payment.m_block_height = t.height;
9332  payment.m_unlock_time = t.unlock_time;
9333  payment.m_timestamp = t.timestamp;
9334  payment.m_coinbase = t.coinbase;
9335 
9336  if (t.mempool) {
9337  if (std::find(unconfirmed_payments_txs.begin(), unconfirmed_payments_txs.end(), tx_hash) == unconfirmed_payments_txs.end()) {
9338  pool_txs.push_back(tx_hash);
9339  // assume false as we don't get that info from the light wallet server
9340  crypto::hash payment_id;
9341  THROW_WALLET_EXCEPTION_IF(!epee::string_tools::hex_to_pod(t.payment_id, payment_id),
9342  error::wallet_internal_error, "Failed to parse payment id");
9343  emplace_or_replace(m_unconfirmed_payments, payment_id, pool_payment_details{payment, false, false});
9344  if (0 != m_callback) {
9345  m_callback->on_lw_unconfirmed_etn_received(t.height, payment.m_tx_hash, payment.m_amount);
9346  }
9347  }
9348  } else {
9349  if (std::find(payments_txs.begin(), payments_txs.end(), tx_hash) == payments_txs.end()) {
9350  m_payments.emplace(tx_hash, payment);
9351  if (0 != m_callback) {
9352  m_callback->on_lw_etn_received(t.height, payment.m_tx_hash, payment.m_amount);
9353  }
9354  }
9355  }
9356  // Outgoing transfers
9357  } else {
9358  uint64_t amount_sent = total_sent - total_received;
9359  cryptonote::transaction dummy_tx; // not used by light wallet
9360  // increase wallet total sent
9361  wallet_total_sent += total_sent;
9362  if (t.mempool)
9363  {
9364  // Handled by add_unconfirmed_tx in commit_tx
9365  // If sent from another wallet instance we need to add it
9366  if(m_unconfirmed_txs.find(tx_hash) == m_unconfirmed_txs.end())
9367  {
9369  utd.m_amount_in = amount_sent;
9370  utd.m_amount_out = amount_sent;
9371  utd.m_change = 0;
9372  utd.m_payment_id = payment_id;
9373  utd.m_timestamp = t.timestamp;
9374  utd.m_state = wallet2::unconfirmed_transfer_details::pending;
9375  m_unconfirmed_txs.emplace(tx_hash,utd);
9376  }
9377  }
9378  else
9379  {
9380  // Only add if new
9381  auto confirmed_tx = m_confirmed_txs.find(tx_hash);
9382  if(confirmed_tx == m_confirmed_txs.end()) {
9383  // tx is added to m_unconfirmed_txs - move to confirmed
9384  if(m_unconfirmed_txs.find(tx_hash) != m_unconfirmed_txs.end())
9385  {
9386  process_unconfirmed(tx_hash, dummy_tx, t.height);
9387  }
9388  // Tx sent by another wallet instance
9389  else
9390  {
9392  ctd.m_amount_in = amount_sent;
9393  ctd.m_amount_out = amount_sent;
9394  ctd.m_change = 0;
9395  ctd.m_payment_id = payment_id;
9396  ctd.m_block_height = t.height;
9397  ctd.m_timestamp = t.timestamp;
9398  m_confirmed_txs.emplace(tx_hash,ctd);
9399  }
9400  if (0 != m_callback)
9401  {
9402  m_callback->on_lw_etn_spent(t.height, tx_hash, amount_sent);
9403  }
9404  }
9405  // If not new - check the amount and update if necessary.
9406  // when sending a tx to same wallet the receiving amount has to be credited
9407  else
9408  {
9409  if(confirmed_tx->second.m_amount_in != amount_sent || confirmed_tx->second.m_amount_out != amount_sent)
9410  {
9411  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));
9412  confirmed_tx->second.m_amount_in = amount_sent;
9413  confirmed_tx->second.m_amount_out = amount_sent;
9414  confirmed_tx->second.m_change = 0;
9415  }
9416  }
9417  }
9418  }
9419  }
9420  // TODO: purge old unconfirmed_txs
9421  remove_obsolete_pool_txs(pool_txs);
9422 
9423  // Calculate wallet balance
9424  m_light_wallet_balance = ires.total_received-wallet_total_sent;
9425  // MyMonero doesn't send unlocked balance
9426  if(ires.total_received_unlocked > 0)
9427  m_light_wallet_unlocked_balance = ires.total_received_unlocked-wallet_total_sent;
9428  else
9429  m_light_wallet_unlocked_balance = m_light_wallet_balance;
9430 }
9431 
9432 bool 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
9433 {
9434  // rct string is empty if output is non RCT
9435  if (rct_string.empty())
9436  return false;
9437  // rct_string is a string with length 64+64+64 (<rct commit> + <encrypted mask> + <rct amount>)
9438  rct::key encrypted_mask;
9439  std::string rct_commit_str = rct_string.substr(0,64);
9440  std::string encrypted_mask_str = rct_string.substr(64,64);
9441  THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, rct_commit_str), error::wallet_internal_error, "Invalid rct commit hash: " + rct_commit_str);
9442  THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, encrypted_mask_str), error::wallet_internal_error, "Invalid rct mask: " + encrypted_mask_str);
9443  string_tools::hex_to_pod(rct_commit_str, rct_commit);
9444  string_tools::hex_to_pod(encrypted_mask_str, encrypted_mask);
9445  if (decrypt) {
9446  // Decrypt the mask
9447  crypto::key_derivation derivation;
9448  bool r = generate_key_derivation(tx_pub_key, get_account().get_keys().m_view_secret_key, derivation);
9449  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation");
9450  crypto::secret_key scalar;
9451  crypto::derivation_to_scalar(derivation, internal_output_index, scalar);
9452  sc_sub(decrypted_mask.bytes,encrypted_mask.bytes,rct::hash_to_scalar(rct::sk2rct(scalar)).bytes);
9453  }
9454  return true;
9455 }
9456 
9457 bool wallet2::light_wallet_key_image_is_ours(const crypto::key_image& key_image, const crypto::public_key& tx_public_key, uint64_t out_index)
9458 {
9459  // Lookup key image from cache
9460  std::map<uint64_t, crypto::key_image> index_keyimage_map;
9461  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);
9462  if(found_pub_key != m_key_image_cache.end()) {
9463  // pub key found. key image for index cached?
9464  index_keyimage_map = found_pub_key->second;
9465  std::map<uint64_t,crypto::key_image>::const_iterator index_found = index_keyimage_map.find(out_index);
9466  if(index_found != index_keyimage_map.end())
9467  return key_image == index_found->second;
9468  }
9469 
9470  // Not in cache - calculate key image
9471  crypto::key_image calculated_key_image;
9472  cryptonote::keypair in_ephemeral;
9473 
9474  // Subaddresses aren't supported in mymonero/openmonero yet. Roll out the original scheme:
9475  // compute D = a*R
9476  // compute P = Hs(D || i)*G + B
9477  // compute x = Hs(D || i) + b (and check if P==x*G)
9478  // compute I = x*Hp(P)
9479  const account_keys& ack = get_account().get_keys();
9480  crypto::key_derivation derivation;
9481  bool r = crypto::generate_key_derivation(tx_public_key, ack.m_view_secret_key, derivation);
9482  CHECK_AND_ASSERT_MES(r, false, "failed to generate_key_derivation(" << tx_public_key << ", " << ack.m_view_secret_key << ")");
9483 
9484  r = crypto::derive_public_key(derivation, out_index, ack.m_account_address.m_spend_public_key, in_ephemeral.pub);
9485  CHECK_AND_ASSERT_MES(r, false, "failed to derive_public_key (" << derivation << ", " << out_index << ", " << ack.m_account_address.m_spend_public_key << ")");
9486 
9487  crypto::derive_secret_key(derivation, out_index, ack.m_spend_secret_key, in_ephemeral.sec);
9488  crypto::public_key out_pkey_test;
9489  r = crypto::secret_key_to_public_key(in_ephemeral.sec, out_pkey_test);
9490  CHECK_AND_ASSERT_MES(r, false, "failed to secret_key_to_public_key(" << in_ephemeral.sec << ")");
9491  CHECK_AND_ASSERT_MES(in_ephemeral.pub == out_pkey_test, false, "derived secret key doesn't match derived public key");
9492 
9493  crypto::generate_key_image(in_ephemeral.pub, in_ephemeral.sec, calculated_key_image);
9494 
9495  index_keyimage_map.emplace(out_index, calculated_key_image);
9496  m_key_image_cache.emplace(tx_public_key, index_keyimage_map);
9497  return key_image == calculated_key_image;
9498 }
9499 
9500 // Another implementation of transaction creation that is hopefully better
9501 // While there is anything left to pay, it goes through random outputs and tries
9502 // to fill the next destination/amount. If it fully fills it, it will use the
9503 // remainder to try to fill the next one as well.
9504 // The tx size if roughly estimated as a linear function of only inputs, and a
9505 // new tx will be created when that size goes above a given fraction of the
9506 // max tx size. At that point, more outputs may be added if the fee cannot be
9507 // satisfied.
9508 // If the next output in the next tx would go to the same destination (ie, we
9509 // cut off at a tx boundary in the middle of paying a given destination), the
9510 // fee will be carved out of the current input if possible, to avoid having to
9511 // add another output just for the fee and getting change.
9512 // This system allows for sending (almost) the entire balance, since it does
9513 // not generate spurious change in all txes, thus decreasing the instantaneous
9514 // usable balance.
9515 std::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)
9516 {
9517  //ensure device is let in NONE mode in any case
9518  hw::device &hwdev = m_account.get_device();
9519  boost::unique_lock<hw::device> hwdev_lock (hwdev);
9520  hw::reset_mode rst(hwdev);
9521 
9522  //destinations in the full etn-address format
9523  auto original_dsts = dsts;
9524 
9525  if(m_light_wallet) {
9526  // Populate m_transfers
9527  light_wallet_get_unspent_outs();
9528  }
9529 
9530  uint8_t tx_version = this->public_transactions_required() ? 3 : 1;
9531  //vector of pairs of <subaddr minor index : vector<transfer indexes for that subaddr index inside m_transfers>>
9532  std::vector<std::pair<uint32_t, std::vector<size_t>>> unused_transfers_indices_per_subaddr;
9533  std::vector<std::pair<uint32_t, std::vector<size_t>>> unused_dust_indices_per_subaddr;
9534  uint64_t needed_etn;
9535  uint64_t accumulated_fee, accumulated_outputs, accumulated_change;
9536  struct TX {
9537  std::vector<size_t> selected_transfers;
9538  std::vector<cryptonote::tx_destination_entry> dsts;
9540  pending_tx ptx;
9541  size_t weight;
9542  uint64_t needed_fee;
9543  std::vector<std::vector<tools::wallet2::get_outs_entry>> outs;
9544 
9545  TX() : weight(0), needed_fee(0) {}
9546 
9547  void add(const cryptonote::tx_destination_entry &de, uint64_t amount, unsigned int original_output_index, bool merge_destinations) {
9548  if (merge_destinations)
9549  {
9550  std::vector<cryptonote::tx_destination_entry>::iterator i;
9551  i = std::find_if(dsts.begin(), dsts.end(), [&](const cryptonote::tx_destination_entry &d) { return !memcmp (&d.addr, &de.addr, sizeof(de.addr)); });
9552  if (i == dsts.end())
9553  {
9554  dsts.push_back(de);
9555  i = dsts.end() - 1;
9556  i->amount = 0;
9557  }
9558  i->amount += amount;
9559  }
9560  else
9561  {
9562  THROW_WALLET_EXCEPTION_IF(original_output_index > dsts.size(), error::wallet_internal_error,
9563  std::string("original_output_index too large: ") + std::to_string(original_output_index) + " > " + std::to_string(dsts.size()));
9564  if (original_output_index == dsts.size())
9565  {
9566  dsts.push_back(de);
9567  dsts.back().amount = 0;
9568  }
9569  THROW_WALLET_EXCEPTION_IF(memcmp(&dsts[original_output_index].addr, &de.addr, sizeof(de.addr)), error::wallet_internal_error, "Mismatched destination address");
9570  dsts[original_output_index].amount += amount;
9571  }
9572  }
9573  };
9574  std::vector<TX> txes;
9575  bool adding_fee; // true if new outputs go towards fee, rather than destinations
9576 
9577  uint64_t needed_fee, available_for_fee = 0;
9578  uint64_t upper_transaction_weight_limit;
9579  uint64_t extra_bytes = extra.size();
9580  switch(hwdev.get_type()){
9581 
9582  // Normal Software Limit
9583  case 0 : upper_transaction_weight_limit = get_upper_transaction_weight_limit(); break;
9584 
9585  // Ledger NanoS: ~3.3kB of RAM for app variables. Give a bit of buffer (300) for other variables on device
9586  // and subtract the size of the extra.
9587  // because in the Ledger app this still lives on the stack at the same time the entire prefix does.
9588  // This is a rough rule of thumb estimate... The logic can be updated at a later stage.
9589  // Right now we just need to make we don't fail to build any tx (and split the tx to avoid this happening)
9590  case 1 : upper_transaction_weight_limit = 3000 - extra_bytes; break;
9591 
9592  //Trezor limit set at the same as Ledger for the time being.
9593  case 2 : upper_transaction_weight_limit = 3000 - extra_bytes; break;
9594  //Future hw devices
9595  }
9596  const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
9597  const bool use_rct = use_fork_rules(HF_VERSION_ENABLE_RCT, 0);
9598  const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
9599  const rct::RCTConfig rct_config {
9601  bulletproof ? (use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1) : 0
9602  };
9603 
9604  const uint64_t base_fee = get_base_fee();
9605  const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
9606  const uint64_t fee_quantization_mask = get_fee_quantization_mask();
9607 
9608  // throw if attempting a transaction with no destinations
9610 
9611  // calculate total amount being sent to all destinations
9612  // throw if total amount overflows uint64_t
9613  needed_etn = 0;
9614  for(auto& dt: dsts)
9615  {
9617  needed_etn += dt.amount;
9618  LOG_PRINT_L2("transfer: adding " << print_etn(dt.amount) << ", for a total of " << print_etn (needed_etn));
9619  THROW_WALLET_EXCEPTION_IF(needed_etn < dt.amount, error::tx_sum_overflow, dsts, 0, m_nettype);
9620  }
9621 
9622  // throw if attempting a transaction with no etn
9624 
9625  std::map<uint32_t, std::pair<uint64_t, uint64_t>> unlocked_balance_per_subaddr = unlocked_balance_per_subaddress(subaddr_account, tx_version >= 3);
9626  std::map<uint32_t, uint64_t> balance_per_subaddr = balance_per_subaddress(subaddr_account, tx_version >= 3);
9627 
9628  if (subaddr_indices.empty()) // "index=<N1>[,<N2>,...]" wasn't specified -> use all the indices with non-zero unlocked balance
9629  {
9630  for (const auto& i : balance_per_subaddr)
9631  subaddr_indices.insert(i.first);
9632  }
9633 
9634  // early out if we know we can't make it anyway
9635  // we could also check for being within FEE_PER_KB, but if the fee calculation
9636  // ever changes, this might be missed, so let this go through
9637  uint64_t min_fee = (fee_multiplier * base_fee * estimate_tx_size(use_rct, 1, fake_outs_count, 2, extra.size(), bulletproof));
9638  //Whilst we're still dealing with fee/kb:
9639  if (!use_per_byte_fee){
9640  min_fee /= 1000;
9641  if(min_fee == 0){min_fee += 10;}
9642  }
9643  uint64_t balance_subtotal = 0;
9644  uint64_t unlocked_balance_subtotal = 0;
9645  for (uint32_t index_minor : subaddr_indices)
9646  {
9647  balance_subtotal += balance_per_subaddr[index_minor];
9648  unlocked_balance_subtotal += unlocked_balance_per_subaddr[index_minor].first;
9649  }
9650  THROW_WALLET_EXCEPTION_IF(needed_etn + min_fee > balance_subtotal, error::not_enough_etn,
9651  balance_subtotal, needed_etn, 0);
9652  // first check overall balance is enough, then unlocked one, so we throw distinct exceptions
9653  THROW_WALLET_EXCEPTION_IF(needed_etn + min_fee > unlocked_balance_subtotal, error::not_enough_unlocked_etn,
9654  unlocked_balance_subtotal, needed_etn, 0);
9655 
9656  for (uint32_t i : subaddr_indices)
9657  LOG_PRINT_L2("Candidate subaddress index for spending: " << i);
9658 
9659  // determine threshold for fractional amount
9660  const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof);
9661  const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof);
9662  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!");
9663  const size_t tx_weight_per_ring = tx_weight_two_rings - tx_weight_one_ring;
9664  const uint64_t fractional_threshold = (fee_multiplier * base_fee * tx_weight_per_ring) / (use_per_byte_fee ? 1 : 1024);
9665 
9666  // gather all dust and non-dust outputs belonging to specified subaddresses
9667  size_t num_nondust_outputs = 0;
9668  size_t num_dust_outputs = 0;
9669  for (size_t i = 0; i < m_transfers.size(); ++i)
9670  {
9671  const transfer_details& td = m_transfers[i];
9672  if (m_ignore_fractional_outputs && td.amount() < fractional_threshold)
9673  {
9674  MDEBUG("Ignoring output " << i << " of amount " << print_etn(td.amount()) << " which is below threshold " << print_etn(fractional_threshold));
9675  continue;
9676  }
9677  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)
9678  {
9679  if((tx_version < 3 && td.m_tx.version > 1) || (tx_version >= 3 && td.m_tx.version == 1))
9680  continue;
9681 
9682  const uint32_t index_minor = td.m_subaddr_index.minor;
9683  auto find_predicate = [&index_minor](const std::pair<uint32_t, std::vector<size_t>>& x) { return x.first == index_minor; };
9684  if ((td.is_rct()) || is_valid_decomposed_amount(td.amount()))
9685  {
9686  auto found = std::find_if(unused_transfers_indices_per_subaddr.begin(), unused_transfers_indices_per_subaddr.end(), find_predicate);
9687  if (found == unused_transfers_indices_per_subaddr.end())
9688  {
9689  unused_transfers_indices_per_subaddr.push_back({index_minor, {i}});
9690  }
9691  else
9692  {
9693  found->second.push_back(i);
9694  }
9695  ++num_nondust_outputs;
9696  }
9697  else
9698  {
9699  auto found = std::find_if(unused_dust_indices_per_subaddr.begin(), unused_dust_indices_per_subaddr.end(), find_predicate);
9700  if (found == unused_dust_indices_per_subaddr.end())
9701  {
9702  unused_dust_indices_per_subaddr.push_back({index_minor, {i}});
9703  }
9704  else
9705  {
9706  found->second.push_back(i);
9707  }
9708  ++num_dust_outputs;
9709  }
9710  }
9711  }
9712 
9713  // sort output indices
9714  {
9715  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)
9716  {
9717  return unlocked_balance_per_subaddr[x.first].first > unlocked_balance_per_subaddr[y.first].first;
9718  };
9719  std::sort(unused_transfers_indices_per_subaddr.begin(), unused_transfers_indices_per_subaddr.end(), sort_predicate);
9720  std::sort(unused_dust_indices_per_subaddr.begin(), unused_dust_indices_per_subaddr.end(), sort_predicate);
9721  }
9722 
9723  LOG_PRINT_L2("Starting with " << num_nondust_outputs << " non-dust outputs and " << num_dust_outputs << " dust outputs");
9724 
9725  if (unused_dust_indices_per_subaddr.empty() && unused_transfers_indices_per_subaddr.empty())
9726  return std::vector<wallet2::pending_tx>();
9727 
9728  // if empty, put dummy entry so that the front can be referenced later in the loop
9729  if (unused_dust_indices_per_subaddr.empty())
9730  unused_dust_indices_per_subaddr.push_back({});
9731  if (unused_transfers_indices_per_subaddr.empty())
9732  unused_transfers_indices_per_subaddr.push_back({});
9733 
9734  // start with an empty tx
9735  txes.push_back(TX());
9736  accumulated_fee = 0;
9737  accumulated_outputs = 0;
9738  accumulated_change = 0;
9739  adding_fee = false;
9740  needed_fee = 0;
9741  std::vector<std::vector<tools::wallet2::get_outs_entry>> outs;
9742 
9743  // for rct, since we don't see the amounts, we will try to make all transactions
9744  // look the same, with 1 or 2 inputs, and 2 outputs. One input is preferable, as
9745  // this prevents linking to another by provenance analysis, but two is ok if we
9746  // try to pick outputs not from the same block. We will get two outputs, one for
9747  // the destination, and one for change.
9748  LOG_PRINT_L2("checking preferred");
9749  std::vector<size_t> preferred_inputs;
9750  uint64_t rct_outs_needed = 2 * (fake_outs_count + 1);
9751  rct_outs_needed += 100; // some fudge factor since we don't know how many are locked
9752  if (use_rct)
9753  {
9754  // this is used to build a tx that's 1 or 2 inputs, and 2 outputs, which
9755  // will get us a known fee.
9756  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);
9757  preferred_inputs = pick_preferred_rct_inputs(needed_etn + estimated_fee, subaddr_account, subaddr_indices);
9758  if (!preferred_inputs.empty())
9759  {
9760  string s;
9761  for (auto i: preferred_inputs) s += boost::lexical_cast<std::string>(i) + " (" + print_etn(m_transfers[i].amount()) + ") ";
9762  LOG_PRINT_L1("Found preferred rct inputs for rct tx: " << s);
9763 
9764  // bring the list of available outputs stored by the same subaddress index to the front of the list
9765  uint32_t index_minor = m_transfers[preferred_inputs[0]].m_subaddr_index.minor;
9766  for (size_t i = 1; i < unused_transfers_indices_per_subaddr.size(); ++i)
9767  {
9768  if (unused_transfers_indices_per_subaddr[i].first == index_minor)
9769  {
9770  std::swap(unused_transfers_indices_per_subaddr[0], unused_transfers_indices_per_subaddr[i]);
9771  break;
9772  }
9773  }
9774  for (size_t i = 1; i < unused_dust_indices_per_subaddr.size(); ++i)
9775  {
9776  if (unused_dust_indices_per_subaddr[i].first == index_minor)
9777  {
9778  std::swap(unused_dust_indices_per_subaddr[0], unused_dust_indices_per_subaddr[i]);
9779  break;
9780  }
9781  }
9782  }
9783  }
9784  LOG_PRINT_L2("done checking preferred");
9785 
9786  // while:
9787  // - we have something to send
9788  // - or we need to gather more fee
9789  // - or we have just one input in that tx, which is rct (to try and make all/most rct txes 2/2)
9790  unsigned int original_output_index = 0;
9791  std::vector<size_t>* unused_transfers_indices = &unused_transfers_indices_per_subaddr[0].second;
9792  std::vector<size_t>* unused_dust_indices = &unused_dust_indices_per_subaddr[0].second;
9793 
9795  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)) {
9796  TX &tx = txes.back();
9797 
9798  LOG_PRINT_L2("Start of loop with " << unused_transfers_indices->size() << " " << unused_dust_indices->size() << ", tx.dsts.size() " << tx.dsts.size());
9799  LOG_PRINT_L2("unused_transfers_indices: " << strjoin(*unused_transfers_indices, " "));
9800  LOG_PRINT_L2("unused_dust_indices: " << strjoin(*unused_dust_indices, " "));
9801  LOG_PRINT_L2("dsts size " << dsts.size() << ", first " << (dsts.empty() ? "-" : cryptonote::print_etn(dsts[0].amount)));
9802  LOG_PRINT_L2("adding_fee " << adding_fee << ", use_rct " << use_rct);
9803 
9804  // if we need to spend etn and don't have any left, we fail
9805  if (unused_dust_indices->empty() && unused_transfers_indices->empty()) {
9806  LOG_PRINT_L2("No more outputs to choose from");
9807  THROW_WALLET_EXCEPTION_IF(1, error::tx_not_possible, unlocked_balance(subaddr_account, tx_version >= 3), needed_etn, accumulated_fee + needed_fee);
9808  }
9809 
9810  // get a random unspent output and use it to pay part (or all) of the current destination (and maybe next one, etc)
9811  // This could be more clever, but maybe at the cost of making probabilistic inferences easier
9812  size_t idx;
9813  if (!preferred_inputs.empty()) {
9814  idx = pop_back(preferred_inputs);
9815  pop_if_present(*unused_transfers_indices, idx);
9816  pop_if_present(*unused_dust_indices, idx);
9817  } else if ((dsts.empty() || dsts[0].amount == 0) && !adding_fee) {
9818  // the "make rct txes 2/2" case - we pick a small value output to "clean up" the wallet too
9819  std::vector<size_t> indices = get_only_rct(*unused_dust_indices, *unused_transfers_indices);
9820  idx = pop_best_value(indices, tx.selected_transfers, true);
9821 
9822  // we might not want to add it if it's a large output and we don't have many left
9823  uint64_t min_output_value = m_min_output_value;
9824  uint32_t min_output_count = m_min_output_count;
9825  if (min_output_value == 0 && min_output_count == 0)
9826  {
9827  min_output_value = DEFAULT_MIN_OUTPUT_VALUE;
9828  min_output_count = DEFAULT_MIN_OUTPUT_COUNT;
9829  }
9830  if (m_transfers[idx].amount() >= min_output_value) {
9831  if (get_count_above(m_transfers, *unused_transfers_indices, min_output_value) < min_output_count) {
9832  LOG_PRINT_L2("Second output was not strictly needed, and we're running out of outputs above " << print_etn(min_output_value) << ", not adding");
9833  break;
9834  }
9835  }
9836 
9837  // since we're trying to add a second output which is not strictly needed,
9838  // we only add it if it's unrelated enough to the first one
9839  float relatedness = get_output_relatedness(m_transfers[idx], m_transfers[tx.selected_transfers.front()]);
9840  if (relatedness > SECOND_OUTPUT_RELATEDNESS_THRESHOLD)
9841  {
9842  LOG_PRINT_L2("Second output was not strictly needed, and relatedness " << relatedness << ", not adding");
9843  break;
9844  }
9845  pop_if_present(*unused_transfers_indices, idx);
9846  pop_if_present(*unused_dust_indices, idx);
9847  } else
9848  idx = pop_best_value(unused_transfers_indices->empty() ? *unused_dust_indices : *unused_transfers_indices, tx.selected_transfers);
9849 
9850  const transfer_details &td = m_transfers[idx];
9851  LOG_PRINT_L2("Picking output " << idx << ", amount " << print_etn(td.amount()) << ", ki " << td.m_key_image);
9852 
9853  // add this output to the list to spend
9854  tx.selected_transfers.push_back(idx);
9855  uint64_t available_amount = td.amount();
9856  accumulated_outputs += available_amount;
9857 
9858  // clear any fake outs we'd already gathered, since we'll need a new set
9859  outs.clear();
9860 
9861  if (adding_fee)
9862  {
9863  LOG_PRINT_L2("We need more fee, adding it to fee");
9864  available_for_fee += available_amount;
9865  }
9866  else
9867  {
9868  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))
9869  {
9870  // we can fully pay that destination
9871  LOG_PRINT_L2("We can fully pay " << get_account_address_as_str(m_nettype, dsts[0].is_subaddress, dsts[0].addr) <<
9872  " for " << print_etn(dsts[0].amount));
9873  tx.add(dsts[0], dsts[0].amount, original_output_index, m_merge_destinations);
9874  available_amount -= dsts[0].amount;
9875  dsts[0].amount = 0;
9876  pop_index(dsts, 0);
9877  ++original_output_index;
9878  }
9879 
9880  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)) {
9881  // we can partially fill that destination
9882  LOG_PRINT_L2("We can partially pay " << get_account_address_as_str(m_nettype, dsts[0].is_subaddress, dsts[0].addr) <<
9883  " for " << print_etn(available_amount) << "/" << print_etn(dsts[0].amount));
9884  tx.add(dsts[0], available_amount, original_output_index, m_merge_destinations);
9885  dsts[0].amount -= available_amount;
9886  available_amount = 0;
9887  }
9888  }
9889 
9890  // here, check if we need to sent tx and start a new one
9891  LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit "
9892  << upper_transaction_weight_limit);
9893  bool try_tx = false;
9894  // if we have preferred picks, but haven't yet used all of them, continue
9895  if (preferred_inputs.empty())
9896  {
9897  if (adding_fee)
9898  {
9899  /* might not actually be enough if adding this output bumps size to next kB, but we need to try */
9900  try_tx = available_for_fee >= needed_fee;
9901  }
9902  else
9903  {
9904  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);
9905  try_tx = dsts.empty() || (estimated_rct_tx_weight >= TX_WEIGHT_TARGET(upper_transaction_weight_limit));
9906  THROW_WALLET_EXCEPTION_IF(try_tx && tx.dsts.empty(), error::tx_too_big, estimated_rct_tx_weight, upper_transaction_weight_limit);
9907  }
9908  }
9909 
9910  if (try_tx) {
9911  cryptonote::transaction test_tx;
9912  test_tx.version = tx_version;
9913  pending_tx test_ptx;
9914 
9915  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);
9916 
9917  uint64_t inputs = 0, outputs = needed_fee;
9918  for (size_t idx: tx.selected_transfers) inputs += m_transfers[idx].amount();
9919  for (const auto &o: tx.dsts) outputs += o.amount;
9920 
9921  if (inputs < outputs)
9922  {
9923  LOG_PRINT_L2("We don't have enough for the basic fee, switching to adding_fee");
9924  adding_fee = true;
9925  goto skip_tx;
9926  }
9927 
9928  LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " outputs and " <<
9929  tx.selected_transfers.size() << " inputs");
9930 
9931  transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
9933  auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
9934  needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
9935  available_for_fee = test_ptx.fee + test_ptx.change_dts.amount + (!test_ptx.dust_added_to_fee ? test_ptx.dust : 0);
9936  LOG_PRINT_L2("Made a " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_etn(available_for_fee) << " available for fee (" <<
9937  print_etn(needed_fee) << " needed)");
9938 
9939  if (needed_fee > available_for_fee && !dsts.empty() && dsts[0].amount > 0)
9940  {
9941  // we don't have enough for the fee, but we've only partially paid the current address,
9942  // so we can take the fee from the paid amount, since we'll have to make another tx anyway
9943  std::vector<cryptonote::tx_destination_entry>::iterator i;
9944  i = std::find_if(tx.dsts.begin(), tx.dsts.end(),
9945  [&](const cryptonote::tx_destination_entry &d) { return !memcmp (&d.addr, &dsts[0].addr, sizeof(dsts[0].addr)); });
9946  THROW_WALLET_EXCEPTION_IF(i == tx.dsts.end(), error::wallet_internal_error, "paid address not found in outputs");
9947  if (i->amount > needed_fee)
9948  {
9949  uint64_t new_paid_amount = i->amount /*+ test_ptx.fee*/ - needed_fee;
9950  LOG_PRINT_L2("Adjusting amount paid to " << get_account_address_as_str(m_nettype, i->is_subaddress, i->addr) << " from " <<
9951  print_etn(i->amount) << " to " << print_etn(new_paid_amount) << " to accommodate " <<
9952  print_etn(needed_fee) << " fee");
9953  dsts[0].amount += i->amount - new_paid_amount;
9954  i->amount = new_paid_amount;
9955  test_ptx.fee = needed_fee;
9956  available_for_fee = needed_fee;
9957  }
9958  }
9959 
9960  if (needed_fee > available_for_fee)
9961  {
9962  LOG_PRINT_L2("We could not make a tx, switching to fee accumulation");
9963 
9964  adding_fee = true;
9965  }
9966  else
9967  {
9968  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));
9969  while (needed_fee > test_ptx.fee) {
9970  transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
9972  txBlob = t_serializable_object_to_blob(test_ptx.tx);
9973  needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
9974  LOG_PRINT_L2("Made an attempt at a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_etn(test_ptx.fee) <<
9975  " fee and " << print_etn(test_ptx.change_dts.amount) << " change");
9976  }
9977 
9978  LOG_PRINT_L2("Made a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_etn(test_ptx.fee) <<
9979  " fee and " << print_etn(test_ptx.change_dts.amount) << " change");
9980 
9981  tx.tx = test_tx;
9982  tx.ptx = test_ptx;
9983  tx.weight = get_transaction_weight(test_tx, txBlob.size());
9984  tx.outs = outs;
9985  tx.needed_fee = test_ptx.fee;
9986  accumulated_fee += test_ptx.fee;
9987  accumulated_change += test_ptx.change_dts.amount;
9988  adding_fee = false;
9989  if (!dsts.empty())
9990  {
9991  LOG_PRINT_L2("We have more to pay, starting another tx");
9992  txes.push_back(TX());
9993  original_output_index = 0;
9994  }
9995  }
9996  }
9997 
9998 skip_tx:
9999  // if unused_*_indices is empty while unused_*_indices_per_subaddr has multiple elements, and if we still have something to pay,
10000  // pop front of unused_*_indices_per_subaddr and have unused_*_indices point to the front of unused_*_indices_per_subaddr
10001  if ((!dsts.empty() && dsts[0].amount > 0) || adding_fee)
10002  {
10003  if (unused_transfers_indices->empty() && unused_transfers_indices_per_subaddr.size() > 1)
10004  {
10005  unused_transfers_indices_per_subaddr.erase(unused_transfers_indices_per_subaddr.begin());
10006  unused_transfers_indices = &unused_transfers_indices_per_subaddr[0].second;
10007  }
10008  if (unused_dust_indices->empty() && unused_dust_indices_per_subaddr.size() > 1)
10009  {
10010  unused_dust_indices_per_subaddr.erase(unused_dust_indices_per_subaddr.begin());
10011  unused_dust_indices = &unused_dust_indices_per_subaddr[0].second;
10012  }
10013  }
10014  }
10015 
10016  if (adding_fee)
10017  {
10018  LOG_PRINT_L1("We ran out of outputs while trying to gather final fee");
10019  THROW_WALLET_EXCEPTION_IF(1, error::tx_not_possible, unlocked_balance(subaddr_account, tx_version >= 3), needed_etn, accumulated_fee + needed_fee);
10020  }
10021 
10022  LOG_PRINT_L1("Done creating " << txes.size() << " transactions, " << print_etn(accumulated_fee) <<
10023  " total fee, " << print_etn(accumulated_change) << " total change");
10024 
10026  for (std::vector<TX>::iterator i = txes.begin(); i != txes.end(); ++i)
10027  {
10028  TX &tx = *i;
10029  cryptonote::transaction test_tx;
10030  test_tx.version = tx_version;
10031  pending_tx test_ptx;
10032  transfer_selected(tx.dsts,
10033  tx.selected_transfers,
10034  fake_outs_count,
10035  tx.outs,
10036  unlock_time,
10037  tx.needed_fee,
10038  extra,
10041  test_tx,
10042  test_ptx);
10043 
10044  auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
10045  tx.tx = test_tx;
10046  tx.ptx = test_ptx;
10047  tx.weight = get_transaction_weight(test_tx, txBlob.size());
10048  }
10049 
10050  std::vector<wallet2::pending_tx> ptx_vector;
10051  for (std::vector<TX>::iterator i = txes.begin(); i != txes.end(); ++i)
10052  {
10053  TX &tx = *i;
10054  uint64_t tx_etn = 0;
10055  for (size_t idx: tx.selected_transfers)
10056  tx_etn += m_transfers[idx].amount();
10057  LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() <<
10058  " " << get_transaction_hash(tx.ptx.tx) << ": " << get_weight_string(tx.weight) << ", sending " << print_etn(tx_etn) << " in " << tx.selected_transfers.size() <<
10059  " outputs to " << tx.dsts.size() << " destination(s), including " <<
10060  print_etn(tx.ptx.fee) << " fee, " << print_etn(tx.ptx.change_dts.amount) << " change");
10061  ptx_vector.push_back(tx.ptx);
10062  }
10063 
10064  THROW_WALLET_EXCEPTION_IF(!sanity_check(ptx_vector, original_dsts), error::wallet_internal_error, "Created transaction(s) failed sanity check");
10065 
10066  // if we made it this far, we're OK to actually send the transactions
10067  return ptx_vector;
10068 }
10069 
10070 bool wallet2::sanity_check(const std::vector<wallet2::pending_tx> &ptx_vector,
10071  std::vector<cryptonote::tx_destination_entry> dsts) const {
10072  MDEBUG("sanity_check: " << ptx_vector.size() << " txes, " << dsts.size() << " destinations");
10073 
10074  hw::device &hwdev = m_account.get_device();
10075 
10076  THROW_WALLET_EXCEPTION_IF(ptx_vector.empty(), error::wallet_internal_error, "No transactions");
10077 
10078  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
10079  // check every party in there does receive at least the required amount
10080  std::unordered_map<account_public_address, std::pair<uint64_t, bool>> required;
10081  for (const auto &d: dsts) {
10082  required[d.addr].first += d.amount;
10083  required[d.addr].second = d.is_subaddress;
10084  }
10085 
10086  // add change
10087  uint64_t change = 0;
10088  for (const auto &ptx: ptx_vector) {
10089  for (size_t idx: ptx.selected_transfers) //1:add the amount you're spending
10090  change += m_transfers[idx].amount();
10091  change -= ptx.fee; //2: take off the fee
10092  }
10093  for (const auto &r: required)
10094  change -= r.second.first; // 3: subtract the destination required amount
10095  MDEBUG("Adding " << cryptonote::print_etn(change) << " expected change");
10096 
10097  // for all txes that have actual change, check change is coming back to the sending wallet
10098  for (const pending_tx &ptx: ptx_vector) {
10099  if (ptx.change_dts.amount == 0)
10100  continue;
10102  m_subaddresses.find(ptx.change_dts.addr.m_spend_public_key) == m_subaddresses.end(),
10103  error::wallet_internal_error, "Change address is not ours");
10104  required[ptx.change_dts.addr].first += ptx.change_dts.amount;
10105  required[ptx.change_dts.addr].second = ptx.change_dts.is_subaddress;
10106  }
10107 
10108 
10109  for (const auto &r: required) {
10110  const account_public_address &address = r.first;
10111  const crypto::public_key &view_pkey = address.m_view_public_key;
10112 
10113  uint64_t total_received = 0;
10114 
10115  for (const auto &ptx: ptx_vector) {
10116  uint64_t received = 0;
10117  try {
10118  std::string proof = get_tx_proof(ptx.tx, ptx.tx_key, ptx.additional_tx_keys, address,
10119  r.second.second,
10120  "automatic-sanity-check");
10121  check_tx_proof(ptx.tx, address, r.second.second, "automatic-sanity-check", proof, received);
10122  }
10123  catch (const std::exception &e) { received = 0; }
10124  total_received += received;
10125  }
10126 
10127  std::stringstream ss;
10128  ss << "Total received by "
10129  << cryptonote::get_account_address_as_str(m_nettype, r.second.second, address)
10130  << ": "
10131  << cryptonote::print_etn(total_received) << ", expected " << cryptonote::print_etn(r.second.first);
10132  MDEBUG(ss.str());
10133  THROW_WALLET_EXCEPTION_IF(total_received < r.second.first, error::wallet_internal_error, ss.str());
10134  }
10135  } // end of v1 sanity check
10136  else {
10137  // for all txes that have actual change, check change is coming back to the sending wallet
10138  for (const pending_tx &ptx: ptx_vector) {
10139  if (ptx.change_dts.amount == 0)
10140  continue;
10142  m_subaddresses.find(ptx.change_dts.addr.m_spend_public_key) == m_subaddresses.end(),
10143  error::wallet_internal_error, "Change address is not ours");
10144  }
10145  }
10146  return true;
10147 }
10148 
10149 std::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)
10150 {
10151  std::vector<size_t> unused_transfers_indices;
10152  std::vector<size_t> unused_dust_indices;
10153  const bool use_rct = use_fork_rules(4, 0);
10154  uint8_t tx_version = public_transactions_required() ? (migrate ? 2 : 3) : 1;
10155  THROW_WALLET_EXCEPTION_IF(unlocked_balance(subaddr_account, tx_version >=3) == 0, error::wallet_internal_error, "No unlocked balance in the entire wallet");
10156  std::map<uint32_t, std::pair<std::vector<size_t>, std::vector<size_t>>> unused_transfer_dust_indices_per_subaddr;
10157 
10158  // gather all dust ***and*** non-dust outputs of specified subaddress (if any) and below specified threshold (if any)
10159  bool fund_found = false;
10160  for (size_t i = 0; i < m_transfers.size(); ++i)
10161  {
10162  const transfer_details& td = m_transfers[i];
10163 
10164  if((tx_version < 3 && td.m_tx.version > 1) || (tx_version >= 3 && td.m_tx.version == 1))
10165  continue;
10166 
10167  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))
10168  {
10169  fund_found = true;
10170  if (below == 0 || td.amount() < below)
10171  {
10172  if ((td.is_rct()) || is_valid_decomposed_amount(td.amount()))
10173  unused_transfer_dust_indices_per_subaddr[td.m_subaddr_index.minor].first.push_back(i);
10174  else
10175  unused_transfer_dust_indices_per_subaddr[td.m_subaddr_index.minor].second.push_back(i);
10176  }
10177  }
10178  }
10179  THROW_WALLET_EXCEPTION_IF(!fund_found, error::wallet_internal_error, "No unlocked balance in the specified subaddress(es)");
10180  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");
10181 
10182  if (subaddr_indices.empty())
10183  {
10184  // in case subaddress index wasn't specified, choose non-empty subaddress randomly (with index=0 being chosen last)
10185  if (unused_transfer_dust_indices_per_subaddr.count(0) == 1 && unused_transfer_dust_indices_per_subaddr.size() > 1)
10186  unused_transfer_dust_indices_per_subaddr.erase(0);
10187  auto i = unused_transfer_dust_indices_per_subaddr.begin();
10188  std::advance(i, crypto::rand_idx(unused_transfer_dust_indices_per_subaddr.size()));
10189  unused_transfers_indices = i->second.first;
10190  unused_dust_indices = i->second.second;
10191  LOG_PRINT_L2("Spending from subaddress index " << i->first);
10192  }
10193  else
10194  {
10195  for (const auto& p : unused_transfer_dust_indices_per_subaddr)
10196  {
10197  unused_transfers_indices.insert(unused_transfers_indices.end(), p.second.first.begin(), p.second.first.end());
10198  unused_dust_indices.insert(unused_dust_indices.end(), p.second.second.begin(), p.second.second.end());
10199  LOG_PRINT_L2("Spending from subaddress index " << p.first);
10200  }
10201  }
10202 
10203  return create_transactions_from(address, is_subaddress, outputs, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra, tx_version);
10204 }
10205 
10206 std::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)
10207 {
10208  std::vector<size_t> unused_transfers_indices;
10209  std::vector<size_t> unused_dust_indices;
10210  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
10211  const bool use_rct = use_fork_rules(4, 0);
10212  // find output with the given key image (
10213  for (size_t i = 0; i < m_transfers.size(); ++i)
10214  {
10215  const transfer_details& td = m_transfers[i];
10216 
10217  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))
10218  {
10219  if (td.is_rct() || is_valid_decomposed_amount(td.amount()))
10220  unused_transfers_indices.push_back(i);
10221  else
10222  unused_dust_indices.push_back(i);
10223  break;
10224  }
10225  }
10226  return create_transactions_from(address, is_subaddress, outputs, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra, tx_version);
10227 }
10228 
10229 std::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)
10230 {
10231  //ensure device is let in NONE mode in any case
10232  hw::device &hwdev = m_account.get_device();
10233  boost::unique_lock<hw::device> hwdev_lock (hwdev);
10234  hw::reset_mode rst(hwdev);
10235 
10236  uint64_t accumulated_fee, accumulated_outputs, accumulated_change;
10237  struct TX {
10238  std::vector<size_t> selected_transfers;
10239  std::vector<cryptonote::tx_destination_entry> dsts;
10241  pending_tx ptx;
10242  size_t weight;
10243  uint64_t needed_fee;
10244  std::vector<std::vector<get_outs_entry>> outs;
10245 
10246  TX() : weight(0), needed_fee(0) {}
10247  };
10248  std::vector<TX> txes;
10249  uint64_t needed_fee, available_for_fee = 0;
10250  uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit(); //max size of a tx - usually ~ half the block size
10251  std::vector<std::vector<get_outs_entry>> outs;
10252 
10253  const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE);
10254  const bool use_rct = fake_outs_count > 0 && use_fork_rules(4, 0);
10255  const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
10256  const rct::RCTConfig rct_config {
10258  bulletproof ? (use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1) : 0,
10259  };
10260  const uint64_t base_fee = get_base_fee();
10261  const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
10262  const uint64_t fee_quantization_mask = get_fee_quantization_mask();
10263 
10264  LOG_PRINT_L2("Starting with " << unused_transfers_indices.size() << " non-dust outputs and " << unused_dust_indices.size() << " dust outputs");
10265 
10266  if (unused_dust_indices.empty() && unused_transfers_indices.empty())
10267  return std::vector<wallet2::pending_tx>();
10268 
10269  // start with an empty tx
10270  txes.push_back(TX());
10271  accumulated_fee = 0;
10272  accumulated_outputs = 0;
10273  accumulated_change = 0;
10274  needed_fee = 0;
10275 
10276  // while we have something to send
10278  while (!unused_dust_indices.empty() || !unused_transfers_indices.empty()) {
10279  TX &tx = txes.back();
10280 
10281  // get a random unspent output and use it to pay next chunk. We try to alternate
10282  // dust and non dust to ensure we never get with only dust, from which we might
10283  // get a tx that can't pay for itself
10284  uint64_t fee_dust_threshold;
10285  if (use_fork_rules(HF_VERSION_PER_BYTE_FEE))
10286  {
10287  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);
10288  fee_dust_threshold = calculate_fee_from_weight(base_fee, estimated_tx_weight_with_one_extra_output, fee_multiplier, fee_quantization_mask);
10289  }
10290  else
10291  {
10292  fee_dust_threshold = base_fee * fee_multiplier * (upper_transaction_weight_limit + 1023) / 1024;
10293  }
10294 
10295  size_t idx =
10296  unused_transfers_indices.empty()
10297  ? pop_best_value(unused_dust_indices, tx.selected_transfers)
10298  : unused_dust_indices.empty()
10299  ? pop_best_value(unused_transfers_indices, tx.selected_transfers)
10300  : ((tx.selected_transfers.size() & 1) || accumulated_outputs > fee_dust_threshold)
10301  ? pop_best_value(unused_dust_indices, tx.selected_transfers)
10302  : pop_best_value(unused_transfers_indices, tx.selected_transfers);
10303 
10304  const transfer_details &td = m_transfers[idx];
10305  LOG_PRINT_L2("Picking output " << idx << ", amount " << print_etn(td.amount()));
10306 
10307  // add this output to the list to spend
10308  tx.selected_transfers.push_back(idx);
10309  uint64_t available_amount = td.amount();
10310  accumulated_outputs += available_amount;
10311 
10312  // clear any fake outs we'd already gathered, since we'll need a new set
10313  outs.clear();
10314 
10315  // here, check if we need to sent tx and start a new one
10316  LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit "
10317  << upper_transaction_weight_limit);
10318  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);
10319  bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || ( estimated_rct_tx_weight >= TX_WEIGHT_TARGET(upper_transaction_weight_limit));
10320 
10321  if (try_tx) {
10322  cryptonote::transaction test_tx;
10323  test_tx.version = tx_version;
10324  pending_tx test_ptx;
10325 
10326  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);
10327 
10328  // add N - 1 outputs for correct initial fee estimation
10329  for (size_t i = 0; i < ((outputs > 1) ? outputs - 1 : outputs); ++i)
10330  tx.dsts.push_back(tx_destination_entry(1, address, is_subaddress));
10331 
10332  LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " destinations and " <<
10333  tx.selected_transfers.size() << " outputs");
10334 
10335  transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
10337 
10338  auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
10339  needed_fee = tx_version == 2 ? 0 : calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
10340  available_for_fee = test_ptx.fee + test_ptx.change_dts.amount;
10341  for (auto &dt: test_ptx.dests)
10342  available_for_fee += dt.amount;
10343  LOG_PRINT_L2("Made a " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_etn(available_for_fee) << " available for fee (" <<
10344  print_etn(needed_fee) << " needed)");
10345 
10346  // add last output, missed for fee estimation
10347  if (outputs > 1)
10348  tx.dsts.push_back(tx_destination_entry(1, address, is_subaddress));
10349 
10350  THROW_WALLET_EXCEPTION_IF(needed_fee > available_for_fee, error::wallet_internal_error, "Transaction cannot pay for itself");
10351 
10352  do {
10353  LOG_PRINT_L2("We made a tx, adjusting fee and saving it");
10354  // distribute total transferred amount between outputs
10355  uint64_t amount_transferred = available_for_fee - needed_fee; //shouuld be zero for migration transactions
10356  uint64_t dt_amount = amount_transferred / outputs;
10357  // residue is distributed as one atomic unit per output until it reaches zero
10358  uint64_t residue = amount_transferred % outputs;
10359  for (auto &dt: tx.dsts)
10360  {
10361  uint64_t dt_residue = 0;
10362  if (residue > 0)
10363  {
10364  dt_residue = 1;
10365  residue -= 1;
10366  }
10367  dt.amount = dt_amount + dt_residue;
10368  }
10369  transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
10371  txBlob = t_serializable_object_to_blob(test_ptx.tx);
10372  needed_fee = tx_version == 2 ? 0 : calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
10373  LOG_PRINT_L2("Made an attempt at a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_etn(test_ptx.fee) <<
10374  " fee and " << print_etn(test_ptx.change_dts.amount) << " change");
10375  } while (needed_fee > test_ptx.fee);
10376 
10377  LOG_PRINT_L2("Made a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_etn(test_ptx.fee) <<
10378  " fee and " << print_etn(test_ptx.change_dts.amount) << " change");
10379 
10380  tx.tx = test_tx;
10381  tx.ptx = test_ptx;
10382  tx.weight = get_transaction_weight(test_tx, txBlob.size());
10383  tx.outs = outs;
10384  tx.needed_fee = test_ptx.fee;
10385  accumulated_fee += test_ptx.fee;
10386  accumulated_change += test_ptx.change_dts.amount;
10387  if (!unused_transfers_indices.empty() || !unused_dust_indices.empty())
10388  {
10389  LOG_PRINT_L2("We have more to pay, starting another tx");
10390  txes.push_back(TX());
10391  }
10392  }
10393  }
10394 
10395  LOG_PRINT_L1("Done creating " << txes.size() << " transactions, " << print_etn(accumulated_fee) <<
10396  " total fee, " << print_etn(accumulated_change) << " total change");
10397 
10399  for (std::vector<TX>::iterator i = txes.begin(); i != txes.end(); ++i)
10400  {
10401  TX &tx = *i;
10402  cryptonote::transaction test_tx;
10403  test_tx.version = tx_version;
10404  pending_tx test_ptx;
10405  transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra,
10407 
10408  auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
10409  tx.tx = test_tx;
10410  tx.ptx = test_ptx;
10411  tx.weight = get_transaction_weight(test_tx, txBlob.size());
10412  }
10413 
10414  std::vector<wallet2::pending_tx> ptx_vector;
10415  for (std::vector<TX>::iterator i = txes.begin(); i != txes.end(); ++i)
10416  {
10417  TX &tx = *i;
10418  uint64_t tx_etn = 0;
10419  for (size_t idx: tx.selected_transfers)
10420  tx_etn += m_transfers[idx].amount();
10421  LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() <<
10422  " " << get_transaction_hash(tx.ptx.tx) << ": " << get_weight_string(tx.weight) << ", sending " << print_etn(tx_etn) << " in " << tx.selected_transfers.size() <<
10423  " outputs to " << tx.dsts.size() << " destination(s), including " <<
10424  print_etn(tx.ptx.fee) << " fee, " << print_etn(tx.ptx.change_dts.amount) << " change");
10425  ptx_vector.push_back(tx.ptx);
10426  }
10427 
10428  uint64_t a = 0;
10429  for (const TX &tx: txes)
10430  {
10431  for (size_t idx: tx.selected_transfers)
10432  {
10433  a += m_transfers[idx].amount();
10434  }
10435  a -= tx.ptx.fee;
10436  }
10437  std::vector<cryptonote::tx_destination_entry> synthetic_dsts(1, cryptonote::tx_destination_entry("", a, address, is_subaddress));
10438  THROW_WALLET_EXCEPTION_IF(!sanity_check(ptx_vector, synthetic_dsts), error::wallet_internal_error, "Created transaction(s) failed sanity check");
10439 
10440  // if we made it this far, we're OK to actually send the transactions
10441  return ptx_vector;
10442 }
10443 
10444 //----------------------------------------------------------------------------------------------------
10445 void wallet2::cold_tx_aux_import(const std::vector<pending_tx> & ptx, const std::vector<std::string> & tx_device_aux)
10446 {
10447  CHECK_AND_ASSERT_THROW_MES(ptx.size() == tx_device_aux.size(), "TX aux has invalid size");
10448  for (size_t i = 0; i < ptx.size(); ++i){
10449  crypto::hash txid;
10450  txid = get_transaction_hash(ptx[i].tx);
10451  set_tx_device_aux(txid, tx_device_aux[i]);
10452  }
10453 }
10454 //----------------------------------------------------------------------------------------------------
10455 void 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)
10456 {
10457  auto & hwdev = get_account().get_device();
10458  if (!hwdev.has_tx_cold_sign()){
10459  throw std::invalid_argument("Device does not support cold sign protocol");
10460  }
10461 
10462  unsigned_tx_set txs;
10463  for (auto &tx: ptx_vector)
10464  {
10465  txs.txes.push_back(get_construction_data_with_decrypted_short_payment_id(tx, m_account.get_device()));
10466  }
10467  txs.transfers = std::make_pair(0, m_transfers);
10468 
10469  auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev);
10470  CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface");
10471 
10472  hw::tx_aux_data aux_data;
10474  setup_shim(&wallet_shim, this);
10475  aux_data.tx_recipients = dsts_info;
10476  aux_data.bp_version = use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1;
10477  dev_cold->tx_sign(&wallet_shim, txs, exported_txs, aux_data);
10478  tx_device_aux = aux_data.tx_device_aux;
10479 
10480  MDEBUG("Signed tx data from hw: " << exported_txs.ptx.size() << " transactions");
10481  for (auto &c_ptx: exported_txs.ptx) LOG_PRINT_L0(cryptonote::obj_to_json_str(c_ptx.tx));
10482 }
10483 //----------------------------------------------------------------------------------------------------
10484 uint64_t wallet2::cold_key_image_sync(uint64_t &spent, uint64_t &unspent) {
10485  auto & hwdev = get_account().get_device();
10486  CHECK_AND_ASSERT_THROW_MES(hwdev.has_ki_cold_sync(), "Device does not support cold ki sync protocol");
10487 
10488  auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev);
10489  CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface");
10490 
10491  std::vector<std::pair<crypto::key_image, crypto::signature>> ski;
10493  setup_shim(&wallet_shim, this);
10494 
10495  dev_cold->ki_sync(&wallet_shim, m_transfers, ski);
10496 
10497  // Call COMMAND_RPC_IS_KEY_IMAGE_SPENT only if daemon is trusted.
10498  uint64_t import_res = import_key_images(ski, 0, spent, unspent, is_trusted_daemon());
10499  m_device_last_key_image_sync = time(NULL);
10500 
10501  return import_res;
10502 }
10503 //----------------------------------------------------------------------------------------------------
10504 void wallet2::get_hard_fork_info(uint8_t version, uint64_t &earliest_height) const
10505 {
10506  boost::optional<std::string> result = m_node_rpc_proxy.get_earliest_height(version, earliest_height);
10507  throw_on_rpc_response_error(result, "get_hard_fork_info");
10508 }
10509 //----------------------------------------------------------------------------------------------------
10510 bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks) const
10511 {
10512  // TODO: How to get fork rule info from light wallet node?
10513  if(m_light_wallet)
10514  return true;
10515  uint64_t height, earliest_height;
10516  boost::optional<std::string> result = m_node_rpc_proxy.get_height(height);
10517  throw_on_rpc_response_error(result, "get_info");
10518  result = m_node_rpc_proxy.get_earliest_height(version, earliest_height);
10519  throw_on_rpc_response_error(result, "get_hard_fork_info");
10520 
10521  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
10522  if (close_enough)
10523  LOG_PRINT_L2("Using v" << (unsigned)version << " rules");
10524  else
10525  LOG_PRINT_L2("Not using v" << (unsigned)version << " rules");
10526  return close_enough;
10527 }
10528 //----------------------------------------------------------------------------------------------------
10529 uint64_t wallet2::get_upper_transaction_weight_limit() const
10530 {
10531  if (m_upper_transaction_weight_limit > 0)
10532  return m_upper_transaction_weight_limit;
10533  uint64_t full_reward_zone = use_fork_rules(10, 10) ?
10534  CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V10 : use_fork_rules(8, 10) ?
10535  CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V8 : use_fork_rules(5, 10) ?
10536  CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : use_fork_rules(2, 10) ?
10538 
10539  if (use_fork_rules(8, 10))
10540  return full_reward_zone / 2 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
10541  else
10542  return full_reward_zone - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
10543 }
10544 //----------------------------------------------------------------------------------------------------
10545 std::vector<size_t> wallet2::select_available_outputs(const std::function<bool(const transfer_details &td)> &f) const
10546 {
10547  std::vector<size_t> outputs;
10548  size_t n = 0;
10549  for (transfer_container::const_iterator i = m_transfers.begin(); i != m_transfers.end(); ++i, ++n)
10550  {
10551  if (i->m_spent)
10552  continue;
10553  if (i->m_frozen)
10554  continue;
10555  if (i->m_key_image_partial)
10556  continue;
10557  if (!is_transfer_unlocked(*i))
10558  continue;
10559  if (f(*i))
10560  outputs.push_back(n);
10561  }
10562  return outputs;
10563 }
10564 //----------------------------------------------------------------------------------------------------
10565 std::vector<uint64_t> wallet2::get_unspent_amounts_vector() const
10566 {
10567  std::set<uint64_t> set;
10568  for (const auto &td: m_transfers)
10569  {
10570  if (!td.m_spent && !td.m_frozen)
10571  set.insert(td.is_rct() ? 0 : td.amount());
10572  }
10573  std::vector<uint64_t> vector;
10574  vector.reserve(set.size());
10575  for (const auto &i: set)
10576  {
10577  vector.push_back(i);
10578  }
10579  return vector;
10580 }
10581 //----------------------------------------------------------------------------------------------------
10582 std::vector<size_t> wallet2::select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct)
10583 {
10586  m_daemon_rpc_mutex.lock();
10587  if (is_trusted_daemon())
10588  req_t.amounts = get_unspent_amounts_vector();
10589  req_t.min_count = count;
10590  req_t.max_count = 0;
10591  req_t.unlocked = unlocked;
10592  req_t.recent_cutoff = 0;
10593  bool r = invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, rpc_timeout);
10594  m_daemon_rpc_mutex.unlock();
10595  THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "select_available_outputs_from_histogram");
10596  THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
10598 
10599  std::set<uint64_t> mixable;
10600  for (const auto &i: resp_t.histogram)
10601  {
10602  mixable.insert(i.amount);
10603  }
10604 
10605  return select_available_outputs([mixable, atleast, allow_rct](const transfer_details &td) {
10606  if (!allow_rct && td.is_rct())
10607  return false;
10608  const uint64_t amount = td.is_rct() ? 0 : td.amount();
10609  if (atleast) {
10610  if (mixable.find(amount) != mixable.end())
10611  return true;
10612  }
10613  else {
10614  if (mixable.find(amount) == mixable.end())
10615  return true;
10616  }
10617  return false;
10618  });
10619 }
10620 //----------------------------------------------------------------------------------------------------
10621 uint64_t wallet2::get_num_rct_outputs()
10622 {
10625  m_daemon_rpc_mutex.lock();
10626  req_t.amounts.push_back(0);
10627  req_t.min_count = 0;
10628  req_t.max_count = 0;
10629  req_t.unlocked = true;
10630  req_t.recent_cutoff = 0;
10631  bool r = invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, rpc_timeout);
10632  m_daemon_rpc_mutex.unlock();
10633  THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_num_rct_outputs");
10634  THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
10636  THROW_WALLET_EXCEPTION_IF(resp_t.histogram.size() != 1, error::get_histogram_error, "Expected exactly one response");
10637  THROW_WALLET_EXCEPTION_IF(resp_t.histogram[0].amount != 0, error::get_histogram_error, "Expected 0 amount");
10638 
10639  return resp_t.histogram[0].total_instances;
10640 }
10641 //----------------------------------------------------------------------------------------------------
10642 const wallet2::transfer_details &wallet2::get_transfer_details(size_t idx) const
10643 {
10644  THROW_WALLET_EXCEPTION_IF(idx >= m_transfers.size(), error::wallet_internal_error, "Bad transfer index");
10645  return m_transfers[idx];
10646 }
10647 //----------------------------------------------------------------------------------------------------
10648 std::vector<size_t> wallet2::select_available_unmixable_outputs()
10649 {
10650  // request all outputs with less instances than the min ring size
10651  return select_available_outputs_from_histogram(get_min_ring_size(), false, true, false);
10652 }
10653 //----------------------------------------------------------------------------------------------------
10654 std::vector<size_t> wallet2::select_available_mixable_outputs()
10655 {
10656  // request all outputs with at least as many instances as the min ring size
10657  return select_available_outputs_from_histogram(get_min_ring_size(), true, true, true);
10658 }
10659 //----------------------------------------------------------------------------------------------------
10660 std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions()
10661 {
10662  // From hard fork 1, we don't consider small amounts to be dust anymore
10663  const bool hf1_rules = use_fork_rules(2, 10); // first hard fork has version 2
10664  tx_dust_policy dust_policy(hf1_rules ? 0 : ::config::DEFAULT_DUST_THRESHOLD);
10665 
10666  uint8_t tx_version = this->public_transactions_required() ? 3 : 1;
10667  const uint64_t base_fee = get_base_fee();
10668 
10669  // may throw
10670  std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs();
10671  size_t num_dust_outputs = unmixable_outputs.size();
10672 
10673  if (num_dust_outputs == 0)
10674  {
10675  return std::vector<wallet2::pending_tx>();
10676  }
10677 
10678  // split in "dust" and "non dust" to make it easier to select outputs
10679  std::vector<size_t> unmixable_transfer_outputs, unmixable_dust_outputs;
10680  for (auto n: unmixable_outputs)
10681  {
10682  if (m_transfers[n].amount() < base_fee)
10683  unmixable_dust_outputs.push_back(n);
10684  else
10685  unmixable_transfer_outputs.push_back(n);
10686  }
10687 
10688  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);
10689 }
10690 //----------------------------------------------------------------------------------------------------
10691 void wallet2::discard_unmixable_outputs()
10692 {
10693  // may throw
10694  std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs();
10695  for (size_t idx : unmixable_outputs)
10696  {
10697  freeze(idx);
10698  }
10699 }
10700 
10701 bool wallet2::get_tx_key_cached(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const
10702 {
10703  additional_tx_keys.clear();
10704  const std::unordered_map<crypto::hash, crypto::secret_key>::const_iterator i = m_tx_keys.find(txid);
10705  if (i == m_tx_keys.end())
10706  return false;
10707  tx_key = i->second;
10708  const auto j = m_additional_tx_keys.find(txid);
10709  if (j != m_additional_tx_keys.end())
10710  additional_tx_keys = j->second;
10711  return true;
10712 }
10713 //----------------------------------------------------------------------------------------------------
10714 bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys)
10715 {
10716  bool r = get_tx_key_cached(txid, tx_key, additional_tx_keys);
10717  if (r)
10718  {
10719  return true;
10720  }
10721 
10722  auto & hwdev = get_account().get_device();
10723 
10724  // So far only Cold protocol devices are supported.
10726  {
10727  return false;
10728  }
10729 
10730  const auto tx_data_it = m_tx_device.find(txid);
10731  if (tx_data_it == m_tx_device.end())
10732  {
10733  MDEBUG("Aux data not found for txid: " << txid);
10734  return false;
10735  }
10736 
10737  auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev);
10738  CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface");
10739  if (!dev_cold->is_get_tx_key_supported())
10740  {
10741  MDEBUG("get_tx_key not supported by the device");
10742  return false;
10743  }
10744 
10745  hw::device_cold::tx_key_data_t tx_key_data;
10746  dev_cold->load_tx_key_data(tx_key_data, tx_data_it->second);
10747 
10748  // Load missing tx prefix hash
10749  if (tx_key_data.tx_prefix_hash.empty())
10750  {
10753  req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
10754  req.decode_as_json = false;
10755  req.prune = true;
10756  m_daemon_rpc_mutex.lock();
10757  bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout);
10758  m_daemon_rpc_mutex.unlock();
10759  THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
10760  error::wallet_internal_error, "Failed to get transaction from daemon");
10761 
10763  crypto::hash tx_hash{};
10764  cryptonote::blobdata tx_data;
10765  crypto::hash tx_prefix_hash{};
10766  ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
10767  THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
10768  THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx, tx_hash, tx_prefix_hash),
10769  error::wallet_internal_error, "Failed to validate transaction from daemon");
10771  "Failed to get the right transaction from daemon");
10772 
10773  tx_key_data.tx_prefix_hash = std::string(tx_prefix_hash.data, 32);
10774  }
10775 
10776  std::vector<crypto::secret_key> tx_keys;
10777  dev_cold->get_tx_key(tx_keys, tx_key_data, m_account.get_keys().m_view_secret_key);
10778  if (tx_keys.empty())
10779  {
10780  return false;
10781  }
10782 
10783  tx_key = tx_keys[0];
10784  tx_keys.erase(tx_keys.begin());
10785  additional_tx_keys = tx_keys;
10786 
10787  return true;
10788 }
10789 //----------------------------------------------------------------------------------------------------
10790 void wallet2::set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys)
10791 {
10792  // fetch tx from daemon and check if secret keys agree with corresponding public keys
10794  req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
10795  req.decode_as_json = false;
10796  req.prune = true;
10798  bool r;
10799  {
10800  const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
10801  r = invoke_http_json("/gettransactions", req, res, rpc_timeout);
10802  }
10807  "daemon returned wrong response for gettransactions, wrong txs count = " +
10808  std::to_string(res.txs.size()) + ", expected 1");
10810  crypto::hash tx_hash;
10811  THROW_WALLET_EXCEPTION_IF(!get_pruned_tx(res.txs[0], tx, tx_hash), error::wallet_internal_error,
10812  "Failed to get transaction from daemon");
10813  THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "txid mismatch");
10814  std::vector<tx_extra_field> tx_extra_fields;
10815  THROW_WALLET_EXCEPTION_IF(!parse_tx_extra(tx.extra, tx_extra_fields), error::wallet_internal_error, "Transaction extra has unsupported format");
10816  tx_extra_pub_key pub_key_field;
10817  bool found = false;
10818  size_t index = 0;
10819  while (find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, index++))
10820  {
10821  crypto::public_key calculated_pub_key;
10822  crypto::secret_key_to_public_key(tx_key, calculated_pub_key);
10823  if (calculated_pub_key == pub_key_field.pub_key)
10824  {
10825  found = true;
10826  break;
10827  }
10828  }
10829  THROW_WALLET_EXCEPTION_IF(!found, error::wallet_internal_error, "Given tx secret key doesn't agree with the tx public key in the blockchain");
10830  tx_extra_additional_pub_keys additional_tx_pub_keys;
10831  find_tx_extra_field_by_type(tx_extra_fields, additional_tx_pub_keys);
10832  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" );
10833  m_tx_keys.insert(std::make_pair(txid, tx_key));
10834  m_additional_tx_keys.insert(std::make_pair(txid, additional_tx_keys));
10835 }
10836 //----------------------------------------------------------------------------------------------------
10837 std::string wallet2::get_spend_proof(const crypto::hash &txid, const std::string &message)
10838 {
10840  "get_spend_proof requires spend secret key and is not available for a watch-only wallet");
10841 
10842  // fetch tx from daemon
10844  req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
10845  req.decode_as_json = false;
10846  req.prune = true;
10848  bool r;
10849  {
10850  const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
10851  r = invoke_http_json("/gettransactions", req, res, rpc_timeout);
10852  }
10857  "daemon returned wrong response for gettransactions, wrong txs count = " +
10858  std::to_string(res.txs.size()) + ", expected 1");
10859 
10861  crypto::hash tx_hash;
10862  THROW_WALLET_EXCEPTION_IF(!get_pruned_tx(res.txs[0], tx, tx_hash), error::wallet_internal_error, "Failed to get tx from daemon");
10863 
10864  std::vector<std::vector<crypto::signature>> signatures;
10865 
10866  // get signature prefix hash
10867  std::string sig_prefix_data((const char*)&txid, sizeof(crypto::hash));
10868  sig_prefix_data += message;
10869  crypto::hash sig_prefix_hash;
10870  crypto::cn_fast_hash(sig_prefix_data.data(), sig_prefix_data.size(), sig_prefix_hash);
10871 
10872  for(size_t i = 0; i < tx.vin.size(); ++i)
10873  {
10874  const txin_to_key* const in_key = boost::get<txin_to_key>(std::addressof(tx.vin[i]));
10875  if (in_key == nullptr)
10876  continue;
10877 
10878  // check if the key image belongs to us
10879  const auto found = m_key_images.find(in_key->k_image);
10880  if(found == m_key_images.end())
10881  {
10882  THROW_WALLET_EXCEPTION_IF(i > 0, error::wallet_internal_error, "subset of key images belong to us, very weird!");
10883  THROW_WALLET_EXCEPTION_IF(true, error::wallet_internal_error, "This tx wasn't generated by this wallet!");
10884  }
10885 
10886  // derive the real output keypair
10887  const transfer_details& in_td = m_transfers[found->second];
10888  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));
10889  THROW_WALLET_EXCEPTION_IF(in_tx_out_pkey == nullptr, error::wallet_internal_error, "Output is not txout_to_key");
10890  const crypto::public_key in_tx_pub_key = get_tx_pub_key_from_extra(in_td.m_tx, in_td.m_pk_index);
10891  const std::vector<crypto::public_key> in_additionakl_tx_pub_keys = get_additional_tx_pub_keys_from_extra(in_td.m_tx);
10892  keypair in_ephemeral;
10893  crypto::key_image in_img;
10894  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),
10895  error::wallet_internal_error, "failed to generate key image");
10896  THROW_WALLET_EXCEPTION_IF(in_key->k_image != in_img, error::wallet_internal_error, "key image mismatch");
10897 
10898  // get output pubkeys in the ring
10899  const std::vector<uint64_t> absolute_offsets = cryptonote::relative_output_offsets_to_absolute(in_key->key_offsets);
10900  const size_t ring_size = in_key->key_offsets.size();
10901  THROW_WALLET_EXCEPTION_IF(absolute_offsets.size() != ring_size, error::wallet_internal_error, "absolute offsets size is wrong");
10903  req.outputs.resize(ring_size);
10904  for (size_t j = 0; j < ring_size; ++j)
10905  {
10906  req.outputs[j].amount = in_key->amount;
10907  req.outputs[j].index = absolute_offsets[j];
10908  }
10910  bool r;
10911  {
10912  const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
10913  r = invoke_http_bin("/get_outs.bin", req, res, rpc_timeout);
10914  }
10919  "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
10920  std::to_string(res.outs.size()) + ", expected " + std::to_string(ring_size));
10921 
10922  // copy pubkey pointers
10923  std::vector<const crypto::public_key *> p_output_keys;
10924  for (const COMMAND_RPC_GET_OUTPUTS_BIN::outkey &out : res.outs)
10925  p_output_keys.push_back(&out.key);
10926 
10927  // figure out real output index and secret key
10928  size_t sec_index = -1;
10929  for (size_t j = 0; j < ring_size; ++j)
10930  {
10931  if (res.outs[j].key == in_ephemeral.pub)
10932  {
10933  sec_index = j;
10934  break;
10935  }
10936  }
10937  THROW_WALLET_EXCEPTION_IF(sec_index >= ring_size, error::wallet_internal_error, "secret index not found");
10938 
10939  // generate ring sig for this input
10940  signatures.push_back(std::vector<crypto::signature>());
10941  std::vector<crypto::signature>& sigs = signatures.back();
10942  sigs.resize(in_key->key_offsets.size());
10943  crypto::generate_ring_signature(sig_prefix_hash, in_key->k_image, p_output_keys, in_ephemeral.sec, sec_index, sigs.data());
10944  }
10945 
10946  std::string sig_str = "SpendProofV1";
10947  for (const std::vector<crypto::signature>& ring_sig : signatures)
10948  for (const crypto::signature& sig : ring_sig)
10949  sig_str += tools::base58::encode(std::string((const char *)&sig, sizeof(crypto::signature)));
10950  return sig_str;
10951 }
10952 //----------------------------------------------------------------------------------------------------
10953 bool wallet2::check_spend_proof(const crypto::hash &txid, const std::string &message, const std::string &sig_str)
10954 {
10955  const std::string header = "SpendProofV1";
10956  const size_t header_len = header.size();
10957  THROW_WALLET_EXCEPTION_IF(sig_str.size() < header_len || sig_str.substr(0, header_len) != header, error::wallet_internal_error,
10958  "Signature header check error");
10959 
10960  // fetch tx from daemon
10962  req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
10963  req.decode_as_json = false;
10964  req.prune = true;
10966  bool r;
10967  {
10968  const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
10969  r = invoke_http_json("/gettransactions", req, res, rpc_timeout);
10970  }
10975  "daemon returned wrong response for gettransactions, wrong txs count = " +
10976  std::to_string(res.txs.size()) + ", expected 1");
10977 
10979  crypto::hash tx_hash;
10980  THROW_WALLET_EXCEPTION_IF(!get_pruned_tx(res.txs[0], tx, tx_hash), error::wallet_internal_error, "failed to get tx from daemon");
10981 
10982  // check signature size
10983  size_t num_sigs = 0;
10984  for(size_t i = 0; i < tx.vin.size(); ++i)
10985  {
10986  const txin_to_key* const in_key = boost::get<txin_to_key>(std::addressof(tx.vin[i]));
10987  if (in_key != nullptr)
10988  num_sigs += in_key->key_offsets.size();
10989  }
10990  std::vector<std::vector<crypto::signature>> signatures = { std::vector<crypto::signature>(1) };
10991  const size_t sig_len = tools::base58::encode(std::string((const char *)&signatures[0][0], sizeof(crypto::signature))).size();
10992  if( sig_str.size() != header_len + num_sigs * sig_len ) {
10993  return false;
10994  }
10995 
10996  // decode base58
10997  signatures.clear();
10998  size_t offset = header_len;
10999  for(size_t i = 0; i < tx.vin.size(); ++i)
11000  {
11001  const txin_to_key* const in_key = boost::get<txin_to_key>(std::addressof(tx.vin[i]));
11002  if (in_key == nullptr)
11003  continue;
11004  signatures.resize(signatures.size() + 1);
11005  signatures.back().resize(in_key->key_offsets.size());
11006  for (size_t j = 0; j < in_key->key_offsets.size(); ++j)
11007  {
11008  std::string sig_decoded;
11009  THROW_WALLET_EXCEPTION_IF(!tools::base58::decode(sig_str.substr(offset, sig_len), sig_decoded), error::wallet_internal_error, "Signature decoding error");
11010  THROW_WALLET_EXCEPTION_IF(sizeof(crypto::signature) != sig_decoded.size(), error::wallet_internal_error, "Signature decoding error");
11011  memcpy(&signatures.back()[j], sig_decoded.data(), sizeof(crypto::signature));
11012  offset += sig_len;
11013  }
11014  }
11015 
11016  // get signature prefix hash
11017  std::string sig_prefix_data((const char*)&txid, sizeof(crypto::hash));
11018  sig_prefix_data += message;
11019  crypto::hash sig_prefix_hash;
11020  crypto::cn_fast_hash(sig_prefix_data.data(), sig_prefix_data.size(), sig_prefix_hash);
11021 
11022  std::vector<std::vector<crypto::signature>>::const_iterator sig_iter = signatures.cbegin();
11023  for(size_t i = 0; i < tx.vin.size(); ++i)
11024  {
11025  const txin_to_key* const in_key = boost::get<txin_to_key>(std::addressof(tx.vin[i]));
11026  if (in_key == nullptr)
11027  continue;
11028 
11029  // get output pubkeys in the ring
11031  const std::vector<uint64_t> absolute_offsets = cryptonote::relative_output_offsets_to_absolute(in_key->key_offsets);
11032  req.outputs.resize(absolute_offsets.size());
11033  for (size_t j = 0; j < absolute_offsets.size(); ++j)
11034  {
11035  req.outputs[j].amount = in_key->amount;
11036  req.outputs[j].index = absolute_offsets[j];
11037  }
11039  bool r;
11040  {
11041  const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
11042  r = invoke_http_bin("/get_outs.bin", req, res, rpc_timeout);
11043  }
11047  THROW_WALLET_EXCEPTION_IF(res.outs.size() != req.outputs.size(), error::wallet_internal_error,
11048  "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
11049  std::to_string(res.outs.size()) + ", expected " + std::to_string(req.outputs.size()));
11050 
11051  // copy pointers
11052  std::vector<const crypto::public_key *> p_output_keys;
11053  for (const COMMAND_RPC_GET_OUTPUTS_BIN::outkey &out : res.outs)
11054  p_output_keys.push_back(&out.key);
11055 
11056  // check this ring
11057  if (!crypto::check_ring_signature(sig_prefix_hash, in_key->k_image, p_output_keys, sig_iter->data()))
11058  return false;
11059  ++sig_iter;
11060  }
11061  THROW_WALLET_EXCEPTION_IF(sig_iter != signatures.cend(), error::wallet_internal_error, "Signature iterator didn't reach the end");
11062  return true;
11063 }
11064 //----------------------------------------------------------------------------------------------------
11065 
11066 void 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)
11067 {
11068  crypto::key_derivation derivation;
11070  "Failed to generate key derivation from supplied parameters");
11071 
11072  std::vector<crypto::key_derivation> additional_derivations;
11073  additional_derivations.resize(additional_tx_keys.size());
11074  for (size_t i = 0; i < additional_tx_keys.size(); ++i)
11075  THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(address.m_view_public_key, additional_tx_keys[i], additional_derivations[i]), error::wallet_internal_error,
11076  "Failed to generate key derivation from supplied parameters");
11077 
11078  check_tx_key_helper(txid, derivation, additional_derivations, address, received, in_pool, confirmations);
11079 }
11080 
11081 void 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
11082 {
11083  received = 0;
11084 
11085  for (size_t n = 0; n < tx.vout.size(); ++n)
11086  {
11087  const cryptonote::txout_to_key* const out_key = boost::get<cryptonote::txout_to_key>(std::addressof(tx.vout[n].target));
11088  if (!out_key)
11089  continue;
11090 
11091  crypto::public_key derived_out_key;
11092  bool r = crypto::derive_public_key(derivation, n, address.m_spend_public_key, derived_out_key);
11093  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to derive public key");
11094  bool found = out_key->key == derived_out_key;
11095  crypto::key_derivation found_derivation = derivation;
11096  if (!found && !additional_derivations.empty())
11097  {
11098  r = crypto::derive_public_key(additional_derivations[n], n, address.m_spend_public_key, derived_out_key);
11099  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to derive public key");
11100  found = out_key->key == derived_out_key;
11101  found_derivation = additional_derivations[n];
11102  }
11103 
11104  if (found)
11105  {
11106  uint64_t amount;
11107  amount = tx.vout[n].amount;
11108  received += amount;
11109  }
11110  }
11111 }
11112 
11113 void 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)
11114 {
11117  req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
11118  req.decode_as_json = false;
11119  req.prune = true;
11120  m_daemon_rpc_mutex.lock();
11121  bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout);
11122  m_daemon_rpc_mutex.unlock();
11123  THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
11124  error::wallet_internal_error, "Failed to get transaction from daemon");
11125 
11127  crypto::hash tx_hash;
11128  if (res.txs.size() == 1)
11129  {
11130  ok = get_pruned_tx(res.txs.front(), tx, tx_hash);
11131  THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
11132  }
11133  else
11134  {
11135  cryptonote::blobdata tx_data;
11136  ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
11137  THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
11139  error::wallet_internal_error, "Failed to validate transaction from daemon");
11140  tx_hash = cryptonote::get_transaction_hash(tx);
11141  }
11142 
11144  "Failed to get the right transaction from daemon");
11145  THROW_WALLET_EXCEPTION_IF(!additional_derivations.empty() && additional_derivations.size() != tx.vout.size(), error::wallet_internal_error,
11146  "The size of additional derivations is wrong");
11147 
11148  check_tx_key_helper(tx, derivation, additional_derivations, address, received);
11149 
11150  in_pool = res.txs.front().in_pool;
11151  confirmations = 0;
11152  if (!in_pool)
11153  {
11154  std::string err;
11155  uint64_t bc_height = get_daemon_blockchain_height(err);
11156  if (err.empty())
11157  confirmations = bc_height - res.txs.front().block_height;
11158  }
11159 }
11160 
11161 std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message)
11162 {
11163  // fetch tx pubkey from the daemon
11166  req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
11167  req.decode_as_json = false;
11168  req.prune = true;
11169  m_daemon_rpc_mutex.lock();
11170  bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout);
11171  m_daemon_rpc_mutex.unlock();
11172  THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
11173  error::wallet_internal_error, "Failed to get transaction from daemon");
11174 
11176  crypto::hash tx_hash;
11177  if (res.txs.size() == 1)
11178  {
11179  ok = get_pruned_tx(res.txs.front(), tx, tx_hash);
11180  THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
11181  }
11182  else
11183  {
11184  cryptonote::blobdata tx_data;
11185  ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
11186  THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
11188  error::wallet_internal_error, "Failed to validate transaction from daemon");
11189  tx_hash = cryptonote::get_transaction_hash(tx);
11190  }
11191 
11192  CHECK_AND_ASSERT_THROW_MES(tx.version == 1, "Tx proofs are for v1 transactions only");
11193  THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "Failed to get the right transaction from daemon");
11194 
11195  // determine if the address is found in the subaddress hash table (i.e. whether the proof is outbound or inbound)
11197  std::vector<crypto::secret_key> additional_tx_keys;
11198  const bool is_out = m_subaddresses.count(address.m_spend_public_key) == 0;
11199  if (is_out)
11200  {
11201  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.");
11202  }
11203 
11204  return get_tx_proof(tx, tx_key, additional_tx_keys, address, is_subaddress, message);
11205 }
11206 
11207 std::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
11208 {
11209  hw::device &hwdev = m_account.get_device();
11210  rct::key aP;
11211  // determine if the address is found in the subaddress hash table (i.e. whether the proof is outbound or inbound)
11212  const bool is_out = m_subaddresses.count(address.m_spend_public_key) == 0;
11213 
11215  std::string prefix_data((const char*)&txid, sizeof(crypto::hash));
11216  prefix_data += message;
11217  crypto::hash prefix_hash;
11218  crypto::cn_fast_hash(prefix_data.data(), prefix_data.size(), prefix_hash);
11219 
11220  std::vector<crypto::public_key> shared_secret;
11221  std::vector<crypto::signature> sig;
11222  std::string sig_str;
11223  if (is_out)
11224  {
11225  const size_t num_sigs = 1 + additional_tx_keys.size();
11226  shared_secret.resize(num_sigs);
11227  sig.resize(num_sigs);
11228 
11229  hwdev.scalarmultKey(aP, rct::pk2rct(address.m_view_public_key), rct::sk2rct(tx_key));
11230  shared_secret[0] = rct::rct2pk(aP);
11231  crypto::public_key tx_pub_key;
11232  if (is_subaddress)
11233  {
11234  hwdev.scalarmultKey(aP, rct::pk2rct(address.m_spend_public_key), rct::sk2rct(tx_key));
11235  tx_pub_key = rct2pk(aP);
11236  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]);
11237  }
11238  else
11239  {
11240  hwdev.secret_key_to_public_key(tx_key, tx_pub_key);
11241  hwdev.generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, boost::none, shared_secret[0], tx_key, sig[0]);
11242  }
11243  for (size_t i = 1; i < num_sigs; ++i)
11244  {
11245  hwdev.scalarmultKey(aP, rct::pk2rct(address.m_view_public_key), rct::sk2rct(additional_tx_keys[i - 1]));
11246  shared_secret[i] = rct::rct2pk(aP);
11247  if (is_subaddress)
11248  {
11249  hwdev.scalarmultKey(aP, rct::pk2rct(address.m_spend_public_key), rct::sk2rct(additional_tx_keys[i - 1]));
11250  tx_pub_key = rct2pk(aP);
11251  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]);
11252  }
11253  else
11254  {
11255  hwdev.secret_key_to_public_key(additional_tx_keys[i - 1], tx_pub_key);
11256  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]);
11257  }
11258  }
11259  sig_str = std::string("OutProofV1");
11260  }
11261  else
11262  {
11264  THROW_WALLET_EXCEPTION_IF(tx_pub_key == null_pkey, error::wallet_internal_error, "Tx pubkey was not found");
11265 
11266  std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(tx);
11267  const size_t num_sigs = 1 + additional_tx_pub_keys.size();
11268  shared_secret.resize(num_sigs);
11269  sig.resize(num_sigs);
11270 
11271  const crypto::secret_key& a = m_account.get_keys().m_view_secret_key;
11272  hwdev.scalarmultKey(aP, rct::pk2rct(tx_pub_key), rct::sk2rct(a));
11273  shared_secret[0] = rct2pk(aP);
11274  if (is_subaddress)
11275  {
11276  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]);
11277  }
11278  else
11279  {
11280  hwdev.generate_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, boost::none, shared_secret[0], a, sig[0]);
11281  }
11282  for (size_t i = 1; i < num_sigs; ++i)
11283  {
11284  hwdev.scalarmultKey(aP,rct::pk2rct(additional_tx_pub_keys[i - 1]), rct::sk2rct(a));
11285  shared_secret[i] = rct2pk(aP);
11286  if (is_subaddress)
11287  {
11288  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]);
11289  }
11290  else
11291  {
11292  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]);
11293  }
11294  }
11295  sig_str = std::string("InProofV1");
11296  }
11297  const size_t num_sigs = shared_secret.size();
11298 
11299  // check if this address actually received any funds
11300  crypto::key_derivation derivation;
11301  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");
11302  std::vector<crypto::key_derivation> additional_derivations(num_sigs - 1);
11303  for (size_t i = 1; i < num_sigs; ++i)
11304  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");
11305  uint64_t received;
11306  check_tx_key_helper(tx, derivation, additional_derivations, address, received);
11307  THROW_WALLET_EXCEPTION_IF(!received, error::wallet_internal_error, tr("No funds received in this tx."));
11308 
11309  // concatenate all signature strings
11310  for (size_t i = 0; i < num_sigs; ++i)
11311  sig_str +=
11312  tools::base58::encode(std::string((const char *)&shared_secret[i], sizeof(crypto::public_key))) +
11313  tools::base58::encode(std::string((const char *)&sig[i], sizeof(crypto::signature)));
11314  return sig_str;
11315 }
11316 
11317 bool 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)
11318 {
11319  // fetch tx pubkey from the daemon
11322  req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
11323  req.decode_as_json = false;
11324  req.prune = true;
11325  m_daemon_rpc_mutex.lock();
11326  bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout);
11327  m_daemon_rpc_mutex.unlock();
11328  THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
11329  error::wallet_internal_error, "Failed to get transaction from daemon");
11330 
11332  crypto::hash tx_hash;
11333  if (res.txs.size() == 1)
11334  {
11335  ok = get_pruned_tx(res.txs.front(), tx, tx_hash);
11336  THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
11337  }
11338  else
11339  {
11340  cryptonote::blobdata tx_data;
11341  ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
11342  THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
11344  error::wallet_internal_error, "Failed to validate transaction from daemon");
11345  tx_hash = cryptonote::get_transaction_hash(tx);
11346  }
11347 
11348  THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "Failed to get the right transaction from daemon");
11349 
11350  if (!check_tx_proof(tx, address, is_subaddress, message, sig_str, received))
11351  return false;
11352 
11353  in_pool = res.txs.front().in_pool;
11354  confirmations = 0;
11355  if (!in_pool)
11356  {
11357  std::string err;
11358  uint64_t bc_height = get_daemon_blockchain_height(err);
11359  if (err.empty())
11360  confirmations = bc_height - res.txs.front().block_height;
11361  }
11362 
11363  return true;
11364 }
11365 
11366 bool 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
11367 {
11368  CHECK_AND_ASSERT_THROW_MES(tx.version == 1, "Tx proofs are for v1 transactions only");
11369  const bool is_out = sig_str.substr(0, 3) == "Out";
11370  const std::string header = is_out ? "OutProofV1" : "InProofV1";
11371  const size_t header_len = header.size();
11372  THROW_WALLET_EXCEPTION_IF(sig_str.size() < header_len || sig_str.substr(0, header_len) != header, error::wallet_internal_error,
11373  "Signature header check error");
11374 
11375  // decode base58
11376  std::vector<crypto::public_key> shared_secret(1);
11377  std::vector<crypto::signature> sig(1);
11378  const size_t pk_len = tools::base58::encode(std::string((const char *)&shared_secret[0], sizeof(crypto::public_key))).size();
11379  const size_t sig_len = tools::base58::encode(std::string((const char *)&sig[0], sizeof(crypto::signature))).size();
11380  const size_t num_sigs = (sig_str.size() - header_len) / (pk_len + sig_len);
11381  THROW_WALLET_EXCEPTION_IF(sig_str.size() != header_len + num_sigs * (pk_len + sig_len), error::wallet_internal_error,
11382  "Wrong signature size");
11383  shared_secret.resize(num_sigs);
11384  sig.resize(num_sigs);
11385  for (size_t i = 0; i < num_sigs; ++i)
11386  {
11387  std::string pk_decoded;
11388  std::string sig_decoded;
11389  const size_t offset = header_len + i * (pk_len + sig_len);
11390  THROW_WALLET_EXCEPTION_IF(!tools::base58::decode(sig_str.substr(offset, pk_len), pk_decoded), error::wallet_internal_error,
11391  "Signature decoding error");
11392  THROW_WALLET_EXCEPTION_IF(!tools::base58::decode(sig_str.substr(offset + pk_len, sig_len), sig_decoded), error::wallet_internal_error,
11393  "Signature decoding error");
11394  THROW_WALLET_EXCEPTION_IF(sizeof(crypto::public_key) != pk_decoded.size() || sizeof(crypto::signature) != sig_decoded.size(), error::wallet_internal_error,
11395  "Signature decoding error");
11396  memcpy(&shared_secret[i], pk_decoded.data(), sizeof(crypto::public_key));
11397  memcpy(&sig[i], sig_decoded.data(), sizeof(crypto::signature));
11398  }
11399 
11401  THROW_WALLET_EXCEPTION_IF(tx_pub_key == null_pkey, error::wallet_internal_error, "Tx pubkey was not found");
11402 
11403  std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(tx);
11404  THROW_WALLET_EXCEPTION_IF(additional_tx_pub_keys.size() + 1 != num_sigs, error::wallet_internal_error, "Signature size mismatch with additional tx pubkeys");
11405 
11407  std::string prefix_data((const char*)&txid, sizeof(crypto::hash));
11408  prefix_data += message;
11409  crypto::hash prefix_hash;
11410  crypto::cn_fast_hash(prefix_data.data(), prefix_data.size(), prefix_hash);
11411 
11412  // check signature
11413  std::vector<int> good_signature(num_sigs, 0);
11414  if (is_out)
11415  {
11416  good_signature[0] = is_subaddress ?
11417  crypto::check_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[0], sig[0]) :
11418  crypto::check_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, boost::none, shared_secret[0], sig[0]);
11419 
11420  for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
11421  {
11422  good_signature[i + 1] = is_subaddress ?
11423  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]) :
11424  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]);
11425  }
11426  }
11427  else
11428  {
11429  good_signature[0] = is_subaddress ?
11430  crypto::check_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, address.m_spend_public_key, shared_secret[0], sig[0]) :
11431  crypto::check_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, boost::none, shared_secret[0], sig[0]);
11432 
11433  for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
11434  {
11435  good_signature[i + 1] = is_subaddress ?
11436  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]) :
11437  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]);
11438  }
11439  }
11440 
11441  if (std::any_of(good_signature.begin(), good_signature.end(), [](int i) { return i > 0; }))
11442  {
11443  // obtain key derivation by multiplying scalar 1 to the shared secret
11444  crypto::key_derivation derivation;
11445  if (good_signature[0])
11446  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");
11447 
11448  std::vector<crypto::key_derivation> additional_derivations(num_sigs - 1);
11449  for (size_t i = 1; i < num_sigs; ++i)
11450  if (good_signature[i])
11451  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");
11452 
11453  check_tx_key_helper(tx, derivation, additional_derivations, address, received);
11454  return true;
11455  }
11456  return false;
11457 }
11458 
11459 std::string wallet2::get_reserve_proof(const boost::optional<std::pair<uint32_t, uint64_t>> &account_minreserve, const std::string &message)
11460 {
11461  THROW_WALLET_EXCEPTION_IF(m_watch_only || m_multisig, error::wallet_internal_error, "Reserve proof can only be generated by a full wallet");
11462  THROW_WALLET_EXCEPTION_IF(balance_all(false) == 0, error::wallet_internal_error, "Zero balance");
11463  THROW_WALLET_EXCEPTION_IF(account_minreserve && balance(account_minreserve->first, false) < account_minreserve->second, error::wallet_internal_error,
11464  "Not enough balance in this account for the requested minimum reserve amount");
11465 
11466  // determine which outputs to include in the proof
11467  std::vector<size_t> selected_transfers;
11468  for (size_t i = 0; i < m_transfers.size(); ++i)
11469  {
11470  const transfer_details &td = m_transfers[i];
11471  if (!td.m_spent && !td.m_frozen && (!account_minreserve || account_minreserve->first == td.m_subaddr_index.major))
11472  selected_transfers.push_back(i);
11473  }
11474 
11475  if (account_minreserve)
11476  {
11477  THROW_WALLET_EXCEPTION_IF(account_minreserve->second == 0, error::wallet_internal_error, "Proved amount must be greater than 0");
11478  // minimize the number of outputs included in the proof, by only picking the N largest outputs that can cover the requested min reserve amount
11479  std::sort(selected_transfers.begin(), selected_transfers.end(), [&](const size_t a, const size_t b)
11480  { return m_transfers[a].amount() > m_transfers[b].amount(); });
11481  while (selected_transfers.size() >= 2 && m_transfers[selected_transfers[1]].amount() >= account_minreserve->second)
11482  selected_transfers.erase(selected_transfers.begin());
11483  size_t sz = 0;
11484  uint64_t total = 0;
11485  while (total < account_minreserve->second)
11486  {
11487  total += m_transfers[selected_transfers[sz]].amount();
11488  ++sz;
11489  }
11490  selected_transfers.resize(sz);
11491  }
11492 
11493  // compute signature prefix hash
11494  std::string prefix_data = message;
11495  prefix_data.append((const char*)&m_account.get_keys().m_account_address, sizeof(cryptonote::account_public_address));
11496  for (size_t i = 0; i < selected_transfers.size(); ++i)
11497  {
11498  prefix_data.append((const char*)&m_transfers[selected_transfers[i]].m_key_image, sizeof(crypto::key_image));
11499  }
11500  crypto::hash prefix_hash;
11501  crypto::cn_fast_hash(prefix_data.data(), prefix_data.size(), prefix_hash);
11502 
11503  // generate proof entries
11504  std::vector<reserve_proof_entry> proofs(selected_transfers.size());
11505  std::unordered_set<cryptonote::subaddress_index> subaddr_indices = { {0,0} };
11506  for (size_t i = 0; i < selected_transfers.size(); ++i)
11507  {
11508  transfer_details &td = m_transfers[selected_transfers[i]];
11509  reserve_proof_entry& proof = proofs[i];
11510  proof.txid = td.m_txid;
11512  proof.key_image = td.m_key_image;
11513  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};
11514  td.m_subaddr_index = index2;
11515  subaddr_indices.insert(td.m_subaddr_index);
11516 
11517  // get tx pub key
11518  const crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(td.m_tx, td.m_pk_index);
11519  THROW_WALLET_EXCEPTION_IF(tx_pub_key == crypto::null_pkey, error::wallet_internal_error, "The tx public key isn't found");
11520  const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx);
11521 
11522  // determine which tx pub key was used for deriving the output key
11523  const crypto::public_key *tx_pub_key_used = &tx_pub_key;
11524  for (int i = 0; i < 2; ++i)
11525  {
11526  proof.shared_secret = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(*tx_pub_key_used), rct::sk2rct(m_account.get_keys().m_view_secret_key)));
11527  crypto::key_derivation derivation;
11528  THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(proof.shared_secret, rct::rct2sk(rct::I), derivation),
11529  error::wallet_internal_error, "Failed to generate key derivation");
11530  crypto::public_key subaddress_spendkey;
11531  THROW_WALLET_EXCEPTION_IF(!derive_subaddress_public_key(td.get_public_key(), derivation, proof.index_in_tx, subaddress_spendkey),
11532  error::wallet_internal_error, "Failed to derive subaddress public key");
11533  if (m_subaddresses.count(subaddress_spendkey) == 1)
11534  break;
11535  THROW_WALLET_EXCEPTION_IF(additional_tx_pub_keys.empty(), error::wallet_internal_error,
11536  "Normal tx pub key doesn't derive the expected output, while the additional tx pub keys are empty");
11538  "Neither normal tx pub key nor additional tx pub key derive the expected output key");
11539  tx_pub_key_used = &additional_tx_pub_keys[proof.index_in_tx];
11540  }
11541 
11542  // generate signature for shared secret
11543  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);
11544 
11545  // derive ephemeral secret key
11546  crypto::key_image ki;
11547  cryptonote::keypair ephemeral;
11548  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);
11549  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
11550  THROW_WALLET_EXCEPTION_IF(ephemeral.pub != td.get_public_key(), error::wallet_internal_error, "Derived public key doesn't agree with the stored one");
11551 
11552  // generate signature for key image
11553  const std::vector<const crypto::public_key*> pubs = { &ephemeral.pub };
11554  crypto::generate_ring_signature(prefix_hash, td.m_key_image, &pubs[0], 1, ephemeral.sec, 0, &proof.key_image_sig);
11555  }
11556 
11557  // collect all subaddress spend keys that received those outputs and generate their signatures
11558  std::unordered_map<crypto::public_key, crypto::signature> subaddr_spendkeys;
11559  for (const cryptonote::subaddress_index &index : subaddr_indices)
11560  {
11561  crypto::secret_key subaddr_spend_skey = m_account.get_keys().m_spend_secret_key;
11562  if (!index.is_zero())
11563  {
11564  crypto::secret_key m = m_account.get_device().get_subaddress_secret_key(m_account.get_keys().m_view_secret_key, index);
11565  crypto::secret_key tmp = subaddr_spend_skey;
11566  sc_add((unsigned char*)&subaddr_spend_skey, (unsigned char*)&m, (unsigned char*)&tmp);
11567  }
11568  crypto::public_key subaddr_spend_pkey;
11569  secret_key_to_public_key(subaddr_spend_skey, subaddr_spend_pkey);
11570  crypto::generate_signature(prefix_hash, subaddr_spend_pkey, subaddr_spend_skey, subaddr_spendkeys[subaddr_spend_pkey]);
11571  }
11572 
11573  // serialize & encode
11574  std::ostringstream oss;
11576  ar << proofs << subaddr_spendkeys;
11577  return "ReserveProofV1" + tools::base58::encode(oss.str());
11578 }
11579 
11580 bool 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)
11581 {
11582  uint32_t rpc_version;
11583  THROW_WALLET_EXCEPTION_IF(!check_connection(&rpc_version), error::wallet_internal_error, "Failed to connect to daemon: " + get_daemon_address());
11584  THROW_WALLET_EXCEPTION_IF(rpc_version < MAKE_CORE_RPC_VERSION(1, 0), error::wallet_internal_error, "Daemon RPC version is too old");
11585 
11586  static constexpr char header[] = "ReserveProofV1";
11587  THROW_WALLET_EXCEPTION_IF(!boost::string_ref{sig_str}.starts_with(header), error::wallet_internal_error,
11588  "Signature header check error");
11589 
11590  std::string sig_decoded;
11591  THROW_WALLET_EXCEPTION_IF(!tools::base58::decode(sig_str.substr(std::strlen(header)), sig_decoded), error::wallet_internal_error,
11592  "Signature decoding error");
11593 
11594  std::istringstream iss(sig_decoded);
11596  std::vector<reserve_proof_entry> proofs;
11597  std::unordered_map<crypto::public_key, crypto::signature> subaddr_spendkeys;
11598  ar >> proofs >> subaddr_spendkeys;
11599 
11600  THROW_WALLET_EXCEPTION_IF(subaddr_spendkeys.count(address.m_spend_public_key) == 0, error::wallet_internal_error,
11601  "The given address isn't found in the proof");
11602 
11603  // compute signature prefix hash
11604  std::string prefix_data = message;
11605  prefix_data.append((const char*)&address, sizeof(cryptonote::account_public_address));
11606  for (size_t i = 0; i < proofs.size(); ++i)
11607  {
11608  prefix_data.append((const char*)&proofs[i].key_image, sizeof(crypto::key_image));
11609  }
11610  crypto::hash prefix_hash;
11611  crypto::cn_fast_hash(prefix_data.data(), prefix_data.size(), prefix_hash);
11612 
11613  // fetch txes from daemon
11616  for (size_t i = 0; i < proofs.size(); ++i)
11617  gettx_req.txs_hashes.push_back(epee::string_tools::pod_to_hex(proofs[i].txid));
11618  gettx_req.decode_as_json = false;
11619  gettx_req.prune = true;
11620  m_daemon_rpc_mutex.lock();
11621  bool ok = invoke_http_json("/gettransactions", gettx_req, gettx_res, rpc_timeout);
11622  m_daemon_rpc_mutex.unlock();
11623  THROW_WALLET_EXCEPTION_IF(!ok || gettx_res.txs.size() != proofs.size(),
11624  error::wallet_internal_error, "Failed to get transaction from daemon");
11625 
11626  // check spent status
11629  for (size_t i = 0; i < proofs.size(); ++i)
11630  kispent_req.key_images.push_back(epee::string_tools::pod_to_hex(proofs[i].key_image));
11631  m_daemon_rpc_mutex.lock();
11632  ok = invoke_http_json("/is_key_image_spent", kispent_req, kispent_res, rpc_timeout);
11633  m_daemon_rpc_mutex.unlock();
11634  THROW_WALLET_EXCEPTION_IF(!ok || kispent_res.spent_status.size() != proofs.size(),
11635  error::wallet_internal_error, "Failed to get key image spent status from daemon");
11636 
11637  total = spent = 0;
11638  for (size_t i = 0; i < proofs.size(); ++i)
11639  {
11640  const reserve_proof_entry& proof = proofs[i];
11641  THROW_WALLET_EXCEPTION_IF(gettx_res.txs[i].in_pool, error::wallet_internal_error, "Tx is unconfirmed");
11642 
11644  crypto::hash tx_hash;
11645  ok = get_pruned_tx(gettx_res.txs[i], tx, tx_hash);
11646  THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
11647 
11648  THROW_WALLET_EXCEPTION_IF(tx_hash != proof.txid, error::wallet_internal_error, "Failed to get the right transaction from daemon");
11649 
11650  THROW_WALLET_EXCEPTION_IF(proof.index_in_tx >= tx.vout.size(), error::wallet_internal_error, "index_in_tx is out of bound");
11651 
11652  const cryptonote::txout_to_key* const out_key = boost::get<cryptonote::txout_to_key>(std::addressof(tx.vout[proof.index_in_tx].target));
11653  THROW_WALLET_EXCEPTION_IF(!out_key, error::wallet_internal_error, "Output key wasn't found")
11654 
11655  // get tx pub key
11656  const crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx);
11657  THROW_WALLET_EXCEPTION_IF(tx_pub_key == crypto::null_pkey, error::wallet_internal_error, "The tx public key isn't found");
11658  const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(tx);
11659 
11660  // check singature for shared secret
11661  ok = crypto::check_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, boost::none, proof.shared_secret, proof.shared_secret_sig);
11662  if (!ok && additional_tx_pub_keys.size() == tx.vout.size())
11663  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);
11664  if (!ok)
11665  return false;
11666 
11667  // check signature for key image
11668  const std::vector<const crypto::public_key*> pubs = { &out_key->key };
11669  ok = crypto::check_ring_signature(prefix_hash, proof.key_image, &pubs[0], 1, &proof.key_image_sig);
11670  if (!ok)
11671  return false;
11672 
11673  // check if the address really received the fund
11674  crypto::key_derivation derivation;
11675  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");
11676  crypto::public_key subaddr_spendkey;
11677  crypto::derive_subaddress_public_key(out_key->key, derivation, proof.index_in_tx, subaddr_spendkey);
11678  THROW_WALLET_EXCEPTION_IF(subaddr_spendkeys.count(subaddr_spendkey) == 0, error::wallet_internal_error,
11679  "The address doesn't seem to have received the fund");
11680 
11681  // check amount
11682  uint64_t amount = tx.vout[proof.index_in_tx].amount;
11683  if (amount == 0)
11684  {
11685  // decode rct
11686  crypto::secret_key shared_secret;
11687  crypto::derivation_to_scalar(derivation, proof.index_in_tx, shared_secret);
11688  rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[proof.index_in_tx];
11689  rct::ecdhDecode(ecdh_info, rct::sk2rct(shared_secret), tx.rct_signatures.type == rct::RCTTypeBulletproof2);
11690  amount = rct::h2d(ecdh_info.amount);
11691  }
11692  total += amount;
11693  if (kispent_res.spent_status[i])
11694  spent += amount;
11695  }
11696 
11697  // check signatures for all subaddress spend keys
11698  for (const auto &i : subaddr_spendkeys)
11699  {
11700  if (!crypto::check_signature(prefix_hash, i.first, i.second))
11701  return false;
11702  }
11703  return true;
11704 }
11705 
11706 std::string wallet2::get_wallet_file() const
11707 {
11708  return m_wallet_file;
11709 }
11710 
11711 std::string wallet2::get_keys_file() const
11712 {
11713  return m_keys_file;
11714 }
11715 
11716 std::string wallet2::get_daemon_address() const
11717 {
11718  return m_daemon_address;
11719 }
11720 
11721 uint64_t wallet2::get_daemon_blockchain_height(string &err) const
11722 {
11723  uint64_t height;
11724 
11725  boost::optional<std::string> result = m_node_rpc_proxy.get_height(height);
11726  if (result)
11727  {
11728  if (m_trusted_daemon)
11729  err = *result;
11730  else
11731  err = "daemon error";
11732  return 0;
11733  }
11734 
11735  err = "";
11736  return height;
11737 }
11738 
11739 uint64_t wallet2::get_daemon_blockchain_target_height(string &err)
11740 {
11741  err = "";
11742  uint64_t target_height = 0;
11743  const auto result = m_node_rpc_proxy.get_target_height(target_height);
11744  if (result && *result != CORE_RPC_STATUS_OK)
11745  {
11746  if (m_trusted_daemon)
11747  err = *result;
11748  else
11749  err = "daemon error";
11750  return 0;
11751  }
11752  return target_height;
11753 }
11754 
11755 uint64_t wallet2::get_approximate_blockchain_height() const
11756 {
11757  // time of v2 fork
11758  const time_t fork_time = m_nettype == TESTNET ? 1341378000 : m_nettype == STAGENET ? 1521000000 : 1538815057;
11759  // v2 fork block
11760  const uint64_t fork_block = m_nettype == TESTNET ? 190060 : m_nettype == STAGENET ? 32000 : 307500;
11761  // avg seconds per block
11762  const int seconds_per_block = DIFFICULTY_TARGET_V6;
11763  // Calculated blockchain height
11764  uint64_t approx_blockchain_height = fork_block + (time(NULL) - fork_time)/seconds_per_block;
11765  // testnet got some huge rollbacks, so the estimation is way off
11766  static const uint64_t approximate_testnet_rolled_back_blocks = 303967;
11767  if (m_nettype == TESTNET && approx_blockchain_height > approximate_testnet_rolled_back_blocks)
11768  approx_blockchain_height -= approximate_testnet_rolled_back_blocks;
11769  // estiamte blocks from v6
11770  if(m_nettype == MAINNET) {
11771  approx_blockchain_height += 82000;
11772  }
11773  LOG_PRINT_L2("Calculated blockchain height: " << approx_blockchain_height);
11774  return approx_blockchain_height;
11775 }
11776 
11777 void wallet2::set_tx_note(const crypto::hash &txid, const std::string &note)
11778 {
11779  m_tx_notes[txid] = note;
11780 }
11781 
11782 std::string wallet2::get_tx_note(const crypto::hash &txid) const
11783 {
11784  std::unordered_map<crypto::hash, std::string>::const_iterator i = m_tx_notes.find(txid);
11785  if (i == m_tx_notes.end())
11786  return std::string();
11787  return i->second;
11788 }
11789 
11790 void wallet2::set_tx_device_aux(const crypto::hash &txid, const std::string &aux)
11791 {
11792  m_tx_device[txid] = aux;
11793 }
11794 
11795 std::string wallet2::get_tx_device_aux(const crypto::hash &txid) const
11796 {
11797  std::unordered_map<crypto::hash, std::string>::const_iterator i = m_tx_device.find(txid);
11798  if (i == m_tx_device.end())
11799  return std::string();
11800  return i->second;
11801 }
11802 
11803 void wallet2::set_attribute(const std::string &key, const std::string &value)
11804 {
11805  m_attributes[key] = value;
11806 }
11807 
11808 std::string wallet2::get_attribute(const std::string &key) const
11809 {
11810  std::unordered_map<std::string, std::string>::const_iterator i = m_attributes.find(key);
11811  if (i == m_attributes.end())
11812  return std::string();
11813  return i->second;
11814 }
11815 
11816 void wallet2::set_description(const std::string &description)
11817 {
11818  set_attribute(ATTRIBUTE_DESCRIPTION, description);
11819 }
11820 
11821 std::string wallet2::get_description() const
11822 {
11823  return get_attribute(ATTRIBUTE_DESCRIPTION);
11824 }
11825 
11826 const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& wallet2::get_account_tags()
11827 {
11828  // ensure consistency
11829  if (m_account_tags.second.size() != get_num_subaddress_accounts())
11830  m_account_tags.second.resize(get_num_subaddress_accounts(), "");
11831  for (const std::string& tag : m_account_tags.second)
11832  {
11833  if (!tag.empty() && m_account_tags.first.count(tag) == 0)
11834  m_account_tags.first.insert({tag, ""});
11835  }
11836  for (auto i = m_account_tags.first.begin(); i != m_account_tags.first.end(); )
11837  {
11838  if (std::find(m_account_tags.second.begin(), m_account_tags.second.end(), i->first) == m_account_tags.second.end())
11839  i = m_account_tags.first.erase(i);
11840  else
11841  ++i;
11842  }
11843  return m_account_tags;
11844 }
11845 
11846 void wallet2::set_account_tag(const std::set<uint32_t> &account_indices, const std::string& tag)
11847 {
11848  for (uint32_t account_index : account_indices)
11849  {
11850  THROW_WALLET_EXCEPTION_IF(account_index >= get_num_subaddress_accounts(), error::wallet_internal_error, "Account index out of bound");
11851  if (m_account_tags.second[account_index] == tag)
11852  MDEBUG("This tag is already assigned to this account");
11853  else
11854  m_account_tags.second[account_index] = tag;
11855  }
11856  get_account_tags();
11857 }
11858 
11859 void wallet2::set_account_tag_description(const std::string& tag, const std::string& description)
11860 {
11861  THROW_WALLET_EXCEPTION_IF(tag.empty(), error::wallet_internal_error, "Tag must not be empty");
11862  THROW_WALLET_EXCEPTION_IF(m_account_tags.first.count(tag) == 0, error::wallet_internal_error, "Tag is unregistered");
11863  m_account_tags.first[tag] = description;
11864 }
11865 
11866 std::string wallet2::sign(const std::string &data) const
11867 {
11869  crypto::cn_fast_hash(data.data(), data.size(), hash);
11870  const cryptonote::account_keys &keys = m_account.get_keys();
11873  return std::string("SigV1") + tools::base58::encode(std::string((const char *)&signature, sizeof(signature)));
11874 }
11875 
11876 bool wallet2::verify(const std::string &data, const cryptonote::account_public_address &address, const std::string &signature) const
11877 {
11878  const size_t header_len = strlen("SigV1");
11879  if (signature.size() < header_len || signature.substr(0, header_len) != "SigV1") {
11880  LOG_PRINT_L0("Signature header check error");
11881  return false;
11882  }
11884  crypto::cn_fast_hash(data.data(), data.size(), hash);
11885  std::string decoded;
11886  if (!tools::base58::decode(signature.substr(header_len), decoded)) {
11887  LOG_PRINT_L0("Signature decoding error");
11888  return false;
11889  }
11891  if (sizeof(s) != decoded.size()) {
11892  LOG_PRINT_L0("Signature decoding error");
11893  return false;
11894  }
11895  memcpy(&s, decoded.data(), sizeof(s));
11896  return crypto::check_signature(hash, address.m_spend_public_key, s);
11897 }
11898 
11899 std::string wallet2::sign_multisig_participant(const std::string& data) const
11900 {
11901  CHECK_AND_ASSERT_THROW_MES(m_multisig, "Wallet is not multisig");
11902 
11904  crypto::cn_fast_hash(data.data(), data.size(), hash);
11905  const cryptonote::account_keys &keys = m_account.get_keys();
11907  crypto::generate_signature(hash, get_multisig_signer_public_key(), keys.m_spend_secret_key, signature);
11908  return MULTISIG_SIGNATURE_MAGIC + tools::base58::encode(std::string((const char *)&signature, sizeof(signature)));
11909 }
11910 
11911 bool wallet2::verify_with_public_key(const std::string &data, const crypto::public_key &public_key, const std::string &signature) const
11912 {
11913  if (signature.size() < MULTISIG_SIGNATURE_MAGIC.size() || signature.substr(0, MULTISIG_SIGNATURE_MAGIC.size()) != MULTISIG_SIGNATURE_MAGIC) {
11914  MERROR("Signature header check error");
11915  return false;
11916  }
11918  crypto::cn_fast_hash(data.data(), data.size(), hash);
11919  std::string decoded;
11920  if (!tools::base58::decode(signature.substr(MULTISIG_SIGNATURE_MAGIC.size()), decoded)) {
11921  MERROR("Signature decoding error");
11922  return false;
11923  }
11925  if (sizeof(s) != decoded.size()) {
11926  MERROR("Signature decoding error");
11927  return false;
11928  }
11929  memcpy(&s, decoded.data(), sizeof(s));
11931 }
11932 //----------------------------------------------------------------------------------------------------
11933 crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const
11934 {
11935  std::vector<tx_extra_field> tx_extra_fields;
11936  if(!parse_tx_extra(td.m_tx.extra, tx_extra_fields))
11937  {
11938  // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key
11939  }
11940 
11941  // Due to a previous bug, there might be more than one tx pubkey in extra, one being
11942  // the result of a previously discarded signature.
11943  // For speed, since scanning for outputs is a slow process, we check whether extra
11944  // contains more than one pubkey. If not, the first one is returned. If yes, they're
11945  // checked for whether they yield at least one output
11946  tx_extra_pub_key pub_key_field;
11948  "Public key wasn't found in the transaction extra");
11949  const crypto::public_key tx_pub_key = pub_key_field.pub_key;
11950  bool two_found = find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, 1);
11951  if (!two_found) {
11952  // easy case, just one found
11953  return tx_pub_key;
11954  }
11955 
11956  // more than one, loop and search
11957  const cryptonote::account_keys& keys = m_account.get_keys();
11958  size_t pk_index = 0;
11959  hw::device &hwdev = m_account.get_device();
11960 
11961  while (find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, pk_index++)) {
11962  const crypto::public_key tx_pub_key = pub_key_field.pub_key;
11963  crypto::key_derivation derivation;
11964  bool r = hwdev.generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation);
11965  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation");
11966 
11967  for (size_t i = 0; i < td.m_tx.vout.size(); ++i)
11968  {
11969  tx_scan_info_t tx_scan_info;
11970  check_acc_out_precomp(td.m_tx.vout[i], derivation, {}, i, tx_scan_info);
11971  if (!tx_scan_info.error && tx_scan_info.received)
11972  return tx_pub_key;
11973  }
11974  }
11975 
11976  // we found no key yielding an output, but it might be in the additional
11977  // tx pub keys only, which we do not need to check, so return the first one
11978  return tx_pub_key;
11979 }
11980 
11981 bool wallet2::export_key_images(const std::string &filename) const
11982 {
11983  PERF_TIMER(export_key_images);
11984  std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> ski = export_key_images();
11986  const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
11987  const uint32_t offset = ski.first;
11988 
11989  std::string data;
11990  data.reserve(4 + ski.second.size() * (sizeof(crypto::key_image) + sizeof(crypto::signature)) + 2 * sizeof(crypto::public_key));
11991  data.resize(4);
11992  data[0] = offset & 0xff;
11993  data[1] = (offset >> 8) & 0xff;
11994  data[2] = (offset >> 16) & 0xff;
11995  data[3] = (offset >> 24) & 0xff;
11996  data += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key));
11997  data += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key));
11998  for (const auto &i: ski.second)
11999  {
12000  data += std::string((const char *)&i.first, sizeof(crypto::key_image));
12001  data += std::string((const char *)&i.second, sizeof(crypto::signature));
12002  }
12003 
12004  // encrypt data, keep magic plaintext
12005  PERF_TIMER(export_key_images_encrypt);
12006  std::string ciphertext = encrypt_with_view_secret_key(data);
12007  return epee::file_io_utils::save_string_to_file(filename, magic + ciphertext);
12008 }
12009 
12010 //----------------------------------------------------------------------------------------------------
12011 std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> wallet2::export_key_images(bool all) const
12012 {
12013  PERF_TIMER(export_key_images_raw);
12014  std::vector<std::pair<crypto::key_image, crypto::signature>> ski;
12015 
12016  size_t offset = 0;
12017  if (!all)
12018  {
12019  while (offset < m_transfers.size() && !m_transfers[offset].m_key_image_request)
12020  ++offset;
12021  }
12022 
12023  ski.reserve(m_transfers.size() - offset);
12024  for (size_t n = offset; n < m_transfers.size(); ++n)
12025  {
12026  const transfer_details &td = m_transfers[n];
12027 
12028  // get ephemeral public key
12031  "Output is not txout_to_key");
12032  const cryptonote::txout_to_key &o = boost::get<const cryptonote::txout_to_key>(out.target);
12033  const crypto::public_key pkey = o.key;
12034 
12035  // get tx pub key
12036  std::vector<tx_extra_field> tx_extra_fields;
12037  if(!parse_tx_extra(td.m_tx.extra, tx_extra_fields))
12038  {
12039  // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key
12040  }
12041 
12042  crypto::public_key tx_pub_key = get_tx_pub_key_from_received_outs(td);
12043  const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx);
12044 
12045  // generate ephemeral secret key
12046  crypto::key_image ki;
12047  cryptonote::keypair in_ephemeral;
12048  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);
12049  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
12050 
12052  error::wallet_internal_error, "key_image generated not matched with cached key image");
12053  THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != pkey,
12054  error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
12055 
12056  // sign the key image with the output secret key
12058  std::vector<const crypto::public_key*> key_ptrs;
12059  key_ptrs.push_back(&pkey);
12060 
12061  crypto::generate_ring_signature((const crypto::hash&)td.m_key_image, td.m_key_image, key_ptrs, in_ephemeral.sec, 0, &signature);
12062 
12063  ski.push_back(std::make_pair(td.m_key_image, signature));
12064  }
12065  return std::make_pair(offset, ski);
12066 }
12067 
12068 uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent)
12069 {
12070  PERF_TIMER(import_key_images_fsu);
12071  std::string data;
12072  bool r = epee::file_io_utils::load_file_to_string(filename, data);
12073 
12074  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, std::string(tr("failed to read file ")) + filename);
12075 
12076  const size_t magiclen = strlen(KEY_IMAGE_EXPORT_FILE_MAGIC);
12077  if (data.size() < magiclen || memcmp(data.data(), KEY_IMAGE_EXPORT_FILE_MAGIC, magiclen))
12078  {
12079  THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Bad key image export file magic in ") + filename);
12080  }
12081 
12082  try
12083  {
12084  PERF_TIMER(import_key_images_decrypt);
12085  data = decrypt_with_view_secret_key(std::string(data, magiclen));
12086  }
12087  catch (const std::exception &e)
12088  {
12089  THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to decrypt ") + filename + ": " + e.what());
12090  }
12091 
12092  const size_t headerlen = 4 + 2 * sizeof(crypto::public_key);
12093  THROW_WALLET_EXCEPTION_IF(data.size() < headerlen, error::wallet_internal_error, std::string("Bad data size from file ") + filename);
12094  const uint32_t offset = (uint8_t)data[0] | (((uint8_t)data[1]) << 8) | (((uint8_t)data[2]) << 16) | (((uint8_t)data[3]) << 24);
12095  const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[4];
12096  const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[4 + sizeof(crypto::public_key)];
12097  const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
12098  if (public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key)
12099  {
12100  THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string( "Key images from ") + filename + " are for a different account");
12101  }
12102  THROW_WALLET_EXCEPTION_IF(offset > m_transfers.size(), error::wallet_internal_error, "Offset larger than known outputs");
12103 
12104  const size_t record_size = sizeof(crypto::key_image) + sizeof(crypto::signature);
12105  THROW_WALLET_EXCEPTION_IF((data.size() - headerlen) % record_size,
12106  error::wallet_internal_error, std::string("Bad data size from file ") + filename);
12107  size_t nki = (data.size() - headerlen) / record_size;
12108 
12109  std::vector<std::pair<crypto::key_image, crypto::signature>> ski;
12110  ski.reserve(nki);
12111  for (size_t n = 0; n < nki; ++n)
12112  {
12113  crypto::key_image key_image = *reinterpret_cast<const crypto::key_image*>(&data[headerlen + n * record_size]);
12114  crypto::signature signature = *reinterpret_cast<const crypto::signature*>(&data[headerlen + n * record_size + sizeof(crypto::key_image)]);
12115 
12116  ski.push_back(std::make_pair(key_image, signature));
12117  }
12118 
12119  return import_key_images(ski, offset, spent, unspent);
12120 }
12121 
12122 //----------------------------------------------------------------------------------------------------
12123 uint64_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)
12124 {
12125  PERF_TIMER(import_key_images_lots);
12127  COMMAND_RPC_IS_KEY_IMAGE_SPENT::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
12128 
12129  THROW_WALLET_EXCEPTION_IF(offset > m_transfers.size(), error::wallet_internal_error, "Offset larger than known outputs");
12130  THROW_WALLET_EXCEPTION_IF(signed_key_images.size() > m_transfers.size() - offset, error::wallet_internal_error,
12131  "The blockchain is out of date compared to the signed key images");
12132 
12133  if (signed_key_images.empty() && offset == 0)
12134  {
12135  spent = 0;
12136  unspent = 0;
12137  return 0;
12138  }
12139 
12140  req.key_images.reserve(signed_key_images.size());
12141 
12142  PERF_TIMER_START(import_key_images_A);
12143  for (size_t n = 0; n < signed_key_images.size(); ++n)
12144  {
12145  const transfer_details &td = m_transfers[n + offset];
12146  const crypto::key_image &key_image = signed_key_images[n].first;
12147  const crypto::signature &signature = signed_key_images[n].second;
12148 
12149  // get ephemeral public key
12152  "Non txout_to_key output found");
12153  const cryptonote::txout_to_key &o = boost::get<cryptonote::txout_to_key>(out.target);
12154  const crypto::public_key pkey = o.key;
12155 
12156  if (!td.m_key_image_known || !(key_image == td.m_key_image))
12157  {
12158  std::vector<const crypto::public_key*> pkeys;
12159  pkeys.push_back(&pkey);
12161  error::wallet_internal_error, "Key image out of validity domain: input " + boost::lexical_cast<std::string>(n + offset) + "/"
12162  + boost::lexical_cast<std::string>(signed_key_images.size()) + ", key image " + epee::string_tools::pod_to_hex(key_image));
12163 
12165  error::signature_check_failed, boost::lexical_cast<std::string>(n + offset) + "/"
12166  + boost::lexical_cast<std::string>(signed_key_images.size()) + ", key image " + epee::string_tools::pod_to_hex(key_image)
12167  + ", signature " + epee::string_tools::pod_to_hex(signature) + ", pubkey " + epee::string_tools::pod_to_hex(*pkeys[0]));
12168  }
12169  req.key_images.push_back(epee::string_tools::pod_to_hex(key_image));
12170  }
12171  PERF_TIMER_STOP(import_key_images_A);
12172 
12173  PERF_TIMER_START(import_key_images_B);
12174  for (size_t n = 0; n < signed_key_images.size(); ++n)
12175  {
12176  m_transfers[n + offset].m_key_image = signed_key_images[n].first;
12177  m_key_images[m_transfers[n + offset].m_key_image] = n + offset;
12178  m_transfers[n + offset].m_key_image_known = true;
12179  m_transfers[n + offset].m_key_image_request = false;
12180  m_transfers[n + offset].m_key_image_partial = false;
12181  }
12182  PERF_TIMER_STOP(import_key_images_B);
12183 
12184  if(check_spent)
12185  {
12186  PERF_TIMER(import_key_images_RPC);
12187  m_daemon_rpc_mutex.lock();
12188  bool r = invoke_http_json("/is_key_image_spent", req, daemon_resp, rpc_timeout);
12189  m_daemon_rpc_mutex.unlock();
12190  THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "is_key_image_spent");
12191  THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "is_key_image_spent");
12192  THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::is_key_image_spent_error, daemon_resp.status);
12193  THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != signed_key_images.size(), error::wallet_internal_error,
12194  "daemon returned wrong response for is_key_image_spent, wrong amounts count = " +
12195  std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(signed_key_images.size()));
12196  for (size_t n = 0; n < daemon_resp.spent_status.size(); ++n)
12197  {
12198  transfer_details &td = m_transfers[n + offset];
12199  td.m_spent = daemon_resp.spent_status[n] != COMMAND_RPC_IS_KEY_IMAGE_SPENT::UNSPENT;
12200  }
12201  }
12202  spent = 0;
12203  unspent = 0;
12204  std::unordered_set<crypto::hash> spent_txids; // For each spent key image, search for a tx in m_transfers that uses it as input.
12205  std::vector<size_t> swept_transfers; // If such a spending tx wasn't found in m_transfers, this means the spending tx
12206  // was created by sweep_all, so we can't know the spent height and other detailed info.
12207  std::unordered_map<crypto::key_image, crypto::hash> spent_key_images;
12208 
12209  PERF_TIMER_START(import_key_images_C);
12210  for (const transfer_details &td: m_transfers)
12211  {
12212  for (const cryptonote::txin_v& in : td.m_tx.vin)
12213  {
12214  if (in.type() == typeid(cryptonote::txin_to_key))
12215  spent_key_images.insert(std::make_pair(boost::get<cryptonote::txin_to_key>(in).k_image, td.m_txid));
12216  }
12217  }
12218  PERF_TIMER_STOP(import_key_images_C);
12219 
12220  // accumulate outputs before the updated data
12221  for(size_t i = 0; i < offset; ++i)
12222  {
12223  const transfer_details &td = m_transfers[i];
12224  if (td.m_frozen)
12225  continue;
12226  uint64_t amount = td.amount();
12227  if (td.m_spent)
12228  spent += amount;
12229  else
12230  unspent += amount;
12231  }
12232 
12233  PERF_TIMER_START(import_key_images_D);
12234  for(size_t i = 0; i < signed_key_images.size(); ++i)
12235  {
12236  const transfer_details &td = m_transfers[i + offset];
12237  if (td.m_frozen)
12238  continue;
12239  uint64_t amount = td.amount();
12240  if (td.m_spent)
12241  spent += amount;
12242  else
12243  unspent += amount;
12244  LOG_PRINT_L2("Transfer " << i << ": " << print_etn(amount) << " (" << td.m_global_output_index << "): "
12245  << (td.m_spent ? "spent" : "unspent") << " (key image " << req.key_images[i] << ")");
12246 
12247  if (i < daemon_resp.spent_status.size() && daemon_resp.spent_status[i] == COMMAND_RPC_IS_KEY_IMAGE_SPENT::SPENT_IN_BLOCKCHAIN)
12248  {
12249  const std::unordered_map<crypto::key_image, crypto::hash>::const_iterator skii = spent_key_images.find(td.m_key_image);
12250  if (skii == spent_key_images.end())
12251  swept_transfers.push_back(i);
12252  else
12253  spent_txids.insert(skii->second);
12254  }
12255  }
12256  PERF_TIMER_STOP(import_key_images_D);
12257 
12258  MDEBUG("Total: " << print_etn(spent) << " spent, " << print_etn(unspent) << " unspent");
12259 
12260  if (check_spent)
12261  {
12262  // query outgoing txes
12265  gettxs_req.decode_as_json = false;
12266  gettxs_req.prune = true;
12267  gettxs_req.txs_hashes.reserve(spent_txids.size());
12268  for (const crypto::hash& spent_txid : spent_txids)
12269  gettxs_req.txs_hashes.push_back(epee::string_tools::pod_to_hex(spent_txid));
12270 
12271 
12272  PERF_TIMER_START(import_key_images_E);
12273  m_daemon_rpc_mutex.lock();
12274  bool r = invoke_http_json("/gettransactions", gettxs_req, gettxs_res, rpc_timeout);
12275  m_daemon_rpc_mutex.unlock();
12277  THROW_WALLET_EXCEPTION_IF(gettxs_res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions");
12278  THROW_WALLET_EXCEPTION_IF(gettxs_res.txs.size() != spent_txids.size(), error::wallet_internal_error,
12279  "daemon returned wrong response for gettransactions, wrong count = " + std::to_string(gettxs_res.txs.size()) + ", expected " + std::to_string(spent_txids.size()));
12280  PERF_TIMER_STOP(import_key_images_E);
12281 
12282  // process each outgoing tx
12283  PERF_TIMER_START(import_key_images_F);
12284  auto spent_txid = spent_txids.begin();
12285  hw::device &hwdev = m_account.get_device();
12286  auto it = spent_txids.begin();
12287  for (const COMMAND_RPC_GET_TRANSACTIONS::entry& e : gettxs_res.txs)
12288  {
12289  THROW_WALLET_EXCEPTION_IF(e.in_pool, error::wallet_internal_error, "spent tx isn't supposed to be in txpool");
12290 
12291  cryptonote::transaction spent_tx;
12292  crypto::hash spnet_txid_parsed;
12293  THROW_WALLET_EXCEPTION_IF(!get_pruned_tx(e, spent_tx, spnet_txid_parsed), error::wallet_internal_error, "Failed to get tx from daemon");
12294  THROW_WALLET_EXCEPTION_IF(!(spnet_txid_parsed == *it), error::wallet_internal_error, "parsed txid mismatch");
12295  ++it;
12296 
12297  // get received (change) amount
12298  uint64_t tx_etn_got_in_outs = 0;
12299  const cryptonote::account_keys& keys = m_account.get_keys();
12300  const crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(spent_tx);
12301  crypto::key_derivation derivation;
12302  bool r = hwdev.generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation);
12303  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation");
12304  const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(spent_tx);
12305  std::vector<crypto::key_derivation> additional_derivations;
12306  for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
12307  {
12308  additional_derivations.push_back({});
12309  r = hwdev.generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back());
12310  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation");
12311  }
12312  size_t output_index = 0;
12313  bool miner_tx = cryptonote::is_coinbase(spent_tx);
12314  for (const cryptonote::tx_out& out : spent_tx.vout)
12315  {
12316  tx_scan_info_t tx_scan_info;
12317  check_acc_out_precomp(out, derivation, additional_derivations, output_index, tx_scan_info);
12318  THROW_WALLET_EXCEPTION_IF(tx_scan_info.error, error::wallet_internal_error, "check_acc_out_precomp failed");
12319  if (tx_scan_info.received)
12320  {
12321  if (tx_scan_info.etn_transfered == 0 && !miner_tx)
12322  {
12323  rct::key mask;
12324  tx_scan_info.etn_transfered = tools::decodeRct(spent_tx.rct_signatures, tx_scan_info.received->derivation, output_index, mask, hwdev);
12325  }
12326  THROW_WALLET_EXCEPTION_IF(tx_etn_got_in_outs >= std::numeric_limits<uint64_t>::max() - tx_scan_info.etn_transfered,
12327  error::wallet_internal_error, "Overflow in received amounts");
12328  tx_etn_got_in_outs += tx_scan_info.etn_transfered;
12329  }
12330  ++output_index;
12331  }
12332 
12333  // get spent amount
12334  uint64_t tx_etn_spent_in_ins = 0;
12335  uint32_t subaddr_account = (uint32_t)-1;
12336  std::set<uint32_t> subaddr_indices;
12337  for (const cryptonote::txin_v& in : spent_tx.vin)
12338  {
12339  if (in.type() != typeid(cryptonote::txin_to_key))
12340  continue;
12341  auto it = m_key_images.find(boost::get<cryptonote::txin_to_key>(in).k_image);
12342  if (it != m_key_images.end())
12343  {
12344  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()));
12345  const transfer_details& td = m_transfers[it->second];
12346  uint64_t amount = boost::get<cryptonote::txin_to_key>(in).amount;
12347  if (amount > 0)
12348  {
12350  std::string("Inconsistent amount in tx input: got ") + print_etn(amount) +
12351  std::string(", expected ") + print_etn(td.amount()));
12352  }
12353  amount = td.amount();
12354  tx_etn_spent_in_ins += amount;
12355 
12356  LOG_PRINT_L0("Spent ETN: " << print_etn(amount) << ", with tx: " << *spent_txid);
12357  set_spent(it->second, e.block_height);
12358  if (m_callback)
12359  m_callback->on_etn_spent(e.block_height, *spent_txid, spent_tx, amount, spent_tx, td.m_subaddr_index);
12360  if (subaddr_account != (uint32_t)-1 && subaddr_account != td.m_subaddr_index.major)
12361  LOG_PRINT_L0("WARNING: This tx spends outputs received by different subaddress accounts, which isn't supposed to happen");
12362  subaddr_account = td.m_subaddr_index.major;
12363  subaddr_indices.insert(td.m_subaddr_index.minor);
12364  }
12365  }
12366 
12367  // create outgoing payment
12368  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);
12369 
12370  // erase corresponding incoming payment
12371  for (auto j = m_payments.begin(); j != m_payments.end(); ++j)
12372  {
12373  if (j->second.m_tx_hash == *spent_txid)
12374  {
12375  m_payments.erase(j);
12376  break;
12377  }
12378  }
12379 
12380  ++spent_txid;
12381  }
12382  PERF_TIMER_STOP(import_key_images_F);
12383 
12384  PERF_TIMER_START(import_key_images_G);
12385  for (size_t n : swept_transfers)
12386  {
12387  const transfer_details& td = m_transfers[n];
12389  pd.m_change = (uint64_t)-1; // change is unknown
12390  pd.m_amount_in = pd.m_amount_out = td.amount(); // fee is unknown
12391  pd.m_block_height = 0; // spent block height is unknown
12392  pd.m_is_migration = td.m_tx.version == 2;
12393  const crypto::hash &spent_txid = crypto::null_hash; // spent txid is unknown
12394  m_confirmed_txs.insert(std::make_pair(spent_txid, pd));
12395  }
12396  PERF_TIMER_STOP(import_key_images_G);
12397  }
12398 
12399  // this can be 0 if we do not know the height
12400  return m_transfers[signed_key_images.size() + offset - 1].m_block_height;
12401 }
12402 
12403 bool wallet2::import_key_images(std::vector<crypto::key_image> key_images, size_t offset, boost::optional<std::unordered_set<size_t>> selected_transfers)
12404 {
12405  if (key_images.size() + offset > m_transfers.size())
12406  {
12407  LOG_PRINT_L1("More key images returned that we know outputs for");
12408  return false;
12409  }
12410  for (size_t ki_idx = 0; ki_idx < key_images.size(); ++ki_idx)
12411  {
12412  const size_t transfer_idx = ki_idx + offset;
12413  if (selected_transfers && selected_transfers.get().find(transfer_idx) == selected_transfers.get().end())
12414  continue;
12415 
12416  transfer_details &td = m_transfers[transfer_idx];
12417  if (td.m_key_image_known && !td.m_key_image_partial && td.m_key_image != key_images[ki_idx])
12418  LOG_PRINT_L0("WARNING: imported key image differs from previously known key image at index " << ki_idx << ": trusting imported one");
12419  td.m_key_image = key_images[ki_idx];
12420  m_key_images[td.m_key_image] = transfer_idx;
12421  td.m_key_image_known = true;
12422  td.m_key_image_request = false;
12423  td.m_key_image_partial = false;
12424  m_pub_keys[td.get_public_key()] = transfer_idx;
12425  }
12426 
12427  return true;
12428 }
12429 
12430 bool wallet2::import_key_images(signed_tx_set & signed_tx, size_t offset, bool only_selected_transfers)
12431 {
12432  std::unordered_set<size_t> selected_transfers;
12433  if (only_selected_transfers)
12434  {
12435  for (const pending_tx & ptx : signed_tx.ptx)
12436  {
12437  for (const size_t s: ptx.selected_transfers)
12438  selected_transfers.insert(s);
12439  }
12440  }
12441 
12442  return import_key_images(signed_tx.key_images, offset, only_selected_transfers ? boost::make_optional(selected_transfers) : boost::none);
12443 }
12444 
12445 wallet2::payment_container wallet2::export_payments() const
12446 {
12447  payment_container payments;
12448  for (auto const &p : m_payments)
12449  {
12450  payments.emplace(p);
12451  }
12452  return payments;
12453 }
12454 void wallet2::import_payments(const payment_container &payments)
12455 {
12456  m_payments.clear();
12457  for (auto const &p : payments)
12458  {
12459  m_payments.emplace(p);
12460  }
12461 }
12462 void wallet2::import_payments_out(const std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>> &confirmed_payments)
12463 {
12464  m_confirmed_txs.clear();
12465  for (auto const &p : confirmed_payments)
12466  {
12467  m_confirmed_txs.emplace(p);
12468  }
12469 }
12470 
12471 std::tuple<size_t,crypto::hash,std::vector<crypto::hash>> wallet2::export_blockchain() const
12472 {
12473  std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> bc;
12474  std::get<0>(bc) = m_blockchain.offset();
12475  std::get<1>(bc) = m_blockchain.empty() ? crypto::null_hash: m_blockchain.genesis();
12476  for (size_t n = m_blockchain.offset(); n < m_blockchain.size(); ++n)
12477  {
12478  std::get<2>(bc).push_back(m_blockchain[n]);
12479  }
12480  return bc;
12481 }
12482 
12483 void wallet2::import_blockchain(const std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> &bc)
12484 {
12485  m_blockchain.clear();
12486  if (std::get<0>(bc))
12487  {
12488  for (size_t n = std::get<0>(bc); n > 0; --n)
12489  m_blockchain.push_back(std::get<1>(bc));
12490  m_blockchain.trim(std::get<0>(bc));
12491  }
12492  for (auto const &b : std::get<2>(bc))
12493  {
12494  m_blockchain.push_back(b);
12495  }
12496  cryptonote::block genesis;
12497  generate_genesis(genesis);
12498  crypto::hash genesis_hash = get_block_hash(genesis);
12499  check_genesis(genesis_hash);
12500  m_last_block_reward = cryptonote::get_outs_etn_amount(genesis.miner_tx);
12501 }
12502 //----------------------------------------------------------------------------------------------------
12503 std::pair<size_t, std::vector<tools::wallet2::transfer_details>> wallet2::export_outputs(bool all) const
12504 {
12505  PERF_TIMER(export_outputs);
12506  std::vector<tools::wallet2::transfer_details> outs;
12507 
12508  size_t offset = 0;
12509  if (!all)
12510  while (offset < m_transfers.size() && (m_transfers[offset].m_key_image_known && !m_transfers[offset].m_key_image_request))
12511  ++offset;
12512 
12513  outs.reserve(m_transfers.size() - offset);
12514  for (size_t n = offset; n < m_transfers.size(); ++n)
12515  {
12516  const transfer_details &td = m_transfers[n];
12517 
12518  outs.push_back(td);
12519  }
12520 
12521  return std::make_pair(offset, outs);
12522 }
12523 //----------------------------------------------------------------------------------------------------
12524 std::string wallet2::export_outputs_to_str(bool all) const
12525 {
12526  PERF_TIMER(export_outputs_to_str);
12527 
12528  std::stringstream oss;
12530  const auto& outputs = export_outputs(all);
12531  ar << outputs;
12532 
12534  const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
12535  std::string header;
12536  header += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key));
12537  header += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key));
12538  PERF_TIMER(export_outputs_encryption);
12539  std::string ciphertext = encrypt_with_view_secret_key(header + oss.str());
12540  return magic + ciphertext;
12541 }
12542 //----------------------------------------------------------------------------------------------------
12543 size_t wallet2::import_outputs(const std::pair<size_t, std::vector<tools::wallet2::transfer_details>> &outputs)
12544 {
12545  PERF_TIMER(import_outputs);
12546 
12547  THROW_WALLET_EXCEPTION_IF(outputs.first > m_transfers.size(), error::wallet_internal_error,
12548  "Imported outputs omit more outputs that we know of");
12549 
12550  const size_t offset = outputs.first;
12551  const size_t original_size = m_transfers.size();
12552  m_transfers.resize(offset + outputs.second.size());
12553  for (size_t i = 0; i < offset; ++i)
12554  m_transfers[i].m_key_image_request = false;
12555  for (size_t i = 0; i < outputs.second.size(); ++i)
12556  {
12557  transfer_details td = outputs.second[i];
12558 
12559  // skip those we've already imported, or which have different data
12560  if (i + offset < original_size)
12561  {
12562  // compare the data used to create the key image below
12563  const transfer_details &org_td = m_transfers[i + offset];
12564  if (!org_td.m_key_image_known)
12565  goto process;
12566 #define CMPF(f) if (!(td.f == org_td.f)) goto process
12567  CMPF(m_txid);
12568  CMPF(m_key_image);
12569  CMPF(m_internal_output_index);
12570 #undef CMPF
12572  goto process;
12573 
12574  // copy anyway, since the comparison does not include ancillary fields which may have changed
12575  m_transfers[i + offset] = std::move(td);
12576  continue;
12577  }
12578 
12579 process:
12580 
12581  // the hot wallet wouldn't have known about key images (except if we already exported them)
12582  cryptonote::keypair in_ephemeral;
12583 
12584  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));
12585  crypto::public_key tx_pub_key = get_tx_pub_key_from_received_outs(td);
12586  const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx);
12587 
12589  error::wallet_internal_error, "Unsupported output type");
12590  const crypto::public_key& out_key = boost::get<cryptonote::txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key;
12591  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);
12592  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
12593  expand_subaddresses(td.m_subaddr_index);
12594  td.m_key_image_known = true;
12595  td.m_key_image_request = true;
12596  td.m_key_image_partial = false;
12597  THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != out_key,
12598  error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key at index " + boost::lexical_cast<std::string>(i + offset));
12599 
12600  m_key_images[td.m_key_image] = i + offset;
12601  m_pub_keys[td.get_public_key()] = i + offset;
12602  m_transfers[i + offset] = std::move(td);
12603  }
12604 
12605  return m_transfers.size();
12606 }
12607 //----------------------------------------------------------------------------------------------------
12608 size_t wallet2::import_outputs_from_str(const std::string &outputs_st)
12609 {
12610  PERF_TIMER(import_outputs_from_str);
12611  std::string data = outputs_st;
12612  const size_t magiclen = strlen(OUTPUT_EXPORT_FILE_MAGIC);
12613  if (data.size() < magiclen || memcmp(data.data(), OUTPUT_EXPORT_FILE_MAGIC, magiclen))
12614  {
12616  }
12617 
12618  try
12619  {
12620  PERF_TIMER(import_outputs_decrypt);
12621  data = decrypt_with_view_secret_key(std::string(data, magiclen));
12622  }
12623  catch (const std::exception &e)
12624  {
12625  THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to decrypt outputs: ") + e.what());
12626  }
12627 
12628  const size_t headerlen = 2 * sizeof(crypto::public_key);
12629  if (data.size() < headerlen)
12630  {
12631  THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Bad data size for outputs"));
12632  }
12633  const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[0];
12634  const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[sizeof(crypto::public_key)];
12635  const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
12636  if (public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key)
12637  {
12638  THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Outputs from are for a different account"));
12639  }
12640 
12641  size_t imported_outputs = 0;
12642  try
12643  {
12644  std::string body(data, headerlen);
12645  std::stringstream iss;
12646  iss << body;
12647  std::pair<size_t, std::vector<tools::wallet2::transfer_details>> outputs;
12648  try
12649  {
12651  ar >> outputs;
12652  }
12653  catch (...)
12654  {
12655  iss.str("");
12656  iss << body;
12657  boost::archive::binary_iarchive ar(iss);
12658  ar >> outputs;
12659  }
12660 
12661  imported_outputs = import_outputs(outputs);
12662  }
12663  catch (const std::exception &e)
12664  {
12665  THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to import outputs") + e.what());
12666  }
12667 
12668  return imported_outputs;
12669 }
12670 //----------------------------------------------------------------------------------------------------
12671 crypto::public_key wallet2::get_multisig_signer_public_key(const crypto::secret_key &spend_skey) const
12672 {
12673  crypto::public_key pkey;
12675  return pkey;
12676 }
12677 //----------------------------------------------------------------------------------------------------
12678 crypto::public_key wallet2::get_multisig_signer_public_key() const
12679 {
12680  CHECK_AND_ASSERT_THROW_MES(m_multisig, "Wallet is not multisig");
12681  crypto::public_key signer;
12682  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");
12683  return signer;
12684 }
12685 //----------------------------------------------------------------------------------------------------
12686 crypto::public_key wallet2::get_multisig_signing_public_key(const crypto::secret_key &msk) const
12687 {
12688  CHECK_AND_ASSERT_THROW_MES(m_multisig, "Wallet is not multisig");
12689  crypto::public_key pkey;
12690  CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(msk, pkey), "Failed to derive public key");
12691  return pkey;
12692 }
12693 //----------------------------------------------------------------------------------------------------
12694 crypto::public_key wallet2::get_multisig_signing_public_key(size_t idx) const
12695 {
12696  CHECK_AND_ASSERT_THROW_MES(m_multisig, "Wallet is not multisig");
12697  CHECK_AND_ASSERT_THROW_MES(idx < get_account().get_multisig_keys().size(), "Multisig signing key index out of range");
12698  return get_multisig_signing_public_key(get_account().get_multisig_keys()[idx]);
12699 }
12700 //----------------------------------------------------------------------------------------------------
12701 rct::key wallet2::get_multisig_k(size_t idx, const std::unordered_set<rct::key> &used_L) const
12702 {
12703  CHECK_AND_ASSERT_THROW_MES(m_multisig, "Wallet is not multisig");
12704  CHECK_AND_ASSERT_THROW_MES(idx < m_transfers.size(), "idx out of range");
12705  for (const auto &k: m_transfers[idx].m_multisig_k)
12706  {
12707  rct::key L;
12708  rct::scalarmultBase(L, k);
12709  if (used_L.find(L) != used_L.end())
12710  return k;
12711  }
12713  return rct::zero();
12714 }
12715 //----------------------------------------------------------------------------------------------------
12716 rct::multisig_kLRki wallet2::get_multisig_kLRki(size_t n, const rct::key &k) const
12717 {
12718  CHECK_AND_ASSERT_THROW_MES(n < m_transfers.size(), "Bad m_transfers index");
12719  rct::multisig_kLRki kLRki;
12720  kLRki.k = k;
12721  cryptonote::generate_multisig_LR(m_transfers[n].get_public_key(), rct::rct2sk(kLRki.k), (crypto::public_key&)kLRki.L, (crypto::public_key&)kLRki.R);
12722  kLRki.ki = rct::ki2rct(m_transfers[n].m_key_image);
12723  return kLRki;
12724 }
12725 //----------------------------------------------------------------------------------------------------
12726 rct::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
12727 {
12728  CHECK_AND_ASSERT_THROW_MES(n < m_transfers.size(), "Bad transfer index");
12729 
12730  const transfer_details &td = m_transfers[n];
12731  rct::multisig_kLRki kLRki = get_multisig_kLRki(n, rct::skGen());
12732 
12733  // pick a L/R pair from every other participant but one
12734  size_t n_signers_used = 1;
12735  for (const auto &p: m_transfers[n].m_multisig_info)
12736  {
12737  if (ignore_set.find(p.m_signer) != ignore_set.end())
12738  continue;
12739 
12740  for (const auto &lr: p.m_LR)
12741  {
12742  if (used_L.find(lr.m_L) != used_L.end())
12743  continue;
12744  used_L.insert(lr.m_L);
12745  new_used_L.insert(lr.m_L);
12746  rct::addKeys(kLRki.L, kLRki.L, lr.m_L);
12747  rct::addKeys(kLRki.R, kLRki.R, lr.m_R);
12748  ++n_signers_used;
12749  break;
12750  }
12751  }
12752  CHECK_AND_ASSERT_THROW_MES(n_signers_used >= m_multisig_threshold, "LR not found for enough participants");
12753 
12754  return kLRki;
12755 }
12756 //----------------------------------------------------------------------------------------------------
12757 crypto::key_image wallet2::get_multisig_composite_key_image(size_t n) const
12758 {
12759  CHECK_AND_ASSERT_THROW_MES(n < m_transfers.size(), "Bad output index");
12760 
12761  const transfer_details &td = m_transfers[n];
12762  const crypto::public_key tx_key = get_tx_pub_key_from_received_outs(td);
12763  const std::vector<crypto::public_key> additional_tx_keys = cryptonote::get_additional_tx_pub_keys_from_extra(td.m_tx);
12764  crypto::key_image ki;
12765  std::vector<crypto::key_image> pkis;
12766  for (const auto &info: td.m_multisig_info)
12767  for (const auto &pki: info.m_partial_key_images)
12768  pkis.push_back(pki);
12769  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);
12770  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
12771  return ki;
12772 }
12773 //----------------------------------------------------------------------------------------------------
12774 cryptonote::blobdata wallet2::export_multisig()
12775 {
12776  std::vector<tools::wallet2::multisig_info> info;
12777 
12778  const crypto::public_key signer = get_multisig_signer_public_key();
12779 
12780  info.resize(m_transfers.size());
12781  for (size_t n = 0; n < m_transfers.size(); ++n)
12782  {
12783  transfer_details &td = m_transfers[n];
12784  crypto::key_image ki;
12785  td.m_multisig_k.clear();
12786  info[n].m_LR.clear();
12787  info[n].m_partial_key_images.clear();
12788 
12789  for (size_t m = 0; m < get_account().get_multisig_keys().size(); ++m)
12790  {
12791  // we want to export the partial key image, not the full one, so we can't use td.m_key_image
12792  bool r = generate_multisig_key_image(get_account().get_keys(), m, td.get_public_key(), ki);
12793  CHECK_AND_ASSERT_THROW_MES(r, "Failed to generate key image");
12794  info[n].m_partial_key_images.push_back(ki);
12795  }
12796 
12797  // Wallet tries to create as many transactions as many signers combinations. We calculate the maximum number here as follows:
12798  // 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.
12799  // That means counting combinations for excluding 2-of-3 wallets (k = total signers count - threshold, n = total signers count - 1).
12800  size_t nlr = tools::combinations_count(m_multisig_signers.size() - m_multisig_threshold, m_multisig_signers.size() - 1);
12801  for (size_t m = 0; m < nlr; ++m)
12802  {
12803  td.m_multisig_k.push_back(rct::skGen());
12804  const rct::multisig_kLRki kLRki = get_multisig_kLRki(n, td.m_multisig_k.back());
12805  info[n].m_LR.push_back({kLRki.L, kLRki.R});
12806  }
12807 
12808  info[n].m_signer = signer;
12809  }
12810 
12811  std::stringstream oss;
12813  ar << info;
12814 
12815  const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
12816  std::string header;
12817  header += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key));
12818  header += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key));
12819  header += std::string((const char *)&signer, sizeof(crypto::public_key));
12820  std::string ciphertext = encrypt_with_view_secret_key(header + oss.str());
12821 
12822  return MULTISIG_EXPORT_FILE_MAGIC + ciphertext;
12823 }
12824 //----------------------------------------------------------------------------------------------------
12825 void 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)
12826 {
12827  CHECK_AND_ASSERT_THROW_MES(n < m_transfers.size(), "Bad index in update_multisig_info");
12828  CHECK_AND_ASSERT_THROW_MES(multisig_k.size() >= m_transfers.size(), "Mismatched sizes of multisig_k and info");
12829 
12830  MDEBUG("update_multisig_rescan_info: updating index " << n);
12831  transfer_details &td = m_transfers[n];
12832  td.m_multisig_info.clear();
12833  for (const auto &pi: info)
12834  {
12835  CHECK_AND_ASSERT_THROW_MES(n < pi.size(), "Bad pi size");
12836  td.m_multisig_info.push_back(pi[n]);
12837  }
12838  m_key_images.erase(td.m_key_image);
12839  td.m_key_image = get_multisig_composite_key_image(n);
12840  td.m_key_image_known = true;
12841  td.m_key_image_request = false;
12842  td.m_key_image_partial = false;
12843  td.m_multisig_k = multisig_k[n];
12844  m_key_images[td.m_key_image] = n;
12845 }
12846 //----------------------------------------------------------------------------------------------------
12847 size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs)
12848 {
12849  CHECK_AND_ASSERT_THROW_MES(m_multisig, "Wallet is not multisig");
12850 
12851  std::vector<std::vector<tools::wallet2::multisig_info>> info;
12852  std::unordered_set<crypto::public_key> seen;
12853  for (cryptonote::blobdata &data: blobs)
12854  {
12855  const size_t magiclen = strlen(MULTISIG_EXPORT_FILE_MAGIC);
12856  THROW_WALLET_EXCEPTION_IF(data.size() < magiclen || memcmp(data.data(), MULTISIG_EXPORT_FILE_MAGIC, magiclen),
12857  error::wallet_internal_error, "Bad multisig info file magic in ");
12858 
12859  data = decrypt_with_view_secret_key(std::string(data, magiclen));
12860 
12861  const size_t headerlen = 3 * sizeof(crypto::public_key);
12862  THROW_WALLET_EXCEPTION_IF(data.size() < headerlen, error::wallet_internal_error, "Bad data size");
12863 
12864  const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[0];
12865  const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[sizeof(crypto::public_key)];
12866  const crypto::public_key &signer = *(const crypto::public_key*)&data[2*sizeof(crypto::public_key)];
12867  const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
12868  THROW_WALLET_EXCEPTION_IF(public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key,
12869  error::wallet_internal_error, "Multisig info is for a different account");
12870  if (get_multisig_signer_public_key() == signer)
12871  {
12872  MINFO("Multisig info from this wallet ignored");
12873  continue;
12874  }
12875  if (seen.find(signer) != seen.end())
12876  {
12877  MINFO("Duplicate multisig info ignored");
12878  continue;
12879  }
12880  seen.insert(signer);
12881 
12882  std::string body(data, headerlen);
12883  std::istringstream iss(body);
12884  std::vector<tools::wallet2::multisig_info> i;
12886  ar >> i;
12887  MINFO(boost::format("%u outputs found") % boost::lexical_cast<std::string>(i.size()));
12888  info.push_back(std::move(i));
12889  }
12890 
12891  CHECK_AND_ASSERT_THROW_MES(info.size() + 1 <= m_multisig_signers.size() && info.size() + 1 >= m_multisig_threshold, "Wrong number of multisig sources");
12892 
12893  std::vector<std::vector<rct::key>> k;
12894  k.reserve(m_transfers.size());
12895  for (const auto &td: m_transfers)
12896  k.push_back(td.m_multisig_k);
12897 
12898  // how many outputs we're going to update
12899  size_t n_outputs = m_transfers.size();
12900  for (const auto &pi: info)
12901  if (pi.size() < n_outputs)
12902  n_outputs = pi.size();
12903 
12904  if (n_outputs == 0)
12905  return 0;
12906 
12907  // check signers are consistent
12908  for (const auto &pi: info)
12909  {
12910  CHECK_AND_ASSERT_THROW_MES(std::find(m_multisig_signers.begin(), m_multisig_signers.end(), pi[0].m_signer) != m_multisig_signers.end(),
12911  "Signer is not a member of this multisig wallet");
12912  for (size_t n = 1; n < n_outputs; ++n)
12913  CHECK_AND_ASSERT_THROW_MES(pi[n].m_signer == pi[0].m_signer, "Mismatched signers in imported multisig info");
12914  }
12915 
12916  // trim data we don't have info for from all participants
12917  for (auto &pi: info)
12918  pi.resize(n_outputs);
12919 
12920  // sort by signer
12921  if (!info.empty() && !info.front().empty())
12922  {
12923  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)); });
12924  }
12925 
12926  // first pass to determine where to detach the blockchain
12927  for (size_t n = 0; n < n_outputs; ++n)
12928  {
12929  const transfer_details &td = m_transfers[n];
12930  if (!td.m_key_image_partial)
12931  continue;
12932  MINFO("Multisig info importing from block height " << td.m_block_height);
12933  detach_blockchain(td.m_block_height);
12934  break;
12935  }
12936 
12937  for (size_t n = 0; n < n_outputs && n < m_transfers.size(); ++n)
12938  {
12939  update_multisig_rescan_info(k, info, n);
12940  }
12941 
12942  m_multisig_rescan_k = &k;
12943  m_multisig_rescan_info = &info;
12944  try
12945  {
12946 
12947  refresh(false);
12948  }
12949  catch (...)
12950  {
12951  m_multisig_rescan_info = NULL;
12952  m_multisig_rescan_k = NULL;
12953  throw;
12954  }
12955  m_multisig_rescan_info = NULL;
12956  m_multisig_rescan_k = NULL;
12957 
12958  return n_outputs;
12959 }
12960 //----------------------------------------------------------------------------------------------------
12961 std::string wallet2::encrypt(const char *plaintext, size_t len, const crypto::secret_key &skey, bool authenticated) const
12962 {
12963  crypto::chacha_key key;
12964  crypto::generate_chacha_key(&skey, sizeof(skey), key, m_kdf_rounds);
12965  std::string ciphertext;
12966  crypto::chacha_iv iv = crypto::rand<crypto::chacha_iv>();
12967  ciphertext.resize(len + sizeof(iv) + (authenticated ? sizeof(crypto::signature) : 0));
12968  crypto::chacha20(plaintext, len, key, iv, &ciphertext[sizeof(iv)]);
12969  memcpy(&ciphertext[0], &iv, sizeof(iv));
12970  if (authenticated)
12971  {
12973  crypto::cn_fast_hash(ciphertext.data(), ciphertext.size() - sizeof(signature), hash);
12974  crypto::public_key pkey;
12976  crypto::signature &signature = *(crypto::signature*)&ciphertext[ciphertext.size() - sizeof(crypto::signature)];
12978  }
12979  return ciphertext;
12980 }
12981 //----------------------------------------------------------------------------------------------------
12982 std::string wallet2::encrypt(const epee::span<char> &plaintext, const crypto::secret_key &skey, bool authenticated) const
12983 {
12984  return encrypt(plaintext.data(), plaintext.size(), skey, authenticated);
12985 }
12986 //----------------------------------------------------------------------------------------------------
12987 std::string wallet2::encrypt(const std::string &plaintext, const crypto::secret_key &skey, bool authenticated) const
12988 {
12989  return encrypt(plaintext.data(), plaintext.size(), skey, authenticated);
12990 }
12991 //----------------------------------------------------------------------------------------------------
12992 std::string wallet2::encrypt(const epee::wipeable_string &plaintext, const crypto::secret_key &skey, bool authenticated) const
12993 {
12994  return encrypt(plaintext.data(), plaintext.size(), skey, authenticated);
12995 }
12996 //----------------------------------------------------------------------------------------------------
12997 std::string wallet2::encrypt_with_view_secret_key(const std::string &plaintext, bool authenticated) const
12998 {
12999  return encrypt(plaintext, get_account().get_keys().m_view_secret_key, authenticated);
13000 }
13001 //----------------------------------------------------------------------------------------------------
13002 template<typename T>
13003 T wallet2::decrypt(const std::string &ciphertext, const crypto::secret_key &skey, bool authenticated) const
13004 {
13005  const size_t prefix_size = sizeof(chacha_iv) + (authenticated ? sizeof(crypto::signature) : 0);
13006  THROW_WALLET_EXCEPTION_IF(ciphertext.size() < prefix_size,
13007  error::wallet_internal_error, "Unexpected ciphertext size");
13008 
13009  crypto::chacha_key key;
13010  crypto::generate_chacha_key(&skey, sizeof(skey), key, m_kdf_rounds);
13011  const crypto::chacha_iv &iv = *(const crypto::chacha_iv*)&ciphertext[0];
13012  if (authenticated)
13013  {
13015  crypto::cn_fast_hash(ciphertext.data(), ciphertext.size() - sizeof(signature), hash);
13016  crypto::public_key pkey;
13018  const crypto::signature &signature = *(const crypto::signature*)&ciphertext[ciphertext.size() - sizeof(crypto::signature)];
13020  error::wallet_internal_error, "Failed to authenticate ciphertext");
13021  }
13022  std::unique_ptr<char[]> buffer{new char[ciphertext.size() - prefix_size]};
13023  auto wiper = epee::misc_utils::create_scope_leave_handler([&]() { memwipe(buffer.get(), ciphertext.size() - prefix_size); });
13024  crypto::chacha20(ciphertext.data() + sizeof(iv), ciphertext.size() - prefix_size, key, iv, buffer.get());
13025  return T(buffer.get(), ciphertext.size() - prefix_size);
13026 }
13027 //----------------------------------------------------------------------------------------------------
13028 template epee::wipeable_string wallet2::decrypt(const std::string &ciphertext, const crypto::secret_key &skey, bool authenticated) const;
13029 //----------------------------------------------------------------------------------------------------
13030 std::string wallet2::decrypt_with_view_secret_key(const std::string &ciphertext, bool authenticated) const
13031 {
13032  return decrypt(ciphertext, get_account().get_keys().m_view_secret_key, authenticated);
13033 }
13034 //----------------------------------------------------------------------------------------------------
13035 std::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
13036 {
13038  if(!get_account_address_from_str(info, nettype(), address))
13039  {
13040  error = std::string("wrong address: ") + address;
13041  return std::string();
13042  }
13043 
13044  // we want only one payment id
13045  if (info.has_payment_id && !payment_id.empty())
13046  {
13047  error = "A single payment id is allowed";
13048  return std::string();
13049  }
13050 
13051  if (!payment_id.empty())
13052  {
13053  crypto::hash pid32;
13054  crypto::hash8 pid8;
13055  if (!wallet2::parse_long_payment_id(payment_id, pid32) && !parse_short_payment_id(payment_id, pid8))
13056  {
13057  error = "Invalid payment id";
13058  return std::string();
13059  }
13060  }
13061 
13062  std::string uri = "electroneum:" + address;
13063  unsigned int n_fields = 0;
13064 
13065  if (!payment_id.empty())
13066  {
13067  uri += (n_fields++ ? "&" : "?") + std::string("tx_payment_id=") + payment_id;
13068  }
13069 
13070  if (amount > 0)
13071  {
13072  // URI encoded amount is in decimal units, not atomic units
13073  uri += (n_fields++ ? "&" : "?") + std::string("tx_amount=") + cryptonote::print_etn(amount);
13074  }
13075 
13076  if (!recipient_name.empty())
13077  {
13078  uri += (n_fields++ ? "&" : "?") + std::string("recipient_name=") + epee::net_utils::conver_to_url_format(recipient_name);
13079  }
13080 
13081  if (!tx_description.empty())
13082  {
13083  uri += (n_fields++ ? "&" : "?") + std::string("tx_description=") + epee::net_utils::conver_to_url_format(tx_description);
13084  }
13085 
13086  return uri;
13087 }
13088 //----------------------------------------------------------------------------------------------------
13089 bool 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)
13090 {
13091  if (uri.substr(0, 12) != "electroneum:")
13092  {
13093  error = std::string("URI has wrong scheme (expected \"electroneum:\"): ") + uri;
13094  return false;
13095  }
13096 
13097  std::string remainder = uri.substr(12);
13098  const char *ptr = strchr(remainder.c_str(), '?');
13099  address = ptr ? remainder.substr(0, ptr-remainder.c_str()) : remainder;
13100 
13102  if(!get_account_address_from_str(info, nettype(), address))
13103  {
13104  error = std::string("URI has wrong address: ") + address;
13105  return false;
13106  }
13107  if (!strchr(remainder.c_str(), '?'))
13108  return true;
13109 
13110  std::vector<std::string> arguments;
13111  std::string body = remainder.substr(address.size() + 1);
13112  if (body.empty())
13113  return true;
13114  boost::split(arguments, body, boost::is_any_of("&"));
13115  std::set<std::string> have_arg;
13116  for (const auto &arg: arguments)
13117  {
13118  std::vector<std::string> kv;
13119  boost::split(kv, arg, boost::is_any_of("="));
13120  if (kv.size() != 2)
13121  {
13122  error = std::string("URI has wrong parameter: ") + arg;
13123  return false;
13124  }
13125  if (have_arg.find(kv[0]) != have_arg.end())
13126  {
13127  error = std::string("URI has more than one instance of " + kv[0]);
13128  return false;
13129  }
13130  have_arg.insert(kv[0]);
13131 
13132  if (kv[0] == "tx_amount")
13133  {
13134  amount = 0;
13135  if (!cryptonote::parse_amount(amount, kv[1]))
13136  {
13137  error = std::string("URI has invalid amount: ") + kv[1];
13138  return false;
13139  }
13140  }
13141  else if (kv[0] == "tx_payment_id")
13142  {
13143  if (info.has_payment_id)
13144  {
13145  error = "Separate payment id given with an integrated address";
13146  return false;
13147  }
13150  if (!wallet2::parse_long_payment_id(kv[1], hash) && !wallet2::parse_short_payment_id(kv[1], hash8))
13151  {
13152  error = "Invalid payment id: " + kv[1];
13153  return false;
13154  }
13155  payment_id = kv[1];
13156  }
13157  else if (kv[0] == "recipient_name")
13158  {
13159  recipient_name = epee::net_utils::convert_from_url_format(kv[1]);
13160  }
13161  else if (kv[0] == "tx_description")
13162  {
13163  tx_description = epee::net_utils::convert_from_url_format(kv[1]);
13164  }
13165  else
13166  {
13167  unknown_parameters.push_back(arg);
13168  }
13169  }
13170  return true;
13171 }
13172 //----------------------------------------------------------------------------------------------------
13173 uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, uint8_t day)
13174 {
13175  uint32_t version;
13176  if (!check_connection(&version))
13177  {
13178  throw std::runtime_error("failed to connect to daemon: " + get_daemon_address());
13179  }
13180  if (version < MAKE_CORE_RPC_VERSION(1, 6))
13181  {
13182  throw std::runtime_error("this function requires RPC version 1.6 or higher");
13183  }
13184  std::tm date = { 0, 0, 0, 0, 0, 0, 0, 0 };
13185  date.tm_year = year - 1900;
13186  date.tm_mon = month - 1;
13187  date.tm_mday = day;
13188  if (date.tm_mon < 0 || 11 < date.tm_mon || date.tm_mday < 1 || 31 < date.tm_mday)
13189  {
13190  throw std::runtime_error("month or day out of range");
13191  }
13192  uint64_t timestamp_target = std::mktime(&date);
13193  std::string err;
13194  uint64_t height_min = 0;
13195  uint64_t height_max = get_daemon_blockchain_height(err) - 1;
13196  if (!err.empty())
13197  {
13198  throw std::runtime_error("failed to get blockchain height");
13199  }
13200  while (true)
13201  {
13204  uint64_t height_mid = (height_min + height_max) / 2;
13205  req.heights =
13206  {
13207  height_min,
13208  height_mid,
13209  height_max
13210  };
13211  bool r = invoke_http_bin("/getblocks_by_height.bin", req, res, rpc_timeout);
13212  if (!r || res.status != CORE_RPC_STATUS_OK)
13213  {
13214  std::ostringstream oss;
13215  oss << "failed to get blocks by heights: ";
13216  for (auto height : req.heights)
13217  oss << height << ' ';
13218  oss << endl << "reason: ";
13219  if (!r)
13220  oss << "possibly lost connection to daemon";
13221  else if (res.status == CORE_RPC_STATUS_BUSY)
13222  oss << "daemon is busy";
13223  else
13224  oss << get_rpc_status(res.status);
13225  throw std::runtime_error(oss.str());
13226  }
13227  cryptonote::block blk_min, blk_mid, blk_max;
13228  if (res.blocks.size() < 3) throw std::runtime_error("Not enough blocks returned from daemon");
13229  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));
13230  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));
13231  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));
13232  uint64_t timestamp_min = blk_min.timestamp;
13233  uint64_t timestamp_mid = blk_mid.timestamp;
13234  uint64_t timestamp_max = blk_max.timestamp;
13235  if (!(timestamp_min <= timestamp_mid && timestamp_mid <= timestamp_max))
13236  {
13237  // the timestamps are not in the chronological order.
13238  // assuming they're sufficiently close to each other, simply return the smallest height
13239  return std::min({height_min, height_mid, height_max});
13240  }
13241  if (timestamp_target > timestamp_max)
13242  {
13243  throw std::runtime_error("specified date is in the future");
13244  }
13245  if (timestamp_target <= timestamp_min + 2 * 24 * 60 * 60) // two days of "buffer" period
13246  {
13247  return height_min;
13248  }
13249  if (timestamp_target <= timestamp_mid)
13250  height_max = height_mid;
13251  else
13252  height_min = height_mid;
13253  if (height_max - height_min <= 2 * 24 * 30) // don't divide the height range finer than two days
13254  {
13255  return height_min;
13256  }
13257  }
13258 }
13259 //----------------------------------------------------------------------------------------------------
13260 bool wallet2::is_synced() const
13261 {
13262  uint64_t height;
13263  boost::optional<std::string> result = m_node_rpc_proxy.get_target_height(height);
13264  if (result && *result != CORE_RPC_STATUS_OK)
13265  return false;
13266  return get_blockchain_current_height() >= height;
13267 }
13268 //----------------------------------------------------------------------------------------------------
13269 std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(const std::vector<std::pair<double, double>> &fee_levels)
13270 {
13271  for (const auto &fee_level: fee_levels)
13272  {
13273  THROW_WALLET_EXCEPTION_IF(fee_level.first == 0.0, error::wallet_internal_error, "Invalid 0 fee");
13274  THROW_WALLET_EXCEPTION_IF(fee_level.second == 0.0, error::wallet_internal_error, "Invalid 0 fee");
13275  }
13276 
13277  // get txpool backlog
13280  m_daemon_rpc_mutex.lock();
13281  bool r = invoke_http_json_rpc("/json_rpc", "get_txpool_backlog", req, res, rpc_timeout);
13282  m_daemon_rpc_mutex.unlock();
13283  THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "Failed to connect to daemon");
13284  THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_txpool_backlog");
13286 
13287  uint64_t block_weight_limit = 0;
13288  const auto result = m_node_rpc_proxy.get_block_weight_limit(block_weight_limit);
13289  throw_on_rpc_response_error(result, "get_info");
13290  uint64_t full_reward_zone = block_weight_limit / 2;
13291  THROW_WALLET_EXCEPTION_IF(full_reward_zone == 0, error::wallet_internal_error, "Invalid block weight limit from daemon");
13292 
13293  std::vector<std::pair<uint64_t, uint64_t>> blocks;
13294  for (const auto &fee_level: fee_levels)
13295  {
13296  const double our_fee_byte_min = fee_level.first;
13297  const double our_fee_byte_max = fee_level.second;
13298  uint64_t priority_weight_min = 0, priority_weight_max = 0;
13299  for (const auto &i: res.backlog)
13300  {
13301  if (i.weight == 0)
13302  {
13303  MWARNING("Got 0 weight tx from txpool, ignored");
13304  continue;
13305  }
13306  double this_fee_byte = i.fee / (double)i.weight;
13307  if (this_fee_byte >= our_fee_byte_min)
13308  priority_weight_min += i.weight;
13309  if (this_fee_byte >= our_fee_byte_max)
13310  priority_weight_max += i.weight;
13311  }
13312 
13313  uint64_t nblocks_min = priority_weight_min / full_reward_zone;
13314  uint64_t nblocks_max = priority_weight_max / full_reward_zone;
13315  MDEBUG("estimate_backlog: priority_weight " << priority_weight_min << " - " << priority_weight_max << " for "
13316  << our_fee_byte_min << " - " << our_fee_byte_max << " piconero byte fee, "
13317  << nblocks_min << " - " << nblocks_max << " blocks at block weight " << full_reward_zone);
13318  blocks.push_back(std::make_pair(nblocks_min, nblocks_max));
13319  }
13320  return blocks;
13321 }
13322 //----------------------------------------------------------------------------------------------------
13323 std::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)
13324 {
13325  THROW_WALLET_EXCEPTION_IF(min_tx_weight == 0, error::wallet_internal_error, "Invalid 0 fee");
13326  THROW_WALLET_EXCEPTION_IF(max_tx_weight == 0, error::wallet_internal_error, "Invalid 0 fee");
13327  for (uint64_t fee: fees)
13328  {
13329  THROW_WALLET_EXCEPTION_IF(fee == 0, error::wallet_internal_error, "Invalid 0 fee");
13330  }
13331  std::vector<std::pair<double, double>> fee_levels;
13332  for (uint64_t fee: fees)
13333  {
13334  double our_fee_byte_min = fee / (double)min_tx_weight, our_fee_byte_max = fee / (double)max_tx_weight;
13335  fee_levels.emplace_back(our_fee_byte_min, our_fee_byte_max);
13336  }
13337  return estimate_backlog(fee_levels);
13338 }
13339 //----------------------------------------------------------------------------------------------------
13340 uint64_t wallet2::get_segregation_fork_height() const
13341 {
13342  if (m_nettype == TESTNET)
13344  if (m_nettype == STAGENET)
13346  THROW_WALLET_EXCEPTION_IF(m_nettype != MAINNET, tools::error::wallet_internal_error, "Invalid network type");
13347 
13348  if (m_segregation_height > 0)
13349  return m_segregation_height;
13350 
13351  if (m_use_dns && !m_offline)
13352  {
13353  // All four ElectroneumPulse domains have DNSSEC on and valid
13354  static const std::vector<std::string> dns_urls = {
13355  "segheights.electroneumpulse.org",
13356  "segheights.electroneumpulse.net",
13357  "segheights.electroneumpulse.co",
13358  "segheights.electroneumpulse.se"
13359  };
13360 
13361  const uint64_t current_height = get_blockchain_current_height();
13362  uint64_t best_diff = std::numeric_limits<uint64_t>::max(), best_height = 0;
13363  std::vector<std::string> records;
13364  if (tools::dns_utils::load_txt_records_from_dns(records, dns_urls, "heights"))
13365  {
13366  for (const auto& record : records)
13367  {
13368  std::vector<std::string> fields;
13369  boost::split(fields, record, boost::is_any_of(":"));
13370  if (fields.size() != 2)
13371  continue;
13372  uint64_t height;
13374  continue;
13375 
13376  MINFO("Found segregation height via DNS: " << fields[0] << " fork height at " << height);
13377  uint64_t diff = height > current_height ? height - current_height : current_height - height;
13378  if (diff < best_diff)
13379  {
13380  best_diff = diff;
13381  best_height = height;
13382  }
13383  }
13384  if (best_height)
13385  return best_height;
13386  }
13387  }
13388  return SEGREGATION_FORK_HEIGHT;
13389 }
13390 //----------------------------------------------------------------------------------------------------
13391 void wallet2::generate_genesis(cryptonote::block& b) const {
13393 }
13394 //----------------------------------------------------------------------------------------------------
13395 mms::multisig_wallet_state wallet2::get_multisig_wallet_state() const
13396 {
13398  state.nettype = m_nettype;
13399  state.multisig = multisig(&state.multisig_is_ready);
13400  state.has_multisig_partial_key_images = has_multisig_partial_key_images();
13401  state.multisig_rounds_passed = m_multisig_rounds_passed;
13402  state.num_transfer_details = m_transfers.size();
13403  if (state.multisig)
13404  {
13405  THROW_WALLET_EXCEPTION_IF(!m_original_keys_available, error::wallet_internal_error, "MMS use not possible because own original Electroneum address not available");
13406  state.address = m_original_address;
13407  state.view_secret_key = m_original_view_secret_key;
13408  }
13409  else
13410  {
13411  state.address = m_account.get_keys().m_account_address;
13412  state.view_secret_key = m_account.get_keys().m_view_secret_key;
13413  }
13414  state.mms_file=m_mms_file;
13415  return state;
13416 }
13417 //----------------------------------------------------------------------------------------------------
13418 wallet_device_callback * wallet2::get_device_callback()
13419 {
13420  if (!m_device_callback){
13421  m_device_callback.reset(new wallet_device_callback(this));
13422  }
13423  return m_device_callback.get();
13424 }//----------------------------------------------------------------------------------------------------
13425 void wallet2::on_device_button_request(uint64_t code)
13426 {
13427  if (nullptr != m_callback)
13428  m_callback->on_device_button_request(code);
13429 }
13430 //----------------------------------------------------------------------------------------------------
13431 void wallet2::on_device_button_pressed()
13432 {
13433  if (nullptr != m_callback)
13434  m_callback->on_device_button_pressed();
13435 }
13436 //----------------------------------------------------------------------------------------------------
13437 boost::optional<epee::wipeable_string> wallet2::on_device_pin_request()
13438 {
13439  if (nullptr != m_callback)
13440  return m_callback->on_device_pin_request();
13441  return boost::none;
13442 }
13443 //----------------------------------------------------------------------------------------------------
13444 boost::optional<epee::wipeable_string> wallet2::on_device_passphrase_request(bool on_device)
13445 {
13446  if (nullptr != m_callback)
13447  return m_callback->on_device_passphrase_request(on_device);
13448  return boost::none;
13449 }
13450 //----------------------------------------------------------------------------------------------------
13451 void wallet2::on_device_progress(const hw::device_progress& event)
13452 {
13453  if (nullptr != m_callback)
13454  m_callback->on_device_progress(event);
13455 }
13456 //----------------------------------------------------------------------------------------------------
13457 std::string wallet2::get_rpc_status(const std::string &s) const
13458 {
13459  if (m_trusted_daemon)
13460  return s;
13461  return "<error>";
13462 }
13463 //----------------------------------------------------------------------------------------------------
13464 void wallet2::throw_on_rpc_response_error(const boost::optional<std::string> &status, const char *method) const
13465 {
13466  // no error
13467  if (!status)
13468  return;
13469 
13470  MERROR("RPC error: " << method << ": status " << *status);
13471 
13472  // empty string -> not connection
13474 
13476  THROW_WALLET_EXCEPTION_IF(*status != CORE_RPC_STATUS_OK, tools::error::wallet_generic_rpc_error, method, m_trusted_daemon ? *status : "daemon error");
13477 }
13478 //----------------------------------------------------------------------------------------------------
13479 void wallet2::hash_m_transfer(const transfer_details & transfer, crypto::hash &hash) const
13480 {
13481  KECCAK_CTX state;
13482  keccak_init(&state);
13483  keccak_update(&state, (const uint8_t *) transfer.m_txid.data, sizeof(transfer.m_txid.data));
13484  keccak_update(&state, (const uint8_t *) transfer.m_internal_output_index, sizeof(transfer.m_internal_output_index));
13485  keccak_update(&state, (const uint8_t *) transfer.m_global_output_index, sizeof(transfer.m_global_output_index));
13486  keccak_update(&state, (const uint8_t *) transfer.m_amount, sizeof(transfer.m_amount));
13487  keccak_finish(&state, (uint8_t *) hash.data);
13488 }
13489 //----------------------------------------------------------------------------------------------------
13490 uint64_t wallet2::hash_m_transfers(int64_t transfer_height, crypto::hash &hash) const
13491 {
13492  CHECK_AND_ASSERT_THROW_MES(transfer_height > (int64_t)m_transfers.size(), "Hash height is greater than number of transfers");
13493 
13494  KECCAK_CTX state;
13495  crypto::hash tmp_hash{};
13496  uint64_t current_height = 0;
13497 
13498  keccak_init(&state);
13499  for(const transfer_details & transfer : m_transfers){
13500  if (transfer_height >= 0 && current_height >= (uint64_t)transfer_height){
13501  break;
13502  }
13503 
13504  hash_m_transfer(transfer, tmp_hash);
13505  keccak_update(&state, (const uint8_t *) transfer.m_block_height, sizeof(transfer.m_block_height));
13506  keccak_update(&state, (const uint8_t *) tmp_hash.data, sizeof(tmp_hash.data));
13507  current_height += 1;
13508  }
13509 
13510  keccak_finish(&state, (uint8_t *) hash.data);
13511  return current_height;
13512 }
13513 //----------------------------------------------------------------------------------------------------
13514 void wallet2::finish_rescan_bc_keep_key_images(uint64_t transfer_height, const crypto::hash &hash)
13515 {
13516  // Compute hash of m_transfers, if differs there had to be BC reorg.
13517  crypto::hash new_transfers_hash{};
13518  hash_m_transfers((int64_t) transfer_height, new_transfers_hash);
13519 
13520  if (new_transfers_hash != hash)
13521  {
13522  // Soft-Reset to avoid inconsistency in case of BC reorg.
13523  clear_soft(false); // keep_key_images works only with soft reset.
13524  THROW_WALLET_EXCEPTION_IF(true, error::wallet_internal_error, "Transfers changed during rescan, soft or hard rescan is needed");
13525  }
13526 
13527  // Restore key images in m_transfers from m_key_images
13528  for(auto it = m_key_images.begin(); it != m_key_images.end(); it++)
13529  {
13530  THROW_WALLET_EXCEPTION_IF(it->second >= m_transfers.size(), error::wallet_internal_error, "Key images cache contains illegal transfer offset");
13531  m_transfers[it->second].m_key_image = it->first;
13532  m_transfers[it->second].m_key_image_known = true;
13533  }
13534 }
13535 //----------------------------------------------------------------------------------------------------
13536 uint64_t wallet2::get_bytes_sent() const
13537 {
13538  return m_http_client.get_bytes_sent();
13539 }
13540 //----------------------------------------------------------------------------------------------------
13541 uint64_t wallet2::get_bytes_received() const
13542 {
13543  return m_http_client.get_bytes_received();
13544 }
13545 
13546 void wallet2::add_checkpoint(uint64_t height, std::string hash){
13547  m_checkpoints.add_checkpoint(height, hash);
13548 }
13549 }
return true
else if(0==res)
uint64_t height
Definition: blockchain.cpp:91
time_t time
Definition: blockchain.cpp:93
uint8_t threshold
Definition: blockchain.cpp:92
void chacha20(const void *data, size_t length, const uint8_t *key, const uint8_t *iv, char *cipher)
void chacha8(const void *data, size_t length, const uint8_t *key, const uint8_t *iv, char *cipher)
std::tuple< uint64_t, crypto::public_key, rct::key > get_outs_entry
Definition: chaingen.h:352
hw::device & get_device() const
Definition: account.h:91
void encrypt_viewkey(const crypto::chacha_key &key)
Definition: account.h:106
void set_device(hw::device &hwdev)
Definition: account.h:92
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
bool init_default_checkpoints(network_type nettype)
loads the default main chain checkpoints
std::vector< uint8_t > extra
bool set_server(const std::string &address, boost::optional< login > user, ssl_options_t ssl_options=ssl_support_t::e_ssl_support_autodetect)
Definition: http_client.h:302
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
void append(const char *ptr, size_t len)
const char * data() const noexcept
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
bool decrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key)
Definition: device.hpp:210
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
@ 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
Definition: device_cold.hpp:46
boost::optional< int > bp_version
Definition: device_cold.hpp:48
std::vector< cryptonote::address_parse_info > tx_recipients
Definition: device_cold.hpp:47
static void init_options(boost::program_options::options_description &desc_params)
gamma_picker(const std::vector< uint64_t > &rct_offsets)
Definition: wallet2.cpp:1010
virtual boost::optional< epee::wipeable_string > on_get_password(const char *reason)
Definition: wallet2.h:132
void wait(threadpool *tpool)
Definition: threadpool.cpp:115
A global thread pool.
Definition: threadpool.h:43
static threadpool & getInstance()
Definition: threadpool.h:45
void submit(waiter *waiter, std::function< void()> f, bool leaf=false)
Definition: threadpool.cpp:69
unsigned int get_max_concurrency() const
Definition: threadpool.cpp:92
bool get_multisig_seed(epee::wipeable_string &seed, const epee::wipeable_string &passphrase=std::string(), bool raw=true) const
Definition: wallet2.cpp:1326
void expand_subaddresses(const cryptonote::subaddress_index &index, const bool udpate_account_tags=true)
Definition: wallet2.cpp:1477
static const char * tr(const char *str)
Definition: wallet2.cpp:993
static bool has_testnet_option(const boost::program_options::variables_map &vm)
Definition: wallet2.cpp:1172
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:1267
bool is_unattended() const
Definition: wallet2.h:1266
bool watch_only() const
Definition: wallet2.h:819
std::string encrypt(const char *plaintext, size_t len, const crypto::secret_key &skey, bool authenticated=true) const
Definition: wallet2.cpp:12961
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:1249
void freeze(size_t idx)
Definition: wallet2.cpp:1583
wallet2(cryptonote::network_type nettype=cryptonote::MAINNET, uint64_t kdf_rounds=1, bool unattended=false)
Definition: wallet2.cpp:1101
const transfer_details & get_transfer_details(size_t idx) const
Definition: wallet2.cpp:10642
crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const
Definition: wallet2.cpp:11933
void add_subaddress(uint32_t index_major, const std::string &label)
Definition: wallet2.cpp:1469
void decrypt_keys(const crypto::chacha_key &key)
Definition: wallet2.cpp:4642
std::string get_daemon_address() const
Definition: wallet2.cpp:11716
static std::string device_derivation_path_option(const boost::program_options::variables_map &vm)
Definition: wallet2.cpp:1187
bool get_seed(epee::wipeable_string &electrum_words, const epee::wipeable_string &passphrase=epee::wipeable_string()) const
Definition: wallet2.cpp:1300
std::tuple< uint64_t, crypto::public_key, rct::key > get_outs_entry
Definition: wallet2.h:556
size_t get_num_subaddress_accounts() const
Definition: wallet2.h:795
void add_subaddress_account(const std::string &label, const bool update_account_tags=true)
Definition: wallet2.cpp:1462
@ AskPasswordToDecrypt
Definition: wallet2.h:228
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:1226
std::string get_subaddress_label(const cryptonote::subaddress_index &index) const
Definition: wallet2.cpp:1517
static std::string device_name_option(const boost::program_options::variables_map &vm)
Definition: wallet2.cpp:1182
void set_subaddress_label(const cryptonote::subaddress_index &index, const std::string &label)
Definition: wallet2.cpp:1527
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:1281
bool reconnect_device()
Definition: wallet2.cpp:1389
void thaw(size_t idx)
Definition: wallet2.cpp:1590
AskPasswordType ask_password() const
Definition: wallet2.h:1071
std::vector< transfer_details > transfer_container
Definition: wallet2.h:443
void set_seed_language(const std::string &language)
Sets the seed language.
Definition: wallet2.cpp:1424
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:1260
void encrypt_keys(const crypto::chacha_key &key)
Definition: wallet2.cpp:4636
const std::pair< std::map< std::string, std::string >, std::vector< std::string > > & get_account_tags()
Get the list of registered account tags.
Definition: wallet2.cpp:11826
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:1232
void set_subaddress_lookahead(size_t major, size_t minor)
Definition: wallet2.cpp:1534
boost::optional< cryptonote::subaddress_index > get_subaddress_index(const cryptonote::account_public_address &address) const
Definition: wallet2.cpp:1436
std::string get_integrated_address_as_str(const crypto::hash8 &payment_id) const
Definition: wallet2.cpp:1457
size_t get_num_subaddresses(uint32_t index_major) const
Definition: wallet2.h:796
bool is_deterministic() const
Checks if deterministic wallet.
Definition: wallet2.cpp:1292
bool multisig(bool *ready=NULL, uint32_t *threshold=NULL, uint32_t *total=NULL) const
Definition: wallet2.cpp:5524
cryptonote::account_public_address get_subaddress(const cryptonote::subaddress_index &index) const
Definition: wallet2.cpp:1429
static bool has_stagenet_option(const boost::program_options::variables_map &vm)
Definition: wallet2.cpp:1177
std::string get_subaddress_as_str(const cryptonote::subaddress_index &index) const
Definition: wallet2.cpp:1451
const std::string & get_seed_language() const
Gets the seed language.
Definition: wallet2.cpp:1416
std::unordered_multimap< crypto::hash, payment_details > payment_container
Definition: wallet2.h:444
bool frozen(size_t idx) const
Definition: wallet2.cpp:1597
cryptonote::account_base & get_account()
Definition: wallet2.h:728
static void init_options(boost::program_options::options_description &desc_params)
Definition: wallet2.cpp:1192
bool is_deprecated() const
Tells if the wallet file is deprecated.
Definition: wallet2.cpp:1545
cryptonote::account_public_address get_address() const
Definition: wallet2.h:787
crypto::public_key get_subaddress_spend_public_key(const cryptonote::subaddress_index &index) const
Definition: wallet2.cpp:1444
const boost::optional< epee::net_utils::http::login > & get_daemon_login() const
Definition: wallet2.h:1157
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:4589
boost::optional< epee::wipeable_string > on_passphrase_request(bool on_device) override
Definition: wallet2.cpp:1088
void on_button_pressed() override
Definition: wallet2.cpp:1075
void on_button_request(uint64_t code=0) override
Definition: wallet2.cpp:1069
void on_progress(const hw::device_progress &event) override
Definition: wallet2.cpp:1095
boost::optional< epee::wipeable_string > on_pin_request() override
Definition: wallet2.cpp:1081
wallet_keys_unlocker(wallet2 &w, const boost::optional< tools::password_container > &password)
Definition: wallet2.cpp:1033
#define tr(x)
Definition: common_defines.h:4
#define MAKE_CORE_RPC_VERSION(major, minor)
#define CORE_RPC_STATUS_OK
#define CORE_RPC_STATUS_BUSY
void sc_reduce32(unsigned char *)
void sc_sub(unsigned char *, const unsigned char *, const unsigned char *)
void sc_add(unsigned char *, const unsigned char *, const unsigned char *)
#define HF_VERSION_ENABLE_RCT
#define CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE
#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5
#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_MIN_MIXIN_4
std::string message("Message requiring signing")
GenericValue< UTF8<> > Value
GenericValue with UTF8 encoding.
Definition: document.h:2116
GenericDocument< UTF8<> > Document
GenericDocument with UTF8 encoding.
Definition: document.h:2512
#define ELPP
Mnemonic seed generation and wallet restoration from them.
expect< void > success() noexcept
Definition: expect.h:397
GenericStringBuffer< UTF8< char >, CrtAllocator > StringBuffer
Definition: fwd.h:59
void * memcpy(void *a, const void *b, size_t c)
const uint32_t T[512]
void cn_fast_hash(const void *data, size_t length, char *hash)
@ HASH_SIZE
Definition: hash-ops.h:78
const char * res
Definition: hmac_keccak.cpp:41
const char * key
Definition: hmac_keccak.cpp:39
const char * i18n_translate(const char *s, const std::string &context)
Definition: i18n.cpp:322
#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)
Definition: misc_language.h:53
#define MERROR(x)
Definition: misc_log_ex.h:73
#define LOG_PRINT_L3(x)
Definition: misc_log_ex.h:102
#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
Definition: misc_log_ex.h:149
#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message)
Definition: misc_log_ex.h:181
#define LOG_PRINT_L1(x)
Definition: misc_log_ex.h:100
#define LOG_ERROR(x)
Definition: misc_log_ex.h:98
#define CHECK_AND_ASSERT_THROW_MES(expr, message)
Definition: misc_log_ex.h:173
#define MTRACE(x)
Definition: misc_log_ex.h:77
#define MINFO(x)
Definition: misc_log_ex.h:75
#define LOG_PRINT_L2(x)
Definition: misc_log_ex.h:101
#define LOG_PRINT_L0(x)
Definition: misc_log_ex.h:99
void load(Archive &a, std::unordered_map< h_key, hval > &x, const boost::serialization::version_type ver)
void add_arg(boost::program_options::options_description &description, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg, bool unique=true)
Definition: command_line.h:188
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)
Definition: command_line.h:258
bool is_arg_defaulted(const boost::program_options::variables_map &vm, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg)
Definition: command_line.h:265
T get_arg(const boost::program_options::variables_map &vm, const arg_descriptor< T, false, true > &arg)
Definition: command_line.h:271
std::string const GENESIS_TX
uint64_t const DEFAULT_DUST_THRESHOLD
uint32_t const GENESIS_NONCE
uint64_t const CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX
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:290
void cn_fast_hash(const void *data, std::size_t length, hash &hash)
Definition: hash.h:69
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:309
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
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:284
POD_CLASS hash8
Definition: hash.h:53
POD_CLASS key_derivation
Definition: crypto.h:98
void derive_secret_key(const key_derivation &derivation, std::size_t output_index, const secret_key &base, secret_key &derived_key)
Definition: crypto.h:280
bool generate_key_derivation(const public_key &key1, const secret_key &key2, key_derivation &derivation)
Definition: crypto.h:270
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:325
POD_CLASS public_key
Definition: crypto.h:76
bool derive_public_key(const key_derivation &derivation, std::size_t output_index, const public_key &base, public_key &derived_key)
Definition: crypto.h:273
POD_CLASS key_image
Definition: crypto.h:102
bool check_signature(const hash &prefix_hash, const public_key &pub, const signature &sig)
Definition: crypto.h:293
bool secret_key_to_public_key(const secret_key &sec, public_key &pub)
Definition: crypto.h:260
std::enable_if< std::is_unsigned< T >::value, T >::type rand_idx(T sz)
Definition: crypto.h:242
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:312
POD_CLASS hash
Definition: hash.h:50
void generate_key_image(const public_key &pub, const secret_key &sec, key_image &image)
Definition: crypto.h:322
void cn_slow_hash(const void *data, std::size_t length, hash &hash, int variant=0, uint64_t height=0)
Definition: hash.h:79
void derivation_to_scalar(const key_derivation &derivation, size_t output_index, ec_scalar &res)
Definition: crypto.h:277
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:331
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_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)
const config_t & get_config(network_type nettype)
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
Definition: blobdatatype.h:39
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)
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)
epee::misc_utils::struct_init< response_t > response
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)
Definition: file_io_utils.h:73
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 parse_uri(const std::string uri, http::uri_content &content)
bool invoke_http_json(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 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")
std::string to_string(t_connection_type type)
@ user_certificates
Verify peer via specific (non-chain) certificate(s) only.
@ user_ca
Verify peer via specific (possibly chain) certificate(s) only.
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)
Definition: string_tools.h:92
std::string cut_off_extension(const std::string &str)
Definition: string_tools.h:358
std::string pod_to_hex(const t_pod_type &s)
Definition: string_tools.h:317
std::string get_extension(const std::string &str)
Definition: string_tools.h:347
bool hex_to_pod(const std::string &hex_str, t_pod_type &s)
Definition: string_tools.h:324
PUSH_WARNINGS bool get_xtype_from_string(OUT XType &val, const std::string &str_id)
Definition: string_tools.h:125
bool validate_hex(uint64_t length, const std::string &str)
std::string buff_to_hex_nodelimer(const std::string &src)
Definition: string_tools.h:87
void decrypt(const void *ciphertext, size_t length, const uint8_t *key, const uint8_t *iv, char *plaintext, size_t *plaintext_len)
Definition: protocol.cpp:120
std::shared_ptr< messages::Electroneum::ElectroneumGetTxKeyRequest > get_tx_key(const hw::device_cold::tx_key_data_t &tx_data)
Definition: protocol.cpp:1038
void register_all(std::map< std::string, std::unique_ptr< device >> &registry)
Definition: device.cpp:38
device & get_device(const std::string &device_descriptor)
Definition: device.cpp:95
struct hw::wallet_shim wallet_shim
mdb_size_t count(MDB_cursor *cur)
error
Tracks LMDB error codes.
Definition: error.h:45
version
Supported socks variants.
Definition: socks.h:58
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
@ RCTTypeFull
Definition: rctTypes.h:230
@ RCTTypeSimple
Definition: rctTypes.h:231
@ RCTTypeBulletproof2
Definition: rctTypes.h:233
@ RCTTypeBulletproof
Definition: rctTypes.h:232
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
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
void copy(key &AA, const key &A)
Definition: rctOps.h:79
@ 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)
Definition: binary_utils.h:41
bool dump_binary(T &v, std::string &blob)
Definition: binary_utils.h:51
tuple make_tuple()
Definition: gtest-tuple.h:675
::std::string string
Definition: gtest-port.h:1097
const T & move(const T &t)
Definition: gtest-port.h:1317
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:2126
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:2087
bool load_txt_records_from_dns(std::vector< std::string > &good_records, const std::vector< std::string > &dns_urls, std::string type)
Definition: dns_utils.cpp:515
file_error_base< file_save_error_message_index > file_save_error
failed_rpc_request< refresh_error, get_hashes_error_message_index > get_hashes_error
failed_rpc_request< refresh_error, get_blocks_error_message_index > get_blocks_error
failed_rpc_request< transfer_error, get_outs_error_message_index > get_outs_error
Various Tools.
Definition: tools.cpp:31
const size_t MAX_SPLIT_ATTEMPTS
Definition: wallet2.cpp:990
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)
Definition: combinator.cpp:35
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
crypto::hash get_block_hash(uint64_t height)
bool get_output_distribution(uint64_t amount, uint64_t from, uint64_t to, uint64_t &start_height, std::vector< uint64_t > &distribution, uint64_t &base)
#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)
Definition: profile_tools.h:64
#define TIME_MEASURE_START(var_name)
Definition: profile_tools.h:61
@ kStringType
string
Definition: rapidjson.h:626
@ kNumberType
number
Definition: rapidjson.h:627
void serialize(Archive &a, unsigned_tx_set &x, const boost::serialization::version_type ver)
const char * buf
Definition: slow_memmem.cpp:74
boost::endian::big_uint32_t ip
Definition: socks.cpp:61
CXA_THROW_INFO_T * info
Definition: stack_trace.cpp:91
#define false
Definition: stdbool.h:38
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
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:167
crypto::public_key pub_key
Definition: tx_extra.h:99
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
crypto::public_key key
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
Definition: device_cold.hpp:41
Primarily for use with epee::net_utils::http_client.
Definition: socks_connect.h:42
unsigned char bytes[32]
Definition: rctTypes.h:86
std::vector< ecdhTuple > ecdhInfo
Definition: rctTypes.h:246
uint8_t type
Definition: rctTypes.h:241
Definition: blake256.h:37
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
crypto::chacha_iv iv
Definition: wallet2.h:528
crypto::chacha_iv iv
Definition: wallet2.h:517
std::unordered_set< crypto::public_key > m_signers
Definition: wallet2.h:507
std::vector< pending_tx > m_ptx
Definition: wallet2.h:506
cryptonote::subaddress_index m_subaddr_index
Definition: wallet2.h:362
tx_construction_data construction_data
Definition: wallet2.h:471
std::vector< size_t > selected_transfers
Definition: wallet2.h:464
crypto::secret_key tx_key
Definition: wallet2.h:466
std::vector< cryptonote::tx_destination_entry > dests
Definition: wallet2.h:468
std::vector< multisig_sig > multisig_sigs
Definition: wallet2.h:469
cryptonote::transaction tx
Definition: wallet2.h:460
std::vector< crypto::secret_key > additional_tx_keys
Definition: wallet2.h:467
std::string key_images
Definition: wallet2.h:465
cryptonote::tx_destination_entry change_dts
Definition: wallet2.h:463
Definition: wallet2.h:547
crypto::public_key shared_secret
Definition: wallet2.h:550
crypto::signature key_image_sig
Definition: wallet2.h:553
crypto::signature shared_secret_sig
Definition: wallet2.h:552
uint64_t index_in_tx
Definition: wallet2.h:549
crypto::key_image key_image
Definition: wallet2.h:551
crypto::hash txid
Definition: wallet2.h:548
std::unordered_map< crypto::public_key, crypto::key_image > tx_key_images
Definition: wallet2.h:501
std::vector< crypto::key_image > key_images
Definition: wallet2.h:500
std::vector< pending_tx > ptx
Definition: wallet2.h:499
crypto::key_image m_key_image
Definition: wallet2.h:311
cryptonote::subaddress_index m_subaddr_index
Definition: wallet2.h:318
const crypto::public_key & get_public_key() const
Definition: wallet2.h:326
cryptonote::transaction_prefix m_tx
Definition: wallet2.h:304
std::vector< rct::key > m_multisig_k
Definition: wallet2.h:320
std::vector< uint8_t > extra
Definition: wallet2.h:420
std::vector< cryptonote::tx_source_entry > sources
Definition: wallet2.h:416
std::vector< cryptonote::tx_destination_entry > splitted_dsts
Definition: wallet2.h:418
std::vector< cryptonote::tx_destination_entry > dests
Definition: wallet2.h:424
std::vector< size_t > selected_transfers
Definition: wallet2.h:419
std::set< uint32_t > subaddr_indices
Definition: wallet2.h:426
cryptonote::tx_destination_entry change_dts
Definition: wallet2.h:417
boost::optional< cryptonote::subaddress_receive_info > received
Definition: wallet2.h:296
enum tools::wallet2::unconfirmed_transfer_details::@62 m_state
std::vector< cryptonote::tx_destination_entry > m_dests
Definition: wallet2.h:385
std::vector< tx_construction_data > txes
Definition: wallet2.h:493
std::pair< size_t, wallet2::transfer_container > transfers
Definition: wallet2.h:494
#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
rapidjson::Document json
Definition: transport.cpp:49
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:99
#define UNSIGNED_TX_PREFIX
Definition: wallet2.cpp:106
#define GAMMA_SCALE
Definition: wallet2.cpp:134
#define KEY_IMAGE_EXPORT_FILE_MAGIC
Definition: wallet2.cpp:122
#define TESTNET_SEGREGATION_FORK_HEIGHT
Definition: wallet2.cpp:127
#define MULTISIG_EXPORT_FILE_MAGIC
Definition: wallet2.cpp:124
#define ELECTRONEUM_DEFAULT_LOG_CATEGORY
Definition: wallet2.cpp:93
#define FEE_ESTIMATE_GRACE_BLOCKS
Definition: wallet2.cpp:115
#define STAGENET_SEGREGATION_FORK_HEIGHT
Definition: wallet2.cpp:128
#define SEGREGATION_FORK_VICINITY
Definition: wallet2.cpp:129
#define FIRST_REFRESH_GRANULARITY
Definition: wallet2.cpp:131
#define DEFAULT_MIN_OUTPUT_VALUE
Definition: wallet2.cpp:137
#define SEGREGATION_FORK_HEIGHT
Definition: wallet2.cpp:126
#define RECENT_OUTPUT_BLOCKS
Definition: wallet2.cpp:113
#define MULTISIG_UNSIGNED_TX_PREFIX
Definition: wallet2.cpp:108
#define SUBADDRESS_LOOKAHEAD_MAJOR
Definition: wallet2.cpp:119
#define APPROXIMATE_INPUT_BYTES
Definition: wallet2.cpp:96
#define SECOND_OUTPUT_RELATEDNESS_THRESHOLD
Definition: wallet2.cpp:117
#define SUBADDRESS_LOOKAHEAD_MINOR
Definition: wallet2.cpp:120
#define DEFAULT_MIN_OUTPUT_COUNT
Definition: wallet2.cpp:136
#define RECENT_OUTPUT_RATIO
Definition: wallet2.cpp:110
#define SIGNED_TX_PREFIX
Definition: wallet2.cpp:107
#define CACHE_KEY_TAIL
Definition: wallet2.cpp:103
#define OUTPUT_EXPORT_FILE_MAGIC
Definition: wallet2.cpp:139
#define GAMMA_SHAPE
Definition: wallet2.cpp:133
#define RECENT_OUTPUT_ZONE
Definition: wallet2.cpp:112
#define CMPF(f)
#define THROW_WALLET_EXCEPTION_IF(cond, err_type,...)
#define THROW_WALLET_EXCEPTION(err_type,...)