From 0b726be703de9a85369481e78a81f9aa5620264e Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 13 Nov 2017 21:50:37 +0000 Subject: [PATCH] wallet2: minimize the number of construct_tx calls --- src/wallet/wallet2.cpp | 185 ++++++++++++++++++++--------------------- 1 file changed, 90 insertions(+), 95 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 868af2794..214680a50 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -455,6 +455,59 @@ void drop_from_short_history(std::list &short_chain_history, size_ } } +size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra_size) +{ + size_t size = 0; + + // tx prefix + + // first few bytes + size += 1 + 6; + + // vin + size += n_inputs * (1+6+(mixin+1)*2+32); + + // vout + size += n_outputs * (6+32); + + // extra + size += extra_size; + + // rct signatures + + // type + size += 1; + + // rangeSigs + size += (2*64*32+32+64*32) * n_outputs; + + // MGs + size += n_inputs * (64 * (mixin+1) + 32); + + // mixRing - not serialized, can be reconstructed + /* size += 2 * 32 * (mixin+1) * n_inputs; */ + + // pseudoOuts + size += 32 * n_inputs; + // ecdhInfo + size += 2 * 32 * n_outputs; + // outPk - only commitment is saved + size += 32 * n_outputs; + // txnFee + size += 4; + + LOG_PRINT_L2("estimated rct tx size for " << n_inputs << " with ring size " << (mixin+1) << " and " << n_outputs << ": " << size << " (" << ((32 * n_inputs/*+1*/) + 2 * 32 * (mixin+1) * n_inputs + 32 * n_outputs) << " saved)"); + return size; +} + +size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size) +{ + if (use_rct) + return estimate_rct_tx_size(n_inputs, mixin, n_outputs + 1, extra_size); + else + return n_inputs * (mixin+1) * APPROXIMATE_INPUT_BYTES + extra_size; +} + } //namespace namespace tools @@ -3990,7 +4043,8 @@ std::vector wallet2::create_transactions(std::vector wallet2::pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set &subaddr_indices) const { std::vector picks; @@ -4785,19 +4786,7 @@ std::vector wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui LOG_PRINT_L2("pick_preferred_rct_inputs: needed_money " << print_money(needed_money)); - // try to find a rct input of enough size - for (size_t i = 0; i < m_transfers.size(); ++i) - { - const transfer_details& td = m_transfers[i]; - if (!td.m_spent && td.is_rct() && td.amount() >= needed_money && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1) - { - LOG_PRINT_L2("We can use " << i << " alone: " << print_money(td.amount())); - picks.push_back(i); - return picks; - } - } - - // then try to find two outputs + // try to find two outputs // this could be made better by picking one of the outputs to be a small one, since those // are less useful since often below the needed money, so if one can be used in a pair, // it gets rid of it for the future @@ -5551,7 +5540,7 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector* unused_transfers_indices = &unused_transfers_indices_per_subaddr[0].second; std::vector* unused_dust_indices = &unused_dust_indices_per_subaddr[0].second; - while ((!dsts.empty() && dsts[0].amount > 0) || adding_fee || should_pick_a_second_output(use_rct, txes.back().selected_transfers.size(), *unused_transfers_indices, *unused_dust_indices)) { + while ((!dsts.empty() && dsts[0].amount > 0) || adding_fee || !preferred_inputs.empty() || should_pick_a_second_output(use_rct, txes.back().selected_transfers.size(), *unused_transfers_indices, *unused_dust_indices)) { TX &tx = txes.back(); LOG_PRINT_L2("Start of loop with " << unused_transfers_indices->size() << " " << unused_dust_indices->size()); @@ -5606,7 +5595,11 @@ std::vector wallet2::create_transactions_2(std::vector indices = get_only_rct(*unused_dust_indices, *unused_transfers_indices); idx = pop_best_value(indices, tx.selected_transfers, true); @@ -5629,10 +5622,6 @@ std::vector wallet2::create_transactions_2(std::vectorempty() ? *unused_dust_indices : *unused_transfers_indices, tx.selected_transfers); @@ -5654,7 +5643,7 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector 0 && !dsts.empty() && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()) < TX_SIZE_TARGET(upper_transaction_size_limit)) { + if (available_amount > 0 && !dsts.empty() && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size()) < TX_SIZE_TARGET(upper_transaction_size_limit)) { // we can partially fill that destination LOG_PRINT_L2("We can partially pay " << get_account_address_as_str(m_testnet, dsts[0].is_subaddress, dsts[0].addr) << " for " << print_money(available_amount) << "/" << print_money(dsts[0].amount)); @@ -5679,26 +5668,31 @@ std::vector wallet2::create_transactions_2(std::vector= needed_fee; - } - else - { - const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()); - try_tx = dsts.empty() || (estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit)); + if (adding_fee) + { + /* might not actually be enough if adding this output bumps size to next kB, but we need to try */ + try_tx = available_for_fee >= needed_fee; + } + else + { + const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size()); + try_tx = dsts.empty() || (estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit)); + } } if (try_tx) { cryptonote::transaction test_tx; pending_tx test_ptx; - needed_fee = 0; + const size_t estimated_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size()); + needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier); - LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " destinations and " << - tx.selected_transfers.size() << " outputs"); + LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " outputs and " << + tx.selected_transfers.size() << " inputs"); if (use_rct) transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, test_tx, test_ptx); @@ -5740,8 +5734,8 @@ std::vector wallet2::create_transactions_2(std::vector test_ptx.fee) { if (use_rct) transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, test_tx, test_ptx); @@ -5752,7 +5746,7 @@ std::vector wallet2::create_transactions_2(std::vector test_ptx.fee); + } LOG_PRINT_L2("Made a final " << ((txBlob.size() + 1023)/1024) << " kB tx, with " << print_money(test_ptx.fee) << " fee and " << print_money(test_ptx.change_dts.amount) << " change"); @@ -5934,14 +5928,15 @@ std::vector wallet2::create_transactions_from(const crypton // 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); - const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 1); + const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 1, extra.size()); bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || ( estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit)); if (try_tx) { cryptonote::transaction test_tx; pending_tx test_ptx; - needed_fee = 0; + const size_t estimated_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size()); + needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier); tx.dsts.push_back(tx_destination_entry(1, address, is_subaddress)); @@ -5961,7 +5956,7 @@ std::vector wallet2::create_transactions_from(const crypton THROW_WALLET_EXCEPTION_IF(needed_fee > available_for_fee, error::wallet_internal_error, "Transaction cannot pay for itself"); - do { + while (needed_fee > test_ptx.fee) { LOG_PRINT_L2("We made a tx, adjusting fee and saving it"); tx.dsts[0].amount = available_for_fee - needed_fee; if (use_rct) @@ -5974,7 +5969,7 @@ std::vector wallet2::create_transactions_from(const crypton needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier); LOG_PRINT_L2("Made an attempt at a final " << ((txBlob.size() + 1023)/1024) << " kB tx, with " << print_money(test_ptx.fee) << " fee and " << print_money(test_ptx.change_dts.amount) << " change"); - } while (needed_fee > test_ptx.fee); + } LOG_PRINT_L2("Made a final " << ((txBlob.size() + 1023)/1024) << " kB tx, with " << print_money(test_ptx.fee) << " fee and " << print_money(test_ptx.change_dts.amount) << " change");