mirror of
https://git.wownero.com/wownero/wownero.git
synced 2024-08-15 01:03:23 +00:00
Merge pull request #4934
5dc590cb
wallet2: speedup output tracking (moneromooo-monero)db3f2a91
wallet: optionally keep track of owned outputs uses (moneromooo-monero)
This commit is contained in:
commit
31b6d128f2
4 changed files with 156 additions and 30 deletions
|
@ -146,7 +146,7 @@ namespace
|
||||||
const char* USAGE_START_MINING("start_mining [<number_of_threads>] [bg_mining] [ignore_battery]");
|
const char* USAGE_START_MINING("start_mining [<number_of_threads>] [bg_mining] [ignore_battery]");
|
||||||
const char* USAGE_SET_DAEMON("set_daemon <host>[:<port>] [trusted|untrusted]");
|
const char* USAGE_SET_DAEMON("set_daemon <host>[:<port>] [trusted|untrusted]");
|
||||||
const char* USAGE_SHOW_BALANCE("balance [detail]");
|
const char* USAGE_SHOW_BALANCE("balance [detail]");
|
||||||
const char* USAGE_INCOMING_TRANSFERS("incoming_transfers [available|unavailable] [verbose] [index=<N1>[,<N2>[,...]]]");
|
const char* USAGE_INCOMING_TRANSFERS("incoming_transfers [available|unavailable] [verbose] [uses] [index=<N1>[,<N2>[,...]]]");
|
||||||
const char* USAGE_PAYMENTS("payments <PID_1> [<PID_2> ... <PID_N>]");
|
const char* USAGE_PAYMENTS("payments <PID_1> [<PID_2> ... <PID_N>]");
|
||||||
const char* USAGE_PAYMENT_ID("payment_id");
|
const char* USAGE_PAYMENT_ID("payment_id");
|
||||||
const char* USAGE_TRANSFER("transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <address> <amount>) [<payment_id>]");
|
const char* USAGE_TRANSFER("transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <address> <amount>) [<payment_id>]");
|
||||||
|
@ -2488,6 +2488,19 @@ bool simple_wallet::set_ignore_fractional_outputs(const std::vector<std::string>
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool simple_wallet::set_track_uses(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
|
||||||
|
{
|
||||||
|
const auto pwd_container = get_and_verify_password();
|
||||||
|
if (pwd_container)
|
||||||
|
{
|
||||||
|
parse_bool_and_use(args[1], [&](bool r) {
|
||||||
|
m_wallet->track_uses(r);
|
||||||
|
m_wallet->rewrite(m_wallet_file, pwd_container->password());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool simple_wallet::set_device_name(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
|
bool simple_wallet::set_device_name(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
|
||||||
{
|
{
|
||||||
const auto pwd_container = get_and_verify_password();
|
const auto pwd_container = get_and_verify_password();
|
||||||
|
@ -3032,6 +3045,7 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
|
||||||
success_msg_writer() << "subaddress-lookahead = " << lookahead.first << ":" << lookahead.second;
|
success_msg_writer() << "subaddress-lookahead = " << lookahead.first << ":" << lookahead.second;
|
||||||
success_msg_writer() << "segregation-height = " << m_wallet->segregation_height();
|
success_msg_writer() << "segregation-height = " << m_wallet->segregation_height();
|
||||||
success_msg_writer() << "ignore-fractional-outputs = " << m_wallet->ignore_fractional_outputs();
|
success_msg_writer() << "ignore-fractional-outputs = " << m_wallet->ignore_fractional_outputs();
|
||||||
|
success_msg_writer() << "track-uses = " << m_wallet->track_uses();
|
||||||
success_msg_writer() << "device_name = " << m_wallet->device_name();
|
success_msg_writer() << "device_name = " << m_wallet->device_name();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -3088,6 +3102,7 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
|
||||||
CHECK_SIMPLE_VARIABLE("subaddress-lookahead", set_subaddress_lookahead, tr("<major>:<minor>"));
|
CHECK_SIMPLE_VARIABLE("subaddress-lookahead", set_subaddress_lookahead, tr("<major>:<minor>"));
|
||||||
CHECK_SIMPLE_VARIABLE("segregation-height", set_segregation_height, tr("unsigned integer"));
|
CHECK_SIMPLE_VARIABLE("segregation-height", set_segregation_height, tr("unsigned integer"));
|
||||||
CHECK_SIMPLE_VARIABLE("ignore-fractional-outputs", set_ignore_fractional_outputs, tr("0 or 1"));
|
CHECK_SIMPLE_VARIABLE("ignore-fractional-outputs", set_ignore_fractional_outputs, tr("0 or 1"));
|
||||||
|
CHECK_SIMPLE_VARIABLE("track-uses", set_track_uses, tr("0 or 1"));
|
||||||
CHECK_SIMPLE_VARIABLE("device-name", set_device_name, tr("<device_name[:device_spec]>"));
|
CHECK_SIMPLE_VARIABLE("device-name", set_device_name, tr("<device_name[:device_spec]>"));
|
||||||
}
|
}
|
||||||
fail_msg_writer() << tr("set: unrecognized argument(s)");
|
fail_msg_writer() << tr("set: unrecognized argument(s)");
|
||||||
|
@ -4813,6 +4828,7 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args
|
||||||
bool filter = false;
|
bool filter = false;
|
||||||
bool available = false;
|
bool available = false;
|
||||||
bool verbose = false;
|
bool verbose = false;
|
||||||
|
bool uses = false;
|
||||||
if (local_args.size() > 0)
|
if (local_args.size() > 0)
|
||||||
{
|
{
|
||||||
if (local_args[0] == "available")
|
if (local_args[0] == "available")
|
||||||
|
@ -4828,12 +4844,22 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args
|
||||||
local_args.erase(local_args.begin());
|
local_args.erase(local_args.begin());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (local_args.size() > 0 && local_args[0] == "verbose")
|
while (local_args.size() > 0)
|
||||||
{
|
{
|
||||||
|
if (local_args[0] == "verbose")
|
||||||
verbose = true;
|
verbose = true;
|
||||||
|
else if (local_args[0] == "uses")
|
||||||
|
uses = true;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fail_msg_writer() << tr("Invalid keyword: ") << local_args.front();
|
||||||
|
break;
|
||||||
|
}
|
||||||
local_args.erase(local_args.begin());
|
local_args.erase(local_args.begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uint64_t blockchain_height = m_wallet->get_blockchain_current_height();
|
||||||
|
|
||||||
PAUSE_READLINE();
|
PAUSE_READLINE();
|
||||||
|
|
||||||
std::set<uint32_t> subaddr_indices;
|
std::set<uint32_t> subaddr_indices;
|
||||||
|
@ -4867,9 +4893,16 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args
|
||||||
verbose_string = (boost::format("%68s%68s") % tr("pubkey") % tr("key image")).str();
|
verbose_string = (boost::format("%68s%68s") % tr("pubkey") % tr("key image")).str();
|
||||||
message_writer() << boost::format("%21s%8s%12s%8s%16s%68s%16s%s") % tr("amount") % tr("spent") % tr("unlocked") % tr("ringct") % tr("global index") % tr("tx id") % tr("addr index") % verbose_string;
|
message_writer() << boost::format("%21s%8s%12s%8s%16s%68s%16s%s") % tr("amount") % tr("spent") % tr("unlocked") % tr("ringct") % tr("global index") % tr("tx id") % tr("addr index") % verbose_string;
|
||||||
}
|
}
|
||||||
std::string verbose_string;
|
std::string extra_string;
|
||||||
if (verbose)
|
if (verbose)
|
||||||
verbose_string = (boost::format("%68s%68s") % td.get_public_key() % (td.m_key_image_known ? epee::string_tools::pod_to_hex(td.m_key_image) : td.m_key_image_partial ? (epee::string_tools::pod_to_hex(td.m_key_image) + "/p") : std::string(64, '?'))).str();
|
extra_string += (boost::format("%68s%68s") % td.get_public_key() % (td.m_key_image_known ? epee::string_tools::pod_to_hex(td.m_key_image) : td.m_key_image_partial ? (epee::string_tools::pod_to_hex(td.m_key_image) + "/p") : std::string(64, '?'))).str();
|
||||||
|
if (uses)
|
||||||
|
{
|
||||||
|
std::vector<uint64_t> heights;
|
||||||
|
for (const auto &e: td.m_uses) heights.push_back(e.first);
|
||||||
|
const std::pair<std::string, std::string> line = show_outputs_line(heights, blockchain_height, td.m_spent_height);
|
||||||
|
extra_string += tr("Heights: ") + line.first + "\n" + line.second;
|
||||||
|
}
|
||||||
message_writer(td.m_spent ? console_color_magenta : console_color_green, false) <<
|
message_writer(td.m_spent ? console_color_magenta : console_color_green, false) <<
|
||||||
boost::format("%21s%8s%12s%8s%16u%68s%16u%s") %
|
boost::format("%21s%8s%12s%8s%16u%68s%16u%s") %
|
||||||
print_money(td.amount()) %
|
print_money(td.amount()) %
|
||||||
|
@ -4879,7 +4912,7 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args
|
||||||
td.m_global_output_index %
|
td.m_global_output_index %
|
||||||
td.m_txid %
|
td.m_txid %
|
||||||
td.m_subaddr_index.minor %
|
td.m_subaddr_index.minor %
|
||||||
verbose_string;
|
extra_string;
|
||||||
++transfers_found;
|
++transfers_found;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5031,6 +5064,33 @@ bool simple_wallet::rescan_spent(const std::vector<std::string> &args)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
std::pair<std::string, std::string> simple_wallet::show_outputs_line(const std::vector<uint64_t> &heights, uint64_t blockchain_height, uint64_t highlight_height) const
|
||||||
|
{
|
||||||
|
std::stringstream ostr;
|
||||||
|
|
||||||
|
for (uint64_t h: heights)
|
||||||
|
blockchain_height = std::max(blockchain_height, h);
|
||||||
|
|
||||||
|
for (size_t j = 0; j < heights.size(); ++j)
|
||||||
|
ostr << (heights[j] == highlight_height ? " *" : " ") << heights[j];
|
||||||
|
|
||||||
|
// visualize the distribution, using the code by moneroexamples onion-monero-viewer
|
||||||
|
const uint64_t resolution = 79;
|
||||||
|
std::string ring_str(resolution, '_');
|
||||||
|
for (size_t j = 0; j < heights.size(); ++j)
|
||||||
|
{
|
||||||
|
uint64_t pos = (heights[j] * resolution) / blockchain_height;
|
||||||
|
ring_str[pos] = 'o';
|
||||||
|
}
|
||||||
|
if (highlight_height < blockchain_height)
|
||||||
|
{
|
||||||
|
uint64_t pos = (highlight_height * resolution) / blockchain_height;
|
||||||
|
ring_str[pos] = '*';
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_pair(ostr.str(), ring_str);
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------------------
|
||||||
bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr)
|
bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr)
|
||||||
{
|
{
|
||||||
uint32_t version;
|
uint32_t version;
|
||||||
|
@ -5101,21 +5161,18 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ostr << tr("\nOriginating block heights: ");
|
ostr << tr("\nOriginating block heights: ");
|
||||||
for (size_t j = 0; j < absolute_offsets.size(); ++j)
|
|
||||||
ostr << tr(j == source.real_output ? " *" : " ") << res.outs[j].height;
|
|
||||||
spent_key_height[i] = res.outs[source.real_output].height;
|
spent_key_height[i] = res.outs[source.real_output].height;
|
||||||
spent_key_txid [i] = res.outs[source.real_output].txid;
|
spent_key_txid [i] = res.outs[source.real_output].txid;
|
||||||
// visualize the distribution, using the code by moneroexamples onion-monero-viewer
|
std::vector<uint64_t> heights(absolute_offsets.size(), 0);
|
||||||
const uint64_t resolution = 79;
|
uint64_t highlight_height = std::numeric_limits<uint64_t>::max();
|
||||||
std::string ring_str(resolution, '_');
|
|
||||||
for (size_t j = 0; j < absolute_offsets.size(); ++j)
|
for (size_t j = 0; j < absolute_offsets.size(); ++j)
|
||||||
{
|
{
|
||||||
uint64_t pos = (res.outs[j].height * resolution) / blockchain_height;
|
heights[j] = res.outs[j].height;
|
||||||
ring_str[pos] = 'o';
|
if (j == source.real_output)
|
||||||
|
highlight_height = heights[j];
|
||||||
}
|
}
|
||||||
uint64_t pos = (res.outs[source.real_output].height * resolution) / blockchain_height;
|
std::pair<std::string, std::string> ring_str = show_outputs_line(heights, highlight_height);
|
||||||
ring_str[pos] = '*';
|
ostr << ring_str.first << tr("\n|") << ring_str.second << tr("|\n");
|
||||||
ostr << tr("\n|") << ring_str << tr("|\n");
|
|
||||||
}
|
}
|
||||||
// warn if rings contain keys originating from the same tx or temporally very close block heights
|
// warn if rings contain keys originating from the same tx or temporally very close block heights
|
||||||
bool are_keys_from_same_tx = false;
|
bool are_keys_from_same_tx = false;
|
||||||
|
|
|
@ -142,6 +142,7 @@ namespace cryptonote
|
||||||
bool set_subaddress_lookahead(const std::vector<std::string> &args = std::vector<std::string>());
|
bool set_subaddress_lookahead(const std::vector<std::string> &args = std::vector<std::string>());
|
||||||
bool set_segregation_height(const std::vector<std::string> &args = std::vector<std::string>());
|
bool set_segregation_height(const std::vector<std::string> &args = std::vector<std::string>());
|
||||||
bool set_ignore_fractional_outputs(const std::vector<std::string> &args = std::vector<std::string>());
|
bool set_ignore_fractional_outputs(const std::vector<std::string> &args = std::vector<std::string>());
|
||||||
|
bool set_track_uses(const std::vector<std::string> &args = std::vector<std::string>());
|
||||||
bool set_device_name(const std::vector<std::string> &args = std::vector<std::string>());
|
bool set_device_name(const std::vector<std::string> &args = std::vector<std::string>());
|
||||||
bool help(const std::vector<std::string> &args = std::vector<std::string>());
|
bool help(const std::vector<std::string> &args = std::vector<std::string>());
|
||||||
bool start_mining(const std::vector<std::string> &args);
|
bool start_mining(const std::vector<std::string> &args);
|
||||||
|
@ -251,6 +252,7 @@ namespace cryptonote
|
||||||
bool print_seed(bool encrypted);
|
bool print_seed(bool encrypted);
|
||||||
void key_images_sync_intern();
|
void key_images_sync_intern();
|
||||||
void on_refresh_finished(uint64_t start_height, uint64_t fetched_blocks, bool is_init, bool received_money);
|
void on_refresh_finished(uint64_t start_height, uint64_t fetched_blocks, bool is_init, bool received_money);
|
||||||
|
std::pair<std::string, std::string> show_outputs_line(const std::vector<uint64_t> &heights, uint64_t blockchain_height, uint64_t highlight_height = std::numeric_limits<uint64_t>::max()) const;
|
||||||
|
|
||||||
struct transfer_view
|
struct transfer_view
|
||||||
{
|
{
|
||||||
|
|
|
@ -894,6 +894,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
|
||||||
m_key_reuse_mitigation2(true),
|
m_key_reuse_mitigation2(true),
|
||||||
m_segregation_height(0),
|
m_segregation_height(0),
|
||||||
m_ignore_fractional_outputs(true),
|
m_ignore_fractional_outputs(true),
|
||||||
|
m_track_uses(false),
|
||||||
m_is_initialized(false),
|
m_is_initialized(false),
|
||||||
m_kdf_rounds(kdf_rounds),
|
m_kdf_rounds(kdf_rounds),
|
||||||
is_old_file_format(false),
|
is_old_file_format(false),
|
||||||
|
@ -1446,8 +1447,9 @@ void wallet2::cache_tx_data(const cryptonote::transaction& tx, const crypto::has
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
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, const tx_cache_data &tx_cache_data)
|
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, const tx_cache_data &tx_cache_data, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache)
|
||||||
{
|
{
|
||||||
|
PERF_TIMER(process_new_transaction);
|
||||||
// In this function, tx (probably) only contains the base information
|
// In this function, tx (probably) only contains the base information
|
||||||
// (that is, the prunable stuff may or may not be included)
|
// (that is, the prunable stuff may or may not be included)
|
||||||
if (!miner_tx && !pool)
|
if (!miner_tx && !pool)
|
||||||
|
@ -1684,6 +1686,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
|
||||||
if (!m_multisig && !m_watch_only)
|
if (!m_multisig && !m_watch_only)
|
||||||
m_key_images[td.m_key_image] = m_transfers.size()-1;
|
m_key_images[td.m_key_image] = m_transfers.size()-1;
|
||||||
m_pub_keys[tx_scan_info[o].in_ephemeral.pub] = m_transfers.size()-1;
|
m_pub_keys[tx_scan_info[o].in_ephemeral.pub] = m_transfers.size()-1;
|
||||||
|
if (output_tracker_cache)
|
||||||
|
(*output_tracker_cache)[std::make_pair(tx.vout[o].amount, td.m_global_output_index)] = m_transfers.size() - 1;
|
||||||
if (m_multisig)
|
if (m_multisig)
|
||||||
{
|
{
|
||||||
THROW_WALLET_EXCEPTION_IF(!m_multisig_rescan_k && m_multisig_rescan_info,
|
THROW_WALLET_EXCEPTION_IF(!m_multisig_rescan_k && m_multisig_rescan_info,
|
||||||
|
@ -1749,6 +1753,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
|
||||||
td.m_mask = rct::identity();
|
td.m_mask = rct::identity();
|
||||||
td.m_rct = false;
|
td.m_rct = false;
|
||||||
}
|
}
|
||||||
|
if (output_tracker_cache)
|
||||||
|
(*output_tracker_cache)[std::make_pair(tx.vout[o].amount, td.m_global_output_index)] = kit->second;
|
||||||
if (m_multisig)
|
if (m_multisig)
|
||||||
{
|
{
|
||||||
THROW_WALLET_EXCEPTION_IF(!m_multisig_rescan_k && m_multisig_rescan_info,
|
THROW_WALLET_EXCEPTION_IF(!m_multisig_rescan_k && m_multisig_rescan_info,
|
||||||
|
@ -1780,11 +1786,12 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
|
||||||
{
|
{
|
||||||
if(in.type() != typeid(cryptonote::txin_to_key))
|
if(in.type() != typeid(cryptonote::txin_to_key))
|
||||||
continue;
|
continue;
|
||||||
auto it = m_key_images.find(boost::get<cryptonote::txin_to_key>(in).k_image);
|
const cryptonote::txin_to_key &in_to_key = boost::get<cryptonote::txin_to_key>(in);
|
||||||
|
auto it = m_key_images.find(in_to_key.k_image);
|
||||||
if(it != m_key_images.end())
|
if(it != m_key_images.end())
|
||||||
{
|
{
|
||||||
transfer_details& td = m_transfers[it->second];
|
transfer_details& td = m_transfers[it->second];
|
||||||
uint64_t amount = boost::get<cryptonote::txin_to_key>(in).amount;
|
uint64_t amount = in_to_key.amount;
|
||||||
if (amount > 0)
|
if (amount > 0)
|
||||||
{
|
{
|
||||||
if(amount != td.amount())
|
if(amount != td.amount())
|
||||||
|
@ -1815,6 +1822,34 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
|
||||||
m_callback->on_money_spent(height, txid, tx, amount, tx, td.m_subaddr_index);
|
m_callback->on_money_spent(height, txid, tx, amount, tx, td.m_subaddr_index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!pool && m_track_uses)
|
||||||
|
{
|
||||||
|
PERF_TIMER(track_uses);
|
||||||
|
const uint64_t amount = in_to_key.amount;
|
||||||
|
std::vector<uint64_t> offsets = cryptonote::relative_output_offsets_to_absolute(in_to_key.key_offsets);
|
||||||
|
if (output_tracker_cache)
|
||||||
|
{
|
||||||
|
for (uint64_t offset: offsets)
|
||||||
|
{
|
||||||
|
const std::map<std::pair<uint64_t, uint64_t>, size_t>::const_iterator i = output_tracker_cache->find(std::make_pair(amount, offset));
|
||||||
|
if (i != output_tracker_cache->end())
|
||||||
|
{
|
||||||
|
size_t idx = i->second;
|
||||||
|
THROW_WALLET_EXCEPTION_IF(idx >= m_transfers.size(), error::wallet_internal_error, "Output tracker cache index out of range");
|
||||||
|
m_transfers[idx].m_uses.push_back(std::make_pair(height, txid));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else for (transfer_details &td: m_transfers)
|
||||||
|
{
|
||||||
|
if (amount != in_to_key.amount)
|
||||||
|
continue;
|
||||||
|
for (uint64_t offset: offsets)
|
||||||
|
if (offset == td.m_global_output_index)
|
||||||
|
td.m_uses.push_back(std::make_pair(height, txid));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t fee = miner_tx ? 0 : tx.version == 1 ? tx_money_spent_in_ins - get_outs_money_amount(tx) : tx.rct_signatures.txnFee;
|
uint64_t fee = miner_tx ? 0 : tx.version == 1 ? tx_money_spent_in_ins - get_outs_money_amount(tx) : tx.rct_signatures.txnFee;
|
||||||
|
@ -1997,7 +2032,7 @@ void wallet2::process_outgoing(const crypto::hash &txid, const cryptonote::trans
|
||||||
add_rings(tx);
|
add_rings(tx);
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
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)
|
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::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache)
|
||||||
{
|
{
|
||||||
THROW_WALLET_EXCEPTION_IF(bche.txs.size() + 1 != parsed_block.o_indices.indices.size(), error::wallet_internal_error,
|
THROW_WALLET_EXCEPTION_IF(bche.txs.size() + 1 != parsed_block.o_indices.indices.size(), error::wallet_internal_error,
|
||||||
"block transactions=" + std::to_string(bche.txs.size()) +
|
"block transactions=" + std::to_string(bche.txs.size()) +
|
||||||
|
@ -2010,7 +2045,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry
|
||||||
{
|
{
|
||||||
TIME_MEASURE_START(miner_tx_handle_time);
|
TIME_MEASURE_START(miner_tx_handle_time);
|
||||||
if (m_refresh_type != RefreshNoCoinbase)
|
if (m_refresh_type != RefreshNoCoinbase)
|
||||||
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, tx_cache_data[tx_cache_data_offset]);
|
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, tx_cache_data[tx_cache_data_offset], output_tracker_cache);
|
||||||
++tx_cache_data_offset;
|
++tx_cache_data_offset;
|
||||||
TIME_MEASURE_FINISH(miner_tx_handle_time);
|
TIME_MEASURE_FINISH(miner_tx_handle_time);
|
||||||
|
|
||||||
|
@ -2019,7 +2054,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry
|
||||||
THROW_WALLET_EXCEPTION_IF(bche.txs.size() != parsed_block.txes.size(), error::wallet_internal_error, "Wrong amount of transactions for block");
|
THROW_WALLET_EXCEPTION_IF(bche.txs.size() != parsed_block.txes.size(), error::wallet_internal_error, "Wrong amount of transactions for block");
|
||||||
for (size_t idx = 0; idx < b.tx_hashes.size(); ++idx)
|
for (size_t idx = 0; idx < b.tx_hashes.size(); ++idx)
|
||||||
{
|
{
|
||||||
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, tx_cache_data[tx_cache_data_offset++]);
|
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, tx_cache_data[tx_cache_data_offset++], output_tracker_cache);
|
||||||
}
|
}
|
||||||
TIME_MEASURE_FINISH(txs_handle_time);
|
TIME_MEASURE_FINISH(txs_handle_time);
|
||||||
m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
|
m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
|
||||||
|
@ -2117,7 +2152,7 @@ void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height,
|
||||||
hashes = std::move(res.m_block_ids);
|
hashes = std::move(res.m_block_ids);
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
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)
|
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::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache)
|
||||||
{
|
{
|
||||||
size_t current_index = start_height;
|
size_t current_index = start_height;
|
||||||
blocks_added = 0;
|
blocks_added = 0;
|
||||||
|
@ -2224,7 +2259,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
|
||||||
|
|
||||||
if(current_index >= m_blockchain.size())
|
if(current_index >= m_blockchain.size())
|
||||||
{
|
{
|
||||||
process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset);
|
process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset, output_tracker_cache);
|
||||||
++blocks_added;
|
++blocks_added;
|
||||||
}
|
}
|
||||||
else if(bl_id != m_blockchain[current_index])
|
else if(bl_id != m_blockchain[current_index])
|
||||||
|
@ -2236,7 +2271,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
|
||||||
string_tools::pod_to_hex(m_blockchain[current_index]));
|
string_tools::pod_to_hex(m_blockchain[current_index]));
|
||||||
|
|
||||||
detach_blockchain(current_index);
|
detach_blockchain(current_index);
|
||||||
process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset);
|
process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset, output_tracker_cache);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -2668,6 +2703,17 @@ bool wallet2::delete_address_book_row(std::size_t row_id) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
std::shared_ptr<std::map<std::pair<uint64_t, uint64_t>, size_t>> wallet2::create_output_tracker_cache() const
|
||||||
|
{
|
||||||
|
std::shared_ptr<std::map<std::pair<uint64_t, uint64_t>, size_t>> cache{new std::map<std::pair<uint64_t, uint64_t>, size_t>()};
|
||||||
|
for (size_t i = 0; i < m_transfers.size(); ++i)
|
||||||
|
{
|
||||||
|
const transfer_details &td = m_transfers[i];
|
||||||
|
(*cache)[std::make_pair(td.is_rct() ? 0 : td.amount(), td.m_global_output_index)] = i;
|
||||||
|
}
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched, bool& received_money)
|
void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched, bool& received_money)
|
||||||
{
|
{
|
||||||
|
@ -2715,6 +2761,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
|
||||||
std::vector<cryptonote::block_complete_entry> blocks;
|
std::vector<cryptonote::block_complete_entry> blocks;
|
||||||
std::vector<parsed_block> parsed_blocks;
|
std::vector<parsed_block> parsed_blocks;
|
||||||
bool refreshed = false;
|
bool refreshed = false;
|
||||||
|
std::shared_ptr<std::map<std::pair<uint64_t, uint64_t>, size_t>> output_tracker_cache;
|
||||||
|
|
||||||
// pull the first set of blocks
|
// pull the first set of blocks
|
||||||
get_short_chain_history(short_chain_history, (m_first_refresh_done || trusted_daemon) ? 1 : FIRST_REFRESH_GRANULARITY);
|
get_short_chain_history(short_chain_history, (m_first_refresh_done || trusted_daemon) ? 1 : FIRST_REFRESH_GRANULARITY);
|
||||||
|
@ -2768,7 +2815,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
process_parsed_blocks(blocks_start_height, blocks, parsed_blocks, added_blocks);
|
process_parsed_blocks(blocks_start_height, blocks, parsed_blocks, added_blocks, output_tracker_cache.get());
|
||||||
}
|
}
|
||||||
catch (const tools::error::out_of_hashchain_bounds_error&)
|
catch (const tools::error::out_of_hashchain_bounds_error&)
|
||||||
{
|
{
|
||||||
|
@ -2811,6 +2858,11 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
|
||||||
throw std::runtime_error("proxy exception in refresh thread");
|
throw std::runtime_error("proxy exception in refresh thread");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if we've got at least 10 blocks to refresh, assume we're starting
|
||||||
|
// a long refresh, and setup a tracking output cache if we need to
|
||||||
|
if (m_track_uses && !output_tracker_cache && next_blocks.size() >= 10)
|
||||||
|
output_tracker_cache = create_output_tracker_cache();
|
||||||
|
|
||||||
// switch to the new blocks from the daemon
|
// switch to the new blocks from the daemon
|
||||||
blocks_start_height = next_blocks_start_height;
|
blocks_start_height = next_blocks_start_height;
|
||||||
blocks = std::move(next_blocks);
|
blocks = std::move(next_blocks);
|
||||||
|
@ -3183,6 +3235,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
|
||||||
value2.SetInt(m_ignore_fractional_outputs ? 1 : 0);
|
value2.SetInt(m_ignore_fractional_outputs ? 1 : 0);
|
||||||
json.AddMember("ignore_fractional_outputs", value2, json.GetAllocator());
|
json.AddMember("ignore_fractional_outputs", value2, json.GetAllocator());
|
||||||
|
|
||||||
|
value2.SetInt(m_track_uses ? 1 : 0);
|
||||||
|
json.AddMember("track_uses", value2, json.GetAllocator());
|
||||||
|
|
||||||
value2.SetUint(m_subaddress_lookahead_major);
|
value2.SetUint(m_subaddress_lookahead_major);
|
||||||
json.AddMember("subaddress_lookahead_major", value2, json.GetAllocator());
|
json.AddMember("subaddress_lookahead_major", value2, json.GetAllocator());
|
||||||
|
|
||||||
|
@ -3329,6 +3384,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
|
||||||
m_key_reuse_mitigation2 = true;
|
m_key_reuse_mitigation2 = true;
|
||||||
m_segregation_height = 0;
|
m_segregation_height = 0;
|
||||||
m_ignore_fractional_outputs = true;
|
m_ignore_fractional_outputs = true;
|
||||||
|
m_track_uses = false;
|
||||||
m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR;
|
m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR;
|
||||||
m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR;
|
m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR;
|
||||||
m_original_keys_available = false;
|
m_original_keys_available = false;
|
||||||
|
@ -3481,6 +3537,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
|
||||||
m_segregation_height = field_segregation_height;
|
m_segregation_height = field_segregation_height;
|
||||||
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ignore_fractional_outputs, int, Int, false, true);
|
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ignore_fractional_outputs, int, Int, false, true);
|
||||||
m_ignore_fractional_outputs = field_ignore_fractional_outputs;
|
m_ignore_fractional_outputs = field_ignore_fractional_outputs;
|
||||||
|
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, track_uses, int, Int, false, false);
|
||||||
|
m_track_uses = field_track_uses;
|
||||||
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_major, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MAJOR);
|
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_major, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MAJOR);
|
||||||
m_subaddress_lookahead_major = field_subaddress_lookahead_major;
|
m_subaddress_lookahead_major = field_subaddress_lookahead_major;
|
||||||
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_minor, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MINOR);
|
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_minor, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MINOR);
|
||||||
|
|
|
@ -273,6 +273,7 @@ namespace tools
|
||||||
bool m_key_image_partial;
|
bool m_key_image_partial;
|
||||||
std::vector<rct::key> m_multisig_k;
|
std::vector<rct::key> m_multisig_k;
|
||||||
std::vector<multisig_info> m_multisig_info; // one per other participant
|
std::vector<multisig_info> m_multisig_info; // one per other participant
|
||||||
|
std::vector<std::pair<uint64_t, crypto::hash>> m_uses;
|
||||||
|
|
||||||
bool is_rct() const { return m_rct; }
|
bool is_rct() const { return m_rct; }
|
||||||
uint64_t amount() const { return m_amount; }
|
uint64_t amount() const { return m_amount; }
|
||||||
|
@ -297,6 +298,7 @@ namespace tools
|
||||||
FIELD(m_key_image_partial)
|
FIELD(m_key_image_partial)
|
||||||
FIELD(m_multisig_k)
|
FIELD(m_multisig_k)
|
||||||
FIELD(m_multisig_info)
|
FIELD(m_multisig_info)
|
||||||
|
FIELD(m_uses)
|
||||||
END_SERIALIZE()
|
END_SERIALIZE()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -984,6 +986,8 @@ namespace tools
|
||||||
void ignore_fractional_outputs(bool value) { m_ignore_fractional_outputs = value; }
|
void ignore_fractional_outputs(bool value) { m_ignore_fractional_outputs = value; }
|
||||||
bool confirm_non_default_ring_size() const { return m_confirm_non_default_ring_size; }
|
bool confirm_non_default_ring_size() const { return m_confirm_non_default_ring_size; }
|
||||||
void confirm_non_default_ring_size(bool always) { m_confirm_non_default_ring_size = always; }
|
void confirm_non_default_ring_size(bool always) { m_confirm_non_default_ring_size = always; }
|
||||||
|
bool track_uses() const { return m_track_uses; }
|
||||||
|
void track_uses(bool value) { m_track_uses = value; }
|
||||||
const std::string & device_name() const { return m_device_name; }
|
const std::string & device_name() const { return m_device_name; }
|
||||||
void device_name(const std::string & device_name) { m_device_name = device_name; }
|
void device_name(const std::string & device_name) { m_device_name = device_name; }
|
||||||
const std::string & device_derivation_path() const { return m_device_derivation_path; }
|
const std::string & device_derivation_path() const { return m_device_derivation_path; }
|
||||||
|
@ -1249,8 +1253,8 @@ namespace tools
|
||||||
* \param password Password of wallet file
|
* \param password Password of wallet file
|
||||||
*/
|
*/
|
||||||
bool load_keys(const std::string& keys_file_name, const epee::wipeable_string& password);
|
bool load_keys(const std::string& keys_file_name, const epee::wipeable_string& password);
|
||||||
void 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, const tx_cache_data &tx_cache_data);
|
void 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, const tx_cache_data &tx_cache_data, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL);
|
||||||
void 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);
|
void 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::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL);
|
||||||
void detach_blockchain(uint64_t height);
|
void detach_blockchain(uint64_t height);
|
||||||
void get_short_chain_history(std::list<crypto::hash>& ids, uint64_t granularity = 1) const;
|
void get_short_chain_history(std::list<crypto::hash>& ids, uint64_t granularity = 1) const;
|
||||||
bool clear();
|
bool clear();
|
||||||
|
@ -1258,7 +1262,7 @@ namespace tools
|
||||||
void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<crypto::hash> &hashes);
|
void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<crypto::hash> &hashes);
|
||||||
void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, bool force = false);
|
void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, bool force = false);
|
||||||
void 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);
|
void 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);
|
||||||
void 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);
|
void 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::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL);
|
||||||
uint64_t select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::vector<size_t>& selected_transfers) const;
|
uint64_t select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::vector<size_t>& selected_transfers) const;
|
||||||
bool prepare_file_names(const std::string& file_path);
|
bool prepare_file_names(const std::string& file_path);
|
||||||
void process_unconfirmed(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height);
|
void process_unconfirmed(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height);
|
||||||
|
@ -1312,6 +1316,7 @@ namespace tools
|
||||||
std::unordered_set<crypto::public_key> &pkeys) const;
|
std::unordered_set<crypto::public_key> &pkeys) const;
|
||||||
|
|
||||||
void cache_tx_data(const cryptonote::transaction& tx, const crypto::hash &txid, tx_cache_data &tx_cache_data) const;
|
void cache_tx_data(const cryptonote::transaction& tx, const crypto::hash &txid, tx_cache_data &tx_cache_data) const;
|
||||||
|
std::shared_ptr<std::map<std::pair<uint64_t, uint64_t>, size_t>> create_output_tracker_cache() const;
|
||||||
|
|
||||||
void setup_new_blockchain();
|
void setup_new_blockchain();
|
||||||
void create_keys_file(const std::string &wallet_, bool watch_only, const epee::wipeable_string &password, bool create_address_file);
|
void create_keys_file(const std::string &wallet_, bool watch_only, const epee::wipeable_string &password, bool create_address_file);
|
||||||
|
@ -1395,6 +1400,7 @@ namespace tools
|
||||||
bool m_key_reuse_mitigation2;
|
bool m_key_reuse_mitigation2;
|
||||||
uint64_t m_segregation_height;
|
uint64_t m_segregation_height;
|
||||||
bool m_ignore_fractional_outputs;
|
bool m_ignore_fractional_outputs;
|
||||||
|
bool m_track_uses;
|
||||||
bool m_is_initialized;
|
bool m_is_initialized;
|
||||||
NodeRPCProxy m_node_rpc_proxy;
|
NodeRPCProxy m_node_rpc_proxy;
|
||||||
std::unordered_set<crypto::hash> m_scanned_pool_txs[2];
|
std::unordered_set<crypto::hash> m_scanned_pool_txs[2];
|
||||||
|
@ -1444,7 +1450,7 @@ namespace tools
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
BOOST_CLASS_VERSION(tools::wallet2, 27)
|
BOOST_CLASS_VERSION(tools::wallet2, 27)
|
||||||
BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 10)
|
BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 11)
|
||||||
BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1)
|
BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1)
|
||||||
BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0)
|
BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0)
|
||||||
BOOST_CLASS_VERSION(tools::wallet2::multisig_tx_set, 1)
|
BOOST_CLASS_VERSION(tools::wallet2::multisig_tx_set, 1)
|
||||||
|
@ -1593,6 +1599,9 @@ namespace boost
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
a & x.m_key_image_requested;
|
a & x.m_key_image_requested;
|
||||||
|
if (ver < 11)
|
||||||
|
return;
|
||||||
|
a & x.m_uses;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Archive>
|
template <class Archive>
|
||||||
|
|
Loading…
Reference in a new issue