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"
47 using namespace epee;
48 
49 #include "cryptonote_config.h"
50 #include "wallet2.h"
53 #include "misc_language.h"
55 #include "multisig/multisig.h"
57 #include "common/command_line.h"
58 #include "common/threadpool.h"
59 #include "profile_tools.h"
60 #include "crypto/crypto.h"
62 #include "serialization/string.h"
65 #include "common/i18n.h"
66 #include "common/util.h"
68 #include "rapidjson/document.h"
69 #include "rapidjson/writer.h"
70 #include "rapidjson/stringbuffer.h"
71 #include "common/json_util.h"
72 #include "memwipe.h"
73 #include "common/base58.h"
74 #include "common/combinator.h"
75 #include "common/dns_utils.h"
76 #include "common/notify.h"
77 #include "common/perf_timer.h"
78 #include "ringct/rctSigs.h"
79 #include "ringdb.h"
80 #include "device/device_cold.hpp"
82 #include "net/socks_connect.h"
83 
84 extern "C"
85 {
86 #include "crypto/keccak.h"
87 #include "crypto/crypto-ops.h"
88 }
89 using namespace std;
90 using namespace crypto;
91 using namespace cryptonote;
92 
93 #undef ELECTRONEUM_DEFAULT_LOG_CATEGORY
94 #define ELECTRONEUM_DEFAULT_LOG_CATEGORY "wallet.wallet2"
95 
96 // used to choose when to stop adding outputs to a tx
97 #define APPROXIMATE_INPUT_BYTES 80
98 
99 // used to target a given block weight (additional outputs may be added on top to build fee)
100 #define TX_WEIGHT_TARGET(bytes) (bytes*2/3)
101 
102 // arbitrary, used to generate different hashes from the same input
103 #define CHACHA8_KEY_TAIL 0x8c
104 #define CACHE_KEY_TAIL 0x8d
105 
106 // Magic number 004 means these payloads are encrypted.
107 #define UNSIGNED_TX_PREFIX "Electroneum unsigned tx set\004"
108 #define SIGNED_TX_PREFIX "Electroneum signed tx set\004"
109 #define MULTISIG_UNSIGNED_TX_PREFIX "Electroneum multisig unsigned tx set\001"
110 
111 #define RECENT_OUTPUT_RATIO (0.5) // 50% of outputs are from the recent zone
112 #define RECENT_OUTPUT_DAYS (1.8) // last 1.8 day makes up the recent zone (taken from electroneumlink.pdf, Miller et al)
113 #define RECENT_OUTPUT_ZONE ((time_t)(RECENT_OUTPUT_DAYS * 86400))
114 #define RECENT_OUTPUT_BLOCKS (RECENT_OUTPUT_DAYS * 720)
115 
116 #define FEE_ESTIMATE_GRACE_BLOCKS 10 // estimate fee valid for that many blocks
117 
118 #define SECOND_OUTPUT_RELATEDNESS_THRESHOLD 0.0f
119 
120 #define SUBADDRESS_LOOKAHEAD_MAJOR 50
121 #define SUBADDRESS_LOOKAHEAD_MINOR 200
122 
123 #define KEY_IMAGE_EXPORT_FILE_MAGIC "Electroneum key image export\002"
124 
125 #define MULTISIG_EXPORT_FILE_MAGIC "Electroneum multisig export\001"
126 
127 #define SEGREGATION_FORK_HEIGHT 99999999
128 #define TESTNET_SEGREGATION_FORK_HEIGHT 99999999
129 #define STAGENET_SEGREGATION_FORK_HEIGHT 99999999
130 #define SEGREGATION_FORK_VICINITY 1500 /* blocks */
131 
132 #define FIRST_REFRESH_GRANULARITY 1024
133 
134 #define GAMMA_SHAPE 19.28
135 #define GAMMA_SCALE (1/1.61)
136 
137 #define DEFAULT_MIN_OUTPUT_COUNT 5
138 #define DEFAULT_MIN_OUTPUT_VALUE (2*COIN)
139 
140 #define OUTPUT_EXPORT_FILE_MAGIC "Electroneum output export\003"
141 
142 static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1";
143 static const std::string MULTISIG_EXTRA_INFO_MAGIC = "MultisigxV1";
144 
145 namespace
146 {
147  std::string get_default_ringdb_path()
148  {
149  boost::filesystem::path dir = tools::get_default_data_dir();
150  // remove .electroneum, replace with .shared-ringdb
151  dir = dir.remove_filename();
152  dir /= ".shared-ringdb";
153  return dir.string();
154  }
155 
156  std::string pack_multisignature_keys(const std::string& prefix, const std::vector<crypto::public_key>& keys, const crypto::secret_key& signer_secret_key)
157  {
158  std::string data;
159  crypto::public_key signer;
160  CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(signer_secret_key, signer), "Failed to derive public spend key");
161  data += std::string((const char *)&signer, sizeof(crypto::public_key));
162 
163  for (const auto &key: keys)
164  {
165  data += std::string((const char *)&key, sizeof(crypto::public_key));
166  }
167 
168  data.resize(data.size() + sizeof(crypto::signature));
169 
171  crypto::cn_fast_hash(data.data(), data.size() - sizeof(crypto::signature), hash);
172  crypto::signature &signature = *(crypto::signature*)&data[data.size() - sizeof(crypto::signature)];
173  crypto::generate_signature(hash, signer, signer_secret_key, signature);
174 
175  return MULTISIG_EXTRA_INFO_MAGIC + tools::base58::encode(data);
176  }
177 
178  std::vector<crypto::public_key> secret_keys_to_public_keys(const std::vector<crypto::secret_key>& keys)
179  {
180  std::vector<crypto::public_key> public_keys;
181  public_keys.reserve(keys.size());
182 
183  std::transform(keys.begin(), keys.end(), std::back_inserter(public_keys), [] (const crypto::secret_key& k) -> crypto::public_key {
185  CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(k, p), "Failed to derive public spend key");
186  return p;
187  });
188 
189  return public_keys;
190  }
191 
192  bool keys_intersect(const std::unordered_set<crypto::public_key>& s1, const std::unordered_set<crypto::public_key>& s2)
193  {
194  if (s1.empty() || s2.empty())
195  return false;
196 
197  for (const auto& e: s1)
198  {
199  if (s2.find(e) != s2.end())
200  return true;
201  }
202 
203  return false;
204  }
205 
206  void add_reason(std::string &reasons, const char *reason)
207  {
208  if (!reasons.empty())
209  reasons += ", ";
210  reasons += reason;
211  }
212 
214  {
215  std::string reason;
216  if (res.low_mixin)
217  add_reason(reason, "bad ring size");
218  if (res.double_spend)
219  add_reason(reason, "double spend");
220  if (res.invalid_input)
221  add_reason(reason, "invalid input");
222  if (res.invalid_output)
223  add_reason(reason, "invalid output");
224  if (res.too_big)
225  add_reason(reason, "too big");
226  if (res.overspend)
227  add_reason(reason, "overspend");
228  if (res.fee_too_low)
229  add_reason(reason, "fee too low");
230  if (res.not_rct)
231  add_reason(reason, "tx is not ringct");
232  if (res.sanity_check_failed)
233  add_reason(reason, "tx sanity check failed");
234  if (res.not_relayed)
235  add_reason(reason, "tx was not relayed");
236  return reason;
237  }
238 }
239 
240 namespace
241 {
242 // Create on-demand to prevent static initialization order fiasco issues.
243 struct options {
244  const command_line::arg_descriptor<std::string> daemon_address = {"daemon-address", tools::wallet2::tr("Use daemon instance at <host>:<port>"), ""};
245  const command_line::arg_descriptor<std::string> daemon_host = {"daemon-host", tools::wallet2::tr("Use daemon instance at host <arg> instead of localhost"), ""};
246  const command_line::arg_descriptor<std::string> proxy = {"proxy", tools::wallet2::tr("[<ip>:]<port> socks proxy to use for daemon connections"), {}, true};
247  const command_line::arg_descriptor<bool> trusted_daemon = {"trusted-daemon", tools::wallet2::tr("Enable commands which rely on a trusted daemon"), false};
248  const command_line::arg_descriptor<bool> untrusted_daemon = {"untrusted-daemon", tools::wallet2::tr("Disable commands which rely on a trusted daemon"), false};
249  const command_line::arg_descriptor<std::string> password = {"password", tools::wallet2::tr("Wallet password (For a wallet without a password use \"\")"), "", true};
250  const command_line::arg_descriptor<std::string> password_file = {"password-file", tools::wallet2::tr("Wallet password file"), "", true};
251  const command_line::arg_descriptor<int> daemon_port = {"daemon-port", tools::wallet2::tr("Use daemon instance at port <arg> instead of 26968"), 0};
252  const command_line::arg_descriptor<std::string> daemon_login = {"daemon-login", tools::wallet2::tr("Specify username[:password] for daemon RPC client"), "", true};
253  const command_line::arg_descriptor<std::string> daemon_ssl = {"daemon-ssl", tools::wallet2::tr("Enable SSL on daemon RPC connections: enabled|disabled|autodetect"), "autodetect"};
254  const command_line::arg_descriptor<std::string> daemon_ssl_private_key = {"daemon-ssl-private-key", tools::wallet2::tr("Path to a PEM format private key"), ""};
255  const command_line::arg_descriptor<std::string> daemon_ssl_certificate = {"daemon-ssl-certificate", tools::wallet2::tr("Path to a PEM format certificate"), ""};
256  const command_line::arg_descriptor<std::string> daemon_ssl_ca_certificates = {"daemon-ssl-ca-certificates", tools::wallet2::tr("Path to file containing concatenated PEM format certificate(s) to replace system CA(s).")};
257  const command_line::arg_descriptor<std::vector<std::string>> daemon_ssl_allowed_fingerprints = {"daemon-ssl-allowed-fingerprints", tools::wallet2::tr("List of valid fingerprints of allowed RPC servers")};
258  const command_line::arg_descriptor<bool> daemon_ssl_allow_any_cert = {"daemon-ssl-allow-any-cert", tools::wallet2::tr("Allow any SSL certificate from the daemon"), false};
259  const command_line::arg_descriptor<bool> daemon_ssl_allow_chained = {"daemon-ssl-allow-chained", tools::wallet2::tr("Allow user (via --daemon-ssl-ca-certificates) chain certificates"), false};
260  const command_line::arg_descriptor<bool> testnet = {"testnet", tools::wallet2::tr("For testnet. Daemon must also be launched with --testnet flag"), false};
261  const command_line::arg_descriptor<bool> stagenet = {"stagenet", tools::wallet2::tr("For stagenet. Daemon must also be launched with --stagenet flag"), false};
262  const command_line::arg_descriptor<uint64_t> fallback_to_pow_checkpoint_height = {"fallback-to-pow-checkpoint-height", tools::wallet2::tr("Warning: This is to set the height for a custom checkpoint in the event of PoW fallback. Do not use in normal circumstances. See docs for details "), 0, false};
263  const command_line::arg_descriptor<std::string> fallback_to_pow_checkpoint_hash = {"fallback-to-pow-checkpoint-hash", tools::wallet2::tr("Warning: This is to set the hash for a custom checkpoint in the event of PoW fallback. Do not use in normal circumstances. See docs for details "), "", false};
265  "shared-ringdb-dir", tools::wallet2::tr("Set shared ring database path"),
266  get_default_ringdb_path(),
267  {{ &testnet, &stagenet }},
268  [](std::array<bool, 2> testnet_stagenet, bool defaulted, std::string val)->std::string {
269  if (testnet_stagenet[0])
270  return (boost::filesystem::path(val) / "testnet").string();
271  else if (testnet_stagenet[1])
272  return (boost::filesystem::path(val) / "stagenet").string();
273  return val;
274  }
275  };
276  const command_line::arg_descriptor<uint64_t> kdf_rounds = {"kdf-rounds", tools::wallet2::tr("Number of rounds for the key derivation function"), 1};
277  const command_line::arg_descriptor<std::string> hw_device = {"hw-device", tools::wallet2::tr("HW device to use"), ""};
278  const command_line::arg_descriptor<std::string> hw_device_derivation_path = {"hw-device-deriv-path", tools::wallet2::tr("HW device wallet derivation path (e.g., SLIP-10)"), ""};
279  const command_line::arg_descriptor<std::string> tx_notify = { "tx-notify" , "Run a program for each new incoming transaction, '%s' will be replaced by the transaction hash" , "" };
280  const command_line::arg_descriptor<bool> no_dns = {"no-dns", tools::wallet2::tr("Do not use DNS"), false};
281  const command_line::arg_descriptor<bool> offline = {"offline", tools::wallet2::tr("Do not connect to a daemon, nor use DNS"), false};
282 
283  const command_line::arg_descriptor<bool> restricted = {"restricted-rpc", tools::wallet2::tr("Restricts to view-only commands"), false};
284  const command_line::arg_descriptor<std::string> data_dir = {"data-dir", tools::wallet2::tr("Path to blockchain db"), ""};
285 };
286 
287 void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file, std::string &mms_file)
288 {
289  keys_file = file_path;
290  wallet_file = file_path;
291  boost::system::error_code e;
292  if(string_tools::get_extension(keys_file) == "keys")
293  {//provided keys file name
294  wallet_file = string_tools::cut_off_extension(wallet_file);
295  }else
296  {//provided wallet file name
297  keys_file += ".keys";
298  }
299  mms_file = file_path + ".mms";
300 }
301 
302 uint64_t calculate_fee(uint64_t fee_per_kb, size_t bytes, uint64_t fee_multiplier)
303 {
304  uint64_t kB = (bytes + 1023) / 1024;
305  return kB * fee_per_kb * fee_multiplier;
306 }
307 
308 uint64_t calculate_fee_from_weight(uint64_t base_fee, uint64_t weight, uint64_t fee_multiplier, uint64_t fee_quantization_mask)
309 {
310  uint64_t fee = weight * base_fee * fee_multiplier;
311  fee = (fee + fee_quantization_mask - 1) / fee_quantization_mask * fee_quantization_mask;
312  return fee;
313 }
314 
315 std::string get_weight_string(size_t weight)
316 {
317  return std::to_string(weight) + " weight";
318 }
319 
320 std::string get_weight_string(const cryptonote::transaction &tx, size_t blob_size)
321 {
322  return get_weight_string(get_transaction_weight(tx, blob_size));
323 }
324 
325 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)
326 {
327  namespace ip = boost::asio::ip;
328 
329  const bool testnet = command_line::get_arg(vm, opts.testnet);
330  const bool stagenet = command_line::get_arg(vm, opts.stagenet);
331  const network_type nettype = testnet ? TESTNET : stagenet ? STAGENET : MAINNET;
332  const uint64_t kdf_rounds = command_line::get_arg(vm, opts.kdf_rounds);
333  THROW_WALLET_EXCEPTION_IF(kdf_rounds == 0, tools::error::wallet_internal_error, "KDF rounds must not be 0");
334 
335  const bool use_proxy = command_line::has_arg(vm, opts.proxy);
336  auto daemon_address = command_line::get_arg(vm, opts.daemon_address);
337  auto daemon_host = command_line::get_arg(vm, opts.daemon_host);
338  auto daemon_port = command_line::get_arg(vm, opts.daemon_port);
339  auto device_name = command_line::get_arg(vm, opts.hw_device);
340  auto device_derivation_path = command_line::get_arg(vm, opts.hw_device_derivation_path);
341  auto daemon_ssl_private_key = command_line::get_arg(vm, opts.daemon_ssl_private_key);
342  auto daemon_ssl_certificate = command_line::get_arg(vm, opts.daemon_ssl_certificate);
343  auto daemon_ssl_ca_file = command_line::get_arg(vm, opts.daemon_ssl_ca_certificates);
344  auto daemon_ssl_allowed_fingerprints = command_line::get_arg(vm, opts.daemon_ssl_allowed_fingerprints);
345  auto daemon_ssl_allow_any_cert = command_line::get_arg(vm, opts.daemon_ssl_allow_any_cert);
346  auto daemon_ssl = command_line::get_arg(vm, opts.daemon_ssl);
347  auto fallback_to_pow_checkpoint_height = command_line::get_arg(vm, opts.fallback_to_pow_checkpoint_height);
348  auto fallback_to_pow_checkpoint_hash = command_line::get_arg(vm, opts.fallback_to_pow_checkpoint_hash);
349 
350  // user specified CA file or fingeprints implies enabled SSL by default
352  if (command_line::get_arg(vm, opts.daemon_ssl_allow_any_cert))
354  else if (!daemon_ssl_ca_file.empty() || !daemon_ssl_allowed_fingerprints.empty())
355  {
356  std::vector<std::vector<uint8_t>> ssl_allowed_fingerprints{ daemon_ssl_allowed_fingerprints.size() };
357  std::transform(daemon_ssl_allowed_fingerprints.begin(), daemon_ssl_allowed_fingerprints.end(), ssl_allowed_fingerprints.begin(), epee::from_hex::vector);
358  for (const auto &fpr: ssl_allowed_fingerprints)
359  {
361  "SHA-256 fingerprint should be " BOOST_PP_STRINGIZE(SSL_FINGERPRINT_SIZE) " bytes long.");
362  }
363 
364  ssl_options = epee::net_utils::ssl_options_t{
365  std::move(ssl_allowed_fingerprints), std::move(daemon_ssl_ca_file)
366  };
367 
368  if (command_line::get_arg(vm, opts.daemon_ssl_allow_chained))
370  }
371 
372  auto data_dir = command_line::get_arg(vm, opts.data_dir);
373 
375  {
377  tools::wallet2::tr("Invalid argument for ") + std::string(opts.daemon_ssl.name));
378  }
379 
381  std::move(daemon_ssl_private_key), std::move(daemon_ssl_certificate)
382  };
383 
384  THROW_WALLET_EXCEPTION_IF(!daemon_address.empty() && !daemon_host.empty() && 0 != daemon_port,
385  tools::error::wallet_internal_error, tools::wallet2::tr("can't specify daemon host or port more than once"));
386 
387  boost::optional<epee::net_utils::http::login> login{};
388  if (command_line::has_arg(vm, opts.daemon_login))
389  {
390  auto parsed = tools::login::parse(
391  command_line::get_arg(vm, opts.daemon_login), false, [password_prompter](bool verify) {
392  return password_prompter("Daemon client password", verify);
393  }
394  );
395  if (!parsed)
396  return nullptr;
397 
398  login.emplace(std::move(parsed->username), std::move(parsed->password).password());
399  }
400 
401  if (daemon_host.empty())
402  daemon_host = "localhost";
403 
404  if (!daemon_port)
405  {
406  daemon_port = get_config(nettype).RPC_DEFAULT_PORT;
407  }
408 
409  if (daemon_address.empty())
410  daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port);
411 
412  {
413  const boost::string_ref real_daemon = boost::string_ref{daemon_address}.substr(0, daemon_address.rfind(':'));
414 
415  /* If SSL or proxy is enabled, then a specific cert, CA or fingerprint must
416  be specified. This is specific to the wallet. */
417  const bool verification_required =
420 
422  verification_required && !ssl_options.has_strong_verification(real_daemon),
424  tools::wallet2::tr("Enabling --") + std::string{use_proxy ? opts.proxy.name : opts.daemon_ssl.name} + tools::wallet2::tr(" requires --") +
425  opts.daemon_ssl_ca_certificates.name + tools::wallet2::tr(" or --") + opts.daemon_ssl_allowed_fingerprints.name + tools::wallet2::tr(" or use of a .onion/.i2p domain")
426  );
427  }
428 
429  boost::asio::ip::tcp::endpoint proxy{};
430  if (use_proxy)
431  {
432  namespace ip = boost::asio::ip;
433 
434  const auto proxy_address = command_line::get_arg(vm, opts.proxy);
435 
436  boost::string_ref proxy_port{proxy_address};
437  boost::string_ref proxy_host = proxy_port.substr(0, proxy_port.rfind(":"));
438  if (proxy_port.size() == proxy_host.size())
439  proxy_host = "127.0.0.1";
440  else
441  proxy_port = proxy_port.substr(proxy_host.size() + 1);
442 
443  uint16_t port_value = 0;
445  !epee::string_tools::get_xtype_from_string(port_value, std::string{proxy_port}),
447  std::string{"Invalid port specified for --"} + opts.proxy.name
448  );
449 
450  boost::system::error_code error{};
451  proxy = ip::tcp::endpoint{ip::address::from_string(std::string{proxy_host}, error), port_value};
452  THROW_WALLET_EXCEPTION_IF(bool(error), tools::error::wallet_internal_error, std::string{"Invalid IP address specified for --"} + opts.proxy.name);
453  }
454 
455  boost::optional<bool> trusted_daemon;
456  if (!command_line::is_arg_defaulted(vm, opts.trusted_daemon) || !command_line::is_arg_defaulted(vm, opts.untrusted_daemon))
457  trusted_daemon = command_line::get_arg(vm, opts.trusted_daemon) && !command_line::get_arg(vm, opts.untrusted_daemon);
458  THROW_WALLET_EXCEPTION_IF(!command_line::is_arg_defaulted(vm, opts.trusted_daemon) && !command_line::is_arg_defaulted(vm, opts.untrusted_daemon),
459  tools::error::wallet_internal_error, tools::wallet2::tr("--trusted-daemon and --untrusted-daemon are both seen, assuming untrusted"));
460 
461  // set --trusted-daemon if local and not overridden
462  if (!trusted_daemon)
463  {
464  try
465  {
466  trusted_daemon = false;
468  {
469  MINFO(tools::wallet2::tr("Daemon is local, assuming trusted"));
470  trusted_daemon = true;
471  }
472  }
473  catch (const std::exception &e) { }
474  }
475 
476  std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(nettype, kdf_rounds, unattended));
477  wallet->init(std::move(daemon_address), std::move(login), std::move(proxy), 0, *trusted_daemon, std::move(ssl_options), std::move(data_dir));
478  // Add the pow-fallback checkpoint if necessary
479  if(fallback_to_pow_checkpoint_hash != "" && fallback_to_pow_checkpoint_height != 0) {
480  wallet->add_checkpoint(fallback_to_pow_checkpoint_height, fallback_to_pow_checkpoint_hash);
481  }
482  boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir);
483  wallet->set_ring_database(ringdb_path.string());
484  wallet->get_message_store().set_options(vm);
485  wallet->device_name(device_name);
486  wallet->device_derivation_path(device_derivation_path);
487 
488  if (command_line::get_arg(vm, opts.no_dns))
489  wallet->enable_dns(false);
490 
491  if (command_line::get_arg(vm, opts.offline))
492  wallet->set_offline();
493 
494  try
495  {
496  if (!command_line::is_arg_defaulted(vm, opts.tx_notify))
497  wallet->set_tx_notify(std::shared_ptr<tools::Notify>(new tools::Notify(command_line::get_arg(vm, opts.tx_notify).c_str())));
498  }
499  catch (const std::exception &e)
500  {
501  MERROR("Failed to parse tx notify spec: " << e.what());
502  }
503 
504  return wallet;
505 }
506 
507 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)
508 {
509  if (command_line::has_arg(vm, opts.password) && command_line::has_arg(vm, opts.password_file))
510  {
511  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("can't specify more than one of --password and --password-file"));
512  }
513 
514  if (command_line::has_arg(vm, opts.password))
515  {
516  return tools::password_container{command_line::get_arg(vm, opts.password)};
517  }
518 
519  if (command_line::has_arg(vm, opts.password_file))
520  {
521  std::string password;
522  bool r = epee::file_io_utils::load_file_to_string(command_line::get_arg(vm, opts.password_file),
523  password);
524  THROW_WALLET_EXCEPTION_IF(!r, tools::error::wallet_internal_error, tools::wallet2::tr("the password file specified could not be read"));
525 
526  // Remove line breaks the user might have inserted
527  boost::trim_right_if(password, boost::is_any_of("\r\n"));
528  return {tools::password_container{std::move(password)}};
529  }
530 
531  THROW_WALLET_EXCEPTION_IF(!password_prompter, tools::error::wallet_internal_error, tools::wallet2::tr("no password specified; use --prompt-for-password to prompt for a password"));
532 
533  return password_prompter(verify ? tools::wallet2::tr("Enter a new password for the wallet") : tools::wallet2::tr("Wallet password"), verify);
534 }
535 
536 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)
537 {
538  const bool testnet = command_line::get_arg(vm, opts.testnet);
539  const bool stagenet = command_line::get_arg(vm, opts.stagenet);
540  const network_type nettype = testnet ? TESTNET : stagenet ? STAGENET : MAINNET;
541 
542  /* GET_FIELD_FROM_JSON_RETURN_ON_ERROR Is a generic macro that can return
543  false. Gcc will coerce this into unique_ptr(nullptr), but clang correctly
544  fails. This large wrapper is for the use of that macro */
545  std::unique_ptr<tools::wallet2> wallet;
546  epee::wipeable_string password;
547  const auto do_generate = [&]() -> bool {
551  return false;
552  }
553 
555  if (json.Parse(buf.c_str()).HasParseError()) {
557  return false;
558  }
559 
560  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, version, unsigned, Uint, true, 0);
561  const int current_version = 1;
562  THROW_WALLET_EXCEPTION_IF(field_version > current_version, tools::error::wallet_internal_error,
563  ((boost::format(tools::wallet2::tr("Version %u too new, we can only grok up to %u")) % field_version % current_version)).str());
564 
566 
567  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, scan_from_height, uint64_t, Uint64, false, 0);
568  const bool recover = true;
569 
570  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, password, std::string, String, false, std::string());
571 
573  crypto::secret_key viewkey;
574  if (field_viewkey_found)
575  {
576  cryptonote::blobdata viewkey_data;
577  if(!epee::string_tools::parse_hexstr_to_binbuff(field_viewkey, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key))
578  {
579  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to parse view key secret key"));
580  }
581  viewkey = *reinterpret_cast<const crypto::secret_key*>(viewkey_data.data());
582  crypto::public_key pkey;
583  if (!crypto::secret_key_to_public_key(viewkey, pkey)) {
584  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify view key secret key"));
585  }
586  }
587 
590  if (field_spendkey_found)
591  {
592  cryptonote::blobdata spendkey_data;
593  if(!epee::string_tools::parse_hexstr_to_binbuff(field_spendkey, spendkey_data) || spendkey_data.size() != sizeof(crypto::secret_key))
594  {
595  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to parse spend key secret key"));
596  }
597  spendkey = *reinterpret_cast<const crypto::secret_key*>(spendkey_data.data());
598  crypto::public_key pkey;
600  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify spend key secret key"));
601  }
602  }
603 
605  std::string old_language;
606  crypto::secret_key recovery_key;
607  bool restore_deterministic_wallet = false;
608  if (field_seed_found)
609  {
610  if (!crypto::ElectrumWords::words_to_bytes(field_seed, recovery_key, old_language))
611  {
612  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("Electrum-style word list failed verification"));
613  }
614  restore_deterministic_wallet = true;
615 
616  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, seed_passphrase, std::string, String, false, std::string());
617  if (field_seed_passphrase_found)
618  {
619  if (!field_seed_passphrase.empty())
620  recovery_key = cryptonote::decrypt_key(recovery_key, field_seed_passphrase);
621  }
622  }
623 
625 
626  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, create_address_file, int, Int, false, false);
627  bool create_address_file = field_create_address_file;
628 
629  // compatibility checks
630  if (!field_seed_found && !field_viewkey_found && !field_spendkey_found)
631  {
632  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("At least one of either an Electrum-style word list, private view key, or private spend key must be specified"));
633  }
634  if (field_seed_found && (field_viewkey_found || field_spendkey_found))
635  {
636  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("Both Electrum-style word list and private key(s) specified"));
637  }
638 
639  // if an address was given, we check keys against it, and deduce the spend
640  // public key if it was not given
641  if (field_address_found)
642  {
644  if(!get_account_address_from_str(info, nettype, field_address))
645  {
647  }
648  if (field_viewkey_found)
649  {
650  crypto::public_key pkey;
651  if (!crypto::secret_key_to_public_key(viewkey, pkey)) {
652  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify view key secret key"));
653  }
654  if (info.address.m_view_public_key != pkey) {
655  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("view key does not match standard address"));
656  }
657  }
658  if (field_spendkey_found)
659  {
660  crypto::public_key pkey;
662  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify spend key secret key"));
663  }
664  if (info.address.m_spend_public_key != pkey) {
665  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("spend key does not match standard address"));
666  }
667  }
668  }
669 
670  const bool deprecated_wallet = restore_deterministic_wallet && ((old_language == crypto::ElectrumWords::old_language_name) ||
673  tools::wallet2::tr("Cannot generate deprecated wallets from JSON"));
674 
675  wallet.reset(make_basic(vm, unattended, opts, password_prompter).release());
676  wallet->set_refresh_from_block_height(field_scan_from_height);
677  wallet->explicit_refresh_from_block_height(field_scan_from_height_found);
678  if (!old_language.empty())
679  wallet->set_seed_language(old_language);
680 
681  try
682  {
683  if (!field_seed.empty())
684  {
685  wallet->generate(field_filename, field_password, recovery_key, recover, false, create_address_file);
686  password = field_password;
687  }
688  else if (field_viewkey.empty() && !field_spendkey.empty())
689  {
690  wallet->generate(field_filename, field_password, spendkey, recover, false, create_address_file);
691  password = field_password;
692  }
693  else
694  {
696  if (!crypto::secret_key_to_public_key(viewkey, address.m_view_public_key)) {
697  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify view key secret key"));
698  }
699 
700  if (field_spendkey.empty())
701  {
702  // if we have an address but no spend key, we can deduce the spend public key
703  // from the address
704  if (field_address_found)
705  {
707  if(!get_account_address_from_str(info, nettype, field_address))
708  {
709  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, std::string(tools::wallet2::tr("failed to parse address: ")) + field_address);
710  }
711  address.m_spend_public_key = info.address.m_spend_public_key;
712  }
713  else
714  {
715  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("Address must be specified in order to create watch-only wallet"));
716  }
717  wallet->generate(field_filename, field_password, address, viewkey, create_address_file);
718  password = field_password;
719  }
720  else
721  {
722  if (!crypto::secret_key_to_public_key(spendkey, address.m_spend_public_key)) {
723  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify spend key secret key"));
724  }
725  wallet->generate(field_filename, field_password, address, spendkey, viewkey, create_address_file);
726  password = field_password;
727  }
728  }
729  }
730  catch (const std::exception& e)
731  {
732  THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, std::string(tools::wallet2::tr("failed to generate new wallet: ")) + e.what());
733  }
734  return true;
735  };
736 
737  if (do_generate())
738  {
739  return {std::move(wallet), tools::password_container(password)};
740  }
741  return {nullptr, tools::password_container{}};
742 }
743 
744 std::string strjoin(const std::vector<size_t> &V, const char *sep)
745 {
746  std::stringstream ss;
747  bool first = true;
748  for (const auto &v: V)
749  {
750  if (!first)
751  ss << sep;
752  ss << std::to_string(v);
753  first = false;
754  }
755  return ss.str();
756 }
757 
758 static bool emplace_or_replace(std::unordered_multimap<crypto::hash, tools::wallet2::pool_payment_details> &container,
760 {
761  auto range = container.equal_range(key);
762  for (auto i = range.first; i != range.second; ++i)
763  {
764  if (i->second.m_pd.m_tx_hash == pd.m_pd.m_tx_hash && i->second.m_pd.m_subaddr_index == pd.m_pd.m_subaddr_index)
765  {
766  i->second = pd;
767  return false;
768  }
769  }
770  container.emplace(key, pd);
771  return true;
772 }
773 
774 void drop_from_short_history(std::list<crypto::hash> &short_chain_history, size_t N)
775 {
776  std::list<crypto::hash>::iterator right;
777  // drop early N off, skipping the genesis block
778  if (short_chain_history.size() > N) {
779  right = short_chain_history.end();
780  std::advance(right,-1);
781  std::list<crypto::hash>::iterator left = right;
782  std::advance(left, -N);
783  short_chain_history.erase(left, right);
784  }
785 }
786 
787 size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof)
788 {
789  size_t size = 0;
790 
791  // tx prefix
792 
793  // first few bytes
794  size += 1 + 6;
795 
796  // vin
797  size += n_inputs * (1+6+(mixin+1)*2+32);
798 
799  // vout
800  size += n_outputs * (6+32);
801 
802  // extra
803  size += extra_size;
804 
805  // rct signatures
806 
807  // type
808  size += 1;
809 
810  // rangeSigs
811  if (bulletproof)
812  {
813  size_t log_padded_outputs = 0;
814  while ((1<<log_padded_outputs) < n_outputs)
815  ++log_padded_outputs;
816  size += (2 * (6 + log_padded_outputs) + 4 + 5) * 32 + 3;
817  }
818  else
819  size += (2*64*32+32+64*32) * n_outputs;
820 
821  // MGs
822  size += n_inputs * (64 * (mixin+1) + 32);
823 
824  // mixRing - not serialized, can be reconstructed
825  /* size += 2 * 32 * (mixin+1) * n_inputs; */
826 
827  // pseudoOuts
828  size += 32 * n_inputs;
829  // ecdhInfo
830  size += 8 * n_outputs;
831  // outPk - only commitment is saved
832  size += 32 * n_outputs;
833  // txnFee
834  size += 4;
835 
836  LOG_PRINT_L2("estimated " << (bulletproof ? "bulletproof" : "borromean") << " rct tx size for " << n_inputs << " inputs with ring size " << (mixin+1) << " and " << n_outputs << " outputs: " << size << " (" << ((32 * n_inputs/*+1*/) + 2 * 32 * (mixin+1) * n_inputs + 32 * n_outputs) << " saved)");
837  return size;
838 }
839 
840 size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof)
841 {
842  if (use_rct)
843  return estimate_rct_tx_size(n_inputs, mixin, n_outputs, extra_size, bulletproof);
844  else
845  return n_inputs * (mixin+1) * APPROXIMATE_INPUT_BYTES + extra_size;
846 }
847 
848 uint64_t estimate_tx_weight(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof)
849 {
850  size_t size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
851  if (use_rct && bulletproof && n_outputs > 2)
852  {
853  const uint64_t bp_base = 368;
854  size_t log_padded_outputs = 2;
855  while ((1<<log_padded_outputs) < n_outputs)
856  ++log_padded_outputs;
857  uint64_t nlr = 2 * (6 + log_padded_outputs);
858  const uint64_t bp_size = 32 * (9 + nlr);
859  const uint64_t bp_clawback = (bp_base * (1<<log_padded_outputs) - bp_size) * 4 / 5;
860  MDEBUG("clawback on size " << size << ": " << bp_clawback);
861  size += bp_clawback;
862  }
863  return size;
864 }
865 
866 uint8_t get_bulletproof_fork()
867 {
868  return 99;
869 }
870 
871 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)
872 {
873  if (use_per_byte_fee)
874  {
875  const size_t estimated_tx_weight = estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
876  return calculate_fee_from_weight(base_fee, estimated_tx_weight, fee_multiplier, fee_quantization_mask);
877  }
878  else
879  {
880  const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
881  return calculate_fee(base_fee, estimated_tx_size, fee_multiplier);
882  }
883 }
884 
885 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)
886 {
887  if (use_per_byte_fee)
888  return calculate_fee_from_weight(base_fee, cryptonote::get_transaction_weight(tx, blob_size), fee_multiplier, fee_quantization_mask);
889  else
890  return calculate_fee(base_fee, blob_size, fee_multiplier);
891 }
892 
893 bool get_short_payment_id(crypto::hash8 &payment_id8, const tools::wallet2::pending_tx &ptx, hw::device &hwdev)
894 {
895  std::vector<tx_extra_field> tx_extra_fields;
896  parse_tx_extra(ptx.tx.extra, tx_extra_fields); // ok if partially parsed
897  cryptonote::tx_extra_nonce extra_nonce;
898  if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
899  {
900  if(get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
901  {
902  if (ptx.dests.empty())
903  {
904  MWARNING("Encrypted payment id found, but no destinations public key, cannot decrypt");
905  return false;
906  }
907  return hwdev.decrypt_payment_id(payment_id8, ptx.dests[0].addr.m_view_public_key, ptx.tx_key);
908  }
909  }
910  return false;
911 }
912 
913 tools::wallet2::tx_construction_data get_construction_data_with_decrypted_short_payment_id(const tools::wallet2::pending_tx &ptx, hw::device &hwdev)
914 {
916  crypto::hash8 payment_id8 = null_hash8;
917  if (get_short_payment_id(payment_id8, ptx, hwdev))
918  {
919  // Remove encrypted
920  remove_field_from_tx_extra(construction_data.extra, typeid(cryptonote::tx_extra_nonce));
921  // Add decrypted
922  std::string extra_nonce;
923  crypto::hash payment_id = null_hash;
924  memcpy(payment_id.data, payment_id8.data, 8); // convert short pid to regular
925  memset(payment_id.data + 8, 0, 24); // merely a sanity check
926 
927  set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
928  THROW_WALLET_EXCEPTION_IF(!add_extra_nonce_to_tx_extra(construction_data.extra, extra_nonce),
929  tools::error::wallet_internal_error, "Failed to add decrypted payment id to tx extra");
930  LOG_PRINT_L1("Decrypted payment ID: " << payment_id);
931  }
932  return construction_data;
933 }
934 
935 uint32_t get_subaddress_clamped_sum(uint32_t idx, uint32_t extra)
936 {
937  static constexpr uint32_t uint32_max = std::numeric_limits<uint32_t>::max();
938  if (idx > uint32_max - extra)
939  return uint32_max;
940  return idx + extra;
941 }
942 
943 static void setup_shim(hw::wallet_shim * shim, tools::wallet2 * wallet)
944 {
945  shim->get_tx_pub_key_from_received_outs = std::bind(&tools::wallet2::get_tx_pub_key_from_received_outs, wallet, std::placeholders::_1);
946 }
947 
948 bool get_pruned_tx(const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::entry &entry, cryptonote::transaction &tx, crypto::hash &tx_hash)
949 {
951 
952  // easy case if we have the whole tx
953  if (!entry.as_hex.empty() || (!entry.prunable_as_hex.empty() && !entry.pruned_as_hex.empty()))
954  {
955  CHECK_AND_ASSERT_MES(epee::string_tools::parse_hexstr_to_binbuff(entry.as_hex.empty() ? entry.pruned_as_hex + entry.prunable_as_hex : entry.as_hex, bd), false, "Failed to parse tx data");
956  CHECK_AND_ASSERT_MES(cryptonote::parse_and_validate_tx_from_blob(bd, tx), false, "Invalid tx data");
957  tx_hash = cryptonote::get_transaction_hash(tx);
958  // if the hash was given, check it matches
959  CHECK_AND_ASSERT_MES(entry.tx_hash.empty() || epee::string_tools::pod_to_hex(tx_hash) == entry.tx_hash, false,
960  "Response claims a different hash than the data yields");
961  return true;
962  }
963  // case of a pruned tx with its prunable data hash
964  if (!entry.pruned_as_hex.empty() && !entry.prunable_hash.empty())
965  {
966  crypto::hash ph;
967  CHECK_AND_ASSERT_MES(epee::string_tools::hex_to_pod(entry.prunable_hash, ph), false, "Failed to parse prunable hash");
968  CHECK_AND_ASSERT_MES(epee::string_tools::parse_hexstr_to_binbuff(entry.pruned_as_hex, bd), false, "Failed to parse pruned data");
969  CHECK_AND_ASSERT_MES(parse_and_validate_tx_base_from_blob(bd, tx), false, "Invalid base tx data");
970  // only v2 txes can calculate their txid after pruned
971  if (bd[0] > 1)
972  {
973  tx_hash = cryptonote::get_pruned_transaction_hash(tx, ph);
974  }
975  else
976  {
977  // for v1, we trust the dameon
978  CHECK_AND_ASSERT_MES(epee::string_tools::hex_to_pod(entry.tx_hash, tx_hash), false, "Failed to parse tx hash");
979  }
980  return true;
981  }
982  return false;
983 }
984 
985  //-----------------------------------------------------------------
986 } //namespace
987 
988 namespace tools
989 {
990 // for now, limit to 30 attempts. TODO: discuss a good number to limit to.
991 const size_t MAX_SPLIT_ATTEMPTS = 30;
992 
993 constexpr const std::chrono::seconds wallet2::rpc_timeout;
994 const char* wallet2::tr(const char* str) { return i18n_translate(str, "tools::wallet2"); }
995 
996 gamma_picker::gamma_picker(const std::vector<uint64_t> &rct_offsets, double shape, double scale):
997  rct_offsets(rct_offsets)
998 {
999  gamma = std::gamma_distribution<double>(shape, scale);
1000  THROW_WALLET_EXCEPTION_IF(rct_offsets.size() <= ETN_DEFAULT_TX_SPENDABLE_AGE_V8, error::wallet_internal_error, "Bad offset calculation");
1001  const size_t blocks_in_a_year = 86400 * 365 / DIFFICULTY_TARGET_V6;
1002  const size_t blocks_to_consider = std::min<size_t>(rct_offsets.size(), blocks_in_a_year);
1003  const size_t outputs_to_consider = rct_offsets.back() - (blocks_to_consider < rct_offsets.size() ? rct_offsets[rct_offsets.size() - blocks_to_consider - 1] : 0);
1004  begin = rct_offsets.data();
1005  end = rct_offsets.data() + rct_offsets.size() - ETN_DEFAULT_TX_SPENDABLE_AGE_V8;
1006  num_rct_outputs = *(end - 1);
1007  THROW_WALLET_EXCEPTION_IF(num_rct_outputs == 0, error::wallet_internal_error, "No rct outputs");
1008  average_output_time = DIFFICULTY_TARGET_V6 * blocks_to_consider / outputs_to_consider; // this assumes constant target over the whole rct range
1009 };
1010 
1011 gamma_picker::gamma_picker(const std::vector<uint64_t> &rct_offsets): gamma_picker(rct_offsets, GAMMA_SHAPE, GAMMA_SCALE) {}
1012 
1014 {
1015  double x = gamma(engine);
1016  x = exp(x);
1017  uint64_t output_index = x / average_output_time;
1018  if (output_index >= num_rct_outputs)
1019  return std::numeric_limits<uint64_t>::max(); // bad pick
1020  output_index = num_rct_outputs - 1 - output_index;
1021 
1022  const uint64_t *it = std::lower_bound(begin, end, output_index);
1023  THROW_WALLET_EXCEPTION_IF(it == end, error::wallet_internal_error, "output_index not found");
1024  uint64_t index = std::distance(begin, it);
1025 
1026  const uint64_t first_rct = index == 0 ? 0 : rct_offsets[index - 1];
1027  const uint64_t n_rct = rct_offsets[index] - first_rct;
1028  if (n_rct == 0)
1029  return std::numeric_limits<uint64_t>::max(); // bad pick
1030  MTRACE("Picking 1/" << n_rct << " in block " << index);
1031  return first_rct + crypto::rand_idx(n_rct);
1032 };
1033 
1034 wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, const boost::optional<tools::password_container> &password):
1035  w(w),
1036  locked(password != boost::none)
1037 {
1038  if (!locked || w.is_unattended() || w.ask_password() != tools::wallet2::AskPasswordToDecrypt || w.watch_only())
1039  {
1040  locked = false;
1041  return;
1042  }
1043  const epee::wipeable_string pass = password->password();
1044  w.generate_chacha_key_from_password(pass, key);
1045  w.decrypt_keys(key);
1046 }
1047 
1049  w(w),
1050  locked(locked)
1051 {
1052  if (!locked)
1053  return;
1054  w.generate_chacha_key_from_password(password, key);
1055  w.decrypt_keys(key);
1056 }
1057 
1059 {
1060  if (!locked)
1061  return;
1062  try { w.encrypt_keys(key); }
1063  catch (...)
1064  {
1065  MERROR("Failed to re-encrypt wallet keys");
1066  // do not propagate through dtor, we'd crash
1067  }
1068 }
1069 
1071 {
1072  if (wallet)
1073  wallet->on_device_button_request(code);
1074 }
1075 
1077 {
1078  if (wallet)
1079  wallet->on_device_button_pressed();
1080 }
1081 
1082 boost::optional<epee::wipeable_string> wallet_device_callback::on_pin_request()
1083 {
1084  if (wallet)
1085  return wallet->on_device_pin_request();
1086  return boost::none;
1087 }
1088 
1089 boost::optional<epee::wipeable_string> wallet_device_callback::on_passphrase_request(bool on_device)
1090 {
1091  if (wallet)
1092  return wallet->on_device_passphrase_request(on_device);
1093  return boost::none;
1094 }
1095 
1097 {
1098  if (wallet)
1099  wallet->on_device_progress(event);
1100 }
1101 
1102 wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
1103  m_multisig_rescan_info(NULL),
1104  m_multisig_rescan_k(NULL),
1105  m_upper_transaction_weight_limit(0),
1106  m_run(true),
1107  m_callback(0),
1108  m_trusted_daemon(false),
1109  m_nettype(nettype),
1110  m_multisig_rounds_passed(0),
1111  m_always_confirm_transfers(true),
1112  m_print_ring_members(false),
1113  m_store_tx_info(true),
1114  m_default_mixin(0),
1115  m_default_priority(0),
1116  m_refresh_type(RefreshOptimizeCoinbase),
1117  m_auto_refresh(true),
1118  m_first_refresh_done(false),
1119  m_refresh_from_block_height(0),
1120  m_explicit_refresh_from_block_height(true),
1121  m_confirm_missing_payment_id(true),
1122  m_confirm_non_default_ring_size(true),
1123  m_ask_password(AskPasswordOnAction),
1124  m_min_output_count(0),
1125  m_min_output_value(0),
1126  m_merge_destinations(false),
1127  m_confirm_backlog(true),
1128  m_confirm_backlog_threshold(0),
1129  m_confirm_export_overwrite(true),
1130  m_auto_low_priority(true),
1131  m_segregate_pre_fork_outputs(true),
1132  m_key_reuse_mitigation2(true),
1133  m_segregation_height(0),
1134  m_ignore_fractional_outputs(true),
1135  m_track_uses(false),
1136  m_setup_background_mining(BackgroundMiningMaybe),
1137  m_is_initialized(false),
1138  m_kdf_rounds(kdf_rounds),
1139  is_old_file_format(false),
1140  m_watch_only(false),
1141  m_multisig(false),
1142  m_multisig_threshold(0),
1143  m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex),
1144  m_account_public_address{crypto::null_pkey, crypto::null_pkey},
1145  m_subaddress_lookahead_major(SUBADDRESS_LOOKAHEAD_MAJOR),
1146  m_subaddress_lookahead_minor(SUBADDRESS_LOOKAHEAD_MINOR),
1147  m_light_wallet(false),
1148  m_light_wallet_scanned_block_height(0),
1149  m_light_wallet_blockchain_height(0),
1150  m_light_wallet_connected(false),
1151  m_light_wallet_balance(0),
1152  m_light_wallet_unlocked_balance(0),
1153  m_original_keys_available(false),
1154  m_message_store(),
1155  m_key_device_type(hw::device::device_type::SOFTWARE),
1156  m_ring_history_saved(false),
1157  m_ringdb(),
1158  m_last_block_reward(0),
1159  m_encrypt_keys_after_refresh(boost::none),
1160  m_unattended(unattended),
1161  m_devices_registered(false),
1162  m_device_last_key_image_sync(0),
1163  m_use_dns(true),
1164  m_offline(false),
1165  m_account_major_offset(0)
1166 {
1167 }
1168 
1170 {
1171 }
1172 
1173 bool wallet2::has_testnet_option(const boost::program_options::variables_map& vm)
1174 {
1175  return command_line::get_arg(vm, options().testnet);
1176 }
1177 
1178 bool wallet2::has_stagenet_option(const boost::program_options::variables_map& vm)
1179 {
1180  return command_line::get_arg(vm, options().stagenet);
1181 }
1182 
1183 std::string wallet2::device_name_option(const boost::program_options::variables_map& vm)
1184 {
1185  return command_line::get_arg(vm, options().hw_device);
1186 }
1187 
1188 std::string wallet2::device_derivation_path_option(const boost::program_options::variables_map &vm)
1189 {
1190  return command_line::get_arg(vm, options().hw_device_derivation_path);
1191 }
1192 
1193 void wallet2::init_options(boost::program_options::options_description& desc_params)
1194 {
1195  const options opts{};
1196  command_line::add_arg(desc_params, opts.daemon_address);
1197  command_line::add_arg(desc_params, opts.daemon_host);
1198  command_line::add_arg(desc_params, opts.proxy);
1199  command_line::add_arg(desc_params, opts.trusted_daemon);
1200  command_line::add_arg(desc_params, opts.untrusted_daemon);
1201  command_line::add_arg(desc_params, opts.password);
1202  command_line::add_arg(desc_params, opts.password_file);
1203  command_line::add_arg(desc_params, opts.daemon_port);
1204  command_line::add_arg(desc_params, opts.daemon_login);
1205  command_line::add_arg(desc_params, opts.daemon_ssl);
1206  command_line::add_arg(desc_params, opts.daemon_ssl_private_key);
1207  command_line::add_arg(desc_params, opts.daemon_ssl_certificate);
1208  command_line::add_arg(desc_params, opts.daemon_ssl_ca_certificates);
1209  command_line::add_arg(desc_params, opts.daemon_ssl_allowed_fingerprints);
1210  command_line::add_arg(desc_params, opts.daemon_ssl_allow_any_cert);
1211  command_line::add_arg(desc_params, opts.daemon_ssl_allow_chained);
1212  command_line::add_arg(desc_params, opts.testnet);
1213  command_line::add_arg(desc_params, opts.stagenet);
1214  command_line::add_arg(desc_params, opts.shared_ringdb_dir);
1215  command_line::add_arg(desc_params, opts.kdf_rounds);
1216  mms::message_store::init_options(desc_params);
1217  command_line::add_arg(desc_params, opts.hw_device);
1218  command_line::add_arg(desc_params, opts.hw_device_derivation_path);
1219  command_line::add_arg(desc_params, opts.tx_notify);
1220  command_line::add_arg(desc_params, opts.no_dns);
1221  command_line::add_arg(desc_params, opts.offline);
1222  command_line::add_arg(desc_params, opts.data_dir);
1223  command_line::add_arg(desc_params, opts.fallback_to_pow_checkpoint_height);
1224  command_line::add_arg(desc_params, opts.fallback_to_pow_checkpoint_hash);
1225 }
1226 
1227 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)
1228 {
1229  const options opts{};
1230  return generate_from_json(json_file, vm, unattended, opts, password_prompter);
1231 }
1232 
1233 std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file(
1234  const boost::program_options::variables_map& vm, bool unattended, const std::string& wallet_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
1235 {
1236  const options opts{};
1237  auto pwd = get_password(vm, opts, password_prompter, false);
1238  if (!pwd)
1239  {
1240  return {nullptr, password_container{}};
1241  }
1242  auto wallet = make_basic(vm, unattended, opts, password_prompter);
1243  if (wallet && !wallet_file.empty())
1244  {
1245  wallet->load(wallet_file, pwd->password());
1246  }
1247  return {std::move(wallet), std::move(*pwd)};
1248 }
1249 
1250 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)
1251 {
1252  const options opts{};
1253  auto pwd = get_password(vm, opts, password_prompter, true);
1254  if (!pwd)
1255  {
1256  return {nullptr, password_container{}};
1257  }
1258  return {make_basic(vm, unattended, opts, password_prompter), std::move(*pwd)};
1259 }
1260 
1261 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)
1262 {
1263  const options opts{};
1264  return make_basic(vm, unattended, opts, password_prompter);
1265 }
1266 
1267 //----------------------------------------------------------------------------------------------------
1268 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)
1269 {
1270  boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex);
1271 
1272  if(m_http_client.is_connected())
1273  m_http_client.disconnect();
1274  m_daemon_address = std::move(daemon_address);
1275  m_daemon_login = std::move(daemon_login);
1276  m_trusted_daemon = trusted_daemon;
1277 
1278  MINFO("setting daemon to " << get_daemon_address());
1279  return m_http_client.set_server(get_daemon_address(), get_daemon_login(), std::move(ssl_options));
1280 }
1281 //----------------------------------------------------------------------------------------------------
1282 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)
1283 {
1284  m_checkpoints.init_default_checkpoints(m_nettype);
1285  m_is_initialized = true;
1286  m_upper_transaction_weight_limit = upper_transaction_weight_limit;
1287 
1288  if (proxy != boost::asio::ip::tcp::endpoint{})
1289  m_http_client.set_connector(net::socks::connector{std::move(proxy)});
1290  return set_daemon(daemon_address, daemon_login, trusted_daemon, std::move(ssl_options));
1291 }
1292 //----------------------------------------------------------------------------------------------------
1294 {
1295  crypto::secret_key second;
1296  keccak((uint8_t *)&get_account().get_keys().m_spend_secret_key, sizeof(crypto::secret_key), (uint8_t *)&second, sizeof(crypto::secret_key));
1297  sc_reduce32((uint8_t *)&second);
1298  return memcmp(second.data,get_account().get_keys().m_view_secret_key.data, sizeof(crypto::secret_key)) == 0;
1299 }
1300 //----------------------------------------------------------------------------------------------------
1301 bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeable_string &passphrase) const
1302 {
1303  bool keys_deterministic = is_deterministic();
1304  if (!keys_deterministic)
1305  {
1306  std::cout << "This is not a deterministic wallet" << std::endl;
1307  return false;
1308  }
1309  if (seed_language.empty())
1310  {
1311  std::cout << "seed_language not set" << std::endl;
1312  return false;
1313  }
1314 
1316  if (!passphrase.empty())
1317  key = cryptonote::encrypt_key(key, passphrase);
1318  if (!crypto::ElectrumWords::bytes_to_words(key, electrum_words, seed_language))
1319  {
1320  std::cout << "Failed to create seed from key for language: " << seed_language << std::endl;
1321  return false;
1322  }
1323 
1324  return true;
1325 }
1326 //----------------------------------------------------------------------------------------------------
1327 bool wallet2::get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase, bool raw) const
1328 {
1329  bool ready;
1330  uint32_t threshold, total;
1331  if (!multisig(&ready, &threshold, &total))
1332  {
1333  std::cout << "This is not a multisig wallet" << std::endl;
1334  return false;
1335  }
1336  if (!ready)
1337  {
1338  std::cout << "This multisig wallet is not yet finalized" << std::endl;
1339  return false;
1340  }
1341  if (!raw && seed_language.empty())
1342  {
1343  std::cout << "seed_language not set" << std::endl;
1344  return false;
1345  }
1346 
1347  crypto::secret_key skey;
1348  crypto::public_key pkey;
1349  const account_keys &keys = get_account().get_keys();
1350  epee::wipeable_string data;
1351  data.append((const char*)&threshold, sizeof(uint32_t));
1352  data.append((const char*)&total, sizeof(uint32_t));
1353  skey = keys.m_spend_secret_key;
1354  data.append((const char*)&skey, sizeof(skey));
1356  data.append((const char*)&pkey, sizeof(pkey));
1357  skey = keys.m_view_secret_key;
1358  data.append((const char*)&skey, sizeof(skey));
1360  data.append((const char*)&pkey, sizeof(pkey));
1361  for (const auto &skey: keys.m_multisig_keys)
1362  data.append((const char*)&skey, sizeof(skey));
1363  for (const auto &signer: m_multisig_signers)
1364  data.append((const char*)&signer, sizeof(signer));
1365 
1366  if (!passphrase.empty())
1367  {
1369  crypto::cn_slow_hash(passphrase.data(), passphrase.size(), (crypto::hash&)key);
1370  sc_reduce32((unsigned char*)key.data);
1371  data = encrypt(data, key, true);
1372  }
1373 
1374  if (raw)
1375  {
1376  seed = epee::to_hex::wipeable_string({(const unsigned char*)data.data(), data.size()});
1377  }
1378  else
1379  {
1380  if (!crypto::ElectrumWords::bytes_to_words(data.data(), data.size(), seed, seed_language))
1381  {
1382  std::cout << "Failed to encode seed";
1383  return false;
1384  }
1385  }
1386 
1387  return true;
1388 }
1389 //----------------------------------------------------------------------------------------------------
1391 {
1392  bool r = true;
1393  hw::device &hwdev = lookup_device(m_device_name);
1394  hwdev.set_name(m_device_name);
1395  hwdev.set_network_type(m_nettype);
1396  hwdev.set_derivation_path(m_device_derivation_path);
1397  hwdev.set_callback(get_device_callback());
1398  r = hwdev.init();
1399  if (!r){
1400  MERROR("Could not init device");
1401  return false;
1402  }
1403 
1404  r = hwdev.connect();
1405  if (!r){
1406  MERROR("Could not connect to the device");
1407  return false;
1408  }
1409 
1410  m_account.set_device(hwdev);
1411  return true;
1412 }
1413 //----------------------------------------------------------------------------------------------------
1418 {
1419  return seed_language;
1420 }
1426 {
1427  seed_language = language;
1428 }
1429 //----------------------------------------------------------------------------------------------------
1431 {
1432  hw::device &hwdev = m_account.get_device();
1433  cryptonote::subaddress_index index2 = {index.major + (index.major != 0 ? m_account_major_offset : 0), index.minor};
1434  return hwdev.get_subaddress(m_account.get_keys(), index2);
1435 }
1436 //----------------------------------------------------------------------------------------------------
1437 boost::optional<cryptonote::subaddress_index> wallet2::get_subaddress_index(const cryptonote::account_public_address& address) const
1438 {
1439  auto index = m_subaddresses.find(address.m_spend_public_key);
1440  if (index == m_subaddresses.end())
1441  return boost::none;
1442  return index->second;
1443 }
1444 //----------------------------------------------------------------------------------------------------
1446 {
1447  hw::device &hwdev = m_account.get_device();
1448  cryptonote::subaddress_index index2 = {index.major + (index.major != 0 ? m_account_major_offset : 0), index.minor};
1449  return hwdev.get_subaddress_spend_public_key(m_account.get_keys(), index2);
1450 }
1451 //----------------------------------------------------------------------------------------------------
1453 {
1455  return cryptonote::get_account_address_as_str(m_nettype, !index.is_zero(), address);
1456 }
1457 //----------------------------------------------------------------------------------------------------
1459 {
1460  return cryptonote::get_account_integrated_address_as_str(m_nettype, get_address(), payment_id);
1461 }
1462 //----------------------------------------------------------------------------------------------------
1463 void wallet2::add_subaddress_account(const std::string& label, const bool update_account_tags)
1464 {
1466  expand_subaddresses({index_major, 0}, update_account_tags);
1467  m_subaddress_labels[index_major][0] = label;
1468 }
1469 //----------------------------------------------------------------------------------------------------
1470 void wallet2::add_subaddress(uint32_t index_major, const std::string& label)
1471 {
1472  THROW_WALLET_EXCEPTION_IF(index_major >= m_subaddress_labels.size(), error::account_index_outofbound);
1473  uint32_t index_minor = (uint32_t)get_num_subaddresses(index_major);
1474  expand_subaddresses({index_major, index_minor});
1475  m_subaddress_labels[index_major][index_minor] = label;
1476 }
1477 //----------------------------------------------------------------------------------------------------
1478 void wallet2::expand_subaddresses(const cryptonote::subaddress_index& index, const bool update_account_tags)
1479 {
1480  hw::device &hwdev = m_account.get_device();
1481  if (m_subaddress_labels.size() <= index.major)
1482  {
1483  // add new accounts
1485  const uint32_t major_end = get_subaddress_clamped_sum(index.major, m_subaddress_lookahead_major);
1486  for (index2.major = m_subaddress_labels.size(); index2.major < major_end; ++index2.major)
1487  {
1488  const uint32_t end = get_subaddress_clamped_sum((index2.major == index.major ? index.minor : 0), m_subaddress_lookahead_minor);
1489  const std::vector<crypto::public_key> pkeys = hwdev.get_subaddress_spend_public_keys(m_account.get_keys(), index2.major + (index2.major != 0 ? m_account_major_offset : 0), 0, end);
1490  for (index2.minor = 0; index2.minor < end; ++index2.minor)
1491  {
1492  const crypto::public_key &D = pkeys[index2.minor];
1493  m_subaddresses[D] = index2;
1494  }
1495  }
1496  m_subaddress_labels.resize(index.major + 1, {"Untitled account"});
1497  m_subaddress_labels[index.major].resize(index.minor + 1);
1498 
1499  if(update_account_tags)
1500  get_account_tags();
1501  }
1502  else if (m_subaddress_labels[index.major].size() <= index.minor)
1503  {
1504  // add new subaddresses
1505  const uint32_t end = get_subaddress_clamped_sum(index.minor, m_subaddress_lookahead_minor);
1506  const uint32_t begin = m_subaddress_labels[index.major].size();
1507  cryptonote::subaddress_index index2 = {index.major, begin};
1508  const std::vector<crypto::public_key> pkeys = hwdev.get_subaddress_spend_public_keys(m_account.get_keys(), index2.major + (index2.major != 0 ? m_account_major_offset : 0), index2.minor, end);
1509  for (; index2.minor < end; ++index2.minor)
1510  {
1511  const crypto::public_key &D = pkeys[index2.minor - begin];
1512  m_subaddresses[D] = index2;
1513  }
1514  m_subaddress_labels[index.major].resize(index.minor + 1);
1515  }
1516 }
1517 //----------------------------------------------------------------------------------------------------
1519 {
1520  if (index.major >= m_subaddress_labels.size() || index.minor >= m_subaddress_labels[index.major].size())
1521  {
1522  MERROR("Subaddress label doesn't exist");
1523  return "";
1524  }
1525  return m_subaddress_labels[index.major][index.minor];
1526 }
1527 //----------------------------------------------------------------------------------------------------
1529 {
1530  THROW_WALLET_EXCEPTION_IF(index.major >= m_subaddress_labels.size(), error::account_index_outofbound);
1531  THROW_WALLET_EXCEPTION_IF(index.minor >= m_subaddress_labels[index.major].size(), error::address_index_outofbound);
1532  m_subaddress_labels[index.major][index.minor] = label;
1533 }
1534 //----------------------------------------------------------------------------------------------------
1535 void wallet2::set_subaddress_lookahead(size_t major, size_t minor)
1536 {
1537  THROW_WALLET_EXCEPTION_IF(major > 0xffffffff, error::wallet_internal_error, "Subaddress major lookahead is too large");
1538  THROW_WALLET_EXCEPTION_IF(minor > 0xffffffff, error::wallet_internal_error, "Subaddress minor lookahead is too large");
1539  m_subaddress_lookahead_major = major;
1540  m_subaddress_lookahead_minor = minor;
1541 }
1542 //----------------------------------------------------------------------------------------------------
1547 {
1548  return is_old_file_format;
1549 }
1550 //----------------------------------------------------------------------------------------------------
1551 void wallet2::set_spent(size_t idx, uint64_t height, bool public_out)
1552 {
1553  transfer_details &td = m_transfers[idx];
1554 
1555  if(public_out){
1556  LOG_PRINT_L2("Setting SPENT at "
1557  << height << ": chainstate index " << td.m_txid << ":" << td.m_internal_output_index
1558  << ", amount " << print_etn(td.m_amount));
1559  }else{
1560  LOG_PRINT_L2("Setting SPENT at "
1561  << height << ": ki " << td.m_key_image
1562  << ", amount " << print_etn(td.m_amount));
1563  }
1564 
1565  td.m_spent = true;
1566  td.m_spent_height = height;
1567 }
1568 //----------------------------------------------------------------------------------------------------
1569 void wallet2::set_unspent(size_t idx, bool public_out)
1570 {
1571  transfer_details &td = m_transfers[idx];
1572 
1573  if(public_out){
1574  LOG_PRINT_L2("Setting UNSPENT: chainstate index "
1575  << td.m_txid << ":"<< td.m_internal_output_index << ", amount " << print_etn(td.m_amount));
1576  }else{
1577  LOG_PRINT_L2("Setting UNSPENT: ki " << td.m_key_image << ", amount " << print_etn(td.m_amount));
1578  }
1579 
1580  td.m_spent = false;
1581  td.m_spent_height = 0;
1582 }
1583 //----------------------------------------------------------------------------------------------------
1584 void wallet2::freeze(size_t idx)
1585 {
1586  CHECK_AND_ASSERT_THROW_MES(idx < m_transfers.size(), "Invalid transfer_details index");
1587  transfer_details &td = m_transfers[idx];
1588  td.m_frozen = true;
1589 }
1590 //----------------------------------------------------------------------------------------------------
1591 void wallet2::thaw(size_t idx)
1592 {
1593  CHECK_AND_ASSERT_THROW_MES(idx < m_transfers.size(), "Invalid transfer_details index");
1594  transfer_details &td = m_transfers[idx];
1595  td.m_frozen = false;
1596 }
1597 //----------------------------------------------------------------------------------------------------
1598 bool wallet2::frozen(size_t idx) const
1599 {
1600  CHECK_AND_ASSERT_THROW_MES(idx < m_transfers.size(), "Invalid transfer_details index");
1601  const transfer_details &td = m_transfers[idx];
1602  return td.m_frozen;
1603 }
1604 //----------------------------------------------------------------------------------------------------
1606 {
1608 }
1609 //----------------------------------------------------------------------------------------------------
1611 {
1613 }
1614 //----------------------------------------------------------------------------------------------------
1615 bool wallet2::frozen(const crypto::key_image &ki) const
1616 {
1617  return frozen(get_transfer_details(ki));
1618 }
1619 //----------------------------------------------------------------------------------------------------
1620 size_t wallet2::get_transfer_details(const crypto::key_image &ki) const
1621 {
1622  for (size_t idx = 0; idx < m_transfers.size(); ++idx)
1623  {
1624  const transfer_details &td = m_transfers[idx];
1625  if (td.m_key_image_known && td.m_key_image == ki)
1626  return idx;
1627  }
1628  CHECK_AND_ASSERT_THROW_MES(false, "Key image not found");
1629 }
1630 //----------------------------------------------------------------------------------------------------
1631 bool wallet2::frozen(const transfer_details &td) const
1632 {
1633  return td.m_frozen;
1634 }
1635 //----------------------------------------------------------------------------------------------------
1636 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
1637 {
1638  hw::device &hwdev = m_account.get_device();
1639  boost::unique_lock<hw::device> hwdev_lock (hwdev);
1641  if (o.target.type() != typeid(txout_to_key) && o.target.type() != typeid(txout_to_key_public))
1642  {
1643  tx_scan_info.error = true;
1644  LOG_ERROR("wrong type id in transaction out");
1645  return;
1646  }
1647  if(o.target.type() != typeid(txout_to_key_public)) {
1648  tx_scan_info.received = is_out_to_acc_precomp(m_subaddresses, boost::get<txout_to_key>(o.target).key, derivation,
1649  additional_derivations, i, hwdev);
1650  }else{
1651  //only assign subaddress recipient if view key also matches too as we now spend with combined keys (a+b) and we wont be
1652  // doing key image related checks later to check if we can really spend the out (ie checking view key match by proxy)
1653  auto out_address = boost::get<txout_to_key_public>(o.target).address;
1654  auto receive_info = cryptonote::is_out_to_acc_precomp_public(m_subaddresses, out_address);
1655  tx_scan_info.received =
1656  (receive_info == boost::none) ?
1657  (receive_info) :
1658  get_subaddress(receive_info->index).m_view_public_key == out_address.m_view_public_key ?
1659  receive_info : boost::none; //todo: refactor with function pointers
1660  }
1661  if(tx_scan_info.received)
1662  {
1663  tx_scan_info.etn_transfered = o.amount; // may be 0 for ringct outputs
1664  }
1665  else
1666  {
1667  tx_scan_info.etn_transfered = 0;
1668  }
1669  tx_scan_info.error = false;
1670 }
1671 //----------------------------------------------------------------------------------------------------
1672 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
1673 {
1674  // if(we're not pointing at a pre cached data member || we're attempting to process a receive entry before it's populated from cache thread)
1675  if (!is_out_data || i >= is_out_data->received.size())
1676  return check_acc_out_precomp(o, derivation, additional_derivations, i, tx_scan_info);
1677 
1678  tx_scan_info.received = is_out_data->received[i];
1679  if(tx_scan_info.received)
1680  {
1681  tx_scan_info.etn_transfered = o.amount; // may be 0 for ringct outputs
1682  }
1683  else
1684  {
1685  tx_scan_info.etn_transfered = 0;
1686  }
1687  tx_scan_info.error = false;
1688 }
1689 //----------------------------------------------------------------------------------------------------
1690 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
1691 {
1692  tx_scan_info.received = boost::none;
1693  if (already_seen)
1694  return;
1695  check_acc_out_precomp(o, derivation, additional_derivations, i, is_out_data, tx_scan_info);
1696  if (tx_scan_info.received)
1697  already_seen = true;
1698 }
1699 //----------------------------------------------------------------------------------------------------
1700 static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &derivation, unsigned int i, rct::key & mask, hw::device &hwdev)
1701 {
1702  crypto::secret_key scalar1;
1703  hwdev.derivation_to_scalar(derivation, i, scalar1);
1704  try
1705  {
1706  switch (rv.type)
1707  {
1708  case rct::RCTTypeSimple:
1711  return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask, hwdev);
1712  case rct::RCTTypeFull:
1713  return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask, hwdev);
1714  default:
1715  LOG_ERROR("Unsupported rct type: " << rv.type);
1716  return 0;
1717  }
1718  }
1719  catch (const std::exception &e)
1720  {
1721  LOG_ERROR("Failed to decode input " << i);
1722  return 0;
1723  }
1724 }
1725 //----------------------------------------------------------------------------------------------------
1726 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)
1727 {
1728  THROW_WALLET_EXCEPTION_IF(i >= tx.vout.size(), error::wallet_internal_error, "Invalid vout index");
1729 
1730  // if keys are encrypted, ask for password
1731  if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only && !m_multisig_rescan_k)
1732  {
1733  static critical_section password_lock;
1734  CRITICAL_REGION_LOCAL(password_lock);
1735  if (!m_encrypt_keys_after_refresh)
1736  {
1737  boost::optional<epee::wipeable_string> pwd = m_callback->on_get_password(pool ? "output found in pool" : "output received");
1738  THROW_WALLET_EXCEPTION_IF(!pwd, error::password_needed, tr("Password is needed to compute key image for incoming etn"));
1739  THROW_WALLET_EXCEPTION_IF(!verify_password(*pwd), error::password_needed, tr("Invalid password: password is needed to compute key image for incoming etn"));
1740  decrypt_keys(*pwd);
1741  m_encrypt_keys_after_refresh = *pwd;
1742  }
1743  }
1744 
1745  if (m_multisig)
1746  {
1747  tx_scan_info.in_ephemeral.pub = boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key;
1748  tx_scan_info.in_ephemeral.sec = crypto::null_skey;
1749  tx_scan_info.ki = rct::rct2ki(rct::zero());
1750  }
1751  else if (tx.version == 1)
1752  { //calculate the key image as if we were going to spend this output
1753  bool r = cryptonote::generate_key_image_helper_precomp(m_account.get_keys(), boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key, tx_scan_info.received->derivation, i, tx_scan_info.received->index, tx_scan_info.in_ephemeral, tx_scan_info.ki, m_account.get_device(), m_account_major_offset);
1754  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
1755  THROW_WALLET_EXCEPTION_IF(tx_scan_info.in_ephemeral.pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key,
1756  error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
1757  }
1758 
1759  THROW_WALLET_EXCEPTION_IF(std::find(outs.begin(), outs.end(), i) != outs.end(), error::wallet_internal_error, "Same output cannot be added twice");
1760  //we should never enter this unless it's an rct output
1761  if (tx_scan_info.etn_transfered == 0 && !miner_tx)
1762  {
1763  tx_scan_info.etn_transfered = tools::decodeRct(tx.rct_signatures, tx_scan_info.received->derivation, i, tx_scan_info.mask, m_account.get_device());
1764  }
1765  if (tx_scan_info.etn_transfered == 0)
1766  {
1767  MERROR("Invalid output amount, skipping");
1768  tx_scan_info.error = true;
1769  return;
1770  }
1771  outs.push_back(i);
1772  THROW_WALLET_EXCEPTION_IF(tx_etn_got_in_outs[tx_scan_info.received->index] >= std::numeric_limits<uint64_t>::max() - tx_scan_info.etn_transfered,
1773  error::wallet_internal_error, "Overflow in received amounts");
1774  //index here is a subaddress index (major & minor)
1775  tx_etn_got_in_outs[tx_scan_info.received->index] += tx_scan_info.etn_transfered;
1776  tx_scan_info.amount = tx_scan_info.etn_transfered;
1777  ++num_vouts_received;
1778 }
1779 //----------------------------------------------------------------------------------------------------
1780 void wallet2::cache_tx_data(const cryptonote::transaction& tx, const crypto::hash &txid, tx_cache_data &tx_cache_data) const
1781 {
1782  if(!parse_tx_extra(tx.extra, tx_cache_data.tx_extra_fields))
1783  {
1784  // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key
1785  LOG_PRINT_L0("Transaction extra has unsupported format: " << txid);
1786  if (tx_cache_data.tx_extra_fields.empty())
1787  return;
1788  }
1789 
1790 
1791  // Don't try to extract tx public key if tx has no ouputs
1792  const bool is_miner = tx.vin.size() == 1 && tx.vin[0].type() == typeid(cryptonote::txin_gen);
1793  if (!is_miner || m_refresh_type != RefreshType::RefreshNoCoinbase)
1794  {
1795  const size_t rec_size = is_miner && m_refresh_type == RefreshType::RefreshOptimizeCoinbase ? 1 : tx.vout.size();
1796  if (!tx.vout.empty())
1797  {
1798  const std::vector<boost::optional<cryptonote::subaddress_receive_info>> rec(rec_size, boost::none);
1799  if(tx.version > 1){
1800  tx_cache_data.public_outs.push_back({crypto::null_pkey, {}, rec});
1801  return;
1802  }
1803 
1804  // if tx.vout is not empty, we loop through all tx pubkeys
1805  tx_extra_pub_key pub_key_field;
1806  size_t pk_index = 0;
1807  while (find_tx_extra_field_by_type(tx_cache_data.tx_extra_fields, pub_key_field, pk_index++))
1808  tx_cache_data.primary.push_back({pub_key_field.pub_key, {}, rec});
1809 
1810  // additional tx pubkeys and derivations for multi-destination transfers involving one or more subaddresses
1811  tx_extra_additional_pub_keys additional_tx_pub_keys;
1812  if (find_tx_extra_field_by_type(tx_cache_data.tx_extra_fields, additional_tx_pub_keys))
1813  {
1814  for (size_t i = 0; i < additional_tx_pub_keys.data.size(); ++i)
1815  tx_cache_data.additional.push_back({additional_tx_pub_keys.data[i], {}, {}});
1816  }
1817  }
1818  }
1819 }
1820 //----------------------------------------------------------------------------------------------------
1821 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)
1822 {
1823  PERF_TIMER(process_new_transaction);
1824  // In this function, tx (probably) only contains the base information
1825  // (that is, the prunable stuff may or may not be included)
1826  if (!miner_tx && !pool)
1827  process_unconfirmed(txid, tx, height);
1828  std::unordered_map<cryptonote::subaddress_index, uint64_t> tx_etn_got_in_outs; // per receiving subaddress index
1829  crypto::public_key tx_pub_key = null_pkey;
1830  bool notify = false;
1831 
1832  std::vector<tx_extra_field> local_tx_extra_fields;
1833  if (tx_cache_data.tx_extra_fields.empty())
1834  {
1835  if(!parse_tx_extra(tx.extra, local_tx_extra_fields))
1836  {
1837  // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key
1838  LOG_PRINT_L0("Transaction extra has unsupported format: " << txid);
1839  }
1840  }
1841  const std::vector<tx_extra_field> &tx_extra_fields = tx_cache_data.tx_extra_fields.empty() ? local_tx_extra_fields : tx_cache_data.tx_extra_fields;
1842 
1843  // Don't try to extract tx public key if tx has no ouputs
1844  size_t pk_index = 0; //this is an index for searching for pubkeys
1845  std::vector<tx_scan_info_t> tx_scan_info(tx.vout.size());
1846  std::deque<bool> output_found(tx.vout.size(), false);
1847  uint64_t total_received_1 = 0;
1848 
1849  while (!tx.vout.empty()) {
1850  std::vector<size_t> outs;
1851 
1852  // if tx.vout is not empty, we loop through all tx pubkeys
1853  tx_extra_pub_key pub_key_field;
1854  if (tx.version == 1) {
1855  if (!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, pk_index++)) {
1856  if (pk_index > 1) // we should find we hit this afer one iteration for v1 transactions
1857  break;
1858  LOG_PRINT_L0("Public key wasn't found in the transaction extra. Skipping transaction " << txid);
1859  if (0 != m_callback)
1860  m_callback->on_skip_transaction(height, txid, tx);
1861  break;
1862  }
1863  if (!tx_cache_data.primary.empty()) {
1864  THROW_WALLET_EXCEPTION_IF(tx_cache_data.primary.size() < pk_index ||
1865  pub_key_field.pub_key != tx_cache_data.primary[pk_index - 1].pkey,
1866  error::wallet_internal_error, "tx_cache_data is out of sync");
1867  }
1868  }
1869  int num_vouts_received = 0;
1870  tx_pub_key = pub_key_field.pub_key;
1873  const cryptonote::account_keys &keys = m_account.get_keys();
1874  crypto::key_derivation derivation;
1875 
1876  std::vector<crypto::key_derivation> additional_derivations;
1877  tx_extra_additional_pub_keys additional_tx_pub_keys;
1878 
1879  const wallet2::is_out_data *is_out_data_ptr = NULL; //will point to pre-cached tx data if data is available
1880 
1881  if (tx.version == 1) {
1882  // THIS IF/ELSE IS PURELY PROCESSING DERIVATIONS (DIFFIE H SHARED SECRETS aR1....aRN) FOR V1 TX
1883  if (tx_cache_data.primary.empty()) {
1884  hw::device &hwdev = m_account.get_device();
1885  boost::unique_lock<hw::device> hwdev_lock(hwdev);
1886  hw::reset_mode rst(hwdev);
1887 
1889  if (!hwdev.generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation)) {
1890  MWARNING("Failed to generate key derivation from tx pubkey in " << txid << ", skipping");
1891  static_assert(sizeof(derivation) == sizeof(rct::key),
1892  "Mismatched sizes of key_derivation and rct::key");
1893  memcpy(&derivation, rct::identity().bytes, sizeof(derivation));
1894  }
1895 
1896  if (pk_index == 1) {
1897  // additional tx pubkeys and derivations for multi-destination transfers involving one or more subaddresses
1898  if (find_tx_extra_field_by_type(tx_extra_fields, additional_tx_pub_keys)) {
1899  for (size_t i = 0; i < additional_tx_pub_keys.data.size(); ++i) {
1900  additional_derivations.push_back({});
1901  if (!hwdev.generate_key_derivation(additional_tx_pub_keys.data[i],
1902  keys.m_view_secret_key,
1903  additional_derivations.back())) {
1904  MWARNING("Failed to generate key derivation from additional tx pubkey in " << txid
1905  << ", skipping");
1906  memcpy(&additional_derivations.back(), rct::identity().bytes,
1907  sizeof(crypto::key_derivation));
1908  }
1909  }
1910  }
1911  }
1912  } else {
1913  THROW_WALLET_EXCEPTION_IF(pk_index - 1 >= tx_cache_data.primary.size(),
1914  error::wallet_internal_error, "pk_index out of range of tx_cache_data");
1915  is_out_data_ptr = &tx_cache_data.primary[pk_index - 1];
1916  derivation = tx_cache_data.primary[pk_index - 1].derivation;
1917  if (pk_index == 1) {
1918  for (size_t n = 0; n < tx_cache_data.additional.size(); ++n) {
1919  additional_tx_pub_keys.data.push_back(tx_cache_data.additional[n].pkey);
1920  additional_derivations.push_back(tx_cache_data.additional[n].derivation);
1921  }
1922  }
1923  }
1924  }
1925  // END OF DERIVATIONS PROCESSING (V1 ONLY)
1926  // NOW WE BEGIN TO CHECK THE OUTS //
1927 
1928  //if prior precomp have built the cache, then set is out_data_ptr. Otherwise later thread (check_acc_out_precomp) will find the info itself
1929  if (tx.version > 1 && !tx_cache_data.public_outs.empty()) {
1930  is_out_data_ptr = &tx_cache_data.public_outs[0];
1931  }
1932 
1933  // IGNORE COINBASE
1934  if (miner_tx && m_refresh_type == RefreshNoCoinbase) {
1935  // assume coinbase isn't for us
1936  }
1937  // PROCESS COINBASE
1938  else if (miner_tx && m_refresh_type == RefreshOptimizeCoinbase) {
1939  //put amount in the scan info this time and check output it correct type... before (process_parsed_blocks/geniod) we only precomputed whether we owned an output or not.
1940  //both tx_scan_info and output_found are populated INSIDE the precomp function only
1941  check_acc_out_precomp_once(tx.vout[0], derivation, additional_derivations, 0, is_out_data_ptr,
1942  tx_scan_info[0], output_found[0]); //is the miner tx ours?
1943  THROW_WALLET_EXCEPTION_IF(tx_scan_info[0].error, error::acc_outs_lookup_error, tx, tx_pub_key,
1944  m_account.get_keys());
1945 
1946  // this assumes that the miner tx pays a single address
1947  if (tx_scan_info[0].received) {
1948  // process the other outs from that miner tx. the first one was already checked
1949  for (size_t i = 1; i < tx.vout.size(); ++i) {
1950  tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp_once, this, std::cref(tx.vout[i]),
1951  std::cref(derivation), std::cref(additional_derivations), i,
1952  std::cref(is_out_data_ptr), std::ref(tx_scan_info[i]),
1953  std::ref(output_found[i])), true);
1954  }
1955  waiter.wait(&tpool);
1956  // then scan all outputs from 0
1957  hw::device &hwdev = m_account.get_device();
1958  boost::unique_lock<hw::device> hwdev_lock(hwdev);
1959  hwdev.set_mode(hw::device::NONE);
1960  for (size_t i = 0; i < tx.vout.size(); ++i) {
1961  THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key,
1962  m_account.get_keys());
1963  if (tx_scan_info[i].received) //at this point we are only scanning the entried that we marked as received in precomp
1964  {
1965  if (tx.version == 1) {
1966  hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key,
1967  additional_tx_pub_keys.data, derivation, additional_derivations);
1968  }
1969  scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_etn_got_in_outs,
1970  outs, pool);
1971  }
1972  }
1973  }
1974  }
1975  // PROCESS SINGLE NON COINBASE TX (IF THEY EXIST) WITH THREADS IF THERE IS MORE THAN ONE OUT AND MULTITHREADING IS PERMITTED
1976  else if (tx.vout.size() > 1 && tools::threadpool::getInstance().get_max_concurrency() > 1 && !is_out_data_ptr) {
1977  for (size_t i = 0; i < tx.vout.size(); ++i) {
1978  tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp_once, this, std::cref(tx.vout[i]),
1979  std::cref(derivation), std::cref(additional_derivations), i,
1980  std::cref(is_out_data_ptr), std::ref(tx_scan_info[i]),
1981  std::ref(output_found[i])), true);
1982  }
1983  waiter.wait(&tpool);
1984 
1985  hw::device &hwdev = m_account.get_device();
1986  boost::unique_lock<hw::device> hwdev_lock(hwdev);
1987  hwdev.set_mode(hw::device::NONE);
1988  for (size_t i = 0; i < tx.vout.size(); ++i) {
1989  THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key,
1990  m_account.get_keys());
1991  if (tx_scan_info[i].received) //at this point we are only scanning the entried that we marked as received in precomp
1992  {
1993  // todo: 4.0.0.0 ledger code only
1994  if (tx.version == 1) {
1995  hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key,
1996  additional_tx_pub_keys.data,
1997  derivation, additional_derivations);
1998  }
1999  scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_etn_got_in_outs,
2000  outs, pool);
2001  }
2002  }
2003  }
2004  // IF ONLY ONE OUT OR MULTITHREADING ISN'T ENABLED, PROCESS SINGLE TX NORMALLY
2005  else {
2006  for (size_t i = 0; i < tx.vout.size(); ++i) {
2007  check_acc_out_precomp_once(tx.vout[i], derivation, additional_derivations, i, is_out_data_ptr,
2008  tx_scan_info[i], output_found[i]);
2009  THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key,
2010  m_account.get_keys());
2011  if (tx_scan_info[i].received) //at this point we are only scanning the entried that we marked as received in precomp
2012  {
2013  hw::device &hwdev = m_account.get_device();
2014  boost::unique_lock<hw::device> hwdev_lock(hwdev);
2015  hwdev.set_mode(hw::device::NONE);
2016  if (tx.version == 1) {
2017  hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key,
2018  additional_tx_pub_keys.data,
2019  derivation, additional_derivations);
2020  }
2021  scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_etn_got_in_outs,
2022  outs, pool);
2023  }
2024  }
2025  }
2026  if (tx.version == 1){
2027  if (!outs.empty() && num_vouts_received > 0) { // we will loop over outs below, which is just the index
2028  //good news - got etn! take care about it
2029  //usually we have only one transfer for user in transaction
2030  if (!pool) {
2031  THROW_WALLET_EXCEPTION_IF(tx.vout.size() != o_indices.size(), error::wallet_internal_error,
2032  "transactions outputs size=" + std::to_string(tx.vout.size()) +
2033  " not match with daemon response size=" + std::to_string(o_indices.size()));
2034  }
2035 
2036  for (const size_t o: outs) {
2037  THROW_WALLET_EXCEPTION_IF(tx.vout.size() <= o, error::wallet_internal_error,
2038  "wrong out in transaction: internal index=" +
2039  std::to_string(o) + ", total_outs=" + std::to_string(tx.vout.size()));
2040 
2041  auto kit = m_pub_keys.find(tx_scan_info[o].in_ephemeral.pub);
2042  //stealth address already exists in a transfer entry or we have more pubkeys than transfers for some unkown reason
2043  THROW_WALLET_EXCEPTION_IF(kit != m_pub_keys.end() && kit->second >= m_transfers.size(),
2044  error::wallet_internal_error,
2045  std::string("Unexpected transfer index from public key: ")
2046  + "got " +
2047  (kit == m_pub_keys.end() ? "<none>" : boost::lexical_cast<std::string>(
2048  kit->second))
2049  + ", m_transfers.size() is " +
2050  boost::lexical_cast<std::string>(m_transfers.size()));
2051  if (kit == m_pub_keys.end()) { //typical
2052  uint64_t amount = tx.vout[o].amount ? tx.vout[o].amount : tx_scan_info[o].amount;
2053  if (!pool) {
2054  m_transfers.push_back(boost::value_initialized<transfer_details>());
2055  transfer_details &td = m_transfers.back();
2056  td.m_block_height = height;
2057  td.m_internal_output_index = o;
2058  td.m_global_output_index = o_indices[o];
2059  td.m_tx = (const cryptonote::transaction_prefix &) tx;
2060  td.m_txid = txid;
2061  td.m_key_image = tx_scan_info[o].ki;
2062  td.m_key_image_known = !m_watch_only && !m_multisig;
2063  if (!td.m_key_image_known) {
2064  // we might have cold signed, and have a mapping to key images
2065  std::unordered_map<crypto::public_key, crypto::key_image>::const_iterator i = m_cold_key_images.find(
2066  tx_scan_info[o].in_ephemeral.pub);
2067  if (i != m_cold_key_images.end()) {
2068  td.m_key_image = i->second;
2069  td.m_key_image_known = true;
2070  }
2071  }
2072  if (m_watch_only) {
2073  // for view wallets, that flag means "we want to request it"
2074  td.m_key_image_request = true;
2075  } else {
2076  td.m_key_image_request = false;
2077  }
2078  td.m_key_image_partial = m_multisig;
2079  td.m_amount = amount;
2080  td.m_pk_index = pk_index - 1;
2081  td.m_subaddr_index = tx_scan_info[o].received->index;
2082  expand_subaddresses(tx_scan_info[o].received->index);
2083 
2084  //TODO: Public
2085  td.m_mask = rct::identity();
2086  td.m_rct = false;
2087 
2088  td.m_frozen = false;
2089  set_unspent(m_transfers.size() - 1);
2090  if (td.m_key_image_known)
2091  m_key_images[td.m_key_image] = m_transfers.size() - 1;
2092  m_pub_keys[tx_scan_info[o].in_ephemeral.pub] = m_transfers.size() - 1;
2093  if (output_tracker_cache)
2094  (*output_tracker_cache).first[std::make_pair(tx.vout[o].amount, td.m_global_output_index)] =
2095  m_transfers.size() - 1;
2096  if (m_multisig) {
2097  THROW_WALLET_EXCEPTION_IF(!m_multisig_rescan_k && m_multisig_rescan_info,
2098  error::wallet_internal_error, "NULL m_multisig_rescan_k");
2099  if (m_multisig_rescan_info &&
2100  m_multisig_rescan_info->front().size() >= m_transfers.size())
2101  update_multisig_rescan_info(*m_multisig_rescan_k, *m_multisig_rescan_info,
2102  m_transfers.size() - 1);
2103  }
2104  LOG_PRINT_L0("Received ETN: " << print_etn(td.amount()) << ", with tx: " << txid);
2105  if (0 != m_callback)
2106  m_callback->on_etn_received(height, txid, tx, td.m_amount, td.m_subaddr_index,
2107  td.m_tx.unlock_time);
2108  }
2109  total_received_1 += amount;
2110  notify = true;
2111  } else if (m_transfers[kit->second].m_spent ||
2112  // if weve seen this stealth before, check if it's spent or if the amount is larger or equal than the one we scanned for
2113  m_transfers[kit->second].amount() >= tx_scan_info[o].amount) {
2114  LOG_ERROR("Public key " << epee::string_tools::pod_to_hex(kit->first)
2115  << " from received " << print_etn(tx_scan_info[o].amount)
2116  << " output already exists with "
2117  << (m_transfers[kit->second].m_spent ? "spent" : "unspent") << " "
2118  << print_etn(m_transfers[kit->second].amount()) << " in tx "
2119  << m_transfers[kit->second].m_txid << ", received output ignored");
2121  tx_etn_got_in_outs[tx_scan_info[o].received->index] < tx_scan_info[o].amount,
2122  error::wallet_internal_error, "Unexpected values of new and old outputs");
2123  tx_etn_got_in_outs[tx_scan_info[o].received->index] -= tx_scan_info[o].amount;
2124  } else { //otherwise, we might still have this stealth on file, but we found an out to the same stealth with a greater amount during scan.... therefore swap with file version
2125  LOG_ERROR("Public key " << epee::string_tools::pod_to_hex(kit->first)
2126  << " from received " << print_etn(tx_scan_info[o].amount)
2127  << " output already exists with "
2128  << print_etn(m_transfers[kit->second].amount())
2129  << ", replacing with new output");
2130  // The new larger output replaced a previous smaller one
2132  tx_etn_got_in_outs[tx_scan_info[o].received->index] < tx_scan_info[o].amount,
2133  error::wallet_internal_error, "Unexpected values of new and old outputs");
2134  THROW_WALLET_EXCEPTION_IF(m_transfers[kit->second].amount() > tx_scan_info[o].amount,
2135  error::wallet_internal_error,
2136  "Unexpected values of new and old outputs");
2137  tx_etn_got_in_outs[tx_scan_info[o].received->index] -= m_transfers[kit->second].amount();
2138 
2139  uint64_t amount = tx.vout[o].amount ? tx.vout[o].amount : tx_scan_info[o].amount;
2140  uint64_t extra_amount = amount - m_transfers[kit->second].amount();
2141  if (!pool) {
2142  transfer_details &td = m_transfers[kit->second];
2143  td.m_block_height = height;
2144  td.m_internal_output_index = o;
2145  td.m_global_output_index = o_indices[o];
2146  td.m_tx = (const cryptonote::transaction_prefix &) tx;
2147  td.m_txid = txid;
2148  td.m_amount = amount;
2149  td.m_pk_index = pk_index - 1;
2150  td.m_subaddr_index = tx_scan_info[o].received->index;
2151  expand_subaddresses(tx_scan_info[o].received->index);
2152 
2153  //TODO: Public
2154  td.m_mask = rct::identity();
2155  td.m_rct = false;
2156 
2157  if (output_tracker_cache)
2158  (*output_tracker_cache).first[std::make_pair(tx.vout[o].amount,
2159  td.m_global_output_index)] = kit->second;
2160  if (m_multisig) {
2161  THROW_WALLET_EXCEPTION_IF(!m_multisig_rescan_k && m_multisig_rescan_info,
2162  error::wallet_internal_error, "NULL m_multisig_rescan_k");
2163  if (m_multisig_rescan_info &&
2164  m_multisig_rescan_info->front().size() >= m_transfers.size())
2165  update_multisig_rescan_info(*m_multisig_rescan_k, *m_multisig_rescan_info,
2166  m_transfers.size() - 1);
2167  }
2168  THROW_WALLET_EXCEPTION_IF(td.get_public_key() != tx_scan_info[o].in_ephemeral.pub,
2169  error::wallet_internal_error, "Inconsistent public keys");
2170  THROW_WALLET_EXCEPTION_IF(td.m_spent, error::wallet_internal_error,
2171  "Inconsistent spent status");
2172 
2173  LOG_PRINT_L1("Received ETN: " << print_etn(td.amount()) << ", with tx: " << txid);
2174  if (0 != m_callback)
2175  m_callback->on_etn_received(height, txid, tx, td.m_amount, td.m_subaddr_index,
2176  td.m_tx.unlock_time);
2177  }
2178  total_received_1 += extra_amount;
2179  notify = true;
2180  }
2181  }
2182  }
2183  } //end of v1 tx outs processing
2184  else { //process v2+ tx outs
2185  if (!outs.empty() && num_vouts_received > 0) { // we will loop over outs below, which is just the index
2186  //good news - got etn! take care about it
2187  //usually we have only one transfer for user in transaction
2188  for (const size_t o: outs) {
2189  THROW_WALLET_EXCEPTION_IF(tx.vout.size() <= o, error::wallet_internal_error,
2190  "wrong out in transaction: internal index=" +
2191  std::to_string(o) + ", total_outs=" + std::to_string(tx.vout.size()));
2192 
2193  auto kit = m_chainstate_indexes.find(std::make_pair(txid, o));
2194  // Chainstate index already exists in a transfer entry or we have more chainstate indexes than transfers for some unkown reason
2195  THROW_WALLET_EXCEPTION_IF(kit != m_chainstate_indexes.end() && kit->second >= m_transfers.size(),
2196  error::wallet_internal_error,
2197  std::string("Unexpected transfer index from chainstate index: ")
2198  + "got " +
2199  (kit == m_chainstate_indexes.end() ? "<none>" : boost::lexical_cast<std::string>(
2200  kit->second))
2201  + ", m_transfers.size() is " +
2202  boost::lexical_cast<std::string>(m_transfers.size()));
2203  if (kit == m_chainstate_indexes.end()) { //typical
2204  uint64_t amount = tx.vout[o].amount ? tx.vout[o].amount : tx_scan_info[o].amount;
2205  if (!pool) {
2206  m_transfers.push_back(boost::value_initialized<transfer_details>());
2207  transfer_details &td = m_transfers.back();
2208  td.m_block_height = height;
2209  td.m_internal_output_index = o;
2210  td.m_global_output_index = std::numeric_limits<uint64_t>::max();
2211  td.m_tx = (const cryptonote::transaction_prefix &) tx;
2212  td.m_txid = txid;
2213  td.m_key_image = boost::value_initialized<crypto::key_image>();
2214  td.m_key_image_known = false;
2215  td.m_key_image_partial = false;
2216  td.m_amount = amount;
2217  td.m_pk_index = pk_index - 1;
2218  td.m_subaddr_index = tx_scan_info[o].received->index;
2219  expand_subaddresses(tx_scan_info[o].received->index);
2220  td.m_mask = rct::identity();
2221  td.m_rct = false;
2222  td.m_frozen = false;
2223  set_unspent(m_transfers.size() - 1, true);
2224  m_chainstate_indexes[std::make_pair(txid, o)] = m_transfers.size() - 1;
2225  if (output_tracker_cache) {
2226  std::array<char, 32> transaction_id;
2227  std::copy(std::begin(td.m_txid.data), std::end(td.m_txid.data), transaction_id.begin());
2228  (*output_tracker_cache).second[std::make_pair(transaction_id, td.m_internal_output_index)] =
2229  m_transfers.size() - 1;
2230  }
2231  if (m_multisig) {
2232  THROW_WALLET_EXCEPTION_IF(!m_multisig_rescan_k && m_multisig_rescan_info,
2233  error::wallet_internal_error, "NULL m_multisig_rescan_k");
2234  if (m_multisig_rescan_info &&
2235  m_multisig_rescan_info->front().size() >= m_transfers.size())
2236  update_multisig_rescan_info(*m_multisig_rescan_k, *m_multisig_rescan_info,
2237  m_transfers.size() - 1);
2238  }
2239  LOG_PRINT_L0("Received ETN: " << print_etn(td.amount()) << ", with tx: " << txid);
2240  if (0 != m_callback)
2241  m_callback->on_etn_received(height, txid, tx, td.m_amount, td.m_subaddr_index,
2242  td.m_tx.unlock_time);
2243  }
2244  total_received_1 += amount;
2245  notify = true;
2246  } else if (m_transfers[kit->second].m_spent ||
2247  // if weve seen this chainstate index before, check if it's spent or if the amount is larger or equal than the one we scanned for
2248  m_transfers[kit->second].amount() >= tx_scan_info[o].amount) {
2249  LOG_ERROR("Chainstate index " << epee::string_tools::pod_to_hex(kit->first.first)
2250  << ":" << kit->first.second
2251  << " from received " << print_etn(tx_scan_info[o].amount)
2252  << " output already exists with "
2253  << (m_transfers[kit->second].m_spent ? "spent" : "unspent") << " "
2254  << print_etn(m_transfers[kit->second].amount()) << " in tx "
2255  << m_transfers[kit->second].m_txid << ", received output ignored");
2257  tx_etn_got_in_outs[tx_scan_info[o].received->index] < tx_scan_info[o].amount,
2258  error::wallet_internal_error, "Unexpected values of new and old outputs");
2259  tx_etn_got_in_outs[tx_scan_info[o].received->index] -= tx_scan_info[o].amount;
2260  } else { //otherwise, we might still have this chainstate index on file, but we found an out to the same stealth with a greater amount during scan.... therefore swap with file version
2261  LOG_ERROR("Chainstate index " << epee::string_tools::pod_to_hex(kit->first.first)
2262  << ":" << kit->first.second
2263  << " from received " << print_etn(tx_scan_info[o].amount)
2264  << " output already exists with "
2265  << print_etn(m_transfers[kit->second].amount())
2266  << ", replacing with new output");
2267  // The new larger output replaced a previous smaller one
2269  tx_etn_got_in_outs[tx_scan_info[o].received->index] < tx_scan_info[o].amount,
2270  error::wallet_internal_error, "Unexpected values of new and old outputs");
2271  THROW_WALLET_EXCEPTION_IF(m_transfers[kit->second].amount() > tx_scan_info[o].amount,
2272  error::wallet_internal_error,
2273  "Unexpected values of new and old outputs");
2274  tx_etn_got_in_outs[tx_scan_info[o].received->index] -= m_transfers[kit->second].amount();
2275 
2276  uint64_t amount = tx.vout[o].amount ? tx.vout[o].amount : tx_scan_info[o].amount;
2277  uint64_t extra_amount = amount - m_transfers[kit->second].amount();
2278  if (!pool) {
2279  transfer_details &td = m_transfers[kit->second];
2280  td.m_block_height = height;
2281  td.m_internal_output_index = o;
2282  td.m_global_output_index = std::numeric_limits<uint64_t>::max();
2283  td.m_tx = (const cryptonote::transaction_prefix &) tx;
2284  td.m_txid = txid;
2285  td.m_amount = amount;
2286  td.m_pk_index = pk_index - 1;
2287  td.m_subaddr_index = tx_scan_info[o].received->index;
2288  expand_subaddresses(tx_scan_info[o].received->index);
2289  td.m_mask = rct::identity();
2290  td.m_rct = false;
2291 
2292  if (output_tracker_cache) {
2293  std::array<char, 32> transaction_id;
2294  std::copy(std::begin(td.m_txid.data), std::end(td.m_txid.data), transaction_id.begin());
2295  (*output_tracker_cache).second[std::make_pair(transaction_id,
2296  td.m_internal_output_index)] = kit->second;
2297  }
2298  if (m_multisig) {
2299  THROW_WALLET_EXCEPTION_IF(!m_multisig_rescan_k && m_multisig_rescan_info,
2300  error::wallet_internal_error, "NULL m_multisig_rescan_k");
2301  if (m_multisig_rescan_info &&
2302  m_multisig_rescan_info->front().size() >= m_transfers.size())
2303  update_multisig_rescan_info(*m_multisig_rescan_k, *m_multisig_rescan_info,
2304  m_transfers.size() - 1);
2305  }
2306  THROW_WALLET_EXCEPTION_IF(td.get_chainstate_index() != std::make_pair(txid, o),
2307  error::wallet_internal_error, "Inconsistent public keys");
2308  THROW_WALLET_EXCEPTION_IF(td.m_spent, error::wallet_internal_error,
2309  "Inconsistent spent status");
2310 
2311  LOG_PRINT_L1("Received ETN: " << print_etn(td.amount()) << ", with tx: " << txid);
2312  if (0 != m_callback)
2313  m_callback->on_etn_received(height, txid, tx, td.m_amount, td.m_subaddr_index,
2314  td.m_tx.unlock_time);
2315  }
2316  total_received_1 += extra_amount;
2317  notify = true;
2318  }
2319  }
2320  }
2321  break; // we don't have to iterate again for v2+ outs becuase there aren't multiple pubkeys (see pk index)
2322  } //end of v2+ outs processing
2323  } // end of all outs processing
2324 
2325  uint64_t tx_etn_spent_in_ins = 0;
2326  // The line below is equivalent to "boost::optional<uint32_t> subaddr_account;", but avoids the GCC warning: ‘*((void*)& subaddr_account +4)’ may be used uninitialized in this function
2327  // It's a GCC bug with boost::optional, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47679
2328  auto subaddr_account ([]()->boost::optional<uint32_t> {return boost::none;}());
2329  std::set<uint32_t> subaddr_indices;
2330  // check all outputs for spending (compare key images)
2331  for(auto& in: tx.vin)
2332  {
2333  if(tx.version < 3) { // we still use old txin_to_key for migration transactions (v2)
2334  if (in.type() != typeid(cryptonote::txin_to_key))
2335  continue;
2336  const cryptonote::txin_to_key &in_to_key = boost::get<cryptonote::txin_to_key>(in);
2337  auto it = m_key_images.find(in_to_key.k_image);
2338  if (it != m_key_images.end()) //these are UNspent key images
2339  {
2340  transfer_details &td = m_transfers[it->second];
2341  uint64_t amount = in_to_key.amount;
2342  if (amount > 0) {
2343  if (amount != td.amount()) {
2344  MERROR("Inconsistent amount in tx input: got " << print_etn(amount) <<
2345  ", expected " << print_etn(td.amount()));
2346  // this means:
2347  // 1) the same output pub key was used as destination multiple times,
2348  // 2) the wallet set the highest amount among them to transfer_details::m_amount, and
2349  // 3) the wallet somehow spent that output with an amount smaller than the above amount, causing inconsistency
2350  td.m_amount = amount;
2351  }
2352  } else {
2353  amount = td.amount();
2354  }
2355  tx_etn_spent_in_ins += amount;
2356  if (subaddr_account && *subaddr_account != td.m_subaddr_index.major)
2357  LOG_ERROR(
2358  "spent funds are from different subaddress accounts; count of incoming/outgoing payments will be incorrect");
2359  subaddr_account = td.m_subaddr_index.major;
2360  subaddr_indices.insert(td.m_subaddr_index.minor);
2361  if (!pool) {
2362  LOG_PRINT_L1("Spent ETN: " << print_etn(amount) << ", with tx: " << txid);
2363  set_spent(it->second, height);
2364  if (0 != m_callback)
2365  m_callback->on_etn_spent(height, txid, tx, amount, tx, td.m_subaddr_index);
2366  }
2367  }
2368 
2369  if (!pool && m_track_uses) {
2370  PERF_TIMER(track_uses);
2371  const uint64_t amount = in_to_key.amount; //amount to check against transfer details
2372  std::vector<uint64_t> offsets = cryptonote::relative_output_offsets_to_absolute(
2373  in_to_key.key_offsets); //todo: 4.0.0.0
2374  if (output_tracker_cache) {
2375  for (uint64_t offset: offsets) {
2376  const std::map<std::pair<uint64_t, uint64_t>, size_t>::const_iterator i = output_tracker_cache->first.find(
2377  std::make_pair(amount, offset));
2378  if (i != output_tracker_cache->first.end()) {
2379  size_t idx = i->second;
2380  THROW_WALLET_EXCEPTION_IF(idx >= m_transfers.size(), error::wallet_internal_error,
2381  "Output tracker cache index out of range");
2382  m_transfers[idx].m_uses.push_back(std::make_pair(height, txid));
2383  }
2384  }
2385  } else {
2386  //essentially the long way of doing it without a cache - loop over all m_transfers to find a match
2387  for (transfer_details &td: m_transfers) {
2388  if (amount != td.m_amount) //need to check against the amounts in the list of transfer details to find the matching amount and global out index
2389  continue;
2390  for (uint64_t offset: offsets)
2391  if (offset == td.m_global_output_index)
2392  td.m_uses.push_back(std::make_pair(height, txid));
2393  }
2394  }
2395  }
2396  }else{ // Public inputs (v3)
2397  if (in.type() != typeid(cryptonote::txin_to_key_public))
2398  continue;
2399  const cryptonote::txin_to_key_public &in_to_key_public = boost::get<cryptonote::txin_to_key_public>(in);
2400  auto it = m_chainstate_indexes.find(std::make_pair(in_to_key_public.tx_hash, in_to_key_public.relative_offset));
2401  if (it != m_chainstate_indexes.end()) //these are UNspent chainstate indexes
2402  {
2403  transfer_details &td = m_transfers[it->second];
2404  uint64_t amount = in_to_key_public.amount; // here we're just grabbing the amount of the input from m_transfers
2405  if (amount > 0) {
2406  if (amount != td.amount()) {
2407  MERROR("Inconsistent amount in tx input: got " << print_etn(amount) <<
2408  ", expected " << print_etn(td.amount()));
2409  // this means:
2410  // 1) the same chainstate index was used as destination multiple times,
2411  // 2) the wallet set the highest amount among them to transfer_details::m_amount, and
2412  // 3) the wallet somehow spent that output with an amount smaller than the above amount, causing inconsistency
2413  td.m_amount = amount;
2414  }
2415  } else {
2416  amount = td.amount();
2417  }
2418  tx_etn_spent_in_ins += amount;
2419  if (subaddr_account && *subaddr_account != td.m_subaddr_index.major)
2420  LOG_ERROR(
2421  "spent funds are from different subaddress accounts; count of incoming/outgoing payments will be incorrect");
2422  subaddr_account = td.m_subaddr_index.major;
2423  subaddr_indices.insert(td.m_subaddr_index.minor);
2424  if (!pool) {
2425  LOG_PRINT_L1("Spent ETN: " << print_etn(amount) << ", with tx: " << txid);
2426  set_spent(it->second, height, true);
2427  if (0 != m_callback)
2428  m_callback->on_etn_spent(height, txid, tx, amount, tx, td.m_subaddr_index);
2429  }
2430  }
2431 
2432  if (!pool && m_track_uses) {
2433  PERF_TIMER(track_uses);
2434  const uint64_t amount = in_to_key_public.amount;
2435  if (output_tracker_cache) {
2436  std::array<char, 32> transaction_id;
2437  std::copy(std::begin(in_to_key_public.tx_hash.data), std::end(in_to_key_public.tx_hash.data), transaction_id.begin());
2438  const std::map<std::pair<std::array<char, 32>, size_t>, size_t>::const_iterator i = output_tracker_cache->second.find(
2439  std::make_pair(transaction_id, in_to_key_public.relative_offset));
2440  if (i != output_tracker_cache->second.end()) {
2441  size_t idx = i->second;
2442  THROW_WALLET_EXCEPTION_IF(idx >= m_transfers.size(), error::wallet_internal_error,
2443  "Output tracker cache index out of range");
2444  m_transfers[idx].m_uses.push_back(std::make_pair(height, txid));
2445  }
2446  } else
2447  for (transfer_details &td: m_transfers) {
2448  if (in_to_key_public.tx_hash != td.m_txid)
2449  continue;
2450  if (in_to_key_public.relative_offset == td.m_internal_output_index)
2451  td.m_uses.push_back(std::make_pair(height, txid));
2452  }
2453  }
2454  }
2455  }
2456 
2457  uint64_t fee;
2458  if(miner_tx){
2459  fee = 0;
2460  }else{
2461  uint64_t tx_etn_total_inputs;
2462  get_inputs_etn_amount(tx, tx_etn_total_inputs);
2463  fee = tx_etn_total_inputs - get_outs_etn_amount(tx); //it doesn't matter if we personally spent ins, fee is alwasys total ins minus total outs
2464  }
2465 
2466  if (tx_etn_spent_in_ins > 0 && !pool)
2467  {
2468  // only used for v1. refactor later
2469  uint64_t self_received = std::accumulate<decltype(tx_etn_got_in_outs.begin()), uint64_t>(tx_etn_got_in_outs.begin(), tx_etn_got_in_outs.end(), 0,
2470  [&subaddr_account] (uint64_t acc, const std::pair<cryptonote::subaddress_index, uint64_t>& p)
2471  {
2472  return acc + (p.first.major == *subaddr_account ? p.second : 0);
2473  });
2474  process_outgoing(txid, tx, height, ts, tx_etn_spent_in_ins, self_received, *subaddr_account, subaddr_indices);
2475  // if sending to yourself at the same subaddress account, set the outgoing payment amount to 0 so that it's less confusing
2476  if (tx_etn_spent_in_ins == self_received + fee)
2477  {
2478  auto i = m_confirmed_txs.find(txid);
2479  THROW_WALLET_EXCEPTION_IF(i == m_confirmed_txs.end(), error::wallet_internal_error,
2480  "confirmed tx wasn't found: " + string_tools::pod_to_hex(txid));
2481  if(tx.version == 1)
2482  i->second.m_change = self_received;
2483  }
2484  }
2485 
2486  // remove change sent to the spending subaddress account from the list of received funds
2487  uint64_t sub_change = 0;
2488  for (auto i = tx_etn_got_in_outs.begin(); i != tx_etn_got_in_outs.end();)
2489  {
2490  if (subaddr_account && i->first.major == *subaddr_account)
2491  {
2492  sub_change += i->second;
2493  i = tx_etn_got_in_outs.erase(i);
2494  }
2495  else
2496  ++i;
2497  }
2498 
2499  // create payment_details for each incoming transfer to a subaddress index
2500  if (tx_etn_got_in_outs.size() > 0)
2501  {
2502  tx_extra_nonce extra_nonce;
2503  crypto::hash payment_id = null_hash;
2504  if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
2505  {
2506  crypto::hash8 payment_id8 = null_hash8;
2507  if(get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
2508  {
2509  // We got a payment ID to go with this tx
2510  LOG_PRINT_L2("Found encrypted payment ID: " << payment_id8);
2511  MINFO("Consider using subaddresses instead of encrypted payment IDs");
2512  if (tx_pub_key != null_pkey)
2513  {
2514  if (!m_account.get_device().decrypt_payment_id(payment_id8, tx_pub_key, m_account.get_keys().m_view_secret_key))
2515  {
2516  LOG_PRINT_L0("Failed to decrypt payment ID: " << payment_id8);
2517  }
2518  else
2519  {
2520  // put the 64 bit decrypted payment id in the first 8 bytes
2521  memcpy(payment_id.data, payment_id8.data, 8);
2522  // rest is already 0, but guard against code changes above
2523  memset(payment_id.data + 8, 0, 24);
2524  LOG_PRINT_L2(" payment ID: " << payment_id);
2525  }
2526  }
2527  else
2528  {
2529  LOG_PRINT_L1("No public key found in tx, unable to decrypt payment id");
2530  }
2531  }
2532  else if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
2533  {
2534  LOG_PRINT_L2("Found unencrypted payment ID: " << payment_id);
2535  MWARNING("Found unencrypted payment ID: these are bad for privacy, consider using subaddresses instead");
2536  }
2537  }
2538 
2539  uint64_t total_received_2 = sub_change;
2540  for (const auto& i : tx_etn_got_in_outs)
2541  total_received_2 += i.second;
2542  if (total_received_1 != total_received_2)
2543  {
2544  const el::Level level = el::Level::Warning;
2545  MCLOG_RED(level, "global", "**********************************************************************");
2546  MCLOG_RED(level, "global", "Consistency failure in amounts received");
2547  MCLOG_RED(level, "global", "Check transaction " << txid);
2548  MCLOG_RED(level, "global", "**********************************************************************");
2549  exit(1);
2550  return;
2551  }
2552 
2553  bool all_same = true;
2554  for (const auto& i : tx_etn_got_in_outs)
2555  {
2556  payment_details payment;
2557  payment.m_tx_hash = txid;
2558  payment.m_fee = fee;
2559  payment.m_amount = i.second;
2560  payment.m_block_height = height;
2561  payment.m_unlock_time = tx.unlock_time;
2562  payment.m_timestamp = ts;
2563  payment.m_coinbase = miner_tx;
2564  payment.m_subaddr_index = i.first;
2565  if (pool) {
2566  if (emplace_or_replace(m_unconfirmed_payments, payment_id, pool_payment_details{payment, double_spend_seen, nonexistent_utxo_seen}))
2567  all_same = false;
2568  if (0 != m_callback)
2569  m_callback->on_unconfirmed_etn_received(height, txid, tx, payment.m_amount, payment.m_subaddr_index);
2570  }
2571  else
2572  m_payments.emplace(payment_id, payment);
2573  LOG_PRINT_L2("Payment found in " << (pool ? "pool" : "block") << ": " << payment_id << " / " << payment.m_tx_hash << " / " << payment.m_amount);
2574  }
2575 
2576  // if it's a pool tx and we already had it, don't notify again
2577  if (pool && all_same)
2578  notify = false;
2579  }
2580 
2581  if (notify)
2582  {
2583  std::shared_ptr<tools::Notify> tx_notify = m_tx_notify;
2584  if (tx_notify)
2585  tx_notify->notify("%s", epee::string_tools::pod_to_hex(txid).c_str(), NULL);
2586  }
2587 }
2588 //----------------------------------------------------------------------------------------------------
2589 void wallet2::process_unconfirmed(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height)
2590 {
2591  if (m_unconfirmed_txs.empty())
2592  return;
2593 
2594  auto unconf_it = m_unconfirmed_txs.find(txid);
2595  if(unconf_it != m_unconfirmed_txs.end()) {
2596  if (store_tx_info()) {
2597  try {
2598  m_confirmed_txs.insert(std::make_pair(txid, confirmed_transfer_details(unconf_it->second, height)));
2599  }
2600  catch (...) {
2601  // can fail if the tx has unexpected input types
2602  LOG_PRINT_L0("Failed to add outgoing transaction to confirmed transaction map");
2603  }
2604  }
2605  m_unconfirmed_txs.erase(unconf_it);
2606  }
2607 }
2608 //----------------------------------------------------------------------------------------------------
2609 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)
2610 {
2611  std::pair<std::unordered_map<crypto::hash, confirmed_transfer_details>::iterator, bool> entry = m_confirmed_txs.insert(std::make_pair(txid, confirmed_transfer_details()));
2612  // fill with the info we know, some info might already be there
2613  if (entry.second)
2614  {
2615  // this case will happen if the tx is from our outputs, but was sent by another
2616  // wallet (eg, we're a cold wallet and the hot wallet sent it). For RCT transactions,
2617  // we only see 0 input amounts, so have to deduce amount out from other parameters.
2618  entry.first->second.m_amount_in = spent;
2619  entry.first->second.m_amount_out = get_outs_etn_amount(tx);
2620  if(tx.version == 1)
2621  entry.first->second.m_change = received;
2622 
2623  std::vector<tx_extra_field> tx_extra_fields;
2624  parse_tx_extra(tx.extra, tx_extra_fields); // ok if partially parsed
2625  tx_extra_nonce extra_nonce;
2626  if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
2627  {
2628  // we do not care about failure here
2629  get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, entry.first->second.m_payment_id);
2630  }
2631  entry.first->second.m_subaddr_account = subaddr_account;
2632  entry.first->second.m_subaddr_indices = subaddr_indices;
2633  }
2634 
2635  entry.first->second.m_rings.clear();
2636  for (const auto &in: tx.vin)
2637  {
2638  if (in.type() != typeid(cryptonote::txin_to_key))
2639  continue;
2640  const auto &txin = boost::get<cryptonote::txin_to_key>(in);
2641  entry.first->second.m_rings.push_back(std::make_pair(txin.k_image, txin.key_offsets));
2642  }
2643  entry.first->second.m_block_height = height;
2644  entry.first->second.m_timestamp = ts;
2645  entry.first->second.m_unlock_time = tx.unlock_time;
2646  entry.first->second.m_is_migration = tx.version == 2;
2647 
2648  // is tx going to the portal address? check the first output's dest...
2649  if(tx.version == 3){ //
2650  cryptonote::account_public_address dest_address = boost::get<cryptonote::txout_to_key_public>(tx.vout[0].target).address; //
2651  std::string portal_address_viewkey_hex_str;
2652  std::string portal_address_spendkey_hex_str;
2653  bool is_portal_address;
2654  if(nettype() == MAINNET){
2655  is_portal_address = epee::string_tools::pod_to_hex(dest_address.m_spend_public_key) == "8ce0f34fd37c7f7d07c44024eb5b3cdf275d1b3e75c3464b808dce532e861137" && epee::string_tools::pod_to_hex(dest_address.m_view_public_key) == "2b95a2eb2c62253c57e82b082b850bbf22a1a7829aaea09c7c1511c1cced4375";
2656  }else{
2657  is_portal_address = epee::string_tools::pod_to_hex(dest_address.m_spend_public_key) == "5bd0c0e25eee6133850edd2b255ed9e3d6bb99fd5f08b7b5cf7f2618ad6ff2a3" && epee::string_tools::pod_to_hex(dest_address.m_view_public_key) == "5866666666666666666666666666666666666666666666666666666666666666";
2658  }
2659 
2660 
2661  entry.first->second.m_is_sc_migration = is_portal_address;
2662  }
2663 
2664 
2665  if(tx.version > 1){
2666  // grab the input owner keys/address by using the subaddr indicies used for the transaction
2667  std::vector<account_public_address> input_addresses;
2668  for (auto minor_index : subaddr_indices) {
2669  cryptonote::subaddress_index index{subaddr_account, minor_index};
2670  input_addresses.push_back(get_subaddress(index));
2671  }
2672 
2673  //build list of potential change outputs - NB if *ALL* outs go to input addresses, then we DON'T conside them change; the transaction is a looped sweep.
2674  // If one or more outs do not go to an input address, we consider ALL other outputs as change outputs
2675  std::unordered_set<uint32_t> change_indexes;
2676  for (size_t i = 0; i < tx.vout.size(); ++i) {
2677  for (auto input_address : input_addresses) {
2678  if (boost::get<txout_to_key_public>(tx.vout[i].target).address == input_address) {
2679  change_indexes.insert(i);
2680  continue;
2681  }
2682  }
2683  }
2684 
2685  // if this is true we have a sweep tx so clear all change out indexes
2686  if (change_indexes.size() == tx.vout.size()) {
2687  change_indexes.clear();
2688  }
2689 
2690  int64_t total_change = 0;
2691  for (auto &change_index : change_indexes)
2692  total_change += tx.vout[change_index].amount;
2693  entry.first->second.m_change = total_change;
2694 
2695  // For V2+ tx, we can repopulate tx destinations in the wallet cache during a rescan by simply reading them from the transactions
2696  //todo: optimise
2697  if (entry.first->second.m_dests.empty()) {
2698 
2699  // fill destinations
2700  for (size_t i = 0; i < tx.vout.size(); ++i) {
2701  if (change_indexes.find(i) == change_indexes.end()) { // only include non-change outs as dests
2702  auto output = boost::get<txout_to_key_public>(tx.vout[i].target); // grab output from the tx
2703  //predicate for comparison later on
2704  auto pred = [output](const tx_destination_entry &destination) {
2705  return destination.addr == output.address;
2706  };
2707 
2708  //search our working list of destinations in entry, and either add output amount to the
2709  // running total in the case of a match, or add a new destination otherwise
2710  auto dest_ptr = std::find_if(std::begin(entry.first->second.m_dests),
2711  std::end(entry.first->second.m_dests), pred);
2712  if (dest_ptr != std::end(entry.first->second.m_dests)) {
2713  dest_ptr->amount += tx.vout[i].amount;
2714  } else {
2715  entry.first->second.m_dests.push_back(tx_destination_entry(
2716  tx.vout[i].amount,
2717  output.address,
2718  output.m_address_prefix ==
2719  get_config(this->m_nettype).CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX ? true : false
2720  ));
2721  }
2722  }
2723  }
2724  }
2725  }
2726  add_rings(tx);
2727 }
2728 //----------------------------------------------------------------------------------------------------
2729 bool wallet2::should_skip_block(const cryptonote::block &b, uint64_t height) const
2730 {
2731  // seeking only for blocks that are not older then the wallet creation time plus 1 day. 1 day is for possible user incorrect time setup
2732  return !(b.timestamp + 60*60*24 > m_account.get_createtime() && height >= m_refresh_from_block_height);
2733 }
2734 //----------------------------------------------------------------------------------------------------
2735 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)
2736 {
2737  if(b.major_version < 10) {
2738  THROW_WALLET_EXCEPTION_IF(bche.txs.size() + 1 != parsed_block.o_indices.indices.size(),
2739  error::wallet_internal_error,
2740  "block transactions=" + std::to_string(bche.txs.size()) +
2741  " not match with daemon response size=" +
2742  std::to_string(parsed_block.o_indices.indices.size()));
2743  }
2744 
2745  //handle transactions from new block
2746 
2747  //optimization: seeking only for blocks that are not older then the wallet creation time plus 1 day. 1 day is for possible user incorrect time setup
2748  if (!should_skip_block(b, height))
2749  {
2750  //FIRST PROCESS THE MINER TX FOR THE NEW BLOCK
2751  TIME_MEASURE_START(miner_tx_handle_time);
2752  if (m_refresh_type != RefreshNoCoinbase)
2753  process_new_transaction(get_transaction_hash(b.miner_tx), b.miner_tx, parsed_block.o_indices.indices[0].indices, height, b.timestamp, true, false, false, false, tx_cache_data[tx_cache_data_offset], output_tracker_cache);
2754  ++tx_cache_data_offset;
2755  TIME_MEASURE_FINISH(miner_tx_handle_time);
2756  //NOW THE OTHER TRANSACTIONS
2757  TIME_MEASURE_START(txs_handle_time);
2758  THROW_WALLET_EXCEPTION_IF(bche.txs.size() != b.tx_hashes.size(), error::wallet_internal_error, "Wrong amount of transactions for block");
2759  THROW_WALLET_EXCEPTION_IF(bche.txs.size() != parsed_block.txes.size(), error::wallet_internal_error, "Wrong amount of transactions for block");
2760  for (size_t idx = 0; idx < b.tx_hashes.size(); ++idx)
2761  {
2762  process_new_transaction(b.tx_hashes[idx], parsed_block.txes[idx], parsed_block.o_indices.indices[idx+1].indices, height, b.timestamp, false, false, false, false, tx_cache_data[tx_cache_data_offset++], output_tracker_cache);
2763  }
2764  TIME_MEASURE_FINISH(txs_handle_time);
2765  m_last_block_reward = cryptonote::get_outs_etn_amount(b.miner_tx);
2766  LOG_PRINT_L2("Processed block: " << bl_id << ", height " << height << ", " << miner_tx_handle_time + txs_handle_time << "(" << miner_tx_handle_time << "/" << txs_handle_time <<")ms");
2767  }else
2768  {
2769  if (!(height % 128))
2770  LOG_PRINT_L2( "Skipped block by timestamp, height: " << height << ", block time " << b.timestamp << ", account time " << m_account.get_createtime());
2771  }
2772  m_blockchain.push_back(bl_id);
2773 
2774  if (0 != m_callback)
2775  m_callback->on_new_block(height, b);
2776 }
2777 //----------------------------------------------------------------------------------------------------
2778 void wallet2::get_short_chain_history(std::list<crypto::hash>& ids, uint64_t granularity) const
2779 {
2780  size_t i = 0;
2781  size_t current_multiplier = 1;
2782  size_t blockchain_size = std::max((size_t)(m_blockchain.size() / granularity * granularity), m_blockchain.offset());
2783  size_t sz = blockchain_size - m_blockchain.offset();
2784  if(!sz)
2785  {
2786  ids.push_back(m_blockchain.genesis());
2787  return;
2788  }
2789  size_t current_back_offset = 1;
2790  bool base_included = false;
2791  while(current_back_offset < sz)
2792  {
2793  ids.push_back(m_blockchain[m_blockchain.offset() + sz-current_back_offset]);
2794  if(sz-current_back_offset == 0)
2795  base_included = true;
2796  if(i < 10)
2797  {
2798  ++current_back_offset;
2799  }else
2800  {
2801  current_back_offset += current_multiplier *= 2;
2802  }
2803  ++i;
2804  }
2805  if(!base_included)
2806  ids.push_back(m_blockchain[m_blockchain.offset()]);
2807  if(m_blockchain.offset())
2808  ids.push_back(m_blockchain.genesis());
2809 }
2810 //----------------------------------------------------------------------------------------------------
2811 void wallet2::parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const
2812 {
2814 }
2815 //----------------------------------------------------------------------------------------------------
2816 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)
2817 {
2820  req.block_ids = short_chain_history;
2821 
2822  req.prune = true;
2823  req.start_height = start_height;
2824  req.no_miner_tx = m_refresh_type == RefreshNoCoinbase;
2825  m_daemon_rpc_mutex.lock();
2826 
2827  bool r = invoke_http_bin("/getblocks.bin", req, res, rpc_timeout);
2828 
2829  m_daemon_rpc_mutex.unlock();
2830  THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getblocks.bin");
2831  THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getblocks.bin");
2832  THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_blocks_error, get_rpc_status(res.status));
2833  THROW_WALLET_EXCEPTION_IF(res.blocks.size() != res.output_indices.size(), error::wallet_internal_error,
2834  "mismatched blocks (" + boost::lexical_cast<std::string>(res.blocks.size()) + ") and output_indices (" +
2835  boost::lexical_cast<std::string>(res.output_indices.size()) + ") sizes from daemon");
2836 
2837  blocks_start_height = res.start_height;
2838  blocks = std::move(res.blocks);
2839  o_indices = std::move(res.output_indices);
2840 }
2841 
2842 //------------------------------------------------------------------------------------------------------------------------------
2843 cryptonote::blobdata wallet2::get_pruned_tx_blob(const cryptonote::blobdata &blobdata)
2844 {
2846 
2848  {
2849  MERROR("Failed to parse and validate tx from blob");
2850  return blobdata;
2851  }
2852 
2853  std::stringstream ss;
2854  binary_archive<true> ba(ss);
2855  bool r = tx.serialize_base(ba);
2856  CHECK_AND_ASSERT_MES(r, blobdata, "Failed to serialize rct signatures base");
2857  return ss.str();
2858 }
2859 //----------------------------------------------------------------------------------------------------
2860 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)
2861 {
2864  req.block_ids = short_chain_history;
2865 
2866  req.start_height = start_height;
2867  m_daemon_rpc_mutex.lock();
2868 
2869  bool r = invoke_http_bin("/gethashes.bin", req, res, rpc_timeout);
2870 
2871  m_daemon_rpc_mutex.unlock();
2872  THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gethashes.bin");
2873  THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gethashes.bin");
2874  THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_hashes_error, get_rpc_status(res.status));
2875 
2876  blocks_start_height = res.start_height;
2877  hashes = std::move(res.m_block_ids);
2878 }
2879 
2880 //----------------------------------------------------------------------------------------------------
2881 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)
2882 {
2883  size_t current_index = start_height;
2884  blocks_added = 0;
2885 
2886  THROW_WALLET_EXCEPTION_IF(blocks.size() != parsed_blocks.size(), error::wallet_internal_error, "size mismatch");
2887  THROW_WALLET_EXCEPTION_IF(!m_blockchain.is_in_bounds(current_index), error::out_of_hashchain_bounds_error);
2888 
2891 
2892  //this is the total number of tx in the batch of blocks
2893  size_t num_txes = 0;
2894  std::vector<tx_cache_data> tx_cache_data;
2895  for (size_t i = 0; i < blocks.size(); ++i)
2896  num_txes += 1 + parsed_blocks[i].txes.size();
2897  tx_cache_data.resize(num_txes);
2898  size_t txidx = 0;
2899  for (size_t i = 0; i < blocks.size(); ++i)
2900  {
2901  THROW_WALLET_EXCEPTION_IF(parsed_blocks[i].txes.size() != parsed_blocks[i].block.tx_hashes.size(),
2902  error::wallet_internal_error, "Mismatched parsed_blocks[i].txes.size() and parsed_blocks[i].block.tx_hashes.size()");
2903 
2904  //ONLY CACHE TX PUBLIC KEYS R0....Rn FOR V1 TX (that's if we don't want to skip the blocks anyway)
2905  if (should_skip_block(parsed_blocks[i].block, start_height + i)){
2906  txidx += 1 + parsed_blocks[i].block.tx_hashes.size();
2907  continue;
2908  }else{
2909  if (m_refresh_type != RefreshNoCoinbase) // we're caching pubkeys only
2910  tpool.submit(&waiter, [&, i, txidx]() {
2911  cache_tx_data(parsed_blocks[i].block.miner_tx, get_transaction_hash(parsed_blocks[i].block.miner_tx),
2912  tx_cache_data[txidx]);
2913  });
2914  ++txidx;
2915  for (size_t idx = 0; idx < parsed_blocks[i].txes.size(); ++idx) {
2916  tpool.submit(&waiter, [&, i, idx, txidx]() {
2917  cache_tx_data(parsed_blocks[i].txes[idx], parsed_blocks[i].block.tx_hashes[idx], tx_cache_data[txidx]);
2918  });
2919  ++txidx;
2920  }
2921  }
2922  }
2923  THROW_WALLET_EXCEPTION_IF(txidx != num_txes, error::wallet_internal_error, "txidx does not match tx_cache_data size");
2924  waiter.wait(&tpool);
2925 
2926  hw::device &hwdev = m_account.get_device();
2927  hw::reset_mode rst(hwdev);
2929  const cryptonote::account_keys &keys = m_account.get_keys();
2930 
2931  auto gender = [&](wallet2::is_out_data &iod) {
2932  if (!hwdev.generate_key_derivation(iod.pkey, keys.m_view_secret_key, iod.derivation))
2933  {
2934  MWARNING("Failed to generate key derivation from tx pubkey, skipping");
2935  static_assert(sizeof(iod.derivation) == sizeof(rct::key), "Mismatched sizes of key_derivation and rct::key");
2936  memcpy(&iod.derivation, rct::identity().bytes, sizeof(iod.derivation));
2937  }
2938  };
2939 
2940  //call gender above in a thread
2941  for (size_t i = 0; i < tx_cache_data.size(); ++i) {
2942  if (tx_cache_data[i].empty())
2943  continue;
2944  if(!tx_cache_data[i].public_only()){ // no need to thread key derivations for public outs
2945  tpool.submit(&waiter, [&hwdev, &gender, &tx_cache_data, i]() {
2946  auto &slot = tx_cache_data[i];
2947  boost::unique_lock<hw::device> hwdev_lock(hwdev);
2948  for (auto &iod: slot.primary)
2949  gender(iod);
2950  for (auto &iod: slot.additional)
2951  gender(iod);
2952  }, true);
2953  }
2954  }
2955 
2956  waiter.wait(&tpool);
2957 
2958  auto geniod = [&](const cryptonote::transaction &tx, size_t n_vouts, size_t txidx) {
2959  for (size_t k = 0; k < n_vouts; ++k)
2960  {
2961  const auto &o = tx.vout[k];
2962  if (o.target.type() == typeid(cryptonote::txout_to_key))
2963  {
2964  std::vector<crypto::key_derivation> additional_derivations;
2965  additional_derivations.reserve(tx_cache_data[txidx].additional.size());
2966  for (const auto &iod: tx_cache_data[txidx].additional)
2967  additional_derivations.push_back(iod.derivation);
2968  const auto &key = boost::get<txout_to_key>(o.target).key;
2969  for (size_t l = 0; l < tx_cache_data[txidx].primary.size(); ++l) // afaik this loop over l is for additional *primary* tx pubkeys (accidentally?) put in the extra
2970  {
2971  //NB this doesn't mean that all of the outs were received with the primary txpubkey R, it was just a convention to push this number of
2972  // empty rec's to tx_cache_data[txidx].primary in the tx_cache thread before (just in case they were)
2973  THROW_WALLET_EXCEPTION_IF(tx_cache_data[txidx].primary[l].received.size() != n_vouts,
2974  error::wallet_internal_error, "Unexpected received array size");
2975  // if we find that an out belongs to us, mark as received in the cache
2976  tx_cache_data[txidx].primary[l].received[k] = is_out_to_acc_precomp(m_subaddresses, key, tx_cache_data[txidx].primary[l].derivation, additional_derivations, k, hwdev);
2977  additional_derivations.clear();
2978  }
2979  }else if(o.target.type() == typeid(cryptonote::txout_to_key_public)){ // this is the equivalent of our ownership precomp
2980  const auto etn_address = boost::get<txout_to_key_public>(o.target).address;
2981  THROW_WALLET_EXCEPTION_IF(tx_cache_data[txidx].public_outs[0].received.size() != n_vouts,
2982  error::wallet_internal_error, "Unexpected received array size");
2983  THROW_WALLET_EXCEPTION_IF(tx_cache_data[txidx].public_outs.size() != 1,
2984  error::wallet_internal_error, "Unexpected received vector size");
2985  // no loop over l required for public outputs ^
2986 
2987  //only assign subaddress recipient if view key also matches too as we now spend with combined keys (a+b) and we wont be
2988  // doing key image related checks later to check if we can really spend the out (ie checking view key match by proxy)
2989  auto receive_info = cryptonote::is_out_to_acc_precomp_public(m_subaddresses, etn_address);
2990  tx_cache_data[txidx].public_outs[0].received[k] =
2991  (receive_info == boost::none) ?
2992  (receive_info) :
2993  get_subaddress(receive_info->index).m_view_public_key == etn_address.m_view_public_key ?
2994  receive_info : boost::none; //todo: refactor with function pointers
2995 
2996  }
2997  }
2998  };
2999 
3000  //we reset txidx from above ready to precompute ownership.
3001  txidx = 0;
3002  for (size_t i = 0; i < blocks.size(); ++i)
3003  {
3004  if (should_skip_block(parsed_blocks[i].block, start_height + i))
3005  {
3006  txidx += 1 + parsed_blocks[i].block.tx_hashes.size();
3007  continue;
3008  }
3009 
3010  //Run geniod on a thread which is a proxy for is_out_to_acc_precomp which precomputes ownership of outputs
3011  if (m_refresh_type != RefreshType::RefreshNoCoinbase)
3012  {
3013  THROW_WALLET_EXCEPTION_IF(txidx >= tx_cache_data.size(), error::wallet_internal_error, "txidx out of range");
3014  const size_t n_vouts = m_refresh_type == RefreshType::RefreshOptimizeCoinbase ? 1 : parsed_blocks[i].block.miner_tx.vout.size();
3015  tpool.submit(&waiter, [&, i, n_vouts, txidx](){ geniod(parsed_blocks[i].block.miner_tx, n_vouts, txidx); }, true);
3016  }
3017  ++txidx;
3018  for (size_t j = 0; j < parsed_blocks[i].txes.size(); ++j)
3019  {
3020  THROW_WALLET_EXCEPTION_IF(txidx >= tx_cache_data.size(), error::wallet_internal_error, "txidx out of range");
3021  tpool.submit(&waiter, [&, i, j, txidx](){ geniod(parsed_blocks[i].txes[j], parsed_blocks[i].txes[j].vout.size(), txidx); }, true);
3022  ++txidx;
3023  }
3024  }
3025  THROW_WALLET_EXCEPTION_IF(txidx != tx_cache_data.size(), error::wallet_internal_error, "txidx did not reach expected value");
3026  waiter.wait(&tpool);
3027  hwdev.set_mode(hw::device::NONE);
3028 
3029  size_t tx_cache_data_offset = 0;
3030  for (size_t i = 0; i < blocks.size(); ++i)
3031  {
3032  const crypto::hash &bl_id = parsed_blocks[i].hash;
3033  const cryptonote::block &bl = parsed_blocks[i].block;
3034 
3035  if(current_index >= m_blockchain.size())
3036  {
3037  process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset, output_tracker_cache);
3038  ++blocks_added;
3039  }
3040  else if(bl_id != m_blockchain[current_index])
3041  {
3042  //split detected here !!!
3043  THROW_WALLET_EXCEPTION_IF(current_index == start_height, error::wallet_internal_error,
3044  "wrong daemon response: split starts from the first block in response " + string_tools::pod_to_hex(bl_id) +
3045  " (height " + std::to_string(start_height) + "), local block id at this height: " +
3046  string_tools::pod_to_hex(m_blockchain[current_index]));
3047 
3048  detach_blockchain(current_index, output_tracker_cache);
3049  process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset, output_tracker_cache);
3050  }
3051  else
3052  {
3053  LOG_PRINT_L2("Block is already in blockchain: " << string_tools::pod_to_hex(bl_id));
3054  }
3055  ++current_index;
3056  tx_cache_data_offset += 1 + parsed_blocks[i].txes.size();
3057  }
3058 }
3059 //----------------------------------------------------------------------------------------------------
3060 void wallet2::refresh(bool trusted_daemon)
3061 {
3062  uint64_t blocks_fetched = 0;
3063  refresh(trusted_daemon, 0, blocks_fetched);
3064 }
3065 //----------------------------------------------------------------------------------------------------
3066 void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched)
3067 {
3068  bool received_etn = false;
3069  refresh(trusted_daemon, start_height, blocks_fetched, received_etn);
3070 }
3071 //----------------------------------------------------------------------------------------------------
3072 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)
3073 {
3074  error = false;
3075 
3076  try
3077  {
3078  drop_from_short_history(short_chain_history, 3);
3079 
3080  THROW_WALLET_EXCEPTION_IF(prev_blocks.size() != prev_parsed_blocks.size(), error::wallet_internal_error, "size mismatch");
3081 
3082  // prepend the last 3 blocks, should be enough to guard against a block or two's reorg
3083  auto s = std::next(prev_parsed_blocks.rbegin(), std::min((size_t)3, prev_parsed_blocks.size())).base();
3084  for (; s != prev_parsed_blocks.end(); ++s)
3085  {
3086  short_chain_history.push_front(s->hash);
3087  }
3088 
3089  // pull the new blocks
3090  std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> o_indices;
3091  pull_blocks(start_height, blocks_start_height, short_chain_history, blocks, o_indices);
3092  THROW_WALLET_EXCEPTION_IF(blocks.size() != o_indices.size(), error::wallet_internal_error, "Mismatched sizes of blocks and o_indices");
3093 
3096  parsed_blocks.resize(blocks.size());
3097  for (size_t i = 0; i < blocks.size(); ++i)
3098  {
3099  tpool.submit(&waiter, boost::bind(&wallet2::parse_block_round, this, std::cref(blocks[i].block),
3100  std::ref(parsed_blocks[i].block), std::ref(parsed_blocks[i].hash), std::ref(parsed_blocks[i].error)), true);
3101  }
3102  waiter.wait(&tpool);
3103  for (size_t i = 0; i < blocks.size(); ++i)
3104  {
3105  if (parsed_blocks[i].error)
3106  {
3107  error = true;
3108  break;
3109  }
3110  parsed_blocks[i].o_indices = std::move(o_indices[i]);
3111  }
3112 
3113  boost::mutex error_lock;
3114  for (size_t i = 0; i < blocks.size(); ++i)
3115  {
3116  parsed_blocks[i].txes.resize(blocks[i].txs.size());
3117  for (size_t j = 0; j < blocks[i].txs.size(); ++j)
3118  {
3119  tpool.submit(&waiter, [&, i, j](){
3120  if (!parse_and_validate_tx_base_from_blob(blocks[i].txs[j], parsed_blocks[i].txes[j]))
3121  {
3122  boost::unique_lock<boost::mutex> lock(error_lock);
3123  error = true;
3124  }
3125  }, true);
3126  }
3127  }
3128  waiter.wait(&tpool);
3129  }
3130  catch(...)
3131  {
3132  error = true;
3133  }
3134 }
3135 
3136 void wallet2::remove_obsolete_pool_txs(const std::vector<crypto::hash> &tx_hashes)
3137 {
3138  // remove pool txes to us that aren't in the pool anymore
3139  std::unordered_multimap<crypto::hash, wallet2::pool_payment_details>::iterator uit = m_unconfirmed_payments.begin();
3140  while (uit != m_unconfirmed_payments.end())
3141  {
3142  const crypto::hash &txid = uit->second.m_pd.m_tx_hash;
3143  bool found = false;
3144  for (const auto &it2: tx_hashes)
3145  {
3146  if (it2 == txid)
3147  {
3148  found = true;
3149  break;
3150  }
3151  }
3152  auto pit = uit++;
3153  if (!found)
3154  {
3155  MDEBUG("Removing " << txid << " from unconfirmed payments, not found in pool");
3156  m_unconfirmed_payments.erase(pit);
3157  if (0 != m_callback)
3158  m_callback->on_pool_tx_removed(txid);
3159  }
3160  }
3161 }
3162 
3163 //----------------------------------------------------------------------------------------------------
3164 void wallet2::update_pool_state(bool refreshed)
3165 {
3166  MTRACE("update_pool_state start");
3167 
3168  auto keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this]() {
3169  if (m_encrypt_keys_after_refresh)
3170  {
3171  encrypt_keys(*m_encrypt_keys_after_refresh);
3172  m_encrypt_keys_after_refresh = boost::none;
3173  }
3174  });
3175 
3176  // get the pool state
3179  m_daemon_rpc_mutex.lock();
3180  bool r = invoke_http_json("/get_transaction_pool_hashes.bin", req, res, rpc_timeout);
3181  m_daemon_rpc_mutex.unlock();
3182  THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_transaction_pool_hashes.bin");
3183  THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_transaction_pool_hashes.bin");
3185  MTRACE("update_pool_state got pool");
3186 
3187  // remove any pending tx that's not in the pool
3188  std::unordered_map<crypto::hash, wallet2::unconfirmed_transfer_details>::iterator it = m_unconfirmed_txs.begin();
3189  while (it != m_unconfirmed_txs.end())
3190  {
3191  const crypto::hash &txid = it->first;
3192  bool found = false;
3193  for (const auto &it2: res.tx_hashes)
3194  {
3195  if (it2 == txid)
3196  {
3197  found = true;
3198  break;
3199  }
3200  }
3201  auto pit = it++;
3202  if (!found)
3203  {
3204  // we want to avoid a false positive when we ask for the pool just after
3205  // a tx is removed from the pool due to being found in a new block, but
3206  // just before the block is visible by refresh. So we keep a boolean, so
3207  // that the first time we don't see the tx, we set that boolean, and only
3208  // delete it the second time it is checked (but only when refreshed, so
3209  // we're sure we've seen the blockchain state first)
3210  if (pit->second.m_state == wallet2::unconfirmed_transfer_details::pending)
3211  {
3212  LOG_PRINT_L1("Pending txid " << txid << " not in pool, marking as not in pool");
3213  pit->second.m_state = wallet2::unconfirmed_transfer_details::pending_not_in_pool;
3214  }
3215  else if (pit->second.m_state == wallet2::unconfirmed_transfer_details::pending_not_in_pool && refreshed)
3216  {
3217  LOG_PRINT_L1("Pending txid " << txid << " not in pool, marking as failed");
3218  pit->second.m_state = wallet2::unconfirmed_transfer_details::failed;
3219 
3220  // the inputs aren't spent anymore, since the tx failed
3221  remove_rings(pit->second.m_tx);
3222  for (size_t vini = 0; vini < pit->second.m_tx.vin.size(); ++vini)
3223  {
3224  if (pit->second.m_tx.vin[vini].type() == typeid(txin_to_key))
3225  {
3226  txin_to_key &tx_in_to_key = boost::get<txin_to_key>(pit->second.m_tx.vin[vini]);
3227  for (size_t i = 0; i < m_transfers.size(); ++i)
3228  {
3229  const transfer_details &td = m_transfers[i];
3230  if (td.m_key_image == tx_in_to_key.k_image)
3231  {
3232  LOG_PRINT_L1("Resetting spent status for output " << vini << ": " << td.m_key_image);
3233  set_unspent(i);
3234  break;
3235  }
3236  }
3237  }
3238  }
3239  }
3240  }
3241  }
3242  MTRACE("update_pool_state done first loop");
3243 
3244  // remove pool txes to us that aren't in the pool anymore
3245  // but only if we just refreshed, so that the tx can go in
3246  // the in transfers list instead (or nowhere if it just
3247  // disappeared without being mined)
3248  if (refreshed)
3249  remove_obsolete_pool_txs(res.tx_hashes);
3250 
3251  MTRACE("update_pool_state done second loop");
3252 
3253  // gather txids of new pool txes to us
3254  std::vector<std::pair<crypto::hash, bool>> txids;
3255  for (const auto &txid: res.tx_hashes)
3256  {
3257  bool txid_found_in_up = false;
3258  for (const auto &up: m_unconfirmed_payments)
3259  {
3260  if (up.second.m_pd.m_tx_hash == txid)
3261  {
3262  txid_found_in_up = true;
3263  break;
3264  }
3265  }
3266  if (m_scanned_pool_txs[0].find(txid) != m_scanned_pool_txs[0].end() || m_scanned_pool_txs[1].find(txid) != m_scanned_pool_txs[1].end())
3267  {
3268  // if it's for us, we want to keep track of whether we saw a double spend, so don't bail out
3269  if (!txid_found_in_up)
3270  {
3271  LOG_PRINT_L2("Already seen " << txid << ", and not for us, skipped");
3272  continue;
3273  }
3274  }
3275  if (!txid_found_in_up)
3276  {
3277  LOG_PRINT_L1("Found new pool tx: " << txid);
3278  bool found = false;
3279  for (const auto &i: m_unconfirmed_txs)
3280  {
3281  if (i.first == txid)
3282  {
3283  found = true;
3284  // if this is a payment to yourself at a different subaddress account, don't skip it
3285  // so that you can see the incoming pool tx with 'show_transfers' on that receiving subaddress account
3286  const unconfirmed_transfer_details& utd = i.second;
3287  for (const auto& dst : utd.m_dests)
3288  {
3289  auto subaddr_index = m_subaddresses.find(dst.addr.m_spend_public_key);
3290  if (subaddr_index != m_subaddresses.end() && subaddr_index->second.major != utd.m_subaddr_account)
3291  {
3292  found = false;
3293  break;
3294  }
3295  }
3296  break;
3297  }
3298  }
3299  if (!found)
3300  {
3301  // not one of those we sent ourselves
3302  txids.push_back({txid, false});
3303  }
3304  else
3305  {
3306  LOG_PRINT_L1("We sent that one");
3307  }
3308  }
3309  else
3310  {
3311  LOG_PRINT_L1("Already saw that one, it's for us");
3312  txids.push_back({txid, true});
3313  }
3314  }
3315 
3316  // get those txes
3317  if (!txids.empty())
3318  {
3321  for (const auto &p: txids)
3322  req.txs_hashes.push_back(epee::string_tools::pod_to_hex(p.first));
3323  MDEBUG("asking for " << txids.size() << " transactions");
3324  req.decode_as_json = false;
3325  req.prune = true;
3326  m_daemon_rpc_mutex.lock();
3327  bool r = invoke_http_json("/gettransactions", req, res, rpc_timeout);
3328  m_daemon_rpc_mutex.unlock();
3329  MDEBUG("Got " << r << " and " << res.status);
3330  if (r && res.status == CORE_RPC_STATUS_OK)
3331  {
3332  if (res.txs.size() == txids.size())
3333  {
3334  for (const auto &tx_entry: res.txs)
3335  {
3336  if (tx_entry.in_pool)
3337  {
3340  crypto::hash tx_hash;
3341 
3342  if (get_pruned_tx(tx_entry, tx, tx_hash))
3343  {
3344  const std::vector<std::pair<crypto::hash, bool>>::const_iterator i = std::find_if(txids.begin(), txids.end(),
3345  [tx_hash](const std::pair<crypto::hash, bool> &e) { return e.first == tx_hash; });
3346  if (i != txids.end())
3347  {
3348  process_new_transaction(tx_hash, tx, std::vector<uint64_t>(), 0, time(NULL), false, true, tx_entry.double_spend_seen, false, {});
3349  m_scanned_pool_txs[0].insert(tx_hash);
3350  if (m_scanned_pool_txs[0].size() > 5000)
3351  {
3352  std::swap(m_scanned_pool_txs[0], m_scanned_pool_txs[1]);
3353  m_scanned_pool_txs[0].clear();
3354  }
3355  }
3356  else
3357  {
3358  MERROR("Got txid " << tx_hash << " which we did not ask for");
3359  }
3360  }
3361  else
3362  {
3363  LOG_PRINT_L0("Failed to parse transaction from daemon");
3364  }
3365  }
3366  else
3367  {
3368  LOG_PRINT_L1("Transaction from daemon was in pool, but is no more");
3369  }
3370  }
3371  }
3372  else
3373  {
3374  LOG_PRINT_L0("Expected " << txids.size() << " tx(es), got " << res.txs.size());
3375  }
3376  }
3377  else
3378  {
3379  LOG_PRINT_L0("Error calling gettransactions daemon RPC: r " << r << ", status " << get_rpc_status(res.status));
3380  }
3381  }
3382  MTRACE("update_pool_state end");
3383 }
3384 
3385 //----------------------------------------------------------------------------------------------------
3386 void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, bool force)
3387 {
3388  std::vector<crypto::hash> hashes;
3389 
3390  const uint64_t checkpoint_height = m_checkpoints.get_max_height();
3391  if ((stop_height > checkpoint_height && m_blockchain.size()-1 < checkpoint_height) && !force)
3392  {
3393  // we will drop all these, so don't bother getting them
3394  uint64_t missing_blocks = m_checkpoints.get_max_height() - m_blockchain.size();
3395  while (missing_blocks-- > 0)
3396  m_blockchain.push_back(crypto::null_hash); // maybe a bit suboptimal, but deque won't do huge reallocs like vector
3397  m_blockchain.push_back(m_checkpoints.get_points().at(checkpoint_height));
3398  m_blockchain.trim(checkpoint_height);
3399  short_chain_history.clear();
3400  get_short_chain_history(short_chain_history);
3401  }
3402 
3403  size_t current_index = m_blockchain.size();
3404  while(m_run.load(std::memory_order_relaxed) && current_index < stop_height)
3405  {
3406  pull_hashes(0, blocks_start_height, short_chain_history, hashes);
3407  if (hashes.size() <= 3)
3408  return;
3409  if (blocks_start_height < m_blockchain.offset())
3410  {
3411  MERROR("Blocks start before blockchain offset: " << blocks_start_height << " " << m_blockchain.offset());
3412  return;
3413  }
3414  if (hashes.size() + current_index < stop_height) {
3415  drop_from_short_history(short_chain_history, 3);
3416  std::vector<crypto::hash>::iterator right = hashes.end();
3417  // prepend 3 more
3418  for (int i = 0; i<3; i++) {
3419  right--;
3420  short_chain_history.push_front(*right);
3421  }
3422  }
3423  current_index = blocks_start_height;
3424  for(auto& bl_id: hashes)
3425  {
3426  if(current_index >= m_blockchain.size())
3427  {
3428  if (!(current_index % 1024))
3429  LOG_PRINT_L2( "Skipped block by height: " << current_index);
3430  m_blockchain.push_back(bl_id);
3431 
3432  if (0 != m_callback)
3433  { // FIXME: this isn't right, but simplewallet just logs that we got a block.
3434  cryptonote::block dummy;
3435  m_callback->on_new_block(current_index, dummy);
3436  }
3437  }
3438  else if(bl_id != m_blockchain[current_index])
3439  {
3440  //split detected here !!!
3441  return;
3442  }
3443  ++current_index;
3444  if (current_index >= stop_height)
3445  return;
3446  }
3447  }
3448 }
3449 
3450 
3451 bool wallet2::add_address_book_row(const cryptonote::account_public_address &address, const crypto::hash &payment_id, const std::string &description, bool is_subaddress)
3452 {
3454  a.m_address = address;
3455  a.m_payment_id = payment_id;
3456  a.m_description = description;
3457  a.m_is_subaddress = is_subaddress;
3458 
3459  auto old_size = m_address_book.size();
3460  m_address_book.push_back(a);
3461  if(m_address_book.size() == old_size+1)
3462  return true;
3463  return false;
3464 }
3465 
3466 bool wallet2::delete_address_book_row(std::size_t row_id) {
3467  if(m_address_book.size() <= row_id)
3468  return false;
3469 
3470  m_address_book.erase(m_address_book.begin()+row_id);
3471 
3472  return true;
3473 }
3474 
3475 //----------------------------------------------------------------------------------------------------
3476 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
3477 { // output tracker cache at the pointed-to address is a map where the key is a pair of <output amount, global out index>
3478  // and the value is the m_transfers index. Essentially, this is a cache of output unique identifier against it's location in m_transfers (if it exists there)
3479  std::shared_ptr<std::pair<std::map<std::pair<uint64_t, uint64_t>, size_t>, std::map<std::pair<std::array<char,32>, size_t>, size_t>>> cache{new std::pair<std::map<std::pair<uint64_t, uint64_t>, size_t>, std::map<std::pair<std::array<char,32>, size_t>, size_t>>()};
3480  for (size_t i = 0; i < m_transfers.size(); ++i)
3481  {
3482  const transfer_details &td = m_transfers[i];
3483  //amount, global out index
3484  (*cache).first[std::make_pair(td.is_rct() ? 0 : td.amount(), td.m_global_output_index)] = i;
3485  //txid, relative out index
3486  std::array<char, 32> transaction_id;
3487  std::copy(std::begin(td.m_txid.data), std::end(td.m_txid.data), transaction_id.begin());
3488  (*cache).second[std::make_pair(transaction_id, td.m_internal_output_index)] = i;
3489  }
3490  return cache;
3491 }
3492 //----------------------------------------------------------------------------------------------------
3493 void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched, bool& received_etn, bool check_pool) {
3494  if (m_offline) {
3495  blocks_fetched = 0;
3496  received_etn = 0;
3497  return;
3498  }
3499 
3500  if (m_light_wallet) {
3501 
3502  // MyMonero get_address_info needs to be called occasionally to trigger wallet sync.
3503  // This call is not really needed for other purposes and can be removed if mymonero changes their backend.
3505 
3506  // Get basic info
3507  if (light_wallet_get_address_info(res)) {
3508  // Last stored block height
3509  uint64_t prev_height = m_light_wallet_blockchain_height;
3510  // Update lw heights
3511  m_light_wallet_scanned_block_height = res.scanned_block_height;
3512  m_light_wallet_blockchain_height = res.blockchain_height;
3513  // If new height - call new_block callback
3514  if (m_light_wallet_blockchain_height != prev_height) {
3515  MDEBUG("new block since last time!");
3516  m_callback->on_lw_new_block(m_light_wallet_blockchain_height - 1);
3517  }
3518  m_light_wallet_connected = true;
3519  MDEBUG("lw scanned block height: " << m_light_wallet_scanned_block_height);
3520  MDEBUG("lw blockchain height: " << m_light_wallet_blockchain_height);
3521  MDEBUG(m_light_wallet_blockchain_height - m_light_wallet_scanned_block_height << " blocks behind");
3522  // TODO: add wallet created block info
3523 
3524  light_wallet_get_address_txs();
3525  } else
3526  m_light_wallet_connected = false;
3527 
3528  // Lighwallet refresh done
3529  return;
3530  }
3531  received_etn = false;
3532  blocks_fetched = 0;
3533  uint64_t added_blocks = 0;
3534  size_t try_count = 0;
3535  crypto::hash last_tx_hash_id = m_transfers.size() ? m_transfers.back().m_txid : null_hash;
3536  std::list<crypto::hash> short_chain_history;
3539  uint64_t blocks_start_height;
3540  std::vector<cryptonote::block_complete_entry> blocks;
3541  std::vector<parsed_block> parsed_blocks;
3542  bool refreshed = false;
3543  std::shared_ptr<std::pair<std::map<std::pair<uint64_t, uint64_t>, size_t>, std::map<std::pair<std::array<char, 32>, size_t>, size_t>>> output_tracker_cache; //this is where the only usage of output_tracker cache begins
3544  hw::device &hwdev = m_account.get_device();
3545 
3546  // pull the first set of blocks
3547  get_short_chain_history(short_chain_history,
3548  (m_first_refresh_done || trusted_daemon) ? 1 : FIRST_REFRESH_GRANULARITY);
3549  m_run.store(true, std::memory_order_relaxed);
3550  if (start_height > m_blockchain.size() || m_refresh_from_block_height > m_blockchain.size()) {
3551  if (!start_height)
3552  start_height = m_refresh_from_block_height;
3553  // we can shortcut by only pulling hashes up to the start_height
3554  fast_refresh(start_height, blocks_start_height, short_chain_history);
3555  // regenerate the history now that we've got a full set of hashes
3556  short_chain_history.clear();
3557  get_short_chain_history(short_chain_history,
3558  (m_first_refresh_done || trusted_daemon) ? 1 : FIRST_REFRESH_GRANULARITY);
3559  start_height = 0;
3560  // and then fall through to regular refresh processing
3561  }
3562 
3563  // If stop() is called during fast refresh we don't need to continue
3564  if (!m_run.load(std::memory_order_relaxed))
3565  return;
3566  // always reset start_height to 0 to force short_chain_ history to be used on
3567  // subsequent pulls in this refresh.
3568  start_height = 0;
3569 
3570  auto keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this]() {
3571  if (m_encrypt_keys_after_refresh) {
3572  encrypt_keys(*m_encrypt_keys_after_refresh);
3573  m_encrypt_keys_after_refresh = boost::none;
3574  }
3575  });
3576 
3577  auto scope_exit_handler_hwdev = epee::misc_utils::create_scope_leave_handler(
3578  [&]() { hwdev.computing_key_images(false); });
3579  bool first = true;
3580  while (m_run.load(std::memory_order_relaxed)) {
3581  uint64_t next_blocks_start_height;
3582  std::vector<cryptonote::block_complete_entry> next_blocks;
3583  std::vector<parsed_block> next_parsed_blocks;
3584  bool error;
3585  try {
3586  // pull the next set of blocks while we're processing the current one
3587  error = false;
3588  next_blocks.clear();
3589  next_parsed_blocks.clear();
3590  added_blocks = 0;
3591  if (!first && blocks.empty()) {
3592  refreshed = false;
3593  break;
3594  }
3595  tpool.submit(&waiter, [&] {
3596  pull_and_parse_next_blocks(start_height, next_blocks_start_height, short_chain_history, blocks,
3597  parsed_blocks, next_blocks, next_parsed_blocks, error);
3598  });
3599 
3600  if (!first) {
3601  try {
3602  process_parsed_blocks(blocks_start_height, blocks, parsed_blocks, added_blocks,
3603  output_tracker_cache.get());
3604  }
3606  MINFO("Daemon claims next refresh block is out of hash chain bounds, resetting hash chain");
3607  uint64_t stop_height = m_blockchain.offset();
3608  std::vector<crypto::hash> tip(m_blockchain.size() - m_blockchain.offset());
3609  for (size_t i = m_blockchain.offset(); i < m_blockchain.size(); ++i)
3610  tip[i - m_blockchain.offset()] = m_blockchain[i];
3612  generate_genesis(b);
3613  m_blockchain.clear();
3614  m_blockchain.push_back(get_block_hash(b));
3615  short_chain_history.clear();
3616  get_short_chain_history(short_chain_history);
3617  fast_refresh(stop_height, blocks_start_height, short_chain_history, true);
3619  (m_blockchain.size() == stop_height || (m_blockchain.size() == 1 && stop_height == 0)
3620  ? false : true), error::wallet_internal_error, "Unexpected hashchain size");
3621  THROW_WALLET_EXCEPTION_IF(m_blockchain.offset() != 0, error::wallet_internal_error,
3622  "Unexpected hashchain offset");
3623  for (const auto &h: tip)
3624  m_blockchain.push_back(h);
3625  short_chain_history.clear();
3626  get_short_chain_history(short_chain_history);
3627  start_height = stop_height;
3628  throw std::runtime_error(""); // loop again
3629  }
3630  catch (const std::exception &e) {
3631  MERROR("Error parsing blocks: " << e.what());
3632  error = true;
3633  }
3634  blocks_fetched += added_blocks;
3635  }
3636  waiter.wait(&tpool);
3637  if (!first && blocks_start_height == next_blocks_start_height) {
3638  m_node_rpc_proxy.set_height(m_blockchain.size());
3639  refreshed = true;
3640  break;
3641  }
3642 
3643  first = false;
3644 
3645  // handle error from async fetching thread
3646  if (error) {
3647  throw std::runtime_error("proxy exception in refresh thread");
3648  }
3649 
3650  // if we've got at least 10 blocks to refresh, assume we're starting
3651  // a long refresh, and setup a tracking output cache if we need to
3652  // We hit create_output_tracker_cache before doing processing our blocks in process_parsed_blocks above( see 'first' variable)
3653  if (m_track_uses && (!output_tracker_cache ||
3654  (output_tracker_cache->first.empty() && output_tracker_cache->second.empty())) &&
3655  next_blocks.size() >= 10)
3656  output_tracker_cache = create_output_tracker_cache();
3657 
3658  // switch to the new blocks from the daemon
3659  blocks_start_height = next_blocks_start_height;
3660  blocks = std::move(next_blocks);
3661  parsed_blocks = std::move(next_parsed_blocks);
3662  }
3663  catch (const tools::error::password_needed &) {
3664  blocks_fetched += added_blocks;
3665  waiter.wait(&tpool);
3666  throw;
3667  }
3668  catch (const std::exception &) {
3669  blocks_fetched += added_blocks;
3670  waiter.wait(&tpool);
3671  if (try_count < 3) {
3672  LOG_PRINT_L1("Another try pull_blocks (try_count=" << try_count << ")...");
3673  first = true;
3674  start_height = 0;
3675  blocks.clear();
3676  parsed_blocks.clear();
3677  short_chain_history.clear();
3678  get_short_chain_history(short_chain_history, 1);
3679  ++try_count;
3680  } else {
3681  LOG_ERROR("pull_blocks failed, try_count=" << try_count);
3682  throw;
3683  }
3684  }
3685  }
3686  if (last_tx_hash_id != (m_transfers.size() ? m_transfers.back().m_txid : null_hash))
3687  received_etn = true;
3688 
3689  try {
3690  // If stop() is called we don't need to check pending transactions
3691  if (check_pool && m_run.load(std::memory_order_relaxed))
3692  update_pool_state(refreshed);
3693  }
3694  catch (...) {
3695  LOG_PRINT_L1("Failed to check pending transactions");
3696  }
3697 
3698  m_first_refresh_done = true;
3699 
3700  LOG_PRINT_L1("Refresh done, blocks received: " << blocks_fetched << ", pre v10 balance (all accounts): "
3701  << print_etn(balance_all(false)) << ", unlocked: "
3702  << print_etn(unlocked_balance_all(false))
3703  << ", post v10 balance (all accounts): "
3704  << print_etn(balance_all(true)) << ", unlocked: "
3705  << print_etn(unlocked_balance_all(true)));
3706 
3707  //get the testnet bridge address - should be same as mainnet because of our netbyte being erroneously set to the same thing when Electroneum was first created
3708  // cryptonote::account_public_address bridge_public_address;
3709  // std::string portal_address_viewkey_hex_str = "5866666666666666666666666666666666666666666666666666666666666666"; //private view is just 0100000000000000000000000000000000000000000000000000000000000000
3710  // std::string portal_address_spendkey_hex_str = "5bd0c0e25eee6133850edd2b255ed9e3d6bb99fd5f08b7b5cf7f2618ad6ff2a3"; //private view is just 0100000000000000000000000000000000000000000000000000000000000000
3711  // epee::string_tools::hex_to_pod(portal_address_viewkey_hex_str, bridge_public_address.m_view_public_key);
3712  // epee::string_tools::hex_to_pod(portal_address_spendkey_hex_str, bridge_public_address.m_spend_public_key);
3713  // std::string bridge_address = cryptonote::get_account_address_as_str(this->nettype(), false, bridge_public_address); //OK
3714 
3715  //generate the coinbase burn address. spendkey is "9511fabcb699b4f9dffc1779713d0dd7eb1ca56ba5b8ab8d3253a0a6ccf736b3", address "etnkCys4uGhSi9h48ajL9vBDJTcn2s2ttXtXq3SXWPAbiMHNhHitu5fJ8QgRfFWTzmJ8QgRfFWTzmJ8QgRfFWTzm4t51HTfCtK"
3716  //cryptonote::account_public_address coinbase_burn_address;
3717  //crypto::hash h;
3718  //crypto::ec_point point;
3719  //epee::string_tools::hex_to_pod("714c8d8eeee5243e7f266e5210f76f58b8b1d6330cedfbc4eda6d5947b212012", h); // genesis hash hex ---> hash type
3720  //crypto::hash_to_point(h, point); // generate curve point (burn address spendkey) deterministically in such a way that we can't recover the private key
3721  //crypto::public_key coinbase_burn_address_spendkey;
3722  //std::copy(std::begin(point.data), std::end(point.data), std::begin(coinbase_burn_address_spendkey.data)); // serialise point to pubkey type
3723  //std::string coinbase_burn_address_spendkey_hex_str = epee::string_tools::pod_to_hex(coinbase_burn_address_spendkey); // for testing only. pub spend =
3724  //std::string coinbase_burn_address_viewkey_hex_str = "5866666666666666666666666666666666666666666666666666666666666666"; //private view is just 0100000000000000000000000000000000000000000000000000000000000000
3725  //coinbase_burn_address.m_spend_public_key = coinbase_burn_address_spendkey;
3726  //epee::string_tools::hex_to_pod(coinbase_burn_address_viewkey_hex_str, coinbase_burn_address.m_view_public_key);
3727  //std::string coinbase_burn_address_str = cryptonote::get_account_address_as_str(this->nettype(), false, coinbase_burn_address); //OK
3728 
3729 
3730  try {
3731  // V9-->V10 PUBLIC MIGRATIONS
3732  // check that the local blockchain height is at least the v10 fork height + 5 blocks (so we know we don't need to scan for any more v1 outputs and they have all have 5 confs)
3733  //todo: write function for wallet that gets the b.major version for a given *local* blockchain height, to save hardcoding heights.
3734  uint64_t migration_minheight = this->nettype() == TESTNET ? 1086402 + 5 : 1175315 + 5;
3735  if (this->get_blockchain_current_height() > migration_minheight && this->unlocked_balance_all(false) != 0) {
3736  LOG_PRINT_L0(
3737  "You are now on the transparent version of Electroneum and so we're giving you the chance to migrate your funds via a sweep transaction back to your address.\n Don't worry, this migration is completely free of charge. Please follow the prompts to continue.");
3738  std::map<uint32_t, std::map<uint32_t, std::pair<uint64_t, uint64_t>>> unlocked_balance_per_subaddress_per_account; // map of: account index ----> (subaddress index, pair(u-balance, unlock time))
3739  // for each account, grab all of the subaddress info (index, (balance, unlock))
3740  for (uint32_t account_index = 0; account_index < this->get_num_subaddress_accounts(); ++account_index) {
3741  unlocked_balance_per_subaddress_per_account[account_index] = this->unlocked_balance_per_subaddress(
3742  account_index, false);
3743  }
3744  for (uint32_t i = 0; i < this->get_num_subaddress_accounts(); i++) {
3746  index.major = i;
3747  for (auto subaddress: unlocked_balance_per_subaddress_per_account[i]) {
3748  index.minor = subaddress.first;
3749 
3750  if (subaddress.second.first != 0 &&
3751  subaddress.second.second == 0/*is there a fully unlocked nonzero balance /sanity check*/) {
3752  cryptonote::account_public_address address = get_subaddress(index); // BRIDGE PORTAL ADDRESS
3753  std::set<uint32_t> subaddress_source{index.minor};
3754  std::vector<wallet2::pending_tx> ptx_vector = this->create_transactions_all(0,
3755  address /*dest address*/,
3756  index.major !=
3757  0 ||
3758  index.minor !=
3759  0 /*is dest a subaddress*/,
3760  1 /*one output only*/,
3761  0 /* don't mix*/,
3762  0 /*default unlock time*/,
3763  4 /*highest priority*/,
3764  vector<uint8_t>() /*empty tx extra */,
3765  index.major /*account index*/,
3766  subaddress_source /*source subaddr index*/,
3767  true /*migrate*/);
3768  this->commit_tx(ptx_vector);
3769  }
3770  }
3771  }
3772  LOG_PRINT_L0("Migration to the public version of the blockchain has completed. Please use the command show_transfers (CLI Wallet) or get_transfers (RPC Wallet) to see the details of your migration transactions.");
3773  }
3774 
3775  } catch(...) {
3776  THROW_WALLET_EXCEPTION(error::wallet_internal_error, "V9 (Privatised)-->V10 (Public) wallet migration failed.");
3777  }
3778 
3779  try {
3780  // V10 Migration to Electroneum Smart Chain
3781  cryptonote::account_public_address portal_address;
3782  std::string portal_address_viewkey_hex_str;
3783  std::string portal_address_spendkey_hex_str;
3784  if(m_nettype == MAINNET){
3785  portal_address_viewkey_hex_str = "2b95a2eb2c62253c57e82b082b850bbf22a1a7829aaea09c7c1511c1cced4375";
3786  portal_address_spendkey_hex_str = "8ce0f34fd37c7f7d07c44024eb5b3cdf275d1b3e75c3464b808dce532e861137";
3787  }else{
3788  portal_address_viewkey_hex_str = "5866666666666666666666666666666666666666666666666666666666666666"; //private view is just 0100000000000000000000000000000000000000000000000000000000000000
3789  portal_address_spendkey_hex_str = "5bd0c0e25eee6133850edd2b255ed9e3d6bb99fd5f08b7b5cf7f2618ad6ff2a3"; //
3790  }
3791 
3792  bool portal_wallet = //if the portal address wallet ever needs opening, don't allow it to sweep to itself
3793  epee::string_tools::pod_to_hex(get_address().m_spend_public_key) ==
3794  portal_address_spendkey_hex_str &&
3795  epee::string_tools::pod_to_hex(get_address().m_view_public_key) == portal_address_viewkey_hex_str;
3796 
3797  epee::string_tools::hex_to_pod(portal_address_spendkey_hex_str, portal_address.m_spend_public_key);
3798  epee::string_tools::hex_to_pod(portal_address_viewkey_hex_str, portal_address.m_view_public_key);
3799 
3800  // ONLY do migration transactions after the fork block
3801  uint64_t smartchain_migration_minheight = this->nettype() == MAINNET ? 1811310 : 1455270;
3802  if (this->get_blockchain_current_height() > smartchain_migration_minheight) {
3803  // check that unlocked balance = unlocked balance as a best-effort to ensure that we're not migrating the funds whilst more are in transit/confirming
3804  if ((!portal_wallet) && (this->balance_all(true) != 0) &&
3805  (this->unlocked_balance_all(true) == this->balance_all(true))) {
3806  std::cout << std::endl << "You are beginning your token migration over to the Electroneum Smart Chain." << std::endl;
3807  std::cout << "This transaction is feeless. For further information, please read our documentation over at https:///developer.electroneum.com/migration-to-smart-chain/overview" << std::endl;
3808  std::map<uint32_t, std::map<uint32_t, std::pair<uint64_t, uint64_t>>> unlocked_balance_per_subaddress_per_account; // map of: account index ----> (subaddress index, pair(u-balance, unlock time))
3809  // for each account, grab all of the subaddress info (index, (balance, unlock))
3810  for (uint32_t account_index = 0;
3811  account_index < this->get_num_subaddress_accounts(); ++account_index) {
3812  unlocked_balance_per_subaddress_per_account[account_index] = this->unlocked_balance_per_subaddress(
3813  account_index, true);
3814  }
3815  for (uint32_t i = 0; i < this->get_num_subaddress_accounts(); i++) {
3817  index.major = i;
3818  for (auto subaddress: unlocked_balance_per_subaddress_per_account[i]) {
3819  index.minor = subaddress.first;
3820 
3821  if (subaddress.second.first != 0 &&
3822  subaddress.second.second ==
3823  0/*is there a fully unlocked nonzero balance /sanity check*/) {
3824  std::set<uint32_t> subaddress_source{index.minor};
3825  std::vector<wallet2::pending_tx> ptx_vector = this->create_transactions_all(0,
3826  portal_address /*dest address (portal address for bridge)*/,
3827  0 /*is dest a subaddress*/,
3828  1 /*one output only*/, //???????
3829  0 /* don't mix*/,
3830  0 /*default unlock time*/,
3831  1 /*priority - set low in case they don't have fees for high priority but do for low priority*/,
3832  vector<uint8_t>() /*empty tx extra */,
3833  index.major /*account index*/,
3834  subaddress_source /*source subaddr index*/,
3835  false /*migrate to transparent chain*/);
3836  this->commit_tx(ptx_vector);
3837  }
3838  }
3839  }
3840  std::cout << std::endl;
3841  std::cout << "Migration to Smart Chain portal address completed. Please use the command show_transfers (CLI Wallet) or get_transfers (RPC Wallet) to see the details of your Smart Chain migration transactions." << std::endl;
3842  std::cout << "Please note that the entire migration process is not instant and your funds may take some time to show up in the Smart Chain." << std::endl;
3843  std::cout << "You can find your SmartChain address using the \"spendkey\" command in the CLI wallet." << std::endl;
3844  }
3845  }
3846  } catch(...) {
3847  THROW_WALLET_EXCEPTION(error::wallet_internal_error, "V10 Smart Chain migration failed.");
3848  }
3849 }
3850 //----------------------------------------------------------------------------------------------------
3851 bool wallet2::refresh(bool trusted_daemon, uint64_t & blocks_fetched, bool& received_etn, bool& ok)
3852 {
3853  try
3854  {
3855  refresh(trusted_daemon, 0, blocks_fetched, received_etn);
3856  ok = true;
3857  }
3858  catch (...)
3859  {
3860  ok = false;
3861  }
3862  return ok;
3863 }
3864 //----------------------------------------------------------------------------------------------------
3865 bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector<uint64_t> &distribution)
3866 {
3867  uint32_t rpc_version;
3868  boost::optional<std::string> result = m_node_rpc_proxy.get_rpc_version(rpc_version);
3869  // no error
3870  if (!!result)
3871  {
3872  // empty string -> not connection
3873  THROW_WALLET_EXCEPTION_IF(result->empty(), tools::error::no_connection_to_daemon, "getversion");
3875  if (*result != CORE_RPC_STATUS_OK)
3876  {
3877  MDEBUG("Cannot determine daemon RPC version, not requesting rct distribution");
3878  return false;
3879  }
3880  }
3881  else
3882  {
3883  if (rpc_version >= MAKE_CORE_RPC_VERSION(1, 19))
3884  {
3885  MDEBUG("Daemon is recent enough, requesting rct distribution");
3886  }
3887  else
3888  {
3889  MDEBUG("Daemon is too old, not requesting rct distribution");
3890  return false;
3891  }
3892  }
3893 
3896  req.amounts.push_back(0);
3897  req.from_height = 0;
3898  req.cumulative = false;
3899  req.binary = true;
3900  req.compress = true;
3901  m_daemon_rpc_mutex.lock();
3902  bool r = invoke_http_bin("/get_output_distribution.bin", req, res, rpc_timeout);
3903  m_daemon_rpc_mutex.unlock();
3904  if (!r)
3905  {
3906  MWARNING("Failed to request output distribution: no connection to daemon");
3907  return false;
3908  }
3909  if (res.status == CORE_RPC_STATUS_BUSY)
3910  {
3911  MWARNING("Failed to request output distribution: daemon is busy");
3912  return false;
3913  }
3914  if (res.status != CORE_RPC_STATUS_OK)
3915  {
3916  MWARNING("Failed to request output distribution: " << res.status);
3917  return false;
3918  }
3919  if (res.distributions.size() != 1)
3920  {
3921  MWARNING("Failed to request output distribution: not the expected single result");
3922  return false;
3923  }
3924  if (res.distributions[0].amount != 0)
3925  {
3926  MWARNING("Failed to request output distribution: results are not for amount 0");
3927  return false;
3928  }
3929  for (size_t i = 1; i < res.distributions[0].data.distribution.size(); ++i)
3930  res.distributions[0].data.distribution[i] += res.distributions[0].data.distribution[i-1];
3931  start_height = res.distributions[0].data.start_height;
3932  distribution = std::move(res.distributions[0].data.distribution);
3933  return true;
3934 }
3935 //----------------------------------------------------------------------------------------------------
3936 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)
3937 {
3938  LOG_PRINT_L0("Detaching blockchain on height " << height);
3939 
3940  // size 1 2 3 4 5 6 7 8 9
3941  // block 0 1 2 3 4 5 6 7 8
3942  // C
3943  THROW_WALLET_EXCEPTION_IF(height < m_blockchain.offset() && m_blockchain.size() > m_blockchain.offset(),
3944  error::wallet_internal_error, "Daemon claims reorg below last checkpoint");
3945 
3946  size_t transfers_detached = 0;
3947 
3948  for (size_t i = 0; i < m_transfers.size(); ++i)
3949  {
3950  wallet2::transfer_details &td = m_transfers[i];
3951  if (td.m_spent && td.m_spent_height >= height)
3952  {
3953  if(td.m_tx.version == 1){ // we're resetting chainstate indexes for ver > 1
3954  LOG_PRINT_L1("Resetting spent/frozen status for output " << i << ": " << td.m_key_image);
3955  }else{
3956  LOG_PRINT_L1("Resetting spent/frozen status for output "
3957  << i << ": " << "chainstate index " << td.m_txid <<": " << td.m_internal_output_index);
3958  }
3959  set_unspent(i);
3960  thaw(i);
3961  }
3962  }
3963 
3964  for (transfer_details &td: m_transfers)
3965  {
3966  while (!td.m_uses.empty() && td.m_uses.back().first >= height)
3967  td.m_uses.pop_back();
3968  }
3969 
3970  if (output_tracker_cache) {
3971  output_tracker_cache->first.clear();
3972  output_tracker_cache->second.clear();
3973  }
3974 
3975  auto it = std::find_if(m_transfers.begin(), m_transfers.end(), [&](const transfer_details& td){return td.m_block_height >= height;});
3976  size_t i_start = it - m_transfers.begin();
3977 
3978  for(size_t i = i_start; i!= m_transfers.size();i++)
3979  {
3980  if (!m_transfers[i].m_key_image_known || m_transfers[i].m_key_image_partial)
3981  continue;
3982  auto it_ki = m_key_images.find(m_transfers[i].m_key_image);
3983  THROW_WALLET_EXCEPTION_IF(it_ki == m_key_images.end(), error::wallet_internal_error, "key image not found: index " + std::to_string(i) + ", ki " + epee::string_tools::pod_to_hex(m_transfers[i].m_key_image) + ", " + std::to_string(m_key_images.size()) + " key images known");
3984  m_key_images.erase(it_ki);
3985  }
3986 
3987  for(size_t i = i_start; i!= m_transfers.size();i++)
3988  {
3989  auto it_pk = m_pub_keys.find(m_transfers[i].get_public_key());
3990  THROW_WALLET_EXCEPTION_IF(it_pk == m_pub_keys.end(), error::wallet_internal_error, "public key not found");
3991  m_pub_keys.erase(it_pk);
3992  }
3993 
3994  for(size_t i = i_start; i!= m_transfers.size();i++)
3995  {
3996  auto it_pk = m_chainstate_indexes.find(m_transfers[i].get_chainstate_index());
3997  if(m_transfers[i].m_tx.version > 1) {
3998  THROW_WALLET_EXCEPTION_IF(it_pk == m_chainstate_indexes.end(), error::wallet_internal_error,
3999  "chainstate index not found");
4000  m_chainstate_indexes.erase(it_pk);
4001  }else{
4002  continue;
4003  }
4004  }
4005 
4006 
4007  m_transfers.erase(it, m_transfers.end());
4008 
4009  size_t blocks_detached = m_blockchain.size() - height;
4010  m_blockchain.crop(height);
4011 
4012  for (auto it = m_payments.begin(); it != m_payments.end(); )
4013  {
4014  if(height <= it->second.m_block_height)
4015  it = m_payments.erase(it);
4016  else
4017  ++it;
4018  }
4019 
4020  for (auto it = m_confirmed_txs.begin(); it != m_confirmed_txs.end(); )
4021  {
4022  if(height <= it->second.m_block_height)
4023  it = m_confirmed_txs.erase(it);
4024  else
4025  ++it;
4026  }
4027 
4028  LOG_PRINT_L0("Detached blockchain on height " << height << ", transfers detached " << transfers_detached << ", blocks detached " << blocks_detached);
4029 }
4030 //----------------------------------------------------------------------------------------------------
4032 {
4033  m_is_initialized=false;
4034  unlock_keys_file();
4035  m_account.deinit();
4036  return true;
4037 }
4038 //----------------------------------------------------------------------------------------------------
4039 bool wallet2::clear()
4040 {
4041  m_blockchain.clear();
4042  m_transfers.clear();
4043  m_key_images.clear();
4044  m_pub_keys.clear();
4045  m_chainstate_indexes.clear();
4046  m_unconfirmed_txs.clear();
4047  m_payments.clear();
4048  m_tx_keys.clear();
4049  m_additional_tx_keys.clear();
4050  m_confirmed_txs.clear();
4051  m_unconfirmed_payments.clear();
4052  m_scanned_pool_txs[0].clear();
4053  m_scanned_pool_txs[1].clear();
4054  m_address_book.clear();
4055  m_subaddresses.clear();
4056  m_subaddress_labels.clear();
4057  m_multisig_rounds_passed = 0;
4058  m_device_last_key_image_sync = 0;
4059  return true;
4060 }
4061 //----------------------------------------------------------------------------------------------------
4062 void wallet2::clear_soft(bool keep_key_images)
4063 {
4064  m_blockchain.clear();
4065  m_transfers.clear();
4066  if (!keep_key_images)
4067  m_key_images.clear();
4068  m_pub_keys.clear();
4069  m_chainstate_indexes.clear();
4070  m_unconfirmed_txs.clear();
4071  m_payments.clear();
4072  m_confirmed_txs.clear();
4073  m_unconfirmed_payments.clear();
4074  m_scanned_pool_txs[0].clear();
4075  m_scanned_pool_txs[1].clear();
4076 
4078  generate_genesis(b);
4079  m_blockchain.push_back(get_block_hash(b));
4080  m_last_block_reward = cryptonote::get_outs_etn_amount(b.miner_tx);
4081 }
4082 
4090 bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable_string& password, bool watch_only)
4091 {
4092  std::string account_data;
4093  std::string multisig_signers;
4094  std::string multisig_derivations;
4095  cryptonote::account_base account = m_account;
4096 
4097  crypto::chacha_key key;
4098  crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
4099 
4100  if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
4101  {
4102  account.encrypt_viewkey(key);
4103  account.decrypt_keys(key);
4104  }
4105 
4106  if (watch_only)
4107  account.forget_spend_key();
4108 
4109  account.encrypt_keys(key);
4110 
4111  bool r = epee::serialization::store_t_to_binary(account, account_data);
4112  CHECK_AND_ASSERT_MES(r, false, "failed to serialize wallet keys");
4113  wallet2::keys_file_data keys_file_data = boost::value_initialized<wallet2::keys_file_data>();
4114 
4115  // Create a JSON object with "key_data" and "seed_language" as keys.
4117  json.SetObject();
4119  value.SetString(account_data.c_str(), account_data.length());
4120  json.AddMember("key_data", value, json.GetAllocator());
4121  if (!seed_language.empty())
4122  {
4123  value.SetString(seed_language.c_str(), seed_language.length());
4124  json.AddMember("seed_language", value, json.GetAllocator());
4125  }
4126 
4128 
4129  value2.SetInt(m_key_device_type);
4130  json.AddMember("key_on_device", value2, json.GetAllocator());
4131 
4132  value2.SetInt(watch_only ? 1 :0); // WTF ? JSON has different true and false types, and not boolean ??
4133  json.AddMember("watch_only", value2, json.GetAllocator());
4134 
4135  value2.SetInt(m_multisig ? 1 :0);
4136  json.AddMember("multisig", value2, json.GetAllocator());
4137 
4138  value2.SetUint(m_multisig_threshold);
4139  json.AddMember("multisig_threshold", value2, json.GetAllocator());
4140 
4141  if (m_multisig)
4142  {
4143  bool r = ::serialization::dump_binary(m_multisig_signers, multisig_signers);
4144  CHECK_AND_ASSERT_MES(r, false, "failed to serialize wallet multisig signers");
4145  value.SetString(multisig_signers.c_str(), multisig_signers.length());
4146  json.AddMember("multisig_signers", value, json.GetAllocator());
4147 
4148  r = ::serialization::dump_binary(m_multisig_derivations, multisig_derivations);
4149  CHECK_AND_ASSERT_MES(r, false, "failed to serialize wallet multisig derivations");
4150  value.SetString(multisig_derivations.c_str(), multisig_derivations.length());
4151  json.AddMember("multisig_derivations", value, json.GetAllocator());
4152 
4153  value2.SetUint(m_multisig_rounds_passed);
4154  json.AddMember("multisig_rounds_passed", value2, json.GetAllocator());
4155  }
4156 
4157  value2.SetInt(m_always_confirm_transfers ? 1 :0);
4158  json.AddMember("always_confirm_transfers", value2, json.GetAllocator());
4159 
4160  value2.SetInt(m_print_ring_members ? 1 :0);
4161  json.AddMember("print_ring_members", value2, json.GetAllocator());
4162 
4163  value2.SetInt(m_store_tx_info ? 1 :0);
4164  json.AddMember("store_tx_info", value2, json.GetAllocator());
4165 
4166  value2.SetUint(m_default_mixin);
4167  json.AddMember("default_mixin", value2, json.GetAllocator());
4168 
4169  value2.SetUint(m_default_priority);
4170  json.AddMember("default_priority", value2, json.GetAllocator());
4171 
4172  value2.SetInt(m_auto_refresh ? 1 :0);
4173  json.AddMember("auto_refresh", value2, json.GetAllocator());
4174 
4175  value2.SetInt(m_refresh_type);
4176  json.AddMember("refresh_type", value2, json.GetAllocator());
4177 
4178  value2.SetUint64(m_refresh_from_block_height);
4179  json.AddMember("refresh_height", value2, json.GetAllocator());
4180 
4181  value2.SetInt(m_confirm_missing_payment_id ? 1 :0);
4182  json.AddMember("confirm_missing_payment_id", value2, json.GetAllocator());
4183 
4184  value2.SetInt(m_confirm_non_default_ring_size ? 1 :0);
4185  json.AddMember("confirm_non_default_ring_size", value2, json.GetAllocator());
4186 
4187  value2.SetInt(m_ask_password);
4188  json.AddMember("ask_password", value2, json.GetAllocator());
4189 
4190  value2.SetUint(m_min_output_count);
4191  json.AddMember("min_output_count", value2, json.GetAllocator());
4192 
4193  value2.SetUint64(m_min_output_value);
4194  json.AddMember("min_output_value", value2, json.GetAllocator());
4195 
4196  value2.SetInt(cryptonote::get_default_decimal_point());
4197  json.AddMember("default_decimal_point", value2, json.GetAllocator());
4198 
4199  value2.SetInt(m_merge_destinations ? 1 :0);
4200  json.AddMember("merge_destinations", value2, json.GetAllocator());
4201 
4202  value2.SetInt(m_confirm_backlog ? 1 :0);
4203  json.AddMember("confirm_backlog", value2, json.GetAllocator());
4204 
4205  value2.SetUint(m_confirm_backlog_threshold);
4206  json.AddMember("confirm_backlog_threshold", value2, json.GetAllocator());
4207 
4208  value2.SetInt(m_confirm_export_overwrite ? 1 :0);
4209  json.AddMember("confirm_export_overwrite", value2, json.GetAllocator());
4210 
4211  value2.SetInt(m_auto_low_priority ? 1 : 0);
4212  json.AddMember("auto_low_priority", value2, json.GetAllocator());
4213 
4214  value2.SetUint(m_nettype);
4215  json.AddMember("nettype", value2, json.GetAllocator());
4216 
4217  value2.SetInt(m_segregate_pre_fork_outputs ? 1 : 0);
4218  json.AddMember("segregate_pre_fork_outputs", value2, json.GetAllocator());
4219 
4220  value2.SetInt(m_key_reuse_mitigation2 ? 1 : 0);
4221  json.AddMember("key_reuse_mitigation2", value2, json.GetAllocator());
4222 
4223  value2.SetUint(m_segregation_height);
4224  json.AddMember("segregation_height", value2, json.GetAllocator());
4225 
4226  value2.SetInt(m_ignore_fractional_outputs ? 1 : 0);
4227  json.AddMember("ignore_fractional_outputs", value2, json.GetAllocator());
4228 
4229  value2.SetInt(m_track_uses ? 1 : 0);
4230  json.AddMember("track_uses", value2, json.GetAllocator());
4231 
4232  value2.SetInt(m_setup_background_mining);
4233  json.AddMember("setup_background_mining", value2, json.GetAllocator());
4234 
4235  value2.SetUint(m_subaddress_lookahead_major);
4236  json.AddMember("subaddress_lookahead_major", value2, json.GetAllocator());
4237 
4238  value2.SetUint(m_subaddress_lookahead_minor);
4239  json.AddMember("subaddress_lookahead_minor", value2, json.GetAllocator());
4240 
4241  value2.SetInt(m_original_keys_available ? 1 : 0);
4242  json.AddMember("original_keys_available", value2, json.GetAllocator());
4243 
4244  value2.SetUint(1);
4245  json.AddMember("encrypted_secret_keys", value2, json.GetAllocator());
4246 
4247  value.SetString(m_device_name.c_str(), m_device_name.size());
4248  json.AddMember("device_name", value, json.GetAllocator());
4249 
4250  value.SetString(m_device_derivation_path.c_str(), m_device_derivation_path.size());
4251  json.AddMember("device_derivation_path", value, json.GetAllocator());
4252 
4253  value2.SetUint(m_account_major_offset);
4254  json.AddMember("account_major_offset", value2, json.GetAllocator());
4255 
4256  std::string original_address;
4257  std::string original_view_secret_key;
4258  if (m_original_keys_available)
4259  {
4260  original_address = get_account_address_as_str(m_nettype, false, m_original_address);
4261  value.SetString(original_address.c_str(), original_address.length());
4262  json.AddMember("original_address", value, json.GetAllocator());
4263  original_view_secret_key = epee::string_tools::pod_to_hex(m_original_view_secret_key);
4264  value.SetString(original_view_secret_key.c_str(), original_view_secret_key.length());
4265  json.AddMember("original_view_secret_key", value, json.GetAllocator());
4266  }
4267 
4268  // Serialize the JSON object
4269  rapidjson::StringBuffer buffer;
4270  rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
4271  json.Accept(writer);
4272  account_data = buffer.GetString();
4273 
4274  // Encrypt the entire JSON object.
4275  std::string cipher;
4276  cipher.resize(account_data.size());
4277  keys_file_data.iv = crypto::rand<crypto::chacha_iv>();
4278  crypto::chacha20(account_data.data(), account_data.size(), key, keys_file_data.iv, &cipher[0]);
4279  keys_file_data.account_data = cipher;
4280 
4281  std::string tmp_file_name = keys_file_name + ".new";
4282  std::string buf;
4283  r = ::serialization::dump_binary(keys_file_data, buf);
4284  r = r && epee::file_io_utils::save_string_to_file(tmp_file_name, buf);
4285  CHECK_AND_ASSERT_MES(r, false, "failed to generate wallet keys file " << tmp_file_name);
4286 
4287  unlock_keys_file();
4288  std::error_code e = tools::replace_file(tmp_file_name, keys_file_name);
4289  lock_keys_file();
4290 
4291  if (e) {
4292  boost::filesystem::remove(tmp_file_name);
4293  LOG_ERROR("failed to update wallet keys file " << keys_file_name);
4294  return false;
4295  }
4296 
4297  return true;
4298 }
4299 //----------------------------------------------------------------------------------------------------
4300 void wallet2::setup_keys(const epee::wipeable_string &password)
4301 {
4302  crypto::chacha_key key;
4303  crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
4304 
4305  // re-encrypt, but keep viewkey unencrypted
4306  if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
4307  {
4308  m_account.encrypt_keys(key);
4309  m_account.decrypt_viewkey(key);
4310  }
4311 
4312  static_assert(HASH_SIZE == sizeof(crypto::chacha_key), "Mismatched sizes of hash and chacha key");
4314  memcpy(cache_key_data.data(), &key, HASH_SIZE);
4315  cache_key_data[HASH_SIZE] = CACHE_KEY_TAIL;
4316  cn_fast_hash(cache_key_data.data(), HASH_SIZE+1, (crypto::hash&)m_cache_key);
4317  get_ringdb_key();
4318 }
4319 //----------------------------------------------------------------------------------------------------
4320 void wallet2::change_password(const std::string &filename, const epee::wipeable_string &original_password, const epee::wipeable_string &new_password)
4321 {
4322  if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
4323  decrypt_keys(original_password);
4324  setup_keys(new_password);
4325  rewrite(filename, new_password);
4326  if (!filename.empty())
4327  store();
4328 }
4329 //----------------------------------------------------------------------------------------------------
4335 bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_string& password)
4336 {
4338  wallet2::keys_file_data keys_file_data;
4339  std::string buf;
4340  bool encrypted_secret_keys = false;
4341  bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf);
4343 
4344  // Decrypt the contents
4345  r = ::serialization::parse_binary(buf, keys_file_data);
4346  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + keys_file_name + '\"');
4347  crypto::chacha_key key;
4348  crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
4349  std::string account_data;
4350  account_data.resize(keys_file_data.account_data.size());
4351  crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
4352  if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject())
4353  crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
4354 
4355  // The contents should be JSON if the wallet follows the new format.
4356  if (json.Parse(account_data.c_str()).HasParseError())
4357  {
4358  is_old_file_format = true;
4359  m_watch_only = false;
4360  m_multisig = false;
4361  m_multisig_threshold = 0;
4362  m_multisig_signers.clear();
4363  m_multisig_rounds_passed = 0;
4364  m_multisig_derivations.clear();
4365  m_always_confirm_transfers = true;
4366  m_print_ring_members = false;
4367  m_store_tx_info = true;
4368  m_default_mixin = 0;
4369  m_default_priority = 0;
4370  m_auto_refresh = true;
4371  m_refresh_type = RefreshType::RefreshDefault;
4372  m_refresh_from_block_height = 0;
4373  m_confirm_missing_payment_id = true;
4374  m_confirm_non_default_ring_size = true;
4375  m_ask_password = AskPasswordOnAction;
4377  m_min_output_count = 0;
4378  m_min_output_value = 0;
4379  m_merge_destinations = false;
4380  m_confirm_backlog = true;
4381  m_confirm_backlog_threshold = 0;
4382  m_confirm_export_overwrite = true;
4383  m_auto_low_priority = true;
4384  m_segregate_pre_fork_outputs = true;
4385  m_key_reuse_mitigation2 = true;
4386  m_segregation_height = 0;
4387  m_ignore_fractional_outputs = true;
4388  m_track_uses = false;
4389  m_setup_background_mining = BackgroundMiningMaybe;
4390  m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR;
4391  m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR;
4392  m_original_keys_available = false;
4393  m_device_name = "";
4394  m_device_derivation_path = "";
4395  m_key_device_type = hw::device::device_type::SOFTWARE;
4396  m_account_major_offset = 0;
4397  encrypted_secret_keys = false;
4398  }
4399  else if(json.IsObject())
4400  {
4401  if (!json.HasMember("key_data"))
4402  {
4403  LOG_ERROR("Field key_data not found in JSON");
4404  return false;
4405  }
4406  if (!json["key_data"].IsString())
4407  {
4408  LOG_ERROR("Field key_data found in JSON, but not String");
4409  return false;
4410  }
4411  const char *field_key_data = json["key_data"].GetString();
4412  account_data = std::string(field_key_data, field_key_data + json["key_data"].GetStringLength());
4413 
4414  if (json.HasMember("key_on_device"))
4415  {
4416  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, key_on_device, int, Int, false, hw::device::device_type::SOFTWARE);
4417  m_key_device_type = static_cast<hw::device::device_type>(field_key_on_device);
4418  }
4419 
4420  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, seed_language, std::string, String, false, std::string());
4421  if (field_seed_language_found)
4422  {
4423  set_seed_language(field_seed_language);
4424  }
4425  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, watch_only, int, Int, false, false);
4426  m_watch_only = field_watch_only;
4427  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, multisig, int, Int, false, false);
4428  m_multisig = field_multisig;
4429  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, multisig_threshold, unsigned int, Uint, m_multisig, 0);
4430  m_multisig_threshold = field_multisig_threshold;
4431  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, multisig_rounds_passed, unsigned int, Uint, false, 0);
4432  m_multisig_rounds_passed = field_multisig_rounds_passed;
4433  if (m_multisig)
4434  {
4435  if (!json.HasMember("multisig_signers"))
4436  {
4437  LOG_ERROR("Field multisig_signers not found in JSON");
4438  return false;
4439  }
4440  if (!json["multisig_signers"].IsString())
4441  {
4442  LOG_ERROR("Field multisig_signers found in JSON, but not String");
4443  return false;
4444  }
4445  const char *field_multisig_signers = json["multisig_signers"].GetString();
4446  std::string multisig_signers = std::string(field_multisig_signers, field_multisig_signers + json["multisig_signers"].GetStringLength());
4447  r = ::serialization::parse_binary(multisig_signers, m_multisig_signers);
4448  if (!r)
4449  {
4450  LOG_ERROR("Field multisig_signers found in JSON, but failed to parse");
4451  return false;
4452  }
4453 
4454  //previous version of multisig does not have this field
4455  if (json.HasMember("multisig_derivations"))
4456  {
4457  if (!json["multisig_derivations"].IsString())
4458  {
4459  LOG_ERROR("Field multisig_derivations found in JSON, but not String");
4460  return false;
4461  }
4462  const char *field_multisig_derivations = json["multisig_derivations"].GetString();
4463  std::string multisig_derivations = std::string(field_multisig_derivations, field_multisig_derivations + json["multisig_derivations"].GetStringLength());
4464  r = ::serialization::parse_binary(multisig_derivations, m_multisig_derivations);
4465  if (!r)
4466  {
4467  LOG_ERROR("Field multisig_derivations found in JSON, but failed to parse");
4468  return false;
4469  }
4470  }
4471  }
4472  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, always_confirm_transfers, int, Int, false, true);
4473  m_always_confirm_transfers = field_always_confirm_transfers;
4474  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, print_ring_members, int, Int, false, true);
4475  m_print_ring_members = field_print_ring_members;
4476  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_keys, int, Int, false, true);
4477  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_info, int, Int, false, true);
4478  m_store_tx_info = ((field_store_tx_keys != 0) || (field_store_tx_info != 0));
4479  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_mixin, unsigned int, Uint, false, 0);
4480  m_default_mixin = field_default_mixin;
4481  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_priority, unsigned int, Uint, false, 0);
4482  if (field_default_priority_found)
4483  {
4484  m_default_priority = field_default_priority;
4485  }
4486  else
4487  {
4488  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_fee_multiplier, unsigned int, Uint, false, 0);
4489  if (field_default_fee_multiplier_found)
4490  m_default_priority = field_default_fee_multiplier;
4491  else
4492  m_default_priority = 0;
4493  }
4494  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, auto_refresh, int, Int, false, true);
4495  m_auto_refresh = field_auto_refresh;
4496  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, refresh_type, int, Int, false, RefreshType::RefreshDefault);
4497  m_refresh_type = RefreshType::RefreshDefault;
4498  if (field_refresh_type_found)
4499  {
4500  if (field_refresh_type == RefreshFull || field_refresh_type == RefreshOptimizeCoinbase || field_refresh_type == RefreshNoCoinbase)
4501  m_refresh_type = (RefreshType)field_refresh_type;
4502  else
4503  LOG_PRINT_L0("Unknown refresh-type value (" << field_refresh_type << "), using default");
4504  }
4505  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, refresh_height, uint64_t, Uint64, false, 0);
4506  m_refresh_from_block_height = field_refresh_height;
4507  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_missing_payment_id, int, Int, false, true);
4508  m_confirm_missing_payment_id = field_confirm_missing_payment_id;
4509  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_non_default_ring_size, int, Int, false, true);
4510  m_confirm_non_default_ring_size = field_confirm_non_default_ring_size;
4511  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ask_password, AskPasswordType, Int, false, AskPasswordToDecrypt);
4512  m_ask_password = field_ask_password;
4513  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_decimal_point, int, Int, false, CRYPTONOTE_DISPLAY_DECIMAL_POINT);
4514  cryptonote::set_default_decimal_point(field_default_decimal_point);
4515  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, min_output_count, uint32_t, Uint, false, 0);
4516  m_min_output_count = field_min_output_count;
4517  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, min_output_value, uint64_t, Uint64, false, 0);
4518  m_min_output_value = field_min_output_value;
4519  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, merge_destinations, int, Int, false, false);
4520  m_merge_destinations = field_merge_destinations;
4521  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_backlog, int, Int, false, true);
4522  m_confirm_backlog = field_confirm_backlog;
4523  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_backlog_threshold, uint32_t, Uint, false, 0);
4524  m_confirm_backlog_threshold = field_confirm_backlog_threshold;
4525  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_export_overwrite, int, Int, false, true);
4526  m_confirm_export_overwrite = field_confirm_export_overwrite;
4527  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, auto_low_priority, int, Int, false, true);
4528  m_auto_low_priority = field_auto_low_priority;
4529  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, nettype, uint8_t, Uint, false, static_cast<uint8_t>(m_nettype));
4530  // The network type given in the program argument is inconsistent with the network type saved in the wallet
4531  THROW_WALLET_EXCEPTION_IF(static_cast<uint8_t>(m_nettype) != field_nettype, error::wallet_internal_error,
4532  (boost::format("%s wallet cannot be opened as %s wallet")
4533  % (field_nettype == 0 ? "Mainnet" : field_nettype == 1 ? "Testnet" : "Stagenet")
4534  % (m_nettype == MAINNET ? "mainnet" : m_nettype == TESTNET ? "testnet" : "stagenet")).str());
4535  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, segregate_pre_fork_outputs, int, Int, false, true);
4536  m_segregate_pre_fork_outputs = field_segregate_pre_fork_outputs;
4537  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, key_reuse_mitigation2, int, Int, false, true);
4538  m_key_reuse_mitigation2 = field_key_reuse_mitigation2;
4539  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, segregation_height, int, Uint, false, 0);
4540  m_segregation_height = field_segregation_height;
4541  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ignore_fractional_outputs, int, Int, false, true);
4542  m_ignore_fractional_outputs = field_ignore_fractional_outputs;
4543  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, track_uses, int, Int, false, false);
4544  m_track_uses = field_track_uses;
4545  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, setup_background_mining, BackgroundMiningSetupType, Int, false, BackgroundMiningMaybe);
4546  m_setup_background_mining = field_setup_background_mining;
4547  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_major, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MAJOR);
4548  m_subaddress_lookahead_major = field_subaddress_lookahead_major;
4549  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_minor, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MINOR);
4550  m_subaddress_lookahead_minor = field_subaddress_lookahead_minor;
4551 
4552  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, encrypted_secret_keys, uint32_t, Uint, false, false);
4553  encrypted_secret_keys = field_encrypted_secret_keys;
4554 
4555  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, account_major_offset, uint32_t, Uint, false, 0);
4556  m_account_major_offset = field_account_major_offset;
4557 
4558  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, device_name, std::string, String, false, std::string());
4559  if (m_device_name.empty())
4560  {
4561  if (field_device_name_found)
4562  {
4563  m_device_name = field_device_name;
4564  }
4565  else
4566  {
4567  m_device_name = m_key_device_type == hw::device::device_type::LEDGER ? "Ledger" : "default";
4568  }
4569  }
4570 
4571  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, device_derivation_path, std::string, String, false, std::string());
4572  m_device_derivation_path = field_device_derivation_path;
4573 
4574  if (json.HasMember("original_keys_available"))
4575  {
4576  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, original_keys_available, int, Int, false, false);
4577  m_original_keys_available = field_original_keys_available;
4578  if (m_original_keys_available)
4579  {
4580  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, original_address, std::string, String, true, std::string());
4582  bool ok = get_account_address_from_str(info, m_nettype, field_original_address);
4583  if (!ok)
4584  {
4585  LOG_ERROR("Failed to parse original_address from JSON");
4586  return false;
4587  }
4588  m_original_address = info.address;
4589  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, original_view_secret_key, std::string, String, true, std::string());
4590  ok = epee::string_tools::hex_to_pod(field_original_view_secret_key, m_original_view_secret_key);
4591  if (!ok)
4592  {
4593  LOG_ERROR("Failed to parse original_view_secret_key from JSON");
4594  return false;
4595  }
4596  }
4597  }
4598  else
4599  {
4600  m_original_keys_available = false;
4601  }
4602  }
4603  else
4604  {
4605  THROW_WALLET_EXCEPTION(error::wallet_internal_error, "invalid password");
4606  return false;
4607  }
4608 
4609  r = epee::serialization::load_t_from_binary(m_account, account_data);
4610  THROW_WALLET_EXCEPTION_IF(!r, error::invalid_password);
4611  if (m_key_device_type == hw::device::device_type::LEDGER || m_key_device_type == hw::device::device_type::TREZOR) {
4612  LOG_PRINT_L0("Account on device. Initing device...");
4613  hw::device &hwdev = lookup_device(m_device_name);
4614  THROW_WALLET_EXCEPTION_IF(!hwdev.set_name(m_device_name), error::wallet_internal_error, "Could not set device name " + m_device_name);
4615  hwdev.set_network_type(m_nettype);
4616  hwdev.set_derivation_path(m_device_derivation_path);
4617  hwdev.set_callback(get_device_callback());
4618  THROW_WALLET_EXCEPTION_IF(!hwdev.init(), error::wallet_internal_error, "Could not initialize the device " + m_device_name);
4619  THROW_WALLET_EXCEPTION_IF(!hwdev.connect(), error::wallet_internal_error, "Could not connect to the device " + m_device_name);
4620  m_account.set_device(hwdev);
4621 
4622  account_public_address device_account_public_address;
4623  THROW_WALLET_EXCEPTION_IF(!hwdev.get_public_address(device_account_public_address), error::wallet_internal_error, "Cannot get a device address");
4624  THROW_WALLET_EXCEPTION_IF(device_account_public_address != m_account.get_keys().m_account_address, error::wallet_internal_error, "Device wallet does not match wallet address. "
4625  "Device address: " + cryptonote::get_account_address_as_str(m_nettype, false, device_account_public_address) +
4626  ", wallet address: " + m_account.get_public_address_str(m_nettype));
4627  LOG_PRINT_L0("Device inited...");
4628  } else if (key_on_device()) {
4629  THROW_WALLET_EXCEPTION(error::wallet_internal_error, "hardware device not supported");
4630  }
4631 
4632  if (r)
4633  {
4634  if (encrypted_secret_keys)
4635  {
4636  m_account.decrypt_keys(key);
4637  }
4638  else
4639  {
4640  // rewrite with encrypted keys, ignore errors
4641  if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
4642  encrypt_keys(key);
4643  bool saved_ret = store_keys(keys_file_name, password, m_watch_only);
4644  if (!saved_ret)
4645  {
4646  // just moan a bit, but not fatal
4647  MERROR("Error saving keys file with encrypted keys, not fatal");
4648  }
4649  if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
4650  decrypt_keys(key);
4651  m_keys_file_locker.reset();
4652  }
4653  }
4654  const cryptonote::account_keys& keys = m_account.get_keys();
4655  hw::device &hwdev = m_account.get_device();
4657  if(!m_watch_only && !m_multisig && hwdev.device_protocol() != hw::device::PROTOCOL_COLD)
4659  THROW_WALLET_EXCEPTION_IF(!r, error::invalid_password);
4660 
4661  if (r)
4662  setup_keys(password);
4663 
4664  return true;
4665 }
4666 
4677 bool wallet2::verify_password(const epee::wipeable_string& password)
4678 {
4679  // this temporary unlocking is necessary for Windows (otherwise the file couldn't be loaded).
4680  unlock_keys_file();
4681  bool r = verify_password(m_keys_file, password, m_account.get_device().device_protocol() == hw::device::PROTOCOL_COLD || m_watch_only || m_multisig, m_account.get_device(), m_kdf_rounds);
4682  lock_keys_file();
4683  return r;
4684 }
4685 
4699 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)
4700 {
4703  std::string buf;
4704  bool encrypted_secret_keys = false;
4705  bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf);
4707 
4708  // Decrypt the contents
4710  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + keys_file_name + '\"');
4711  crypto::chacha_key key;
4712  crypto::generate_chacha_key(password.data(), password.size(), key, kdf_rounds);
4713  std::string account_data;
4714  account_data.resize(keys_file_data.account_data.size());
4716  if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject())
4718 
4719  // The contents should be JSON if the wallet follows the new format.
4720  if (json.Parse(account_data.c_str()).HasParseError())
4721  {
4722  // old format before JSON wallet key file format
4723  }
4724  else
4725  {
4726  account_data = std::string(json["key_data"].GetString(), json["key_data"].GetString() +
4727  json["key_data"].GetStringLength());
4728  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, encrypted_secret_keys, uint32_t, Uint, false, false);
4729  encrypted_secret_keys = field_encrypted_secret_keys;
4730  }
4731 
4732  cryptonote::account_base account_data_check;
4733 
4734  r = epee::serialization::load_t_from_binary(account_data_check, account_data);
4735 
4736  if (encrypted_secret_keys)
4737  account_data_check.decrypt_keys(key);
4738 
4739  const cryptonote::account_keys& keys = account_data_check.get_keys();
4741  if(!no_spend_key)
4743  return r;
4744 }
4745 
4746 void wallet2::encrypt_keys(const crypto::chacha_key &key)
4747 {
4748  m_account.encrypt_keys(key);
4749  m_account.decrypt_viewkey(key);
4750 }
4751 
4752 void wallet2::decrypt_keys(const crypto::chacha_key &key)
4753 {
4754  m_account.encrypt_viewkey(key);
4755  m_account.decrypt_keys(key);
4756 }
4757 
4758 void wallet2::encrypt_keys(const epee::wipeable_string &password)
4759 {
4760  crypto::chacha_key key;
4761  crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
4762  encrypt_keys(key);
4763 }
4764 
4765 void wallet2::decrypt_keys(const epee::wipeable_string &password)
4766 {
4767  crypto::chacha_key key;
4768  crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
4769  decrypt_keys(key);
4770 }
4771 
4772 void wallet2::setup_new_blockchain()
4773 {
4775  generate_genesis(b);
4776  m_blockchain.push_back(get_block_hash(b));
4777  m_last_block_reward = cryptonote::get_outs_etn_amount(b.miner_tx);
4778  add_subaddress_account(tr("Primary account"));
4779 }
4780 
4781 void wallet2::create_keys_file(const std::string &wallet_, bool watch_only, const epee::wipeable_string &password, bool create_address_file)
4782 {
4783  if (!wallet_.empty())
4784  {
4785  bool r = store_keys(m_keys_file, password, watch_only);
4787 
4788  if (create_address_file)
4789  {
4790  r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
4791  if(!r) MERROR("String with address text not saved");
4792  }
4793  }
4794 }
4795 
4796 
4807 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)
4808 {
4811  std::string buf;
4812  bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf);
4814 
4815  // Decrypt the contents
4817  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + keys_file_name + '\"');
4818  crypto::chacha_key key;
4819  crypto::generate_chacha_key(password.data(), password.size(), key, kdf_rounds);
4820  std::string account_data;
4821  account_data.resize(keys_file_data.account_data.size());
4823  if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject())
4825 
4826  device_type = hw::device::device_type::SOFTWARE;
4827  // The contents should be JSON if the wallet follows the new format.
4828  if (json.Parse(account_data.c_str()).HasParseError())
4829  {
4830  // old format before JSON wallet key file format
4831  }
4832  else
4833  {
4834  account_data = std::string(json["key_data"].GetString(), json["key_data"].GetString() +
4835  json["key_data"].GetStringLength());
4836 
4837  if (json.HasMember("key_on_device"))
4838  {
4839  GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, key_on_device, int, Int, false, hw::device::device_type::SOFTWARE);
4840  device_type = static_cast<hw::device::device_type>(field_key_on_device);
4841  }
4842  }
4843 
4844  cryptonote::account_base account_data_check;
4845 
4846  r = epee::serialization::load_t_from_binary(account_data_check, account_data);
4847  if (!r) return false;
4848  return true;
4849 }
4850 
4851 void wallet2::init_type(hw::device::device_type device_type)
4852 {
4853  m_account_public_address = m_account.get_keys().m_account_address;
4854  m_watch_only = false;
4855  m_multisig = false;
4856  m_multisig_threshold = 0;
4857  m_multisig_signers.clear();
4858  m_original_keys_available = false;
4859  m_key_device_type = device_type;
4860 }
4861 
4869 void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password,
4870  const epee::wipeable_string& multisig_data, bool create_address_file)
4871 {
4872  clear();
4873  prepare_file_names(wallet_);
4874 
4875  if (!wallet_.empty())
4876  {
4877  boost::system::error_code ignored_ec;
4878  THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, m_wallet_file);
4879  THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
4880  }
4881 
4882  m_account.generate(rct::rct2sk(rct::zero()), true, false);
4883 
4885  size_t offset = 0;
4886  uint32_t threshold = *(uint32_t*)(multisig_data.data() + offset);
4887  offset += sizeof(uint32_t);
4888  uint32_t total = *(uint32_t*)(multisig_data.data() + offset);
4889  offset += sizeof(uint32_t);
4892  const size_t n_multisig_keys = total == threshold ? 1 : threshold;
4893  THROW_WALLET_EXCEPTION_IF(multisig_data.size() != 8 + 32 * (4 + n_multisig_keys + total), error::invalid_multisig_seed);
4894 
4895  std::vector<crypto::secret_key> multisig_keys;
4896  std::vector<crypto::public_key> multisig_signers;
4897  crypto::secret_key spend_secret_key = *(crypto::secret_key*)(multisig_data.data() + offset);
4898  offset += sizeof(crypto::secret_key);
4899  crypto::public_key spend_public_key = *(crypto::public_key*)(multisig_data.data() + offset);
4900  offset += sizeof(crypto::public_key);
4901  crypto::secret_key view_secret_key = *(crypto::secret_key*)(multisig_data.data() + offset);
4902  offset += sizeof(crypto::secret_key);
4903  crypto::public_key view_public_key = *(crypto::public_key*)(multisig_data.data() + offset);
4904  offset += sizeof(crypto::public_key);
4905  for (size_t n = 0; n < n_multisig_keys; ++n)
4906  {
4907  multisig_keys.push_back(*(crypto::secret_key*)(multisig_data.data() + offset));
4908  offset += sizeof(crypto::secret_key);
4909  }
4910  for (size_t n = 0; n < total; ++n)
4911  {
4912  multisig_signers.push_back(*(crypto::public_key*)(multisig_data.data() + offset));
4913  offset += sizeof(crypto::public_key);
4914  }
4915 
4916  crypto::public_key calculated_view_public_key;
4917  THROW_WALLET_EXCEPTION_IF(!crypto::secret_key_to_public_key(view_secret_key, calculated_view_public_key), error::invalid_multisig_seed);
4918  THROW_WALLET_EXCEPTION_IF(view_public_key != calculated_view_public_key, error::invalid_multisig_seed);
4919  crypto::public_key local_signer;
4921  THROW_WALLET_EXCEPTION_IF(std::find(multisig_signers.begin(), multisig_signers.end(), local_signer) == multisig_signers.end(), error::invalid_multisig_seed);
4922  rct::key skey = rct::zero();
4923  for (const auto &msk: multisig_keys)
4924  sc_add(skey.bytes, skey.bytes, rct::sk2rct(msk).bytes);
4925  THROW_WALLET_EXCEPTION_IF(!(rct::rct2sk(skey) == spend_secret_key), error::invalid_multisig_seed);
4926  memwipe(&skey, sizeof(rct::key));
4927 
4928  m_account.make_multisig(view_secret_key, spend_secret_key, spend_public_key, multisig_keys);
4929  m_account.finalize_multisig(spend_public_key);
4930 
4931  // Not possible to restore a multisig wallet that is able to activate the MMS
4932  // (because the original keys are not (yet) part of the restore info), so
4933  // keep m_original_keys_available to false
4934  init_type(hw::device::device_type::SOFTWARE);
4935  m_multisig = true;
4936  m_multisig_threshold = threshold;
4937  m_multisig_signers = multisig_signers;
4938  setup_keys(password);
4939 
4940  create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file);
4941  setup_new_blockchain();
4942 
4943  if (!wallet_.empty())
4944  store();
4945 }
4946 
4957 crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password,
4958  const crypto::secret_key& recovery_param, bool recover, bool two_random, bool create_address_file)
4959 {
4960  clear();
4961  prepare_file_names(wallet_);
4962 
4963  if (!wallet_.empty())
4964  {
4965  boost::system::error_code ignored_ec;
4966  THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, m_wallet_file);
4967  THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
4968  }
4969 
4970  crypto::secret_key retval = m_account.generate(recovery_param, recover, two_random);
4971 
4972  init_type(hw::device::device_type::SOFTWARE);
4973  setup_keys(password);
4974 
4975  // calculate a starting refresh height
4976  if(m_refresh_from_block_height == 0 && !recover){
4977  m_refresh_from_block_height = estimate_blockchain_height();
4978  }
4979 
4980  create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file);
4981 
4982  setup_new_blockchain();
4983 
4984  if (!wallet_.empty())
4985  store();
4986 
4987  return retval;
4988 }
4989 
4990  uint64_t wallet2::estimate_blockchain_height()
4991  {
4992  // -1 month for fluctuations in block time and machine date/time setup.
4993  // avg seconds per block
4994  const int seconds_per_block = DIFFICULTY_TARGET_V6;
4995  // ~num blocks per month
4996  const uint64_t blocks_per_month = 60*60*24*30/seconds_per_block;
4997 
4998  // try asking the daemon first
4999  std::string err;
5000  uint64_t height = 0;
5001 
5002  // we get the max of approximated height and local height.
5003  // approximated height is the least of daemon target height
5004  // (the max of what the other daemons are claiming is their
5005  // height) and the theoretical height based on the local
5006  // clock. This will be wrong only if both the local clock
5007  // is bad *and* a peer daemon claims a highest height than
5008  // the real chain.
5009  // local height is the height the local daemon is currently
5010  // synced to, it will be lower than the real chain height if
5011  // the daemon is currently syncing.
5012  // If we use the approximate height we subtract one month as
5013  // a safety margin.
5014  height = get_approximate_blockchain_height();
5015  uint64_t target_height = get_daemon_blockchain_target_height(err);
5016  if (err.empty()) {
5017  if (target_height < height)
5018  height = target_height;
5019  } else {
5020  // if we couldn't talk to the daemon, check safety margin.
5021  if (height > blocks_per_month)
5022  height -= blocks_per_month;
5023  else
5024  height = 0;
5025  }
5026  uint64_t local_height = get_daemon_blockchain_height(err);
5027  if (err.empty() && local_height > height)
5028  height = local_height;
5029  return height;
5030  }
5031 
5040 void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password,
5042  const crypto::secret_key& viewkey, bool create_address_file)
5043 {
5044  clear();
5045  prepare_file_names(wallet_);
5046 
5047  if (!wallet_.empty())
5048  {
5049  boost::system::error_code ignored_ec;
5050  THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, m_wallet_file);
5051  THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
5052  }
5053 
5054  m_account.create_from_viewkey(account_public_address, viewkey);
5055  init_type(hw::device::device_type::SOFTWARE);
5056  m_watch_only = true;
5057  m_account_public_address = account_public_address;
5058  setup_keys(password);
5059 
5060  create_keys_file(wallet_, true, password, m_nettype != MAINNET || create_address_file);
5061 
5062  setup_new_blockchain();
5063 
5064  if (!wallet_.empty())
5065  store();
5066 }
5067 
5077 void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password,
5079  const crypto::secret_key& spendkey, const crypto::secret_key& viewkey, bool create_address_file)
5080 {
5081  clear();
5082  prepare_file_names(wallet_);
5083 
5084  if (!wallet_.empty())
5085  {
5086  boost::system::error_code ignored_ec;
5087  THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, m_wallet_file);
5088  THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
5089  }
5090 
5091  m_account.create_from_keys(account_public_address, spendkey, viewkey);
5092  init_type(hw::device::device_type::SOFTWARE);
5093  m_account_public_address = account_public_address;
5094  setup_keys(password);
5095 
5096  create_keys_file(wallet_, false, password, create_address_file);
5097 
5098  setup_new_blockchain();
5099 
5100  if (!wallet_.empty())
5101  store();
5102 }
5103 
5110 void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& password, const std::string &device_name, bool create_address_file)
5111 {
5112  clear();
5113  prepare_file_names(wallet_);
5114 
5115  boost::system::error_code ignored_ec;
5116  if (!wallet_.empty()) {
5117  THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, m_wallet_file);
5118  THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
5119  }
5120 
5121  auto &hwdev = lookup_device(device_name);
5122  hwdev.set_name(device_name);
5123  hwdev.set_network_type(m_nettype);
5124  hwdev.set_derivation_path(m_device_derivation_path);
5125  hwdev.set_callback(get_device_callback());
5126 
5127  m_account.create_from_device(hwdev);
5128  init_type(m_account.get_device().get_type());
5129  setup_keys(password);
5130  m_device_name = device_name;
5131 
5132  create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file);
5133  if (m_subaddress_lookahead_major == SUBADDRESS_LOOKAHEAD_MAJOR && m_subaddress_lookahead_minor == SUBADDRESS_LOOKAHEAD_MINOR)
5134  {
5135  // the default lookahead setting (50:200) is clearly too much for hardware wallet
5136  m_subaddress_lookahead_major = 5;
5137  m_subaddress_lookahead_minor = 20;
5138  }
5139  setup_new_blockchain();
5140  if (!wallet_.empty()) {
5141  store();
5142  }
5143 }
5144 
5145 std::string wallet2::make_multisig(const epee::wipeable_string &password,
5146  const std::vector<crypto::secret_key> &view_keys,
5147  const std::vector<crypto::public_key> &spend_keys,
5149 {
5150  CHECK_AND_ASSERT_THROW_MES(!view_keys.empty(), "empty view keys");
5151  CHECK_AND_ASSERT_THROW_MES(view_keys.size() == spend_keys.size(), "Mismatched view/spend key sizes");
5152  CHECK_AND_ASSERT_THROW_MES(threshold > 1 && threshold <= spend_keys.size() + 1, "Invalid threshold");
5153 
5154  std::string extra_multisig_info;
5155  std::vector<crypto::secret_key> multisig_keys;
5156  rct::key spend_pkey = rct::identity();
5157  rct::key spend_skey;
5158  std::vector<crypto::public_key> multisig_signers;
5159 
5160  // decrypt keys
5162  if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
5163  {
5164  crypto::chacha_key chacha_key;
5165  crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds);
5166  m_account.encrypt_viewkey(chacha_key);
5167  m_account.decrypt_keys(chacha_key);
5168  keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this, chacha_key]() { m_account.encrypt_keys(chacha_key); m_account.decrypt_viewkey(chacha_key); });
5169  }
5170 
5171  // In common multisig scheme there are 4 types of key exchange rounds:
5172  // 1. First round is exchange of view secret keys and public spend keys.
5173  // 2. Middle round is exchange of derivations: Ki = b * Mj, where b - spend secret key,
5174  // M - public multisig key (in first round it equals to public spend key), K - new public multisig key.
5175  // 3. Secret spend establishment round sets your secret multisig keys as follows: kl = H(Ml), where M - is *your* public multisig key,
5176  // k - secret multisig key used to sign transactions. k and M are sets of keys, of course.
5177  // And secret spend key as the sum of all participant's secret multisig keys
5178  // 4. Last round establishes multisig wallet's public spend key. Participants exchange their public multisig keys
5179  // and calculate common spend public key as sum of all unique participants' public multisig keys.
5180  // Note that N/N scheme has only first round. N-1/N has 2 rounds: first and last. Common M/N has all 4 rounds.
5181 
5182  // IMPORTANT: wallet's public spend key is not equal to secret_spend_key * G!
5183  // Wallet's public spend key is the sum of unique public multisig keys of all participants.
5184  // secret_spend_key * G = public signer key
5185 
5186  if (threshold == spend_keys.size() + 1)
5187  {
5188  // In N / N case we only need to do one round and calculate secret multisig keys and new secret spend key
5189  MINFO("Creating spend key...");
5190 
5191  // Calculates all multisig keys and spend key
5192  cryptonote::generate_multisig_N_N(get_account().get_keys(), spend_keys, multisig_keys, spend_skey, spend_pkey);
5193 
5194  // Our signer key is b * G, where b is secret spend key.
5195  multisig_signers = spend_keys;
5196  multisig_signers.push_back(get_multisig_signer_public_key(get_account().get_keys().m_spend_secret_key));
5197  }
5198  else
5199  {
5200  // We just got public spend keys of all participants and deriving multisig keys (set of Mi = b * Bi).
5201  // note that derivations are public keys as DH exchange suppose it to be
5202  auto derivations = cryptonote::generate_multisig_derivations(get_account().get_keys(), spend_keys);
5203 
5204  spend_pkey = rct::identity();
5205  multisig_signers = std::vector<crypto::public_key>(spend_keys.size() + 1, crypto::null_pkey);
5206 
5207  if (threshold == spend_keys.size())
5208  {
5209  // N - 1 / N case
5210 
5211  // We need an extra step, so we package all the composite public keys
5212  // we know about, and make a signed string out of them
5213  MINFO("Creating spend key...");
5214 
5215  // Calculating set of our secret multisig keys as follows: mi = H(Mi),
5216  // where mi - secret multisig key, Mi - others' participants public multisig key
5217  multisig_keys = cryptonote::calculate_multisig_keys(derivations);
5218 
5219  // calculating current participant's spend secret key as sum of all secret multisig keys for current participant.
5220  // IMPORTANT: participant's secret spend key is not an entire wallet's secret spend!
5221  // Entire wallet's secret spend is sum of all unique secret multisig keys
5222  // among all of participants and is not held by anyone!
5223  spend_skey = rct::sk2rct(cryptonote::calculate_multisig_signer_key(multisig_keys));
5224 
5225  // Preparing data for the last round to calculate common public spend key. The data contains public multisig keys.
5226  extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, secret_keys_to_public_keys(multisig_keys), rct::rct2sk(spend_skey));
5227  }
5228  else
5229  {
5230  // M / N case
5231  MINFO("Preparing keys for next exchange round...");
5232 
5233  // Preparing data for middle round - packing new public multisig keys to exchage with others.
5234  extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, derivations, m_account.get_keys().m_spend_secret_key);
5235  spend_skey = rct::sk2rct(m_account.get_keys().m_spend_secret_key);
5236 
5237  // Need to store middle keys to be able to proceed in case of wallet shutdown.
5238  m_multisig_derivations = derivations;
5239  }
5240  }
5241 
5242  if (!m_original_keys_available)
5243  {
5244  // Save the original i.e. non-multisig keys so the MMS can continue to use them to encrypt and decrypt messages
5245  // (making a wallet multisig overwrites those keys, see account_base::make_multisig)
5246  m_original_address = m_account.get_keys().m_account_address;
5247  m_original_view_secret_key = m_account.get_keys().m_view_secret_key;
5248  m_original_keys_available = true;
5249  }
5250 
5251  clear();
5252  MINFO("Creating view key...");
5253  crypto::secret_key view_skey = cryptonote::generate_multisig_view_secret_key(get_account().get_keys().m_view_secret_key, view_keys);
5254 
5255  MINFO("Creating multisig address...");
5256  CHECK_AND_ASSERT_THROW_MES(m_account.make_multisig(view_skey, rct::rct2sk(spend_skey), rct::rct2pk(spend_pkey), multisig_keys),
5257  "Failed to create multisig wallet due to bad keys");
5258  memwipe(&spend_skey, sizeof(rct::key));
5259 
5260  init_type(hw::device::device_type::SOFTWARE);
5261  m_original_keys_available = true;
5262  m_multisig = true;
5263  m_multisig_threshold = threshold;
5264  m_multisig_signers = multisig_signers;
5265  ++m_multisig_rounds_passed;
5266 
5267  // re-encrypt keys
5268  keys_reencryptor = epee::misc_utils::auto_scope_leave_caller();
5269 
5270  create_keys_file(m_wallet_file, false, password, boost::filesystem::exists(m_wallet_file + ".address.txt"));
5271 
5272  setup_new_blockchain();
5273 
5274  if (!m_wallet_file.empty())
5275  store();
5276 
5277  return extra_multisig_info;
5278 }
5279 
5280 std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &password,
5281  const std::vector<std::string> &info)
5282 {
5284  error::wallet_internal_error, "Empty multisig info");
5285 
5286  if (info[0].substr(0, MULTISIG_EXTRA_INFO_MAGIC.size()) != MULTISIG_EXTRA_INFO_MAGIC)
5287  {
5289  error::wallet_internal_error, "Unsupported info string");
5290  }
5291 
5292  std::vector<crypto::public_key> signers;
5293  std::unordered_set<crypto::public_key> pkeys;
5294 
5295  THROW_WALLET_EXCEPTION_IF(!unpack_extra_multisig_info(info, signers, pkeys),
5296  error::wallet_internal_error, "Bad extra multisig info");
5297 
5298  return exchange_multisig_keys(password, pkeys, signers);
5299 }
5300 
5301 std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &password,
5302  std::unordered_set<crypto::public_key> derivations,
5303  std::vector<crypto::public_key> signers)
5304 {
5305  CHECK_AND_ASSERT_THROW_MES(!derivations.empty(), "empty pkeys");
5306  CHECK_AND_ASSERT_THROW_MES(!signers.empty(), "empty signers");
5307 
5308  bool ready = false;
5309  CHECK_AND_ASSERT_THROW_MES(multisig(&ready), "The wallet is not multisig");
5310  CHECK_AND_ASSERT_THROW_MES(!ready, "Multisig wallet creation process has already been finished");
5311 
5312  // keys are decrypted
5314  if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
5315  {
5316  crypto::chacha_key chacha_key;
5317  crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds);
5318  m_account.encrypt_viewkey(chacha_key);
5319  m_account.decrypt_keys(chacha_key);
5320  keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this, chacha_key]() { m_account.encrypt_keys(chacha_key); m_account.decrypt_viewkey(chacha_key); });
5321  }
5322 
5323  if (m_multisig_rounds_passed == multisig_rounds_required(m_multisig_signers.size(), m_multisig_threshold) - 1)
5324  {
5325  // the last round is passed and we have to calculate spend public key
5326  // add ours if not included
5327  crypto::public_key local_signer = get_multisig_signer_public_key();
5328 
5329  if (std::find(signers.begin(), signers.end(), local_signer) == signers.end())
5330  {
5331  signers.push_back(local_signer);
5332  for (const auto &msk: get_account().get_multisig_keys())
5333  {
5334  derivations.insert(rct::rct2pk(rct::scalarmultBase(rct::sk2rct(msk))));
5335  }
5336  }
5337 
5338  CHECK_AND_ASSERT_THROW_MES(signers.size() == m_multisig_signers.size(), "Bad signers size");
5339 
5340  // Summing all of unique public multisig keys to calculate common public spend key
5341  crypto::public_key spend_public_key = cryptonote::generate_multisig_M_N_spend_public_key(std::vector<crypto::public_key>(derivations.begin(), derivations.end()));
5342  m_account_public_address.m_spend_public_key = spend_public_key;
5343  m_account.finalize_multisig(spend_public_key);
5344 
5345  m_multisig_signers = signers;
5346  std::sort(m_multisig_signers.begin(), m_multisig_signers.end(), [](const crypto::public_key &e0, const crypto::public_key &e1){ return memcmp(&e0, &e1, sizeof(e0)); });
5347 
5348  ++m_multisig_rounds_passed;
5349  m_multisig_derivations.clear();
5350 
5351  // keys are encrypted again
5352  keys_reencryptor = epee::misc_utils::auto_scope_leave_caller();
5353 
5354  if (!m_wallet_file.empty())
5355  {
5356  bool r = store_keys(m_keys_file, password, false);
5358 
5359  if (boost::filesystem::exists(m_wallet_file + ".address.txt"))
5360  {
5361  r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
5362  if(!r) MERROR("String with address text not saved");
5363  }
5364  }
5365 
5366  m_subaddresses.clear();
5367  m_subaddress_labels.clear();
5368  add_subaddress_account(tr("Primary account"));
5369 
5370  if (!m_wallet_file.empty())
5371  store();
5372 
5373  return {};
5374  }
5375 
5376  // Below are either middle or secret spend key establishment rounds
5377 
5378  for (const auto& key: m_multisig_derivations)
5379  derivations.erase(key);
5380 
5381  // Deriving multisig keys (set of Mi = b * Bi) according to DH from other participants' multisig keys.
5382  auto new_derivations = cryptonote::generate_multisig_derivations(get_account().get_keys(), std::vector<crypto::public_key>(derivations.begin(), derivations.end()));
5383 
5384  std::string extra_multisig_info;
5385  if (m_multisig_rounds_passed == multisig_rounds_required(m_multisig_signers.size(), m_multisig_threshold) - 2) // next round is last
5386  {
5387  // Next round is last therefore we are performing secret spend establishment round as described above.
5388  MINFO("Creating spend key...");
5389 
5390  // Calculating our secret multisig keys by hashing our public multisig keys.
5391  auto multisig_keys = cryptonote::calculate_multisig_keys(std::vector<crypto::public_key>(new_derivations.begin(), new_derivations.end()));
5392  // And summing it to get personal secret spend key
5394 
5395  m_account.make_multisig(m_account.get_keys().m_view_secret_key, spend_skey, rct::rct2pk(rct::identity()), multisig_keys);
5396 
5397  // Packing public multisig keys to exchange with others and calculate common public spend key in the last round
5398  extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, secret_keys_to_public_keys(multisig_keys), spend_skey);
5399  }
5400  else
5401  {
5402  // This is just middle round
5403  MINFO("Preparing keys for next exchange round...");
5404  extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, new_derivations, m_account.get_keys().m_spend_secret_key);
5405  m_multisig_derivations = new_derivations;
5406  }
5407 
5408  ++m_multisig_rounds_passed;
5409 
5410  create_keys_file(m_wallet_file, false, password, boost::filesystem::exists(m_wallet_file + ".address.txt"));
5411  return extra_multisig_info;
5412 }
5413 
5414 void wallet2::unpack_multisig_info(const std::vector<std::string>& info,
5415  std::vector<crypto::public_key> &public_keys,
5416  std::vector<crypto::secret_key> &secret_keys) const
5417 {
5418  // parse all multisig info
5419  public_keys.resize(info.size());
5420  secret_keys.resize(info.size());
5421  for (size_t i = 0; i < info.size(); ++i)
5422  {
5423  THROW_WALLET_EXCEPTION_IF(!verify_multisig_info(info[i], secret_keys[i], public_keys[i]),
5424  error::wallet_internal_error, "Bad multisig info: " + info[i]);
5425  }
5426 
5427  // remove duplicates
5428  for (size_t i = 0; i < secret_keys.size(); ++i)
5429  {
5430  for (size_t j = i + 1; j < secret_keys.size(); ++j)
5431  {
5432  if (rct::sk2rct(secret_keys[i]) == rct::sk2rct(secret_keys[j]))
5433  {
5434  MDEBUG("Duplicate key found, ignoring");
5435  secret_keys[j] = secret_keys.back();
5436  public_keys[j] = public_keys.back();
5437  secret_keys.pop_back();
5438  public_keys.pop_back();
5439  --j;
5440  }
5441  }
5442  }
5443 
5444  // people may include their own, weed it out
5445  const crypto::secret_key local_skey = cryptonote::get_multisig_blinded_secret_key(get_account().get_keys().m_view_secret_key);
5446  const crypto::public_key local_pkey = get_multisig_signer_public_key(get_account().get_keys().m_spend_secret_key);
5447  for (size_t i = 0; i < secret_keys.size(); ++i)
5448  {
5449  if (secret_keys[i] == local_skey)
5450  {
5451  MDEBUG("Local key is present, ignoring");
5452  secret_keys[i] = secret_keys.back();
5453  public_keys[i] = public_keys.back();
5454  secret_keys.pop_back();
5455  public_keys.pop_back();
5456  --i;
5457  }
5458  else
5459  {
5460  THROW_WALLET_EXCEPTION_IF(public_keys[i] == local_pkey, error::wallet_internal_error,
5461  "Found local spend public key, but not local view secret key - something very weird");
5462  }
5463  }
5464 }
5465 
5466 std::string wallet2::make_multisig(const epee::wipeable_string &password,
5467  const std::vector<std::string> &info,
5469 {
5470  std::vector<crypto::secret_key> secret_keys(info.size());
5471  std::vector<crypto::public_key> public_keys(info.size());
5472  unpack_multisig_info(info, public_keys, secret_keys);
5473  return make_multisig(password, secret_keys, public_keys, threshold);
5474 }
5475 
5476 bool wallet2::finalize_multisig(const epee::wipeable_string &password, const std::unordered_set<crypto::public_key> &pkeys, std::vector<crypto::public_key> signers)
5477 {
5478  bool ready;
5479  uint32_t threshold, total;
5480  if (!multisig(&ready, &threshold, &total))
5481  {
5482  MERROR("This is not a multisig wallet");
5483  return false;
5484  }
5485  if (ready)
5486  {
5487  MERROR("This multisig wallet is already finalized");
5488  return false;
5489  }
5490  if (threshold + 1 != total)
5491  {
5492  MERROR("finalize_multisig should only be used for N-1/N wallets, use exchange_multisig_keys instead");
5493  return false;
5494  }
5495  exchange_multisig_keys(password, pkeys, signers);
5496  return true;
5497 }
5498 
5499 bool wallet2::unpack_extra_multisig_info(const std::vector<std::string>& info,
5500  std::vector<crypto::public_key> &signers,
5501  std::unordered_set<crypto::public_key> &pkeys) const
5502 {
5503  // parse all multisig info
5504  signers.resize(info.size(), crypto::null_pkey);
5505  for (size_t i = 0; i < info.size(); ++i)
5506  {
5507  if (!verify_extra_multisig_info(info[i], pkeys, signers[i]))
5508  {
5509  return false;
5510  }
5511  }
5512 
5513  return true;
5514 }
5515 
5516 bool wallet2::finalize_multisig(const epee::wipeable_string &password, const std::vector<std::string> &info)
5517 {
5518  std::unordered_set<crypto::public_key> public_keys;
5519  std::vector<crypto::public_key> signers;
5520  if (!unpack_extra_multisig_info(info, signers, public_keys))
5521  {
5522  MERROR("Bad multisig info");
5523  return false;
5524  }
5525 
5526  return finalize_multisig(password, public_keys, signers);
5527 }
5528 
5529 std::string wallet2::get_multisig_info() const
5530 {
5531  // It's a signed package of private view key and public spend key
5532  const crypto::secret_key skey = cryptonote::get_multisig_blinded_secret_key(get_account().get_keys().m_view_secret_key);
5533  const crypto::public_key pkey = get_multisig_signer_public_key(get_account().get_keys().m_spend_secret_key);
5535 
5536  std::string data;
5537  data += std::string((const char *)&skey, sizeof(crypto::secret_key));
5538  data += std::string((const char *)&pkey, sizeof(crypto::public_key));
5539 
5540  data.resize(data.size() + sizeof(crypto::signature));
5541  crypto::cn_fast_hash(data.data(), data.size() - sizeof(signature), hash);
5542  crypto::signature &signature = *(crypto::signature*)&data[data.size() - sizeof(crypto::signature)];
5543  crypto::generate_signature(hash, pkey, get_multisig_blinded_secret_key(get_account().get_keys().m_spend_secret_key), signature);
5544 
5545  return std::string("MultisigV1") + tools::base58::encode(data);
5546 }
5547 
5548 bool wallet2::verify_multisig_info(const std::string &data, crypto::secret_key &skey, crypto::public_key &pkey)
5549 {
5550  const size_t header_len = strlen("MultisigV1");
5551  if (data.size() < header_len || data.substr(0, header_len) != "MultisigV1")
5552  {
5553  MERROR("Multisig info header check error");
5554  return false;
5555  }
5556  std::string decoded;
5557  if (!tools::base58::decode(data.substr(header_len), decoded))
5558  {
5559  MERROR("Multisig info decoding error");
5560  return false;
5561  }
5562  if (decoded.size() != sizeof(crypto::secret_key) + sizeof(crypto::public_key) + sizeof(crypto::signature))
5563  {
5564  MERROR("Multisig info is corrupt");
5565  return false;
5566  }
5567 
5568  size_t offset = 0;
5569  skey = *(const crypto::secret_key*)(decoded.data() + offset);
5570  offset += sizeof(skey);
5571  pkey = *(const crypto::public_key*)(decoded.data() + offset);
5572  offset += sizeof(pkey);
5573  const crypto::signature &signature = *(const crypto::signature*)(decoded.data() + offset);
5574 
5576  crypto::cn_fast_hash(decoded.data(), decoded.size() - sizeof(signature), hash);
5577  if (!crypto::check_signature(hash, pkey, signature))
5578  {
5579  MERROR("Multisig info signature is invalid");
5580  return false;
5581  }
5582 
5583  return true;
5584 }
5585 
5586 bool wallet2::verify_extra_multisig_info(const std::string &data, std::unordered_set<crypto::public_key> &pkeys, crypto::public_key &signer)
5587 {
5588  if (data.size() < MULTISIG_EXTRA_INFO_MAGIC.size() || data.substr(0, MULTISIG_EXTRA_INFO_MAGIC.size()) != MULTISIG_EXTRA_INFO_MAGIC)
5589  {
5590  MERROR("Multisig info header check error");
5591  return false;
5592  }
5593  std::string decoded;
5594  if (!tools::base58::decode(data.substr(MULTISIG_EXTRA_INFO_MAGIC.size()), decoded))
5595  {
5596  MERROR("Multisig info decoding error");
5597  return false;
5598  }
5599  if (decoded.size() < sizeof(crypto::public_key) + sizeof(crypto::signature))
5600  {
5601  MERROR("Multisig info is corrupt");
5602  return false;
5603  }
5604  if ((decoded.size() - (sizeof(crypto::public_key) + sizeof(crypto::signature))) % sizeof(crypto::public_key))
5605  {
5606  MERROR("Multisig info is corrupt");
5607  return false;
5608  }
5609 
5610  const size_t n_keys = (decoded.size() - (sizeof(crypto::public_key) + sizeof(crypto::signature))) / sizeof(crypto::public_key);
5611  size_t offset = 0;
5612  signer = *(const crypto::public_key*)(decoded.data() + offset);
5613  offset += sizeof(signer);
5614  const crypto::signature &signature = *(const crypto::signature*)(decoded.data() + offset + n_keys * sizeof(crypto::public_key));
5615 
5617  crypto::cn_fast_hash(decoded.data(), decoded.size() - sizeof(signature), hash);
5618  if (!crypto::check_signature(hash, signer, signature))
5619  {
5620  MERROR("Multisig info signature is invalid");
5621  return false;
5622  }
5623 
5624  for (size_t n = 0; n < n_keys; ++n)
5625  {
5626  crypto::public_key mspk = *(const crypto::public_key*)(decoded.data() + offset);
5627  pkeys.insert(mspk);
5628  offset += sizeof(mspk);
5629  }
5630 
5631  return true;
5632 }
5633 
5634 bool wallet2::multisig(bool *ready, uint32_t *threshold, uint32_t *total) const
5635 {
5636  if (!m_multisig)
5637  return false;
5638  if (threshold)
5639  *threshold = m_multisig_threshold;
5640  if (total)
5641  *total = m_multisig_signers.size();
5642  if (ready)
5643  *ready = !(get_account().get_keys().m_account_address.m_spend_public_key == rct::rct2pk(rct::identity()));
5644  return true;
5645 }
5646 
5647 bool wallet2::has_multisig_partial_key_images() const
5648 {
5649  if (!m_multisig)
5650  return false;
5651  for (const auto &td: m_transfers)
5652  if (td.m_key_image_partial && td.m_tx.version == 1)
5653  return true;
5654  return false;
5655 }
5656 
5657 bool wallet2::has_unknown_key_images() const
5658 {
5659  for (const auto &td: m_transfers)
5660  if (!td.m_key_image_known && td.m_tx.version == 1)
5661  return true;
5662  return false;
5663 }
5664 
5670 void wallet2::rewrite(const std::string& wallet_name, const epee::wipeable_string& password)
5671 {
5672  if (wallet_name.empty())
5673  return;
5674  prepare_file_names(wallet_name);
5675  boost::system::error_code ignored_ec;
5676  THROW_WALLET_EXCEPTION_IF(!boost::filesystem::exists(m_keys_file, ignored_ec), error::file_not_found, m_keys_file);
5677  bool r = store_keys(m_keys_file, password, m_watch_only);
5679 }
5686 void wallet2::write_watch_only_wallet(const std::string& wallet_name, const epee::wipeable_string& password, std::string &new_keys_filename)
5687 {
5688  prepare_file_names(wallet_name);
5689  boost::system::error_code ignored_ec;
5690  new_keys_filename = m_wallet_file + "-watchonly.keys";
5691  bool watch_only_keys_file_exists = boost::filesystem::exists(new_keys_filename, ignored_ec);
5692  THROW_WALLET_EXCEPTION_IF(watch_only_keys_file_exists, error::file_save_error, new_keys_filename);
5693  bool r = store_keys(new_keys_filename, password, true);
5694  THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, new_keys_filename);
5695 }
5696 //----------------------------------------------------------------------------------------------------
5697 void wallet2::wallet_exists(const std::string& file_path, bool& keys_file_exists, bool& wallet_file_exists)
5698 {
5699  std::string keys_file, wallet_file, mms_file;
5700  do_prepare_file_names(file_path, keys_file, wallet_file, mms_file);
5701 
5702  boost::system::error_code ignore;
5703  keys_file_exists = boost::filesystem::exists(keys_file, ignore);
5704  wallet_file_exists = boost::filesystem::exists(wallet_file, ignore);
5705 }
5706 //----------------------------------------------------------------------------------------------------
5707 bool wallet2::wallet_valid_path_format(const std::string& file_path)
5708 {
5709  return !file_path.empty();
5710 }
5711 //----------------------------------------------------------------------------------------------------
5712 bool wallet2::parse_long_payment_id(const std::string& payment_id_str, crypto::hash& payment_id)
5713 {
5714  cryptonote::blobdata payment_id_data;
5715  if(!epee::string_tools::parse_hexstr_to_binbuff(payment_id_str, payment_id_data))
5716  return false;
5717 
5718  if(sizeof(crypto::hash) != payment_id_data.size())
5719  return false;
5720 
5721  payment_id = *reinterpret_cast<const crypto::hash*>(payment_id_data.data());
5722  return true;
5723 }
5724 //----------------------------------------------------------------------------------------------------
5725 bool wallet2::parse_short_payment_id(const std::string& payment_id_str, crypto::hash8& payment_id)
5726 {
5727  cryptonote::blobdata payment_id_data;
5728  if(!epee::string_tools::parse_hexstr_to_binbuff(payment_id_str, payment_id_data))
5729  return false;
5730 
5731  if(sizeof(crypto::hash8) != payment_id_data.size())
5732  return false;
5733 
5734  payment_id = *reinterpret_cast<const crypto::hash8*>(payment_id_data.data());
5735  return true;
5736 }
5737 //----------------------------------------------------------------------------------------------------
5738 bool wallet2::parse_payment_id(const std::string& payment_id_str, crypto::hash& payment_id)
5739 {
5740  if (parse_long_payment_id(payment_id_str, payment_id))
5741  return true;
5742  crypto::hash8 payment_id8;
5743  if (parse_short_payment_id(payment_id_str, payment_id8))
5744  {
5745  memcpy(payment_id.data, payment_id8.data, 8);
5746  memset(payment_id.data + 8, 0, 24);
5747  return true;
5748  }
5749  return false;
5750 }
5751 //----------------------------------------------------------------------------------------------------
5752 bool wallet2::prepare_file_names(const std::string& file_path)
5753 {
5754  do_prepare_file_names(file_path, m_keys_file, m_wallet_file, m_mms_file);
5755  return true;
5756 }
5757 //----------------------------------------------------------------------------------------------------
5758 bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout)
5759 {
5761 
5762  if (m_offline)
5763  {
5764  if (version)
5765  *version = 0;
5766  if (ssl)
5767  *ssl = false;
5768  return false;
5769  }
5770 
5771  // TODO: Add light wallet version check.
5772  if(m_light_wallet) {
5773  if (version)
5774  *version = 0;
5775  if (ssl)
5776  *ssl = m_light_wallet_connected; // light wallet is always SSL
5777  return m_light_wallet_connected;
5778  }
5779 
5780  {
5781  boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex);
5782  if(!m_http_client.is_connected(ssl))
5783  {
5784  m_node_rpc_proxy.invalidate();
5785  if (!m_http_client.connect(std::chrono::milliseconds(timeout)))
5786  return false;
5787  if(!m_http_client.is_connected(ssl))
5788  return false;
5789  }
5790  }
5791 
5792  if (version)
5793  {
5796  bool r = invoke_http_json_rpc("/json_rpc", "get_version", req_t, resp_t);
5797  if(!r) {
5798  *version = 0;
5799  return false;
5800  }
5801  if (resp_t.status != CORE_RPC_STATUS_OK)
5802  *version = 0;
5803  else
5804  *version = resp_t.version;
5805  }
5806 
5807  return true;
5808 }
5809 //----------------------------------------------------------------------------------------------------
5810 void wallet2::set_offline(bool offline)
5811 {
5812  m_offline = offline;
5813  m_http_client.set_auto_connect(!offline);
5814  if (offline)
5815  {
5816  boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex);
5817  if(m_http_client.is_connected())
5818  m_http_client.disconnect();
5819  }
5820 }
5821 //----------------------------------------------------------------------------------------------------
5822 bool wallet2::generate_chacha_key_from_secret_keys(crypto::chacha_key &key) const
5823 {
5824  hw::device &hwdev = m_account.get_device();
5825  return hwdev.generate_chacha_key(m_account.get_keys(), key, m_kdf_rounds);
5826 }
5827 //----------------------------------------------------------------------------------------------------
5828 void wallet2::generate_chacha_key_from_password(const epee::wipeable_string &pass, crypto::chacha_key &key) const
5829 {
5830  crypto::generate_chacha_key(pass.data(), pass.size(), key, m_kdf_rounds);
5831 }
5832 //----------------------------------------------------------------------------------------------------
5833 void wallet2::load(const std::string& wallet_, const epee::wipeable_string& password)
5834 {
5835  clear();
5836  prepare_file_names(wallet_);
5837 
5838  boost::system::error_code e;
5839  bool exists = boost::filesystem::exists(m_keys_file, e);
5840  THROW_WALLET_EXCEPTION_IF(e || !exists, error::file_not_found, m_keys_file);
5841  lock_keys_file();
5842  THROW_WALLET_EXCEPTION_IF(!is_keys_file_locked(), error::wallet_internal_error, "internal error: \"" + m_keys_file + "\" is opened by another wallet program");
5843 
5844  // this temporary unlocking is necessary for Windows (otherwise the file couldn't be loaded).
5845  unlock_keys_file();
5846  if (!load_keys(m_keys_file, password))
5847  {
5849  }
5850  LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str(m_nettype));
5851  lock_keys_file();
5852 
5853  wallet_keys_unlocker unlocker(*this, m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only, password);
5854 
5855  //keys loaded ok!
5856  //try to load wallet file. but even if we failed, it is not big problem
5857  if(!boost::filesystem::exists(m_wallet_file, e) || e)
5858  {
5859  LOG_PRINT_L0("file not found: " << m_wallet_file << ", starting with empty blockchain");
5860  m_account_public_address = m_account.get_keys().m_account_address;
5861  }
5862  else
5863  {
5865  std::string buf;
5866  bool r = epee::file_io_utils::load_file_to_string(m_wallet_file, buf, std::numeric_limits<size_t>::max());
5868 
5869  // try to read it as an encrypted cache
5870  try
5871  {
5872  LOG_PRINT_L1("Trying to decrypt cache data");
5873 
5875  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + m_wallet_file + '\"');
5876  std::string cache_data;
5877  cache_data.resize(cache_file_data.cache_data.size());
5878  crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), m_cache_key, cache_file_data.iv, &cache_data[0]);
5879 
5880  try {
5881  std::stringstream iss;
5882  iss << cache_data;
5884  ar >> *this;
5885  }
5886  catch(...)
5887  {
5888  // try with previous scheme: direct from keys
5889  crypto::chacha_key key;
5890  generate_chacha_key_from_secret_keys(key);
5892  try {
5893  std::stringstream iss;
5894  iss << cache_data;
5896  ar >> *this;
5897  }
5898  catch (...)
5899  {
5901  try
5902  {
5903  std::stringstream iss;
5904  iss << cache_data;
5906  ar >> *this;
5907  }
5908  catch (...)
5909  {
5910  LOG_PRINT_L0("Failed to open portable binary, trying unportable");
5911  boost::filesystem::copy_file(m_wallet_file, m_wallet_file + ".unportable", boost::filesystem::copy_option::overwrite_if_exists);
5912  std::stringstream iss;
5913  iss.str("");
5914  iss << cache_data;
5915  boost::archive::binary_iarchive ar(iss);
5916  ar >> *this;
5917  }
5918  }
5919  }
5920  }
5921  catch (...)
5922  {
5923  LOG_PRINT_L1("Failed to load encrypted cache, trying unencrypted");
5924  try {
5925  std::stringstream iss;
5926  iss << buf;
5928  ar >> *this;
5929  }
5930  catch (...)
5931  {
5932  LOG_PRINT_L0("Failed to open portable binary, trying unportable");
5933  boost::filesystem::copy_file(m_wallet_file, m_wallet_file + ".unportable", boost::filesystem::copy_option::overwrite_if_exists);
5934  std::stringstream iss;
5935  iss.str("");
5936  iss << buf;
5937  boost::archive::binary_iarchive ar(iss);
5938  ar >> *this;
5939  }
5940  }
5942  m_account_public_address.m_spend_public_key != m_account.get_keys().m_account_address.m_spend_public_key ||
5943  m_account_public_address.m_view_public_key != m_account.get_keys().m_account_address.m_view_public_key,
5944  error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file);
5945  }
5946 
5947  cryptonote::block genesis;
5948  generate_genesis(genesis);
5949  crypto::hash genesis_hash = get_block_hash(genesis);
5950 
5951  if (m_blockchain.empty())
5952  {
5953  m_blockchain.push_back(genesis_hash);
5954  m_last_block_reward = cryptonote::get_outs_etn_amount(genesis.miner_tx);
5955  }
5956  else
5957  {
5958  check_genesis(genesis_hash);
5959  }
5960 
5961  trim_hashchain();
5962 
5963  if (get_num_subaddress_accounts() == 0)
5964  add_subaddress_account(tr("Primary account"));
5965 
5966  try
5967  {
5968  find_and_save_rings(false);
5969  }
5970  catch (const std::exception &e)
5971  {
5972  MERROR("Failed to save rings, will try again next time");
5973  }
5974 
5975  try
5976  {
5977  m_message_store.read_from_file(get_multisig_wallet_state(), m_mms_file);
5978  }
5979  catch (const std::exception &e)
5980  {
5981  MERROR("Failed to initialize MMS, it will be unusable");
5982  }
5983 }
5984 //----------------------------------------------------------------------------------------------------
5985 void wallet2::trim_hashchain()
5986 {
5987  uint64_t height = m_checkpoints.get_max_height();
5988 
5989  for (const transfer_details &td: m_transfers)
5990  if (td.m_block_height < height)
5991  height = td.m_block_height;
5992 
5993  if (!m_blockchain.empty() && m_blockchain.size() == m_blockchain.offset())
5994  {
5995  MINFO("Fixing empty hashchain");
5998  m_daemon_rpc_mutex.lock();
5999  req.height = m_blockchain.size() - 1;
6000  bool r = invoke_http_json_rpc("/json_rpc", "getblockheaderbyheight", req, res, rpc_timeout);
6001  m_daemon_rpc_mutex.unlock();
6002  if (r && res.status == CORE_RPC_STATUS_OK)
6003  {
6005  epee::string_tools::hex_to_pod(res.block_header.hash, hash);
6006  m_blockchain.refill(hash);
6007  }
6008  else
6009  {
6010  MERROR("Failed to request block header from daemon, hash chain may be unable to sync till the wallet is loaded with a usable daemon");
6011  }
6012  }
6013  if (height > 0 && m_blockchain.size() > height)
6014  {
6015  --height;
6016  MDEBUG("trimming to " << height << ", offset " << m_blockchain.offset());
6017  m_blockchain.trim(height);
6018  }
6019 }
6020 //----------------------------------------------------------------------------------------------------
6021 void wallet2::check_genesis(const crypto::hash& genesis_hash) const {
6022  std::string what("Genesis block mismatch. You probably use wallet without testnet (or stagenet) flag with blockchain from test (or stage) network or vice versa");
6023 
6024  THROW_WALLET_EXCEPTION_IF(genesis_hash != m_blockchain.genesis(), error::wallet_internal_error, what);
6025 }
6026 //----------------------------------------------------------------------------------------------------
6027 std::string wallet2::path() const
6028 {
6029  return m_wallet_file;
6030 }
6031 //----------------------------------------------------------------------------------------------------
6032 void wallet2::store()
6033 {
6034  if (!m_wallet_file.empty())
6035  store_to("", epee::wipeable_string());
6036 }
6037 //----------------------------------------------------------------------------------------------------
6038 void wallet2::store_to(const std::string &path, const epee::wipeable_string &password)
6039 {
6040  trim_hashchain();
6041 
6042  // if file is the same, we do:
6043  // 1. save wallet to the *.new file
6044  // 2. remove old wallet file
6045  // 3. rename *.new to wallet_name
6046 
6047  // handle if we want just store wallet state to current files (ex store() replacement);
6048  bool same_file = true;
6049  if (!path.empty())
6050  {
6051  std::string canonical_path = boost::filesystem::canonical(m_wallet_file).string();
6052  size_t pos = canonical_path.find(path);
6053  same_file = pos != std::string::npos;
6054  }
6055 
6056 
6057  if (!same_file)
6058  {
6059  // check if we want to store to directory which doesn't exists yet
6060  boost::filesystem::path parent_path = boost::filesystem::path(path).parent_path();
6061 
6062  // if path is not exists, try to create it
6063  if (!parent_path.empty() && !boost::filesystem::exists(parent_path))
6064  {
6065  boost::system::error_code ec;
6066  if (!boost::filesystem::create_directories(parent_path, ec))
6067  {
6068  throw std::logic_error(ec.message());
6069  }
6070  }
6071  }
6072  // preparing wallet data
6073  std::stringstream oss;
6075  ar << *this;
6076 
6077  wallet2::cache_file_data cache_file_data = boost::value_initialized<wallet2::cache_file_data>();
6078  cache_file_data.cache_data = oss.str();
6079  std::string cipher;
6080  cipher.resize(cache_file_data.cache_data.size());
6081  cache_file_data.iv = crypto::rand<crypto::chacha_iv>();
6082  crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), m_cache_key, cache_file_data.iv, &cipher[0]);
6083  cache_file_data.cache_data = cipher;
6084 
6085  const std::string new_file = same_file ? m_wallet_file + ".new" : path;
6086  const std::string old_file = m_wallet_file;
6087  const std::string old_keys_file = m_keys_file;
6088  const std::string old_address_file = m_wallet_file + ".address.txt";
6089  const std::string old_mms_file = m_mms_file;
6090 
6091  // save keys to the new file
6092  // if we here, main wallet file is saved and we only need to save keys and address files
6093  if (!same_file) {
6094  prepare_file_names(path);
6095  bool r = store_keys(m_keys_file, password, false);
6097  if (boost::filesystem::exists(old_address_file))
6098  {
6099  // save address to the new file
6100  const std::string address_file = m_wallet_file + ".address.txt";
6101  r = file_io_utils::save_string_to_file(address_file, m_account.get_public_address_str(m_nettype));
6103  }
6104  // remove old wallet file
6105  r = boost::filesystem::remove(old_file);
6106  if (!r) {
6107  LOG_ERROR("error removing file: " << old_file);
6108  }
6109  // remove old keys file
6110  r = boost::filesystem::remove(old_keys_file);
6111  if (!r) {
6112  LOG_ERROR("error removing file: " << old_keys_file);
6113  }
6114  // remove old address file
6115  r = boost::filesystem::remove(old_address_file);
6116  if (!r) {
6117  LOG_ERROR("error removing file: " << old_address_file);
6118  }
6119  // remove old message store file
6120  if (boost::filesystem::exists(old_mms_file))
6121  {
6122  r = boost::filesystem::remove(old_mms_file);
6123  if (!r) {
6124  LOG_ERROR("error removing file: " << old_mms_file);
6125  }
6126  }
6127  } else {
6128  // save to new file
6129 #ifdef WIN32
6130  // On Windows avoid using std::ofstream which does not work with UTF-8 filenames
6131  // The price to pay is temporary higher memory consumption for string stream + binary archive
6132  std::ostringstream oss;
6133  binary_archive<true> oar(oss);
6135  if (success) {
6136  success = epee::file_io_utils::save_string_to_file(new_file, oss.str());
6137  }
6139 #else
6140  std::ofstream ostr;
6141  ostr.open(new_file, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);
6142  binary_archive<true> oar(ostr);
6144  ostr.close();
6145  THROW_WALLET_EXCEPTION_IF(!success || !ostr.good(), error::file_save_error, new_file);
6146 #endif
6147 
6148  // here we have "*.new" file, we need to rename it to be without ".new"
6149  std::error_code e = tools::replace_file(new_file, m_wallet_file);
6150  THROW_WALLET_EXCEPTION_IF(e, error::file_save_error, m_wallet_file, e);
6151  }
6152 
6153  if (m_message_store.get_active())
6154  {
6155  // While the "m_message_store" object of course always exist, a file for the message
6156  // store should only exist if the MMS is really active
6157  m_message_store.write_to_file(get_multisig_wallet_state(), m_mms_file);
6158  }
6159 
6160 }
6161 //----------------------------------------------------------------------------------------------------
6162 uint64_t wallet2::balance(uint32_t index_major, bool public_blockchain) const
6163 {
6164  uint64_t amount = 0;
6165  if(m_light_wallet)
6166  return m_light_wallet_unlocked_balance;
6167  for (const auto& i : balance_per_subaddress(index_major, public_blockchain))
6168  amount += i.second;
6169  return amount;
6170 }
6171 //----------------------------------------------------------------------------------------------------
6172 uint64_t wallet2::unlocked_balance(uint32_t index_major, bool public_blockchain, uint64_t *blocks_to_unlock) const
6173 {
6174  uint64_t amount = 0;
6175  if (blocks_to_unlock)
6176  *blocks_to_unlock = 0;
6177  if(m_light_wallet)
6178  return m_light_wallet_balance;
6179  for (const auto& i : unlocked_balance_per_subaddress(index_major, public_blockchain))
6180  {
6181  amount += i.second.first;
6182  if (blocks_to_unlock && i.second.second > *blocks_to_unlock)
6183  *blocks_to_unlock = i.second.second;
6184  }
6185  return amount;
6186 }
6187 //----------------------------------------------------------------------------------------------------
6188 std::map<uint32_t, uint64_t> wallet2::balance_per_subaddress(uint32_t index_major, bool public_blockchain) const
6189 {
6190  std::map<uint32_t, uint64_t> amount_per_subaddr;
6191  for (const auto& td: m_transfers)
6192  {
6193  if((public_blockchain && td.m_tx.version == 1) || (!public_blockchain && td.m_tx.version > 1))
6194  continue;
6195 
6196  if (td.m_subaddr_index.major == index_major && !td.m_spent && !td.m_frozen)
6197  {
6198  auto found = amount_per_subaddr.find(td.m_subaddr_index.minor);
6199  if (found == amount_per_subaddr.end())
6200  amount_per_subaddr[td.m_subaddr_index.minor] = td.amount();
6201  else
6202  found->second += td.amount();
6203  }
6204  }
6205  for (const auto& utx: m_unconfirmed_txs) {
6206  if ((public_blockchain && utx.second.m_tx.version == 1) || (!public_blockchain && utx.second.m_tx.version > 1))
6207  continue;
6208 
6209  if (utx.second.m_state != wallet2::unconfirmed_transfer_details::failed) {
6210  //HANDLE LOOPBACK OUTS INCLUDING CHANGE
6211  if(utx.second.m_tx.version > 1){
6212  for (const cryptonote::tx_out &out : utx.second.m_tx.vout) {
6213  if (out.target.type() == typeid(txout_to_key_public)) {
6214  // check whether this out is to one of our subaddresses
6215  auto target = boost::get<cryptonote::txout_to_key_public>(out.target);
6216  auto subaddr_found = m_subaddresses.find(target.address.m_spend_public_key);
6217  // if this out is to us
6218  // and the view key part of the destination matches that of our our subaddress,
6219  // and the major index of this subaddress corresponds to the current account we're getting balance for,
6220  // then add amount to balance
6221  if (subaddr_found != m_subaddresses.end() && get_subaddress(subaddr_found->second).m_view_public_key == target.address.m_view_public_key && subaddr_found->second.major == index_major) {
6222  auto found = amount_per_subaddr.find(subaddr_found->second.minor);
6223  if (found == amount_per_subaddr.end())
6224  amount_per_subaddr[subaddr_found->second.minor] = out.amount;
6225  else
6226  found->second += out.amount;
6227  } else {
6228  continue;
6229  }
6230  }
6231  }
6232  }
6233  // CHANGE HANDLING FOR V1 TX
6234  // (NB LOOPBACK OUTS APART FROM CHANGE AREN'T FACTORED INTO BAL WHILST TX IS UNCONFIRMED
6235  // and there is no need to fix this (monero) issue as we've migrated to a transparent chain)
6236  if (utx.second.m_tx.version == 1 && utx.second.m_subaddr_account == index_major) {
6237  // all changes go to 0-th subaddress (in the current subaddress account)
6238  auto found = amount_per_subaddr.find(0);
6239  if (found == amount_per_subaddr.end())
6240  amount_per_subaddr[0] = utx.second.m_change;
6241  else
6242  found->second += utx.second.m_change;
6243  }
6244  }
6245  }
6246  return amount_per_subaddr;
6247 }
6248 //----------------------------------------------------------------------------------------------------
6249 std::map<uint32_t, std::pair<uint64_t, uint64_t>> wallet2::unlocked_balance_per_subaddress(uint32_t index_major, bool public_blockchain) const
6250 {
6251  std::map<uint32_t, std::pair<uint64_t, uint64_t>> amount_per_subaddr; //map of subaddr minor index : <amount,unlock t>
6252  const uint64_t blockchain_height = get_blockchain_current_height();
6253  //Figure out amount & blocks_to_unlock for the major subaddress index for each transfer
6254  for(const transfer_details& td: m_transfers)
6255  {
6256  if((public_blockchain && td.m_tx.version == 1) || (!public_blockchain && td.m_tx.version > 1))
6257  continue;
6258 
6259  if(td.m_subaddr_index.major == index_major && !td.m_spent && !td.m_frozen)
6260  {
6261  uint64_t amount = 0, blocks_to_unlock = 0;
6262  if (is_transfer_unlocked(td))
6263  {
6264  amount = td.amount();
6265  blocks_to_unlock = 0;
6266  }
6267  else
6268  {
6269 
6270  uint64_t v8height = m_nettype == TESTNET ? 446674 : 589169;
6271  uint16_t UNLOCK_WINDOW = td.m_block_height > v8height ? ETN_DEFAULT_TX_SPENDABLE_AGE_V8 : CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE;
6272  uint64_t unlock_height = td.m_block_height + std::max<uint64_t>(UNLOCK_WINDOW, CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS);
6273  if (td.m_tx.unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER && td.m_tx.unlock_time > unlock_height)
6274  unlock_height = td.m_tx.unlock_time;
6275  blocks_to_unlock = unlock_height > blockchain_height ? unlock_height - blockchain_height : 0;
6276  amount = 0;
6277  }
6278  auto found = amount_per_subaddr.find(td.m_subaddr_index.minor);
6279  // if we don't have this subaddress index (key) in our map, create a new entry, otherwise just add a new pair
6280  if (found == amount_per_subaddr.end())
6281  amount_per_subaddr[td.m_subaddr_index.minor] = std::make_pair(amount, blocks_to_unlock);
6282  else
6283  {
6284  found->second.first += amount;
6285  found->second.second = std::max(found->second.second, blocks_to_unlock);
6286  }
6287  }
6288  }
6289  return amount_per_subaddr;
6290 }
6291 //----------------------------------------------------------------------------------------------------
6292 uint64_t wallet2::balance_all(bool public_blockchain) const
6293 {
6294  uint64_t r = 0;
6295  for (uint32_t index_major = 0; index_major < get_num_subaddress_accounts(); ++index_major)
6296  r += balance(index_major, public_blockchain);
6297  return r;
6298 }
6299 //----------------------------------------------------------------------------------------------------
6300 uint64_t wallet2::unlocked_balance_all(bool public_blockchain, uint64_t *blocks_to_unlock) const
6301 {
6302  uint64_t r = 0;
6303  if (blocks_to_unlock)
6304  *blocks_to_unlock = 0;
6305  for (uint32_t index_major = 0; index_major < get_num_subaddress_accounts(); ++index_major)
6306  {
6307  uint64_t local_blocks_to_unlock;
6308  r += unlocked_balance(index_major, public_blockchain ,blocks_to_unlock ? &local_blocks_to_unlock : NULL);
6309  if (blocks_to_unlock)
6310  *blocks_to_unlock = std::max(*blocks_to_unlock, local_blocks_to_unlock);
6311  }
6312  return r;
6313 }
6314 //----------------------------------------------------------------------------------------------------
6315 void wallet2::get_transfers(wallet2::transfer_container& incoming_transfers) const
6316 {
6317  incoming_transfers = m_transfers;
6318 }
6319 //----------------------------------------------------------------------------------------------------
6320 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
6321 {
6322  auto range = m_payments.equal_range(payment_id);
6323  std::for_each(range.first, range.second, [&payments, &min_height, &subaddr_account, &subaddr_indices](const payment_container::value_type& x) {
6324  if (min_height < x.second.m_block_height &&
6325  (!subaddr_account || *subaddr_account == x.second.m_subaddr_index.major) &&
6326  (subaddr_indices.empty() || subaddr_indices.count(x.second.m_subaddr_index.minor) == 1))
6327  {
6328  payments.push_back(x.second);
6329  }
6330  });
6331 }
6332 //----------------------------------------------------------------------------------------------------
6333 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
6334 {
6335  auto range = std::make_pair(m_payments.begin(), m_payments.end());
6336  std::for_each(range.first, range.second, [&payments, &min_height, &max_height, &subaddr_account, &subaddr_indices](const payment_container::value_type& x) {
6337  if (min_height < x.second.m_block_height && max_height >= x.second.m_block_height &&
6338  (!subaddr_account || *subaddr_account == x.second.m_subaddr_index.major) &&
6339  (subaddr_indices.empty() || subaddr_indices.count(x.second.m_subaddr_index.minor) == 1))
6340  {
6341  payments.push_back(x);
6342  }
6343  });
6344 }
6345 //----------------------------------------------------------------------------------------------------
6346 void wallet2::get_payments_out(std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>>& confirmed_payments,
6347  uint64_t min_height, uint64_t max_height, const boost::optional<uint32_t>& subaddr_account, const std::set<uint32_t>& subaddr_indices) const
6348 {
6349  for (auto i = m_confirmed_txs.begin(); i != m_confirmed_txs.end(); ++i) {
6350  if (i->second.m_block_height <= min_height || i->second.m_block_height > max_height)
6351  continue;
6352  if (subaddr_account && *subaddr_account != i->second.m_subaddr_account)
6353  continue;
6354  if (!subaddr_indices.empty() && std::count_if(i->second.m_subaddr_indices.begin(), i->second.m_subaddr_indices.end(), [&subaddr_indices](uint32_t index) { return subaddr_indices.count(index) == 1; }) == 0)
6355  continue;
6356  if (i->second.m_is_migration) //avoid as processed by separate function
6357  continue;
6358  if(i->second.m_is_sc_migration) //avoid as processed by separate function
6359  continue;
6360  confirmed_payments.push_back(*i);
6361  }
6362 }//----------------------------------------------------------------------------------------------------
6363 void wallet2::get_payments_out_migration(std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>>& confirmed_payments,
6364  uint64_t min_height, uint64_t max_height, const boost::optional<uint32_t>& subaddr_account, const std::set<uint32_t>& subaddr_indices) const
6365 {
6366  for (auto i = m_confirmed_txs.begin(); i != m_confirmed_txs.end(); ++i) {
6367  if (i->second.m_block_height <= min_height || i->second.m_block_height > max_height)
6368  continue;
6369  if (subaddr_account && *subaddr_account != i->second.m_subaddr_account)
6370  continue;
6371  if (!subaddr_indices.empty() && std::count_if(i->second.m_subaddr_indices.begin(), i->second.m_subaddr_indices.end(), [&subaddr_indices](uint32_t index) { return subaddr_indices.count(index) == 1; }) == 0)
6372  continue;
6373  if (!i->second.m_is_migration)
6374  continue;
6375 
6376  confirmed_payments.push_back(*i);
6377  }
6378 }
6379 //----------------------------------------------------------------------------------------------------
6380 void wallet2::get_payments_out_sc_migration(std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>>& confirmed_payments,
6381  uint64_t min_height, uint64_t max_height, const boost::optional<uint32_t>& subaddr_account, const std::set<uint32_t>& subaddr_indices) const{
6382 
6383  for (auto i = m_confirmed_txs.begin(); i != m_confirmed_txs.end(); ++i) {
6384  if (i->second.m_block_height <= min_height || i->second.m_block_height > max_height)
6385  continue;
6386  if (subaddr_account && *subaddr_account != i->second.m_subaddr_account)
6387  continue;
6388  if (!subaddr_indices.empty() && std::count_if(i->second.m_subaddr_indices.begin(), i->second.m_subaddr_indices.end(), [&subaddr_indices](uint32_t index) { return subaddr_indices.count(index) == 1; }) == 0)
6389  continue;
6390  if (!i->second.m_is_sc_migration)
6391  continue;
6392  confirmed_payments.push_back(*i);
6393  }
6394 }
6395 //----------------------------------------------------------------------------------------------------
6396 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
6397 {
6398  for (auto i = m_unconfirmed_txs.begin(); i != m_unconfirmed_txs.end(); ++i) {
6399  if (subaddr_account && *subaddr_account != i->second.m_subaddr_account)
6400  continue;
6401  if (!subaddr_indices.empty() && std::count_if(i->second.m_subaddr_indices.begin(), i->second.m_subaddr_indices.end(), [&subaddr_indices](uint32_t index) { return subaddr_indices.count(index) == 1; }) == 0)
6402  continue;
6403  unconfirmed_payments.push_back(*i);
6404  }
6405 }
6406 //----------------------------------------------------------------------------------------------------
6407 
6408 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
6409 {
6410  for (auto i = m_unconfirmed_payments.begin(); i != m_unconfirmed_payments.end(); ++i) {
6411  if ((!subaddr_account || *subaddr_account == i->second.m_pd.m_subaddr_index.major) &&
6412  (subaddr_indices.empty() || subaddr_indices.count(i->second.m_pd.m_subaddr_index.minor) == 1))
6413  unconfirmed_payments.push_back(*i);
6414  }
6415 }
6416 //----------------------------------------------------------------------------------------------------
6417 void wallet2::rescan_spent()
6418 {
6419  // This is RPC call that can take a long time if there are many outputs,
6420  // so we call it several times, in stripes, so we don't time out spuriously
6421 
6422  // M_TRANSFERS is a container of OUTPUTS and NOT a container of entire transfers!
6423 
6424  // The logic for dealing with publicised (v8 hf) outputs is as follows:
6425  // 1. Check the output block height in m_transfers. If it's >= v8 hard fork height, the output must be a public one,
6426  // 2. m_transfers contains the tx hash and relative out index for each output which uniquely determine
6427  // 'chainstate UTXOs' in the blockchain database; if a chainstate UTXO is present in the DB, the output is truly
6428  // unspent. However nonexistence of the UTXO in the db doesn't mean it's spent, only that it doesn't exist. So we
6429  // must first check (by some means) that the output did exist. Use the tx input db.
6430  // 3. Call the daemon for this output and ask of the spent status.
6431  // 4. Set the correct spent status of the output in m_transfers
6432 
6433  std::vector<int> spent_status;
6434  spent_status.reserve(m_transfers.size());
6435  const size_t chunk_size = 1000;
6436  for (size_t start_offset = 0; start_offset < m_transfers.size(); start_offset += chunk_size)
6437  {
6438  const size_t n_outputs = std::min<size_t>(chunk_size, m_transfers.size() - start_offset); // 1000 or less if we dont have 1000
6439  MDEBUG("Calling is_key_image_spent on " << start_offset << " - " << (start_offset + n_outputs - 1) << ", out of " << m_transfers.size());
6441  COMMAND_RPC_IS_KEY_IMAGE_SPENT::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
6442  for (size_t n = start_offset; n < start_offset + n_outputs; ++n) //loop over key images for the outputs in m_transfers and put the key image in the request
6443  req.key_images.push_back(string_tools::pod_to_hex(m_transfers[n].m_key_image));
6444  m_daemon_rpc_mutex.lock();
6445  bool r = invoke_http_json("/is_key_image_spent", req, daemon_resp, rpc_timeout); //fire off the check command
6446  m_daemon_rpc_mutex.unlock();
6447  THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "is_key_image_spent");
6448  THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "is_key_image_spent");
6449  THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::is_key_image_spent_error, get_rpc_status(daemon_resp.status));
6450  THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != n_outputs, error::wallet_internal_error,
6451  "daemon returned wrong response for is_key_image_spent, wrong amounts count = " +
6452  std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(n_outputs));
6453  std::copy(daemon_resp.spent_status.begin(), daemon_resp.spent_status.end(), std::back_inserter(spent_status));
6454  }
6455 
6456 
6457  // urgent code update so just duplicate code above for public outputs
6458  uint64_t v8height = m_nettype == TESTNET ? 446674 : 589169;
6459  for (size_t start_offset = 0; start_offset < m_transfers.size(); start_offset += chunk_size)
6460  {
6461  const size_t n_outputs = std::min<size_t>(chunk_size, m_transfers.size() - start_offset);
6462  MDEBUG("Preparing is_public_output_spent request for outputs " << start_offset << " - " << (start_offset + n_outputs - 1) << ", out of " << m_transfers.size());
6463 
6466 
6467  // Prepare the request for public outputs only for m_transfers after v8 height
6468  for (size_t k = start_offset; k < start_offset + n_outputs; ++k) {
6469  if (m_transfers[k].m_block_height >= v8height) {
6470  public_output pub_out;
6471  pub_out.txid = epee::string_tools::pod_to_hex(m_transfers[k].m_txid);
6472  pub_out.relative_out_index = static_cast<uint64_t>(m_transfers[k].m_internal_output_index);
6473  req.public_outputs.push_back(pub_out);
6474  }
6475  }
6476 
6477  if(req.public_outputs.size() == 0){
6478  MDEBUG("No public outs found in the range: " << start_offset << " - " << (start_offset + n_outputs - 1) << ", out of " << m_transfers.size() << ", skipping chunk");
6479  continue;
6480  }
6481  // We always call the daemon, but the request may be empty if no outputs meet the criteria
6482  m_daemon_rpc_mutex.lock();
6483  bool r = invoke_http_json("/is_public_output_spent", req, daemon_resp, rpc_timeout);
6484  m_daemon_rpc_mutex.unlock();
6485  THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "is_public_output_spent");
6486  THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "is_public_output_spent");
6487  THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::is_public_output_spent_error, get_rpc_status(daemon_resp.status));
6488  THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != req.public_outputs.size(), error::wallet_internal_error,
6489  "daemon returned wrong response for is_public_output_spent, wrong amount count = " +
6490  std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(req.public_outputs.size()));
6491 
6492  // Update spent_status only for outputs that were included in the request. do this by iterating through m_transfers and if it's >= v8 height
6493  // then set the corresponding spent status for the same index. use a request index like so because not always does n == request index
6494  // because not all outputs are public outputs
6495  size_t request_index = 0;
6496  for (size_t k = start_offset; k < start_offset + n_outputs; ++k) {
6497  if (m_transfers[k].m_block_height >= v8height) {
6498  spent_status[k] = daemon_resp.spent_status[request_index++];
6499  }
6500  }
6501  }
6502 
6503  // update spent status in m_transfers
6504  // spent_status[i] guide:
6505  // UNSPENT = 0,
6506  // SPENT_IN_BLOCKCHAIN = 1,
6507  // SPENT_IN_POOL = 2,
6508  for (size_t i = 0; i < m_transfers.size(); ++i)
6509  {
6510  transfer_details& td = m_transfers[i];
6511  // a view wallet may not know about key images. only skip in this case IF it isn't a public output
6512  if (!(m_transfers[i].m_block_height >= v8height) && (!td.m_key_image_known || td.m_key_image_partial)) //we will hit this for all public outs, so modify here
6513  continue;
6514 
6515  if (td.m_spent != (spent_status[i] != SPENT_STATUS::UNSPENT)) // if output in m_transfers is unspent and the daemon says spent or the other way round, handle either which way
6516  {
6517  if (td.m_spent) // given parent if statement, spent means we need to change to unspent
6518  {
6519  if(!(m_transfers[i].m_block_height >= v8height)){
6520  LOG_PRINT_L0("Marking output " << i << "(" << td.m_key_image << ") as unspent, it was marked as spent");
6521  } else{
6522  LOG_PRINT_L0("Marking public output " << i << " (txid: " << td.m_txid << ", index: " << td.m_internal_output_index << ") as unspent, it was marked as spent");
6523  }
6524  set_unspent(i);
6525  td.m_spent_height = 0;
6526  }
6527  else // given parent if statement, unspent means we need to change to spent
6528  {
6529  if (!(m_transfers[i].m_block_height >= v8height)) {
6530  LOG_PRINT_L0("Marking output " << i << " (key image: " << epee::string_tools::pod_to_hex(td.m_key_image) << ") as spent, it was marked as unspent");
6531  } else {
6532  LOG_PRINT_L0("Not marking output " << i << " (txid: " << epee::string_tools::pod_to_hex(td.m_txid) << ", index: " << td.m_internal_output_index << ") as spent since block height " << td.m_block_height << " is below the threshold of " << v8height);
6533  }
6534  set_spent(i, td.m_spent_height);
6535  }
6536  }
6537  }
6538 }
6539 //----------------------------------------------------------------------------------------------------
6540 void wallet2::rescan_blockchain(bool hard, bool refresh, bool keep_key_images)
6541 {
6542  CHECK_AND_ASSERT_THROW_MES(!hard || !keep_key_images, "Cannot preserve key images on hard rescan");
6543  const size_t transfers_cnt = m_transfers.size();
6544  crypto::hash transfers_hash{};
6545 
6546  if(hard)
6547  {
6548  clear();
6549  setup_new_blockchain();
6550  }
6551  else
6552  {
6553  if (keep_key_images && refresh)
6554  hash_m_transfers((int64_t) transfers_cnt, transfers_hash);
6555  clear_soft(keep_key_images);
6556  }
6557 
6558  if (refresh)
6559  this->refresh(false);
6560 
6561  if (refresh && keep_key_images)
6562  finish_rescan_bc_keep_key_images(transfers_cnt, transfers_hash);
6563 }
6564 //----------------------------------------------------------------------------------------------------
6565 bool wallet2::is_transfer_unlocked(const transfer_details& td) const
6566 {
6567  return is_transfer_unlocked(td.m_tx.unlock_time, td.m_block_height);
6568 }
6569 //----------------------------------------------------------------------------------------------------
6570 bool wallet2::is_transfer_unlocked(uint64_t unlock_time, uint64_t block_height) const
6571 {
6572  if(!is_tx_spendtime_unlocked(unlock_time, block_height))
6573  return false;
6574 
6575  uint64_t v8height = m_nettype == TESTNET ? 446674 : 589169;
6576  uint16_t UNLOCK_WINDOW = block_height > v8height ? ETN_DEFAULT_TX_SPENDABLE_AGE_V8 : CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE;
6577 
6578  if(block_height + UNLOCK_WINDOW > get_blockchain_current_height())
6579  return false;
6580 
6581  return true;
6582 }
6583 //----------------------------------------------------------------------------------------------------
6584 bool wallet2::is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_height) const
6585 {
6586  if(unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER)
6587  {
6588  //interpret as block index
6589  if(get_blockchain_current_height()-1 + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS >= unlock_time)
6590  return true;
6591  else
6592  return false;
6593  }else
6594  {
6595  //interpret as time
6596  uint64_t current_time = static_cast<uint64_t>(time(NULL));
6597  // XXX: this needs to be fast, so we'd need to get the starting heights
6598  // from the daemon to be correct once voting kicks in
6599  uint64_t v6height = m_nettype == TESTNET ? 190059 : 307499;
6601  if(current_time + leeway >= unlock_time)
6602  return true;
6603  else
6604  return false;
6605  }
6606  return false;
6607 }
6608 //----------------------------------------------------------------------------------------------------
6609 namespace
6610 {
6611  template<typename T>
6612  T pop_index(std::vector<T>& vec, size_t idx)
6613  {
6614  CHECK_AND_ASSERT_MES(!vec.empty(), T(), "Vector must be non-empty");
6615  CHECK_AND_ASSERT_MES(idx < vec.size(), T(), "idx out of bounds");
6616 
6617  T res = vec[idx];
6618  if (idx + 1 != vec.size())
6619  {
6620  vec[idx] = vec.back();
6621  }
6622  vec.resize(vec.size() - 1);
6623 
6624  return res;
6625  }
6626 
6627  template<typename T>
6628  T pop_random_value(std::vector<T>& vec)
6629  {
6630  CHECK_AND_ASSERT_MES(!vec.empty(), T(), "Vector must be non-empty");
6631 
6632  size_t idx = crypto::rand_idx(vec.size());
6633  return pop_index (vec, idx);
6634  }
6635 
6636  template<typename T>
6637  T pop_back(std::vector<T>& vec)
6638  {
6639  CHECK_AND_ASSERT_MES(!vec.empty(), T(), "Vector must be non-empty");
6640 
6641  T res = vec.back();
6642  vec.pop_back();
6643  return res;
6644  }
6645 
6646  template<typename T>
6647  void pop_if_present(std::vector<T>& vec, T e)
6648  {
6649  for (size_t i = 0; i < vec.size(); ++i)
6650  {
6651  if (e == vec[i])
6652  {
6653  pop_index (vec, i);
6654  return;
6655  }
6656  }
6657  }
6658 }
6659 //----------------------------------------------------------------------------------------------------
6660 // This returns a handwavy estimation of how much two outputs are related
6661 // If they're from the same tx, then they're fully related. From close block
6662 // heights, they're kinda related. The actual values don't matter, just
6663 // their ordering, but it could become more murky if we add scores later.
6664 float wallet2::get_output_relatedness(const transfer_details &td0, const transfer_details &td1) const
6665 {
6666  int dh;
6667 
6668  // expensive test, and same tx will fall onto the same block height below
6669  if (td0.m_txid == td1.m_txid)
6670  return 1.0f;
6671 
6672  // same block height -> possibly tx burst, or same tx (since above is disabled)
6673  dh = td0.m_block_height > td1.m_block_height ? td0.m_block_height - td1.m_block_height : td1.m_block_height - td0.m_block_height;
6674  if (dh == 0)
6675  return 0.9f;
6676 
6677  // adjacent blocks -> possibly tx burst
6678  if (dh == 1)
6679  return 0.8f;
6680 
6681  // could extract the payment id, and compare them, but this is a bit expensive too
6682 
6683  // similar block heights
6684  if (dh < 10)
6685  return 0.2f;
6686 
6687  // don't think these are particularly related
6688  return 0.0f;
6689 }
6690 //----------------------------------------------------------------------------------------------------
6691 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
6692 {
6693  std::vector<size_t> candidates;
6694  float best_relatedness = 1.0f;
6695  for (size_t n = 0; n < unused_indices.size(); ++n)
6696  {
6697  const transfer_details &candidate = transfers[unused_indices[n]];
6698  float relatedness = 0.0f;
6699  for (std::vector<size_t>::const_iterator i = selected_transfers.begin(); i != selected_transfers.end(); ++i)
6700  {
6701  float r = get_output_relatedness(candidate, transfers[*i]);
6702  if (r > relatedness)
6703  {
6704  relatedness = r;
6705  if (relatedness == 1.0f)
6706  break;
6707  }
6708  }
6709 
6710  if (relatedness < best_relatedness)
6711  {
6712  best_relatedness = relatedness;
6713  candidates.clear();
6714  }
6715 
6716  if (relatedness == best_relatedness)
6717  candidates.push_back(n);
6718  }
6719 
6720  // we have all the least related outputs in candidates, so we can pick either
6721  // the smallest, or a random one, depending on request
6722  size_t idx;
6723  if (smallest)
6724  {
6725  idx = 0;
6726  for (size_t n = 0; n < candidates.size(); ++n)
6727  {
6728  const transfer_details &td = transfers[unused_indices[candidates[n]]];
6729  if (td.amount() < transfers[unused_indices[candidates[idx]]].amount())
6730  idx = n;
6731  }
6732  }
6733  else
6734  {
6735  idx = crypto::rand_idx(candidates.size());
6736  }
6737  return pop_index (unused_indices, candidates[idx]);
6738 }
6739 //----------------------------------------------------------------------------------------------------
6740 size_t wallet2::pop_best_value(std::vector<size_t> &unused_indices, const std::vector<size_t>& selected_transfers, bool smallest) const
6741 {
6742  return pop_best_value_from(m_transfers, unused_indices, selected_transfers, smallest);
6743 }
6744 //----------------------------------------------------------------------------------------------------
6745 // Select random input sources for transaction.
6746 // returns:
6747 // direct return: amount of etn found
6748 // modified reference: selected_transfers, a list of iterators/indices of input sources
6749 uint64_t wallet2::select_transfers(uint64_t needed_etn, std::vector<size_t> unused_transfers_indices, std::vector<size_t>& selected_transfers) const
6750 {
6751  uint64_t found_etn = 0;
6752  selected_transfers.reserve(unused_transfers_indices.size());
6753  while (found_etn < needed_etn && !unused_transfers_indices.empty())
6754  {
6755  size_t idx = pop_best_value(unused_transfers_indices, selected_transfers);
6756 
6757  const transfer_container::const_iterator it = m_transfers.begin() + idx;
6758  selected_transfers.push_back(idx);
6759  found_etn += it->amount();
6760  }
6761 
6762  return found_etn;
6763 }
6764 //----------------------------------------------------------------------------------------------------
6765 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)
6766 {
6767  unconfirmed_transfer_details& utd = m_unconfirmed_txs[cryptonote::get_transaction_hash(tx)];
6768  utd.m_amount_in = amount_in;
6769  utd.m_amount_out = 0;
6770 
6771  if(tx.version == 1){
6772  for (const auto &d: dests)
6773  utd.m_amount_out += d.amount;
6774  utd.m_amount_out += change_amount; // dests does not contain change
6775  utd.m_change = change_amount;
6776  utd.m_dests = dests;
6777  } else {
6778  // grab the input owner keys/address by using the subaddr indicies used for the transaction
6779  std::vector<account_public_address> input_addresses;
6780  for (auto minor_index : subaddr_indices) {
6781  cryptonote::subaddress_index index{subaddr_account, minor_index};
6782  input_addresses.push_back(get_subaddress(index));
6783  }
6784 
6785  //build list of potential change outputs - NB if *ALL* outs go to input addresses, then we DON'T conside them change; the transaction is a looped sweep.
6786  // If one or more outs do not go to an input address, we consider ALL other outputs as change outputs
6787  std::unordered_set<uint32_t> change_indexes;
6788  for (size_t i = 0; i < tx.vout.size(); ++i) {
6789  for (auto input_address : input_addresses) {
6790  if (boost::get<txout_to_key_public>(tx.vout[i].target).address == input_address) {
6791  change_indexes.insert(i);
6792  continue;
6793  }
6794  }
6795  }
6796 
6797  // if this is true we have a sweep tx so clear all change out indexes
6798  if (change_indexes.size() == tx.vout.size()) {
6799  change_indexes.clear();
6800  }
6801 
6802  int64_t total_change = 0;
6803  for (auto &change_index : change_indexes)
6804  total_change += tx.vout[change_index].amount;
6805  utd.m_change = total_change;
6806 
6807  //todo: optimise & refactor
6808  // fill destinations
6809  for (size_t i = 0; i < tx.vout.size(); ++i) {
6810  if (change_indexes.find(i) == change_indexes.end()) { // only include non-change outs as dests
6811  auto output = boost::get<txout_to_key_public>(tx.vout[i].target); // grab output from the tx
6812  //predicate for comparison later on
6813  auto pred = [output](const tx_destination_entry &destination) {
6814  return destination.addr == output.address;
6815  };
6816 
6817  //search our working list of destinations in entry, and either add output amount to the
6818  // running total in the case of a match, or add a new destination otherwise
6819  auto dest_ptr = std::find_if(std::begin(utd.m_dests),
6820  std::end(utd.m_dests), pred);
6821  if (dest_ptr != std::end(utd.m_dests)) {
6822  dest_ptr->amount += tx.vout[i].amount;
6823  } else {
6824  utd.m_dests.push_back(tx_destination_entry(
6825  tx.vout[i].amount,
6826  output.address,
6827  output.m_address_prefix ==
6828  get_config(this->m_nettype).CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX ? true : false
6829  ));
6830  }
6831  }
6832  }
6833  //amount out is the sum of destinations and change (if loopback tx change = 0 so this still checks out)
6834  for (const auto &d: utd.m_dests)
6835  utd.m_amount_out += d.amount;
6836  utd.m_amount_out += total_change;
6837  }
6838 
6839  utd.m_tx = (const cryptonote::transaction_prefix&)tx;
6840  utd.m_sent_time = time(NULL);
6841  utd.m_payment_id = payment_id;
6842  utd.m_state = wallet2::unconfirmed_transfer_details::pending;
6843  utd.m_timestamp = time(NULL);
6844  utd.m_subaddr_account = subaddr_account;
6845  utd.m_subaddr_indices = subaddr_indices;
6846  for (const auto &in: tx.vin)
6847  {
6848  if (in.type() != typeid(cryptonote::txin_to_key))
6849  continue;
6850  const auto &txin = boost::get<cryptonote::txin_to_key>(in);
6851  utd.m_rings.push_back(std::make_pair(txin.k_image, txin.key_offsets));
6852  }
6853 }
6854 
6855 //----------------------------------------------------------------------------------------------------
6856 crypto::hash wallet2::get_payment_id(const pending_tx &ptx) const
6857 {
6858  std::vector<tx_extra_field> tx_extra_fields;
6859  parse_tx_extra(ptx.tx.extra, tx_extra_fields); // ok if partially parsed
6860  tx_extra_nonce extra_nonce;
6861  crypto::hash payment_id = null_hash;
6862  if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
6863  {
6864  crypto::hash8 payment_id8 = null_hash8;
6865  if(get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
6866  {
6867  if (ptx.dests.empty())
6868  {
6869  MWARNING("Encrypted payment id found, but no destinations public key, cannot decrypt");
6870  return crypto::null_hash;
6871  }
6872  if (m_account.get_device().decrypt_payment_id(payment_id8, ptx.dests[0].addr.m_view_public_key, ptx.tx_key))
6873  {
6874  memcpy(payment_id.data, payment_id8.data, 8);
6875  }
6876  }
6877  else if (!get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
6878  {
6879  payment_id = crypto::null_hash;
6880  }
6881  }
6882  return payment_id;
6883 }
6884 
6885 //----------------------------------------------------------------------------------------------------
6886 // take a pending tx and actually send it to the daemon
6887 void wallet2::commit_tx(pending_tx& ptx)
6888 {
6889  using namespace cryptonote;
6890 
6891  if(m_light_wallet)
6892  {
6895  oreq.address = get_account().get_public_address_str(m_nettype);
6896  oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
6898  m_daemon_rpc_mutex.lock();
6899  bool r = invoke_http_json("/submit_raw_tx", oreq, ores, rpc_timeout, "POST");
6900  m_daemon_rpc_mutex.unlock();
6902  // MyMonero and OpenMonero use different status strings
6903  THROW_WALLET_EXCEPTION_IF(ores.status != "OK" && ores.status != "success" , error::tx_rejected, ptx.tx, get_rpc_status(ores.status), ores.error);
6904  }
6905  else
6906  {
6907  // Normal submit
6910  req.do_not_relay = false;
6911  req.do_sanity_checks = true;
6912  COMMAND_RPC_SEND_RAW_TX::response daemon_send_resp;
6913  m_daemon_rpc_mutex.lock();
6914  bool r = invoke_http_json("/sendrawtransaction", req, daemon_send_resp, rpc_timeout);
6915  m_daemon_rpc_mutex.unlock();
6916  THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "sendrawtransaction");
6917  THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "sendrawtransaction");
6918  THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status != CORE_RPC_STATUS_OK, error::tx_rejected, ptx.tx, get_rpc_status(daemon_send_resp.status), get_text_reason(daemon_send_resp));
6919  // sanity checks
6920  for (size_t idx: ptx.selected_transfers)
6921  {
6922  THROW_WALLET_EXCEPTION_IF(idx >= m_transfers.size(), error::wallet_internal_error,
6923  "Bad output index in selected transfers: " + boost::lexical_cast<std::string>(idx));
6924  }
6925  }
6926  crypto::hash txid;
6927 
6928  txid = get_transaction_hash(ptx.tx);
6929  crypto::hash payment_id = crypto::null_hash;
6930  std::vector<cryptonote::tx_destination_entry> dests;
6931  uint64_t amount_in = 0;
6932  if (store_tx_info())
6933  {
6934  payment_id = get_payment_id(ptx);
6935  dests = ptx.dests;
6936  for(size_t idx: ptx.selected_transfers)
6937  amount_in += m_transfers[idx].amount();
6938  }
6939  add_unconfirmed_tx(ptx.tx, amount_in, dests, payment_id, ptx.change_dts.amount, ptx.construction_data.subaddr_account, ptx.construction_data.subaddr_indices);
6940  if (store_tx_info())
6941  {
6942  m_tx_keys.insert(std::make_pair(txid, ptx.tx_key));
6943  m_additional_tx_keys.insert(std::make_pair(txid, ptx.additional_tx_keys));
6944  }
6945 
6946  LOG_PRINT_L2("transaction " << txid << " generated ok and sent to daemon, key_images: [" << ptx.key_images << "]");
6947 
6948  for(size_t idx: ptx.selected_transfers)
6949  {
6950  set_spent(idx, 0);
6951  }
6952 
6953  // tx generated, get rid of used k values
6954  for (size_t idx: ptx.selected_transfers)
6955  m_transfers[idx].m_multisig_k.clear();
6956 
6957  //fee includes dust if dust policy specified it.
6958  LOG_PRINT_L1("Transaction successfully sent. <" << txid << ">" << ENDL
6959  << "Commission: " << print_etn(ptx.fee) << " (dust sent to dust addr: " << print_etn((ptx.dust_added_to_fee ? 0 : ptx.dust)) << ")" << ENDL
6960  << "Pre V10 Balance: " << print_etn(balance(ptx.construction_data.subaddr_account, false)) << ENDL
6961  << "Pre V10 Unlocked Balance: " << print_etn(unlocked_balance(ptx.construction_data.subaddr_account, false)) << ENDL
6962  << "Public Chain Balance: " << print_etn(balance(ptx.construction_data.subaddr_account, true)) << ENDL
6963  << "Public Chain Unlocked Balance: " << print_etn(unlocked_balance(ptx.construction_data.subaddr_account, true)) << ENDL
6964  << "Please, wait for confirmation for your balance to be unlocked.");
6965 }
6966 
6967 void wallet2::commit_tx(std::vector<pending_tx>& ptx_vector)
6968 {
6969  for (auto & ptx : ptx_vector)
6970  {
6971  commit_tx(ptx);
6972  }
6973 }
6974 //----------------------------------------------------------------------------------------------------
6975 bool wallet2::save_tx(const std::vector<pending_tx>& ptx_vector, const std::string &filename) const
6976 {
6977  LOG_PRINT_L0("saving " << ptx_vector.size() << " transactions");
6978  std::string ciphertext = dump_tx_to_str(ptx_vector);
6979  if (ciphertext.empty())
6980  return false;
6981  return epee::file_io_utils::save_string_to_file(filename, ciphertext);
6982 }
6983 //----------------------------------------------------------------------------------------------------
6984 std::string wallet2::dump_tx_to_str(const std::vector<pending_tx> &ptx_vector) const
6985 {
6986  LOG_PRINT_L0("saving " << ptx_vector.size() << " transactions");
6987  unsigned_tx_set txs;
6988  for (auto &tx: ptx_vector)
6989  {
6990  // Short payment id is encrypted with tx_key.
6991  // Since sign_tx() generates new tx_keys and encrypts the payment id, we need to save the decrypted payment ID
6992  // Save tx construction_data to unsigned_tx_set
6993  txs.txes.push_back(get_construction_data_with_decrypted_short_payment_id(tx, m_account.get_device()));
6994  }
6995 
6996  txs.transfers = export_outputs();
6997  // save as binary
6998  std::ostringstream oss;
7000  try
7001  {
7002  ar << txs;
7003  }
7004  catch (...)
7005  {
7006  return std::string();
7007  }
7008  LOG_PRINT_L2("Saving unsigned tx data: " << oss.str());
7009  std::string ciphertext = encrypt_with_view_secret_key(oss.str());
7010 
7011  return std::string(UNSIGNED_TX_PREFIX) + ciphertext;
7012 }
7013 //----------------------------------------------------------------------------------------------------
7014 bool wallet2::load_unsigned_tx(const std::string &unsigned_filename, unsigned_tx_set &exported_txs) const
7015 {
7016  std::string s;
7017  boost::system::error_code errcode;
7018 
7019  if (!boost::filesystem::exists(unsigned_filename, errcode))
7020  {
7021  LOG_PRINT_L0("File " << unsigned_filename << " does not exist: " << errcode);
7022  return false;
7023  }
7024  if (!epee::file_io_utils::load_file_to_string(unsigned_filename.c_str(), s))
7025  {
7026  LOG_PRINT_L0("Failed to load from " << unsigned_filename);
7027  return false;
7028  }
7029 
7030  return parse_unsigned_tx_from_str(s, exported_txs);
7031 }
7032 //----------------------------------------------------------------------------------------------------
7033 bool wallet2::parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const
7034 {
7035  std::string s = unsigned_tx_st;
7036  const size_t magiclen = strlen(UNSIGNED_TX_PREFIX) - 1;
7037  if (strncmp(s.c_str(), UNSIGNED_TX_PREFIX, magiclen))
7038  {
7039  LOG_PRINT_L0("Bad magic from unsigned tx");
7040  return false;
7041  }
7042  s = s.substr(magiclen);
7043  const char version = s[0];
7044  s = s.substr(1);
7045  if (version == '\003')
7046  {
7047  try
7048  {
7049  std::istringstream iss(s);
7051  ar >> exported_txs;
7052  }
7053  catch (const std::exception &e)
7054  {
7055  LOG_PRINT_L0("Failed to parse data from unsigned tx: " << e.what());
7056  return false;
7057  }
7058  catch (...)
7059  {
7060  LOG_PRINT_L0("Failed to parse data from unsigned tx");
7061  return false;
7062  }
7063  }
7064  else if (version == '\004')
7065  {
7066  try
7067  {
7068  s = decrypt_with_view_secret_key(s);
7069  try
7070  {
7071  std::istringstream iss(s);
7073  ar >> exported_txs;
7074  }
7075  catch (const std::exception &e)
7076  {
7077  LOG_PRINT_L0("Failed to parse decrypted data from unsigned tx: " << e.what());
7078  return false;
7079  }
7080  }
7081  catch (const std::exception &e)
7082  {
7083  LOG_PRINT_L0("Failed to decrypt unsigned tx: " << e.what());
7084  return false;
7085  }
7086  catch(...)
7087  {
7088  LOG_PRINT_L0("Failed to parse decrypted data from unsigned tx");
7089  return false;
7090  }
7091  }
7092  else
7093  {
7094  LOG_PRINT_L0("Unsupported version in unsigned tx");
7095  return false;
7096  }
7097  LOG_PRINT_L1("Loaded tx unsigned data from binary: " << exported_txs.txes.size() << " transactions");
7098 
7099  return true;
7100 }
7101 //----------------------------------------------------------------------------------------------------
7102 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)
7103 {
7104  unsigned_tx_set exported_txs;
7105  if(!load_unsigned_tx(unsigned_filename, exported_txs))
7106  return false;
7107 
7108  if (accept_func && !accept_func(exported_txs))
7109  {
7110  LOG_PRINT_L1("Transactions rejected by callback");
7111  return false;
7112  }
7113  return sign_tx(exported_txs, signed_filename, txs, export_raw);
7114 }
7115 //----------------------------------------------------------------------------------------------------
7116 bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pending_tx> &txs, signed_tx_set &signed_txes)
7117 {
7118  import_outputs(exported_txs.transfers);
7119 
7120  // sign the transactions
7121  for (size_t n = 0; n < exported_txs.txes.size(); ++n)
7122  {
7123  tools::wallet2::tx_construction_data &sd = exported_txs.txes[n];
7124  THROW_WALLET_EXCEPTION_IF(sd.sources.empty(), error::wallet_internal_error, "Empty sources");
7125  LOG_PRINT_L1(" " << (n+1) << ": " << sd.sources.size() << " inputs, ring size " << sd.sources[0].outputs.size());
7126  signed_txes.ptx.push_back(pending_tx());
7127  tools::wallet2::pending_tx &ptx = signed_txes.ptx.back();
7128  rct::RCTConfig rct_config = sd.rct_config;
7129  crypto::secret_key tx_key;
7130  std::vector<crypto::secret_key> additional_tx_keys;
7131  rct::multisig_out msout;
7132 
7133  // NB no early blocks this time for v3 transactions, as nobody should be sending v3 transactions before their v2 (migration) tx have confirmed. V2 confirmations always take place at, or after, the fork block.
7134  // todo: 4.0.0.0 Migrate vs send regular tx.
7135  uint16_t hard_fork_version = use_fork_rules(CURRENT_HARDFORK_VERSION, 0) ? CURRENT_HARDFORK_VERSION : (CURRENT_HARDFORK_VERSION - 1);
7136  bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sd.sources, sd.splitted_dsts, sd.change_dts.addr, sd.extra, ptx.tx, sd.unlock_time, tx_key, additional_tx_keys, sd.use_rct, rct_config, m_multisig ? &msout : NULL, m_account_major_offset, this->m_nettype);
7138  // we don't test tx size, because we don't know the current limit, due to not having a blockchain,
7139  // and it's a bit pointless to fail there anyway, since it'd be a (good) guess only. We sign anyway,
7140  // and if we really go over limit, the daemon will reject when it gets submitted. Chances are it's
7141  // OK anyway since it was generated in the first place, and rerolling should be within a few bytes.
7142 
7143  // normally, the tx keys are saved in commit_tx, when the tx is actually sent to the daemon.
7144  // we can't do that here since the tx will be sent from the compromised wallet, which we don't want
7145  // to see that info, so we save it here
7146  if (store_tx_info())
7147  {
7148  const crypto::hash txid = get_transaction_hash(ptx.tx);
7149  m_tx_keys.insert(std::make_pair(txid, tx_key));
7150  m_additional_tx_keys.insert(std::make_pair(txid, additional_tx_keys));
7151  }
7152 
7153  std::string key_images;
7154 
7155  if(ptx.tx.version < 3) {
7156  bool all_are_txin_to_key = std::all_of(ptx.tx.vin.begin(), ptx.tx.vin.end(), [&](const txin_v &s_e) -> bool {
7157  CHECKED_GET_SPECIFIC_VARIANT(s_e, const txin_to_key, in, false);
7158  key_images += boost::to_string(in.k_image) + " ";
7159  return true;
7160  });
7161  THROW_WALLET_EXCEPTION_IF(!all_are_txin_to_key, error::unexpected_txin_type, ptx.tx);
7162  }else{
7163  bool all_are_txin_to_key_public = std::all_of(ptx.tx.vin.begin(), ptx.tx.vin.end(), [&](const txin_v &s_e) -> bool {
7164  CHECKED_GET_SPECIFIC_VARIANT(s_e, const txin_to_key_public, in, false);
7165  return true;
7166  });
7167  THROW_WALLET_EXCEPTION_IF(!all_are_txin_to_key_public, error::unexpected_txin_type, ptx.tx);
7168  }
7169  if(ptx.tx.version > 1) {
7170  bool all_are_txout_to_key_public = std::all_of(ptx.tx.vout.begin(), ptx.tx.vout.end(), [&](const tx_out &s_e) -> bool {
7171  CHECKED_GET_SPECIFIC_VARIANT(s_e.target, const txout_to_key_public, in, false);
7172  return true;
7173  });
7174  THROW_WALLET_EXCEPTION_IF(!all_are_txout_to_key_public, error::unexpected_txout_type, ptx.tx);
7175  }
7176  ptx.key_images = key_images;
7177  ptx.fee = 0;
7178  for (const auto &i: sd.sources) ptx.fee += i.amount;
7179  for (const auto &i: sd.splitted_dsts) ptx.fee -= i.amount;
7180  ptx.dust = 0;
7181  ptx.dust_added_to_fee = false;
7182  ptx.change_dts = sd.change_dts;
7184  ptx.tx_key = rct::rct2sk(rct::identity()); // don't send it back to the untrusted view wallet
7185  ptx.dests = sd.dests;
7186  ptx.construction_data = sd;
7187 
7188  txs.push_back(ptx);
7189 
7190  // add tx keys only to ptx
7191  txs.back().tx_key = tx_key;
7192  txs.back().additional_tx_keys = additional_tx_keys;
7193  }
7194 
7195  // add key image mapping for these txes
7196  const account_keys &keys = get_account().get_keys();
7197  hw::device &hwdev = m_account.get_device();
7198  for (size_t n = 0; n < exported_txs.txes.size(); ++n)
7199  {
7200  const cryptonote::transaction &tx = signed_txes.ptx[n].tx;
7201 
7202  crypto::key_derivation derivation;
7203  std::vector<crypto::key_derivation> additional_derivations;
7204 
7205  // compute public keys from out secret keys
7206  crypto::public_key tx_pub_key;
7207  crypto::secret_key_to_public_key(txs[n].tx_key, tx_pub_key);
7208  std::vector<crypto::public_key> additional_tx_pub_keys;
7209  for (const crypto::secret_key &skey: txs[n].additional_tx_keys)
7210  {
7211  additional_tx_pub_keys.resize(additional_tx_pub_keys.size() + 1);
7212  crypto::secret_key_to_public_key(skey, additional_tx_pub_keys.back());
7213  }
7214 
7215  // compute derivations
7217  if (!hwdev.generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation))
7218  {
7219  MWARNING("Failed to generate key derivation from tx pubkey in " << cryptonote::get_transaction_hash(tx) << ", skipping");
7220  static_assert(sizeof(derivation) == sizeof(rct::key), "Mismatched sizes of key_derivation and rct::key");
7221  memcpy(&derivation, rct::identity().bytes, sizeof(derivation));
7222  }
7223  for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
7224  {
7225  additional_derivations.push_back({});
7226  if (!hwdev.generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back()))
7227  {
7228  MWARNING("Failed to generate key derivation from additional tx pubkey in " << cryptonote::get_transaction_hash(tx) << ", skipping");
7229  memcpy(&additional_derivations.back(), rct::identity().bytes, sizeof(crypto::key_derivation));
7230  }
7231  }
7232 
7233  for (size_t i = 0; i < tx.vout.size(); ++i)
7234  {
7235  if (tx.vout[i].target.type() != typeid(cryptonote::txout_to_key))
7236  continue;
7237  const cryptonote::txout_to_key &out = boost::get<cryptonote::txout_to_key>(tx.vout[i].target);
7238  // if this output is back to this wallet, we can calculate its key image already
7239  if (!is_out_to_acc_precomp(m_subaddresses, out.key, derivation, additional_derivations, i, hwdev))
7240  continue;
7241  crypto::key_image ki;
7242  cryptonote::keypair in_ephemeral;
7243  if (generate_key_image_helper(keys, m_subaddresses, out.key, tx_pub_key, additional_tx_pub_keys, i, in_ephemeral, ki, hwdev))
7244  signed_txes.tx_key_images[out.key] = ki;
7245  else
7246  MERROR("Failed to calculate key image");
7247  }
7248  }
7249 
7250  // add key images
7251  signed_txes.key_images.resize(m_transfers.size());
7252  for (size_t i = 0; i < m_transfers.size(); ++i)
7253  {
7254  if (!m_transfers[i].m_key_image_known || m_transfers[i].m_key_image_partial)
7255  LOG_PRINT_L0("WARNING: key image not known in signing wallet at index " << i);
7256  signed_txes.key_images[i] = m_transfers[i].m_key_image;
7257  }
7258 
7259  return true;
7260 }
7261 //----------------------------------------------------------------------------------------------------
7262 bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_filename, std::vector<wallet2::pending_tx> &txs, bool export_raw)
7263 {
7264  // sign the transactions
7265  signed_tx_set signed_txes;
7266  std::string ciphertext = sign_tx_dump_to_str(exported_txs, txs, signed_txes);
7267  if (ciphertext.empty())
7268  {
7269  LOG_PRINT_L0("Failed to sign unsigned_tx_set");
7270  return false;
7271  }
7272 
7273  if (!epee::file_io_utils::save_string_to_file(signed_filename, ciphertext))
7274  {
7275  LOG_PRINT_L0("Failed to save file to " << signed_filename);
7276  return false;
7277  }
7278  // export signed raw tx without encryption
7279  if (export_raw)
7280  {
7281  for (size_t i = 0; i < signed_txes.ptx.size(); ++i)
7282  {
7283  std::string tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(signed_txes.ptx[i].tx));
7284  std::string raw_filename = signed_filename + "_raw" + (signed_txes.ptx.size() == 1 ? "" : ("_" + std::to_string(i)));
7285  if (!epee::file_io_utils::save_string_to_file(raw_filename, tx_as_hex))
7286  {
7287  LOG_PRINT_L0("Failed to save file to " << raw_filename);
7288  return false;
7289  }
7290  }
7291  }
7292  return true;
7293 }
7294 //----------------------------------------------------------------------------------------------------
7295 std::string wallet2::sign_tx_dump_to_str(unsigned_tx_set &exported_txs, std::vector<wallet2::pending_tx> &ptx, signed_tx_set &signed_txes)
7296 {
7297  // sign the transactions
7298  bool r = sign_tx(exported_txs, ptx, signed_txes);
7299  if (!r)
7300  {
7301  LOG_PRINT_L0("Failed to sign unsigned_tx_set");
7302  return std::string();
7303  }
7304 
7305  // save as binary
7306  std::ostringstream oss;
7308  try
7309  {
7310  ar << signed_txes;
7311  }
7312  catch(...)
7313  {
7314  return std::string();
7315  }
7316  LOG_PRINT_L3("Saving signed tx data (with encryption): " << oss.str());
7317  std::string ciphertext = encrypt_with_view_secret_key(oss.str());
7318  return std::string(SIGNED_TX_PREFIX) + ciphertext;
7319 }
7320 //----------------------------------------------------------------------------------------------------
7321 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)
7322 {
7323  std::string s;
7324  boost::system::error_code errcode;
7325  signed_tx_set signed_txs;
7326 
7327  if (!boost::filesystem::exists(signed_filename, errcode))
7328  {
7329  LOG_PRINT_L0("File " << signed_filename << " does not exist: " << errcode);
7330  return false;
7331  }
7332 
7333  if (!epee::file_io_utils::load_file_to_string(signed_filename.c_str(), s))
7334  {
7335  LOG_PRINT_L0("Failed to load from " << signed_filename);
7336  return false;
7337  }
7338 
7339  return parse_tx_from_str(s, ptx, accept_func);
7340 }
7341 //----------------------------------------------------------------------------------------------------
7342 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)
7343 {
7344  std::string s = signed_tx_st;
7345  boost::system::error_code errcode;
7346  signed_tx_set signed_txs;
7347 
7348  const size_t magiclen = strlen(SIGNED_TX_PREFIX) - 1;
7349  if (strncmp(s.c_str(), SIGNED_TX_PREFIX, magiclen))
7350  {
7351  LOG_PRINT_L0("Bad magic from signed transaction");
7352  return false;
7353  }
7354  s = s.substr(magiclen);
7355  const char version = s[0];
7356  s = s.substr(1);
7357  if (version == '\003')
7358  {
7359  try
7360  {
7361  std::istringstream iss(s);
7363  ar >> signed_txs;
7364  }
7365  catch (const std::exception &e)
7366  {
7367  LOG_PRINT_L0("Failed to parse data from signed transaction: " << e.what());
7368  return false;
7369  }
7370  catch(...)
7371  {
7372  LOG_PRINT_L0("Failed to parse data from signed transaction");
7373  return false;
7374  }
7375  }
7376  else if (version == '\004')
7377  {
7378  try
7379  {
7380  s = decrypt_with_view_secret_key(s);
7381  try
7382  {
7383  std::istringstream iss(s);
7385  ar >> signed_txs;
7386  }
7387  catch (const std::exception &e)
7388  {
7389  LOG_PRINT_L0("Failed to parse decrypted data from signed transaction: " << e.what());
7390  return false;
7391  }
7392  }
7393  catch (const std::exception &e)
7394  {
7395  LOG_PRINT_L0("Failed to decrypt signed transaction: " << e.what());
7396  return false;
7397  }
7398  catch(...)
7399  {
7400  LOG_PRINT_L0("Failed to decrypt signed transaction");
7401  return false;
7402  }
7403  }
7404  else
7405  {
7406  LOG_PRINT_L0("Unsupported version in signed transaction");
7407  return false;
7408  }
7409  LOG_PRINT_L0("Loaded signed tx data from binary: " << signed_txs.ptx.size() << " transactions");
7410  for (auto &c_ptx: signed_txs.ptx) LOG_PRINT_L0(cryptonote::obj_to_json_str(c_ptx.tx));
7411 
7412  if (accept_func && !accept_func(signed_txs))
7413  {
7414  LOG_PRINT_L1("Transactions rejected by callback");
7415  return false;
7416  }
7417 
7418  // import key images
7419  bool r = import_key_images(signed_txs.key_images);
7420  if (!r) return false;
7421 
7422  // remember key images for this tx, for when we get those txes from the blockchain
7423  for (const auto &e: signed_txs.tx_key_images)
7424  m_cold_key_images.insert(e);
7425 
7426  ptx = signed_txs.ptx;
7427 
7428  return true;
7429 }
7430 //----------------------------------------------------------------------------------------------------
7431 std::string wallet2::save_multisig_tx(multisig_tx_set txs)
7432 {
7433  LOG_PRINT_L0("saving " << txs.m_ptx.size() << " multisig transactions");
7434 
7435  // txes generated, get rid of used k values
7436  for (size_t n = 0; n < txs.m_ptx.size(); ++n)
7437  for (size_t idx: txs.m_ptx[n].construction_data.selected_transfers)
7438  m_transfers[idx].m_multisig_k.clear();
7439 
7440  // zero out some data we don't want to share
7441  for (auto &ptx: txs.m_ptx)
7442  {
7443  for (auto &e: ptx.construction_data.sources)
7444  e.multisig_kLRki.k = rct::zero();
7445  }
7446 
7447  for (auto &ptx: txs.m_ptx)
7448  {
7449  // Get decrypted payment id from pending_tx
7450  ptx.construction_data = get_construction_data_with_decrypted_short_payment_id(ptx, m_account.get_device());
7451  }
7452 
7453  // save as binary
7454  std::ostringstream oss;
7456  try
7457  {
7458  ar << txs;
7459  }
7460  catch (...)
7461  {
7462  return std::string();
7463  }
7464  LOG_PRINT_L2("Saving multisig unsigned tx data: " << oss.str());
7465  std::string ciphertext = encrypt_with_view_secret_key(oss.str());
7466  return std::string(MULTISIG_UNSIGNED_TX_PREFIX) + ciphertext;
7467 }
7468 //----------------------------------------------------------------------------------------------------
7469 bool wallet2::save_multisig_tx(const multisig_tx_set &txs, const std::string &filename)
7470 {
7471  std::string ciphertext = save_multisig_tx(txs);
7472  if (ciphertext.empty())
7473  return false;
7474  return epee::file_io_utils::save_string_to_file(filename, ciphertext);
7475 }
7476 //----------------------------------------------------------------------------------------------------
7477 wallet2::multisig_tx_set wallet2::make_multisig_tx_set(const std::vector<pending_tx>& ptx_vector) const
7478 {
7479  multisig_tx_set txs;
7480  txs.m_ptx = ptx_vector;
7481 
7482  for (const auto &msk: get_account().get_multisig_keys())
7483  {
7484  crypto::public_key pkey = get_multisig_signing_public_key(msk);
7485  for (auto &ptx: txs.m_ptx) for (auto &sig: ptx.multisig_sigs) sig.signing_keys.insert(pkey);
7486  }
7487 
7488  txs.m_signers.insert(get_multisig_signer_public_key());
7489  return txs;
7490 }
7491 
7492 std::string wallet2::save_multisig_tx(const std::vector<pending_tx>& ptx_vector)
7493 {
7494  return save_multisig_tx(make_multisig_tx_set(ptx_vector));
7495 }
7496 //----------------------------------------------------------------------------------------------------
7497 bool wallet2::save_multisig_tx(const std::vector<pending_tx>& ptx_vector, const std::string &filename)
7498 {
7499  std::string ciphertext = save_multisig_tx(ptx_vector);
7500  if (ciphertext.empty())
7501  return false;
7502  return epee::file_io_utils::save_string_to_file(filename, ciphertext);
7503 }
7504 //----------------------------------------------------------------------------------------------------
7505 bool wallet2::parse_multisig_tx_from_str(std::string multisig_tx_st, multisig_tx_set &exported_txs) const
7506 {
7507  const size_t magiclen = strlen(MULTISIG_UNSIGNED_TX_PREFIX);
7508  if (strncmp(multisig_tx_st.c_str(), MULTISIG_UNSIGNED_TX_PREFIX, magiclen))
7509  {
7510  LOG_PRINT_L0("Bad magic from multisig tx data");
7511  return false;
7512  }
7513  try
7514  {
7515  multisig_tx_st = decrypt_with_view_secret_key(std::string(multisig_tx_st, magiclen));
7516  }
7517  catch (const std::exception &e)
7518  {
7519  LOG_PRINT_L0("Failed to decrypt multisig tx data: " << e.what());
7520  return false;
7521  }
7522  try
7523  {
7524  std::istringstream iss(multisig_tx_st);
7526  ar >> exported_txs;
7527  }
7528  catch (...)
7529  {
7530  LOG_PRINT_L0("Failed to parse multisig tx data");
7531  return false;
7532  }
7533 
7534  // sanity checks
7535  for (const auto &ptx: exported_txs.m_ptx)
7536  {
7537  CHECK_AND_ASSERT_MES(ptx.selected_transfers.size() == ptx.tx.vin.size(), false, "Mismatched selected_transfers/vin sizes");
7538  for (size_t idx: ptx.selected_transfers)
7539  CHECK_AND_ASSERT_MES(idx < m_transfers.size(), false, "Transfer index out of range");
7540  CHECK_AND_ASSERT_MES(ptx.construction_data.selected_transfers.size() == ptx.tx.vin.size(), false, "Mismatched cd selected_transfers/vin sizes");
7541  for (size_t idx: ptx.construction_data.selected_transfers)
7542  CHECK_AND_ASSERT_MES(idx < m_transfers.size(), false, "Transfer index out of range");
7543  CHECK_AND_ASSERT_MES(ptx.construction_data.sources.size() == ptx.tx.vin.size(), false, "Mismatched sources/vin sizes");
7544  }
7545 
7546  return true;
7547 }
7548 //----------------------------------------------------------------------------------------------------
7549 bool wallet2::load_multisig_tx(cryptonote::blobdata s, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func)
7550 {
7551  if(!parse_multisig_tx_from_str(s, exported_txs))
7552  {
7553  LOG_PRINT_L0("Failed to parse multisig transaction from string");
7554  return false;
7555  }
7556 
7557  LOG_PRINT_L1("Loaded multisig tx unsigned data from binary: " << exported_txs.m_ptx.size() << " transactions");
7558  for (auto &ptx: exported_txs.m_ptx) LOG_PRINT_L0(cryptonote::obj_to_json_str(ptx.tx));
7559 
7560  if (accept_func && !accept_func(exported_txs))
7561  {
7562  LOG_PRINT_L1("Transactions rejected by callback");
7563  return false;
7564  }
7565 
7566  const bool is_signed = exported_txs.m_signers.size() >= m_multisig_threshold;
7567  if (is_signed)
7568  {
7569  for (const auto &ptx: exported_txs.m_ptx)
7570  {
7571  const crypto::hash txid = get_transaction_hash(ptx.tx);
7572  if (store_tx_info())
7573  {
7574  m_tx_keys.insert(std::make_pair(txid, ptx.tx_key));
7575  m_additional_tx_keys.insert(std::make_pair(txid, ptx.additional_tx_keys));
7576  }
7577  }
7578  }
7579 
7580  return true;
7581 }
7582 //----------------------------------------------------------------------------------------------------
7583 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)
7584 {
7585  std::string s;
7586  boost::system::error_code errcode;
7587 
7588  if (!boost::filesystem::exists(filename, errcode))
7589  {
7590  LOG_PRINT_L0("File " << filename << " does not exist: " << errcode);
7591  return false;
7592  }
7593  if (!epee::file_io_utils::load_file_to_string(filename.c_str(), s))
7594  {
7595  LOG_PRINT_L0("Failed to load from " << filename);
7596  return false;
7597  }
7598 
7599  if (!load_multisig_tx(s, exported_txs, accept_func))
7600  {
7601  LOG_PRINT_L0("Failed to parse multisig tx data from " << filename);
7602  return false;
7603  }
7604  return true;
7605 }
7606 //----------------------------------------------------------------------------------------------------
7607 bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto::hash> &txids)
7608 {
7609  THROW_WALLET_EXCEPTION_IF(exported_txs.m_ptx.empty(), error::wallet_internal_error, "No tx found");
7610 
7611  const crypto::public_key local_signer = get_multisig_signer_public_key();
7612 
7613  THROW_WALLET_EXCEPTION_IF(exported_txs.m_signers.find(local_signer) != exported_txs.m_signers.end(),
7614  error::wallet_internal_error, "Transaction already signed by this private key");
7615  THROW_WALLET_EXCEPTION_IF(exported_txs.m_signers.size() > m_multisig_threshold,
7616  error::wallet_internal_error, "Transaction was signed by too many signers");
7617  THROW_WALLET_EXCEPTION_IF(exported_txs.m_signers.size() == m_multisig_threshold,
7618  error::wallet_internal_error, "Transaction is already fully signed");
7619 
7620  txids.clear();
7621 
7622  // sign the transactions
7623  for (size_t n = 0; n < exported_txs.m_ptx.size(); ++n)
7624  {
7625  tools::wallet2::pending_tx &ptx = exported_txs.m_ptx[n];
7626  THROW_WALLET_EXCEPTION_IF(ptx.multisig_sigs.empty(), error::wallet_internal_error, "No signatures found in multisig tx");
7628  LOG_PRINT_L1(" " << (n+1) << ": " << sd.sources.size() << " inputs, mixin " << (sd.sources[0].outputs.size()-1) <<
7629  ", signed by " << exported_txs.m_signers.size() << "/" << m_multisig_threshold);
7631  rct::multisig_out msout = ptx.multisig_sigs.front().msout;
7632  auto sources = sd.sources;
7633  rct::RCTConfig rct_config = sd.rct_config;
7634  bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources, sd.splitted_dsts, ptx.change_dts.addr, sd.extra, tx, sd.unlock_time, ptx.tx_key, ptx.additional_tx_keys, sd.use_rct, rct_config, &msout, false, 0 /*===default value*/, this->m_nettype);
7635  THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype);
7636 
7638  error::wallet_internal_error, "Transaction prefix does not match data");
7639 
7640  // Tests passed, sign
7641  std::vector<unsigned int> indices;
7642  for (const auto &source: sources)
7643  indices.push_back(source.real_output);
7644 
7645  for (auto &sig: ptx.multisig_sigs)
7646  {
7647  if (sig.ignore.find(local_signer) == sig.ignore.end())
7648  {
7649  ptx.tx.rct_signatures = sig.sigs;
7650 
7651  rct::keyV k;
7652  for (size_t idx: sd.selected_transfers)
7653  k.push_back(get_multisig_k(idx, sig.used_L));
7654 
7655  rct::key skey = rct::zero();
7656  for (const auto &msk: get_account().get_multisig_keys())
7657  {
7658  crypto::public_key pmsk = get_multisig_signing_public_key(msk);
7659 
7660  if (sig.signing_keys.find(pmsk) == sig.signing_keys.end())
7661  {
7662  sc_add(skey.bytes, skey.bytes, rct::sk2rct(msk).bytes);
7663  sig.signing_keys.insert(pmsk);
7664  }
7665  }
7666  THROW_WALLET_EXCEPTION_IF(!rct::signMultisig(ptx.tx.rct_signatures, indices, k, sig.msout, skey),
7667  error::wallet_internal_error, "Failed signing, transaction likely malformed");
7668 
7669  sig.sigs = ptx.tx.rct_signatures;
7670  }
7671  }
7672 
7673  const bool is_last = exported_txs.m_signers.size() + 1 >= m_multisig_threshold;
7674  if (is_last)
7675  {
7676  // when the last signature on a multisig tx is made, we select the right
7677  // signature to plug into the final tx
7678  bool found = false;
7679  for (const auto &sig: ptx.multisig_sigs)
7680  {
7681  if (sig.ignore.find(local_signer) == sig.ignore.end() && !keys_intersect(sig.ignore, exported_txs.m_signers))
7682  {
7683  THROW_WALLET_EXCEPTION_IF(found, error::wallet_internal_error, "More than one transaction is final");
7684  ptx.tx.rct_signatures = sig.sigs;
7685  found = true;
7686  }
7687  }
7689  "Final signed transaction not found: this transaction was likely made without our export data, so we cannot sign it");
7690  const crypto::hash txid = get_transaction_hash(ptx.tx);
7691  if (store_tx_info())
7692  {
7693  m_tx_keys.insert(std::make_pair(txid, ptx.tx_key));
7694  m_additional_tx_keys.insert(std::make_pair(txid, ptx.additional_tx_keys));
7695  }
7696  txids.push_back(txid);
7697  }
7698  }
7699 
7700  // txes generated, get rid of used k values
7701  for (size_t n = 0; n < exported_txs.m_ptx.size(); ++n)
7702  for (size_t idx: exported_txs.m_ptx[n].construction_data.selected_transfers)
7703  m_transfers[idx].m_multisig_k.clear();
7704 
7705  exported_txs.m_signers.insert(get_multisig_signer_public_key());
7706 
7707  return true;
7708 }
7709 //----------------------------------------------------------------------------------------------------
7710 bool wallet2::sign_multisig_tx_to_file(multisig_tx_set &exported_txs, const std::string &filename, std::vector<crypto::hash> &txids)
7711 {
7712  bool r = sign_multisig_tx(exported_txs, txids);
7713  if (!r)
7714  return false;
7715  return save_multisig_tx(exported_txs, filename);
7716 }
7717 //----------------------------------------------------------------------------------------------------
7718 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)
7719 {
7720  multisig_tx_set exported_txs;
7721  if(!load_multisig_tx_from_file(filename, exported_txs))
7722  return false;
7723 
7724  if (accept_func && !accept_func(exported_txs))
7725  {
7726  LOG_PRINT_L1("Transactions rejected by callback");
7727  return false;
7728  }
7729  return sign_multisig_tx_to_file(exported_txs, filename, txids);
7730 }
7731 //----------------------------------------------------------------------------------------------------
7732 uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm) const
7733 {
7734  static const struct
7735  {
7736  size_t count;
7737  uint64_t multipliers[4];
7738  }
7739  multipliers[] =
7740  {
7741  { 3, {1, 2, 3} },
7742  { 3, {1, 20, 166} },
7743  { 4, {1, 4, 20, 166} },
7744  { 4, {1, 2, 4, 8} },
7745  };
7746 
7747  if (fee_algorithm == -1)
7748  fee_algorithm = get_fee_algorithm();
7749 
7750  // 0 -> default (here, x1 till fee algorithm 2, x4 from it)
7751  if (priority == 0)
7752  priority = m_default_priority;
7753  if (priority == 0)
7754  {
7755  if (fee_algorithm == 2)
7756  priority = 2;
7757  else
7758  priority = 1;
7759  }
7760 
7761  THROW_WALLET_EXCEPTION_IF(fee_algorithm < 0 || fee_algorithm > 4, error::invalid_priority);
7762 
7763  // 1 to 3/4 are allowed as priorities
7764  const uint32_t max_priority = multipliers[fee_algorithm].count;
7765  if (priority >= 1 && priority <= max_priority)
7766  {
7767  return multipliers[fee_algorithm].multipliers[priority-1];
7768  }
7769 
7771  return 1;
7772 }
7773 //----------------------------------------------------------------------------------------------------
7774 uint64_t wallet2::get_dynamic_base_fee_estimate() const
7775 {
7776  uint64_t fee;
7777  boost::optional<std::string> result = m_node_rpc_proxy.get_dynamic_base_fee_estimate(FEE_ESTIMATE_GRACE_BLOCKS, fee);
7778  if (!result)
7779  return fee;
7780  const uint64_t base_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE) ? FEE_PER_BYTE : use_fork_rules(HF_VERSION_ZERO_FEE) ? FEE_PER_KB_V11 : FEE_PER_KB_V6;
7781  LOG_PRINT_L1("Failed to query base fee, using " << print_etn(base_fee));
7782  return base_fee;
7783 }
7784 //----------------------------------------------------------------------------------------------------
7785 uint64_t wallet2::get_base_fee() const
7786 {
7787  if(m_light_wallet)
7788  {
7789  if (use_fork_rules(HF_VERSION_PER_BYTE_FEE))
7790  return m_light_wallet_per_kb_fee / 1024;
7791  else
7792  return m_light_wallet_per_kb_fee;
7793  }
7794  bool use_dyn_fee = use_fork_rules(HF_VERSION_DYNAMIC_FEE, -720 * 1);
7795  if (!use_dyn_fee){
7796  if (use_fork_rules(HF_VERSION_ZERO_FEE)){
7797  return FEE_PER_KB_V11;
7798  } else{
7799  return FEE_PER_KB_V6;
7800  }
7801  }
7802 
7803 
7804  return get_dynamic_base_fee_estimate(); //this never gets hit for any version before 100
7805 }
7806 //----------------------------------------------------------------------------------------------------
7807 uint64_t wallet2::get_fee_quantization_mask() const
7808 {
7809  if(m_light_wallet)
7810  {
7811  return 1; // TODO
7812  }
7813  bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
7814  if (!use_per_byte_fee)
7815  return 1;
7816 
7817  uint64_t fee_quantization_mask;
7818  boost::optional<std::string> result = m_node_rpc_proxy.get_fee_quantization_mask(fee_quantization_mask);
7819  if (result)
7820  return 1;
7821  return fee_quantization_mask;
7822 }
7823 //----------------------------------------------------------------------------------------------------
7824 int wallet2::get_fee_algorithm() const
7825 {
7826  // changes at v3, v5, v8
7827  if (use_fork_rules(6, 0))
7828  return 3;
7829  if (use_fork_rules(5, 0))
7830  return 2;
7831  if (use_fork_rules(3, -720 * 14))
7832  return 1;
7833  return 0;
7834 }
7835 //------------------------------------------------------------------------------------------------------------------------------
7836 uint64_t wallet2::get_min_ring_size() const
7837 {
7838  if (use_fork_rules(HF_VERSION_MIN_MIXIN_10, 10))
7839  return 11;
7840  if (use_fork_rules(HF_VERSION_MIN_MIXIN_6, 10))
7841  return 7;
7842  if (use_fork_rules(HF_VERSION_MIN_MIXIN_4, 10))
7843  return 5;
7844  if (use_fork_rules(HF_VERSION_ENFORCE_0_DECOY_TXS, 10))
7845  return 1;
7846  if (use_fork_rules(HF_VERSION_MIN_MIXIN_2, 10))
7847  return 3;
7848  return 0;
7849 }
7850 //------------------------------------------------------------------------------------------------------------------------------
7851 uint64_t wallet2::get_max_ring_size() const
7852 {
7853  if (use_fork_rules(HF_VERSION_MAX_RING_11, 10))
7854  return 11;
7855  if (use_fork_rules(HF_VERSION_ENFORCE_0_DECOY_TXS, 10))
7856  return 1;
7857  return 0;
7858 }
7859 //------------------------------------------------------------------------------------------------------------------------------
7860 uint64_t wallet2::adjust_mixin(uint64_t mixin) const
7861 {
7862  const uint64_t min_ring_size = get_min_ring_size();
7863  if (mixin + 1 < min_ring_size)
7864  {
7865  MWARNING("Requested ring size " << (mixin + 1) << " too low, using " << min_ring_size);
7866  mixin = min_ring_size-1;
7867  }
7868  const uint64_t max_ring_size = get_max_ring_size();
7869  if (max_ring_size && mixin + 1 > max_ring_size)
7870  {
7871  MWARNING("Requested ring size " << (mixin + 1) << " too high, using " << max_ring_size);
7872  mixin = max_ring_size-1;
7873  }
7874  return mixin;
7875 }
7876 //----------------------------------------------------------------------------------------------------
7877 uint32_t wallet2::adjust_priority(uint32_t priority)
7878 {
7879  // just return 1 for normal priority for aurelius instead of being concerned with backlog and adjusting priority because fees are 0 for everyone
7880  return 1;
7881 
7882  if (priority == 0 && m_default_priority == 0 && auto_low_priority())
7883  {
7884  try
7885  {
7886  // check if there's a backlog in the tx pool
7887  const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
7888  const uint64_t base_fee = get_base_fee();
7889  const uint64_t fee_multiplier = get_fee_multiplier(1);
7890  const double fee_level = fee_multiplier * base_fee * (use_per_byte_fee ? 1 : (12/(double)13 / (double)1024));
7891  const std::vector<std::pair<uint64_t, uint64_t>> blocks = estimate_backlog({std::make_pair(fee_level, fee_level)});
7892  if (blocks.size() != 1)
7893  {
7894  MERROR("Bad estimated backlog array size");
7895  return priority;
7896  }
7897  else if (blocks[0].first > 0)
7898  {
7899  MINFO("We don't use the low priority because there's a backlog in the tx pool.");
7900  return priority;
7901  }
7902 
7903  // get the current full reward zone
7904  uint64_t block_weight_limit = 0;
7905  const auto result = m_node_rpc_proxy.get_block_weight_limit(block_weight_limit);
7906  throw_on_rpc_response_error(result, "get_info");
7907  const uint64_t full_reward_zone = block_weight_limit / 2;
7908 
7909  // get the last N block headers and sum the block sizes
7910  const size_t N = 10;
7911  if (m_blockchain.size() < N)
7912  {
7913  MERROR("The blockchain is too short");
7914  return priority;
7915  }
7918  m_daemon_rpc_mutex.lock();
7919  getbh_req.start_height = m_blockchain.size() - N;
7920  getbh_req.end_height = m_blockchain.size() - 1;
7921  bool r = invoke_http_json_rpc("/json_rpc", "getblockheadersrange", getbh_req, getbh_res, rpc_timeout);
7922  m_daemon_rpc_mutex.unlock();
7923  THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getblockheadersrange");
7924  THROW_WALLET_EXCEPTION_IF(getbh_res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getblockheadersrange");
7925  THROW_WALLET_EXCEPTION_IF(getbh_res.status != CORE_RPC_STATUS_OK, error::get_blocks_error, get_rpc_status(getbh_res.status));
7926  if (getbh_res.headers.size() != N)
7927  {
7928  MERROR("Bad blockheaders size");
7929  return priority;
7930  }
7931  size_t block_weight_sum = 0;
7932  for (const cryptonote::block_header_response &i : getbh_res.headers)
7933  {
7934  block_weight_sum += i.block_weight;
7935  }
7936 
7937  // estimate how 'full' the last N blocks are
7938  const size_t P = 100 * block_weight_sum / (N * full_reward_zone);
7939  MINFO((boost::format("The last %d blocks fill roughly %d%% of the full reward zone.") % N % P).str());
7940  if (P > 80)
7941  {
7942  MINFO("We don't use the low priority because recent blocks are quite full.");
7943  return priority;
7944  }
7945  MINFO("We'll use the low priority because probably it's safe to do so.");
7946  return 1;
7947  }
7948  catch (const std::exception &e)
7949  {
7950  MERROR(e.what());
7951  }
7952  }
7953  return priority;
7954 }
7955 //----------------------------------------------------------------------------------------------------
7956 bool wallet2::set_ring_database(const std::string &filename)
7957 {
7958  m_ring_database = filename;
7959  MINFO("ringdb path set to " << filename);
7960  m_ringdb.reset();
7961  if (!m_ring_database.empty())
7962  {
7963  try
7964  {
7966  generate_genesis(b);
7967  m_ringdb.reset(new tools::ringdb(m_ring_database, epee::string_tools::pod_to_hex(get_block_hash(b))));
7968  }
7969  catch (const std::exception &e)
7970  {
7971  MERROR("Failed to initialize ringdb: " << e.what());
7972  m_ring_database = "";
7973  return false;
7974  }
7975  }
7976  return true;
7977 }
7978 
7979 crypto::chacha_key wallet2::get_ringdb_key()
7980 {
7981  if (!m_ringdb_key)
7982  {
7983  MINFO("caching ringdb key");
7984  crypto::chacha_key key;
7985  generate_chacha_key_from_secret_keys(key);
7986  m_ringdb_key = key;
7987  }
7988  return *m_ringdb_key;
7989 }
7990 
7991 void wallet2::register_devices(){
7993 }
7994 
7995 hw::device& wallet2::lookup_device(const std::string & device_descriptor){
7996  if (!m_devices_registered){
7997  m_devices_registered = true;
7998  register_devices();
7999  }
8000 
8001  return hw::get_device(device_descriptor);
8002 }
8003 
8004 bool wallet2::add_rings(const crypto::chacha_key &key, const cryptonote::transaction_prefix &tx)
8005 {
8006  if (!m_ringdb)
8007  return false;
8008  try { return m_ringdb->add_rings(key, tx); }
8009  catch (const std::exception &e) { return false; }
8010 }
8011 
8012 bool wallet2::add_rings(const cryptonote::transaction_prefix &tx)
8013 {
8014  try { return add_rings(get_ringdb_key(), tx); }
8015  catch (const std::exception &e) { return false; }
8016 }
8017 
8018 bool wallet2::remove_rings(const cryptonote::transaction_prefix &tx)
8019 {
8020  if (!m_ringdb)
8021  return false;
8022  try { return m_ringdb->remove_rings(get_ringdb_key(), tx); }
8023  catch (const std::exception &e) { return false; }
8024 }
8025 
8026 bool wallet2::get_ring(const crypto::chacha_key &key, const crypto::key_image &key_image, std::vector<uint64_t> &outs)
8027 {
8028  if (!m_ringdb)
8029  return false;
8030  try { return m_ringdb->get_ring(key, key_image, outs); }
8031  catch (const std::exception &e) { return false; }
8032 }
8033 
8034 bool wallet2::get_rings(const crypto::hash &txid, std::vector<std::pair<crypto::key_image, std::vector<uint64_t>>> &outs)
8035 {
8036  for (auto i: m_confirmed_txs)
8037  {
8038  if (txid == i.first)
8039  {
8040  for (const auto &x: i.second.m_rings)
8041  outs.push_back({x.first, cryptonote::relative_output_offsets_to_absolute(x.second)});
8042  return true;
8043  }
8044  }
8045  for (auto i: m_unconfirmed_txs)
8046  {
8047  if (txid == i.first)
8048  {
8049  for (const auto &x: i.second.m_rings)
8050  outs.push_back({x.first, cryptonote::relative_output_offsets_to_absolute(x.second)});
8051  return true;
8052  }
8053  }
8054  return false;
8055 }
8056 
8057 bool wallet2::get_ring(const crypto::key_image &key_image, std::vector<uint64_t> &outs)
8058 {
8059  try { return get_ring(get_ringdb_key(), key_image, outs); }
8060  catch (const std::exception &e) { return false; }
8061 }
8062 
8063 bool wallet2::set_ring(const crypto::key_image &key_image, const std::vector<uint64_t> &outs, bool relative)
8064 {
8065  if (!m_ringdb)
8066  return false;
8067 
8068  try { return m_ringdb->set_ring(get_ringdb_key(), key_image, outs, relative); }
8069  catch (const std::exception &e) { return false; }
8070 }
8071 
8072 bool wallet2::unset_ring(const std::vector<crypto::key_image> &key_images)
8073 {
8074  if (!m_ringdb)
8075  return false;
8076 
8077  try { return m_ringdb->remove_rings(get_ringdb_key(), key_images); }
8078  catch (const std::exception &e) { return false; }
8079 }
8080 
8081 bool wallet2::unset_ring(const crypto::hash &txid)
8082 {
8083  if (!m_ringdb)
8084  return false;
8085 
8088  req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
8089  req.decode_as_json = false;
8090  req.prune = true;
8091  m_daemon_rpc_mutex.lock();
8092  bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout);
8093  m_daemon_rpc_mutex.unlock();
8094  THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to get transaction from daemon");
8095  if (res.txs.empty())
8096  return false;
8097  THROW_WALLET_EXCEPTION_IF(res.txs.size(), error::wallet_internal_error, "Failed to get transaction from daemon");
8098 
8100  crypto::hash tx_hash;
8101  if (!get_pruned_tx(res.txs.front(), tx, tx_hash))
8102  return false;
8103  THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "Failed to get the right transaction from daemon");
8104 
8105  try { return m_ringdb->remove_rings(get_ringdb_key(), tx); }
8106  catch (const std::exception &e) { return false; }
8107 }
8108 
8109 bool wallet2::find_and_save_rings(bool force)
8110 {
8111  if (!force && m_ring_history_saved)
8112  return true;
8113  if (!m_ringdb)
8114  return false;
8115 
8118 
8119  MDEBUG("Finding and saving rings...");
8120 
8121  // get payments we made
8122  std::vector<crypto::hash> txs_hashes;
8123  std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>> payments;
8124  get_payments_out(payments, 0, std::numeric_limits<uint64_t>::max(), boost::none, std::set<uint32_t>());
8125  for (const std::pair<crypto::hash,wallet2::confirmed_transfer_details> &entry: payments)
8126  {
8127  const crypto::hash &txid = entry.first;
8128  txs_hashes.push_back(txid);
8129  }
8130 
8131  MDEBUG("Found " << std::to_string(txs_hashes.size()) << " transactions");
8132 
8133  // get those transactions from the daemon
8134  auto it = txs_hashes.begin();
8135  static const size_t SLICE_SIZE = 200;
8136  for (size_t slice = 0; slice < txs_hashes.size(); slice += SLICE_SIZE)
8137  {
8138  req.decode_as_json = false;
8139  req.prune = true;
8140  req.txs_hashes.clear();
8141  size_t ntxes = slice + SLICE_SIZE > txs_hashes.size() ? txs_hashes.size() - slice : SLICE_SIZE;
8142  for (size_t s = slice; s < slice + ntxes; ++s)
8143  req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txs_hashes[s]));
8144  bool r;
8145  {
8146  const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
8147  r = invoke_http_json("/gettransactions", req, res, rpc_timeout);
8148  }
8152  THROW_WALLET_EXCEPTION_IF(res.txs.size() != req.txs_hashes.size(), error::wallet_internal_error,
8153  "daemon returned wrong response for gettransactions, wrong txs count = " +
8154  std::to_string(res.txs.size()) + ", expected " + std::to_string(req.txs_hashes.size()));
8155 
8156  MDEBUG("Scanning " << res.txs.size() << " transactions");
8157  THROW_WALLET_EXCEPTION_IF(slice + res.txs.size() > txs_hashes.size(), error::wallet_internal_error, "Unexpected tx array size");
8158  for (size_t i = 0; i < res.txs.size(); ++i, ++it)
8159  {
8160  const auto &tx_info = res.txs[i];
8162  crypto::hash tx_hash;
8163  THROW_WALLET_EXCEPTION_IF(!get_pruned_tx(tx_info, tx, tx_hash), error::wallet_internal_error,
8164  "Failed to get transaction from daemon");
8165  THROW_WALLET_EXCEPTION_IF(!(tx_hash == *it), error::wallet_internal_error, "Wrong txid received");
8166  THROW_WALLET_EXCEPTION_IF(!add_rings(get_ringdb_key(), tx), error::wallet_internal_error, "Failed to save ring");
8167  }
8168  }
8169 
8170  MINFO("Found and saved rings for " << txs_hashes.size() << " transactions");
8171  m_ring_history_saved = true;
8172  return true;
8173 }
8174 
8175 bool wallet2::blackball_output(const std::pair<uint64_t, uint64_t> &output)
8176 {
8177  if (!m_ringdb)
8178  return false;
8179  try { return m_ringdb->blackball(output); }
8180  catch (const std::exception &e) { return false; }
8181 }
8182 
8183 bool wallet2::set_blackballed_outputs(const std::vector<std::pair<uint64_t, uint64_t>> &outputs, bool add)
8184 {
8185  if (!m_ringdb)
8186  return false;
8187  try
8188  {
8189  bool ret = true;
8190  if (!add)
8191  ret &= m_ringdb->clear_blackballs();
8192  ret &= m_ringdb->blackball(outputs);
8193  return ret;
8194  }
8195  catch (const std::exception &e) { return false; }
8196 }
8197 
8198 bool wallet2::unblackball_output(const std::pair<uint64_t, uint64_t> &output)
8199 {
8200  if (!m_ringdb)
8201  return false;
8202  try { return m_ringdb->unblackball(output); }
8203  catch (const std::exception &e) { return false; }
8204 }
8205 
8206 bool wallet2::is_output_blackballed(const std::pair<uint64_t, uint64_t> &output) const
8207 {
8208  if (!m_ringdb)
8209  return false;
8210  try { return m_ringdb->blackballed(output); }
8211  catch (const std::exception &e) { return false; }
8212 }
8213 
8214 bool wallet2::lock_keys_file()
8215 {
8216  if (m_keys_file_locker)
8217  {
8218  MDEBUG(m_keys_file << " is already locked.");
8219  return false;
8220  }
8221  m_keys_file_locker.reset(new tools::file_locker(m_keys_file));
8222  return true;
8223 }
8224 
8225 bool wallet2::unlock_keys_file()
8226 {
8227  if (!m_keys_file_locker)
8228  {
8229  MDEBUG(m_keys_file << " is already unlocked.");
8230  return false;
8231  }
8232  m_keys_file_locker.reset();
8233  return true;
8234 }
8235 
8236 bool wallet2::is_keys_file_locked() const
8237 {
8238  return m_keys_file_locker->locked();
8239 }
8240 
8241 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
8242 {
8243  if (!unlocked) // don't add locked outs
8244  return false;
8245  if (global_index == real_index) // don't re-add real one
8246  return false;
8247  auto item = std::make_tuple(global_index, output_public_key, mask);
8248  CHECK_AND_ASSERT_MES(!outs.empty(), false, "internal error: outs is empty");
8249  if (std::find(outs.back().begin(), outs.back().end(), item) != outs.back().end()) // don't add duplicates
8250  return false;
8251  // check the keys are valid
8252  if (!rct::isInMainSubgroup(rct::pk2rct(output_public_key)))
8253  {
8254  MWARNING("Key " << output_public_key << " at index " << global_index << " is not in the main subgroup");
8255  return false;
8256  }
8257  if (!rct::isInMainSubgroup(mask))
8258  {
8259  MWARNING("Commitment " << mask << " at index " << global_index << " is not in the main subgroup");
8260  return false;
8261  }
8262 // if (is_output_blackballed(output_public_key)) // don't add blackballed outputs
8263 // return false;
8264  outs.back().push_back(item);
8265  return true;
8266 }
8267 
8268 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) {
8269 
8270  MDEBUG("LIGHTWALLET - Getting random outs");
8271 
8274 
8275  size_t light_wallet_requested_outputs_count = (size_t)((fake_outputs_count + 1) * 1.5 + 1);
8276 
8277  // Amounts to ask for
8278  // MyMonero api handle amounts and fees as strings
8279  for(size_t idx: selected_transfers) {
8280  const uint64_t ask_amount = m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount();
8281  std::ostringstream amount_ss;
8282  amount_ss << ask_amount;
8283  oreq.amounts.push_back(amount_ss.str());
8284  }
8285 
8286  oreq.count = light_wallet_requested_outputs_count;
8287  m_daemon_rpc_mutex.lock();
8288  bool r = invoke_http_json("/get_random_outs", oreq, ores, rpc_timeout, "POST");
8289  m_daemon_rpc_mutex.unlock();
8291  THROW_WALLET_EXCEPTION_IF(ores.amount_outs.empty() , error::wallet_internal_error, "No outputs received from light wallet node. Error: " + ores.Error);
8292 
8293  // Check if we got enough outputs for each amount
8294  for(auto& out: ores.amount_outs) {
8295  const uint64_t out_amount = boost::lexical_cast<uint64_t>(out.amount);
8296  THROW_WALLET_EXCEPTION_IF(out.outputs.size() < light_wallet_requested_outputs_count , error::wallet_internal_error, "Not enough outputs for amount: " + boost::lexical_cast<std::string>(out.amount));
8297  MDEBUG(out.outputs.size() << " outputs for amount "+ boost::lexical_cast<std::string>(out.amount) + " received from light wallet node");
8298  }
8299 
8300  MDEBUG("selected transfers size: " << selected_transfers.size());
8301 
8302  for(size_t idx: selected_transfers)
8303  {
8304  // Create new index
8305  outs.push_back(std::vector<get_outs_entry>());
8306  outs.back().reserve(fake_outputs_count + 1);
8307 
8308  // add real output first
8309  const transfer_details &td = m_transfers[idx];
8310  const uint64_t amount = td.is_rct() ? 0 : td.amount();
8311  outs.back().push_back(std::make_tuple(td.m_global_output_index, td.get_public_key(), rct::commit(td.amount(), td.m_mask)));
8312  MDEBUG("added real output " << string_tools::pod_to_hex(td.get_public_key()));
8313 
8314  // Even if the lightwallet server returns random outputs, we pick them randomly.
8315  std::vector<size_t> order;
8316  order.resize(light_wallet_requested_outputs_count);
8317  for (size_t n = 0; n < order.size(); ++n)
8318  order[n] = n;
8319  std::shuffle(order.begin(), order.end(), std::default_random_engine(crypto::rand<unsigned>()));
8320 
8321 
8322  LOG_PRINT_L2("Looking for " << (fake_outputs_count+1) << " outputs with amounts " << print_etn(td.is_rct() ? 0 : td.amount()));
8323  MDEBUG("OUTS SIZE: " << outs.back().size());
8324  for (size_t o = 0; o < light_wallet_requested_outputs_count && outs.back().size() < fake_outputs_count + 1; ++o)
8325  {
8326  // Random pick
8327  size_t i = order[o];
8328 
8329  // Find which random output key to use
8330  bool found_amount = false;
8331  size_t amount_key;
8332  for(amount_key = 0; amount_key < ores.amount_outs.size(); ++amount_key)
8333  {
8334  if(boost::lexical_cast<uint64_t>(ores.amount_outs[amount_key].amount) == amount) {
8335  found_amount = true;
8336  break;
8337  }
8338  }
8339  THROW_WALLET_EXCEPTION_IF(!found_amount , error::wallet_internal_error, "Outputs for amount " + boost::lexical_cast<std::string>(ores.amount_outs[amount_key].amount) + " not found" );
8340 
8341  LOG_PRINT_L2("Index " << i << "/" << light_wallet_requested_outputs_count << ": idx " << ores.amount_outs[amount_key].outputs[i].global_index << " (real " << td.m_global_output_index << "), unlocked " << "(always in light)" << ", key " << ores.amount_outs[0].outputs[i].public_key);
8342 
8343  // Convert light wallet string data to proper data structures
8344  crypto::public_key tx_public_key;
8345  rct::key mask = AUTO_VAL_INIT(mask); // decrypted mask - not used here
8346  rct::key rct_commit = AUTO_VAL_INIT(rct_commit);
8347  THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, ores.amount_outs[amount_key].outputs[i].public_key), error::wallet_internal_error, "Invalid public_key");
8348  string_tools::hex_to_pod(ores.amount_outs[amount_key].outputs[i].public_key, tx_public_key);
8349  const uint64_t global_index = ores.amount_outs[amount_key].outputs[i].global_index;
8350  if(!light_wallet_parse_rct_str(ores.amount_outs[amount_key].outputs[i].rct, tx_public_key, 0, mask, rct_commit, false))
8351  rct_commit = rct::zeroCommit(td.amount());
8352 
8353  if (tx_add_fake_output(outs, global_index, tx_public_key, rct_commit, td.m_global_output_index, true)) {
8354  MDEBUG("added fake output " << ores.amount_outs[amount_key].outputs[i].public_key);
8355  MDEBUG("index " << global_index);
8356  }
8357  }
8358 
8359  THROW_WALLET_EXCEPTION_IF(outs.back().size() < fake_outputs_count + 1 , error::wallet_internal_error, "Not enough fake outputs found" );
8360 
8361  // Real output is the first. Shuffle outputs
8362  MTRACE(outs.back().size() << " outputs added. Sorting outputs by index:");
8363  std::sort(outs.back().begin(), outs.back().end(), [](const get_outs_entry &a, const get_outs_entry &b) { return std::get<0>(a) < std::get<0>(b); });
8364 
8365  // Print output order
8366  for(auto added_out: outs.back())
8367  MTRACE(std::get<0>(added_out));
8368 
8369  }
8370 }
8371 
8372 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)
8373 {
8374  LOG_PRINT_L2("fake_outputs_count: " << fake_outputs_count);
8375  outs.clear();
8376 
8377  if(m_light_wallet && fake_outputs_count > 0) {
8378  light_wallet_get_outs(outs, selected_transfers, fake_outputs_count);
8379  return;
8380  }
8381 
8382  if (fake_outputs_count > 0) // zero for electroneum, so skip a lot of code
8383  {
8384  uint64_t segregation_fork_height = get_segregation_fork_height();
8385  // check whether we're shortly after the fork
8386  uint64_t height;
8387  boost::optional<std::string> result = m_node_rpc_proxy.get_height(height);
8388  throw_on_rpc_response_error(result, "get_info");
8389  bool is_shortly_after_segregation_fork = height >= segregation_fork_height && height < segregation_fork_height + SEGREGATION_FORK_VICINITY;
8390  bool is_after_segregation_fork = height >= segregation_fork_height;
8391 
8392  uint64_t v8height = m_nettype == TESTNET ? 446674 : 589169;
8394  uint16_t MINED_ETN_SPENDABLE_AGE = height > v8height ? ETN_MINED_ETN_UNLOCK_WINDOW_V8 : CRYPTONOTE_MINED_ETN_UNLOCK_WINDOW;
8395 
8396  // if we have at least one rct out, get the distribution, or fall back to the previous system
8397  uint64_t rct_start_height;
8398  std::vector<uint64_t> rct_offsets;
8399  bool has_rct = false;
8400  uint64_t max_rct_index = 0;
8401  for (size_t idx: selected_transfers)
8402  if (m_transfers[idx].is_rct())
8403  {
8404  has_rct = true;
8405  max_rct_index = std::max(max_rct_index, m_transfers[idx].m_global_output_index);
8406  }
8407  const bool has_rct_distribution = has_rct && get_rct_distribution(rct_start_height, rct_offsets);
8408  if (has_rct_distribution)
8409  {
8410  // check we're clear enough of rct start, to avoid corner cases below
8411  THROW_WALLET_EXCEPTION_IF(rct_offsets.size() <= TX_SPENDABLE_AGE,
8412  error::get_output_distribution, "Not enough rct outputs");
8413  THROW_WALLET_EXCEPTION_IF(rct_offsets.back() <= max_rct_index,
8414  error::get_output_distribution, "Daemon reports suspicious number of rct outputs");
8415  }
8416 
8417  // get histogram for the amounts we need
8420  // request histogram for all outputs, except 0 if we have the rct distribution
8421  for(size_t idx: selected_transfers)
8422  if (!m_transfers[idx].is_rct() || !has_rct_distribution)
8423  req_t.amounts.push_back(m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount());
8424  if (!req_t.amounts.empty())
8425  {
8426  std::sort(req_t.amounts.begin(), req_t.amounts.end());
8427  auto end = std::unique(req_t.amounts.begin(), req_t.amounts.end());
8428  req_t.amounts.resize(std::distance(req_t.amounts.begin(), end));
8429  req_t.unlocked = true;
8430  req_t.recent_cutoff = time(NULL) - RECENT_OUTPUT_ZONE;
8431  m_daemon_rpc_mutex.lock();
8432  bool r = invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, rpc_timeout);
8433  m_daemon_rpc_mutex.unlock();
8434  THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected");
8435  THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
8436  THROW_WALLET_EXCEPTION_IF(resp_t.status != CORE_RPC_STATUS_OK, error::get_histogram_error, get_rpc_status(resp_t.status));
8437  }
8438 
8439  // if we want to segregate fake outs pre or post fork, get distribution
8440  std::unordered_map<uint64_t, std::pair<uint64_t, uint64_t>> segregation_limit;
8441  if (is_after_segregation_fork && (m_segregate_pre_fork_outputs || m_key_reuse_mitigation2))
8442  {
8445  for(size_t idx: selected_transfers)
8446  req_t.amounts.push_back(m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount());
8447  std::sort(req_t.amounts.begin(), req_t.amounts.end());
8448  auto end = std::unique(req_t.amounts.begin(), req_t.amounts.end());
8449  req_t.amounts.resize(std::distance(req_t.amounts.begin(), end));
8450  req_t.from_height = std::max<uint64_t>(segregation_fork_height, RECENT_OUTPUT_BLOCKS) - RECENT_OUTPUT_BLOCKS;
8451  req_t.to_height = segregation_fork_height + 1;
8452  req_t.cumulative = true;
8453  req_t.binary = true;
8454  m_daemon_rpc_mutex.lock();
8455  bool r = invoke_http_json_rpc("/json_rpc", "get_output_distribution", req_t, resp_t, rpc_timeout * 1000);
8456  m_daemon_rpc_mutex.unlock();
8457  THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected");
8458  THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_distribution");
8459  THROW_WALLET_EXCEPTION_IF(resp_t.status != CORE_RPC_STATUS_OK, error::get_output_distribution, get_rpc_status(resp_t.status));
8460 
8461  // check we got all data
8462  for(size_t idx: selected_transfers)
8463  {
8464  const uint64_t amount = m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount();
8465  bool found = false;
8466  for (const auto &d: resp_t.distributions)
8467  {
8468  if (d.amount == amount)
8469  {
8470  THROW_WALLET_EXCEPTION_IF(d.data.start_height > segregation_fork_height, error::get_output_distribution, "Distribution start_height too high");
8471  THROW_WALLET_EXCEPTION_IF(segregation_fork_height - d.data.start_height >= d.data.distribution.size(), error::get_output_distribution, "Distribution size too small");
8472  THROW_WALLET_EXCEPTION_IF(segregation_fork_height - RECENT_OUTPUT_BLOCKS - d.data.start_height >= d.data.distribution.size(), error::get_output_distribution, "Distribution size too small");
8473  THROW_WALLET_EXCEPTION_IF(segregation_fork_height <= RECENT_OUTPUT_BLOCKS, error::wallet_internal_error, "Fork height too low");
8474  THROW_WALLET_EXCEPTION_IF(segregation_fork_height - RECENT_OUTPUT_BLOCKS < d.data.start_height, error::get_output_distribution, "Bad start height");
8475  uint64_t till_fork = d.data.distribution[segregation_fork_height - d.data.start_height];
8476  uint64_t recent = till_fork - d.data.distribution[segregation_fork_height - RECENT_OUTPUT_BLOCKS - d.data.start_height];
8477  segregation_limit[amount] = std::make_pair(till_fork, recent);
8478  found = true;
8479  break;
8480  }
8481  }
8482  THROW_WALLET_EXCEPTION_IF(!found, error::get_output_distribution, "Requested amount not found in response");
8483  }
8484  }
8485 
8486  // we ask for more, to have spares if some outputs are still locked
8487  size_t base_requested_outputs_count = (size_t)((fake_outputs_count + 1) * 1.5 + 1);
8488  LOG_PRINT_L2("base_requested_outputs_count: " << base_requested_outputs_count);
8489 
8490  // generate output indices to request
8492  COMMAND_RPC_GET_OUTPUTS_BIN::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
8493 
8494  std::unique_ptr<gamma_picker> gamma;
8495  if (has_rct_distribution)
8496  gamma.reset(new gamma_picker(rct_offsets));
8497 
8498  size_t num_selected_transfers = 0;
8499  for(size_t idx: selected_transfers)
8500  {
8501  ++num_selected_transfers;
8502  const transfer_details &td = m_transfers[idx];
8503  const uint64_t amount = td.is_rct() ? 0 : td.amount();
8504  std::unordered_set<uint64_t> seen_indices;
8505  // request more for rct in base recent (locked) coinbases are picked, since they're locked for longer
8506  size_t requested_outputs_count = base_requested_outputs_count + (td.is_rct() ? MINED_ETN_SPENDABLE_AGE - TX_SPENDABLE_AGE : 0);
8507  size_t start = req.outputs.size();
8508  bool use_histogram = amount != 0 || !has_rct_distribution;
8509 
8510  const bool output_is_pre_fork = td.m_block_height < segregation_fork_height;
8511  uint64_t num_outs = 0, num_recent_outs = 0;
8512  uint64_t num_post_fork_outs = 0;
8513  float pre_fork_num_out_ratio = 0.0f;
8514  float post_fork_num_out_ratio = 0.0f;
8515 
8516  if (is_after_segregation_fork && m_segregate_pre_fork_outputs && output_is_pre_fork)
8517  {
8518  num_outs = segregation_limit[amount].first;
8519  num_recent_outs = segregation_limit[amount].second;
8520  }
8521  else
8522  {
8523  // if there are just enough outputs to mix with, use all of them.
8524  // Eventually this should become impossible.
8525  for (const auto &he: resp_t.histogram)
8526  {
8527  if (he.amount == amount)
8528  {
8529  LOG_PRINT_L2("Found " << print_etn(amount) << ": " << he.total_instances << " total, "
8530  << he.unlocked_instances << " unlocked, " << he.recent_instances << " recent");
8531  num_outs = he.unlocked_instances;
8532  num_recent_outs = he.recent_instances;
8533  break;
8534  }
8535  }
8536  if (is_after_segregation_fork && m_key_reuse_mitigation2)
8537  {
8538  if (output_is_pre_fork)
8539  {
8540  if (is_shortly_after_segregation_fork)
8541  {
8542  pre_fork_num_out_ratio = 33.4/100.0f * (1.0f - RECENT_OUTPUT_RATIO);
8543  }
8544  else
8545  {
8546  pre_fork_num_out_ratio = 33.4/100.0f * (1.0f - RECENT_OUTPUT_RATIO);
8547  post_fork_num_out_ratio = 33.4/100.0f * (1.0f - RECENT_OUTPUT_RATIO);
8548  }
8549  }
8550  else
8551  {
8552  if (is_shortly_after_segregation_fork)
8553  {
8554  }
8555  else
8556  {
8557  post_fork_num_out_ratio = 67.8/100.0f * (1.0f - RECENT_OUTPUT_RATIO);
8558  }
8559  }
8560  }
8561  num_post_fork_outs = num_outs - segregation_limit[amount].first;
8562  }
8563 
8564  if (use_histogram)
8565  {
8566  LOG_PRINT_L1("" << num_outs << " unlocked outputs of size " << print_etn(amount));
8567  THROW_WALLET_EXCEPTION_IF(num_outs == 0, error::wallet_internal_error,
8568  "histogram reports no unlocked outputs for " + boost::lexical_cast<std::string>(amount) + ", not even ours");
8569  THROW_WALLET_EXCEPTION_IF(num_recent_outs > num_outs, error::wallet_internal_error,
8570  "histogram reports more recent outs than outs for " + boost::lexical_cast<std::string>(amount));
8571  }
8572  else
8573  {
8574  // the base offset of the first rct output in the first unlocked block (or the one to be if there's none)
8575  num_outs = rct_offsets[rct_offsets.size() - TX_SPENDABLE_AGE];
8576  LOG_PRINT_L1("" << num_outs << " unlocked rct outputs");
8577  THROW_WALLET_EXCEPTION_IF(num_outs == 0, error::wallet_internal_error,
8578  "histogram reports no unlocked rct outputs, not even ours");
8579  }
8580 
8581  // how many fake outs to draw on a pre-fork distribution
8582  size_t pre_fork_outputs_count = requested_outputs_count * pre_fork_num_out_ratio;
8583  size_t post_fork_outputs_count = requested_outputs_count * post_fork_num_out_ratio;
8584  // how many fake outs to draw otherwise
8585  size_t normal_output_count = requested_outputs_count - pre_fork_outputs_count - post_fork_outputs_count;
8586 
8587  size_t recent_outputs_count = 0;
8588  if (use_histogram)
8589  {
8590  // X% of those outs are to be taken from recent outputs
8591  recent_outputs_count = normal_output_count * RECENT_OUTPUT_RATIO;
8592  if (recent_outputs_count == 0)
8593  recent_outputs_count = 1; // ensure we have at least one, if possible
8594  if (recent_outputs_count > num_recent_outs)
8595  recent_outputs_count = num_recent_outs;
8596  if (td.m_global_output_index >= num_outs - num_recent_outs && recent_outputs_count > 0)
8597  --recent_outputs_count; // if the real out is recent, pick one less recent fake out
8598  }
8599  LOG_PRINT_L1("Fake output makeup: " << requested_outputs_count << " requested: " << recent_outputs_count << " recent, " <<
8600  pre_fork_outputs_count << " pre-fork, " << post_fork_outputs_count << " post-fork, " <<
8601  (requested_outputs_count - recent_outputs_count - pre_fork_outputs_count - post_fork_outputs_count) << " full-chain");
8602 
8603  uint64_t num_found = 0;
8604 
8605  // if we have a known ring, use it
8606  if (td.m_key_image_known && !td.m_key_image_partial)
8607  {
8608  std::vector<uint64_t> ring;
8609  if (get_ring(get_ringdb_key(), td.m_key_image, ring))
8610  {
8611  MINFO("This output has a known ring, reusing (size " << ring.size() << ")");
8612  THROW_WALLET_EXCEPTION_IF(ring.size() > fake_outputs_count + 1, error::wallet_internal_error,
8613  "An output in this transaction was previously spent on another chain with ring size " +
8614  std::to_string(ring.size()) + ", it cannot be spent now with ring size " +
8615  std::to_string(fake_outputs_count + 1) + " as it is smaller: use a higher ring size");
8616  bool own_found = false;
8617  for (const auto &out: ring)
8618  {
8619  MINFO("Ring has output " << out);
8620  if (out < num_outs)
8621  {
8622  MINFO("Using it");
8623  req.outputs.push_back({amount, out});
8624  ++num_found;
8625  seen_indices.emplace(out);
8626  if (out == td.m_global_output_index)
8627  {
8628  MINFO("This is the real output");
8629  own_found = true;
8630  }
8631  }
8632  else
8633  {
8634  MINFO("Ignoring output " << out << ", too recent");
8635  }
8636  }
8637  THROW_WALLET_EXCEPTION_IF(!own_found, error::wallet_internal_error,
8638  "Known ring does not include the spent output: " + std::to_string(td.m_global_output_index));
8639  }
8640  }
8641 
8642  if (num_outs <= requested_outputs_count)
8643  {
8644  for (uint64_t i = 0; i < num_outs; i++)
8645  req.outputs.push_back({amount, i});
8646  // duplicate to make up shortfall: this will be caught after the RPC call,
8647  // so we can also output the amounts for which we can't reach the required
8648  // mixin after checking the actual unlockedness
8649  for (uint64_t i = num_outs; i < requested_outputs_count; ++i)
8650  req.outputs.push_back({amount, num_outs - 1});
8651  }
8652  else
8653  {
8654  // start with real one
8655  if (num_found == 0)
8656  {
8657  num_found = 1;
8658  seen_indices.emplace(td.m_global_output_index);
8659  req.outputs.push_back({amount, td.m_global_output_index});
8660  LOG_PRINT_L1("Selecting real output: " << td.m_global_output_index << " for " << print_etn(amount));
8661  }
8662 
8663  std::unordered_map<const char*, std::set<uint64_t>> picks;
8664 
8665  // while we still need more mixins
8666  uint64_t num_usable_outs = num_outs;
8667  bool allow_blackballed = false;
8668  MDEBUG("Starting gamma picking with " << num_outs << ", num_usable_outs " << num_usable_outs
8669  << ", requested_outputs_count " << requested_outputs_count);
8670  while (num_found < requested_outputs_count)
8671  {
8672  // if we've gone through every possible output, we've gotten all we can
8673  if (seen_indices.size() == num_usable_outs)
8674  {
8675  // there is a first pass which rejects blackballed outputs, then a second pass
8676  // which allows them if we don't have enough non blackballed outputs to reach
8677  // the required amount of outputs (since consensus does not care about blackballed
8678  // outputs, we still need to reach the minimum ring size)
8679  if (allow_blackballed)
8680  break;
8681  MINFO("Not enough output not marked as spent, we'll allow outputs marked as spent");
8682  allow_blackballed = true;
8683  num_usable_outs = num_outs;
8684  }
8685 
8686  // get a random output index from the DB. If we've already seen it,
8687  // return to the top of the loop and try again, otherwise add it to the
8688  // list of output indices we've seen.
8689 
8690  uint64_t i;
8691  const char *type = "";
8692  if (amount == 0 && has_rct_distribution)
8693  {
8694  THROW_WALLET_EXCEPTION_IF(!gamma, error::wallet_internal_error, "No gamma picker");
8695  // gamma distribution
8696  if (num_found -1 < recent_outputs_count + pre_fork_outputs_count)
8697  {
8698  do i = gamma->pick(); while (i >= segregation_limit[amount].first);
8699  type = "pre-fork gamma";
8700  }
8701  else if (num_found -1 < recent_outputs_count + pre_fork_outputs_count + post_fork_outputs_count)
8702  {
8703  do i = gamma->pick(); while (i < segregation_limit[amount].first || i >= num_outs);
8704  type = "post-fork gamma";
8705  }
8706  else
8707  {
8708  do i = gamma->pick(); while (i >= num_outs);
8709  type = "gamma";
8710  }
8711  }
8712  else if (num_found - 1 < recent_outputs_count) // -1 to account for the real one we seeded with
8713  {
8714  // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
8715  uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53);
8716  double frac = std::sqrt((double)r / ((uint64_t)1 << 53));
8717  i = (uint64_t)(frac*num_recent_outs) + num_outs - num_recent_outs;
8718  // just in case rounding up to 1 occurs after calc
8719  if (i == num_outs)
8720  --i;
8721  type = "recent";
8722  }
8723  else if (num_found -1 < recent_outputs_count + pre_fork_outputs_count)
8724  {
8725  // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
8726  uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53);
8727  double frac = std::sqrt((double)r / ((uint64_t)1 << 53));
8728  i = (uint64_t)(frac*segregation_limit[amount].first);
8729  // just in case rounding up to 1 occurs after calc
8730  if (i == num_outs)
8731  --i;
8732  type = " pre-fork";
8733  }
8734  else if (num_found -1 < recent_outputs_count + pre_fork_outputs_count + post_fork_outputs_count)
8735  {
8736  // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
8737  uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53);
8738  double frac = std::sqrt((double)r / ((uint64_t)1 << 53));
8739  i = (uint64_t)(frac*num_post_fork_outs) + segregation_limit[amount].first;
8740  // just in case rounding up to 1 occurs after calc
8741  if (i == num_post_fork_outs+segregation_limit[amount].first)
8742  --i;
8743  type = "post-fork";
8744  }
8745  else
8746  {
8747  // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
8748  uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53);
8749  double frac = std::sqrt((double)r / ((uint64_t)1 << 53));
8750  i = (uint64_t)(frac*num_outs);
8751  // just in case rounding up to 1 occurs after calc
8752  if (i == num_outs)
8753  --i;
8754  type = "triangular";
8755  }
8756 
8757  if (seen_indices.count(i))
8758  continue;
8759  if (!allow_blackballed && is_output_blackballed(std::make_pair(amount, i))) // don't add blackballed outputs
8760  {
8761  --num_usable_outs;
8762  continue;
8763  }
8764  seen_indices.emplace(i);
8765 
8766  picks[type].insert(i);
8767  req.outputs.push_back({amount, i});
8768  ++num_found;
8769  MDEBUG("picked " << i << ", " << num_found << " now picked");
8770  }
8771 
8772  for (const auto &pick: picks)
8773  MDEBUG("picking " << pick.first << " outputs: " <<
8774  boost::join(pick.second | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " "));
8775 
8776  // if we had enough unusable outputs, we might fall off here and still
8777  // have too few outputs, so we stuff with one to keep counts good, and
8778  // we'll error out later
8779  while (num_found < requested_outputs_count)
8780  {
8781  req.outputs.push_back({amount, 0});
8782  ++num_found;
8783  }
8784  }
8785 
8786  // sort the subsection, to ensure the daemon doesn't know which output is ours
8787  std::sort(req.outputs.begin() + start, req.outputs.end(),
8788  [](const get_outputs_out &a, const get_outputs_out &b) { return a.index < b.index; });
8789  }
8790 
8791  if (ELPP->vRegistry()->allowed(el::Level::Debug, ELECTRONEUM_DEFAULT_LOG_CATEGORY))
8792  {
8793  std::map<uint64_t, std::set<uint64_t>> outs;
8794  for (const auto &i: req.outputs)
8795  outs[i.amount].insert(i.index);
8796  for (const auto &o: outs)
8797  MDEBUG("asking for outputs with amount " << print_etn(o.first) << ": " <<
8798  boost::join(o.second | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " "));
8799  }
8800 
8801  // get the keys for those
8802  req.get_txid = false;
8803  m_daemon_rpc_mutex.lock();
8804  bool r = invoke_http_bin("/get_outs.bin", req, daemon_resp, rpc_timeout);
8805  m_daemon_rpc_mutex.unlock();
8806  THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin");
8807  THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin");
8808  THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_outs_error, get_rpc_status(daemon_resp.status));
8809  THROW_WALLET_EXCEPTION_IF(daemon_resp.outs.size() != req.outputs.size(), error::wallet_internal_error,
8810  "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
8811  std::to_string(daemon_resp.outs.size()) + ", expected " + std::to_string(req.outputs.size()));
8812 
8813  std::unordered_map<uint64_t, uint64_t> scanty_outs;
8814  size_t base = 0;
8815  outs.reserve(num_selected_transfers);
8816  for(size_t idx: selected_transfers)
8817  {
8818  const transfer_details &td = m_transfers[idx];
8819  size_t requested_outputs_count = base_requested_outputs_count + (td.is_rct() ? MINED_ETN_SPENDABLE_AGE - TX_SPENDABLE_AGE : 0);
8820  outs.push_back(std::vector<get_outs_entry>());
8821  outs.back().reserve(fake_outputs_count + 1);
8822  const rct::key mask = td.is_rct() ? rct::commit(td.amount(), td.m_mask) : rct::zeroCommit(td.amount());
8823 
8824  uint64_t num_outs = 0;
8825  const uint64_t amount = td.is_rct() ? 0 : td.amount();
8826  const bool output_is_pre_fork = td.m_block_height < segregation_fork_height;
8827  if (is_after_segregation_fork && m_segregate_pre_fork_outputs && output_is_pre_fork)
8828  num_outs = segregation_limit[amount].first;
8829  else for (const auto &he: resp_t.histogram)
8830  {
8831  if (he.amount == amount)
8832  {
8833  num_outs = he.unlocked_instances;
8834  break;
8835  }
8836  }
8837  bool use_histogram = amount != 0 || !has_rct_distribution;
8838  if (!use_histogram)
8839  num_outs = rct_offsets[rct_offsets.size() - TX_SPENDABLE_AGE];
8840 
8841  // make sure the real outputs we asked for are really included, along
8842  // with the correct key and mask: this guards against an active attack
8843  // where the node sends dummy data for all outputs, and we then send
8844  // the real one, which the node can then tell from the fake outputs,
8845  // as it has different data than the dummy data it had sent earlier
8846  bool real_out_found = false;
8847  for (size_t n = 0; n < requested_outputs_count; ++n)
8848  {
8849  size_t i = base + n;
8850  if (req.outputs[i].index == td.m_global_output_index)
8851  if (daemon_resp.outs[i].key == boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key)
8852  if (daemon_resp.outs[i].mask == mask)
8853  real_out_found = true;
8854  }
8855  THROW_WALLET_EXCEPTION_IF(!real_out_found, error::wallet_internal_error,
8856  "Daemon response did not include the requested real output");
8857 
8858  // pick real out first (it will be sorted when done)
8859  outs.back().push_back(std::make_tuple(td.m_global_output_index, boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key, mask));
8860 
8861  // then pick outs from an existing ring, if any
8862  if (td.m_key_image_known && !td.m_key_image_partial)
8863  {
8864  std::vector<uint64_t> ring;
8865  if (get_ring(get_ringdb_key(), td.m_key_image, ring))
8866  {
8867  for (uint64_t out: ring)
8868  {
8869  if (out < num_outs)
8870  {
8871  if (out != td.m_global_output_index)
8872  {
8873  bool found = false;
8874  for (size_t o = 0; o < requested_outputs_count; ++o)
8875  {
8876  size_t i = base + o;
8877  if (req.outputs[i].index == out)
8878  {
8879  LOG_PRINT_L2("Index " << i << "/" << requested_outputs_count << ": idx " << req.outputs[i].index << " (real " << td.m_global_output_index << "), unlocked " << daemon_resp.outs[i].unlocked << ", key " << daemon_resp.outs[i].key << " (from existing ring)");
8880  tx_add_fake_output(outs, req.outputs[i].index, daemon_resp.outs[i].key, daemon_resp.outs[i].mask, td.m_global_output_index, daemon_resp.outs[i].unlocked);
8881  found = true;
8882  break;
8883  }
8884  }
8885  THROW_WALLET_EXCEPTION_IF(!found, error::wallet_internal_error, "Falied to find existing ring output in daemon out data");
8886  }
8887  }
8888  }
8889  }
8890  }
8891 
8892  // then pick others in random order till we reach the required number
8893  // since we use an equiprobable pick here, we don't upset the triangular distribution
8894  std::vector<size_t> order;
8895  order.resize(requested_outputs_count);
8896  for (size_t n = 0; n < order.size(); ++n)
8897  order[n] = n;
8898  std::shuffle(order.begin(), order.end(), std::default_random_engine(crypto::rand<unsigned>()));
8899 
8900  LOG_PRINT_L2("Looking for " << (fake_outputs_count+1) << " outputs of size " << print_etn(td.is_rct() ? 0 : td.amount()));
8901  for (size_t o = 0; o < requested_outputs_count && outs.back().size() < fake_outputs_count + 1; ++o)
8902  {
8903  size_t i = base + order[o];
8904  LOG_PRINT_L2("Index " << i << "/" << requested_outputs_count << ": idx " << req.outputs[i].index << " (real " << td.m_global_output_index << "), unlocked " << daemon_resp.outs[i].unlocked << ", key " << daemon_resp.outs[i].key);
8905  tx_add_fake_output(outs, req.outputs[i].index, daemon_resp.outs[i].key, daemon_resp.outs[i].mask, td.m_global_output_index, daemon_resp.outs[i].unlocked);
8906  }
8907  if (outs.back().size() < fake_outputs_count + 1)
8908  {
8909  scanty_outs[td.is_rct() ? 0 : td.amount()] = outs.back().size();
8910  }
8911  else
8912  {
8913  // sort the subsection, so any spares are reset in order
8914  std::sort(outs.back().begin(), outs.back().end(), [](const get_outs_entry &a, const get_outs_entry &b) { return std::get<0>(a) < std::get<0>(b); });
8915  }
8916  base += requested_outputs_count;
8917  }
8918  THROW_WALLET_EXCEPTION_IF(!scanty_outs.empty(), error::not_enough_outs_to_mix, scanty_outs, fake_outputs_count);
8919  }
8920  else //no fake outs => start reading here
8921  {
8922  if(tx_version < 3) {
8923  for (size_t idx: selected_transfers) {
8924  const transfer_details &td = m_transfers[idx];
8925  std::vector<get_outs_entry> v;
8926  const rct::key mask = td.is_rct() ? rct::commit(td.amount(), td.m_mask) : rct::zeroCommit(td.amount());
8927  v.push_back(std::make_tuple(td.m_global_output_index, td.get_public_key(),
8928  mask)); // get pub key is where our error is (wrong get)
8929  outs.push_back(v);
8930  }
8931  }
8932  }
8933 
8934  if(tx_version < 3) {
8935  // save those outs in the ringdb for reuse
8936  for (size_t i = 0; i < selected_transfers.size(); ++i) {
8937  const size_t idx = selected_transfers[i];
8938  THROW_WALLET_EXCEPTION_IF(idx >= m_transfers.size(), error::wallet_internal_error,
8939  "selected_transfers entry out of range");
8940  const transfer_details &td = m_transfers[idx];
8941  std::vector<uint64_t> ring;
8942  ring.reserve(outs[i].size());
8943  for (const auto &e: outs[i])
8944  ring.push_back(std::get<0>(e));
8945  if (!set_ring(td.m_key_image, ring, false))//
8946  MERROR("Failed to set ring for " << td.m_key_image);
8947  }
8948  }
8949 }
8950 
8951 template<typename T>
8952 void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
8953  std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
8954  uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx)
8955 {
8956  using namespace cryptonote;
8957  // throw if attempting a transaction with no destinations
8959 
8960  THROW_WALLET_EXCEPTION_IF(m_multisig, error::wallet_internal_error, "Multisig wallets cannot spend non rct outputs");
8961 
8962  uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit();
8963  uint64_t needed_etn = fee;
8964  LOG_PRINT_L2("transfer: starting with fee " << print_etn (needed_etn));
8965 
8966  // calculate total amount being sent to all destinations
8967  // throw if total amount overflows uint64_t
8968  for(auto& dt: dsts)
8969  {
8971  needed_etn += dt.amount;
8972  LOG_PRINT_L2("transfer: adding " << print_etn(dt.amount) << ", for a total of " << print_etn (needed_etn));
8973  THROW_WALLET_EXCEPTION_IF(needed_etn < dt.amount, error::tx_sum_overflow, dsts, fee, m_nettype);
8974  }
8975 
8976  uint64_t found_etn = 0;
8977  for(size_t idx: selected_transfers)
8978  {
8979  found_etn += m_transfers[idx].amount();
8980  }
8981 
8982  LOG_PRINT_L2("wanted " << print_etn(needed_etn) << ", found " << print_etn(found_etn) << ", fee " << print_etn(fee));
8983  THROW_WALLET_EXCEPTION_IF(found_etn < needed_etn, error::not_enough_unlocked_etn, found_etn, needed_etn - fee, fee);
8984 
8985  uint32_t subaddr_account = m_transfers[*selected_transfers.begin()].m_subaddr_index.major;
8986  for (auto i = ++selected_transfers.begin(); i != selected_transfers.end(); ++i)
8987  THROW_WALLET_EXCEPTION_IF(subaddr_account != m_transfers[*i].m_subaddr_index.major, error::wallet_internal_error, "the tx uses funds from multiple accounts");
8988 
8989  if (outs.empty())
8990  get_outs(outs, selected_transfers, fake_outputs_count, tx.version); // may throw
8991 
8992  //prepare inputs
8993  LOG_PRINT_L2("preparing outputs");
8994  typedef cryptonote::tx_source_entry::output_entry tx_output_entry;
8995  size_t i = 0, out_index = 0;
8996  std::vector<cryptonote::tx_source_entry> sources;
8997  for(size_t idx: selected_transfers)
8998  {
8999  sources.resize(sources.size()+1);
9000  cryptonote::tx_source_entry& src = sources.back();
9001  const transfer_details& td = m_transfers[idx];
9002  src.amount = td.amount();
9003  src.rct = td.is_rct();
9004  if(tx.version < 3) {
9005  //paste keys (fake and real)
9006  // adding pairs of global index & stealth address to our vector of source outs (needed forold ins only)
9007  for (size_t n = 0; n < fake_outputs_count + 1; ++n) {
9008  tx_output_entry oe;
9009  oe.first = std::get<0>(outs[out_index][n]);
9010  oe.second.dest = rct::pk2rct(std::get<1>(outs[out_index][n]));
9011  oe.second.mask = std::get<2>(outs[out_index][n]);
9012 
9013  src.outputs.push_back(oe);
9014  ++i;
9015  }
9016 
9017  //paste real transaction to the random index
9018  auto it_to_replace = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const tx_output_entry &a) {
9019  return a.first == td.m_global_output_index;
9020  });
9022  "real output not found");
9023 
9024  tx_output_entry real_oe;
9025  real_oe.first = td.m_global_output_index;
9026  real_oe.second.dest = rct::pk2rct(
9027  boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key);
9028  real_oe.second.mask = rct::commit(td.amount(), td.m_mask);
9029  *it_to_replace = real_oe;
9032  src.real_output = it_to_replace - src.outputs.begin();
9034  }
9035  src.real_output_in_tx_index = td.m_internal_output_index; // these two are all we really need for v3 sources
9036  src.tx_hash = td.m_txid;
9037  src.subaddr_index = td.m_subaddr_index;
9039  ++out_index;
9040  }
9041  LOG_PRINT_L2("outputs prepared");
9042 
9043  cryptonote::tx_destination_entry change_dts = AUTO_VAL_INIT(change_dts);
9044  if (needed_etn < found_etn)
9045  {
9046  // send change to the first input's address for v3+ tx
9047  uint32_t change_subaddress_minor = tx.version > 2 ? sources.front().subaddr_index.minor : 0;
9048  change_dts.addr = get_subaddress({subaddr_account, change_subaddress_minor});
9049  change_dts.is_subaddress = (subaddr_account != 0 || change_subaddress_minor != 0);
9050  change_dts.amount = found_etn - needed_etn;
9051  }
9052 
9053  std::vector<cryptonote::tx_destination_entry> splitted_dsts, dust_dsts;
9054  uint64_t dust = 0;
9055  destination_split_strategy(dsts, change_dts, dust_policy.dust_threshold, splitted_dsts, dust_dsts);
9056  for(auto& d: dust_dsts) {
9057  THROW_WALLET_EXCEPTION_IF(dust_policy.dust_threshold < d.amount, error::wallet_internal_error, "invalid dust value: dust = " +
9058  std::to_string(d.amount) + ", dust_threshold = " + std::to_string(dust_policy.dust_threshold));
9059  }
9060  for(auto& d: dust_dsts) {
9061  if (!dust_policy.add_to_fee)
9062  splitted_dsts.push_back(cryptonote::tx_destination_entry(d.amount, dust_policy.addr_for_dust, d.is_subaddress));
9063  dust += d.amount;
9064  }
9065 
9066  crypto::secret_key tx_key;
9067  std::vector<crypto::secret_key> additional_tx_keys;
9068  rct::multisig_out msout;
9069 
9070  LOG_PRINT_L2("constructing tx");
9071  bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, {}, m_multisig ? &msout : NULL, m_account_major_offset, this->m_nettype);
9072  LOG_PRINT_L2("constructed tx, r="<<r);
9073  THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype);
9074  THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit);
9075 
9076  std::string key_images;
9077  bool are_ins_correct_type = tx.version >= 3 ?
9078  std::all_of(tx.vin.begin(), tx.vin.end(), [&](const txin_v& s_e) -> bool
9079  {
9080  CHECKED_GET_SPECIFIC_VARIANT(s_e, const txin_to_key_public, in, false);
9081  return true;
9082  })
9083  :
9084  std::all_of(tx.vin.begin(), tx.vin.end(), [&](const txin_v& s_e) -> bool
9085  {
9086  CHECKED_GET_SPECIFIC_VARIANT(s_e, const txin_to_key, in, false);
9087  key_images += boost::to_string(in.k_image) + " ";
9088  return true;
9089  });
9090 
9091  THROW_WALLET_EXCEPTION_IF(!are_ins_correct_type, error::unexpected_txin_type, tx);
9092 
9093 
9094  bool dust_sent_elsewhere = (dust_policy.addr_for_dust.m_view_public_key != change_dts.addr.m_view_public_key
9095  || dust_policy.addr_for_dust.m_spend_public_key != change_dts.addr.m_spend_public_key);
9096 
9097  if (dust_policy.add_to_fee || dust_sent_elsewhere) change_dts.amount -= dust;
9098 
9099  ptx.key_images = key_images;
9100  ptx.fee = (dust_policy.add_to_fee ? fee+dust : fee);
9101  ptx.dust = ((dust_policy.add_to_fee || dust_sent_elsewhere) ? dust : 0);
9102  ptx.dust_added_to_fee = dust_policy.add_to_fee;
9103  ptx.tx = tx;
9104  ptx.change_dts = change_dts;
9105  ptx.selected_transfers = selected_transfers;
9106  ptx.tx_key = tx_key;
9107  ptx.additional_tx_keys = additional_tx_keys;
9108  ptx.dests = dsts;
9109  ptx.construction_data.sources = sources;
9110  ptx.construction_data.change_dts = change_dts;
9111  ptx.construction_data.splitted_dsts = splitted_dsts;
9112  ptx.construction_data.selected_transfers = selected_transfers;
9113  ptx.construction_data.extra = tx.extra;
9114  ptx.construction_data.unlock_time = unlock_time;
9115  ptx.construction_data.use_rct = false;
9117  ptx.construction_data.dests = dsts;
9118  // record which subaddress indices are being used as inputs
9119  ptx.construction_data.subaddr_account = subaddr_account;
9120  ptx.construction_data.subaddr_indices.clear();
9121  for (size_t idx: selected_transfers)
9122  ptx.construction_data.subaddr_indices.insert(m_transfers[idx].m_subaddr_index.minor);
9123  LOG_PRINT_L2("transfer_selected done");
9124 }
9125 
9126 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
9127 {
9128  std::vector<size_t> picks;
9129  float current_output_relatdness = 1.0f;
9130 
9131  LOG_PRINT_L2("pick_preferred_rct_inputs: needed_etn " << print_etn(needed_etn));
9132 
9133  // try to find a rct input of enough size
9134  for (size_t i = 0; i < m_transfers.size(); ++i)
9135  {
9136  const transfer_details& td = m_transfers[i];
9137  if (!td.m_spent && !td.m_frozen && td.is_rct() && td.amount() >= needed_etn && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
9138  {
9139  LOG_PRINT_L2("We can use " << i << " alone: " << print_etn(td.amount()));
9140  picks.push_back(i);
9141  return picks;
9142  }
9143  }
9144 
9145  // then try to find two outputs
9146  // this could be made better by picking one of the outputs to be a small one, since those
9147  // are less useful since often below the needed etn, so if one can be used in a pair,
9148  // it gets rid of it for the future
9149  for (size_t i = 0; i < m_transfers.size(); ++i)
9150  {
9151  const transfer_details& td = m_transfers[i];
9152  if (!td.m_spent && !td.m_frozen && !td.m_key_image_partial && td.is_rct() && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
9153  {
9154  LOG_PRINT_L2("Considering input " << i << ", " << print_etn(td.amount()));
9155  for (size_t j = i + 1; j < m_transfers.size(); ++j)
9156  {
9157  const transfer_details& td2 = m_transfers[j];
9158  if (!td2.m_spent && !td2.m_frozen && !td.m_key_image_partial && td2.is_rct() && td.amount() + td2.amount() >= needed_etn && is_transfer_unlocked(td2) && td2.m_subaddr_index == td.m_subaddr_index)
9159  {
9160  // update our picks if those outputs are less related than any we
9161  // already found. If the same, don't update, and oldest suitable outputs
9162  // will be used in preference.
9163  float relatedness = get_output_relatedness(td, td2);
9164  LOG_PRINT_L2(" with input " << j << ", " << print_etn(td2.amount()) << ", relatedness " << relatedness);
9165  if (relatedness < current_output_relatdness)
9166  {
9167  // reset the current picks with those, and return them directly
9168  // if they're unrelated. If they are related, we'll end up returning
9169  // them if we find nothing better
9170  picks.clear();
9171  picks.push_back(i);
9172  picks.push_back(j);
9173  LOG_PRINT_L0("we could use " << i << " and " << j);
9174  if (relatedness == 0.0f)
9175  return picks;
9176  current_output_relatdness = relatedness;
9177  }
9178  }
9179  }
9180  }
9181  }
9182 
9183  return picks;
9184 }
9185 
9186 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
9187 {
9188  if (!use_rct)
9189  return false;
9190  if (n_transfers > 1)
9191  return false;
9192  if (unused_dust_indices.empty() && unused_transfers_indices.empty())
9193  return false;
9194  // we want at least one free rct output to avoid a corner case where
9195  // we'd choose a non rct output which doesn't have enough "siblings"
9196  // value-wise on the chain, and thus can't be mixed
9197  bool found = false;
9198  for (auto i: unused_dust_indices)
9199  {
9200  if (m_transfers[i].is_rct())
9201  {
9202  found = true;
9203  break;
9204  }
9205  }
9206  if (!found) for (auto i: unused_transfers_indices)
9207  {
9208  if (m_transfers[i].is_rct())
9209  {
9210  found = true;
9211  break;
9212  }
9213  }
9214  if (!found)
9215  return false;
9216  return true;
9217 }
9218 
9219 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
9220 {
9221  std::vector<size_t> indices;
9222  for (size_t n: unused_dust_indices)
9223  if (m_transfers[n].is_rct())
9224  indices.push_back(n);
9225  for (size_t n: unused_transfers_indices)
9226  if (m_transfers[n].is_rct())
9227  indices.push_back(n);
9228  return indices;
9229 }
9230 
9231 static uint32_t get_count_above(const std::vector<wallet2::transfer_details> &transfers, const std::vector<size_t> &indices, uint64_t threshold)
9232 {
9233  uint32_t count = 0;
9234  for (size_t idx: indices)
9235  if (transfers[idx].amount() >= threshold)
9236  ++count;
9237  return count;
9238 }
9239 
9240 bool wallet2::light_wallet_login(bool &new_address)
9241 {
9242  MDEBUG("Light wallet login request");
9243  m_light_wallet_connected = false;
9246  request.address = get_account().get_public_address_str(m_nettype);
9247  request.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
9248  // Always create account if it doesn't exist.
9249  request.create_account = true;
9250  m_daemon_rpc_mutex.lock();
9251  bool connected = invoke_http_json("/login", request, response, rpc_timeout, "POST");
9252  m_daemon_rpc_mutex.unlock();
9253  // MyMonero doesn't send any status message. OpenMonero does.
9254  m_light_wallet_connected = connected && (response.status.empty() || response.status == "success");
9255  new_address = response.new_address;
9256  MDEBUG("Status: " << response.status);
9257  MDEBUG("Reason: " << response.reason);
9258  MDEBUG("New wallet: " << response.new_address);
9259  if(m_light_wallet_connected)
9260  {
9261  // Clear old data on successful login.
9262  // m_transfers.clear();
9263  // m_payments.clear();
9264  // m_unconfirmed_payments.clear();
9265  }
9266  return m_light_wallet_connected;
9267 }
9268 
9269 bool wallet2::light_wallet_import_wallet_request(tools::COMMAND_RPC_IMPORT_WALLET_REQUEST::response &response)
9270 {
9271  MDEBUG("Light wallet import wallet request");
9273  oreq.address = get_account().get_public_address_str(m_nettype);
9274  oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
9275  m_daemon_rpc_mutex.lock();
9276  bool r = invoke_http_json("/import_wallet_request", oreq, response, rpc_timeout, "POST");
9277  m_daemon_rpc_mutex.unlock();
9278  THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "import_wallet_request");
9279 
9280 
9281  return true;
9282 }
9283 
9284 void wallet2::light_wallet_get_unspent_outs()
9285 {
9286  MDEBUG("Getting unspent outs");
9287 
9290 
9291  oreq.amount = "0";
9292  oreq.address = get_account().get_public_address_str(m_nettype);
9293  oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
9294  // openMonero specific
9295  oreq.dust_threshold = boost::lexical_cast<std::string>(::config::DEFAULT_DUST_THRESHOLD);
9296  // below are required by openMonero api - but are not used.
9297  oreq.mixin = 0;
9298  oreq.use_dust = true;
9299 
9300 
9301  m_daemon_rpc_mutex.lock();
9302  bool r = invoke_http_json("/get_unspent_outs", oreq, ores, rpc_timeout, "POST");
9303  m_daemon_rpc_mutex.unlock();
9305  THROW_WALLET_EXCEPTION_IF(ores.status == "error", error::wallet_internal_error, ores.reason);
9306 
9307  m_light_wallet_per_kb_fee = ores.per_kb_fee;
9308 
9309  std::unordered_map<crypto::hash,bool> transfers_txs;
9310  for(const auto &t: m_transfers)
9311  transfers_txs.emplace(t.m_txid,t.m_spent);
9312 
9313  MDEBUG("FOUND " << ores.outputs.size() <<" outputs");
9314 
9315  // return if no outputs found
9316  if(ores.outputs.empty())
9317  return;
9318 
9319  // Clear old outputs
9320  m_transfers.clear();
9321 
9322  for (const auto &o: ores.outputs) {
9323  bool spent = false;
9324  bool add_transfer = true;
9325  crypto::key_image unspent_key_image;
9326  crypto::public_key tx_public_key = AUTO_VAL_INIT(tx_public_key);
9327  THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, o.tx_pub_key), error::wallet_internal_error, "Invalid tx_pub_key field");
9328  string_tools::hex_to_pod(o.tx_pub_key, tx_public_key);
9329 
9330  for (const std::string &ski: o.spend_key_images) {
9331  spent = false;
9332 
9333  // Check if key image is ours
9335  string_tools::hex_to_pod(ski, unspent_key_image);
9336  if(light_wallet_key_image_is_ours(unspent_key_image, tx_public_key, o.index)){
9337  MTRACE("Output " << o.public_key << " is spent. Key image: " << ski);
9338  spent = true;
9339  break;
9340  } {
9341  MTRACE("Unspent output found. " << o.public_key);
9342  }
9343  }
9344 
9345  // Check if tx already exists in m_transfers.
9346  crypto::hash txid;
9347  crypto::public_key tx_pub_key;
9349  THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, o.tx_hash), error::wallet_internal_error, "Invalid tx_hash field");
9350  THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, o.public_key), error::wallet_internal_error, "Invalid public_key field");
9351  THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, o.tx_pub_key), error::wallet_internal_error, "Invalid tx_pub_key field");
9352  string_tools::hex_to_pod(o.tx_hash, txid);
9353  string_tools::hex_to_pod(o.public_key, public_key);
9354  string_tools::hex_to_pod(o.tx_pub_key, tx_pub_key);
9355 
9356  for(auto &t: m_transfers){
9357  if(t.get_public_key() == public_key) {
9358  t.m_spent = spent;
9359  add_transfer = false;
9360  break;
9361  }
9362  }
9363 
9364  if(!add_transfer)
9365  continue;
9366 
9367  m_transfers.push_back(boost::value_initialized<transfer_details>());
9368  transfer_details& td = m_transfers.back();
9369 
9370  td.m_block_height = o.height;
9371  td.m_global_output_index = o.global_index;
9372  td.m_txid = txid;
9373 
9374  // Add to extra
9375  add_tx_pub_key_to_extra(td.m_tx, tx_pub_key);
9376 
9377  td.m_key_image = unspent_key_image;
9378  td.m_key_image_known = !m_watch_only && !m_multisig;
9379  td.m_key_image_request = false;
9380  td.m_key_image_partial = m_multisig;
9381  td.m_amount = o.amount;
9382  td.m_pk_index = 0;
9383  td.m_internal_output_index = o.index;
9384  td.m_spent = spent;
9385  td.m_frozen = false;
9386 
9387  tx_out txout;
9388  txout.target = txout_to_key(public_key);
9389  txout.amount = td.m_amount;
9390 
9391  td.m_tx.vout.resize(td.m_internal_output_index + 1);
9392  td.m_tx.vout[td.m_internal_output_index] = txout;
9393 
9394  // Add unlock time and coinbase bool got from get_address_txs api call
9395  std::unordered_map<crypto::hash,address_tx>::const_iterator found = m_light_wallet_address_txs.find(txid);
9396  THROW_WALLET_EXCEPTION_IF(found == m_light_wallet_address_txs.end(), error::wallet_internal_error, "Lightwallet: tx not found in m_light_wallet_address_txs");
9397  bool miner_tx = found->second.m_coinbase;
9398  td.m_tx.unlock_time = found->second.m_unlock_time;
9399 
9400  if (!o.rct.empty())
9401  {
9402  // Coinbase tx's
9403  if(miner_tx)
9404  {
9405  td.m_mask = rct::identity();
9406  }
9407  else
9408  {
9409  // rct txs
9410  // decrypt rct mask, calculate commit hash and compare against blockchain commit hash
9411  rct::key rct_commit;
9412  light_wallet_parse_rct_str(o.rct, tx_pub_key, td.m_internal_output_index, td.m_mask, rct_commit, true);
9413  bool valid_commit = (rct_commit == rct::commit(td.amount(), td.m_mask));
9414  if(!valid_commit)
9415  {
9416  MDEBUG("output index: " << o.global_index);
9417  MDEBUG("mask: " + string_tools::pod_to_hex(td.m_mask));
9418  MDEBUG("calculated commit: " + string_tools::pod_to_hex(rct::commit(td.amount(), td.m_mask)));
9419  MDEBUG("expected commit: " + string_tools::pod_to_hex(rct_commit));
9420  MDEBUG("amount: " << td.amount());
9421  }
9422  THROW_WALLET_EXCEPTION_IF(!valid_commit, error::wallet_internal_error, "Lightwallet: rct commit hash mismatch!");
9423  }
9424  td.m_rct = true;
9425  }
9426  else
9427  {
9428  td.m_mask = rct::identity();
9429  td.m_rct = false;
9430  }
9431  if(!spent)
9432  set_unspent(m_transfers.size()-1);
9433  m_key_images[td.m_key_image] = m_transfers.size()-1;
9434  m_pub_keys[td.get_public_key()] = m_transfers.size()-1;
9435  }
9436 }
9437 
9438 bool wallet2::light_wallet_get_address_info(tools::COMMAND_RPC_GET_ADDRESS_INFO::response &response)
9439 {
9440  MTRACE(__FUNCTION__);
9441 
9443 
9444  request.address = get_account().get_public_address_str(m_nettype);
9445  request.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
9446  m_daemon_rpc_mutex.lock();
9447  bool r = invoke_http_json("/get_address_info", request, response, rpc_timeout, "POST");
9448  m_daemon_rpc_mutex.unlock();
9450  // TODO: Validate result
9451  return true;
9452 }
9453 
9454 void wallet2::light_wallet_get_address_txs()
9455 {
9456  MDEBUG("Refreshing light wallet");
9457 
9460 
9461  ireq.address = get_account().get_public_address_str(m_nettype);
9462  ireq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
9463  m_daemon_rpc_mutex.lock();
9464  bool r = invoke_http_json("/get_address_txs", ireq, ires, rpc_timeout, "POST");
9465  m_daemon_rpc_mutex.unlock();
9467  //OpenMonero sends status=success, Mymonero doesn't.
9468  THROW_WALLET_EXCEPTION_IF((!ires.status.empty() && ires.status != "success"), error::no_connection_to_daemon, "get_address_txs");
9469 
9470 
9471  // Abort if no transactions
9472  if(ires.transactions.empty())
9473  return;
9474 
9475  // Create searchable vectors
9476  std::vector<crypto::hash> payments_txs;
9477  for(const auto &p: m_payments)
9478  payments_txs.push_back(p.second.m_tx_hash);
9479  std::vector<crypto::hash> unconfirmed_payments_txs;
9480  for(const auto &up: m_unconfirmed_payments)
9481  unconfirmed_payments_txs.push_back(up.second.m_pd.m_tx_hash);
9482 
9483  // for balance calculation
9484  uint64_t wallet_total_sent = 0;
9485  // txs in pool
9486  std::vector<crypto::hash> pool_txs;
9487 
9488  for (const auto &t: ires.transactions) {
9489  const uint64_t total_received = t.total_received;
9490  uint64_t total_sent = t.total_sent;
9491 
9492  // Check key images - subtract fake outputs from total_sent
9493  for(const auto &so: t.spent_outputs)
9494  {
9495  crypto::public_key tx_public_key;
9497  THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, so.tx_pub_key), error::wallet_internal_error, "Invalid tx_pub_key field");
9498  THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, so.key_image), error::wallet_internal_error, "Invalid key_image field");
9499  string_tools::hex_to_pod(so.tx_pub_key, tx_public_key);
9500  string_tools::hex_to_pod(so.key_image, key_image);
9501 
9502  if(!light_wallet_key_image_is_ours(key_image, tx_public_key, so.out_index)) {
9503  THROW_WALLET_EXCEPTION_IF(so.amount > t.total_sent, error::wallet_internal_error, "Lightwallet: total sent is negative!");
9504  total_sent -= so.amount;
9505  }
9506  }
9507 
9508  // Do not add tx if empty.
9509  if(total_sent == 0 && total_received == 0)
9510  continue;
9511 
9512  crypto::hash payment_id = null_hash;
9513  crypto::hash tx_hash;
9514 
9515  THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, t.payment_id), error::wallet_internal_error, "Invalid payment_id field");
9517  string_tools::hex_to_pod(t.payment_id, payment_id);
9518  string_tools::hex_to_pod(t.hash, tx_hash);
9519 
9520  // lightwallet specific info
9521  bool incoming = (total_received > total_sent);
9523  address_tx.m_tx_hash = tx_hash;
9524  address_tx.m_incoming = incoming;
9525  address_tx.m_amount = incoming ? total_received - total_sent : total_sent - total_received;
9526  address_tx.m_fee = 0; // TODO
9527  address_tx.m_block_height = t.height;
9528  address_tx.m_unlock_time = t.unlock_time;
9529  address_tx.m_timestamp = t.timestamp;
9530  address_tx.m_coinbase = t.coinbase;
9531  address_tx.m_mempool = t.mempool;
9532  m_light_wallet_address_txs.emplace(tx_hash,address_tx);
9533 
9534  // populate data needed for history (m_payments, m_unconfirmed_payments, m_confirmed_txs)
9535  // INCOMING transfers
9536  if(total_received > total_sent) {
9537  payment_details payment;
9538  payment.m_tx_hash = tx_hash;
9539  payment.m_amount = total_received - total_sent;
9540  payment.m_fee = 0; // TODO
9541  payment.m_block_height = t.height;
9542  payment.m_unlock_time = t.unlock_time;
9543  payment.m_timestamp = t.timestamp;
9544  payment.m_coinbase = t.coinbase;
9545 
9546  if (t.mempool) {
9547  if (std::find(unconfirmed_payments_txs.begin(), unconfirmed_payments_txs.end(), tx_hash) == unconfirmed_payments_txs.end()) {
9548  pool_txs.push_back(tx_hash);
9549  // assume false as we don't get that info from the light wallet server
9550  crypto::hash payment_id;
9551  THROW_WALLET_EXCEPTION_IF(!epee::string_tools::hex_to_pod(t.payment_id, payment_id),
9552  error::wallet_internal_error, "Failed to parse payment id");
9553  emplace_or_replace(m_unconfirmed_payments, payment_id, pool_payment_details{payment, false, false});
9554  if (0 != m_callback) {
9555  m_callback->on_lw_unconfirmed_etn_received(t.height, payment.m_tx_hash, payment.m_amount);
9556  }
9557  }
9558  } else {
9559  if (std::find(payments_txs.begin(), payments_txs.end(), tx_hash) == payments_txs.end()) {
9560  m_payments.emplace(tx_hash, payment);
9561  if (0 != m_callback) {
9562  m_callback->on_lw_etn_received(t.height, payment.m_tx_hash, payment.m_amount);
9563  }
9564  }
9565  }
9566  // Outgoing transfers
9567  } else {
9568  uint64_t amount_sent = total_sent - total_received;
9569  cryptonote::transaction dummy_tx; // not used by light wallet
9570  // increase wallet total sent
9571  wallet_total_sent += total_sent;
9572  if (t.mempool)
9573  {
9574  // Handled by add_unconfirmed_tx in commit_tx
9575  // If sent from another wallet instance we need to add it
9576  if(m_unconfirmed_txs.find(tx_hash) == m_unconfirmed_txs.end())
9577  {
9579  utd.m_amount_in = amount_sent;
9580  utd.m_amount_out = amount_sent;
9581  utd.m_change = 0;
9582  utd.m_payment_id = payment_id;
9583  utd.m_timestamp = t.timestamp;
9584  utd.m_state = wallet2::unconfirmed_transfer_details::pending;
9585  m_unconfirmed_txs.emplace(tx_hash,utd);
9586  }
9587  }
9588  else
9589  {
9590  // Only add if new
9591  auto confirmed_tx = m_confirmed_txs.find(tx_hash);
9592  if(confirmed_tx == m_confirmed_txs.end()) {
9593  // tx is added to m_unconfirmed_txs - move to confirmed
9594  if(m_unconfirmed_txs.find(tx_hash) != m_unconfirmed_txs.end())
9595  {
9596  process_unconfirmed(tx_hash, dummy_tx, t.height);
9597  }
9598  // Tx sent by another wallet instance
9599  else
9600  {
9602  ctd.m_amount_in = amount_sent;
9603  ctd.m_amount_out = amount_sent;
9604  ctd.m_change = 0;
9605  ctd.m_payment_id = payment_id;
9606  ctd.m_block_height = t.height;
9607  ctd.m_timestamp = t.timestamp;
9608  m_confirmed_txs.emplace(tx_hash,ctd);
9609  }
9610  if (0 != m_callback)
9611  {
9612  m_callback->on_lw_etn_spent(t.height, tx_hash, amount_sent);
9613  }
9614  }
9615  // If not new - check the amount and update if necessary.
9616  // when sending a tx to same wallet the receiving amount has to be credited
9617  else
9618  {
9619  if(confirmed_tx->second.m_amount_in != amount_sent || confirmed_tx->second.m_amount_out != amount_sent)
9620  {
9621  MDEBUG("Adjusting amount sent/received for tx: <" + t.hash + ">. Is tx sent to own wallet? " << print_etn(amount_sent) << " != " << print_etn(confirmed_tx->second.m_amount_in));
9622  confirmed_tx->second.m_amount_in = amount_sent;
9623  confirmed_tx->second.m_amount_out = amount_sent;
9624  confirmed_tx->second.m_change = 0;
9625  }
9626  }
9627  }
9628  }
9629  }
9630  // TODO: purge old unconfirmed_txs
9631  remove_obsolete_pool_txs(pool_txs);
9632 
9633  // Calculate wallet balance
9634  m_light_wallet_balance = ires.total_received-wallet_total_sent;
9635  // MyMonero doesn't send unlocked balance
9636  if(ires.total_received_unlocked > 0)
9637  m_light_wallet_unlocked_balance = ires.total_received_unlocked-wallet_total_sent;
9638  else
9639  m_light_wallet_unlocked_balance = m_light_wallet_balance;
9640 }
9641 
9642 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
9643 {
9644  // rct string is empty if output is non RCT
9645  if (rct_string.empty())
9646  return false;
9647  // rct_string is a string with length 64+64+64 (<rct commit> + <encrypted mask> + <rct amount>)
9648  rct::key encrypted_mask;
9649  std::string rct_commit_str = rct_string.substr(0,64);
9650  std::string encrypted_mask_str = rct_string.substr(64,64);
9651  THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, rct_commit_str), error::wallet_internal_error, "Invalid rct commit hash: " + rct_commit_str);
9652  THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, encrypted_mask_str), error::wallet_internal_error, "Invalid rct mask: " + encrypted_mask_str);
9653  string_tools::hex_to_pod(rct_commit_str, rct_commit);
9654  string_tools::hex_to_pod(encrypted_mask_str, encrypted_mask);
9655  if (decrypt) {
9656  // Decrypt the mask
9657  crypto::key_derivation derivation;
9658  bool r = generate_key_derivation(tx_pub_key, get_account().get_keys().m_view_secret_key, derivation);
9659  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation");
9660  crypto::secret_key scalar;
9661  crypto::derivation_to_scalar(derivation, internal_output_index, scalar);
9662  sc_sub(decrypted_mask.bytes,encrypted_mask.bytes,rct::hash_to_scalar(rct::sk2rct(scalar)).bytes);
9663  }
9664  return true;
9665 }
9666 
9667 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)
9668 {
9669  // Lookup key image from cache
9670  std::map<uint64_t, crypto::key_image> index_keyimage_map;
9671  std::unordered_map<crypto::public_key, std::map<uint64_t, crypto::key_image> >::const_iterator found_pub_key = m_key_image_cache.find(tx_public_key);
9672  if(found_pub_key != m_key_image_cache.end()) {
9673  // pub key found. key image for index cached?
9674  index_keyimage_map = found_pub_key->second;
9675  std::map<uint64_t,crypto::key_image>::const_iterator index_found = index_keyimage_map.find(out_index);
9676  if(index_found != index_keyimage_map.end())
9677  return key_image == index_found->second;
9678  }
9679 
9680  // Not in cache - calculate key image
9681  crypto::key_image calculated_key_image;
9682  cryptonote::keypair in_ephemeral;
9683 
9684  // Subaddresses aren't supported in mymonero/openmonero yet. Roll out the original scheme:
9685  // compute D = a*R
9686  // compute P = Hs(D || i)*G + B
9687  // compute x = Hs(D || i) + b (and check if P==x*G)
9688  // compute I = x*Hp(P)
9689  const account_keys& ack = get_account().get_keys();
9690  crypto::key_derivation derivation;
9691  bool r = crypto::generate_key_derivation(tx_public_key, ack.m_view_secret_key, derivation);
9692  CHECK_AND_ASSERT_MES(r, false, "failed to generate_key_derivation(" << tx_public_key << ", " << ack.m_view_secret_key << ")");
9693 
9694  r = crypto::derive_public_key(derivation, out_index, ack.m_account_address.m_spend_public_key, in_ephemeral.pub);
9695  CHECK_AND_ASSERT_MES(r, false, "failed to derive_public_key (" << derivation << ", " << out_index << ", " << ack.m_account_address.m_spend_public_key << ")");
9696 
9697  crypto::derive_secret_key(derivation, out_index, ack.m_spend_secret_key, in_ephemeral.sec);
9698  crypto::public_key out_pkey_test;
9699  r = crypto::secret_key_to_public_key(in_ephemeral.sec, out_pkey_test);
9700  CHECK_AND_ASSERT_MES(r, false, "failed to secret_key_to_public_key(" << in_ephemeral.sec << ")");
9701  CHECK_AND_ASSERT_MES(in_ephemeral.pub == out_pkey_test, false, "derived secret key doesn't match derived public key");
9702 
9703  crypto::generate_key_image(in_ephemeral.pub, in_ephemeral.sec, calculated_key_image);
9704 
9705  index_keyimage_map.emplace(out_index, calculated_key_image);
9706  m_key_image_cache.emplace(tx_public_key, index_keyimage_map);
9707  return key_image == calculated_key_image;
9708 }
9709 
9710 // Another implementation of transaction creation that is hopefully better
9711 // While there is anything left to pay, it goes through random outputs and tries
9712 // to fill the next destination/amount. If it fully fills it, it will use the
9713 // remainder to try to fill the next one as well.
9714 // The tx size if roughly estimated as a linear function of only inputs, and a
9715 // new tx will be created when that size goes above a given fraction of the
9716 // max tx size. At that point, more outputs may be added if the fee cannot be
9717 // satisfied.
9718 // If the next output in the next tx would go to the same destination (ie, we
9719 // cut off at a tx boundary in the middle of paying a given destination), the
9720 // fee will be carved out of the current input if possible, to avoid having to
9721 // add another output just for the fee and getting change.
9722 // This system allows for sending (almost) the entire balance, since it does
9723 // not generate spurious change in all txes, thus decreasing the instantaneous
9724 // usable balance.
9725 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)
9726 {
9727  //ensure device is let in NONE mode in any case
9728  hw::device &hwdev = m_account.get_device();
9729  boost::unique_lock<hw::device> hwdev_lock (hwdev);
9730  hw::reset_mode rst(hwdev);
9731 
9732  //destinations in the full etn-address format
9733  auto original_dsts = dsts;
9734 
9735  if(m_light_wallet) {
9736  // Populate m_transfers
9737  light_wallet_get_unspent_outs();
9738  }
9739 
9740  uint8_t tx_version = this->public_transactions_required() ? 3 : 1;
9741  //vector of pairs of <subaddr minor index : vector<transfer indexes for that subaddr index inside m_transfers>>
9742  std::vector<std::pair<uint32_t, std::vector<size_t>>> unused_transfers_indices_per_subaddr;
9743  std::vector<std::pair<uint32_t, std::vector<size_t>>> unused_dust_indices_per_subaddr;
9744  uint64_t needed_etn;
9745  uint64_t accumulated_fee, accumulated_outputs, accumulated_change;
9746  struct TX {
9747  std::vector<size_t> selected_transfers;
9748  std::vector<cryptonote::tx_destination_entry> dsts;
9750  pending_tx ptx;
9751  size_t weight;
9752  uint64_t needed_fee;
9753  std::vector<std::vector<tools::wallet2::get_outs_entry>> outs;
9754 
9755  TX() : weight(0), needed_fee(0) {}
9756 
9757  void add(const cryptonote::tx_destination_entry &de, uint64_t amount, unsigned int original_output_index, bool merge_destinations) {
9758  if (merge_destinations)
9759  {
9760  std::vector<cryptonote::tx_destination_entry>::iterator i;
9761  i = std::find_if(dsts.begin(), dsts.end(), [&](const cryptonote::tx_destination_entry &d) { return !memcmp (&d.addr, &de.addr, sizeof(de.addr)); });
9762  if (i == dsts.end())
9763  {
9764  dsts.push_back(de);
9765  i = dsts.end() - 1;
9766  i->amount = 0;
9767  }
9768  i->amount += amount;
9769  }
9770  else
9771  {
9772  THROW_WALLET_EXCEPTION_IF(original_output_index > dsts.size(), error::wallet_internal_error,
9773  std::string("original_output_index too large: ") + std::to_string(original_output_index) + " > " + std::to_string(dsts.size()));
9774  if (original_output_index == dsts.size())
9775  {
9776  dsts.push_back(de);
9777  dsts.back().amount = 0;
9778  }
9779  THROW_WALLET_EXCEPTION_IF(memcmp(&dsts[original_output_index].addr, &de.addr, sizeof(de.addr)), error::wallet_internal_error, "Mismatched destination address");
9780  dsts[original_output_index].amount += amount;
9781  }
9782  }
9783  };
9784  std::vector<TX> txes;
9785  bool adding_fee; // true if new outputs go towards fee, rather than destinations
9786 
9787  uint64_t needed_fee, available_for_fee = 0;
9788  uint64_t upper_transaction_weight_limit;
9789  uint64_t extra_bytes = extra.size();
9790  switch(hwdev.get_type()){
9791 
9792  // Normal Software Limit
9793  case 0 : upper_transaction_weight_limit = get_upper_transaction_weight_limit(); break;
9794 
9795  // Ledger NanoS: ~3.3kB of RAM for app variables. Give a bit of buffer (300) for other variables on device
9796  // and subtract the size of the extra.
9797  // because in the Ledger app this still lives on the stack at the same time the entire prefix does.
9798  // This is a rough rule of thumb estimate... The logic can be updated at a later stage.
9799  // Right now we just need to make we don't fail to build any tx (and split the tx to avoid this happening)
9800  case 1 : upper_transaction_weight_limit = 3000 - extra_bytes; break;
9801 
9802  //Trezor limit set at the same as Ledger for the time being.
9803  case 2 : upper_transaction_weight_limit = 3000 - extra_bytes; break;
9804  //Future hw devices
9805  }
9806  const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
9807  const bool use_rct = use_fork_rules(HF_VERSION_ENABLE_RCT, 0);
9808  const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
9809  const rct::RCTConfig rct_config {
9811  bulletproof ? (use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1) : 0
9812  };
9813 
9814  const uint64_t base_fee = get_base_fee();
9815  const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
9816  const uint64_t fee_quantization_mask = get_fee_quantization_mask();
9817 
9818  // throw if attempting a transaction with no destinations
9820 
9821  // calculate total amount being sent to all destinations
9822  // throw if total amount overflows uint64_t
9823  needed_etn = 0;
9824  for(auto& dt: dsts)
9825  {
9827  needed_etn += dt.amount;
9828  LOG_PRINT_L2("transfer: adding " << print_etn(dt.amount) << ", for a total of " << print_etn (needed_etn));
9829  THROW_WALLET_EXCEPTION_IF(needed_etn < dt.amount, error::tx_sum_overflow, dsts, 0, m_nettype);
9830  }
9831 
9832  // throw if attempting a transaction with no etn
9834 
9835  std::map<uint32_t, std::pair<uint64_t, uint64_t>> unlocked_balance_per_subaddr = unlocked_balance_per_subaddress(subaddr_account, tx_version >= 3);
9836  std::map<uint32_t, uint64_t> balance_per_subaddr = balance_per_subaddress(subaddr_account, tx_version >= 3);
9837 
9838  if (subaddr_indices.empty()) // "index=<N1>[,<N2>,...]" wasn't specified -> use all the indices with non-zero unlocked balance
9839  {
9840  for (const auto& i : balance_per_subaddr)
9841  subaddr_indices.insert(i.first);
9842  }
9843 
9844  // early out if we know we can't make it anyway
9845  // we could also check for being within FEE_PER_KB, but if the fee calculation
9846  // ever changes, this might be missed, so let this go through
9847  uint64_t min_fee = (fee_multiplier * base_fee * estimate_tx_size(use_rct, 1, fake_outs_count, 2, extra.size(), bulletproof));
9848  //Whilst we're still dealing with fee/kb:
9849  if (!use_per_byte_fee){
9850  min_fee /= 1000;
9851  if(min_fee == 0){min_fee += 10;}
9852  }
9853  uint64_t balance_subtotal = 0;
9854  uint64_t unlocked_balance_subtotal = 0;
9855  for (uint32_t index_minor : subaddr_indices)
9856  {
9857  balance_subtotal += balance_per_subaddr[index_minor];
9858  unlocked_balance_subtotal += unlocked_balance_per_subaddr[index_minor].first;
9859  }
9860  THROW_WALLET_EXCEPTION_IF(needed_etn + min_fee > balance_subtotal, error::not_enough_etn,
9861  balance_subtotal, needed_etn, 0);
9862  // first check overall balance is enough, then unlocked one, so we throw distinct exceptions
9863  THROW_WALLET_EXCEPTION_IF(needed_etn + min_fee > unlocked_balance_subtotal, error::not_enough_unlocked_etn,
9864  unlocked_balance_subtotal, needed_etn, 0);
9865 
9866  for (uint32_t i : subaddr_indices)
9867  LOG_PRINT_L2("Candidate subaddress index for spending: " << i);
9868 
9869  // determine threshold for fractional amount
9870  const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof);
9871  const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof);
9872  THROW_WALLET_EXCEPTION_IF(tx_weight_one_ring > tx_weight_two_rings, error::wallet_internal_error, "Estimated tx weight with 1 input is larger than with 2 inputs!");
9873  const size_t tx_weight_per_ring = tx_weight_two_rings - tx_weight_one_ring;
9874  const uint64_t fractional_threshold = (fee_multiplier * base_fee * tx_weight_per_ring) / (use_per_byte_fee ? 1 : 1024);
9875 
9876  // gather all dust and non-dust outputs belonging to specified subaddresses
9877  size_t num_nondust_outputs = 0;
9878  size_t num_dust_outputs = 0;
9879  for (size_t i = 0; i < m_transfers.size(); ++i)
9880  {
9881  const transfer_details& td = m_transfers[i];
9882  if (m_ignore_fractional_outputs && td.amount() < fractional_threshold)
9883  {
9884  MDEBUG("Ignoring output " << i << " of amount " << print_etn(td.amount()) << " which is below threshold " << print_etn(fractional_threshold));
9885  continue;
9886  }
9887  if (!td.m_spent && !td.m_frozen && (!td.m_key_image_partial || td.m_tx.version > 1) && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
9888  {
9889  if((tx_version < 3 && td.m_tx.version > 1) || (tx_version >= 3 && td.m_tx.version == 1))
9890  continue;
9891 
9892  const uint32_t index_minor = td.m_subaddr_index.minor;
9893  auto find_predicate = [&index_minor](const std::pair<uint32_t, std::vector<size_t>>& x) { return x.first == index_minor; };
9894  if ((td.is_rct()) || is_valid_decomposed_amount(td.amount()))
9895  {
9896  auto found = std::find_if(unused_transfers_indices_per_subaddr.begin(), unused_transfers_indices_per_subaddr.end(), find_predicate);
9897  if (found == unused_transfers_indices_per_subaddr.end())
9898  {
9899  unused_transfers_indices_per_subaddr.push_back({index_minor, {i}});
9900  }
9901  else
9902  {
9903  found->second.push_back(i);
9904  }
9905  ++num_nondust_outputs;
9906  }
9907  else
9908  {
9909  auto found = std::find_if(unused_dust_indices_per_subaddr.begin(), unused_dust_indices_per_subaddr.end(), find_predicate);
9910  if (found == unused_dust_indices_per_subaddr.end())
9911  {
9912  unused_dust_indices_per_subaddr.push_back({index_minor, {i}});
9913  }
9914  else
9915  {
9916  found->second.push_back(i);
9917  }
9918  ++num_dust_outputs;
9919  }
9920  }
9921  }
9922 
9923  // sort output indices
9924  {
9925  auto sort_predicate = [&unlocked_balance_per_subaddr] (const std::pair<uint32_t, std::vector<size_t>>& x, const std::pair<uint32_t, std::vector<size_t>>& y)
9926  {
9927  return unlocked_balance_per_subaddr[x.first].first > unlocked_balance_per_subaddr[y.first].first;
9928  };
9929  std::sort(unused_transfers_indices_per_subaddr.begin(), unused_transfers_indices_per_subaddr.end(), sort_predicate);
9930  std::sort(unused_dust_indices_per_subaddr.begin(), unused_dust_indices_per_subaddr.end(), sort_predicate);
9931  }
9932 
9933  LOG_PRINT_L2("Starting with " << num_nondust_outputs << " non-dust outputs and " << num_dust_outputs << " dust outputs");
9934 
9935  if (unused_dust_indices_per_subaddr.empty() && unused_transfers_indices_per_subaddr.empty())
9936  return std::vector<wallet2::pending_tx>();
9937 
9938  // if empty, put dummy entry so that the front can be referenced later in the loop
9939  if (unused_dust_indices_per_subaddr.empty())
9940  unused_dust_indices_per_subaddr.push_back({});
9941  if (unused_transfers_indices_per_subaddr.empty())
9942  unused_transfers_indices_per_subaddr.push_back({});
9943 
9944  // start with an empty tx
9945  txes.push_back(TX());
9946  accumulated_fee = 0;
9947  accumulated_outputs = 0;
9948  accumulated_change = 0;
9949  adding_fee = false;
9950  needed_fee = 0;
9951  std::vector<std::vector<tools::wallet2::get_outs_entry>> outs;
9952 
9953  // for rct, since we don't see the amounts, we will try to make all transactions
9954  // look the same, with 1 or 2 inputs, and 2 outputs. One input is preferable, as
9955  // this prevents linking to another by provenance analysis, but two is ok if we
9956  // try to pick outputs not from the same block. We will get two outputs, one for
9957  // the destination, and one for change.
9958  LOG_PRINT_L2("checking preferred");
9959  std::vector<size_t> preferred_inputs;
9960  uint64_t rct_outs_needed = 2 * (fake_outs_count + 1);
9961  rct_outs_needed += 100; // some fudge factor since we don't know how many are locked
9962  if (use_rct)
9963  {
9964  // this is used to build a tx that's 1 or 2 inputs, and 2 outputs, which
9965  // will get us a known fee.
9966  uint64_t estimated_fee = estimate_fee(use_per_byte_fee, use_rct, 2, fake_outs_count, 2, extra.size(), bulletproof, base_fee, fee_multiplier, fee_quantization_mask);
9967  preferred_inputs = pick_preferred_rct_inputs(needed_etn + estimated_fee, subaddr_account, subaddr_indices);
9968  if (!preferred_inputs.empty())
9969  {
9970  string s;
9971  for (auto i: preferred_inputs) s += boost::lexical_cast<std::string>(i) + " (" + print_etn(m_transfers[i].amount()) + ") ";
9972  LOG_PRINT_L1("Found preferred rct inputs for rct tx: " << s);
9973 
9974  // bring the list of available outputs stored by the same subaddress index to the front of the list
9975  uint32_t index_minor = m_transfers[preferred_inputs[0]].m_subaddr_index.minor;
9976  for (size_t i = 1; i < unused_transfers_indices_per_subaddr.size(); ++i)
9977  {
9978  if (unused_transfers_indices_per_subaddr[i].first == index_minor)
9979  {
9980  std::swap(unused_transfers_indices_per_subaddr[0], unused_transfers_indices_per_subaddr[i]);
9981  break;
9982  }
9983  }
9984  for (size_t i = 1; i < unused_dust_indices_per_subaddr.size(); ++i)
9985  {
9986  if (unused_dust_indices_per_subaddr[i].first == index_minor)
9987  {
9988  std::swap(unused_dust_indices_per_subaddr[0], unused_dust_indices_per_subaddr[i]);
9989  break;
9990  }
9991  }
9992  }
9993  }
9994  LOG_PRINT_L2("done checking preferred");
9995 
9996  // while:
9997  // - we have something to send
9998  // - or we need to gather more fee
9999  // - or we have just one input in that tx, which is rct (to try and make all/most rct txes 2/2)
10000  unsigned int original_output_index = 0;
10001  std::vector<size_t>* unused_transfers_indices = &unused_transfers_indices_per_subaddr[0].second;
10002  std::vector<size_t>* unused_dust_indices = &unused_dust_indices_per_subaddr[0].second;
10003 
10005  while ((!dsts.empty() && dsts[0].amount > 0) || adding_fee || !preferred_inputs.empty() || should_pick_a_second_output(use_rct, txes.back().selected_transfers.size(), *unused_transfers_indices, *unused_dust_indices)) {
10006  TX &tx = txes.back();
10007 
10008  LOG_PRINT_L2("Start of loop with " << unused_transfers_indices->size() << " " << unused_dust_indices->size() << ", tx.dsts.size() " << tx.dsts.size());
10009  LOG_PRINT_L2("unused_transfers_indices: " << strjoin(*unused_transfers_indices, " "));
10010  LOG_PRINT_L2("unused_dust_indices: " << strjoin(*unused_dust_indices, " "));
10011  LOG_PRINT_L2("dsts size " << dsts.size() << ", first " << (dsts.empty() ? "-" : cryptonote::print_etn(dsts[0].amount)));
10012  LOG_PRINT_L2("adding_fee " << adding_fee << ", use_rct " << use_rct);
10013 
10014  // if we need to spend etn and don't have any left, we fail
10015  if (unused_dust_indices->empty() && unused_transfers_indices->empty()) {
10016  LOG_PRINT_L2("No more outputs to choose from");
10017  THROW_WALLET_EXCEPTION_IF(1, error::tx_not_possible, unlocked_balance(subaddr_account, tx_version >= 3), needed_etn, accumulated_fee + needed_fee);
10018  }
10019 
10020  // get a random unspent output and use it to pay part (or all) of the current destination (and maybe next one, etc)
10021  // This could be more clever, but maybe at the cost of making probabilistic inferences easier
10022  size_t idx;
10023  if (!preferred_inputs.empty()) {
10024  idx = pop_back(preferred_inputs);
10025  pop_if_present(*unused_transfers_indices, idx);
10026  pop_if_present(*unused_dust_indices, idx);
10027  } else if ((dsts.empty() || dsts[0].amount == 0) && !adding_fee) {
10028  // the "make rct txes 2/2" case - we pick a small value output to "clean up" the wallet too
10029  std::vector<size_t> indices = get_only_rct(*unused_dust_indices, *unused_transfers_indices);
10030  idx = pop_best_value(indices, tx.selected_transfers, true);
10031 
10032  // we might not want to add it if it's a large output and we don't have many left
10033  uint64_t min_output_value = m_min_output_value;
10034  uint32_t min_output_count = m_min_output_count;
10035  if (min_output_value == 0 && min_output_count == 0)
10036  {
10037  min_output_value = DEFAULT_MIN_OUTPUT_VALUE;
10038  min_output_count = DEFAULT_MIN_OUTPUT_COUNT;
10039  }
10040  if (m_transfers[idx].amount() >= min_output_value) {
10041  if (get_count_above(m_transfers, *unused_transfers_indices, min_output_value) < min_output_count) {
10042  LOG_PRINT_L2("Second output was not strictly needed, and we're running out of outputs above " << print_etn(min_output_value) << ", not adding");
10043  break;
10044  }
10045  }
10046 
10047  // since we're trying to add a second output which is not strictly needed,
10048  // we only add it if it's unrelated enough to the first one
10049  float relatedness = get_output_relatedness(m_transfers[idx], m_transfers[tx.selected_transfers.front()]);
10050  if (relatedness > SECOND_OUTPUT_RELATEDNESS_THRESHOLD)
10051  {
10052  LOG_PRINT_L2("Second output was not strictly needed, and relatedness " << relatedness << ", not adding");
10053  break;
10054  }
10055  pop_if_present(*unused_transfers_indices, idx);
10056  pop_if_present(*unused_dust_indices, idx);
10057  } else
10058  idx = pop_best_value(unused_transfers_indices->empty() ? *unused_dust_indices : *unused_transfers_indices, tx.selected_transfers);
10059 
10060  const transfer_details &td = m_transfers[idx];
10061  LOG_PRINT_L2("Picking output " << idx << ", amount " << print_etn(td.amount()) << ", ki " << td.m_key_image);
10062 
10063  // add this output to the list to spend
10064  tx.selected_transfers.push_back(idx);
10065  uint64_t available_amount = td.amount();
10066  accumulated_outputs += available_amount;
10067 
10068  // clear any fake outs we'd already gathered, since we'll need a new set
10069  outs.clear();
10070 
10071  if (adding_fee)
10072  {
10073  LOG_PRINT_L2("We need more fee, adding it to fee");
10074  available_for_fee += available_amount;
10075  }
10076  else
10077  {
10078  while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof) < TX_WEIGHT_TARGET(upper_transaction_weight_limit))
10079  {
10080  // we can fully pay that destination
10081  LOG_PRINT_L2("We can fully pay " << get_account_address_as_str(m_nettype, dsts[0].is_subaddress, dsts[0].addr) <<
10082  " for " << print_etn(dsts[0].amount));
10083  tx.add(dsts[0], dsts[0].amount, original_output_index, m_merge_destinations);
10084  available_amount -= dsts[0].amount;
10085  dsts[0].amount = 0;
10086  pop_index(dsts, 0);
10087  ++original_output_index;
10088  }
10089 
10090  if (available_amount > 0 && !dsts.empty() && estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof) < TX_WEIGHT_TARGET(upper_transaction_weight_limit)) {
10091  // we can partially fill that destination
10092  LOG_PRINT_L2("We can partially pay " << get_account_address_as_str(m_nettype, dsts[0].is_subaddress, dsts[0].addr) <<
10093  " for " << print_etn(available_amount) << "/" << print_etn(dsts[0].amount));
10094  tx.add(dsts[0], available_amount, original_output_index, m_merge_destinations);
10095  dsts[0].amount -= available_amount;
10096  available_amount = 0;
10097  }
10098  }
10099 
10100  // here, check if we need to sent tx and start a new one
10101  LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit "
10102  << upper_transaction_weight_limit);
10103  bool try_tx = false;
10104  // if we have preferred picks, but haven't yet used all of them, continue
10105  if (preferred_inputs.empty())
10106  {
10107  if (adding_fee)
10108  {
10109  /* might not actually be enough if adding this output bumps size to next kB, but we need to try */
10110  try_tx = available_for_fee >= needed_fee;
10111  }
10112  else
10113  {
10114  const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof);
10115  try_tx = dsts.empty() || (estimated_rct_tx_weight >= TX_WEIGHT_TARGET(upper_transaction_weight_limit));
10116  THROW_WALLET_EXCEPTION_IF(try_tx && tx.dsts.empty(), error::tx_too_big, estimated_rct_tx_weight, upper_transaction_weight_limit);
10117  }
10118  }
10119 
10120  if (try_tx) {
10121  cryptonote::transaction test_tx;
10122  test_tx.version = tx_version;
10123  pending_tx test_ptx;
10124 
10125  needed_fee = estimate_fee(use_per_byte_fee, use_rct ,tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, base_fee, fee_multiplier, fee_quantization_mask);
10126 
10127  uint64_t inputs = 0, outputs = needed_fee;
10128  for (size_t idx: tx.selected_transfers) inputs += m_transfers[idx].amount();
10129  for (const auto &o: tx.dsts) outputs += o.amount;
10130 
10131  if (inputs < outputs)
10132  {
10133  LOG_PRINT_L2("We don't have enough for the basic fee, switching to adding_fee");
10134  adding_fee = true;
10135  goto skip_tx;
10136  }
10137 
10138  LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " outputs and " <<
10139  tx.selected_transfers.size() << " inputs");
10140 
10141  transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
10143  auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
10144  needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
10145  available_for_fee = test_ptx.fee + test_ptx.change_dts.amount + (!test_ptx.dust_added_to_fee ? test_ptx.dust : 0);
10146  LOG_PRINT_L2("Made a " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_etn(available_for_fee) << " available for fee (" <<
10147  print_etn(needed_fee) << " needed)");
10148 
10149  if (needed_fee > available_for_fee && !dsts.empty() && dsts[0].amount > 0)
10150  {
10151  // we don't have enough for the fee, but we've only partially paid the current address,
10152  // so we can take the fee from the paid amount, since we'll have to make another tx anyway
10153  std::vector<cryptonote::tx_destination_entry>::iterator i;
10154  i = std::find_if(tx.dsts.begin(), tx.dsts.end(),
10155  [&](const cryptonote::tx_destination_entry &d) { return !memcmp (&d.addr, &dsts[0].addr, sizeof(dsts[0].addr)); });
10156  THROW_WALLET_EXCEPTION_IF(i == tx.dsts.end(), error::wallet_internal_error, "paid address not found in outputs");
10157  if (i->amount > needed_fee)
10158  {
10159  uint64_t new_paid_amount = i->amount /*+ test_ptx.fee*/ - needed_fee;
10160  LOG_PRINT_L2("Adjusting amount paid to " << get_account_address_as_str(m_nettype, i->is_subaddress, i->addr) << " from " <<
10161  print_etn(i->amount) << " to " << print_etn(new_paid_amount) << " to accommodate " <<
10162  print_etn(needed_fee) << " fee");
10163  dsts[0].amount += i->amount - new_paid_amount;
10164  i->amount = new_paid_amount;
10165  test_ptx.fee = needed_fee;
10166  available_for_fee = needed_fee;
10167  }
10168  }
10169 
10170  if (needed_fee > available_for_fee)
10171  {
10172  LOG_PRINT_L2("We could not make a tx, switching to fee accumulation");
10173 
10174  adding_fee = true;
10175  }
10176  else
10177  {
10178  LOG_PRINT_L2("We made a tx, adjusting fee and saving it, we need " << print_etn(needed_fee) << " and we have " << print_etn(test_ptx.fee));
10179  while (needed_fee > test_ptx.fee) {
10180  transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
10182  txBlob = t_serializable_object_to_blob(test_ptx.tx);
10183  needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
10184  LOG_PRINT_L2("Made an attempt at a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_etn(test_ptx.fee) <<
10185  " fee and " << print_etn(test_ptx.change_dts.amount) << " change");
10186  }
10187 
10188  LOG_PRINT_L2("Made a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_etn(test_ptx.fee) <<
10189  " fee and " << print_etn(test_ptx.change_dts.amount) << " change");
10190 
10191  tx.tx = test_tx;
10192  tx.ptx = test_ptx;
10193  tx.weight = get_transaction_weight(test_tx, txBlob.size());
10194  tx.outs = outs;
10195  tx.needed_fee = test_ptx.fee;
10196  accumulated_fee += test_ptx.fee;
10197  accumulated_change += test_ptx.change_dts.amount;
10198  adding_fee = false;
10199  if (!dsts.empty())
10200  {
10201  LOG_PRINT_L2("We have more to pay, starting another tx");
10202  txes.push_back(TX());
10203  original_output_index = 0;
10204  }
10205  }
10206  }
10207 
10208 skip_tx:
10209  // if unused_*_indices is empty while unused_*_indices_per_subaddr has multiple elements, and if we still have something to pay,
10210  // pop front of unused_*_indices_per_subaddr and have unused_*_indices point to the front of unused_*_indices_per_subaddr
10211  if ((!dsts.empty() && dsts[0].amount > 0) || adding_fee)
10212  {
10213  if (unused_transfers_indices->empty() && unused_transfers_indices_per_subaddr.size() > 1)
10214  {
10215  unused_transfers_indices_per_subaddr.erase(unused_transfers_indices_per_subaddr.begin());
10216  unused_transfers_indices = &unused_transfers_indices_per_subaddr[0].second;
10217  }
10218  if (unused_dust_indices->empty() && unused_dust_indices_per_subaddr.size() > 1)
10219  {
10220  unused_dust_indices_per_subaddr.erase(unused_dust_indices_per_subaddr.begin());
10221  unused_dust_indices = &unused_dust_indices_per_subaddr[0].second;
10222  }
10223  }
10224  }
10225 
10226  if (adding_fee)
10227  {
10228  LOG_PRINT_L1("We ran out of outputs while trying to gather final fee");
10229  THROW_WALLET_EXCEPTION_IF(1, error::tx_not_possible, unlocked_balance(subaddr_account, tx_version >= 3), needed_etn, accumulated_fee + needed_fee);
10230  }
10231 
10232  LOG_PRINT_L1("Done creating " << txes.size() << " transactions, " << print_etn(accumulated_fee) <<
10233  " total fee, " << print_etn(accumulated_change) << " total change");
10234 
10236  for (std::vector<TX>::iterator i = txes.begin(); i != txes.end(); ++i)
10237  {
10238  TX &tx = *i;
10239  cryptonote::transaction test_tx;
10240  test_tx.version = tx_version;
10241  pending_tx test_ptx;
10242  transfer_selected(tx.dsts,
10243  tx.selected_transfers,
10244  fake_outs_count,
10245  tx.outs,
10246  unlock_time,
10247  tx.needed_fee,
10248  extra,
10251  test_tx,
10252  test_ptx);
10253 
10254  auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
10255  tx.tx = test_tx;
10256  tx.ptx = test_ptx;
10257  tx.weight = get_transaction_weight(test_tx, txBlob.size());
10258  }
10259 
10260  std::vector<wallet2::pending_tx> ptx_vector;
10261  for (std::vector<TX>::iterator i = txes.begin(); i != txes.end(); ++i)
10262  {
10263  TX &tx = *i;
10264  uint64_t tx_etn = 0;
10265  for (size_t idx: tx.selected_transfers)
10266  tx_etn += m_transfers[idx].amount();
10267  LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() <<
10268  " " << get_transaction_hash(tx.ptx.tx) << ": " << get_weight_string(tx.weight) << ", sending " << print_etn(tx_etn) << " in " << tx.selected_transfers.size() <<
10269  " outputs to " << tx.dsts.size() << " destination(s), including " <<
10270  print_etn(tx.ptx.fee) << " fee, " << print_etn(tx.ptx.change_dts.amount) << " change");
10271  ptx_vector.push_back(tx.ptx);
10272  }
10273 
10274  THROW_WALLET_EXCEPTION_IF(!sanity_check(ptx_vector, original_dsts), error::wallet_internal_error, "Created transaction(s) failed sanity check");
10275 
10276  // if we made it this far, we're OK to actually send the transactions
10277  return ptx_vector;
10278 }
10279 
10280 bool wallet2::sanity_check(const std::vector<wallet2::pending_tx> &ptx_vector,
10281  std::vector<cryptonote::tx_destination_entry> dsts) const {
10282  MDEBUG("sanity_check: " << ptx_vector.size() << " txes, " << dsts.size() << " destinations");
10283 
10284  hw::device &hwdev = m_account.get_device();
10285 
10286  THROW_WALLET_EXCEPTION_IF(ptx_vector.empty(), error::wallet_internal_error, "No transactions");
10287 
10288  if (std::all_of(ptx_vector.begin(), ptx_vector.end(), [](const pending_tx &ptx) { return ptx.tx.version == 1; })) { // we only need do tx proofs for v1
10289  // check every party in there does receive at least the required amount
10290  std::unordered_map<account_public_address, std::pair<uint64_t, bool>> required;
10291  for (const auto &d: dsts) {
10292  required[d.addr].first += d.amount;
10293  required[d.addr].second = d.is_subaddress;
10294  }
10295 
10296  // add change
10297  uint64_t change = 0;
10298  for (const auto &ptx: ptx_vector) {
10299  for (size_t idx: ptx.selected_transfers) //1:add the amount you're spending
10300  change += m_transfers[idx].amount();
10301  change -= ptx.fee; //2: take off the fee
10302  }
10303  for (const auto &r: required)
10304  change -= r.second.first; // 3: subtract the destination required amount
10305  MDEBUG("Adding " << cryptonote::print_etn(change) << " expected change");
10306 
10307  // for all txes that have actual change, check change is coming back to the sending wallet
10308  for (const pending_tx &ptx: ptx_vector) {
10309  if (ptx.change_dts.amount == 0)
10310  continue;
10312  m_subaddresses.find(ptx.change_dts.addr.m_spend_public_key) == m_subaddresses.end(),
10313  error::wallet_internal_error, "Change address is not ours");
10314  required[ptx.change_dts.addr].first += ptx.change_dts.amount;
10315  required[ptx.change_dts.addr].second = ptx.change_dts.is_subaddress;
10316  }
10317 
10318 
10319  for (const auto &r: required) {
10320  const account_public_address &address = r.first;
10321  const crypto::public_key &view_pkey = address.m_view_public_key;
10322 
10323  uint64_t total_received = 0;
10324 
10325  for (const auto &ptx: ptx_vector) {
10326  uint64_t received = 0;
10327  try {
10328  std::string proof = get_tx_proof(ptx.tx, ptx.tx_key, ptx.additional_tx_keys, address,
10329  r.second.second,
10330  "automatic-sanity-check");
10331  check_tx_proof(ptx.tx, address, r.second.second, "automatic-sanity-check", proof, received);
10332  }
10333  catch (const std::exception &e) { received = 0; }
10334  total_received += received;
10335  }
10336 
10337  std::stringstream ss;
10338  ss << "Total received by "
10339  << cryptonote::get_account_address_as_str(m_nettype, r.second.second, address)
10340  << ": "
10341  << cryptonote::print_etn(total_received) << ", expected " << cryptonote::print_etn(r.second.first);
10342  MDEBUG(ss.str());
10343  THROW_WALLET_EXCEPTION_IF(total_received < r.second.first, error::wallet_internal_error, ss.str());
10344  }
10345  } // end of v1 sanity check
10346  else {
10347  // for all txes that have actual change, check change is coming back to the sending wallet
10348  for (const pending_tx &ptx: ptx_vector) {
10349  if (ptx.change_dts.amount == 0)
10350  continue;
10352  m_subaddresses.find(ptx.change_dts.addr.m_spend_public_key) == m_subaddresses.end(),
10353  error::wallet_internal_error, "Change address is not ours");
10354  }
10355  }
10356  return true;
10357 }
10358 
10359 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)
10360 {
10361  std::vector<size_t> unused_transfers_indices;
10362  std::vector<size_t> unused_dust_indices;
10363  const bool use_rct = use_fork_rules(4, 0);
10364  uint8_t tx_version = public_transactions_required() ? (migrate ? 2 : 3) : 1; //public migration **NOT** SC migration. SC migration tx are just vanilla v3 tx.
10365  THROW_WALLET_EXCEPTION_IF(unlocked_balance(subaddr_account, tx_version >=3) == 0, error::wallet_internal_error, "No unlocked balance in the entire wallet");
10366  std::map<uint32_t, std::pair<std::vector<size_t>, std::vector<size_t>>> unused_transfer_dust_indices_per_subaddr;
10367 
10368  // gather all dust ***and*** non-dust outputs of specified subaddress (if any) and below specified threshold (if any)
10369  bool fund_found = false;
10370  for (size_t i = 0; i < m_transfers.size(); ++i)
10371  {
10372  const transfer_details& td = m_transfers[i];
10373 
10374  if((tx_version < 3 && td.m_tx.version > 1) || (tx_version >= 3 && td.m_tx.version == 1))
10375  continue;
10376 
10377  if (!td.m_spent && !td.m_frozen && (!td.m_key_image_partial || tx_version >= 3) && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && (subaddr_indices.empty() || subaddr_indices.count(td.m_subaddr_index.minor) == 1))
10378  {
10379  fund_found = true;
10380  if (below == 0 || td.amount() < below)
10381  {
10382  if ((td.is_rct()) || is_valid_decomposed_amount(td.amount()))
10383  unused_transfer_dust_indices_per_subaddr[td.m_subaddr_index.minor].first.push_back(i);
10384  else
10385  unused_transfer_dust_indices_per_subaddr[td.m_subaddr_index.minor].second.push_back(i);
10386  }
10387  }
10388  }
10389  THROW_WALLET_EXCEPTION_IF(!fund_found, error::wallet_internal_error, "No unlocked balance in the specified subaddress(es)");
10390  THROW_WALLET_EXCEPTION_IF(unused_transfer_dust_indices_per_subaddr.empty(), error::wallet_internal_error, "The smallest amount found is not below the specified threshold");
10391 
10392  if (subaddr_indices.empty())
10393  {
10394  // in case subaddress index wasn't specified, choose non-empty subaddress randomly (with index=0 being chosen last)
10395  if (unused_transfer_dust_indices_per_subaddr.count(0) == 1 && unused_transfer_dust_indices_per_subaddr.size() > 1)
10396  unused_transfer_dust_indices_per_subaddr.erase(0);
10397  auto i = unused_transfer_dust_indices_per_subaddr.begin();
10398  std::advance(i, crypto::rand_idx(unused_transfer_dust_indices_per_subaddr.size()));
10399  unused_transfers_indices = i->second.first;
10400  unused_dust_indices = i->second.second;
10401  LOG_PRINT_L2("Spending from subaddress index " << i->first);
10402  }
10403  else
10404  {
10405  for (const auto& p : unused_transfer_dust_indices_per_subaddr)
10406  {
10407  unused_transfers_indices.insert(unused_transfers_indices.end(), p.second.first.begin(), p.second.first.end());
10408  unused_dust_indices.insert(unused_dust_indices.end(), p.second.second.begin(), p.second.second.end());
10409  LOG_PRINT_L2("Spending from subaddress index " << p.first);
10410  }
10411  }
10412 
10413  return create_transactions_from(address, is_subaddress, outputs, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra, tx_version);
10414 }
10415 
10416 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)
10417 {
10418  std::vector<size_t> unused_transfers_indices;
10419  std::vector<size_t> unused_dust_indices;
10420  uint8_t tx_version = 1; //todo: 4.0.0.0 leave it as this for now until we update it for sending by chainstate index
10421  const bool use_rct = use_fork_rules(4, 0);
10422  // find output with the given key image (
10423  for (size_t i = 0; i < m_transfers.size(); ++i)
10424  {
10425  const transfer_details& td = m_transfers[i];
10426 
10427  if (td.m_key_image_known && td.m_tx.version == 1 && td.m_key_image == ki && !td.m_spent && !td.m_frozen && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td))
10428  {
10429  if (td.is_rct() || is_valid_decomposed_amount(td.amount()))
10430  unused_transfers_indices.push_back(i);
10431  else
10432  unused_dust_indices.push_back(i);
10433  break;
10434  }
10435  }
10436  return create_transactions_from(address, is_subaddress, outputs, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra, tx_version);
10437 }
10438 
10439 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)
10440 {
10441  //ensure device is let in NONE mode in any case
10442  hw::device &hwdev = m_account.get_device();
10443  boost::unique_lock<hw::device> hwdev_lock (hwdev);
10444  hw::reset_mode rst(hwdev);
10445 
10446  uint64_t accumulated_fee, accumulated_outputs, accumulated_change;
10447  struct TX {
10448  std::vector<size_t> selected_transfers;
10449  std::vector<cryptonote::tx_destination_entry> dsts;
10451  pending_tx ptx;
10452  size_t weight;
10453  uint64_t needed_fee;
10454  std::vector<std::vector<get_outs_entry>> outs;
10455 
10456  TX() : weight(0), needed_fee(0) {}
10457  };
10458  std::vector<TX> txes;
10459  uint64_t needed_fee, available_for_fee = 0;
10460  uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit(); //max size of a tx - usually ~ half the block size
10461  std::vector<std::vector<get_outs_entry>> outs;
10462 
10463  const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE);
10464  const bool use_rct = fake_outs_count > 0 && use_fork_rules(4, 0);
10465  const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
10466  const rct::RCTConfig rct_config {
10468  bulletproof ? (use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1) : 0,
10469  };
10470  const uint64_t base_fee = get_base_fee();
10471  const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
10472  const uint64_t fee_quantization_mask = get_fee_quantization_mask();
10473 
10474  LOG_PRINT_L2("Starting with " << unused_transfers_indices.size() << " non-dust outputs and " << unused_dust_indices.size() << " dust outputs");
10475 
10476  if (unused_dust_indices.empty() && unused_transfers_indices.empty())
10477  return std::vector<wallet2::pending_tx>();
10478 
10479  // start with an empty tx
10480  txes.push_back(TX());
10481  accumulated_fee = 0;
10482  accumulated_outputs = 0;
10483  accumulated_change = 0;
10484  needed_fee = 0;
10485 
10486  // while we have something to send
10488  while (!unused_dust_indices.empty() || !unused_transfers_indices.empty()) {
10489  TX &tx = txes.back();
10490 
10491  // get a random unspent output and use it to pay next chunk. We try to alternate
10492  // dust and non dust to ensure we never get with only dust, from which we might
10493  // get a tx that can't pay for itself
10494  uint64_t fee_dust_threshold;
10495  if (use_fork_rules(HF_VERSION_PER_BYTE_FEE))
10496  {
10497  const uint64_t estimated_tx_weight_with_one_extra_output = estimate_tx_weight(use_rct, tx.selected_transfers.size() + 1, fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof);
10498  fee_dust_threshold = calculate_fee_from_weight(base_fee, estimated_tx_weight_with_one_extra_output, fee_multiplier, fee_quantization_mask);
10499  }
10500  else
10501  {
10502  fee_dust_threshold = base_fee * fee_multiplier * (upper_transaction_weight_limit + 1023) / 1024;
10503  }
10504 
10505  size_t idx =
10506  unused_transfers_indices.empty()
10507  ? pop_best_value(unused_dust_indices, tx.selected_transfers)
10508  : unused_dust_indices.empty()
10509  ? pop_best_value(unused_transfers_indices, tx.selected_transfers)
10510  : ((tx.selected_transfers.size() & 1) || accumulated_outputs > fee_dust_threshold)
10511  ? pop_best_value(unused_dust_indices, tx.selected_transfers)
10512  : pop_best_value(unused_transfers_indices, tx.selected_transfers);
10513 
10514  const transfer_details &td = m_transfers[idx];
10515  LOG_PRINT_L2("Picking output " << idx << ", amount " << print_etn(td.amount()));
10516 
10517  // add this output to the list to spend
10518  tx.selected_transfers.push_back(idx);
10519  uint64_t available_amount = td.amount();
10520  accumulated_outputs += available_amount;
10521 
10522  // clear any fake outs we'd already gathered, since we'll need a new set
10523  outs.clear();
10524 
10525  // here, check if we need to sent tx and start a new one
10526  LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit "
10527  << upper_transaction_weight_limit);
10528  const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 2, extra.size(), bulletproof);
10529  bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || ( estimated_rct_tx_weight >= TX_WEIGHT_TARGET(upper_transaction_weight_limit));
10530 
10531  if (try_tx) {
10532  cryptonote::transaction test_tx;
10533  test_tx.version = tx_version;
10534  pending_tx test_ptx;
10535 
10536  needed_fee = tx_version == 2 ? 0 : estimate_fee(use_per_byte_fee, use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, base_fee, fee_multiplier, fee_quantization_mask);
10537 
10538  // add N - 1 outputs for correct initial fee estimation
10539  for (size_t i = 0; i < ((outputs > 1) ? outputs - 1 : outputs); ++i)
10540  tx.dsts.push_back(tx_destination_entry(1, address, is_subaddress));
10541 
10542  LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " destinations and " <<
10543  tx.selected_transfers.size() << " outputs");
10544 
10545  transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
10547 
10548  auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
10549  needed_fee = tx_version == 2 ? 0 : calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
10550  available_for_fee = test_ptx.fee + test_ptx.change_dts.amount;
10551  for (auto &dt: test_ptx.dests)
10552  available_for_fee += dt.amount;
10553  LOG_PRINT_L2("Made a " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_etn(available_for_fee) << " available for fee (" <<
10554  print_etn(needed_fee) << " needed)");
10555 
10556  // add last output, missed for fee estimation
10557  if (outputs > 1)
10558  tx.dsts.push_back(tx_destination_entry(1, address, is_subaddress));
10559 
10560  THROW_WALLET_EXCEPTION_IF(needed_fee > available_for_fee, error::wallet_internal_error, "Transaction cannot pay for itself");
10561 
10562  do {
10563  LOG_PRINT_L2("We made a tx, adjusting fee and saving it");
10564  // distribute total transferred amount between outputs
10565  uint64_t amount_transferred = available_for_fee - needed_fee; //shouuld be zero for migration transactions
10566  uint64_t dt_amount = amount_transferred / outputs;
10567  // residue is distributed as one atomic unit per output until it reaches zero
10568  uint64_t residue = amount_transferred % outputs;
10569  for (auto &dt: tx.dsts)
10570  {
10571  uint64_t dt_residue = 0;
10572  if (residue > 0)
10573  {
10574  dt_residue = 1;
10575  residue -= 1;
10576  }
10577  dt.amount = dt_amount + dt_residue;
10578  }
10579  transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
10581  txBlob = t_serializable_object_to_blob(test_ptx.tx);
10582  needed_fee = tx_version == 2 ? 0 : calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
10583  LOG_PRINT_L2("Made an attempt at a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_etn(test_ptx.fee) <<
10584  " fee and " << print_etn(test_ptx.change_dts.amount) << " change");
10585  } while (needed_fee > test_ptx.fee);
10586 
10587  LOG_PRINT_L2("Made a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_etn(test_ptx.fee) <<
10588  " fee and " << print_etn(test_ptx.change_dts.amount) << " change");
10589 
10590  tx.tx = test_tx;
10591  tx.ptx = test_ptx;
10592  tx.weight = get_transaction_weight(test_tx, txBlob.size());
10593  tx.outs = outs;
10594  tx.needed_fee = test_ptx.fee;
10595  accumulated_fee += test_ptx.fee;
10596  accumulated_change += test_ptx.change_dts.amount;
10597  if (!unused_transfers_indices.empty() || !unused_dust_indices.empty())
10598  {
10599  LOG_PRINT_L2("We have more to pay, starting another tx");
10600  txes.push_back(TX());
10601  }
10602  }
10603  }
10604 
10605  LOG_PRINT_L1("Done creating " << txes.size() << " transactions, " << print_etn(accumulated_fee) <<
10606  " total fee, " << print_etn(accumulated_change) << " total change");
10607 
10609  for (std::vector<TX>::iterator i = txes.begin(); i != txes.end(); ++i)
10610  {
10611  TX &tx = *i;
10612  cryptonote::transaction test_tx;
10613  test_tx.version = tx_version;
10614  pending_tx test_ptx;
10615  transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra,
10617 
10618  auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
10619  tx.tx = test_tx;
10620  tx.ptx = test_ptx;
10621  tx.weight = get_transaction_weight(test_tx, txBlob.size());
10622  }
10623 
10624  std::vector<wallet2::pending_tx> ptx_vector;
10625  for (std::vector<TX>::iterator i = txes.begin(); i != txes.end(); ++i)
10626  {
10627  TX &tx = *i;
10628  uint64_t tx_etn = 0;
10629  for (size_t idx: tx.selected_transfers)
10630  tx_etn += m_transfers[idx].amount();
10631  LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() <<
10632  " " << get_transaction_hash(tx.ptx.tx) << ": " << get_weight_string(tx.weight) << ", sending " << print_etn(tx_etn) << " in " << tx.selected_transfers.size() <<
10633  " outputs to " << tx.dsts.size() << " destination(s), including " <<
10634  print_etn(tx.ptx.fee) << " fee, " << print_etn(tx.ptx.change_dts.amount) << " change");
10635  ptx_vector.push_back(tx.ptx);
10636  }
10637 
10638  uint64_t a = 0;
10639  for (const TX &tx: txes)
10640  {
10641  for (size_t idx: tx.selected_transfers)
10642  {
10643  a += m_transfers[idx].amount();
10644  }
10645  a -= tx.ptx.fee;
10646  }
10647  std::vector<cryptonote::tx_destination_entry> synthetic_dsts(1, cryptonote::tx_destination_entry("", a, address, is_subaddress));
10648  THROW_WALLET_EXCEPTION_IF(!sanity_check(ptx_vector, synthetic_dsts), error::wallet_internal_error, "Created transaction(s) failed sanity check");
10649 
10650  // if we made it this far, we're OK to actually send the transactions
10651  return ptx_vector;
10652 }
10653 
10654 //----------------------------------------------------------------------------------------------------
10655 void wallet2::cold_tx_aux_import(const std::vector<pending_tx> & ptx, const std::vector<std::string> & tx_device_aux)
10656 {
10657  CHECK_AND_ASSERT_THROW_MES(ptx.size() == tx_device_aux.size(), "TX aux has invalid size");
10658  for (size_t i = 0; i < ptx.size(); ++i){
10659  crypto::hash txid;
10660  txid = get_transaction_hash(ptx[i].tx);
10661  set_tx_device_aux(txid, tx_device_aux[i]);
10662  }
10663 }
10664 //----------------------------------------------------------------------------------------------------
10665 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)
10666 {
10667  auto & hwdev = get_account().get_device();
10668  if (!hwdev.has_tx_cold_sign()){
10669  throw std::invalid_argument("Device does not support cold sign protocol");
10670  }
10671 
10672  unsigned_tx_set txs;
10673  for (auto &tx: ptx_vector)
10674  {
10675  txs.txes.push_back(get_construction_data_with_decrypted_short_payment_id(tx, m_account.get_device()));
10676  }
10677  txs.transfers = std::make_pair(0, m_transfers);
10678 
10679  auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev);
10680  CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface");
10681 
10682  hw::tx_aux_data aux_data;
10684  setup_shim(&wallet_shim, this);
10685  aux_data.tx_recipients = dsts_info;
10686  aux_data.bp_version = use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1;
10687  dev_cold->tx_sign(&wallet_shim, txs, exported_txs, aux_data);
10688  tx_device_aux = aux_data.tx_device_aux;
10689 
10690  MDEBUG("Signed tx data from hw: " << exported_txs.ptx.size() << " transactions");
10691  for (auto &c_ptx: exported_txs.ptx) LOG_PRINT_L0(cryptonote::obj_to_json_str(c_ptx.tx));
10692 }
10693 //----------------------------------------------------------------------------------------------------
10694 uint64_t wallet2::cold_key_image_sync(uint64_t &spent, uint64_t &unspent) {
10695  auto & hwdev = get_account().get_device();
10696  CHECK_AND_ASSERT_THROW_MES(hwdev.has_ki_cold_sync(), "Device does not support cold ki sync protocol");
10697 
10698  auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev);
10699  CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface");
10700 
10701  std::vector<std::pair<crypto::key_image, crypto::signature>> ski;
10703  setup_shim(&wallet_shim, this);
10704 
10705  dev_cold->ki_sync(&wallet_shim, m_transfers, ski);
10706 
10707  // Call COMMAND_RPC_IS_KEY_IMAGE_SPENT only if daemon is trusted.
10708  uint64_t import_res = import_key_images(ski, 0, spent, unspent, is_trusted_daemon());
10709  m_device_last_key_image_sync = time(NULL);
10710 
10711  return import_res;
10712 }
10713 //----------------------------------------------------------------------------------------------------
10714 void wallet2::get_hard_fork_info(uint8_t version, uint64_t &earliest_height) const
10715 {
10716  boost::optional<std::string> result = m_node_rpc_proxy.get_earliest_height(version, earliest_height);
10717  throw_on_rpc_response_error(result, "get_hard_fork_info");
10718 }
10719 //----------------------------------------------------------------------------------------------------
10720 bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks) const
10721 {
10722  // TODO: How to get fork rule info from light wallet node?
10723  if(m_light_wallet)
10724  return true;
10725  uint64_t height, earliest_height;
10726  boost::optional<std::string> result = m_node_rpc_proxy.get_height(height);
10727  throw_on_rpc_response_error(result, "get_info");
10728  result = m_node_rpc_proxy.get_earliest_height(version, earliest_height);
10729  throw_on_rpc_response_error(result, "get_hard_fork_info");
10730 
10731  bool close_enough = (int64_t)height >= (int64_t)earliest_height - early_blocks && earliest_height != std::numeric_limits<uint64_t>::max() && version <= CURRENT_HARDFORK_VERSION; // start using the rules that many blocks beforehand
10732  if (close_enough)
10733  LOG_PRINT_L2("Using v" << (unsigned)version << " rules");
10734  else
10735  LOG_PRINT_L2("Not using v" << (unsigned)version << " rules");
10736  return close_enough;
10737 }
10738 //----------------------------------------------------------------------------------------------------
10739 uint64_t wallet2::get_upper_transaction_weight_limit() const
10740 {
10741  if (m_upper_transaction_weight_limit > 0)
10742  return m_upper_transaction_weight_limit;
10743  uint64_t full_reward_zone = use_fork_rules(10, 10) ?
10744  CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V10 : use_fork_rules(8, 10) ?
10745  CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V8 : use_fork_rules(5, 10) ?
10746  CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : use_fork_rules(2, 10) ?
10748 
10749  if (use_fork_rules(8, 10))
10750  return full_reward_zone / 2 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
10751  else
10752  return full_reward_zone - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
10753 }
10754 //----------------------------------------------------------------------------------------------------
10755 std::vector<size_t> wallet2::select_available_outputs(const std::function<bool(const transfer_details &td)> &f) const
10756 {
10757  std::vector<size_t> outputs;
10758  size_t n = 0;
10759  for (transfer_container::const_iterator i = m_transfers.begin(); i != m_transfers.end(); ++i, ++n)
10760  {
10761  if (i->m_spent)
10762  continue;
10763  if (i->m_frozen)
10764  continue;
10765  if (i->m_key_image_partial)
10766  continue;
10767  if (!is_transfer_unlocked(*i))
10768  continue;
10769  if (f(*i))
10770  outputs.push_back(n);
10771  }
10772  return outputs;
10773 }
10774 //----------------------------------------------------------------------------------------------------
10775 std::vector<uint64_t> wallet2::get_unspent_amounts_vector() const
10776 {
10777  std::set<uint64_t> set;
10778  for (const auto &td: m_transfers)
10779  {
10780  if (!td.m_spent && !td.m_frozen)
10781  set.insert(td.is_rct() ? 0 : td.amount());
10782  }
10783  std::vector<uint64_t> vector;
10784  vector.reserve(set.size());
10785  for (const auto &i: set)
10786  {
10787  vector.push_back(i);
10788  }
10789  return vector;
10790 }
10791 //----------------------------------------------------------------------------------------------------
10792 std::vector<size_t> wallet2::select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct)
10793 {
10796  m_daemon_rpc_mutex.lock();
10797  if (is_trusted_daemon())
10798  req_t.amounts = get_unspent_amounts_vector();
10799  req_t.min_count = count;
10800  req_t.max_count = 0;
10801  req_t.unlocked = unlocked;
10802  req_t.recent_cutoff = 0;
10803  bool r = invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, rpc_timeout);
10804  m_daemon_rpc_mutex.unlock();
10805  THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "select_available_outputs_from_histogram");
10806  THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
10808 
10809  std::set<uint64_t> mixable;
10810  for (const auto &i: resp_t.histogram)
10811  {
10812  mixable.insert(i.amount);
10813  }
10814 
10815  return select_available_outputs([mixable, atleast, allow_rct](const transfer_details &td) {
10816  if (!allow_rct && td.is_rct())
10817  return false;
10818  const uint64_t amount = td.is_rct() ? 0 : td.amount();
10819  if (atleast) {
10820  if (mixable.find(amount) != mixable.end())
10821  return true;
10822  }
10823  else {
10824  if (mixable.find(amount) == mixable.end())
10825  return true;
10826  }
10827  return false;
10828  });
10829 }
10830 //----------------------------------------------------------------------------------------------------
10831 uint64_t wallet2::get_num_rct_outputs()
10832 {
10835  m_daemon_rpc_mutex.lock();
10836  req_t.amounts.push_back(0);
10837  req_t.min_count = 0;
10838  req_t.max_count = 0;
10839  req_t.unlocked = true;
10840  req_t.recent_cutoff = 0;
10841  bool r = invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, rpc_timeout);
10842  m_daemon_rpc_mutex.unlock();
10843  THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_num_rct_outputs");
10844  THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
10846  THROW_WALLET_EXCEPTION_IF(resp_t.histogram.size() != 1, error::get_histogram_error, "Expected exactly one response");
10847  THROW_WALLET_EXCEPTION_IF(resp_t.histogram[0].amount != 0, error::get_histogram_error, "Expected 0 amount");
10848 
10849  return resp_t.histogram[0].total_instances;
10850 }
10851 //----------------------------------------------------------------------------------------------------
10852 const wallet2::transfer_details &wallet2::get_transfer_details(size_t idx) const
10853 {
10854  THROW_WALLET_EXCEPTION_IF(idx >= m_transfers.size(), error::wallet_internal_error, "Bad transfer index");
10855  return m_transfers[idx];
10856 }
10857 //----------------------------------------------------------------------------------------------------
10858 std::vector<size_t> wallet2::select_available_unmixable_outputs()
10859 {
10860  // request all outputs with less instances than the min ring size
10861  return select_available_outputs_from_histogram(get_min_ring_size(), false, true, false);
10862 }
10863 //----------------------------------------------------------------------------------------------------
10864 std::vector<size_t> wallet2::select_available_mixable_outputs()
10865 {
10866  // request all outputs with at least as many instances as the min ring size
10867  return select_available_outputs_from_histogram(get_min_ring_size(), true, true, true);
10868 }
10869 //----------------------------------------------------------------------------------------------------
10870 std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions()
10871 {
10872  // From hard fork 1, we don't consider small amounts to be dust anymore
10873  const bool hf1_rules = use_fork_rules(2, 10); // first hard fork has version 2
10874  tx_dust_policy dust_policy(hf1_rules ? 0 : ::config::DEFAULT_DUST_THRESHOLD);
10875 
10876  uint8_t tx_version = this->public_transactions_required() ? 3 : 1;
10877  const uint64_t base_fee = get_base_fee();
10878 
10879  // may throw
10880  std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs();
10881  size_t num_dust_outputs = unmixable_outputs.size();
10882 
10883  if (num_dust_outputs == 0)
10884  {
10885  return std::vector<wallet2::pending_tx>();
10886  }
10887 
10888  // split in "dust" and "non dust" to make it easier to select outputs
10889  std::vector<size_t> unmixable_transfer_outputs, unmixable_dust_outputs;
10890  for (auto n: unmixable_outputs)
10891  {
10892  if (m_transfers[n].amount() < base_fee)
10893  unmixable_dust_outputs.push_back(n);
10894  else
10895  unmixable_transfer_outputs.push_back(n);
10896  }
10897 
10898  return create_transactions_from(m_account_public_address, false, 1, unmixable_transfer_outputs, unmixable_dust_outputs, 0 /*fake_outs_count */, 0 /* unlock_time */, 1 /*priority */, std::vector<uint8_t>(), tx_version);
10899 }
10900 //----------------------------------------------------------------------------------------------------
10901 void wallet2::discard_unmixable_outputs()
10902 {
10903  // may throw
10904  std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs();
10905  for (size_t idx : unmixable_outputs)
10906  {
10907  freeze(idx);
10908  }
10909 }
10910 
10911 bool wallet2::get_tx_key_cached(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const
10912 {
10913  additional_tx_keys.clear();
10914  const std::unordered_map<crypto::hash, crypto::secret_key>::const_iterator i = m_tx_keys.find(txid);
10915  if (i == m_tx_keys.end())
10916  return false;
10917  tx_key = i->second;
10918  const auto j = m_additional_tx_keys.find(txid);
10919  if (j != m_additional_tx_keys.end())
10920  additional_tx_keys = j->second;
10921  return true;
10922 }
10923 //----------------------------------------------------------------------------------------------------
10924 bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys)
10925 {
10926  bool r = get_tx_key_cached(txid, tx_key, additional_tx_keys);
10927  if (r)
10928  {
10929  return true;
10930  }
10931 
10932  auto & hwdev = get_account().get_device();
10933 
10934  // So far only Cold protocol devices are supported.
10936  {
10937  return false;
10938  }
10939 
10940  const auto tx_data_it = m_tx_device.find(txid);
10941  if (tx_data_it == m_tx_device.end())
10942  {
10943  MDEBUG("Aux data not found for txid: " << txid);
10944  return false;
10945  }
10946 
10947  auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev);
10948  CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface");
10949  if (!dev_cold->is_get_tx_key_supported())
10950  {
10951  MDEBUG("get_tx_key not supported by the device");
10952  return false;
10953  }
10954 
10955  hw::device_cold::tx_key_data_t tx_key_data;
10956  dev_cold->load_tx_key_data(tx_key_data, tx_data_it->second);
10957 
10958  // Load missing tx prefix hash
10959  if (tx_key_data.tx_prefix_hash.empty())
10960  {
10963  req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
10964  req.decode_as_json = false;
10965  req.prune = true;
10966  m_daemon_rpc_mutex.lock();
10967  bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout);
10968  m_daemon_rpc_mutex.unlock();
10969  THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
10970  error::wallet_internal_error, "Failed to get transaction from daemon");
10971 
10973  crypto::hash tx_hash{};
10974  cryptonote::blobdata tx_data;
10975  crypto::hash tx_prefix_hash{};
10976  ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
10977  THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
10978  THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx, tx_hash, tx_prefix_hash),
10979  error::wallet_internal_error, "Failed to validate transaction from daemon");
10981  "Failed to get the right transaction from daemon");
10982 
10983  tx_key_data.tx_prefix_hash = std::string(tx_prefix_hash.data, 32);
10984  }
10985 
10986  std::vector<crypto::secret_key> tx_keys;
10987  dev_cold->get_tx_key(tx_keys, tx_key_data, m_account.get_keys().m_view_secret_key);
10988  if (tx_keys.empty())
10989  {
10990  return false;
10991  }
10992 
10993  tx_key = tx_keys[0];
10994  tx_keys.erase(tx_keys.begin());
10995  additional_tx_keys = tx_keys;
10996 
10997  return true;
10998 }
10999 //----------------------------------------------------------------------------------------------------
11000 void wallet2::set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys)
11001 {
11002  // fetch tx from daemon and check if secret keys agree with corresponding public keys
11004  req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
11005  req.decode_as_json = false;
11006  req.prune = true;
11008  bool r;
11009  {
11010  const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
11011  r = invoke_http_json("/gettransactions", req, res, rpc_timeout);
11012  }
11017  "daemon returned wrong response for gettransactions, wrong txs count = " +
11018  std::to_string(res.txs.size()) + ", expected 1");
11020  crypto::hash tx_hash;
11021  THROW_WALLET_EXCEPTION_IF(!get_pruned_tx(res.txs[0], tx, tx_hash), error::wallet_internal_error,
11022  "Failed to get transaction from daemon");
11023  THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "txid mismatch");
11024  std::vector<tx_extra_field> tx_extra_fields;
11025  THROW_WALLET_EXCEPTION_IF(!parse_tx_extra(tx.extra, tx_extra_fields), error::wallet_internal_error, "Transaction extra has unsupported format");
11026  tx_extra_pub_key pub_key_field;
11027  bool found = false;
11028  size_t index = 0;
11029  while (find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, index++))
11030  {
11031  crypto::public_key calculated_pub_key;
11032  crypto::secret_key_to_public_key(tx_key, calculated_pub_key);
11033  if (calculated_pub_key == pub_key_field.pub_key)
11034  {
11035  found = true;
11036  break;
11037  }
11038  }
11039  THROW_WALLET_EXCEPTION_IF(!found, error::wallet_internal_error, "Given tx secret key doesn't agree with the tx public key in the blockchain");
11040  tx_extra_additional_pub_keys additional_tx_pub_keys;
11041  find_tx_extra_field_by_type(tx_extra_fields, additional_tx_pub_keys);
11042  THROW_WALLET_EXCEPTION_IF(additional_tx_keys.size() != additional_tx_pub_keys.data.size(), error::wallet_internal_error, "The number of additional tx secret keys doesn't agree with the number of additional tx public keys in the blockchain" );
11043  m_tx_keys.insert(std::make_pair(txid, tx_key));
11044  m_additional_tx_keys.insert(std::make_pair(txid, additional_tx_keys));
11045 }
11046 //----------------------------------------------------------------------------------------------------
11047 std::string wallet2::get_spend_proof(const crypto::hash &txid, const std::string &message)
11048 {
11050  "get_spend_proof requires spend secret key and is not available for a watch-only wallet");
11051 
11052  // fetch tx from daemon
11054  req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
11055  req.decode_as_json = false;
11056  req.prune = true;
11058  bool r;
11059  {
11060  const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
11061  r = invoke_http_json("/gettransactions", req, res, rpc_timeout);
11062  }
11067  "daemon returned wrong response for gettransactions, wrong txs count = " +
11068  std::to_string(res.txs.size()) + ", expected 1");
11069 
11071  crypto::hash tx_hash;
11072  THROW_WALLET_EXCEPTION_IF(!get_pruned_tx(res.txs[0], tx, tx_hash), error::wallet_internal_error, "Failed to get tx from daemon");
11073 
11074  std::vector<std::vector<crypto::signature>> signatures;
11075 
11076  // get signature prefix hash
11077  std::string sig_prefix_data((const char*)&txid, sizeof(crypto::hash));
11078  sig_prefix_data += message;
11079  crypto::hash sig_prefix_hash;
11080  crypto::cn_fast_hash(sig_prefix_data.data(), sig_prefix_data.size(), sig_prefix_hash);
11081 
11082  for(size_t i = 0; i < tx.vin.size(); ++i)
11083  {
11084  const txin_to_key* const in_key = boost::get<txin_to_key>(std::addressof(tx.vin[i]));
11085  if (in_key == nullptr)
11086  continue;
11087 
11088  // check if the key image belongs to us
11089  const auto found = m_key_images.find(in_key->k_image);
11090  if(found == m_key_images.end())
11091  {
11092  THROW_WALLET_EXCEPTION_IF(i > 0, error::wallet_internal_error, "subset of key images belong to us, very weird!");
11093  THROW_WALLET_EXCEPTION_IF(true, error::wallet_internal_error, "This tx wasn't generated by this wallet!");
11094  }
11095 
11096  // derive the real output keypair
11097  const transfer_details& in_td = m_transfers[found->second];
11098  const txout_to_key* const in_tx_out_pkey = boost::get<txout_to_key>(std::addressof(in_td.m_tx.vout[in_td.m_internal_output_index].target));
11099  THROW_WALLET_EXCEPTION_IF(in_tx_out_pkey == nullptr, error::wallet_internal_error, "Output is not txout_to_key");
11100  const crypto::public_key in_tx_pub_key = get_tx_pub_key_from_extra(in_td.m_tx, in_td.m_pk_index);
11101  const std::vector<crypto::public_key> in_additionakl_tx_pub_keys = get_additional_tx_pub_keys_from_extra(in_td.m_tx);
11102  keypair in_ephemeral;
11103  crypto::key_image in_img;
11104  THROW_WALLET_EXCEPTION_IF(!generate_key_image_helper(m_account.get_keys(), m_subaddresses, in_tx_out_pkey->key, in_tx_pub_key, in_additionakl_tx_pub_keys, in_td.m_internal_output_index, in_ephemeral, in_img, m_account.get_device(), m_account_major_offset),
11105  error::wallet_internal_error, "failed to generate key image");
11106  THROW_WALLET_EXCEPTION_IF(in_key->k_image != in_img, error::wallet_internal_error, "key image mismatch");
11107 
11108  // get output pubkeys in the ring
11109  const std::vector<uint64_t> absolute_offsets = cryptonote::relative_output_offsets_to_absolute(in_key->key_offsets);
11110  const size_t ring_size = in_key->key_offsets.size();
11111  THROW_WALLET_EXCEPTION_IF(absolute_offsets.size() != ring_size, error::wallet_internal_error, "absolute offsets size is wrong");
11113  req.outputs.resize(ring_size);
11114  for (size_t j = 0; j < ring_size; ++j)
11115  {
11116  req.outputs[j].amount = in_key->amount;
11117  req.outputs[j].index = absolute_offsets[j];
11118  }
11120  bool r;
11121  {
11122  const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
11123  r = invoke_http_bin("/get_outs.bin", req, res, rpc_timeout);
11124  }
11129  "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
11130  std::to_string(res.outs.size()) + ", expected " + std::to_string(ring_size));
11131 
11132  // copy pubkey pointers
11133  std::vector<const crypto::public_key *> p_output_keys;
11134  for (const COMMAND_RPC_GET_OUTPUTS_BIN::outkey &out : res.outs)
11135  p_output_keys.push_back(&out.key);
11136 
11137  // figure out real output index and secret key
11138  size_t sec_index = -1;
11139  for (size_t j = 0; j < ring_size; ++j)
11140  {
11141  if (res.outs[j].key == in_ephemeral.pub)
11142  {
11143  sec_index = j;
11144  break;
11145  }
11146  }
11147  THROW_WALLET_EXCEPTION_IF(sec_index >= ring_size, error::wallet_internal_error, "secret index not found");
11148 
11149  // generate ring sig for this input
11150  signatures.push_back(std::vector<crypto::signature>());
11151  std::vector<crypto::signature>& sigs = signatures.back();
11152  sigs.resize(in_key->key_offsets.size());
11153  crypto::generate_ring_signature(sig_prefix_hash, in_key->k_image, p_output_keys, in_ephemeral.sec, sec_index, sigs.data());
11154  }
11155 
11156  std::string sig_str = "SpendProofV1";
11157  for (const std::vector<crypto::signature>& ring_sig : signatures)
11158  for (const crypto::signature& sig : ring_sig)
11159  sig_str += tools::base58::encode(std::string((const char *)&sig, sizeof(crypto::signature)));
11160  return sig_str;
11161 }
11162 //----------------------------------------------------------------------------------------------------
11163 bool wallet2::check_spend_proof(const crypto::hash &txid, const std::string &message, const std::string &sig_str)
11164 {
11165  const std::string header = "SpendProofV1";
11166  const size_t header_len = header.size();
11167  THROW_WALLET_EXCEPTION_IF(sig_str.size() < header_len || sig_str.substr(0, header_len) != header, error::wallet_internal_error,
11168  "Signature header check error");
11169 
11170  // fetch tx from daemon
11172  req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
11173  req.decode_as_json = false;
11174  req.prune = true;
11176  bool r;
11177  {
11178  const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
11179  r = invoke_http_json("/gettransactions", req, res, rpc_timeout);
11180  }
11185  "daemon returned wrong response for gettransactions, wrong txs count = " +
11186  std::to_string(res.txs.size()) + ", expected 1");
11187 
11189  crypto::hash tx_hash;
11190  THROW_WALLET_EXCEPTION_IF(!get_pruned_tx(res.txs[0], tx, tx_hash), error::wallet_internal_error, "failed to get tx from daemon");
11191 
11192  // check signature size
11193  size_t num_sigs = 0;
11194  for(size_t i = 0; i < tx.vin.size(); ++i)
11195  {
11196  const txin_to_key* const in_key = boost::get<txin_to_key>(std::addressof(tx.vin[i]));
11197  if (in_key != nullptr)
11198  num_sigs += in_key->key_offsets.size();
11199  }
11200  std::vector<std::vector<crypto::signature>> signatures = { std::vector<crypto::signature>(1) };
11201  const size_t sig_len = tools::base58::encode(std::string((const char *)&signatures[0][0], sizeof(crypto::signature))).size();
11202  if( sig_str.size() != header_len + num_sigs * sig_len ) {
11203  return false;
11204  }
11205 
11206  // decode base58
11207  signatures.clear();
11208  size_t offset = header_len;
11209  for(size_t i = 0; i < tx.vin.size(); ++i)
11210  {
11211  const txin_to_key* const in_key = boost::get<txin_to_key>(std::addressof(tx.vin[i]));
11212  if (in_key == nullptr)
11213  continue;
11214  signatures.resize(signatures.size() + 1);
11215  signatures.back().resize(in_key->key_offsets.size());
11216  for (size_t j = 0; j < in_key->key_offsets.size(); ++j)
11217  {
11218  std::string sig_decoded;
11219  THROW_WALLET_EXCEPTION_IF(!tools::base58::decode(sig_str.substr(offset, sig_len), sig_decoded), error::wallet_internal_error, "Signature decoding error");
11220  THROW_WALLET_EXCEPTION_IF(sizeof(crypto::signature) != sig_decoded.size(), error::wallet_internal_error, "Signature decoding error");
11221  memcpy(&signatures.back()[j], sig_decoded.data(), sizeof(crypto::signature));
11222  offset += sig_len;
11223  }
11224  }
11225 
11226  // get signature prefix hash
11227  std::string sig_prefix_data((const char*)&txid, sizeof(crypto::hash));
11228  sig_prefix_data += message;
11229  crypto::hash sig_prefix_hash;
11230  crypto::cn_fast_hash(sig_prefix_data.data(), sig_prefix_data.size(), sig_prefix_hash);
11231 
11232  std::vector<std::vector<crypto::signature>>::const_iterator sig_iter = signatures.cbegin();
11233  for(size_t i = 0; i < tx.vin.size(); ++i)
11234  {
11235  const txin_to_key* const in_key = boost::get<txin_to_key>(std::addressof(tx.vin[i]));
11236  if (in_key == nullptr)
11237  continue;
11238 
11239  // get output pubkeys in the ring
11241  const std::vector<uint64_t> absolute_offsets = cryptonote::relative_output_offsets_to_absolute(in_key->key_offsets);
11242  req.outputs.resize(absolute_offsets.size());
11243  for (size_t j = 0; j < absolute_offsets.size(); ++j)
11244  {
11245  req.outputs[j].amount = in_key->amount;
11246  req.outputs[j].index = absolute_offsets[j];
11247  }
11249  bool r;
11250  {
11251  const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
11252  r = invoke_http_bin("/get_outs.bin", req, res, rpc_timeout);
11253  }
11257  THROW_WALLET_EXCEPTION_IF(res.outs.size() != req.outputs.size(), error::wallet_internal_error,
11258  "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
11259  std::to_string(res.outs.size()) + ", expected " + std::to_string(req.outputs.size()));
11260 
11261  // copy pointers
11262  std::vector<const crypto::public_key *> p_output_keys;
11263  for (const COMMAND_RPC_GET_OUTPUTS_BIN::outkey &out : res.outs)
11264  p_output_keys.push_back(&out.key);
11265 
11266  // check this ring
11267  if (!crypto::check_ring_signature(sig_prefix_hash, in_key->k_image, p_output_keys, sig_iter->data()))
11268  return false;
11269  ++sig_iter;
11270  }
11271  THROW_WALLET_EXCEPTION_IF(sig_iter != signatures.cend(), error::wallet_internal_error, "Signature iterator didn't reach the end");
11272  return true;
11273 }
11274 //----------------------------------------------------------------------------------------------------
11275 
11276 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)
11277 {
11278  crypto::key_derivation derivation;
11280  "Failed to generate key derivation from supplied parameters");
11281 
11282  std::vector<crypto::key_derivation> additional_derivations;
11283  additional_derivations.resize(additional_tx_keys.size());
11284  for (size_t i = 0; i < additional_tx_keys.size(); ++i)
11285  THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(address.m_view_public_key, additional_tx_keys[i], additional_derivations[i]), error::wallet_internal_error,
11286  "Failed to generate key derivation from supplied parameters");
11287 
11288  check_tx_key_helper(txid, derivation, additional_derivations, address, received, in_pool, confirmations);
11289 }
11290 
11291 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
11292 {
11293  received = 0;
11294 
11295  for (size_t n = 0; n < tx.vout.size(); ++n)
11296  {
11297  const cryptonote::txout_to_key* const out_key = boost::get<cryptonote::txout_to_key>(std::addressof(tx.vout[n].target));
11298  if (!out_key)
11299  continue;
11300 
11301  crypto::public_key derived_out_key;
11302  bool r = crypto::derive_public_key(derivation, n, address.m_spend_public_key, derived_out_key);
11303  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to derive public key");
11304  bool found = out_key->key == derived_out_key;
11305  crypto::key_derivation found_derivation = derivation;
11306  if (!found && !additional_derivations.empty())
11307  {
11308  r = crypto::derive_public_key(additional_derivations[n], n, address.m_spend_public_key, derived_out_key);
11309  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to derive public key");
11310  found = out_key->key == derived_out_key;
11311  found_derivation = additional_derivations[n];
11312  }
11313 
11314  if (found)
11315  {
11316  uint64_t amount;
11317  amount = tx.vout[n].amount;
11318  received += amount;
11319  }
11320  }
11321 }
11322 
11323 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)
11324 {
11327  req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
11328  req.decode_as_json = false;
11329  req.prune = true;
11330  m_daemon_rpc_mutex.lock();
11331  bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout);
11332  m_daemon_rpc_mutex.unlock();
11333  THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
11334  error::wallet_internal_error, "Failed to get transaction from daemon");
11335 
11337  crypto::hash tx_hash;
11338  if (res.txs.size() == 1)
11339  {
11340  ok = get_pruned_tx(res.txs.front(), tx, tx_hash);
11341  THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
11342  }
11343  else
11344  {
11345  cryptonote::blobdata tx_data;
11346  ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
11347  THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
11349  error::wallet_internal_error, "Failed to validate transaction from daemon");
11350  tx_hash = cryptonote::get_transaction_hash(tx);
11351  }
11352 
11354  "Failed to get the right transaction from daemon");
11355  THROW_WALLET_EXCEPTION_IF(!additional_derivations.empty() && additional_derivations.size() != tx.vout.size(), error::wallet_internal_error,
11356  "The size of additional derivations is wrong");
11357 
11358  check_tx_key_helper(tx, derivation, additional_derivations, address, received);
11359 
11360  in_pool = res.txs.front().in_pool;
11361  confirmations = 0;
11362  if (!in_pool)
11363  {
11364  std::string err;
11365  uint64_t bc_height = get_daemon_blockchain_height(err);
11366  if (err.empty())
11367  confirmations = bc_height - res.txs.front().block_height;
11368  }
11369 }
11370 
11371 std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message)
11372 {
11373  // fetch tx pubkey from the daemon
11376  req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
11377  req.decode_as_json = false;
11378  req.prune = true;
11379  m_daemon_rpc_mutex.lock();
11380  bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout);
11381  m_daemon_rpc_mutex.unlock();
11382  THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
11383  error::wallet_internal_error, "Failed to get transaction from daemon");
11384 
11386  crypto::hash tx_hash;
11387  if (res.txs.size() == 1)
11388  {
11389  ok = get_pruned_tx(res.txs.front(), tx, tx_hash);
11390  THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
11391  }
11392  else
11393  {
11394  cryptonote::blobdata tx_data;
11395  ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
11396  THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
11398  error::wallet_internal_error, "Failed to validate transaction from daemon");
11399  tx_hash = cryptonote::get_transaction_hash(tx);
11400  }
11401 
11402  CHECK_AND_ASSERT_THROW_MES(tx.version == 1, "Tx proofs are for v1 transactions only");
11403  THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "Failed to get the right transaction from daemon");
11404 
11405  // determine if the address is found in the subaddress hash table (i.e. whether the proof is outbound or inbound)
11407  std::vector<crypto::secret_key> additional_tx_keys;
11408  const bool is_out = m_subaddresses.count(address.m_spend_public_key) == 0;
11409  if (is_out)
11410  {
11411  THROW_WALLET_EXCEPTION_IF(!get_tx_key(txid, tx_key, additional_tx_keys), error::wallet_internal_error, "Tx secret key wasn't found in the wallet file.");
11412  }
11413 
11414  return get_tx_proof(tx, tx_key, additional_tx_keys, address, is_subaddress, message);
11415 }
11416 
11417 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
11418 {
11419  hw::device &hwdev = m_account.get_device();
11420  rct::key aP;
11421  // determine if the address is found in the subaddress hash table (i.e. whether the proof is outbound or inbound)
11422  const bool is_out = m_subaddresses.count(address.m_spend_public_key) == 0;
11423 
11425  std::string prefix_data((const char*)&txid, sizeof(crypto::hash));
11426  prefix_data += message;
11427  crypto::hash prefix_hash;
11428  crypto::cn_fast_hash(prefix_data.data(), prefix_data.size(), prefix_hash);
11429 
11430  std::vector<crypto::public_key> shared_secret;
11431  std::vector<crypto::signature> sig;
11432  std::string sig_str;
11433  if (is_out)
11434  {
11435  const size_t num_sigs = 1 + additional_tx_keys.size();
11436  shared_secret.resize(num_sigs);
11437  sig.resize(num_sigs);
11438 
11439  hwdev.scalarmultKey(aP, rct::pk2rct(address.m_view_public_key), rct::sk2rct(tx_key));
11440  shared_secret[0] = rct::rct2pk(aP);
11441  crypto::public_key tx_pub_key;
11442  if (is_subaddress)
11443  {
11444  hwdev.scalarmultKey(aP, rct::pk2rct(address.m_spend_public_key), rct::sk2rct(tx_key));
11445  tx_pub_key = rct2pk(aP);
11446  hwdev.generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[0], tx_key, sig[0]);
11447  }
11448  else
11449  {
11450  hwdev.secret_key_to_public_key(tx_key, tx_pub_key);
11451  hwdev.generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, boost::none, shared_secret[0], tx_key, sig[0]);
11452  }
11453  for (size_t i = 1; i < num_sigs; ++i)
11454  {
11455  hwdev.scalarmultKey(aP, rct::pk2rct(address.m_view_public_key), rct::sk2rct(additional_tx_keys[i - 1]));
11456  shared_secret[i] = rct::rct2pk(aP);
11457  if (is_subaddress)
11458  {
11459  hwdev.scalarmultKey(aP, rct::pk2rct(address.m_spend_public_key), rct::sk2rct(additional_tx_keys[i - 1]));
11460  tx_pub_key = rct2pk(aP);
11461  hwdev.generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[i], additional_tx_keys[i - 1], sig[i]);
11462  }
11463  else
11464  {
11465  hwdev.secret_key_to_public_key(additional_tx_keys[i - 1], tx_pub_key);
11466  hwdev.generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, boost::none, shared_secret[i], additional_tx_keys[i - 1], sig[i]);
11467  }
11468  }
11469  sig_str = std::string("OutProofV1");
11470  }
11471  else
11472  {
11474  THROW_WALLET_EXCEPTION_IF(tx_pub_key == null_pkey, error::wallet_internal_error, "Tx pubkey was not found");
11475 
11476  std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(tx);
11477  const size_t num_sigs = 1 + additional_tx_pub_keys.size();
11478  shared_secret.resize(num_sigs);
11479  sig.resize(num_sigs);
11480 
11481  const crypto::secret_key& a = m_account.get_keys().m_view_secret_key;
11482  hwdev.scalarmultKey(aP, rct::pk2rct(tx_pub_key), rct::sk2rct(a));
11483  shared_secret[0] = rct2pk(aP);
11484  if (is_subaddress)
11485  {
11486  hwdev.generate_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, address.m_spend_public_key, shared_secret[0], a, sig[0]);
11487  }
11488  else
11489  {
11490  hwdev.generate_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, boost::none, shared_secret[0], a, sig[0]);
11491  }
11492  for (size_t i = 1; i < num_sigs; ++i)
11493  {
11494  hwdev.scalarmultKey(aP,rct::pk2rct(additional_tx_pub_keys[i - 1]), rct::sk2rct(a));
11495  shared_secret[i] = rct2pk(aP);
11496  if (is_subaddress)
11497  {
11498  hwdev.generate_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[i - 1], address.m_spend_public_key, shared_secret[i], a, sig[i]);
11499  }
11500  else
11501  {
11502  hwdev.generate_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[i - 1], boost::none, shared_secret[i], a, sig[i]);
11503  }
11504  }
11505  sig_str = std::string("InProofV1");
11506  }
11507  const size_t num_sigs = shared_secret.size();
11508 
11509  // check if this address actually received any funds
11510  crypto::key_derivation derivation;
11511  THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(shared_secret[0], rct::rct2sk(rct::I), derivation), error::wallet_internal_error, "Failed to generate key derivation");
11512  std::vector<crypto::key_derivation> additional_derivations(num_sigs - 1);
11513  for (size_t i = 1; i < num_sigs; ++i)
11514  THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(shared_secret[i], rct::rct2sk(rct::I), additional_derivations[i - 1]), error::wallet_internal_error, "Failed to generate key derivation");
11515  uint64_t received;
11516  check_tx_key_helper(tx, derivation, additional_derivations, address, received);
11517  THROW_WALLET_EXCEPTION_IF(!received, error::wallet_internal_error, tr("No funds received in this tx."));
11518 
11519  // concatenate all signature strings
11520  for (size_t i = 0; i < num_sigs; ++i)
11521  sig_str +=
11522  tools::base58::encode(std::string((const char *)&shared_secret[i], sizeof(crypto::public_key))) +
11523  tools::base58::encode(std::string((const char *)&sig[i], sizeof(crypto::signature)));
11524  return sig_str;
11525 }
11526 
11527 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)
11528 {
11529  // fetch tx pubkey from the daemon
11532  req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
11533  req.decode_as_json = false;
11534  req.prune = true;
11535  m_daemon_rpc_mutex.lock();
11536  bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout);
11537  m_daemon_rpc_mutex.unlock();
11538  THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
11539  error::wallet_internal_error, "Failed to get transaction from daemon");
11540 
11542  crypto::hash tx_hash;
11543  if (res.txs.size() == 1)
11544  {
11545  ok = get_pruned_tx(res.txs.front(), tx, tx_hash);
11546  THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
11547  }
11548  else
11549  {
11550  cryptonote::blobdata tx_data;
11551  ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
11552  THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
11554  error::wallet_internal_error, "Failed to validate transaction from daemon");
11555  tx_hash = cryptonote::get_transaction_hash(tx);
11556  }
11557 
11558  THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "Failed to get the right transaction from daemon");
11559 
11560  if (!check_tx_proof(tx, address, is_subaddress, message, sig_str, received))
11561  return false;
11562 
11563  in_pool = res.txs.front().in_pool;
11564  confirmations = 0;
11565  if (!in_pool)
11566  {
11567  std::string err;
11568  uint64_t bc_height = get_daemon_blockchain_height(err);
11569  if (err.empty())
11570  confirmations = bc_height - res.txs.front().block_height;
11571  }
11572 
11573  return true;
11574 }
11575 
11576 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
11577 {
11578  CHECK_AND_ASSERT_THROW_MES(tx.version == 1, "Tx proofs are for v1 transactions only");
11579  const bool is_out = sig_str.substr(0, 3) == "Out";
11580  const std::string header = is_out ? "OutProofV1" : "InProofV1";
11581  const size_t header_len = header.size();
11582  THROW_WALLET_EXCEPTION_IF(sig_str.size() < header_len || sig_str.substr(0, header_len) != header, error::wallet_internal_error,
11583  "Signature header check error");
11584 
11585  // decode base58
11586  std::vector<crypto::public_key> shared_secret(1);
11587  std::vector<crypto::signature> sig(1);
11588  const size_t pk_len = tools::base58::encode(std::string((const char *)&shared_secret[0], sizeof(crypto::public_key))).size();
11589  const size_t sig_len = tools::base58::encode(std::string((const char *)&sig[0], sizeof(crypto::signature))).size();
11590  const size_t num_sigs = (sig_str.size() - header_len) / (pk_len + sig_len);
11591  THROW_WALLET_EXCEPTION_IF(sig_str.size() != header_len + num_sigs * (pk_len + sig_len), error::wallet_internal_error,
11592  "Wrong signature size");
11593  shared_secret.resize(num_sigs);
11594  sig.resize(num_sigs);
11595  for (size_t i = 0; i < num_sigs; ++i)
11596  {
11597  std::string pk_decoded;
11598  std::string sig_decoded;
11599  const size_t offset = header_len + i * (pk_len + sig_len);
11600  THROW_WALLET_EXCEPTION_IF(!tools::base58::decode(sig_str.substr(offset, pk_len), pk_decoded), error::wallet_internal_error,
11601  "Signature decoding error");
11602  THROW_WALLET_EXCEPTION_IF(!tools::base58::decode(sig_str.substr(offset + pk_len, sig_len), sig_decoded), error::wallet_internal_error,
11603  "Signature decoding error");
11604  THROW_WALLET_EXCEPTION_IF(sizeof(crypto::public_key) != pk_decoded.size() || sizeof(crypto::signature) != sig_decoded.size(), error::wallet_internal_error,
11605  "Signature decoding error");
11606  memcpy(&shared_secret[i], pk_decoded.data(), sizeof(crypto::public_key));
11607  memcpy(&sig[i], sig_decoded.data(), sizeof(crypto::signature));
11608  }
11609 
11611  THROW_WALLET_EXCEPTION_IF(tx_pub_key == null_pkey, error::wallet_internal_error, "Tx pubkey was not found");
11612 
11613  std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(tx);
11614  THROW_WALLET_EXCEPTION_IF(additional_tx_pub_keys.size() + 1 != num_sigs, error::wallet_internal_error, "Signature size mismatch with additional tx pubkeys");
11615 
11617  std::string prefix_data((const char*)&txid, sizeof(crypto::hash));
11618  prefix_data += message;
11619  crypto::hash prefix_hash;
11620  crypto::cn_fast_hash(prefix_data.data(), prefix_data.size(), prefix_hash);
11621 
11622  // check signature
11623  std::vector<int> good_signature(num_sigs, 0);
11624  if (is_out)
11625  {
11626  good_signature[0] = is_subaddress ?
11627  crypto::check_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[0], sig[0]) :
11628  crypto::check_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, boost::none, shared_secret[0], sig[0]);
11629 
11630  for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
11631  {
11632  good_signature[i + 1] = is_subaddress ?
11633  crypto::check_tx_proof(prefix_hash, additional_tx_pub_keys[i], address.m_view_public_key, address.m_spend_public_key, shared_secret[i + 1], sig[i + 1]) :
11634  crypto::check_tx_proof(prefix_hash, additional_tx_pub_keys[i], address.m_view_public_key, boost::none, shared_secret[i + 1], sig[i + 1]);
11635  }
11636  }
11637  else
11638  {
11639  good_signature[0] = is_subaddress ?
11640  crypto::check_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, address.m_spend_public_key, shared_secret[0], sig[0]) :
11641  crypto::check_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, boost::none, shared_secret[0], sig[0]);
11642 
11643  for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
11644  {
11645  good_signature[i + 1] = is_subaddress ?
11646  crypto::check_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[i], address.m_spend_public_key, shared_secret[i + 1], sig[i + 1]) :
11647  crypto::check_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[i], boost::none, shared_secret[i + 1], sig[i + 1]);
11648  }
11649  }
11650 
11651  if (std::any_of(good_signature.begin(), good_signature.end(), [](int i) { return i > 0; }))
11652  {
11653  // obtain key derivation by multiplying scalar 1 to the shared secret
11654  crypto::key_derivation derivation;
11655  if (good_signature[0])
11656  THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(shared_secret[0], rct::rct2sk(rct::I), derivation), error::wallet_internal_error, "Failed to generate key derivation");
11657 
11658  std::vector<crypto::key_derivation> additional_derivations(num_sigs - 1);
11659  for (size_t i = 1; i < num_sigs; ++i)
11660  if (good_signature[i])
11661  THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(shared_secret[i], rct::rct2sk(rct::I), additional_derivations[i - 1]), error::wallet_internal_error, "Failed to generate key derivation");
11662 
11663  check_tx_key_helper(tx, derivation, additional_derivations, address, received);
11664  return true;
11665  }
11666  return false;
11667 }
11668 
11669 std::string wallet2::get_reserve_proof(const boost::optional<std::pair<uint32_t, uint64_t>> &account_minreserve, const std::string &message)
11670 {
11671  THROW_WALLET_EXCEPTION_IF(m_watch_only || m_multisig, error::wallet_internal_error, "Reserve proof can only be generated by a full wallet");
11672  THROW_WALLET_EXCEPTION_IF(balance_all(false) == 0, error::wallet_internal_error, "Zero balance");
11673  THROW_WALLET_EXCEPTION_IF(account_minreserve && balance(account_minreserve->first, false) < account_minreserve->second, error::wallet_internal_error,
11674  "Not enough balance in this account for the requested minimum reserve amount");
11675 
11676  // determine which outputs to include in the proof
11677  std::vector<size_t> selected_transfers;
11678  for (size_t i = 0; i < m_transfers.size(); ++i)
11679  {
11680  const transfer_details &td = m_transfers[i];
11681  if (!td.m_spent && !td.m_frozen && (!account_minreserve || account_minreserve->first == td.m_subaddr_index.major))
11682  selected_transfers.push_back(i);
11683  }
11684 
11685  if (account_minreserve)
11686  {
11687  THROW_WALLET_EXCEPTION_IF(account_minreserve->second == 0, error::wallet_internal_error, "Proved amount must be greater than 0");
11688  // minimize the number of outputs included in the proof, by only picking the N largest outputs that can cover the requested min reserve amount
11689  std::sort(selected_transfers.begin(), selected_transfers.end(), [&](const size_t a, const size_t b)
11690  { return m_transfers[a].amount() > m_transfers[b].amount(); });
11691  while (selected_transfers.size() >= 2 && m_transfers[selected_transfers[1]].amount() >= account_minreserve->second)
11692  selected_transfers.erase(selected_transfers.begin());
11693  size_t sz = 0;
11694  uint64_t total = 0;
11695  while (total < account_minreserve->second)
11696  {
11697  total += m_transfers[selected_transfers[sz]].amount();
11698  ++sz;
11699  }
11700  selected_transfers.resize(sz);
11701  }
11702 
11703  // compute signature prefix hash
11704  std::string prefix_data = message;
11705  prefix_data.append((const char*)&m_account.get_keys().m_account_address, sizeof(cryptonote::account_public_address));
11706  for (size_t i = 0; i < selected_transfers.size(); ++i)
11707  {
11708  prefix_data.append((const char*)&m_transfers[selected_transfers[i]].m_key_image, sizeof(crypto::key_image));
11709  }
11710  crypto::hash prefix_hash;
11711  crypto::cn_fast_hash(prefix_data.data(), prefix_data.size(), prefix_hash);
11712 
11713  // generate proof entries
11714  std::vector<reserve_proof_entry> proofs(selected_transfers.size());
11715  std::unordered_set<cryptonote::subaddress_index> subaddr_indices = { {0,0} };
11716  for (size_t i = 0; i < selected_transfers.size(); ++i)
11717  {
11718  transfer_details &td = m_transfers[selected_transfers[i]];
11719  reserve_proof_entry& proof = proofs[i];
11720  proof.txid = td.m_txid;
11722  proof.key_image = td.m_key_image;
11723  cryptonote::subaddress_index index2 = {td.m_subaddr_index.major + (td.m_subaddr_index.major != 0 ? m_account_major_offset : 0), td.m_subaddr_index.minor};
11724  td.m_subaddr_index = index2;
11725  subaddr_indices.insert(td.m_subaddr_index);
11726 
11727  // get tx pub key
11728  const crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(td.m_tx, td.m_pk_index);
11729  THROW_WALLET_EXCEPTION_IF(tx_pub_key == crypto::null_pkey, error::wallet_internal_error, "The tx public key isn't found");
11730  const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx);
11731 
11732  // determine which tx pub key was used for deriving the output key
11733  const crypto::public_key *tx_pub_key_used = &tx_pub_key;
11734  for (int i = 0; i < 2; ++i)
11735  {
11736  proof.shared_secret = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(*tx_pub_key_used), rct::sk2rct(m_account.get_keys().m_view_secret_key)));
11737  crypto::key_derivation derivation;
11738  THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(proof.shared_secret, rct::rct2sk(rct::I), derivation),
11739  error::wallet_internal_error, "Failed to generate key derivation");
11740  crypto::public_key subaddress_spendkey;
11741  THROW_WALLET_EXCEPTION_IF(!derive_subaddress_public_key(td.get_public_key(), derivation, proof.index_in_tx, subaddress_spendkey),
11742  error::wallet_internal_error, "Failed to derive subaddress public key");
11743  if (m_subaddresses.count(subaddress_spendkey) == 1)
11744  break;
11745  THROW_WALLET_EXCEPTION_IF(additional_tx_pub_keys.empty(), error::wallet_internal_error,
11746  "Normal tx pub key doesn't derive the expected output, while the additional tx pub keys are empty");
11748  "Neither normal tx pub key nor additional tx pub key derive the expected output key");
11749  tx_pub_key_used = &additional_tx_pub_keys[proof.index_in_tx];
11750  }
11751 
11752  // generate signature for shared secret
11753  crypto::generate_tx_proof(prefix_hash, m_account.get_keys().m_account_address.m_view_public_key, *tx_pub_key_used, boost::none, proof.shared_secret, m_account.get_keys().m_view_secret_key, proof.shared_secret_sig);
11754 
11755  // derive ephemeral secret key
11756  crypto::key_image ki;
11757  cryptonote::keypair ephemeral;
11758  const bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, td.get_public_key(), tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, ephemeral, ki, m_account.get_device(), m_account_major_offset);
11759  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
11760  THROW_WALLET_EXCEPTION_IF(ephemeral.pub != td.get_public_key(), error::wallet_internal_error, "Derived public key doesn't agree with the stored one");
11761 
11762  // generate signature for key image
11763  const std::vector<const crypto::public_key*> pubs = { &ephemeral.pub };
11764  crypto::generate_ring_signature(prefix_hash, td.m_key_image, &pubs[0], 1, ephemeral.sec, 0, &proof.key_image_sig);
11765  }
11766 
11767  // collect all subaddress spend keys that received those outputs and generate their signatures
11768  std::unordered_map<crypto::public_key, crypto::signature> subaddr_spendkeys;
11769  for (const cryptonote::subaddress_index &index : subaddr_indices)
11770  {
11771  crypto::secret_key subaddr_spend_skey = m_account.get_keys().m_spend_secret_key;
11772  if (!index.is_zero())
11773  {
11774  crypto::secret_key m = m_account.get_device().get_subaddress_secret_key(m_account.get_keys().m_view_secret_key, index);
11775  crypto::secret_key tmp = subaddr_spend_skey;
11776  sc_add((unsigned char*)&subaddr_spend_skey, (unsigned char*)&m, (unsigned char*)&tmp);
11777  }
11778  crypto::public_key subaddr_spend_pkey;
11779  secret_key_to_public_key(subaddr_spend_skey, subaddr_spend_pkey);
11780  crypto::generate_signature(prefix_hash, subaddr_spend_pkey, subaddr_spend_skey, subaddr_spendkeys[subaddr_spend_pkey]);
11781  }
11782 
11783  // serialize & encode
11784  std::ostringstream oss;
11786  ar << proofs << subaddr_spendkeys;
11787  return "ReserveProofV1" + tools::base58::encode(oss.str());
11788 }
11789 
11790 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)
11791 {
11792  uint32_t rpc_version;
11793  THROW_WALLET_EXCEPTION_IF(!check_connection(&rpc_version), error::wallet_internal_error, "Failed to connect to daemon: " + get_daemon_address());
11794  THROW_WALLET_EXCEPTION_IF(rpc_version < MAKE_CORE_RPC_VERSION(1, 0), error::wallet_internal_error, "Daemon RPC version is too old");
11795 
11796  static constexpr char header[] = "ReserveProofV1";
11797  THROW_WALLET_EXCEPTION_IF(!boost::string_ref{sig_str}.starts_with(header), error::wallet_internal_error,
11798  "Signature header check error");
11799 
11800  std::string sig_decoded;
11801  THROW_WALLET_EXCEPTION_IF(!tools::base58::decode(sig_str.substr(std::strlen(header)), sig_decoded), error::wallet_internal_error,
11802  "Signature decoding error");
11803 
11804  std::istringstream iss(sig_decoded);
11806  std::vector<reserve_proof_entry> proofs;
11807  std::unordered_map<crypto::public_key, crypto::signature> subaddr_spendkeys;
11808  ar >> proofs >> subaddr_spendkeys;
11809 
11810  THROW_WALLET_EXCEPTION_IF(subaddr_spendkeys.count(address.m_spend_public_key) == 0, error::wallet_internal_error,
11811  "The given address isn't found in the proof");
11812 
11813  // compute signature prefix hash
11814  std::string prefix_data = message;
11815  prefix_data.append((const char*)&address, sizeof(cryptonote::account_public_address));
11816  for (size_t i = 0; i < proofs.size(); ++i)
11817  {
11818  prefix_data.append((const char*)&proofs[i].key_image, sizeof(crypto::key_image));
11819  }
11820  crypto::hash prefix_hash;
11821  crypto::cn_fast_hash(prefix_data.data(), prefix_data.size(), prefix_hash);
11822 
11823  // fetch txes from daemon
11826  for (size_t i = 0; i < proofs.size(); ++i)
11827  gettx_req.txs_hashes.push_back(epee::string_tools::pod_to_hex(proofs[i].txid));
11828  gettx_req.decode_as_json = false;
11829  gettx_req.prune = true;
11830  m_daemon_rpc_mutex.lock();
11831  bool ok = invoke_http_json("/gettransactions", gettx_req, gettx_res, rpc_timeout);
11832  m_daemon_rpc_mutex.unlock();
11833  THROW_WALLET_EXCEPTION_IF(!ok || gettx_res.txs.size() != proofs.size(),
11834  error::wallet_internal_error, "Failed to get transaction from daemon");
11835 
11836  // check spent status
11839  for (size_t i = 0; i < proofs.size(); ++i)
11840  kispent_req.key_images.push_back(epee::string_tools::pod_to_hex(proofs[i].key_image));
11841  m_daemon_rpc_mutex.lock();
11842  ok = invoke_http_json("/is_key_image_spent", kispent_req, kispent_res, rpc_timeout);
11843  m_daemon_rpc_mutex.unlock();
11844  THROW_WALLET_EXCEPTION_IF(!ok || kispent_res.spent_status.size() != proofs.size(),
11845  error::wallet_internal_error, "Failed to get key image spent status from daemon");
11846 
11847  total = spent = 0;
11848  for (size_t i = 0; i < proofs.size(); ++i)
11849  {
11850  const reserve_proof_entry& proof = proofs[i];
11851  THROW_WALLET_EXCEPTION_IF(gettx_res.txs[i].in_pool, error::wallet_internal_error, "Tx is unconfirmed");
11852 
11854  crypto::hash tx_hash;
11855  ok = get_pruned_tx(gettx_res.txs[i], tx, tx_hash);
11856  THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
11857 
11858  THROW_WALLET_EXCEPTION_IF(tx_hash != proof.txid, error::wallet_internal_error, "Failed to get the right transaction from daemon");
11859 
11860  THROW_WALLET_EXCEPTION_IF(proof.index_in_tx >= tx.vout.size(), error::wallet_internal_error, "index_in_tx is out of bound");
11861 
11862  const cryptonote::txout_to_key* const out_key = boost::get<cryptonote::txout_to_key>(std::addressof(tx.vout[proof.index_in_tx].target));
11863  THROW_WALLET_EXCEPTION_IF(!out_key, error::wallet_internal_error, "Output key wasn't found")
11864 
11865  // get tx pub key
11866  const crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx);
11867  THROW_WALLET_EXCEPTION_IF(tx_pub_key == crypto::null_pkey, error::wallet_internal_error, "The tx public key isn't found");
11868  const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(tx);
11869 
11870  // check singature for shared secret
11871  ok = crypto::check_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, boost::none, proof.shared_secret, proof.shared_secret_sig);
11872  if (!ok && additional_tx_pub_keys.size() == tx.vout.size())
11873  ok = crypto::check_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[proof.index_in_tx], boost::none, proof.shared_secret, proof.shared_secret_sig);
11874  if (!ok)
11875  return false;
11876 
11877  // check signature for key image
11878  const std::vector<const crypto::public_key*> pubs = { &out_key->key };
11879  ok = crypto::check_ring_signature(prefix_hash, proof.key_image, &pubs[0], 1, &proof.key_image_sig);
11880  if (!ok)
11881  return false;
11882 
11883  // check if the address really received the fund
11884  crypto::key_derivation derivation;
11885  THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(proof.shared_secret, rct::rct2sk(rct::I), derivation), error::wallet_internal_error, "Failed to generate key derivation");
11886  crypto::public_key subaddr_spendkey;
11887  crypto::derive_subaddress_public_key(out_key->key, derivation, proof.index_in_tx, subaddr_spendkey);
11888  THROW_WALLET_EXCEPTION_IF(subaddr_spendkeys.count(subaddr_spendkey) == 0, error::wallet_internal_error,
11889  "The address doesn't seem to have received the fund");
11890 
11891  // check amount
11892  uint64_t amount = tx.vout[proof.index_in_tx].amount;
11893  if (amount == 0)
11894  {
11895  // decode rct
11896  crypto::secret_key shared_secret;
11897  crypto::derivation_to_scalar(derivation, proof.index_in_tx, shared_secret);
11898  rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[proof.index_in_tx];
11899  rct::ecdhDecode(ecdh_info, rct::sk2rct(shared_secret), tx.rct_signatures.type == rct::RCTTypeBulletproof2);
11900  amount = rct::h2d(ecdh_info.amount);
11901  }
11902  total += amount;
11903  if (kispent_res.spent_status[i])
11904  spent += amount;
11905  }
11906 
11907  // check signatures for all subaddress spend keys
11908  for (const auto &i : subaddr_spendkeys)
11909  {
11910  if (!crypto::check_signature(prefix_hash, i.first, i.second))
11911  return false;
11912  }
11913  return true;
11914 }
11915 
11916 std::string wallet2::get_wallet_file() const
11917 {
11918  return m_wallet_file;
11919 }
11920 
11921 std::string wallet2::get_keys_file() const
11922 {
11923  return m_keys_file;
11924 }
11925 
11926 std::string wallet2::get_daemon_address() const
11927 {
11928  return m_daemon_address;
11929 }
11930 
11931 uint64_t wallet2::get_daemon_blockchain_height(string &err) const
11932 {
11933  uint64_t height;
11934 
11935  boost::optional<std::string> result = m_node_rpc_proxy.get_height(height);
11936  if (result)
11937  {
11938  if (m_trusted_daemon)
11939  err = *result;
11940  else
11941  err = "daemon error";
11942  return 0;
11943  }
11944 
11945  err = "";
11946  return height;
11947 }
11948 
11949 uint64_t wallet2::get_daemon_blockchain_target_height(string &err)
11950 {
11951  err = "";
11952  uint64_t target_height = 0;
11953  const auto result = m_node_rpc_proxy.get_target_height(target_height);
11954  if (result && *result != CORE_RPC_STATUS_OK)
11955  {
11956  if (m_trusted_daemon)
11957  err = *result;
11958  else
11959  err = "daemon error";
11960  return 0;
11961  }
11962  return target_height;
11963 }
11964 
11965 uint64_t wallet2::get_approximate_blockchain_height() const
11966 {
11967  // time of v2 fork
11968  const time_t fork_time = m_nettype == TESTNET ? 1341378000 : m_nettype == STAGENET ? 1521000000 : 1538815057;
11969  // v2 fork block
11970  const uint64_t fork_block = m_nettype == TESTNET ? 190060 : m_nettype == STAGENET ? 32000 : 307500;
11971  // avg seconds per block
11972  const int seconds_per_block = DIFFICULTY_TARGET_V6;
11973  // Calculated blockchain height
11974  uint64_t approx_blockchain_height = fork_block + (time(NULL) - fork_time)/seconds_per_block;
11975  // testnet got some huge rollbacks, so the estimation is way off
11976  static const uint64_t approximate_testnet_rolled_back_blocks = 303967;
11977  if (m_nettype == TESTNET && approx_blockchain_height > approximate_testnet_rolled_back_blocks)
11978  approx_blockchain_height -= approximate_testnet_rolled_back_blocks;
11979  // estiamte blocks from v6
11980  if(m_nettype == MAINNET) {
11981  approx_blockchain_height += 82000;
11982  }
11983  LOG_PRINT_L2("Calculated blockchain height: " << approx_blockchain_height);
11984  return approx_blockchain_height;
11985 }
11986 
11987 void wallet2::set_tx_note(const crypto::hash &txid, const std::string &note)
11988 {
11989  m_tx_notes[txid] = note;
11990 }
11991 
11992 std::string wallet2::get_tx_note(const crypto::hash &txid) const
11993 {
11994  std::unordered_map<crypto::hash, std::string>::const_iterator i = m_tx_notes.find(txid);
11995  if (i == m_tx_notes.end())
11996  return std::string();
11997  return i->second;
11998 }
11999 
12000 void wallet2::set_tx_device_aux(const crypto::hash &txid, const std::string &aux)
12001 {
12002  m_tx_device[txid] = aux;
12003 }
12004 
12005 std::string wallet2::get_tx_device_aux(const crypto::hash &txid) const
12006 {
12007  std::unordered_map<crypto::hash, std::string>::const_iterator i = m_tx_device.find(txid);
12008  if (i == m_tx_device.end())
12009  return std::string();
12010  return i->second;
12011 }
12012 
12013 void wallet2::set_attribute(const std::string &key, const std::string &value)
12014 {
12015  m_attributes[key] = value;
12016 }
12017 
12018 std::string wallet2::get_attribute(const std::string &key) const
12019 {
12020  std::unordered_map<std::string, std::string>::const_iterator i = m_attributes.find(key);
12021  if (i == m_attributes.end())
12022  return std::string();
12023  return i->second;
12024 }
12025 
12026 void wallet2::set_description(const std::string &description)
12027 {
12028  set_attribute(ATTRIBUTE_DESCRIPTION, description);
12029 }
12030 
12031 std::string wallet2::get_description() const
12032 {
12033  return get_attribute(ATTRIBUTE_DESCRIPTION);
12034 }
12035 
12036 const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& wallet2::get_account_tags()
12037 {
12038  // ensure consistency
12039  if (m_account_tags.second.size() != get_num_subaddress_accounts())
12040  m_account_tags.second.resize(get_num_subaddress_accounts(), "");
12041  for (const std::string& tag : m_account_tags.second)
12042  {
12043  if (!tag.empty() && m_account_tags.first.count(tag) == 0)
12044  m_account_tags.first.insert({tag, ""});
12045  }
12046  for (auto i = m_account_tags.first.begin(); i != m_account_tags.first.end(); )
12047  {
12048  if (std::find(m_account_tags.second.begin(), m_account_tags.second.end(), i->first) == m_account_tags.second.end())
12049  i = m_account_tags.first.erase(i);
12050  else
12051  ++i;
12052  }
12053  return m_account_tags;
12054 }
12055 
12056 void wallet2::set_account_tag(const std::set<uint32_t> &account_indices, const std::string& tag)
12057 {
12058  for (uint32_t account_index : account_indices)
12059  {
12060  THROW_WALLET_EXCEPTION_IF(account_index >= get_num_subaddress_accounts(), error::wallet_internal_error, "Account index out of bound");
12061  if (m_account_tags.second[account_index] == tag)
12062  MDEBUG("This tag is already assigned to this account");
12063  else
12064  m_account_tags.second[account_index] = tag;
12065  }
12066  get_account_tags();
12067 }
12068 
12069 void wallet2::set_account_tag_description(const std::string& tag, const std::string& description)
12070 {
12071  THROW_WALLET_EXCEPTION_IF(tag.empty(), error::wallet_internal_error, "Tag must not be empty");
12072  THROW_WALLET_EXCEPTION_IF(m_account_tags.first.count(tag) == 0, error::wallet_internal_error, "Tag is unregistered");
12073  m_account_tags.first[tag] = description;
12074 }
12075 
12076 std::string wallet2::sign(const std::string &data) const
12077 {
12079  crypto::cn_fast_hash(data.data(), data.size(), hash);
12080  const cryptonote::account_keys &keys = m_account.get_keys();
12082  crypto::generate_signature(hash, keys.m_account_address.m_spend_public_key, keys.m_spend_secret_key, signature);
12083  return std::string("SigV1") + tools::base58::encode(std::string((const char *)&signature, sizeof(signature)));
12084 }
12085 
12086 bool wallet2::verify(const std::string &data, const cryptonote::account_public_address &address, const std::string &signature) const
12087 {
12088  const size_t header_len = strlen("SigV1");
12089  if (signature.size() < header_len || signature.substr(0, header_len) != "SigV1") {
12090  LOG_PRINT_L0("Signature header check error");
12091  return false;
12092  }
12094  crypto::cn_fast_hash(data.data(), data.size(), hash);
12095  std::string decoded;
12096  if (!tools::base58::decode(signature.substr(header_len), decoded)) {
12097  LOG_PRINT_L0("Signature decoding error");
12098  return false;
12099  }
12101  if (sizeof(s) != decoded.size()) {
12102  LOG_PRINT_L0("Signature decoding error");
12103  return false;
12104  }
12105  memcpy(&s, decoded.data(), sizeof(s));
12106  return crypto::check_signature(hash, address.m_spend_public_key, s);
12107 }
12108 
12109 std::string wallet2::sign_multisig_participant(const std::string& data) const
12110 {
12111  CHECK_AND_ASSERT_THROW_MES(m_multisig, "Wallet is not multisig");
12112 
12114  crypto::cn_fast_hash(data.data(), data.size(), hash);
12115  const cryptonote::account_keys &keys = m_account.get_keys();
12117  crypto::generate_signature(hash, get_multisig_signer_public_key(), keys.m_spend_secret_key, signature);
12118  return MULTISIG_SIGNATURE_MAGIC + tools::base58::encode(std::string((const char *)&signature, sizeof(signature)));
12119 }
12120 
12121 bool wallet2::verify_with_public_key(const std::string &data, const crypto::public_key &public_key, const std::string &signature) const
12122 {
12123  if (signature.size() < MULTISIG_SIGNATURE_MAGIC.size() || signature.substr(0, MULTISIG_SIGNATURE_MAGIC.size()) != MULTISIG_SIGNATURE_MAGIC) {
12124  MERROR("Signature header check error");
12125  return false;
12126  }
12128  crypto::cn_fast_hash(data.data(), data.size(), hash);
12129  std::string decoded;
12130  if (!tools::base58::decode(signature.substr(MULTISIG_SIGNATURE_MAGIC.size()), decoded)) {
12131  MERROR("Signature decoding error");
12132  return false;
12133  }
12135  if (sizeof(s) != decoded.size()) {
12136  MERROR("Signature decoding error");
12137  return false;
12138  }
12139  memcpy(&s, decoded.data(), sizeof(s));
12141 }
12142 //----------------------------------------------------------------------------------------------------
12143 crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const
12144 {
12145  std::vector<tx_extra_field> tx_extra_fields;
12146  if(!parse_tx_extra(td.m_tx.extra, tx_extra_fields))
12147  {
12148  // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key
12149  }
12150 
12151  // Due to a previous bug, there might be more than one tx pubkey in extra, one being
12152  // the result of a previously discarded signature.
12153  // For speed, since scanning for outputs is a slow process, we check whether extra
12154  // contains more than one pubkey. If not, the first one is returned. If yes, they're
12155  // checked for whether they yield at least one output
12156  tx_extra_pub_key pub_key_field;
12158  "Public key wasn't found in the transaction extra");
12159  const crypto::public_key tx_pub_key = pub_key_field.pub_key;
12160  bool two_found = find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, 1);
12161  if (!two_found) {
12162  // easy case, just one found
12163  return tx_pub_key;
12164  }
12165 
12166  // more than one, loop and search
12167  const cryptonote::account_keys& keys = m_account.get_keys();
12168  size_t pk_index = 0;
12169  hw::device &hwdev = m_account.get_device();
12170 
12171  while (find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, pk_index++)) {
12172  const crypto::public_key tx_pub_key = pub_key_field.pub_key;
12173  crypto::key_derivation derivation;
12174  bool r = hwdev.generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation);
12175  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation");
12176 
12177  for (size_t i = 0; i < td.m_tx.vout.size(); ++i)
12178  {
12179  tx_scan_info_t tx_scan_info;
12180  check_acc_out_precomp(td.m_tx.vout[i], derivation, {}, i, tx_scan_info);
12181  if (!tx_scan_info.error && tx_scan_info.received)
12182  return tx_pub_key;
12183  }
12184  }
12185 
12186  // we found no key yielding an output, but it might be in the additional
12187  // tx pub keys only, which we do not need to check, so return the first one
12188  return tx_pub_key;
12189 }
12190 
12191 bool wallet2::export_key_images(const std::string &filename) const
12192 {
12193  PERF_TIMER(export_key_images);
12194  std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> ski = export_key_images();
12196  const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
12197  const uint32_t offset = ski.first;
12198 
12199  std::string data;
12200  data.reserve(4 + ski.second.size() * (sizeof(crypto::key_image) + sizeof(crypto::signature)) + 2 * sizeof(crypto::public_key));
12201  data.resize(4);
12202  data[0] = offset & 0xff;
12203  data[1] = (offset >> 8) & 0xff;
12204  data[2] = (offset >> 16) & 0xff;
12205  data[3] = (offset >> 24) & 0xff;
12206  data += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key));
12207  data += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key));
12208  for (const auto &i: ski.second)
12209  {
12210  data += std::string((const char *)&i.first, sizeof(crypto::key_image));
12211  data += std::string((const char *)&i.second, sizeof(crypto::signature));
12212  }
12213 
12214  // encrypt data, keep magic plaintext
12215  PERF_TIMER(export_key_images_encrypt);
12216  std::string ciphertext = encrypt_with_view_secret_key(data);
12217  return epee::file_io_utils::save_string_to_file(filename, magic + ciphertext);
12218 }
12219 
12220 //----------------------------------------------------------------------------------------------------
12221 std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> wallet2::export_key_images(bool all) const
12222 {
12223  PERF_TIMER(export_key_images_raw);
12224  std::vector<std::pair<crypto::key_image, crypto::signature>> ski;
12225 
12226  size_t offset = 0;
12227  if (!all)
12228  {
12229  while (offset < m_transfers.size() && !m_transfers[offset].m_key_image_request)
12230  ++offset;
12231  }
12232 
12233  ski.reserve(m_transfers.size() - offset);
12234  for (size_t n = offset; n < m_transfers.size(); ++n)
12235  {
12236  const transfer_details &td = m_transfers[n];
12237 
12238  // get ephemeral public key
12241  "Output is not txout_to_key");
12242  const cryptonote::txout_to_key &o = boost::get<const cryptonote::txout_to_key>(out.target);
12243  const crypto::public_key pkey = o.key;
12244 
12245  // get tx pub key
12246  std::vector<tx_extra_field> tx_extra_fields;
12247  if(!parse_tx_extra(td.m_tx.extra, tx_extra_fields))
12248  {
12249  // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key
12250  }
12251 
12252  crypto::public_key tx_pub_key = get_tx_pub_key_from_received_outs(td);
12253  const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx);
12254 
12255  // generate ephemeral secret key
12256  crypto::key_image ki;
12257  cryptonote::keypair in_ephemeral;
12258  bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, pkey, tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, in_ephemeral, ki, m_account.get_device(), m_account_major_offset);
12259  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
12260 
12262  error::wallet_internal_error, "key_image generated not matched with cached key image");
12263  THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != pkey,
12264  error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
12265 
12266  // sign the key image with the output secret key
12268  std::vector<const crypto::public_key*> key_ptrs;
12269  key_ptrs.push_back(&pkey);
12270 
12271  crypto::generate_ring_signature((const crypto::hash&)td.m_key_image, td.m_key_image, key_ptrs, in_ephemeral.sec, 0, &signature);
12272 
12273  ski.push_back(std::make_pair(td.m_key_image, signature));
12274  }
12275  return std::make_pair(offset, ski);
12276 }
12277 
12278 uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent)
12279 {
12280  PERF_TIMER(import_key_images_fsu);
12281  std::string data;
12282  bool r = epee::file_io_utils::load_file_to_string(filename, data);
12283 
12284  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, std::string(tr("failed to read file ")) + filename);
12285 
12286  const size_t magiclen = strlen(KEY_IMAGE_EXPORT_FILE_MAGIC);
12287  if (data.size() < magiclen || memcmp(data.data(), KEY_IMAGE_EXPORT_FILE_MAGIC, magiclen))
12288  {
12289  THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Bad key image export file magic in ") + filename);
12290  }
12291 
12292  try
12293  {
12294  PERF_TIMER(import_key_images_decrypt);
12295  data = decrypt_with_view_secret_key(std::string(data, magiclen));
12296  }
12297  catch (const std::exception &e)
12298  {
12299  THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to decrypt ") + filename + ": " + e.what());
12300  }
12301 
12302  const size_t headerlen = 4 + 2 * sizeof(crypto::public_key);
12303  THROW_WALLET_EXCEPTION_IF(data.size() < headerlen, error::wallet_internal_error, std::string("Bad data size from file ") + filename);
12304  const uint32_t offset = (uint8_t)data[0] | (((uint8_t)data[1]) << 8) | (((uint8_t)data[2]) << 16) | (((uint8_t)data[3]) << 24);
12305  const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[4];
12306  const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[4 + sizeof(crypto::public_key)];
12307  const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
12308  if (public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key)
12309  {
12310  THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string( "Key images from ") + filename + " are for a different account");
12311  }
12312  THROW_WALLET_EXCEPTION_IF(offset > m_transfers.size(), error::wallet_internal_error, "Offset larger than known outputs");
12313 
12314  const size_t record_size = sizeof(crypto::key_image) + sizeof(crypto::signature);
12315  THROW_WALLET_EXCEPTION_IF((data.size() - headerlen) % record_size,
12316  error::wallet_internal_error, std::string("Bad data size from file ") + filename);
12317  size_t nki = (data.size() - headerlen) / record_size;
12318 
12319  std::vector<std::pair<crypto::key_image, crypto::signature>> ski;
12320  ski.reserve(nki);
12321  for (size_t n = 0; n < nki; ++n)
12322  {
12323  crypto::key_image key_image = *reinterpret_cast<const crypto::key_image*>(&data[headerlen + n * record_size]);
12324  crypto::signature signature = *reinterpret_cast<const crypto::signature*>(&data[headerlen + n * record_size + sizeof(crypto::key_image)]);
12325 
12326  ski.push_back(std::make_pair(key_image, signature));
12327  }
12328 
12329  return import_key_images(ski, offset, spent, unspent);
12330 }
12331 
12332 //----------------------------------------------------------------------------------------------------
12333 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)
12334 {
12335  PERF_TIMER(import_key_images_lots);
12337  COMMAND_RPC_IS_KEY_IMAGE_SPENT::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
12338 
12339  THROW_WALLET_EXCEPTION_IF(offset > m_transfers.size(), error::wallet_internal_error, "Offset larger than known outputs");
12340  THROW_WALLET_EXCEPTION_IF(signed_key_images.size() > m_transfers.size() - offset, error::wallet_internal_error,
12341  "The blockchain is out of date compared to the signed key images");
12342 
12343  if (signed_key_images.empty() && offset == 0)
12344  {
12345  spent = 0;
12346  unspent = 0;
12347  return 0;
12348  }
12349 
12350  req.key_images.reserve(signed_key_images.size());
12351 
12352  PERF_TIMER_START(import_key_images_A);
12353  for (size_t n = 0; n < signed_key_images.size(); ++n)
12354  {
12355  const transfer_details &td = m_transfers[n + offset];
12356  const crypto::key_image &key_image = signed_key_images[n].first;
12357  const crypto::signature &signature = signed_key_images[n].second;
12358 
12359  // get ephemeral public key
12362  "Non txout_to_key output found");
12363  const cryptonote::txout_to_key &o = boost::get<cryptonote::txout_to_key>(out.target);
12364  const crypto::public_key pkey = o.key;
12365 
12366  if (!td.m_key_image_known || !(key_image == td.m_key_image))
12367  {
12368  std::vector<const crypto::public_key*> pkeys;
12369  pkeys.push_back(&pkey);
12371  error::wallet_internal_error, "Key image out of validity domain: input " + boost::lexical_cast<std::string>(n + offset) + "/"
12372  + boost::lexical_cast<std::string>(signed_key_images.size()) + ", key image " + epee::string_tools::pod_to_hex(key_image));
12373 
12375  error::signature_check_failed, boost::lexical_cast<std::string>(n + offset) + "/"
12376  + boost::lexical_cast<std::string>(signed_key_images.size()) + ", key image " + epee::string_tools::pod_to_hex(key_image)
12377  + ", signature " + epee::string_tools::pod_to_hex(signature) + ", pubkey " + epee::string_tools::pod_to_hex(*pkeys[0]));
12378  }
12379  req.key_images.push_back(epee::string_tools::pod_to_hex(key_image));
12380  }
12381  PERF_TIMER_STOP(import_key_images_A);
12382 
12383  PERF_TIMER_START(import_key_images_B);
12384  for (size_t n = 0; n < signed_key_images.size(); ++n)
12385  {
12386  m_transfers[n + offset].m_key_image = signed_key_images[n].first;
12387  m_key_images[m_transfers[n + offset].m_key_image] = n + offset;
12388  m_transfers[n + offset].m_key_image_known = true;
12389  m_transfers[n + offset].m_key_image_request = false;
12390  m_transfers[n + offset].m_key_image_partial = false;
12391  }
12392  PERF_TIMER_STOP(import_key_images_B);
12393 
12394  if(check_spent)
12395  {
12396  PERF_TIMER(import_key_images_RPC);
12397  m_daemon_rpc_mutex.lock();
12398  bool r = invoke_http_json("/is_key_image_spent", req, daemon_resp, rpc_timeout);
12399  m_daemon_rpc_mutex.unlock();
12400  THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "is_key_image_spent");
12401  THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "is_key_image_spent");
12402  THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::is_key_image_spent_error, daemon_resp.status);
12403  THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != signed_key_images.size(), error::wallet_internal_error,
12404  "daemon returned wrong response for is_key_image_spent, wrong amounts count = " +
12405  std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(signed_key_images.size()));
12406  for (size_t n = 0; n < daemon_resp.spent_status.size(); ++n)
12407  {
12408  transfer_details &td = m_transfers[n + offset];
12409  td.m_spent = daemon_resp.spent_status[n] != SPENT_STATUS::UNSPENT;
12410  }
12411  }
12412  spent = 0;
12413  unspent = 0;
12414  std::unordered_set<crypto::hash> spent_txids; // For each spent key image, search for a tx in m_transfers that uses it as input.
12415  std::vector<size_t> swept_transfers; // If such a spending tx wasn't found in m_transfers, this means the spending tx
12416  // was created by sweep_all, so we can't know the spent height and other detailed info.
12417  std::unordered_map<crypto::key_image, crypto::hash> spent_key_images;
12418 
12419  PERF_TIMER_START(import_key_images_C);
12420  for (const transfer_details &td: m_transfers)
12421  {
12422  for (const cryptonote::txin_v& in : td.m_tx.vin)
12423  {
12424  if (in.type() == typeid(cryptonote::txin_to_key))
12425  spent_key_images.insert(std::make_pair(boost::get<cryptonote::txin_to_key>(in).k_image, td.m_txid));
12426  }
12427  }
12428  PERF_TIMER_STOP(import_key_images_C);
12429 
12430  // accumulate outputs before the updated data
12431  for(size_t i = 0; i < offset; ++i)
12432  {
12433  const transfer_details &td = m_transfers[i];
12434  if (td.m_frozen)
12435  continue;
12436  uint64_t amount = td.amount();
12437  if (td.m_spent)
12438  spent += amount;
12439  else
12440  unspent += amount;
12441  }
12442 
12443  PERF_TIMER_START(import_key_images_D);
12444  for(size_t i = 0; i < signed_key_images.size(); ++i)
12445  {
12446  const transfer_details &td = m_transfers[i + offset];
12447  if (td.m_frozen)
12448  continue;
12449  uint64_t amount = td.amount();
12450  if (td.m_spent)
12451  spent += amount;
12452  else
12453  unspent += amount;
12454  LOG_PRINT_L2("Transfer " << i << ": " << print_etn(amount) << " (" << td.m_global_output_index << "): "
12455  << (td.m_spent ? "spent" : "unspent") << " (key image " << req.key_images[i] << ")");
12456 
12457  if (i < daemon_resp.spent_status.size() && daemon_resp.spent_status[i] == SPENT_STATUS::SPENT_IN_BLOCKCHAIN)
12458  {
12459  const std::unordered_map<crypto::key_image, crypto::hash>::const_iterator skii = spent_key_images.find(td.m_key_image);
12460  if (skii == spent_key_images.end())
12461  swept_transfers.push_back(i);
12462  else
12463  spent_txids.insert(skii->second);
12464  }
12465  }
12466  PERF_TIMER_STOP(import_key_images_D);
12467 
12468  MDEBUG("Total: " << print_etn(spent) << " spent, " << print_etn(unspent) << " unspent");
12469 
12470  if (check_spent)
12471  {
12472  // query outgoing txes
12475  gettxs_req.decode_as_json = false;
12476  gettxs_req.prune = true;
12477  gettxs_req.txs_hashes.reserve(spent_txids.size());
12478  for (const crypto::hash& spent_txid : spent_txids)
12479  gettxs_req.txs_hashes.push_back(epee::string_tools::pod_to_hex(spent_txid));
12480 
12481 
12482  PERF_TIMER_START(import_key_images_E);
12483  m_daemon_rpc_mutex.lock();
12484  bool r = invoke_http_json("/gettransactions", gettxs_req, gettxs_res, rpc_timeout);
12485  m_daemon_rpc_mutex.unlock();
12487  THROW_WALLET_EXCEPTION_IF(gettxs_res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions");
12488  THROW_WALLET_EXCEPTION_IF(gettxs_res.txs.size() != spent_txids.size(), error::wallet_internal_error,
12489  "daemon returned wrong response for gettransactions, wrong count = " + std::to_string(gettxs_res.txs.size()) + ", expected " + std::to_string(spent_txids.size()));
12490  PERF_TIMER_STOP(import_key_images_E);
12491 
12492  // process each outgoing tx
12493  PERF_TIMER_START(import_key_images_F);
12494  auto spent_txid = spent_txids.begin();
12495  hw::device &hwdev = m_account.get_device();
12496  auto it = spent_txids.begin();
12497  for (const COMMAND_RPC_GET_TRANSACTIONS::entry& e : gettxs_res.txs)
12498  {
12499  THROW_WALLET_EXCEPTION_IF(e.in_pool, error::wallet_internal_error, "spent tx isn't supposed to be in txpool");
12500 
12501  cryptonote::transaction spent_tx;
12502  crypto::hash spnet_txid_parsed;
12503  THROW_WALLET_EXCEPTION_IF(!get_pruned_tx(e, spent_tx, spnet_txid_parsed), error::wallet_internal_error, "Failed to get tx from daemon");
12504  THROW_WALLET_EXCEPTION_IF(!(spnet_txid_parsed == *it), error::wallet_internal_error, "parsed txid mismatch");
12505  ++it;
12506 
12507  // get received (change) amount
12508  uint64_t tx_etn_got_in_outs = 0;
12509  const cryptonote::account_keys& keys = m_account.get_keys();
12510  const crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(spent_tx);
12511  crypto::key_derivation derivation;
12512  bool r = hwdev.generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation);
12513  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation");
12514  const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(spent_tx);
12515  std::vector<crypto::key_derivation> additional_derivations;
12516  for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
12517  {
12518  additional_derivations.push_back({});
12519  r = hwdev.generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back());
12520  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation");
12521  }
12522  size_t output_index = 0;
12523  bool miner_tx = cryptonote::is_coinbase(spent_tx);
12524  for (const cryptonote::tx_out& out : spent_tx.vout)
12525  {
12526  tx_scan_info_t tx_scan_info;
12527  check_acc_out_precomp(out, derivation, additional_derivations, output_index, tx_scan_info);
12528  THROW_WALLET_EXCEPTION_IF(tx_scan_info.error, error::wallet_internal_error, "check_acc_out_precomp failed");
12529  if (tx_scan_info.received)
12530  {
12531  if (tx_scan_info.etn_transfered == 0 && !miner_tx)
12532  {
12533  rct::key mask;
12534  tx_scan_info.etn_transfered = tools::decodeRct(spent_tx.rct_signatures, tx_scan_info.received->derivation, output_index, mask, hwdev);
12535  }
12536  THROW_WALLET_EXCEPTION_IF(tx_etn_got_in_outs >= std::numeric_limits<uint64_t>::max() - tx_scan_info.etn_transfered,
12537  error::wallet_internal_error, "Overflow in received amounts");
12538  tx_etn_got_in_outs += tx_scan_info.etn_transfered;
12539  }
12540  ++output_index;
12541  }
12542 
12543  // get spent amount
12544  uint64_t tx_etn_spent_in_ins = 0;
12545  uint32_t subaddr_account = (uint32_t)-1;
12546  std::set<uint32_t> subaddr_indices;
12547  for (const cryptonote::txin_v& in : spent_tx.vin)
12548  {
12549  if (in.type() != typeid(cryptonote::txin_to_key))
12550  continue;
12551  auto it = m_key_images.find(boost::get<cryptonote::txin_to_key>(in).k_image);
12552  if (it != m_key_images.end())
12553  {
12554  THROW_WALLET_EXCEPTION_IF(it->second >= m_transfers.size(), error::wallet_internal_error, std::string("Key images cache contains illegal transfer offset: ") + std::to_string(it->second) + std::string(" m_transfers.size() = ") + std::to_string(m_transfers.size()));
12555  const transfer_details& td = m_transfers[it->second];
12556  uint64_t amount = boost::get<cryptonote::txin_to_key>(in).amount;
12557  if (amount > 0)
12558  {
12560  std::string("Inconsistent amount in tx input: got ") + print_etn(amount) +
12561  std::string(", expected ") + print_etn(td.amount()));
12562  }
12563  amount = td.amount();
12564  tx_etn_spent_in_ins += amount;
12565 
12566  LOG_PRINT_L0("Spent ETN: " << print_etn(amount) << ", with tx: " << *spent_txid);
12567  set_spent(it->second, e.block_height);
12568  if (m_callback)
12569  m_callback->on_etn_spent(e.block_height, *spent_txid, spent_tx, amount, spent_tx, td.m_subaddr_index);
12570  if (subaddr_account != (uint32_t)-1 && subaddr_account != td.m_subaddr_index.major)
12571  LOG_PRINT_L0("WARNING: This tx spends outputs received by different subaddress accounts, which isn't supposed to happen");
12572  subaddr_account = td.m_subaddr_index.major;
12573  subaddr_indices.insert(td.m_subaddr_index.minor);
12574  }
12575  }
12576 
12577  // create outgoing payment
12578  process_outgoing(*spent_txid, spent_tx, e.block_height, e.block_timestamp, tx_etn_spent_in_ins, tx_etn_got_in_outs, subaddr_account, subaddr_indices);
12579 
12580  // erase corresponding incoming payment
12581  for (auto j = m_payments.begin(); j != m_payments.end(); ++j)
12582  {
12583  if (j->second.m_tx_hash == *spent_txid)
12584  {
12585  m_payments.erase(j);
12586  break;
12587  }
12588  }
12589 
12590  ++spent_txid;
12591  }
12592  PERF_TIMER_STOP(import_key_images_F);
12593 
12594  PERF_TIMER_START(import_key_images_G);
12595  for (size_t n : swept_transfers)
12596  {
12597  const transfer_details& td = m_transfers[n];
12599  pd.m_change = (uint64_t)-1; // change is unknown
12600  pd.m_amount_in = pd.m_amount_out = td.amount(); // fee is unknown
12601  pd.m_block_height = 0; // spent block height is unknown
12602  pd.m_is_migration = td.m_tx.version == 2;
12603  if(td.m_tx.version == 3){
12604  cryptonote::account_public_address dest_address = boost::get<cryptonote::txout_to_key_public>(td.m_tx.vout[0].target).address;
12605  bool is_portal_address;
12606  if(m_nettype == MAINNET){
12607  is_portal_address = epee::string_tools::pod_to_hex(dest_address.m_spend_public_key) == "8ce0f34fd37c7f7d07c44024eb5b3cdf275d1b3e75c3464b808dce532e861137" && epee::string_tools::pod_to_hex(dest_address.m_view_public_key) == "2b95a2eb2c62253c57e82b082b850bbf22a1a7829aaea09c7c1511c1cced4375";
12608  }else{
12609  is_portal_address = epee::string_tools::pod_to_hex(dest_address.m_spend_public_key) == "5bd0c0e25eee6133850edd2b255ed9e3d6bb99fd5f08b7b5cf7f2618ad6ff2a3" && epee::string_tools::pod_to_hex(dest_address.m_view_public_key) == "5866666666666666666666666666666666666666666666666666666666666666";
12610  }
12611  pd.m_is_sc_migration = is_portal_address;
12612  }
12613  const crypto::hash &spent_txid = crypto::null_hash; // spent txid is unknown
12614  m_confirmed_txs.insert(std::make_pair(spent_txid, pd));
12615  }
12616  PERF_TIMER_STOP(import_key_images_G);
12617  }
12618 
12619  // this can be 0 if we do not know the height
12620  return m_transfers[signed_key_images.size() + offset - 1].m_block_height;
12621 }
12622 
12623 bool wallet2::import_key_images(std::vector<crypto::key_image> key_images, size_t offset, boost::optional<std::unordered_set<size_t>> selected_transfers)
12624 {
12625  if (key_images.size() + offset > m_transfers.size())
12626  {
12627  LOG_PRINT_L1("More key images returned that we know outputs for");
12628  return false;
12629  }
12630  for (size_t ki_idx = 0; ki_idx < key_images.size(); ++ki_idx)
12631  {
12632  const size_t transfer_idx = ki_idx + offset;
12633  if (selected_transfers && selected_transfers.get().find(transfer_idx) == selected_transfers.get().end())
12634  continue;
12635 
12636  transfer_details &td = m_transfers[transfer_idx];
12637  if (td.m_key_image_known && !td.m_key_image_partial && td.m_key_image != key_images[ki_idx])
12638  LOG_PRINT_L0("WARNING: imported key image differs from previously known key image at index " << ki_idx << ": trusting imported one");
12639  td.m_key_image = key_images[ki_idx];
12640  m_key_images[td.m_key_image] = transfer_idx;
12641  td.m_key_image_known = true;
12642  td.m_key_image_request = false;
12643  td.m_key_image_partial = false;
12644  m_pub_keys[td.get_public_key()] = transfer_idx;
12645  }
12646 
12647  return true;
12648 }
12649 
12650 bool wallet2::import_key_images(signed_tx_set & signed_tx, size_t offset, bool only_selected_transfers)
12651 {
12652  std::unordered_set<size_t> selected_transfers;
12653  if (only_selected_transfers)
12654  {
12655  for (const pending_tx & ptx : signed_tx.ptx)
12656  {
12657  for (const size_t s: ptx.selected_transfers)
12658  selected_transfers.insert(s);
12659  }
12660  }
12661 
12662  return import_key_images(signed_tx.key_images, offset, only_selected_transfers ? boost::make_optional(selected_transfers) : boost::none);
12663 }
12664 
12665 wallet2::payment_container wallet2::export_payments() const
12666 {
12667  payment_container payments;
12668  for (auto const &p : m_payments)
12669  {
12670  payments.emplace(p);
12671  }
12672  return payments;
12673 }
12674 void wallet2::import_payments(const payment_container &payments)
12675 {
12676  m_payments.clear();
12677  for (auto const &p : payments)
12678  {
12679  m_payments.emplace(p);
12680  }
12681 }
12682 void wallet2::import_payments_out(const std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>> &confirmed_payments)
12683 {
12684  m_confirmed_txs.clear();
12685  for (auto const &p : confirmed_payments)
12686  {
12687  m_confirmed_txs.emplace(p);
12688  }
12689 }
12690 
12691 std::tuple<size_t,crypto::hash,std::vector<crypto::hash>> wallet2::export_blockchain() const
12692 {
12693  std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> bc;
12694  std::get<0>(bc) = m_blockchain.offset();
12695  std::get<1>(bc) = m_blockchain.empty() ? crypto::null_hash: m_blockchain.genesis();
12696  for (size_t n = m_blockchain.offset(); n < m_blockchain.size(); ++n)
12697  {
12698  std::get<2>(bc).push_back(m_blockchain[n]);
12699  }
12700  return bc;
12701 }
12702 
12703 void wallet2::import_blockchain(const std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> &bc)
12704 {
12705  m_blockchain.clear();
12706  if (std::get<0>(bc))
12707  {
12708  for (size_t n = std::get<0>(bc); n > 0; --n)
12709  m_blockchain.push_back(std::get<1>(bc));
12710  m_blockchain.trim(std::get<0>(bc));
12711  }
12712  for (auto const &b : std::get<2>(bc))
12713  {
12714  m_blockchain.push_back(b);
12715  }
12716  cryptonote::block genesis;
12717  generate_genesis(genesis);
12718  crypto::hash genesis_hash = get_block_hash(genesis);
12719  check_genesis(genesis_hash);
12720  m_last_block_reward = cryptonote::get_outs_etn_amount(genesis.miner_tx);
12721 }
12722 //----------------------------------------------------------------------------------------------------
12723 std::pair<size_t, std::vector<tools::wallet2::transfer_details>> wallet2::export_outputs(bool all) const
12724 {
12725  PERF_TIMER(export_outputs);
12726  std::vector<tools::wallet2::transfer_details> outs;
12727 
12728  size_t offset = 0;
12729  if (!all)
12730  while (offset < m_transfers.size() && (m_transfers[offset].m_key_image_known && !m_transfers[offset].m_key_image_request))
12731  ++offset;
12732 
12733  outs.reserve(m_transfers.size() - offset);
12734  for (size_t n = offset; n < m_transfers.size(); ++n)
12735  {
12736  const transfer_details &td = m_transfers[n];
12737 
12738  outs.push_back(td);
12739  }
12740 
12741  return std::make_pair(offset, outs);
12742 }
12743 //----------------------------------------------------------------------------------------------------
12744 std::string wallet2::export_outputs_to_str(bool all) const
12745 {
12746  PERF_TIMER(export_outputs_to_str);
12747 
12748  std::stringstream oss;
12750  const auto& outputs = export_outputs(all);
12751  ar << outputs;
12752 
12754  const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
12755  std::string header;
12756  header += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key));
12757  header += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key));
12758  PERF_TIMER(export_outputs_encryption);
12759  std::string ciphertext = encrypt_with_view_secret_key(header + oss.str());
12760  return magic + ciphertext;
12761 }
12762 //----------------------------------------------------------------------------------------------------
12763 size_t wallet2::import_outputs(const std::pair<size_t, std::vector<tools::wallet2::transfer_details>> &outputs)
12764 {
12765  PERF_TIMER(import_outputs);
12766 
12767  THROW_WALLET_EXCEPTION_IF(outputs.first > m_transfers.size(), error::wallet_internal_error,
12768  "Imported outputs omit more outputs that we know of");
12769 
12770  const size_t offset = outputs.first;
12771  const size_t original_size = m_transfers.size();
12772  m_transfers.resize(offset + outputs.second.size());
12773  for (size_t i = 0; i < offset; ++i)
12774  m_transfers[i].m_key_image_request = false;
12775  for (size_t i = 0; i < outputs.second.size(); ++i)
12776  {
12777  transfer_details td = outputs.second[i];
12778 
12779  // skip those we've already imported, or which have different data
12780  if (i + offset < original_size)
12781  {
12782  // compare the data used to create the key image below
12783  const transfer_details &org_td = m_transfers[i + offset];
12784  if (!org_td.m_key_image_known)
12785  goto process;
12786 #define CMPF(f) if (!(td.f == org_td.f)) goto process
12787  CMPF(m_txid);
12788  CMPF(m_key_image);
12789  CMPF(m_internal_output_index);
12790 #undef CMPF
12792  goto process;
12793 
12794  // copy anyway, since the comparison does not include ancillary fields which may have changed
12795  m_transfers[i + offset] = std::move(td);
12796  continue;
12797  }
12798 
12799 process:
12800 
12801  // the hot wallet wouldn't have known about key images (except if we already exported them)
12802  cryptonote::keypair in_ephemeral;
12803 
12804  THROW_WALLET_EXCEPTION_IF(td.m_tx.vout.empty(), error::wallet_internal_error, "tx with no outputs at index " + boost::lexical_cast<std::string>(i + offset));
12805  crypto::public_key tx_pub_key = get_tx_pub_key_from_received_outs(td);
12806  const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx);
12807 
12809  error::wallet_internal_error, "Unsupported output type");
12810  const crypto::public_key& out_key = boost::get<cryptonote::txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key;
12811  bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, out_key, tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, in_ephemeral, td.m_key_image, m_account.get_device(), m_account_major_offset);
12812  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
12813  expand_subaddresses(td.m_subaddr_index);
12814  td.m_key_image_known = true;
12815  td.m_key_image_request = true;
12816  td.m_key_image_partial = false;
12817  THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != out_key,
12818  error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key at index " + boost::lexical_cast<std::string>(i + offset));
12819 
12820  m_key_images[td.m_key_image] = i + offset;
12821  m_pub_keys[td.get_public_key()] = i + offset;
12822  m_transfers[i + offset] = std::move(td);
12823  }
12824 
12825  return m_transfers.size();
12826 }
12827 //----------------------------------------------------------------------------------------------------
12828 size_t wallet2::import_outputs_from_str(const std::string &outputs_st)
12829 {
12830  PERF_TIMER(import_outputs_from_str);
12831  std::string data = outputs_st;
12832  const size_t magiclen = strlen(OUTPUT_EXPORT_FILE_MAGIC);
12833  if (data.size() < magiclen || memcmp(data.data(), OUTPUT_EXPORT_FILE_MAGIC, magiclen))
12834  {
12836  }
12837 
12838  try
12839  {
12840  PERF_TIMER(import_outputs_decrypt);
12841  data = decrypt_with_view_secret_key(std::string(data, magiclen));
12842  }
12843  catch (const std::exception &e)
12844  {
12845  THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to decrypt outputs: ") + e.what());
12846  }
12847 
12848  const size_t headerlen = 2 * sizeof(crypto::public_key);
12849  if (data.size() < headerlen)
12850  {
12851  THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Bad data size for outputs"));
12852  }
12853  const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[0];
12854  const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[sizeof(crypto::public_key)];
12855  const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
12856  if (public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key)
12857  {
12858  THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Outputs from are for a different account"));
12859  }
12860 
12861  size_t imported_outputs = 0;
12862  try
12863  {
12864  std::string body(data, headerlen);
12865  std::stringstream iss;
12866  iss << body;
12867  std::pair<size_t, std::vector<tools::wallet2::transfer_details>> outputs;
12868  try
12869  {
12871  ar >> outputs;
12872  }
12873  catch (...)
12874  {
12875  iss.str("");
12876  iss << body;
12877  boost::archive::binary_iarchive ar(iss);
12878  ar >> outputs;
12879  }
12880 
12881  imported_outputs = import_outputs(outputs);
12882  }
12883  catch (const std::exception &e)
12884  {
12885  THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to import outputs") + e.what());
12886  }
12887 
12888  return imported_outputs;
12889 }
12890 //----------------------------------------------------------------------------------------------------
12891 crypto::public_key wallet2::get_multisig_signer_public_key(const crypto::secret_key &spend_skey) const
12892 {
12893  crypto::public_key pkey;
12895  return pkey;
12896 }
12897 //----------------------------------------------------------------------------------------------------
12898 crypto::public_key wallet2::get_multisig_signer_public_key() const
12899 {
12900  CHECK_AND_ASSERT_THROW_MES(m_multisig, "Wallet is not multisig");
12901  crypto::public_key signer;
12902  CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(get_account().get_keys().m_spend_secret_key, signer), "Failed to generate signer public key");
12903  return signer;
12904 }
12905 //----------------------------------------------------------------------------------------------------
12906 crypto::public_key wallet2::get_multisig_signing_public_key(const crypto::secret_key &msk) const
12907 {
12908  CHECK_AND_ASSERT_THROW_MES(m_multisig, "Wallet is not multisig");
12909  crypto::public_key pkey;
12910  CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(msk, pkey), "Failed to derive public key");
12911  return pkey;
12912 }
12913 //----------------------------------------------------------------------------------------------------
12914 crypto::public_key wallet2::get_multisig_signing_public_key(size_t idx) const
12915 {
12916  CHECK_AND_ASSERT_THROW_MES(m_multisig, "Wallet is not multisig");
12917  CHECK_AND_ASSERT_THROW_MES(idx < get_account().get_multisig_keys().size(), "Multisig signing key index out of range");
12918  return get_multisig_signing_public_key(get_account().get_multisig_keys()[idx]);
12919 }
12920 //----------------------------------------------------------------------------------------------------
12921 rct::key wallet2::get_multisig_k(size_t idx, const std::unordered_set<rct::key> &used_L) const
12922 {
12923  CHECK_AND_ASSERT_THROW_MES(m_multisig, "Wallet is not multisig");
12924  CHECK_AND_ASSERT_THROW_MES(idx < m_transfers.size(), "idx out of range");
12925  for (const auto &k: m_transfers[idx].m_multisig_k)
12926  {
12927  rct::key L;
12928  rct::scalarmultBase(L, k);
12929  if (used_L.find(L) != used_L.end())
12930  return k;
12931  }
12933  return rct::zero();
12934 }
12935 //----------------------------------------------------------------------------------------------------
12936 rct::multisig_kLRki wallet2::get_multisig_kLRki(size_t n, const rct::key &k) const
12937 {
12938  CHECK_AND_ASSERT_THROW_MES(n < m_transfers.size(), "Bad m_transfers index");
12939  rct::multisig_kLRki kLRki;
12940  kLRki.k = k;
12941  cryptonote::generate_multisig_LR(m_transfers[n].get_public_key(), rct::rct2sk(kLRki.k), (crypto::public_key&)kLRki.L, (crypto::public_key&)kLRki.R);
12942  kLRki.ki = rct::ki2rct(m_transfers[n].m_key_image);
12943  return kLRki;
12944 }
12945 //----------------------------------------------------------------------------------------------------
12946 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
12947 {
12948  CHECK_AND_ASSERT_THROW_MES(n < m_transfers.size(), "Bad transfer index");
12949 
12950  const transfer_details &td = m_transfers[n];
12951  rct::multisig_kLRki kLRki = get_multisig_kLRki(n, rct::skGen());
12952 
12953  // pick a L/R pair from every other participant but one
12954  size_t n_signers_used = 1;
12955  for (const auto &p: m_transfers[n].m_multisig_info)
12956  {
12957  if (ignore_set.find(p.m_signer) != ignore_set.end())
12958  continue;
12959 
12960  for (const auto &lr: p.m_LR)
12961  {
12962  if (used_L.find(lr.m_L) != used_L.end())
12963  continue;
12964  used_L.insert(lr.m_L);
12965  new_used_L.insert(lr.m_L);
12966  rct::addKeys(kLRki.L, kLRki.L, lr.m_L);
12967  rct::addKeys(kLRki.R, kLRki.R, lr.m_R);
12968  ++n_signers_used;
12969  break;
12970  }
12971  }
12972  CHECK_AND_ASSERT_THROW_MES(n_signers_used >= m_multisig_threshold, "LR not found for enough participants");
12973 
12974  return kLRki;
12975 }
12976 //----------------------------------------------------------------------------------------------------
12977 crypto::key_image wallet2::get_multisig_composite_key_image(size_t n) const
12978 {
12979  CHECK_AND_ASSERT_THROW_MES(n < m_transfers.size(), "Bad output index");
12980 
12981  const transfer_details &td = m_transfers[n];
12982  const crypto::public_key tx_key = get_tx_pub_key_from_received_outs(td);
12983  const std::vector<crypto::public_key> additional_tx_keys = cryptonote::get_additional_tx_pub_keys_from_extra(td.m_tx);
12984  crypto::key_image ki;
12985  std::vector<crypto::key_image> pkis;
12986  for (const auto &info: td.m_multisig_info)
12987  for (const auto &pki: info.m_partial_key_images)
12988  pkis.push_back(pki);
12989  bool r = cryptonote::generate_multisig_composite_key_image(get_account().get_keys(), m_subaddresses, td.get_public_key(), tx_key, additional_tx_keys, td.m_internal_output_index, pkis, ki);
12990  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
12991  return ki;
12992 }
12993 //----------------------------------------------------------------------------------------------------
12994 cryptonote::blobdata wallet2::export_multisig()
12995 {
12996  std::vector<tools::wallet2::multisig_info> info;
12997 
12998  const crypto::public_key signer = get_multisig_signer_public_key();
12999 
13000  info.resize(m_transfers.size());
13001  for (size_t n = 0; n < m_transfers.size(); ++n)
13002  {
13003  transfer_details &td = m_transfers[n];
13004  crypto::key_image ki;
13005  td.m_multisig_k.clear();
13006  info[n].m_LR.clear();
13007  info[n].m_partial_key_images.clear();
13008 
13009  for (size_t m = 0; m < get_account().get_multisig_keys().size(); ++m)
13010  {
13011  // we want to export the partial key image, not the full one, so we can't use td.m_key_image
13012  bool r = generate_multisig_key_image(get_account().get_keys(), m, td.get_public_key(), ki);
13013  CHECK_AND_ASSERT_THROW_MES(r, "Failed to generate key image");
13014  info[n].m_partial_key_images.push_back(ki);
13015  }
13016 
13017  // Wallet tries to create as many transactions as many signers combinations. We calculate the maximum number here as follows:
13018  // if we have 2/4 wallet with signers: A, B, C, D and A is a transaction creator it will need to pick up 1 signer from 3 wallets left.
13019  // That means counting combinations for excluding 2-of-3 wallets (k = total signers count - threshold, n = total signers count - 1).
13020  size_t nlr = tools::combinations_count(m_multisig_signers.size() - m_multisig_threshold, m_multisig_signers.size() - 1);
13021  for (size_t m = 0; m < nlr; ++m)
13022  {
13023  td.m_multisig_k.push_back(rct::skGen());
13024  const rct::multisig_kLRki kLRki = get_multisig_kLRki(n, td.m_multisig_k.back());
13025  info[n].m_LR.push_back({kLRki.L, kLRki.R});
13026  }
13027 
13028  info[n].m_signer = signer;
13029  }
13030 
13031  std::stringstream oss;
13033  ar << info;
13034 
13035  const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
13036  std::string header;
13037  header += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key));
13038  header += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key));
13039  header += std::string((const char *)&signer, sizeof(crypto::public_key));
13040  std::string ciphertext = encrypt_with_view_secret_key(header + oss.str());
13041 
13042  return MULTISIG_EXPORT_FILE_MAGIC + ciphertext;
13043 }
13044 //----------------------------------------------------------------------------------------------------
13045 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)
13046 {
13047  CHECK_AND_ASSERT_THROW_MES(n < m_transfers.size(), "Bad index in update_multisig_info");
13048  CHECK_AND_ASSERT_THROW_MES(multisig_k.size() >= m_transfers.size(), "Mismatched sizes of multisig_k and info");
13049 
13050  MDEBUG("update_multisig_rescan_info: updating index " << n);
13051  transfer_details &td = m_transfers[n];
13052  td.m_multisig_info.clear();
13053  for (const auto &pi: info)
13054  {
13055  CHECK_AND_ASSERT_THROW_MES(n < pi.size(), "Bad pi size");
13056  td.m_multisig_info.push_back(pi[n]);
13057  }
13058  m_key_images.erase(td.m_key_image);
13059  td.m_key_image = get_multisig_composite_key_image(n);
13060  td.m_key_image_known = true;
13061  td.m_key_image_request = false;
13062  td.m_key_image_partial = false;
13063  td.m_multisig_k = multisig_k[n];
13064  m_key_images[td.m_key_image] = n;
13065 }
13066 //----------------------------------------------------------------------------------------------------
13067 size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs)
13068 {
13069  CHECK_AND_ASSERT_THROW_MES(m_multisig, "Wallet is not multisig");
13070 
13071  std::vector<std::vector<tools::wallet2::multisig_info>> info;
13072  std::unordered_set<crypto::public_key> seen;
13073  for (cryptonote::blobdata &data: blobs)
13074  {
13075  const size_t magiclen = strlen(MULTISIG_EXPORT_FILE_MAGIC);
13076  THROW_WALLET_EXCEPTION_IF(data.size() < magiclen || memcmp(data.data(), MULTISIG_EXPORT_FILE_MAGIC, magiclen),
13077  error::wallet_internal_error, "Bad multisig info file magic in ");
13078 
13079  data = decrypt_with_view_secret_key(std::string(data, magiclen));
13080 
13081  const size_t headerlen = 3 * sizeof(crypto::public_key);
13082  THROW_WALLET_EXCEPTION_IF(data.size() < headerlen, error::wallet_internal_error, "Bad data size");
13083 
13084  const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[0];
13085  const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[sizeof(crypto::public_key)];
13086  const crypto::public_key &signer = *(const crypto::public_key*)&data[2*sizeof(crypto::public_key)];
13087  const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
13088  THROW_WALLET_EXCEPTION_IF(public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key,
13089  error::wallet_internal_error, "Multisig info is for a different account");
13090  if (get_multisig_signer_public_key() == signer)
13091  {
13092  MINFO("Multisig info from this wallet ignored");
13093  continue;
13094  }
13095  if (seen.find(signer) != seen.end())
13096  {
13097  MINFO("Duplicate multisig info ignored");
13098  continue;
13099  }
13100  seen.insert(signer);
13101 
13102  std::string body(data, headerlen);
13103  std::istringstream iss(body);
13104  std::vector<tools::wallet2::multisig_info> i;
13106  ar >> i;
13107  MINFO(boost::format("%u outputs found") % boost::lexical_cast<std::string>(i.size()));
13108  info.push_back(std::move(i));
13109  }
13110 
13111  CHECK_AND_ASSERT_THROW_MES(info.size() + 1 <= m_multisig_signers.size() && info.size() + 1 >= m_multisig_threshold, "Wrong number of multisig sources");
13112 
13113  std::vector<std::vector<rct::key>> k;
13114  k.reserve(m_transfers.size());
13115  for (const auto &td: m_transfers)
13116  k.push_back(td.m_multisig_k);
13117 
13118  // how many outputs we're going to update
13119  size_t n_outputs = m_transfers.size();
13120  for (const auto &pi: info)
13121  if (pi.size() < n_outputs)
13122  n_outputs = pi.size();
13123 
13124  if (n_outputs == 0)
13125  return 0;
13126 
13127  // check signers are consistent
13128  for (const auto &pi: info)
13129  {
13130  CHECK_AND_ASSERT_THROW_MES(std::find(m_multisig_signers.begin(), m_multisig_signers.end(), pi[0].m_signer) != m_multisig_signers.end(),
13131  "Signer is not a member of this multisig wallet");
13132  for (size_t n = 1; n < n_outputs; ++n)
13133  CHECK_AND_ASSERT_THROW_MES(pi[n].m_signer == pi[0].m_signer, "Mismatched signers in imported multisig info");
13134  }
13135 
13136  // trim data we don't have info for from all participants
13137  for (auto &pi: info)
13138  pi.resize(n_outputs);
13139 
13140  // sort by signer
13141  if (!info.empty() && !info.front().empty())
13142  {
13143  std::sort(info.begin(), info.end(), [](const std::vector<tools::wallet2::multisig_info> &i0, const std::vector<tools::wallet2::multisig_info> &i1){ return memcmp(&i0[0].m_signer, &i1[0].m_signer, sizeof(i0[0].m_signer)); });
13144  }
13145 
13146  // first pass to determine where to detach the blockchain
13147  for (size_t n = 0; n < n_outputs; ++n)
13148  {
13149  const transfer_details &td = m_transfers[n];
13150  if (!td.m_key_image_partial)
13151  continue;
13152  MINFO("Multisig info importing from block height " << td.m_block_height);
13153  detach_blockchain(td.m_block_height);
13154  break;
13155  }
13156 
13157  for (size_t n = 0; n < n_outputs && n < m_transfers.size(); ++n)
13158  {
13159  update_multisig_rescan_info(k, info, n);
13160  }
13161 
13162  m_multisig_rescan_k = &k;
13163  m_multisig_rescan_info = &info;
13164  try
13165  {
13166 
13167  refresh(false);
13168  }
13169  catch (...)
13170  {
13171  m_multisig_rescan_info = NULL;
13172  m_multisig_rescan_k = NULL;
13173  throw;
13174  }
13175  m_multisig_rescan_info = NULL;
13176  m_multisig_rescan_k = NULL;
13177 
13178  return n_outputs;
13179 }
13180 //----------------------------------------------------------------------------------------------------
13181 std::string wallet2::encrypt(const char *plaintext, size_t len, const crypto::secret_key &skey, bool authenticated) const
13182 {
13183  crypto::chacha_key key;
13184  crypto::generate_chacha_key(&skey, sizeof(skey), key, m_kdf_rounds);
13185  std::string ciphertext;
13186  crypto::chacha_iv iv = crypto::rand<crypto::chacha_iv>();
13187  ciphertext.resize(len + sizeof(iv) + (authenticated ? sizeof(crypto::signature) : 0));
13188  crypto::chacha20(plaintext, len, key, iv, &ciphertext[sizeof(iv)]);
13189  memcpy(&ciphertext[0], &iv, sizeof(iv));
13190  if (authenticated)
13191  {
13193  crypto::cn_fast_hash(ciphertext.data(), ciphertext.size() - sizeof(signature), hash);
13194  crypto::public_key pkey;
13196  crypto::signature &signature = *(crypto::signature*)&ciphertext[ciphertext.size() - sizeof(crypto::signature)];
13198  }
13199  return ciphertext;
13200 }
13201 //----------------------------------------------------------------------------------------------------
13202 std::string wallet2::encrypt(const epee::span<char> &plaintext, const crypto::secret_key &skey, bool authenticated) const
13203 {
13204  return encrypt(plaintext.data(), plaintext.size(), skey, authenticated);
13205 }
13206 //----------------------------------------------------------------------------------------------------
13207 std::string wallet2::encrypt(const std::string &plaintext, const crypto::secret_key &skey, bool authenticated) const
13208 {
13209  return encrypt(plaintext.data(), plaintext.size(), skey, authenticated);
13210 }
13211 //----------------------------------------------------------------------------------------------------
13212 std::string wallet2::encrypt(const epee::wipeable_string &plaintext, const crypto::secret_key &skey, bool authenticated) const
13213 {
13214  return encrypt(plaintext.data(), plaintext.size(), skey, authenticated);
13215 }
13216 //----------------------------------------------------------------------------------------------------
13217 std::string wallet2::encrypt_with_view_secret_key(const std::string &plaintext, bool authenticated) const
13218 {
13219  return encrypt(plaintext, get_account().get_keys().m_view_secret_key, authenticated);
13220 }
13221 //----------------------------------------------------------------------------------------------------
13222 template<typename T>
13223 T wallet2::decrypt(const std::string &ciphertext, const crypto::secret_key &skey, bool authenticated) const
13224 {
13225  const size_t prefix_size = sizeof(chacha_iv) + (authenticated ? sizeof(crypto::signature) : 0);
13226  THROW_WALLET_EXCEPTION_IF(ciphertext.size() < prefix_size,
13227  error::wallet_internal_error, "Unexpected ciphertext size");
13228 
13229  crypto::chacha_key key;
13230  crypto::generate_chacha_key(&skey, sizeof(skey), key, m_kdf_rounds);
13231  const crypto::chacha_iv &iv = *(const crypto::chacha_iv*)&ciphertext[0];
13232  if (authenticated)
13233  {
13235  crypto::cn_fast_hash(ciphertext.data(), ciphertext.size() - sizeof(signature), hash);
13236  crypto::public_key pkey;
13238  const crypto::signature &signature = *(const crypto::signature*)&ciphertext[ciphertext.size() - sizeof(crypto::signature)];
13240  error::wallet_internal_error, "Failed to authenticate ciphertext");
13241  }
13242  std::unique_ptr<char[]> buffer{new char[ciphertext.size() - prefix_size]};
13243  auto wiper = epee::misc_utils::create_scope_leave_handler([&]() { memwipe(buffer.get(), ciphertext.size() - prefix_size); });
13244  crypto::chacha20(ciphertext.data() + sizeof(iv), ciphertext.size() - prefix_size, key, iv, buffer.get());
13245  return T(buffer.get(), ciphertext.size() - prefix_size);
13246 }
13247 //----------------------------------------------------------------------------------------------------
13248 template epee::wipeable_string wallet2::decrypt(const std::string &ciphertext, const crypto::secret_key &skey, bool authenticated) const;
13249 //----------------------------------------------------------------------------------------------------
13250 std::string wallet2::decrypt_with_view_secret_key(const std::string &ciphertext, bool authenticated) const
13251 {
13252  return decrypt(ciphertext, get_account().get_keys().m_view_secret_key, authenticated);
13253 }
13254 //----------------------------------------------------------------------------------------------------
13255 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
13256 {
13258  if(!get_account_address_from_str(info, nettype(), address))
13259  {
13260  error = std::string("wrong address: ") + address;
13261  return std::string();
13262  }
13263 
13264  // we want only one payment id
13265  if (info.has_payment_id && !payment_id.empty())
13266  {
13267  error = "A single payment id is allowed";
13268  return std::string();
13269  }
13270 
13271  if (!payment_id.empty())
13272  {
13273  crypto::hash pid32;
13274  crypto::hash8 pid8;
13275  if (!wallet2::parse_long_payment_id(payment_id, pid32) && !parse_short_payment_id(payment_id, pid8))
13276  {
13277  error = "Invalid payment id";
13278  return std::string();
13279  }
13280  }
13281 
13282  std::string uri = "electroneum:" + address;
13283  unsigned int n_fields = 0;
13284 
13285  if (!payment_id.empty())
13286  {
13287  uri += (n_fields++ ? "&" : "?") + std::string("tx_payment_id=") + payment_id;
13288  }
13289 
13290  if (amount > 0)
13291  {
13292  // URI encoded amount is in decimal units, not atomic units
13293  uri += (n_fields++ ? "&" : "?") + std::string("tx_amount=") + cryptonote::print_etn(amount);
13294  }
13295 
13296  if (!recipient_name.empty())
13297  {
13298  uri += (n_fields++ ? "&" : "?") + std::string("recipient_name=") + epee::net_utils::conver_to_url_format(recipient_name);
13299  }
13300 
13301  if (!tx_description.empty())
13302  {
13303  uri += (n_fields++ ? "&" : "?") + std::string("tx_description=") + epee::net_utils::conver_to_url_format(tx_description);
13304  }
13305 
13306  return uri;
13307 }
13308 //----------------------------------------------------------------------------------------------------
13309 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)
13310 {
13311  if (uri.substr(0, 12) != "electroneum:")
13312  {
13313  error = std::string("URI has wrong scheme (expected \"electroneum:\"): ") + uri;
13314  return false;
13315  }
13316 
13317  std::string remainder = uri.substr(12);
13318  const char *ptr = strchr(remainder.c_str(), '?');
13319  address = ptr ? remainder.substr(0, ptr-remainder.c_str()) : remainder;
13320 
13322  if(!get_account_address_from_str(info, nettype(), address))
13323  {
13324  error = std::string("URI has wrong address: ") + address;
13325  return false;
13326  }
13327  if (!strchr(remainder.c_str(), '?'))
13328  return true;
13329 
13330  std::vector<std::string> arguments;
13331  std::string body = remainder.substr(address.size() + 1);
13332  if (body.empty())
13333  return true;
13334  boost::split(arguments, body, boost::is_any_of("&"));
13335  std::set<std::string> have_arg;
13336  for (const auto &arg: arguments)
13337  {
13338  std::vector<std::string> kv;
13339  boost::split(kv, arg, boost::is_any_of("="));
13340  if (kv.size() != 2)
13341  {
13342  error = std::string("URI has wrong parameter: ") + arg;
13343  return false;
13344  }
13345  if (have_arg.find(kv[0]) != have_arg.end())
13346  {
13347  error = std::string("URI has more than one instance of " + kv[0]);
13348  return false;
13349  }
13350  have_arg.insert(kv[0]);
13351 
13352  if (kv[0] == "tx_amount")
13353  {
13354  amount = 0;
13355  if (!cryptonote::parse_amount(amount, kv[1]))
13356  {
13357  error = std::string("URI has invalid amount: ") + kv[1];
13358  return false;
13359  }
13360  }
13361  else if (kv[0] == "tx_payment_id")
13362  {
13363  if (info.has_payment_id)
13364  {
13365  error = "Separate payment id given with an integrated address";
13366  return false;
13367  }
13370  if (!wallet2::parse_long_payment_id(kv[1], hash) && !wallet2::parse_short_payment_id(kv[1], hash8))
13371  {
13372  error = "Invalid payment id: " + kv[1];
13373  return false;
13374  }
13375  payment_id = kv[1];
13376  }
13377  else if (kv[0] == "recipient_name")
13378  {
13379  recipient_name = epee::net_utils::convert_from_url_format(kv[1]);
13380  }
13381  else if (kv[0] == "tx_description")
13382  {
13383  tx_description = epee::net_utils::convert_from_url_format(kv[1]);
13384  }
13385  else
13386  {
13387  unknown_parameters.push_back(arg);
13388  }
13389  }
13390  return true;
13391 }
13392 //----------------------------------------------------------------------------------------------------
13393 uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, uint8_t day)
13394 {
13395  uint32_t version;
13396  if (!check_connection(&version))
13397  {
13398  throw std::runtime_error("failed to connect to daemon: " + get_daemon_address());
13399  }
13400  if (version < MAKE_CORE_RPC_VERSION(1, 6))
13401  {
13402  throw std::runtime_error("this function requires RPC version 1.6 or higher");
13403  }
13404  std::tm date = { 0, 0, 0, 0, 0, 0, 0, 0 };
13405  date.tm_year = year - 1900;
13406  date.tm_mon = month - 1;
13407  date.tm_mday = day;
13408  if (date.tm_mon < 0 || 11 < date.tm_mon || date.tm_mday < 1 || 31 < date.tm_mday)
13409  {
13410  throw std::runtime_error("month or day out of range");
13411  }
13412  uint64_t timestamp_target = std::mktime(&date);
13413  std::string err;
13414  uint64_t height_min = 0;
13415  uint64_t height_max = get_daemon_blockchain_height(err) - 1;
13416  if (!err.empty())
13417  {
13418  throw std::runtime_error("failed to get blockchain height");
13419  }
13420  while (true)
13421  {
13424  uint64_t height_mid = (height_min + height_max) / 2;
13425  req.heights =
13426  {
13427  height_min,
13428  height_mid,
13429  height_max
13430  };
13431  bool r = invoke_http_bin("/getblocks_by_height.bin", req, res, rpc_timeout);
13432  if (!r || res.status != CORE_RPC_STATUS_OK)
13433  {
13434  std::ostringstream oss;
13435  oss << "failed to get blocks by heights: ";
13436  for (auto height : req.heights)
13437  oss << height << ' ';
13438  oss << endl << "reason: ";
13439  if (!r)
13440  oss << "possibly lost connection to daemon";
13441  else if (res.status == CORE_RPC_STATUS_BUSY)
13442  oss << "daemon is busy";
13443  else
13444  oss << get_rpc_status(res.status);
13445  throw std::runtime_error(oss.str());
13446  }
13447  cryptonote::block blk_min, blk_mid, blk_max;
13448  if (res.blocks.size() < 3) throw std::runtime_error("Not enough blocks returned from daemon");
13449  if (!parse_and_validate_block_from_blob(res.blocks[0].block, blk_min)) throw std::runtime_error("failed to parse blob at height " + std::to_string(height_min));
13450  if (!parse_and_validate_block_from_blob(res.blocks[1].block, blk_mid)) throw std::runtime_error("failed to parse blob at height " + std::to_string(height_mid));
13451  if (!parse_and_validate_block_from_blob(res.blocks[2].block, blk_max)) throw std::runtime_error("failed to parse blob at height " + std::to_string(height_max));
13452  uint64_t timestamp_min = blk_min.timestamp;
13453  uint64_t timestamp_mid = blk_mid.timestamp;
13454  uint64_t timestamp_max = blk_max.timestamp;
13455  if (!(timestamp_min <= timestamp_mid && timestamp_mid <= timestamp_max))
13456  {
13457  // the timestamps are not in the chronological order.
13458  // assuming they're sufficiently close to each other, simply return the smallest height
13459  return std::min({height_min, height_mid, height_max});
13460  }
13461  if (timestamp_target > timestamp_max)
13462  {
13463  throw std::runtime_error("specified date is in the future");
13464  }
13465  if (timestamp_target <= timestamp_min + 2 * 24 * 60 * 60) // two days of "buffer" period
13466  {
13467  return height_min;
13468  }
13469  if (timestamp_target <= timestamp_mid)
13470  height_max = height_mid;
13471  else
13472  height_min = height_mid;
13473  if (height_max - height_min <= 2 * 24 * 30) // don't divide the height range finer than two days
13474  {
13475  return height_min;
13476  }
13477  }
13478 }
13479 //----------------------------------------------------------------------------------------------------
13480 bool wallet2::is_synced() const
13481 {
13482  uint64_t height;
13483  boost::optional<std::string> result = m_node_rpc_proxy.get_target_height(height);
13484  if (result && *result != CORE_RPC_STATUS_OK)
13485  return false;
13486  return get_blockchain_current_height() >= height;
13487 }
13488 //----------------------------------------------------------------------------------------------------
13489 std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(const std::vector<std::pair<double, double>> &fee_levels)
13490 {
13491  for (const auto &fee_level: fee_levels)
13492  {
13493  THROW_WALLET_EXCEPTION_IF(fee_level.first == 0.0, error::wallet_internal_error, "Invalid 0 fee");
13494  THROW_WALLET_EXCEPTION_IF(fee_level.second == 0.0, error::wallet_internal_error, "Invalid 0 fee");
13495  }
13496 
13497  // get txpool backlog
13500  m_daemon_rpc_mutex.lock();
13501  bool r = invoke_http_json_rpc("/json_rpc", "get_txpool_backlog", req, res, rpc_timeout);
13502  m_daemon_rpc_mutex.unlock();
13503  THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "Failed to connect to daemon");
13504  THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_txpool_backlog");
13506 
13507  uint64_t block_weight_limit = 0;
13508  const auto result = m_node_rpc_proxy.get_block_weight_limit(block_weight_limit);
13509  throw_on_rpc_response_error(result, "get_info");
13510  uint64_t full_reward_zone = block_weight_limit / 2;
13511  THROW_WALLET_EXCEPTION_IF(full_reward_zone == 0, error::wallet_internal_error, "Invalid block weight limit from daemon");
13512 
13513  std::vector<std::pair<uint64_t, uint64_t>> blocks;
13514  for (const auto &fee_level: fee_levels)
13515  {
13516  const double our_fee_byte_min = fee_level.first;
13517  const double our_fee_byte_max = fee_level.second;
13518  uint64_t priority_weight_min = 0, priority_weight_max = 0;
13519  for (const auto &i: res.backlog)
13520  {
13521  if (i.weight == 0)
13522  {
13523  MWARNING("Got 0 weight tx from txpool, ignored");
13524  continue;
13525  }
13526  double this_fee_byte = i.fee / (double)i.weight;
13527  if (this_fee_byte >= our_fee_byte_min)
13528  priority_weight_min += i.weight;
13529  if (this_fee_byte >= our_fee_byte_max)
13530  priority_weight_max += i.weight;
13531  }
13532 
13533  uint64_t nblocks_min = priority_weight_min / full_reward_zone;
13534  uint64_t nblocks_max = priority_weight_max / full_reward_zone;
13535  MDEBUG("estimate_backlog: priority_weight " << priority_weight_min << " - " << priority_weight_max << " for "
13536  << our_fee_byte_min << " - " << our_fee_byte_max << " piconero byte fee, "
13537  << nblocks_min << " - " << nblocks_max << " blocks at block weight " << full_reward_zone);
13538  blocks.push_back(std::make_pair(nblocks_min, nblocks_max));
13539  }
13540  return blocks;
13541 }
13542 //----------------------------------------------------------------------------------------------------
13543 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)
13544 {
13545  THROW_WALLET_EXCEPTION_IF(min_tx_weight == 0, error::wallet_internal_error, "Invalid 0 fee");
13546  THROW_WALLET_EXCEPTION_IF(max_tx_weight == 0, error::wallet_internal_error, "Invalid 0 fee");
13547  for (uint64_t fee: fees)
13548  {
13549  THROW_WALLET_EXCEPTION_IF(fee == 0, error::wallet_internal_error, "Invalid 0 fee");
13550  }
13551  std::vector<std::pair<double, double>> fee_levels;
13552  for (uint64_t fee: fees)
13553  {
13554  double our_fee_byte_min = fee / (double)min_tx_weight, our_fee_byte_max = fee / (double)max_tx_weight;
13555  fee_levels.emplace_back(our_fee_byte_min, our_fee_byte_max);
13556  }
13557  return estimate_backlog(fee_levels);
13558 }
13559 //----------------------------------------------------------------------------------------------------
13560 uint64_t wallet2::get_segregation_fork_height() const
13561 {
13562  if (m_nettype == TESTNET)
13564  if (m_nettype == STAGENET)
13566  THROW_WALLET_EXCEPTION_IF(m_nettype != MAINNET, tools::error::wallet_internal_error, "Invalid network type");
13567 
13568  if (m_segregation_height > 0)
13569  return m_segregation_height;
13570 
13571  if (m_use_dns && !m_offline)
13572  {
13573  // All four ElectroneumPulse domains have DNSSEC on and valid
13574  static const std::vector<std::string> dns_urls = {
13575  "segheights.electroneumpulse.org",
13576  "segheights.electroneumpulse.net",
13577  "segheights.electroneumpulse.co",
13578  "segheights.electroneumpulse.se"
13579  };
13580 
13581  const uint64_t current_height = get_blockchain_current_height();
13582  uint64_t best_diff = std::numeric_limits<uint64_t>::max(), best_height = 0;
13583  std::vector<std::string> records;
13584  if (tools::dns_utils::load_txt_records_from_dns(records, dns_urls, "heights"))
13585  {
13586  for (const auto& record : records)
13587  {
13588  std::vector<std::string> fields;
13589  boost::split(fields, record, boost::is_any_of(":"));
13590  if (fields.size() != 2)
13591  continue;
13592  uint64_t height;
13594  continue;
13595 
13596  MINFO("Found segregation height via DNS: " << fields[0] << " fork height at " << height);
13597  uint64_t diff = height > current_height ? height - current_height : current_height - height;
13598  if (diff < best_diff)
13599  {
13600  best_diff = diff;
13601  best_height = height;
13602  }
13603  }
13604  if (best_height)
13605  return best_height;
13606  }
13607  }
13608  return SEGREGATION_FORK_HEIGHT;
13609 }
13610 //----------------------------------------------------------------------------------------------------
13611 void wallet2::generate_genesis(cryptonote::block& b) const {
13613 }
13614 //----------------------------------------------------------------------------------------------------
13615 mms::multisig_wallet_state wallet2::get_multisig_wallet_state() const
13616 {
13618  state.nettype = m_nettype;
13619  state.multisig = multisig(&state.multisig_is_ready);
13620  state.has_multisig_partial_key_images = has_multisig_partial_key_images();
13621  state.multisig_rounds_passed = m_multisig_rounds_passed;
13622  state.num_transfer_details = m_transfers.size();
13623  if (state.multisig)
13624  {
13625  THROW_WALLET_EXCEPTION_IF(!m_original_keys_available, error::wallet_internal_error, "MMS use not possible because own original Electroneum address not available");
13626  state.address = m_original_address;
13627  state.view_secret_key = m_original_view_secret_key;
13628  }
13629  else
13630  {
13631  state.address = m_account.get_keys().m_account_address;
13632  state.view_secret_key = m_account.get_keys().m_view_secret_key;
13633  }
13634  state.mms_file=m_mms_file;
13635  return state;
13636 }
13637 //----------------------------------------------------------------------------------------------------
13638 wallet_device_callback * wallet2::get_device_callback()
13639 {
13640  if (!m_device_callback){
13641  m_device_callback.reset(new wallet_device_callback(this));
13642  }
13643  return m_device_callback.get();
13644 }//----------------------------------------------------------------------------------------------------
13645 void wallet2::on_device_button_request(uint64_t code)
13646 {
13647  if (nullptr != m_callback)
13648  m_callback->on_device_button_request(code);
13649 }
13650 //----------------------------------------------------------------------------------------------------
13651 void wallet2::on_device_button_pressed()
13652 {
13653  if (nullptr != m_callback)
13654  m_callback->on_device_button_pressed();
13655 }
13656 //----------------------------------------------------------------------------------------------------
13657 boost::optional<epee::wipeable_string> wallet2::on_device_pin_request()
13658 {
13659  if (nullptr != m_callback)
13660  return m_callback->on_device_pin_request();
13661  return boost::none;
13662 }
13663 //----------------------------------------------------------------------------------------------------
13664 boost::optional<epee::wipeable_string> wallet2::on_device_passphrase_request(bool on_device)
13665 {
13666  if (nullptr != m_callback)
13667  return m_callback->on_device_passphrase_request(on_device);
13668  return boost::none;
13669 }
13670 //----------------------------------------------------------------------------------------------------
13671 void wallet2::on_device_progress(const hw::device_progress& event)
13672 {
13673  if (nullptr != m_callback)
13674  m_callback->on_device_progress(event);
13675 }
13676 //----------------------------------------------------------------------------------------------------
13677 std::string wallet2::get_rpc_status(const std::string &s) const
13678 {
13679  if (m_trusted_daemon)
13680  return s;
13681  return "<error>";
13682 }
13683 //----------------------------------------------------------------------------------------------------
13684 void wallet2::throw_on_rpc_response_error(const boost::optional<std::string> &status, const char *method) const
13685 {
13686  // no error
13687  if (!status)
13688  return;
13689 
13690  MERROR("RPC error: " << method << ": status " << *status);
13691 
13692  // empty string -> not connection
13694 
13696  THROW_WALLET_EXCEPTION_IF(*status != CORE_RPC_STATUS_OK, tools::error::wallet_generic_rpc_error, method, m_trusted_daemon ? *status : "daemon error");
13697 }
13698 //----------------------------------------------------------------------------------------------------
13699 void wallet2::hash_m_transfer(const transfer_details & transfer, crypto::hash &hash) const
13700 {
13701  KECCAK_CTX state;
13702  keccak_init(&state);
13703  keccak_update(&state, (const uint8_t *) transfer.m_txid.data, sizeof(transfer.m_txid.data));
13704  keccak_update(&state, (const uint8_t *) transfer.m_internal_output_index, sizeof(transfer.m_internal_output_index));
13705  keccak_update(&state, (const uint8_t *) transfer.m_global_output_index, sizeof(transfer.m_global_output_index));
13706  keccak_update(&state, (const uint8_t *) transfer.m_amount, sizeof(transfer.m_amount));
13707  keccak_finish(&state, (uint8_t *) hash.data);
13708 }
13709 //----------------------------------------------------------------------------------------------------
13710 uint64_t wallet2::hash_m_transfers(int64_t transfer_height, crypto::hash &hash) const
13711 {
13712  CHECK_AND_ASSERT_THROW_MES(transfer_height > (int64_t)m_transfers.size(), "Hash height is greater than number of transfers");
13713 
13714  KECCAK_CTX state;
13715  crypto::hash tmp_hash{};
13716  uint64_t current_height = 0;
13717 
13718  keccak_init(&state);
13719  for(const transfer_details & transfer : m_transfers){
13720  if (transfer_height >= 0 && current_height >= (uint64_t)transfer_height){
13721  break;
13722  }
13723 
13724  hash_m_transfer(transfer, tmp_hash);
13725  keccak_update(&state, (const uint8_t *) transfer.m_block_height, sizeof(transfer.m_block_height));
13726  keccak_update(&state, (const uint8_t *) tmp_hash.data, sizeof(tmp_hash.data));
13727  current_height += 1;
13728  }
13729 
13730  keccak_finish(&state, (uint8_t *) hash.data);
13731  return current_height;
13732 }
13733 //----------------------------------------------------------------------------------------------------
13734 void wallet2::finish_rescan_bc_keep_key_images(uint64_t transfer_height, const crypto::hash &hash)
13735 {
13736  // Compute hash of m_transfers, if differs there had to be BC reorg.
13737  crypto::hash new_transfers_hash{};
13738  hash_m_transfers((int64_t) transfer_height, new_transfers_hash);
13739 
13740  if (new_transfers_hash != hash)
13741  {
13742  // Soft-Reset to avoid inconsistency in case of BC reorg.
13743  clear_soft(false); // keep_key_images works only with soft reset.
13744  THROW_WALLET_EXCEPTION_IF(true, error::wallet_internal_error, "Transfers changed during rescan, soft or hard rescan is needed");
13745  }
13746 
13747  // Restore key images in m_transfers from m_key_images
13748  for(auto it = m_key_images.begin(); it != m_key_images.end(); it++)
13749  {
13750  THROW_WALLET_EXCEPTION_IF(it->second >= m_transfers.size(), error::wallet_internal_error, "Key images cache contains illegal transfer offset");
13751  m_transfers[it->second].m_key_image = it->first;
13752  m_transfers[it->second].m_key_image_known = true;
13753  }
13754 }
13755 //----------------------------------------------------------------------------------------------------
13756 uint64_t wallet2::get_bytes_sent() const
13757 {
13758  return m_http_client.get_bytes_sent();
13759 }
13760 //----------------------------------------------------------------------------------------------------
13761 uint64_t wallet2::get_bytes_received() const
13762 {
13763  return m_http_client.get_bytes_received();
13764 }
13765 
13766 void wallet2::add_checkpoint(uint64_t height, std::string hash){
13767  m_checkpoints.add_checkpoint(height, hash);
13768 }
13769 }
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 reconnect_device()
Definition: wallet2.cpp:1390
const char * res
Definition: hmac_keccak.cpp:41
std::unordered_map< crypto::public_key, crypto::key_image > tx_key_images
Definition: wallet2.h:507
bool watch_only() const
Definition: wallet2.h:825
static void init_options(boost::program_options::options_description &desc_params)
crypto::public_key pub
virtual bool has_ki_cold_sync(void) const
Definition: device.hpp:243
#define tr(x)
Definition: common_defines.h:4
virtual device_type get_type() const =0
#define HF_VERSION_MIN_MIXIN_2
bool parse_tx_extra(const std::vector< uint8_t > &tx_extra, std::vector< tx_extra_field > &tx_extra_fields)
std::vector< crypto::key_image > key_images
Definition: wallet2.h:506
#define CRYPTONOTE_MINED_ETN_UNLOCK_WINDOW
std::string get_extension(const std::string &str)
Definition: string_tools.h:347
#define MERROR(x)
Definition: misc_log_ex.h:73
const config_t & get_config(network_type nettype)
virtual bool secret_key_to_public_key(const crypto::secret_key &sec, crypto::public_key &pub)=0
void derivation_to_scalar(const key_derivation &derivation, size_t output_index, ec_scalar &res)
Definition: crypto.h:279
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)
cryptonote::transaction tx
Definition: wallet2.h:466
bool is_coinbase(const transaction &tx)
crypto::chacha_iv iv
Definition: wallet2.h:523
#define CHECK_AND_ASSERT_THROW_MES(expr, message)
Definition: misc_log_ex.h:173
virtual device_protocol_t device_protocol() const
Definition: device.hpp:135
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
bool store_t_to_binary(t_struct &str_in, std::string &binary_buff, size_t indent=0)
key curveOrder()
Definition: rctOps.h:76
AskPasswordType ask_password() const
Definition: wallet2.h:1079
#define FEE_PER_KB_V11
virtual bool scalarmultKey(rct::key &aP, const rct::key &P, const rct::key &a)=0
#define LOG_PRINT_L2(x)
Definition: misc_log_ex.h:101
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")
const boost::optional< epee::net_utils::http::login > & get_daemon_login() const
Definition: wallet2.h:1165
const uint32_t T[512]
std::vector< crypto::hash > tx_hashes
size_t get_num_subaddresses(uint32_t index_major) const
Definition: wallet2.h:802
const CharType(& source)[N]
Definition: pointer.h:1147
void register_all(std::map< std::string, std::unique_ptr< device >> &registry)
crypto::public_key real_out_tx_key
#define SEGREGATION_FORK_VICINITY
Definition: wallet2.cpp:130
boost::function< crypto::public_key(const tools::wallet2::transfer_details &td)> get_tx_pub_key_from_received_outs
Definition: device_cold.hpp:41
etn_amount h2d(const key &test)
Definition: rctTypes.cpp:161
std::string cut_off_extension(const std::string &str)
Definition: string_tools.h:358
crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const
Definition: wallet2.cpp:12143
void derive_secret_key(const key_derivation &derivation, std::size_t output_index, const secret_key &base, secret_key &derived_key)
Definition: crypto.h:282
bool get_seed(epee::wipeable_string &electrum_words, const epee::wipeable_string &passphrase=epee::wipeable_string()) const
Definition: wallet2.cpp:1301
#define GAMMA_SCALE
Definition: wallet2.cpp:135
std::string get_account_address_as_str(network_type nettype, bool subaddress, account_public_address const &adr)
bool frozen(size_t idx) const
Definition: wallet2.cpp:1598
#define KEY_IMAGE_EXPORT_FILE_MAGIC
Definition: wallet2.cpp:123
size_t size() const noexcept
#define MTRACE(x)
Definition: misc_log_ex.h:77
#define MINFO(x)
Definition: misc_log_ex.h:75
bool isInMainSubgroup(const key &A)
Definition: rctOps.cpp:412
bool ssl_support_from_string(ssl_support_t &ssl, boost::string_ref s)
Definition: net_ssl.cpp:516
std::vector< crypto::public_key > data
Definition: tx_extra.h:169
virtual bool verify_keys(const crypto::secret_key &secret_key, const crypto::public_key &public_key)=0
static std::unique_ptr< wallet2 > make_dummy(const boost::program_options::variables_map &vm, bool unattended, const std::function< boost::optional< password_container >(const char *, bool)> &password_prompter)
Just parses variables.
Definition: wallet2.cpp:1261
virtual boost::optional< epee::wipeable_string > on_get_password(const char *reason)
Definition: wallet2.h:132
static std::string device_derivation_path_option(const boost::program_options::variables_map &vm)
Definition: wallet2.cpp:1188
cryptonote::subaddress_index m_subaddr_index
Definition: wallet2.h:362
const std::string & get_seed_language() const
Gets the seed language.
Definition: wallet2.cpp:1417
crypto::secret_key sec
virtual bool generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key, uint64_t kdf_rounds)=0
#define PERF_TIMER(name)
Definition: perf_timer.h:82
#define LOG_PRINT_L1(x)
Definition: misc_log_ex.h:100
#define HF_VERSION_ZERO_FEE
cryptonote::subaddress_index m_subaddr_index
Definition: wallet2.h:318
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)
boost::optional< epee::wipeable_string > on_passphrase_request(bool on_device) override
Definition: wallet2.cpp:1089
#define DEFAULT_MIN_OUTPUT_VALUE
Definition: wallet2.cpp:138
enum tools::wallet2::unconfirmed_transfer_details::@63 m_state
std::vector< crypto::secret_key > additional_tx_keys
Definition: wallet2.h:473
std::vector< cryptonote::tx_destination_entry > dests
Definition: wallet2.h:474
#define CHECKED_GET_SPECIFIC_VARIANT(variant_var, specific_type, variable_name, fail_return_val)
std::vector< rct::key > m_multisig_k
Definition: wallet2.h:320
#define APPROXIMATE_INPUT_BYTES
Definition: wallet2.cpp:97
#define CRYPTONOTE_DISPLAY_DECIMAL_POINT
crypto::public_key shared_secret
Definition: wallet2.h:556
::std::string string
Definition: gtest-port.h:1097
#define HF_VERSION_MAX_RING_11
uint64_t get_outs_etn_amount(const transaction &tx)
#define HF_VERSION_ENFORCE_0_DECOY_TXS
A global thread pool.
Definition: threadpool.h:43
epee::misc_utils::struct_init< response_t > response
crypto::public_key pub_key
Definition: tx_extra.h:101
POD_CLASS key_derivation
Definition: crypto.h:98
auto_scope_leave_caller create_scope_leave_handler(t_scope_leave_handler f)
bool is_local_address(const std::string &address)
Definition: util.cpp:874
bool decode(const std::string &enc, std::string &data)
Definition: base58.cpp:196
std::string print_etn(uint64_t amount, unsigned int decimal_point)
void scalarmultKey(key &aP, const key &P, const key &a)
Definition: rctOps.cpp:368
key commit(etn_amount amount, const key &mask)
Definition: rctOps.cpp:336
std::vector< size_t > selected_transfers
Definition: wallet2.h:470
#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message)
Definition: misc_log_ex.h:181
bool is_unattended() const
Definition: wallet2.h:1274
#define CRYPTONOTE_MAX_BLOCK_NUMBER
const std::string old_language_name
std::vector< std::string > tx_device_aux
Definition: device_cold.hpp:46
#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2
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")
wallet2(cryptonote::network_type nettype=cryptonote::MAINNET, uint64_t kdf_rounds=1, bool unattended=false)
Definition: wallet2.cpp:1102
#define ELPP
void decrypt_keys(const crypto::chacha_key &key)
Definition: wallet2.cpp:4752
uint64_t height
Definition: blockchain.cpp:91
#define CORE_RPC_STATUS_BUSY
void sc_sub(unsigned char *, const unsigned char *, const unsigned char *)
Informational events most useful for developers to debug application.
boost::variant< txin_gen, txin_to_script, txin_to_scripthash, txin_to_key, txin_to_key_public > txin_v
virtual bool set_mode(device_mode mode)
Definition: device.hpp:130
std::string key_images
Definition: wallet2.h:471
boost::optional< cryptonote::subaddress_receive_info > received
Definition: wallet2.h:296
std::vector< blobdata > txs
std::string get_daemon_address() const
Definition: wallet2.cpp:11926
#define ETN_DEFAULT_TX_SPENDABLE_AGE_V8
void chacha20(const void *data, size_t length, const uint8_t *key, const uint8_t *iv, char *cipher)
bool check_ring_signature(const hash &prefix_hash, const key_image &image, const public_key *const *pubs, std::size_t pubs_count, const signature *sig)
Definition: crypto.h:333
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")
void generate_signature(const hash &prefix_hash, const public_key &pub, const secret_key &sec, signature &sig)
Definition: crypto.h:292
std::vector< pending_tx > ptx
Definition: wallet2.h:505
uint64_t index_in_tx
Definition: wallet2.h:555
uint64_t combinations_count(uint32_t k, uint32_t n)
Definition: combinator.cpp:35
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
const char * key
Definition: hmac_keccak.cpp:39
static std::pair< std::unique_ptr< wallet2 >, password_container > make_from_json(const boost::program_options::variables_map &vm, bool unattended, const std::string &json_file, const std::function< boost::optional< password_container >(const char *, bool)> &password_prompter)
Uses stdin and stdout. Returns a wallet2 if no errors.
Definition: wallet2.cpp:1227
cryptonote::tx_destination_entry change_dts
Definition: wallet2.h:423
void print_source_entry(const cryptonote::tx_source_entry &src)
Definition: wallet2.h:2137
#define FIRST_REFRESH_GRANULARITY
Definition: wallet2.cpp:132
#define MCLOG_RED(level, cat, x)
Definition: misc_log_ex.h:58
#define LOG_PRINT_L0(x)
Definition: misc_log_ex.h:99
wallet_keys_unlocker(wallet2 &w, const boost::optional< tools::password_container > &password)
Definition: wallet2.cpp:1034
crypto namespace.
Definition: crypto.cpp:58
failed_rpc_request< transfer_error, get_outs_error_message_index > get_outs_error
epee::mlocked< tools::scrubbed< ec_scalar > > secret_key
Definition: crypto.h:82
#define SIGNED_TX_PREFIX
Definition: wallet2.cpp:108
STL namespace.
unsigned short uint16_t
Definition: stdint.h:125
Definition: wallet2.h:552
bool find_tx_extra_field_by_type(const std::vector< tx_extra_field > &tx_extra_fields, T &field, size_t index=0)
#define SUBADDRESS_LOOKAHEAD_MINOR
Definition: wallet2.cpp:121
bool init_default_checkpoints(network_type nettype)
loads the default main chain checkpoints
bool load_file_to_string(const std::string &path_to_file, std::string &target_str, size_t max_size=1000000000)
std::vector< uint64_t > key_offsets
Non-owning sequence of data. Does not deep copy.
Definition: span.h:56
const size_t MAX_SPLIT_ATTEMPTS
Definition: wallet2.cpp:991
std::vector< uint64_t > relative_output_offsets_to_absolute(const std::vector< uint64_t > &off)
std::unordered_multimap< crypto::hash, payment_details > payment_container
Definition: wallet2.h:450
bool parse_binary(const std::string &blob, T &v)
Definition: binary_utils.h:41
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 rct
void keccak_finish(KECCAK_CTX *ctx, uint8_t *md)
#define HF_VERSION_MIN_MIXIN_4
unsigned char uint8_t
Definition: stdint.h:124
#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V8
void on_progress(const hw::device_progress &event) override
Definition: wallet2.cpp:1096
boost::optional< cryptonote::subaddress_index > get_subaddress_index(const cryptonote::account_public_address &address) const
Definition: wallet2.cpp:1437
uint64_t get_transaction_weight(const transaction &tx, size_t blob_size)
static threadpool & getInstance()
Definition: threadpool.h:46
Level
Represents enumeration for severity level used to determine level of logging.
std::shared_ptr< messages::Electroneum::ElectroneumGetTxKeyRequest > get_tx_key(const hw::device_cold::tx_key_data_t &tx_data)
Definition: protocol.cpp:1038
uint64_t amount
#define MULTISIG_EXPORT_FILE_MAGIC
Definition: wallet2.cpp:125
cryptonote::account_public_address addr_for_dust
Definition: wallet2.h:166
crypto::secret_key encrypt_key(crypto::secret_key key, const epee::wipeable_string &passphrase)
void add_subaddress_account(const std::string &label, const bool update_account_tags=true)
Definition: wallet2.cpp:1463
boost::filesystem::path data_dir
Definition: main.cpp:50
uint8_t type
Definition: rctTypes.h:241
#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V6
std::unordered_set< crypto::public_key > m_signers
Definition: wallet2.h:513
struct hash_func hashes[]
virtual bool set_name(const std::string &name)=0
etn_amount decodeRct(const rctSig &rv, const key &sk, unsigned int i, key &mask, hw::device &hwdev)
Definition: rctSigs.cpp:1150
#define PERF_TIMER_START(name)
Definition: perf_timer.h:85
const char * i18n_translate(const char *s, const std::string &context)
Definition: i18n.cpp:323
#define CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE
#define OUTPUT_EXPORT_FILE_MAGIC
Definition: wallet2.cpp:140
void copy(key &AA, const key &A)
Definition: rctOps.h:79
bool generate_key_derivation(const public_key &key1, const secret_key &key2, key_derivation &derivation)
Definition: crypto.h:272
std::string encode(const std::string &data)
Definition: base58.cpp:173
size_t real_output
static std::string device_name_option(const boost::program_options::variables_map &vm)
Definition: wallet2.cpp:1183
tuple make_tuple()
Definition: gtest-tuple.h:675
void append(const char *ptr, size_t len)
static epee::wipeable_string wipeable_string(const span< const std::uint8_t > src)
Definition: hex.cpp:69
void encrypt_keys(const crypto::chacha_key &key)
Definition: account.h:104
virtual bool generate_key_derivation(const crypto::public_key &pub, const crypto::secret_key &sec, crypto::key_derivation &derivation)=0
void set_seed_language(const std::string &language)
Sets the seed language.
Definition: wallet2.cpp:1425
std::vector< uint8_t > extra
bool dump_binary(T &v, std::string &blob)
Definition: binary_utils.h:51
void chacha8(const void *data, size_t length, const uint8_t *key, const uint8_t *iv, char *cipher)
#define CORE_RPC_STATUS_OK
std::enable_if< std::is_unsigned< T >::value, T >::type rand_idx(T sz)
Definition: crypto.h:244
std::vector< cryptonote::tx_destination_entry > m_dests
Definition: wallet2.h:385
std::vector< cryptonote::tx_source_entry > sources
Definition: wallet2.h:422
std::vector< key > keyV
Definition: rctTypes.h:88
#define RECENT_OUTPUT_BLOCKS
Definition: wallet2.cpp:114
uint16_t const RPC_DEFAULT_PORT
void on_button_pressed() override
Definition: wallet2.cpp:1076
#define MDEBUG(x)
Definition: misc_log_ex.h:76
const account_keys & get_keys() const
Definition: account.cpp:264
#define ETN_MINED_ETN_UNLOCK_WINDOW_V8
Primarily for use with epee::net_utils::http_client.
Definition: socks_connect.h:41
bool validate_hex(uint64_t length, const std::string &str)
std::vector< ecdhTuple > ecdhInfo
Definition: rctTypes.h:246
#define STAGENET_SEGREGATION_FORK_HEIGHT
Definition: wallet2.cpp:129
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 get_default_data_dir()
Returns the default data directory.
Definition: util.cpp:600
constexpr std::size_t size() const noexcept
Definition: span.h:111
std::vector< tx_construction_data > txes
Definition: wallet2.h:499
bool is_deprecated() const
Tells if the wallet file is deprecated.
Definition: wallet2.cpp:1546
Holds cryptonote related classes and helpers.
Definition: ban.cpp:40
const crypto::secret_key null_skey
Definition: crypto.cpp:73
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&#39;s spend public key by summing all o...
Definition: multisig.cpp:132
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)
bool parse_amount(uint64_t &amount, const std::string &str_amount_)
return true
#define FEE_ESTIMATE_GRACE_BLOCKS
Definition: wallet2.cpp:116
#define DEFAULT_MIN_OUTPUT_COUNT
Definition: wallet2.cpp:137
#define SECOND_OUTPUT_RELATEDNESS_THRESHOLD
Definition: wallet2.cpp:118
blobdata tx_to_blob(const transaction &tx)
mdb_size_t count(MDB_cursor *cur)
time_t time
Definition: blockchain.cpp:93
void generate_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional< public_key > &B, const public_key &D, const secret_key &r, signature &sig)
Definition: crypto.h:311
bool is_valid_decomposed_amount(uint64_t amount)
cryptonote::account_public_address get_subaddress(const cryptonote::subaddress_index &index) const
Definition: wallet2.cpp:1430
string
Definition: rapidjson.h:626
bool add_tx_pub_key_to_extra(transaction &tx, const crypto::public_key &tx_pub_key)
uint32_t multisig_rounds_required(uint32_t participants, uint32_t threshold)
Definition: multisig.cpp:181
std::vector< crypto::secret_key > calculate_multisig_keys(const std::vector< crypto::public_key > &derivations)
calculate_multisig_keys. Calculates secret multisig keys from others&#39; participants ones as follows: m...
Definition: multisig.cpp:111
std::string pod_to_hex(const t_pod_type &s)
Definition: string_tools.h:317
void generate_key_image(const public_key &pub, const secret_key &sec, key_image &image)
Definition: crypto.h:324
file_error_base< file_save_error_message_index > file_save_error
unsigned int get_default_decimal_point()
void wait(threadpool *tpool)
Definition: threadpool.cpp:115
std::vector< transfer_details > transfer_container
Definition: wallet2.h:449
#define SUBADDRESS_LOOKAHEAD_MAJOR
Definition: wallet2.cpp:120
#define HF_VERSION_MIN_MIXIN_6
#define FEE_PER_KB_V6
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.
bool generate_genesis_block(block &bl, std::string const &genesis_tx, uint32_t nonce)
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
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
#define MAKE_CORE_RPC_VERSION(major, minor)
GenericValue< UTF8<> > Value
GenericValue with UTF8 encoding.
Definition: document.h:2116
static std::vector< uint8_t > vector(boost::string_ref src)
Definition: hex.cpp:88
void decrypt_keys(const crypto::chacha_key &key)
Definition: account.h:105
bool load_t_from_binary(t_struct &out, const epee::span< const uint8_t > binary_buff)
unsigned int uint32_t
Definition: stdint.h:126
#define RECENT_OUTPUT_RATIO
Definition: wallet2.cpp:111
#define DIFFICULTY_TARGET_V6
const crypto::public_key null_pkey
Definition: crypto.cpp:72
std::string get_account_integrated_address_as_str(network_type nettype, account_public_address const &adr, crypto::hash8 const &payment_id)
struct hw::wallet_shim wallet_shim
void cn_fast_hash(const void *data, size_t length, char *hash)
hw::device & get_device() const
Definition: account.h:91
#define HF_VERSION_DYNAMIC_FEE
std::pair< size_t, wallet2::transfer_container > transfers
Definition: wallet2.h:500
std::set< uint32_t > subaddr_indices
Definition: wallet2.h:432
Various Tools.
Definition: tools.cpp:31
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)
bool parse_uri(const std::string uri, http::uri_content &content)
failed_rpc_request< refresh_error, get_hashes_error_message_index > get_hashes_error
bool derive_subaddress_public_key(const public_key &out_key, const key_derivation &derivation, std::size_t output_index, public_key &result)
Definition: crypto.h:286
void sc_reduce32(unsigned char *)
virtual bool get_public_address(cryptonote::account_public_address &pubkey)=0
Verify peer via specific (possibly chain) certificate(s) only.
void serialize(Archive &a, unsigned_tx_set &x, const boost::serialization::version_type ver)
void generate_ring_signature(const hash &prefix_hash, const key_image &image, const public_key *const *pubs, std::size_t pubs_count, const secret_key &sec, std::size_t sec_index, signature *sig)
Definition: crypto.h:327
boost::shared_ptr< call_befor_die_base > auto_scope_leave_caller
virtual void set_callback(i_device_callback *callback)
Definition: device.hpp:136
ssl_authentication_t auth
Definition: net_ssl.h:80
device & get_device(const std::string &device_descriptor)
Definition: device.cpp:95
std::string const GENESIS_TX
#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS
bool empty() const noexcept
bool get_short_payment_id(crypto::hash8 &payment_id8, const tools::wallet2::pending_tx &ptx, hw::device &hwdev)
void thaw(size_t idx)
Definition: wallet2.cpp:1591
static bool verify_password(const std::string &keys_file_name, const epee::wipeable_string &password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds)
verify password for specified wallet keys file.
Definition: wallet2.cpp:4699
#define HF_VERSION_SMALLER_BP
#define TESTNET_SEGREGATION_FORK_HEIGHT
Definition: wallet2.cpp:128
bool init(std::string daemon_address="http://localhost:8080", boost::optional< epee::net_utils::http::login > daemon_login=boost::none, boost::asio::ip::tcp::endpoint proxy={}, uint64_t upper_transaction_weight_limit=0, bool trusted_daemon=true, epee::net_utils::ssl_options_t ssl_options=epee::net_utils::ssl_support_t::e_ssl_support_autodetect, std::string blockchain_db_path="")
Definition: wallet2.cpp:1282
std::string obj_to_json_str(T &obj)
unsigned int get_max_concurrency() const
Definition: threadpool.cpp:92
unsigned __int64 uint64_t
Definition: stdint.h:136
std::vector< cryptonote::tx_destination_entry > splitted_dsts
Definition: wallet2.h:424
std::string get_subaddress_as_str(const cryptonote::subaddress_index &index) const
Definition: wallet2.cpp:1452
std::vector< multisig_sig > multisig_sigs
Definition: wallet2.h:475
bool decrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key)
Definition: device.hpp:210
#define CRITICAL_REGION_LOCAL(x)
Definition: syncobj.h:228
GenericDocument< UTF8<> > Document
GenericDocument with UTF8 encoding.
Definition: document.h:2512
bool t_serializable_object_to_blob(const t_object &to, blobdata &b_blob)
void load(Archive &a, std::unordered_map< h_key, hval > &x, const boost::serialization::version_type ver)
void keccak_init(KECCAK_CTX *ctx)
bool get_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash &payment_id)
crypto::public_key get_tx_pub_key_from_extra(const std::vector< uint8_t > &tx_extra, size_t pk_index)
cryptonote::transaction_prefix m_tx
Definition: wallet2.h:304
number
Definition: rapidjson.h:627
uint64_t amount
bool hex_to_pod(const std::string &hex_str, t_pod_type &s)
Definition: string_tools.h:324
bool save_string_to_file(const std::string &path_to_file, const std::string &str)
Definition: file_io_utils.h:73
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
#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V10
crypto::secret_key get_multisig_blinded_secret_key(const crypto::secret_key &key)
Definition: multisig.cpp:47
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
#define false
Definition: stdbool.h:38
GenericStringBuffer< UTF8< char >, CrtAllocator > StringBuffer
Definition: fwd.h:59
Verify peer via specific (non-chain) certificate(s) only.
void hash_to_scalar(key &hash, const void *data, const std::size_t l)
Definition: rctOps.cpp:536
uint64_t const CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX
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
static void init_options(boost::program_options::options_description &desc_params)
Definition: wallet2.cpp:1193
virtual cryptonote::account_public_address get_subaddress(const cryptonote::account_keys &keys, const cryptonote::subaddress_index &index)=0
#define TIME_MEASURE_START(var_name)
Definition: profile_tools.h:61
bool is_subaddress
std::vector< uint8_t > extra
Definition: wallet2.h:426
#define SSL_FINGERPRINT_SIZE
Definition: net_ssl.h:40
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
std::tuple< uint64_t, crypto::public_key, rct::key > get_outs_entry
Definition: wallet2.h:562
void set_subaddress_lookahead(size_t major, size_t minor)
Definition: wallet2.cpp:1535
bool parse_and_validate_tx_base_from_blob(const blobdata &tx_blob, transaction &tx)
boost::optional< epee::wipeable_string > on_pin_request() override
Definition: wallet2.cpp:1082
bool parse_and_validate_tx_from_blob(const blobdata &tx_blob, transaction &tx)
tx_construction_data construction_data
Definition: wallet2.h:477
crypto::hash get_pruned_transaction_hash(const transaction &t, const crypto::hash &pruned_data_hash)
#define HF_VERSION_ENABLE_RCT
#define LOG_PRINT_L3(x)
Definition: misc_log_ex.h:102
cryptonote::account_public_address get_address(const var_addr_t &inp)
Definition: chaingen.cpp:665
POD_CLASS public_key
Definition: crypto.h:76
const char * buf
Definition: slow_memmem.cpp:74
Useful when application has potentially harmful situtaions.
version
Supported socks variants.
Definition: socks.h:57
void set_default_decimal_point(unsigned int decimal_point)
#define MWARNING(x)
Definition: misc_log_ex.h:74
void expand_subaddresses(const cryptonote::subaddress_index &index, const bool udpate_account_tags=true)
Definition: wallet2.cpp:1478
void cn_fast_hash(const void *data, size_t length, char *hash)
bool get_encrypted_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash8 &payment_id)
void skGen(key &sk)
Definition: rctOps.cpp:253
#define TIME_MEASURE_FINISH(var_name)
Definition: profile_tools.h:64
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
crypto::hash tx_hash
void set_device(hw::device &hwdev)
Definition: account.h:92
#define CMPF(f)
rct::multisig_kLRki multisig_kLRki
std::string message("Message requiring signing")
void set_subaddress_label(const cryptonote::subaddress_index &index, const std::string &label)
Definition: wallet2.cpp:1528
crypto::secret_key m_view_secret_key
Definition: account.h:45
void set_refresh_from_block_height(uint64_t height)
Definition: wallet2.h:742
boost::endian::big_uint32_t ip
Definition: socks.cpp:61
void ecdhDecode(ecdhTuple &masked, const key &sharedSec, bool v2)
Definition: rctOps.cpp:712
bool set_daemon(std::string daemon_address="http://localhost:8080", boost::optional< epee::net_utils::http::login > daemon_login=boost::none, bool trusted_daemon=true, epee::net_utils::ssl_options_t ssl_options=epee::net_utils::ssl_support_t::e_ssl_support_autodetect)
Definition: wallet2.cpp:1268
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
Definition: pointer.h:1124
#define MULTISIG_UNSIGNED_TX_PREFIX
Definition: wallet2.cpp:109
std::tuple< uint64_t, crypto::public_key, rct::key > get_outs_entry
Definition: chaingen.h:352
account_public_address addr
std::string blobdata
Definition: blobdatatype.h:39
crypto::public_key get_subaddress_spend_public_key(const cryptonote::subaddress_index &index) const
Definition: wallet2.cpp:1445
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
static std::pair< std::unique_ptr< wallet2 >, password_container > make_new(const boost::program_options::variables_map &vm, bool unattended, const std::function< boost::optional< password_container >(const char *, bool)> &password_prompter)
Uses stdin and stdout. Returns a wallet2 and password for wallet with no file if no errors...
Definition: wallet2.cpp:1250
const transfer_details & get_transfer_details(size_t idx) const
Definition: wallet2.cpp:10852
CXA_THROW_INFO_T * info
Definition: stack_trace.cpp:91
static boost::optional< login > parse(std::string &&userpass, bool verify, const std::function< boost::optional< password_container >(bool)> &prompt)
Definition: password.cpp:268
static std::pair< std::unique_ptr< wallet2 >, password_container > make_from_file(const boost::program_options::variables_map &vm, bool unattended, const std::string &wallet_file, const std::function< boost::optional< password_container >(const char *, bool)> &password_prompter)
Uses stdin and stdout. Returns a wallet2 and password for wallet_file if no errors.
Definition: wallet2.cpp:1233
void scalarmultBase(key &aG, const key &a)
Definition: rctOps.cpp:350
expect< void > success() noexcept
Definition: expect.h:397
bool is_deterministic() const
Checks if deterministic wallet.
Definition: wallet2.cpp:1293
account_public_address m_account_address
Definition: account.h:43
bool check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional< public_key > &B, const public_key &D, const signature &sig)
Definition: crypto.h:314
cryptonote::account_public_address get_address() const
Definition: wallet2.h:793
uint64_t const DEFAULT_DUST_THRESHOLD
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
void digit_split_strategy(const std::vector< cryptonote::tx_destination_entry > &dsts, const cryptonote::tx_destination_entry &change_dst, uint64_t dust_threshold, std::vector< cryptonote::tx_destination_entry > &splitted_dsts, std::vector< cryptonote::tx_destination_entry > &dust_dsts)
Definition: wallet2.h:2098
POD_CLASS signature
Definition: crypto.h:108
void submit(waiter *waiter, std::function< void()> f, bool leaf=false)
Definition: threadpool.cpp:69
void freeze(size_t idx)
Definition: wallet2.cpp:1584
void get_transaction_prefix_hash(const transaction_prefix &tx, crypto::hash &h)
unsigned char bytes[32]
Definition: rctTypes.h:86
size_t get_num_subaddress_accounts() const
Definition: wallet2.h:801
key identity()
Definition: rctOps.h:73
POD_CLASS hash8
Definition: hash.h:53
void encrypt_keys(const crypto::chacha_key &key)
Definition: wallet2.cpp:4746
crypto::public_key key
crypto::signature shared_secret_sig
Definition: wallet2.h:558
const T & move(const T &t)
Definition: gtest-port.h:1317
gamma_picker(const std::vector< uint64_t > &rct_offsets)
Definition: wallet2.cpp:1011
string daemon_address
Definition: transfers.cpp:42
#define LOG_ERROR(x)
Definition: misc_log_ex.h:98
std::vector< crypto::public_key > real_out_additional_tx_keys
#define ENDL
Definition: misc_log_ex.h:149
Definition: blake256.h:37
void load(const std::string &wallet, const epee::wipeable_string &password)
Definition: wallet2.cpp:5833
std::string buff_to_hex_nodelimer(const std::string &src)
Definition: string_tools.h:87
void on_button_request(uint64_t code=0) override
Definition: wallet2.cpp:1070
POD_CLASS key_image
Definition: crypto.h:102
static const char * tr(const char *str)
Definition: wallet2.cpp:994
#define GAMMA_SHAPE
Definition: wallet2.cpp:134
#define PERF_TIMER_STOP(name)
Definition: perf_timer.h:86
crypto::secret_key generate_multisig_view_secret_key(const crypto::secret_key &skey, const std::vector< crypto::secret_key > &skeys)
Definition: multisig.cpp:124
#define THROW_WALLET_EXCEPTION_IF(cond, err_type,...)
const GenericPointer< typename T::ValueType > T2 value
Definition: pointer.h:1225
void * memcpy(void *a, const void *b, size_t c)
#define CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE
virtual bool derivation_to_scalar(const crypto::key_derivation &derivation, const size_t output_index, crypto::ec_scalar &res)=0
txout_target_v target
std::vector< cryptonote::tx_destination_entry > dests
Definition: wallet2.h:430
#define GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, name, type, jtype, mandatory, def)
Definition: json_util.h:32
crypto::secret_key m_spend_secret_key
Definition: account.h:44
size_t real_output_in_tx_index
bool derive_public_key(const key_derivation &derivation, std::size_t output_index, const public_key &base, public_key &derived_key)
Definition: crypto.h:275
static bool has_testnet_option(const boost::program_options::variables_map &vm)
Definition: wallet2.cpp:1173
crypto::key_image k_image
crypto::hash get_transaction_hash(const transaction &t)
bool remove_field_from_tx_extra(std::vector< uint8_t > &tx_extra, const std::type_info &type)
T get_arg(const boost::program_options::variables_map &vm, const arg_descriptor< T, false, true > &arg)
Definition: command_line.h:271
bool add_extra_nonce_to_tx_extra(std::vector< uint8_t > &tx_extra, const blobdata &extra_nonce)
signed __int64 int64_t
Definition: stdint.h:135
bool get_account_address_from_str(address_parse_info &info, network_type nettype, std::string const &str)
ssl_verification_t verification
Definition: net_ssl.h:82
static bool has_stagenet_option(const boost::program_options::variables_map &vm)
Definition: wallet2.cpp:1178
#define SEGREGATION_FORK_HEIGHT
Definition: wallet2.cpp:127
Mnemonic seed generation and wallet restoration from them.
std::vector< crypto::secret_key > m_multisig_keys
Definition: account.h:46
void keccak_update(KECCAK_CTX *ctx, const uint8_t *in, size_t inlen)
#define CURRENT_HARDFORK_VERSION
crypto::key_image m_key_image
Definition: wallet2.h:311
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
std::vector< output_entry > outputs
uint32_t const GENESIS_NONCE
#define AUTO_VAL_INIT(v)
Definition: misc_language.h:53
#define FEE_PER_BYTE
bool get_inputs_etn_amount(const transaction &tx, uint64_t &etn)
void keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen)
bool has_strong_verification(boost::string_ref host) const noexcept
True if host can be verified using this configuration WITHOUT system "root" CAs.
Definition: net_ssl.cpp:402
crypto::signature key_image_sig
Definition: wallet2.h:559
uint64_t dust_threshold
Definition: wallet2.h:164
crypto::hash txid
Definition: wallet2.h:554
key zeroCommit(etn_amount amount)
Definition: rctOps.cpp:322
failed_rpc_request< refresh_error, get_blocks_error_message_index > get_blocks_error
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)
crypto::hash get_block_hash(uint64_t height)
crypto::secret_key decrypt_key(crypto::secret_key key, const epee::wipeable_string &passphrase)
boost::optional< int > bp_version
Definition: device_cold.hpp:48
cryptonote::account_base & get_account()
Definition: wallet2.h:734
POD_CLASS hash
Definition: hash.h:50
#define THROW_WALLET_EXCEPTION(err_type,...)
cryptonote::tx_destination_entry change_dts
Definition: wallet2.h:469
void addKeys(key &AB, const key &A, const key &B)
Definition: rctOps.cpp:420
std::string get_integrated_address_as_str(const crypto::hash8 &payment_id) const
Definition: wallet2.cpp:1458
#define HF_VERSION_MIN_MIXIN_10
crypto::secret_key tx_key
Definition: wallet2.h:472
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
void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, uint64_t height)
const char * address
Definition: multisig.cpp:37
std::vector< cryptonote::address_parse_info > tx_recipients
Definition: device_cold.hpp:47
bool secret_key_to_public_key(const secret_key &sec, public_key &pub)
Definition: crypto.h:262
const char * spendkey
Definition: multisig.cpp:38
std::string to_string(t_connection_type type)
virtual crypto::public_key get_subaddress_spend_public_key(const cryptonote::account_keys &keys, const cryptonote::subaddress_index &index)=0
crypto::key_image key_image
Definition: wallet2.h:557
std::pair< uint64_t, rct::ctkey > output_entry
void set_payment_id_to_tx_extra_nonce(blobdata &extra_nonce, const crypto::hash &payment_id)
std::vector< crypto::public_key > get_additional_tx_pub_keys_from_extra(const std::vector< uint8_t > &tx_extra)
crypto::chacha_iv iv
Definition: wallet2.h:534
bool get_multisig_seed(epee::wipeable_string &seed, const epee::wipeable_string &passphrase=std::string(), bool raw=true) const
Definition: wallet2.cpp:1327
else if(0==res)
bool get_is_old_style_seed(const epee::wipeable_string &seed)
Tells if the seed passed is an old style seed or not.
#define UNSIGNED_TX_PREFIX
Definition: wallet2.cpp:107
void * memwipe(void *src, size_t n)
virtual bool connect(void)=0
std::vector< pending_tx > m_ptx
Definition: wallet2.h:512
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:12036
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)
virtual void set_derivation_path(const std::string &derivation_path)
Definition: device.hpp:137
const char * data() const noexcept
#define RECENT_OUTPUT_ZONE
Definition: wallet2.cpp:113
#define HF_VERSION_PER_BYTE_FEE
virtual bool has_tx_cold_sign(void) const
Definition: device.hpp:244
void sc_add(unsigned char *, const unsigned char *, const unsigned char *)
PUSH_WARNINGS bool get_xtype_from_string(OUT XType &val, const std::string &str_id)
Definition: string_tools.h:125
#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS
#define ELECTRONEUM_DEFAULT_LOG_CATEGORY
Definition: wallet2.cpp:94
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)
key zero()
Definition: rctOps.h:70
void explicit_refresh_from_block_height(bool expl)
Definition: wallet2.h:745
error
Tracks LMDB error codes.
Definition: error.h:44
const crypto::public_key & get_public_key() const
Definition: wallet2.h:326
bool parse_hexstr_to_binbuff(const epee::span< const char > s, epee::span< char > &res)
Definition: string_tools.h:92
#define TX_WEIGHT_TARGET(bytes)
Definition: wallet2.cpp:100
std::string encrypt(const char *plaintext, size_t len, const crypto::secret_key &skey, bool authenticated=true) const
Definition: wallet2.cpp:13181
#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5
etn_amount decodeRctSimple(const rctSig &rv, const key &sk, unsigned int i, key &mask, hw::device &hwdev)
Definition: rctSigs.cpp:1180
void generate(const std::string &wallet_, const epee::wipeable_string &password, const epee::wipeable_string &multisig_data, bool create_address_file=false)
Generates a wallet or restores one.
Definition: wallet2.cpp:4869
virtual bool init(void)=0
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
#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1
rapidjson::Document json
Definition: transport.cpp:49
void add_subaddress(uint32_t index_major, const std::string &label)
Definition: wallet2.cpp:1470
bool check_signature(const hash &prefix_hash, const public_key &pub, const signature &sig)
Definition: crypto.h:295
bool parse_and_validate_block_from_blob(const blobdata &b_blob, block &b, crypto::hash *block_hash)
virtual void computing_key_images(bool started)
Definition: device.hpp:247
std::vector< size_t > selected_transfers
Definition: wallet2.h:425
#define CACHE_KEY_TAIL
Definition: wallet2.cpp:104
void encrypt_viewkey(const crypto::chacha_key &key)
Definition: account.h:106
virtual void set_network_type(cryptonote::network_type network_type)
Definition: device.hpp:248
std::string get_subaddress_label(const cryptonote::subaddress_index &index) const
Definition: wallet2.cpp:1518
subaddress_index subaddr_index
crypto::secret_key calculate_multisig_signer_key(const std::vector< crypto::secret_key > &multisig_keys)
Definition: multisig.cpp:100
bool multisig(bool *ready=NULL, uint32_t *threshold=NULL, uint32_t *total=NULL) const
Definition: wallet2.cpp:5634
constexpr pointer data() const noexcept
Definition: span.h:110
const sources_t & sources() const
uint8_t threshold
Definition: blockchain.cpp:92