aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/wallet2.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet/wallet2.cpp')
-rw-r--r--src/wallet/wallet2.cpp293
1 files changed, 40 insertions, 253 deletions
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 3565945d4..2a873d32e 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -2974,247 +2974,6 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
ptx.dests = dsts;
}
-// Another implementation of transaction creation that is hopefully better
-// While there is anything left to pay, it goes through random outputs and tries
-// to fill the next destination/amount. If it fully fills it, it will use the
-// remainder to try to fill the next one as well.
-// The tx size if roughly estimated as a linear function of only inputs, and a
-// new tx will be created when that size goes above a given fraction of the
-// max tx size. At that point, more outputs may be added if the fee cannot be
-// satisfied.
-// If the next output in the next tx would go to the same destination (ie, we
-// cut off at a tx boundary in the middle of paying a given destination), the
-// fee will be carved out of the current input if possible, to avoid having to
-// add another output just for the fee and getting change.
-// This system allows for sending (almost) the entire balance, since it does
-// not generate spurious change in all txes, thus decreasing the instantaneous
-// usable balance.
-std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint64_t fee_multiplier, 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 needed_money;
- 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;
-
- void add(const account_public_address &addr, uint64_t amount) {
- std::vector<cryptonote::tx_destination_entry>::iterator i;
- i = std::find_if(dsts.begin(), dsts.end(), [&](const cryptonote::tx_destination_entry &d) { return !memcmp (&d.addr, &addr, sizeof(addr)); });
- if (i == dsts.end())
- dsts.push_back(tx_destination_entry(amount,addr));
- else
- i->amount += amount;
- }
- };
- std::vector<TX> txes;
- bool adding_fee; // true if new outputs go towards fee, rather than destinations
- uint64_t needed_fee, available_for_fee = 0;
- uint64_t upper_transaction_size_limit = get_upper_tranaction_size_limit();
-
- // throw if attempting a transaction with no destinations
- THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination);
-
- fee_multiplier = sanitize_fee_multiplier (fee_multiplier);
-
- // calculate total amount being sent to all destinations
- // throw if total amount overflows uint64_t
- needed_money = 0;
- BOOST_FOREACH(auto& dt, dsts)
- {
- THROW_WALLET_EXCEPTION_IF(0 == dt.amount, error::zero_destination);
- needed_money += dt.amount;
- LOG_PRINT_L2("transfer: adding " << print_money(dt.amount) << ", for a total of " << print_money (needed_money));
- THROW_WALLET_EXCEPTION_IF(needed_money < dt.amount, error::tx_sum_overflow, dsts, 0, m_testnet);
- }
-
- // throw if attempting a transaction with no money
- THROW_WALLET_EXCEPTION_IF(needed_money == 0, error::zero_destination);
-
- // 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 && !td.is_rct() && 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;
- adding_fee = false;
- needed_fee = 0;
-
- // while we have something to send
- while ((!dsts.empty() && dsts[0].amount > 0) || adding_fee) {
- TX &tx = txes.back();
-
- // if we need to spend money and don't have any left, we fail
- if (unused_dust_indices.empty() && unused_transfers_indices.empty()) {
- LOG_PRINT_L2("No more outputs to choose from");
- THROW_WALLET_EXCEPTION_IF(1, error::not_enough_money, unlocked_balance(), needed_money, accumulated_fee + needed_fee);
- }
-
- // 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 = !unused_transfers_indices.empty() ? pop_random_value(unused_transfers_indices) : pop_random_value(unused_dust_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;
-
- if (adding_fee)
- {
- LOG_PRINT_L2("We need more fee, adding it to fee");
- available_for_fee += available_amount;
- }
- else
- {
- while (!dsts.empty() && dsts[0].amount <= available_amount)
- {
- // we can fully pay that destination
- LOG_PRINT_L2("We can fully pay " << get_account_address_as_str(m_testnet, dsts[0].addr) <<
- " for " << print_money(dsts[0].amount));
- tx.add(dsts[0].addr, dsts[0].amount);
- available_amount -= dsts[0].amount;
- dsts[0].amount = 0;
- pop_index(dsts, 0);
- }
-
- if (available_amount > 0 && !dsts.empty()) {
- // we can partially fill that destination
- LOG_PRINT_L2("We can partially pay " << get_account_address_as_str(m_testnet, dsts[0].addr) <<
- " for " << print_money(available_amount) << "/" << print_money(dsts[0].amount));
- tx.add(dsts[0].addr, available_amount);
- dsts[0].amount -= available_amount;
- available_amount = 0;
- }
- }
-
- // 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
- {
- try_tx = dsts.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;
-
- 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, fee_multiplier);
- available_for_fee = test_ptx.fee + test_ptx.change_dts.amount + (!test_ptx.dust_added_to_fee ? test_ptx.dust : 0);
- LOG_PRINT_L2("Made a " << txBlob.size() << " kB tx, with " << print_money(available_for_fee) << " available for fee (" <<
- print_money(needed_fee) << " needed)");
-
- if (needed_fee > available_for_fee && dsts[0].amount > 0)
- {
- // we don't have enough for the fee, but we've only partially paid the current address,
- // so we can take the fee from the paid amount, since we'll have to make another tx anyway
- std::vector<cryptonote::tx_destination_entry>::iterator i;
- i = std::find_if(tx.dsts.begin(), tx.dsts.end(),
- [&](const cryptonote::tx_destination_entry &d) { return !memcmp (&d.addr, &dsts[0].addr, sizeof(dsts[0].addr)); });
- THROW_WALLET_EXCEPTION_IF(i == tx.dsts.end(), error::wallet_internal_error, "paid address not found in outputs");
- if (i->amount > needed_fee)
- {
- uint64_t new_paid_amount = i->amount /*+ test_ptx.fee*/ - needed_fee;
- LOG_PRINT_L2("Adjusting amount paid to " << get_account_address_as_str(m_testnet, i->addr) << " from " <<
- print_money(i->amount) << " to " << print_money(new_paid_amount) << " to accomodate " <<
- print_money(needed_fee) << " fee");
- dsts[0].amount += i->amount - new_paid_amount;
- i->amount = new_paid_amount;
- test_ptx.fee = needed_fee;
- available_for_fee = needed_fee;
- }
- }
-
- if (needed_fee > available_for_fee)
- {
- LOG_PRINT_L2("We could not make a tx, switching to fee accumulation");
-
- adding_fee = true;
- }
- else
- {
- LOG_PRINT_L2("We made a tx, adjusting fee and saving it");
- 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);
- 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;
- adding_fee = false;
- if (!dsts.empty())
- {
- LOG_PRINT_L2("We have more to pay, starting another tx");
- txes.push_back(TX());
- }
- }
- }
- }
-
- if (adding_fee)
- {
- LOG_PRINT_L1("We ran out of outputs while trying to gather final fee");
- THROW_WALLET_EXCEPTION_IF(1, error::not_enough_money, unlocked_balance(), needed_money, accumulated_fee + needed_fee);
- }
-
- 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;
-}
-
static size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs)
{
size_t size = 0;
@@ -3253,7 +3012,22 @@ static size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs)
return size;
}
-std::vector<wallet2::pending_tx> wallet2::create_transactions_rct(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint64_t fee_multiplier, const std::vector<uint8_t> extra, bool trusted_daemon)
+// Another implementation of transaction creation that is hopefully better
+// While there is anything left to pay, it goes through random outputs and tries
+// to fill the next destination/amount. If it fully fills it, it will use the
+// remainder to try to fill the next one as well.
+// The tx size if roughly estimated as a linear function of only inputs, and a
+// new tx will be created when that size goes above a given fraction of the
+// max tx size. At that point, more outputs may be added if the fee cannot be
+// satisfied.
+// If the next output in the next tx would go to the same destination (ie, we
+// cut off at a tx boundary in the middle of paying a given destination), the
+// fee will be carved out of the current input if possible, to avoid having to
+// add another output just for the fee and getting change.
+// This system allows for sending (almost) the entire balance, since it does
+// not generate spurious change in all txes, thus decreasing the instantaneous
+// usable balance.
+std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint64_t fee_multiplier, const std::vector<uint8_t> extra, bool trusted_daemon)
{
std::vector<size_t> unused_transfers_indices;
std::vector<size_t> unused_dust_indices;
@@ -3279,6 +3053,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_rct(std::vector<cr
bool adding_fee; // true if new outputs go towards fee, rather than destinations
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(3);
fee_multiplier = sanitize_fee_multiplier(fee_multiplier);
@@ -3303,9 +3078,9 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_rct(std::vector<cr
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 (!td.m_spent && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td))
{
- if (is_valid_decomposed_amount(td.amount()) || td.is_rct())
+ if ((td.is_rct()) || is_valid_decomposed_amount(td.amount()))
unused_transfers_indices.push_back(i);
else
unused_dust_indices.push_back(i);
@@ -3382,7 +3157,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_rct(std::vector<cr
}
else
{
- size_t estimated_rct_tx_size = estimate_rct_tx_size(tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 1);
+ size_t estimated_rct_tx_size;
+ if (use_rct)
+ estimated_rct_tx_size = estimate_rct_tx_size(tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 1);
+ else
+ estimated_rct_tx_size = tx.selected_transfers.size() * (fake_outs_count+1) * APPROXIMATE_INPUT_BYTES;
try_tx = dsts.empty() || (estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit));
}
@@ -3394,8 +3173,12 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_rct(std::vector<cr
LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " destinations and " <<
tx.selected_transfers.size() << " outputs");
- transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, unlock_time, needed_fee, extra,
- test_tx, test_ptx);
+ if (use_rct)
+ transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, 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,
+ 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, fee_multiplier);
available_for_fee = test_ptx.fee + test_ptx.change_dts.amount + (!test_ptx.dust_added_to_fee ? test_ptx.dust : 0);
@@ -3432,11 +3215,15 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_rct(std::vector<cr
else
{
LOG_PRINT_L2("We made a tx, adjusting fee and saving it");
- transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, unlock_time, needed_fee, extra,
- test_tx, test_ptx);
+ if (use_rct)
+ transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, 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,
+ detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
txBlob = t_serializable_object_to_blob(test_ptx.tx);
- 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");
+ 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;
@@ -3757,9 +3544,9 @@ bool wallet2::use_fork_rules(uint8_t version)
bool close_enough = res.height >= resp_t.result.earliest_height - 10; // start using the rules a bit beforehand
if (close_enough)
- LOG_PRINT_L2("Using HF1 rules");
+ LOG_PRINT_L2("Using v" << (unsigned)version << " rules");
else
- LOG_PRINT_L2("Not using HF1 rules");
+ LOG_PRINT_L2("Not using v" << (unsigned)version << " rules");
return close_enough;
}
//----------------------------------------------------------------------------------------------------