diff options
author | moneromooo-monero <moneromooo-monero@users.noreply.github.com> | 2017-01-07 16:06:07 +0000 |
---|---|---|
committer | moneromooo-monero <moneromooo-monero@users.noreply.github.com> | 2017-01-16 08:59:08 +0000 |
commit | d86ae2bec6737ca1c1f85b3a8395a4a241659a72 (patch) | |
tree | 2f56732e2cdd11d08c57c881e8c81da63d507ba8 | |
parent | core: don't try to deserialize an empty extra to remove a field (diff) | |
download | monero-d86ae2bec6737ca1c1f85b3a8395a4a241659a72.tar.xz |
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.
-rw-r--r-- | src/wallet/wallet2.cpp | 66 | ||||
-rw-r--r-- | src/wallet/wallet2.h | 7 | ||||
-rw-r--r-- | 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::pending_tx> wallet2::create_transactions(std::vector<crypto } } -template<typename entry> -void wallet2::get_outs(std::vector<std::vector<entry>> &outs, const std::list<size_t> &selected_transfers, size_t fake_outputs_count) +void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::list<size_t> &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<std::vector<entry>> &outs, const std::list<si uint64_t num_found = 1; seen_indices.emplace(td.m_global_output_index); req.outputs.push_back({amount, td.m_global_output_index}); + LOG_PRINT_L1("Selecting real output: " << td.m_global_output_index << " for " << print_money(amount)); // while we still need more mixins while (num_found < requested_outputs_count) @@ -3563,7 +3563,7 @@ void wallet2::get_outs(std::vector<std::vector<entry>> &outs, const std::list<si { const transfer_details &td = m_transfers[idx]; size_t requested_outputs_count = base_requested_outputs_count + (td.is_rct() ? CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE : 0); - outs.push_back(std::vector<entry>()); + outs.push_back(std::vector<get_outs_entry>()); 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<std::vector<entry>> &outs, const std::list<si else { // sort the subsection, so any spares are reset in order - std::sort(outs.back().begin(), outs.back().end(), [](const entry &a, const entry &b) { return std::get<0>(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<std::vector<entry>> &outs, const std::list<si for (size_t idx: selected_transfers) { const transfer_details &td = m_transfers[idx]; - std::vector<entry> v; + std::vector<get_outs_entry> 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<txout_to_key>(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<std::vector<entry>> &outs, const std::list<si } template<typename T> -void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::list<size_t> selected_transfers, size_t fake_outputs_count, +void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::list<size_t> selected_transfers, size_t fake_outputs_count, + std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& 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<cryptonote::tx_destination_ent LOG_PRINT_L2("wanted " << print_money(needed_money) << ", found " << print_money(found_money) << ", fee " << print_money(fee)); THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_money, found_money, needed_money - fee, fee); - typedef std::tuple<uint64_t, crypto::public_key, rct::key> entry; - std::vector<std::vector<entry>> 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<cryptonote::tx_source_entry> sources; @@ -3713,6 +3714,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent detail::print_source_entry(src); ++out_index; } + LOG_PRINT_L2("outputs prepared"); cryptonote::tx_destination_entry change_dts = AUTO_VAL_INIT(change_dts); if (needed_money < found_money) @@ -3735,7 +3737,9 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent } crypto::secret_key tx_key; + LOG_PRINT_L2("constructing tx"); bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), sources, splitted_dsts, extra, tx, unlock_time, tx_key); + LOG_PRINT_L2("constructed tx, r="<<r); THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_testnet); THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit); @@ -3771,9 +3775,11 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent ptx.construction_data.unlock_time = unlock_time; ptx.construction_data.use_rct = false; ptx.construction_data.dests = dsts; + LOG_PRINT_L2("transfer_selected done"); } void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::list<size_t> selected_transfers, size_t fake_outputs_count, + std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx) { using namespace cryptonote; @@ -3782,7 +3788,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry uint64_t upper_transaction_size_limit = get_upper_tranaction_size_limit(); uint64_t needed_money = fee; - LOG_PRINT_L2("transfer: starting with fee " << print_money (needed_money)); + LOG_PRINT_L2("transfer_selected_rct: starting with fee " << print_money (needed_money)); LOG_PRINT_L0("selected transfers: "); for (auto t: selected_transfers) LOG_PRINT_L2(" " << t); @@ -3806,11 +3812,11 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry LOG_PRINT_L2("wanted " << print_money(needed_money) << ", found " << print_money(found_money) << ", fee " << print_money(fee)); THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_money, found_money, needed_money - fee, fee); - typedef std::tuple<uint64_t, crypto::public_key, rct::key> entry; - std::vector<std::vector<entry>> 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<cryptonote::tx_source_entry> sources; BOOST_FOREACH(size_t idx, selected_transfers) @@ -3853,6 +3859,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry detail::print_source_entry(src); ++out_index; } + LOG_PRINT_L2("outputs prepared"); // we still keep a copy, since we want to keep dsts free of change for user feedback purposes std::vector<cryptonote::tx_destination_entry> splitted_dsts = dsts; @@ -3864,9 +3871,11 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry // the sender with a 0 amount output. We send a 0 amount in order to avoid // letting the destination be able to work out which of the inputs is the // real one in our rings + LOG_PRINT_L2("generating dummy address for 0 change"); cryptonote::account_base dummy; dummy.generate(); change_dts.addr = dummy.get_keys().m_account_address; + LOG_PRINT_L2("generated dummy address for 0 change"); } else { @@ -3875,10 +3884,13 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry splitted_dsts.push_back(change_dts); crypto::secret_key tx_key; + LOG_PRINT_L2("constructing tx"); bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), sources, splitted_dsts, extra, tx, unlock_time, tx_key, true); + LOG_PRINT_L2("constructed tx, r="<<r); THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, dsts, unlock_time, m_testnet); THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit); + LOG_PRINT_L2("gathering key images"); std::string key_images; bool all_are_txin_to_key = std::all_of(tx.vin.begin(), tx.vin.end(), [&](const txin_v& s_e) -> bool { @@ -3887,6 +3899,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry return true; }); THROW_WALLET_EXCEPTION_IF(!all_are_txin_to_key, error::unexpected_txin_type, tx); + LOG_PRINT_L2("gathered key images"); ptx.key_images = key_images; ptx.fee = fee; @@ -3905,6 +3918,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry ptx.construction_data.unlock_time = unlock_time; ptx.construction_data.use_rct = true; ptx.construction_data.dests = dsts; + LOG_PRINT_L2("transfer_selected_rct done"); } static size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs) @@ -4106,12 +4120,14 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp accumulated_change = 0; adding_fee = false; needed_fee = 0; + std::vector<std::vector<tools::wallet2::get_outs_entry>> 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<size_t> 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::pending_tx> wallet2::create_transactions_2(std::vector<cryp LOG_PRINT_L1("Found prefered rct inputs for rct tx: " << s); } } + LOG_PRINT_L2("done checking preferred"); // while: // - we have something to send @@ -4161,6 +4178,9 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp 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(); + if (adding_fee) { LOG_PRINT_L2("We need more fee, adding it to fee"); @@ -4217,10 +4237,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp 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); @@ -4260,10 +4280,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp LOG_PRINT_L2("We made a tx, adjusting fee and saving it"); do { 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); @@ -4351,6 +4371,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton std::vector<TX> txes; uint64_t needed_fee, available_for_fee = 0; uint64_t upper_transaction_size_limit = get_upper_tranaction_size_limit(); + std::vector<std::vector<get_outs_entry>> 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::pending_tx> 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::pending_tx> 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::pending_tx> 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<uint64_t, crypto::public_key, rct::key> 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<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx& ptx, bool trusted_daemon); template<typename T> void transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::list<size_t> selected_transfers, size_t fake_outputs_count, + std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx); void transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::list<size_t> selected_transfers, size_t fake_outputs_count, + std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx); void commit_tx(pending_tx& ptx_vector); @@ -607,8 +611,7 @@ namespace tools std::vector<size_t> 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<typename entry> - void get_outs(std::vector<std::vector<entry>> &outs, const std::list<size_t> &selected_transfers, size_t fake_outputs_count); + void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::list<size_t> &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<cryptonote::tx_destination_entry> dsts; std::vector<uint8_t> 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<wallet2::pending_tx> 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) |