From 1017a7546065adb21dba6e29783858578ce620c6 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 2 Jul 2016 09:35:25 +0100 Subject: wallet: factor transfer_rct code with transfer code The "transfer" simplewallet command is renamed to "transfer_original". "transfer_new" is renamed "transfer", "transfer_rct" is removed, and the new "transfer" now selects rct or non rct transactions based on the current block height. --- src/simplewallet/simplewallet.cpp | 14 +- src/simplewallet/simplewallet.h | 1 - src/wallet/wallet2.cpp | 293 ++++++-------------------------------- src/wallet/wallet2.h | 1 - 4 files changed, 42 insertions(+), 267 deletions(-) (limited to 'src') diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 78d135451..c8c440a04 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -102,7 +102,6 @@ typedef cryptonote::simple_wallet sw; enum TransferType { TransferOriginal, TransferNew, - TransferRingCT, }; namespace @@ -648,9 +647,8 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("incoming_transfers", boost::bind(&simple_wallet::show_incoming_transfers, this, _1), tr("incoming_transfers [available|unavailable] - Show incoming transfers, all or filtered by availability")); m_cmd_binder.set_handler("payments", boost::bind(&simple_wallet::show_payments, this, _1), tr("payments [ ... ] - Show payments for given payment ID[s]")); m_cmd_binder.set_handler("bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), tr("Show blockchain height")); - m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer, this, _1), tr("transfer [] [ ... ] [payment_id] - Transfer ,... to ,... , respectively. is the number of extra inputs to include for untraceability (from 0 to maximum available)")); - m_cmd_binder.set_handler("transfer_new", boost::bind(&simple_wallet::transfer_new, this, _1), tr("Same as transfer, but using a new transaction building algorithm")); - m_cmd_binder.set_handler("transfer_rct", boost::bind(&simple_wallet::transfer_rct, this, _1), tr("Same as transfer, but using RingCT transactions")); + m_cmd_binder.set_handler("transfer_original", boost::bind(&simple_wallet::transfer, this, _1), tr("transfer [] [ ... ] [payment_id] - Transfer ,... to ,... , respectively. is the number of extra inputs to include for untraceability (from 0 to maximum available)")); + m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer_new, this, _1), tr("Same as transfer_original, but using a new transaction building algorithm")); m_cmd_binder.set_handler("sweep_unmixable", boost::bind(&simple_wallet::sweep_unmixable, this, _1), tr("Send all unmixable outputs to yourself with mixin 0")); m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), tr("Send all unlocked balance an address")); m_cmd_binder.set_handler("set_log", boost::bind(&simple_wallet::set_log, this, _1), tr("set_log - Change current log detail level, <0-4>")); @@ -2405,9 +2403,6 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vectorcreate_transactions(dsts, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon); break; - case TransferRingCT: - ptx_vector = m_wallet->create_transactions_rct(dsts, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon); - break; } // if more than one tx necessary, prompt user to confirm @@ -2558,11 +2553,6 @@ bool simple_wallet::transfer_new(const std::vector &args_) return transfer_main(TransferNew, args_); } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::transfer_rct(const std::vector &args_) -{ - return transfer_main(TransferRingCT, args_); -} -//---------------------------------------------------------------------------------------------------- bool simple_wallet::sweep_unmixable(const std::vector &args_) { if (!try_connect_to_daemon()) diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 91ebb8e6e..1259b95b0 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -123,7 +123,6 @@ namespace cryptonote bool transfer_main(int transfer_type, const std::vector &args); bool transfer(const std::vector &args); bool transfer_new(const std::vector &args); - bool transfer_rct(const std::vector &args); bool sweep_all(const std::vector &args); bool sweep_unmixable(const std::vector &args); std::vector> split_amounts( 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 wallet2::create_transactions_2(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint64_t fee_multiplier, const std::vector extra, bool trusted_daemon) -{ - std::vector unused_transfers_indices; - std::vector unused_dust_indices; - uint64_t needed_money; - uint64_t accumulated_fee, accumulated_outputs, accumulated_change; - struct TX { - std::list selected_transfers; - std::vector dsts; - cryptonote::transaction tx; - pending_tx ptx; - size_t bytes; - - void add(const account_public_address &addr, uint64_t amount) { - std::vector::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 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::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 ptx_vector; - for (std::vector::iterator i = txes.begin(); i != txes.end(); ++i) - { - TX &tx = *i; - uint64_t tx_money = 0; - for (std::list::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::create_transactions_rct(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint64_t fee_multiplier, const std::vector 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::create_transactions_2(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint64_t fee_multiplier, const std::vector extra, bool trusted_daemon) { std::vector unused_transfers_indices; std::vector unused_dust_indices; @@ -3279,6 +3053,7 @@ std::vector wallet2::create_transactions_rct(std::vector wallet2::create_transactions_rct(std::vector wallet2::create_transactions_rct(std::vector= TX_SIZE_TARGET(upper_transaction_size_limit)); } @@ -3394,8 +3173,12 @@ std::vector wallet2::create_transactions_rct(std::vector wallet2::create_transactions_rct(std::vector= 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; } //---------------------------------------------------------------------------------------------------- diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 1c835429f..80e8f7f49 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -305,7 +305,6 @@ namespace tools void commit_tx(std::vector& ptx_vector); std::vector create_transactions(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint64_t fee_multiplier, const std::vector extra, bool trusted_daemon); std::vector create_transactions_2(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint64_t fee_multiplier, const std::vector extra, bool trusted_daemon); - std::vector create_transactions_rct(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint64_t fee_multiplier, const std::vector extra, bool trusted_daemon); std::vector create_transactions_all(const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, uint64_t fee_multiplier, const std::vector extra, bool trusted_daemon); std::vector create_unmixable_sweep_transactions(bool trusted_daemon); bool check_connection(bool *same_version = NULL); -- cgit v1.2.3