aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/wallet2.cpp
diff options
context:
space:
mode:
authormoneromooo-monero <moneromooo-monero@users.noreply.github.com>2016-04-19 21:20:27 +0100
committermoneromooo-monero <moneromooo-monero@users.noreply.github.com>2016-04-19 21:20:27 +0100
commitb0850a9beacb3d6d89003ac38d46e859c45124c6 (patch)
tree4c20f3075cf2300b085de14d11a69ce52e3b2671 /src/wallet/wallet2.cpp
parentMerge pull request #803 (diff)
downloadmonero-b0850a9beacb3d6d89003ac38d46e859c45124c6.tar.xz
wallet: add a new sweep_all command and RPC command
This sends all outputs in a wallet to a given address, alleviating the difficulty people have had trying to send all monero but being left with some small amount left.
Diffstat (limited to 'src/wallet/wallet2.cpp')
-rw-r--r--src/wallet/wallet2.cpp127
1 files changed, 127 insertions, 0 deletions
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 14da8d7a2..078ae7f0c 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -2475,6 +2475,133 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
return ptx_vector;
}
+std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, const uint64_t fee_UNUSED, const std::vector<uint8_t> extra, bool trusted_daemon)
+{
+ std::vector<size_t> unused_transfers_indices;
+ std::vector<size_t> unused_dust_indices;
+ uint64_t accumulated_fee, accumulated_outputs, accumulated_change;
+ struct TX {
+ std::list<transfer_container::iterator> selected_transfers;
+ std::vector<cryptonote::tx_destination_entry> dsts;
+ cryptonote::transaction tx;
+ pending_tx ptx;
+ size_t bytes;
+ };
+ std::vector<TX> txes;
+ uint64_t needed_fee, available_for_fee = 0;
+ uint64_t upper_transaction_size_limit = get_upper_tranaction_size_limit();
+
+ // gather all our dust and non dust outputs
+ for (size_t i = 0; i < m_transfers.size(); ++i)
+ {
+ const transfer_details& td = m_transfers[i];
+ if (!td.m_spent && is_transfer_unlocked(td))
+ {
+ if (is_valid_decomposed_amount(td.amount()))
+ unused_transfers_indices.push_back(i);
+ else
+ unused_dust_indices.push_back(i);
+ }
+ }
+ LOG_PRINT_L2("Starting with " << unused_transfers_indices.size() << " non-dust outputs and " << unused_dust_indices.size() << " dust outputs");
+
+ // start with an empty tx
+ txes.push_back(TX());
+ accumulated_fee = 0;
+ accumulated_outputs = 0;
+ accumulated_change = 0;
+ needed_fee = 0;
+
+ // while we have something to send
+ while (!unused_dust_indices.empty() || !unused_transfers_indices.empty()) {
+ TX &tx = txes.back();
+
+ // get a random unspent output and use it to pay next chunk. We try to alternate
+ // dust and non dust to ensure we never get with only dust, from which we might
+ // get a tx that can't pay for itself
+ size_t idx = unused_transfers_indices.empty() ? pop_random_value(unused_dust_indices) : unused_dust_indices.empty() ? pop_random_value(unused_transfers_indices) : ((tx.selected_transfers.size() & 1) || accumulated_outputs > FEE_PER_KB * (upper_transaction_size_limit + 1023) / 1024) ? pop_random_value(unused_dust_indices) : pop_random_value(unused_transfers_indices);
+
+ const transfer_details &td = m_transfers[idx];
+ LOG_PRINT_L2("Picking output " << idx << ", amount " << print_money(td.amount()));
+
+ // add this output to the list to spend
+ tx.selected_transfers.push_back(m_transfers.begin() + idx);
+ uint64_t available_amount = td.amount();
+ accumulated_outputs += available_amount;
+
+ // 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 = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || (tx.selected_transfers.size() * (fake_outs_count+1) * APPROXIMATE_INPUT_BYTES >= TX_SIZE_TARGET(upper_transaction_size_limit));
+
+ if (try_tx) {
+ cryptonote::transaction test_tx;
+ pending_tx test_ptx;
+
+ needed_fee = 0;
+
+ tx.dsts.push_back(tx_destination_entry(1, address));
+
+ LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " destinations and " <<
+ tx.selected_transfers.size() << " outputs");
+ transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, 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(txBlob);
+ available_for_fee = test_ptx.fee + test_ptx.dests[0].amount + test_ptx.change_dts.amount;
+ LOG_PRINT_L2("Made a " << txBlob.size() << " kB tx, with " << print_money(available_for_fee) << " available for fee (" <<
+ print_money(needed_fee) << " needed)");
+
+ THROW_WALLET_EXCEPTION_IF(needed_fee > available_for_fee, error::wallet_internal_error, "Transaction cannot pay for itself");
+
+ do {
+ LOG_PRINT_L2("We made a tx, adjusting fee and saving it");
+ tx.dsts[0].amount = available_for_fee - needed_fee;
+ transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, 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(txBlob);
+ 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");
+
+ tx.tx = test_tx;
+ tx.ptx = test_ptx;
+ tx.bytes = txBlob.size();
+ accumulated_fee += test_ptx.fee;
+ accumulated_change += test_ptx.change_dts.amount;
+ if (!unused_transfers_indices.empty() || !unused_dust_indices.empty())
+ {
+ LOG_PRINT_L2("We have more to pay, starting another tx");
+ txes.push_back(TX());
+ }
+ }
+ }
+
+ LOG_PRINT_L1("Done creating " << txes.size() << " transactions, " << print_money(accumulated_fee) <<
+ " total fee, " << print_money(accumulated_change) << " total change");
+
+ std::vector<wallet2::pending_tx> ptx_vector;
+ for (std::vector<TX>::iterator i = txes.begin(); i != txes.end(); ++i)
+ {
+ TX &tx = *i;
+ uint64_t tx_money = 0;
+ for (std::list<transfer_container::iterator>::const_iterator mi = tx.selected_transfers.begin(); mi != tx.selected_transfers.end(); ++mi)
+ tx_money += (*mi)->amount();
+ LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() <<
+ ": " << (tx.bytes+1023)/1024 << " kB, sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
+ " outputs to " << tx.dsts.size() << " destination(s), including " <<
+ print_money(tx.ptx.fee) << " fee, " << print_money(tx.ptx.change_dts.amount) << " change");
+ ptx_vector.push_back(tx.ptx);
+ }
+
+ // if we made it this far, we're OK to actually send the transactions
+ return ptx_vector;
+}
+
uint64_t wallet2::unlocked_dust_balance(const tx_dust_policy &dust_policy) const
{
uint64_t money = 0;