From d86ae2bec6737ca1c1f85b3a8395a4a241659a72 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 7 Jan 2017 16:06:07 +0000 Subject: [PATCH] wallet2: reuse fake outs when adjusting fee on transfer This avoids indirectly leaking the real output to the daemon, and is faster. This will still happen for more complex cases, especially when cancelling a tx and "re-rolling" it. --- src/wallet/wallet2.cpp | 66 ++++++++++++++++++++++---------- src/wallet/wallet2.h | 7 +++- src/wallet/wallet_rpc_server.cpp | 5 +++ 3 files changed, 55 insertions(+), 23 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 8badebbfa..1d990d6d5 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -3400,8 +3400,7 @@ std::vector wallet2::create_transactions(std::vector -void wallet2::get_outs(std::vector> &outs, const std::list &selected_transfers, size_t fake_outputs_count) +void wallet2::get_outs(std::vector> &outs, const std::list &selected_transfers, size_t fake_outputs_count) { LOG_PRINT_L2("fake_outputs_count: " << fake_outputs_count); outs.clear(); @@ -3492,6 +3491,7 @@ void wallet2::get_outs(std::vector> &outs, const std::list> &outs, const std::list()); + outs.push_back(std::vector()); outs.back().reserve(fake_outputs_count + 1); const rct::key mask = td.is_rct() ? rct::commit(td.amount(), td.m_mask) : rct::zeroCommit(td.amount()); @@ -3616,7 +3616,7 @@ void wallet2::get_outs(std::vector> &outs, const std::list(a) < std::get<0>(b); }); + 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); }); } base += requested_outputs_count; } @@ -3627,7 +3627,7 @@ void wallet2::get_outs(std::vector> &outs, const std::list v; + std::vector v; const rct::key mask = td.is_rct() ? rct::commit(td.amount(), td.m_mask) : rct::zeroCommit(td.amount()); v.push_back(std::make_tuple(td.m_global_output_index, boost::get(td.m_tx.vout[td.m_internal_output_index].target).key, mask)); outs.push_back(v); @@ -3636,7 +3636,8 @@ void wallet2::get_outs(std::vector> &outs, const std::list -void wallet2::transfer_selected(const std::vector& dsts, const std::list selected_transfers, size_t fake_outputs_count, +void wallet2::transfer_selected(const std::vector& dsts, const std::list selected_transfers, size_t fake_outputs_count, + std::vector> &outs, uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx) { using namespace cryptonote; @@ -3666,11 +3667,11 @@ void wallet2::transfer_selected(const std::vector entry; - std::vector> outs; - get_outs(outs, selected_transfers, fake_outputs_count); // may throw + if (outs.empty()) + get_outs(outs, selected_transfers, fake_outputs_count); // may throw //prepare inputs + LOG_PRINT_L2("preparing outputs"); typedef cryptonote::tx_source_entry::output_entry tx_output_entry; size_t i = 0, out_index = 0; std::vector sources; @@ -3713,6 +3714,7 @@ void wallet2::transfer_selected(const std::vector dsts, const std::list selected_transfers, size_t fake_outputs_count, + std::vector> &outs, uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::transaction& tx, pending_tx &ptx) { using namespace cryptonote; @@ -3782,7 +3788,7 @@ void wallet2::transfer_selected_rct(std::vector entry; - std::vector> outs; - get_outs(outs, selected_transfers, fake_outputs_count); // may throw + if (outs.empty()) + get_outs(outs, selected_transfers, fake_outputs_count); // may throw //prepare inputs + LOG_PRINT_L2("preparing outputs"); size_t i = 0, out_index = 0; std::vector sources; BOOST_FOREACH(size_t idx, selected_transfers) @@ -3853,6 +3859,7 @@ void wallet2::transfer_selected_rct(std::vector splitted_dsts = dsts; @@ -3864,9 +3871,11 @@ void wallet2::transfer_selected_rct(std::vector bool { @@ -3887,6 +3899,7 @@ void wallet2::transfer_selected_rct(std::vector wallet2::create_transactions_2(std::vector> outs; // for rct, since we don't see the amounts, we will try to make all transactions // look the same, with 1 or 2 inputs, and 2 outputs. One input is preferable, as // this prevents linking to another by provenance analysis, but two is ok if we // try to pick outputs not from the same block. We will get two outputs, one for // the destination, and one for change. + LOG_PRINT_L2("checking preferred"); std::vector prefered_inputs; uint64_t rct_outs_needed = 2 * (fake_outs_count + 1); rct_outs_needed += 100; // some fudge factor since we don't know how many are locked @@ -4128,6 +4144,7 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_from(const crypton std::vector txes; uint64_t needed_fee, available_for_fee = 0; uint64_t upper_transaction_size_limit = get_upper_tranaction_size_limit(); + std::vector> outs; const bool use_rct = fake_outs_count > 0 && use_fork_rules(4, 0); const bool use_new_fee = use_fork_rules(3, -720 * 14); @@ -4386,6 +4407,9 @@ std::vector wallet2::create_transactions_from(const crypton uint64_t available_amount = td.amount(); accumulated_outputs += available_amount; + // clear any fake outs we'd already gathered, since we'll need a new set + outs.clear(); + // here, check if we need to sent tx and start a new one LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit " << upper_transaction_size_limit); @@ -4407,10 +4431,10 @@ std::vector wallet2::create_transactions_from(const crypton LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " destinations and " << tx.selected_transfers.size() << " outputs"); if (use_rct) - transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, unlock_time, needed_fee, extra, + transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, test_tx, test_ptx); else - transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, unlock_time, needed_fee, extra, + transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); auto txBlob = t_serializable_object_to_blob(test_ptx.tx); needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier); @@ -4424,10 +4448,10 @@ std::vector wallet2::create_transactions_from(const crypton LOG_PRINT_L2("We made a tx, adjusting fee and saving it"); tx.dsts[0].amount = available_for_fee - needed_fee; if (use_rct) - transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, unlock_time, needed_fee, extra, + transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, test_tx, test_ptx); else - transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, unlock_time, needed_fee, extra, + transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); txBlob = t_serializable_object_to_blob(test_ptx.tx); needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index b5cc56e56..b0aec681f 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -275,6 +275,8 @@ namespace tools std::string m_description; }; + typedef std::tuple get_outs_entry; + /*! * \brief Generates a wallet or restores one. * \param wallet_ Name of wallet file @@ -387,8 +389,10 @@ namespace tools void transfer(const std::vector& dsts, const size_t fake_outputs_count, const std::vector &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::transaction& tx, pending_tx& ptx, bool trusted_daemon); template void transfer_selected(const std::vector& dsts, const std::list selected_transfers, size_t fake_outputs_count, + std::vector> &outs, uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx); void transfer_selected_rct(std::vector dsts, const std::list selected_transfers, size_t fake_outputs_count, + std::vector> &outs, uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::transaction& tx, pending_tx &ptx); void commit_tx(pending_tx& ptx_vector); @@ -607,8 +611,7 @@ namespace tools std::vector pick_preferred_rct_inputs(uint64_t needed_money) const; void set_spent(size_t idx, uint64_t height); void set_unspent(size_t idx); - template - void get_outs(std::vector> &outs, const std::list &selected_transfers, size_t fake_outputs_count); + void get_outs(std::vector> &outs, const std::list &selected_transfers, size_t fake_outputs_count); bool wallet_generate_key_image_helper(const cryptonote::account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, cryptonote::keypair& in_ephemeral, crypto::key_image& ki); crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const; diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index bf2cba346..33e099ceb 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -396,6 +396,7 @@ namespace tools std::vector dsts; std::vector extra; + LOG_PRINT_L3("on_transfer_split starts"); if (m_wallet.restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; @@ -485,9 +486,13 @@ namespace tools mixin = 2; } std::vector ptx_vector; + LOG_PRINT_L2("on_transfer_split calling create_transactions_2"); ptx_vector = m_wallet.create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, req.trusted_daemon); + LOG_PRINT_L2("on_transfer_split called create_transactions_2"); + LOG_PRINT_L2("on_transfer_split calling commit_txyy"); m_wallet.commit_tx(ptx_vector); + LOG_PRINT_L2("on_transfer_split called commit_txyy"); // populate response with tx hashes for (auto & ptx : ptx_vector)