diff --git a/src/wallet/api/coins.cpp b/src/wallet/api/coins.cpp index 99c3a51f8..fe54b82cf 100644 --- a/src/wallet/api/coins.cpp +++ b/src/wallet/api/coins.cpp @@ -84,6 +84,7 @@ namespace Monero { ci->m_unlocked = m_wallet->m_wallet->is_transfer_unlocked(td); ci->m_pubKey = string_tools::pod_to_hex(td.get_public_key()); ci->m_coinbase = td.m_tx.vin.size() == 1 && td.m_tx.vin[0].type() == typeid(cryptonote::txin_gen); + ci->m_description = m_wallet->m_wallet->get_tx_note(td.m_txid); m_rows.push_back(ci); } diff --git a/src/wallet/api/coins_info.cpp b/src/wallet/api/coins_info.cpp index 06d874ddf..ccec395c4 100644 --- a/src/wallet/api/coins_info.cpp +++ b/src/wallet/api/coins_info.cpp @@ -113,6 +113,10 @@ namespace Monero { bool CoinsInfoImpl::coinbase() const { return m_coinbase; } + + string CoinsInfoImpl::description() const { + return m_description; + } } // namespace namespace Bitmonero = Monero; diff --git a/src/wallet/api/coins_info.h b/src/wallet/api/coins_info.h index c76109334..18555ed09 100644 --- a/src/wallet/api/coins_info.h +++ b/src/wallet/api/coins_info.h @@ -35,6 +35,7 @@ namespace Monero { virtual bool unlocked() const override; virtual std::string pubKey() const override; virtual bool coinbase() const override; + virtual std::string description() const override; private: uint64_t m_blockHeight; @@ -57,6 +58,7 @@ namespace Monero { bool m_unlocked; std::string m_pubKey; bool m_coinbase; + std::string m_description; friend class CoinsImpl; diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 331089c39..823f122c2 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1680,13 +1680,13 @@ PendingTransaction* WalletImpl::restoreMultisigTransaction(const string& signDat // - unconfirmed_transfer_details; // - confirmed_transfer_details) -PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector &dst_addr, const string &payment_id, optional> amount, uint32_t mixin_count, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set subaddr_indices) +PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector &dst_addr, const string &payment_id, optional> amount, uint32_t mixin_count, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set subaddr_indices, const std::set &preferred_inputs) { clearStatus(); // Pause refresh thread while creating transaction pauseRefresh(); - + cryptonote::address_parse_info info; uint32_t adjusted_priority = m_wallet->adjust_priority(static_cast(priority)); @@ -1746,6 +1746,19 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector preferred_input_list; + if (!preferred_inputs.empty()) { + for (const auto &public_key : preferred_inputs) { + crypto::key_image keyImage; + bool r = epee::string_tools::hex_to_pod(public_key, keyImage); + if (!r) { + error = true; + setStatusError(tr("failed to parse key image")); + break; + } + preferred_input_list.push_back(keyImage); + } + } if (error) { break; } @@ -1760,11 +1773,11 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vectorm_pending_tx = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, adjusted_priority, - extra, subaddr_account, subaddr_indices); + extra, subaddr_account, subaddr_indices, preferred_input_list); } else { transaction->m_pending_tx = m_wallet->create_transactions_all(0, info.address, info.is_subaddress, 1, fake_outs_count, 0 /* unlock_time */, adjusted_priority, - extra, subaddr_account, subaddr_indices); + extra, subaddr_account, subaddr_indices, preferred_input_list); } pendingTxPostProcess(transaction); @@ -1845,10 +1858,10 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector amount, uint32_t mixin_count, - PendingTransaction::Priority priority, uint32_t subaddr_account, std::set subaddr_indices) + PendingTransaction::Priority priority, uint32_t subaddr_account, std::set subaddr_indices, const std::set &preferred_inputs) { - return createTransactionMultDest(std::vector {dst_addr}, payment_id, amount ? (std::vector {*amount}) : (optional>()), mixin_count, priority, subaddr_account, subaddr_indices); + return createTransactionMultDest(std::vector {dst_addr}, payment_id, amount ? (std::vector {*amount}) : (optional>()), mixin_count, priority, subaddr_account, subaddr_indices, preferred_inputs); } PendingTransaction *WalletImpl::createTransactionSingle(const string &key_image, const string &dst_addr, diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 517f4b01e..03b5a98e9 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -164,13 +164,14 @@ public: optional> amount, uint32_t mixin_count, PendingTransaction::Priority priority = PendingTransaction::Priority_Low, uint32_t subaddr_account = 0, - std::set subaddr_indices = {}) override; + std::set subaddr_indices = {}, + const std::set &preferred_inputs = {}) override; PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id, optional amount, uint32_t mixin_count, PendingTransaction::Priority priority = PendingTransaction::Priority_Low, uint32_t subaddr_account = 0, - std::set subaddr_indices = {}) override; - + std::set subaddr_indices = {}, + const std::set &preferred_inputs = {}) override; PendingTransaction * createTransactionSingle(const std::string &key_image, const std::string &dst_addr, size_t outputs = 1, PendingTransaction::Priority priority = PendingTransaction::Priority_Low) override; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index 03988f393..6df661dc2 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -342,6 +342,7 @@ struct CoinsInfo virtual bool unlocked() const = 0; virtual std::string pubKey() const = 0; virtual bool coinbase() const = 0; + virtual std::string description() const = 0; }; struct Coins @@ -934,6 +935,7 @@ struct Wallet * \param subaddr_account subaddress account from which the input funds are taken * \param subaddr_indices set of subaddress indices to use for transfer or sweeping. if set empty, all are chosen when sweeping, and one or more are automatically chosen when transferring. after execution, returns the set of actually used indices * \param priority + * \param preferred_inputs optional set of key_image strings from preferred inputs * \return PendingTransaction object. caller is responsible to check PendingTransaction::status() * after object returned */ @@ -942,7 +944,7 @@ struct Wallet optional> amount, uint32_t mixin_count, PendingTransaction::Priority = PendingTransaction::Priority_Low, uint32_t subaddr_account = 0, - std::set subaddr_indices = {}) = 0; + std::set subaddr_indices = {}, const std::set &preferred_inputs = {}) = 0; /*! * \brief createTransaction creates transaction. if dst_addr is an integrated address, payment_id is ignored @@ -953,6 +955,7 @@ struct Wallet * \param subaddr_account subaddress account from which the input funds are taken * \param subaddr_indices set of subaddress indices to use for transfer or sweeping. if set empty, all are chosen when sweeping, and one or more are automatically chosen when transferring. after execution, returns the set of actually used indices * \param priority + * \param preferred_inputs optional set of key_image strings from preferred inputs * \return PendingTransaction object. caller is responsible to check PendingTransaction::status() * after object returned */ @@ -961,7 +964,8 @@ struct Wallet optional amount, uint32_t mixin_count, PendingTransaction::Priority = PendingTransaction::Priority_Low, uint32_t subaddr_account = 0, - std::set subaddr_indices = {}) = 0; + std::set subaddr_indices = {}, + const std::set &preferred_inputs = {}) = 0; /*! * \brief createTransactionSingle creates transaction with single input diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 093b77c0d..e586d67f7 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -218,6 +218,17 @@ namespace return reason; } + bool is_preferred_input(const std::vector& preferred_input_list, const crypto::key_image& input) { + bool res = true; + if (preferred_input_list.size() > 0) { + auto it = std::find(preferred_input_list.begin(), preferred_input_list.end(), input); + if (it == preferred_input_list.end()) { + res = false; + } + } + return res; + } + size_t get_num_outputs(const std::vector &dsts, const std::vector &transfers, const std::vector &selected_transfers) { size_t outputs = dsts.size(); @@ -10317,7 +10328,7 @@ void wallet2::transfer_selected_rct(std::vector wallet2::pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set &subaddr_indices) +std::vector wallet2::pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set &subaddr_indices, const std::vector& preferred_input_list) { std::vector picks; float current_output_relatdness = 1.0f; @@ -10328,6 +10339,10 @@ std::vector wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui for (size_t i = 0; i < m_transfers.size(); ++i) { const transfer_details& td = m_transfers[i]; + if (!is_preferred_input(preferred_input_list, td.m_key_image)) { + continue; + } + if (!is_spent(td, false) && !td.m_frozen && 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) { if (td.amount() > m_ignore_outputs_above || td.amount() < m_ignore_outputs_below) @@ -10348,6 +10363,10 @@ std::vector wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui for (size_t i = 0; i < m_transfers.size(); ++i) { const transfer_details& td = m_transfers[i]; + if (!is_preferred_input(preferred_input_list, td.m_key_image)) { + continue; + } + if (!is_spent(td, false) && !td.m_frozen && !td.m_key_image_partial && td.is_rct() && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1) { if (td.amount() > m_ignore_outputs_above || td.amount() < m_ignore_outputs_below) @@ -10359,6 +10378,10 @@ std::vector wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui for (size_t j = i + 1; j < m_transfers.size(); ++j) { const transfer_details& td2 = m_transfers[j]; + if (!is_preferred_input(preferred_input_list, td.m_key_image)) { + continue; + } + if (td2.amount() > m_ignore_outputs_above || td2.amount() < m_ignore_outputs_below) { MDEBUG("Ignoring output " << j << " of amount " << print_money(td2.amount()) << " which is outside prescribed range [" << print_money(m_ignore_outputs_below) << ", " << print_money(m_ignore_outputs_above) << "]"); @@ -10931,7 +10954,7 @@ bool wallet2::light_wallet_key_image_is_ours(const crypto::key_image& key_image, // 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, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, const unique_index_container& subtract_fee_from_outputs) +std::vector wallet2::create_transactions_2(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, const unique_index_container& subtract_fee_from_outputs, const std::vector& preferred_input_list) { //ensure device is let in NONE mode in any case hw::device &hwdev = m_account.get_device(); @@ -11139,6 +11162,10 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector* unused_transfers_indices = &unused_transfers_indices_per_subaddr[0].second; std::vector* unused_dust_indices = &unused_dust_indices_per_subaddr[0].second; - + hwdev.set_mode(hw::device::TRANSACTION_CREATE_FAKE); 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(); @@ -11705,7 +11732,7 @@ bool wallet2::sanity_check(const std::vector &ptx_vector, c return true; } -std::vector wallet2::create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices) +std::vector wallet2::create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, const std::vector& preferred_input_list) { std::vector unused_transfers_indices; std::vector unused_dust_indices; @@ -11734,6 +11761,10 @@ std::vector wallet2::create_transactions_all(uint64_t below for (size_t i = 0; i < m_transfers.size(); ++i) { const transfer_details& td = m_transfers[i]; + if (!is_preferred_input(preferred_input_list, td.m_key_image)) { + continue; + } + if (m_ignore_fractional_outputs && td.amount() < fractional_threshold) { MDEBUG("Ignoring output " << i << " of amount " << print_money(td.amount()) << " which is below threshold " << print_money(fractional_threshold)); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 9a79d06f0..df34f9abf 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1114,8 +1114,8 @@ private: bool parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const; bool load_tx(const std::string &signed_filename, std::vector &ptx, std::function accept_func = NULL); bool parse_tx_from_str(const std::string &signed_tx_st, std::vector &ptx, std::function accept_func); - std::vector create_transactions_2(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, const unique_index_container& subtract_fee_from_outputs = {}); // pass subaddr_indices by value on purpose - std::vector create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices); + std::vector create_transactions_2(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, const unique_index_container& subtract_fee_from_outputs = {}, const std::vector& preferred_input_list = {}); // pass subaddr_indices by value on purpose + std::vector create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, const std::vector& preferred_input_list = {}); std::vector create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra); std::vector create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector unused_transfers_indices, std::vector unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra); bool sanity_check(const std::vector &ptx_vector, const std::vector& dsts, const unique_index_container& subtract_fee_from_outputs = {}) const; @@ -1799,7 +1799,7 @@ private: std::vector get_unspent_amounts_vector(bool strict); uint64_t get_dynamic_base_fee_estimate(); float get_output_relatedness(const transfer_details &td0, const transfer_details &td1) const; - std::vector pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set &subaddr_indices); + std::vector pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set &subaddr_indices, const std::vector& preferred_input_list); void set_spent(size_t idx, uint64_t height); void set_unspent(size_t idx); bool is_spent(const transfer_details &td, bool strict = true) const;