diff options
Diffstat (limited to 'src/wallet/wallet2.cpp')
-rw-r--r-- | src/wallet/wallet2.cpp | 89 |
1 files changed, 67 insertions, 22 deletions
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 98dbc4fd6..b499f6b24 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -77,6 +77,9 @@ using namespace cryptonote; #define UNSIGNED_TX_PREFIX "Monero unsigned tx set\001" #define SIGNED_TX_PREFIX "Monero signed tx set\001" +#define RECENT_OUTPUT_RATIO (0.25) // 25% of outputs are from the recent zone +#define RECENT_OUTPUT_ZONE (5 * 86400) // last 5 days are the recent zone + #define KILL_IOSERVICE() \ do { \ work.reset(); \ @@ -1514,8 +1517,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const std::string& pa } GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, watch_only, int, Int, false, false); m_watch_only = field_watch_only; - GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, always_confirm_transfers, int, Int, false, false); - m_always_confirm_transfers = field_always_confirm_transfers_found && field_always_confirm_transfers; + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, always_confirm_transfers, int, Int, false, true); + m_always_confirm_transfers = field_always_confirm_transfers; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_keys, int, Int, false, true); GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_info, int, Int, false, true); m_store_tx_info = ((field_store_tx_keys != 0) || (field_store_tx_info != 0)); @@ -1546,10 +1549,9 @@ bool wallet2::load_keys(const std::string& keys_file_name, const std::string& pa LOG_PRINT_L0("Unknown refresh-type value (" << field_refresh_type << "), using default"); } GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, refresh_height, uint64_t, Uint64, false, 0); - if (field_refresh_height_found) - m_refresh_from_block_height = field_refresh_height; - GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_missing_payment_id, int, Int, false, false); - m_confirm_missing_payment_id = !field_confirm_missing_payment_id_found || field_confirm_missing_payment_id; + m_refresh_from_block_height = field_refresh_height; + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_missing_payment_id, int, Int, false, true); + m_confirm_missing_payment_id = field_confirm_missing_payment_id; } const cryptonote::account_keys& keys = m_account.get_keys(); @@ -2284,9 +2286,9 @@ size_t wallet2::pop_best_value_from(const transfer_container &transfers, std::ve { const transfer_details &candidate = transfers[unused_indices[n]]; float relatedness = 0.0f; - for (size_t i = 0; i < selected_transfers.size(); ++i) + for (std::list<size_t>::const_iterator i = selected_transfers.begin(); i != selected_transfers.end(); ++i) { - float r = get_output_relatedness(candidate, transfers[i]); + float r = get_output_relatedness(candidate, transfers[*i]); if (r > relatedness) { relatedness = r; @@ -2855,6 +2857,7 @@ void wallet2::get_outs(std::vector<std::vector<entry>> &outs, const std::list<si auto end = std::unique(req_t.params.amounts.begin(), req_t.params.amounts.end()); req_t.params.amounts.resize(std::distance(req_t.params.amounts.begin(), end)); req_t.params.unlocked = true; + req_t.params.recent_cutoff = time(NULL) - RECENT_OUTPUT_ZONE; bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/json_rpc", req_t, resp_t, m_http_client); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected"); @@ -2869,8 +2872,10 @@ void wallet2::get_outs(std::vector<std::vector<entry>> &outs, const std::list<si COMMAND_RPC_GET_OUTPUTS::request req = AUTO_VAL_INIT(req); COMMAND_RPC_GET_OUTPUTS::response daemon_resp = AUTO_VAL_INIT(daemon_resp); + size_t num_selected_transfers = 0; for(size_t idx: selected_transfers) { + ++num_selected_transfers; const transfer_details &td = m_transfers[idx]; const uint64_t amount = td.is_rct() ? 0 : td.amount(); std::unordered_set<uint64_t> seen_indices; @@ -2880,18 +2885,33 @@ void wallet2::get_outs(std::vector<std::vector<entry>> &outs, const std::list<si // if there are just enough outputs to mix with, use all of them. // Eventually this should become impossible. - uint64_t num_outs = 0; + uint64_t num_outs = 0, num_recent_outs = 0; for (auto he: resp_t.result.histogram) { if (he.amount == amount) { - num_outs = he.instances; + LOG_PRINT_L2("Found " << print_money(amount) << ": " << he.total_instances << " total, " + << he.unlocked_instances << " unlocked, " << he.recent_instances << " recent"); + num_outs = he.unlocked_instances; + num_recent_outs = he.recent_instances; break; } } LOG_PRINT_L1("" << num_outs << " outputs of size " << print_money(amount)); THROW_WALLET_EXCEPTION_IF(num_outs == 0, error::wallet_internal_error, "histogram reports no outputs for " + boost::lexical_cast<std::string>(amount) + ", not even ours"); + 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)); + + // X% of those outs are to be taken from recent outputs + size_t recent_outputs_count = requested_outputs_count * RECENT_OUTPUT_RATIO; + if (recent_outputs_count == 0) + recent_outputs_count = 1; // ensure we have at least one, if possible + if (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; // if the real out is recent, pick one less recent fake out + LOG_PRINT_L1("Using " << recent_outputs_count << " recent outputs"); if (num_outs <= requested_outputs_count) { @@ -2921,11 +2941,24 @@ void wallet2::get_outs(std::vector<std::vector<entry>> &outs, const std::list<si // return to the top of the loop and try again, otherwise add it to the // list of output indices we've seen. - // 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)); - uint64_t i = (uint64_t)(frac*num_outs); - // just in case rounding up to 1 occurs after sqrt + uint64_t i; + if (num_found - 1 < recent_outputs_count) // -1 to account for the real one we seeded with + { + // equiprobable distribution over the recent outs + 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_recent_outs) + num_outs - num_recent_outs; + LOG_PRINT_L2("picking " << i << " as recent"); + } + else + { + // 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_outs); + LOG_PRINT_L2("picking " << i << " as triangular"); + } + // just in case rounding up to 1 occurs after calc if (i == num_outs) --i; @@ -2959,7 +2992,7 @@ void wallet2::get_outs(std::vector<std::vector<entry>> &outs, const std::list<si std::unordered_map<uint64_t, uint64_t> scanty_outs; size_t base = 0; - outs.reserve(selected_transfers.size()); + outs.reserve(num_selected_transfers); for(size_t idx: selected_transfers) { const transfer_details &td = m_transfers[idx]; @@ -3457,6 +3490,12 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp } LOG_PRINT_L2("Starting with " << unused_transfers_indices.size() << " non-dust outputs and " << unused_dust_indices.size() << " dust outputs"); + // early out if we know we can't make it anyway + // we could also check for being within FEE_PER_KB, but if the fee calculation + // ever changes, this might be missed, so let this go through + THROW_WALLET_EXCEPTION_IF(needed_money > unlocked_balance(), error::not_enough_money, + unlocked_balance(), needed_money, 0); + if (unused_dust_indices.empty() && unused_transfers_indices.empty()) return std::vector<wallet2::pending_tx>(); @@ -3497,7 +3536,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp // if we need to spend money and don't have any left, we fail if (unused_dust_indices.empty() && unused_transfers_indices.empty()) { LOG_PRINT_L2("No more outputs to choose from"); - THROW_WALLET_EXCEPTION_IF(1, error::not_enough_money, unlocked_balance(), needed_money, accumulated_fee + needed_fee); + THROW_WALLET_EXCEPTION_IF(1, error::tx_not_possible, unlocked_balance(), needed_money, accumulated_fee + needed_fee); } // get a random unspent output and use it to pay part (or all) of the current destination (and maybe next one, etc) @@ -3637,7 +3676,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp if (adding_fee) { LOG_PRINT_L1("We ran out of outputs while trying to gather final fee"); - THROW_WALLET_EXCEPTION_IF(1, error::not_enough_money, unlocked_balance(), needed_money, accumulated_fee + needed_fee); + THROW_WALLET_EXCEPTION_IF(1, error::tx_not_possible, unlocked_balance(), needed_money, accumulated_fee + needed_fee); } LOG_PRINT_L1("Done creating " << txes.size() << " transactions, " << print_money(accumulated_fee) << @@ -3697,7 +3736,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton uint64_t needed_fee, available_for_fee = 0; uint64_t upper_transaction_size_limit = get_upper_tranaction_size_limit(); - const bool use_rct = use_fork_rules(4, 0); + const bool use_rct = fake_outs_count > 0 && use_fork_rules(4, 0); const bool use_new_fee = use_fork_rules(3, -720 * 14); const uint64_t fee_per_kb = use_new_fee ? FEE_PER_KB : FEE_PER_KB_OLD; const uint64_t fee_multiplier = get_fee_multiplier(priority, use_new_fee); @@ -3975,20 +4014,26 @@ uint64_t wallet2::get_num_rct_outputs() THROW_WALLET_EXCEPTION_IF(resp_t.result.histogram.size() != 1, error::get_histogram_error, "Expected exactly one response"); THROW_WALLET_EXCEPTION_IF(resp_t.result.histogram[0].amount != 0, error::get_histogram_error, "Expected 0 amount"); - return resp_t.result.histogram[0].instances; + return resp_t.result.histogram[0].total_instances; +} +//---------------------------------------------------------------------------------------------------- +const wallet2::transfer_details &wallet2::get_transfer_details(size_t idx) const +{ + THROW_WALLET_EXCEPTION_IF(idx >= m_transfers.size(), error::wallet_internal_error, "Bad transfer index"); + return m_transfers[idx]; } //---------------------------------------------------------------------------------------------------- std::vector<size_t> wallet2::select_available_unmixable_outputs(bool trusted_daemon) { // request all outputs with less than 3 instances - const size_t min_mixin = use_fork_rules(5, 10) ? 2 : 4; // v5 increases min mixin from 2 to 4 + const size_t min_mixin = use_fork_rules(5, 10) ? 4 : 2; // v5 increases min mixin from 2 to 4 return select_available_outputs_from_histogram(min_mixin + 1, false, true, trusted_daemon); } //---------------------------------------------------------------------------------------------------- std::vector<size_t> wallet2::select_available_mixable_outputs(bool trusted_daemon) { // request all outputs with at least 3 instances, so we can use mixin 2 with - const size_t min_mixin = use_fork_rules(5, 10) ? 2 : 4; // v5 increases min mixin from 2 to 4 + const size_t min_mixin = use_fork_rules(5, 10) ? 4 : 2; // v5 increases min mixin from 2 to 4 return select_available_outputs_from_histogram(min_mixin + 1, true, true, trusted_daemon); } //---------------------------------------------------------------------------------------------------- |