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> 93 #undef ELECTRONEUM_DEFAULT_LOG_CATEGORY 94 #define ELECTRONEUM_DEFAULT_LOG_CATEGORY "wallet.wallet2" 97 #define APPROXIMATE_INPUT_BYTES 80 100 #define TX_WEIGHT_TARGET(bytes) (bytes*2/3) 103 #define CHACHA8_KEY_TAIL 0x8c 104 #define CACHE_KEY_TAIL 0x8d 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" 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) 116 #define FEE_ESTIMATE_GRACE_BLOCKS 10 // estimate fee valid for that many blocks 118 #define SECOND_OUTPUT_RELATEDNESS_THRESHOLD 0.0f 120 #define SUBADDRESS_LOOKAHEAD_MAJOR 50 121 #define SUBADDRESS_LOOKAHEAD_MINOR 200 123 #define KEY_IMAGE_EXPORT_FILE_MAGIC "Electroneum key image export\002" 125 #define MULTISIG_EXPORT_FILE_MAGIC "Electroneum multisig export\001" 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 132 #define FIRST_REFRESH_GRANULARITY 1024 134 #define GAMMA_SHAPE 19.28 135 #define GAMMA_SCALE (1/1.61) 137 #define DEFAULT_MIN_OUTPUT_COUNT 5 138 #define DEFAULT_MIN_OUTPUT_VALUE (2*COIN) 140 #define OUTPUT_EXPORT_FILE_MAGIC "Electroneum output export\003" 142 static const std::string MULTISIG_SIGNATURE_MAGIC =
"SigMultisigPkV1";
143 static const std::string MULTISIG_EXTRA_INFO_MAGIC =
"MultisigxV1";
151 dir = dir.remove_filename();
152 dir /=
".shared-ringdb";
163 for (
const auto &
key: keys)
178 std::vector<crypto::public_key> secret_keys_to_public_keys(
const std::vector<crypto::secret_key>& keys)
180 std::vector<crypto::public_key> public_keys;
181 public_keys.reserve(keys.size());
192 bool keys_intersect(
const std::unordered_set<crypto::public_key>& s1,
const std::unordered_set<crypto::public_key>& s2)
194 if (s1.empty() || s2.empty())
197 for (
const auto& e: s1)
199 if (s2.find(e) != s2.end())
206 void add_reason(
std::string &reasons,
const char *reason)
208 if (!reasons.empty())
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");
225 add_reason(reason,
"too big");
227 add_reason(reason,
"overspend");
229 add_reason(reason,
"fee too low");
231 add_reason(reason,
"tx is not ringct");
232 if (
res.sanity_check_failed)
233 add_reason(reason,
"tx sanity check failed");
235 add_reason(reason,
"tx was not relayed");
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};
266 get_default_ringdb_path(),
267 {{ &testnet, &stagenet }},
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();
289 keys_file = file_path;
290 wallet_file = file_path;
291 boost::system::error_code e;
297 keys_file +=
".keys";
299 mms_file = file_path +
".mms";
304 uint64_t kB = (bytes + 1023) / 1024;
305 return kB * fee_per_kb * fee_multiplier;
310 uint64_t fee = weight * base_fee * fee_multiplier;
311 fee = (fee + fee_quantization_mask - 1) / fee_quantization_mask * fee_quantization_mask;
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)
344 auto daemon_ssl_allowed_fingerprints =
command_line::get_arg(vm, opts.daemon_ssl_allowed_fingerprints);
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);
354 else if (!daemon_ssl_ca_file.empty() || !daemon_ssl_allowed_fingerprints.empty())
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)
387 boost::optional<epee::net_utils::http::login> login{};
392 return password_prompter(
"Daemon client password", verify);
401 if (daemon_host.empty())
402 daemon_host =
"localhost";
417 const bool verification_required =
429 boost::asio::ip::tcp::endpoint proxy{};
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";
441 proxy_port = proxy_port.substr(proxy_host.size() + 1);
447 std::string{
"Invalid port specified for --"} + opts.proxy.name
450 boost::system::error_code
error{};
451 proxy = ip::tcp::endpoint{ip::address::from_string(
std::string{proxy_host},
error), port_value};
455 boost::optional<bool> trusted_daemon;
466 trusted_daemon =
false;
470 trusted_daemon =
true;
473 catch (
const std::exception &e) { }
476 std::unique_ptr<tools::wallet2> wallet(
new tools::wallet2(nettype, kdf_rounds, unattended));
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);
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);
489 wallet->enable_dns(
false);
492 wallet->set_offline();
499 catch (
const std::exception &e)
501 MERROR(
"Failed to parse tx notify spec: " << e.what());
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)
527 boost::trim_right_if(password, boost::is_any_of(
"\r\n"));
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)
545 std::unique_ptr<tools::wallet2> wallet;
547 const auto do_generate = [&]() ->
bool {
555 if (
json.Parse(
buf.c_str()).HasParseError()) {
561 const int current_version = 1;
563 ((boost::format(
tools::wallet2::tr(
"Version %u too new, we can only grok up to %u")) % field_version % current_version)).str());
568 const bool recover =
true;
574 if (field_viewkey_found)
590 if (field_spendkey_found)
607 bool restore_deterministic_wallet =
false;
608 if (field_seed_found)
614 restore_deterministic_wallet =
true;
617 if (field_seed_passphrase_found)
619 if (!field_seed_passphrase.empty())
627 bool create_address_file = field_create_address_file;
630 if (!field_seed_found && !field_viewkey_found && !field_spendkey_found)
634 if (field_seed_found && (field_viewkey_found || field_spendkey_found))
641 if (field_address_found)
648 if (field_viewkey_found)
654 if (
info.address.m_view_public_key != pkey) {
658 if (field_spendkey_found)
664 if (
info.address.m_spend_public_key != pkey) {
675 wallet.reset(make_basic(vm, unattended, opts, password_prompter).release());
678 if (!old_language.empty())
683 if (!field_seed.empty())
685 wallet->
generate(field_filename, field_password, recovery_key, recover,
false, create_address_file);
686 password = field_password;
688 else if (field_viewkey.empty() && !field_spendkey.empty())
690 wallet->
generate(field_filename, field_password,
spendkey, recover,
false, create_address_file);
691 password = field_password;
700 if (field_spendkey.empty())
704 if (field_address_found)
711 address.m_spend_public_key =
info.address.m_spend_public_key;
717 wallet->
generate(field_filename, field_password,
address, viewkey, create_address_file);
718 password = field_password;
726 password = field_password;
730 catch (
const std::exception& e)
744 std::string strjoin(
const std::vector<size_t> &V,
const char *sep)
746 std::stringstream ss;
748 for (
const auto &v: V)
758 static bool emplace_or_replace(std::unordered_multimap<crypto::hash, tools::wallet2::pool_payment_details> &container,
761 auto range = container.equal_range(
key);
762 for (
auto i = range.first; i != range.second; ++i)
770 container.emplace(
key, pd);
774 void drop_from_short_history(std::list<crypto::hash> &short_chain_history,
size_t N)
776 std::list<crypto::hash>::iterator right;
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);
787 size_t estimate_rct_tx_size(
int n_inputs,
int mixin,
int n_outputs,
size_t extra_size,
bool bulletproof)
797 size += n_inputs * (1+6+(mixin+1)*2+32);
800 size += n_outputs * (6+32);
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;
819 size += (2*64*32+32+64*32) * n_outputs;
822 size += n_inputs * (64 * (mixin+1) + 32);
828 size += 32 * n_inputs;
830 size += 8 * n_outputs;
832 size += 32 * n_outputs;
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) + 2 * 32 * (mixin+1) * n_inputs + 32 * n_outputs) <<
" saved)");
840 size_t estimate_tx_size(
bool use_rct,
int n_inputs,
int mixin,
int n_outputs,
size_t extra_size,
bool bulletproof)
843 return estimate_rct_tx_size(n_inputs, mixin, n_outputs, extra_size, bulletproof);
848 uint64_t estimate_tx_weight(
bool use_rct,
int n_inputs,
int mixin,
int n_outputs,
size_t extra_size,
bool bulletproof)
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)
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);
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)
873 if (use_per_byte_fee)
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);
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);
887 if (use_per_byte_fee)
890 return calculate_fee(base_fee, blob_size, fee_multiplier);
895 std::vector<tx_extra_field> tx_extra_fields;
902 if (ptx.
dests.empty())
904 MWARNING(
"Encrypted payment id found, but no destinations public key, cannot decrypt");
924 memcpy(payment_id.data, payment_id8.data, 8);
925 memset(payment_id.data + 8, 0, 24);
932 return construction_data;
937 static constexpr
uint32_t uint32_max = std::numeric_limits<uint32_t>::max();
938 if (idx > uint32_max - extra)
960 "Response claims a different hash than the data yields");
993 constexpr
const std::chrono::seconds wallet2::rpc_timeout;
996 gamma_picker::gamma_picker(
const std::vector<uint64_t> &rct_offsets,
double shape,
double scale):
997 rct_offsets(rct_offsets)
999 gamma = std::gamma_distribution<double>(shape, scale);
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();
1006 num_rct_outputs = *(end - 1);
1015 double x = gamma(engine);
1019 return std::numeric_limits<uint64_t>::max();
1024 uint64_t index = std::distance(begin, it);
1026 const uint64_t first_rct = index == 0 ? 0 : rct_offsets[index - 1];
1027 const uint64_t n_rct = rct_offsets[index] - first_rct;
1029 return std::numeric_limits<uint64_t>::max();
1030 MTRACE(
"Picking 1/" << n_rct <<
" in block " << index);
1036 locked(password !=
boost::none)
1044 w.generate_chacha_key_from_password(pass,
key);
1054 w.generate_chacha_key_from_password(password,
key);
1065 MERROR(
"Failed to re-encrypt wallet keys");
1073 wallet->on_device_button_request(code);
1079 wallet->on_device_button_pressed();
1085 return wallet->on_device_pin_request();
1092 return wallet->on_device_passphrase_request(on_device);
1099 wallet->on_device_progress(
event);
1103 m_multisig_rescan_info(NULL),
1104 m_multisig_rescan_k(NULL),
1105 m_upper_transaction_weight_limit(0),
1108 m_trusted_daemon(
false),
1110 m_multisig_rounds_passed(0),
1111 m_always_confirm_transfers(
true),
1112 m_print_ring_members(
false),
1113 m_store_tx_info(
true),
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),
1142 m_multisig_threshold(0),
1143 m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex),
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),
1155 m_key_device_type(hw::device::device_type::SOFTWARE),
1156 m_ring_history_saved(
false),
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),
1165 m_account_major_offset(0)
1230 return generate_from_json(json_file, vm, unattended, opts, password_prompter);
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)
1237 auto pwd = get_password(vm, opts, password_prompter,
false);
1242 auto wallet = make_basic(vm, unattended, opts, password_prompter);
1243 if (wallet && !wallet_file.empty())
1245 wallet->
load(wallet_file, pwd->password());
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)
1253 auto pwd = get_password(vm, opts, password_prompter,
true);
1258 return {make_basic(vm, unattended, opts, password_prompter),
std::move(*pwd)};
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)
1264 return make_basic(vm, unattended, opts, password_prompter);
1270 boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex);
1275 m_daemon_login =
std::move(daemon_login);
1276 m_trusted_daemon = trusted_daemon;
1285 m_is_initialized =
true;
1286 m_upper_transaction_weight_limit = upper_transaction_weight_limit;
1288 if (proxy != boost::asio::ip::tcp::endpoint{})
1304 if (!keys_deterministic)
1306 std::cout <<
"This is not a deterministic wallet" << std::endl;
1309 if (seed_language.empty())
1311 std::cout <<
"seed_language not set" << std::endl;
1316 if (!passphrase.
empty())
1320 std::cout <<
"Failed to create seed from key for language: " << seed_language << std::endl;
1333 std::cout <<
"This is not a multisig wallet" << std::endl;
1338 std::cout <<
"This multisig wallet is not yet finalized" << std::endl;
1341 if (!raw && seed_language.empty())
1343 std::cout <<
"seed_language not set" << std::endl;
1354 data.
append((
const char*)&skey,
sizeof(skey));
1356 data.
append((
const char*)&pkey,
sizeof(pkey));
1358 data.
append((
const char*)&skey,
sizeof(skey));
1360 data.
append((
const char*)&pkey,
sizeof(pkey));
1362 data.
append((
const char*)&skey,
sizeof(skey));
1363 for (
const auto &signer: m_multisig_signers)
1364 data.
append((
const char*)&signer,
sizeof(signer));
1366 if (!passphrase.
empty())
1382 std::cout <<
"Failed to encode seed";
1393 hw::device &hwdev = lookup_device(m_device_name);
1400 MERROR(
"Could not init device");
1406 MERROR(
"Could not connect to the device");
1419 return seed_language;
1427 seed_language = language;
1439 auto index = m_subaddresses.find(
address.m_spend_public_key);
1440 if (index == m_subaddresses.end())
1442 return index->second;
1467 m_subaddress_labels[index_major][0] = label;
1475 m_subaddress_labels[index_major][index_minor] = label;
1481 if (m_subaddress_labels.size() <= index.
major)
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)
1488 const uint32_t end = get_subaddress_clamped_sum((index2.
major == index.
major ? index.
minor : 0), m_subaddress_lookahead_minor);
1493 m_subaddresses[D] = index2;
1496 m_subaddress_labels.resize(index.
major + 1, {
"Untitled account"});
1497 m_subaddress_labels[index.
major].resize(index.
minor + 1);
1499 if(update_account_tags)
1502 else if (m_subaddress_labels[index.
major].size() <= index.
minor)
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();
1512 m_subaddresses[D] = index2;
1514 m_subaddress_labels[index.
major].resize(index.
minor + 1);
1520 if (index.
major >= m_subaddress_labels.size() || index.
minor >= m_subaddress_labels[index.
major].size())
1522 MERROR(
"Subaddress label doesn't exist");
1525 return m_subaddress_labels[index.
major][index.
minor];
1532 m_subaddress_labels[index.
major][index.
minor] = label;
1539 m_subaddress_lookahead_major = major;
1540 m_subaddress_lookahead_minor = minor;
1548 return is_old_file_format;
1551 void wallet2::set_spent(
size_t idx,
uint64_t height,
bool public_out)
1553 transfer_details &td = m_transfers[idx];
1557 <<
height <<
": chainstate index " << td.m_txid <<
":" << td.m_internal_output_index
1558 <<
", amount " <<
print_etn(td.m_amount));
1561 <<
height <<
": ki " << td.m_key_image
1562 <<
", amount " <<
print_etn(td.m_amount));
1566 td.m_spent_height =
height;
1569 void wallet2::set_unspent(
size_t idx,
bool public_out)
1571 transfer_details &td = m_transfers[idx];
1575 << td.m_txid <<
":"<< td.m_internal_output_index <<
", amount " <<
print_etn(td.m_amount));
1581 td.m_spent_height = 0;
1595 td.m_frozen =
false;
1622 for (
size_t idx = 0; idx < m_transfers.size(); ++idx)
1624 const transfer_details &td = m_transfers[idx];
1625 if (td.m_key_image_known && td.m_key_image == ki)
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 1639 boost::unique_lock<hw::device> hwdev_lock (hwdev);
1643 tx_scan_info.error =
true;
1644 LOG_ERROR(
"wrong type id in transaction out");
1649 additional_derivations, i, hwdev);
1653 auto out_address = boost::get<txout_to_key_public>(o.
target).
address;
1655 tx_scan_info.received =
1656 (receive_info == boost::none) ?
1659 receive_info : boost::none;
1661 if(tx_scan_info.received)
1663 tx_scan_info.etn_transfered = o.
amount;
1667 tx_scan_info.etn_transfered = 0;
1669 tx_scan_info.error =
false;
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 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);
1678 tx_scan_info.received = is_out_data->received[i];
1679 if(tx_scan_info.received)
1681 tx_scan_info.etn_transfered = o.
amount;
1685 tx_scan_info.etn_transfered = 0;
1687 tx_scan_info.error =
false;
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 1692 tx_scan_info.received = boost::none;
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;
1719 catch (
const std::exception &e)
1721 LOG_ERROR(
"Failed to decode input " << i);
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)
1731 if (m_ask_password ==
AskPasswordToDecrypt && !m_unattended && !m_watch_only && !m_multisig_rescan_k)
1735 if (!m_encrypt_keys_after_refresh)
1737 boost::optional<epee::wipeable_string> pwd = m_callback->
on_get_password(pool ?
"output found in pool" :
"output received");
1741 m_encrypt_keys_after_refresh = *pwd;
1747 tx_scan_info.in_ephemeral.pub = boost::get<cryptonote::txout_to_key>(tx.
vout[i].target).
key;
1749 tx_scan_info.ki = rct::rct2ki(
rct::zero());
1756 error::wallet_internal_error,
"key_image generated ephemeral public key not matched with output_key");
1759 THROW_WALLET_EXCEPTION_IF(std::find(outs.begin(), outs.end(), i) != outs.end(), error::wallet_internal_error,
"Same output cannot be added twice");
1761 if (tx_scan_info.etn_transfered == 0 && !miner_tx)
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());
1765 if (tx_scan_info.etn_transfered == 0)
1767 MERROR(
"Invalid output amount, skipping");
1768 tx_scan_info.error =
true;
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");
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;
1785 LOG_PRINT_L0(
"Transaction extra has unsupported format: " << txid);
1786 if (tx_cache_data.tx_extra_fields.empty())
1793 if (!is_miner || m_refresh_type != RefreshType::RefreshNoCoinbase)
1795 const size_t rec_size = is_miner && m_refresh_type == RefreshType::RefreshOptimizeCoinbase ? 1 : tx.
vout.size();
1796 if (!tx.
vout.empty())
1798 const std::vector<boost::optional<cryptonote::subaddress_receive_info>> rec(rec_size, boost::none);
1806 size_t pk_index = 0;
1808 tx_cache_data.primary.push_back({pub_key_field.
pub_key, {}, rec});
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], {}, {}});
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)
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;
1830 bool notify =
false;
1832 std::vector<tx_extra_field> local_tx_extra_fields;
1833 if (tx_cache_data.tx_extra_fields.empty())
1838 LOG_PRINT_L0(
"Transaction extra has unsupported format: " << txid);
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;
1844 size_t pk_index = 0;
1845 std::vector<tx_scan_info_t> tx_scan_info(tx.
vout.size());
1846 std::deque<bool> output_found(tx.
vout.size(),
false);
1849 while (!tx.
vout.empty()) {
1850 std::vector<size_t> outs;
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);
1863 if (!tx_cache_data.primary.empty()) {
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");
1869 int num_vouts_received = 0;
1870 tx_pub_key = pub_key_field.
pub_key;
1876 std::vector<crypto::key_derivation> additional_derivations;
1879 const wallet2::is_out_data *is_out_data_ptr = NULL;
1883 if (tx_cache_data.primary.empty()) {
1885 boost::unique_lock<hw::device> hwdev_lock(hwdev);
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");
1896 if (pk_index == 1) {
1899 for (
size_t i = 0; i < additional_tx_pub_keys.
data.size(); ++i) {
1900 additional_derivations.push_back({});
1903 additional_derivations.back())) {
1904 MWARNING(
"Failed to generate key derivation from additional tx pubkey in " << txid
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);
1929 if (tx.
version > 1 && !tx_cache_data.public_outs.empty()) {
1930 is_out_data_ptr = &tx_cache_data.public_outs[0];
1934 if (miner_tx && m_refresh_type == RefreshNoCoinbase) {
1938 else if (miner_tx && m_refresh_type == RefreshOptimizeCoinbase) {
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]);
1944 m_account.get_keys());
1947 if (tx_scan_info[0].received) {
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);
1955 waiter.
wait(&tpool);
1958 boost::unique_lock<hw::device> hwdev_lock(hwdev);
1960 for (
size_t i = 0; i < tx.
vout.size(); ++i) {
1962 m_account.get_keys());
1963 if (tx_scan_info[i].received)
1967 additional_tx_pub_keys.
data, derivation, additional_derivations);
1969 scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_etn_got_in_outs,
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);
1983 waiter.
wait(&tpool);
1986 boost::unique_lock<hw::device> hwdev_lock(hwdev);
1988 for (
size_t i = 0; i < tx.
vout.size(); ++i) {
1990 m_account.get_keys());
1991 if (tx_scan_info[i].received)
1996 additional_tx_pub_keys.
data,
1997 derivation, additional_derivations);
1999 scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_etn_got_in_outs,
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]);
2010 m_account.get_keys());
2011 if (tx_scan_info[i].received)
2014 boost::unique_lock<hw::device> hwdev_lock(hwdev);
2018 additional_tx_pub_keys.
data,
2019 derivation, additional_derivations);
2021 scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_etn_got_in_outs,
2027 if (!outs.empty() && num_vouts_received > 0) {
2033 " not match with daemon response size=" +
std::to_string(o_indices.size()));
2036 for (
const size_t o: outs) {
2038 "wrong out in transaction: internal index=" +
2041 auto kit = m_pub_keys.find(tx_scan_info[o].in_ephemeral.pub);
2044 error::wallet_internal_error,
2045 std::string(
"Unexpected transfer index from public key: ")
2047 (kit == m_pub_keys.end() ?
"<none>" : boost::lexical_cast<
std::string>(
2049 +
", m_transfers.size() is " +
2050 boost::lexical_cast<
std::string>(m_transfers.size()));
2051 if (kit == m_pub_keys.end()) {
2052 uint64_t amount = tx.
vout[o].amount ? tx.
vout[o].amount : tx_scan_info[o].amount;
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];
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) {
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;
2074 td.m_key_image_request =
true;
2076 td.m_key_image_request =
false;
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);
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;
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);
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);
2109 total_received_1 += amount;
2111 }
else if (m_transfers[kit->second].m_spent ||
2113 m_transfers[kit->second].amount() >= tx_scan_info[o].amount) {
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;
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");
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");
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();
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();
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];
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);
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;
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);
2169 error::wallet_internal_error,
"Inconsistent public keys");
2171 "Inconsistent spent status");
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);
2178 total_received_1 += extra_amount;
2185 if (!outs.empty() && num_vouts_received > 0) {
2188 for (
const size_t o: outs) {
2190 "wrong out in transaction: internal index=" +
2193 auto kit = m_chainstate_indexes.find(std::make_pair(txid, o));
2196 error::wallet_internal_error,
2197 std::string(
"Unexpected transfer index from chainstate index: ")
2199 (kit == m_chainstate_indexes.end() ?
"<none>" : boost::lexical_cast<
std::string>(
2201 +
", m_transfers.size() is " +
2202 boost::lexical_cast<
std::string>(m_transfers.size()));
2203 if (kit == m_chainstate_indexes.end()) {
2204 uint64_t amount = tx.
vout[o].amount ? tx.
vout[o].amount : tx_scan_info[o].amount;
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();
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);
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;
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);
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);
2244 total_received_1 += amount;
2246 }
else if (m_transfers[kit->second].m_spent ||
2248 m_transfers[kit->second].amount() >= tx_scan_info[o].amount) {
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;
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");
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");
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();
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();
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();
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);
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;
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);
2307 error::wallet_internal_error,
"Inconsistent public keys");
2309 "Inconsistent spent status");
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);
2316 total_received_1 += extra_amount;
2328 auto subaddr_account ([]()->boost::optional<uint32_t> {
return boost::none;}());
2329 std::set<uint32_t> subaddr_indices;
2331 for(
auto& in: tx.
vin)
2337 auto it = m_key_images.find(in_to_key.
k_image);
2338 if (it != m_key_images.end())
2340 transfer_details &td = m_transfers[it->second];
2343 if (amount != td.amount()) {
2344 MERROR(
"Inconsistent amount in tx input: got " <<
print_etn(amount) <<
2345 ", expected " <<
print_etn(td.amount()));
2350 td.m_amount = amount;
2353 amount = td.amount();
2355 tx_etn_spent_in_ins += amount;
2356 if (subaddr_account && *subaddr_account != td.m_subaddr_index.major)
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);
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);
2369 if (!pool && m_track_uses) {
2374 if (output_tracker_cache) {
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;
2381 "Output tracker cache index out of range");
2382 m_transfers[idx].m_uses.push_back(std::make_pair(
height, txid));
2387 for (transfer_details &td: m_transfers) {
2388 if (amount != td.m_amount)
2391 if (offset == td.m_global_output_index)
2392 td.m_uses.push_back(std::make_pair(
height, txid));
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())
2403 transfer_details &td = m_transfers[it->second];
2406 if (amount != td.amount()) {
2407 MERROR(
"Inconsistent amount in tx input: got " <<
print_etn(amount) <<
2408 ", expected " <<
print_etn(td.amount()));
2413 td.m_amount = amount;
2416 amount = td.amount();
2418 tx_etn_spent_in_ins += amount;
2419 if (subaddr_account && *subaddr_account != td.m_subaddr_index.major)
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);
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);
2432 if (!pool && m_track_uses) {
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(
2440 if (i != output_tracker_cache->second.end()) {
2441 size_t idx = i->second;
2443 "Output tracker cache index out of range");
2444 m_transfers[idx].m_uses.push_back(std::make_pair(
height, txid));
2447 for (transfer_details &td: m_transfers) {
2448 if (in_to_key_public.
tx_hash != td.m_txid)
2451 td.m_uses.push_back(std::make_pair(
height, txid));
2466 if (tx_etn_spent_in_ins > 0 && !pool)
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)
2472 return acc + (p.first.major == *subaddr_account ? p.second : 0);
2474 process_outgoing(txid, tx,
height, ts, tx_etn_spent_in_ins, self_received, *subaddr_account, subaddr_indices);
2476 if (tx_etn_spent_in_ins == self_received + fee)
2478 auto i = m_confirmed_txs.find(txid);
2482 i->second.m_change = self_received;
2488 for (
auto i = tx_etn_got_in_outs.begin(); i != tx_etn_got_in_outs.end();)
2490 if (subaddr_account && i->first.major == *subaddr_account)
2492 sub_change += i->second;
2493 i = tx_etn_got_in_outs.erase(i);
2500 if (tx_etn_got_in_outs.size() > 0)
2510 LOG_PRINT_L2(
"Found encrypted payment ID: " << payment_id8);
2511 MINFO(
"Consider using subaddresses instead of encrypted payment IDs");
2514 if (!m_account.get_device().decrypt_payment_id(payment_id8, tx_pub_key, m_account.get_keys().m_view_secret_key))
2516 LOG_PRINT_L0(
"Failed to decrypt payment ID: " << payment_id8);
2521 memcpy(payment_id.data, payment_id8.data, 8);
2523 memset(payment_id.data + 8, 0, 24);
2529 LOG_PRINT_L1(
"No public key found in tx, unable to decrypt payment id");
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");
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)
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",
"**********************************************************************");
2553 bool all_same =
true;
2554 for (
const auto& i : tx_etn_got_in_outs)
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;
2562 payment.m_timestamp = ts;
2563 payment.m_coinbase = miner_tx;
2564 payment.m_subaddr_index = i.first;
2566 if (emplace_or_replace(m_unconfirmed_payments, payment_id, pool_payment_details{payment, double_spend_seen, nonexistent_utxo_seen}))
2568 if (0 != m_callback)
2569 m_callback->on_unconfirmed_etn_received(
height, txid, tx, payment.m_amount, payment.m_subaddr_index);
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);
2577 if (pool && all_same)
2583 std::shared_ptr<tools::Notify> tx_notify = m_tx_notify;
2591 if (m_unconfirmed_txs.empty())
2594 auto unconf_it = m_unconfirmed_txs.find(txid);
2595 if(unconf_it != m_unconfirmed_txs.end()) {
2596 if (store_tx_info()) {
2598 m_confirmed_txs.insert(std::make_pair(txid, confirmed_transfer_details(unconf_it->second,
height)));
2602 LOG_PRINT_L0(
"Failed to add outgoing transaction to confirmed transaction map");
2605 m_unconfirmed_txs.erase(unconf_it);
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()));
2618 entry.first->second.m_amount_in = spent;
2621 entry.first->second.m_change = received;
2623 std::vector<tx_extra_field> tx_extra_fields;
2631 entry.first->second.m_subaddr_account = subaddr_account;
2632 entry.first->second.m_subaddr_indices = subaddr_indices;
2635 entry.first->second.m_rings.clear();
2636 for (
const auto &in: tx.
vin)
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));
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;
2653 bool is_portal_address;
2661 entry.first->second.m_is_sc_migration = is_portal_address;
2667 std::vector<account_public_address> input_addresses;
2668 for (
auto minor_index : subaddr_indices) {
2670 input_addresses.push_back(get_subaddress(index));
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);
2686 if (change_indexes.size() == tx.
vout.size()) {
2687 change_indexes.clear();
2691 for (
auto &change_index : change_indexes)
2692 total_change += tx.
vout[change_index].amount;
2693 entry.first->second.m_change = total_change;
2697 if (entry.first->second.m_dests.empty()) {
2700 for (
size_t i = 0; i < tx.
vout.size(); ++i) {
2701 if (change_indexes.find(i) == change_indexes.end()) {
2702 auto output = boost::get<txout_to_key_public>(tx.
vout[i].target);
2705 return destination.addr == output.address;
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;
2718 output.m_address_prefix ==
2732 return !(b.
timestamp + 60*60*24 > m_account.get_createtime() &&
height >= m_refresh_from_block_height);
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)
2739 error::wallet_internal_error,
2741 " not match with daemon response size=" +
2748 if (!should_skip_block(b,
height))
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;
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)
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);
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");
2770 LOG_PRINT_L2(
"Skipped block by timestamp, height: " <<
height <<
", block time " << b.
timestamp <<
", account time " << m_account.get_createtime());
2772 m_blockchain.push_back(bl_id);
2774 if (0 != m_callback)
2775 m_callback->on_new_block(
height, b);
2778 void wallet2::get_short_chain_history(std::list<crypto::hash>& ids,
uint64_t granularity)
const 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();
2786 ids.push_back(m_blockchain.genesis());
2789 size_t current_back_offset = 1;
2790 bool base_included =
false;
2791 while(current_back_offset < sz)
2793 ids.push_back(m_blockchain[m_blockchain.offset() + sz-current_back_offset]);
2794 if(sz-current_back_offset == 0)
2795 base_included =
true;
2798 ++current_back_offset;
2801 current_back_offset += current_multiplier *= 2;
2806 ids.push_back(m_blockchain[m_blockchain.offset()]);
2807 if(m_blockchain.offset())
2808 ids.push_back(m_blockchain.genesis());
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)
2820 req.block_ids = short_chain_history;
2823 req.start_height = start_height;
2824 req.no_miner_tx = m_refresh_type == RefreshNoCoinbase;
2825 m_daemon_rpc_mutex.lock();
2829 m_daemon_rpc_mutex.unlock();
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");
2837 blocks_start_height =
res.start_height;
2849 MERROR(
"Failed to parse and validate tx from blob");
2853 std::stringstream ss;
2855 bool r = tx.serialize_base(ba);
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)
2864 req.block_ids = short_chain_history;
2866 req.start_height = start_height;
2867 m_daemon_rpc_mutex.lock();
2871 m_daemon_rpc_mutex.unlock();
2876 blocks_start_height =
res.start_height;
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)
2883 size_t current_index = start_height;
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);
2899 for (
size_t i = 0; i <
blocks.size(); ++i)
2902 error::wallet_internal_error,
"Mismatched parsed_blocks[i].txes.size() and parsed_blocks[i].block.tx_hashes.size()");
2905 if (should_skip_block(parsed_blocks[i].
block, start_height + i)){
2906 txidx += 1 + parsed_blocks[i].block.tx_hashes.size();
2909 if (m_refresh_type != RefreshNoCoinbase)
2910 tpool.submit(&waiter, [&, i, txidx]() {
2912 tx_cache_data[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]);
2924 waiter.
wait(&tpool);
2931 auto gender = [&](wallet2::is_out_data &iod) {
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");
2941 for (
size_t i = 0; i < tx_cache_data.size(); ++i) {
2942 if (tx_cache_data[i].
empty())
2944 if(!tx_cache_data[i].public_only()){
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)
2950 for (
auto &iod: slot.additional)
2956 waiter.
wait(&tpool);
2959 for (
size_t k = 0; k < n_vouts; ++k)
2961 const auto &o = tx.
vout[k];
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)
2974 error::wallet_internal_error,
"Unexpected received array size");
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();
2980 const auto etn_address = boost::get<txout_to_key_public>(o.target).
address;
2982 error::wallet_internal_error,
"Unexpected received array size");
2984 error::wallet_internal_error,
"Unexpected received vector size");
2990 tx_cache_data[txidx].public_outs[0].received[k] =
2991 (receive_info == boost::none) ?
2993 get_subaddress(receive_info->index).m_view_public_key == etn_address.m_view_public_key ?
2994 receive_info : boost::none;
3002 for (
size_t i = 0; i <
blocks.size(); ++i)
3004 if (should_skip_block(parsed_blocks[i].
block, start_height + i))
3006 txidx += 1 + parsed_blocks[i].block.tx_hashes.size();
3011 if (m_refresh_type != RefreshType::RefreshNoCoinbase)
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);
3018 for (
size_t j = 0; j < parsed_blocks[i].txes.size(); ++j)
3021 tpool.submit(&waiter, [&, i, j, txidx](){ geniod(parsed_blocks[i].txes[j], parsed_blocks[i].txes[j].vout.size(), txidx); },
true);
3025 THROW_WALLET_EXCEPTION_IF(txidx != tx_cache_data.size(), error::wallet_internal_error,
"txidx did not reach expected value");
3026 waiter.
wait(&tpool);
3029 size_t tx_cache_data_offset = 0;
3030 for (
size_t i = 0; i <
blocks.size(); ++i)
3035 if(current_index >= m_blockchain.size())
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);
3040 else if(bl_id != m_blockchain[current_index])
3045 " (height " +
std::to_string(start_height) +
"), local block id at this height: " +
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);
3056 tx_cache_data_offset += 1 + parsed_blocks[i].txes.size();
3060 void wallet2::refresh(
bool trusted_daemon)
3063 refresh(trusted_daemon, 0, blocks_fetched);
3068 bool received_etn =
false;
3069 refresh(trusted_daemon, start_height, blocks_fetched, received_etn);
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)
3078 drop_from_short_history(short_chain_history, 3);
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)
3086 short_chain_history.push_front(s->hash);
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);
3096 parsed_blocks.resize(
blocks.size());
3097 for (
size_t i = 0; i <
blocks.size(); ++i)
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);
3102 waiter.
wait(&tpool);
3103 for (
size_t i = 0; i <
blocks.size(); ++i)
3105 if (parsed_blocks[i].
error)
3110 parsed_blocks[i].o_indices =
std::move(o_indices[i]);
3113 boost::mutex error_lock;
3114 for (
size_t i = 0; i <
blocks.size(); ++i)
3116 parsed_blocks[i].txes.resize(
blocks[i].txs.size());
3117 for (
size_t j = 0; j <
blocks[i].txs.size(); ++j)
3119 tpool.submit(&waiter, [&, i, j](){
3122 boost::unique_lock<boost::mutex> lock(error_lock);
3128 waiter.
wait(&tpool);
3136 void wallet2::remove_obsolete_pool_txs(
const std::vector<crypto::hash> &tx_hashes)
3139 std::unordered_multimap<crypto::hash, wallet2::pool_payment_details>::iterator uit = m_unconfirmed_payments.begin();
3140 while (uit != m_unconfirmed_payments.end())
3144 for (
const auto &it2: tx_hashes)
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);
3164 void wallet2::update_pool_state(
bool refreshed)
3166 MTRACE(
"update_pool_state start");
3169 if (m_encrypt_keys_after_refresh)
3171 encrypt_keys(*m_encrypt_keys_after_refresh);
3172 m_encrypt_keys_after_refresh = boost::none;
3179 m_daemon_rpc_mutex.lock();
3181 m_daemon_rpc_mutex.unlock();
3185 MTRACE(
"update_pool_state got pool");
3188 std::unordered_map<crypto::hash, wallet2::unconfirmed_transfer_details>::iterator it = m_unconfirmed_txs.begin();
3189 while (it != m_unconfirmed_txs.end())
3193 for (
const auto &it2:
res.tx_hashes)
3210 if (pit->second.m_state == wallet2::unconfirmed_transfer_details::pending)
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;
3215 else if (pit->second.m_state == wallet2::unconfirmed_transfer_details::pending_not_in_pool && refreshed)
3217 LOG_PRINT_L1(
"Pending txid " << txid <<
" not in pool, marking as failed");
3218 pit->second.m_state = wallet2::unconfirmed_transfer_details::failed;
3221 remove_rings(pit->second.m_tx);
3222 for (
size_t vini = 0; vini < pit->second.m_tx.vin.size(); ++vini)
3224 if (pit->second.m_tx.vin[vini].type() ==
typeid(
txin_to_key))
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)
3242 MTRACE(
"update_pool_state done first loop");
3249 remove_obsolete_pool_txs(
res.tx_hashes);
3251 MTRACE(
"update_pool_state done second loop");
3254 std::vector<std::pair<crypto::hash, bool>> txids;
3255 for (
const auto &txid:
res.tx_hashes)
3257 bool txid_found_in_up =
false;
3258 for (
const auto &up: m_unconfirmed_payments)
3260 if (up.second.m_pd.m_tx_hash == txid)
3262 txid_found_in_up =
true;
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())
3269 if (!txid_found_in_up)
3271 LOG_PRINT_L2(
"Already seen " << txid <<
", and not for us, skipped");
3275 if (!txid_found_in_up)
3279 for (
const auto &i: m_unconfirmed_txs)
3281 if (i.first == txid)
3287 for (
const auto& dst : utd.
m_dests)
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)
3302 txids.push_back({txid,
false});
3312 txids.push_back({txid,
true});
3321 for (
const auto &p: txids)
3323 MDEBUG(
"asking for " << txids.size() <<
" transactions");
3324 req.decode_as_json =
false;
3326 m_daemon_rpc_mutex.lock();
3328 m_daemon_rpc_mutex.unlock();
3329 MDEBUG(
"Got " << r <<
" and " <<
res.status);
3332 if (
res.txs.size() == txids.size())
3334 for (
const auto &tx_entry:
res.txs)
3336 if (tx_entry.in_pool)
3342 if (get_pruned_tx(tx_entry, tx, tx_hash))
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())
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)
3352 std::swap(m_scanned_pool_txs[0], m_scanned_pool_txs[1]);
3353 m_scanned_pool_txs[0].clear();
3358 MERROR(
"Got txid " << tx_hash <<
" which we did not ask for");
3363 LOG_PRINT_L0(
"Failed to parse transaction from daemon");
3368 LOG_PRINT_L1(
"Transaction from daemon was in pool, but is no more");
3374 LOG_PRINT_L0(
"Expected " << txids.size() <<
" tx(es), got " <<
res.txs.size());
3379 LOG_PRINT_L0(
"Error calling gettransactions daemon RPC: r " << r <<
", status " << get_rpc_status(
res.status));
3382 MTRACE(
"update_pool_state end");
3386 void wallet2::fast_refresh(
uint64_t stop_height,
uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history,
bool force)
3388 std::vector<crypto::hash>
hashes;
3390 const uint64_t checkpoint_height = m_checkpoints.get_max_height();
3391 if ((stop_height > checkpoint_height && m_blockchain.size()-1 < checkpoint_height) && !force)
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);
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);
3403 size_t current_index = m_blockchain.size();
3404 while(m_run.load(std::memory_order_relaxed) && current_index < stop_height)
3406 pull_hashes(0, blocks_start_height, short_chain_history,
hashes);
3409 if (blocks_start_height < m_blockchain.offset())
3411 MERROR(
"Blocks start before blockchain offset: " << blocks_start_height <<
" " << m_blockchain.offset());
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();
3418 for (
int i = 0; i<3; i++) {
3420 short_chain_history.push_front(*right);
3423 current_index = blocks_start_height;
3426 if(current_index >= m_blockchain.size())
3428 if (!(current_index % 1024))
3429 LOG_PRINT_L2(
"Skipped block by height: " << current_index);
3430 m_blockchain.push_back(bl_id);
3432 if (0 != m_callback)
3435 m_callback->on_new_block(current_index, dummy);
3438 else if(bl_id != m_blockchain[current_index])
3444 if (current_index >= stop_height)
3455 a.m_payment_id = payment_id;
3456 a.m_description = description;
3457 a.m_is_subaddress = is_subaddress;
3459 auto old_size = m_address_book.size();
3460 m_address_book.push_back(
a);
3461 if(m_address_book.size() == old_size+1)
3466 bool wallet2::delete_address_book_row(std::size_t row_id) {
3467 if(m_address_book.size() <= row_id)
3470 m_address_book.erase(m_address_book.begin()+row_id);
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 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)
3482 const transfer_details &td = m_transfers[i];
3484 (*cache).first[std::make_pair(td.is_rct() ? 0 : td.amount(), td.m_global_output_index)] = i;
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;
3493 void wallet2::refresh(
bool trusted_daemon,
uint64_t start_height,
uint64_t & blocks_fetched,
bool& received_etn,
bool check_pool) {
3500 if (m_light_wallet) {
3507 if (light_wallet_get_address_info(
res)) {
3509 uint64_t prev_height = m_light_wallet_blockchain_height;
3511 m_light_wallet_scanned_block_height =
res.scanned_block_height;
3512 m_light_wallet_blockchain_height =
res.blockchain_height;
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);
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");
3524 light_wallet_get_address_txs();
3526 m_light_wallet_connected =
false;
3531 received_etn =
false;
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;
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;
3547 get_short_chain_history(short_chain_history,
3549 m_run.store(
true, std::memory_order_relaxed);
3550 if (start_height > m_blockchain.size() || m_refresh_from_block_height > m_blockchain.size()) {
3552 start_height = m_refresh_from_block_height;
3554 fast_refresh(start_height, blocks_start_height, short_chain_history);
3556 short_chain_history.clear();
3557 get_short_chain_history(short_chain_history,
3564 if (!m_run.load(std::memory_order_relaxed))
3571 if (m_encrypt_keys_after_refresh) {
3572 encrypt_keys(*m_encrypt_keys_after_refresh);
3573 m_encrypt_keys_after_refresh = boost::none;
3580 while (m_run.load(std::memory_order_relaxed)) {
3582 std::vector<cryptonote::block_complete_entry> next_blocks;
3583 std::vector<parsed_block> next_parsed_blocks;
3588 next_blocks.clear();
3589 next_parsed_blocks.clear();
3591 if (!first &&
blocks.empty()) {
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);
3602 process_parsed_blocks(blocks_start_height,
blocks, parsed_blocks, added_blocks,
3603 output_tracker_cache.get());
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();
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)
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(
"");
3630 catch (
const std::exception &e) {
3631 MERROR(
"Error parsing blocks: " << e.what());
3634 blocks_fetched += added_blocks;
3636 waiter.
wait(&tpool);
3637 if (!first && blocks_start_height == next_blocks_start_height) {
3638 m_node_rpc_proxy.set_height(m_blockchain.size());
3647 throw std::runtime_error(
"proxy exception in refresh thread");
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();
3659 blocks_start_height = next_blocks_start_height;
3661 parsed_blocks =
std::move(next_parsed_blocks);
3664 blocks_fetched += added_blocks;
3665 waiter.
wait(&tpool);
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 <<
")...");
3676 parsed_blocks.clear();
3677 short_chain_history.clear();
3678 get_short_chain_history(short_chain_history, 1);
3681 LOG_ERROR(
"pull_blocks failed, try_count=" << try_count);
3686 if (last_tx_hash_id != (m_transfers.size() ? m_transfers.back().m_txid : null_hash))
3687 received_etn =
true;
3691 if (check_pool && m_run.load(std::memory_order_relaxed))
3692 update_pool_state(refreshed);
3698 m_first_refresh_done =
true;
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)));
3734 uint64_t migration_minheight = this->nettype() ==
TESTNET ? 1086402 + 5 : 1175315 + 5;
3735 if (this->get_blockchain_current_height() > migration_minheight && this->unlocked_balance_all(
false) != 0) {
3737 "You are now on the transparent version of Electroneum and so we're giving you the chance to migrate your funds via a sweep transaction back to your address.\n Don't worry, this migration is completely free of charge. Please follow the prompts to continue.");
3738 std::map<uint32_t, std::map<uint32_t, std::pair<uint64_t, uint64_t>>> unlocked_balance_per_subaddress_per_account;
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);
3744 for (
uint32_t i = 0; i < this->get_num_subaddress_accounts(); i++) {
3747 for (
auto subaddress: unlocked_balance_per_subaddress_per_account[i]) {
3748 index.
minor = subaddress.first;
3750 if (subaddress.second.first != 0 &&
3751 subaddress.second.second == 0) {
3753 std::set<uint32_t> subaddress_source{index.
minor};
3754 std::vector<wallet2::pending_tx> ptx_vector = this->create_transactions_all(0,
3768 this->commit_tx(ptx_vector);
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.");
3785 portal_address_viewkey_hex_str =
"2b95a2eb2c62253c57e82b082b850bbf22a1a7829aaea09c7c1511c1cced4375";
3786 portal_address_spendkey_hex_str =
"8ce0f34fd37c7f7d07c44024eb5b3cdf275d1b3e75c3464b808dce532e861137";
3788 portal_address_viewkey_hex_str =
"5866666666666666666666666666666666666666666666666666666666666666";
3789 portal_address_spendkey_hex_str =
"5bd0c0e25eee6133850edd2b255ed9e3d6bb99fd5f08b7b5cf7f2618ad6ff2a3";
3792 bool portal_wallet =
3794 portal_address_spendkey_hex_str &&
3801 uint64_t smartchain_migration_minheight = this->nettype() ==
MAINNET ? 1811310 : 1455270;
3802 if (this->get_blockchain_current_height() > smartchain_migration_minheight) {
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;
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);
3815 for (
uint32_t i = 0; i < this->get_num_subaddress_accounts(); i++) {
3818 for (
auto subaddress: unlocked_balance_per_subaddress_per_account[i]) {
3819 index.
minor = subaddress.first;
3821 if (subaddress.second.first != 0 &&
3822 subaddress.second.second ==
3824 std::set<uint32_t> subaddress_source{index.
minor};
3825 std::vector<wallet2::pending_tx> ptx_vector = this->create_transactions_all(0,
3836 this->commit_tx(ptx_vector);
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;
3851 bool wallet2::refresh(
bool trusted_daemon,
uint64_t & blocks_fetched,
bool& received_etn,
bool& ok)
3855 refresh(trusted_daemon, 0, blocks_fetched, received_etn);
3865 bool wallet2::get_rct_distribution(
uint64_t &start_height, std::vector<uint64_t> &distribution)
3868 boost::optional<std::string> result = m_node_rpc_proxy.get_rpc_version(rpc_version);
3877 MDEBUG(
"Cannot determine daemon RPC version, not requesting rct distribution");
3885 MDEBUG(
"Daemon is recent enough, requesting rct distribution");
3889 MDEBUG(
"Daemon is too old, not requesting rct distribution");
3896 req.amounts.push_back(0);
3897 req.from_height = 0;
3898 req.cumulative =
false;
3900 req.compress =
true;
3901 m_daemon_rpc_mutex.lock();
3903 m_daemon_rpc_mutex.unlock();
3906 MWARNING(
"Failed to request output distribution: no connection to daemon");
3911 MWARNING(
"Failed to request output distribution: daemon is busy");
3916 MWARNING(
"Failed to request output distribution: " <<
res.status);
3919 if (
res.distributions.size() != 1)
3921 MWARNING(
"Failed to request output distribution: not the expected single result");
3924 if (
res.distributions[0].amount != 0)
3926 MWARNING(
"Failed to request output distribution: results are not for amount 0");
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);
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)
3944 error::wallet_internal_error,
"Daemon claims reorg below last checkpoint");
3946 size_t transfers_detached = 0;
3948 for (
size_t i = 0; i < m_transfers.size(); ++i)
3950 wallet2::transfer_details &td = m_transfers[i];
3951 if (td.m_spent && td.m_spent_height >=
height)
3953 if(td.m_tx.version == 1){
3954 LOG_PRINT_L1(
"Resetting spent/frozen status for output " << i <<
": " << td.m_key_image);
3956 LOG_PRINT_L1(
"Resetting spent/frozen status for output " 3957 << i <<
": " <<
"chainstate index " << td.m_txid <<
": " << td.m_internal_output_index);
3964 for (transfer_details &td: m_transfers)
3966 while (!td.m_uses.empty() && td.m_uses.back().first >=
height)
3967 td.m_uses.pop_back();
3970 if (output_tracker_cache) {
3971 output_tracker_cache->first.clear();
3972 output_tracker_cache->second.clear();
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();
3978 for(
size_t i = i_start; i!= m_transfers.size();i++)
3980 if (!m_transfers[i].m_key_image_known || m_transfers[i].m_key_image_partial)
3982 auto it_ki = m_key_images.find(m_transfers[i].m_key_image);
3984 m_key_images.erase(it_ki);
3987 for(
size_t i = i_start; i!= m_transfers.size();i++)
3989 auto it_pk = m_pub_keys.find(m_transfers[i].get_public_key());
3991 m_pub_keys.erase(it_pk);
3994 for(
size_t i = i_start; i!= m_transfers.size();i++)
3996 auto it_pk = m_chainstate_indexes.find(m_transfers[i].get_chainstate_index());
3997 if(m_transfers[i].m_tx.version > 1) {
3999 "chainstate index not found");
4000 m_chainstate_indexes.erase(it_pk);
4007 m_transfers.erase(it, m_transfers.end());
4009 size_t blocks_detached = m_blockchain.size() -
height;
4010 m_blockchain.crop(
height);
4012 for (
auto it = m_payments.begin(); it != m_payments.end(); )
4014 if(height <= it->second.m_block_height)
4015 it = m_payments.erase(it);
4020 for (
auto it = m_confirmed_txs.begin(); it != m_confirmed_txs.end(); )
4022 if(height <= it->second.m_block_height)
4023 it = m_confirmed_txs.erase(it);
4028 LOG_PRINT_L0(
"Detached blockchain on height " <<
height <<
", transfers detached " << transfers_detached <<
", blocks detached " << blocks_detached);
4033 m_is_initialized=
false;
4039 bool wallet2::clear()
4041 m_blockchain.clear();
4042 m_transfers.clear();
4043 m_key_images.clear();
4045 m_chainstate_indexes.clear();
4046 m_unconfirmed_txs.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;
4062 void wallet2::clear_soft(
bool keep_key_images)
4064 m_blockchain.clear();
4065 m_transfers.clear();
4066 if (!keep_key_images)
4067 m_key_images.clear();
4069 m_chainstate_indexes.clear();
4070 m_unconfirmed_txs.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();
4078 generate_genesis(b);
4097 crypto::chacha_key
key;
4098 crypto::generate_chacha_key(password.
data(), password.
size(),
key, m_kdf_rounds);
4100 if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
4113 wallet2::keys_file_data keys_file_data = boost::value_initialized<wallet2::keys_file_data>();
4119 value.SetString(account_data.c_str(), account_data.length());
4121 if (!seed_language.empty())
4123 value.SetString(seed_language.c_str(), seed_language.length());
4124 json.AddMember(
"seed_language",
value,
json.GetAllocator());
4129 value2.SetInt(m_key_device_type);
4130 json.AddMember(
"key_on_device", value2,
json.GetAllocator());
4132 value2.SetInt(watch_only ? 1 :0);
4133 json.AddMember(
"watch_only", value2,
json.GetAllocator());
4135 value2.SetInt(m_multisig ? 1 :0);
4136 json.AddMember(
"multisig", value2,
json.GetAllocator());
4138 value2.SetUint(m_multisig_threshold);
4139 json.AddMember(
"multisig_threshold", value2,
json.GetAllocator());
4145 value.SetString(multisig_signers.c_str(), multisig_signers.length());
4146 json.AddMember(
"multisig_signers",
value,
json.GetAllocator());
4150 value.SetString(multisig_derivations.c_str(), multisig_derivations.length());
4151 json.AddMember(
"multisig_derivations",
value,
json.GetAllocator());
4153 value2.SetUint(m_multisig_rounds_passed);
4154 json.AddMember(
"multisig_rounds_passed", value2,
json.GetAllocator());
4157 value2.SetInt(m_always_confirm_transfers ? 1 :0);
4158 json.AddMember(
"always_confirm_transfers", value2,
json.GetAllocator());
4160 value2.SetInt(m_print_ring_members ? 1 :0);
4161 json.AddMember(
"print_ring_members", value2,
json.GetAllocator());
4163 value2.SetInt(m_store_tx_info ? 1 :0);
4164 json.AddMember(
"store_tx_info", value2,
json.GetAllocator());
4166 value2.SetUint(m_default_mixin);
4167 json.AddMember(
"default_mixin", value2,
json.GetAllocator());
4169 value2.SetUint(m_default_priority);
4170 json.AddMember(
"default_priority", value2,
json.GetAllocator());
4172 value2.SetInt(m_auto_refresh ? 1 :0);
4173 json.AddMember(
"auto_refresh", value2,
json.GetAllocator());
4175 value2.SetInt(m_refresh_type);
4176 json.AddMember(
"refresh_type", value2,
json.GetAllocator());
4178 value2.SetUint64(m_refresh_from_block_height);
4179 json.AddMember(
"refresh_height", value2,
json.GetAllocator());
4181 value2.SetInt(m_confirm_missing_payment_id ? 1 :0);
4182 json.AddMember(
"confirm_missing_payment_id", value2,
json.GetAllocator());
4184 value2.SetInt(m_confirm_non_default_ring_size ? 1 :0);
4185 json.AddMember(
"confirm_non_default_ring_size", value2,
json.GetAllocator());
4187 value2.SetInt(m_ask_password);
4188 json.AddMember(
"ask_password", value2,
json.GetAllocator());
4190 value2.SetUint(m_min_output_count);
4191 json.AddMember(
"min_output_count", value2,
json.GetAllocator());
4193 value2.SetUint64(m_min_output_value);
4194 json.AddMember(
"min_output_value", value2,
json.GetAllocator());
4197 json.AddMember(
"default_decimal_point", value2,
json.GetAllocator());
4199 value2.SetInt(m_merge_destinations ? 1 :0);
4200 json.AddMember(
"merge_destinations", value2,
json.GetAllocator());
4202 value2.SetInt(m_confirm_backlog ? 1 :0);
4203 json.AddMember(
"confirm_backlog", value2,
json.GetAllocator());
4205 value2.SetUint(m_confirm_backlog_threshold);
4206 json.AddMember(
"confirm_backlog_threshold", value2,
json.GetAllocator());
4208 value2.SetInt(m_confirm_export_overwrite ? 1 :0);
4209 json.AddMember(
"confirm_export_overwrite", value2,
json.GetAllocator());
4211 value2.SetInt(m_auto_low_priority ? 1 : 0);
4212 json.AddMember(
"auto_low_priority", value2,
json.GetAllocator());
4214 value2.SetUint(m_nettype);
4215 json.AddMember(
"nettype", value2,
json.GetAllocator());
4217 value2.SetInt(m_segregate_pre_fork_outputs ? 1 : 0);
4218 json.AddMember(
"segregate_pre_fork_outputs", value2,
json.GetAllocator());
4220 value2.SetInt(m_key_reuse_mitigation2 ? 1 : 0);
4221 json.AddMember(
"key_reuse_mitigation2", value2,
json.GetAllocator());
4223 value2.SetUint(m_segregation_height);
4224 json.AddMember(
"segregation_height", value2,
json.GetAllocator());
4226 value2.SetInt(m_ignore_fractional_outputs ? 1 : 0);
4227 json.AddMember(
"ignore_fractional_outputs", value2,
json.GetAllocator());
4229 value2.SetInt(m_track_uses ? 1 : 0);
4230 json.AddMember(
"track_uses", value2,
json.GetAllocator());
4232 value2.SetInt(m_setup_background_mining);
4233 json.AddMember(
"setup_background_mining", value2,
json.GetAllocator());
4235 value2.SetUint(m_subaddress_lookahead_major);
4236 json.AddMember(
"subaddress_lookahead_major", value2,
json.GetAllocator());
4238 value2.SetUint(m_subaddress_lookahead_minor);
4239 json.AddMember(
"subaddress_lookahead_minor", value2,
json.GetAllocator());
4241 value2.SetInt(m_original_keys_available ? 1 : 0);
4242 json.AddMember(
"original_keys_available", value2,
json.GetAllocator());
4245 json.AddMember(
"encrypted_secret_keys", value2,
json.GetAllocator());
4247 value.SetString(m_device_name.c_str(), m_device_name.size());
4250 value.SetString(m_device_derivation_path.c_str(), m_device_derivation_path.size());
4251 json.AddMember(
"device_derivation_path",
value,
json.GetAllocator());
4253 value2.SetUint(m_account_major_offset);
4254 json.AddMember(
"account_major_offset", value2,
json.GetAllocator());
4258 if (m_original_keys_available)
4261 value.SetString(original_address.c_str(), original_address.length());
4262 json.AddMember(
"original_address",
value,
json.GetAllocator());
4264 value.SetString(original_view_secret_key.c_str(), original_view_secret_key.length());
4265 json.AddMember(
"original_view_secret_key",
value,
json.GetAllocator());
4270 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
4271 json.Accept(writer);
4272 account_data = buffer.GetString();
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;
4281 std::string tmp_file_name = keys_file_name +
".new";
4292 boost::filesystem::remove(tmp_file_name);
4293 LOG_ERROR(
"failed to update wallet keys file " << keys_file_name);
4302 crypto::chacha_key
key;
4303 crypto::generate_chacha_key(password.
data(), password.
size(),
key, m_kdf_rounds);
4306 if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
4308 m_account.encrypt_keys(
key);
4309 m_account.decrypt_viewkey(
key);
4312 static_assert(
HASH_SIZE ==
sizeof(crypto::chacha_key),
"Mismatched sizes of hash and chacha key");
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())
4340 bool encrypted_secret_keys =
false;
4347 crypto::chacha_key
key;
4348 crypto::generate_chacha_key(password.
data(), password.
size(),
key, m_kdf_rounds);
4350 account_data.resize(keys_file_data.
account_data.size());
4352 if (
json.Parse(account_data.c_str()).HasParseError() || !
json.IsObject())
4356 if (
json.Parse(account_data.c_str()).HasParseError())
4358 is_old_file_format =
true;
4359 m_watch_only =
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;
4392 m_original_keys_available =
false;
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;
4399 else if(
json.IsObject())
4401 if (!
json.HasMember(
"key_data"))
4403 LOG_ERROR(
"Field key_data not found in JSON");
4406 if (!
json[
"key_data"].IsString())
4408 LOG_ERROR(
"Field key_data found in JSON, but not String");
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());
4414 if (
json.HasMember(
"key_on_device"))
4421 if (field_seed_language_found)
4423 set_seed_language(field_seed_language);
4426 m_watch_only = field_watch_only;
4428 m_multisig = field_multisig;
4430 m_multisig_threshold = field_multisig_threshold;
4432 m_multisig_rounds_passed = field_multisig_rounds_passed;
4435 if (!
json.HasMember(
"multisig_signers"))
4437 LOG_ERROR(
"Field multisig_signers not found in JSON");
4440 if (!
json[
"multisig_signers"].IsString())
4442 LOG_ERROR(
"Field multisig_signers found in JSON, but not String");
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());
4450 LOG_ERROR(
"Field multisig_signers found in JSON, but failed to parse");
4455 if (
json.HasMember(
"multisig_derivations"))
4457 if (!
json[
"multisig_derivations"].IsString())
4459 LOG_ERROR(
"Field multisig_derivations found in JSON, but not String");
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());
4467 LOG_ERROR(
"Field multisig_derivations found in JSON, but failed to parse");
4473 m_always_confirm_transfers = field_always_confirm_transfers;
4475 m_print_ring_members = field_print_ring_members;
4478 m_store_tx_info = ((field_store_tx_keys != 0) || (field_store_tx_info != 0));
4480 m_default_mixin = field_default_mixin;
4482 if (field_default_priority_found)
4484 m_default_priority = field_default_priority;
4489 if (field_default_fee_multiplier_found)
4490 m_default_priority = field_default_fee_multiplier;
4492 m_default_priority = 0;
4495 m_auto_refresh = field_auto_refresh;
4497 m_refresh_type = RefreshType::RefreshDefault;
4498 if (field_refresh_type_found)
4500 if (field_refresh_type == RefreshFull || field_refresh_type == RefreshOptimizeCoinbase || field_refresh_type == RefreshNoCoinbase)
4501 m_refresh_type = (RefreshType)field_refresh_type;
4503 LOG_PRINT_L0(
"Unknown refresh-type value (" << field_refresh_type <<
"), using default");
4506 m_refresh_from_block_height = field_refresh_height;
4508 m_confirm_missing_payment_id = field_confirm_missing_payment_id;
4510 m_confirm_non_default_ring_size = field_confirm_non_default_ring_size;
4512 m_ask_password = field_ask_password;
4516 m_min_output_count = field_min_output_count;
4518 m_min_output_value = field_min_output_value;
4520 m_merge_destinations = field_merge_destinations;
4522 m_confirm_backlog = field_confirm_backlog;
4524 m_confirm_backlog_threshold = field_confirm_backlog_threshold;
4526 m_confirm_export_overwrite = field_confirm_export_overwrite;
4528 m_auto_low_priority = field_auto_low_priority;
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());
4536 m_segregate_pre_fork_outputs = field_segregate_pre_fork_outputs;
4538 m_key_reuse_mitigation2 = field_key_reuse_mitigation2;
4540 m_segregation_height = field_segregation_height;
4542 m_ignore_fractional_outputs = field_ignore_fractional_outputs;
4544 m_track_uses = field_track_uses;
4546 m_setup_background_mining = field_setup_background_mining;
4548 m_subaddress_lookahead_major = field_subaddress_lookahead_major;
4550 m_subaddress_lookahead_minor = field_subaddress_lookahead_minor;
4553 encrypted_secret_keys = field_encrypted_secret_keys;
4556 m_account_major_offset = field_account_major_offset;
4559 if (m_device_name.empty())
4561 if (field_device_name_found)
4563 m_device_name = field_device_name;
4567 m_device_name = m_key_device_type == hw::device::device_type::LEDGER ?
"Ledger" :
"default";
4572 m_device_derivation_path = field_device_derivation_path;
4574 if (
json.HasMember(
"original_keys_available"))
4577 m_original_keys_available = field_original_keys_available;
4578 if (m_original_keys_available)
4585 LOG_ERROR(
"Failed to parse original_address from JSON");
4588 m_original_address =
info.address;
4593 LOG_ERROR(
"Failed to parse original_view_secret_key from JSON");
4600 m_original_keys_available =
false;
4611 if (m_key_device_type == hw::device::device_type::LEDGER || m_key_device_type == hw::device::device_type::TREZOR) {
4613 hw::device &hwdev = lookup_device(m_device_name);
4620 m_account.set_device(hwdev);
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. " 4626 ", wallet address: " + m_account.get_public_address_str(m_nettype));
4628 }
else if (key_on_device()) {
4634 if (encrypted_secret_keys)
4636 m_account.decrypt_keys(
key);
4641 if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
4643 bool saved_ret = store_keys(keys_file_name, password, m_watch_only);
4647 MERROR(
"Error saving keys file with encrypted keys, not fatal");
4649 if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
4651 m_keys_file_locker.reset();
4662 setup_keys(password);
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);
4704 bool encrypted_secret_keys =
false;
4711 crypto::chacha_key
key;
4712 crypto::generate_chacha_key(password.
data(), password.
size(),
key, kdf_rounds);
4716 if (
json.Parse(account_data.c_str()).HasParseError() || !
json.IsObject())
4720 if (
json.Parse(account_data.c_str()).HasParseError())
4727 json[
"key_data"].GetStringLength());
4729 encrypted_secret_keys = field_encrypted_secret_keys;
4736 if (encrypted_secret_keys)
4746 void wallet2::encrypt_keys(
const crypto::chacha_key &
key)
4748 m_account.encrypt_keys(
key);
4749 m_account.decrypt_viewkey(
key);
4752 void wallet2::decrypt_keys(
const crypto::chacha_key &
key)
4754 m_account.encrypt_viewkey(
key);
4755 m_account.decrypt_keys(
key);
4760 crypto::chacha_key
key;
4761 crypto::generate_chacha_key(password.
data(), password.
size(),
key, m_kdf_rounds);
4767 crypto::chacha_key
key;
4768 crypto::generate_chacha_key(password.
data(), password.
size(),
key, m_kdf_rounds);
4772 void wallet2::setup_new_blockchain()
4775 generate_genesis(b);
4778 add_subaddress_account(
tr(
"Primary account"));
4783 if (!wallet_.empty())
4785 bool r = store_keys(m_keys_file, password, watch_only);
4788 if (create_address_file)
4791 if(!r)
MERROR(
"String with address text not saved");
4818 crypto::chacha_key
key;
4819 crypto::generate_chacha_key(password.
data(), password.
size(),
key, kdf_rounds);
4823 if (
json.Parse(account_data.c_str()).HasParseError() || !
json.IsObject())
4826 device_type = hw::device::device_type::SOFTWARE;
4828 if (
json.Parse(account_data.c_str()).HasParseError())
4835 json[
"key_data"].GetStringLength());
4837 if (
json.HasMember(
"key_on_device"))
4847 if (!r)
return false;
4853 m_account_public_address = m_account.get_keys().m_account_address;
4854 m_watch_only =
false;
4856 m_multisig_threshold = 0;
4857 m_multisig_signers.clear();
4858 m_original_keys_available =
false;
4859 m_key_device_type = device_type;
4873 prepare_file_names(wallet_);
4875 if (!wallet_.empty())
4877 boost::system::error_code ignored_ec;
4882 m_account.generate(rct::rct2sk(
rct::zero()),
true,
false);
4895 std::vector<crypto::secret_key> multisig_keys;
4896 std::vector<crypto::public_key> multisig_signers;
4905 for (
size_t n = 0; n < n_multisig_keys; ++n)
4910 for (
size_t n = 0; n < total; ++n)
4923 for (
const auto &msk: multisig_keys)
4924 sc_add(skey.bytes, skey.bytes, rct::sk2rct(msk).
bytes);
4928 m_account.make_multisig(view_secret_key, spend_secret_key, spend_public_key, multisig_keys);
4929 m_account.finalize_multisig(spend_public_key);
4934 init_type(hw::device::device_type::SOFTWARE);
4937 m_multisig_signers = multisig_signers;
4938 setup_keys(password);
4940 create_keys_file(wallet_,
false, password, m_nettype !=
MAINNET || create_address_file);
4941 setup_new_blockchain();
4943 if (!wallet_.empty())
4958 const crypto::secret_key& recovery_param,
bool recover,
bool two_random,
bool create_address_file)
4961 prepare_file_names(wallet_);
4963 if (!wallet_.empty())
4965 boost::system::error_code ignored_ec;
4972 init_type(hw::device::device_type::SOFTWARE);
4973 setup_keys(password);
4976 if(m_refresh_from_block_height == 0 && !recover){
4977 m_refresh_from_block_height = estimate_blockchain_height();
4980 create_keys_file(wallet_,
false, password, m_nettype !=
MAINNET || create_address_file);
4982 setup_new_blockchain();
4984 if (!wallet_.empty())
4996 const uint64_t blocks_per_month = 60*60*24*30/seconds_per_block;
5014 height = get_approximate_blockchain_height();
5015 uint64_t target_height = get_daemon_blockchain_target_height(err);
5017 if (target_height <
height)
5021 if (
height > blocks_per_month)
5022 height -= blocks_per_month;
5026 uint64_t local_height = get_daemon_blockchain_height(err);
5027 if (err.empty() && local_height >
height)
5045 prepare_file_names(wallet_);
5047 if (!wallet_.empty())
5049 boost::system::error_code ignored_ec;
5055 init_type(hw::device::device_type::SOFTWARE);
5056 m_watch_only =
true;
5058 setup_keys(password);
5060 create_keys_file(wallet_,
true, password, m_nettype !=
MAINNET || create_address_file);
5062 setup_new_blockchain();
5064 if (!wallet_.empty())
5082 prepare_file_names(wallet_);
5084 if (!wallet_.empty())
5086 boost::system::error_code ignored_ec;
5092 init_type(hw::device::device_type::SOFTWARE);
5094 setup_keys(password);
5096 create_keys_file(wallet_,
false, password, create_address_file);
5098 setup_new_blockchain();
5100 if (!wallet_.empty())
5113 prepare_file_names(wallet_);
5115 boost::system::error_code ignored_ec;
5116 if (!wallet_.empty()) {
5121 auto &hwdev = lookup_device(device_name);
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;
5132 create_keys_file(wallet_,
false, password, m_nettype !=
MAINNET || create_address_file);
5136 m_subaddress_lookahead_major = 5;
5137 m_subaddress_lookahead_minor = 20;
5139 setup_new_blockchain();
5140 if (!wallet_.empty()) {
5146 const std::vector<crypto::secret_key> &view_keys,
5147 const std::vector<crypto::public_key> &spend_keys,
5155 std::vector<crypto::secret_key> multisig_keys;
5158 std::vector<crypto::public_key> multisig_signers;
5162 if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
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);
5189 MINFO(
"Creating 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));
5205 multisig_signers = std::vector<crypto::public_key>(spend_keys.size() + 1,
crypto::null_pkey);
5213 MINFO(
"Creating spend key...");
5226 extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, secret_keys_to_public_keys(multisig_keys), rct::rct2sk(spend_skey));
5231 MINFO(
"Preparing keys for next exchange round...");
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);
5238 m_multisig_derivations = derivations;
5242 if (!m_original_keys_available)
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;
5252 MINFO(
"Creating view key...");
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");
5260 init_type(hw::device::device_type::SOFTWARE);
5261 m_original_keys_available =
true;
5264 m_multisig_signers = multisig_signers;
5265 ++m_multisig_rounds_passed;
5270 create_keys_file(m_wallet_file,
false, password, boost::filesystem::exists(m_wallet_file +
".address.txt"));
5272 setup_new_blockchain();
5274 if (!m_wallet_file.empty())
5277 return extra_multisig_info;
5281 const std::vector<std::string> &
info)
5286 if (
info[0].substr(0, MULTISIG_EXTRA_INFO_MAGIC.size()) != MULTISIG_EXTRA_INFO_MAGIC)
5292 std::vector<crypto::public_key> signers;
5293 std::unordered_set<crypto::public_key> pkeys;
5298 return exchange_multisig_keys(password, pkeys, signers);
5302 std::unordered_set<crypto::public_key> derivations,
5303 std::vector<crypto::public_key> signers)
5314 if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
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);
5329 if (std::find(signers.begin(), signers.end(), local_signer) == signers.end())
5331 signers.push_back(local_signer);
5332 for (
const auto &msk: get_account().get_multisig_keys())
5342 m_account_public_address.m_spend_public_key = spend_public_key;
5343 m_account.finalize_multisig(spend_public_key);
5345 m_multisig_signers = signers;
5348 ++m_multisig_rounds_passed;
5349 m_multisig_derivations.clear();
5354 if (!m_wallet_file.empty())
5356 bool r = store_keys(m_keys_file, password,
false);
5359 if (boost::filesystem::exists(m_wallet_file +
".address.txt"))
5362 if(!r)
MERROR(
"String with address text not saved");
5366 m_subaddresses.clear();
5367 m_subaddress_labels.clear();
5368 add_subaddress_account(
tr(
"Primary account"));
5370 if (!m_wallet_file.empty())
5378 for (
const auto&
key: m_multisig_derivations)
5379 derivations.erase(
key);
5388 MINFO(
"Creating spend key...");
5395 m_account.make_multisig(m_account.get_keys().m_view_secret_key, spend_skey, rct::rct2pk(
rct::identity()), multisig_keys);
5398 extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, secret_keys_to_public_keys(multisig_keys), spend_skey);
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;
5408 ++m_multisig_rounds_passed;
5410 create_keys_file(m_wallet_file,
false, password, boost::filesystem::exists(m_wallet_file +
".address.txt"));
5411 return extra_multisig_info;
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 5419 public_keys.resize(
info.size());
5420 secret_keys.resize(
info.size());
5421 for (
size_t i = 0; i <
info.size(); ++i)
5428 for (
size_t i = 0; i < secret_keys.size(); ++i)
5430 for (
size_t j = i + 1; j < secret_keys.size(); ++j)
5432 if (rct::sk2rct(secret_keys[i]) == rct::sk2rct(secret_keys[j]))
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();
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)
5449 if (secret_keys[i] == local_skey)
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();
5461 "Found local spend public key, but not local view secret key - something very weird");
5467 const std::vector<std::string> &
info,
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);
5476 bool wallet2::finalize_multisig(
const epee::wipeable_string &password,
const std::unordered_set<crypto::public_key> &pkeys, std::vector<crypto::public_key> signers)
5480 if (!multisig(&ready, &
threshold, &total))
5482 MERROR(
"This is not a multisig wallet");
5487 MERROR(
"This multisig wallet is already finalized");
5492 MERROR(
"finalize_multisig should only be used for N-1/N wallets, use exchange_multisig_keys instead");
5495 exchange_multisig_keys(password, pkeys, signers);
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 5505 for (
size_t i = 0; i <
info.size(); ++i)
5507 if (!verify_extra_multisig_info(
info[i], pkeys, signers[i]))
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))
5522 MERROR(
"Bad multisig info");
5526 return finalize_multisig(password, public_keys, signers);
5533 const crypto::public_key pkey = get_multisig_signer_public_key(get_account().get_keys().m_spend_secret_key);
5550 const size_t header_len = strlen(
"MultisigV1");
5551 if (data.size() < header_len || data.substr(0, header_len) !=
"MultisigV1")
5553 MERROR(
"Multisig info header check error");
5559 MERROR(
"Multisig info decoding error");
5564 MERROR(
"Multisig info is corrupt");
5570 offset +=
sizeof(skey);
5572 offset +=
sizeof(pkey);
5579 MERROR(
"Multisig info signature is invalid");
5588 if (data.size() < MULTISIG_EXTRA_INFO_MAGIC.size() || data.substr(0, MULTISIG_EXTRA_INFO_MAGIC.size()) != MULTISIG_EXTRA_INFO_MAGIC)
5590 MERROR(
"Multisig info header check error");
5596 MERROR(
"Multisig info decoding error");
5601 MERROR(
"Multisig info is corrupt");
5606 MERROR(
"Multisig info is corrupt");
5613 offset +=
sizeof(signer);
5620 MERROR(
"Multisig info signature is invalid");
5624 for (
size_t n = 0; n < n_keys; ++n)
5628 offset +=
sizeof(mspk);
5641 *total = m_multisig_signers.size();
5643 *ready = !(get_account().get_keys().m_account_address.m_spend_public_key == rct::rct2pk(
rct::identity()));
5647 bool wallet2::has_multisig_partial_key_images()
const 5651 for (
const auto &td: m_transfers)
5652 if (td.m_key_image_partial && td.m_tx.version == 1)
5657 bool wallet2::has_unknown_key_images()
const 5659 for (
const auto &td: m_transfers)
5660 if (!td.m_key_image_known && td.m_tx.version == 1)
5672 if (wallet_name.empty())
5674 prepare_file_names(wallet_name);
5675 boost::system::error_code ignored_ec;
5677 bool r = store_keys(m_keys_file, password, m_watch_only);
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);
5693 bool r = store_keys(new_keys_filename, password,
true);
5697 void wallet2::wallet_exists(
const std::string& file_path,
bool& keys_file_exists,
bool& wallet_file_exists)
5700 do_prepare_file_names(file_path, keys_file, wallet_file, mms_file);
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);
5709 return !file_path.empty();
5721 payment_id = *
reinterpret_cast<const crypto::hash*
>(payment_id_data.data());
5734 payment_id = *
reinterpret_cast<const crypto::hash8*
>(payment_id_data.data());
5740 if (parse_long_payment_id(payment_id_str, payment_id))
5743 if (parse_short_payment_id(payment_id_str, payment_id8))
5745 memcpy(payment_id.data, payment_id8.data, 8);
5746 memset(payment_id.data + 8, 0, 24);
5752 bool wallet2::prepare_file_names(
const std::string& file_path)
5754 do_prepare_file_names(file_path, m_keys_file, m_wallet_file, m_mms_file);
5772 if(m_light_wallet) {
5776 *ssl = m_light_wallet_connected;
5777 return m_light_wallet_connected;
5781 boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex);
5782 if(!m_http_client.is_connected(ssl))
5784 m_node_rpc_proxy.invalidate();
5785 if (!m_http_client.connect(std::chrono::milliseconds(timeout)))
5787 if(!m_http_client.is_connected(ssl))
5810 void wallet2::set_offline(
bool offline)
5812 m_offline = offline;
5813 m_http_client.set_auto_connect(!offline);
5816 boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex);
5817 if(m_http_client.is_connected())
5818 m_http_client.disconnect();
5822 bool wallet2::generate_chacha_key_from_secret_keys(crypto::chacha_key &
key)
const 5830 crypto::generate_chacha_key(pass.
data(), pass.
size(),
key, m_kdf_rounds);
5836 prepare_file_names(wallet_);
5838 boost::system::error_code e;
5839 bool exists = boost::filesystem::exists(m_keys_file, e);
5846 if (!load_keys(m_keys_file, password))
5850 LOG_PRINT_L0(
"Loaded wallet keys file, with public address: " << m_account.get_public_address_str(m_nettype));
5853 wallet_keys_unlocker unlocker(*
this, m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only, password);
5857 if(!boost::filesystem::exists(m_wallet_file, e) || e)
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;
5881 std::stringstream iss;
5889 crypto::chacha_key
key;
5890 generate_chacha_key_from_secret_keys(
key);
5893 std::stringstream iss;
5903 std::stringstream iss;
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;
5915 boost::archive::binary_iarchive ar(iss);
5923 LOG_PRINT_L1(
"Failed to load encrypted cache, trying unencrypted");
5925 std::stringstream iss;
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;
5937 boost::archive::binary_iarchive ar(iss);
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,
5948 generate_genesis(genesis);
5951 if (m_blockchain.empty())
5953 m_blockchain.push_back(genesis_hash);
5958 check_genesis(genesis_hash);
5963 if (get_num_subaddress_accounts() == 0)
5964 add_subaddress_account(
tr(
"Primary account"));
5968 find_and_save_rings(
false);
5970 catch (
const std::exception &e)
5972 MERROR(
"Failed to save rings, will try again next time");
5977 m_message_store.read_from_file(get_multisig_wallet_state(), m_mms_file);
5979 catch (
const std::exception &e)
5981 MERROR(
"Failed to initialize MMS, it will be unusable");
5985 void wallet2::trim_hashchain()
5989 for (
const transfer_details &td: m_transfers)
5990 if (td.m_block_height <
height)
5991 height = td.m_block_height;
5993 if (!m_blockchain.empty() && m_blockchain.size() == m_blockchain.offset())
5995 MINFO(
"Fixing empty hashchain");
5998 m_daemon_rpc_mutex.lock();
5999 req.height = m_blockchain.size() - 1;
6001 m_daemon_rpc_mutex.unlock();
6006 m_blockchain.refill(
hash);
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");
6016 MDEBUG(
"trimming to " <<
height <<
", offset " << m_blockchain.offset());
6017 m_blockchain.trim(
height);
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");
6029 return m_wallet_file;
6032 void wallet2::store()
6034 if (!m_wallet_file.empty())
6048 bool same_file =
true;
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;
6060 boost::filesystem::path parent_path = boost::filesystem::path(path).parent_path();
6063 if (!parent_path.empty() && !boost::filesystem::exists(parent_path))
6065 boost::system::error_code ec;
6066 if (!boost::filesystem::create_directories(parent_path, ec))
6068 throw std::logic_error(ec.message());
6073 std::stringstream oss;
6085 const std::string new_file = same_file ? m_wallet_file +
".new" : path;
6088 const std::string old_address_file = m_wallet_file +
".address.txt";
6094 prepare_file_names(path);
6095 bool r = store_keys(m_keys_file, password,
false);
6097 if (boost::filesystem::exists(old_address_file))
6100 const std::string address_file = m_wallet_file +
".address.txt";
6105 r = boost::filesystem::remove(old_file);
6107 LOG_ERROR(
"error removing file: " << old_file);
6110 r = boost::filesystem::remove(old_keys_file);
6112 LOG_ERROR(
"error removing file: " << old_keys_file);
6115 r = boost::filesystem::remove(old_address_file);
6117 LOG_ERROR(
"error removing file: " << old_address_file);
6120 if (boost::filesystem::exists(old_mms_file))
6122 r = boost::filesystem::remove(old_mms_file);
6124 LOG_ERROR(
"error removing file: " << old_mms_file);
6132 std::ostringstream oss;
6141 ostr.open(new_file, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);
6153 if (m_message_store.get_active())
6157 m_message_store.write_to_file(get_multisig_wallet_state(), m_mms_file);
6166 return m_light_wallet_unlocked_balance;
6167 for (
const auto& i : balance_per_subaddress(index_major, public_blockchain))
6175 if (blocks_to_unlock)
6176 *blocks_to_unlock = 0;
6178 return m_light_wallet_balance;
6179 for (
const auto& i : unlocked_balance_per_subaddress(index_major, public_blockchain))
6181 amount += i.second.first;
6182 if (blocks_to_unlock && i.second.second > *blocks_to_unlock)
6183 *blocks_to_unlock = i.second.second;
6188 std::map<uint32_t, uint64_t> wallet2::balance_per_subaddress(
uint32_t index_major,
bool public_blockchain)
const 6190 std::map<uint32_t, uint64_t> amount_per_subaddr;
6191 for (
const auto& td: m_transfers)
6193 if((public_blockchain && td.m_tx.version == 1) || (!public_blockchain && td.m_tx.version > 1))
6196 if (td.m_subaddr_index.major == index_major && !td.m_spent && !td.m_frozen)
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();
6202 found->second += td.amount();
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))
6209 if (utx.second.m_state != wallet2::unconfirmed_transfer_details::failed) {
6211 if(utx.second.m_tx.version > 1){
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);
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;
6226 found->second += out.amount;
6236 if (utx.second.m_tx.version == 1 && utx.second.m_subaddr_account == index_major) {
6238 auto found = amount_per_subaddr.find(0);
6239 if (found == amount_per_subaddr.end())
6240 amount_per_subaddr[0] = utx.second.m_change;
6242 found->second += utx.second.m_change;
6246 return amount_per_subaddr;
6249 std::map<uint32_t, std::pair<uint64_t, uint64_t>> wallet2::unlocked_balance_per_subaddress(
uint32_t index_major,
bool public_blockchain)
const 6251 std::map<uint32_t, std::pair<uint64_t, uint64_t>> amount_per_subaddr;
6252 const uint64_t blockchain_height = get_blockchain_current_height();
6256 if((public_blockchain && td.m_tx.version == 1) || (!public_blockchain && td.m_tx.version > 1))
6259 if(td.m_subaddr_index.major == index_major && !td.m_spent && !td.m_frozen)
6261 uint64_t amount = 0, blocks_to_unlock = 0;
6262 if (is_transfer_unlocked(td))
6264 amount = td.amount();
6265 blocks_to_unlock = 0;
6274 unlock_height = td.m_tx.unlock_time;
6275 blocks_to_unlock = unlock_height > blockchain_height ? unlock_height - blockchain_height : 0;
6278 auto found = amount_per_subaddr.find(td.m_subaddr_index.minor);
6280 if (found == amount_per_subaddr.end())
6281 amount_per_subaddr[td.m_subaddr_index.minor] = std::make_pair(amount, blocks_to_unlock);
6284 found->second.first += amount;
6285 found->second.second = std::max(found->second.second, blocks_to_unlock);
6289 return amount_per_subaddr;
6292 uint64_t wallet2::balance_all(
bool public_blockchain)
const 6295 for (
uint32_t index_major = 0; index_major < get_num_subaddress_accounts(); ++index_major)
6296 r += balance(index_major, public_blockchain);
6300 uint64_t wallet2::unlocked_balance_all(
bool public_blockchain,
uint64_t *blocks_to_unlock)
const 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)
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);
6317 incoming_transfers = m_transfers;
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 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))
6328 payments.push_back(x.second);
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 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))
6341 payments.push_back(x);
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 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)
6352 if (subaddr_account && *subaddr_account != i->second.m_subaddr_account)
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)
6356 if (i->second.m_is_migration)
6358 if(i->second.m_is_sc_migration)
6360 confirmed_payments.push_back(*i);
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 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)
6369 if (subaddr_account && *subaddr_account != i->second.m_subaddr_account)
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)
6373 if (!i->second.m_is_migration)
6376 confirmed_payments.push_back(*i);
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{
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)
6386 if (subaddr_account && *subaddr_account != i->second.m_subaddr_account)
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)
6390 if (!i->second.m_is_sc_migration)
6392 confirmed_payments.push_back(*i);
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 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)
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)
6403 unconfirmed_payments.push_back(*i);
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 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);
6417 void wallet2::rescan_spent()
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)
6438 const size_t n_outputs = std::min<size_t>(chunk_size, m_transfers.size() - start_offset);
6439 MDEBUG(
"Calling is_key_image_spent on " << start_offset <<
" - " << (start_offset + n_outputs - 1) <<
", out of " << m_transfers.size());
6442 for (
size_t n = start_offset; n < start_offset + n_outputs; ++n)
6444 m_daemon_rpc_mutex.lock();
6445 bool r =
invoke_http_json(
"/is_key_image_spent", req, daemon_resp, rpc_timeout);
6446 m_daemon_rpc_mutex.unlock();
6451 "daemon returned wrong response for is_key_image_spent, wrong amounts count = " +
6453 std::copy(daemon_resp.spent_status.begin(), daemon_resp.spent_status.end(), std::back_inserter(spent_status));
6459 for (
size_t start_offset = 0; start_offset < m_transfers.size(); start_offset += chunk_size)
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());
6468 for (
size_t k = start_offset; k < start_offset + n_outputs; ++k) {
6469 if (m_transfers[k].m_block_height >= v8height) {
6473 req.public_outputs.push_back(pub_out);
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");
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();
6489 "daemon returned wrong response for is_public_output_spent, wrong amount count = " +
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++];
6508 for (
size_t i = 0; i < m_transfers.size(); ++i)
6519 if(!(m_transfers[i].m_block_height >= v8height)){
6529 if (!(m_transfers[i].m_block_height >= v8height)) {
6540 void wallet2::rescan_blockchain(
bool hard,
bool refresh,
bool keep_key_images)
6543 const size_t transfers_cnt = m_transfers.size();
6549 setup_new_blockchain();
6553 if (keep_key_images && refresh)
6554 hash_m_transfers((
int64_t) transfers_cnt, transfers_hash);
6555 clear_soft(keep_key_images);
6559 this->refresh(
false);
6561 if (refresh && keep_key_images)
6562 finish_rescan_bc_keep_key_images(transfers_cnt, transfers_hash);
6572 if(!is_tx_spendtime_unlocked(unlock_time, block_height))
6578 if(block_height + UNLOCK_WINDOW > get_blockchain_current_height())
6601 if(current_time + leeway >= unlock_time)
6611 template<
typename T>
6612 T pop_index(std::vector<T>& vec,
size_t idx)
6618 if (idx + 1 != vec.size())
6620 vec[idx] = vec.back();
6622 vec.resize(vec.size() - 1);
6627 template<
typename T>
6628 T pop_random_value(std::vector<T>& vec)
6633 return pop_index (vec, idx);
6636 template<
typename T>
6637 T pop_back(std::vector<T>& vec)
6646 template<
typename T>
6647 void pop_if_present(std::vector<T>& vec,
T e)
6649 for (
size_t i = 0; i < vec.size(); ++i)
6664 float wallet2::get_output_relatedness(
const transfer_details &td0,
const transfer_details &td1)
const 6669 if (td0.m_txid == td1.m_txid)
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;
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 6693 std::vector<size_t> candidates;
6694 float best_relatedness = 1.0f;
6695 for (
size_t n = 0; n < unused_indices.size(); ++n)
6698 float relatedness = 0.0f;
6699 for (std::vector<size_t>::const_iterator i = selected_transfers.begin(); i != selected_transfers.end(); ++i)
6701 float r = get_output_relatedness(candidate, transfers[*i]);
6702 if (r > relatedness)
6705 if (relatedness == 1.0f)
6710 if (relatedness < best_relatedness)
6712 best_relatedness = relatedness;
6716 if (relatedness == best_relatedness)
6717 candidates.push_back(n);
6726 for (
size_t n = 0; n < candidates.size(); ++n)
6729 if (td.
amount() < transfers[unused_indices[candidates[idx]]].amount())
6737 return pop_index (unused_indices, candidates[idx]);
6740 size_t wallet2::pop_best_value(std::vector<size_t> &unused_indices,
const std::vector<size_t>& selected_transfers,
bool smallest)
const 6742 return pop_best_value_from(m_transfers, unused_indices, selected_transfers, smallest);
6749 uint64_t wallet2::select_transfers(
uint64_t needed_etn, std::vector<size_t> unused_transfers_indices, std::vector<size_t>& selected_transfers)
const 6752 selected_transfers.reserve(unused_transfers_indices.size());
6753 while (found_etn < needed_etn && !unused_transfers_indices.empty())
6755 size_t idx = pop_best_value(unused_transfers_indices, selected_transfers);
6757 const transfer_container::const_iterator it = m_transfers.begin() + idx;
6758 selected_transfers.push_back(idx);
6759 found_etn += it->amount();
6768 utd.m_amount_in = amount_in;
6769 utd.m_amount_out = 0;
6772 for (
const auto &d: dests)
6773 utd.m_amount_out += d.amount;
6774 utd.m_amount_out += change_amount;
6775 utd.m_change = change_amount;
6776 utd.m_dests = dests;
6779 std::vector<account_public_address> input_addresses;
6780 for (
auto minor_index : subaddr_indices) {
6782 input_addresses.push_back(get_subaddress(index));
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);
6798 if (change_indexes.size() == tx.
vout.size()) {
6799 change_indexes.clear();
6803 for (
auto &change_index : change_indexes)
6804 total_change += tx.
vout[change_index].amount;
6805 utd.m_change = total_change;
6809 for (
size_t i = 0; i < tx.
vout.size(); ++i) {
6810 if (change_indexes.find(i) == change_indexes.end()) {
6811 auto output = boost::get<txout_to_key_public>(tx.
vout[i].target);
6814 return destination.addr == output.address;
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;
6827 output.m_address_prefix ==
6834 for (
const auto &d: utd.m_dests)
6835 utd.m_amount_out += d.amount;
6836 utd.m_amount_out += total_change;
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)
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));
6856 crypto::hash wallet2::get_payment_id(
const pending_tx &ptx)
const 6858 std::vector<tx_extra_field> tx_extra_fields;
6867 if (ptx.dests.empty())
6869 MWARNING(
"Encrypted payment id found, but no destinations public key, cannot decrypt");
6870 return crypto::null_hash;
6872 if (m_account.get_device().decrypt_payment_id(payment_id8, ptx.dests[0].addr.m_view_public_key, ptx.tx_key))
6874 memcpy(payment_id.data, payment_id8.data, 8);
6879 payment_id = crypto::null_hash;
6895 oreq.address = get_account().get_public_address_str(m_nettype);
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();
6910 req.do_not_relay =
false;
6911 req.do_sanity_checks =
true;
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();
6923 "Bad output index in selected transfers: " + boost::lexical_cast<
std::string>(idx));
6930 std::vector<cryptonote::tx_destination_entry> dests;
6932 if (store_tx_info())
6934 payment_id = get_payment_id(ptx);
6937 amount_in += m_transfers[idx].amount();
6940 if (store_tx_info())
6942 m_tx_keys.insert(std::make_pair(txid, ptx.
tx_key));
6946 LOG_PRINT_L2(
"transaction " << txid <<
" generated ok and sent to daemon, key_images: [" << ptx.
key_images <<
"]");
6955 m_transfers[idx].m_multisig_k.clear();
6964 <<
"Please, wait for confirmation for your balance to be unlocked.");
6967 void wallet2::commit_tx(std::vector<pending_tx>& ptx_vector)
6969 for (
auto & ptx : ptx_vector)
6975 bool wallet2::save_tx(
const std::vector<pending_tx>& ptx_vector,
const std::string &filename)
const 6977 LOG_PRINT_L0(
"saving " << ptx_vector.size() <<
" transactions");
6978 std::string ciphertext = dump_tx_to_str(ptx_vector);
6979 if (ciphertext.empty())
6984 std::string wallet2::dump_tx_to_str(
const std::vector<pending_tx> &ptx_vector)
const 6986 LOG_PRINT_L0(
"saving " << ptx_vector.size() <<
" transactions");
6988 for (
auto &tx: ptx_vector)
6993 txs.
txes.push_back(get_construction_data_with_decrypted_short_payment_id(tx, m_account.get_device()));
6996 txs.transfers = export_outputs();
6998 std::ostringstream oss;
7008 LOG_PRINT_L2(
"Saving unsigned tx data: " << oss.str());
7009 std::string ciphertext = encrypt_with_view_secret_key(oss.str());
7017 boost::system::error_code errcode;
7019 if (!boost::filesystem::exists(unsigned_filename, errcode))
7021 LOG_PRINT_L0(
"File " << unsigned_filename <<
" does not exist: " << errcode);
7026 LOG_PRINT_L0(
"Failed to load from " << unsigned_filename);
7030 return parse_unsigned_tx_from_str(s, exported_txs);
7042 s = s.substr(magiclen);
7049 std::istringstream iss(s);
7053 catch (
const std::exception &e)
7055 LOG_PRINT_L0(
"Failed to parse data from unsigned tx: " << e.what());
7068 s = decrypt_with_view_secret_key(s);
7071 std::istringstream iss(s);
7075 catch (
const std::exception &e)
7077 LOG_PRINT_L0(
"Failed to parse decrypted data from unsigned tx: " << e.what());
7081 catch (
const std::exception &e)
7083 LOG_PRINT_L0(
"Failed to decrypt unsigned tx: " << e.what());
7088 LOG_PRINT_L0(
"Failed to parse decrypted data from unsigned tx");
7097 LOG_PRINT_L1(
"Loaded tx unsigned data from binary: " << exported_txs.
txes.size() <<
" transactions");
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)
7105 if(!load_unsigned_tx(unsigned_filename, exported_txs))
7108 if (accept_func && !accept_func(exported_txs))
7113 return sign_tx(exported_txs, signed_filename, txs, export_raw);
7121 for (
size_t n = 0; n < exported_txs.
txes.size(); ++n)
7130 std::vector<crypto::secret_key> additional_tx_keys;
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);
7146 if (store_tx_info())
7149 m_tx_keys.insert(std::make_pair(txid, tx_key));
7150 m_additional_tx_keys.insert(std::make_pair(txid, additional_tx_keys));
7156 bool all_are_txin_to_key = std::all_of(ptx.
tx.
vin.begin(), ptx.
tx.
vin.end(), [&](
const txin_v &s_e) ->
bool {
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 {
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 {
7178 for (
const auto &i: sd.
sources) ptx.
fee += i.amount;
7191 txs.back().tx_key = tx_key;
7192 txs.back().additional_tx_keys = additional_tx_keys;
7198 for (
size_t n = 0; n < exported_txs.
txes.size(); ++n)
7203 std::vector<crypto::key_derivation> additional_derivations;
7208 std::vector<crypto::public_key> additional_tx_pub_keys;
7211 additional_tx_pub_keys.resize(additional_tx_pub_keys.size() + 1);
7220 static_assert(
sizeof(derivation) ==
sizeof(
rct::key),
"Mismatched sizes of key_derivation and rct::key");
7223 for (
size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
7225 additional_derivations.push_back({});
7233 for (
size_t i = 0; i < tx.
vout.size(); ++i)
7243 if (
generate_key_image_helper(keys, m_subaddresses, out.key, tx_pub_key, additional_tx_pub_keys, i, in_ephemeral, ki, hwdev))
7246 MERROR(
"Failed to calculate key image");
7251 signed_txes.
key_images.resize(m_transfers.size());
7252 for (
size_t i = 0; i < m_transfers.size(); ++i)
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;
7266 std::string ciphertext = sign_tx_dump_to_str(exported_txs, txs, signed_txes);
7267 if (ciphertext.empty())
7275 LOG_PRINT_L0(
"Failed to save file to " << signed_filename);
7281 for (
size_t i = 0; i < signed_txes.
ptx.size(); ++i)
7287 LOG_PRINT_L0(
"Failed to save file to " << raw_filename);
7298 bool r = sign_tx(exported_txs, ptx, signed_txes);
7306 std::ostringstream oss;
7316 LOG_PRINT_L3(
"Saving signed tx data (with encryption): " << oss.str());
7317 std::string ciphertext = encrypt_with_view_secret_key(oss.str());
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)
7324 boost::system::error_code errcode;
7327 if (!boost::filesystem::exists(signed_filename, errcode))
7329 LOG_PRINT_L0(
"File " << signed_filename <<
" does not exist: " << errcode);
7335 LOG_PRINT_L0(
"Failed to load from " << signed_filename);
7339 return parse_tx_from_str(s, ptx, accept_func);
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)
7345 boost::system::error_code errcode;
7354 s = s.substr(magiclen);
7361 std::istringstream iss(s);
7365 catch (
const std::exception &e)
7367 LOG_PRINT_L0(
"Failed to parse data from signed transaction: " << e.what());
7372 LOG_PRINT_L0(
"Failed to parse data from signed transaction");
7380 s = decrypt_with_view_secret_key(s);
7383 std::istringstream iss(s);
7387 catch (
const std::exception &e)
7389 LOG_PRINT_L0(
"Failed to parse decrypted data from signed transaction: " << e.what());
7393 catch (
const std::exception &e)
7395 LOG_PRINT_L0(
"Failed to decrypt signed transaction: " << e.what());
7406 LOG_PRINT_L0(
"Unsupported version in signed transaction");
7409 LOG_PRINT_L0(
"Loaded signed tx data from binary: " << signed_txs.
ptx.size() <<
" transactions");
7412 if (accept_func && !accept_func(signed_txs))
7419 bool r = import_key_images(signed_txs.
key_images);
7420 if (!r)
return false;
7424 m_cold_key_images.insert(e);
7426 ptx = signed_txs.
ptx;
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();
7441 for (
auto &ptx: txs.
m_ptx)
7443 for (
auto &e: ptx.construction_data.sources)
7447 for (
auto &ptx: txs.
m_ptx)
7450 ptx.construction_data = get_construction_data_with_decrypted_short_payment_id(ptx, m_account.get_device());
7454 std::ostringstream oss;
7464 LOG_PRINT_L2(
"Saving multisig unsigned tx data: " << oss.str());
7465 std::string ciphertext = encrypt_with_view_secret_key(oss.str());
7472 if (ciphertext.empty())
7480 txs.
m_ptx = ptx_vector;
7482 for (
const auto &msk: get_account().get_multisig_keys())
7485 for (
auto &ptx: txs.
m_ptx)
for (
auto &sig: ptx.multisig_sigs) sig.signing_keys.insert(pkey);
7488 txs.
m_signers.insert(get_multisig_signer_public_key());
7492 std::string wallet2::save_multisig_tx(
const std::vector<pending_tx>& ptx_vector)
7494 return save_multisig_tx(make_multisig_tx_set(ptx_vector));
7497 bool wallet2::save_multisig_tx(
const std::vector<pending_tx>& ptx_vector,
const std::string &filename)
7499 std::string ciphertext = save_multisig_tx(ptx_vector);
7500 if (ciphertext.empty())
7515 multisig_tx_st = decrypt_with_view_secret_key(
std::string(multisig_tx_st, magiclen));
7517 catch (
const std::exception &e)
7519 LOG_PRINT_L0(
"Failed to decrypt multisig tx data: " << e.what());
7524 std::istringstream iss(multisig_tx_st);
7535 for (
const auto &ptx: exported_txs.
m_ptx)
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)
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)
7543 CHECK_AND_ASSERT_MES(ptx.construction_data.sources.size() == ptx.tx.vin.size(),
false,
"Mismatched sources/vin sizes");
7551 if(!parse_multisig_tx_from_str(s, exported_txs))
7553 LOG_PRINT_L0(
"Failed to parse multisig transaction from string");
7557 LOG_PRINT_L1(
"Loaded multisig tx unsigned data from binary: " << exported_txs.
m_ptx.size() <<
" transactions");
7560 if (accept_func && !accept_func(exported_txs))
7566 const bool is_signed = exported_txs.
m_signers.size() >= m_multisig_threshold;
7569 for (
const auto &ptx: exported_txs.
m_ptx)
7572 if (store_tx_info())
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));
7586 boost::system::error_code errcode;
7588 if (!boost::filesystem::exists(filename, errcode))
7590 LOG_PRINT_L0(
"File " << filename <<
" does not exist: " << errcode);
7599 if (!load_multisig_tx(s, exported_txs, accept_func))
7601 LOG_PRINT_L0(
"Failed to parse multisig tx data from " << filename);
7607 bool wallet2::sign_multisig_tx(
multisig_tx_set &exported_txs, std::vector<crypto::hash> &txids)
7623 for (
size_t n = 0; n < exported_txs.
m_ptx.size(); ++n)
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);
7632 auto sources = sd.sources;
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 , this->m_nettype);
7641 std::vector<unsigned int> indices;
7642 for (
const auto &
source: sources)
7643 indices.push_back(
source.real_output);
7647 if (sig.ignore.find(local_signer) == sig.ignore.end())
7652 for (
size_t idx: sd.selected_transfers)
7653 k.push_back(get_multisig_k(idx, sig.used_L));
7656 for (
const auto &msk: get_account().get_multisig_keys())
7660 if (sig.signing_keys.find(pmsk) == sig.signing_keys.end())
7663 sig.signing_keys.insert(pmsk);
7673 const bool is_last = exported_txs.
m_signers.size() + 1 >= m_multisig_threshold;
7681 if (sig.ignore.find(local_signer) == sig.ignore.end() && !keys_intersect(sig.ignore, exported_txs.
m_signers))
7689 "Final signed transaction not found: this transaction was likely made without our export data, so we cannot sign it");
7691 if (store_tx_info())
7693 m_tx_keys.insert(std::make_pair(txid, ptx.
tx_key));
7696 txids.push_back(txid);
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();
7705 exported_txs.
m_signers.insert(get_multisig_signer_public_key());
7712 bool r = sign_multisig_tx(exported_txs, txids);
7715 return save_multisig_tx(exported_txs, filename);
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)
7721 if(!load_multisig_tx_from_file(filename, exported_txs))
7724 if (accept_func && !accept_func(exported_txs))
7729 return sign_multisig_tx_to_file(exported_txs, filename, txids);
7742 { 3, {1, 20, 166} },
7743 { 4, {1, 4, 20, 166} },
7744 { 4, {1, 2, 4, 8} },
7747 if (fee_algorithm == -1)
7748 fee_algorithm = get_fee_algorithm();
7752 priority = m_default_priority;
7755 if (fee_algorithm == 2)
7764 const uint32_t max_priority = multipliers[fee_algorithm].count;
7765 if (priority >= 1 && priority <= max_priority)
7767 return multipliers[fee_algorithm].multipliers[priority-1];
7774 uint64_t wallet2::get_dynamic_base_fee_estimate()
const 7790 return m_light_wallet_per_kb_fee / 1024;
7792 return m_light_wallet_per_kb_fee;
7804 return get_dynamic_base_fee_estimate();
7814 if (!use_per_byte_fee)
7818 boost::optional<std::string> result = m_node_rpc_proxy.get_fee_quantization_mask(fee_quantization_mask);
7821 return fee_quantization_mask;
7824 int wallet2::get_fee_algorithm()
const 7827 if (use_fork_rules(6, 0))
7829 if (use_fork_rules(5, 0))
7831 if (use_fork_rules(3, -720 * 14))
7862 const uint64_t min_ring_size = get_min_ring_size();
7863 if (mixin + 1 < min_ring_size)
7865 MWARNING(
"Requested ring size " << (mixin + 1) <<
" too low, using " << min_ring_size);
7866 mixin = min_ring_size-1;
7868 const uint64_t max_ring_size = get_max_ring_size();
7869 if (max_ring_size && mixin + 1 > max_ring_size)
7871 MWARNING(
"Requested ring size " << (mixin + 1) <<
" too high, using " << max_ring_size);
7872 mixin = max_ring_size-1;
7882 if (priority == 0 && m_default_priority == 0 && auto_low_priority())
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)});
7894 MERROR(
"Bad estimated backlog array size");
7897 else if (
blocks[0].first > 0)
7899 MINFO(
"We don't use the low priority because there's a backlog in the tx pool.");
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;
7910 const size_t N = 10;
7911 if (m_blockchain.size() < N)
7913 MERROR(
"The blockchain is too short");
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();
7926 if (getbh_res.headers.size() != N)
7928 MERROR(
"Bad blockheaders size");
7931 size_t block_weight_sum = 0;
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());
7942 MINFO(
"We don't use the low priority because recent blocks are quite full.");
7945 MINFO(
"We'll use the low priority because probably it's safe to do so.");
7948 catch (
const std::exception &e)
7958 m_ring_database = filename;
7959 MINFO(
"ringdb path set to " << filename);
7961 if (!m_ring_database.empty())
7966 generate_genesis(b);
7969 catch (
const std::exception &e)
7971 MERROR(
"Failed to initialize ringdb: " << e.what());
7972 m_ring_database =
"";
7979 crypto::chacha_key wallet2::get_ringdb_key()
7983 MINFO(
"caching ringdb key");
7984 crypto::chacha_key
key;
7985 generate_chacha_key_from_secret_keys(
key);
7988 return *m_ringdb_key;
7991 void wallet2::register_devices(){
7996 if (!m_devices_registered){
7997 m_devices_registered =
true;
8008 try {
return m_ringdb->add_rings(
key, tx); }
8009 catch (
const std::exception &e) {
return false; }
8014 try {
return add_rings(get_ringdb_key(), tx); }
8015 catch (
const std::exception &e) {
return false; }
8022 try {
return m_ringdb->remove_rings(get_ringdb_key(), tx); }
8023 catch (
const std::exception &e) {
return false; }
8030 try {
return m_ringdb->get_ring(
key,
key_image, outs); }
8031 catch (
const std::exception &e) {
return false; }
8036 for (
auto i: m_confirmed_txs)
8038 if (txid == i.first)
8040 for (
const auto &x: i.second.m_rings)
8045 for (
auto i: m_unconfirmed_txs)
8047 if (txid == i.first)
8049 for (
const auto &x: i.second.m_rings)
8059 try {
return get_ring(get_ringdb_key(),
key_image, outs); }
8060 catch (
const std::exception &e) {
return false; }
8068 try {
return m_ringdb->set_ring(get_ringdb_key(),
key_image, outs, relative); }
8069 catch (
const std::exception &e) {
return false; }
8072 bool wallet2::unset_ring(
const std::vector<crypto::key_image> &key_images)
8077 try {
return m_ringdb->remove_rings(get_ringdb_key(), key_images); }
8078 catch (
const std::exception &e) {
return false; }
8089 req.decode_as_json =
false;
8091 m_daemon_rpc_mutex.lock();
8093 m_daemon_rpc_mutex.unlock();
8095 if (
res.txs.empty())
8101 if (!get_pruned_tx(
res.txs.front(), tx, tx_hash))
8105 try {
return m_ringdb->remove_rings(get_ringdb_key(), tx); }
8106 catch (
const std::exception &e) {
return false; }
8109 bool wallet2::find_and_save_rings(
bool force)
8111 if (!force && m_ring_history_saved)
8119 MDEBUG(
"Finding and saving rings...");
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)
8128 txs_hashes.push_back(txid);
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)
8138 req.decode_as_json =
false;
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)
8146 const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
8153 "daemon returned wrong response for gettransactions, wrong txs count = " +
8156 MDEBUG(
"Scanning " <<
res.txs.size() <<
" transactions");
8158 for (
size_t i = 0; i <
res.txs.size(); ++i, ++it)
8164 "Failed to get transaction from daemon");
8170 MINFO(
"Found and saved rings for " << txs_hashes.size() <<
" transactions");
8171 m_ring_history_saved =
true;
8175 bool wallet2::blackball_output(
const std::pair<uint64_t, uint64_t> &output)
8179 try {
return m_ringdb->blackball(output); }
8180 catch (
const std::exception &e) {
return false; }
8183 bool wallet2::set_blackballed_outputs(
const std::vector<std::pair<uint64_t, uint64_t>> &outputs,
bool add)
8191 ret &= m_ringdb->clear_blackballs();
8192 ret &= m_ringdb->blackball(outputs);
8195 catch (
const std::exception &e) {
return false; }
8198 bool wallet2::unblackball_output(
const std::pair<uint64_t, uint64_t> &output)
8202 try {
return m_ringdb->unblackball(output); }
8203 catch (
const std::exception &e) {
return false; }
8206 bool wallet2::is_output_blackballed(
const std::pair<uint64_t, uint64_t> &output)
const 8210 try {
return m_ringdb->blackballed(output); }
8211 catch (
const std::exception &e) {
return false; }
8214 bool wallet2::lock_keys_file()
8216 if (m_keys_file_locker)
8218 MDEBUG(m_keys_file <<
" is already locked.");
8225 bool wallet2::unlock_keys_file()
8227 if (!m_keys_file_locker)
8229 MDEBUG(m_keys_file <<
" is already unlocked.");
8232 m_keys_file_locker.reset();
8236 bool wallet2::is_keys_file_locked()
const 8238 return m_keys_file_locker->locked();
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 8245 if (global_index == real_index)
8249 if (std::find(outs.back().begin(), outs.back().end(), item) != outs.back().end())
8254 MWARNING(
"Key " << output_public_key <<
" at index " << global_index <<
" is not in the main subgroup");
8259 MWARNING(
"Commitment " << mask <<
" at index " << global_index <<
" is not in the main subgroup");
8264 outs.back().push_back(item);
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) {
8270 MDEBUG(
"LIGHTWALLET - Getting random outs");
8275 size_t light_wallet_requested_outputs_count = (size_t)((fake_outputs_count + 1) * 1.5 + 1);
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());
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();
8294 for(
auto& out: ores.amount_outs) {
8297 MDEBUG(out.outputs.size() <<
" outputs for amount "+ boost::lexical_cast<
std::string>(out.amount) +
" received from light wallet node");
8300 MDEBUG(
"selected transfers size: " << selected_transfers.size());
8302 for(
size_t idx: selected_transfers)
8305 outs.push_back(std::vector<get_outs_entry>());
8306 outs.back().reserve(fake_outputs_count + 1);
8315 std::vector<size_t> order;
8316 order.resize(light_wallet_requested_outputs_count);
8317 for (
size_t n = 0; n < order.size(); ++n)
8319 std::shuffle(order.begin(), order.end(), std::default_random_engine(crypto::rand<unsigned>()));
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)
8327 size_t i = order[o];
8330 bool found_amount =
false;
8332 for(amount_key = 0; amount_key < ores.amount_outs.size(); ++amount_key)
8334 if(boost::lexical_cast<uint64_t>(ores.amount_outs[amount_key].amount) == amount) {
8335 found_amount =
true;
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);
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))
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);
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); });
8366 for(
auto added_out: outs.back())
8367 MTRACE(std::get<0>(added_out));
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)
8374 LOG_PRINT_L2(
"fake_outputs_count: " << fake_outputs_count);
8377 if(m_light_wallet && fake_outputs_count > 0) {
8378 light_wallet_get_outs(outs, selected_transfers, fake_outputs_count);
8382 if (fake_outputs_count > 0)
8384 uint64_t segregation_fork_height = get_segregation_fork_height();
8387 boost::optional<std::string> result = m_node_rpc_proxy.get_height(
height);
8388 throw_on_rpc_response_error(result,
"get_info");
8390 bool is_after_segregation_fork =
height >= segregation_fork_height;
8398 std::vector<uint64_t> rct_offsets;
8399 bool has_rct =
false;
8401 for (
size_t idx: selected_transfers)
8402 if (m_transfers[idx].is_rct())
8405 max_rct_index = std::max(max_rct_index, m_transfers[idx].m_global_output_index);
8407 const bool has_rct_distribution = has_rct && get_rct_distribution(rct_start_height, rct_offsets);
8408 if (has_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())
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;
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();
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))
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));
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();
8462 for(
size_t idx: selected_transfers)
8464 const uint64_t amount = m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount();
8466 for (
const auto &d: resp_t.distributions)
8468 if (d.amount == amount)
8475 uint64_t till_fork = d.data.distribution[segregation_fork_height - d.data.start_height];
8477 segregation_limit[amount] = std::make_pair(till_fork, recent);
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);
8494 std::unique_ptr<gamma_picker> gamma;
8495 if (has_rct_distribution)
8496 gamma.reset(
new gamma_picker(rct_offsets));
8498 size_t num_selected_transfers = 0;
8499 for(
size_t idx: selected_transfers)
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;
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;
8510 const bool output_is_pre_fork = td.m_block_height < segregation_fork_height;
8511 uint64_t num_outs = 0, num_recent_outs = 0;
8513 float pre_fork_num_out_ratio = 0.0f;
8514 float post_fork_num_out_ratio = 0.0f;
8516 if (is_after_segregation_fork && m_segregate_pre_fork_outputs && output_is_pre_fork)
8518 num_outs = segregation_limit[amount].first;
8519 num_recent_outs = segregation_limit[amount].second;
8525 for (
const auto &he: resp_t.histogram)
8527 if (he.amount == amount)
8530 << he.unlocked_instances <<
" unlocked, " << he.recent_instances <<
" recent");
8531 num_outs = he.unlocked_instances;
8532 num_recent_outs = he.recent_instances;
8536 if (is_after_segregation_fork && m_key_reuse_mitigation2)
8538 if (output_is_pre_fork)
8540 if (is_shortly_after_segregation_fork)
8552 if (is_shortly_after_segregation_fork)
8561 num_post_fork_outs = num_outs - segregation_limit[amount].first;
8568 "histogram reports no unlocked outputs for " + boost::lexical_cast<std::string>(amount) +
", not even ours");
8570 "histogram reports more recent outs than outs for " + boost::lexical_cast<std::string>(amount));
8575 num_outs = rct_offsets[rct_offsets.size() - TX_SPENDABLE_AGE];
8576 LOG_PRINT_L1(
"" << num_outs <<
" unlocked rct outputs");
8578 "histogram reports no unlocked rct outputs, not even ours");
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;
8585 size_t normal_output_count = requested_outputs_count - pre_fork_outputs_count - post_fork_outputs_count;
8587 size_t recent_outputs_count = 0;
8592 if (recent_outputs_count == 0)
8593 recent_outputs_count = 1;
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;
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");
8606 if (td.m_key_image_known && !td.m_key_image_partial)
8608 std::vector<uint64_t> ring;
8609 if (get_ring(get_ringdb_key(), td.m_key_image, ring))
8611 MINFO(
"This output has a known ring, reusing (size " << ring.size() <<
")");
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)
8619 MINFO(
"Ring has output " << out);
8623 req.outputs.push_back({amount,
out});
8625 seen_indices.emplace(out);
8626 if (out == td.m_global_output_index)
8628 MINFO(
"This is the real output");
8634 MINFO(
"Ignoring output " << out <<
", too recent");
8638 "Known ring does not include the spent output: " +
std::to_string(td.m_global_output_index));
8642 if (num_outs <= requested_outputs_count)
8644 for (
uint64_t i = 0; i < num_outs; i++)
8645 req.outputs.push_back({amount, i});
8649 for (
uint64_t i = num_outs; i < requested_outputs_count; ++i)
8650 req.outputs.push_back({amount, num_outs - 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));
8663 std::unordered_map<const char*, std::set<uint64_t>> picks;
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)
8673 if (seen_indices.size() == num_usable_outs)
8679 if (allow_blackballed)
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;
8691 const char *type =
"";
8692 if (amount == 0 && has_rct_distribution)
8696 if (num_found -1 < recent_outputs_count + pre_fork_outputs_count)
8698 do i = gamma->pick();
while (i >= segregation_limit[amount].first);
8699 type =
"pre-fork gamma";
8701 else if (num_found -1 < recent_outputs_count + pre_fork_outputs_count + post_fork_outputs_count)
8703 do i = gamma->pick();
while (i < segregation_limit[amount].first || i >= num_outs);
8704 type =
"post-fork gamma";
8708 do i = gamma->pick();
while (i >= num_outs);
8712 else if (num_found - 1 < recent_outputs_count)
8716 double frac = std::sqrt((
double)r / ((
uint64_t)1 << 53));
8717 i = (
uint64_t)(frac*num_recent_outs) + num_outs - num_recent_outs;
8723 else if (num_found -1 < recent_outputs_count + pre_fork_outputs_count)
8727 double frac = std::sqrt((
double)r / ((
uint64_t)1 << 53));
8728 i = (
uint64_t)(frac*segregation_limit[amount].first);
8734 else if (num_found -1 < recent_outputs_count + pre_fork_outputs_count + post_fork_outputs_count)
8738 double frac = std::sqrt((
double)r / ((
uint64_t)1 << 53));
8739 i = (
uint64_t)(frac*num_post_fork_outs) + segregation_limit[amount].first;
8741 if (i == num_post_fork_outs+segregation_limit[amount].first)
8749 double frac = std::sqrt((
double)r / ((
uint64_t)1 << 53));
8754 type =
"triangular";
8757 if (seen_indices.count(i))
8759 if (!allow_blackballed && is_output_blackballed(std::make_pair(amount, i)))
8764 seen_indices.emplace(i);
8766 picks[type].insert(i);
8767 req.outputs.push_back({amount, i});
8769 MDEBUG(
"picked " << i <<
", " << num_found <<
" now picked");
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);}),
" "));
8779 while (num_found < requested_outputs_count)
8781 req.outputs.push_back({amount, 0});
8787 std::sort(req.outputs.begin() + start, req.outputs.end(),
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);}),
" "));
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();
8810 "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
8813 std::unordered_map<uint64_t, uint64_t> scanty_outs;
8815 outs.reserve(num_selected_transfers);
8816 for(
size_t idx: selected_transfers)
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);
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)
8831 if (he.amount == amount)
8833 num_outs = he.unlocked_instances;
8837 bool use_histogram = amount != 0 || !has_rct_distribution;
8839 num_outs = rct_offsets[rct_offsets.size() - TX_SPENDABLE_AGE];
8846 bool real_out_found =
false;
8847 for (
size_t n = 0; n < requested_outputs_count; ++n)
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;
8856 "Daemon response did not include the requested real output");
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));
8862 if (td.m_key_image_known && !td.m_key_image_partial)
8864 std::vector<uint64_t> ring;
8865 if (get_ring(get_ringdb_key(), td.m_key_image, ring))
8871 if (out != td.m_global_output_index)
8874 for (
size_t o = 0; o < requested_outputs_count; ++o)
8876 size_t i = base + o;
8877 if (req.outputs[i].index == out)
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);
8885 THROW_WALLET_EXCEPTION_IF(!found, error::wallet_internal_error,
"Falied to find existing ring output in daemon out data");
8894 std::vector<size_t> order;
8895 order.resize(requested_outputs_count);
8896 for (
size_t n = 0; n < order.size(); ++n)
8898 std::shuffle(order.begin(), order.end(), std::default_random_engine(crypto::rand<unsigned>()));
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)
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);
8907 if (outs.back().size() < fake_outputs_count + 1)
8909 scanty_outs[td.is_rct() ? 0 : td.amount()] = outs.back().size();
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); });
8916 base += requested_outputs_count;
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;
8927 v.push_back(
std::make_tuple(td.m_global_output_index, td.get_public_key(),
8934 if(tx_version < 3) {
8936 for (
size_t i = 0; i < selected_transfers.size(); ++i) {
8937 const size_t idx = selected_transfers[i];
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);
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,
8962 uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit();
8971 needed_etn += dt.amount;
8977 for(
size_t idx: selected_transfers)
8979 found_etn += m_transfers[idx].amount();
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)
8990 get_outs(outs, selected_transfers, fake_outputs_count, tx.
version);
8995 size_t i = 0, out_index = 0;
8996 std::vector<cryptonote::tx_source_entry> sources;
8997 for(
size_t idx: selected_transfers)
8999 sources.resize(sources.size()+1);
9007 for (
size_t n = 0; n < fake_outputs_count + 1; ++n) {
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]);
9018 auto it_to_replace = std::find_if(src.
outputs.begin(), src.
outputs.end(), [&](
const tx_output_entry &
a) {
9022 "real output not found");
9024 tx_output_entry real_oe;
9026 real_oe.second.dest = rct::pk2rct(
9029 *it_to_replace = real_oe;
9044 if (needed_etn < found_etn)
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;
9053 std::vector<cryptonote::tx_destination_entry> splitted_dsts, dust_dsts;
9055 destination_split_strategy(dsts, change_dts, dust_policy.
dust_threshold, splitted_dsts, dust_dsts);
9056 for(
auto& d: dust_dsts) {
9060 for(
auto& d: dust_dsts) {
9067 std::vector<crypto::secret_key> additional_tx_keys;
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);
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 9084 std::all_of(tx.vin.begin(), tx.vin.end(), [&](
const txin_v& s_e) ->
bool 9097 if (dust_policy.
add_to_fee || dust_sent_elsewhere) change_dts.
amount -= dust;
9101 ptx.
dust = ((dust_policy.
add_to_fee || dust_sent_elsewhere) ? dust : 0);
9121 for (
size_t idx: selected_transfers)
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 9128 std::vector<size_t> picks;
9129 float current_output_relatdness = 1.0f;
9134 for (
size_t i = 0; i < m_transfers.size(); ++i)
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)
9149 for (
size_t i = 0; i < m_transfers.size(); ++i)
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)
9155 for (
size_t j = i + 1; j < m_transfers.size(); ++j)
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)
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)
9174 if (relatedness == 0.0f)
9176 current_output_relatdness = relatedness;
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 9190 if (n_transfers > 1)
9192 if (unused_dust_indices.empty() && unused_transfers_indices.empty())
9198 for (
auto i: unused_dust_indices)
9200 if (m_transfers[i].is_rct())
9206 if (!found)
for (
auto i: unused_transfers_indices)
9208 if (m_transfers[i].is_rct())
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 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);
9231 static uint32_t get_count_above(
const std::vector<wallet2::transfer_details> &transfers,
const std::vector<size_t> &indices,
uint64_t threshold)
9234 for (
size_t idx: indices)
9235 if (transfers[idx].amount() >=
threshold)
9240 bool wallet2::light_wallet_login(
bool &new_address)
9242 MDEBUG(
"Light wallet login request");
9243 m_light_wallet_connected =
false;
9246 request.address = get_account().get_public_address_str(m_nettype);
9249 request.create_account =
true;
9250 m_daemon_rpc_mutex.lock();
9252 m_daemon_rpc_mutex.unlock();
9254 m_light_wallet_connected = connected && (
response.status.empty() ||
response.status ==
"success");
9255 new_address =
response.new_address;
9259 if(m_light_wallet_connected)
9266 return m_light_wallet_connected;
9271 MDEBUG(
"Light wallet import wallet request");
9273 oreq.address = get_account().get_public_address_str(m_nettype);
9275 m_daemon_rpc_mutex.lock();
9277 m_daemon_rpc_mutex.unlock();
9284 void wallet2::light_wallet_get_unspent_outs()
9286 MDEBUG(
"Getting unspent outs");
9292 oreq.address = get_account().get_public_address_str(m_nettype);
9298 oreq.use_dust =
true;
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();
9307 m_light_wallet_per_kb_fee = ores.per_kb_fee;
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);
9313 MDEBUG(
"FOUND " << ores.outputs.size() <<
" outputs");
9316 if(ores.outputs.empty())
9320 m_transfers.clear();
9322 for (
const auto &o: ores.outputs) {
9324 bool add_transfer =
true;
9330 for (
const std::string &ski: o.spend_key_images) {
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);
9341 MTRACE(
"Unspent output found. " << o.public_key);
9356 for(
auto &t: m_transfers){
9359 add_transfer =
false;
9367 m_transfers.push_back(boost::value_initialized<transfer_details>());
9395 std::unordered_map<crypto::hash,address_tx>::const_iterator found = m_light_wallet_address_txs.find(txid);
9397 bool miner_tx = found->second.m_coinbase;
9416 MDEBUG(
"output index: " << o.global_index);
9432 set_unspent(m_transfers.size()-1);
9433 m_key_images[td.
m_key_image] = m_transfers.size()-1;
9444 request.address = get_account().get_public_address_str(m_nettype);
9446 m_daemon_rpc_mutex.lock();
9448 m_daemon_rpc_mutex.unlock();
9454 void wallet2::light_wallet_get_address_txs()
9456 MDEBUG(
"Refreshing light wallet");
9461 ireq.address = get_account().get_public_address_str(m_nettype);
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();
9472 if(ires.transactions.empty())
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);
9486 std::vector<crypto::hash> pool_txs;
9488 for (
const auto &t: ires.transactions) {
9489 const uint64_t total_received = t.total_received;
9490 uint64_t total_sent = t.total_sent;
9493 for(
const auto &so: t.spent_outputs)
9502 if(!light_wallet_key_image_is_ours(
key_image, tx_public_key, so.out_index)) {
9504 total_sent -= so.amount;
9509 if(total_sent == 0 && total_received == 0)
9521 bool incoming = (total_received > total_sent);
9525 address_tx.
m_amount = incoming ? total_received - total_sent : total_sent - total_received;
9532 m_light_wallet_address_txs.emplace(tx_hash,
address_tx);
9536 if(total_received > total_sent) {
9539 payment.
m_amount = total_received - total_sent;
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);
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);
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) {
9568 uint64_t amount_sent = total_sent - total_received;
9571 wallet_total_sent += total_sent;
9576 if(m_unconfirmed_txs.find(tx_hash) == m_unconfirmed_txs.end())
9584 utd.
m_state = wallet2::unconfirmed_transfer_details::pending;
9585 m_unconfirmed_txs.emplace(tx_hash,utd);
9591 auto confirmed_tx = m_confirmed_txs.find(tx_hash);
9592 if(confirmed_tx == m_confirmed_txs.end()) {
9594 if(m_unconfirmed_txs.find(tx_hash) != m_unconfirmed_txs.end())
9596 process_unconfirmed(tx_hash, dummy_tx, t.height);
9608 m_confirmed_txs.emplace(tx_hash,ctd);
9610 if (0 != m_callback)
9612 m_callback->on_lw_etn_spent(t.height, tx_hash, amount_sent);
9619 if(confirmed_tx->second.m_amount_in != amount_sent || confirmed_tx->second.m_amount_out != amount_sent)
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;
9631 remove_obsolete_pool_txs(pool_txs);
9634 m_light_wallet_balance = ires.total_received-wallet_total_sent;
9636 if(ires.total_received_unlocked > 0)
9637 m_light_wallet_unlocked_balance = ires.total_received_unlocked-wallet_total_sent;
9639 m_light_wallet_unlocked_balance = m_light_wallet_balance;
9645 if (rct_string.empty())
9649 std::string rct_commit_str = rct_string.substr(0,64);
9650 std::string encrypted_mask_str = rct_string.substr(64,64);
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()) {
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;
9701 CHECK_AND_ASSERT_MES(in_ephemeral.
pub == out_pkey_test,
false,
"derived secret key doesn't match derived public key");
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;
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)
9729 boost::unique_lock<hw::device> hwdev_lock (hwdev);
9733 auto original_dsts = dsts;
9735 if(m_light_wallet) {
9737 light_wallet_get_unspent_outs();
9740 uint8_t tx_version = this->public_transactions_required() ? 3 : 1;
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;
9745 uint64_t accumulated_fee, accumulated_outputs, accumulated_change;
9747 std::vector<size_t> selected_transfers;
9748 std::vector<cryptonote::tx_destination_entry> dsts;
9753 std::vector<std::vector<tools::wallet2::get_outs_entry>> outs;
9755 TX() : weight(0), needed_fee(0) {}
9758 if (merge_destinations)
9760 std::vector<cryptonote::tx_destination_entry>::iterator i;
9762 if (i == dsts.end())
9768 i->amount += amount;
9774 if (original_output_index == dsts.size())
9777 dsts.back().amount = 0;
9780 dsts[original_output_index].amount += amount;
9784 std::vector<TX> txes;
9787 uint64_t needed_fee, available_for_fee = 0;
9788 uint64_t upper_transaction_weight_limit;
9789 uint64_t extra_bytes = extra.size();
9793 case 0 : upper_transaction_weight_limit = get_upper_transaction_weight_limit();
break;
9800 case 1 : upper_transaction_weight_limit = 3000 - extra_bytes;
break;
9803 case 2 : upper_transaction_weight_limit = 3000 - extra_bytes;
break;
9808 const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
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();
9827 needed_etn += dt.amount;
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);
9838 if (subaddr_indices.empty())
9840 for (
const auto& i : balance_per_subaddr)
9841 subaddr_indices.insert(i.first);
9847 uint64_t min_fee = (fee_multiplier * base_fee * estimate_tx_size(use_rct, 1, fake_outs_count, 2, extra.size(), bulletproof));
9849 if (!use_per_byte_fee){
9851 if(min_fee == 0){min_fee += 10;}
9854 uint64_t unlocked_balance_subtotal = 0;
9855 for (
uint32_t index_minor : subaddr_indices)
9857 balance_subtotal += balance_per_subaddr[index_minor];
9858 unlocked_balance_subtotal += unlocked_balance_per_subaddr[index_minor].first;
9861 balance_subtotal, needed_etn, 0);
9864 unlocked_balance_subtotal, needed_etn, 0);
9867 LOG_PRINT_L2(
"Candidate subaddress index for spending: " << i);
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);
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);
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)
9882 if (m_ignore_fractional_outputs && td.
amount() < fractional_threshold)
9889 if((tx_version < 3 && td.m_tx.version > 1) || (tx_version >= 3 && td.
m_tx.
version == 1))
9893 auto find_predicate = [&index_minor](
const std::pair<uint32_t, std::vector<size_t>>& x) {
return x.first == index_minor; };
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())
9899 unused_transfers_indices_per_subaddr.push_back({index_minor, {i}});
9903 found->second.push_back(i);
9905 ++num_nondust_outputs;
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())
9912 unused_dust_indices_per_subaddr.push_back({index_minor, {i}});
9916 found->second.push_back(i);
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)
9927 return unlocked_balance_per_subaddr[x.first].first > unlocked_balance_per_subaddr[y.first].first;
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);
9933 LOG_PRINT_L2(
"Starting with " << num_nondust_outputs <<
" non-dust outputs and " << num_dust_outputs <<
" dust outputs");
9935 if (unused_dust_indices_per_subaddr.empty() && unused_transfers_indices_per_subaddr.empty())
9936 return std::vector<wallet2::pending_tx>();
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({});
9945 txes.push_back(TX());
9946 accumulated_fee = 0;
9947 accumulated_outputs = 0;
9948 accumulated_change = 0;
9951 std::vector<std::vector<tools::wallet2::get_outs_entry>> outs;
9959 std::vector<size_t> preferred_inputs;
9960 uint64_t rct_outs_needed = 2 * (fake_outs_count + 1);
9961 rct_outs_needed += 100;
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())
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);
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)
9978 if (unused_transfers_indices_per_subaddr[i].first == index_minor)
9980 std::swap(unused_transfers_indices_per_subaddr[0], unused_transfers_indices_per_subaddr[i]);
9984 for (
size_t i = 1; i < unused_dust_indices_per_subaddr.size(); ++i)
9986 if (unused_dust_indices_per_subaddr[i].first == index_minor)
9988 std::swap(unused_dust_indices_per_subaddr[0], unused_dust_indices_per_subaddr[i]);
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;
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();
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,
" "));
10012 LOG_PRINT_L2(
"adding_fee " << adding_fee <<
", use_rct " << use_rct);
10015 if (unused_dust_indices->empty() && unused_transfers_indices->empty()) {
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) {
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);
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)
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");
10049 float relatedness = get_output_relatedness(m_transfers[idx], m_transfers[tx.selected_transfers.front()]);
10052 LOG_PRINT_L2(
"Second output was not strictly needed, and relatedness " << relatedness <<
", not adding");
10055 pop_if_present(*unused_transfers_indices, idx);
10056 pop_if_present(*unused_dust_indices, idx);
10058 idx = pop_best_value(unused_transfers_indices->empty() ? *unused_dust_indices : *unused_transfers_indices, tx.selected_transfers);
10061 LOG_PRINT_L2(
"Picking output " << idx <<
", amount " <<
print_etn(td.amount()) <<
", ki " << td.m_key_image);
10064 tx.selected_transfers.push_back(idx);
10065 uint64_t available_amount = td.amount();
10066 accumulated_outputs += available_amount;
10074 available_for_fee += available_amount;
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))
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;
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)) {
10094 tx.add(dsts[0], available_amount, original_output_index, m_merge_destinations);
10095 dsts[0].amount -= available_amount;
10096 available_amount = 0;
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;
10105 if (preferred_inputs.empty())
10110 try_tx = available_for_fee >= needed_fee;
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));
10122 test_tx.
version = tx_version;
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);
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;
10131 if (inputs < outputs)
10133 LOG_PRINT_L2(
"We don't have enough for the basic fee, switching to adding_fee");
10138 LOG_PRINT_L2(
"Trying to create a tx now, with " << tx.dsts.size() <<
" outputs and " <<
10139 tx.selected_transfers.size() <<
" inputs");
10141 transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
10144 needed_fee = calculate_fee(use_per_byte_fee, test_ptx.
tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
10146 LOG_PRINT_L2(
"Made a " << get_weight_string(test_ptx.
tx, txBlob.size()) <<
" tx, with " <<
print_etn(available_for_fee) <<
" available for fee (" <<
10149 if (needed_fee > available_for_fee && !dsts.empty() && dsts[0].amount > 0)
10153 std::vector<cryptonote::tx_destination_entry>::iterator i;
10154 i = std::find_if(tx.dsts.begin(), tx.dsts.end(),
10157 if (i->amount > needed_fee)
10159 uint64_t new_paid_amount = i->amount - needed_fee;
10161 print_etn(i->amount) <<
" to " <<
print_etn(new_paid_amount) <<
" to accommodate " <<
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;
10170 if (needed_fee > available_for_fee)
10172 LOG_PRINT_L2(
"We could not make a tx, switching to fee accumulation");
10179 while (needed_fee > test_ptx.
fee) {
10180 transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
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) <<
10195 tx.needed_fee = test_ptx.
fee;
10196 accumulated_fee += test_ptx.
fee;
10198 adding_fee =
false;
10201 LOG_PRINT_L2(
"We have more to pay, starting another tx");
10202 txes.push_back(TX());
10203 original_output_index = 0;
10211 if ((!dsts.empty() && dsts[0].amount > 0) || adding_fee)
10213 if (unused_transfers_indices->empty() && unused_transfers_indices_per_subaddr.size() > 1)
10215 unused_transfers_indices_per_subaddr.erase(unused_transfers_indices_per_subaddr.begin());
10216 unused_transfers_indices = &unused_transfers_indices_per_subaddr[0].second;
10218 if (unused_dust_indices->empty() && unused_dust_indices_per_subaddr.size() > 1)
10220 unused_dust_indices_per_subaddr.erase(unused_dust_indices_per_subaddr.begin());
10221 unused_dust_indices = &unused_dust_indices_per_subaddr[0].second;
10228 LOG_PRINT_L1(
"We ran out of outputs while trying to gather final fee");
10233 " total fee, " <<
print_etn(accumulated_change) <<
" total change");
10236 for (std::vector<TX>::iterator i = txes.begin(); i != txes.end(); ++i)
10240 test_tx.
version = tx_version;
10242 transfer_selected(tx.dsts,
10243 tx.selected_transfers,
10260 std::vector<wallet2::pending_tx> ptx_vector;
10261 for (std::vector<TX>::iterator i = txes.begin(); i != txes.end(); ++i)
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);
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");
10288 if (std::all_of(ptx_vector.begin(), ptx_vector.end(), [](
const pending_tx &ptx) {
return ptx.tx.version == 1; })) {
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;
10298 for (
const auto &ptx: ptx_vector) {
10299 for (
size_t idx: ptx.selected_transfers)
10300 change += m_transfers[idx].amount();
10303 for (
const auto &r: required)
10304 change -= r.second.first;
10309 if (ptx.change_dts.amount == 0)
10312 m_subaddresses.find(ptx.change_dts.addr.m_spend_public_key) == m_subaddresses.end(),
10314 required[ptx.change_dts.addr].first += ptx.change_dts.amount;
10315 required[ptx.change_dts.addr].second = ptx.change_dts.is_subaddress;
10319 for (
const auto &r: required) {
10325 for (
const auto &ptx: ptx_vector) {
10328 std::string proof = get_tx_proof(ptx.tx, ptx.tx_key, ptx.additional_tx_keys,
address,
10330 "automatic-sanity-check");
10333 catch (
const std::exception &e) { received = 0; }
10334 total_received += received;
10337 std::stringstream ss;
10338 ss <<
"Total received by " 10349 if (ptx.change_dts.amount == 0)
10352 m_subaddresses.find(ptx.change_dts.addr.m_spend_public_key) == m_subaddresses.end(),
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)
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;
10366 std::map<uint32_t, std::pair<std::vector<size_t>, std::vector<size_t>>> unused_transfer_dust_indices_per_subaddr;
10369 bool fund_found =
false;
10370 for (
size_t i = 0; i < m_transfers.size(); ++i)
10374 if((tx_version < 3 && td.m_tx.version > 1) || (tx_version >= 3 && td.
m_tx.
version == 1))
10380 if (below == 0 || td.
amount() < below)
10392 if (subaddr_indices.empty())
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);
10405 for (
const auto& p : unused_transfer_dust_indices_per_subaddr)
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);
10413 return create_transactions_from(
address, is_subaddress, outputs, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra, tx_version);
10418 std::vector<size_t> unused_transfers_indices;
10419 std::vector<size_t> unused_dust_indices;
10421 const bool use_rct = use_fork_rules(4, 0);
10423 for (
size_t i = 0; i < m_transfers.size(); ++i)
10430 unused_transfers_indices.push_back(i);
10432 unused_dust_indices.push_back(i);
10436 return create_transactions_from(
address, is_subaddress, outputs, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra, tx_version);
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)
10443 boost::unique_lock<hw::device> hwdev_lock (hwdev);
10446 uint64_t accumulated_fee, accumulated_outputs, accumulated_change;
10448 std::vector<size_t> selected_transfers;
10449 std::vector<cryptonote::tx_destination_entry> dsts;
10454 std::vector<std::vector<get_outs_entry>> outs;
10456 TX() : weight(0), needed_fee(0) {}
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();
10461 std::vector<std::vector<get_outs_entry>> outs;
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);
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();
10474 LOG_PRINT_L2(
"Starting with " << unused_transfers_indices.size() <<
" non-dust outputs and " << unused_dust_indices.size() <<
" dust outputs");
10476 if (unused_dust_indices.empty() && unused_transfers_indices.empty())
10477 return std::vector<wallet2::pending_tx>();
10480 txes.push_back(TX());
10481 accumulated_fee = 0;
10482 accumulated_outputs = 0;
10483 accumulated_change = 0;
10488 while (!unused_dust_indices.empty() || !unused_transfers_indices.empty()) {
10489 TX &tx = txes.back();
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);
10502 fee_dust_threshold = base_fee * fee_multiplier * (upper_transaction_weight_limit + 1023) / 1024;
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);
10518 tx.selected_transfers.push_back(idx);
10520 accumulated_outputs += available_amount;
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));
10533 test_tx.
version = tx_version;
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);
10539 for (
size_t i = 0; i < ((outputs > 1) ? outputs - 1 : outputs); ++i)
10542 LOG_PRINT_L2(
"Trying to create a tx now, with " << tx.dsts.size() <<
" destinations and " <<
10543 tx.selected_transfers.size() <<
" outputs");
10545 transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
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);
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 (" <<
10563 LOG_PRINT_L2(
"We made a tx, adjusting fee and saving it");
10565 uint64_t amount_transferred = available_for_fee - needed_fee;
10566 uint64_t dt_amount = amount_transferred / outputs;
10568 uint64_t residue = amount_transferred % outputs;
10569 for (
auto &dt: tx.dsts)
10577 dt.amount = dt_amount + dt_residue;
10579 transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
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) <<
10585 }
while (needed_fee > test_ptx.
fee);
10594 tx.needed_fee = test_ptx.
fee;
10595 accumulated_fee += test_ptx.
fee;
10597 if (!unused_transfers_indices.empty() || !unused_dust_indices.empty())
10599 LOG_PRINT_L2(
"We have more to pay, starting another tx");
10600 txes.push_back(TX());
10606 " total fee, " <<
print_etn(accumulated_change) <<
" total change");
10609 for (std::vector<TX>::iterator i = txes.begin(); i != txes.end(); ++i)
10613 test_tx.
version = tx_version;
10615 transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra,
10624 std::vector<wallet2::pending_tx> ptx_vector;
10625 for (std::vector<TX>::iterator i = txes.begin(); i != txes.end(); ++i)
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);
10639 for (
const TX &tx: txes)
10641 for (
size_t idx: tx.selected_transfers)
10643 a += m_transfers[idx].amount();
10655 void wallet2::cold_tx_aux_import(
const std::vector<pending_tx> & ptx,
const std::vector<std::string> & tx_device_aux)
10658 for (
size_t i = 0; i < ptx.size(); ++i){
10661 set_tx_device_aux(txid, tx_device_aux[i]);
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)
10667 auto & hwdev = get_account().get_device();
10669 throw std::invalid_argument(
"Device does not support cold sign protocol");
10673 for (
auto &tx: ptx_vector)
10675 txs.
txes.push_back(get_construction_data_with_decrypted_short_payment_id(tx, m_account.get_device()));
10677 txs.
transfers = std::make_pair(0, m_transfers);
10687 dev_cold->tx_sign(&
wallet_shim, txs, exported_txs, aux_data);
10690 MDEBUG(
"Signed tx data from hw: " << exported_txs.
ptx.size() <<
" transactions");
10695 auto & hwdev = get_account().get_device();
10701 std::vector<std::pair<crypto::key_image, crypto::signature>> ski;
10705 dev_cold->ki_sync(&
wallet_shim, m_transfers, ski);
10708 uint64_t import_res = import_key_images(ski, 0, spent, unspent, is_trusted_daemon());
10709 m_device_last_key_image_sync =
time(NULL);
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");
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");
10736 return close_enough;
10739 uint64_t wallet2::get_upper_transaction_weight_limit()
const 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) ?
10749 if (use_fork_rules(8, 10))
10755 std::vector<size_t> wallet2::select_available_outputs(
const std::function<
bool(
const transfer_details &td)> &f)
const 10757 std::vector<size_t> outputs;
10759 for (transfer_container::const_iterator i = m_transfers.begin(); i != m_transfers.end(); ++i, ++n)
10765 if (i->m_key_image_partial)
10767 if (!is_transfer_unlocked(*i))
10770 outputs.push_back(n);
10775 std::vector<uint64_t> wallet2::get_unspent_amounts_vector()
const 10777 std::set<uint64_t>
set;
10778 for (
const auto &td: m_transfers)
10780 if (!td.m_spent && !td.m_frozen)
10781 set.insert(td.is_rct() ? 0 : td.amount());
10783 std::vector<uint64_t> vector;
10784 vector.reserve(
set.size());
10785 for (
const auto &i:
set)
10787 vector.push_back(i);
10792 std::vector<size_t> wallet2::select_available_outputs_from_histogram(
uint64_t count,
bool atleast,
bool unlocked,
bool allow_rct)
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();
10809 std::set<uint64_t> mixable;
10810 for (
const auto &i: resp_t.histogram)
10812 mixable.insert(i.amount);
10815 return select_available_outputs([mixable, atleast, allow_rct](
const transfer_details &td) {
10816 if (!allow_rct && td.
is_rct())
10820 if (mixable.find(amount) != mixable.end())
10824 if (mixable.find(amount) == mixable.end())
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();
10849 return resp_t.histogram[0].total_instances;
10855 return m_transfers[idx];
10858 std::vector<size_t> wallet2::select_available_unmixable_outputs()
10861 return select_available_outputs_from_histogram(get_min_ring_size(),
false,
true,
false);
10864 std::vector<size_t> wallet2::select_available_mixable_outputs()
10867 return select_available_outputs_from_histogram(get_min_ring_size(),
true,
true,
true);
10870 std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions()
10873 const bool hf1_rules = use_fork_rules(2, 10);
10876 uint8_t tx_version = this->public_transactions_required() ? 3 : 1;
10877 const uint64_t base_fee = get_base_fee();
10880 std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs();
10881 size_t num_dust_outputs = unmixable_outputs.size();
10883 if (num_dust_outputs == 0)
10885 return std::vector<wallet2::pending_tx>();
10889 std::vector<size_t> unmixable_transfer_outputs, unmixable_dust_outputs;
10890 for (
auto n: unmixable_outputs)
10892 if (m_transfers[n].amount() < base_fee)
10893 unmixable_dust_outputs.push_back(n);
10895 unmixable_transfer_outputs.push_back(n);
10898 return create_transactions_from(m_account_public_address,
false, 1, unmixable_transfer_outputs, unmixable_dust_outputs, 0 , 0 , 1 , std::vector<uint8_t>(), tx_version);
10901 void wallet2::discard_unmixable_outputs()
10904 std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs();
10905 for (
size_t idx : unmixable_outputs)
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())
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;
10926 bool r = get_tx_key_cached(txid, tx_key, additional_tx_keys);
10932 auto & hwdev = get_account().get_device();
10940 const auto tx_data_it = m_tx_device.find(txid);
10941 if (tx_data_it == m_tx_device.end())
10943 MDEBUG(
"Aux data not found for txid: " << txid);
10949 if (!dev_cold->is_get_tx_key_supported())
10951 MDEBUG(
"get_tx_key not supported by the device");
10956 dev_cold->load_tx_key_data(tx_key_data, tx_data_it->second);
10964 req.decode_as_json =
false;
10966 m_daemon_rpc_mutex.lock();
10968 m_daemon_rpc_mutex.unlock();
10981 "Failed to get the right transaction from daemon");
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())
10993 tx_key = tx_keys[0];
10994 tx_keys.erase(tx_keys.begin());
10995 additional_tx_keys = tx_keys;
11005 req.decode_as_json =
false;
11010 const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
11017 "daemon returned wrong response for gettransactions, wrong txs count = " +
11022 "Failed to get transaction from daemon");
11024 std::vector<tx_extra_field> tx_extra_fields;
11027 bool found =
false;
11033 if (calculated_pub_key == pub_key_field.
pub_key)
11043 m_tx_keys.insert(std::make_pair(txid, tx_key));
11044 m_additional_tx_keys.insert(std::make_pair(txid, additional_tx_keys));
11050 "get_spend_proof requires spend secret key and is not available for a watch-only wallet");
11055 req.decode_as_json =
false;
11060 const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
11067 "daemon returned wrong response for gettransactions, wrong txs count = " +
11074 std::vector<std::vector<crypto::signature>> signatures;
11082 for(
size_t i = 0; i < tx.
vin.size(); ++i)
11084 const txin_to_key*
const in_key = boost::get<txin_to_key>(std::addressof(tx.
vin[i]));
11085 if (in_key ==
nullptr)
11089 const auto found = m_key_images.find(in_key->
k_image);
11090 if(found == m_key_images.end())
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),
11110 const size_t ring_size = in_key->
key_offsets.size();
11113 req.outputs.resize(ring_size);
11114 for (
size_t j = 0; j < ring_size; ++j)
11116 req.outputs[j].amount = in_key->
amount;
11117 req.outputs[j].index = absolute_offsets[j];
11122 const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
11129 "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
11133 std::vector<const crypto::public_key *> p_output_keys;
11135 p_output_keys.push_back(&out.key);
11138 size_t sec_index = -1;
11139 for (
size_t j = 0; j < ring_size; ++j)
11141 if (
res.outs[j].key == in_ephemeral.
pub)
11150 signatures.push_back(std::vector<crypto::signature>());
11151 std::vector<crypto::signature>& sigs = signatures.back();
11157 for (
const std::vector<crypto::signature>& ring_sig : signatures)
11166 const size_t header_len = header.size();
11168 "Signature header check error");
11173 req.decode_as_json =
false;
11178 const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
11185 "daemon returned wrong response for gettransactions, wrong txs count = " +
11193 size_t num_sigs = 0;
11194 for(
size_t i = 0; i < tx.
vin.size(); ++i)
11196 const txin_to_key*
const in_key = boost::get<txin_to_key>(std::addressof(tx.
vin[i]));
11197 if (in_key !=
nullptr)
11200 std::vector<std::vector<crypto::signature>> signatures = { std::vector<crypto::signature>(1) };
11202 if( sig_str.size() != header_len + num_sigs * sig_len ) {
11207 signatures.clear();
11208 size_t offset = header_len;
11209 for(
size_t i = 0; i < tx.
vin.size(); ++i)
11211 const txin_to_key*
const in_key = boost::get<txin_to_key>(std::addressof(tx.
vin[i]));
11212 if (in_key ==
nullptr)
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)
11232 std::vector<std::vector<crypto::signature>>::const_iterator sig_iter = signatures.cbegin();
11233 for(
size_t i = 0; i < tx.
vin.size(); ++i)
11235 const txin_to_key*
const in_key = boost::get<txin_to_key>(std::addressof(tx.
vin[i]));
11236 if (in_key ==
nullptr)
11242 req.outputs.resize(absolute_offsets.size());
11243 for (
size_t j = 0; j < absolute_offsets.size(); ++j)
11245 req.outputs[j].amount = in_key->
amount;
11246 req.outputs[j].index = absolute_offsets[j];
11251 const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
11258 "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
11262 std::vector<const crypto::public_key *> p_output_keys;
11264 p_output_keys.push_back(&out.key);
11280 "Failed to generate key derivation from supplied parameters");
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)
11286 "Failed to generate key derivation from supplied parameters");
11288 check_tx_key_helper(txid, derivation, additional_derivations,
address, received, in_pool, confirmations);
11295 for (
size_t n = 0; n < tx.
vout.size(); ++n)
11304 bool found = out_key->
key == derived_out_key;
11306 if (!found && !additional_derivations.empty())
11310 found = out_key->
key == derived_out_key;
11311 found_derivation = additional_derivations[n];
11317 amount = tx.
vout[n].amount;
11318 received += amount;
11328 req.decode_as_json =
false;
11330 m_daemon_rpc_mutex.lock();
11332 m_daemon_rpc_mutex.unlock();
11338 if (
res.txs.size() == 1)
11340 ok = get_pruned_tx(
res.txs.front(), tx, tx_hash);
11354 "Failed to get the right transaction from daemon");
11356 "The size of additional derivations is wrong");
11358 check_tx_key_helper(tx, derivation, additional_derivations,
address, received);
11360 in_pool =
res.txs.front().in_pool;
11365 uint64_t bc_height = get_daemon_blockchain_height(err);
11367 confirmations = bc_height -
res.txs.front().block_height;
11377 req.decode_as_json =
false;
11379 m_daemon_rpc_mutex.lock();
11381 m_daemon_rpc_mutex.unlock();
11387 if (
res.txs.size() == 1)
11389 ok = get_pruned_tx(
res.txs.front(), tx, tx_hash);
11407 std::vector<crypto::secret_key> additional_tx_keys;
11408 const bool is_out = m_subaddresses.count(
address.m_spend_public_key) == 0;
11414 return get_tx_proof(tx, tx_key, additional_tx_keys,
address, is_subaddress,
message);
11422 const bool is_out = m_subaddresses.count(
address.m_spend_public_key) == 0;
11430 std::vector<crypto::public_key> shared_secret;
11431 std::vector<crypto::signature> sig;
11435 const size_t num_sigs = 1 + additional_tx_keys.size();
11436 shared_secret.resize(num_sigs);
11437 sig.resize(num_sigs);
11440 shared_secret[0] = rct::rct2pk(aP);
11445 tx_pub_key = rct2pk(aP);
11451 hwdev.
generate_tx_proof(prefix_hash, tx_pub_key,
address.m_view_public_key, boost::none, shared_secret[0], tx_key, sig[0]);
11453 for (
size_t i = 1; i < num_sigs; ++i)
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);
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]);
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]);
11477 const size_t num_sigs = 1 + additional_tx_pub_keys.size();
11478 shared_secret.resize(num_sigs);
11479 sig.resize(num_sigs);
11482 hwdev.
scalarmultKey(aP, rct::pk2rct(tx_pub_key), rct::sk2rct(
a));
11483 shared_secret[0] = rct2pk(aP);
11492 for (
size_t i = 1; i < num_sigs; ++i)
11494 hwdev.
scalarmultKey(aP,rct::pk2rct(additional_tx_pub_keys[i - 1]), rct::sk2rct(
a));
11495 shared_secret[i] = rct2pk(aP);
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]);
11507 const size_t num_sigs = shared_secret.size();
11512 std::vector<crypto::key_derivation> additional_derivations(num_sigs - 1);
11513 for (
size_t i = 1; i < num_sigs; ++i)
11516 check_tx_key_helper(tx, derivation, additional_derivations,
address, received);
11520 for (
size_t i = 0; i < num_sigs; ++i)
11533 req.decode_as_json =
false;
11535 m_daemon_rpc_mutex.lock();
11537 m_daemon_rpc_mutex.unlock();
11543 if (
res.txs.size() == 1)
11545 ok = get_pruned_tx(
res.txs.front(), tx, tx_hash);
11563 in_pool =
res.txs.front().in_pool;
11568 uint64_t bc_height = get_daemon_blockchain_height(err);
11570 confirmations = bc_height -
res.txs.front().block_height;
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();
11583 "Signature header check error");
11586 std::vector<crypto::public_key> shared_secret(1);
11587 std::vector<crypto::signature> sig(1);
11590 const size_t num_sigs = (sig_str.size() - header_len) / (pk_len + sig_len);
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)
11599 const size_t offset = header_len + i * (pk_len + sig_len);
11601 "Signature decoding error");
11603 "Signature decoding error");
11605 "Signature decoding error");
11623 std::vector<int> good_signature(num_sigs, 0);
11626 good_signature[0] = is_subaddress ?
11630 for (
size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
11632 good_signature[i + 1] = is_subaddress ?
11639 good_signature[0] = is_subaddress ?
11643 for (
size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
11645 good_signature[i + 1] = is_subaddress ?
11651 if (std::any_of(good_signature.begin(), good_signature.end(), [](
int i) {
return i > 0; }))
11655 if (good_signature[0])
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])
11663 check_tx_key_helper(tx, derivation, additional_derivations,
address, received);
11674 "Not enough balance in this account for the requested minimum reserve amount");
11677 std::vector<size_t> selected_transfers;
11678 for (
size_t i = 0; i < m_transfers.size(); ++i)
11682 selected_transfers.push_back(i);
11685 if (account_minreserve)
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());
11695 while (total < account_minreserve->second)
11697 total += m_transfers[selected_transfers[sz]].amount();
11700 selected_transfers.resize(sz);
11706 for (
size_t i = 0; i < selected_transfers.size(); ++i)
11708 prefix_data.append((
const char*)&m_transfers[selected_transfers[i]].m_key_image,
sizeof(
crypto::key_image));
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)
11734 for (
int i = 0; i < 2; ++i)
11743 if (m_subaddresses.count(subaddress_spendkey) == 1)
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];
11763 const std::vector<const crypto::public_key*> pubs = { &ephemeral.
pub };
11768 std::unordered_map<crypto::public_key, crypto::signature> subaddr_spendkeys;
11772 if (!index.is_zero())
11774 crypto::secret_key m = m_account.get_device().get_subaddress_secret_key(m_account.get_keys().m_view_secret_key, index);
11776 sc_add((
unsigned char*)&subaddr_spend_skey, (
unsigned char*)&m, (
unsigned char*)&tmp);
11784 std::ostringstream oss;
11786 ar << proofs << subaddr_spendkeys;
11796 static constexpr
char header[] =
"ReserveProofV1";
11798 "Signature header check error");
11802 "Signature decoding error");
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;
11811 "The given address isn't found in the proof");
11816 for (
size_t i = 0; i < proofs.size(); ++i)
11826 for (
size_t i = 0; i < proofs.size(); ++i)
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();
11839 for (
size_t i = 0; i < proofs.size(); ++i)
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();
11848 for (
size_t i = 0; i < proofs.size(); ++i)
11855 ok = get_pruned_tx(gettx_res.txs[i], tx, tx_hash);
11872 if (!ok && additional_tx_pub_keys.size() == tx.
vout.size())
11878 const std::vector<const crypto::public_key*> pubs = { &out_key->key };
11889 "The address doesn't seem to have received the fund");
11903 if (kispent_res.spent_status[i])
11908 for (
const auto &i : subaddr_spendkeys)
11918 return m_wallet_file;
11923 return m_keys_file;
11928 return m_daemon_address;
11931 uint64_t wallet2::get_daemon_blockchain_height(
string &err)
const 11935 boost::optional<std::string> result = m_node_rpc_proxy.get_height(
height);
11938 if (m_trusted_daemon)
11941 err =
"daemon error";
11949 uint64_t wallet2::get_daemon_blockchain_target_height(
string &err)
11953 const auto result = m_node_rpc_proxy.get_target_height(target_height);
11956 if (m_trusted_daemon)
11959 err =
"daemon error";
11962 return target_height;
11965 uint64_t wallet2::get_approximate_blockchain_height()
const 11968 const time_t fork_time = m_nettype ==
TESTNET ? 1341378000 : m_nettype ==
STAGENET ? 1521000000 : 1538815057;
11974 uint64_t approx_blockchain_height = fork_block + (
time(NULL) - fork_time)/seconds_per_block;
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;
11981 approx_blockchain_height += 82000;
11983 LOG_PRINT_L2(
"Calculated blockchain height: " << approx_blockchain_height);
11984 return approx_blockchain_height;
11989 m_tx_notes[txid] = note;
11994 std::unordered_map<crypto::hash, std::string>::const_iterator i = m_tx_notes.find(txid);
11995 if (i == m_tx_notes.end())
12002 m_tx_device[txid] = aux;
12007 std::unordered_map<crypto::hash, std::string>::const_iterator i = m_tx_device.find(txid);
12008 if (i == m_tx_device.end())
12020 std::unordered_map<std::string, std::string>::const_iterator i = m_attributes.find(
key);
12021 if (i == m_attributes.end())
12028 set_attribute(ATTRIBUTE_DESCRIPTION, description);
12033 return get_attribute(ATTRIBUTE_DESCRIPTION);
12036 const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& wallet2::get_account_tags()
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)
12043 if (!tag.empty() && m_account_tags.first.count(tag) == 0)
12044 m_account_tags.first.insert({tag,
""});
12046 for (
auto i = m_account_tags.first.begin(); i != m_account_tags.first.end(); )
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);
12053 return m_account_tags;
12056 void wallet2::set_account_tag(
const std::set<uint32_t> &account_indices,
const std::string& tag)
12058 for (
uint32_t account_index : account_indices)
12061 if (m_account_tags.second[account_index] == tag)
12062 MDEBUG(
"This tag is already assigned to this account");
12064 m_account_tags.second[account_index] = tag;
12066 get_account_tags();
12073 m_account_tags.first[tag] = description;
12088 const size_t header_len = strlen(
"SigV1");
12101 if (
sizeof(s) != decoded.size()) {
12105 memcpy(&s, decoded.data(),
sizeof(s));
12123 if (
signature.size() < MULTISIG_SIGNATURE_MAGIC.size() ||
signature.substr(0, MULTISIG_SIGNATURE_MAGIC.size()) != MULTISIG_SIGNATURE_MAGIC) {
12124 MERROR(
"Signature header check error");
12131 MERROR(
"Signature decoding error");
12135 if (
sizeof(s) != decoded.size()) {
12136 MERROR(
"Signature decoding error");
12139 memcpy(&s, decoded.data(),
sizeof(s));
12145 std::vector<tx_extra_field> tx_extra_fields;
12158 "Public key wasn't found in the transaction extra");
12168 size_t pk_index = 0;
12177 for (
size_t i = 0; i < td.
m_tx.
vout.size(); ++i)
12180 check_acc_out_precomp(td.
m_tx.
vout[i], derivation, {}, i, tx_scan_info);
12194 std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> ski = export_key_images();
12197 const uint32_t offset = ski.first;
12202 data[0] = offset & 0xff;
12203 data[1] = (offset >> 8) & 0xff;
12204 data[2] = (offset >> 16) & 0xff;
12205 data[3] = (offset >> 24) & 0xff;
12208 for (
const auto &i: ski.second)
12216 std::string ciphertext = encrypt_with_view_secret_key(data);
12221 std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> wallet2::export_key_images(
bool all)
const 12224 std::vector<std::pair<crypto::key_image, crypto::signature>> ski;
12229 while (offset < m_transfers.size() && !m_transfers[offset].m_key_image_request)
12233 ski.reserve(m_transfers.size() - offset);
12234 for (
size_t n = offset; n < m_transfers.size(); ++n)
12241 "Output is not txout_to_key");
12246 std::vector<tx_extra_field> tx_extra_fields;
12268 std::vector<const crypto::public_key*> key_ptrs;
12269 key_ptrs.push_back(&pkey);
12275 return std::make_pair(offset, ski);
12295 data = decrypt_with_view_secret_key(
std::string(data, magiclen));
12297 catch (
const std::exception &e)
12317 size_t nki = (data.size() - headerlen) / record_size;
12319 std::vector<std::pair<crypto::key_image, crypto::signature>> ski;
12321 for (
size_t n = 0; n < nki; ++n)
12329 return import_key_images(ski, offset, spent, unspent);
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)
12341 "The blockchain is out of date compared to the signed key images");
12343 if (signed_key_images.empty() && offset == 0)
12350 req.key_images.reserve(signed_key_images.size());
12353 for (
size_t n = 0; n < signed_key_images.size(); ++n)
12362 "Non txout_to_key output found");
12368 std::vector<const crypto::public_key*> pkeys;
12369 pkeys.push_back(&pkey);
12384 for (
size_t n = 0; n < signed_key_images.size(); ++n)
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;
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();
12404 "daemon returned wrong response for is_key_image_spent, wrong amounts count = " +
12406 for (
size_t n = 0; n < daemon_resp.spent_status.size(); ++n)
12414 std::unordered_set<crypto::hash> spent_txids;
12415 std::vector<size_t> swept_transfers;
12417 std::unordered_map<crypto::key_image, crypto::hash> spent_key_images;
12425 spent_key_images.insert(std::make_pair(boost::get<cryptonote::txin_to_key>(in).k_image, td.m_txid));
12431 for(
size_t i = 0; i < offset; ++i)
12444 for(
size_t i = 0; i < signed_key_images.size(); ++i)
12455 << (td.
m_spent ?
"spent" :
"unspent") <<
" (key image " << req.key_images[i] <<
")");
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);
12463 spent_txids.insert(skii->second);
12475 gettxs_req.decode_as_json =
false;
12476 gettxs_req.prune =
true;
12477 gettxs_req.txs_hashes.reserve(spent_txids.size());
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();
12489 "daemon returned wrong response for gettransactions, wrong count = " +
std::to_string(gettxs_res.txs.size()) +
", expected " +
std::to_string(spent_txids.size()));
12494 auto spent_txid = spent_txids.begin();
12496 auto it = spent_txids.begin();
12515 std::vector<crypto::key_derivation> additional_derivations;
12516 for (
size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
12518 additional_derivations.push_back({});
12527 check_acc_out_precomp(out, derivation, additional_derivations,
output_index, tx_scan_info);
12546 std::set<uint32_t> subaddr_indices;
12551 auto it = m_key_images.find(boost::get<cryptonote::txin_to_key>(in).k_image);
12552 if (it != m_key_images.end())
12556 uint64_t amount = boost::get<cryptonote::txin_to_key>(in).amount;
12564 tx_etn_spent_in_ins += amount;
12571 LOG_PRINT_L0(
"WARNING: This tx spends outputs received by different subaddress accounts, which isn't supposed to happen");
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);
12581 for (
auto j = m_payments.begin(); j != m_payments.end(); ++j)
12583 if (j->second.m_tx_hash == *spent_txid)
12585 m_payments.erase(j);
12595 for (
size_t n : swept_transfers)
12605 bool is_portal_address;
12614 m_confirmed_txs.insert(std::make_pair(spent_txid, pd));
12620 return m_transfers[signed_key_images.size() + offset - 1].m_block_height;
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)
12625 if (key_images.size() + offset > m_transfers.size())
12627 LOG_PRINT_L1(
"More key images returned that we know outputs for");
12630 for (
size_t ki_idx = 0; ki_idx < key_images.size(); ++ki_idx)
12632 const size_t transfer_idx = ki_idx + offset;
12633 if (selected_transfers && selected_transfers.get().find(transfer_idx) == selected_transfers.get().end())
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;
12650 bool wallet2::import_key_images(
signed_tx_set & signed_tx,
size_t offset,
bool only_selected_transfers)
12652 std::unordered_set<size_t> selected_transfers;
12653 if (only_selected_transfers)
12658 selected_transfers.insert(s);
12662 return import_key_images(signed_tx.
key_images, offset, only_selected_transfers ? boost::make_optional(selected_transfers) : boost::none);
12668 for (
auto const &p : m_payments)
12670 payments.emplace(p);
12676 m_payments.clear();
12677 for (
auto const &p : payments)
12679 m_payments.emplace(p);
12682 void wallet2::import_payments_out(
const std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>> &confirmed_payments)
12684 m_confirmed_txs.clear();
12685 for (
auto const &p : confirmed_payments)
12687 m_confirmed_txs.emplace(p);
12691 std::tuple<size_t,crypto::hash,std::vector<crypto::hash>> wallet2::export_blockchain()
const 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)
12698 std::get<2>(bc).push_back(m_blockchain[n]);
12703 void wallet2::import_blockchain(
const std::tuple<
size_t,
crypto::hash, std::vector<crypto::hash>> &bc)
12705 m_blockchain.clear();
12706 if (std::get<0>(bc))
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));
12712 for (
auto const &b : std::get<2>(bc))
12714 m_blockchain.push_back(b);
12717 generate_genesis(genesis);
12719 check_genesis(genesis_hash);
12723 std::pair<size_t, std::vector<tools::wallet2::transfer_details>> wallet2::export_outputs(
bool all)
const 12726 std::vector<tools::wallet2::transfer_details> outs;
12730 while (offset < m_transfers.size() && (m_transfers[offset].m_key_image_known && !m_transfers[offset].m_key_image_request))
12733 outs.reserve(m_transfers.size() - offset);
12734 for (
size_t n = offset; n < m_transfers.size(); ++n)
12738 outs.push_back(td);
12741 return std::make_pair(offset, outs);
12748 std::stringstream oss;
12750 const auto& outputs = export_outputs(all);
12759 std::string ciphertext = encrypt_with_view_secret_key(header + oss.str());
12760 return magic + ciphertext;
12763 size_t wallet2::import_outputs(
const std::pair<
size_t, std::vector<tools::wallet2::transfer_details>> &outputs)
12768 "Imported outputs omit more outputs that we know of");
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)
12780 if (i + offset < original_size)
12786 #define CMPF(f) if (!(td.f == org_td.f)) goto process 12789 CMPF(m_internal_output_index);
12795 m_transfers[i + offset] =
std::move(td);
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));
12822 m_transfers[i + offset] =
std::move(td);
12825 return m_transfers.size();
12841 data = decrypt_with_view_secret_key(
std::string(data, magiclen));
12843 catch (
const std::exception &e)
12849 if (data.size() < headerlen)
12861 size_t imported_outputs = 0;
12865 std::stringstream iss;
12867 std::pair<size_t, std::vector<tools::wallet2::transfer_details>> outputs;
12877 boost::archive::binary_iarchive ar(iss);
12881 imported_outputs = import_outputs(outputs);
12883 catch (
const std::exception &e)
12888 return imported_outputs;
12918 return get_multisig_signing_public_key(get_account().get_multisig_keys()[idx]);
12921 rct::key wallet2::get_multisig_k(
size_t idx,
const std::unordered_set<rct::key> &used_L)
const 12925 for (
const auto &k: m_transfers[idx].m_multisig_k)
12929 if (used_L.find(L) != used_L.end())
12942 kLRki.ki = rct::ki2rct(m_transfers[n].m_key_image);
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 12950 const transfer_details &td = m_transfers[n];
12954 size_t n_signers_used = 1;
12955 for (
const auto &p: m_transfers[n].m_multisig_info)
12957 if (ignore_set.find(p.m_signer) != ignore_set.end())
12960 for (
const auto &lr: p.m_LR)
12962 if (used_L.find(lr.m_L) != used_L.end())
12964 used_L.insert(lr.m_L);
12965 new_used_L.insert(lr.m_L);
12981 const transfer_details &td = m_transfers[n];
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);
12996 std::vector<tools::wallet2::multisig_info>
info;
13000 info.resize(m_transfers.size());
13001 for (
size_t n = 0; n < m_transfers.size(); ++n)
13006 info[n].m_LR.clear();
13007 info[n].m_partial_key_images.clear();
13009 for (
size_t m = 0; m < get_account().get_multisig_keys().size(); ++m)
13014 info[n].m_partial_key_images.push_back(ki);
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)
13025 info[n].m_LR.push_back({kLRki.
L, kLRki.
R});
13028 info[n].m_signer = signer;
13031 std::stringstream oss;
13040 std::string ciphertext = encrypt_with_view_secret_key(header + oss.str());
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)
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)
13056 td.m_multisig_info.push_back(pi[n]);
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;
13067 size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs)
13071 std::vector<std::vector<tools::wallet2::multisig_info>>
info;
13072 std::unordered_set<crypto::public_key> seen;
13079 data = decrypt_with_view_secret_key(
std::string(data, magiclen));
13090 if (get_multisig_signer_public_key() == signer)
13092 MINFO(
"Multisig info from this wallet ignored");
13095 if (seen.find(signer) != seen.end())
13097 MINFO(
"Duplicate multisig info ignored");
13100 seen.insert(signer);
13103 std::istringstream iss(body);
13104 std::vector<tools::wallet2::multisig_info> i;
13107 MINFO(boost::format(
"%u outputs found") % boost::lexical_cast<std::string>(i.size()));
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);
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();
13124 if (n_outputs == 0)
13128 for (
const auto &pi:
info)
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)
13137 for (
auto &pi:
info)
13138 pi.resize(n_outputs);
13141 if (!
info.empty() && !
info.front().empty())
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)); });
13147 for (
size_t n = 0; n < n_outputs; ++n)
13157 for (
size_t n = 0; n < n_outputs && n < m_transfers.size(); ++n)
13159 update_multisig_rescan_info(k,
info, n);
13162 m_multisig_rescan_k = &k;
13163 m_multisig_rescan_info = &
info;
13171 m_multisig_rescan_info = NULL;
13172 m_multisig_rescan_k = NULL;
13175 m_multisig_rescan_info = NULL;
13176 m_multisig_rescan_k = NULL;
13183 crypto::chacha_key
key;
13184 crypto::generate_chacha_key(&skey,
sizeof(skey),
key, m_kdf_rounds);
13186 crypto::chacha_iv iv = crypto::rand<crypto::chacha_iv>();
13187 ciphertext.resize(len +
sizeof(iv) + (authenticated ?
sizeof(
crypto::signature) : 0));
13189 memcpy(&ciphertext[0], &iv,
sizeof(iv));
13204 return encrypt(plaintext.
data(), plaintext.
size(), skey, authenticated);
13209 return encrypt(plaintext.data(), plaintext.size(), skey, authenticated);
13214 return encrypt(plaintext.
data(), plaintext.
size(), skey, authenticated);
13219 return encrypt(plaintext, get_account().get_keys().m_view_secret_key, authenticated);
13222 template<
typename T>
13225 const size_t prefix_size =
sizeof(chacha_iv) + (authenticated ?
sizeof(
crypto::signature) : 0);
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];
13242 std::unique_ptr<char[]> buffer{
new char[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);
13252 return decrypt(ciphertext, get_account().get_keys().m_view_secret_key, authenticated);
13265 if (
info.has_payment_id && !payment_id.empty())
13267 error =
"A single payment id is allowed";
13271 if (!payment_id.empty())
13275 if (!wallet2::parse_long_payment_id(payment_id, pid32) && !parse_short_payment_id(payment_id, pid8))
13277 error =
"Invalid payment id";
13283 unsigned int n_fields = 0;
13285 if (!payment_id.empty())
13287 uri += (n_fields++ ?
"&" :
"?") +
std::string(
"tx_payment_id=") + payment_id;
13296 if (!recipient_name.empty())
13298 uri += (n_fields++ ?
"&" :
"?") +
std::string(
"recipient_name=") + epee::net_utils::conver_to_url_format(recipient_name);
13301 if (!tx_description.empty())
13303 uri += (n_fields++ ?
"&" :
"?") +
std::string(
"tx_description=") + epee::net_utils::conver_to_url_format(tx_description);
13311 if (uri.substr(0, 12) !=
"electroneum:")
13313 error =
std::string(
"URI has wrong scheme (expected \"electroneum:\"): ") + uri;
13318 const char *ptr = strchr(remainder.c_str(),
'?');
13319 address = ptr ? remainder.substr(0, ptr-remainder.c_str()) : remainder;
13327 if (!strchr(remainder.c_str(),
'?'))
13330 std::vector<std::string> arguments;
13334 boost::split(arguments, body, boost::is_any_of(
"&"));
13335 std::set<std::string> have_arg;
13336 for (
const auto &arg: arguments)
13338 std::vector<std::string> kv;
13339 boost::split(kv, arg, boost::is_any_of(
"="));
13340 if (kv.size() != 2)
13345 if (have_arg.find(kv[0]) != have_arg.end())
13350 have_arg.insert(kv[0]);
13352 if (kv[0] ==
"tx_amount")
13361 else if (kv[0] ==
"tx_payment_id")
13363 if (
info.has_payment_id)
13365 error =
"Separate payment id given with an integrated address";
13370 if (!wallet2::parse_long_payment_id(kv[1],
hash) && !wallet2::parse_short_payment_id(kv[1],
hash8))
13372 error =
"Invalid payment id: " + kv[1];
13375 payment_id = kv[1];
13377 else if (kv[0] ==
"recipient_name")
13379 recipient_name = epee::net_utils::convert_from_url_format(kv[1]);
13381 else if (kv[0] ==
"tx_description")
13383 tx_description = epee::net_utils::convert_from_url_format(kv[1]);
13387 unknown_parameters.push_back(arg);
13396 if (!check_connection(&
version))
13398 throw std::runtime_error(
"failed to connect to daemon: " + get_daemon_address());
13402 throw std::runtime_error(
"this function requires RPC version 1.6 or higher");
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)
13410 throw std::runtime_error(
"month or day out of range");
13412 uint64_t timestamp_target = std::mktime(&date);
13415 uint64_t height_max = get_daemon_blockchain_height(err) - 1;
13418 throw std::runtime_error(
"failed to get blockchain height");
13424 uint64_t height_mid = (height_min + height_max) / 2;
13434 std::ostringstream oss;
13435 oss <<
"failed to get blocks by heights: ";
13436 for (
auto height : req.heights)
13438 oss << endl <<
"reason: ";
13440 oss <<
"possibly lost connection to daemon";
13442 oss <<
"daemon is busy";
13444 oss << get_rpc_status(
res.status);
13445 throw std::runtime_error(oss.str());
13448 if (
res.blocks.size() < 3)
throw std::runtime_error(
"Not enough blocks returned from daemon");
13455 if (!(timestamp_min <= timestamp_mid && timestamp_mid <= timestamp_max))
13459 return std::min({height_min, height_mid, height_max});
13461 if (timestamp_target > timestamp_max)
13463 throw std::runtime_error(
"specified date is in the future");
13465 if (timestamp_target <= timestamp_min + 2 * 24 * 60 * 60)
13469 if (timestamp_target <= timestamp_mid)
13470 height_max = height_mid;
13472 height_min = height_mid;
13473 if (height_max - height_min <= 2 * 24 * 30)
13480 bool wallet2::is_synced()
const 13483 boost::optional<std::string> result = m_node_rpc_proxy.get_target_height(
height);
13486 return get_blockchain_current_height() >=
height;
13489 std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(
const std::vector<std::pair<double, double>> &fee_levels)
13491 for (
const auto &fee_level: fee_levels)
13500 m_daemon_rpc_mutex.lock();
13502 m_daemon_rpc_mutex.unlock();
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;
13513 std::vector<std::pair<uint64_t, uint64_t>>
blocks;
13514 for (
const auto &fee_level: fee_levels)
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)
13523 MWARNING(
"Got 0 weight tx from txpool, ignored");
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;
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));
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)
13551 std::vector<std::pair<double, double>> fee_levels;
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);
13557 return estimate_backlog(fee_levels);
13560 uint64_t wallet2::get_segregation_fork_height()
const 13568 if (m_segregation_height > 0)
13569 return m_segregation_height;
13571 if (m_use_dns && !m_offline)
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" 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;
13586 for (
const auto& record : records)
13588 std::vector<std::string> fields;
13589 boost::split(fields, record, boost::is_any_of(
":"));
13590 if (fields.size() != 2)
13596 MINFO(
"Found segregation height via DNS: " << fields[0] <<
" fork height at " <<
height);
13598 if (diff < best_diff)
13605 return best_height;
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)
13626 state.address = m_original_address;
13627 state.view_secret_key = m_original_view_secret_key;
13631 state.address = m_account.get_keys().m_account_address;
13632 state.view_secret_key = m_account.get_keys().m_view_secret_key;
13634 state.mms_file=m_mms_file;
13640 if (!m_device_callback){
13643 return m_device_callback.get();
13645 void wallet2::on_device_button_request(
uint64_t code)
13647 if (
nullptr != m_callback)
13648 m_callback->on_device_button_request(code);
13651 void wallet2::on_device_button_pressed()
13653 if (
nullptr != m_callback)
13654 m_callback->on_device_button_pressed();
13657 boost::optional<epee::wipeable_string> wallet2::on_device_pin_request()
13659 if (
nullptr != m_callback)
13660 return m_callback->on_device_pin_request();
13661 return boost::none;
13664 boost::optional<epee::wipeable_string> wallet2::on_device_passphrase_request(
bool on_device)
13666 if (
nullptr != m_callback)
13667 return m_callback->on_device_passphrase_request(on_device);
13668 return boost::none;
13673 if (
nullptr != m_callback)
13674 m_callback->on_device_progress(
event);
13679 if (m_trusted_daemon)
13684 void wallet2::throw_on_rpc_response_error(
const boost::optional<std::string> &status,
const char *method)
const 13690 MERROR(
"RPC error: " << method <<
": status " << *status);
13720 if (transfer_height >= 0 && current_height >= (
uint64_t)transfer_height){
13724 hash_m_transfer(transfer, tmp_hash);
13727 current_height += 1;
13731 return current_height;
13738 hash_m_transfers((
int64_t) transfer_height, new_transfers_hash);
13740 if (new_transfers_hash !=
hash)
13748 for(
auto it = m_key_images.begin(); it != m_key_images.end(); it++)
13751 m_transfers[it->second].m_key_image = it->first;
13752 m_transfers[it->second].m_key_image_known =
true;
13758 return m_http_client.get_bytes_sent();
13763 return m_http_client.get_bytes_received();
void generate_multisig_LR(const crypto::public_key pkey, const crypto::secret_key &k, crypto::public_key &L, crypto::public_key &R)
static void init_options(boost::program_options::options_description &desc_params)
virtual bool has_ki_cold_sync(void) const
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)
#define CRYPTONOTE_MINED_ETN_UNLOCK_WINDOW
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)
bool generate_key_image_helper_precomp(const account_keys &ack, const crypto::public_key &out_key, const crypto::key_derivation &recv_derivation, size_t real_output_index, const subaddress_index &received_index, keypair &in_ephemeral, crypto::key_image &ki, hw::device &hwdev, const uint32_t account_major_offset)
bool is_coinbase(const transaction &tx)
#define CHECK_AND_ASSERT_THROW_MES(expr, message)
virtual device_protocol_t device_protocol() const
bool set_server(const std::string &address, boost::optional< login > user, ssl_options_t ssl_options=ssl_support_t::e_ssl_support_autodetect)
bool store_t_to_binary(t_struct &str_in, std::string &binary_buff, size_t indent=0)
virtual bool scalarmultKey(rct::key &aP, const rct::key &P, const rct::key &a)=0
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")
std::vector< crypto::hash > tx_hashes
const CharType(& source)[N]
void register_all(std::map< std::string, std::unique_ptr< device >> ®istry)
crypto::public_key real_out_tx_key
#define SEGREGATION_FORK_VICINITY
boost::function< crypto::public_key(const tools::wallet2::transfer_details &td)> get_tx_pub_key_from_received_outs
bool is_connected(bool *ssl=NULL)
etn_amount h2d(const key &test)
void derive_secret_key(const key_derivation &derivation, std::size_t output_index, const secret_key &base, secret_key &derived_key)
std::string get_account_address_as_str(network_type nettype, bool subaddress, account_public_address const &adr)
#define KEY_IMAGE_EXPORT_FILE_MAGIC
size_t size() const noexcept
bool isInMainSubgroup(const key &A)
bool ssl_support_from_string(ssl_support_t &ssl, boost::string_ref s)
virtual bool verify_keys(const crypto::secret_key &secret_key, const crypto::public_key &public_key)=0
virtual bool generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key, uint64_t kdf_rounds)=0
#define HF_VERSION_ZERO_FEE
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)
#define DEFAULT_MIN_OUTPUT_VALUE
#define APPROXIMATE_INPUT_BYTES
#define CRYPTONOTE_DISPLAY_DECIMAL_POINT
crypto::public_key shared_secret
#define HF_VERSION_MAX_RING_11
uint64_t get_outs_etn_amount(const transaction &tx)
#define HF_VERSION_ENFORCE_0_DECOY_TXS
epee::misc_utils::struct_init< response_t > response
auto_scope_leave_caller create_scope_leave_handler(t_scope_leave_handler f)
std::string print_etn(uint64_t amount, unsigned int decimal_point)
void scalarmultKey(key &aP, const key &P, const key &a)
key commit(etn_amount amount, const key &mask)
#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message)
#define CRYPTONOTE_MAX_BLOCK_NUMBER
const std::string old_language_name
std::vector< std::string > tx_device_aux
#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")
#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)
std::vector< blobdata > txs
#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)
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)
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...
std::string pruned_as_hex
#define FIRST_REFRESH_GRANULARITY
#define MCLOG_RED(level, cat, x)
epee::mlocked< tools::scrubbed< ec_scalar > > secret_key
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
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.
std::vector< uint64_t > relative_output_offsets_to_absolute(const std::vector< uint64_t > &off)
std::string prunable_hash
bool parse_binary(const std::string &blob, T &v)
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).
void keccak_finish(KECCAK_CTX *ctx, uint8_t *md)
#define HF_VERSION_MIN_MIXIN_4
#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V8
uint64_t get_transaction_weight(const transaction &tx, size_t blob_size)
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)
#define MULTISIG_EXPORT_FILE_MAGIC
crypto::secret_key encrypt_key(crypto::secret_key key, const epee::wipeable_string &passphrase)
boost::filesystem::path data_dir
#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V6
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)
#define PERF_TIMER_START(name)
const char * i18n_translate(const char *s, const std::string &context)
#define CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE
#define OUTPUT_EXPORT_FILE_MAGIC
void copy(key &AA, const key &A)
bool generate_key_derivation(const public_key &key1, const secret_key &key2, key_derivation &derivation)
void append(const char *ptr, size_t len)
static epee::wipeable_string wipeable_string(const span< const std::uint8_t > src)
void encrypt_keys(const crypto::chacha_key &key)
virtual bool generate_key_derivation(const crypto::public_key &pub, const crypto::secret_key &sec, crypto::key_derivation &derivation)=0
std::vector< uint8_t > extra
bool dump_binary(T &v, std::string &blob)
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)
#define RECENT_OUTPUT_BLOCKS
uint16_t const RPC_DEFAULT_PORT
const account_keys & get_keys() const
#define ETN_MINED_ETN_UNLOCK_WINDOW_V8
Primarily for use with epee::net_utils::http_client.
std::vector< ecdhTuple > ecdhInfo
#define STAGENET_SEGREGATION_FORK_HEIGHT
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)
std::vector< tx_out > vout
constexpr std::size_t size() const noexcept
Holds cryptonote related classes and helpers.
const crypto::secret_key null_skey
crypto::public_key generate_multisig_M_N_spend_public_key(const std::vector< crypto::public_key > &pkeys)
generate_multisig_M_N_spend_public_key calculates multisig wallet's spend public key by summing all o...
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_)
#define FEE_ESTIMATE_GRACE_BLOCKS
#define DEFAULT_MIN_OUTPUT_COUNT
std::vector< txin_v > vin
#define SECOND_OUTPUT_RELATEDNESS_THRESHOLD
blobdata tx_to_blob(const transaction &tx)
mdb_size_t count(MDB_cursor *cur)
crypto::public_key m_spend_public_key
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)
bool is_valid_decomposed_amount(uint64_t amount)
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)
std::vector< crypto::secret_key > calculate_multisig_keys(const std::vector< crypto::public_key > &derivations)
calculate_multisig_keys. Calculates secret multisig keys from others' participants ones as follows: m...
void generate_key_image(const public_key &pub, const secret_key &sec, key_image &image)
unsigned int get_default_decimal_point()
#define SUBADDRESS_LOOKAHEAD_MAJOR
#define HF_VERSION_MIN_MIXIN_6
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)
crypto::public_key m_view_public_key
bool generate_multisig_key_image(const account_keys &keys, size_t multisig_key_index, const crypto::public_key &out_key, crypto::key_image &ki)
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)
#define MAKE_CORE_RPC_VERSION(major, minor)
GenericValue< UTF8<> > Value
GenericValue with UTF8 encoding.
static std::vector< uint8_t > vector(boost::string_ref src)
void decrypt_keys(const crypto::chacha_key &key)
bool load_t_from_binary(t_struct &out, const epee::span< const uint8_t > binary_buff)
#define RECENT_OUTPUT_RATIO
#define DIFFICULTY_TARGET_V6
const crypto::public_key null_pkey
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
rct::rctSig rct_signatures
void cn_fast_hash(const void *data, size_t length, char *hash)
hw::device & get_device() const
#define HF_VERSION_DYNAMIC_FEE
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)
bool derive_subaddress_public_key(const public_key &out_key, const key_derivation &derivation, std::size_t output_index, public_key &result)
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)
boost::shared_ptr< call_befor_die_base > auto_scope_leave_caller
virtual void set_callback(i_device_callback *callback)
ssl_authentication_t auth
device & get_device(const std::string &device_descriptor)
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)
#define HF_VERSION_SMALLER_BP
#define TESTNET_SEGREGATION_FORK_HEIGHT
std::string obj_to_json_str(T &obj)
unsigned __int64 uint64_t
bool decrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key)
#define CRITICAL_REGION_LOCAL(x)
GenericDocument< UTF8<> > Document
GenericDocument with UTF8 encoding.
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)
std::string tx_prefix_hash
bool save_string_to_file(const std::string &path_to_file, const std::string &str)
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)
GenericStringBuffer< UTF8< char >, CrtAllocator > StringBuffer
Verify peer via specific (non-chain) certificate(s) only.
void hash_to_scalar(key &hash, const void *data, const std::size_t l)
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)
virtual cryptonote::account_public_address get_subaddress(const cryptonote::account_keys &keys, const cryptonote::subaddress_index &index)=0
#define SSL_FINGERPRINT_SIZE
bool signMultisig(rctSig &rv, const std::vector< unsigned int > &indices, const keyV &k, const multisig_out &msout, const key &secret_key)
bool parse_and_validate_tx_base_from_blob(const blobdata &tx_blob, transaction &tx)
bool parse_and_validate_tx_from_blob(const blobdata &tx_blob, transaction &tx)
crypto::hash get_pruned_transaction_hash(const transaction &t, const crypto::hash &pruned_data_hash)
#define HF_VERSION_ENABLE_RCT
cryptonote::account_public_address get_address(const var_addr_t &inp)
Useful when application has potentially harmful situtaions.
version
Supported socks variants.
void set_default_decimal_point(unsigned int decimal_point)
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 add_arg(boost::program_options::options_description &description, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg, bool unique=true)
void set_device(hw::device &hwdev)
rct::multisig_kLRki multisig_kLRki
std::string message("Message requiring signing")
crypto::secret_key m_view_secret_key
boost::endian::big_uint32_t ip
void ecdhDecode(ecdhTuple &masked, const key &sharedSec, bool v2)
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
#define MULTISIG_UNSIGNED_TX_PREFIX
std::tuple< uint64_t, crypto::public_key, rct::key > get_outs_entry
account_public_address addr
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
void scalarmultBase(key &aG, const key &a)
expect< void > success() noexcept
account_public_address m_account_address
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)
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)
void get_transaction_prefix_hash(const transaction_prefix &tx, crypto::hash &h)
crypto::signature shared_secret_sig
const T & move(const T &t)
std::vector< crypto::public_key > real_out_additional_tx_keys
uint64_t relative_out_index
#define PERF_TIMER_STOP(name)
crypto::secret_key generate_multisig_view_secret_key(const crypto::secret_key &skey, const std::vector< crypto::secret_key > &skeys)
#define THROW_WALLET_EXCEPTION_IF(cond, err_type,...)
const GenericPointer< typename T::ValueType > T2 value
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
#define GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, name, type, jtype, mandatory, def)
crypto::secret_key m_spend_secret_key
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)
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)
bool add_extra_nonce_to_tx_extra(std::vector< uint8_t > &tx_extra, const blobdata &extra_nonce)
bool get_account_address_from_str(address_parse_info &info, network_type nettype, std::string const &str)
ssl_verification_t verification
#define SEGREGATION_FORK_HEIGHT
Mnemonic seed generation and wallet restoration from them.
std::vector< crypto::secret_key > m_multisig_keys
void keccak_update(KECCAK_CTX *ctx, const uint8_t *in, size_t inlen)
#define CURRENT_HARDFORK_VERSION
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
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.
crypto::signature key_image_sig
key zeroCommit(etn_amount amount)
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
#define THROW_WALLET_EXCEPTION(err_type,...)
void addKeys(key &AB, const key &A, const key &B)
#define HF_VERSION_MIN_MIXIN_10
void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, uint64_t height)
std::vector< cryptonote::address_parse_info > tx_recipients
bool secret_key_to_public_key(const secret_key &sec, public_key &pub)
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
std::pair< uint64_t, rct::ctkey > output_entry
void set_payment_id_to_tx_extra_nonce(blobdata &extra_nonce, const crypto::hash &payment_id)
void set_connector(F connector)
std::vector< crypto::public_key > get_additional_tx_pub_keys_from_extra(const std::vector< uint8_t > &tx_extra)
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
void * memwipe(void *src, size_t n)
virtual bool connect(void)=0
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)
const char * data() const noexcept
#define RECENT_OUTPUT_ZONE
#define HF_VERSION_PER_BYTE_FEE
virtual bool has_tx_cold_sign(void) const
std::string prunable_as_hex
void sc_add(unsigned char *, const unsigned char *, const unsigned char *)
#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS
#define ELECTRONEUM_DEFAULT_LOG_CATEGORY
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)
error
Tracks LMDB error codes.
#define TX_WEIGHT_TARGET(bytes)
#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)
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)
#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1
bool check_signature(const hash &prefix_hash, const public_key &pub, const signature &sig)
bool parse_and_validate_block_from_blob(const blobdata &b_blob, block &b, crypto::hash *block_hash)
virtual void computing_key_images(bool started)
void encrypt_viewkey(const crypto::chacha_key &key)
virtual void set_network_type(cryptonote::network_type network_type)
subaddress_index subaddr_index
crypto::secret_key calculate_multisig_signer_key(const std::vector< crypto::secret_key > &multisig_keys)
constexpr pointer data() const noexcept