aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/wallet2.cpp
diff options
context:
space:
mode:
authorRiccardo Spagni <ric@spagni.net>2017-11-25 19:50:58 +0200
committerRiccardo Spagni <ric@spagni.net>2017-11-25 19:50:58 +0200
commitdf032aac69a56e5912c5928ed3740961bd9e8cf7 (patch)
treef73aa4169c97d8a2e75fe225b8554f0b8f4ede37 /src/wallet/wallet2.cpp
parentMerge pull request #2797 (diff)
parentwallet2: minimize the number of construct_tx calls (diff)
downloadmonero-df032aac69a56e5912c5928ed3740961bd9e8cf7.tar.xz
Merge pull request #2800
0b726be7 wallet2: minimize the number of construct_tx calls (moneromooo-monero) 9d505d26 wallet2: cleanup some debug logs (moneromooo-monero)
Diffstat (limited to 'src/wallet/wallet2.cpp')
-rw-r--r--src/wallet/wallet2.cpp196
1 files changed, 94 insertions, 102 deletions
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index c995c4333..91c216a49 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -457,6 +457,59 @@ void drop_from_short_history(std::list<crypto::hash> &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
@@ -3992,7 +4045,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
pending_tx ptx;
// loop until fee is met without increasing tx size to next KB boundary.
- uint64_t needed_fee = 0;
+ const size_t estimated_tx_size = estimate_tx_size(false, unused_transfers_indices.size(), fake_outs_count, dst_vector.size(), extra.size());
+ uint64_t needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier);
do
{
transfer(dst_vector, fake_outs_count, unused_transfers_indices, unlock_time, needed_fee, extra, tx, ptx, trusted_daemon);
@@ -4582,9 +4636,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit();
uint64_t needed_money = fee;
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);
+ LOG_PRINT_L2("selected transfers: " << strjoin(selected_transfers, " "));
// calculate total amount being sent to all destinations
// throw if total amount overflows uint64_t
@@ -4733,59 +4785,6 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
LOG_PRINT_L2("transfer_selected_rct done");
}
-static size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs)
-{
- 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 += 40;
-
- // 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;
-}
-
-static size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs)
-{
- if (use_rct)
- return estimate_rct_tx_size(n_inputs, mixin, n_outputs + 1);
- else
- return n_inputs * (mixin+1) * APPROXIMATE_INPUT_BYTES;
-}
-
std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices) const
{
std::vector<size_t> picks;
@@ -4793,19 +4792,7 @@ std::vector<size_t> 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
@@ -5559,12 +5546,12 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
{
// this is used to build a tx that's 1 or 2 inputs, and 2 outputs, which
// will get us a known fee.
- uint64_t estimated_fee = calculate_fee(fee_per_kb, estimate_rct_tx_size(2, fake_outs_count + 1, 2), fee_multiplier);
+ uint64_t estimated_fee = calculate_fee(fee_per_kb, estimate_rct_tx_size(2, fake_outs_count, 2, extra.size()), fee_multiplier);
preferred_inputs = pick_preferred_rct_inputs(needed_money + estimated_fee, subaddr_account, subaddr_indices);
if (!preferred_inputs.empty())
{
string s;
- for (auto i: preferred_inputs) s += boost::lexical_cast<std::string>(i) + "(" + print_money(m_transfers[i].amount()) + ") ";
+ for (auto i: preferred_inputs) s += boost::lexical_cast<std::string>(i) + " (" + print_money(m_transfers[i].amount()) + ") ";
LOG_PRINT_L1("Found prefered rct inputs for rct tx: " << s);
// bring the list of available outputs stored by the same subaddress index to the front of the list
@@ -5596,14 +5583,13 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
unsigned int original_output_index = 0;
std::vector<size_t>* unused_transfers_indices = &unused_transfers_indices_per_subaddr[0].second;
std::vector<size_t>* 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());
- LOG_PRINT_L2("Start of loop with " << unused_transfers_indices->size() << " " << unused_dust_indices->size());
LOG_PRINT_L2("unused_transfers_indices: " << strjoin(*unused_transfers_indices, " "));
- LOG_PRINT_L2("unused_dust_indices:" << strjoin(*unused_dust_indices, " "));
- LOG_PRINT_L2("dsts size " << dsts.size() << ", first " << (dsts.empty() ? -1 : dsts[0].amount));
+ LOG_PRINT_L2("unused_dust_indices: " << strjoin(*unused_dust_indices, " "));
+ LOG_PRINT_L2("dsts size " << dsts.size() << ", first " << (dsts.empty() ? "-" : cryptonote::print_money(dsts[0].amount)));
LOG_PRINT_L2("adding_fee " << adding_fee << ", use_rct " << use_rct);
// if we need to spend money and don't have any left, we fail
@@ -5615,7 +5601,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// get a random unspent output and use it to pay part (or all) of the current destination (and maybe next one, etc)
// This could be more clever, but maybe at the cost of making probabilistic inferences easier
size_t idx;
- if ((dsts.empty() || dsts[0].amount == 0) && !adding_fee) {
+ if (!preferred_inputs.empty()) {
+ idx = pop_back(preferred_inputs);
+ pop_if_present(*unused_transfers_indices, idx);
+ pop_if_present(*unused_dust_indices, idx);
+ } else if ((dsts.empty() || dsts[0].amount == 0) && !adding_fee) {
// the "make rct txes 2/2" case - we pick a small value output to "clean up" the wallet too
std::vector<size_t> indices = get_only_rct(*unused_dust_indices, *unused_transfers_indices);
idx = pop_best_value(indices, tx.selected_transfers, true);
@@ -5638,10 +5628,6 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
pop_if_present(*unused_transfers_indices, idx);
pop_if_present(*unused_dust_indices, idx);
- } else if (!preferred_inputs.empty()) {
- idx = pop_back(preferred_inputs);
- pop_if_present(*unused_transfers_indices, idx);
- pop_if_present(*unused_dust_indices, idx);
} else
idx = pop_best_value(unused_transfers_indices->empty() ? *unused_dust_indices : *unused_transfers_indices, tx.selected_transfers);
@@ -5663,7 +5649,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
else
{
- while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()) < TX_SIZE_TARGET(upper_transaction_size_limit))
+ while (!dsts.empty() && dsts[0].amount <= available_amount && 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 fully pay that destination
LOG_PRINT_L2("We can fully pay " << get_account_address_as_str(m_testnet, dsts[0].is_subaddress, dsts[0].addr) <<
@@ -5675,7 +5661,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
++original_output_index;
}
- if (available_amount > 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));
@@ -5688,26 +5674,31 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// 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);
- bool try_tx;
- 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
+ bool try_tx = false;
+ // if we have preferred picks, but haven't yet used all of them, continue
+ if (preferred_inputs.empty())
{
- 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);
@@ -5749,8 +5740,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
else
{
- LOG_PRINT_L2("We made a tx, adjusting fee and saving it");
- do {
+ LOG_PRINT_L2("We made a tx, adjusting fee and saving it, we need " << print_money(needed_fee) << " and we have " << print_money(test_ptx.fee));
+ while (needed_fee > 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);
@@ -5761,7 +5752,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
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");
@@ -5943,14 +5934,15 @@ std::vector<wallet2::pending_tx> 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));
@@ -5970,7 +5962,7 @@ std::vector<wallet2::pending_tx> 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)
@@ -5983,7 +5975,7 @@ std::vector<wallet2::pending_tx> 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");