PATCH: coin control

This commit is contained in:
Czarek Nakamoto 2024-03-12 11:07:57 +01:00 committed by -
parent e4c4a53e29
commit 33bb95befe
12 changed files with 569 additions and 20 deletions

View file

@ -6780,7 +6780,7 @@ bool simple_wallet::transfer_main(const std::vector<std::string> &args_, bool ca
{ {
// figure out what tx will be necessary // figure out what tx will be necessary
auto ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, priority, extra, auto ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, priority, extra,
m_current_subaddress_account, subaddr_indices, subtract_fee_from_outputs); m_current_subaddress_account, subaddr_indices, {}, subtract_fee_from_outputs);
if (ptx_vector.empty()) if (ptx_vector.empty())
{ {

View file

@ -40,7 +40,9 @@ set(wallet_api_sources
address_book.cpp address_book.cpp
subaddress.cpp subaddress.cpp
subaddress_account.cpp subaddress_account.cpp
unsigned_transaction.cpp) unsigned_transaction.cpp
coins.cpp
coins_info.cpp)
set(wallet_api_headers set(wallet_api_headers
wallet2_api.h) wallet2_api.h)
@ -55,7 +57,9 @@ set(wallet_api_private_headers
address_book.h address_book.h
subaddress.h subaddress.h
subaddress_account.h subaddress_account.h
unsigned_transaction.h) unsigned_transaction.h
coins.h
coins_info.h)
monero_private_headers(wallet_api monero_private_headers(wallet_api
${wallet_api_private_headers}) ${wallet_api_private_headers})

185
src/wallet/api/coins.cpp Normal file
View file

@ -0,0 +1,185 @@
#include "coins.h"
#include "coins_info.h"
#include "wallet.h"
#include "crypto/hash.h"
#include "wallet/wallet2.h"
#include "common_defines.h"
#include <string>
#include <vector>
using namespace epee;
namespace Monero {
Coins::~Coins() = default;
CoinsImpl::CoinsImpl(WalletImpl *wallet)
: m_wallet(wallet) {}
CoinsImpl::~CoinsImpl()
{
for (auto t : m_rows)
delete t;
}
int CoinsImpl::count() const
{
boost::shared_lock<boost::shared_mutex> lock(m_rowsMutex);
int result = m_rows.size();
return result;
}
CoinsInfo *CoinsImpl::coin(int index) const
{
boost::shared_lock<boost::shared_mutex> lock(m_rowsMutex);
// sanity check
if (index < 0)
return nullptr;
auto index_ = static_cast<unsigned>(index);
return index_ < m_rows.size() ? m_rows[index_] : nullptr;
}
std::vector<CoinsInfo *> CoinsImpl::getAll() const
{
boost::shared_lock<boost::shared_mutex> lock(m_rowsMutex);
return m_rows;
}
void CoinsImpl::refresh()
{
LOG_PRINT_L2("Refreshing coins");
boost::unique_lock<boost::shared_mutex> lock(m_rowsMutex);
boost::shared_lock<boost::shared_mutex> transfers_lock(m_wallet->m_wallet->m_transfers_mutex);
// delete old outputs;
for (auto t : m_rows)
delete t;
m_rows.clear();
for (size_t i = 0; i < m_wallet->m_wallet->get_num_transfer_details(); ++i)
{
const tools::wallet2::transfer_details &td = m_wallet->m_wallet->get_transfer_details(i);
auto ci = new CoinsInfoImpl();
ci->m_blockHeight = td.m_block_height;
ci->m_hash = string_tools::pod_to_hex(td.m_txid);
ci->m_internalOutputIndex = td.m_internal_output_index;
ci->m_globalOutputIndex = td.m_global_output_index;
ci->m_spent = td.m_spent;
ci->m_frozen = td.m_frozen;
ci->m_spentHeight = td.m_spent_height;
ci->m_amount = td.m_amount;
ci->m_rct = td.m_rct;
ci->m_keyImageKnown = td.m_key_image_known;
ci->m_pkIndex = td.m_pk_index;
ci->m_subaddrIndex = td.m_subaddr_index.minor;
ci->m_subaddrAccount = td.m_subaddr_index.major;
ci->m_address = m_wallet->m_wallet->get_subaddress_as_str(td.m_subaddr_index); // todo: this is expensive, cache maybe?
ci->m_addressLabel = m_wallet->m_wallet->get_subaddress_label(td.m_subaddr_index);
ci->m_keyImage = string_tools::pod_to_hex(td.m_key_image);
ci->m_unlockTime = td.m_tx.unlock_time;
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);
}
}
void CoinsImpl::setFrozen(std::string public_key)
{
crypto::public_key pk;
if (!epee::string_tools::hex_to_pod(public_key, pk))
{
LOG_ERROR("Invalid public key: " << public_key);
return;
}
try
{
m_wallet->m_wallet->freeze(pk);
refresh();
}
catch (const std::exception& e)
{
LOG_ERROR("setFrozen: " << e.what());
}
}
void CoinsImpl::setFrozen(int index)
{
try
{
m_wallet->m_wallet->freeze(index);
refresh();
}
catch (const std::exception& e)
{
LOG_ERROR("setLabel: " << e.what());
}
}
void CoinsImpl::thaw(std::string public_key)
{
crypto::public_key pk;
if (!epee::string_tools::hex_to_pod(public_key, pk))
{
LOG_ERROR("Invalid public key: " << public_key);
return;
}
try
{
m_wallet->m_wallet->thaw(pk);
refresh();
}
catch (const std::exception& e)
{
LOG_ERROR("thaw: " << e.what());
}
}
void CoinsImpl::thaw(int index)
{
try
{
m_wallet->m_wallet->thaw(index);
refresh();
}
catch (const std::exception& e)
{
LOG_ERROR("thaw: " << e.what());
}
}
bool CoinsImpl::isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) {
return m_wallet->m_wallet->is_transfer_unlocked(unlockTime, blockHeight);
}
void CoinsImpl::setDescription(const std::string &public_key, const std::string &description)
{
crypto::public_key pk;
if (!epee::string_tools::hex_to_pod(public_key, pk))
{
LOG_ERROR("Invalid public key: " << public_key);
return;
}
try
{
const size_t index = m_wallet->m_wallet->get_transfer_details(pk);
const tools::wallet2::transfer_details &td = m_wallet->m_wallet->get_transfer_details(index);
m_wallet->m_wallet->set_tx_note(td.m_txid, description);
refresh();
}
catch (const std::exception& e)
{
LOG_ERROR("setDescription: " << e.what());
}
}
} // namespace

40
src/wallet/api/coins.h Normal file
View file

@ -0,0 +1,40 @@
#ifndef FEATHER_COINS_H
#define FEATHER_COINS_H
#include "wallet/api/wallet2_api.h"
#include "wallet/wallet2.h"
namespace Monero {
class WalletImpl;
class CoinsImpl : public Coins
{
public:
explicit CoinsImpl(WalletImpl * wallet);
~CoinsImpl() override;
int count() const override;
CoinsInfo * coin(int index) const override;
std::vector<CoinsInfo*> getAll() const override;
void refresh() override;
void setFrozen(std::string public_key) override;
void setFrozen(int index) override;
void thaw(std::string public_key) override;
void thaw(int index) override;
bool isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) override;
void setDescription(const std::string &public_key, const std::string &description) override;
private:
WalletImpl *m_wallet;
std::vector<CoinsInfo*> m_rows;
mutable boost::shared_mutex m_rowsMutex;
};
}
namespace Bitmonero = Monero;
#endif //FEATHER_COINS_H

View file

@ -0,0 +1,122 @@
#include "coins_info.h"
using namespace std;
namespace Monero {
CoinsInfo::~CoinsInfo() = default;
CoinsInfoImpl::CoinsInfoImpl()
: m_blockHeight(0)
, m_internalOutputIndex(0)
, m_globalOutputIndex(0)
, m_spent(false)
, m_frozen(false)
, m_spentHeight(0)
, m_amount(0)
, m_rct(false)
, m_keyImageKnown(false)
, m_pkIndex(0)
, m_subaddrAccount(0)
, m_subaddrIndex(0)
, m_unlockTime(0)
, m_unlocked(false)
{
}
CoinsInfoImpl::~CoinsInfoImpl() = default;
uint64_t CoinsInfoImpl::blockHeight() const
{
return m_blockHeight;
}
string CoinsInfoImpl::hash() const
{
return m_hash;
}
size_t CoinsInfoImpl::internalOutputIndex() const {
return m_internalOutputIndex;
}
uint64_t CoinsInfoImpl::globalOutputIndex() const
{
return m_globalOutputIndex;
}
bool CoinsInfoImpl::spent() const
{
return m_spent;
}
bool CoinsInfoImpl::frozen() const
{
return m_frozen;
}
uint64_t CoinsInfoImpl::spentHeight() const
{
return m_spentHeight;
}
uint64_t CoinsInfoImpl::amount() const
{
return m_amount;
}
bool CoinsInfoImpl::rct() const {
return m_rct;
}
bool CoinsInfoImpl::keyImageKnown() const {
return m_keyImageKnown;
}
size_t CoinsInfoImpl::pkIndex() const {
return m_pkIndex;
}
uint32_t CoinsInfoImpl::subaddrIndex() const {
return m_subaddrIndex;
}
uint32_t CoinsInfoImpl::subaddrAccount() const {
return m_subaddrAccount;
}
string CoinsInfoImpl::address() const {
return m_address;
}
string CoinsInfoImpl::addressLabel() const {
return m_addressLabel;
}
string CoinsInfoImpl::keyImage() const {
return m_keyImage;
}
uint64_t CoinsInfoImpl::unlockTime() const {
return m_unlockTime;
}
bool CoinsInfoImpl::unlocked() const {
return m_unlocked;
}
string CoinsInfoImpl::pubKey() const {
return m_pubKey;
}
bool CoinsInfoImpl::coinbase() const {
return m_coinbase;
}
string CoinsInfoImpl::description() const {
return m_description;
}
} // namespace
namespace Bitmonero = Monero;

View file

@ -0,0 +1,71 @@
#ifndef FEATHER_COINS_INFO_H
#define FEATHER_COINS_INFO_H
#include "wallet/api/wallet2_api.h"
#include <string>
#include <ctime>
namespace Monero {
class CoinsImpl;
class CoinsInfoImpl : public CoinsInfo
{
public:
CoinsInfoImpl();
~CoinsInfoImpl();
virtual uint64_t blockHeight() const override;
virtual std::string hash() const override;
virtual size_t internalOutputIndex() const override;
virtual uint64_t globalOutputIndex() const override;
virtual bool spent() const override;
virtual bool frozen() const override;
virtual uint64_t spentHeight() const override;
virtual uint64_t amount() const override;
virtual bool rct() const override;
virtual bool keyImageKnown() const override;
virtual size_t pkIndex() const override;
virtual uint32_t subaddrIndex() const override;
virtual uint32_t subaddrAccount() const override;
virtual std::string address() const override;
virtual std::string addressLabel() const override;
virtual std::string keyImage() const override;
virtual uint64_t unlockTime() const override;
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;
std::string m_hash;
size_t m_internalOutputIndex;
uint64_t m_globalOutputIndex;
bool m_spent;
bool m_frozen;
uint64_t m_spentHeight;
uint64_t m_amount;
bool m_rct;
bool m_keyImageKnown;
size_t m_pkIndex;
uint32_t m_subaddrIndex;
uint32_t m_subaddrAccount;
std::string m_address;
std::string m_addressLabel;
std::string m_keyImage;
uint64_t m_unlockTime;
bool m_unlocked;
std::string m_pubKey;
bool m_coinbase;
std::string m_description;
friend class CoinsImpl;
};
} // namespace
namespace Bitmonero = Monero;
#endif //FEATHER_COINS_INFO_H

View file

@ -35,6 +35,7 @@
#include "transaction_history.h" #include "transaction_history.h"
#include "address_book.h" #include "address_book.h"
#include "subaddress.h" #include "subaddress.h"
#include "coins.h"
#include "subaddress_account.h" #include "subaddress_account.h"
#include "common_defines.h" #include "common_defines.h"
#include "common/util.h" #include "common/util.h"
@ -435,6 +436,7 @@ WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds)
m_refreshEnabled = false; m_refreshEnabled = false;
m_addressBook.reset(new AddressBookImpl(this)); m_addressBook.reset(new AddressBookImpl(this));
m_subaddress.reset(new SubaddressImpl(this)); m_subaddress.reset(new SubaddressImpl(this));
m_coins.reset(new CoinsImpl(this));
m_subaddressAccount.reset(new SubaddressAccountImpl(this)); m_subaddressAccount.reset(new SubaddressAccountImpl(this));
@ -1581,7 +1583,7 @@ PendingTransaction* WalletImpl::restoreMultisigTransaction(const string& signDat
// - unconfirmed_transfer_details; // - unconfirmed_transfer_details;
// - confirmed_transfer_details) // - confirmed_transfer_details)
PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector<string> &dst_addr, const string &payment_id, optional<std::vector<uint64_t>> amount, uint32_t mixin_count, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices) PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector<string> &dst_addr, const string &payment_id, optional<std::vector<uint64_t>> amount, uint32_t mixin_count, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const std::set<std::string> &preferred_inputs)
{ {
clearStatus(); clearStatus();
@ -1647,6 +1649,19 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector<stri
} }
} }
} }
std::vector<crypto::key_image> 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) { if (error) {
break; break;
} }
@ -1661,11 +1676,11 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector<stri
if (amount) { if (amount) {
transaction->m_pending_tx = m_wallet->create_transactions_2(dsts, fake_outs_count, transaction->m_pending_tx = m_wallet->create_transactions_2(dsts, fake_outs_count,
adjusted_priority, adjusted_priority,
extra, subaddr_account, subaddr_indices); extra, subaddr_account, subaddr_indices, preferred_input_list);
} else { } else {
transaction->m_pending_tx = m_wallet->create_transactions_all(0, info.address, info.is_subaddress, 1, fake_outs_count, transaction->m_pending_tx = m_wallet->create_transactions_all(0, info.address, info.is_subaddress, 1, fake_outs_count,
adjusted_priority, adjusted_priority,
extra, subaddr_account, subaddr_indices); extra, subaddr_account, subaddr_indices, preferred_input_list);
} }
pendingTxPostProcess(transaction); pendingTxPostProcess(transaction);
@ -1746,10 +1761,10 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector<stri
} }
PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const string &payment_id, optional<uint64_t> amount, uint32_t mixin_count, PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const string &payment_id, optional<uint64_t> amount, uint32_t mixin_count,
PendingTransaction::Priority priority, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices) PendingTransaction::Priority priority, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const std::set<std::string> &preferred_inputs)
{ {
return createTransactionMultDest(std::vector<string> {dst_addr}, payment_id, amount ? (std::vector<uint64_t> {*amount}) : (optional<std::vector<uint64_t>>()), mixin_count, priority, subaddr_account, subaddr_indices); return createTransactionMultDest(std::vector<string> {dst_addr}, payment_id, amount ? (std::vector<uint64_t> {*amount}) : (optional<std::vector<uint64_t>>()), mixin_count, priority, subaddr_account, subaddr_indices, preferred_inputs);
} }
PendingTransaction *WalletImpl::createSweepUnmixableTransaction() PendingTransaction *WalletImpl::createSweepUnmixableTransaction()
@ -1871,6 +1886,11 @@ AddressBook *WalletImpl::addressBook()
return m_addressBook.get(); return m_addressBook.get();
} }
Coins *WalletImpl::coins()
{
return m_coins.get();
}
Subaddress *WalletImpl::subaddress() Subaddress *WalletImpl::subaddress()
{ {
return m_subaddress.get(); return m_subaddress.get();

View file

@ -46,6 +46,7 @@ class PendingTransactionImpl;
class UnsignedTransactionImpl; class UnsignedTransactionImpl;
class AddressBookImpl; class AddressBookImpl;
class SubaddressImpl; class SubaddressImpl;
class CoinsImpl;
class SubaddressAccountImpl; class SubaddressAccountImpl;
struct Wallet2CallbackImpl; struct Wallet2CallbackImpl;
@ -166,12 +167,14 @@ public:
optional<std::vector<uint64_t>> amount, uint32_t mixin_count, optional<std::vector<uint64_t>> amount, uint32_t mixin_count,
PendingTransaction::Priority priority = PendingTransaction::Priority_Low, PendingTransaction::Priority priority = PendingTransaction::Priority_Low,
uint32_t subaddr_account = 0, uint32_t subaddr_account = 0,
std::set<uint32_t> subaddr_indices = {}) override; std::set<uint32_t> subaddr_indices = {},
const std::set<std::string> &preferred_inputs = {}) override;
PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id, PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id,
optional<uint64_t> amount, uint32_t mixin_count, optional<uint64_t> amount, uint32_t mixin_count,
PendingTransaction::Priority priority = PendingTransaction::Priority_Low, PendingTransaction::Priority priority = PendingTransaction::Priority_Low,
uint32_t subaddr_account = 0, uint32_t subaddr_account = 0,
std::set<uint32_t> subaddr_indices = {}) override; std::set<uint32_t> subaddr_indices = {},
const std::set<std::string> &preferred_inputs = {}) override;
virtual PendingTransaction * createSweepUnmixableTransaction() override; virtual PendingTransaction * createSweepUnmixableTransaction() override;
bool submitTransaction(const std::string &fileName) override; bool submitTransaction(const std::string &fileName) override;
virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) override; virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) override;
@ -186,6 +189,7 @@ public:
PendingTransaction::Priority priority) const override; PendingTransaction::Priority priority) const override;
virtual TransactionHistory * history() override; virtual TransactionHistory * history() override;
virtual AddressBook * addressBook() override; virtual AddressBook * addressBook() override;
virtual Coins * coins() override;
virtual Subaddress * subaddress() override; virtual Subaddress * subaddress() override;
virtual SubaddressAccount * subaddressAccount() override; virtual SubaddressAccount * subaddressAccount() override;
virtual void setListener(WalletListener * l) override; virtual void setListener(WalletListener * l) override;
@ -256,6 +260,7 @@ private:
friend class TransactionHistoryImpl; friend class TransactionHistoryImpl;
friend struct Wallet2CallbackImpl; friend struct Wallet2CallbackImpl;
friend class AddressBookImpl; friend class AddressBookImpl;
friend class CoinsImpl;
friend class SubaddressImpl; friend class SubaddressImpl;
friend class SubaddressAccountImpl; friend class SubaddressAccountImpl;
@ -268,6 +273,7 @@ private:
std::unique_ptr<Wallet2CallbackImpl> m_wallet2Callback; std::unique_ptr<Wallet2CallbackImpl> m_wallet2Callback;
std::unique_ptr<AddressBookImpl> m_addressBook; std::unique_ptr<AddressBookImpl> m_addressBook;
std::unique_ptr<SubaddressImpl> m_subaddress; std::unique_ptr<SubaddressImpl> m_subaddress;
std::unique_ptr<CoinsImpl> m_coins;
std::unique_ptr<SubaddressAccountImpl> m_subaddressAccount; std::unique_ptr<SubaddressAccountImpl> m_subaddressAccount;
// multi-threaded refresh stuff // multi-threaded refresh stuff

View file

@ -261,6 +261,51 @@ struct AddressBook
virtual int lookupPaymentID(const std::string &payment_id) const = 0; virtual int lookupPaymentID(const std::string &payment_id) const = 0;
}; };
/**
* @brief The CoinsInfo - interface for displaying coins information
*/
struct CoinsInfo
{
virtual ~CoinsInfo() = 0;
virtual uint64_t blockHeight() const = 0;
virtual std::string hash() const = 0;
virtual size_t internalOutputIndex() const = 0;
virtual uint64_t globalOutputIndex() const = 0;
virtual bool spent() const = 0;
virtual bool frozen() const = 0;
virtual uint64_t spentHeight() const = 0;
virtual uint64_t amount() const = 0;
virtual bool rct() const = 0;
virtual bool keyImageKnown() const = 0;
virtual size_t pkIndex() const = 0;
virtual uint32_t subaddrIndex() const = 0;
virtual uint32_t subaddrAccount() const = 0;
virtual std::string address() const = 0;
virtual std::string addressLabel() const = 0;
virtual std::string keyImage() const = 0;
virtual uint64_t unlockTime() const = 0;
virtual bool unlocked() const = 0;
virtual std::string pubKey() const = 0;
virtual bool coinbase() const = 0;
virtual std::string description() const = 0;
};
struct Coins
{
virtual ~Coins() = 0;
virtual int count() const = 0;
virtual CoinsInfo * coin(int index) const = 0;
virtual std::vector<CoinsInfo*> getAll() const = 0;
virtual void refresh() = 0;
virtual void setFrozen(std::string public_key) = 0;
virtual void setFrozen(int index) = 0;
virtual void thaw(std::string public_key) = 0;
virtual void thaw(int index) = 0;
virtual bool isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) = 0;
virtual void setDescription(const std::string &public_key, const std::string &description) = 0;
};
struct SubaddressRow { struct SubaddressRow {
public: public:
SubaddressRow(std::size_t _rowId, const std::string &_address, const std::string &_label): SubaddressRow(std::size_t _rowId, const std::string &_address, const std::string &_label):
@ -847,7 +892,8 @@ struct Wallet
optional<std::vector<uint64_t>> amount, uint32_t mixin_count, optional<std::vector<uint64_t>> amount, uint32_t mixin_count,
PendingTransaction::Priority = PendingTransaction::Priority_Low, PendingTransaction::Priority = PendingTransaction::Priority_Low,
uint32_t subaddr_account = 0, uint32_t subaddr_account = 0,
std::set<uint32_t> subaddr_indices = {}) = 0; std::set<uint32_t> subaddr_indices = {},
const std::set<std::string> &preferred_inputs = {}) = 0;
/*! /*!
* \brief createTransaction creates transaction. if dst_addr is an integrated address, payment_id is ignored * \brief createTransaction creates transaction. if dst_addr is an integrated address, payment_id is ignored
@ -866,7 +912,8 @@ struct Wallet
optional<uint64_t> amount, uint32_t mixin_count, optional<uint64_t> amount, uint32_t mixin_count,
PendingTransaction::Priority = PendingTransaction::Priority_Low, PendingTransaction::Priority = PendingTransaction::Priority_Low,
uint32_t subaddr_account = 0, uint32_t subaddr_account = 0,
std::set<uint32_t> subaddr_indices = {}) = 0; std::set<uint32_t> subaddr_indices = {},
const std::set<std::string> &preferred_inputs = {}) = 0;
/*! /*!
* \brief createSweepUnmixableTransaction creates transaction with unmixable outputs. * \brief createSweepUnmixableTransaction creates transaction with unmixable outputs.
@ -942,6 +989,7 @@ struct Wallet
virtual TransactionHistory * history() = 0; virtual TransactionHistory * history() = 0;
virtual AddressBook * addressBook() = 0; virtual AddressBook * addressBook() = 0;
virtual Coins * coins() = 0;
virtual Subaddress * subaddress() = 0; virtual Subaddress * subaddress() = 0;
virtual SubaddressAccount * subaddressAccount() = 0; virtual SubaddressAccount * subaddressAccount() = 0;
virtual void setListener(WalletListener *) = 0; virtual void setListener(WalletListener *) = 0;

View file

@ -947,6 +947,16 @@ uint32_t get_subaddress_clamped_sum(uint32_t idx, uint32_t extra)
return idx + extra; return idx + extra;
} }
bool is_preferred_input(const std::vector<crypto::key_image>& preferred_input_list, const crypto::key_image& input) {
if (!preferred_input_list.empty()) {
auto it = std::find(preferred_input_list.begin(), preferred_input_list.end(), input);
if (it == preferred_input_list.end()) {
return false;
}
}
return true;
}
static void setup_shim(hw::wallet_shim * shim, tools::wallet2 * wallet) static void setup_shim(hw::wallet_shim * shim, tools::wallet2 * wallet)
{ {
shim->get_tx_pub_key_from_received_outs = std::bind(&tools::wallet2::get_tx_pub_key_from_received_outs, wallet, std::placeholders::_1); shim->get_tx_pub_key_from_received_outs = std::bind(&tools::wallet2::get_tx_pub_key_from_received_outs, wallet, std::placeholders::_1);
@ -2063,12 +2073,21 @@ bool wallet2::frozen(const multisig_tx_set& txs) const
return false; return false;
} }
void wallet2::freeze(const crypto::public_key &pk)
{
freeze(get_transfer_details(pk));
}
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
void wallet2::freeze(const crypto::key_image &ki) void wallet2::freeze(const crypto::key_image &ki)
{ {
freeze(get_transfer_details(ki)); freeze(get_transfer_details(ki));
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
void wallet2::thaw(const crypto::public_key &pk)
{
thaw(get_transfer_details(pk));
}
//----------------------------------------------------------------------------------------------------
void wallet2::thaw(const crypto::key_image &ki) void wallet2::thaw(const crypto::key_image &ki)
{ {
thaw(get_transfer_details(ki)); thaw(get_transfer_details(ki));
@ -2079,6 +2098,18 @@ bool wallet2::frozen(const crypto::key_image &ki) const
return frozen(get_transfer_details(ki)); return frozen(get_transfer_details(ki));
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
size_t wallet2::get_transfer_details(const crypto::public_key &pk) const
{
for (size_t idx = 0; idx < m_transfers.size(); ++idx)
{
const transfer_details &td = m_transfers[idx];
if (td.get_public_key() == pk) {
return idx;
}
}
CHECK_AND_ASSERT_THROW_MES(false, "Public key not found");
}
//----------------------------------------------------------------------------------------------------
size_t wallet2::get_transfer_details(const crypto::key_image &ki) const size_t wallet2::get_transfer_details(const crypto::key_image &ki) const
{ {
for (size_t idx = 0; idx < m_transfers.size(); ++idx) for (size_t idx = 0; idx < m_transfers.size(); ++idx)
@ -2474,6 +2505,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
uint64_t amount = tx.vout[o].amount ? tx.vout[o].amount : tx_scan_info[o].amount; uint64_t amount = tx.vout[o].amount ? tx.vout[o].amount : tx_scan_info[o].amount;
if (!pool) if (!pool)
{ {
boost::unique_lock<boost::shared_mutex> lock(m_transfers_mutex);
m_transfers.push_back(transfer_details{}); m_transfers.push_back(transfer_details{});
transfer_details& td = m_transfers.back(); transfer_details& td = m_transfers.back();
td.m_block_height = height; td.m_block_height = height;
@ -2577,6 +2609,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
uint64_t extra_amount = amount - burnt; uint64_t extra_amount = amount - burnt;
if (!pool) if (!pool)
{ {
boost::unique_lock<boost::shared_mutex> lock(m_transfers_mutex);
transfer_details &td = m_transfers[kit->second]; transfer_details &td = m_transfers[kit->second];
td.m_block_height = height; td.m_block_height = height;
td.m_internal_output_index = o; td.m_internal_output_index = o;
@ -10004,7 +10037,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
LOG_PRINT_L2("transfer_selected_rct done"); LOG_PRINT_L2("transfer_selected_rct done");
} }
std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices) std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices, const std::vector<crypto::key_image>& preferred_input_list)
{ {
std::vector<size_t> picks; std::vector<size_t> picks;
float current_output_relatdness = 1.0f; float current_output_relatdness = 1.0f;
@ -10015,6 +10048,9 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui
for (size_t i = 0; i < m_transfers.size(); ++i) for (size_t i = 0; i < m_transfers.size(); ++i)
{ {
const transfer_details& td = m_transfers[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 (!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) if (td.amount() > m_ignore_outputs_above || td.amount() < m_ignore_outputs_below)
@ -10035,6 +10071,9 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui
for (size_t i = 0; i < m_transfers.size(); ++i) for (size_t i = 0; i < m_transfers.size(); ++i)
{ {
const transfer_details& td = m_transfers[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 (!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) if (td.amount() > m_ignore_outputs_above || td.amount() < m_ignore_outputs_below)
@ -10046,6 +10085,9 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui
for (size_t j = i + 1; j < m_transfers.size(); ++j) for (size_t j = i + 1; j < m_transfers.size(); ++j)
{ {
const transfer_details& td2 = m_transfers[j]; const transfer_details& td2 = m_transfers[j];
if (!is_preferred_input(preferred_input_list, td2.m_key_image)) {
continue;
}
if (td2.amount() > m_ignore_outputs_above || td2.amount() < m_ignore_outputs_below) 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) << "]"); 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) << "]");
@ -10618,7 +10660,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 // This system allows for sending (almost) the entire balance, since it does
// not generate spurious change in all txes, thus decreasing the instantaneous // not generate spurious change in all txes, thus decreasing the instantaneous
// usable balance. // usable balance.
std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const unique_index_container& subtract_fee_from_outputs) std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const std::vector<crypto::key_image>& preferred_input_list, const unique_index_container& subtract_fee_from_outputs)
{ {
//ensure device is let in NONE mode in any case //ensure device is let in NONE mode in any case
hw::device &hwdev = m_account.get_device(); hw::device &hwdev = m_account.get_device();
@ -10826,6 +10868,9 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
for (size_t i = 0; i < m_transfers.size(); ++i) for (size_t i = 0; i < m_transfers.size(); ++i)
{ {
const transfer_details& td = m_transfers[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) if (m_ignore_fractional_outputs && td.amount() < fractional_threshold)
{ {
MDEBUG("Ignoring output " << i << " of amount " << print_money(td.amount()) << " which is below fractional threshold " << print_money(fractional_threshold)); MDEBUG("Ignoring output " << i << " of amount " << print_money(td.amount()) << " which is below fractional threshold " << print_money(fractional_threshold));
@ -10911,7 +10956,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// will get us a known fee. // will get us a known fee.
uint64_t estimated_fee = estimate_fee(use_per_byte_fee, use_rct, 2, fake_outs_count, 2, extra.size(), bulletproof, clsag, bulletproof_plus, use_view_tags, base_fee, fee_quantization_mask); uint64_t estimated_fee = estimate_fee(use_per_byte_fee, use_rct, 2, fake_outs_count, 2, extra.size(), bulletproof, clsag, bulletproof_plus, use_view_tags, base_fee, fee_quantization_mask);
total_needed_money = needed_money + (subtract_fee_from_outputs.size() ? 0 : estimated_fee); total_needed_money = needed_money + (subtract_fee_from_outputs.size() ? 0 : estimated_fee);
preferred_inputs = pick_preferred_rct_inputs(total_needed_money, subaddr_account, subaddr_indices); preferred_inputs = pick_preferred_rct_inputs(total_needed_money, subaddr_account, subaddr_indices, preferred_input_list);
if (!preferred_inputs.empty()) if (!preferred_inputs.empty())
{ {
string s; string s;
@ -11390,7 +11435,7 @@ bool wallet2::sanity_check(const std::vector<wallet2::pending_tx> &ptx_vector, c
return true; return true;
} }
std::vector<wallet2::pending_tx> 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, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices) std::vector<wallet2::pending_tx> 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, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const std::vector<crypto::key_image>& preferred_input_list)
{ {
std::vector<size_t> unused_transfers_indices; std::vector<size_t> unused_transfers_indices;
std::vector<size_t> unused_dust_indices; std::vector<size_t> unused_dust_indices;
@ -11419,6 +11464,9 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
for (size_t i = 0; i < m_transfers.size(); ++i) for (size_t i = 0; i < m_transfers.size(); ++i)
{ {
const transfer_details& td = m_transfers[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) 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)); MDEBUG("Ignoring output " << i << " of amount " << print_money(td.amount()) << " which is below threshold " << print_money(fractional_threshold));

View file

@ -1137,8 +1137,8 @@ private:
bool parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const; 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<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func = NULL); bool load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func = NULL);
bool parse_tx_from_str(const std::string &signed_tx_st, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set &)> accept_func); bool parse_tx_from_str(const std::string &signed_tx_st, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set &)> accept_func);
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const unique_index_container& subtract_fee_from_outputs = {}); // pass subaddr_indices by value on purpose std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const std::vector<crypto::key_image>& preferred_input_list = {}, const unique_index_container& subtract_fee_from_outputs = {}); // pass subaddr_indices by value on purpose
std::vector<wallet2::pending_tx> 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, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices); std::vector<wallet2::pending_tx> 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, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const std::vector<crypto::key_image>& preferred_input_list = {});
std::vector<wallet2::pending_tx> 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, uint32_t priority, const std::vector<uint8_t>& extra); std::vector<wallet2::pending_tx> 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, uint32_t priority, const std::vector<uint8_t>& extra);
std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra); std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra);
bool sanity_check(const std::vector<wallet2::pending_tx> &ptx_vector, const std::vector<cryptonote::tx_destination_entry>& dsts, const unique_index_container& subtract_fee_from_outputs = {}) const; bool sanity_check(const std::vector<wallet2::pending_tx> &ptx_vector, const std::vector<cryptonote::tx_destination_entry>& dsts, const unique_index_container& subtract_fee_from_outputs = {}) const;
@ -1473,6 +1473,7 @@ private:
uint64_t get_num_rct_outputs(); uint64_t get_num_rct_outputs();
size_t get_num_transfer_details() const { return m_transfers.size(); } size_t get_num_transfer_details() const { return m_transfers.size(); }
const transfer_details &get_transfer_details(size_t idx) const; const transfer_details &get_transfer_details(size_t idx) const;
size_t get_transfer_details(const crypto::public_key &pk) const;
uint8_t get_current_hard_fork(); uint8_t get_current_hard_fork();
void get_hard_fork_info(uint8_t version, uint64_t &earliest_height); void get_hard_fork_info(uint8_t version, uint64_t &earliest_height);
@ -1702,7 +1703,9 @@ private:
void freeze(size_t idx); void freeze(size_t idx);
void thaw(size_t idx); void thaw(size_t idx);
bool frozen(size_t idx) const; bool frozen(size_t idx) const;
void freeze(const crypto::public_key &pk);
void freeze(const crypto::key_image &ki); void freeze(const crypto::key_image &ki);
void thaw(const crypto::public_key &pk);
void thaw(const crypto::key_image &ki); void thaw(const crypto::key_image &ki);
bool frozen(const crypto::key_image &ki) const; bool frozen(const crypto::key_image &ki) const;
bool frozen(const transfer_details &td) const; bool frozen(const transfer_details &td) const;
@ -1740,6 +1743,8 @@ private:
static std::string get_default_daemon_address() { CRITICAL_REGION_LOCAL(default_daemon_address_lock); return default_daemon_address; } static std::string get_default_daemon_address() { CRITICAL_REGION_LOCAL(default_daemon_address_lock); return default_daemon_address; }
boost::shared_mutex m_transfers_mutex;
private: private:
/*! /*!
* \brief Stores wallet information to wallet file. * \brief Stores wallet information to wallet file.
@ -1798,7 +1803,7 @@ private:
std::vector<uint64_t> get_unspent_amounts_vector(bool strict); std::vector<uint64_t> get_unspent_amounts_vector(bool strict);
uint64_t get_dynamic_base_fee_estimate(); uint64_t get_dynamic_base_fee_estimate();
float get_output_relatedness(const transfer_details &td0, const transfer_details &td1) const; float get_output_relatedness(const transfer_details &td0, const transfer_details &td1) const;
std::vector<size_t> pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices); std::vector<size_t> pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices, const std::vector<crypto::key_image>& preferred_input_list);
void set_spent(size_t idx, uint64_t height); void set_spent(size_t idx, uint64_t height);
void set_unspent(size_t idx); void set_unspent(size_t idx);
bool is_spent(const transfer_details &td, bool strict = true) const; bool is_spent(const transfer_details &td, bool strict = true) const;

View file

@ -1099,7 +1099,7 @@ namespace tools
{ {
uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0); uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0);
uint32_t priority = m_wallet->adjust_priority(req.priority); uint32_t priority = m_wallet->adjust_priority(req.priority);
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, priority, extra, req.account_index, req.subaddr_indices, req.subtract_fee_from_outputs); std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, priority, extra, req.account_index, req.subaddr_indices, {}, req.subtract_fee_from_outputs);
if (ptx_vector.empty()) if (ptx_vector.empty())
{ {