mirror of
https://github.com/monero-project/monero.git
synced 2024-12-24 20:49:37 +00:00
wallet: key reuse mitigation options
If a pre-fork output is spent on both Monero and attack chain, any post-fork output can be deduced to be a fake output, thereby decreasing the effective ring size. The segregate-per-fork-outputs option, on by default, allows selecting only pre-fork outputs in this case, so that the same ring can be used when spending it on the other side, which does not decrease the effective ring size. This is intended to be SET when intending to spend Monero on the attack fork, and to be UNSET if not intending to spend Monero on the attack fork (since it leaks the fact that the output being spent is pre-fork). If the user is not certain yet whether they will spend pre-fork outputs on a key reusing fork, the key-reuse-mitigation2 option should be SET instead. If you use this option and intend to spend Monero on both forks, then spend real Monero first.
This commit is contained in:
parent
5f146873c5
commit
18eaf19489
4 changed files with 211 additions and 18 deletions
|
@ -1690,6 +1690,32 @@ bool simple_wallet::set_auto_low_priority(const std::vector<std::string> &args/*
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool simple_wallet::set_segregate_pre_fork_outputs(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->segregate_pre_fork_outputs(r);
|
||||||
|
m_wallet->rewrite(m_wallet_file, pwd_container->password());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool simple_wallet::set_key_reuse_mitigation2(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->key_reuse_mitigation2(r);
|
||||||
|
m_wallet->rewrite(m_wallet_file, pwd_container->password());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool simple_wallet::help(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
|
bool simple_wallet::help(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
|
||||||
{
|
{
|
||||||
if(args.empty())
|
if(args.empty())
|
||||||
|
@ -1864,7 +1890,11 @@ simple_wallet::simple_wallet()
|
||||||
"refresh-from-block-height [n]\n "
|
"refresh-from-block-height [n]\n "
|
||||||
" Set the height before which to ignore blocks.\n "
|
" Set the height before which to ignore blocks.\n "
|
||||||
"auto-low-priority <1|0>\n "
|
"auto-low-priority <1|0>\n "
|
||||||
" Whether to automatically use the low priority fee level when it's safe to do so."));
|
" Whether to automatically use the low priority fee level when it's safe to do so.\n "
|
||||||
|
"segregate-pre-fork-outputs <1|0>\n "
|
||||||
|
" Set this if you intend to spend outputs on both Monero AND a key reusing fork.\n "
|
||||||
|
"key-reuse-mitigation2 <1|0>\n "
|
||||||
|
" Set this if you are not sure whether you will spend on a key reusing Monero fork later."));
|
||||||
m_cmd_binder.set_handler("encrypted_seed",
|
m_cmd_binder.set_handler("encrypted_seed",
|
||||||
boost::bind(&simple_wallet::encrypted_seed, this, _1),
|
boost::bind(&simple_wallet::encrypted_seed, this, _1),
|
||||||
tr("Display the encrypted Electrum-style mnemonic seed."));
|
tr("Display the encrypted Electrum-style mnemonic seed."));
|
||||||
|
@ -2040,6 +2070,8 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
|
||||||
success_msg_writer() << "confirm-export-overwrite = " << m_wallet->confirm_export_overwrite();
|
success_msg_writer() << "confirm-export-overwrite = " << m_wallet->confirm_export_overwrite();
|
||||||
success_msg_writer() << "refresh-from-block-height = " << m_wallet->get_refresh_from_block_height();
|
success_msg_writer() << "refresh-from-block-height = " << m_wallet->get_refresh_from_block_height();
|
||||||
success_msg_writer() << "auto-low-priority = " << m_wallet->auto_low_priority();
|
success_msg_writer() << "auto-low-priority = " << m_wallet->auto_low_priority();
|
||||||
|
success_msg_writer() << "segregate-pre-fork-outputs = " << m_wallet->segregate_pre_fork_outputs();
|
||||||
|
success_msg_writer() << "key-reuse-mitigation2 = " << m_wallet->key_reuse_mitigation2();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -2090,6 +2122,8 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
|
||||||
CHECK_SIMPLE_VARIABLE("confirm-export-overwrite", set_confirm_export_overwrite, tr("0 or 1"));
|
CHECK_SIMPLE_VARIABLE("confirm-export-overwrite", set_confirm_export_overwrite, tr("0 or 1"));
|
||||||
CHECK_SIMPLE_VARIABLE("refresh-from-block-height", set_refresh_from_block_height, tr("block height"));
|
CHECK_SIMPLE_VARIABLE("refresh-from-block-height", set_refresh_from_block_height, tr("block height"));
|
||||||
CHECK_SIMPLE_VARIABLE("auto-low-priority", set_auto_low_priority, tr("0 or 1"));
|
CHECK_SIMPLE_VARIABLE("auto-low-priority", set_auto_low_priority, tr("0 or 1"));
|
||||||
|
CHECK_SIMPLE_VARIABLE("segregate-pre-fork-outputs", set_segregate_pre_fork_outputs, tr("0 or 1"));
|
||||||
|
CHECK_SIMPLE_VARIABLE("key-reuse-mitigation2", set_key_reuse_mitigation2, tr("0 or 1"));
|
||||||
}
|
}
|
||||||
fail_msg_writer() << tr("set: unrecognized argument(s)");
|
fail_msg_writer() << tr("set: unrecognized argument(s)");
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -133,6 +133,8 @@ namespace cryptonote
|
||||||
bool set_confirm_export_overwrite(const std::vector<std::string> &args = std::vector<std::string>());
|
bool set_confirm_export_overwrite(const std::vector<std::string> &args = std::vector<std::string>());
|
||||||
bool set_refresh_from_block_height(const std::vector<std::string> &args = std::vector<std::string>());
|
bool set_refresh_from_block_height(const std::vector<std::string> &args = std::vector<std::string>());
|
||||||
bool set_auto_low_priority(const std::vector<std::string> &args = std::vector<std::string>());
|
bool set_auto_low_priority(const std::vector<std::string> &args = std::vector<std::string>());
|
||||||
|
bool set_segregate_pre_fork_outputs(const std::vector<std::string> &args = std::vector<std::string>());
|
||||||
|
bool set_key_reuse_mitigation2(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);
|
||||||
bool stop_mining(const std::vector<std::string> &args);
|
bool stop_mining(const std::vector<std::string> &args);
|
||||||
|
|
|
@ -94,7 +94,9 @@ using namespace cryptonote;
|
||||||
#define MULTISIG_UNSIGNED_TX_PREFIX "Monero multisig unsigned tx set\001"
|
#define MULTISIG_UNSIGNED_TX_PREFIX "Monero multisig unsigned tx set\001"
|
||||||
|
|
||||||
#define RECENT_OUTPUT_RATIO (0.5) // 50% of outputs are from the recent zone
|
#define RECENT_OUTPUT_RATIO (0.5) // 50% of outputs are from the recent zone
|
||||||
#define RECENT_OUTPUT_ZONE ((time_t)(1.8 * 86400)) // last 1.8 day makes up the recent zone (taken from monerolink.pdf, Miller et al)
|
#define RECENT_OUTPUT_DAYS (1.8) // last 1.8 day makes up the recent zone (taken from monerolink.pdf, Miller et al)
|
||||||
|
#define RECENT_OUTPUT_ZONE ((time_t)(RECENT_OUTPUT_DAYS * 86400))
|
||||||
|
#define RECENT_OUTPUT_BLOCKS (RECENT_OUTPUT_DAYS * 720)
|
||||||
|
|
||||||
#define FEE_ESTIMATE_GRACE_BLOCKS 10 // estimate fee valid for that many blocks
|
#define FEE_ESTIMATE_GRACE_BLOCKS 10 // estimate fee valid for that many blocks
|
||||||
|
|
||||||
|
@ -107,6 +109,12 @@ using namespace cryptonote;
|
||||||
|
|
||||||
#define MULTISIG_EXPORT_FILE_MAGIC "Monero multisig export\001"
|
#define MULTISIG_EXPORT_FILE_MAGIC "Monero multisig export\001"
|
||||||
|
|
||||||
|
#define SEGREGATION_FORK_HEIGHT 1564965
|
||||||
|
#define TESTNET_SEGREGATION_FORK_HEIGHT 1000000
|
||||||
|
#define STAGENET_SEGREGATION_FORK_HEIGHT 1000000
|
||||||
|
#define SEGREGATION_FORK_VICINITY 1500 /* blocks */
|
||||||
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
std::string get_default_ringdb_path()
|
std::string get_default_ringdb_path()
|
||||||
|
@ -652,6 +660,8 @@ wallet2::wallet2(network_type nettype, bool restricted):
|
||||||
m_confirm_backlog_threshold(0),
|
m_confirm_backlog_threshold(0),
|
||||||
m_confirm_export_overwrite(true),
|
m_confirm_export_overwrite(true),
|
||||||
m_auto_low_priority(true),
|
m_auto_low_priority(true),
|
||||||
|
m_segregate_pre_fork_outputs(true),
|
||||||
|
m_key_reuse_mitigation2(true),
|
||||||
m_is_initialized(false),
|
m_is_initialized(false),
|
||||||
m_restricted(restricted),
|
m_restricted(restricted),
|
||||||
is_old_file_format(false),
|
is_old_file_format(false),
|
||||||
|
@ -2328,6 +2338,8 @@ bool wallet2::get_output_distribution(uint64_t &start_height, std::vector<uint64
|
||||||
cryptonote::COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request req = AUTO_VAL_INIT(req);
|
cryptonote::COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request req = AUTO_VAL_INIT(req);
|
||||||
cryptonote::COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response res = AUTO_VAL_INIT(res);
|
cryptonote::COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response res = AUTO_VAL_INIT(res);
|
||||||
req.amounts.push_back(0);
|
req.amounts.push_back(0);
|
||||||
|
req.from_height = 0;
|
||||||
|
req.cumulative = true;
|
||||||
m_daemon_rpc_mutex.lock();
|
m_daemon_rpc_mutex.lock();
|
||||||
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_distribution", req, res, m_http_client, rpc_timeout);
|
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_distribution", req, res, m_http_client, rpc_timeout);
|
||||||
m_daemon_rpc_mutex.unlock();
|
m_daemon_rpc_mutex.unlock();
|
||||||
|
@ -2563,6 +2575,12 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
|
||||||
value2.SetUint(m_nettype);
|
value2.SetUint(m_nettype);
|
||||||
json.AddMember("nettype", value2, json.GetAllocator());
|
json.AddMember("nettype", value2, json.GetAllocator());
|
||||||
|
|
||||||
|
value2.SetInt(m_segregate_pre_fork_outputs ? 1 : 0);
|
||||||
|
json.AddMember("segregate_pre_fork_outputs", value2, json.GetAllocator());
|
||||||
|
|
||||||
|
value2.SetInt(m_key_reuse_mitigation2 ? 1 : 0);
|
||||||
|
json.AddMember("key_reuse_mitigation2", value2, json.GetAllocator());
|
||||||
|
|
||||||
// Serialize the JSON object
|
// Serialize the JSON object
|
||||||
rapidjson::StringBuffer buffer;
|
rapidjson::StringBuffer buffer;
|
||||||
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||||
|
@ -5656,6 +5674,20 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
|
||||||
|
|
||||||
if (fake_outputs_count > 0)
|
if (fake_outputs_count > 0)
|
||||||
{
|
{
|
||||||
|
uint64_t segregation_fork_height;
|
||||||
|
switch (m_nettype)
|
||||||
|
{
|
||||||
|
case TESTNET: segregation_fork_height = TESTNET_SEGREGATION_FORK_HEIGHT; break;
|
||||||
|
case STAGENET: segregation_fork_height = STAGENET_SEGREGATION_FORK_HEIGHT; break;
|
||||||
|
case MAINNET: segregation_fork_height = SEGREGATION_FORK_HEIGHT; break;
|
||||||
|
default: THROW_WALLET_EXCEPTION(error::wallet_internal_error, "Invalid network type");
|
||||||
|
}
|
||||||
|
// check whether we're shortly after the fork
|
||||||
|
uint64_t height;
|
||||||
|
boost::optional<std::string> result = m_node_rpc_proxy.get_height(height);
|
||||||
|
throw_on_rpc_response_error(result, "get_info");
|
||||||
|
bool is_shortly_after_segregation_fork = height >= segregation_fork_height && height < segregation_fork_height + SEGREGATION_FORK_VICINITY;
|
||||||
|
|
||||||
// get histogram for the amounts we need
|
// get histogram for the amounts we need
|
||||||
cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request req_t = AUTO_VAL_INIT(req_t);
|
cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request req_t = AUTO_VAL_INIT(req_t);
|
||||||
cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response resp_t = AUTO_VAL_INIT(resp_t);
|
cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response resp_t = AUTO_VAL_INIT(resp_t);
|
||||||
|
@ -5673,6 +5705,50 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
|
||||||
THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
|
THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
|
||||||
THROW_WALLET_EXCEPTION_IF(resp_t.status != CORE_RPC_STATUS_OK, error::get_histogram_error, resp_t.status);
|
THROW_WALLET_EXCEPTION_IF(resp_t.status != CORE_RPC_STATUS_OK, error::get_histogram_error, resp_t.status);
|
||||||
|
|
||||||
|
// if we want to segregate fake outs pre or post fork, get distribution
|
||||||
|
std::unordered_map<uint64_t, std::pair<uint64_t, uint64_t>> segregation_limit;
|
||||||
|
if (m_segregate_pre_fork_outputs || m_key_reuse_mitigation2)
|
||||||
|
{
|
||||||
|
cryptonote::COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request req_t = AUTO_VAL_INIT(req_t);
|
||||||
|
cryptonote::COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response resp_t = AUTO_VAL_INIT(resp_t);
|
||||||
|
for(size_t idx: selected_transfers)
|
||||||
|
req_t.amounts.push_back(m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount());
|
||||||
|
std::sort(req_t.amounts.begin(), req_t.amounts.end());
|
||||||
|
auto end = std::unique(req_t.amounts.begin(), req_t.amounts.end());
|
||||||
|
req_t.amounts.resize(std::distance(req_t.amounts.begin(), end));
|
||||||
|
req_t.from_height = segregation_fork_height >= RECENT_OUTPUT_ZONE ? height >= (segregation_fork_height ? segregation_fork_height : height) - RECENT_OUTPUT_BLOCKS : 0;
|
||||||
|
req_t.cumulative = true;
|
||||||
|
m_daemon_rpc_mutex.lock();
|
||||||
|
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_distribution", req_t, resp_t, m_http_client, rpc_timeout);
|
||||||
|
m_daemon_rpc_mutex.unlock();
|
||||||
|
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected");
|
||||||
|
THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_distribution");
|
||||||
|
THROW_WALLET_EXCEPTION_IF(resp_t.status != CORE_RPC_STATUS_OK, error::get_output_distribution, resp_t.status);
|
||||||
|
|
||||||
|
// check we got all data
|
||||||
|
for(size_t idx: selected_transfers)
|
||||||
|
{
|
||||||
|
const uint64_t amount = m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount();
|
||||||
|
bool found = false;
|
||||||
|
for (const auto &d: resp_t.distributions)
|
||||||
|
{
|
||||||
|
if (d.amount == amount)
|
||||||
|
{
|
||||||
|
THROW_WALLET_EXCEPTION_IF(d.start_height > segregation_fork_height, error::get_output_distribution, "Distribution start_height too high");
|
||||||
|
THROW_WALLET_EXCEPTION_IF(segregation_fork_height - d.start_height >= d.distribution.size(), error::get_output_distribution, "Distribution size too small");
|
||||||
|
THROW_WALLET_EXCEPTION_IF(segregation_fork_height <= RECENT_OUTPUT_BLOCKS, error::wallet_internal_error, "Fork height too low");
|
||||||
|
THROW_WALLET_EXCEPTION_IF(segregation_fork_height - RECENT_OUTPUT_BLOCKS < d.start_height, error::get_output_distribution, "Bad start height");
|
||||||
|
uint64_t till_fork = d.distribution[segregation_fork_height - d.start_height];
|
||||||
|
uint64_t recent = till_fork - d.distribution[segregation_fork_height - RECENT_OUTPUT_BLOCKS - d.start_height];
|
||||||
|
segregation_limit[amount] = std::make_pair(till_fork, recent);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
THROW_WALLET_EXCEPTION_IF(!found, error::get_output_distribution, "Requested amount not found in response");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// we ask for more, to have spares if some outputs are still locked
|
// we ask for more, to have spares if some outputs are still locked
|
||||||
size_t base_requested_outputs_count = (size_t)((fake_outputs_count + 1) * 1.5 + 1);
|
size_t base_requested_outputs_count = (size_t)((fake_outputs_count + 1) * 1.5 + 1);
|
||||||
LOG_PRINT_L2("base_requested_outputs_count: " << base_requested_outputs_count);
|
LOG_PRINT_L2("base_requested_outputs_count: " << base_requested_outputs_count);
|
||||||
|
@ -5692,9 +5768,21 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
|
||||||
size_t requested_outputs_count = base_requested_outputs_count + (td.is_rct() ? CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE : 0);
|
size_t requested_outputs_count = base_requested_outputs_count + (td.is_rct() ? CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE : 0);
|
||||||
size_t start = req.outputs.size();
|
size_t start = req.outputs.size();
|
||||||
|
|
||||||
|
const bool output_is_pre_fork = td.m_block_height < segregation_fork_height;
|
||||||
|
uint64_t num_outs = 0, num_recent_outs = 0;
|
||||||
|
uint64_t num_post_fork_outs = 0;
|
||||||
|
float pre_fork_num_out_ratio = 0.0f;
|
||||||
|
float post_fork_num_out_ratio = 0.0f;
|
||||||
|
|
||||||
|
if (m_segregate_pre_fork_outputs && output_is_pre_fork)
|
||||||
|
{
|
||||||
|
num_outs = segregation_limit[amount].first;
|
||||||
|
num_recent_outs = segregation_limit[amount].second;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
// if there are just enough outputs to mix with, use all of them.
|
// if there are just enough outputs to mix with, use all of them.
|
||||||
// Eventually this should become impossible.
|
// Eventually this should become impossible.
|
||||||
uint64_t num_outs = 0, num_recent_outs = 0;
|
|
||||||
for (const auto &he: resp_t.histogram)
|
for (const auto &he: resp_t.histogram)
|
||||||
{
|
{
|
||||||
if (he.amount == amount)
|
if (he.amount == amount)
|
||||||
|
@ -5706,21 +5794,57 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (m_key_reuse_mitigation2)
|
||||||
|
{
|
||||||
|
if (output_is_pre_fork)
|
||||||
|
{
|
||||||
|
if (is_shortly_after_segregation_fork)
|
||||||
|
{
|
||||||
|
pre_fork_num_out_ratio = 33.4/100.0f * (1.0f - RECENT_OUTPUT_RATIO);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pre_fork_num_out_ratio = 33.4/100.0f * (1.0f - RECENT_OUTPUT_RATIO);
|
||||||
|
post_fork_num_out_ratio = 33.4/100.0f * (1.0f - RECENT_OUTPUT_RATIO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (is_shortly_after_segregation_fork)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
post_fork_num_out_ratio = 67.8/100.0f * (1.0f - RECENT_OUTPUT_RATIO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
num_post_fork_outs = num_outs - segregation_limit[amount].first;
|
||||||
|
}
|
||||||
|
|
||||||
LOG_PRINT_L1("" << num_outs << " unlocked outputs of size " << print_money(amount));
|
LOG_PRINT_L1("" << num_outs << " unlocked outputs of size " << print_money(amount));
|
||||||
THROW_WALLET_EXCEPTION_IF(num_outs == 0, error::wallet_internal_error,
|
THROW_WALLET_EXCEPTION_IF(num_outs == 0, error::wallet_internal_error,
|
||||||
"histogram reports no unlocked outputs for " + boost::lexical_cast<std::string>(amount) + ", not even ours");
|
"histogram reports no unlocked outputs for " + boost::lexical_cast<std::string>(amount) + ", not even ours");
|
||||||
THROW_WALLET_EXCEPTION_IF(num_recent_outs > num_outs, error::wallet_internal_error,
|
THROW_WALLET_EXCEPTION_IF(num_recent_outs > num_outs, error::wallet_internal_error,
|
||||||
"histogram reports more recent outs than outs for " + boost::lexical_cast<std::string>(amount));
|
"histogram reports more recent outs than outs for " + boost::lexical_cast<std::string>(amount));
|
||||||
|
|
||||||
|
// how many fake outs to draw on a pre-fork triangular distribution
|
||||||
|
size_t pre_fork_outputs_count = requested_outputs_count * pre_fork_num_out_ratio;
|
||||||
|
size_t post_fork_outputs_count = requested_outputs_count * post_fork_num_out_ratio;
|
||||||
|
// how many fake outs to draw otherwise
|
||||||
|
size_t normal_output_count = requested_outputs_count - pre_fork_outputs_count - post_fork_outputs_count;
|
||||||
|
|
||||||
// X% of those outs are to be taken from recent outputs
|
// X% of those outs are to be taken from recent outputs
|
||||||
size_t recent_outputs_count = requested_outputs_count * RECENT_OUTPUT_RATIO;
|
size_t recent_outputs_count = normal_output_count * RECENT_OUTPUT_RATIO;
|
||||||
if (recent_outputs_count == 0)
|
if (recent_outputs_count == 0)
|
||||||
recent_outputs_count = 1; // ensure we have at least one, if possible
|
recent_outputs_count = 1; // ensure we have at least one, if possible
|
||||||
if (recent_outputs_count > num_recent_outs)
|
if (recent_outputs_count > num_recent_outs)
|
||||||
recent_outputs_count = num_recent_outs;
|
recent_outputs_count = num_recent_outs;
|
||||||
if (td.m_global_output_index >= num_outs - num_recent_outs && recent_outputs_count > 0)
|
if (td.m_global_output_index >= num_outs - num_recent_outs && recent_outputs_count > 0)
|
||||||
--recent_outputs_count; // if the real out is recent, pick one less recent fake out
|
--recent_outputs_count; // if the real out is recent, pick one less recent fake out
|
||||||
LOG_PRINT_L1("Using " << recent_outputs_count << " recent outputs");
|
LOG_PRINT_L1("Fake output makeup: " << requested_outputs_count << " requested: " << recent_outputs_count << " recent, " <<
|
||||||
|
pre_fork_outputs_count << " pre-fork, " << post_fork_outputs_count << " post-fork, " <<
|
||||||
|
(requested_outputs_count - recent_outputs_count - pre_fork_outputs_count - post_fork_outputs_count) << " full-chain");
|
||||||
|
|
||||||
uint64_t num_found = 0;
|
uint64_t num_found = 0;
|
||||||
|
|
||||||
|
@ -5796,6 +5920,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
|
||||||
// list of output indices we've seen.
|
// list of output indices we've seen.
|
||||||
|
|
||||||
uint64_t i;
|
uint64_t i;
|
||||||
|
const char *type = "";
|
||||||
if (num_found - 1 < recent_outputs_count) // -1 to account for the real one we seeded with
|
if (num_found - 1 < recent_outputs_count) // -1 to account for the real one we seeded with
|
||||||
{
|
{
|
||||||
// triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
|
// triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
|
||||||
|
@ -5805,7 +5930,29 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
|
||||||
// just in case rounding up to 1 occurs after calc
|
// just in case rounding up to 1 occurs after calc
|
||||||
if (i == num_outs)
|
if (i == num_outs)
|
||||||
--i;
|
--i;
|
||||||
LOG_PRINT_L2("picking " << i << " as recent");
|
type = "recent";
|
||||||
|
}
|
||||||
|
else if (num_found -1 < recent_outputs_count + pre_fork_outputs_count)
|
||||||
|
{
|
||||||
|
// triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
|
||||||
|
uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53);
|
||||||
|
double frac = std::sqrt((double)r / ((uint64_t)1 << 53));
|
||||||
|
i = (uint64_t)(frac*segregation_limit[amount].first);
|
||||||
|
// just in case rounding up to 1 occurs after calc
|
||||||
|
if (i == num_outs)
|
||||||
|
--i;
|
||||||
|
type = " pre-fork";
|
||||||
|
}
|
||||||
|
else if (num_found -1 < recent_outputs_count + pre_fork_outputs_count + post_fork_outputs_count)
|
||||||
|
{
|
||||||
|
// triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
|
||||||
|
uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53);
|
||||||
|
double frac = std::sqrt((double)r / ((uint64_t)1 << 53));
|
||||||
|
i = (uint64_t)(frac*num_post_fork_outs) + segregation_limit[amount].first;
|
||||||
|
// just in case rounding up to 1 occurs after calc
|
||||||
|
if (i == num_post_fork_outs+segregation_limit[amount].first)
|
||||||
|
--i;
|
||||||
|
type = "post-fork";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -5816,13 +5963,14 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
|
||||||
// just in case rounding up to 1 occurs after calc
|
// just in case rounding up to 1 occurs after calc
|
||||||
if (i == num_outs)
|
if (i == num_outs)
|
||||||
--i;
|
--i;
|
||||||
LOG_PRINT_L2("picking " << i << " as triangular");
|
type = "triangular";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (seen_indices.count(i))
|
if (seen_indices.count(i))
|
||||||
continue;
|
continue;
|
||||||
seen_indices.emplace(i);
|
seen_indices.emplace(i);
|
||||||
|
|
||||||
|
LOG_PRINT_L2("picking " << i << " as " << type);
|
||||||
req.outputs.push_back({amount, i});
|
req.outputs.push_back({amount, i});
|
||||||
++num_found;
|
++num_found;
|
||||||
}
|
}
|
||||||
|
@ -5860,7 +6008,10 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
|
||||||
|
|
||||||
uint64_t num_outs = 0;
|
uint64_t num_outs = 0;
|
||||||
const uint64_t amount = td.is_rct() ? 0 : td.amount();
|
const uint64_t amount = td.is_rct() ? 0 : td.amount();
|
||||||
for (const auto &he: resp_t.histogram)
|
const bool output_is_pre_fork = td.m_block_height < segregation_fork_height;
|
||||||
|
if (m_segregate_pre_fork_outputs && output_is_pre_fork)
|
||||||
|
num_outs = segregation_limit[amount].first;
|
||||||
|
else for (const auto &he: resp_t.histogram)
|
||||||
{
|
{
|
||||||
if (he.amount == amount)
|
if (he.amount == amount)
|
||||||
{
|
{
|
||||||
|
|
|
@ -862,6 +862,10 @@ namespace tools
|
||||||
void confirm_export_overwrite(bool always) { m_confirm_export_overwrite = always; }
|
void confirm_export_overwrite(bool always) { m_confirm_export_overwrite = always; }
|
||||||
bool auto_low_priority() const { return m_auto_low_priority; }
|
bool auto_low_priority() const { return m_auto_low_priority; }
|
||||||
void auto_low_priority(bool value) { m_auto_low_priority = value; }
|
void auto_low_priority(bool value) { m_auto_low_priority = value; }
|
||||||
|
bool segregate_pre_fork_outputs() const { return m_segregate_pre_fork_outputs; }
|
||||||
|
void segregate_pre_fork_outputs(bool value) { m_segregate_pre_fork_outputs = value; }
|
||||||
|
bool key_reuse_mitigation2() const { return m_key_reuse_mitigation2; }
|
||||||
|
void key_reuse_mitigation2(bool value) { m_key_reuse_mitigation2 = value; }
|
||||||
|
|
||||||
bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const;
|
bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const;
|
||||||
void check_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations);
|
void check_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations);
|
||||||
|
@ -1181,6 +1185,8 @@ namespace tools
|
||||||
uint32_t m_confirm_backlog_threshold;
|
uint32_t m_confirm_backlog_threshold;
|
||||||
bool m_confirm_export_overwrite;
|
bool m_confirm_export_overwrite;
|
||||||
bool m_auto_low_priority;
|
bool m_auto_low_priority;
|
||||||
|
bool m_segregate_pre_fork_outputs;
|
||||||
|
bool m_key_reuse_mitigation2;
|
||||||
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];
|
||||||
|
|
Loading…
Reference in a new issue