aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
authormoneromooo-monero <moneromooo-monero@users.noreply.github.com>2016-09-24 08:47:47 +0100
committermoneromooo-monero <moneromooo-monero@users.noreply.github.com>2016-10-04 17:44:56 +0100
commit4e6d70808de359f47f440b55e58dfadbdb540ca3 (patch)
tree3b6a8053c9f92bff9e3f1ba12058e5226aaf0e93 /src/wallet
parentMerge pull request #1180 (diff)
downloadmonero-4e6d70808de359f47f440b55e58dfadbdb540ca3.tar.xz
wallet: better implementation of sweep_unmixable
This was still using the old transaction creation algorithm, coupled with a deterministic output selection scheme, which made it ill suited to the job, since it'd loop indefinitely in case the fee increased between the test tx and adding the fee.
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/wallet2.cpp250
-rw-r--r--src/wallet/wallet2.h3
2 files changed, 31 insertions, 222 deletions
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index e3736bc3d..61c3a5149 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -3662,6 +3662,26 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptono
{
std::vector<size_t> unused_transfers_indices;
std::vector<size_t> unused_dust_indices;
+ const bool use_rct = use_fork_rules(4, 0);
+
+ // 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 && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td))
+ {
+ if (td.is_rct() || is_valid_decomposed_amount(td.amount()))
+ unused_transfers_indices.push_back(i);
+ else
+ unused_dust_indices.push_back(i);
+ }
+ }
+
+ return create_transactions_from(address, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra, trusted_daemon);
+}
+
+std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const cryptonote::account_public_address &address, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon)
+{
uint64_t accumulated_fee, accumulated_outputs, accumulated_change;
struct TX {
std::list<size_t> selected_transfers;
@@ -3673,24 +3693,12 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptono
std::vector<TX> txes;
uint64_t needed_fee, available_for_fee = 0;
uint64_t upper_transaction_size_limit = get_upper_tranaction_size_limit();
- const bool use_rct = use_fork_rules(4, 0);
+ const bool use_rct = use_fork_rules(4, 0);
const bool use_new_fee = use_fork_rules(3, -720 * 14);
const uint64_t fee_per_kb = use_new_fee ? FEE_PER_KB : FEE_PER_KB_OLD;
const uint64_t fee_multiplier = get_fee_multiplier(priority, use_new_fee);
- // 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 && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td))
- {
- if (td.is_rct() || 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");
if (unused_dust_indices.empty() && unused_transfers_indices.empty())
@@ -3820,121 +3828,6 @@ uint64_t wallet2::unlocked_dust_balance(const tx_dust_policy &dust_policy) const
}
return money;
}
-
-template<typename T>
-void wallet2::transfer_from(const std::vector<size_t> &outs, size_t num_outputs, uint64_t unlock_time, uint64_t needed_fee, T destination_split_strategy, const tx_dust_policy& dust_policy, const std::vector<uint8_t> &extra, cryptonote::transaction& tx, pending_tx &ptx)
-{
- using namespace cryptonote;
-
- uint64_t upper_transaction_size_limit = get_upper_tranaction_size_limit();
-
- // select all dust inputs for transaction
- // throw if there are none
- uint64_t money = 0;
- std::list<size_t> selected_transfers;
-#if 1
- for (size_t n = 0; n < outs.size(); ++n)
- {
- const transfer_details& td = m_transfers[outs[n]];
- if (!td.m_spent)
- {
- selected_transfers.push_back (outs[n]);
- money += td.amount();
- if (selected_transfers.size() >= num_outputs)
- break;
- }
- }
-#else
- for (transfer_container::iterator i = m_transfers.begin(); i != m_transfers.end(); ++i)
- {
- const transfer_details& td = *i;
- if (!td.m_spent && (td.amount() < dust_policy.dust_threshold || !is_valid_decomposed_amount(td.amount())) && is_transfer_unlocked(td))
- {
- selected_transfers.push_back (i);
- money += td.amount();
- if (selected_transfers.size() >= num_outputs)
- break;
- }
- }
-#endif
-
- // we don't allow no output to self, easier, but one may want to burn the dust if = fee
- THROW_WALLET_EXCEPTION_IF(money <= needed_fee, error::not_enough_money, money, needed_fee, needed_fee);
-
- typedef cryptonote::tx_source_entry::output_entry tx_output_entry;
-
- //prepare inputs
- size_t i = 0;
- std::vector<cryptonote::tx_source_entry> sources;
- BOOST_FOREACH(size_t idx, selected_transfers)
- {
- sources.resize(sources.size()+1);
- cryptonote::tx_source_entry& src = sources.back();
- const transfer_details& td = m_transfers[idx];
- src.amount = td.amount();
- src.rct = td.is_rct();
-
- //paste real transaction to the random index
- auto it_to_insert = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const tx_output_entry& a)
- {
- return a.first >= td.m_global_output_index;
- });
- tx_output_entry real_oe;
- real_oe.first = td.m_global_output_index;
- real_oe.second.dest = rct::pk2rct(boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key);
- real_oe.second.mask = rct::commit(td.amount(), td.m_mask);
- auto interted_it = src.outputs.insert(it_to_insert, real_oe);
- src.real_out_tx_key = get_tx_pub_key_from_extra(td.m_tx);
- src.real_output = interted_it - src.outputs.begin();
- src.real_output_in_tx_index = td.m_internal_output_index;
- detail::print_source_entry(src);
- ++i;
- }
-
- cryptonote::tx_destination_entry change_dts = AUTO_VAL_INIT(change_dts);
-
- std::vector<cryptonote::tx_destination_entry> dsts;
- uint64_t money_back = money - needed_fee;
- if (dust_policy.dust_threshold > 0)
- money_back = money_back - money_back % dust_policy.dust_threshold;
- dsts.push_back(cryptonote::tx_destination_entry(money_back, m_account_public_address));
- std::vector<cryptonote::tx_destination_entry> splitted_dsts, dust;
- destination_split_strategy(dsts, change_dts, dust_policy.dust_threshold, splitted_dsts, dust);
- BOOST_FOREACH(auto& d, dust) {
- THROW_WALLET_EXCEPTION_IF(dust_policy.dust_threshold < d.amount, error::wallet_internal_error, "invalid dust value: dust = " +
- std::to_string(d.amount) + ", dust_threshold = " + std::to_string(dust_policy.dust_threshold));
- }
-
- crypto::secret_key tx_key;
- bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), sources, splitted_dsts, extra, tx, unlock_time, tx_key);
- 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);
-
- 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
- {
- CHECKED_GET_SPECIFIC_VARIANT(s_e, const txin_to_key, in, false);
- key_images += boost::to_string(in.k_image) + " ";
- return true;
- });
- THROW_WALLET_EXCEPTION_IF(!all_are_txin_to_key, error::unexpected_txin_type, tx);
-
- ptx.key_images = key_images;
- ptx.fee = money - money_back;
- ptx.dust = 0;
- ptx.tx = tx;
- ptx.change_dts = change_dts;
- ptx.selected_transfers = selected_transfers;
- ptx.tx_key = tx_key;
- ptx.dests = dsts;
- ptx.construction_data.sources = sources;
- ptx.construction_data.destinations = dsts;
- ptx.construction_data.change_dts = change_dts;
- ptx.construction_data.extra = tx.extra;
- ptx.construction_data.unlock_time = unlock_time;
- ptx.construction_data.use_rct = false;
-}
-
//----------------------------------------------------------------------------------------------------
void wallet2::get_hard_fork_info(uint8_t version, uint64_t &earliest_height)
{
@@ -4114,100 +4007,17 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bo
return std::vector<wallet2::pending_tx>();
}
- // failsafe split attempt counter
- size_t attempt_count = 0;
-
- for(attempt_count = 1; ;attempt_count++)
+ // split in "dust" and "non dust" to make it easier to select outputs
+ std::vector<size_t> unmixable_transfer_outputs, unmixable_dust_outputs;
+ for (auto n: unmixable_outputs)
{
- size_t num_tx = 0.5 + pow(1.7,attempt_count-1);
- size_t num_outputs_per_tx = (num_dust_outputs + num_tx - 1) / num_tx;
-
- std::vector<pending_tx> ptx_vector;
- try
- {
- // for each new tx
- for (size_t i=0; i<num_tx;++i)
- {
- cryptonote::transaction tx;
- pending_tx ptx;
- std::vector<uint8_t> extra;
-
- // loop until fee is met without increasing tx size to next KB boundary.
- uint64_t needed_fee = 0;
- do
- {
- transfer_from(unmixable_outputs, num_outputs_per_tx, (uint64_t)0 /* unlock_time */, 0, detail::digit_split_strategy, dust_policy, extra, tx, ptx);
- auto txBlob = t_serializable_object_to_blob(ptx.tx);
- needed_fee = calculate_fee(fee_per_kb, txBlob, 1);
-
- // reroll the tx with the actual amount minus the fee
- // if there's not enough for the fee, it'll throw
- transfer_from(unmixable_outputs, num_outputs_per_tx, (uint64_t)0 /* unlock_time */, needed_fee, detail::digit_split_strategy, dust_policy, extra, tx, ptx);
- txBlob = t_serializable_object_to_blob(ptx.tx);
- needed_fee = calculate_fee(fee_per_kb, txBlob, 1);
- } while (ptx.fee < needed_fee);
-
- ptx_vector.push_back(ptx);
-
- // mark transfers to be used as "spent"
- BOOST_FOREACH(size_t idx, ptx.selected_transfers)
- {
- set_spent(idx, 0);
- }
- }
-
- // if we made it this far, we've selected our transactions. committing them will mark them spent,
- // so this is a failsafe in case they don't go through
- // unmark pending tx transfers as spent
- for (auto & ptx : ptx_vector)
- {
- // mark transfers to be used as not spent
- BOOST_FOREACH(size_t idx2, ptx.selected_transfers)
- {
- set_unspent(idx2);
- }
- }
-
- // if we made it this far, we're OK to actually send the transactions
- return ptx_vector;
-
- }
- // only catch this here, other exceptions need to pass through to the calling function
- catch (const tools::error::tx_too_big& e)
- {
-
- // unmark pending tx transfers as spent
- for (auto & ptx : ptx_vector)
- {
- // mark transfers to be used as not spent
- BOOST_FOREACH(size_t idx2, ptx.selected_transfers)
- {
- set_unspent(idx2);
- }
- }
-
- if (attempt_count >= MAX_SPLIT_ATTEMPTS)
- {
- throw;
- }
- }
- catch (...)
- {
- // in case of some other exception, make sure any tx in queue are marked unspent again
-
- // unmark pending tx transfers as spent
- for (auto & ptx : ptx_vector)
- {
- // mark transfers to be used as not spent
- BOOST_FOREACH(size_t idx2, ptx.selected_transfers)
- {
- set_unspent(idx2);
- }
- }
-
- throw;
- }
+ if (m_transfers[n].amount() < fee_per_kb)
+ unmixable_dust_outputs.push_back(n);
+ else
+ unmixable_transfer_outputs.push_back(n);
}
+
+ return create_transactions_from(m_account_public_address, unmixable_transfer_outputs, unmixable_dust_outputs, 0 /*fake_outs_count */, 0 /* unlock_time */, 1 /*priority */, std::vector<uint8_t>(), trusted_daemon);
}
bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key) const
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 2754f4b09..a039c92d6 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -346,8 +346,6 @@ 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, bool trusted_daemon);
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_from(const std::vector<size_t> &outs, size_t num_outputs, uint64_t unlock_time, uint64_t needed_fee, T destination_split_strategy, const tx_dust_policy& dust_policy, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx);
- 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,
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,
@@ -361,6 +359,7 @@ namespace tools
std::vector<pending_tx> create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon);
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon);
std::vector<wallet2::pending_tx> create_transactions_all(const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon);
+ std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon);
std::vector<pending_tx> create_unmixable_sweep_transactions(bool trusted_daemon);
bool check_connection(bool *same_version = NULL);
void get_transfers(wallet2::transfer_container& incoming_transfers) const;