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>
92 #undef ELECTRONEUM_DEFAULT_LOG_CATEGORY
93 #define ELECTRONEUM_DEFAULT_LOG_CATEGORY "wallet.wallet2"
96 #define APPROXIMATE_INPUT_BYTES 80
99 #define TX_WEIGHT_TARGET(bytes) (bytes*2/3)
102 #define CHACHA8_KEY_TAIL 0x8c
103 #define CACHE_KEY_TAIL 0x8d
106 #define UNSIGNED_TX_PREFIX "Electroneum unsigned tx set\004"
107 #define SIGNED_TX_PREFIX "Electroneum signed tx set\004"
108 #define MULTISIG_UNSIGNED_TX_PREFIX "Electroneum multisig unsigned tx set\001"
110 #define RECENT_OUTPUT_RATIO (0.5)
111 #define RECENT_OUTPUT_DAYS (1.8)
112 #define RECENT_OUTPUT_ZONE ((time_t)(RECENT_OUTPUT_DAYS * 86400))
113 #define RECENT_OUTPUT_BLOCKS (RECENT_OUTPUT_DAYS * 720)
115 #define FEE_ESTIMATE_GRACE_BLOCKS 10
117 #define SECOND_OUTPUT_RELATEDNESS_THRESHOLD 0.0f
119 #define SUBADDRESS_LOOKAHEAD_MAJOR 50
120 #define SUBADDRESS_LOOKAHEAD_MINOR 200
122 #define KEY_IMAGE_EXPORT_FILE_MAGIC "Electroneum key image export\002"
124 #define MULTISIG_EXPORT_FILE_MAGIC "Electroneum multisig export\001"
126 #define SEGREGATION_FORK_HEIGHT 99999999
127 #define TESTNET_SEGREGATION_FORK_HEIGHT 99999999
128 #define STAGENET_SEGREGATION_FORK_HEIGHT 99999999
129 #define SEGREGATION_FORK_VICINITY 1500
131 #define FIRST_REFRESH_GRANULARITY 1024
133 #define GAMMA_SHAPE 19.28
134 #define GAMMA_SCALE (1/1.61)
136 #define DEFAULT_MIN_OUTPUT_COUNT 5
137 #define DEFAULT_MIN_OUTPUT_VALUE (2*COIN)
139 #define OUTPUT_EXPORT_FILE_MAGIC "Electroneum output export\003"
141 static const std::string MULTISIG_SIGNATURE_MAGIC =
"SigMultisigPkV1";
142 static const std::string MULTISIG_EXTRA_INFO_MAGIC =
"MultisigxV1";
150 dir = dir.remove_filename();
151 dir /=
".shared-ringdb";
162 for (
const auto &
key: keys)
177 std::vector<crypto::public_key> secret_keys_to_public_keys(
const std::vector<crypto::secret_key>& keys)
179 std::vector<crypto::public_key> public_keys;
180 public_keys.reserve(keys.size());
183 crypto::public_key p;
184 CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(k, p),
"Failed to derive public spend key");
191 bool keys_intersect(
const std::unordered_set<crypto::public_key>& s1,
const std::unordered_set<crypto::public_key>& s2)
193 if (s1.empty() || s2.empty())
196 for (
const auto& e: s1)
198 if (s2.find(e) != s2.end())
205 void add_reason(
std::string &reasons,
const char *reason)
207 if (!reasons.empty())
216 add_reason(reason,
"bad ring size");
217 if (
res.double_spend)
218 add_reason(reason,
"double spend");
219 if (
res.invalid_input)
220 add_reason(reason,
"invalid input");
221 if (
res.invalid_output)
222 add_reason(reason,
"invalid output");
224 add_reason(reason,
"too big");
226 add_reason(reason,
"overspend");
228 add_reason(reason,
"fee too low");
230 add_reason(reason,
"tx is not ringct");
231 if (
res.sanity_check_failed)
232 add_reason(reason,
"tx sanity check failed");
234 add_reason(reason,
"tx was not relayed");
261 const command_line::arg_descriptor<uint64_t> fallback_to_pow_checkpoint_height = {
"fallback-to-pow-checkpoint-height",
tools::wallet2::tr(
"Warning: This is to set the height for a custom checkpoint in the event of PoW fallback. Do not use in normal circumstances. See docs for details "), 0,
false};
262 const command_line::arg_descriptor<std::string> fallback_to_pow_checkpoint_hash = {
"fallback-to-pow-checkpoint-hash",
tools::wallet2::tr(
"Warning: This is to set the hash for a custom checkpoint in the event of PoW fallback. Do not use in normal circumstances. See docs for details "),
"",
false};
265 get_default_ringdb_path(),
266 {{ &testnet, &stagenet }},
268 if (testnet_stagenet[0])
269 return (boost::filesystem::path(val) /
"testnet").
string();
270 else if (testnet_stagenet[1])
271 return (boost::filesystem::path(val) /
"stagenet").
string();
288 keys_file = file_path;
289 wallet_file = file_path;
290 boost::system::error_code e;
296 keys_file +=
".keys";
298 mms_file = file_path +
".mms";
303 uint64_t kB = (bytes + 1023) / 1024;
304 return kB * fee_per_kb * fee_multiplier;
309 uint64_t fee = weight * base_fee * fee_multiplier;
310 fee = (fee + fee_quantization_mask - 1) / fee_quantization_mask * fee_quantization_mask;
324 std::unique_ptr<tools::wallet2> make_basic(
const boost::program_options::variables_map& vm,
bool unattended,
const options& opts,
const std::function<boost::optional<tools::password_container>(
const char *,
bool)> &password_prompter)
343 auto daemon_ssl_allowed_fingerprints =
command_line::get_arg(vm, opts.daemon_ssl_allowed_fingerprints);
346 auto fallback_to_pow_checkpoint_height =
command_line::get_arg(vm, opts.fallback_to_pow_checkpoint_height);
347 auto fallback_to_pow_checkpoint_hash =
command_line::get_arg(vm, opts.fallback_to_pow_checkpoint_hash);
353 else if (!daemon_ssl_ca_file.empty() || !daemon_ssl_allowed_fingerprints.empty())
355 std::vector<std::vector<uint8_t>> ssl_allowed_fingerprints{ daemon_ssl_allowed_fingerprints.size() };
356 std::transform(daemon_ssl_allowed_fingerprints.begin(), daemon_ssl_allowed_fingerprints.end(), ssl_allowed_fingerprints.begin(),
epee::from_hex::vector);
357 for (
const auto &fpr: ssl_allowed_fingerprints)
386 boost::optional<epee::net_utils::http::login> login{};
391 return password_prompter(
"Daemon client password", verify);
400 if (daemon_host.empty())
401 daemon_host =
"localhost";
416 const bool verification_required =
428 boost::asio::ip::tcp::endpoint proxy{};
435 boost::string_ref proxy_port{proxy_address};
436 boost::string_ref proxy_host = proxy_port.substr(0, proxy_port.rfind(
":"));
437 if (proxy_port.size() == proxy_host.size())
438 proxy_host =
"127.0.0.1";
440 proxy_port = proxy_port.substr(proxy_host.size() + 1);
446 std::string{
"Invalid port specified for --"} + opts.proxy.name
449 boost::system::error_code
error{};
450 proxy = ip::tcp::endpoint{ip::address::from_string(
std::string{proxy_host},
error), port_value};
454 boost::optional<bool> trusted_daemon;
465 trusted_daemon =
false;
469 trusted_daemon =
true;
472 catch (
const std::exception &e) { }
475 std::unique_ptr<tools::wallet2> wallet(
new tools::wallet2(nettype, kdf_rounds, unattended));
478 if(fallback_to_pow_checkpoint_hash !=
"" && fallback_to_pow_checkpoint_height != 0) {
479 wallet->add_checkpoint(fallback_to_pow_checkpoint_height, fallback_to_pow_checkpoint_hash);
482 wallet->set_ring_database(ringdb_path.string());
483 wallet->get_message_store().set_options(vm);
484 wallet->device_name(device_name);
485 wallet->device_derivation_path(device_derivation_path);
488 wallet->enable_dns(
false);
491 wallet->set_offline();
498 catch (
const std::exception &e)
500 MERROR(
"Failed to parse tx notify spec: " << e.what());
506 boost::optional<tools::password_container> get_password(
const boost::program_options::variables_map& vm,
const options& opts,
const std::function<boost::optional<tools::password_container>(
const char*,
bool)> &password_prompter,
const bool verify)
526 boost::trim_right_if(password, boost::is_any_of(
"\r\n"));
535 std::pair<std::unique_ptr<tools::wallet2>,
tools::password_container> generate_from_json(
const std::string& json_file,
const boost::program_options::variables_map& vm,
bool unattended,
const options& opts,
const std::function<boost::optional<tools::password_container>(
const char *,
bool)> &password_prompter)
544 std::unique_ptr<tools::wallet2> wallet;
546 const auto do_generate = [&]() ->
bool {
554 if (
json.Parse(
buf.c_str()).HasParseError()) {
560 const int current_version = 1;
562 ((boost::format(
tools::wallet2::tr(
"Version %u too new, we can only grok up to %u")) % field_version % current_version)).str());
567 const bool recover =
true;
573 if (field_viewkey_found)
589 if (field_spendkey_found)
606 bool restore_deterministic_wallet =
false;
607 if (field_seed_found)
613 restore_deterministic_wallet =
true;
616 if (field_seed_passphrase_found)
618 if (!field_seed_passphrase.empty())
626 bool create_address_file = field_create_address_file;
629 if (!field_seed_found && !field_viewkey_found && !field_spendkey_found)
633 if (field_seed_found && (field_viewkey_found || field_spendkey_found))
640 if (field_address_found)
647 if (field_viewkey_found)
653 if (
info.address.m_view_public_key != pkey) {
657 if (field_spendkey_found)
663 if (
info.address.m_spend_public_key != pkey) {
674 wallet.reset(make_basic(vm, unattended, opts, password_prompter).release());
675 wallet->set_refresh_from_block_height(field_scan_from_height);
676 wallet->explicit_refresh_from_block_height(field_scan_from_height_found);
677 if (!old_language.empty())
678 wallet->set_seed_language(old_language);
682 if (!field_seed.empty())
684 wallet->generate(field_filename, field_password, recovery_key, recover,
false, create_address_file);
685 password = field_password;
687 else if (field_viewkey.empty() && !field_spendkey.empty())
689 wallet->generate(field_filename, field_password,
spendkey, recover,
false, create_address_file);
690 password = field_password;
699 if (field_spendkey.empty())
703 if (field_address_found)
710 address.m_spend_public_key =
info.address.m_spend_public_key;
716 wallet->generate(field_filename, field_password,
address, viewkey, create_address_file);
717 password = field_password;
724 wallet->generate(field_filename, field_password,
address,
spendkey, viewkey, create_address_file);
725 password = field_password;
729 catch (
const std::exception& e)
743 std::string strjoin(
const std::vector<size_t> &V,
const char *sep)
745 std::stringstream ss;
747 for (
const auto &v: V)
757 static bool emplace_or_replace(std::unordered_multimap<crypto::hash, tools::wallet2::pool_payment_details> &container,
760 auto range = container.equal_range(
key);
761 for (
auto i = range.first; i != range.second; ++i)
769 container.emplace(
key, pd);
773 void drop_from_short_history(std::list<crypto::hash> &short_chain_history,
size_t N)
775 std::list<crypto::hash>::iterator right;
777 if (short_chain_history.size() > N) {
778 right = short_chain_history.end();
779 std::advance(right,-1);
780 std::list<crypto::hash>::iterator left = right;
781 std::advance(left, -N);
782 short_chain_history.erase(left, right);
786 size_t estimate_rct_tx_size(
int n_inputs,
int mixin,
int n_outputs,
size_t extra_size,
bool bulletproof)
796 size += n_inputs * (1+6+(mixin+1)*2+32);
799 size += n_outputs * (6+32);
812 size_t log_padded_outputs = 0;
813 while ((1<<log_padded_outputs) < n_outputs)
814 ++log_padded_outputs;
815 size += (2 * (6 + log_padded_outputs) + 4 + 5) * 32 + 3;
818 size += (2*64*32+32+64*32) * n_outputs;
821 size += n_inputs * (64 * (mixin+1) + 32);
827 size += 32 * n_inputs;
829 size += 8 * n_outputs;
831 size += 32 * n_outputs;
835 LOG_PRINT_L2(
"estimated " << (bulletproof ?
"bulletproof" :
"borromean") <<
" rct tx size for " << n_inputs <<
" inputs with ring size " << (mixin+1) <<
" and " << n_outputs <<
" outputs: " << size <<
" (" << ((32 * n_inputs) + 2 * 32 * (mixin+1) * n_inputs + 32 * n_outputs) <<
" saved)");
839 size_t estimate_tx_size(
bool use_rct,
int n_inputs,
int mixin,
int n_outputs,
size_t extra_size,
bool bulletproof)
842 return estimate_rct_tx_size(n_inputs, mixin, n_outputs, extra_size, bulletproof);
847 uint64_t estimate_tx_weight(
bool use_rct,
int n_inputs,
int mixin,
int n_outputs,
size_t extra_size,
bool bulletproof)
849 size_t size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
850 if (use_rct && bulletproof && n_outputs > 2)
853 size_t log_padded_outputs = 2;
854 while ((1<<log_padded_outputs) < n_outputs)
855 ++log_padded_outputs;
856 uint64_t nlr = 2 * (6 + log_padded_outputs);
857 const uint64_t bp_size = 32 * (9 + nlr);
858 const uint64_t bp_clawback = (bp_base * (1<<log_padded_outputs) - bp_size) * 4 / 5;
859 MDEBUG(
"clawback on size " << size <<
": " << bp_clawback);
870 uint64_t estimate_fee(
bool use_per_byte_fee,
bool use_rct,
int n_inputs,
int mixin,
int n_outputs,
size_t extra_size,
bool bulletproof,
uint64_t base_fee,
uint64_t fee_multiplier,
uint64_t fee_quantization_mask)
872 if (use_per_byte_fee)
874 const size_t estimated_tx_weight = estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
875 return calculate_fee_from_weight(base_fee, estimated_tx_weight, fee_multiplier, fee_quantization_mask);
879 const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
880 return calculate_fee(base_fee, estimated_tx_size, fee_multiplier);
886 if (use_per_byte_fee)
889 return calculate_fee(base_fee, blob_size, fee_multiplier);
894 std::vector<tx_extra_field> tx_extra_fields;
901 if (ptx.
dests.empty())
903 MWARNING(
"Encrypted payment id found, but no destinations public key, cannot decrypt");
923 memcpy(payment_id.data, payment_id8.data, 8);
924 memset(payment_id.data + 8, 0, 24);
931 return construction_data;
936 static constexpr
uint32_t uint32_max = std::numeric_limits<uint32_t>::max();
937 if (idx > uint32_max - extra)
959 "Response claims a different hash than the data yields");
992 constexpr
const std::chrono::seconds wallet2::rpc_timeout;
995 gamma_picker::gamma_picker(
const std::vector<uint64_t> &rct_offsets,
double shape,
double scale):
996 rct_offsets(rct_offsets)
998 gamma = std::gamma_distribution<double>(shape, scale);
1001 const size_t blocks_to_consider = std::min<size_t>(rct_offsets.size(), blocks_in_a_year);
1002 const size_t outputs_to_consider = rct_offsets.back() - (blocks_to_consider < rct_offsets.size() ? rct_offsets[rct_offsets.size() - blocks_to_consider - 1] : 0);
1003 begin = rct_offsets.data();
1005 num_rct_outputs = *(end - 1);
1014 double x = gamma(engine);
1018 return std::numeric_limits<uint64_t>::max();
1023 uint64_t index = std::distance(begin, it);
1025 const uint64_t first_rct = index == 0 ? 0 : rct_offsets[index - 1];
1026 const uint64_t n_rct = rct_offsets[index] - first_rct;
1028 return std::numeric_limits<uint64_t>::max();
1029 MTRACE(
"Picking 1/" << n_rct <<
" in block " << index);
1035 locked(password !=
boost::none)
1043 w.generate_chacha_key_from_password(pass,
key);
1053 w.generate_chacha_key_from_password(password,
key);
1064 MERROR(
"Failed to re-encrypt wallet keys");
1072 wallet->on_device_button_request(code);
1078 wallet->on_device_button_pressed();
1084 return wallet->on_device_pin_request();
1091 return wallet->on_device_passphrase_request(on_device);
1098 wallet->on_device_progress(
event);
1102 m_multisig_rescan_info(NULL),
1103 m_multisig_rescan_k(NULL),
1104 m_upper_transaction_weight_limit(0),
1107 m_trusted_daemon(
false),
1109 m_multisig_rounds_passed(0),
1110 m_always_confirm_transfers(
true),
1111 m_print_ring_members(
false),
1112 m_store_tx_info(
true),
1114 m_default_priority(0),
1115 m_refresh_type(RefreshOptimizeCoinbase),
1116 m_auto_refresh(
true),
1117 m_first_refresh_done(
false),
1118 m_refresh_from_block_height(0),
1119 m_explicit_refresh_from_block_height(
true),
1120 m_confirm_missing_payment_id(
true),
1121 m_confirm_non_default_ring_size(
true),
1122 m_ask_password(AskPasswordOnAction),
1123 m_min_output_count(0),
1124 m_min_output_value(0),
1125 m_merge_destinations(
false),
1126 m_confirm_backlog(
true),
1127 m_confirm_backlog_threshold(0),
1128 m_confirm_export_overwrite(
true),
1129 m_auto_low_priority(
true),
1130 m_segregate_pre_fork_outputs(
true),
1131 m_key_reuse_mitigation2(
true),
1132 m_segregation_height(0),
1133 m_ignore_fractional_outputs(
true),
1134 m_track_uses(
false),
1135 m_setup_background_mining(BackgroundMiningMaybe),
1136 m_is_initialized(
false),
1137 m_kdf_rounds(kdf_rounds),
1138 is_old_file_format(
false),
1139 m_watch_only(
false),
1141 m_multisig_threshold(0),
1142 m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex),
1146 m_light_wallet(
false),
1147 m_light_wallet_scanned_block_height(0),
1148 m_light_wallet_blockchain_height(0),
1149 m_light_wallet_connected(
false),
1150 m_light_wallet_balance(0),
1151 m_light_wallet_unlocked_balance(0),
1152 m_original_keys_available(
false),
1154 m_key_device_type(
hw::device::device_type::SOFTWARE),
1155 m_ring_history_saved(
false),
1157 m_last_block_reward(0),
1158 m_encrypt_keys_after_refresh(
boost::none),
1159 m_unattended(unattended),
1160 m_devices_registered(
false),
1161 m_device_last_key_image_sync(0),
1164 m_account_major_offset(0)
1229 return generate_from_json(json_file, vm, unattended, opts, password_prompter);
1233 const boost::program_options::variables_map& vm,
bool unattended,
const std::string& wallet_file,
const std::function<boost::optional<tools::password_container>(
const char *,
bool)> &password_prompter)
1236 auto pwd = get_password(vm, opts, password_prompter,
false);
1241 auto wallet = make_basic(vm, unattended, opts, password_prompter);
1242 if (wallet && !wallet_file.empty())
1244 wallet->load(wallet_file, pwd->password());
1249 std::pair<std::unique_ptr<wallet2>,
password_container>
wallet2::make_new(
const boost::program_options::variables_map& vm,
bool unattended,
const std::function<boost::optional<password_container>(
const char *,
bool)> &password_prompter)
1252 auto pwd = get_password(vm, opts, password_prompter,
true);
1257 return {make_basic(vm, unattended, opts, password_prompter),
std::move(*pwd)};
1260 std::unique_ptr<wallet2>
wallet2::make_dummy(
const boost::program_options::variables_map& vm,
bool unattended,
const std::function<boost::optional<tools::password_container>(
const char *,
bool)> &password_prompter)
1263 return make_basic(vm, unattended, opts, password_prompter);
1269 boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex);
1274 m_daemon_login =
std::move(daemon_login);
1275 m_trusted_daemon = trusted_daemon;
1284 m_is_initialized =
true;
1285 m_upper_transaction_weight_limit = upper_transaction_weight_limit;
1287 if (proxy != boost::asio::ip::tcp::endpoint{})
1303 if (!keys_deterministic)
1305 std::cout <<
"This is not a deterministic wallet" << std::endl;
1308 if (seed_language.empty())
1310 std::cout <<
"seed_language not set" << std::endl;
1315 if (!passphrase.
empty())
1319 std::cout <<
"Failed to create seed from key for language: " << seed_language << std::endl;
1332 std::cout <<
"This is not a multisig wallet" << std::endl;
1337 std::cout <<
"This multisig wallet is not yet finalized" << std::endl;
1340 if (!raw && seed_language.empty())
1342 std::cout <<
"seed_language not set" << std::endl;
1353 data.
append((
const char*)&skey,
sizeof(skey));
1355 data.
append((
const char*)&pkey,
sizeof(pkey));
1357 data.
append((
const char*)&skey,
sizeof(skey));
1359 data.
append((
const char*)&pkey,
sizeof(pkey));
1361 data.
append((
const char*)&skey,
sizeof(skey));
1362 for (
const auto &signer: m_multisig_signers)
1363 data.
append((
const char*)&signer,
sizeof(signer));
1365 if (!passphrase.
empty())
1381 std::cout <<
"Failed to encode seed";
1392 hw::device &hwdev = lookup_device(m_device_name);
1399 MERROR(
"Could not init device");
1405 MERROR(
"Could not connect to the device");
1418 return seed_language;
1426 seed_language = language;
1438 auto index = m_subaddresses.find(
address.m_spend_public_key);
1439 if (index == m_subaddresses.end())
1441 return index->second;
1466 m_subaddress_labels[index_major][0] = label;
1474 m_subaddress_labels[index_major][index_minor] = label;
1480 if (m_subaddress_labels.size() <= index.
major)
1484 const uint32_t major_end = get_subaddress_clamped_sum(index.
major, m_subaddress_lookahead_major);
1485 for (index2.
major = m_subaddress_labels.size(); index2.
major < major_end; ++index2.
major)
1487 const uint32_t end = get_subaddress_clamped_sum((index2.
major == index.
major ? index.
minor : 0), m_subaddress_lookahead_minor);
1492 m_subaddresses[D] = index2;
1495 m_subaddress_labels.resize(index.
major + 1, {
"Untitled account"});
1496 m_subaddress_labels[index.
major].resize(index.
minor + 1);
1498 if(update_account_tags)
1501 else if (m_subaddress_labels[index.
major].size() <= index.
minor)
1504 const uint32_t end = get_subaddress_clamped_sum(index.
minor, m_subaddress_lookahead_minor);
1505 const uint32_t begin = m_subaddress_labels[index.
major].size();
1511 m_subaddresses[D] = index2;
1513 m_subaddress_labels[index.
major].resize(index.
minor + 1);
1519 if (index.
major >= m_subaddress_labels.size() || index.
minor >= m_subaddress_labels[index.
major].size())
1521 MERROR(
"Subaddress label doesn't exist");
1524 return m_subaddress_labels[index.
major][index.
minor];
1531 m_subaddress_labels[index.
major][index.
minor] = label;
1538 m_subaddress_lookahead_major = major;
1539 m_subaddress_lookahead_minor = minor;
1547 return is_old_file_format;
1550 void wallet2::set_spent(
size_t idx,
uint64_t height,
bool public_out)
1552 transfer_details &td = m_transfers[idx];
1556 <<
height <<
": chainstate index " << td.m_txid <<
":" << td.m_internal_output_index
1557 <<
", amount " <<
print_etn(td.m_amount));
1560 <<
height <<
": ki " << td.m_key_image
1561 <<
", amount " <<
print_etn(td.m_amount));
1565 td.m_spent_height =
height;
1568 void wallet2::set_unspent(
size_t idx,
bool public_out)
1570 transfer_details &td = m_transfers[idx];
1574 << td.m_txid <<
":"<< td.m_internal_output_index <<
", amount " <<
print_etn(td.m_amount));
1580 td.m_spent_height = 0;
1621 for (
size_t idx = 0; idx < m_transfers.size(); ++idx)
1623 const transfer_details &td = m_transfers[idx];
1624 if (td.m_key_image_known && td.m_key_image == ki)
1635 void wallet2::check_acc_out_precomp(
const tx_out &o,
const crypto::key_derivation &derivation,
const std::vector<crypto::key_derivation> &additional_derivations,
size_t i, tx_scan_info_t &tx_scan_info)
const
1638 boost::unique_lock<hw::device> hwdev_lock (hwdev);
1642 tx_scan_info.error =
true;
1643 LOG_ERROR(
"wrong type id in transaction out");
1648 additional_derivations, i, hwdev);
1652 auto out_address = boost::get<txout_to_key_public>(o.
target).address;
1654 tx_scan_info.received =
1655 (receive_info == boost::none) ?
1658 receive_info : boost::none;
1660 if(tx_scan_info.received)
1662 tx_scan_info.etn_transfered = o.
amount;
1666 tx_scan_info.etn_transfered = 0;
1668 tx_scan_info.error =
false;
1671 void wallet2::check_acc_out_precomp(
const tx_out &o,
const crypto::key_derivation &derivation,
const std::vector<crypto::key_derivation> &additional_derivations,
size_t i,
const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info)
const
1674 if (!is_out_data || i >= is_out_data->received.size())
1675 return check_acc_out_precomp(o, derivation, additional_derivations, i, tx_scan_info);
1677 tx_scan_info.received = is_out_data->received[i];
1678 if(tx_scan_info.received)
1680 tx_scan_info.etn_transfered = o.
amount;
1684 tx_scan_info.etn_transfered = 0;
1686 tx_scan_info.error =
false;
1689 void wallet2::check_acc_out_precomp_once(
const tx_out &o,
const crypto::key_derivation &derivation,
const std::vector<crypto::key_derivation> &additional_derivations,
size_t i,
const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info,
bool &already_seen)
const
1691 tx_scan_info.received = boost::none;
1694 check_acc_out_precomp(o, derivation, additional_derivations, i, is_out_data, tx_scan_info);
1695 if (tx_scan_info.received)
1696 already_seen =
true;
1718 catch (
const std::exception &e)
1720 LOG_ERROR(
"Failed to decode input " << i);
1725 void wallet2::scan_output(
const cryptonote::transaction &tx,
bool miner_tx,
const crypto::public_key &tx_pub_key,
size_t i, tx_scan_info_t &tx_scan_info,
int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_etn_got_in_outs, std::vector<size_t> &outs,
bool pool)
1730 if (m_ask_password ==
AskPasswordToDecrypt && !m_unattended && !m_watch_only && !m_multisig_rescan_k)
1734 if (!m_encrypt_keys_after_refresh)
1736 boost::optional<epee::wipeable_string> pwd = m_callback->
on_get_password(pool ?
"output found in pool" :
"output received");
1740 m_encrypt_keys_after_refresh = *pwd;
1746 tx_scan_info.in_ephemeral.pub = boost::get<cryptonote::txout_to_key>(tx.
vout[i].target).key;
1748 tx_scan_info.ki = rct::rct2ki(
rct::zero());
1755 error::wallet_internal_error,
"key_image generated ephemeral public key not matched with output_key");
1758 THROW_WALLET_EXCEPTION_IF(std::find(outs.begin(), outs.end(), i) != outs.end(), error::wallet_internal_error,
"Same output cannot be added twice");
1760 if (tx_scan_info.etn_transfered == 0 && !miner_tx)
1762 tx_scan_info.etn_transfered = tools::decodeRct(tx.
rct_signatures, tx_scan_info.received->derivation, i, tx_scan_info.mask, m_account.
get_device());
1764 if (tx_scan_info.etn_transfered == 0)
1766 MERROR(
"Invalid output amount, skipping");
1767 tx_scan_info.error =
true;
1771 THROW_WALLET_EXCEPTION_IF(tx_etn_got_in_outs[tx_scan_info.received->index] >= std::numeric_limits<uint64_t>::max() - tx_scan_info.etn_transfered,
1772 error::wallet_internal_error,
"Overflow in received amounts");
1774 tx_etn_got_in_outs[tx_scan_info.received->index] += tx_scan_info.etn_transfered;
1775 tx_scan_info.amount = tx_scan_info.etn_transfered;
1776 ++num_vouts_received;
1784 LOG_PRINT_L0(
"Transaction extra has unsupported format: " << txid);
1785 if (tx_cache_data.tx_extra_fields.empty())
1792 if (!is_miner || m_refresh_type != RefreshType::RefreshNoCoinbase)
1794 const size_t rec_size = is_miner && m_refresh_type == RefreshType::RefreshOptimizeCoinbase ? 1 : tx.
vout.size();
1795 if (!tx.
vout.empty())
1797 const std::vector<boost::optional<cryptonote::subaddress_receive_info>> rec(rec_size, boost::none);
1805 size_t pk_index = 0;
1807 tx_cache_data.primary.push_back({pub_key_field.
pub_key, {}, rec});
1813 for (
size_t i = 0; i < additional_tx_pub_keys.
data.size(); ++i)
1814 tx_cache_data.additional.push_back({additional_tx_pub_keys.data[i], {}, {}});
1820 void wallet2::process_new_transaction(
const crypto::hash &txid,
const cryptonote::transaction& tx,
const std::vector<uint64_t> &o_indices,
uint64_t height,
uint64_t ts,
bool miner_tx,
bool pool,
bool double_spend_seen,
bool nonexistent_utxo_seen,
const tx_cache_data &tx_cache_data, std::pair<std::map<std::pair<uint64_t, uint64_t>,
size_t>, std::map<std::pair<std::array<char, 32>,
size_t>,
size_t>> *output_tracker_cache)
1825 if (!miner_tx && !pool)
1826 process_unconfirmed(txid, tx,
height);
1827 std::unordered_map<cryptonote::subaddress_index, uint64_t> tx_etn_got_in_outs;
1829 bool notify =
false;
1831 std::vector<tx_extra_field> local_tx_extra_fields;
1832 if (tx_cache_data.tx_extra_fields.empty())
1837 LOG_PRINT_L0(
"Transaction extra has unsupported format: " << txid);
1840 const std::vector<tx_extra_field> &tx_extra_fields = tx_cache_data.tx_extra_fields.empty() ? local_tx_extra_fields : tx_cache_data.tx_extra_fields;
1843 size_t pk_index = 0;
1844 std::vector<tx_scan_info_t> tx_scan_info(tx.
vout.size());
1845 std::deque<bool> output_found(tx.
vout.size(),
false);
1848 while (!tx.
vout.empty()) {
1849 std::vector<size_t> outs;
1857 LOG_PRINT_L0(
"Public key wasn't found in the transaction extra. Skipping transaction " << txid);
1858 if (0 != m_callback)
1859 m_callback->on_skip_transaction(
height, txid, tx);
1862 if (!tx_cache_data.primary.empty()) {
1864 pub_key_field.
pub_key != tx_cache_data.primary[pk_index - 1].pkey,
1865 error::wallet_internal_error,
"tx_cache_data is out of sync");
1868 int num_vouts_received = 0;
1869 tx_pub_key = pub_key_field.
pub_key;
1875 std::vector<crypto::key_derivation> additional_derivations;
1878 const wallet2::is_out_data *is_out_data_ptr = NULL;
1882 if (tx_cache_data.primary.empty()) {
1884 boost::unique_lock<hw::device> hwdev_lock(hwdev);
1889 MWARNING(
"Failed to generate key derivation from tx pubkey in " << txid <<
", skipping");
1890 static_assert(
sizeof(derivation) ==
sizeof(
rct::key),
1891 "Mismatched sizes of key_derivation and rct::key");
1895 if (pk_index == 1) {
1898 for (
size_t i = 0; i < additional_tx_pub_keys.
data.size(); ++i) {
1899 additional_derivations.push_back({});
1902 additional_derivations.back())) {
1903 MWARNING(
"Failed to generate key derivation from additional tx pubkey in " << txid
1913 error::wallet_internal_error,
"pk_index out of range of tx_cache_data");
1914 is_out_data_ptr = &tx_cache_data.primary[pk_index - 1];
1915 derivation = tx_cache_data.primary[pk_index - 1].derivation;
1916 if (pk_index == 1) {
1917 for (
size_t n = 0; n < tx_cache_data.additional.size(); ++n) {
1918 additional_tx_pub_keys.
data.push_back(tx_cache_data.additional[n].pkey);
1919 additional_derivations.push_back(tx_cache_data.additional[n].derivation);
1928 if (tx.
version > 1 && !tx_cache_data.public_outs.empty()) {
1929 is_out_data_ptr = &tx_cache_data.public_outs[0];
1933 if (miner_tx && m_refresh_type == RefreshNoCoinbase) {
1937 else if (miner_tx && m_refresh_type == RefreshOptimizeCoinbase) {
1940 check_acc_out_precomp_once(tx.
vout[0], derivation, additional_derivations, 0, is_out_data_ptr,
1941 tx_scan_info[0], output_found[0]);
1943 m_account.get_keys());
1946 if (tx_scan_info[0].received) {
1948 for (
size_t i = 1; i < tx.
vout.size(); ++i) {
1949 tpool.
submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp_once,
this, std::cref(tx.
vout[i]),
1950 std::cref(derivation), std::cref(additional_derivations), i,
1951 std::cref(is_out_data_ptr), std::ref(tx_scan_info[i]),
1952 std::ref(output_found[i])),
true);
1954 waiter.
wait(&tpool);
1957 boost::unique_lock<hw::device> hwdev_lock(hwdev);
1959 for (
size_t i = 0; i < tx.
vout.size(); ++i) {
1961 m_account.get_keys());
1962 if (tx_scan_info[i].received)
1966 additional_tx_pub_keys.
data, derivation, additional_derivations);
1968 scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_etn_got_in_outs,
1976 for (
size_t i = 0; i < tx.
vout.size(); ++i) {
1977 tpool.
submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp_once,
this, std::cref(tx.
vout[i]),
1978 std::cref(derivation), std::cref(additional_derivations), i,
1979 std::cref(is_out_data_ptr), std::ref(tx_scan_info[i]),
1980 std::ref(output_found[i])),
true);
1982 waiter.
wait(&tpool);
1985 boost::unique_lock<hw::device> hwdev_lock(hwdev);
1987 for (
size_t i = 0; i < tx.
vout.size(); ++i) {
1989 m_account.get_keys());
1990 if (tx_scan_info[i].received)
1995 additional_tx_pub_keys.
data,
1996 derivation, additional_derivations);
1998 scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_etn_got_in_outs,
2005 for (
size_t i = 0; i < tx.
vout.size(); ++i) {
2006 check_acc_out_precomp_once(tx.
vout[i], derivation, additional_derivations, i, is_out_data_ptr,
2007 tx_scan_info[i], output_found[i]);
2009 m_account.get_keys());
2010 if (tx_scan_info[i].received)
2013 boost::unique_lock<hw::device> hwdev_lock(hwdev);
2017 additional_tx_pub_keys.
data,
2018 derivation, additional_derivations);
2020 scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_etn_got_in_outs,
2026 if (!outs.empty() && num_vouts_received > 0) {
2032 " not match with daemon response size=" +
std::to_string(o_indices.size()));
2035 for (
const size_t o: outs) {
2037 "wrong out in transaction: internal index=" +
2040 auto kit = m_pub_keys.find(tx_scan_info[o].in_ephemeral.pub);
2043 error::wallet_internal_error,
2044 std::string(
"Unexpected transfer index from public key: ")
2046 (kit == m_pub_keys.end() ?
"<none>" : boost::lexical_cast<std::string>(
2048 +
", m_transfers.size() is " +
2049 boost::lexical_cast<std::string>(m_transfers.size()));
2050 if (kit == m_pub_keys.end()) {
2051 uint64_t amount = tx.
vout[o].amount ? tx.
vout[o].amount : tx_scan_info[o].amount;
2053 m_transfers.push_back(boost::value_initialized<transfer_details>());
2054 transfer_details &td = m_transfers.back();
2055 td.m_block_height =
height;
2056 td.m_internal_output_index = o;
2057 td.m_global_output_index = o_indices[o];
2060 td.m_key_image = tx_scan_info[o].ki;
2061 td.m_key_image_known = !m_watch_only && !m_multisig;
2062 if (!td.m_key_image_known) {
2064 std::unordered_map<crypto::public_key, crypto::key_image>::const_iterator i = m_cold_key_images.find(
2065 tx_scan_info[o].in_ephemeral.pub);
2066 if (i != m_cold_key_images.end()) {
2067 td.m_key_image = i->second;
2068 td.m_key_image_known =
true;
2073 td.m_key_image_request =
true;
2075 td.m_key_image_request =
false;
2077 td.m_key_image_partial = m_multisig;
2078 td.m_amount = amount;
2079 td.m_pk_index = pk_index - 1;
2080 td.m_subaddr_index = tx_scan_info[o].received->index;
2081 expand_subaddresses(tx_scan_info[o].received->index);
2087 td.m_frozen =
false;
2088 set_unspent(m_transfers.size() - 1);
2089 if (td.m_key_image_known)
2090 m_key_images[td.m_key_image] = m_transfers.size() - 1;
2091 m_pub_keys[tx_scan_info[o].in_ephemeral.pub] = m_transfers.size() - 1;
2092 if (output_tracker_cache)
2093 (*output_tracker_cache).first[std::make_pair(tx.
vout[o].amount, td.m_global_output_index)] =
2094 m_transfers.size() - 1;
2097 error::wallet_internal_error,
"NULL m_multisig_rescan_k");
2098 if (m_multisig_rescan_info &&
2099 m_multisig_rescan_info->front().size() >= m_transfers.size())
2100 update_multisig_rescan_info(*m_multisig_rescan_k, *m_multisig_rescan_info,
2101 m_transfers.size() - 1);
2104 if (0 != m_callback)
2105 m_callback->on_etn_received(
height, txid, tx, td.m_amount, td.m_subaddr_index,
2106 td.m_tx.unlock_time);
2108 total_received_1 += amount;
2110 }
else if (m_transfers[kit->second].m_spent ||
2112 m_transfers[kit->second].amount() >= tx_scan_info[o].amount) {
2114 <<
" from received " <<
print_etn(tx_scan_info[o].amount)
2115 <<
" output already exists with "
2116 << (m_transfers[kit->second].m_spent ?
"spent" :
"unspent") <<
" "
2117 <<
print_etn(m_transfers[kit->second].amount()) <<
" in tx "
2118 << m_transfers[kit->second].m_txid <<
", received output ignored");
2120 tx_etn_got_in_outs[tx_scan_info[o].received->index] < tx_scan_info[o].amount,
2121 error::wallet_internal_error,
"Unexpected values of new and old outputs");
2122 tx_etn_got_in_outs[tx_scan_info[o].received->index] -= tx_scan_info[o].amount;
2125 <<
" from received " <<
print_etn(tx_scan_info[o].amount)
2126 <<
" output already exists with "
2127 <<
print_etn(m_transfers[kit->second].amount())
2128 <<
", replacing with new output");
2131 tx_etn_got_in_outs[tx_scan_info[o].received->index] < tx_scan_info[o].amount,
2132 error::wallet_internal_error,
"Unexpected values of new and old outputs");
2134 error::wallet_internal_error,
2135 "Unexpected values of new and old outputs");
2136 tx_etn_got_in_outs[tx_scan_info[o].received->index] -= m_transfers[kit->second].amount();
2138 uint64_t amount = tx.
vout[o].amount ? tx.
vout[o].amount : tx_scan_info[o].amount;
2139 uint64_t extra_amount = amount - m_transfers[kit->second].amount();
2141 transfer_details &td = m_transfers[kit->second];
2142 td.m_block_height =
height;
2143 td.m_internal_output_index = o;
2144 td.m_global_output_index = o_indices[o];
2147 td.m_amount = amount;
2148 td.m_pk_index = pk_index - 1;
2149 td.m_subaddr_index = tx_scan_info[o].received->index;
2150 expand_subaddresses(tx_scan_info[o].received->index);
2156 if (output_tracker_cache)
2157 (*output_tracker_cache).first[std::make_pair(tx.
vout[o].amount,
2158 td.m_global_output_index)] = kit->second;
2161 error::wallet_internal_error,
"NULL m_multisig_rescan_k");
2162 if (m_multisig_rescan_info &&
2163 m_multisig_rescan_info->front().size() >= m_transfers.size())
2164 update_multisig_rescan_info(*m_multisig_rescan_k, *m_multisig_rescan_info,
2165 m_transfers.size() - 1);
2168 error::wallet_internal_error,
"Inconsistent public keys");
2170 "Inconsistent spent status");
2173 if (0 != m_callback)
2174 m_callback->on_etn_received(
height, txid, tx, td.m_amount, td.m_subaddr_index,
2175 td.m_tx.unlock_time);
2177 total_received_1 += extra_amount;
2184 if (!outs.empty() && num_vouts_received > 0) {
2187 for (
const size_t o: outs) {
2189 "wrong out in transaction: internal index=" +
2192 auto kit = m_chainstate_indexes.find(std::make_pair(txid, o));
2195 error::wallet_internal_error,
2196 std::string(
"Unexpected transfer index from chainstate index: ")
2198 (kit == m_chainstate_indexes.end() ?
"<none>" : boost::lexical_cast<std::string>(
2200 +
", m_transfers.size() is " +
2201 boost::lexical_cast<std::string>(m_transfers.size()));
2202 if (kit == m_chainstate_indexes.end()) {
2203 uint64_t amount = tx.
vout[o].amount ? tx.
vout[o].amount : tx_scan_info[o].amount;
2205 m_transfers.push_back(boost::value_initialized<transfer_details>());
2206 transfer_details &td = m_transfers.back();
2207 td.m_block_height =
height;
2208 td.m_internal_output_index = o;
2209 td.m_global_output_index = std::numeric_limits<uint64_t>::max();
2212 td.m_key_image = boost::value_initialized<crypto::key_image>();
2213 td.m_key_image_known =
false;
2214 td.m_key_image_partial =
false;
2215 td.m_amount = amount;
2216 td.m_pk_index = pk_index - 1;
2217 td.m_subaddr_index = tx_scan_info[o].received->index;
2218 expand_subaddresses(tx_scan_info[o].received->index);
2221 td.m_frozen =
false;
2222 set_unspent(m_transfers.size() - 1,
true);
2223 m_chainstate_indexes[std::make_pair(txid, o)] = m_transfers.size() - 1;
2224 if (output_tracker_cache) {
2225 std::array<char, 32> transaction_id;
2226 std::copy(std::begin(td.m_txid.data), std::end(td.m_txid.data), transaction_id.begin());
2227 (*output_tracker_cache).second[std::make_pair(transaction_id, td.m_internal_output_index)] =
2228 m_transfers.size() - 1;
2232 error::wallet_internal_error,
"NULL m_multisig_rescan_k");
2233 if (m_multisig_rescan_info &&
2234 m_multisig_rescan_info->front().size() >= m_transfers.size())
2235 update_multisig_rescan_info(*m_multisig_rescan_k, *m_multisig_rescan_info,
2236 m_transfers.size() - 1);
2239 if (0 != m_callback)
2240 m_callback->on_etn_received(
height, txid, tx, td.m_amount, td.m_subaddr_index,
2241 td.m_tx.unlock_time);
2243 total_received_1 += amount;
2245 }
else if (m_transfers[kit->second].m_spent ||
2247 m_transfers[kit->second].amount() >= tx_scan_info[o].amount) {
2249 <<
":" << kit->first.second
2250 <<
" from received " <<
print_etn(tx_scan_info[o].amount)
2251 <<
" output already exists with "
2252 << (m_transfers[kit->second].m_spent ?
"spent" :
"unspent") <<
" "
2253 <<
print_etn(m_transfers[kit->second].amount()) <<
" in tx "
2254 << m_transfers[kit->second].m_txid <<
", received output ignored");
2256 tx_etn_got_in_outs[tx_scan_info[o].received->index] < tx_scan_info[o].amount,
2257 error::wallet_internal_error,
"Unexpected values of new and old outputs");
2258 tx_etn_got_in_outs[tx_scan_info[o].received->index] -= tx_scan_info[o].amount;
2261 <<
":" << kit->first.second
2262 <<
" from received " <<
print_etn(tx_scan_info[o].amount)
2263 <<
" output already exists with "
2264 <<
print_etn(m_transfers[kit->second].amount())
2265 <<
", replacing with new output");
2268 tx_etn_got_in_outs[tx_scan_info[o].received->index] < tx_scan_info[o].amount,
2269 error::wallet_internal_error,
"Unexpected values of new and old outputs");
2271 error::wallet_internal_error,
2272 "Unexpected values of new and old outputs");
2273 tx_etn_got_in_outs[tx_scan_info[o].received->index] -= m_transfers[kit->second].amount();
2275 uint64_t amount = tx.
vout[o].amount ? tx.
vout[o].amount : tx_scan_info[o].amount;
2276 uint64_t extra_amount = amount - m_transfers[kit->second].amount();
2278 transfer_details &td = m_transfers[kit->second];
2279 td.m_block_height =
height;
2280 td.m_internal_output_index = o;
2281 td.m_global_output_index = std::numeric_limits<uint64_t>::max();
2284 td.m_amount = amount;
2285 td.m_pk_index = pk_index - 1;
2286 td.m_subaddr_index = tx_scan_info[o].received->index;
2287 expand_subaddresses(tx_scan_info[o].received->index);
2291 if (output_tracker_cache) {
2292 std::array<char, 32> transaction_id;
2293 std::copy(std::begin(td.m_txid.data), std::end(td.m_txid.data), transaction_id.begin());
2294 (*output_tracker_cache).second[std::make_pair(transaction_id,
2295 td.m_internal_output_index)] = kit->second;
2299 error::wallet_internal_error,
"NULL m_multisig_rescan_k");
2300 if (m_multisig_rescan_info &&
2301 m_multisig_rescan_info->front().size() >= m_transfers.size())
2302 update_multisig_rescan_info(*m_multisig_rescan_k, *m_multisig_rescan_info,
2303 m_transfers.size() - 1);
2306 error::wallet_internal_error,
"Inconsistent public keys");
2308 "Inconsistent spent status");
2311 if (0 != m_callback)
2312 m_callback->on_etn_received(
height, txid, tx, td.m_amount, td.m_subaddr_index,
2313 td.m_tx.unlock_time);
2315 total_received_1 += extra_amount;
2327 auto subaddr_account ([]()->boost::optional<uint32_t> {
return boost::none;}());
2328 std::set<uint32_t> subaddr_indices;
2330 for(
auto& in: tx.
vin)
2336 auto it = m_key_images.find(in_to_key.
k_image);
2337 if (it != m_key_images.end())
2339 transfer_details &td = m_transfers[it->second];
2342 if (amount != td.amount()) {
2343 MERROR(
"Inconsistent amount in tx input: got " <<
print_etn(amount) <<
2344 ", expected " <<
print_etn(td.amount()));
2349 td.m_amount = amount;
2352 amount = td.amount();
2354 tx_etn_spent_in_ins += amount;
2355 if (subaddr_account && *subaddr_account != td.m_subaddr_index.major)
2357 "spent funds are from different subaddress accounts; count of incoming/outgoing payments will be incorrect");
2358 subaddr_account = td.m_subaddr_index.major;
2359 subaddr_indices.insert(td.m_subaddr_index.minor);
2362 set_spent(it->second,
height);
2363 if (0 != m_callback)
2364 m_callback->on_etn_spent(
height, txid, tx, amount, tx, td.m_subaddr_index);
2368 if (!pool && m_track_uses) {
2373 if (output_tracker_cache) {
2375 const std::map<std::pair<uint64_t, uint64_t>,
size_t>::const_iterator i = output_tracker_cache->first.find(
2376 std::make_pair(amount, offset));
2377 if (i != output_tracker_cache->first.end()) {
2378 size_t idx = i->second;
2380 "Output tracker cache index out of range");
2381 m_transfers[idx].m_uses.push_back(std::make_pair(
height, txid));
2386 for (transfer_details &td: m_transfers) {
2387 if (amount != td.m_amount)
2390 if (offset == td.m_global_output_index)
2391 td.m_uses.push_back(std::make_pair(
height, txid));
2399 auto it = m_chainstate_indexes.find(std::make_pair(in_to_key_public.
tx_hash, in_to_key_public.
relative_offset));
2400 if (it != m_chainstate_indexes.end())
2402 transfer_details &td = m_transfers[it->second];
2405 if (amount != td.amount()) {
2406 MERROR(
"Inconsistent amount in tx input: got " <<
print_etn(amount) <<
2407 ", expected " <<
print_etn(td.amount()));
2412 td.m_amount = amount;
2415 amount = td.amount();
2417 tx_etn_spent_in_ins += amount;
2418 if (subaddr_account && *subaddr_account != td.m_subaddr_index.major)
2420 "spent funds are from different subaddress accounts; count of incoming/outgoing payments will be incorrect");
2421 subaddr_account = td.m_subaddr_index.major;
2422 subaddr_indices.insert(td.m_subaddr_index.minor);
2425 set_spent(it->second,
height,
true);
2426 if (0 != m_callback)
2427 m_callback->on_etn_spent(
height, txid, tx, amount, tx, td.m_subaddr_index);
2431 if (!pool && m_track_uses) {
2434 if (output_tracker_cache) {
2435 std::array<char, 32> transaction_id;
2436 std::copy(std::begin(in_to_key_public.
tx_hash.data), std::end(in_to_key_public.
tx_hash.data), transaction_id.begin());
2437 const std::map<std::pair<std::array<char, 32>,
size_t>,
size_t>::const_iterator i = output_tracker_cache->second.find(
2439 if (i != output_tracker_cache->second.end()) {
2440 size_t idx = i->second;
2442 "Output tracker cache index out of range");
2443 m_transfers[idx].m_uses.push_back(std::make_pair(
height, txid));
2446 for (transfer_details &td: m_transfers) {
2447 if (in_to_key_public.
tx_hash != td.m_txid)
2450 td.m_uses.push_back(std::make_pair(
height, txid));
2465 if (tx_etn_spent_in_ins > 0 && !pool)
2468 uint64_t self_received = std::accumulate<decltype(tx_etn_got_in_outs.begin()),
uint64_t>(tx_etn_got_in_outs.begin(), tx_etn_got_in_outs.end(), 0,
2469 [&subaddr_account] (
uint64_t acc,
const std::pair<cryptonote::subaddress_index, uint64_t>& p)
2471 return acc + (p.first.major == *subaddr_account ? p.second : 0);
2473 process_outgoing(txid, tx,
height, ts, tx_etn_spent_in_ins, self_received, *subaddr_account, subaddr_indices);
2475 if (tx_etn_spent_in_ins == self_received + fee)
2477 auto i = m_confirmed_txs.find(txid);
2481 i->second.m_change = self_received;
2487 for (
auto i = tx_etn_got_in_outs.begin(); i != tx_etn_got_in_outs.end();)
2489 if (subaddr_account && i->first.major == *subaddr_account)
2491 sub_change += i->second;
2492 i = tx_etn_got_in_outs.erase(i);
2499 if (tx_etn_got_in_outs.size() > 0)
2509 LOG_PRINT_L2(
"Found encrypted payment ID: " << payment_id8);
2510 MINFO(
"Consider using subaddresses instead of encrypted payment IDs");
2513 if (!m_account.get_device().decrypt_payment_id(payment_id8, tx_pub_key, m_account.get_keys().m_view_secret_key))
2515 LOG_PRINT_L0(
"Failed to decrypt payment ID: " << payment_id8);
2520 memcpy(payment_id.data, payment_id8.data, 8);
2522 memset(payment_id.data + 8, 0, 24);
2528 LOG_PRINT_L1(
"No public key found in tx, unable to decrypt payment id");
2533 LOG_PRINT_L2(
"Found unencrypted payment ID: " << payment_id);
2534 MWARNING(
"Found unencrypted payment ID: these are bad for privacy, consider using subaddresses instead");
2538 uint64_t total_received_2 = sub_change;
2539 for (
const auto& i : tx_etn_got_in_outs)
2540 total_received_2 += i.second;
2541 if (total_received_1 != total_received_2)
2544 MCLOG_RED(level,
"global",
"**********************************************************************");
2545 MCLOG_RED(level,
"global",
"Consistency failure in amounts received");
2546 MCLOG_RED(level,
"global",
"Check transaction " << txid);
2547 MCLOG_RED(level,
"global",
"**********************************************************************");
2552 bool all_same =
true;
2553 for (
const auto& i : tx_etn_got_in_outs)
2555 payment_details payment;
2556 payment.m_tx_hash = txid;
2557 payment.m_fee = fee;
2558 payment.m_amount = i.second;
2559 payment.m_block_height =
height;
2561 payment.m_timestamp = ts;
2562 payment.m_coinbase = miner_tx;
2563 payment.m_subaddr_index = i.first;
2565 if (emplace_or_replace(m_unconfirmed_payments, payment_id, pool_payment_details{payment, double_spend_seen, nonexistent_utxo_seen}))
2567 if (0 != m_callback)
2568 m_callback->on_unconfirmed_etn_received(
height, txid, tx, payment.m_amount, payment.m_subaddr_index);
2571 m_payments.emplace(payment_id, payment);
2572 LOG_PRINT_L2(
"Payment found in " << (pool ?
"pool" :
"block") <<
": " << payment_id <<
" / " << payment.m_tx_hash <<
" / " << payment.m_amount);
2576 if (pool && all_same)
2582 std::shared_ptr<tools::Notify> tx_notify = m_tx_notify;
2590 if (m_unconfirmed_txs.empty())
2593 auto unconf_it = m_unconfirmed_txs.find(txid);
2594 if(unconf_it != m_unconfirmed_txs.end()) {
2595 if (store_tx_info()) {
2597 m_confirmed_txs.insert(std::make_pair(txid, confirmed_transfer_details(unconf_it->second,
height)));
2601 LOG_PRINT_L0(
"Failed to add outgoing transaction to confirmed transaction map");
2604 m_unconfirmed_txs.erase(unconf_it);
2610 std::pair<std::unordered_map<crypto::hash, confirmed_transfer_details>::iterator,
bool> entry = m_confirmed_txs.insert(std::make_pair(txid, confirmed_transfer_details()));
2617 entry.first->second.m_amount_in = spent;
2620 entry.first->second.m_change = received;
2622 std::vector<tx_extra_field> tx_extra_fields;
2630 entry.first->second.m_subaddr_account = subaddr_account;
2631 entry.first->second.m_subaddr_indices = subaddr_indices;
2634 entry.first->second.m_rings.clear();
2635 for (
const auto &in: tx.
vin)
2639 const auto &txin = boost::get<cryptonote::txin_to_key>(in);
2640 entry.first->second.m_rings.push_back(std::make_pair(txin.k_image, txin.key_offsets));
2642 entry.first->second.m_block_height =
height;
2643 entry.first->second.m_timestamp = ts;
2644 entry.first->second.m_unlock_time = tx.
unlock_time;
2645 entry.first->second.m_is_migration = tx.
version == 2;
2650 std::vector<account_public_address> input_addresses;
2651 for (
auto minor_index : subaddr_indices) {
2653 input_addresses.push_back(get_subaddress(index));
2658 std::unordered_set<uint32_t> change_indexes;
2659 for (
size_t i = 0; i < tx.
vout.size(); ++i) {
2660 for (
auto input_address : input_addresses) {
2661 if (boost::get<txout_to_key_public>(tx.
vout[i].target).address == input_address) {
2662 change_indexes.insert(i);
2669 if (change_indexes.size() == tx.
vout.size()) {
2670 change_indexes.clear();
2674 for (
auto &change_index : change_indexes)
2675 total_change += tx.
vout[change_index].amount;
2676 entry.first->second.m_change = total_change;
2680 if (entry.first->second.m_dests.empty()) {
2683 for (
size_t i = 0; i < tx.
vout.size(); ++i) {
2684 if (change_indexes.find(i) == change_indexes.end()) {
2685 auto output = boost::get<txout_to_key_public>(tx.
vout[i].target);
2688 return destination.addr == output.address;
2693 auto dest_ptr = std::find_if(std::begin(entry.first->second.m_dests),
2694 std::end(entry.first->second.m_dests), pred);
2695 if (dest_ptr != std::end(entry.first->second.m_dests)) {
2696 dest_ptr->amount += tx.
vout[i].amount;
2701 output.m_address_prefix ==
2715 return !(b.
timestamp + 60*60*24 > m_account.get_createtime() &&
height >= m_refresh_from_block_height);
2718 void wallet2::process_new_blockchain_entry(
const cryptonote::block& b,
const cryptonote::block_complete_entry& bche,
const parsed_block &parsed_block,
const crypto::hash& bl_id,
uint64_t height,
const std::vector<tx_cache_data> &tx_cache_data,
size_t tx_cache_data_offset, std::pair<std::map<std::pair<uint64_t, uint64_t>,
size_t>, std::map<std::pair<std::array<char, 32>,
size_t>,
size_t>> *output_tracker_cache)
2722 error::wallet_internal_error,
2724 " not match with daemon response size=" +
2731 if (!should_skip_block(b,
height))
2735 if (m_refresh_type != RefreshNoCoinbase)
2736 process_new_transaction(
get_transaction_hash(b.
miner_tx), b.
miner_tx, parsed_block.o_indices.indices[0].indices,
height, b.
timestamp,
true,
false,
false,
false, tx_cache_data[tx_cache_data_offset], output_tracker_cache);
2737 ++tx_cache_data_offset;
2742 THROW_WALLET_EXCEPTION_IF(bche.
txs.size() != parsed_block.txes.size(), error::wallet_internal_error,
"Wrong amount of transactions for block");
2743 for (
size_t idx = 0; idx < b.
tx_hashes.size(); ++idx)
2745 process_new_transaction(b.
tx_hashes[idx], parsed_block.txes[idx], parsed_block.o_indices.indices[idx+1].indices,
height, b.
timestamp,
false,
false,
false,
false, tx_cache_data[tx_cache_data_offset++], output_tracker_cache);
2749 LOG_PRINT_L2(
"Processed block: " << bl_id <<
", height " <<
height <<
", " << miner_tx_handle_time + txs_handle_time <<
"(" << miner_tx_handle_time <<
"/" << txs_handle_time <<
")ms");
2753 LOG_PRINT_L2(
"Skipped block by timestamp, height: " <<
height <<
", block time " << b.
timestamp <<
", account time " << m_account.get_createtime());
2755 m_blockchain.push_back(bl_id);
2757 if (0 != m_callback)
2758 m_callback->on_new_block(
height, b);
2761 void wallet2::get_short_chain_history(std::list<crypto::hash>& ids,
uint64_t granularity)
const
2764 size_t current_multiplier = 1;
2765 size_t blockchain_size = std::max((
size_t)(m_blockchain.size() / granularity * granularity), m_blockchain.offset());
2766 size_t sz = blockchain_size - m_blockchain.offset();
2769 ids.push_back(m_blockchain.genesis());
2772 size_t current_back_offset = 1;
2773 bool base_included =
false;
2774 while(current_back_offset < sz)
2776 ids.push_back(m_blockchain[m_blockchain.offset() + sz-current_back_offset]);
2777 if(sz-current_back_offset == 0)
2778 base_included =
true;
2781 ++current_back_offset;
2784 current_back_offset += current_multiplier *= 2;
2789 ids.push_back(m_blockchain[m_blockchain.offset()]);
2790 if(m_blockchain.offset())
2791 ids.push_back(m_blockchain.genesis());
2799 void wallet2::pull_blocks(
uint64_t start_height,
uint64_t &blocks_start_height,
const std::list<crypto::hash> &short_chain_history, std::vector<cryptonote::block_complete_entry> &
blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices)
2803 req.block_ids = short_chain_history;
2806 req.start_height = start_height;
2807 req.no_miner_tx = m_refresh_type == RefreshNoCoinbase;
2808 m_daemon_rpc_mutex.lock();
2812 m_daemon_rpc_mutex.unlock();
2817 "mismatched blocks (" + boost::lexical_cast<std::string>(
res.blocks.size()) +
") and output_indices (" +
2818 boost::lexical_cast<std::string>(
res.output_indices.size()) +
") sizes from daemon");
2820 blocks_start_height =
res.start_height;
2832 MERROR(
"Failed to parse and validate tx from blob");
2836 std::stringstream ss;
2838 bool r = tx.serialize_base(ba);
2843 void wallet2::pull_hashes(
uint64_t start_height,
uint64_t &blocks_start_height,
const std::list<crypto::hash> &short_chain_history, std::vector<crypto::hash> &
hashes)
2847 req.block_ids = short_chain_history;
2849 req.start_height = start_height;
2850 m_daemon_rpc_mutex.lock();
2854 m_daemon_rpc_mutex.unlock();
2859 blocks_start_height =
res.start_height;
2864 void wallet2::process_parsed_blocks(
uint64_t start_height,
const std::vector<cryptonote::block_complete_entry> &
blocks,
const std::vector<parsed_block> &parsed_blocks,
uint64_t& blocks_added, std::pair<std::map<std::pair<uint64_t, uint64_t>,
size_t>, std::map<std::pair<std::array<char, 32>,
size_t>,
size_t>> *output_tracker_cache)
2866 size_t current_index = start_height;
2876 size_t num_txes = 0;
2877 std::vector<tx_cache_data> tx_cache_data;
2878 for (
size_t i = 0; i <
blocks.size(); ++i)
2879 num_txes += 1 + parsed_blocks[i].txes.size();
2880 tx_cache_data.resize(num_txes);
2882 for (
size_t i = 0; i <
blocks.size(); ++i)
2885 error::wallet_internal_error,
"Mismatched parsed_blocks[i].txes.size() and parsed_blocks[i].block.tx_hashes.size()");
2888 if (should_skip_block(parsed_blocks[i].
block, start_height + i)){
2889 txidx += 1 + parsed_blocks[i].block.tx_hashes.size();
2892 if (m_refresh_type != RefreshNoCoinbase)
2893 tpool.
submit(&waiter, [&, i, txidx]() {
2895 tx_cache_data[txidx]);
2898 for (
size_t idx = 0; idx < parsed_blocks[i].txes.size(); ++idx) {
2899 tpool.
submit(&waiter, [&, i, idx, txidx]() {
2900 cache_tx_data(parsed_blocks[i].txes[idx], parsed_blocks[i].
block.
tx_hashes[idx], tx_cache_data[txidx]);
2907 waiter.
wait(&tpool);
2914 auto gender = [&](wallet2::is_out_data &iod) {
2917 MWARNING(
"Failed to generate key derivation from tx pubkey, skipping");
2918 static_assert(
sizeof(iod.derivation) ==
sizeof(
rct::key),
"Mismatched sizes of key_derivation and rct::key");
2924 for (
size_t i = 0; i < tx_cache_data.size(); ++i) {
2925 if (tx_cache_data[i].
empty())
2927 if(!tx_cache_data[i].public_only()){
2928 tpool.
submit(&waiter, [&hwdev, &gender, &tx_cache_data, i]() {
2929 auto &slot = tx_cache_data[i];
2930 boost::unique_lock<hw::device> hwdev_lock(hwdev);
2931 for (
auto &iod: slot.primary)
2933 for (
auto &iod: slot.additional)
2939 waiter.
wait(&tpool);
2942 for (
size_t k = 0; k < n_vouts; ++k)
2944 const auto &o = tx.
vout[k];
2947 std::vector<crypto::key_derivation> additional_derivations;
2948 additional_derivations.reserve(tx_cache_data[txidx].additional.size());
2949 for (
const auto &iod: tx_cache_data[txidx].additional)
2950 additional_derivations.push_back(iod.derivation);
2951 const auto &
key = boost::get<txout_to_key>(o.target).key;
2952 for (
size_t l = 0; l < tx_cache_data[txidx].primary.size(); ++l)
2957 error::wallet_internal_error,
"Unexpected received array size");
2959 tx_cache_data[txidx].primary[l].received[k] =
is_out_to_acc_precomp(m_subaddresses,
key, tx_cache_data[txidx].primary[l].derivation, additional_derivations, k, hwdev);
2960 additional_derivations.clear();
2963 const auto etn_address = boost::get<txout_to_key_public>(o.target).address;
2965 error::wallet_internal_error,
"Unexpected received array size");
2967 error::wallet_internal_error,
"Unexpected received vector size");
2973 tx_cache_data[txidx].public_outs[0].received[k] =
2974 (receive_info == boost::none) ?
2976 get_subaddress(receive_info->index).m_view_public_key == etn_address.m_view_public_key ?
2977 receive_info : boost::none;
2985 for (
size_t i = 0; i <
blocks.size(); ++i)
2987 if (should_skip_block(parsed_blocks[i].
block, start_height + i))
2989 txidx += 1 + parsed_blocks[i].block.tx_hashes.size();
2994 if (m_refresh_type != RefreshType::RefreshNoCoinbase)
2997 const size_t n_vouts = m_refresh_type == RefreshType::RefreshOptimizeCoinbase ? 1 : parsed_blocks[i].block.miner_tx.vout.size();
2998 tpool.
submit(&waiter, [&, i, n_vouts, txidx](){ geniod(parsed_blocks[i].
block.
miner_tx, n_vouts, txidx); },
true);
3001 for (
size_t j = 0; j < parsed_blocks[i].txes.size(); ++j)
3004 tpool.
submit(&waiter, [&, i, j, txidx](){ geniod(parsed_blocks[i].txes[j], parsed_blocks[i].txes[j].vout.size(), txidx); },
true);
3008 THROW_WALLET_EXCEPTION_IF(txidx != tx_cache_data.size(), error::wallet_internal_error,
"txidx did not reach expected value");
3009 waiter.
wait(&tpool);
3012 size_t tx_cache_data_offset = 0;
3013 for (
size_t i = 0; i <
blocks.size(); ++i)
3018 if(current_index >= m_blockchain.size())
3020 process_new_blockchain_entry(bl,
blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset, output_tracker_cache);
3023 else if(bl_id != m_blockchain[current_index])
3028 " (height " +
std::to_string(start_height) +
"), local block id at this height: " +
3031 detach_blockchain(current_index, output_tracker_cache);
3032 process_new_blockchain_entry(bl,
blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset, output_tracker_cache);
3039 tx_cache_data_offset += 1 + parsed_blocks[i].txes.size();
3043 void wallet2::refresh(
bool trusted_daemon)
3046 refresh(trusted_daemon, 0, blocks_fetched);
3051 bool received_etn =
false;
3052 refresh(trusted_daemon, start_height, blocks_fetched, received_etn);
3055 void wallet2::pull_and_parse_next_blocks(
uint64_t start_height,
uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history,
const std::vector<cryptonote::block_complete_entry> &prev_blocks,
const std::vector<parsed_block> &prev_parsed_blocks, std::vector<cryptonote::block_complete_entry> &
blocks, std::vector<parsed_block> &parsed_blocks,
bool &
error)
3061 drop_from_short_history(short_chain_history, 3);
3066 auto s = std::next(prev_parsed_blocks.rbegin(), std::min((
size_t)3, prev_parsed_blocks.size())).base();
3067 for (; s != prev_parsed_blocks.end(); ++s)
3069 short_chain_history.push_front(s->hash);
3073 std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> o_indices;
3074 pull_blocks(start_height, blocks_start_height, short_chain_history,
blocks, o_indices);
3079 parsed_blocks.resize(
blocks.size());
3080 for (
size_t i = 0; i <
blocks.size(); ++i)
3082 tpool.
submit(&waiter, boost::bind(&wallet2::parse_block_round,
this, std::cref(
blocks[i].
block),
3083 std::ref(parsed_blocks[i].
block), std::ref(parsed_blocks[i].
hash), std::ref(parsed_blocks[i].
error)),
true);
3085 waiter.
wait(&tpool);
3086 for (
size_t i = 0; i <
blocks.size(); ++i)
3088 if (parsed_blocks[i].
error)
3093 parsed_blocks[i].o_indices =
std::move(o_indices[i]);
3096 boost::mutex error_lock;
3097 for (
size_t i = 0; i <
blocks.size(); ++i)
3099 parsed_blocks[i].txes.resize(
blocks[i].txs.size());
3100 for (
size_t j = 0; j <
blocks[i].txs.size(); ++j)
3102 tpool.
submit(&waiter, [&, i, j](){
3105 boost::unique_lock<boost::mutex> lock(error_lock);
3111 waiter.
wait(&tpool);
3119 void wallet2::remove_obsolete_pool_txs(
const std::vector<crypto::hash> &tx_hashes)
3122 std::unordered_multimap<crypto::hash, wallet2::pool_payment_details>::iterator uit = m_unconfirmed_payments.begin();
3123 while (uit != m_unconfirmed_payments.end())
3127 for (
const auto &it2: tx_hashes)
3138 MDEBUG(
"Removing " << txid <<
" from unconfirmed payments, not found in pool");
3139 m_unconfirmed_payments.erase(pit);
3140 if (0 != m_callback)
3141 m_callback->on_pool_tx_removed(txid);
3147 void wallet2::update_pool_state(
bool refreshed)
3149 MTRACE(
"update_pool_state start");
3152 if (m_encrypt_keys_after_refresh)
3154 encrypt_keys(*m_encrypt_keys_after_refresh);
3155 m_encrypt_keys_after_refresh = boost::none;
3162 m_daemon_rpc_mutex.lock();
3164 m_daemon_rpc_mutex.unlock();
3168 MTRACE(
"update_pool_state got pool");
3171 std::unordered_map<crypto::hash, wallet2::unconfirmed_transfer_details>::iterator it = m_unconfirmed_txs.begin();
3172 while (it != m_unconfirmed_txs.end())
3176 for (
const auto &it2:
res.tx_hashes)
3193 if (pit->second.m_state == wallet2::unconfirmed_transfer_details::pending)
3195 LOG_PRINT_L1(
"Pending txid " << txid <<
" not in pool, marking as not in pool");
3196 pit->second.m_state = wallet2::unconfirmed_transfer_details::pending_not_in_pool;
3198 else if (pit->second.m_state == wallet2::unconfirmed_transfer_details::pending_not_in_pool && refreshed)
3200 LOG_PRINT_L1(
"Pending txid " << txid <<
" not in pool, marking as failed");
3201 pit->second.m_state = wallet2::unconfirmed_transfer_details::failed;
3204 remove_rings(pit->second.m_tx);
3205 for (
size_t vini = 0; vini < pit->second.m_tx.vin.size(); ++vini)
3207 if (pit->second.m_tx.vin[vini].type() ==
typeid(
txin_to_key))
3209 txin_to_key &tx_in_to_key = boost::get<txin_to_key>(pit->second.m_tx.vin[vini]);
3210 for (
size_t i = 0; i < m_transfers.size(); ++i)
3225 MTRACE(
"update_pool_state done first loop");
3232 remove_obsolete_pool_txs(
res.tx_hashes);
3234 MTRACE(
"update_pool_state done second loop");
3237 std::vector<std::pair<crypto::hash, bool>> txids;
3238 for (
const auto &txid:
res.tx_hashes)
3240 bool txid_found_in_up =
false;
3241 for (
const auto &up: m_unconfirmed_payments)
3243 if (up.second.m_pd.m_tx_hash == txid)
3245 txid_found_in_up =
true;
3249 if (m_scanned_pool_txs[0].find(txid) != m_scanned_pool_txs[0].end() || m_scanned_pool_txs[1].find(txid) != m_scanned_pool_txs[1].end())
3252 if (!txid_found_in_up)
3254 LOG_PRINT_L2(
"Already seen " << txid <<
", and not for us, skipped");
3258 if (!txid_found_in_up)
3262 for (
const auto &i: m_unconfirmed_txs)
3264 if (i.first == txid)
3270 for (
const auto& dst : utd.
m_dests)
3272 auto subaddr_index = m_subaddresses.find(dst.addr.m_spend_public_key);
3273 if (subaddr_index != m_subaddresses.end() && subaddr_index->second.major != utd.
m_subaddr_account)
3285 txids.push_back({txid,
false});
3295 txids.push_back({txid,
true});
3304 for (
const auto &p: txids)
3306 MDEBUG(
"asking for " << txids.size() <<
" transactions");
3307 req.decode_as_json =
false;
3309 m_daemon_rpc_mutex.lock();
3311 m_daemon_rpc_mutex.unlock();
3312 MDEBUG(
"Got " << r <<
" and " <<
res.status);
3315 if (
res.txs.size() == txids.size())
3317 for (
const auto &tx_entry:
res.txs)
3319 if (tx_entry.in_pool)
3325 if (get_pruned_tx(tx_entry, tx, tx_hash))
3327 const std::vector<std::pair<crypto::hash, bool>>::const_iterator i = std::find_if(txids.begin(), txids.end(),
3328 [tx_hash](
const std::pair<crypto::hash, bool> &e) { return e.first == tx_hash; });
3329 if (i != txids.end())
3331 process_new_transaction(tx_hash, tx, std::vector<uint64_t>(), 0,
time(NULL),
false,
true, tx_entry.double_spend_seen,
false, {});
3332 m_scanned_pool_txs[0].insert(tx_hash);
3333 if (m_scanned_pool_txs[0].size() > 5000)
3335 std::swap(m_scanned_pool_txs[0], m_scanned_pool_txs[1]);
3336 m_scanned_pool_txs[0].clear();
3341 MERROR(
"Got txid " << tx_hash <<
" which we did not ask for");
3346 LOG_PRINT_L0(
"Failed to parse transaction from daemon");
3351 LOG_PRINT_L1(
"Transaction from daemon was in pool, but is no more");
3357 LOG_PRINT_L0(
"Expected " << txids.size() <<
" tx(es), got " <<
res.txs.size());
3362 LOG_PRINT_L0(
"Error calling gettransactions daemon RPC: r " << r <<
", status " << get_rpc_status(
res.status));
3365 MTRACE(
"update_pool_state end");
3369 void wallet2::fast_refresh(
uint64_t stop_height,
uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history,
bool force)
3371 std::vector<crypto::hash>
hashes;
3373 const uint64_t checkpoint_height = m_checkpoints.get_max_height();
3374 if ((stop_height > checkpoint_height && m_blockchain.size()-1 < checkpoint_height) && !force)
3377 uint64_t missing_blocks = m_checkpoints.get_max_height() - m_blockchain.size();
3378 while (missing_blocks-- > 0)
3379 m_blockchain.push_back(crypto::null_hash);
3380 m_blockchain.push_back(m_checkpoints.get_points().at(checkpoint_height));
3381 m_blockchain.trim(checkpoint_height);
3382 short_chain_history.clear();
3383 get_short_chain_history(short_chain_history);
3386 size_t current_index = m_blockchain.size();
3387 while(m_run.load(std::memory_order_relaxed) && current_index < stop_height)
3389 pull_hashes(0, blocks_start_height, short_chain_history,
hashes);
3392 if (blocks_start_height < m_blockchain.offset())
3394 MERROR(
"Blocks start before blockchain offset: " << blocks_start_height <<
" " << m_blockchain.offset());
3397 if (
hashes.size() + current_index < stop_height) {
3398 drop_from_short_history(short_chain_history, 3);
3399 std::vector<crypto::hash>::iterator right =
hashes.end();
3401 for (
int i = 0; i<3; i++) {
3403 short_chain_history.push_front(*right);
3406 current_index = blocks_start_height;
3409 if(current_index >= m_blockchain.size())
3411 if (!(current_index % 1024))
3412 LOG_PRINT_L2(
"Skipped block by height: " << current_index);
3413 m_blockchain.push_back(bl_id);
3415 if (0 != m_callback)
3418 m_callback->on_new_block(current_index, dummy);
3421 else if(bl_id != m_blockchain[current_index])
3427 if (current_index >= stop_height)
3438 a.m_payment_id = payment_id;
3439 a.m_description = description;
3440 a.m_is_subaddress = is_subaddress;
3442 auto old_size = m_address_book.size();
3443 m_address_book.push_back(
a);
3444 if(m_address_book.size() == old_size+1)
3449 bool wallet2::delete_address_book_row(std::size_t row_id) {
3450 if(m_address_book.size() <= row_id)
3453 m_address_book.erase(m_address_book.begin()+row_id);
3459 std::shared_ptr<std::pair<std::map<std::pair<uint64_t, uint64_t>,
size_t>, std::map<std::pair<std::array<char, 32>,
size_t>,
size_t>>> wallet2::create_output_tracker_cache()
const
3462 std::shared_ptr<std::pair<std::map<std::pair<uint64_t, uint64_t>,
size_t>, std::map<std::pair<std::array<char,32>,
size_t>,
size_t>>> cache{
new std::pair<std::map<std::pair<uint64_t, uint64_t>,
size_t>, std::map<std::pair<std::array<char,32>,
size_t>,
size_t>>()};
3463 for (
size_t i = 0; i < m_transfers.size(); ++i)
3465 const transfer_details &td = m_transfers[i];
3467 (*cache).first[std::make_pair(td.is_rct() ? 0 : td.amount(), td.m_global_output_index)] = i;
3469 std::array<char, 32> transaction_id;
3470 std::copy(std::begin(td.m_txid.data), std::end(td.m_txid.data), transaction_id.begin());
3471 (*cache).second[std::make_pair(transaction_id, td.m_internal_output_index)] = i;
3476 void wallet2::refresh(
bool trusted_daemon,
uint64_t start_height,
uint64_t & blocks_fetched,
bool& received_etn,
bool check_pool)
3485 if(m_light_wallet) {
3492 if(light_wallet_get_address_info(
res)) {
3494 uint64_t prev_height = m_light_wallet_blockchain_height;
3496 m_light_wallet_scanned_block_height =
res.scanned_block_height;
3497 m_light_wallet_blockchain_height =
res.blockchain_height;
3499 if(m_light_wallet_blockchain_height != prev_height)
3501 MDEBUG(
"new block since last time!");
3502 m_callback->on_lw_new_block(m_light_wallet_blockchain_height - 1);
3504 m_light_wallet_connected =
true;
3505 MDEBUG(
"lw scanned block height: " << m_light_wallet_scanned_block_height);
3506 MDEBUG(
"lw blockchain height: " << m_light_wallet_blockchain_height);
3507 MDEBUG(m_light_wallet_blockchain_height-m_light_wallet_scanned_block_height <<
" blocks behind");
3510 light_wallet_get_address_txs();
3512 m_light_wallet_connected =
false;
3517 received_etn =
false;
3520 size_t try_count = 0;
3521 crypto::hash last_tx_hash_id = m_transfers.size() ? m_transfers.back().m_txid : null_hash;
3522 std::list<crypto::hash> short_chain_history;
3526 std::vector<cryptonote::block_complete_entry>
blocks;
3527 std::vector<parsed_block> parsed_blocks;
3528 bool refreshed =
false;
3529 std::shared_ptr<std::pair<std::map<std::pair<uint64_t, uint64_t>,
size_t>, std::map<std::pair<std::array<char, 32>,
size_t>,
size_t>>> output_tracker_cache;
3534 m_run.store(
true, std::memory_order_relaxed);
3535 if (start_height > m_blockchain.size() || m_refresh_from_block_height > m_blockchain.size()) {
3537 start_height = m_refresh_from_block_height;
3539 fast_refresh(start_height, blocks_start_height, short_chain_history);
3541 short_chain_history.clear();
3548 if(!m_run.load(std::memory_order_relaxed))
3555 if (m_encrypt_keys_after_refresh)
3557 encrypt_keys(*m_encrypt_keys_after_refresh);
3558 m_encrypt_keys_after_refresh = boost::none;
3564 while(m_run.load(std::memory_order_relaxed))
3567 std::vector<cryptonote::block_complete_entry> next_blocks;
3568 std::vector<parsed_block> next_parsed_blocks;
3574 next_blocks.clear();
3575 next_parsed_blocks.clear();
3577 if (!first &&
blocks.empty())
3582 tpool.
submit(&waiter, [&]{pull_and_parse_next_blocks(start_height, next_blocks_start_height, short_chain_history,
blocks, parsed_blocks, next_blocks, next_parsed_blocks,
error);});
3588 process_parsed_blocks(blocks_start_height,
blocks, parsed_blocks, added_blocks, output_tracker_cache.get());
3592 MINFO(
"Daemon claims next refresh block is out of hash chain bounds, resetting hash chain");
3593 uint64_t stop_height = m_blockchain.offset();
3594 std::vector<crypto::hash> tip(m_blockchain.size() - m_blockchain.offset());
3595 for (
size_t i = m_blockchain.offset(); i < m_blockchain.size(); ++i)
3596 tip[i - m_blockchain.offset()] = m_blockchain[i];
3598 generate_genesis(b);
3599 m_blockchain.clear();
3601 short_chain_history.clear();
3602 get_short_chain_history(short_chain_history);
3603 fast_refresh(stop_height, blocks_start_height, short_chain_history,
true);
3606 for (
const auto &h: tip)
3607 m_blockchain.push_back(h);
3608 short_chain_history.clear();
3609 get_short_chain_history(short_chain_history);
3610 start_height = stop_height;
3611 throw std::runtime_error(
"");
3613 catch (
const std::exception &e)
3615 MERROR(
"Error parsing blocks: " << e.what());
3618 blocks_fetched += added_blocks;
3620 waiter.
wait(&tpool);
3621 if(!first && blocks_start_height == next_blocks_start_height)
3623 m_node_rpc_proxy.set_height(m_blockchain.size());
3633 throw std::runtime_error(
"proxy exception in refresh thread");
3639 if (m_track_uses && (!output_tracker_cache || (output_tracker_cache->first.empty() && output_tracker_cache->second.empty())) && next_blocks.size() >= 10)
3640 output_tracker_cache = create_output_tracker_cache();
3643 blocks_start_height = next_blocks_start_height;
3645 parsed_blocks =
std::move(next_parsed_blocks);
3649 blocks_fetched += added_blocks;
3650 waiter.
wait(&tpool);
3653 catch (
const std::exception&)
3655 blocks_fetched += added_blocks;
3656 waiter.
wait(&tpool);
3659 LOG_PRINT_L1(
"Another try pull_blocks (try_count=" << try_count <<
")...");
3663 parsed_blocks.clear();
3664 short_chain_history.clear();
3665 get_short_chain_history(short_chain_history, 1);
3670 LOG_ERROR(
"pull_blocks failed, try_count=" << try_count);
3675 if(last_tx_hash_id != (m_transfers.size() ? m_transfers.back().m_txid : null_hash))
3676 received_etn =
true;
3681 if (check_pool && m_run.load(std::memory_order_relaxed))
3682 update_pool_state(refreshed);
3689 m_first_refresh_done =
true;
3691 LOG_PRINT_L1(
"Refresh done, blocks received: " << blocks_fetched <<
", pre v10 balance (all accounts): " <<
print_etn(balance_all(
false)) <<
", unlocked: " <<
print_etn(unlocked_balance_all(
false)) <<
", post v10 balance (all accounts): " <<
print_etn(balance_all(
true)) <<
", unlocked: " <<
print_etn(unlocked_balance_all(
true)));
3697 uint64_t migration_minheight = this->nettype() ==
TESTNET ? 1165235 + 5 : 1175315 + 5;
3698 if (this->get_blockchain_current_height() > migration_minheight && this->unlocked_balance_all(
false) != 0) {
3699 LOG_PRINT_L0(
"You are now on the transparent version of Electroneum and so we're giving you the chance to migrate your funds via a sweep transaction back to your address.\n Don't worry, this migration is completely free of charge. Please follow the prompts to continue.");
3700 std::map < uint32_t, std::map < uint32_t, std::pair <uint64_t, uint64_t>>> unlocked_balance_per_subaddress_per_account;
3702 for (
uint32_t account_index = 0; account_index < this->get_num_subaddress_accounts(); ++account_index) {
3703 unlocked_balance_per_subaddress_per_account[account_index] = this->unlocked_balance_per_subaddress(
3704 account_index,
false);
3706 for (
uint32_t i = 0; i < this->get_num_subaddress_accounts(); i++) {
3709 for (
auto subaddress: unlocked_balance_per_subaddress_per_account[i]) {
3710 index.
minor = subaddress.first;
3712 if (subaddress.second.first != 0 &&
3713 subaddress.second.second == 0) {
3715 std::set <uint32_t> subaddress_source{index.
minor};
3716 std::vector <wallet2::pending_tx> ptx_vector = this->create_transactions_all(0,
3729 this->commit_tx(ptx_vector);
3741 bool wallet2::refresh(
bool trusted_daemon,
uint64_t & blocks_fetched,
bool& received_etn,
bool& ok)
3745 refresh(trusted_daemon, 0, blocks_fetched, received_etn);
3755 bool wallet2::get_rct_distribution(
uint64_t &start_height, std::vector<uint64_t> &distribution)
3758 boost::optional<std::string> result = m_node_rpc_proxy.get_rpc_version(rpc_version);
3767 MDEBUG(
"Cannot determine daemon RPC version, not requesting rct distribution");
3775 MDEBUG(
"Daemon is recent enough, requesting rct distribution");
3779 MDEBUG(
"Daemon is too old, not requesting rct distribution");
3786 req.amounts.push_back(0);
3787 req.from_height = 0;
3788 req.cumulative =
false;
3790 req.compress =
true;
3791 m_daemon_rpc_mutex.lock();
3793 m_daemon_rpc_mutex.unlock();
3796 MWARNING(
"Failed to request output distribution: no connection to daemon");
3801 MWARNING(
"Failed to request output distribution: daemon is busy");
3806 MWARNING(
"Failed to request output distribution: " <<
res.status);
3809 if (
res.distributions.size() != 1)
3811 MWARNING(
"Failed to request output distribution: not the expected single result");
3814 if (
res.distributions[0].amount != 0)
3816 MWARNING(
"Failed to request output distribution: results are not for amount 0");
3819 for (
size_t i = 1; i <
res.distributions[0].data.distribution.size(); ++i)
3820 res.distributions[0].data.distribution[i] +=
res.distributions[0].data.distribution[i-1];
3821 start_height =
res.distributions[0].data.start_height;
3822 distribution =
std::move(
res.distributions[0].data.distribution);
3826 void wallet2::detach_blockchain(
uint64_t height, std::pair<std::map<std::pair<uint64_t, uint64_t>,
size_t>, std::map<std::pair<std::array<char, 32>,
size_t>,
size_t>> *output_tracker_cache)
3834 error::wallet_internal_error,
"Daemon claims reorg below last checkpoint");
3836 size_t transfers_detached = 0;
3838 for (
size_t i = 0; i < m_transfers.size(); ++i)
3840 wallet2::transfer_details &td = m_transfers[i];
3841 if (td.m_spent && td.m_spent_height >=
height)
3843 if(td.m_tx.version == 1){
3844 LOG_PRINT_L1(
"Resetting spent/frozen status for output " << i <<
": " << td.m_key_image);
3846 LOG_PRINT_L1(
"Resetting spent/frozen status for output "
3847 << i <<
": " <<
"chainstate index " << td.m_txid <<
": " << td.m_internal_output_index);
3854 for (transfer_details &td: m_transfers)
3856 while (!td.m_uses.empty() && td.m_uses.back().first >=
height)
3857 td.m_uses.pop_back();
3860 if (output_tracker_cache) {
3861 output_tracker_cache->first.clear();
3862 output_tracker_cache->second.clear();
3865 auto it = std::find_if(m_transfers.begin(), m_transfers.end(), [&](
const transfer_details& td){return td.m_block_height >= height;});
3866 size_t i_start = it - m_transfers.begin();
3868 for(
size_t i = i_start; i!= m_transfers.size();i++)
3870 if (!m_transfers[i].m_key_image_known || m_transfers[i].m_key_image_partial)
3872 auto it_ki = m_key_images.find(m_transfers[i].m_key_image);
3874 m_key_images.erase(it_ki);
3877 for(
size_t i = i_start; i!= m_transfers.size();i++)
3879 auto it_pk = m_pub_keys.find(m_transfers[i].get_public_key());
3881 m_pub_keys.erase(it_pk);
3884 for(
size_t i = i_start; i!= m_transfers.size();i++)
3886 auto it_pk = m_chainstate_indexes.find(m_transfers[i].get_chainstate_index());
3887 if(m_transfers[i].m_tx.version > 1) {
3889 "chainstate index not found");
3890 m_chainstate_indexes.erase(it_pk);
3897 m_transfers.erase(it, m_transfers.end());
3899 size_t blocks_detached = m_blockchain.size() -
height;
3900 m_blockchain.crop(
height);
3902 for (
auto it = m_payments.begin(); it != m_payments.end(); )
3904 if(height <= it->second.m_block_height)
3905 it = m_payments.erase(it);
3910 for (
auto it = m_confirmed_txs.begin(); it != m_confirmed_txs.end(); )
3912 if(height <= it->second.m_block_height)
3913 it = m_confirmed_txs.erase(it);
3918 LOG_PRINT_L0(
"Detached blockchain on height " <<
height <<
", transfers detached " << transfers_detached <<
", blocks detached " << blocks_detached);
3923 m_is_initialized=
false;
3929 bool wallet2::clear()
3931 m_blockchain.clear();
3932 m_transfers.clear();
3933 m_key_images.clear();
3935 m_chainstate_indexes.clear();
3936 m_unconfirmed_txs.clear();
3939 m_additional_tx_keys.clear();
3940 m_confirmed_txs.clear();
3941 m_unconfirmed_payments.clear();
3942 m_scanned_pool_txs[0].clear();
3943 m_scanned_pool_txs[1].clear();
3944 m_address_book.clear();
3945 m_subaddresses.clear();
3946 m_subaddress_labels.clear();
3947 m_multisig_rounds_passed = 0;
3948 m_device_last_key_image_sync = 0;
3952 void wallet2::clear_soft(
bool keep_key_images)
3954 m_blockchain.clear();
3955 m_transfers.clear();
3956 if (!keep_key_images)
3957 m_key_images.clear();
3959 m_chainstate_indexes.clear();
3960 m_unconfirmed_txs.clear();
3962 m_confirmed_txs.clear();
3963 m_unconfirmed_payments.clear();
3964 m_scanned_pool_txs[0].clear();
3965 m_scanned_pool_txs[1].clear();
3968 generate_genesis(b);
3987 crypto::chacha_key
key;
3988 crypto::generate_chacha_key(password.
data(), password.
size(),
key, m_kdf_rounds);
3990 if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
4003 wallet2::keys_file_data keys_file_data = boost::value_initialized<wallet2::keys_file_data>();
4009 value.SetString(account_data.c_str(), account_data.length());
4011 if (!seed_language.empty())
4013 value.SetString(seed_language.c_str(), seed_language.length());
4014 json.AddMember(
"seed_language",
value,
json.GetAllocator());
4019 value2.SetInt(m_key_device_type);
4020 json.AddMember(
"key_on_device", value2,
json.GetAllocator());
4022 value2.SetInt(watch_only ? 1 :0);
4023 json.AddMember(
"watch_only", value2,
json.GetAllocator());
4025 value2.SetInt(m_multisig ? 1 :0);
4026 json.AddMember(
"multisig", value2,
json.GetAllocator());
4028 value2.SetUint(m_multisig_threshold);
4029 json.AddMember(
"multisig_threshold", value2,
json.GetAllocator());
4035 value.SetString(multisig_signers.c_str(), multisig_signers.length());
4036 json.AddMember(
"multisig_signers",
value,
json.GetAllocator());
4040 value.SetString(multisig_derivations.c_str(), multisig_derivations.length());
4041 json.AddMember(
"multisig_derivations",
value,
json.GetAllocator());
4043 value2.SetUint(m_multisig_rounds_passed);
4044 json.AddMember(
"multisig_rounds_passed", value2,
json.GetAllocator());
4047 value2.SetInt(m_always_confirm_transfers ? 1 :0);
4048 json.AddMember(
"always_confirm_transfers", value2,
json.GetAllocator());
4050 value2.SetInt(m_print_ring_members ? 1 :0);
4051 json.AddMember(
"print_ring_members", value2,
json.GetAllocator());
4053 value2.SetInt(m_store_tx_info ? 1 :0);
4054 json.AddMember(
"store_tx_info", value2,
json.GetAllocator());
4056 value2.SetUint(m_default_mixin);
4057 json.AddMember(
"default_mixin", value2,
json.GetAllocator());
4059 value2.SetUint(m_default_priority);
4060 json.AddMember(
"default_priority", value2,
json.GetAllocator());
4062 value2.SetInt(m_auto_refresh ? 1 :0);
4063 json.AddMember(
"auto_refresh", value2,
json.GetAllocator());
4065 value2.SetInt(m_refresh_type);
4066 json.AddMember(
"refresh_type", value2,
json.GetAllocator());
4068 value2.SetUint64(m_refresh_from_block_height);
4069 json.AddMember(
"refresh_height", value2,
json.GetAllocator());
4071 value2.SetInt(m_confirm_missing_payment_id ? 1 :0);
4072 json.AddMember(
"confirm_missing_payment_id", value2,
json.GetAllocator());
4074 value2.SetInt(m_confirm_non_default_ring_size ? 1 :0);
4075 json.AddMember(
"confirm_non_default_ring_size", value2,
json.GetAllocator());
4077 value2.SetInt(m_ask_password);
4078 json.AddMember(
"ask_password", value2,
json.GetAllocator());
4080 value2.SetUint(m_min_output_count);
4081 json.AddMember(
"min_output_count", value2,
json.GetAllocator());
4083 value2.SetUint64(m_min_output_value);
4084 json.AddMember(
"min_output_value", value2,
json.GetAllocator());
4087 json.AddMember(
"default_decimal_point", value2,
json.GetAllocator());
4089 value2.SetInt(m_merge_destinations ? 1 :0);
4090 json.AddMember(
"merge_destinations", value2,
json.GetAllocator());
4092 value2.SetInt(m_confirm_backlog ? 1 :0);
4093 json.AddMember(
"confirm_backlog", value2,
json.GetAllocator());
4095 value2.SetUint(m_confirm_backlog_threshold);
4096 json.AddMember(
"confirm_backlog_threshold", value2,
json.GetAllocator());
4098 value2.SetInt(m_confirm_export_overwrite ? 1 :0);
4099 json.AddMember(
"confirm_export_overwrite", value2,
json.GetAllocator());
4101 value2.SetInt(m_auto_low_priority ? 1 : 0);
4102 json.AddMember(
"auto_low_priority", value2,
json.GetAllocator());
4104 value2.SetUint(m_nettype);
4105 json.AddMember(
"nettype", value2,
json.GetAllocator());
4107 value2.SetInt(m_segregate_pre_fork_outputs ? 1 : 0);
4108 json.AddMember(
"segregate_pre_fork_outputs", value2,
json.GetAllocator());
4110 value2.SetInt(m_key_reuse_mitigation2 ? 1 : 0);
4111 json.AddMember(
"key_reuse_mitigation2", value2,
json.GetAllocator());
4113 value2.SetUint(m_segregation_height);
4114 json.AddMember(
"segregation_height", value2,
json.GetAllocator());
4116 value2.SetInt(m_ignore_fractional_outputs ? 1 : 0);
4117 json.AddMember(
"ignore_fractional_outputs", value2,
json.GetAllocator());
4119 value2.SetInt(m_track_uses ? 1 : 0);
4120 json.AddMember(
"track_uses", value2,
json.GetAllocator());
4122 value2.SetInt(m_setup_background_mining);
4123 json.AddMember(
"setup_background_mining", value2,
json.GetAllocator());
4125 value2.SetUint(m_subaddress_lookahead_major);
4126 json.AddMember(
"subaddress_lookahead_major", value2,
json.GetAllocator());
4128 value2.SetUint(m_subaddress_lookahead_minor);
4129 json.AddMember(
"subaddress_lookahead_minor", value2,
json.GetAllocator());
4131 value2.SetInt(m_original_keys_available ? 1 : 0);
4132 json.AddMember(
"original_keys_available", value2,
json.GetAllocator());
4135 json.AddMember(
"encrypted_secret_keys", value2,
json.GetAllocator());
4137 value.SetString(m_device_name.c_str(), m_device_name.size());
4140 value.SetString(m_device_derivation_path.c_str(), m_device_derivation_path.size());
4141 json.AddMember(
"device_derivation_path",
value,
json.GetAllocator());
4143 value2.SetUint(m_account_major_offset);
4144 json.AddMember(
"account_major_offset", value2,
json.GetAllocator());
4148 if (m_original_keys_available)
4151 value.SetString(original_address.c_str(), original_address.length());
4152 json.AddMember(
"original_address",
value,
json.GetAllocator());
4154 value.SetString(original_view_secret_key.c_str(), original_view_secret_key.length());
4155 json.AddMember(
"original_view_secret_key",
value,
json.GetAllocator());
4160 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
4161 json.Accept(writer);
4162 account_data = buffer.GetString();
4166 cipher.resize(account_data.size());
4167 keys_file_data.iv = crypto::rand<crypto::chacha_iv>();
4168 crypto::chacha20(account_data.data(), account_data.size(),
key, keys_file_data.iv, &cipher[0]);
4169 keys_file_data.account_data = cipher;
4171 std::string tmp_file_name = keys_file_name +
".new";
4182 boost::filesystem::remove(tmp_file_name);
4183 LOG_ERROR(
"failed to update wallet keys file " << keys_file_name);
4192 crypto::chacha_key
key;
4193 crypto::generate_chacha_key(password.
data(), password.
size(),
key, m_kdf_rounds);
4196 if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
4198 m_account.encrypt_keys(
key);
4199 m_account.decrypt_viewkey(
key);
4202 static_assert(
HASH_SIZE ==
sizeof(crypto::chacha_key),
"Mismatched sizes of hash and chacha key");
4212 if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
4213 decrypt_keys(original_password);
4214 setup_keys(new_password);
4215 rewrite(filename, new_password);
4216 if (!filename.empty())
4230 bool encrypted_secret_keys =
false;
4237 crypto::chacha_key
key;
4238 crypto::generate_chacha_key(password.
data(), password.
size(),
key, m_kdf_rounds);
4240 account_data.resize(keys_file_data.
account_data.size());
4242 if (
json.Parse(account_data.c_str()).HasParseError() || !
json.IsObject())
4246 if (
json.Parse(account_data.c_str()).HasParseError())
4248 is_old_file_format =
true;
4249 m_watch_only =
false;
4251 m_multisig_threshold = 0;
4252 m_multisig_signers.clear();
4253 m_multisig_rounds_passed = 0;
4254 m_multisig_derivations.clear();
4255 m_always_confirm_transfers =
true;
4256 m_print_ring_members =
false;
4257 m_store_tx_info =
true;
4258 m_default_mixin = 0;
4259 m_default_priority = 0;
4260 m_auto_refresh =
true;
4261 m_refresh_type = RefreshType::RefreshDefault;
4262 m_refresh_from_block_height = 0;
4263 m_confirm_missing_payment_id =
true;
4264 m_confirm_non_default_ring_size =
true;
4265 m_ask_password = AskPasswordOnAction;
4267 m_min_output_count = 0;
4268 m_min_output_value = 0;
4269 m_merge_destinations =
false;
4270 m_confirm_backlog =
true;
4271 m_confirm_backlog_threshold = 0;
4272 m_confirm_export_overwrite =
true;
4273 m_auto_low_priority =
true;
4274 m_segregate_pre_fork_outputs =
true;
4275 m_key_reuse_mitigation2 =
true;
4276 m_segregation_height = 0;
4277 m_ignore_fractional_outputs =
true;
4278 m_track_uses =
false;
4279 m_setup_background_mining = BackgroundMiningMaybe;
4282 m_original_keys_available =
false;
4284 m_device_derivation_path =
"";
4285 m_key_device_type = hw::device::device_type::SOFTWARE;
4286 m_account_major_offset = 0;
4287 encrypted_secret_keys =
false;
4289 else if(
json.IsObject())
4291 if (!
json.HasMember(
"key_data"))
4293 LOG_ERROR(
"Field key_data not found in JSON");
4296 if (!
json[
"key_data"].IsString())
4298 LOG_ERROR(
"Field key_data found in JSON, but not String");
4301 const char *field_key_data =
json[
"key_data"].GetString();
4302 account_data =
std::string(field_key_data, field_key_data +
json[
"key_data"].GetStringLength());
4304 if (
json.HasMember(
"key_on_device"))
4311 if (field_seed_language_found)
4313 set_seed_language(field_seed_language);
4316 m_watch_only = field_watch_only;
4318 m_multisig = field_multisig;
4320 m_multisig_threshold = field_multisig_threshold;
4322 m_multisig_rounds_passed = field_multisig_rounds_passed;
4325 if (!
json.HasMember(
"multisig_signers"))
4327 LOG_ERROR(
"Field multisig_signers not found in JSON");
4330 if (!
json[
"multisig_signers"].IsString())
4332 LOG_ERROR(
"Field multisig_signers found in JSON, but not String");
4335 const char *field_multisig_signers =
json[
"multisig_signers"].GetString();
4336 std::string multisig_signers =
std::string(field_multisig_signers, field_multisig_signers +
json[
"multisig_signers"].GetStringLength());
4340 LOG_ERROR(
"Field multisig_signers found in JSON, but failed to parse");
4345 if (
json.HasMember(
"multisig_derivations"))
4347 if (!
json[
"multisig_derivations"].IsString())
4349 LOG_ERROR(
"Field multisig_derivations found in JSON, but not String");
4352 const char *field_multisig_derivations =
json[
"multisig_derivations"].GetString();
4353 std::string multisig_derivations =
std::string(field_multisig_derivations, field_multisig_derivations +
json[
"multisig_derivations"].GetStringLength());
4357 LOG_ERROR(
"Field multisig_derivations found in JSON, but failed to parse");
4363 m_always_confirm_transfers = field_always_confirm_transfers;
4365 m_print_ring_members = field_print_ring_members;
4368 m_store_tx_info = ((field_store_tx_keys != 0) || (field_store_tx_info != 0));
4370 m_default_mixin = field_default_mixin;
4372 if (field_default_priority_found)
4374 m_default_priority = field_default_priority;
4379 if (field_default_fee_multiplier_found)
4380 m_default_priority = field_default_fee_multiplier;
4382 m_default_priority = 0;
4385 m_auto_refresh = field_auto_refresh;
4387 m_refresh_type = RefreshType::RefreshDefault;
4388 if (field_refresh_type_found)
4390 if (field_refresh_type == RefreshFull || field_refresh_type == RefreshOptimizeCoinbase || field_refresh_type == RefreshNoCoinbase)
4391 m_refresh_type = (RefreshType)field_refresh_type;
4393 LOG_PRINT_L0(
"Unknown refresh-type value (" << field_refresh_type <<
"), using default");
4396 m_refresh_from_block_height = field_refresh_height;
4398 m_confirm_missing_payment_id = field_confirm_missing_payment_id;
4400 m_confirm_non_default_ring_size = field_confirm_non_default_ring_size;
4402 m_ask_password = field_ask_password;
4406 m_min_output_count = field_min_output_count;
4408 m_min_output_value = field_min_output_value;
4410 m_merge_destinations = field_merge_destinations;
4412 m_confirm_backlog = field_confirm_backlog;
4414 m_confirm_backlog_threshold = field_confirm_backlog_threshold;
4416 m_confirm_export_overwrite = field_confirm_export_overwrite;
4418 m_auto_low_priority = field_auto_low_priority;
4422 (boost::format(
"%s wallet cannot be opened as %s wallet")
4423 % (field_nettype == 0 ?
"Mainnet" : field_nettype == 1 ?
"Testnet" :
"Stagenet")
4424 % (m_nettype ==
MAINNET ?
"mainnet" : m_nettype ==
TESTNET ?
"testnet" :
"stagenet")).str());
4426 m_segregate_pre_fork_outputs = field_segregate_pre_fork_outputs;
4428 m_key_reuse_mitigation2 = field_key_reuse_mitigation2;
4430 m_segregation_height = field_segregation_height;
4432 m_ignore_fractional_outputs = field_ignore_fractional_outputs;
4434 m_track_uses = field_track_uses;
4436 m_setup_background_mining = field_setup_background_mining;
4438 m_subaddress_lookahead_major = field_subaddress_lookahead_major;
4440 m_subaddress_lookahead_minor = field_subaddress_lookahead_minor;
4443 encrypted_secret_keys = field_encrypted_secret_keys;
4446 m_account_major_offset = field_account_major_offset;
4449 if (m_device_name.empty())
4451 if (field_device_name_found)
4453 m_device_name = field_device_name;
4457 m_device_name = m_key_device_type == hw::device::device_type::LEDGER ?
"Ledger" :
"default";
4462 m_device_derivation_path = field_device_derivation_path;
4464 if (
json.HasMember(
"original_keys_available"))
4467 m_original_keys_available = field_original_keys_available;
4468 if (m_original_keys_available)
4475 LOG_ERROR(
"Failed to parse original_address from JSON");
4478 m_original_address =
info.address;
4483 LOG_ERROR(
"Failed to parse original_view_secret_key from JSON");
4490 m_original_keys_available =
false;
4501 if (m_key_device_type == hw::device::device_type::LEDGER || m_key_device_type == hw::device::device_type::TREZOR) {
4503 hw::device &hwdev = lookup_device(m_device_name);
4510 m_account.set_device(hwdev);
4514 THROW_WALLET_EXCEPTION_IF(device_account_public_address != m_account.get_keys().m_account_address, error::wallet_internal_error,
"Device wallet does not match wallet address. "
4516 ", wallet address: " + m_account.get_public_address_str(m_nettype));
4518 }
else if (key_on_device()) {
4524 if (encrypted_secret_keys)
4526 m_account.decrypt_keys(
key);
4531 if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
4533 bool saved_ret = store_keys(keys_file_name, password, m_watch_only);
4537 MERROR(
"Error saving keys file with encrypted keys, not fatal");
4539 if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
4541 m_keys_file_locker.reset();
4552 setup_keys(password);
4571 bool r = verify_password(m_keys_file, password, m_account.get_device().device_protocol() ==
hw::device::PROTOCOL_COLD || m_watch_only || m_multisig, m_account.get_device(), m_kdf_rounds);
4594 bool encrypted_secret_keys =
false;
4601 crypto::chacha_key
key;
4602 crypto::generate_chacha_key(password.
data(), password.
size(),
key, kdf_rounds);
4606 if (
json.Parse(account_data.c_str()).HasParseError() || !
json.IsObject())
4610 if (
json.Parse(account_data.c_str()).HasParseError())
4617 json[
"key_data"].GetStringLength());
4619 encrypted_secret_keys = field_encrypted_secret_keys;
4626 if (encrypted_secret_keys)
4636 void wallet2::encrypt_keys(
const crypto::chacha_key &
key)
4638 m_account.encrypt_keys(
key);
4639 m_account.decrypt_viewkey(
key);
4642 void wallet2::decrypt_keys(
const crypto::chacha_key &
key)
4644 m_account.encrypt_viewkey(
key);
4645 m_account.decrypt_keys(
key);
4650 crypto::chacha_key
key;
4651 crypto::generate_chacha_key(password.
data(), password.
size(),
key, m_kdf_rounds);
4657 crypto::chacha_key
key;
4658 crypto::generate_chacha_key(password.
data(), password.
size(),
key, m_kdf_rounds);
4662 void wallet2::setup_new_blockchain()
4665 generate_genesis(b);
4668 add_subaddress_account(
tr(
"Primary account"));
4673 if (!wallet_.empty())
4675 bool r = store_keys(m_keys_file, password, watch_only);
4678 if (create_address_file)
4681 if(!r)
MERROR(
"String with address text not saved");
4708 crypto::chacha_key
key;
4709 crypto::generate_chacha_key(password.
data(), password.
size(),
key, kdf_rounds);
4713 if (
json.Parse(account_data.c_str()).HasParseError() || !
json.IsObject())
4716 device_type = hw::device::device_type::SOFTWARE;
4718 if (
json.Parse(account_data.c_str()).HasParseError())
4725 json[
"key_data"].GetStringLength());
4727 if (
json.HasMember(
"key_on_device"))
4737 if (!r)
return false;
4743 m_account_public_address = m_account.get_keys().m_account_address;
4744 m_watch_only =
false;
4746 m_multisig_threshold = 0;
4747 m_multisig_signers.clear();
4748 m_original_keys_available =
false;
4749 m_key_device_type = device_type;
4763 prepare_file_names(wallet_);
4765 if (!wallet_.empty())
4767 boost::system::error_code ignored_ec;
4772 m_account.generate(rct::rct2sk(
rct::zero()),
true,
false);
4785 std::vector<crypto::secret_key> multisig_keys;
4786 std::vector<crypto::public_key> multisig_signers;
4795 for (
size_t n = 0; n < n_multisig_keys; ++n)
4800 for (
size_t n = 0; n < total; ++n)
4813 for (
const auto &msk: multisig_keys)
4818 m_account.make_multisig(view_secret_key, spend_secret_key, spend_public_key, multisig_keys);
4819 m_account.finalize_multisig(spend_public_key);
4824 init_type(hw::device::device_type::SOFTWARE);
4827 m_multisig_signers = multisig_signers;
4828 setup_keys(password);
4830 create_keys_file(wallet_,
false, password, m_nettype !=
MAINNET || create_address_file);
4831 setup_new_blockchain();
4833 if (!wallet_.empty())
4848 const crypto::secret_key& recovery_param,
bool recover,
bool two_random,
bool create_address_file)
4851 prepare_file_names(wallet_);
4853 if (!wallet_.empty())
4855 boost::system::error_code ignored_ec;
4862 init_type(hw::device::device_type::SOFTWARE);
4863 setup_keys(password);
4866 if(m_refresh_from_block_height == 0 && !recover){
4867 m_refresh_from_block_height = estimate_blockchain_height();
4870 create_keys_file(wallet_,
false, password, m_nettype !=
MAINNET || create_address_file);
4872 setup_new_blockchain();
4874 if (!wallet_.empty())
4886 const uint64_t blocks_per_month = 60*60*24*30/seconds_per_block;
4904 height = get_approximate_blockchain_height();
4905 uint64_t target_height = get_daemon_blockchain_target_height(err);
4907 if (target_height <
height)
4911 if (
height > blocks_per_month)
4912 height -= blocks_per_month;
4916 uint64_t local_height = get_daemon_blockchain_height(err);
4917 if (err.empty() && local_height >
height)
4935 prepare_file_names(wallet_);
4937 if (!wallet_.empty())
4939 boost::system::error_code ignored_ec;
4945 init_type(hw::device::device_type::SOFTWARE);
4946 m_watch_only =
true;
4948 setup_keys(password);
4950 create_keys_file(wallet_,
true, password, m_nettype !=
MAINNET || create_address_file);
4952 setup_new_blockchain();
4954 if (!wallet_.empty())
4972 prepare_file_names(wallet_);
4974 if (!wallet_.empty())
4976 boost::system::error_code ignored_ec;
4982 init_type(hw::device::device_type::SOFTWARE);
4984 setup_keys(password);
4986 create_keys_file(wallet_,
false, password, create_address_file);
4988 setup_new_blockchain();
4990 if (!wallet_.empty())
5003 prepare_file_names(wallet_);
5005 boost::system::error_code ignored_ec;
5006 if (!wallet_.empty()) {
5011 auto &hwdev = lookup_device(device_name);
5017 m_account.create_from_device(hwdev);
5018 init_type(m_account.get_device().get_type());
5019 setup_keys(password);
5020 m_device_name = device_name;
5022 create_keys_file(wallet_,
false, password, m_nettype !=
MAINNET || create_address_file);
5026 m_subaddress_lookahead_major = 5;
5027 m_subaddress_lookahead_minor = 20;
5029 setup_new_blockchain();
5030 if (!wallet_.empty()) {
5036 const std::vector<crypto::secret_key> &view_keys,
5037 const std::vector<crypto::public_key> &spend_keys,
5045 std::vector<crypto::secret_key> multisig_keys;
5048 std::vector<crypto::public_key> multisig_signers;
5052 if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
5054 crypto::chacha_key chacha_key;
5055 crypto::generate_chacha_key(password.
data(), password.
size(), chacha_key, m_kdf_rounds);
5056 m_account.encrypt_viewkey(chacha_key);
5057 m_account.decrypt_keys(chacha_key);
5079 MINFO(
"Creating spend key...");
5085 multisig_signers = spend_keys;
5086 multisig_signers.push_back(get_multisig_signer_public_key(get_account().get_keys().m_spend_secret_key));
5095 multisig_signers = std::vector<crypto::public_key>(spend_keys.size() + 1,
crypto::null_pkey);
5103 MINFO(
"Creating spend key...");
5116 extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, secret_keys_to_public_keys(multisig_keys), rct::rct2sk(spend_skey));
5121 MINFO(
"Preparing keys for next exchange round...");
5124 extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, derivations, m_account.get_keys().m_spend_secret_key);
5125 spend_skey = rct::sk2rct(m_account.get_keys().m_spend_secret_key);
5128 m_multisig_derivations = derivations;
5132 if (!m_original_keys_available)
5136 m_original_address = m_account.get_keys().m_account_address;
5137 m_original_view_secret_key = m_account.get_keys().m_view_secret_key;
5138 m_original_keys_available =
true;
5142 MINFO(
"Creating view key...");
5145 MINFO(
"Creating multisig address...");
5146 CHECK_AND_ASSERT_THROW_MES(m_account.make_multisig(view_skey, rct::rct2sk(spend_skey), rct::rct2pk(spend_pkey), multisig_keys),
5147 "Failed to create multisig wallet due to bad keys");
5150 init_type(hw::device::device_type::SOFTWARE);
5151 m_original_keys_available =
true;
5154 m_multisig_signers = multisig_signers;
5155 ++m_multisig_rounds_passed;
5160 create_keys_file(m_wallet_file,
false, password, boost::filesystem::exists(m_wallet_file +
".address.txt"));
5162 setup_new_blockchain();
5164 if (!m_wallet_file.empty())
5167 return extra_multisig_info;
5171 const std::vector<std::string> &
info)
5176 if (
info[0].substr(0, MULTISIG_EXTRA_INFO_MAGIC.size()) != MULTISIG_EXTRA_INFO_MAGIC)
5182 std::vector<crypto::public_key> signers;
5183 std::unordered_set<crypto::public_key> pkeys;
5188 return exchange_multisig_keys(password, pkeys, signers);
5192 std::unordered_set<crypto::public_key> derivations,
5193 std::vector<crypto::public_key> signers)
5204 if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
5206 crypto::chacha_key chacha_key;
5207 crypto::generate_chacha_key(password.
data(), password.
size(), chacha_key, m_kdf_rounds);
5208 m_account.encrypt_viewkey(chacha_key);
5209 m_account.decrypt_keys(chacha_key);
5219 if (std::find(signers.begin(), signers.end(), local_signer) == signers.end())
5221 signers.push_back(local_signer);
5222 for (
const auto &msk: get_account().get_multisig_keys())
5232 m_account_public_address.m_spend_public_key = spend_public_key;
5233 m_account.finalize_multisig(spend_public_key);
5235 m_multisig_signers = signers;
5238 ++m_multisig_rounds_passed;
5239 m_multisig_derivations.clear();
5244 if (!m_wallet_file.empty())
5246 bool r = store_keys(m_keys_file, password,
false);
5249 if (boost::filesystem::exists(m_wallet_file +
".address.txt"))
5252 if(!r)
MERROR(
"String with address text not saved");
5256 m_subaddresses.clear();
5257 m_subaddress_labels.clear();
5258 add_subaddress_account(
tr(
"Primary account"));
5260 if (!m_wallet_file.empty())
5268 for (
const auto&
key: m_multisig_derivations)
5269 derivations.erase(
key);
5278 MINFO(
"Creating spend key...");
5285 m_account.make_multisig(m_account.get_keys().m_view_secret_key, spend_skey, rct::rct2pk(
rct::identity()), multisig_keys);
5288 extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, secret_keys_to_public_keys(multisig_keys), spend_skey);
5293 MINFO(
"Preparing keys for next exchange round...");
5294 extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, new_derivations, m_account.get_keys().m_spend_secret_key);
5295 m_multisig_derivations = new_derivations;
5298 ++m_multisig_rounds_passed;
5300 create_keys_file(m_wallet_file,
false, password, boost::filesystem::exists(m_wallet_file +
".address.txt"));
5301 return extra_multisig_info;
5304 void wallet2::unpack_multisig_info(
const std::vector<std::string>&
info,
5305 std::vector<crypto::public_key> &public_keys,
5306 std::vector<crypto::secret_key> &secret_keys)
const
5309 public_keys.resize(
info.size());
5310 secret_keys.resize(
info.size());
5311 for (
size_t i = 0; i <
info.size(); ++i)
5318 for (
size_t i = 0; i < secret_keys.size(); ++i)
5320 for (
size_t j = i + 1; j < secret_keys.size(); ++j)
5322 if (rct::sk2rct(secret_keys[i]) == rct::sk2rct(secret_keys[j]))
5324 MDEBUG(
"Duplicate key found, ignoring");
5325 secret_keys[j] = secret_keys.back();
5326 public_keys[j] = public_keys.back();
5327 secret_keys.pop_back();
5328 public_keys.pop_back();
5336 const crypto::public_key local_pkey = get_multisig_signer_public_key(get_account().get_keys().m_spend_secret_key);
5337 for (
size_t i = 0; i < secret_keys.size(); ++i)
5339 if (secret_keys[i] == local_skey)
5341 MDEBUG(
"Local key is present, ignoring");
5342 secret_keys[i] = secret_keys.back();
5343 public_keys[i] = public_keys.back();
5344 secret_keys.pop_back();
5345 public_keys.pop_back();
5351 "Found local spend public key, but not local view secret key - something very weird");
5357 const std::vector<std::string> &
info,
5360 std::vector<crypto::secret_key> secret_keys(
info.size());
5361 std::vector<crypto::public_key> public_keys(
info.size());
5362 unpack_multisig_info(
info, public_keys, secret_keys);
5363 return make_multisig(password, secret_keys, public_keys,
threshold);
5366 bool wallet2::finalize_multisig(
const epee::wipeable_string &password,
const std::unordered_set<crypto::public_key> &pkeys, std::vector<crypto::public_key> signers)
5370 if (!multisig(&ready, &
threshold, &total))
5372 MERROR(
"This is not a multisig wallet");
5377 MERROR(
"This multisig wallet is already finalized");
5382 MERROR(
"finalize_multisig should only be used for N-1/N wallets, use exchange_multisig_keys instead");
5385 exchange_multisig_keys(password, pkeys, signers);
5389 bool wallet2::unpack_extra_multisig_info(
const std::vector<std::string>&
info,
5390 std::vector<crypto::public_key> &signers,
5391 std::unordered_set<crypto::public_key> &pkeys)
const
5395 for (
size_t i = 0; i <
info.size(); ++i)
5397 if (!verify_extra_multisig_info(
info[i], pkeys, signers[i]))
5408 std::unordered_set<crypto::public_key> public_keys;
5409 std::vector<crypto::public_key> signers;
5410 if (!unpack_extra_multisig_info(
info, signers, public_keys))
5412 MERROR(
"Bad multisig info");
5416 return finalize_multisig(password, public_keys, signers);
5423 const crypto::public_key pkey = get_multisig_signer_public_key(get_account().get_keys().m_spend_secret_key);
5440 const size_t header_len = strlen(
"MultisigV1");
5441 if (data.size() < header_len || data.substr(0, header_len) !=
"MultisigV1")
5443 MERROR(
"Multisig info header check error");
5449 MERROR(
"Multisig info decoding error");
5454 MERROR(
"Multisig info is corrupt");
5460 offset +=
sizeof(skey);
5462 offset +=
sizeof(pkey);
5469 MERROR(
"Multisig info signature is invalid");
5478 if (data.size() < MULTISIG_EXTRA_INFO_MAGIC.size() || data.substr(0, MULTISIG_EXTRA_INFO_MAGIC.size()) != MULTISIG_EXTRA_INFO_MAGIC)
5480 MERROR(
"Multisig info header check error");
5486 MERROR(
"Multisig info decoding error");
5491 MERROR(
"Multisig info is corrupt");
5496 MERROR(
"Multisig info is corrupt");
5503 offset +=
sizeof(signer);
5510 MERROR(
"Multisig info signature is invalid");
5514 for (
size_t n = 0; n < n_keys; ++n)
5518 offset +=
sizeof(mspk);
5531 *total = m_multisig_signers.size();
5533 *ready = !(get_account().get_keys().m_account_address.m_spend_public_key == rct::rct2pk(
rct::identity()));
5537 bool wallet2::has_multisig_partial_key_images()
const
5541 for (
const auto &td: m_transfers)
5542 if (td.m_key_image_partial && td.m_tx.version == 1)
5547 bool wallet2::has_unknown_key_images()
const
5549 for (
const auto &td: m_transfers)
5550 if (!td.m_key_image_known && td.m_tx.version == 1)
5562 if (wallet_name.empty())
5564 prepare_file_names(wallet_name);
5565 boost::system::error_code ignored_ec;
5567 bool r = store_keys(m_keys_file, password, m_watch_only);
5578 prepare_file_names(wallet_name);
5579 boost::system::error_code ignored_ec;
5580 new_keys_filename = m_wallet_file +
"-watchonly.keys";
5581 bool watch_only_keys_file_exists = boost::filesystem::exists(new_keys_filename, ignored_ec);
5583 bool r = store_keys(new_keys_filename, password,
true);
5587 void wallet2::wallet_exists(
const std::string& file_path,
bool& keys_file_exists,
bool& wallet_file_exists)
5590 do_prepare_file_names(file_path, keys_file, wallet_file, mms_file);
5592 boost::system::error_code ignore;
5593 keys_file_exists = boost::filesystem::exists(keys_file, ignore);
5594 wallet_file_exists = boost::filesystem::exists(wallet_file, ignore);
5599 return !file_path.empty();
5611 payment_id = *
reinterpret_cast<const crypto::hash*
>(payment_id_data.data());
5624 payment_id = *
reinterpret_cast<const crypto::hash8*
>(payment_id_data.data());
5630 if (parse_long_payment_id(payment_id_str, payment_id))
5633 if (parse_short_payment_id(payment_id_str, payment_id8))
5635 memcpy(payment_id.data, payment_id8.data, 8);
5636 memset(payment_id.data + 8, 0, 24);
5642 bool wallet2::prepare_file_names(
const std::string& file_path)
5644 do_prepare_file_names(file_path, m_keys_file, m_wallet_file, m_mms_file);
5662 if(m_light_wallet) {
5666 *ssl = m_light_wallet_connected;
5667 return m_light_wallet_connected;
5671 boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex);
5672 if(!m_http_client.is_connected(ssl))
5674 m_node_rpc_proxy.invalidate();
5675 if (!m_http_client.connect(std::chrono::milliseconds(timeout)))
5677 if(!m_http_client.is_connected(ssl))
5700 void wallet2::set_offline(
bool offline)
5702 m_offline = offline;
5703 m_http_client.set_auto_connect(!offline);
5706 boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex);
5707 if(m_http_client.is_connected())
5708 m_http_client.disconnect();
5712 bool wallet2::generate_chacha_key_from_secret_keys(crypto::chacha_key &
key)
const
5720 crypto::generate_chacha_key(pass.
data(), pass.
size(),
key, m_kdf_rounds);
5726 prepare_file_names(wallet_);
5728 boost::system::error_code e;
5729 bool exists = boost::filesystem::exists(m_keys_file, e);
5736 if (!load_keys(m_keys_file, password))
5740 LOG_PRINT_L0(
"Loaded wallet keys file, with public address: " << m_account.get_public_address_str(m_nettype));
5743 wallet_keys_unlocker unlocker(*
this, m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only, password);
5747 if(!boost::filesystem::exists(m_wallet_file, e) || e)
5749 LOG_PRINT_L0(
"file not found: " << m_wallet_file <<
", starting with empty blockchain");
5750 m_account_public_address = m_account.get_keys().m_account_address;
5771 std::stringstream iss;
5779 crypto::chacha_key
key;
5780 generate_chacha_key_from_secret_keys(
key);
5783 std::stringstream iss;
5793 std::stringstream iss;
5800 LOG_PRINT_L0(
"Failed to open portable binary, trying unportable");
5801 boost::filesystem::copy_file(m_wallet_file, m_wallet_file +
".unportable", boost::filesystem::copy_option::overwrite_if_exists);
5802 std::stringstream iss;
5805 boost::archive::binary_iarchive ar(iss);
5813 LOG_PRINT_L1(
"Failed to load encrypted cache, trying unencrypted");
5815 std::stringstream iss;
5822 LOG_PRINT_L0(
"Failed to open portable binary, trying unportable");
5823 boost::filesystem::copy_file(m_wallet_file, m_wallet_file +
".unportable", boost::filesystem::copy_option::overwrite_if_exists);
5824 std::stringstream iss;
5827 boost::archive::binary_iarchive ar(iss);
5832 m_account_public_address.m_spend_public_key != m_account.get_keys().m_account_address.m_spend_public_key ||
5833 m_account_public_address.m_view_public_key != m_account.get_keys().m_account_address.m_view_public_key,
5838 generate_genesis(genesis);
5841 if (m_blockchain.empty())
5843 m_blockchain.push_back(genesis_hash);
5848 check_genesis(genesis_hash);
5853 if (get_num_subaddress_accounts() == 0)
5854 add_subaddress_account(
tr(
"Primary account"));
5858 find_and_save_rings(
false);
5860 catch (
const std::exception &e)
5862 MERROR(
"Failed to save rings, will try again next time");
5867 m_message_store.read_from_file(get_multisig_wallet_state(), m_mms_file);
5869 catch (
const std::exception &e)
5871 MERROR(
"Failed to initialize MMS, it will be unusable");
5875 void wallet2::trim_hashchain()
5879 for (
const transfer_details &td: m_transfers)
5880 if (td.m_block_height <
height)
5881 height = td.m_block_height;
5883 if (!m_blockchain.empty() && m_blockchain.size() == m_blockchain.offset())
5885 MINFO(
"Fixing empty hashchain");
5888 m_daemon_rpc_mutex.lock();
5889 req.height = m_blockchain.size() - 1;
5891 m_daemon_rpc_mutex.unlock();
5896 m_blockchain.refill(
hash);
5900 MERROR(
"Failed to request block header from daemon, hash chain may be unable to sync till the wallet is loaded with a usable daemon");
5906 MDEBUG(
"trimming to " <<
height <<
", offset " << m_blockchain.offset());
5907 m_blockchain.trim(
height);
5911 void wallet2::check_genesis(
const crypto::hash& genesis_hash)
const {
5912 std::string what(
"Genesis block mismatch. You probably use wallet without testnet (or stagenet) flag with blockchain from test (or stage) network or vice versa");
5919 return m_wallet_file;
5922 void wallet2::store()
5924 if (!m_wallet_file.empty())
5938 bool same_file =
true;
5941 std::string canonical_path = boost::filesystem::canonical(m_wallet_file).string();
5942 size_t pos = canonical_path.find(path);
5943 same_file = pos != std::string::npos;
5950 boost::filesystem::path parent_path = boost::filesystem::path(path).parent_path();
5953 if (!parent_path.empty() && !boost::filesystem::exists(parent_path))
5955 boost::system::error_code ec;
5956 if (!boost::filesystem::create_directories(parent_path, ec))
5958 throw std::logic_error(ec.message());
5963 std::stringstream oss;
5975 const std::string new_file = same_file ? m_wallet_file +
".new" : path;
5978 const std::string old_address_file = m_wallet_file +
".address.txt";
5984 prepare_file_names(path);
5985 bool r = store_keys(m_keys_file, password,
false);
5987 if (boost::filesystem::exists(old_address_file))
5990 const std::string address_file = m_wallet_file +
".address.txt";
5995 r = boost::filesystem::remove(old_file);
5997 LOG_ERROR(
"error removing file: " << old_file);
6000 r = boost::filesystem::remove(old_keys_file);
6002 LOG_ERROR(
"error removing file: " << old_keys_file);
6005 r = boost::filesystem::remove(old_address_file);
6007 LOG_ERROR(
"error removing file: " << old_address_file);
6010 if (boost::filesystem::exists(old_mms_file))
6012 r = boost::filesystem::remove(old_mms_file);
6014 LOG_ERROR(
"error removing file: " << old_mms_file);
6022 std::ostringstream oss;
6031 ostr.open(new_file, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);
6043 if (m_message_store.get_active())
6047 m_message_store.write_to_file(get_multisig_wallet_state(), m_mms_file);
6056 return m_light_wallet_unlocked_balance;
6057 for (
const auto& i : balance_per_subaddress(index_major, public_blockchain))
6065 if (blocks_to_unlock)
6066 *blocks_to_unlock = 0;
6068 return m_light_wallet_balance;
6069 for (
const auto& i : unlocked_balance_per_subaddress(index_major, public_blockchain))
6071 amount += i.second.first;
6072 if (blocks_to_unlock && i.second.second > *blocks_to_unlock)
6073 *blocks_to_unlock = i.second.second;
6078 std::map<uint32_t, uint64_t> wallet2::balance_per_subaddress(
uint32_t index_major,
bool public_blockchain)
const
6080 std::map<uint32_t, uint64_t> amount_per_subaddr;
6081 for (
const auto& td: m_transfers)
6083 if((public_blockchain && td.m_tx.version == 1) || (!public_blockchain && td.m_tx.version > 1))
6086 if (td.m_subaddr_index.major == index_major && !td.m_spent && !td.m_frozen)
6088 auto found = amount_per_subaddr.find(td.m_subaddr_index.minor);
6089 if (found == amount_per_subaddr.end())
6090 amount_per_subaddr[td.m_subaddr_index.minor] = td.amount();
6092 found->second += td.amount();
6095 for (
const auto& utx: m_unconfirmed_txs) {
6096 if ((public_blockchain && utx.second.m_tx.version == 1) || (!public_blockchain && utx.second.m_tx.version > 1))
6099 if (utx.second.m_state != wallet2::unconfirmed_transfer_details::failed) {
6101 if(utx.second.m_tx.version > 1){
6105 auto target = boost::get<cryptonote::txout_to_key_public>(out.target);
6106 auto subaddr_found = m_subaddresses.find(target.address.m_spend_public_key);
6111 if (subaddr_found != m_subaddresses.end() && get_subaddress(subaddr_found->second).m_view_public_key == target.address.m_view_public_key && subaddr_found->second.major == index_major) {
6112 auto found = amount_per_subaddr.find(subaddr_found->second.minor);
6113 if (found == amount_per_subaddr.end())
6114 amount_per_subaddr[subaddr_found->second.minor] = out.amount;
6116 found->second += out.amount;
6126 if (utx.second.m_tx.version == 1 && utx.second.m_subaddr_account == index_major) {
6128 auto found = amount_per_subaddr.find(0);
6129 if (found == amount_per_subaddr.end())
6130 amount_per_subaddr[0] = utx.second.m_change;
6132 found->second += utx.second.m_change;
6136 return amount_per_subaddr;
6139 std::map<uint32_t, std::pair<uint64_t, uint64_t>> wallet2::unlocked_balance_per_subaddress(
uint32_t index_major,
bool public_blockchain)
const
6141 std::map<uint32_t, std::pair<uint64_t, uint64_t>> amount_per_subaddr;
6142 const uint64_t blockchain_height = get_blockchain_current_height();
6146 if((public_blockchain && td.m_tx.version == 1) || (!public_blockchain && td.m_tx.version > 1))
6149 if(td.m_subaddr_index.major == index_major && !td.m_spent && !td.m_frozen)
6151 uint64_t amount = 0, blocks_to_unlock = 0;
6152 if (is_transfer_unlocked(td))
6154 amount = td.amount();
6155 blocks_to_unlock = 0;
6164 unlock_height = td.m_tx.unlock_time;
6165 blocks_to_unlock = unlock_height > blockchain_height ? unlock_height - blockchain_height : 0;
6168 auto found = amount_per_subaddr.find(td.m_subaddr_index.minor);
6170 if (found == amount_per_subaddr.end())
6171 amount_per_subaddr[td.m_subaddr_index.minor] = std::make_pair(amount, blocks_to_unlock);
6174 found->second.first += amount;
6175 found->second.second = std::max(found->second.second, blocks_to_unlock);
6179 return amount_per_subaddr;
6182 uint64_t wallet2::balance_all(
bool public_blockchain)
const
6185 for (
uint32_t index_major = 0; index_major < get_num_subaddress_accounts(); ++index_major)
6186 r += balance(index_major, public_blockchain);
6190 uint64_t wallet2::unlocked_balance_all(
bool public_blockchain,
uint64_t *blocks_to_unlock)
const
6193 if (blocks_to_unlock)
6194 *blocks_to_unlock = 0;
6195 for (
uint32_t index_major = 0; index_major < get_num_subaddress_accounts(); ++index_major)
6198 r += unlocked_balance(index_major, public_blockchain ,blocks_to_unlock ? &local_blocks_to_unlock : NULL);
6199 if (blocks_to_unlock)
6200 *blocks_to_unlock = std::max(*blocks_to_unlock, local_blocks_to_unlock);
6207 incoming_transfers = m_transfers;
6210 void wallet2::get_payments(
const crypto::hash& payment_id, std::list<wallet2::payment_details>& payments,
uint64_t min_height,
const boost::optional<uint32_t>& subaddr_account,
const std::set<uint32_t>& subaddr_indices)
const
6212 auto range = m_payments.equal_range(payment_id);
6213 std::for_each(range.first, range.second, [&payments, &min_height, &subaddr_account, &subaddr_indices](
const payment_container::value_type& x) {
6214 if (min_height < x.second.m_block_height &&
6215 (!subaddr_account || *subaddr_account == x.second.m_subaddr_index.major) &&
6216 (subaddr_indices.empty() || subaddr_indices.count(x.second.m_subaddr_index.minor) == 1))
6218 payments.push_back(x.second);
6223 void wallet2::get_payments(std::list<std::pair<crypto::hash,wallet2::payment_details>>& payments,
uint64_t min_height,
uint64_t max_height,
const boost::optional<uint32_t>& subaddr_account,
const std::set<uint32_t>& subaddr_indices)
const
6225 auto range = std::make_pair(m_payments.begin(), m_payments.end());
6226 std::for_each(range.first, range.second, [&payments, &min_height, &max_height, &subaddr_account, &subaddr_indices](
const payment_container::value_type& x) {
6227 if (min_height < x.second.m_block_height && max_height >= x.second.m_block_height &&
6228 (!subaddr_account || *subaddr_account == x.second.m_subaddr_index.major) &&
6229 (subaddr_indices.empty() || subaddr_indices.count(x.second.m_subaddr_index.minor) == 1))
6231 payments.push_back(x);
6236 void wallet2::get_payments_out(std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>>& confirmed_payments,
6237 uint64_t min_height,
uint64_t max_height,
const boost::optional<uint32_t>& subaddr_account,
const std::set<uint32_t>& subaddr_indices)
const
6239 for (
auto i = m_confirmed_txs.begin(); i != m_confirmed_txs.end(); ++i) {
6240 if (i->second.m_block_height <= min_height || i->second.m_block_height > max_height)
6242 if (subaddr_account && *subaddr_account != i->second.m_subaddr_account)
6244 if (!subaddr_indices.empty() && std::count_if(i->second.m_subaddr_indices.begin(), i->second.m_subaddr_indices.end(), [&subaddr_indices](
uint32_t index) { return subaddr_indices.count(index) == 1; }) == 0)
6246 if (i->second.m_is_migration)
6248 confirmed_payments.push_back(*i);
6251 void wallet2::get_payments_out_migration(std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>>& confirmed_payments,
6252 uint64_t min_height,
uint64_t max_height,
const boost::optional<uint32_t>& subaddr_account,
const std::set<uint32_t>& subaddr_indices)
const
6254 for (
auto i = m_confirmed_txs.begin(); i != m_confirmed_txs.end(); ++i) {
6255 if (i->second.m_block_height <= min_height || i->second.m_block_height > max_height)
6257 if (subaddr_account && *subaddr_account != i->second.m_subaddr_account)
6259 if (!subaddr_indices.empty() && std::count_if(i->second.m_subaddr_indices.begin(), i->second.m_subaddr_indices.end(), [&subaddr_indices](
uint32_t index) { return subaddr_indices.count(index) == 1; }) == 0)
6261 if (!i->second.m_is_migration)
6263 confirmed_payments.push_back(*i);
6267 void wallet2::get_unconfirmed_payments_out(std::list<std::pair<crypto::hash,wallet2::unconfirmed_transfer_details>>& unconfirmed_payments,
const boost::optional<uint32_t>& subaddr_account,
const std::set<uint32_t>& subaddr_indices)
const
6269 for (
auto i = m_unconfirmed_txs.begin(); i != m_unconfirmed_txs.end(); ++i) {
6270 if (subaddr_account && *subaddr_account != i->second.m_subaddr_account)
6272 if (!subaddr_indices.empty() && std::count_if(i->second.m_subaddr_indices.begin(), i->second.m_subaddr_indices.end(), [&subaddr_indices](
uint32_t index) { return subaddr_indices.count(index) == 1; }) == 0)
6274 unconfirmed_payments.push_back(*i);
6278 void wallet2::get_unconfirmed_payments(std::list<std::pair<crypto::hash,wallet2::pool_payment_details>>& unconfirmed_payments,
const boost::optional<uint32_t>& subaddr_account,
const std::set<uint32_t>& subaddr_indices)
const
6280 for (
auto i = m_unconfirmed_payments.begin(); i != m_unconfirmed_payments.end(); ++i) {
6281 if ((!subaddr_account || *subaddr_account == i->second.m_pd.m_subaddr_index.major) &&
6282 (subaddr_indices.empty() || subaddr_indices.count(i->second.m_pd.m_subaddr_index.minor) == 1))
6283 unconfirmed_payments.push_back(*i);
6287 void wallet2::rescan_spent()
6291 std::vector<int> spent_status;
6292 spent_status.reserve(m_transfers.size());
6293 const size_t chunk_size = 1000;
6294 for (
size_t start_offset = 0; start_offset < m_transfers.size(); start_offset += chunk_size)
6296 const size_t n_outputs = std::min<size_t>(chunk_size, m_transfers.size() - start_offset);
6297 MDEBUG(
"Calling is_key_image_spent on " << start_offset <<
" - " << (start_offset + n_outputs - 1) <<
", out of " << m_transfers.size());
6300 for (
size_t n = start_offset; n < start_offset + n_outputs; ++n)
6302 m_daemon_rpc_mutex.lock();
6303 bool r =
invoke_http_json(
"/is_key_image_spent", req, daemon_resp, rpc_timeout);
6304 m_daemon_rpc_mutex.unlock();
6309 "daemon returned wrong response for is_key_image_spent, wrong amounts count = " +
6311 std::copy(daemon_resp.spent_status.begin(), daemon_resp.spent_status.end(), std::back_inserter(spent_status));
6315 for (
size_t i = 0; i < m_transfers.size(); ++i)
6339 void wallet2::rescan_blockchain(
bool hard,
bool refresh,
bool keep_key_images)
6342 const size_t transfers_cnt = m_transfers.size();
6348 setup_new_blockchain();
6352 if (keep_key_images && refresh)
6353 hash_m_transfers((
int64_t) transfers_cnt, transfers_hash);
6354 clear_soft(keep_key_images);
6358 this->refresh(
false);
6360 if (refresh && keep_key_images)
6361 finish_rescan_bc_keep_key_images(transfers_cnt, transfers_hash);
6371 if(!is_tx_spendtime_unlocked(unlock_time, block_height))
6377 if(block_height + UNLOCK_WINDOW > get_blockchain_current_height())
6400 if(current_time + leeway >= unlock_time)
6410 template<
typename T>
6411 T pop_index(std::vector<T>& vec,
size_t idx)
6417 if (idx + 1 != vec.size())
6419 vec[idx] = vec.back();
6421 vec.resize(vec.size() - 1);
6426 template<
typename T>
6427 T pop_random_value(std::vector<T>& vec)
6432 return pop_index (vec, idx);
6435 template<
typename T>
6436 T pop_back(std::vector<T>& vec)
6445 template<
typename T>
6446 void pop_if_present(std::vector<T>& vec,
T e)
6448 for (
size_t i = 0; i < vec.size(); ++i)
6463 float wallet2::get_output_relatedness(
const transfer_details &td0,
const transfer_details &td1)
const
6468 if (td0.m_txid == td1.m_txid)
6472 dh = td0.m_block_height > td1.m_block_height ? td0.m_block_height - td1.m_block_height : td1.m_block_height - td0.m_block_height;
6490 size_t wallet2::pop_best_value_from(
const transfer_container &transfers, std::vector<size_t> &unused_indices,
const std::vector<size_t>& selected_transfers,
bool smallest)
const
6492 std::vector<size_t> candidates;
6493 float best_relatedness = 1.0f;
6494 for (
size_t n = 0; n < unused_indices.size(); ++n)
6497 float relatedness = 0.0f;
6498 for (std::vector<size_t>::const_iterator i = selected_transfers.begin(); i != selected_transfers.end(); ++i)
6500 float r = get_output_relatedness(candidate, transfers[*i]);
6501 if (r > relatedness)
6504 if (relatedness == 1.0f)
6509 if (relatedness < best_relatedness)
6511 best_relatedness = relatedness;
6515 if (relatedness == best_relatedness)
6516 candidates.push_back(n);
6525 for (
size_t n = 0; n < candidates.size(); ++n)
6528 if (td.
amount() < transfers[unused_indices[candidates[idx]]].amount())
6536 return pop_index (unused_indices, candidates[idx]);
6539 size_t wallet2::pop_best_value(std::vector<size_t> &unused_indices,
const std::vector<size_t>& selected_transfers,
bool smallest)
const
6541 return pop_best_value_from(m_transfers, unused_indices, selected_transfers, smallest);
6548 uint64_t wallet2::select_transfers(
uint64_t needed_etn, std::vector<size_t> unused_transfers_indices, std::vector<size_t>& selected_transfers)
const
6551 selected_transfers.reserve(unused_transfers_indices.size());
6552 while (found_etn < needed_etn && !unused_transfers_indices.empty())
6554 size_t idx = pop_best_value(unused_transfers_indices, selected_transfers);
6556 const transfer_container::const_iterator it = m_transfers.begin() + idx;
6557 selected_transfers.push_back(idx);
6558 found_etn += it->amount();
6567 utd.m_amount_in = amount_in;
6568 utd.m_amount_out = 0;
6571 for (
const auto &d: dests)
6572 utd.m_amount_out += d.amount;
6573 utd.m_amount_out += change_amount;
6574 utd.m_change = change_amount;
6575 utd.m_dests = dests;
6578 std::vector<account_public_address> input_addresses;
6579 for (
auto minor_index : subaddr_indices) {
6581 input_addresses.push_back(get_subaddress(index));
6586 std::unordered_set<uint32_t> change_indexes;
6587 for (
size_t i = 0; i < tx.
vout.size(); ++i) {
6588 for (
auto input_address : input_addresses) {
6589 if (boost::get<txout_to_key_public>(tx.
vout[i].target).address == input_address) {
6590 change_indexes.insert(i);
6597 if (change_indexes.size() == tx.
vout.size()) {
6598 change_indexes.clear();
6602 for (
auto &change_index : change_indexes)
6603 total_change += tx.
vout[change_index].amount;
6604 utd.m_change = total_change;
6608 for (
size_t i = 0; i < tx.
vout.size(); ++i) {
6609 if (change_indexes.find(i) == change_indexes.end()) {
6610 auto output = boost::get<txout_to_key_public>(tx.
vout[i].target);
6613 return destination.addr == output.address;
6618 auto dest_ptr = std::find_if(std::begin(utd.m_dests),
6619 std::end(utd.m_dests), pred);
6620 if (dest_ptr != std::end(utd.m_dests)) {
6621 dest_ptr->amount += tx.
vout[i].amount;
6626 output.m_address_prefix ==
6633 for (
const auto &d: utd.m_dests)
6634 utd.m_amount_out += d.amount;
6635 utd.m_amount_out += total_change;
6639 utd.m_sent_time =
time(NULL);
6640 utd.m_payment_id = payment_id;
6641 utd.m_state = wallet2::unconfirmed_transfer_details::pending;
6642 utd.m_timestamp =
time(NULL);
6643 utd.m_subaddr_account = subaddr_account;
6644 utd.m_subaddr_indices = subaddr_indices;
6645 for (
const auto &in: tx.
vin)
6649 const auto &txin = boost::get<cryptonote::txin_to_key>(in);
6650 utd.m_rings.push_back(std::make_pair(txin.k_image, txin.key_offsets));
6655 crypto::hash wallet2::get_payment_id(
const pending_tx &ptx)
const
6657 std::vector<tx_extra_field> tx_extra_fields;
6666 if (ptx.dests.empty())
6668 MWARNING(
"Encrypted payment id found, but no destinations public key, cannot decrypt");
6669 return crypto::null_hash;
6671 if (m_account.get_device().decrypt_payment_id(payment_id8, ptx.dests[0].addr.m_view_public_key, ptx.tx_key))
6673 memcpy(payment_id.data, payment_id8.data, 8);
6678 payment_id = crypto::null_hash;
6694 oreq.address = get_account().get_public_address_str(m_nettype);
6697 m_daemon_rpc_mutex.lock();
6698 bool r =
invoke_http_json(
"/submit_raw_tx", oreq, ores, rpc_timeout,
"POST");
6699 m_daemon_rpc_mutex.unlock();
6709 req.do_not_relay =
false;
6710 req.do_sanity_checks =
true;
6712 m_daemon_rpc_mutex.lock();
6713 bool r =
invoke_http_json(
"/sendrawtransaction", req, daemon_send_resp, rpc_timeout);
6714 m_daemon_rpc_mutex.unlock();
6722 "Bad output index in selected transfers: " + boost::lexical_cast<std::string>(idx));
6729 std::vector<cryptonote::tx_destination_entry> dests;
6731 if (store_tx_info())
6733 payment_id = get_payment_id(ptx);
6736 amount_in += m_transfers[idx].amount();
6739 if (store_tx_info())
6741 m_tx_keys.insert(std::make_pair(txid, ptx.
tx_key));
6745 LOG_PRINT_L2(
"transaction " << txid <<
" generated ok and sent to daemon, key_images: [" << ptx.
key_images <<
"]");
6754 m_transfers[idx].m_multisig_k.clear();
6763 <<
"Please, wait for confirmation for your balance to be unlocked.");
6766 void wallet2::commit_tx(std::vector<pending_tx>& ptx_vector)
6768 for (
auto & ptx : ptx_vector)
6774 bool wallet2::save_tx(
const std::vector<pending_tx>& ptx_vector,
const std::string &filename)
const
6776 LOG_PRINT_L0(
"saving " << ptx_vector.size() <<
" transactions");
6777 std::string ciphertext = dump_tx_to_str(ptx_vector);
6778 if (ciphertext.empty())
6783 std::string wallet2::dump_tx_to_str(
const std::vector<pending_tx> &ptx_vector)
const
6785 LOG_PRINT_L0(
"saving " << ptx_vector.size() <<
" transactions");
6787 for (
auto &tx: ptx_vector)
6792 txs.
txes.push_back(get_construction_data_with_decrypted_short_payment_id(tx, m_account.get_device()));
6797 std::ostringstream oss;
6807 LOG_PRINT_L2(
"Saving unsigned tx data: " << oss.str());
6808 std::string ciphertext = encrypt_with_view_secret_key(oss.str());
6816 boost::system::error_code errcode;
6818 if (!boost::filesystem::exists(unsigned_filename, errcode))
6820 LOG_PRINT_L0(
"File " << unsigned_filename <<
" does not exist: " << errcode);
6825 LOG_PRINT_L0(
"Failed to load from " << unsigned_filename);
6829 return parse_unsigned_tx_from_str(s, exported_txs);
6841 s = s.substr(magiclen);
6848 std::istringstream iss(s);
6852 catch (
const std::exception &e)
6854 LOG_PRINT_L0(
"Failed to parse data from unsigned tx: " << e.what());
6867 s = decrypt_with_view_secret_key(s);
6870 std::istringstream iss(s);
6874 catch (
const std::exception &e)
6876 LOG_PRINT_L0(
"Failed to parse decrypted data from unsigned tx: " << e.what());
6880 catch (
const std::exception &e)
6882 LOG_PRINT_L0(
"Failed to decrypt unsigned tx: " << e.what());
6887 LOG_PRINT_L0(
"Failed to parse decrypted data from unsigned tx");
6896 LOG_PRINT_L1(
"Loaded tx unsigned data from binary: " << exported_txs.
txes.size() <<
" transactions");
6901 bool wallet2::sign_tx(
const std::string &unsigned_filename,
const std::string &signed_filename, std::vector<wallet2::pending_tx> &txs, std::function<
bool(
const unsigned_tx_set&)> accept_func,
bool export_raw)
6904 if(!load_unsigned_tx(unsigned_filename, exported_txs))
6907 if (accept_func && !accept_func(exported_txs))
6912 return sign_tx(exported_txs, signed_filename, txs, export_raw);
6920 for (
size_t n = 0; n < exported_txs.
txes.size(); ++n)
6929 std::vector<crypto::secret_key> additional_tx_keys;
6935 bool r =
cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sd.
sources, sd.
splitted_dsts, sd.
change_dts.
addr, sd.
extra, ptx.
tx, sd.
unlock_time, tx_key, additional_tx_keys, sd.
use_rct, rct_config, m_multisig ? &msout : NULL, m_account_major_offset, this->m_nettype);
6945 if (store_tx_info())
6948 m_tx_keys.insert(std::make_pair(txid, tx_key));
6949 m_additional_tx_keys.insert(std::make_pair(txid, additional_tx_keys));
6955 bool all_are_txin_to_key = std::all_of(ptx.
tx.
vin.begin(), ptx.
tx.
vin.end(), [&](
const txin_v &s_e) ->
bool {
6956 CHECKED_GET_SPECIFIC_VARIANT(s_e, const txin_to_key, in, false);
6957 key_images += boost::to_string(in.k_image) +
" ";
6962 bool all_are_txin_to_key_public = std::all_of(ptx.
tx.
vin.begin(), ptx.
tx.
vin.end(), [&](
const txin_v &s_e) ->
bool {
6963 CHECKED_GET_SPECIFIC_VARIANT(s_e, const txin_to_key_public, in, false);
6969 bool all_are_txout_to_key_public = std::all_of(ptx.
tx.
vout.begin(), ptx.
tx.
vout.end(), [&](
const tx_out &s_e) ->
bool {
6970 CHECKED_GET_SPECIFIC_VARIANT(s_e.target, const txout_to_key_public, in, false);
6977 for (
const auto &i: sd.
sources) ptx.
fee += i.amount;
6990 txs.back().tx_key = tx_key;
6991 txs.back().additional_tx_keys = additional_tx_keys;
6997 for (
size_t n = 0; n < exported_txs.
txes.size(); ++n)
7002 std::vector<crypto::key_derivation> additional_derivations;
7007 std::vector<crypto::public_key> additional_tx_pub_keys;
7010 additional_tx_pub_keys.resize(additional_tx_pub_keys.size() + 1);
7019 static_assert(
sizeof(derivation) ==
sizeof(
rct::key),
"Mismatched sizes of key_derivation and rct::key");
7022 for (
size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
7024 additional_derivations.push_back({});
7032 for (
size_t i = 0; i < tx.
vout.size(); ++i)
7042 if (
generate_key_image_helper(keys, m_subaddresses, out.key, tx_pub_key, additional_tx_pub_keys, i, in_ephemeral, ki, hwdev))
7045 MERROR(
"Failed to calculate key image");
7050 signed_txes.
key_images.resize(m_transfers.size());
7051 for (
size_t i = 0; i < m_transfers.size(); ++i)
7053 if (!m_transfers[i].m_key_image_known || m_transfers[i].m_key_image_partial)
7054 LOG_PRINT_L0(
"WARNING: key image not known in signing wallet at index " << i);
7055 signed_txes.
key_images[i] = m_transfers[i].m_key_image;
7065 std::string ciphertext = sign_tx_dump_to_str(exported_txs, txs, signed_txes);
7066 if (ciphertext.empty())
7074 LOG_PRINT_L0(
"Failed to save file to " << signed_filename);
7080 for (
size_t i = 0; i < signed_txes.
ptx.size(); ++i)
7086 LOG_PRINT_L0(
"Failed to save file to " << raw_filename);
7097 bool r = sign_tx(exported_txs, ptx, signed_txes);
7105 std::ostringstream oss;
7115 LOG_PRINT_L3(
"Saving signed tx data (with encryption): " << oss.str());
7116 std::string ciphertext = encrypt_with_view_secret_key(oss.str());
7120 bool wallet2::load_tx(
const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<
bool(
const signed_tx_set&)> accept_func)
7123 boost::system::error_code errcode;
7126 if (!boost::filesystem::exists(signed_filename, errcode))
7128 LOG_PRINT_L0(
"File " << signed_filename <<
" does not exist: " << errcode);
7134 LOG_PRINT_L0(
"Failed to load from " << signed_filename);
7138 return parse_tx_from_str(s, ptx, accept_func);
7141 bool wallet2::parse_tx_from_str(
const std::string &signed_tx_st, std::vector<tools::wallet2::pending_tx> &ptx, std::function<
bool(
const signed_tx_set &)> accept_func)
7144 boost::system::error_code errcode;
7153 s = s.substr(magiclen);
7160 std::istringstream iss(s);
7164 catch (
const std::exception &e)
7166 LOG_PRINT_L0(
"Failed to parse data from signed transaction: " << e.what());
7171 LOG_PRINT_L0(
"Failed to parse data from signed transaction");
7179 s = decrypt_with_view_secret_key(s);
7182 std::istringstream iss(s);
7186 catch (
const std::exception &e)
7188 LOG_PRINT_L0(
"Failed to parse decrypted data from signed transaction: " << e.what());
7192 catch (
const std::exception &e)
7194 LOG_PRINT_L0(
"Failed to decrypt signed transaction: " << e.what());
7205 LOG_PRINT_L0(
"Unsupported version in signed transaction");
7208 LOG_PRINT_L0(
"Loaded signed tx data from binary: " << signed_txs.
ptx.size() <<
" transactions");
7211 if (accept_func && !accept_func(signed_txs))
7218 bool r = import_key_images(signed_txs.
key_images);
7219 if (!r)
return false;
7223 m_cold_key_images.insert(e);
7225 ptx = signed_txs.
ptx;
7235 for (
size_t n = 0; n < txs.
m_ptx.size(); ++n)
7236 for (
size_t idx: txs.
m_ptx[n].construction_data.selected_transfers)
7237 m_transfers[idx].m_multisig_k.clear();
7240 for (
auto &ptx: txs.
m_ptx)
7242 for (
auto &e: ptx.construction_data.sources)
7246 for (
auto &ptx: txs.
m_ptx)
7249 ptx.construction_data = get_construction_data_with_decrypted_short_payment_id(ptx, m_account.get_device());
7253 std::ostringstream oss;
7263 LOG_PRINT_L2(
"Saving multisig unsigned tx data: " << oss.str());
7264 std::string ciphertext = encrypt_with_view_secret_key(oss.str());
7271 if (ciphertext.empty())
7279 txs.
m_ptx = ptx_vector;
7281 for (
const auto &msk: get_account().get_multisig_keys())
7284 for (
auto &ptx: txs.
m_ptx)
for (
auto &sig: ptx.multisig_sigs) sig.signing_keys.insert(pkey);
7287 txs.
m_signers.insert(get_multisig_signer_public_key());
7291 std::string wallet2::save_multisig_tx(
const std::vector<pending_tx>& ptx_vector)
7293 return save_multisig_tx(make_multisig_tx_set(ptx_vector));
7296 bool wallet2::save_multisig_tx(
const std::vector<pending_tx>& ptx_vector,
const std::string &filename)
7298 std::string ciphertext = save_multisig_tx(ptx_vector);
7299 if (ciphertext.empty())
7314 multisig_tx_st = decrypt_with_view_secret_key(
std::string(multisig_tx_st, magiclen));
7316 catch (
const std::exception &e)
7318 LOG_PRINT_L0(
"Failed to decrypt multisig tx data: " << e.what());
7323 std::istringstream iss(multisig_tx_st);
7334 for (
const auto &ptx: exported_txs.
m_ptx)
7336 CHECK_AND_ASSERT_MES(ptx.selected_transfers.size() == ptx.tx.vin.size(),
false,
"Mismatched selected_transfers/vin sizes");
7337 for (
size_t idx: ptx.selected_transfers)
7339 CHECK_AND_ASSERT_MES(ptx.construction_data.selected_transfers.size() == ptx.tx.vin.size(),
false,
"Mismatched cd selected_transfers/vin sizes");
7340 for (
size_t idx: ptx.construction_data.selected_transfers)
7342 CHECK_AND_ASSERT_MES(ptx.construction_data.sources.size() == ptx.tx.vin.size(),
false,
"Mismatched sources/vin sizes");
7350 if(!parse_multisig_tx_from_str(s, exported_txs))
7352 LOG_PRINT_L0(
"Failed to parse multisig transaction from string");
7356 LOG_PRINT_L1(
"Loaded multisig tx unsigned data from binary: " << exported_txs.
m_ptx.size() <<
" transactions");
7359 if (accept_func && !accept_func(exported_txs))
7365 const bool is_signed = exported_txs.
m_signers.size() >= m_multisig_threshold;
7368 for (
const auto &ptx: exported_txs.
m_ptx)
7371 if (store_tx_info())
7373 m_tx_keys.insert(std::make_pair(txid, ptx.tx_key));
7374 m_additional_tx_keys.insert(std::make_pair(txid, ptx.additional_tx_keys));
7385 boost::system::error_code errcode;
7387 if (!boost::filesystem::exists(filename, errcode))
7389 LOG_PRINT_L0(
"File " << filename <<
" does not exist: " << errcode);
7398 if (!load_multisig_tx(s, exported_txs, accept_func))
7400 LOG_PRINT_L0(
"Failed to parse multisig tx data from " << filename);
7406 bool wallet2::sign_multisig_tx(
multisig_tx_set &exported_txs, std::vector<crypto::hash> &txids)
7422 for (
size_t n = 0; n < exported_txs.
m_ptx.size(); ++n)
7428 ", signed by " << exported_txs.
m_signers.size() <<
"/" << m_multisig_threshold);
7433 bool r =
cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources, sd.
splitted_dsts, ptx.
change_dts.
addr, sd.
extra, tx, sd.
unlock_time, ptx.
tx_key, ptx.
additional_tx_keys, sd.
use_rct, rct_config, &msout,
false, 0 , this->m_nettype);
7440 std::vector<unsigned int> indices;
7441 for (
const auto &
source: sources)
7442 indices.push_back(
source.real_output);
7446 if (sig.ignore.find(local_signer) == sig.ignore.end())
7452 k.push_back(get_multisig_k(idx, sig.used_L));
7455 for (
const auto &msk: get_account().get_multisig_keys())
7459 if (sig.signing_keys.find(pmsk) == sig.signing_keys.end())
7462 sig.signing_keys.insert(pmsk);
7472 const bool is_last = exported_txs.
m_signers.size() + 1 >= m_multisig_threshold;
7480 if (sig.ignore.find(local_signer) == sig.ignore.end() && !keys_intersect(sig.ignore, exported_txs.
m_signers))
7488 "Final signed transaction not found: this transaction was likely made without our export data, so we cannot sign it");
7490 if (store_tx_info())
7492 m_tx_keys.insert(std::make_pair(txid, ptx.
tx_key));
7495 txids.push_back(txid);
7500 for (
size_t n = 0; n < exported_txs.
m_ptx.size(); ++n)
7501 for (
size_t idx: exported_txs.
m_ptx[n].construction_data.selected_transfers)
7502 m_transfers[idx].m_multisig_k.clear();
7504 exported_txs.
m_signers.insert(get_multisig_signer_public_key());
7511 bool r = sign_multisig_tx(exported_txs, txids);
7514 return save_multisig_tx(exported_txs, filename);
7517 bool wallet2::sign_multisig_tx_from_file(
const std::string &filename, std::vector<crypto::hash> &txids, std::function<
bool(
const multisig_tx_set&)> accept_func)
7520 if(!load_multisig_tx_from_file(filename, exported_txs))
7523 if (accept_func && !accept_func(exported_txs))
7528 return sign_multisig_tx_to_file(exported_txs, filename, txids);
7541 { 3, {1, 20, 166} },
7542 { 4, {1, 4, 20, 166} },
7543 { 4, {1, 2, 4, 8} },
7546 if (fee_algorithm == -1)
7547 fee_algorithm = get_fee_algorithm();
7551 priority = m_default_priority;
7554 if (fee_algorithm == 2)
7563 const uint32_t max_priority = multipliers[fee_algorithm].count;
7564 if (priority >= 1 && priority <= max_priority)
7566 return multipliers[fee_algorithm].multipliers[priority-1];
7573 uint64_t wallet2::get_dynamic_base_fee_estimate()
const
7589 return m_light_wallet_per_kb_fee / 1024;
7591 return m_light_wallet_per_kb_fee;
7597 return get_dynamic_base_fee_estimate();
7607 if (!use_per_byte_fee)
7611 boost::optional<std::string> result = m_node_rpc_proxy.get_fee_quantization_mask(fee_quantization_mask);
7614 return fee_quantization_mask;
7617 int wallet2::get_fee_algorithm()
const
7620 if (use_fork_rules(6, 0))
7622 if (use_fork_rules(5, 0))
7624 if (use_fork_rules(3, -720 * 14))
7655 const uint64_t min_ring_size = get_min_ring_size();
7656 if (mixin + 1 < min_ring_size)
7658 MWARNING(
"Requested ring size " << (mixin + 1) <<
" too low, using " << min_ring_size);
7659 mixin = min_ring_size-1;
7661 const uint64_t max_ring_size = get_max_ring_size();
7662 if (max_ring_size && mixin + 1 > max_ring_size)
7664 MWARNING(
"Requested ring size " << (mixin + 1) <<
" too high, using " << max_ring_size);
7665 mixin = max_ring_size-1;
7672 if (priority == 0 && m_default_priority == 0 && auto_low_priority())
7678 const uint64_t base_fee = get_base_fee();
7679 const uint64_t fee_multiplier = get_fee_multiplier(1);
7680 const double fee_level = fee_multiplier * base_fee * (use_per_byte_fee ? 1 : (12/(double)13 / (
double)1024));
7681 const std::vector<std::pair<uint64_t, uint64_t>>
blocks = estimate_backlog({std::make_pair(fee_level, fee_level)});
7684 MERROR(
"Bad estimated backlog array size");
7687 else if (
blocks[0].first > 0)
7689 MINFO(
"We don't use the low priority because there's a backlog in the tx pool.");
7695 const auto result = m_node_rpc_proxy.get_block_weight_limit(block_weight_limit);
7696 throw_on_rpc_response_error(result,
"get_info");
7697 const uint64_t full_reward_zone = block_weight_limit / 2;
7700 const size_t N = 10;
7701 if (m_blockchain.size() < N)
7703 MERROR(
"The blockchain is too short");
7708 m_daemon_rpc_mutex.lock();
7709 getbh_req.start_height = m_blockchain.size() - N;
7710 getbh_req.end_height = m_blockchain.size() - 1;
7711 bool r =
invoke_http_json_rpc(
"/json_rpc",
"getblockheadersrange", getbh_req, getbh_res, rpc_timeout);
7712 m_daemon_rpc_mutex.unlock();
7716 if (getbh_res.headers.size() != N)
7718 MERROR(
"Bad blockheaders size");
7721 size_t block_weight_sum = 0;
7728 const size_t P = 100 * block_weight_sum / (N * full_reward_zone);
7729 MINFO((boost::format(
"The last %d blocks fill roughly %d%% of the full reward zone.") % N % P).str());
7732 MINFO(
"We don't use the low priority because recent blocks are quite full.");
7735 MINFO(
"We'll use the low priority because probably it's safe to do so.");
7738 catch (
const std::exception &e)
7748 m_ring_database = filename;
7749 MINFO(
"ringdb path set to " << filename);
7751 if (!m_ring_database.empty())
7756 generate_genesis(b);
7759 catch (
const std::exception &e)
7761 MERROR(
"Failed to initialize ringdb: " << e.what());
7762 m_ring_database =
"";
7769 crypto::chacha_key wallet2::get_ringdb_key()
7773 MINFO(
"caching ringdb key");
7774 crypto::chacha_key
key;
7775 generate_chacha_key_from_secret_keys(
key);
7778 return *m_ringdb_key;
7781 void wallet2::register_devices(){
7786 if (!m_devices_registered){
7787 m_devices_registered =
true;
7798 try {
return m_ringdb->add_rings(
key, tx); }
7799 catch (
const std::exception &e) {
return false; }
7804 try {
return add_rings(get_ringdb_key(), tx); }
7805 catch (
const std::exception &e) {
return false; }
7812 try {
return m_ringdb->remove_rings(get_ringdb_key(), tx); }
7813 catch (
const std::exception &e) {
return false; }
7820 try {
return m_ringdb->get_ring(
key,
key_image, outs); }
7821 catch (
const std::exception &e) {
return false; }
7826 for (
auto i: m_confirmed_txs)
7828 if (txid == i.first)
7830 for (
const auto &x: i.second.m_rings)
7835 for (
auto i: m_unconfirmed_txs)
7837 if (txid == i.first)
7839 for (
const auto &x: i.second.m_rings)
7849 try {
return get_ring(get_ringdb_key(),
key_image, outs); }
7850 catch (
const std::exception &e) {
return false; }
7858 try {
return m_ringdb->set_ring(get_ringdb_key(),
key_image, outs, relative); }
7859 catch (
const std::exception &e) {
return false; }
7862 bool wallet2::unset_ring(
const std::vector<crypto::key_image> &key_images)
7867 try {
return m_ringdb->remove_rings(get_ringdb_key(), key_images); }
7868 catch (
const std::exception &e) {
return false; }
7879 req.decode_as_json =
false;
7881 m_daemon_rpc_mutex.lock();
7883 m_daemon_rpc_mutex.unlock();
7885 if (
res.txs.empty())
7891 if (!get_pruned_tx(
res.txs.front(), tx, tx_hash))
7895 try {
return m_ringdb->remove_rings(get_ringdb_key(), tx); }
7896 catch (
const std::exception &e) {
return false; }
7899 bool wallet2::find_and_save_rings(
bool force)
7901 if (!force && m_ring_history_saved)
7909 MDEBUG(
"Finding and saving rings...");
7912 std::vector<crypto::hash> txs_hashes;
7913 std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>> payments;
7914 get_payments_out(payments, 0, std::numeric_limits<uint64_t>::max(), boost::none, std::set<uint32_t>());
7915 for (
const std::pair<crypto::hash,wallet2::confirmed_transfer_details> &entry: payments)
7918 txs_hashes.push_back(txid);
7924 auto it = txs_hashes.begin();
7925 static const size_t SLICE_SIZE = 200;
7926 for (
size_t slice = 0; slice < txs_hashes.size(); slice += SLICE_SIZE)
7928 req.decode_as_json =
false;
7930 req.txs_hashes.clear();
7931 size_t ntxes = slice + SLICE_SIZE > txs_hashes.size() ? txs_hashes.size() - slice : SLICE_SIZE;
7932 for (
size_t s = slice; s < slice + ntxes; ++s)
7936 const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
7943 "daemon returned wrong response for gettransactions, wrong txs count = " +
7946 MDEBUG(
"Scanning " <<
res.txs.size() <<
" transactions");
7948 for (
size_t i = 0; i <
res.txs.size(); ++i, ++it)
7954 "Failed to get transaction from daemon");
7960 MINFO(
"Found and saved rings for " << txs_hashes.size() <<
" transactions");
7961 m_ring_history_saved =
true;
7965 bool wallet2::blackball_output(
const std::pair<uint64_t, uint64_t> &output)
7969 try {
return m_ringdb->blackball(output); }
7970 catch (
const std::exception &e) {
return false; }
7973 bool wallet2::set_blackballed_outputs(
const std::vector<std::pair<uint64_t, uint64_t>> &outputs,
bool add)
7981 ret &= m_ringdb->clear_blackballs();
7982 ret &= m_ringdb->blackball(outputs);
7985 catch (
const std::exception &e) {
return false; }
7988 bool wallet2::unblackball_output(
const std::pair<uint64_t, uint64_t> &output)
7992 try {
return m_ringdb->unblackball(output); }
7993 catch (
const std::exception &e) {
return false; }
7996 bool wallet2::is_output_blackballed(
const std::pair<uint64_t, uint64_t> &output)
const
8000 try {
return m_ringdb->blackballed(output); }
8001 catch (
const std::exception &e) {
return false; }
8004 bool wallet2::lock_keys_file()
8006 if (m_keys_file_locker)
8008 MDEBUG(m_keys_file <<
" is already locked.");
8015 bool wallet2::unlock_keys_file()
8017 if (!m_keys_file_locker)
8019 MDEBUG(m_keys_file <<
" is already unlocked.");
8022 m_keys_file_locker.reset();
8026 bool wallet2::is_keys_file_locked()
const
8028 return m_keys_file_locker->locked();
8031 bool wallet2::tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
uint64_t global_index,
const crypto::public_key& output_public_key,
const rct::key& mask,
uint64_t real_index,
bool unlocked)
const
8035 if (global_index == real_index)
8039 if (std::find(outs.back().begin(), outs.back().end(), item) != outs.back().end())
8044 MWARNING(
"Key " << output_public_key <<
" at index " << global_index <<
" is not in the main subgroup");
8049 MWARNING(
"Commitment " << mask <<
" at index " << global_index <<
" is not in the main subgroup");
8054 outs.back().push_back(item);
8058 void wallet2::light_wallet_get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
const std::vector<size_t> &selected_transfers,
size_t fake_outputs_count) {
8060 MDEBUG(
"LIGHTWALLET - Getting random outs");
8065 size_t light_wallet_requested_outputs_count = (size_t)((fake_outputs_count + 1) * 1.5 + 1);
8069 for(
size_t idx: selected_transfers) {
8070 const uint64_t ask_amount = m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount();
8071 std::ostringstream amount_ss;
8072 amount_ss << ask_amount;
8073 oreq.amounts.push_back(amount_ss.str());
8076 oreq.count = light_wallet_requested_outputs_count;
8077 m_daemon_rpc_mutex.lock();
8078 bool r =
invoke_http_json(
"/get_random_outs", oreq, ores, rpc_timeout,
"POST");
8079 m_daemon_rpc_mutex.unlock();
8084 for(
auto& out: ores.amount_outs) {
8085 const uint64_t out_amount = boost::lexical_cast<uint64_t>(out.amount);
8087 MDEBUG(out.outputs.size() <<
" outputs for amount "+ boost::lexical_cast<std::string>(out.amount) +
" received from light wallet node");
8090 MDEBUG(
"selected transfers size: " << selected_transfers.size());
8092 for(
size_t idx: selected_transfers)
8095 outs.push_back(std::vector<get_outs_entry>());
8096 outs.back().reserve(fake_outputs_count + 1);
8105 std::vector<size_t> order;
8106 order.resize(light_wallet_requested_outputs_count);
8107 for (
size_t n = 0; n < order.size(); ++n)
8109 std::shuffle(order.begin(), order.end(), std::default_random_engine(crypto::rand<unsigned>()));
8113 MDEBUG(
"OUTS SIZE: " << outs.back().size());
8114 for (
size_t o = 0; o < light_wallet_requested_outputs_count && outs.back().size() < fake_outputs_count + 1; ++o)
8117 size_t i = order[o];
8120 bool found_amount =
false;
8122 for(amount_key = 0; amount_key < ores.amount_outs.size(); ++amount_key)
8124 if(boost::lexical_cast<uint64_t>(ores.amount_outs[amount_key].amount) == amount) {
8125 found_amount =
true;
8131 LOG_PRINT_L2(
"Index " << i <<
"/" << light_wallet_requested_outputs_count <<
": idx " << ores.amount_outs[amount_key].outputs[i].global_index <<
" (real " << td.
m_global_output_index <<
"), unlocked " <<
"(always in light)" <<
", key " << ores.amount_outs[0].outputs[i].public_key);
8139 const uint64_t global_index = ores.amount_outs[amount_key].outputs[i].global_index;
8140 if(!light_wallet_parse_rct_str(ores.amount_outs[amount_key].outputs[i].rct, tx_public_key, 0, mask, rct_commit,
false))
8143 if (tx_add_fake_output(outs, global_index, tx_public_key, rct_commit, td.
m_global_output_index,
true)) {
8144 MDEBUG(
"added fake output " << ores.amount_outs[amount_key].outputs[i].public_key);
8145 MDEBUG(
"index " << global_index);
8152 MTRACE(outs.back().size() <<
" outputs added. Sorting outputs by index:");
8153 std::sort(outs.back().begin(), outs.back().end(), [](
const get_outs_entry &
a,
const get_outs_entry &b) { return std::get<0>(a) < std::get<0>(b); });
8156 for(
auto added_out: outs.back())
8157 MTRACE(std::get<0>(added_out));
8162 void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
const std::vector<size_t> &selected_transfers,
size_t fake_outputs_count,
const uint8_t tx_version)
8164 LOG_PRINT_L2(
"fake_outputs_count: " << fake_outputs_count);
8167 if(m_light_wallet && fake_outputs_count > 0) {
8168 light_wallet_get_outs(outs, selected_transfers, fake_outputs_count);
8172 if (fake_outputs_count > 0)
8174 uint64_t segregation_fork_height = get_segregation_fork_height();
8177 boost::optional<std::string> result = m_node_rpc_proxy.get_height(
height);
8178 throw_on_rpc_response_error(result,
"get_info");
8180 bool is_after_segregation_fork =
height >= segregation_fork_height;
8188 std::vector<uint64_t> rct_offsets;
8189 bool has_rct =
false;
8191 for (
size_t idx: selected_transfers)
8192 if (m_transfers[idx].is_rct())
8195 max_rct_index = std::max(max_rct_index, m_transfers[idx].m_global_output_index);
8197 const bool has_rct_distribution = has_rct && get_rct_distribution(rct_start_height, rct_offsets);
8198 if (has_rct_distribution)
8211 for(
size_t idx: selected_transfers)
8212 if (!m_transfers[idx].is_rct() || !has_rct_distribution)
8213 req_t.amounts.push_back(m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount());
8214 if (!req_t.amounts.empty())
8216 std::sort(req_t.amounts.begin(), req_t.amounts.end());
8217 auto end = std::unique(req_t.amounts.begin(), req_t.amounts.end());
8218 req_t.amounts.resize(std::distance(req_t.amounts.begin(), end));
8219 req_t.unlocked =
true;
8221 m_daemon_rpc_mutex.lock();
8222 bool r =
invoke_http_json_rpc(
"/json_rpc",
"get_output_histogram", req_t, resp_t, rpc_timeout);
8223 m_daemon_rpc_mutex.unlock();
8230 std::unordered_map<uint64_t, std::pair<uint64_t, uint64_t>> segregation_limit;
8231 if (is_after_segregation_fork && (m_segregate_pre_fork_outputs || m_key_reuse_mitigation2))
8235 for(
size_t idx: selected_transfers)
8236 req_t.amounts.push_back(m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount());
8237 std::sort(req_t.amounts.begin(), req_t.amounts.end());
8238 auto end = std::unique(req_t.amounts.begin(), req_t.amounts.end());
8239 req_t.amounts.resize(std::distance(req_t.amounts.begin(), end));
8241 req_t.to_height = segregation_fork_height + 1;
8242 req_t.cumulative =
true;
8243 req_t.binary =
true;
8244 m_daemon_rpc_mutex.lock();
8245 bool r =
invoke_http_json_rpc(
"/json_rpc",
"get_output_distribution", req_t, resp_t, rpc_timeout * 1000);
8246 m_daemon_rpc_mutex.unlock();
8252 for(
size_t idx: selected_transfers)
8254 const uint64_t amount = m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount();
8256 for (
const auto &d: resp_t.distributions)
8258 if (d.amount == amount)
8265 uint64_t till_fork = d.data.distribution[segregation_fork_height - d.data.start_height];
8267 segregation_limit[amount] = std::make_pair(till_fork, recent);
8277 size_t base_requested_outputs_count = (size_t)((fake_outputs_count + 1) * 1.5 + 1);
8278 LOG_PRINT_L2(
"base_requested_outputs_count: " << base_requested_outputs_count);
8284 std::unique_ptr<gamma_picker> gamma;
8285 if (has_rct_distribution)
8286 gamma.reset(
new gamma_picker(rct_offsets));
8288 size_t num_selected_transfers = 0;
8289 for(
size_t idx: selected_transfers)
8291 ++num_selected_transfers;
8292 const transfer_details &td = m_transfers[idx];
8293 const uint64_t amount = td.is_rct() ? 0 : td.amount();
8294 std::unordered_set<uint64_t> seen_indices;
8296 size_t requested_outputs_count = base_requested_outputs_count + (td.is_rct() ? MINED_ETN_SPENDABLE_AGE - TX_SPENDABLE_AGE : 0);
8297 size_t start = req.outputs.size();
8298 bool use_histogram = amount != 0 || !has_rct_distribution;
8300 const bool output_is_pre_fork = td.m_block_height < segregation_fork_height;
8301 uint64_t num_outs = 0, num_recent_outs = 0;
8303 float pre_fork_num_out_ratio = 0.0f;
8304 float post_fork_num_out_ratio = 0.0f;
8306 if (is_after_segregation_fork && m_segregate_pre_fork_outputs && output_is_pre_fork)
8308 num_outs = segregation_limit[amount].first;
8309 num_recent_outs = segregation_limit[amount].second;
8315 for (
const auto &he: resp_t.histogram)
8317 if (he.amount == amount)
8320 << he.unlocked_instances <<
" unlocked, " << he.recent_instances <<
" recent");
8321 num_outs = he.unlocked_instances;
8322 num_recent_outs = he.recent_instances;
8326 if (is_after_segregation_fork && m_key_reuse_mitigation2)
8328 if (output_is_pre_fork)
8330 if (is_shortly_after_segregation_fork)
8342 if (is_shortly_after_segregation_fork)
8351 num_post_fork_outs = num_outs - segregation_limit[amount].first;
8358 "histogram reports no unlocked outputs for " + boost::lexical_cast<std::string>(amount) +
", not even ours");
8360 "histogram reports more recent outs than outs for " + boost::lexical_cast<std::string>(amount));
8365 num_outs = rct_offsets[rct_offsets.size() - TX_SPENDABLE_AGE];
8366 LOG_PRINT_L1(
"" << num_outs <<
" unlocked rct outputs");
8368 "histogram reports no unlocked rct outputs, not even ours");
8372 size_t pre_fork_outputs_count = requested_outputs_count * pre_fork_num_out_ratio;
8373 size_t post_fork_outputs_count = requested_outputs_count * post_fork_num_out_ratio;
8375 size_t normal_output_count = requested_outputs_count - pre_fork_outputs_count - post_fork_outputs_count;
8377 size_t recent_outputs_count = 0;
8382 if (recent_outputs_count == 0)
8383 recent_outputs_count = 1;
8384 if (recent_outputs_count > num_recent_outs)
8385 recent_outputs_count = num_recent_outs;
8386 if (td.m_global_output_index >= num_outs - num_recent_outs && recent_outputs_count > 0)
8387 --recent_outputs_count;
8389 LOG_PRINT_L1(
"Fake output makeup: " << requested_outputs_count <<
" requested: " << recent_outputs_count <<
" recent, " <<
8390 pre_fork_outputs_count <<
" pre-fork, " << post_fork_outputs_count <<
" post-fork, " <<
8391 (requested_outputs_count - recent_outputs_count - pre_fork_outputs_count - post_fork_outputs_count) <<
" full-chain");
8396 if (td.m_key_image_known && !td.m_key_image_partial)
8398 std::vector<uint64_t> ring;
8399 if (get_ring(get_ringdb_key(), td.m_key_image, ring))
8401 MINFO(
"This output has a known ring, reusing (size " << ring.size() <<
")");
8403 "An output in this transaction was previously spent on another chain with ring size " +
8404 std::to_string(ring.size()) +
", it cannot be spent now with ring size " +
8405 std::to_string(fake_outputs_count + 1) +
" as it is smaller: use a higher ring size");
8406 bool own_found =
false;
8407 for (
const auto &out: ring)
8409 MINFO(
"Ring has output " << out);
8413 req.outputs.push_back({amount,
out});
8415 seen_indices.emplace(out);
8416 if (out == td.m_global_output_index)
8418 MINFO(
"This is the real output");
8424 MINFO(
"Ignoring output " << out <<
", too recent");
8428 "Known ring does not include the spent output: " +
std::to_string(td.m_global_output_index));
8432 if (num_outs <= requested_outputs_count)
8434 for (
uint64_t i = 0; i < num_outs; i++)
8435 req.outputs.push_back({amount, i});
8439 for (
uint64_t i = num_outs; i < requested_outputs_count; ++i)
8440 req.outputs.push_back({amount, num_outs - 1});
8448 seen_indices.emplace(td.m_global_output_index);
8449 req.outputs.push_back({amount, td.m_global_output_index});
8450 LOG_PRINT_L1(
"Selecting real output: " << td.m_global_output_index <<
" for " <<
print_etn(amount));
8453 std::unordered_map<const char*, std::set<uint64_t>> picks;
8456 uint64_t num_usable_outs = num_outs;
8457 bool allow_blackballed =
false;
8458 MDEBUG(
"Starting gamma picking with " << num_outs <<
", num_usable_outs " << num_usable_outs
8459 <<
", requested_outputs_count " << requested_outputs_count);
8460 while (num_found < requested_outputs_count)
8463 if (seen_indices.size() == num_usable_outs)
8469 if (allow_blackballed)
8471 MINFO(
"Not enough output not marked as spent, we'll allow outputs marked as spent");
8472 allow_blackballed =
true;
8473 num_usable_outs = num_outs;
8481 const char *type =
"";
8482 if (amount == 0 && has_rct_distribution)
8486 if (num_found -1 < recent_outputs_count + pre_fork_outputs_count)
8488 do i = gamma->pick();
while (i >= segregation_limit[amount].first);
8489 type =
"pre-fork gamma";
8491 else if (num_found -1 < recent_outputs_count + pre_fork_outputs_count + post_fork_outputs_count)
8493 do i = gamma->pick();
while (i < segregation_limit[amount].first || i >= num_outs);
8494 type =
"post-fork gamma";
8498 do i = gamma->pick();
while (i >= num_outs);
8502 else if (num_found - 1 < recent_outputs_count)
8506 double frac = std::sqrt((
double)r / ((
uint64_t)1 << 53));
8507 i = (
uint64_t)(frac*num_recent_outs) + num_outs - num_recent_outs;
8513 else if (num_found -1 < recent_outputs_count + pre_fork_outputs_count)
8517 double frac = std::sqrt((
double)r / ((
uint64_t)1 << 53));
8518 i = (
uint64_t)(frac*segregation_limit[amount].first);
8524 else if (num_found -1 < recent_outputs_count + pre_fork_outputs_count + post_fork_outputs_count)
8528 double frac = std::sqrt((
double)r / ((
uint64_t)1 << 53));
8529 i = (
uint64_t)(frac*num_post_fork_outs) + segregation_limit[amount].first;
8531 if (i == num_post_fork_outs+segregation_limit[amount].first)
8539 double frac = std::sqrt((
double)r / ((
uint64_t)1 << 53));
8544 type =
"triangular";
8547 if (seen_indices.count(i))
8549 if (!allow_blackballed && is_output_blackballed(std::make_pair(amount, i)))
8554 seen_indices.emplace(i);
8556 picks[type].insert(i);
8557 req.outputs.push_back({amount, i});
8559 MDEBUG(
"picked " << i <<
", " << num_found <<
" now picked");
8562 for (
const auto &pick: picks)
8563 MDEBUG(
"picking " << pick.first <<
" outputs: " <<
8564 boost::join(pick.second | boost::adaptors::transformed([](
uint64_t out){return std::to_string(out);}),
" "));
8569 while (num_found < requested_outputs_count)
8571 req.outputs.push_back({amount, 0});
8577 std::sort(req.outputs.begin() + start, req.outputs.end(),
8583 std::map<uint64_t, std::set<uint64_t>> outs;
8584 for (
const auto &i: req.outputs)
8585 outs[i.amount].insert(i.index);
8586 for (
const auto &o: outs)
8587 MDEBUG(
"asking for outputs with amount " <<
print_etn(o.first) <<
": " <<
8588 boost::join(o.second | boost::adaptors::transformed([](
uint64_t out){return std::to_string(out);}),
" "));
8592 req.get_txid =
false;
8593 m_daemon_rpc_mutex.lock();
8594 bool r =
invoke_http_bin(
"/get_outs.bin", req, daemon_resp, rpc_timeout);
8595 m_daemon_rpc_mutex.unlock();
8600 "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
8603 std::unordered_map<uint64_t, uint64_t> scanty_outs;
8605 outs.reserve(num_selected_transfers);
8606 for(
size_t idx: selected_transfers)
8608 const transfer_details &td = m_transfers[idx];
8609 size_t requested_outputs_count = base_requested_outputs_count + (td.is_rct() ? MINED_ETN_SPENDABLE_AGE - TX_SPENDABLE_AGE : 0);
8610 outs.push_back(std::vector<get_outs_entry>());
8611 outs.back().reserve(fake_outputs_count + 1);
8615 const uint64_t amount = td.is_rct() ? 0 : td.amount();
8616 const bool output_is_pre_fork = td.m_block_height < segregation_fork_height;
8617 if (is_after_segregation_fork && m_segregate_pre_fork_outputs && output_is_pre_fork)
8618 num_outs = segregation_limit[amount].first;
8619 else for (
const auto &he: resp_t.histogram)
8621 if (he.amount == amount)
8623 num_outs = he.unlocked_instances;
8627 bool use_histogram = amount != 0 || !has_rct_distribution;
8629 num_outs = rct_offsets[rct_offsets.size() - TX_SPENDABLE_AGE];
8636 bool real_out_found =
false;
8637 for (
size_t n = 0; n < requested_outputs_count; ++n)
8639 size_t i = base + n;
8640 if (req.outputs[i].index == td.m_global_output_index)
8641 if (daemon_resp.outs[i].key == boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key)
8642 if (daemon_resp.outs[i].mask == mask)
8643 real_out_found =
true;
8646 "Daemon response did not include the requested real output");
8649 outs.back().push_back(
std::make_tuple(td.m_global_output_index, boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key, mask));
8652 if (td.m_key_image_known && !td.m_key_image_partial)
8654 std::vector<uint64_t> ring;
8655 if (get_ring(get_ringdb_key(), td.m_key_image, ring))
8661 if (out != td.m_global_output_index)
8664 for (
size_t o = 0; o < requested_outputs_count; ++o)
8666 size_t i = base + o;
8667 if (req.outputs[i].index == out)
8669 LOG_PRINT_L2(
"Index " << i <<
"/" << requested_outputs_count <<
": idx " << req.outputs[i].index <<
" (real " << td.m_global_output_index <<
"), unlocked " << daemon_resp.outs[i].unlocked <<
", key " << daemon_resp.outs[i].key <<
" (from existing ring)");
8670 tx_add_fake_output(outs, req.outputs[i].index, daemon_resp.outs[i].key, daemon_resp.outs[i].mask, td.m_global_output_index, daemon_resp.outs[i].unlocked);
8675 THROW_WALLET_EXCEPTION_IF(!found, error::wallet_internal_error,
"Falied to find existing ring output in daemon out data");
8684 std::vector<size_t> order;
8685 order.resize(requested_outputs_count);
8686 for (
size_t n = 0; n < order.size(); ++n)
8688 std::shuffle(order.begin(), order.end(), std::default_random_engine(crypto::rand<unsigned>()));
8690 LOG_PRINT_L2(
"Looking for " << (fake_outputs_count+1) <<
" outputs of size " <<
print_etn(td.is_rct() ? 0 : td.amount()));
8691 for (
size_t o = 0; o < requested_outputs_count && outs.back().size() < fake_outputs_count + 1; ++o)
8693 size_t i = base + order[o];
8694 LOG_PRINT_L2(
"Index " << i <<
"/" << requested_outputs_count <<
": idx " << req.outputs[i].index <<
" (real " << td.m_global_output_index <<
"), unlocked " << daemon_resp.outs[i].unlocked <<
", key " << daemon_resp.outs[i].key);
8695 tx_add_fake_output(outs, req.outputs[i].index, daemon_resp.outs[i].key, daemon_resp.outs[i].mask, td.m_global_output_index, daemon_resp.outs[i].unlocked);
8697 if (outs.back().size() < fake_outputs_count + 1)
8699 scanty_outs[td.is_rct() ? 0 : td.amount()] = outs.back().size();
8704 std::sort(outs.back().begin(), outs.back().end(), [](
const get_outs_entry &
a,
const get_outs_entry &b) { return std::get<0>(a) < std::get<0>(b); });
8706 base += requested_outputs_count;
8712 if(tx_version < 3) {
8713 for (
size_t idx: selected_transfers) {
8714 const transfer_details &td = m_transfers[idx];
8715 std::vector<get_outs_entry> v;
8717 v.push_back(
std::make_tuple(td.m_global_output_index, td.get_public_key(),
8724 if(tx_version < 3) {
8726 for (
size_t i = 0; i < selected_transfers.size(); ++i) {
8727 const size_t idx = selected_transfers[i];
8729 "selected_transfers entry out of range");
8730 const transfer_details &td = m_transfers[idx];
8731 std::vector<uint64_t> ring;
8732 ring.reserve(outs[i].size());
8733 for (
const auto &e: outs[i])
8734 ring.push_back(std::get<0>(e));
8735 if (!set_ring(td.m_key_image, ring,
false))
8736 MERROR(
"Failed to set ring for " << td.m_key_image);
8741 template<
typename T>
8742 void wallet2::transfer_selected(
const std::vector<cryptonote::tx_destination_entry>& dsts,
const std::vector<size_t>& selected_transfers,
size_t fake_outputs_count,
8743 std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
8752 uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit();
8761 needed_etn += dt.amount;
8767 for(
size_t idx: selected_transfers)
8769 found_etn += m_transfers[idx].amount();
8775 uint32_t subaddr_account = m_transfers[*selected_transfers.begin()].m_subaddr_index.major;
8776 for (
auto i = ++selected_transfers.begin(); i != selected_transfers.end(); ++i)
8780 get_outs(outs, selected_transfers, fake_outputs_count, tx.
version);
8785 size_t i = 0, out_index = 0;
8786 std::vector<cryptonote::tx_source_entry> sources;
8787 for(
size_t idx: selected_transfers)
8789 sources.resize(sources.size()+1);
8797 for (
size_t n = 0; n < fake_outputs_count + 1; ++n) {
8799 oe.first = std::get<0>(outs[out_index][n]);
8800 oe.second.dest = rct::pk2rct(std::get<1>(outs[out_index][n]));
8801 oe.second.mask = std::get<2>(outs[out_index][n]);
8808 auto it_to_replace = std::find_if(src.
outputs.begin(), src.
outputs.end(), [&](
const tx_output_entry &
a) {
8809 return a.first == td.m_global_output_index;
8812 "real output not found");
8814 tx_output_entry real_oe;
8816 real_oe.second.dest = rct::pk2rct(
8819 *it_to_replace = real_oe;
8834 if (needed_etn < found_etn)
8837 uint32_t change_subaddress_minor = tx.
version > 2 ? sources.front().subaddr_index.minor : 0;
8838 change_dts.
addr = get_subaddress({subaddr_account, change_subaddress_minor});
8839 change_dts.
is_subaddress = (subaddr_account != 0 || change_subaddress_minor != 0);
8840 change_dts.
amount = found_etn - needed_etn;
8843 std::vector<cryptonote::tx_destination_entry> splitted_dsts, dust_dsts;
8845 destination_split_strategy(dsts, change_dts, dust_policy.
dust_threshold, splitted_dsts, dust_dsts);
8846 for(
auto& d: dust_dsts) {
8850 for(
auto& d: dust_dsts) {
8857 std::vector<crypto::secret_key> additional_tx_keys;
8861 bool r =
cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.
addr, extra, tx, unlock_time, tx_key, additional_tx_keys,
false, {}, m_multisig ? &msout : NULL, m_account_major_offset, this->m_nettype);
8867 bool are_ins_correct_type = tx.
version >= 3 ?
8868 std::all_of(tx.
vin.begin(), tx.
vin.end(), [&](
const txin_v& s_e) ->
bool
8870 CHECKED_GET_SPECIFIC_VARIANT(s_e, const txin_to_key_public, in, false);
8874 std::all_of(tx.
vin.begin(), tx.
vin.end(), [&](
const txin_v& s_e) ->
bool
8876 CHECKED_GET_SPECIFIC_VARIANT(s_e, const txin_to_key, in, false);
8877 key_images += boost::to_string(in.k_image) +
" ";
8887 if (dust_policy.
add_to_fee || dust_sent_elsewhere) change_dts.
amount -= dust;
8891 ptx.
dust = ((dust_policy.
add_to_fee || dust_sent_elsewhere) ? dust : 0);
8911 for (
size_t idx: selected_transfers)
8916 std::vector<size_t> wallet2::pick_preferred_rct_inputs(
uint64_t needed_etn,
uint32_t subaddr_account,
const std::set<uint32_t> &subaddr_indices)
const
8918 std::vector<size_t> picks;
8919 float current_output_relatdness = 1.0f;
8924 for (
size_t i = 0; i < m_transfers.size(); ++i)
8926 const transfer_details& td = m_transfers[i];
8927 if (!td.m_spent && !td.m_frozen && td.is_rct() && td.amount() >= needed_etn && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
8939 for (
size_t i = 0; i < m_transfers.size(); ++i)
8941 const transfer_details& td = m_transfers[i];
8942 if (!td.m_spent && !td.m_frozen && !td.m_key_image_partial && td.is_rct() && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
8945 for (
size_t j = i + 1; j < m_transfers.size(); ++j)
8947 const transfer_details& td2 = m_transfers[j];
8948 if (!td2.m_spent && !td2.m_frozen && !td.m_key_image_partial && td2.is_rct() && td.amount() + td2.amount() >= needed_etn && is_transfer_unlocked(td2) && td2.m_subaddr_index == td.m_subaddr_index)
8953 float relatedness = get_output_relatedness(td, td2);
8954 LOG_PRINT_L2(
" with input " << j <<
", " <<
print_etn(td2.amount()) <<
", relatedness " << relatedness);
8955 if (relatedness < current_output_relatdness)
8964 if (relatedness == 0.0f)
8966 current_output_relatdness = relatedness;
8976 bool wallet2::should_pick_a_second_output(
bool use_rct,
size_t n_transfers,
const std::vector<size_t> &unused_transfers_indices,
const std::vector<size_t> &unused_dust_indices)
const
8980 if (n_transfers > 1)
8982 if (unused_dust_indices.empty() && unused_transfers_indices.empty())
8988 for (
auto i: unused_dust_indices)
8990 if (m_transfers[i].is_rct())
8996 if (!found)
for (
auto i: unused_transfers_indices)
8998 if (m_transfers[i].is_rct())
9009 std::vector<size_t> wallet2::get_only_rct(
const std::vector<size_t> &unused_dust_indices,
const std::vector<size_t> &unused_transfers_indices)
const
9011 std::vector<size_t> indices;
9012 for (
size_t n: unused_dust_indices)
9013 if (m_transfers[n].is_rct())
9014 indices.push_back(n);
9015 for (
size_t n: unused_transfers_indices)
9016 if (m_transfers[n].is_rct())
9017 indices.push_back(n);
9021 static uint32_t get_count_above(
const std::vector<wallet2::transfer_details> &transfers,
const std::vector<size_t> &indices,
uint64_t threshold)
9024 for (
size_t idx: indices)
9025 if (transfers[idx].amount() >=
threshold)
9030 bool wallet2::light_wallet_login(
bool &new_address)
9032 MDEBUG(
"Light wallet login request");
9033 m_light_wallet_connected =
false;
9036 request.address = get_account().get_public_address_str(m_nettype);
9039 request.create_account =
true;
9040 m_daemon_rpc_mutex.lock();
9042 m_daemon_rpc_mutex.unlock();
9044 m_light_wallet_connected = connected && (
response.status.empty() ||
response.status ==
"success");
9045 new_address =
response.new_address;
9049 if(m_light_wallet_connected)
9056 return m_light_wallet_connected;
9061 MDEBUG(
"Light wallet import wallet request");
9063 oreq.address = get_account().get_public_address_str(m_nettype);
9065 m_daemon_rpc_mutex.lock();
9067 m_daemon_rpc_mutex.unlock();
9074 void wallet2::light_wallet_get_unspent_outs()
9076 MDEBUG(
"Getting unspent outs");
9082 oreq.address = get_account().get_public_address_str(m_nettype);
9088 oreq.use_dust =
true;
9091 m_daemon_rpc_mutex.lock();
9092 bool r =
invoke_http_json(
"/get_unspent_outs", oreq, ores, rpc_timeout,
"POST");
9093 m_daemon_rpc_mutex.unlock();
9097 m_light_wallet_per_kb_fee = ores.per_kb_fee;
9099 std::unordered_map<crypto::hash,bool> transfers_txs;
9100 for(
const auto &t: m_transfers)
9101 transfers_txs.emplace(t.m_txid,t.m_spent);
9103 MDEBUG(
"FOUND " << ores.outputs.size() <<
" outputs");
9106 if(ores.outputs.empty())
9110 m_transfers.clear();
9112 for (
const auto &o: ores.outputs) {
9114 bool add_transfer =
true;
9120 for (
const std::string &ski: o.spend_key_images) {
9126 if(light_wallet_key_image_is_ours(unspent_key_image, tx_public_key, o.index)){
9127 MTRACE(
"Output " << o.public_key <<
" is spent. Key image: " << ski);
9131 MTRACE(
"Unspent output found. " << o.public_key);
9146 for(
auto &t: m_transfers){
9149 add_transfer =
false;
9157 m_transfers.push_back(boost::value_initialized<transfer_details>());
9185 std::unordered_map<crypto::hash,address_tx>::const_iterator found = m_light_wallet_address_txs.find(txid);
9187 bool miner_tx = found->second.m_coinbase;
9206 MDEBUG(
"output index: " << o.global_index);
9222 set_unspent(m_transfers.size()-1);
9223 m_key_images[td.
m_key_image] = m_transfers.size()-1;
9234 request.address = get_account().get_public_address_str(m_nettype);
9236 m_daemon_rpc_mutex.lock();
9238 m_daemon_rpc_mutex.unlock();
9244 void wallet2::light_wallet_get_address_txs()
9246 MDEBUG(
"Refreshing light wallet");
9251 ireq.address = get_account().get_public_address_str(m_nettype);
9253 m_daemon_rpc_mutex.lock();
9254 bool r =
invoke_http_json(
"/get_address_txs", ireq, ires, rpc_timeout,
"POST");
9255 m_daemon_rpc_mutex.unlock();
9262 if(ires.transactions.empty())
9266 std::vector<crypto::hash> payments_txs;
9267 for(
const auto &p: m_payments)
9268 payments_txs.push_back(p.second.m_tx_hash);
9269 std::vector<crypto::hash> unconfirmed_payments_txs;
9270 for(
const auto &up: m_unconfirmed_payments)
9271 unconfirmed_payments_txs.push_back(up.second.m_pd.m_tx_hash);
9276 std::vector<crypto::hash> pool_txs;
9278 for (
const auto &t: ires.transactions) {
9279 const uint64_t total_received = t.total_received;
9280 uint64_t total_sent = t.total_sent;
9283 for(
const auto &so: t.spent_outputs)
9292 if(!light_wallet_key_image_is_ours(
key_image, tx_public_key, so.out_index)) {
9294 total_sent -= so.amount;
9299 if(total_sent == 0 && total_received == 0)
9311 bool incoming = (total_received > total_sent);
9315 address_tx.
m_amount = incoming ? total_received - total_sent : total_sent - total_received;
9322 m_light_wallet_address_txs.emplace(tx_hash,
address_tx);
9326 if(total_received > total_sent) {
9329 payment.
m_amount = total_received - total_sent;
9337 if (std::find(unconfirmed_payments_txs.begin(), unconfirmed_payments_txs.end(), tx_hash) == unconfirmed_payments_txs.end()) {
9338 pool_txs.push_back(tx_hash);
9343 emplace_or_replace(m_unconfirmed_payments, payment_id,
pool_payment_details{payment,
false,
false});
9344 if (0 != m_callback) {
9345 m_callback->on_lw_unconfirmed_etn_received(t.height, payment.
m_tx_hash, payment.
m_amount);
9349 if (std::find(payments_txs.begin(), payments_txs.end(), tx_hash) == payments_txs.end()) {
9350 m_payments.emplace(tx_hash, payment);
9351 if (0 != m_callback) {
9358 uint64_t amount_sent = total_sent - total_received;
9361 wallet_total_sent += total_sent;
9366 if(m_unconfirmed_txs.find(tx_hash) == m_unconfirmed_txs.end())
9374 utd.
m_state = wallet2::unconfirmed_transfer_details::pending;
9375 m_unconfirmed_txs.emplace(tx_hash,utd);
9381 auto confirmed_tx = m_confirmed_txs.find(tx_hash);
9382 if(confirmed_tx == m_confirmed_txs.end()) {
9384 if(m_unconfirmed_txs.find(tx_hash) != m_unconfirmed_txs.end())
9386 process_unconfirmed(tx_hash, dummy_tx, t.height);
9398 m_confirmed_txs.emplace(tx_hash,ctd);
9400 if (0 != m_callback)
9402 m_callback->on_lw_etn_spent(t.height, tx_hash, amount_sent);
9409 if(confirmed_tx->second.m_amount_in != amount_sent || confirmed_tx->second.m_amount_out != amount_sent)
9411 MDEBUG(
"Adjusting amount sent/received for tx: <" + t.hash +
">. Is tx sent to own wallet? " <<
print_etn(amount_sent) <<
" != " <<
print_etn(confirmed_tx->second.m_amount_in));
9412 confirmed_tx->second.m_amount_in = amount_sent;
9413 confirmed_tx->second.m_amount_out = amount_sent;
9414 confirmed_tx->second.m_change = 0;
9421 remove_obsolete_pool_txs(pool_txs);
9424 m_light_wallet_balance = ires.total_received-wallet_total_sent;
9426 if(ires.total_received_unlocked > 0)
9427 m_light_wallet_unlocked_balance = ires.total_received_unlocked-wallet_total_sent;
9429 m_light_wallet_unlocked_balance = m_light_wallet_balance;
9435 if (rct_string.empty())
9439 std::string rct_commit_str = rct_string.substr(0,64);
9440 std::string encrypted_mask_str = rct_string.substr(64,64);
9460 std::map<uint64_t, crypto::key_image> index_keyimage_map;
9461 std::unordered_map<crypto::public_key, std::map<uint64_t, crypto::key_image> >::const_iterator found_pub_key = m_key_image_cache.find(tx_public_key);
9462 if(found_pub_key != m_key_image_cache.end()) {
9464 index_keyimage_map = found_pub_key->second;
9465 std::map<uint64_t,crypto::key_image>::const_iterator index_found = index_keyimage_map.find(out_index);
9466 if(index_found != index_keyimage_map.end())
9467 return key_image == index_found->second;
9491 CHECK_AND_ASSERT_MES(in_ephemeral.
pub == out_pkey_test,
false,
"derived secret key doesn't match derived public key");
9495 index_keyimage_map.emplace(out_index, calculated_key_image);
9496 m_key_image_cache.emplace(tx_public_key, index_keyimage_map);
9497 return key_image == calculated_key_image;
9515 std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts,
const size_t fake_outs_count,
const uint64_t unlock_time,
uint32_t priority,
const std::vector<uint8_t>& extra,
uint32_t subaddr_account, std::set<uint32_t> subaddr_indices)
9519 boost::unique_lock<hw::device> hwdev_lock (hwdev);
9523 auto original_dsts = dsts;
9525 if(m_light_wallet) {
9527 light_wallet_get_unspent_outs();
9530 uint8_t tx_version = this->public_transactions_required() ? 3 : 1;
9532 std::vector<std::pair<uint32_t, std::vector<size_t>>> unused_transfers_indices_per_subaddr;
9533 std::vector<std::pair<uint32_t, std::vector<size_t>>> unused_dust_indices_per_subaddr;
9535 uint64_t accumulated_fee, accumulated_outputs, accumulated_change;
9537 std::vector<size_t> selected_transfers;
9538 std::vector<cryptonote::tx_destination_entry> dsts;
9543 std::vector<std::vector<tools::wallet2::get_outs_entry>> outs;
9545 TX() : weight(0), needed_fee(0) {}
9548 if (merge_destinations)
9550 std::vector<cryptonote::tx_destination_entry>::iterator i;
9552 if (i == dsts.end())
9558 i->amount += amount;
9564 if (original_output_index == dsts.size())
9567 dsts.back().amount = 0;
9570 dsts[original_output_index].amount += amount;
9574 std::vector<TX> txes;
9577 uint64_t needed_fee, available_for_fee = 0;
9578 uint64_t upper_transaction_weight_limit;
9579 uint64_t extra_bytes = extra.size();
9583 case 0 : upper_transaction_weight_limit = get_upper_transaction_weight_limit();
break;
9590 case 1 : upper_transaction_weight_limit = 3000 - extra_bytes;
break;
9593 case 2 : upper_transaction_weight_limit = 3000 - extra_bytes;
break;
9598 const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
9604 const uint64_t base_fee = get_base_fee();
9605 const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
9606 const uint64_t fee_quantization_mask = get_fee_quantization_mask();
9617 needed_etn += dt.amount;
9625 std::map<uint32_t, std::pair<uint64_t, uint64_t>> unlocked_balance_per_subaddr = unlocked_balance_per_subaddress(subaddr_account, tx_version >= 3);
9626 std::map<uint32_t, uint64_t> balance_per_subaddr = balance_per_subaddress(subaddr_account, tx_version >= 3);
9628 if (subaddr_indices.empty())
9630 for (
const auto& i : balance_per_subaddr)
9631 subaddr_indices.insert(i.first);
9637 uint64_t min_fee = (fee_multiplier * base_fee * estimate_tx_size(use_rct, 1, fake_outs_count, 2, extra.size(), bulletproof));
9639 if (!use_per_byte_fee){
9641 if(min_fee == 0){min_fee += 10;}
9644 uint64_t unlocked_balance_subtotal = 0;
9645 for (
uint32_t index_minor : subaddr_indices)
9647 balance_subtotal += balance_per_subaddr[index_minor];
9648 unlocked_balance_subtotal += unlocked_balance_per_subaddr[index_minor].first;
9651 balance_subtotal, needed_etn, 0);
9654 unlocked_balance_subtotal, needed_etn, 0);
9657 LOG_PRINT_L2(
"Candidate subaddress index for spending: " << i);
9660 const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof);
9661 const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof);
9663 const size_t tx_weight_per_ring = tx_weight_two_rings - tx_weight_one_ring;
9664 const uint64_t fractional_threshold = (fee_multiplier * base_fee * tx_weight_per_ring) / (use_per_byte_fee ? 1 : 1024);
9667 size_t num_nondust_outputs = 0;
9668 size_t num_dust_outputs = 0;
9669 for (
size_t i = 0; i < m_transfers.size(); ++i)
9672 if (m_ignore_fractional_outputs && td.
amount() < fractional_threshold)
9679 if((tx_version < 3 && td.m_tx.version > 1) || (tx_version >= 3 && td.
m_tx.
version == 1))
9683 auto find_predicate = [&index_minor](
const std::pair<uint32_t, std::vector<size_t>>& x) {
return x.first == index_minor; };
9686 auto found = std::find_if(unused_transfers_indices_per_subaddr.begin(), unused_transfers_indices_per_subaddr.end(), find_predicate);
9687 if (found == unused_transfers_indices_per_subaddr.end())
9689 unused_transfers_indices_per_subaddr.push_back({index_minor, {i}});
9693 found->second.push_back(i);
9695 ++num_nondust_outputs;
9699 auto found = std::find_if(unused_dust_indices_per_subaddr.begin(), unused_dust_indices_per_subaddr.end(), find_predicate);
9700 if (found == unused_dust_indices_per_subaddr.end())
9702 unused_dust_indices_per_subaddr.push_back({index_minor, {i}});
9706 found->second.push_back(i);
9715 auto sort_predicate = [&unlocked_balance_per_subaddr] (
const std::pair<uint32_t, std::vector<size_t>>& x,
const std::pair<uint32_t, std::vector<size_t>>& y)
9717 return unlocked_balance_per_subaddr[x.first].first > unlocked_balance_per_subaddr[y.first].first;
9719 std::sort(unused_transfers_indices_per_subaddr.begin(), unused_transfers_indices_per_subaddr.end(), sort_predicate);
9720 std::sort(unused_dust_indices_per_subaddr.begin(), unused_dust_indices_per_subaddr.end(), sort_predicate);
9723 LOG_PRINT_L2(
"Starting with " << num_nondust_outputs <<
" non-dust outputs and " << num_dust_outputs <<
" dust outputs");
9725 if (unused_dust_indices_per_subaddr.empty() && unused_transfers_indices_per_subaddr.empty())
9726 return std::vector<wallet2::pending_tx>();
9729 if (unused_dust_indices_per_subaddr.empty())
9730 unused_dust_indices_per_subaddr.push_back({});
9731 if (unused_transfers_indices_per_subaddr.empty())
9732 unused_transfers_indices_per_subaddr.push_back({});
9735 txes.push_back(TX());
9736 accumulated_fee = 0;
9737 accumulated_outputs = 0;
9738 accumulated_change = 0;
9741 std::vector<std::vector<tools::wallet2::get_outs_entry>> outs;
9749 std::vector<size_t> preferred_inputs;
9750 uint64_t rct_outs_needed = 2 * (fake_outs_count + 1);
9751 rct_outs_needed += 100;
9756 uint64_t estimated_fee = estimate_fee(use_per_byte_fee, use_rct, 2, fake_outs_count, 2, extra.size(), bulletproof, base_fee, fee_multiplier, fee_quantization_mask);
9757 preferred_inputs = pick_preferred_rct_inputs(needed_etn + estimated_fee, subaddr_account, subaddr_indices);
9758 if (!preferred_inputs.empty())
9761 for (
auto i: preferred_inputs) s += boost::lexical_cast<std::string>(i) +
" (" +
print_etn(m_transfers[i].amount()) +
") ";
9762 LOG_PRINT_L1(
"Found preferred rct inputs for rct tx: " << s);
9765 uint32_t index_minor = m_transfers[preferred_inputs[0]].m_subaddr_index.minor;
9766 for (
size_t i = 1; i < unused_transfers_indices_per_subaddr.size(); ++i)
9768 if (unused_transfers_indices_per_subaddr[i].first == index_minor)
9770 std::swap(unused_transfers_indices_per_subaddr[0], unused_transfers_indices_per_subaddr[i]);
9774 for (
size_t i = 1; i < unused_dust_indices_per_subaddr.size(); ++i)
9776 if (unused_dust_indices_per_subaddr[i].first == index_minor)
9778 std::swap(unused_dust_indices_per_subaddr[0], unused_dust_indices_per_subaddr[i]);
9790 unsigned int original_output_index = 0;
9791 std::vector<size_t>* unused_transfers_indices = &unused_transfers_indices_per_subaddr[0].second;
9792 std::vector<size_t>* unused_dust_indices = &unused_dust_indices_per_subaddr[0].second;
9795 while ((!dsts.empty() && dsts[0].amount > 0) || adding_fee || !preferred_inputs.empty() || should_pick_a_second_output(use_rct, txes.back().selected_transfers.size(), *unused_transfers_indices, *unused_dust_indices)) {
9796 TX &tx = txes.back();
9798 LOG_PRINT_L2(
"Start of loop with " << unused_transfers_indices->size() <<
" " << unused_dust_indices->size() <<
", tx.dsts.size() " << tx.dsts.size());
9799 LOG_PRINT_L2(
"unused_transfers_indices: " << strjoin(*unused_transfers_indices,
" "));
9800 LOG_PRINT_L2(
"unused_dust_indices: " << strjoin(*unused_dust_indices,
" "));
9802 LOG_PRINT_L2(
"adding_fee " << adding_fee <<
", use_rct " << use_rct);
9805 if (unused_dust_indices->empty() && unused_transfers_indices->empty()) {
9813 if (!preferred_inputs.empty()) {
9814 idx = pop_back(preferred_inputs);
9815 pop_if_present(*unused_transfers_indices, idx);
9816 pop_if_present(*unused_dust_indices, idx);
9817 }
else if ((dsts.empty() || dsts[0].amount == 0) && !adding_fee) {
9819 std::vector<size_t> indices = get_only_rct(*unused_dust_indices, *unused_transfers_indices);
9820 idx = pop_best_value(indices, tx.selected_transfers,
true);
9823 uint64_t min_output_value = m_min_output_value;
9824 uint32_t min_output_count = m_min_output_count;
9825 if (min_output_value == 0 && min_output_count == 0)
9830 if (m_transfers[idx].amount() >= min_output_value) {
9831 if (get_count_above(m_transfers, *unused_transfers_indices, min_output_value) < min_output_count) {
9832 LOG_PRINT_L2(
"Second output was not strictly needed, and we're running out of outputs above " <<
print_etn(min_output_value) <<
", not adding");
9839 float relatedness = get_output_relatedness(m_transfers[idx], m_transfers[tx.selected_transfers.front()]);
9842 LOG_PRINT_L2(
"Second output was not strictly needed, and relatedness " << relatedness <<
", not adding");
9845 pop_if_present(*unused_transfers_indices, idx);
9846 pop_if_present(*unused_dust_indices, idx);
9848 idx = pop_best_value(unused_transfers_indices->empty() ? *unused_dust_indices : *unused_transfers_indices, tx.selected_transfers);
9854 tx.selected_transfers.push_back(idx);
9856 accumulated_outputs += available_amount;
9864 available_for_fee += available_amount;
9868 while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof) <
TX_WEIGHT_TARGET(upper_transaction_weight_limit))
9873 tx.add(dsts[0], dsts[0].amount, original_output_index, m_merge_destinations);
9874 available_amount -= dsts[0].amount;
9877 ++original_output_index;
9880 if (available_amount > 0 && !dsts.empty() && estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof) <
TX_WEIGHT_TARGET(upper_transaction_weight_limit)) {
9884 tx.add(dsts[0], available_amount, original_output_index, m_merge_destinations);
9885 dsts[0].amount -= available_amount;
9886 available_amount = 0;
9891 LOG_PRINT_L2(
"Considering whether to create a tx now, " << tx.selected_transfers.size() <<
" inputs, tx limit "
9892 << upper_transaction_weight_limit);
9893 bool try_tx =
false;
9895 if (preferred_inputs.empty())
9900 try_tx = available_for_fee >= needed_fee;
9904 const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof);
9905 try_tx = dsts.empty() || (estimated_rct_tx_weight >=
TX_WEIGHT_TARGET(upper_transaction_weight_limit));
9915 needed_fee = estimate_fee(use_per_byte_fee, use_rct ,tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, base_fee, fee_multiplier, fee_quantization_mask);
9917 uint64_t inputs = 0, outputs = needed_fee;
9918 for (
size_t idx: tx.selected_transfers) inputs += m_transfers[idx].amount();
9919 for (
const auto &o: tx.dsts) outputs += o.amount;
9921 if (inputs < outputs)
9923 LOG_PRINT_L2(
"We don't have enough for the basic fee, switching to adding_fee");
9928 LOG_PRINT_L2(
"Trying to create a tx now, with " << tx.dsts.size() <<
" outputs and " <<
9929 tx.selected_transfers.size() <<
" inputs");
9931 transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
9934 needed_fee = calculate_fee(use_per_byte_fee, test_ptx.
tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
9936 LOG_PRINT_L2(
"Made a " << get_weight_string(test_ptx.
tx, txBlob.size()) <<
" tx, with " <<
print_etn(available_for_fee) <<
" available for fee (" <<
9939 if (needed_fee > available_for_fee && !dsts.empty() && dsts[0].amount > 0)
9943 std::vector<cryptonote::tx_destination_entry>::iterator i;
9944 i = std::find_if(tx.dsts.begin(), tx.dsts.end(),
9947 if (i->amount > needed_fee)
9949 uint64_t new_paid_amount = i->amount - needed_fee;
9951 print_etn(i->amount) <<
" to " <<
print_etn(new_paid_amount) <<
" to accommodate " <<
9953 dsts[0].amount += i->amount - new_paid_amount;
9954 i->amount = new_paid_amount;
9955 test_ptx.
fee = needed_fee;
9956 available_for_fee = needed_fee;
9960 if (needed_fee > available_for_fee)
9962 LOG_PRINT_L2(
"We could not make a tx, switching to fee accumulation");
9969 while (needed_fee > test_ptx.
fee) {
9970 transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
9973 needed_fee = calculate_fee(use_per_byte_fee, test_ptx.
tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
9974 LOG_PRINT_L2(
"Made an attempt at a final " << get_weight_string(test_ptx.
tx, txBlob.size()) <<
" tx, with " <<
print_etn(test_ptx.
fee) <<
9985 tx.needed_fee = test_ptx.
fee;
9986 accumulated_fee += test_ptx.
fee;
9991 LOG_PRINT_L2(
"We have more to pay, starting another tx");
9992 txes.push_back(TX());
9993 original_output_index = 0;
10001 if ((!dsts.empty() && dsts[0].amount > 0) || adding_fee)
10003 if (unused_transfers_indices->empty() && unused_transfers_indices_per_subaddr.size() > 1)
10005 unused_transfers_indices_per_subaddr.erase(unused_transfers_indices_per_subaddr.begin());
10006 unused_transfers_indices = &unused_transfers_indices_per_subaddr[0].second;
10008 if (unused_dust_indices->empty() && unused_dust_indices_per_subaddr.size() > 1)
10010 unused_dust_indices_per_subaddr.erase(unused_dust_indices_per_subaddr.begin());
10011 unused_dust_indices = &unused_dust_indices_per_subaddr[0].second;
10018 LOG_PRINT_L1(
"We ran out of outputs while trying to gather final fee");
10023 " total fee, " <<
print_etn(accumulated_change) <<
" total change");
10026 for (std::vector<TX>::iterator i = txes.begin(); i != txes.end(); ++i)
10030 test_tx.
version = tx_version;
10032 transfer_selected(tx.dsts,
10033 tx.selected_transfers,
10050 std::vector<wallet2::pending_tx> ptx_vector;
10051 for (std::vector<TX>::iterator i = txes.begin(); i != txes.end(); ++i)
10055 for (
size_t idx: tx.selected_transfers)
10056 tx_etn += m_transfers[idx].amount();
10057 LOG_PRINT_L1(
" Transaction " << (1+std::distance(txes.begin(), i)) <<
"/" << txes.size() <<
10058 " " <<
get_transaction_hash(tx.ptx.tx) <<
": " << get_weight_string(tx.weight) <<
", sending " <<
print_etn(tx_etn) <<
" in " << tx.selected_transfers.size() <<
10059 " outputs to " << tx.dsts.size() <<
" destination(s), including " <<
10060 print_etn(tx.ptx.fee) <<
" fee, " <<
print_etn(tx.ptx.change_dts.amount) <<
" change");
10061 ptx_vector.push_back(tx.ptx);
10070 bool wallet2::sanity_check(
const std::vector<wallet2::pending_tx> &ptx_vector,
10071 std::vector<cryptonote::tx_destination_entry> dsts)
const {
10072 MDEBUG(
"sanity_check: " << ptx_vector.size() <<
" txes, " << dsts.size() <<
" destinations");
10078 if (std::all_of(ptx_vector.begin(), ptx_vector.end(), [](
const pending_tx &ptx) { return ptx.tx.version == 1; })) {
10080 std::unordered_map<account_public_address, std::pair<uint64_t, bool>> required;
10081 for (
const auto &d: dsts) {
10082 required[d.addr].first += d.amount;
10083 required[d.addr].second = d.is_subaddress;
10088 for (
const auto &ptx: ptx_vector) {
10090 change += m_transfers[idx].amount();
10093 for (
const auto &r: required)
10094 change -= r.second.first;
10109 for (
const auto &r: required) {
10115 for (
const auto &ptx: ptx_vector) {
10120 "automatic-sanity-check");
10123 catch (
const std::exception &e) { received = 0; }
10124 total_received += received;
10127 std::stringstream ss;
10128 ss <<
"Total received by "
10149 std::vector<wallet2::pending_tx> wallet2::create_transactions_all(
uint64_t below,
const cryptonote::account_public_address &
address,
bool is_subaddress,
const size_t outputs,
const size_t fake_outs_count,
const uint64_t unlock_time,
uint32_t priority,
const std::vector<uint8_t>& extra,
uint32_t subaddr_account, std::set<uint32_t> subaddr_indices,
const bool migrate)
10151 std::vector<size_t> unused_transfers_indices;
10152 std::vector<size_t> unused_dust_indices;
10153 const bool use_rct = use_fork_rules(4, 0);
10154 uint8_t tx_version = public_transactions_required() ? (migrate ? 2 : 3) : 1;
10156 std::map<uint32_t, std::pair<std::vector<size_t>, std::vector<size_t>>> unused_transfer_dust_indices_per_subaddr;
10159 bool fund_found =
false;
10160 for (
size_t i = 0; i < m_transfers.size(); ++i)
10164 if((tx_version < 3 && td.m_tx.version > 1) || (tx_version >= 3 && td.
m_tx.
version == 1))
10170 if (below == 0 || td.
amount() < below)
10182 if (subaddr_indices.empty())
10185 if (unused_transfer_dust_indices_per_subaddr.count(0) == 1 && unused_transfer_dust_indices_per_subaddr.size() > 1)
10186 unused_transfer_dust_indices_per_subaddr.erase(0);
10187 auto i = unused_transfer_dust_indices_per_subaddr.begin();
10188 std::advance(i,
crypto::rand_idx(unused_transfer_dust_indices_per_subaddr.size()));
10189 unused_transfers_indices = i->second.first;
10190 unused_dust_indices = i->second.second;
10191 LOG_PRINT_L2(
"Spending from subaddress index " << i->first);
10195 for (
const auto& p : unused_transfer_dust_indices_per_subaddr)
10197 unused_transfers_indices.insert(unused_transfers_indices.end(), p.second.first.begin(), p.second.first.end());
10198 unused_dust_indices.insert(unused_dust_indices.end(), p.second.second.begin(), p.second.second.end());
10199 LOG_PRINT_L2(
"Spending from subaddress index " << p.first);
10203 return create_transactions_from(
address, is_subaddress, outputs, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra, tx_version);
10208 std::vector<size_t> unused_transfers_indices;
10209 std::vector<size_t> unused_dust_indices;
10211 const bool use_rct = use_fork_rules(4, 0);
10213 for (
size_t i = 0; i < m_transfers.size(); ++i)
10220 unused_transfers_indices.push_back(i);
10222 unused_dust_indices.push_back(i);
10226 return create_transactions_from(
address, is_subaddress, outputs, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra, tx_version);
10229 std::vector<wallet2::pending_tx> wallet2::create_transactions_from(
const cryptonote::account_public_address &
address,
bool is_subaddress,
const size_t outputs, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices,
const size_t fake_outs_count,
const uint64_t unlock_time,
uint32_t priority,
const std::vector<uint8_t>& extra,
const uint8_t tx_version)
10233 boost::unique_lock<hw::device> hwdev_lock (hwdev);
10236 uint64_t accumulated_fee, accumulated_outputs, accumulated_change;
10238 std::vector<size_t> selected_transfers;
10239 std::vector<cryptonote::tx_destination_entry> dsts;
10244 std::vector<std::vector<get_outs_entry>> outs;
10246 TX() : weight(0), needed_fee(0) {}
10248 std::vector<TX> txes;
10249 uint64_t needed_fee, available_for_fee = 0;
10250 uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit();
10251 std::vector<std::vector<get_outs_entry>> outs;
10254 const bool use_rct = fake_outs_count > 0 && use_fork_rules(4, 0);
10255 const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
10260 const uint64_t base_fee = get_base_fee();
10261 const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
10262 const uint64_t fee_quantization_mask = get_fee_quantization_mask();
10264 LOG_PRINT_L2(
"Starting with " << unused_transfers_indices.size() <<
" non-dust outputs and " << unused_dust_indices.size() <<
" dust outputs");
10266 if (unused_dust_indices.empty() && unused_transfers_indices.empty())
10267 return std::vector<wallet2::pending_tx>();
10270 txes.push_back(TX());
10271 accumulated_fee = 0;
10272 accumulated_outputs = 0;
10273 accumulated_change = 0;
10278 while (!unused_dust_indices.empty() || !unused_transfers_indices.empty()) {
10279 TX &tx = txes.back();
10287 const uint64_t estimated_tx_weight_with_one_extra_output = estimate_tx_weight(use_rct, tx.selected_transfers.size() + 1, fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof);
10288 fee_dust_threshold = calculate_fee_from_weight(base_fee, estimated_tx_weight_with_one_extra_output, fee_multiplier, fee_quantization_mask);
10292 fee_dust_threshold = base_fee * fee_multiplier * (upper_transaction_weight_limit + 1023) / 1024;
10296 unused_transfers_indices.empty()
10297 ? pop_best_value(unused_dust_indices, tx.selected_transfers)
10298 : unused_dust_indices.empty()
10299 ? pop_best_value(unused_transfers_indices, tx.selected_transfers)
10300 : ((tx.selected_transfers.size() & 1) || accumulated_outputs > fee_dust_threshold)
10301 ? pop_best_value(unused_dust_indices, tx.selected_transfers)
10302 : pop_best_value(unused_transfers_indices, tx.selected_transfers);
10308 tx.selected_transfers.push_back(idx);
10310 accumulated_outputs += available_amount;
10316 LOG_PRINT_L2(
"Considering whether to create a tx now, " << tx.selected_transfers.size() <<
" inputs, tx limit "
10317 << upper_transaction_weight_limit);
10318 const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 2, extra.size(), bulletproof);
10319 bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || ( estimated_rct_tx_weight >=
TX_WEIGHT_TARGET(upper_transaction_weight_limit));
10323 test_tx.
version = tx_version;
10326 needed_fee = tx_version == 2 ? 0 : estimate_fee(use_per_byte_fee, use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, base_fee, fee_multiplier, fee_quantization_mask);
10329 for (
size_t i = 0; i < ((outputs > 1) ? outputs - 1 : outputs); ++i)
10332 LOG_PRINT_L2(
"Trying to create a tx now, with " << tx.dsts.size() <<
" destinations and " <<
10333 tx.selected_transfers.size() <<
" outputs");
10335 transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
10339 needed_fee = tx_version == 2 ? 0 : calculate_fee(use_per_byte_fee, test_ptx.
tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
10341 for (
auto &dt: test_ptx.
dests)
10342 available_for_fee += dt.amount;
10343 LOG_PRINT_L2(
"Made a " << get_weight_string(test_ptx.
tx, txBlob.size()) <<
" tx, with " <<
print_etn(available_for_fee) <<
" available for fee (" <<
10353 LOG_PRINT_L2(
"We made a tx, adjusting fee and saving it");
10355 uint64_t amount_transferred = available_for_fee - needed_fee;
10356 uint64_t dt_amount = amount_transferred / outputs;
10358 uint64_t residue = amount_transferred % outputs;
10359 for (
auto &dt: tx.dsts)
10367 dt.amount = dt_amount + dt_residue;
10369 transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
10372 needed_fee = tx_version == 2 ? 0 : calculate_fee(use_per_byte_fee, test_ptx.
tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
10373 LOG_PRINT_L2(
"Made an attempt at a final " << get_weight_string(test_ptx.
tx, txBlob.size()) <<
" tx, with " <<
print_etn(test_ptx.
fee) <<
10375 }
while (needed_fee > test_ptx.
fee);
10384 tx.needed_fee = test_ptx.
fee;
10385 accumulated_fee += test_ptx.
fee;
10387 if (!unused_transfers_indices.empty() || !unused_dust_indices.empty())
10389 LOG_PRINT_L2(
"We have more to pay, starting another tx");
10390 txes.push_back(TX());
10396 " total fee, " <<
print_etn(accumulated_change) <<
" total change");
10399 for (std::vector<TX>::iterator i = txes.begin(); i != txes.end(); ++i)
10403 test_tx.
version = tx_version;
10405 transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra,
10414 std::vector<wallet2::pending_tx> ptx_vector;
10415 for (std::vector<TX>::iterator i = txes.begin(); i != txes.end(); ++i)
10419 for (
size_t idx: tx.selected_transfers)
10420 tx_etn += m_transfers[idx].amount();
10421 LOG_PRINT_L1(
" Transaction " << (1+std::distance(txes.begin(), i)) <<
"/" << txes.size() <<
10422 " " <<
get_transaction_hash(tx.ptx.tx) <<
": " << get_weight_string(tx.weight) <<
", sending " <<
print_etn(tx_etn) <<
" in " << tx.selected_transfers.size() <<
10423 " outputs to " << tx.dsts.size() <<
" destination(s), including " <<
10424 print_etn(tx.ptx.fee) <<
" fee, " <<
print_etn(tx.ptx.change_dts.amount) <<
" change");
10425 ptx_vector.push_back(tx.ptx);
10429 for (
const TX &tx: txes)
10431 for (
size_t idx: tx.selected_transfers)
10433 a += m_transfers[idx].amount();
10445 void wallet2::cold_tx_aux_import(
const std::vector<pending_tx> & ptx,
const std::vector<std::string> & tx_device_aux)
10448 for (
size_t i = 0; i < ptx.size(); ++i){
10451 set_tx_device_aux(txid, tx_device_aux[i]);
10455 void wallet2::cold_sign_tx(
const std::vector<pending_tx>& ptx_vector,
signed_tx_set &exported_txs, std::vector<cryptonote::address_parse_info> &dsts_info, std::vector<std::string> & tx_device_aux)
10457 auto & hwdev = get_account().get_device();
10459 throw std::invalid_argument(
"Device does not support cold sign protocol");
10463 for (
auto &tx: ptx_vector)
10465 txs.
txes.push_back(get_construction_data_with_decrypted_short_payment_id(tx, m_account.get_device()));
10467 txs.
transfers = std::make_pair(0, m_transfers);
10477 dev_cold->tx_sign(&
wallet_shim, txs, exported_txs, aux_data);
10480 MDEBUG(
"Signed tx data from hw: " << exported_txs.
ptx.size() <<
" transactions");
10485 auto & hwdev = get_account().get_device();
10491 std::vector<std::pair<crypto::key_image, crypto::signature>> ski;
10495 dev_cold->ki_sync(&
wallet_shim, m_transfers, ski);
10498 uint64_t import_res = import_key_images(ski, 0, spent, unspent, is_trusted_daemon());
10499 m_device_last_key_image_sync =
time(NULL);
10506 boost::optional<std::string> result = m_node_rpc_proxy.get_earliest_height(
version, earliest_height);
10507 throw_on_rpc_response_error(result,
"get_hard_fork_info");
10516 boost::optional<std::string> result = m_node_rpc_proxy.get_height(
height);
10517 throw_on_rpc_response_error(result,
"get_info");
10518 result = m_node_rpc_proxy.get_earliest_height(
version, earliest_height);
10519 throw_on_rpc_response_error(result,
"get_hard_fork_info");
10526 return close_enough;
10529 uint64_t wallet2::get_upper_transaction_weight_limit()
const
10531 if (m_upper_transaction_weight_limit > 0)
10532 return m_upper_transaction_weight_limit;
10533 uint64_t full_reward_zone = use_fork_rules(10, 10) ?
10539 if (use_fork_rules(8, 10))
10545 std::vector<size_t> wallet2::select_available_outputs(
const std::function<
bool(
const transfer_details &td)> &f)
const
10547 std::vector<size_t> outputs;
10549 for (transfer_container::const_iterator i = m_transfers.begin(); i != m_transfers.end(); ++i, ++n)
10555 if (i->m_key_image_partial)
10557 if (!is_transfer_unlocked(*i))
10560 outputs.push_back(n);
10565 std::vector<uint64_t> wallet2::get_unspent_amounts_vector()
const
10567 std::set<uint64_t> set;
10568 for (
const auto &td: m_transfers)
10570 if (!td.m_spent && !td.m_frozen)
10571 set.insert(td.is_rct() ? 0 : td.amount());
10573 std::vector<uint64_t> vector;
10574 vector.reserve(set.size());
10575 for (
const auto &i: set)
10577 vector.push_back(i);
10582 std::vector<size_t> wallet2::select_available_outputs_from_histogram(
uint64_t count,
bool atleast,
bool unlocked,
bool allow_rct)
10586 m_daemon_rpc_mutex.lock();
10587 if (is_trusted_daemon())
10588 req_t.amounts = get_unspent_amounts_vector();
10589 req_t.min_count =
count;
10590 req_t.max_count = 0;
10591 req_t.unlocked = unlocked;
10592 req_t.recent_cutoff = 0;
10593 bool r =
invoke_http_json_rpc(
"/json_rpc",
"get_output_histogram", req_t, resp_t, rpc_timeout);
10594 m_daemon_rpc_mutex.unlock();
10599 std::set<uint64_t> mixable;
10600 for (
const auto &i: resp_t.histogram)
10602 mixable.insert(i.amount);
10605 return select_available_outputs([mixable, atleast, allow_rct](
const transfer_details &td) {
10606 if (!allow_rct && td.
is_rct())
10610 if (mixable.find(amount) != mixable.end())
10614 if (mixable.find(amount) == mixable.end())
10625 m_daemon_rpc_mutex.lock();
10626 req_t.amounts.push_back(0);
10627 req_t.min_count = 0;
10628 req_t.max_count = 0;
10629 req_t.unlocked =
true;
10630 req_t.recent_cutoff = 0;
10631 bool r =
invoke_http_json_rpc(
"/json_rpc",
"get_output_histogram", req_t, resp_t, rpc_timeout);
10632 m_daemon_rpc_mutex.unlock();
10639 return resp_t.histogram[0].total_instances;
10645 return m_transfers[idx];
10648 std::vector<size_t> wallet2::select_available_unmixable_outputs()
10651 return select_available_outputs_from_histogram(get_min_ring_size(),
false,
true,
false);
10654 std::vector<size_t> wallet2::select_available_mixable_outputs()
10657 return select_available_outputs_from_histogram(get_min_ring_size(),
true,
true,
true);
10660 std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions()
10663 const bool hf1_rules = use_fork_rules(2, 10);
10666 uint8_t tx_version = this->public_transactions_required() ? 3 : 1;
10667 const uint64_t base_fee = get_base_fee();
10670 std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs();
10671 size_t num_dust_outputs = unmixable_outputs.size();
10673 if (num_dust_outputs == 0)
10675 return std::vector<wallet2::pending_tx>();
10679 std::vector<size_t> unmixable_transfer_outputs, unmixable_dust_outputs;
10680 for (
auto n: unmixable_outputs)
10682 if (m_transfers[n].amount() < base_fee)
10683 unmixable_dust_outputs.push_back(n);
10685 unmixable_transfer_outputs.push_back(n);
10688 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);
10691 void wallet2::discard_unmixable_outputs()
10694 std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs();
10695 for (
size_t idx : unmixable_outputs)
10703 additional_tx_keys.clear();
10704 const std::unordered_map<crypto::hash, crypto::secret_key>::const_iterator i = m_tx_keys.find(txid);
10705 if (i == m_tx_keys.end())
10707 tx_key = i->second;
10708 const auto j = m_additional_tx_keys.find(txid);
10709 if (j != m_additional_tx_keys.end())
10710 additional_tx_keys = j->second;
10716 bool r = get_tx_key_cached(txid, tx_key, additional_tx_keys);
10722 auto & hwdev = get_account().get_device();
10730 const auto tx_data_it = m_tx_device.find(txid);
10731 if (tx_data_it == m_tx_device.end())
10733 MDEBUG(
"Aux data not found for txid: " << txid);
10739 if (!dev_cold->is_get_tx_key_supported())
10741 MDEBUG(
"get_tx_key not supported by the device");
10746 dev_cold->load_tx_key_data(tx_key_data, tx_data_it->second);
10754 req.decode_as_json =
false;
10756 m_daemon_rpc_mutex.lock();
10758 m_daemon_rpc_mutex.unlock();
10771 "Failed to get the right transaction from daemon");
10776 std::vector<crypto::secret_key> tx_keys;
10777 dev_cold->get_tx_key(tx_keys, tx_key_data, m_account.get_keys().m_view_secret_key);
10778 if (tx_keys.empty())
10783 tx_key = tx_keys[0];
10784 tx_keys.erase(tx_keys.begin());
10785 additional_tx_keys = tx_keys;
10795 req.decode_as_json =
false;
10800 const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
10807 "daemon returned wrong response for gettransactions, wrong txs count = " +
10812 "Failed to get transaction from daemon");
10814 std::vector<tx_extra_field> tx_extra_fields;
10817 bool found =
false;
10823 if (calculated_pub_key == pub_key_field.
pub_key)
10833 m_tx_keys.insert(std::make_pair(txid, tx_key));
10834 m_additional_tx_keys.insert(std::make_pair(txid, additional_tx_keys));
10840 "get_spend_proof requires spend secret key and is not available for a watch-only wallet");
10845 req.decode_as_json =
false;
10850 const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
10857 "daemon returned wrong response for gettransactions, wrong txs count = " +
10864 std::vector<std::vector<crypto::signature>> signatures;
10872 for(
size_t i = 0; i < tx.
vin.size(); ++i)
10874 const txin_to_key*
const in_key = boost::get<txin_to_key>(std::addressof(tx.
vin[i]));
10875 if (in_key ==
nullptr)
10879 const auto found = m_key_images.find(in_key->
k_image);
10880 if(found == m_key_images.end())
10894 THROW_WALLET_EXCEPTION_IF(!
generate_key_image_helper(m_account.get_keys(), m_subaddresses, in_tx_out_pkey->
key, in_tx_pub_key, in_additionakl_tx_pub_keys, in_td.
m_internal_output_index, in_ephemeral, in_img, m_account.get_device(), m_account_major_offset),
10900 const size_t ring_size = in_key->
key_offsets.size();
10903 req.outputs.resize(ring_size);
10904 for (
size_t j = 0; j < ring_size; ++j)
10906 req.outputs[j].amount = in_key->
amount;
10907 req.outputs[j].index = absolute_offsets[j];
10912 const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
10919 "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
10923 std::vector<const crypto::public_key *> p_output_keys;
10925 p_output_keys.push_back(&out.key);
10928 size_t sec_index = -1;
10929 for (
size_t j = 0; j < ring_size; ++j)
10931 if (
res.outs[j].key == in_ephemeral.
pub)
10940 signatures.push_back(std::vector<crypto::signature>());
10941 std::vector<crypto::signature>& sigs = signatures.back();
10947 for (
const std::vector<crypto::signature>& ring_sig : signatures)
10956 const size_t header_len = header.size();
10958 "Signature header check error");
10963 req.decode_as_json =
false;
10968 const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
10975 "daemon returned wrong response for gettransactions, wrong txs count = " +
10983 size_t num_sigs = 0;
10984 for(
size_t i = 0; i < tx.
vin.size(); ++i)
10986 const txin_to_key*
const in_key = boost::get<txin_to_key>(std::addressof(tx.
vin[i]));
10987 if (in_key !=
nullptr)
10990 std::vector<std::vector<crypto::signature>> signatures = { std::vector<crypto::signature>(1) };
10992 if( sig_str.size() != header_len + num_sigs * sig_len ) {
10997 signatures.clear();
10998 size_t offset = header_len;
10999 for(
size_t i = 0; i < tx.
vin.size(); ++i)
11001 const txin_to_key*
const in_key = boost::get<txin_to_key>(std::addressof(tx.
vin[i]));
11002 if (in_key ==
nullptr)
11004 signatures.resize(signatures.size() + 1);
11005 signatures.back().resize(in_key->
key_offsets.size());
11006 for (
size_t j = 0; j < in_key->
key_offsets.size(); ++j)
11022 std::vector<std::vector<crypto::signature>>::const_iterator sig_iter = signatures.cbegin();
11023 for(
size_t i = 0; i < tx.
vin.size(); ++i)
11025 const txin_to_key*
const in_key = boost::get<txin_to_key>(std::addressof(tx.
vin[i]));
11026 if (in_key ==
nullptr)
11032 req.outputs.resize(absolute_offsets.size());
11033 for (
size_t j = 0; j < absolute_offsets.size(); ++j)
11035 req.outputs[j].amount = in_key->
amount;
11036 req.outputs[j].index = absolute_offsets[j];
11041 const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
11048 "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
11052 std::vector<const crypto::public_key *> p_output_keys;
11054 p_output_keys.push_back(&out.key);
11070 "Failed to generate key derivation from supplied parameters");
11072 std::vector<crypto::key_derivation> additional_derivations;
11073 additional_derivations.resize(additional_tx_keys.size());
11074 for (
size_t i = 0; i < additional_tx_keys.size(); ++i)
11076 "Failed to generate key derivation from supplied parameters");
11078 check_tx_key_helper(txid, derivation, additional_derivations,
address, received, in_pool, confirmations);
11085 for (
size_t n = 0; n < tx.
vout.size(); ++n)
11094 bool found = out_key->
key == derived_out_key;
11096 if (!found && !additional_derivations.empty())
11100 found = out_key->
key == derived_out_key;
11101 found_derivation = additional_derivations[n];
11107 amount = tx.
vout[n].amount;
11108 received += amount;
11118 req.decode_as_json =
false;
11120 m_daemon_rpc_mutex.lock();
11122 m_daemon_rpc_mutex.unlock();
11128 if (
res.txs.size() == 1)
11130 ok = get_pruned_tx(
res.txs.front(), tx, tx_hash);
11144 "Failed to get the right transaction from daemon");
11146 "The size of additional derivations is wrong");
11148 check_tx_key_helper(tx, derivation, additional_derivations,
address, received);
11150 in_pool =
res.txs.front().in_pool;
11155 uint64_t bc_height = get_daemon_blockchain_height(err);
11157 confirmations = bc_height -
res.txs.front().block_height;
11167 req.decode_as_json =
false;
11169 m_daemon_rpc_mutex.lock();
11171 m_daemon_rpc_mutex.unlock();
11177 if (
res.txs.size() == 1)
11179 ok = get_pruned_tx(
res.txs.front(), tx, tx_hash);
11197 std::vector<crypto::secret_key> additional_tx_keys;
11198 const bool is_out = m_subaddresses.count(
address.m_spend_public_key) == 0;
11204 return get_tx_proof(tx, tx_key, additional_tx_keys,
address, is_subaddress,
message);
11212 const bool is_out = m_subaddresses.count(
address.m_spend_public_key) == 0;
11220 std::vector<crypto::public_key> shared_secret;
11221 std::vector<crypto::signature> sig;
11225 const size_t num_sigs = 1 + additional_tx_keys.size();
11226 shared_secret.resize(num_sigs);
11227 sig.resize(num_sigs);
11230 shared_secret[0] = rct::rct2pk(aP);
11235 tx_pub_key = rct2pk(aP);
11241 hwdev.
generate_tx_proof(prefix_hash, tx_pub_key,
address.m_view_public_key, boost::none, shared_secret[0], tx_key, sig[0]);
11243 for (
size_t i = 1; i < num_sigs; ++i)
11245 hwdev.
scalarmultKey(aP, rct::pk2rct(
address.m_view_public_key), rct::sk2rct(additional_tx_keys[i - 1]));
11246 shared_secret[i] = rct::rct2pk(aP);
11249 hwdev.
scalarmultKey(aP, rct::pk2rct(
address.m_spend_public_key), rct::sk2rct(additional_tx_keys[i - 1]));
11250 tx_pub_key = rct2pk(aP);
11251 hwdev.
generate_tx_proof(prefix_hash, tx_pub_key,
address.m_view_public_key,
address.m_spend_public_key, shared_secret[i], additional_tx_keys[i - 1], sig[i]);
11256 hwdev.
generate_tx_proof(prefix_hash, tx_pub_key,
address.m_view_public_key, boost::none, shared_secret[i], additional_tx_keys[i - 1], sig[i]);
11267 const size_t num_sigs = 1 + additional_tx_pub_keys.size();
11268 shared_secret.resize(num_sigs);
11269 sig.resize(num_sigs);
11272 hwdev.
scalarmultKey(aP, rct::pk2rct(tx_pub_key), rct::sk2rct(
a));
11273 shared_secret[0] = rct2pk(aP);
11282 for (
size_t i = 1; i < num_sigs; ++i)
11284 hwdev.
scalarmultKey(aP,rct::pk2rct(additional_tx_pub_keys[i - 1]), rct::sk2rct(
a));
11285 shared_secret[i] = rct2pk(aP);
11292 hwdev.
generate_tx_proof(prefix_hash,
address.m_view_public_key, additional_tx_pub_keys[i - 1], boost::none, shared_secret[i],
a, sig[i]);
11297 const size_t num_sigs = shared_secret.size();
11302 std::vector<crypto::key_derivation> additional_derivations(num_sigs - 1);
11303 for (
size_t i = 1; i < num_sigs; ++i)
11306 check_tx_key_helper(tx, derivation, additional_derivations,
address, received);
11310 for (
size_t i = 0; i < num_sigs; ++i)
11323 req.decode_as_json =
false;
11325 m_daemon_rpc_mutex.lock();
11327 m_daemon_rpc_mutex.unlock();
11333 if (
res.txs.size() == 1)
11335 ok = get_pruned_tx(
res.txs.front(), tx, tx_hash);
11353 in_pool =
res.txs.front().in_pool;
11358 uint64_t bc_height = get_daemon_blockchain_height(err);
11360 confirmations = bc_height -
res.txs.front().block_height;
11369 const bool is_out = sig_str.substr(0, 3) ==
"Out";
11370 const std::string header = is_out ?
"OutProofV1" :
"InProofV1";
11371 const size_t header_len = header.size();
11373 "Signature header check error");
11376 std::vector<crypto::public_key> shared_secret(1);
11377 std::vector<crypto::signature> sig(1);
11380 const size_t num_sigs = (sig_str.size() - header_len) / (pk_len + sig_len);
11382 "Wrong signature size");
11383 shared_secret.resize(num_sigs);
11384 sig.resize(num_sigs);
11385 for (
size_t i = 0; i < num_sigs; ++i)
11389 const size_t offset = header_len + i * (pk_len + sig_len);
11391 "Signature decoding error");
11393 "Signature decoding error");
11395 "Signature decoding error");
11413 std::vector<int> good_signature(num_sigs, 0);
11416 good_signature[0] = is_subaddress ?
11420 for (
size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
11422 good_signature[i + 1] = is_subaddress ?
11429 good_signature[0] = is_subaddress ?
11433 for (
size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
11435 good_signature[i + 1] = is_subaddress ?
11441 if (std::any_of(good_signature.begin(), good_signature.end(), [](
int i) { return i > 0; }))
11445 if (good_signature[0])
11448 std::vector<crypto::key_derivation> additional_derivations(num_sigs - 1);
11449 for (
size_t i = 1; i < num_sigs; ++i)
11450 if (good_signature[i])
11453 check_tx_key_helper(tx, derivation, additional_derivations,
address, received);
11464 "Not enough balance in this account for the requested minimum reserve amount");
11467 std::vector<size_t> selected_transfers;
11468 for (
size_t i = 0; i < m_transfers.size(); ++i)
11472 selected_transfers.push_back(i);
11475 if (account_minreserve)
11479 std::sort(selected_transfers.begin(), selected_transfers.end(), [&](
const size_t a,
const size_t b)
11480 { return m_transfers[a].amount() > m_transfers[b].amount(); });
11481 while (selected_transfers.size() >= 2 && m_transfers[selected_transfers[1]].amount() >= account_minreserve->second)
11482 selected_transfers.erase(selected_transfers.begin());
11485 while (total < account_minreserve->second)
11487 total += m_transfers[selected_transfers[sz]].amount();
11490 selected_transfers.resize(sz);
11496 for (
size_t i = 0; i < selected_transfers.size(); ++i)
11498 prefix_data.append((
const char*)&m_transfers[selected_transfers[i]].m_key_image,
sizeof(
crypto::key_image));
11504 std::vector<reserve_proof_entry> proofs(selected_transfers.size());
11505 std::unordered_set<cryptonote::subaddress_index> subaddr_indices = { {0,0} };
11506 for (
size_t i = 0; i < selected_transfers.size(); ++i)
11524 for (
int i = 0; i < 2; ++i)
11533 if (m_subaddresses.count(subaddress_spendkey) == 1)
11536 "Normal tx pub key doesn't derive the expected output, while the additional tx pub keys are empty");
11538 "Neither normal tx pub key nor additional tx pub key derive the expected output key");
11539 tx_pub_key_used = &additional_tx_pub_keys[proof.
index_in_tx];
11553 const std::vector<const crypto::public_key*> pubs = { &ephemeral.
pub };
11558 std::unordered_map<crypto::public_key, crypto::signature> subaddr_spendkeys;
11562 if (!index.is_zero())
11564 crypto::secret_key m = m_account.get_device().get_subaddress_secret_key(m_account.get_keys().m_view_secret_key, index);
11566 sc_add((
unsigned char*)&subaddr_spend_skey, (
unsigned char*)&m, (
unsigned char*)&tmp);
11574 std::ostringstream oss;
11576 ar << proofs << subaddr_spendkeys;
11586 static constexpr
char header[] =
"ReserveProofV1";
11588 "Signature header check error");
11592 "Signature decoding error");
11594 std::istringstream iss(sig_decoded);
11596 std::vector<reserve_proof_entry> proofs;
11597 std::unordered_map<crypto::public_key, crypto::signature> subaddr_spendkeys;
11598 ar >> proofs >> subaddr_spendkeys;
11601 "The given address isn't found in the proof");
11606 for (
size_t i = 0; i < proofs.size(); ++i)
11616 for (
size_t i = 0; i < proofs.size(); ++i)
11618 gettx_req.decode_as_json =
false;
11619 gettx_req.prune =
true;
11620 m_daemon_rpc_mutex.lock();
11621 bool ok =
invoke_http_json(
"/gettransactions", gettx_req, gettx_res, rpc_timeout);
11622 m_daemon_rpc_mutex.unlock();
11629 for (
size_t i = 0; i < proofs.size(); ++i)
11631 m_daemon_rpc_mutex.lock();
11632 ok =
invoke_http_json(
"/is_key_image_spent", kispent_req, kispent_res, rpc_timeout);
11633 m_daemon_rpc_mutex.unlock();
11638 for (
size_t i = 0; i < proofs.size(); ++i)
11645 ok = get_pruned_tx(gettx_res.txs[i], tx, tx_hash);
11662 if (!ok && additional_tx_pub_keys.size() == tx.
vout.size())
11668 const std::vector<const crypto::public_key*> pubs = { &out_key->
key };
11679 "The address doesn't seem to have received the fund");
11693 if (kispent_res.spent_status[i])
11698 for (
const auto &i : subaddr_spendkeys)
11708 return m_wallet_file;
11713 return m_keys_file;
11718 return m_daemon_address;
11721 uint64_t wallet2::get_daemon_blockchain_height(
string &err)
const
11725 boost::optional<std::string> result = m_node_rpc_proxy.get_height(
height);
11728 if (m_trusted_daemon)
11731 err =
"daemon error";
11739 uint64_t wallet2::get_daemon_blockchain_target_height(
string &err)
11743 const auto result = m_node_rpc_proxy.get_target_height(target_height);
11746 if (m_trusted_daemon)
11749 err =
"daemon error";
11752 return target_height;
11755 uint64_t wallet2::get_approximate_blockchain_height()
const
11758 const time_t fork_time = m_nettype ==
TESTNET ? 1341378000 : m_nettype ==
STAGENET ? 1521000000 : 1538815057;
11764 uint64_t approx_blockchain_height = fork_block + (
time(NULL) - fork_time)/seconds_per_block;
11766 static const uint64_t approximate_testnet_rolled_back_blocks = 303967;
11767 if (m_nettype ==
TESTNET && approx_blockchain_height > approximate_testnet_rolled_back_blocks)
11768 approx_blockchain_height -= approximate_testnet_rolled_back_blocks;
11771 approx_blockchain_height += 82000;
11773 LOG_PRINT_L2(
"Calculated blockchain height: " << approx_blockchain_height);
11774 return approx_blockchain_height;
11779 m_tx_notes[txid] = note;
11784 std::unordered_map<crypto::hash, std::string>::const_iterator i = m_tx_notes.find(txid);
11785 if (i == m_tx_notes.end())
11792 m_tx_device[txid] = aux;
11797 std::unordered_map<crypto::hash, std::string>::const_iterator i = m_tx_device.find(txid);
11798 if (i == m_tx_device.end())
11810 std::unordered_map<std::string, std::string>::const_iterator i = m_attributes.find(
key);
11811 if (i == m_attributes.end())
11818 set_attribute(ATTRIBUTE_DESCRIPTION, description);
11823 return get_attribute(ATTRIBUTE_DESCRIPTION);
11826 const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& wallet2::get_account_tags()
11829 if (m_account_tags.second.size() != get_num_subaddress_accounts())
11830 m_account_tags.second.resize(get_num_subaddress_accounts(),
"");
11831 for (
const std::string& tag : m_account_tags.second)
11833 if (!tag.empty() && m_account_tags.first.count(tag) == 0)
11834 m_account_tags.first.insert({tag,
""});
11836 for (
auto i = m_account_tags.first.begin(); i != m_account_tags.first.end(); )
11838 if (std::find(m_account_tags.second.begin(), m_account_tags.second.end(), i->first) == m_account_tags.second.end())
11839 i = m_account_tags.first.erase(i);
11843 return m_account_tags;
11846 void wallet2::set_account_tag(
const std::set<uint32_t> &account_indices,
const std::string& tag)
11848 for (
uint32_t account_index : account_indices)
11851 if (m_account_tags.second[account_index] == tag)
11852 MDEBUG(
"This tag is already assigned to this account");
11854 m_account_tags.second[account_index] = tag;
11856 get_account_tags();
11863 m_account_tags.first[tag] = description;
11878 const size_t header_len = strlen(
"SigV1");
11891 if (
sizeof(s) != decoded.size()) {
11895 memcpy(&s, decoded.data(),
sizeof(s));
11913 if (
signature.size() < MULTISIG_SIGNATURE_MAGIC.size() ||
signature.substr(0, MULTISIG_SIGNATURE_MAGIC.size()) != MULTISIG_SIGNATURE_MAGIC) {
11914 MERROR(
"Signature header check error");
11921 MERROR(
"Signature decoding error");
11925 if (
sizeof(s) != decoded.size()) {
11926 MERROR(
"Signature decoding error");
11929 memcpy(&s, decoded.data(),
sizeof(s));
11935 std::vector<tx_extra_field> tx_extra_fields;
11948 "Public key wasn't found in the transaction extra");
11958 size_t pk_index = 0;
11967 for (
size_t i = 0; i < td.
m_tx.
vout.size(); ++i)
11970 check_acc_out_precomp(td.
m_tx.
vout[i], derivation, {}, i, tx_scan_info);
11984 std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> ski = export_key_images();
11987 const uint32_t offset = ski.first;
11992 data[0] = offset & 0xff;
11993 data[1] = (offset >> 8) & 0xff;
11994 data[2] = (offset >> 16) & 0xff;
11995 data[3] = (offset >> 24) & 0xff;
11998 for (
const auto &i: ski.second)
12006 std::string ciphertext = encrypt_with_view_secret_key(data);
12011 std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> wallet2::export_key_images(
bool all)
const
12014 std::vector<std::pair<crypto::key_image, crypto::signature>> ski;
12019 while (offset < m_transfers.size() && !m_transfers[offset].m_key_image_request)
12023 ski.reserve(m_transfers.size() - offset);
12024 for (
size_t n = offset; n < m_transfers.size(); ++n)
12031 "Output is not txout_to_key");
12036 std::vector<tx_extra_field> tx_extra_fields;
12058 std::vector<const crypto::public_key*> key_ptrs;
12059 key_ptrs.push_back(&pkey);
12065 return std::make_pair(offset, ski);
12085 data = decrypt_with_view_secret_key(
std::string(data, magiclen));
12087 catch (
const std::exception &e)
12107 size_t nki = (data.size() - headerlen) / record_size;
12109 std::vector<std::pair<crypto::key_image, crypto::signature>> ski;
12111 for (
size_t n = 0; n < nki; ++n)
12119 return import_key_images(ski, offset, spent, unspent);
12123 uint64_t wallet2::import_key_images(
const std::vector<std::pair<crypto::key_image, crypto::signature>> &signed_key_images,
size_t offset,
uint64_t &spent,
uint64_t &unspent,
bool check_spent)
12131 "The blockchain is out of date compared to the signed key images");
12133 if (signed_key_images.empty() && offset == 0)
12140 req.key_images.reserve(signed_key_images.size());
12143 for (
size_t n = 0; n < signed_key_images.size(); ++n)
12152 "Non txout_to_key output found");
12158 std::vector<const crypto::public_key*> pkeys;
12159 pkeys.push_back(&pkey);
12174 for (
size_t n = 0; n < signed_key_images.size(); ++n)
12176 m_transfers[n + offset].m_key_image = signed_key_images[n].first;
12177 m_key_images[m_transfers[n + offset].m_key_image] = n + offset;
12178 m_transfers[n + offset].m_key_image_known =
true;
12179 m_transfers[n + offset].m_key_image_request =
false;
12180 m_transfers[n + offset].m_key_image_partial =
false;
12187 m_daemon_rpc_mutex.lock();
12188 bool r =
invoke_http_json(
"/is_key_image_spent", req, daemon_resp, rpc_timeout);
12189 m_daemon_rpc_mutex.unlock();
12194 "daemon returned wrong response for is_key_image_spent, wrong amounts count = " +
12196 for (
size_t n = 0; n < daemon_resp.spent_status.size(); ++n)
12204 std::unordered_set<crypto::hash> spent_txids;
12205 std::vector<size_t> swept_transfers;
12207 std::unordered_map<crypto::key_image, crypto::hash> spent_key_images;
12215 spent_key_images.insert(std::make_pair(boost::get<cryptonote::txin_to_key>(in).k_image, td.m_txid));
12221 for(
size_t i = 0; i < offset; ++i)
12234 for(
size_t i = 0; i < signed_key_images.size(); ++i)
12245 << (td.
m_spent ?
"spent" :
"unspent") <<
" (key image " << req.key_images[i] <<
")");
12249 const std::unordered_map<crypto::key_image, crypto::hash>::const_iterator skii = spent_key_images.find(td.
m_key_image);
12250 if (skii == spent_key_images.end())
12251 swept_transfers.push_back(i);
12253 spent_txids.insert(skii->second);
12265 gettxs_req.decode_as_json =
false;
12266 gettxs_req.prune =
true;
12267 gettxs_req.txs_hashes.reserve(spent_txids.size());
12273 m_daemon_rpc_mutex.lock();
12274 bool r =
invoke_http_json(
"/gettransactions", gettxs_req, gettxs_res, rpc_timeout);
12275 m_daemon_rpc_mutex.unlock();
12279 "daemon returned wrong response for gettransactions, wrong count = " +
std::to_string(gettxs_res.txs.size()) +
", expected " +
std::to_string(spent_txids.size()));
12284 auto spent_txid = spent_txids.begin();
12286 auto it = spent_txids.begin();
12305 std::vector<crypto::key_derivation> additional_derivations;
12306 for (
size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
12308 additional_derivations.push_back({});
12317 check_acc_out_precomp(out, derivation, additional_derivations,
output_index, tx_scan_info);
12336 std::set<uint32_t> subaddr_indices;
12341 auto it = m_key_images.find(boost::get<cryptonote::txin_to_key>(in).k_image);
12342 if (it != m_key_images.end())
12346 uint64_t amount = boost::get<cryptonote::txin_to_key>(in).
amount;
12354 tx_etn_spent_in_ins += amount;
12361 LOG_PRINT_L0(
"WARNING: This tx spends outputs received by different subaddress accounts, which isn't supposed to happen");
12368 process_outgoing(*spent_txid, spent_tx, e.
block_height, e.
block_timestamp, tx_etn_spent_in_ins, tx_etn_got_in_outs, subaddr_account, subaddr_indices);
12371 for (
auto j = m_payments.begin(); j != m_payments.end(); ++j)
12373 if (j->second.m_tx_hash == *spent_txid)
12375 m_payments.erase(j);
12385 for (
size_t n : swept_transfers)
12394 m_confirmed_txs.insert(std::make_pair(spent_txid, pd));
12400 return m_transfers[signed_key_images.size() + offset - 1].m_block_height;
12403 bool wallet2::import_key_images(std::vector<crypto::key_image> key_images,
size_t offset, boost::optional<std::unordered_set<size_t>> selected_transfers)
12405 if (key_images.size() + offset > m_transfers.size())
12407 LOG_PRINT_L1(
"More key images returned that we know outputs for");
12410 for (
size_t ki_idx = 0; ki_idx < key_images.size(); ++ki_idx)
12412 const size_t transfer_idx = ki_idx + offset;
12413 if (selected_transfers && selected_transfers.get().find(transfer_idx) == selected_transfers.get().end())
12416 transfer_details &td = m_transfers[transfer_idx];
12417 if (td.m_key_image_known && !td.m_key_image_partial && td.m_key_image != key_images[ki_idx])
12418 LOG_PRINT_L0(
"WARNING: imported key image differs from previously known key image at index " << ki_idx <<
": trusting imported one");
12419 td.m_key_image = key_images[ki_idx];
12420 m_key_images[td.m_key_image] = transfer_idx;
12421 td.m_key_image_known =
true;
12422 td.m_key_image_request =
false;
12423 td.m_key_image_partial =
false;
12424 m_pub_keys[td.get_public_key()] = transfer_idx;
12430 bool wallet2::import_key_images(
signed_tx_set & signed_tx,
size_t offset,
bool only_selected_transfers)
12432 std::unordered_set<size_t> selected_transfers;
12433 if (only_selected_transfers)
12438 selected_transfers.insert(s);
12442 return import_key_images(signed_tx.
key_images, offset, only_selected_transfers ? boost::make_optional(selected_transfers) : boost::none);
12448 for (
auto const &p : m_payments)
12450 payments.emplace(p);
12456 m_payments.clear();
12457 for (
auto const &p : payments)
12459 m_payments.emplace(p);
12462 void wallet2::import_payments_out(
const std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>> &confirmed_payments)
12464 m_confirmed_txs.clear();
12465 for (
auto const &p : confirmed_payments)
12467 m_confirmed_txs.emplace(p);
12471 std::tuple<size_t,crypto::hash,std::vector<crypto::hash>> wallet2::export_blockchain()
const
12473 std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> bc;
12474 std::get<0>(bc) = m_blockchain.offset();
12475 std::get<1>(bc) = m_blockchain.empty() ? crypto::null_hash: m_blockchain.genesis();
12476 for (
size_t n = m_blockchain.offset(); n < m_blockchain.size(); ++n)
12478 std::get<2>(bc).push_back(m_blockchain[n]);
12483 void wallet2::import_blockchain(
const std::tuple<
size_t,
crypto::hash, std::vector<crypto::hash>> &bc)
12485 m_blockchain.clear();
12486 if (std::get<0>(bc))
12488 for (
size_t n = std::get<0>(bc); n > 0; --n)
12489 m_blockchain.push_back(std::get<1>(bc));
12490 m_blockchain.trim(std::get<0>(bc));
12492 for (
auto const &b : std::get<2>(bc))
12494 m_blockchain.push_back(b);
12497 generate_genesis(genesis);
12499 check_genesis(genesis_hash);
12503 std::pair<size_t, std::vector<tools::wallet2::transfer_details>> wallet2::export_outputs(
bool all)
const
12506 std::vector<tools::wallet2::transfer_details> outs;
12510 while (offset < m_transfers.size() && (m_transfers[offset].m_key_image_known && !m_transfers[offset].m_key_image_request))
12513 outs.reserve(m_transfers.size() - offset);
12514 for (
size_t n = offset; n < m_transfers.size(); ++n)
12518 outs.push_back(td);
12521 return std::make_pair(offset, outs);
12528 std::stringstream oss;
12530 const auto& outputs = export_outputs(all);
12539 std::string ciphertext = encrypt_with_view_secret_key(header + oss.str());
12540 return magic + ciphertext;
12543 size_t wallet2::import_outputs(
const std::pair<
size_t, std::vector<tools::wallet2::transfer_details>> &outputs)
12548 "Imported outputs omit more outputs that we know of");
12550 const size_t offset = outputs.first;
12551 const size_t original_size = m_transfers.size();
12552 m_transfers.resize(offset + outputs.second.size());
12553 for (
size_t i = 0; i < offset; ++i)
12554 m_transfers[i].m_key_image_request =
false;
12555 for (
size_t i = 0; i < outputs.second.size(); ++i)
12560 if (i + offset < original_size)
12566 #define CMPF(f) if (!(td.f == org_td.f)) goto process
12569 CMPF(m_internal_output_index);
12575 m_transfers[i + offset] =
std::move(td);
12598 error::wallet_internal_error,
"key_image generated ephemeral public key not matched with output_key at index " + boost::lexical_cast<std::string>(i + offset));
12602 m_transfers[i + offset] =
std::move(td);
12605 return m_transfers.size();
12621 data = decrypt_with_view_secret_key(
std::string(data, magiclen));
12623 catch (
const std::exception &e)
12629 if (data.size() < headerlen)
12641 size_t imported_outputs = 0;
12645 std::stringstream iss;
12647 std::pair<size_t, std::vector<tools::wallet2::transfer_details>> outputs;
12657 boost::archive::binary_iarchive ar(iss);
12661 imported_outputs = import_outputs(outputs);
12663 catch (
const std::exception &e)
12668 return imported_outputs;
12698 return get_multisig_signing_public_key(get_account().get_multisig_keys()[idx]);
12701 rct::key wallet2::get_multisig_k(
size_t idx,
const std::unordered_set<rct::key> &used_L)
const
12705 for (
const auto &k: m_transfers[idx].m_multisig_k)
12709 if (used_L.find(L) != used_L.end())
12722 kLRki.
ki = rct::ki2rct(m_transfers[n].m_key_image);
12726 rct::multisig_kLRki wallet2::get_multisig_composite_kLRki(
size_t n,
const std::unordered_set<crypto::public_key> &ignore_set, std::unordered_set<rct::key> &used_L, std::unordered_set<rct::key> &new_used_L)
const
12730 const transfer_details &td = m_transfers[n];
12734 size_t n_signers_used = 1;
12735 for (
const auto &p: m_transfers[n].m_multisig_info)
12737 if (ignore_set.find(p.m_signer) != ignore_set.end())
12740 for (
const auto &lr: p.m_LR)
12742 if (used_L.find(lr.m_L) != used_L.end())
12744 used_L.insert(lr.m_L);
12745 new_used_L.insert(lr.m_L);
12761 const transfer_details &td = m_transfers[n];
12765 std::vector<crypto::key_image> pkis;
12766 for (
const auto &
info: td.m_multisig_info)
12767 for (
const auto &pki:
info.m_partial_key_images)
12768 pkis.push_back(pki);
12776 std::vector<tools::wallet2::multisig_info>
info;
12780 info.resize(m_transfers.size());
12781 for (
size_t n = 0; n < m_transfers.size(); ++n)
12786 info[n].m_LR.clear();
12787 info[n].m_partial_key_images.clear();
12789 for (
size_t m = 0; m < get_account().get_multisig_keys().size(); ++m)
12794 info[n].m_partial_key_images.push_back(ki);
12800 size_t nlr =
tools::combinations_count(m_multisig_signers.size() - m_multisig_threshold, m_multisig_signers.size() - 1);
12801 for (
size_t m = 0; m < nlr; ++m)
12805 info[n].m_LR.push_back({kLRki.
L, kLRki.
R});
12808 info[n].m_signer = signer;
12811 std::stringstream oss;
12820 std::string ciphertext = encrypt_with_view_secret_key(header + oss.str());
12825 void wallet2::update_multisig_rescan_info(
const std::vector<std::vector<rct::key>> &multisig_k,
const std::vector<std::vector<tools::wallet2::multisig_info>> &
info,
size_t n)
12830 MDEBUG(
"update_multisig_rescan_info: updating index " << n);
12831 transfer_details &td = m_transfers[n];
12832 td.m_multisig_info.clear();
12833 for (
const auto &pi:
info)
12836 td.m_multisig_info.push_back(pi[n]);
12838 m_key_images.erase(td.m_key_image);
12839 td.m_key_image = get_multisig_composite_key_image(n);
12840 td.m_key_image_known =
true;
12841 td.m_key_image_request =
false;
12842 td.m_key_image_partial =
false;
12843 td.m_multisig_k = multisig_k[n];
12844 m_key_images[td.m_key_image] = n;
12847 size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs)
12851 std::vector<std::vector<tools::wallet2::multisig_info>>
info;
12852 std::unordered_set<crypto::public_key> seen;
12859 data = decrypt_with_view_secret_key(
std::string(data, magiclen));
12870 if (get_multisig_signer_public_key() == signer)
12872 MINFO(
"Multisig info from this wallet ignored");
12875 if (seen.find(signer) != seen.end())
12877 MINFO(
"Duplicate multisig info ignored");
12880 seen.insert(signer);
12883 std::istringstream iss(body);
12884 std::vector<tools::wallet2::multisig_info> i;
12887 MINFO(boost::format(
"%u outputs found") % boost::lexical_cast<std::string>(i.size()));
12893 std::vector<std::vector<rct::key>> k;
12894 k.reserve(m_transfers.size());
12895 for (
const auto &td: m_transfers)
12896 k.push_back(td.m_multisig_k);
12899 size_t n_outputs = m_transfers.size();
12900 for (
const auto &pi:
info)
12901 if (pi.size() < n_outputs)
12902 n_outputs = pi.size();
12904 if (n_outputs == 0)
12908 for (
const auto &pi:
info)
12910 CHECK_AND_ASSERT_THROW_MES(std::find(m_multisig_signers.begin(), m_multisig_signers.end(), pi[0].m_signer) != m_multisig_signers.end(),
12911 "Signer is not a member of this multisig wallet");
12912 for (
size_t n = 1; n < n_outputs; ++n)
12917 for (
auto &pi:
info)
12918 pi.resize(n_outputs);
12921 if (!
info.empty() && !
info.front().empty())
12923 std::sort(
info.begin(),
info.end(), [](
const std::vector<tools::wallet2::multisig_info> &i0,
const std::vector<tools::wallet2::multisig_info> &i1){ return memcmp(&i0[0].m_signer, &i1[0].m_signer, sizeof(i0[0].m_signer)); });
12927 for (
size_t n = 0; n < n_outputs; ++n)
12937 for (
size_t n = 0; n < n_outputs && n < m_transfers.size(); ++n)
12939 update_multisig_rescan_info(k,
info, n);
12942 m_multisig_rescan_k = &k;
12943 m_multisig_rescan_info = &
info;
12951 m_multisig_rescan_info = NULL;
12952 m_multisig_rescan_k = NULL;
12955 m_multisig_rescan_info = NULL;
12956 m_multisig_rescan_k = NULL;
12963 crypto::chacha_key
key;
12964 crypto::generate_chacha_key(&skey,
sizeof(skey),
key, m_kdf_rounds);
12966 crypto::chacha_iv iv = crypto::rand<crypto::chacha_iv>();
12967 ciphertext.resize(len +
sizeof(iv) + (authenticated ?
sizeof(
crypto::signature) : 0));
12969 memcpy(&ciphertext[0], &iv,
sizeof(iv));
12984 return encrypt(plaintext.
data(), plaintext.
size(), skey, authenticated);
12989 return encrypt(plaintext.data(), plaintext.size(), skey, authenticated);
12994 return encrypt(plaintext.
data(), plaintext.
size(), skey, authenticated);
12999 return encrypt(plaintext, get_account().get_keys().m_view_secret_key, authenticated);
13002 template<
typename T>
13005 const size_t prefix_size =
sizeof(chacha_iv) + (authenticated ?
sizeof(
crypto::signature) : 0);
13009 crypto::chacha_key
key;
13010 crypto::generate_chacha_key(&skey,
sizeof(skey),
key, m_kdf_rounds);
13011 const crypto::chacha_iv &iv = *(
const crypto::chacha_iv*)&ciphertext[0];
13022 std::unique_ptr<char[]> buffer{
new char[ciphertext.size() - prefix_size]};
13024 crypto::chacha20(ciphertext.data() +
sizeof(iv), ciphertext.size() - prefix_size,
key, iv, buffer.get());
13025 return T(buffer.get(), ciphertext.size() - prefix_size);
13032 return decrypt(ciphertext, get_account().get_keys().m_view_secret_key, authenticated);
13045 if (
info.has_payment_id && !payment_id.empty())
13047 error =
"A single payment id is allowed";
13051 if (!payment_id.empty())
13055 if (!wallet2::parse_long_payment_id(payment_id, pid32) && !parse_short_payment_id(payment_id, pid8))
13057 error =
"Invalid payment id";
13063 unsigned int n_fields = 0;
13065 if (!payment_id.empty())
13067 uri += (n_fields++ ?
"&" :
"?") +
std::string(
"tx_payment_id=") + payment_id;
13076 if (!recipient_name.empty())
13078 uri += (n_fields++ ?
"&" :
"?") +
std::string(
"recipient_name=") + epee::net_utils::conver_to_url_format(recipient_name);
13081 if (!tx_description.empty())
13083 uri += (n_fields++ ?
"&" :
"?") +
std::string(
"tx_description=") + epee::net_utils::conver_to_url_format(tx_description);
13091 if (uri.substr(0, 12) !=
"electroneum:")
13093 error =
std::string(
"URI has wrong scheme (expected \"electroneum:\"): ") + uri;
13098 const char *ptr = strchr(remainder.c_str(),
'?');
13099 address = ptr ? remainder.substr(0, ptr-remainder.c_str()) : remainder;
13107 if (!strchr(remainder.c_str(),
'?'))
13110 std::vector<std::string> arguments;
13114 boost::split(arguments, body, boost::is_any_of(
"&"));
13115 std::set<std::string> have_arg;
13116 for (
const auto &arg: arguments)
13118 std::vector<std::string> kv;
13119 boost::split(kv, arg, boost::is_any_of(
"="));
13120 if (kv.size() != 2)
13125 if (have_arg.find(kv[0]) != have_arg.end())
13130 have_arg.insert(kv[0]);
13132 if (kv[0] ==
"tx_amount")
13141 else if (kv[0] ==
"tx_payment_id")
13143 if (
info.has_payment_id)
13145 error =
"Separate payment id given with an integrated address";
13150 if (!wallet2::parse_long_payment_id(kv[1],
hash) && !wallet2::parse_short_payment_id(kv[1],
hash8))
13152 error =
"Invalid payment id: " + kv[1];
13155 payment_id = kv[1];
13157 else if (kv[0] ==
"recipient_name")
13159 recipient_name = epee::net_utils::convert_from_url_format(kv[1]);
13161 else if (kv[0] ==
"tx_description")
13163 tx_description = epee::net_utils::convert_from_url_format(kv[1]);
13167 unknown_parameters.push_back(arg);
13176 if (!check_connection(&
version))
13178 throw std::runtime_error(
"failed to connect to daemon: " + get_daemon_address());
13182 throw std::runtime_error(
"this function requires RPC version 1.6 or higher");
13184 std::tm date = { 0, 0, 0, 0, 0, 0, 0, 0 };
13185 date.tm_year = year - 1900;
13186 date.tm_mon = month - 1;
13187 date.tm_mday = day;
13188 if (date.tm_mon < 0 || 11 < date.tm_mon || date.tm_mday < 1 || 31 < date.tm_mday)
13190 throw std::runtime_error(
"month or day out of range");
13192 uint64_t timestamp_target = std::mktime(&date);
13195 uint64_t height_max = get_daemon_blockchain_height(err) - 1;
13198 throw std::runtime_error(
"failed to get blockchain height");
13204 uint64_t height_mid = (height_min + height_max) / 2;
13214 std::ostringstream oss;
13215 oss <<
"failed to get blocks by heights: ";
13216 for (
auto height : req.heights)
13218 oss << endl <<
"reason: ";
13220 oss <<
"possibly lost connection to daemon";
13222 oss <<
"daemon is busy";
13224 oss << get_rpc_status(
res.status);
13225 throw std::runtime_error(oss.str());
13228 if (
res.blocks.size() < 3)
throw std::runtime_error(
"Not enough blocks returned from daemon");
13235 if (!(timestamp_min <= timestamp_mid && timestamp_mid <= timestamp_max))
13239 return std::min({height_min, height_mid, height_max});
13241 if (timestamp_target > timestamp_max)
13243 throw std::runtime_error(
"specified date is in the future");
13245 if (timestamp_target <= timestamp_min + 2 * 24 * 60 * 60)
13249 if (timestamp_target <= timestamp_mid)
13250 height_max = height_mid;
13252 height_min = height_mid;
13253 if (height_max - height_min <= 2 * 24 * 30)
13260 bool wallet2::is_synced()
const
13263 boost::optional<std::string> result = m_node_rpc_proxy.get_target_height(
height);
13266 return get_blockchain_current_height() >=
height;
13269 std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(
const std::vector<std::pair<double, double>> &fee_levels)
13271 for (
const auto &fee_level: fee_levels)
13280 m_daemon_rpc_mutex.lock();
13282 m_daemon_rpc_mutex.unlock();
13288 const auto result = m_node_rpc_proxy.get_block_weight_limit(block_weight_limit);
13289 throw_on_rpc_response_error(result,
"get_info");
13290 uint64_t full_reward_zone = block_weight_limit / 2;
13293 std::vector<std::pair<uint64_t, uint64_t>>
blocks;
13294 for (
const auto &fee_level: fee_levels)
13296 const double our_fee_byte_min = fee_level.first;
13297 const double our_fee_byte_max = fee_level.second;
13298 uint64_t priority_weight_min = 0, priority_weight_max = 0;
13299 for (
const auto &i:
res.backlog)
13303 MWARNING(
"Got 0 weight tx from txpool, ignored");
13306 double this_fee_byte = i.fee / (double)i.weight;
13307 if (this_fee_byte >= our_fee_byte_min)
13308 priority_weight_min += i.weight;
13309 if (this_fee_byte >= our_fee_byte_max)
13310 priority_weight_max += i.weight;
13313 uint64_t nblocks_min = priority_weight_min / full_reward_zone;
13314 uint64_t nblocks_max = priority_weight_max / full_reward_zone;
13315 MDEBUG(
"estimate_backlog: priority_weight " << priority_weight_min <<
" - " << priority_weight_max <<
" for "
13316 << our_fee_byte_min <<
" - " << our_fee_byte_max <<
" piconero byte fee, "
13317 << nblocks_min <<
" - " << nblocks_max <<
" blocks at block weight " << full_reward_zone);
13318 blocks.push_back(std::make_pair(nblocks_min, nblocks_max));
13323 std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(
uint64_t min_tx_weight,
uint64_t max_tx_weight,
const std::vector<uint64_t> &fees)
13331 std::vector<std::pair<double, double>> fee_levels;
13334 double our_fee_byte_min = fee / (double)min_tx_weight, our_fee_byte_max = fee / (
double)max_tx_weight;
13335 fee_levels.emplace_back(our_fee_byte_min, our_fee_byte_max);
13337 return estimate_backlog(fee_levels);
13340 uint64_t wallet2::get_segregation_fork_height()
const
13348 if (m_segregation_height > 0)
13349 return m_segregation_height;
13351 if (m_use_dns && !m_offline)
13354 static const std::vector<std::string> dns_urls = {
13355 "segheights.electroneumpulse.org",
13356 "segheights.electroneumpulse.net",
13357 "segheights.electroneumpulse.co",
13358 "segheights.electroneumpulse.se"
13361 const uint64_t current_height = get_blockchain_current_height();
13362 uint64_t best_diff = std::numeric_limits<uint64_t>::max(), best_height = 0;
13363 std::vector<std::string> records;
13366 for (
const auto& record : records)
13368 std::vector<std::string> fields;
13369 boost::split(fields, record, boost::is_any_of(
":"));
13370 if (fields.size() != 2)
13376 MINFO(
"Found segregation height via DNS: " << fields[0] <<
" fork height at " <<
height);
13378 if (diff < best_diff)
13385 return best_height;
13398 state.nettype = m_nettype;
13399 state.multisig = multisig(&
state.multisig_is_ready);
13400 state.has_multisig_partial_key_images = has_multisig_partial_key_images();
13401 state.multisig_rounds_passed = m_multisig_rounds_passed;
13402 state.num_transfer_details = m_transfers.size();
13403 if (
state.multisig)
13406 state.address = m_original_address;
13407 state.view_secret_key = m_original_view_secret_key;
13411 state.address = m_account.get_keys().m_account_address;
13412 state.view_secret_key = m_account.get_keys().m_view_secret_key;
13414 state.mms_file=m_mms_file;
13420 if (!m_device_callback){
13423 return m_device_callback.get();
13425 void wallet2::on_device_button_request(
uint64_t code)
13427 if (
nullptr != m_callback)
13428 m_callback->on_device_button_request(code);
13431 void wallet2::on_device_button_pressed()
13433 if (
nullptr != m_callback)
13434 m_callback->on_device_button_pressed();
13437 boost::optional<epee::wipeable_string> wallet2::on_device_pin_request()
13439 if (
nullptr != m_callback)
13440 return m_callback->on_device_pin_request();
13441 return boost::none;
13444 boost::optional<epee::wipeable_string> wallet2::on_device_passphrase_request(
bool on_device)
13446 if (
nullptr != m_callback)
13447 return m_callback->on_device_passphrase_request(on_device);
13448 return boost::none;
13453 if (
nullptr != m_callback)
13454 m_callback->on_device_progress(
event);
13459 if (m_trusted_daemon)
13464 void wallet2::throw_on_rpc_response_error(
const boost::optional<std::string> &status,
const char *method)
const
13470 MERROR(
"RPC error: " << method <<
": status " << *status);
13500 if (transfer_height >= 0 && current_height >= (
uint64_t)transfer_height){
13504 hash_m_transfer(transfer, tmp_hash);
13507 current_height += 1;
13511 return current_height;
13518 hash_m_transfers((
int64_t) transfer_height, new_transfers_hash);
13520 if (new_transfers_hash !=
hash)
13528 for(
auto it = m_key_images.begin(); it != m_key_images.end(); it++)
13531 m_transfers[it->second].m_key_image = it->first;
13532 m_transfers[it->second].m_key_image_known =
true;
13538 return m_http_client.get_bytes_sent();
13543 return m_http_client.get_bytes_received();
void chacha20(const void *data, size_t length, const uint8_t *key, const uint8_t *iv, char *cipher)
void chacha8(const void *data, size_t length, const uint8_t *key, const uint8_t *iv, char *cipher)
std::tuple< uint64_t, crypto::public_key, rct::key > get_outs_entry
hw::device & get_device() const
void encrypt_viewkey(const crypto::chacha_key &key)
void set_device(hw::device &hwdev)
void encrypt_keys(const crypto::chacha_key &key)
void decrypt_keys(const crypto::chacha_key &key)
const account_keys & get_keys() const
bool init_default_checkpoints(network_type nettype)
loads the default main chain checkpoints
std::vector< uint8_t > extra
std::vector< txin_v > vin
std::vector< tx_out > vout
rct::rctSig rct_signatures
void set_connector(F connector)
bool is_connected(bool *ssl=NULL)
bool set_server(const std::string &address, boost::optional< login > user, ssl_options_t ssl_options=ssl_support_t::e_ssl_support_autodetect)
ssl_verification_t verification
bool has_strong_verification(boost::string_ref host) const noexcept
\retrurn True if host can be verified using this configuration WITHOUT system "root" CAs.
ssl_authentication_t auth
Non-owning sequence of data. Does not deep copy.
constexpr std::size_t size() const noexcept
constexpr pointer data() const noexcept
void append(const char *ptr, size_t len)
const char * data() const noexcept
bool empty() const noexcept
size_t size() const noexcept
virtual bool has_tx_cold_sign(void) const
virtual bool connect(void)=0
virtual void generate_tx_proof(const crypto::hash &prefix_hash, const crypto::public_key &R, const crypto::public_key &A, const boost::optional< crypto::public_key > &B, const crypto::public_key &D, const crypto::secret_key &r, crypto::signature &sig)=0
virtual bool conceal_derivation(crypto::key_derivation &derivation, const crypto::public_key &tx_pub_key, const std::vector< crypto::public_key > &additional_tx_pub_keys, const crypto::key_derivation &main_derivation, const std::vector< crypto::key_derivation > &additional_derivations)=0
virtual bool init(void)=0
virtual void set_network_type(cryptonote::network_type network_type)
virtual device_type get_type() const =0
virtual bool derivation_to_scalar(const crypto::key_derivation &derivation, const size_t output_index, crypto::ec_scalar &res)=0
virtual bool has_ki_cold_sync(void) const
@ TRANSACTION_CREATE_FAKE
@ TRANSACTION_CREATE_REAL
virtual crypto::public_key get_subaddress_spend_public_key(const cryptonote::account_keys &keys, const cryptonote::subaddress_index &index)=0
virtual bool set_mode(device_mode mode)
virtual void set_callback(i_device_callback *callback)
virtual bool generate_key_derivation(const crypto::public_key &pub, const crypto::secret_key &sec, crypto::key_derivation &derivation)=0
virtual bool set_name(const std::string &name)=0
virtual void set_derivation_path(const std::string &derivation_path)
virtual bool secret_key_to_public_key(const crypto::secret_key &sec, crypto::public_key &pub)=0
bool decrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key)
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
virtual bool generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key, uint64_t kdf_rounds)=0
virtual bool verify_keys(const crypto::secret_key &secret_key, const crypto::public_key &public_key)=0
virtual void computing_key_images(bool started)
virtual device_protocol_t device_protocol() const
virtual cryptonote::account_public_address get_subaddress(const cryptonote::account_keys &keys, const cryptonote::subaddress_index &index)=0
virtual bool get_public_address(cryptonote::account_public_address &pubkey)=0
virtual bool scalarmultKey(rct::key &aP, const rct::key &P, const rct::key &a)=0
std::vector< std::string > tx_device_aux
boost::optional< int > bp_version
std::vector< cryptonote::address_parse_info > tx_recipients
static void init_options(boost::program_options::options_description &desc_params)
#define MAKE_CORE_RPC_VERSION(major, minor)
#define CORE_RPC_STATUS_OK
#define CORE_RPC_STATUS_BUSY
void sc_reduce32(unsigned char *)
void sc_sub(unsigned char *, const unsigned char *, const unsigned char *)
void sc_add(unsigned char *, const unsigned char *, const unsigned char *)
#define HF_VERSION_ENABLE_RCT
#define CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE
#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5
#define HF_VERSION_MIN_MIXIN_6
#define ETN_DEFAULT_TX_SPENDABLE_AGE_V8
#define HF_VERSION_DYNAMIC_FEE
#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2
#define HF_VERSION_MIN_MIXIN_2
#define HF_VERSION_SMALLER_BP
#define HF_VERSION_ENFORCE_0_DECOY_TXS
#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1
#define CURRENT_HARDFORK_VERSION
#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS
#define DIFFICULTY_TARGET_V6
#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V8
#define HF_VERSION_MAX_RING_11
#define CRYPTONOTE_DISPLAY_DECIMAL_POINT
#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V6
#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V10
#define ETN_MINED_ETN_UNLOCK_WINDOW_V8
#define CRYPTONOTE_MAX_BLOCK_NUMBER
#define HF_VERSION_MIN_MIXIN_10
#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS
#define CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE
#define CRYPTONOTE_MINED_ETN_UNLOCK_WINDOW
#define HF_VERSION_PER_BYTE_FEE
#define HF_VERSION_MIN_MIXIN_4
std::string message("Message requiring signing")
GenericValue< UTF8<> > Value
GenericValue with UTF8 encoding.
GenericDocument< UTF8<> > Document
GenericDocument with UTF8 encoding.
Mnemonic seed generation and wallet restoration from them.
expect< void > success() noexcept
GenericStringBuffer< UTF8< char >, CrtAllocator > StringBuffer
void * memcpy(void *a, const void *b, size_t c)
void cn_fast_hash(const void *data, size_t length, char *hash)
const char * i18n_translate(const char *s, const std::string &context)
#define GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, name, type, jtype, mandatory, def)
void keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen)
void keccak_finish(KECCAK_CTX *ctx, uint8_t *md)
void keccak_update(KECCAK_CTX *ctx, const uint8_t *in, size_t inlen)
void keccak_init(KECCAK_CTX *ctx)
void * memwipe(void *src, size_t n)
#define MCLOG_RED(level, cat, x)
#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message)
#define CHECK_AND_ASSERT_THROW_MES(expr, message)
void load(Archive &a, std::unordered_map< h_key, hval > &x, const boost::serialization::version_type ver)
void add_arg(boost::program_options::options_description &description, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg, bool unique=true)
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)
bool is_arg_defaulted(const boost::program_options::variables_map &vm, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg)
T get_arg(const boost::program_options::variables_map &vm, const arg_descriptor< T, false, true > &arg)
std::string const GENESIS_TX
uint64_t const DEFAULT_DUST_THRESHOLD
uint32_t const GENESIS_NONCE
uint64_t const CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX
bool words_to_bytes(const epee::wipeable_string &words, epee::wipeable_string &dst, size_t len, bool duplicate, std::string &language_name)
Converts seed words to bytes (secret key).
bool get_is_old_style_seed(const epee::wipeable_string &seed)
Tells if the seed passed is an old style seed or not.
const std::string old_language_name
bool bytes_to_words(const char *src, size_t len, epee::wipeable_string &words, const std::string &language_name)
Converts bytes (secret key) to seed words.
const crypto::public_key null_pkey
void generate_signature(const hash &prefix_hash, const public_key &pub, const secret_key &sec, signature &sig)
void cn_fast_hash(const void *data, std::size_t length, hash &hash)
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)
const crypto::secret_key null_skey
epee::mlocked< tools::scrubbed< ec_scalar > > secret_key
bool derive_subaddress_public_key(const public_key &out_key, const key_derivation &derivation, std::size_t output_index, public_key &result)
void derive_secret_key(const key_derivation &derivation, std::size_t output_index, const secret_key &base, secret_key &derived_key)
bool generate_key_derivation(const public_key &key1, const secret_key &key2, key_derivation &derivation)
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)
bool derive_public_key(const key_derivation &derivation, std::size_t output_index, const public_key &base, public_key &derived_key)
bool check_signature(const hash &prefix_hash, const public_key &pub, const signature &sig)
bool secret_key_to_public_key(const secret_key &sec, public_key &pub)
std::enable_if< std::is_unsigned< T >::value, T >::type rand_idx(T sz)
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)
void generate_key_image(const public_key &pub, const secret_key &sec, key_image &image)
void cn_slow_hash(const void *data, std::size_t length, hash &hash, int variant=0, uint64_t height=0)
void derivation_to_scalar(const key_derivation &derivation, size_t output_index, ec_scalar &res)
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)
Holds cryptonote related classes and helpers.
crypto::secret_key calculate_multisig_signer_key(const std::vector< crypto::secret_key > &multisig_keys)
std::string obj_to_json_str(T &obj)
bool construct_tx_and_get_tx_key(const account_keys &sender_account_keys, const std::unordered_map< crypto::public_key, subaddress_index > &subaddresses, std::vector< tx_source_entry > &sources, std::vector< tx_destination_entry > &destinations, const boost::optional< cryptonote::account_public_address > &change_addr, const std::vector< uint8_t > &extra, transaction &tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector< crypto::secret_key > &additional_tx_keys, bool rct, const rct::RCTConfig &rct_config, rct::multisig_out *msout, const uint32_t account_major_offset, const cryptonote::network_type nettype)
crypto::public_key generate_multisig_M_N_spend_public_key(const std::vector< crypto::public_key > &pkeys)
generate_multisig_M_N_spend_public_key calculates multisig wallet's spend public key by summing all o...
bool is_valid_decomposed_amount(uint64_t amount)
bool generate_key_image_helper(const account_keys &ack, const std::unordered_map< crypto::public_key, subaddress_index > &subaddresses, const crypto::public_key &out_key, const crypto::public_key &tx_public_key, const std::vector< crypto::public_key > &additional_tx_public_keys, size_t real_output_index, keypair &in_ephemeral, crypto::key_image &ki, hw::device &hwdev, const uint32_t account_major_offset)
uint64_t get_outs_etn_amount(const transaction &tx)
bool get_encrypted_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash8 &payment_id)
uint32_t multisig_rounds_required(uint32_t participants, uint32_t threshold)
boost::variant< txin_gen, txin_to_script, txin_to_scripthash, txin_to_key, txin_to_key_public > txin_v
std::vector< crypto::public_key > get_additional_tx_pub_keys_from_extra(const std::vector< uint8_t > &tx_extra)
bool get_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash &payment_id)
void set_payment_id_to_tx_extra_nonce(blobdata &extra_nonce, const crypto::hash &payment_id)
void get_transaction_prefix_hash(const transaction_prefix &tx, crypto::hash &h)
unsigned int get_default_decimal_point()
crypto::secret_key get_multisig_blinded_secret_key(const crypto::secret_key &key)
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)
std::vector< uint64_t > relative_output_offsets_to_absolute(const std::vector< uint64_t > &off)
bool get_account_address_from_str(address_parse_info &info, network_type nettype, std::string const &str)
bool parse_and_validate_block_from_blob(const blobdata &b_blob, block &b, crypto::hash *block_hash)
bool generate_multisig_key_image(const account_keys &keys, size_t multisig_key_index, const crypto::public_key &out_key, crypto::key_image &ki)
crypto::secret_key encrypt_key(crypto::secret_key key, const epee::wipeable_string &passphrase)
const config_t & get_config(network_type nettype)
crypto::public_key get_tx_pub_key_from_extra(const std::vector< uint8_t > &tx_extra, size_t pk_index)
boost::optional< subaddress_receive_info > is_out_to_acc_precomp(const std::unordered_map< crypto::public_key, subaddress_index > &subaddresses, const crypto::public_key &out_key, const crypto::key_derivation &derivation, const std::vector< crypto::key_derivation > &additional_derivations, size_t output_index, hw::device &hwdev)
std::string get_account_address_as_str(network_type nettype, bool subaddress, account_public_address const &adr)
bool add_tx_pub_key_to_extra(transaction &tx, const crypto::public_key &tx_pub_key)
crypto::hash get_transaction_hash(const transaction &t)
bool find_tx_extra_field_by_type(const std::vector< tx_extra_field > &tx_extra_fields, T &field, size_t index=0)
bool is_coinbase(const transaction &tx)
crypto::secret_key decrypt_key(crypto::secret_key key, const epee::wipeable_string &passphrase)
bool construct_tx_with_tx_key(const account_keys &sender_account_keys, const std::unordered_map< crypto::public_key, subaddress_index > &subaddresses, std::vector< tx_source_entry > &sources, std::vector< tx_destination_entry > &destinations, const boost::optional< cryptonote::account_public_address > &change_addr, const std::vector< uint8_t > &extra, transaction &tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector< crypto::secret_key > &additional_tx_keys, bool rct, const rct::RCTConfig &rct_config, rct::multisig_out *msout, bool shuffle_outs, const uint32_t account_major_offset, const cryptonote::network_type nettype)
void set_default_decimal_point(unsigned int decimal_point)
blobdata tx_to_blob(const transaction &tx)
bool parse_tx_extra(const std::vector< uint8_t > &tx_extra, std::vector< tx_extra_field > &tx_extra_fields)
bool parse_amount(uint64_t &amount, const std::string &str_amount_)
bool parse_and_validate_tx_from_blob(const blobdata &tx_blob, transaction &tx)
void generate_multisig_LR(const crypto::public_key pkey, const crypto::secret_key &k, crypto::public_key &L, crypto::public_key &R)
bool get_inputs_etn_amount(const transaction &tx, uint64_t &etn)
std::vector< crypto::public_key > generate_multisig_derivations(const account_keys &keys, const std::vector< crypto::public_key > &derivations)
generate_multisig_derivations performs common DH key derivation. Each middle round in M/N scheme is D...
bool remove_field_from_tx_extra(std::vector< uint8_t > &tx_extra, const std::type_info &type)
void generate_multisig_N_N(const account_keys &keys, const std::vector< crypto::public_key > &spend_keys, std::vector< crypto::secret_key > &multisig_keys, rct::key &spend_skey, rct::key &spend_pkey)
std::string print_etn(uint64_t amount, unsigned int decimal_point)
crypto::hash get_pruned_transaction_hash(const transaction &t, const crypto::hash &pruned_data_hash)
std::vector< crypto::secret_key > calculate_multisig_keys(const std::vector< crypto::public_key > &derivations)
calculate_multisig_keys. Calculates secret multisig keys from others' participants ones as follows: m...
boost::optional< subaddress_receive_info > is_out_to_acc_precomp_public(const std::unordered_map< crypto::public_key, subaddress_index > &subaddresses, const cryptonote::account_public_address output_address)
bool generate_genesis_block(block &bl, std::string const &genesis_tx, uint32_t nonce)
std::string get_account_integrated_address_as_str(network_type nettype, account_public_address const &adr, crypto::hash8 const &payment_id)
bool generate_key_image_helper_precomp(const account_keys &ack, const crypto::public_key &out_key, const crypto::key_derivation &recv_derivation, size_t real_output_index, const subaddress_index &received_index, keypair &in_ephemeral, crypto::key_image &ki, hw::device &hwdev, const uint32_t account_major_offset)
bool parse_and_validate_tx_base_from_blob(const blobdata &tx_blob, transaction &tx)
bool t_serializable_object_to_blob(const t_object &to, blobdata &b_blob)
crypto::secret_key generate_multisig_view_secret_key(const crypto::secret_key &skey, const std::vector< crypto::secret_key > &skeys)
uint64_t get_transaction_weight(const transaction &tx, size_t blob_size)
bool add_extra_nonce_to_tx_extra(std::vector< uint8_t > &tx_extra, const blobdata &extra_nonce)
epee::misc_utils::struct_init< response_t > response
Level
Represents enumeration for severity level used to determine level of logging.
@ Warning
Useful when application has potentially harmful situtaions.
@ Debug
Informational events most useful for developers to debug application.
bool load_file_to_string(const std::string &path_to_file, std::string &target_str, size_t max_size=1000000000)
bool save_string_to_file(const std::string &path_to_file, const std::string &str)
boost::shared_ptr< call_befor_die_base > auto_scope_leave_caller
auto_scope_leave_caller create_scope_leave_handler(t_scope_leave_handler f)
bool parse_uri(const std::string uri, http::uri_content &content)
bool invoke_http_json(const boost::string_ref uri, const t_request &out_struct, t_response &result_struct, t_transport &transport, std::chrono::milliseconds timeout=std::chrono::seconds(15), const boost::string_ref method="GET")
bool invoke_http_json_rpc(const boost::string_ref uri, std::string method_name, const t_request &out_struct, t_response &result_struct, t_transport &transport, std::chrono::milliseconds timeout=std::chrono::seconds(15), const boost::string_ref http_method="GET", const std::string &req_id="0")
std::string to_string(t_connection_type type)
@ user_certificates
Verify peer via specific (non-chain) certificate(s) only.
@ none
Do not verify peer.
@ user_ca
Verify peer via specific (possibly chain) certificate(s) only.
bool ssl_support_from_string(ssl_support_t &ssl, boost::string_ref s)
bool invoke_http_bin(const boost::string_ref uri, const t_request &out_struct, t_response &result_struct, t_transport &transport, std::chrono::milliseconds timeout=std::chrono::seconds(15), const boost::string_ref method="GET")
bool load_t_from_binary(t_struct &out, const epee::span< const uint8_t > binary_buff)
bool store_t_to_binary(t_struct &str_in, std::string &binary_buff, size_t indent=0)
void decrypt(const void *ciphertext, size_t length, const uint8_t *key, const uint8_t *iv, char *plaintext, size_t *plaintext_len)
std::shared_ptr< messages::Electroneum::ElectroneumGetTxKeyRequest > get_tx_key(const hw::device_cold::tx_key_data_t &tx_data)
void register_all(std::map< std::string, std::unique_ptr< device >> ®istry)
device & get_device(const std::string &device_descriptor)
struct hw::wallet_shim wallet_shim
mdb_size_t count(MDB_cursor *cur)
error
Tracks LMDB error codes.
version
Supported socks variants.
void scalarmultBase(key &aG, const key &a)
void hash_to_scalar(key &hash, const void *data, const std::size_t l)
etn_amount decodeRctSimple(const rctSig &rv, const key &sk, unsigned int i, key &mask, hw::device &hwdev)
bool isInMainSubgroup(const key &A)
etn_amount decodeRct(const rctSig &rv, const key &sk, unsigned int i, key &mask, hw::device &hwdev)
key zeroCommit(etn_amount amount)
void scalarmultKey(key &aP, const key &P, const key &a)
etn_amount h2d(const key &test)
void addKeys(key &AB, const key &A, const key &B)
bool signMultisig(rctSig &rv, const std::vector< unsigned int > &indices, const keyV &k, const multisig_out &msout, const key &secret_key)
void ecdhDecode(ecdhTuple &masked, const key &sharedSec, bool v2)
void copy(key &AA, const key &A)
@ RangeProofPaddedBulletproof
key commit(etn_amount amount, const key &mask)
bool parse_binary(const std::string &blob, T &v)
bool dump_binary(T &v, std::string &blob)
const T & move(const T &t)
boost::filesystem::path data_dir
#define SSL_FINGERPRINT_SIZE
crypto::hash get_block_hash(uint64_t height)
bool get_output_distribution(uint64_t amount, uint64_t from, uint64_t to, uint64_t &start_height, std::vector< uint64_t > &distribution, uint64_t &base)
#define PERF_TIMER_START(name)
#define PERF_TIMER_STOP(name)
const GenericPointer< typename T::ValueType > T2 value
const CharType(& source)[N]
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
void serialize(Archive &a, unsigned_tx_set &x, const boost::serialization::version_type ver)
boost::endian::big_uint32_t ip
unsigned __int64 uint64_t
std::string prunable_as_hex
std::string prunable_hash
std::string pruned_as_hex
crypto::secret_key m_view_secret_key
crypto::secret_key m_spend_secret_key
std::vector< crypto::secret_key > m_multisig_keys
account_public_address m_account_address
crypto::public_key m_view_public_key
crypto::public_key m_spend_public_key
std::vector< blobdata > txs
std::vector< crypto::hash > tx_hashes
uint16_t const RPC_DEFAULT_PORT
account_public_address addr
std::vector< crypto::public_key > real_out_additional_tx_keys
crypto::public_key real_out_tx_key
rct::multisig_kLRki multisig_kLRki
subaddress_index subaddr_index
std::vector< output_entry > outputs
std::pair< uint64_t, rct::ctkey > output_entry
size_t real_output_in_tx_index
crypto::key_image k_image
std::vector< uint64_t > key_offsets
static std::vector< uint8_t > vector(boost::string_ref src)
static epee::wipeable_string wipeable_string(const span< const std::uint8_t > src)
std::string tx_prefix_hash
boost::function< crypto::public_key(const tools::wallet2::transfer_details &td)> get_tx_pub_key_from_received_outs
Primarily for use with epee::net_utils::http_client.
std::vector< ecdhTuple > ecdhInfo
crypto::public_key shared_secret
crypto::signature key_image_sig
crypto::signature shared_secret_sig
crypto::key_image key_image
#define CRITICAL_REGION_LOCAL(x)
struct hash_func hashes[]
bool get_short_payment_id(crypto::hash8 &payment_id8, const tools::wallet2::pending_tx &ptx, hw::device &hwdev)
#define TX_WEIGHT_TARGET(bytes)
#define UNSIGNED_TX_PREFIX
#define KEY_IMAGE_EXPORT_FILE_MAGIC
#define TESTNET_SEGREGATION_FORK_HEIGHT
#define MULTISIG_EXPORT_FILE_MAGIC
#define ELECTRONEUM_DEFAULT_LOG_CATEGORY
#define FEE_ESTIMATE_GRACE_BLOCKS
#define STAGENET_SEGREGATION_FORK_HEIGHT
#define SEGREGATION_FORK_VICINITY
#define FIRST_REFRESH_GRANULARITY
#define DEFAULT_MIN_OUTPUT_VALUE
#define SEGREGATION_FORK_HEIGHT
#define RECENT_OUTPUT_BLOCKS
#define MULTISIG_UNSIGNED_TX_PREFIX
#define SUBADDRESS_LOOKAHEAD_MAJOR
#define APPROXIMATE_INPUT_BYTES
#define SECOND_OUTPUT_RELATEDNESS_THRESHOLD
#define SUBADDRESS_LOOKAHEAD_MINOR
#define DEFAULT_MIN_OUTPUT_COUNT
#define RECENT_OUTPUT_RATIO
#define OUTPUT_EXPORT_FILE_MAGIC
#define RECENT_OUTPUT_ZONE
#define THROW_WALLET_EXCEPTION_IF(cond, err_type,...)
#define THROW_WALLET_EXCEPTION(err_type,...)