aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/banman.cpp47
-rw-r--r--src/banman.h3
-rw-r--r--src/base58.h1
-rw-r--r--src/bench/coin_selection.cpp4
-rw-r--r--src/bitcoin-cli.cpp8
-rw-r--r--src/core_io.h1
-rw-r--r--src/hash.h1
-rw-r--r--src/init.cpp1
-rw-r--r--src/netaddress.h1
-rw-r--r--src/node/transaction.h1
-rw-r--r--src/outputtype.h1
-rw-r--r--src/psbt.h1
-rw-r--r--src/qt/addressbookpage.cpp18
-rw-r--r--src/qt/bitcoin.cpp18
-rw-r--r--src/qt/optionsmodel.cpp470
-rw-r--r--src/qt/optionsmodel.h9
-rw-r--r--src/qt/test/addressbooktests.cpp54
-rw-r--r--src/qt/test/apptests.cpp2
-rw-r--r--src/qt/test/optiontests.cpp7
-rw-r--r--src/qt/test/optiontests.h2
-rw-r--r--src/qt/test/wallettests.cpp2
-rw-r--r--src/script/standard.h1
-rw-r--r--src/test/coins_tests.cpp1
-rw-r--r--src/test/fuzz/util.h1
-rw-r--r--src/util/bip32.h1
-rw-r--r--src/util/moneystr.h1
-rw-r--r--src/util/strencodings.h1
-rw-r--r--src/util/system.h1
-rw-r--r--src/validationinterface.cpp1
-rw-r--r--src/wallet/coinselection.cpp16
-rw-r--r--src/wallet/coinselection.h47
-rw-r--r--src/wallet/rpc/coins.cpp31
-rw-r--r--src/wallet/rpc/spend.cpp2
-rw-r--r--src/wallet/spend.cpp20
-rw-r--r--src/wallet/spend.h8
-rw-r--r--src/wallet/test/coinselector_tests.cpp94
-rw-r--r--src/wallet/test/wallet_tests.cpp4
37 files changed, 512 insertions, 370 deletions
diff --git a/src/banman.cpp b/src/banman.cpp
index b28e3f7f7c..2a6e0e010f 100644
--- a/src/banman.cpp
+++ b/src/banman.cpp
@@ -16,6 +16,19 @@
BanMan::BanMan(fs::path ban_file, CClientUIInterface* client_interface, int64_t default_ban_time)
: m_client_interface(client_interface), m_ban_db(std::move(ban_file)), m_default_ban_time(default_ban_time)
{
+ LoadBanlist();
+ DumpBanlist();
+}
+
+BanMan::~BanMan()
+{
+ DumpBanlist();
+}
+
+void BanMan::LoadBanlist()
+{
+ LOCK(m_cs_banned);
+
if (m_client_interface) m_client_interface->InitMessage(_("Loading banlist…").translated);
int64_t n_start = GetTimeMillis();
@@ -29,13 +42,6 @@ BanMan::BanMan(fs::path ban_file, CClientUIInterface* client_interface, int64_t
m_banned = {};
m_is_dirty = true;
}
-
- DumpBanlist();
-}
-
-BanMan::~BanMan()
-{
- DumpBanlist();
}
void BanMan::DumpBanlist()
@@ -173,23 +179,24 @@ void BanMan::GetBanned(banmap_t& banmap)
void BanMan::SweepBanned()
{
+ AssertLockHeld(m_cs_banned);
+
int64_t now = GetTime();
bool notify_ui = false;
- {
- LOCK(m_cs_banned);
- banmap_t::iterator it = m_banned.begin();
- while (it != m_banned.end()) {
- CSubNet sub_net = (*it).first;
- CBanEntry ban_entry = (*it).second;
- if (!sub_net.IsValid() || now > ban_entry.nBanUntil) {
- m_banned.erase(it++);
- m_is_dirty = true;
- notify_ui = true;
- LogPrint(BCLog::NET, "Removed banned node address/subnet: %s\n", sub_net.ToString());
- } else
- ++it;
+ banmap_t::iterator it = m_banned.begin();
+ while (it != m_banned.end()) {
+ CSubNet sub_net = (*it).first;
+ CBanEntry ban_entry = (*it).second;
+ if (!sub_net.IsValid() || now > ban_entry.nBanUntil) {
+ m_banned.erase(it++);
+ m_is_dirty = true;
+ notify_ui = true;
+ LogPrint(BCLog::NET, "Removed banned node address/subnet: %s\n", sub_net.ToString());
+ } else {
+ ++it;
}
}
+
// update UI
if (notify_ui && m_client_interface) {
m_client_interface->BannedListChanged();
diff --git a/src/banman.h b/src/banman.h
index f268fffa5a..77b043f081 100644
--- a/src/banman.h
+++ b/src/banman.h
@@ -80,11 +80,12 @@ public:
void DumpBanlist();
private:
+ void LoadBanlist() EXCLUSIVE_LOCKS_REQUIRED(!m_cs_banned);
bool BannedSetIsDirty();
//!set the "dirty" flag for the banlist
void SetBannedSetDirty(bool dirty = true);
//!clean unused entries (if bantime has expired)
- void SweepBanned();
+ void SweepBanned() EXCLUSIVE_LOCKS_REQUIRED(m_cs_banned);
RecursiveMutex m_cs_banned;
banmap_t m_banned GUARDED_BY(m_cs_banned);
diff --git a/src/base58.h b/src/base58.h
index 9ba5af73e0..d2a8d5e3bc 100644
--- a/src/base58.h
+++ b/src/base58.h
@@ -14,7 +14,6 @@
#ifndef BITCOIN_BASE58_H
#define BITCOIN_BASE58_H
-#include <attributes.h>
#include <span.h>
#include <string>
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index de8ab9807c..b2958bcc9f 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -58,7 +58,7 @@ static void CoinSelection(benchmark::Bench& bench)
// Create coins
std::vector<COutput> coins;
for (const auto& wtx : wtxs) {
- coins.emplace_back(COutPoint(wtx->GetHash(), 0), wtx->tx->vout.at(0), /*depth=*/6 * 24, GetTxSpendSize(wallet, *wtx, 0), /*spendable=*/true, /*solvable=*/true, /*safe=*/true, wtx->GetTxTime(), /*from_me=*/true);
+ coins.emplace_back(COutPoint(wtx->GetHash(), 0), wtx->tx->vout.at(0), /*depth=*/6 * 24, GetTxSpendSize(wallet, *wtx, 0), /*spendable=*/true, /*solvable=*/true, /*safe=*/true, wtx->GetTxTime(), /*from_me=*/true, /*fees=*/ 0);
}
const CoinEligibilityFilter filter_standard(1, 6, 0);
@@ -88,7 +88,7 @@ static void add_coin(const CAmount& nValue, int nInput, std::vector<OutputGroup>
CMutableTransaction tx;
tx.vout.resize(nInput + 1);
tx.vout[nInput].nValue = nValue;
- COutput output(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 0, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ true);
+ COutput output(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 0, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ true, /*fees=*/ 0);
set.emplace_back();
set.back().Insert(output, /*ancestors=*/ 0, /*descendants=*/ 0, /*positive_only=*/ false);
}
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 88cbab1d11..b9e5a81f8d 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -412,8 +412,8 @@ private:
bool is_addr_relay_enabled;
bool is_bip152_hb_from;
bool is_bip152_hb_to;
- bool is_block_relay;
bool is_outbound;
+ bool is_tx_relay;
bool operator<(const Peer& rhs) const { return std::tie(is_outbound, min_ping) < std::tie(rhs.is_outbound, rhs.min_ping); }
};
std::vector<Peer> m_peers;
@@ -477,7 +477,7 @@ public:
const int8_t network_id{NetworkStringToId(network)};
if (network_id == UNKNOWN_NETWORK) continue;
const bool is_outbound{!peer["inbound"].get_bool()};
- const bool is_block_relay{!peer["relaytxes"].get_bool()};
+ const bool is_tx_relay{peer["relaytxes"].isNull() ? true : peer["relaytxes"].get_bool()};
const std::string conn_type{peer["connection_type"].get_str()};
++m_counts.at(is_outbound).at(network_id); // in/out by network
++m_counts.at(is_outbound).at(NETWORKS.size()); // in/out overall
@@ -505,7 +505,7 @@ public:
const bool is_addr_relay_enabled{peer["addr_relay_enabled"].isNull() ? false : peer["addr_relay_enabled"].get_bool()};
const bool is_bip152_hb_from{peer["bip152_hb_from"].get_bool()};
const bool is_bip152_hb_to{peer["bip152_hb_to"].get_bool()};
- m_peers.push_back({addr, sub_version, conn_type, network, age, min_ping, ping, addr_processed, addr_rate_limited, last_blck, last_recv, last_send, last_trxn, peer_id, mapped_as, version, is_addr_relay_enabled, is_bip152_hb_from, is_bip152_hb_to, is_block_relay, is_outbound});
+ m_peers.push_back({addr, sub_version, conn_type, network, age, min_ping, ping, addr_processed, addr_rate_limited, last_blck, last_recv, last_send, last_trxn, peer_id, mapped_as, version, is_addr_relay_enabled, is_bip152_hb_from, is_bip152_hb_to, is_outbound, is_tx_relay});
m_max_addr_length = std::max(addr.length() + 1, m_max_addr_length);
m_max_addr_processed_length = std::max(ToString(addr_processed).length(), m_max_addr_processed_length);
m_max_addr_rate_limited_length = std::max(ToString(addr_rate_limited).length(), m_max_addr_rate_limited_length);
@@ -538,7 +538,7 @@ public:
PingTimeToString(peer.ping),
peer.last_send ? ToString(time_now - peer.last_send) : "",
peer.last_recv ? ToString(time_now - peer.last_recv) : "",
- peer.last_trxn ? ToString((time_now - peer.last_trxn) / 60) : peer.is_block_relay ? "*" : "",
+ peer.last_trxn ? ToString((time_now - peer.last_trxn) / 60) : peer.is_tx_relay ? "" : "*",
peer.last_blck ? ToString((time_now - peer.last_blck) / 60) : "",
strprintf("%s%s", peer.is_bip152_hb_to ? "." : " ", peer.is_bip152_hb_from ? "*" : " "),
m_max_addr_processed_length, // variable spacing
diff --git a/src/core_io.h b/src/core_io.h
index aa1381c374..c91c8199d8 100644
--- a/src/core_io.h
+++ b/src/core_io.h
@@ -6,7 +6,6 @@
#define BITCOIN_CORE_IO_H
#include <consensus/amount.h>
-#include <attributes.h>
#include <string>
#include <vector>
diff --git a/src/hash.h b/src/hash.h
index 9f582842c1..0ccef2105f 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -6,7 +6,6 @@
#ifndef BITCOIN_HASH_H
#define BITCOIN_HASH_H
-#include <attributes.h>
#include <crypto/common.h>
#include <crypto/ripemd160.h>
#include <crypto/sha256.h>
diff --git a/src/init.cpp b/src/init.cpp
index b1fe915189..e436d5ea8e 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -86,7 +86,6 @@
#include <vector>
#ifndef WIN32
-#include <attributes.h>
#include <cerrno>
#include <signal.h>
#include <sys/stat.h>
diff --git a/src/netaddress.h b/src/netaddress.h
index 77e6171054..47ba045334 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -9,7 +9,6 @@
#include <config/bitcoin-config.h>
#endif
-#include <attributes.h>
#include <compat.h>
#include <crypto/siphash.h>
#include <prevector.h>
diff --git a/src/node/transaction.h b/src/node/transaction.h
index b7cf225636..0604754a46 100644
--- a/src/node/transaction.h
+++ b/src/node/transaction.h
@@ -5,7 +5,6 @@
#ifndef BITCOIN_NODE_TRANSACTION_H
#define BITCOIN_NODE_TRANSACTION_H
-#include <attributes.h>
#include <policy/feerate.h>
#include <primitives/transaction.h>
#include <util/error.h>
diff --git a/src/outputtype.h b/src/outputtype.h
index 66fe489bb0..6b4e695760 100644
--- a/src/outputtype.h
+++ b/src/outputtype.h
@@ -6,7 +6,6 @@
#ifndef BITCOIN_OUTPUTTYPE_H
#define BITCOIN_OUTPUTTYPE_H
-#include <attributes.h>
#include <script/signingprovider.h>
#include <script/standard.h>
diff --git a/src/psbt.h b/src/psbt.h
index 8a9cbd33d2..8fda889bb4 100644
--- a/src/psbt.h
+++ b/src/psbt.h
@@ -5,7 +5,6 @@
#ifndef BITCOIN_PSBT_H
#define BITCOIN_PSBT_H
-#include <attributes.h>
#include <node/transaction.h>
#include <policy/feerate.h>
#include <primitives/transaction.h>
diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp
index d59a4345f3..a82bd5f73e 100644
--- a/src/qt/addressbookpage.cpp
+++ b/src/qt/addressbookpage.cpp
@@ -19,6 +19,11 @@
#include <QMenu>
#include <QMessageBox>
#include <QSortFilterProxyModel>
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+#include <QRegularExpression>
+#else
+#include <QRegExp>
+#endif
class AddressBookSortFilterProxyModel final : public QSortFilterProxyModel
{
@@ -46,12 +51,13 @@ protected:
auto address = model->index(row, AddressTableModel::Address, parent);
- if (filterRegExp().indexIn(model->data(address).toString()) < 0 &&
- filterRegExp().indexIn(model->data(label).toString()) < 0) {
- return false;
- }
-
- return true;
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+ const auto pattern = filterRegularExpression();
+#else
+ const auto pattern = filterRegExp();
+#endif
+ return (model->data(address).toString().contains(pattern) ||
+ model->data(label).toString().contains(pattern));
}
};
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 8cac28400f..53fbac0106 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -261,7 +261,7 @@ void BitcoinApplication::createPaymentServer()
void BitcoinApplication::createOptionsModel(bool resetSettings)
{
- optionsModel = new OptionsModel(this, resetSettings);
+ optionsModel = new OptionsModel(node(), this, resetSettings);
}
void BitcoinApplication::createWindow(const NetworkStyle *networkStyle)
@@ -292,7 +292,6 @@ void BitcoinApplication::createNode(interfaces::Init& init)
{
assert(!m_node);
m_node = init.makeNode();
- if (optionsModel) optionsModel->setNode(*m_node);
if (m_splash) m_splash->setNode(*m_node);
}
@@ -308,7 +307,9 @@ void BitcoinApplication::startThread()
/* communication to and from thread */
connect(&m_executor.value(), &InitExecutor::initializeResult, this, &BitcoinApplication::initializeResult);
- connect(&m_executor.value(), &InitExecutor::shutdownResult, this, &QCoreApplication::quit);
+ connect(&m_executor.value(), &InitExecutor::shutdownResult, this, [] {
+ QCoreApplication::exit(0);
+ });
connect(&m_executor.value(), &InitExecutor::runawayException, this, &BitcoinApplication::handleRunawayException);
connect(this, &BitcoinApplication::requestedInitialize, &m_executor.value(), &InitExecutor::initialize);
connect(this, &BitcoinApplication::requestedShutdown, &m_executor.value(), &InitExecutor::shutdown);
@@ -633,6 +634,12 @@ int GuiMain(int argc, char* argv[])
// Allow parameter interaction before we create the options model
app.parameterSetup();
GUIUtil::LogQtInfo();
+
+ if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false))
+ app.createSplashScreen(networkStyle.data());
+
+ app.createNode(*init);
+
// Load GUI settings from QSettings
app.createOptionsModel(gArgs.GetBoolArg("-resetguisettings", false));
@@ -641,11 +648,6 @@ int GuiMain(int argc, char* argv[])
app.InitPruneSetting(prune_MiB);
}
- if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false))
- app.createSplashScreen(networkStyle.data());
-
- app.createNode(*init);
-
int rv = EXIT_SUCCESS;
try
{
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index 40b9ed5483..612d3009c1 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -30,8 +30,8 @@ const char *DEFAULT_GUI_PROXY_HOST = "127.0.0.1";
static const QString GetDefaultProxyAddress();
-OptionsModel::OptionsModel(QObject *parent, bool resetSettings) :
- QAbstractListModel(parent)
+OptionsModel::OptionsModel(interfaces::Node& node, QObject *parent, bool resetSettings) :
+ QAbstractListModel(parent), m_node{node}
{
Init(resetSettings);
}
@@ -335,260 +335,272 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const
{
if(role == Qt::EditRole)
{
- QSettings settings;
- switch(index.row())
- {
- case StartAtStartup:
- return GUIUtil::GetStartOnSystemStartup();
- case ShowTrayIcon:
- return m_show_tray_icon;
- case MinimizeToTray:
- return fMinimizeToTray;
- case MapPortUPnP:
+ return getOption(OptionID(index.row()));
+ }
+ return QVariant();
+}
+
+// write QSettings values
+bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, int role)
+{
+ bool successful = true; /* set to false on parse error */
+ if(role == Qt::EditRole)
+ {
+ successful = setOption(OptionID(index.row()), value);
+ }
+
+ Q_EMIT dataChanged(index, index);
+
+ return successful;
+}
+
+QVariant OptionsModel::getOption(OptionID option) const
+{
+ QSettings settings;
+ switch (option) {
+ case StartAtStartup:
+ return GUIUtil::GetStartOnSystemStartup();
+ case ShowTrayIcon:
+ return m_show_tray_icon;
+ case MinimizeToTray:
+ return fMinimizeToTray;
+ case MapPortUPnP:
#ifdef USE_UPNP
- return settings.value("fUseUPnP");
+ return settings.value("fUseUPnP");
#else
- return false;
+ return false;
#endif // USE_UPNP
- case MapPortNatpmp:
+ case MapPortNatpmp:
#ifdef USE_NATPMP
- return settings.value("fUseNatpmp");
+ return settings.value("fUseNatpmp");
#else
- return false;
+ return false;
#endif // USE_NATPMP
- case MinimizeOnClose:
- return fMinimizeOnClose;
-
- // default proxy
- case ProxyUse:
- return settings.value("fUseProxy", false);
- case ProxyIP:
- return GetProxySetting(settings, "addrProxy").ip;
- case ProxyPort:
- return GetProxySetting(settings, "addrProxy").port;
-
- // separate Tor proxy
- case ProxyUseTor:
- return settings.value("fUseSeparateProxyTor", false);
- case ProxyIPTor:
- return GetProxySetting(settings, "addrSeparateProxyTor").ip;
- case ProxyPortTor:
- return GetProxySetting(settings, "addrSeparateProxyTor").port;
+ case MinimizeOnClose:
+ return fMinimizeOnClose;
+
+ // default proxy
+ case ProxyUse:
+ return settings.value("fUseProxy", false);
+ case ProxyIP:
+ return GetProxySetting(settings, "addrProxy").ip;
+ case ProxyPort:
+ return GetProxySetting(settings, "addrProxy").port;
+
+ // separate Tor proxy
+ case ProxyUseTor:
+ return settings.value("fUseSeparateProxyTor", false);
+ case ProxyIPTor:
+ return GetProxySetting(settings, "addrSeparateProxyTor").ip;
+ case ProxyPortTor:
+ return GetProxySetting(settings, "addrSeparateProxyTor").port;
#ifdef ENABLE_WALLET
- case SpendZeroConfChange:
- return settings.value("bSpendZeroConfChange");
- case ExternalSignerPath:
- return settings.value("external_signer_path");
- case SubFeeFromAmount:
- return m_sub_fee_from_amount;
+ case SpendZeroConfChange:
+ return settings.value("bSpendZeroConfChange");
+ case ExternalSignerPath:
+ return settings.value("external_signer_path");
+ case SubFeeFromAmount:
+ return m_sub_fee_from_amount;
#endif
- case DisplayUnit:
- return QVariant::fromValue(m_display_bitcoin_unit);
- case ThirdPartyTxUrls:
- return strThirdPartyTxUrls;
- case Language:
- return settings.value("language");
- case UseEmbeddedMonospacedFont:
- return m_use_embedded_monospaced_font;
- case CoinControlFeatures:
- return fCoinControlFeatures;
- case EnablePSBTControls:
- return settings.value("enable_psbt_controls");
- case Prune:
- return settings.value("bPrune");
- case PruneSize:
- return settings.value("nPruneSize");
- case DatabaseCache:
- return settings.value("nDatabaseCache");
- case ThreadsScriptVerif:
- return settings.value("nThreadsScriptVerif");
- case Listen:
- return settings.value("fListen");
- case Server:
- return settings.value("server");
- default:
- return QVariant();
- }
+ case DisplayUnit:
+ return QVariant::fromValue(m_display_bitcoin_unit);
+ case ThirdPartyTxUrls:
+ return strThirdPartyTxUrls;
+ case Language:
+ return settings.value("language");
+ case UseEmbeddedMonospacedFont:
+ return m_use_embedded_monospaced_font;
+ case CoinControlFeatures:
+ return fCoinControlFeatures;
+ case EnablePSBTControls:
+ return settings.value("enable_psbt_controls");
+ case Prune:
+ return settings.value("bPrune");
+ case PruneSize:
+ return settings.value("nPruneSize");
+ case DatabaseCache:
+ return settings.value("nDatabaseCache");
+ case ThreadsScriptVerif:
+ return settings.value("nThreadsScriptVerif");
+ case Listen:
+ return settings.value("fListen");
+ case Server:
+ return settings.value("server");
+ default:
+ return QVariant();
}
- return QVariant();
}
-// write QSettings values
-bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, int role)
+bool OptionsModel::setOption(OptionID option, const QVariant& value)
{
bool successful = true; /* set to false on parse error */
- if(role == Qt::EditRole)
- {
- QSettings settings;
- switch(index.row())
- {
- case StartAtStartup:
- successful = GUIUtil::SetStartOnSystemStartup(value.toBool());
- break;
- case ShowTrayIcon:
- m_show_tray_icon = value.toBool();
- settings.setValue("fHideTrayIcon", !m_show_tray_icon);
- Q_EMIT showTrayIconChanged(m_show_tray_icon);
- break;
- case MinimizeToTray:
- fMinimizeToTray = value.toBool();
- settings.setValue("fMinimizeToTray", fMinimizeToTray);
- break;
- case MapPortUPnP: // core option - can be changed on-the-fly
- settings.setValue("fUseUPnP", value.toBool());
- break;
- case MapPortNatpmp: // core option - can be changed on-the-fly
- settings.setValue("fUseNatpmp", value.toBool());
- break;
- case MinimizeOnClose:
- fMinimizeOnClose = value.toBool();
- settings.setValue("fMinimizeOnClose", fMinimizeOnClose);
- break;
-
- // default proxy
- case ProxyUse:
- if (settings.value("fUseProxy") != value) {
- settings.setValue("fUseProxy", value.toBool());
- setRestartRequired(true);
- }
- break;
- case ProxyIP: {
- auto ip_port = GetProxySetting(settings, "addrProxy");
- if (!ip_port.is_set || ip_port.ip != value.toString()) {
- ip_port.ip = value.toString();
- SetProxySetting(settings, "addrProxy", ip_port);
- setRestartRequired(true);
- }
- }
+ QSettings settings;
+
+ switch (option) {
+ case StartAtStartup:
+ successful = GUIUtil::SetStartOnSystemStartup(value.toBool());
break;
- case ProxyPort: {
- auto ip_port = GetProxySetting(settings, "addrProxy");
- if (!ip_port.is_set || ip_port.port != value.toString()) {
- ip_port.port = value.toString();
- SetProxySetting(settings, "addrProxy", ip_port);
- setRestartRequired(true);
- }
- }
+ case ShowTrayIcon:
+ m_show_tray_icon = value.toBool();
+ settings.setValue("fHideTrayIcon", !m_show_tray_icon);
+ Q_EMIT showTrayIconChanged(m_show_tray_icon);
+ break;
+ case MinimizeToTray:
+ fMinimizeToTray = value.toBool();
+ settings.setValue("fMinimizeToTray", fMinimizeToTray);
+ break;
+ case MapPortUPnP: // core option - can be changed on-the-fly
+ settings.setValue("fUseUPnP", value.toBool());
+ break;
+ case MapPortNatpmp: // core option - can be changed on-the-fly
+ settings.setValue("fUseNatpmp", value.toBool());
+ break;
+ case MinimizeOnClose:
+ fMinimizeOnClose = value.toBool();
+ settings.setValue("fMinimizeOnClose", fMinimizeOnClose);
break;
- // separate Tor proxy
- case ProxyUseTor:
- if (settings.value("fUseSeparateProxyTor") != value) {
- settings.setValue("fUseSeparateProxyTor", value.toBool());
- setRestartRequired(true);
- }
- break;
- case ProxyIPTor: {
- auto ip_port = GetProxySetting(settings, "addrSeparateProxyTor");
- if (!ip_port.is_set || ip_port.ip != value.toString()) {
- ip_port.ip = value.toString();
- SetProxySetting(settings, "addrSeparateProxyTor", ip_port);
- setRestartRequired(true);
- }
+ // default proxy
+ case ProxyUse:
+ if (settings.value("fUseProxy") != value) {
+ settings.setValue("fUseProxy", value.toBool());
+ setRestartRequired(true);
}
break;
- case ProxyPortTor: {
- auto ip_port = GetProxySetting(settings, "addrSeparateProxyTor");
- if (!ip_port.is_set || ip_port.port != value.toString()) {
- ip_port.port = value.toString();
- SetProxySetting(settings, "addrSeparateProxyTor", ip_port);
- setRestartRequired(true);
- }
+ case ProxyIP: {
+ auto ip_port = GetProxySetting(settings, "addrProxy");
+ if (!ip_port.is_set || ip_port.ip != value.toString()) {
+ ip_port.ip = value.toString();
+ SetProxySetting(settings, "addrProxy", ip_port);
+ setRestartRequired(true);
+ }
+ }
+ break;
+ case ProxyPort: {
+ auto ip_port = GetProxySetting(settings, "addrProxy");
+ if (!ip_port.is_set || ip_port.port != value.toString()) {
+ ip_port.port = value.toString();
+ SetProxySetting(settings, "addrProxy", ip_port);
+ setRestartRequired(true);
+ }
+ }
+ break;
+
+ // separate Tor proxy
+ case ProxyUseTor:
+ if (settings.value("fUseSeparateProxyTor") != value) {
+ settings.setValue("fUseSeparateProxyTor", value.toBool());
+ setRestartRequired(true);
}
break;
+ case ProxyIPTor: {
+ auto ip_port = GetProxySetting(settings, "addrSeparateProxyTor");
+ if (!ip_port.is_set || ip_port.ip != value.toString()) {
+ ip_port.ip = value.toString();
+ SetProxySetting(settings, "addrSeparateProxyTor", ip_port);
+ setRestartRequired(true);
+ }
+ }
+ break;
+ case ProxyPortTor: {
+ auto ip_port = GetProxySetting(settings, "addrSeparateProxyTor");
+ if (!ip_port.is_set || ip_port.port != value.toString()) {
+ ip_port.port = value.toString();
+ SetProxySetting(settings, "addrSeparateProxyTor", ip_port);
+ setRestartRequired(true);
+ }
+ }
+ break;
#ifdef ENABLE_WALLET
- case SpendZeroConfChange:
- if (settings.value("bSpendZeroConfChange") != value) {
- settings.setValue("bSpendZeroConfChange", value);
- setRestartRequired(true);
- }
- break;
- case ExternalSignerPath:
- if (settings.value("external_signer_path") != value.toString()) {
- settings.setValue("external_signer_path", value.toString());
- setRestartRequired(true);
- }
- break;
- case SubFeeFromAmount:
- m_sub_fee_from_amount = value.toBool();
- settings.setValue("SubFeeFromAmount", m_sub_fee_from_amount);
- break;
+ case SpendZeroConfChange:
+ if (settings.value("bSpendZeroConfChange") != value) {
+ settings.setValue("bSpendZeroConfChange", value);
+ setRestartRequired(true);
+ }
+ break;
+ case ExternalSignerPath:
+ if (settings.value("external_signer_path") != value.toString()) {
+ settings.setValue("external_signer_path", value.toString());
+ setRestartRequired(true);
+ }
+ break;
+ case SubFeeFromAmount:
+ m_sub_fee_from_amount = value.toBool();
+ settings.setValue("SubFeeFromAmount", m_sub_fee_from_amount);
+ break;
#endif
- case DisplayUnit:
- setDisplayUnit(value);
- break;
- case ThirdPartyTxUrls:
- if (strThirdPartyTxUrls != value.toString()) {
- strThirdPartyTxUrls = value.toString();
- settings.setValue("strThirdPartyTxUrls", strThirdPartyTxUrls);
- setRestartRequired(true);
- }
- break;
- case Language:
- if (settings.value("language") != value) {
- settings.setValue("language", value);
- setRestartRequired(true);
- }
- break;
- case UseEmbeddedMonospacedFont:
- m_use_embedded_monospaced_font = value.toBool();
- settings.setValue("UseEmbeddedMonospacedFont", m_use_embedded_monospaced_font);
- Q_EMIT useEmbeddedMonospacedFontChanged(m_use_embedded_monospaced_font);
- break;
- case CoinControlFeatures:
- fCoinControlFeatures = value.toBool();
- settings.setValue("fCoinControlFeatures", fCoinControlFeatures);
- Q_EMIT coinControlFeaturesChanged(fCoinControlFeatures);
- break;
- case EnablePSBTControls:
- m_enable_psbt_controls = value.toBool();
- settings.setValue("enable_psbt_controls", m_enable_psbt_controls);
- break;
- case Prune:
- if (settings.value("bPrune") != value) {
- settings.setValue("bPrune", value);
- setRestartRequired(true);
- }
- break;
- case PruneSize:
- if (settings.value("nPruneSize") != value) {
- settings.setValue("nPruneSize", value);
- setRestartRequired(true);
- }
- break;
- case DatabaseCache:
- if (settings.value("nDatabaseCache") != value) {
- settings.setValue("nDatabaseCache", value);
- setRestartRequired(true);
- }
- break;
- case ThreadsScriptVerif:
- if (settings.value("nThreadsScriptVerif") != value) {
- settings.setValue("nThreadsScriptVerif", value);
- setRestartRequired(true);
- }
- break;
- case Listen:
- if (settings.value("fListen") != value) {
- settings.setValue("fListen", value);
- setRestartRequired(true);
- }
- break;
- case Server:
- if (settings.value("server") != value) {
- settings.setValue("server", value);
- setRestartRequired(true);
- }
- break;
- default:
- break;
+ case DisplayUnit:
+ setDisplayUnit(value);
+ break;
+ case ThirdPartyTxUrls:
+ if (strThirdPartyTxUrls != value.toString()) {
+ strThirdPartyTxUrls = value.toString();
+ settings.setValue("strThirdPartyTxUrls", strThirdPartyTxUrls);
+ setRestartRequired(true);
}
+ break;
+ case Language:
+ if (settings.value("language") != value) {
+ settings.setValue("language", value);
+ setRestartRequired(true);
+ }
+ break;
+ case UseEmbeddedMonospacedFont:
+ m_use_embedded_monospaced_font = value.toBool();
+ settings.setValue("UseEmbeddedMonospacedFont", m_use_embedded_monospaced_font);
+ Q_EMIT useEmbeddedMonospacedFontChanged(m_use_embedded_monospaced_font);
+ break;
+ case CoinControlFeatures:
+ fCoinControlFeatures = value.toBool();
+ settings.setValue("fCoinControlFeatures", fCoinControlFeatures);
+ Q_EMIT coinControlFeaturesChanged(fCoinControlFeatures);
+ break;
+ case EnablePSBTControls:
+ m_enable_psbt_controls = value.toBool();
+ settings.setValue("enable_psbt_controls", m_enable_psbt_controls);
+ break;
+ case Prune:
+ if (settings.value("bPrune") != value) {
+ settings.setValue("bPrune", value);
+ setRestartRequired(true);
+ }
+ break;
+ case PruneSize:
+ if (settings.value("nPruneSize") != value) {
+ settings.setValue("nPruneSize", value);
+ setRestartRequired(true);
+ }
+ break;
+ case DatabaseCache:
+ if (settings.value("nDatabaseCache") != value) {
+ settings.setValue("nDatabaseCache", value);
+ setRestartRequired(true);
+ }
+ break;
+ case ThreadsScriptVerif:
+ if (settings.value("nThreadsScriptVerif") != value) {
+ settings.setValue("nThreadsScriptVerif", value);
+ setRestartRequired(true);
+ }
+ break;
+ case Listen:
+ if (settings.value("fListen") != value) {
+ settings.setValue("fListen", value);
+ setRestartRequired(true);
+ }
+ break;
+ case Server:
+ if (settings.value("server") != value) {
+ settings.setValue("server", value);
+ setRestartRequired(true);
+ }
+ break;
+ default:
+ break;
}
- Q_EMIT dataChanged(index, index);
-
return successful;
}
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index 510ebb5cfd..92f80ecf21 100644
--- a/src/qt/optionsmodel.h
+++ b/src/qt/optionsmodel.h
@@ -41,7 +41,7 @@ class OptionsModel : public QAbstractListModel
Q_OBJECT
public:
- explicit OptionsModel(QObject *parent = nullptr, bool resetSettings = false);
+ explicit OptionsModel(interfaces::Node& node, QObject *parent = nullptr, bool resetSettings = false);
enum OptionID {
StartAtStartup, // bool
@@ -80,6 +80,8 @@ public:
int rowCount(const QModelIndex & parent = QModelIndex()) const override;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole) override;
+ QVariant getOption(OptionID option) const;
+ bool setOption(OptionID option, const QVariant& value);
/** Updates current unit in memory, settings and emits displayUnitChanged(new_unit) signal */
void setDisplayUnit(const QVariant& new_unit);
@@ -103,11 +105,10 @@ public:
void setRestartRequired(bool fRequired);
bool isRestartRequired() const;
- interfaces::Node& node() const { assert(m_node); return *m_node; }
- void setNode(interfaces::Node& node) { assert(!m_node); m_node = &node; }
+ interfaces::Node& node() const { return m_node; }
private:
- interfaces::Node* m_node = nullptr;
+ interfaces::Node& m_node;
/* Qt-only settings */
bool m_show_tray_icon;
bool fMinimizeToTray;
diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp
index a60e0647e3..ededde4da9 100644
--- a/src/qt/test/addressbooktests.cpp
+++ b/src/qt/test/addressbooktests.cpp
@@ -24,6 +24,7 @@
#include <chrono>
#include <QApplication>
+#include <QLineEdit>
#include <QMessageBox>
#include <QTableView>
#include <QTimer>
@@ -102,11 +103,13 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
QString s_label("already here (s)");
// Define a new address (which should add to the address book successfully).
- QString new_address;
+ QString new_address_a;
+ QString new_address_b;
std::tie(r_key_dest, preexisting_r_address) = build_address();
std::tie(s_key_dest, preexisting_s_address) = build_address();
- std::tie(std::ignore, new_address) = build_address();
+ std::tie(std::ignore, new_address_a) = build_address();
+ std::tie(std::ignore, new_address_b) = build_address();
{
LOCK(wallet->cs_wallet);
@@ -124,7 +127,7 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
// Initialize relevant QT models.
std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other"));
- OptionsModel optionsModel;
+ OptionsModel optionsModel(node);
ClientModel clientModel(node, &optionsModel);
WalletContext& context = *node.walletLoader().context();
AddWallet(context, wallet);
@@ -159,9 +162,52 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
// Submit a new address which should add successfully - we expect the
// warning message to be blank.
EditAddressAndSubmit(
- &editAddressDialog, QString("new"), new_address, QString(""));
+ &editAddressDialog, QString("io - new A"), new_address_a, QString(""));
check_addbook_size(3);
QCOMPARE(table_view->model()->rowCount(), 2);
+
+ EditAddressAndSubmit(
+ &editAddressDialog, QString("io - new B"), new_address_b, QString(""));
+ check_addbook_size(4);
+ QCOMPARE(table_view->model()->rowCount(), 3);
+
+ auto search_line = address_book.findChild<QLineEdit*>("searchLineEdit");
+
+ search_line->setText(r_label);
+ QCOMPARE(table_view->model()->rowCount(), 0);
+
+ search_line->setText(s_label);
+ QCOMPARE(table_view->model()->rowCount(), 1);
+
+ search_line->setText("io");
+ QCOMPARE(table_view->model()->rowCount(), 2);
+
+ // Check wilcard "?".
+ search_line->setText("io?new");
+ QCOMPARE(table_view->model()->rowCount(), 0);
+ search_line->setText("io???new");
+ QCOMPARE(table_view->model()->rowCount(), 2);
+
+ // Check wilcard "*".
+ search_line->setText("io*new");
+ QCOMPARE(table_view->model()->rowCount(), 2);
+ search_line->setText("*");
+ QCOMPARE(table_view->model()->rowCount(), 3);
+
+ search_line->setText(preexisting_r_address);
+ QCOMPARE(table_view->model()->rowCount(), 0);
+
+ search_line->setText(preexisting_s_address);
+ QCOMPARE(table_view->model()->rowCount(), 1);
+
+ search_line->setText(new_address_a);
+ QCOMPARE(table_view->model()->rowCount(), 1);
+
+ search_line->setText(new_address_b);
+ QCOMPARE(table_view->model()->rowCount(), 1);
+
+ search_line->setText("");
+ QCOMPARE(table_view->model()->rowCount(), 3);
}
} // namespace
diff --git a/src/qt/test/apptests.cpp b/src/qt/test/apptests.cpp
index b191082ab0..9648ef6188 100644
--- a/src/qt/test/apptests.cpp
+++ b/src/qt/test/apptests.cpp
@@ -119,6 +119,6 @@ AppTests::HandleCallback::~HandleCallback()
assert(it != callbacks.end());
callbacks.erase(it);
if (callbacks.empty()) {
- m_app_tests.m_app.quit();
+ m_app_tests.m_app.exit(0);
}
}
diff --git a/src/qt/test/optiontests.cpp b/src/qt/test/optiontests.cpp
index 4a943a6343..2ef9230c59 100644
--- a/src/qt/test/optiontests.cpp
+++ b/src/qt/test/optiontests.cpp
@@ -13,8 +13,7 @@
#include <univalue.h>
-//! Entry point for BitcoinApplication tests.
-void OptionTests::optionTests()
+void OptionTests::integerGetArgBug()
{
// Test regression https://github.com/bitcoin/bitcoin/issues/24457. Ensure
// that setting integer prune value doesn't cause an exception to be thrown
@@ -24,7 +23,7 @@ void OptionTests::optionTests()
settings.rw_settings["prune"] = 3814;
});
gArgs.WriteSettingsFile();
- OptionsModel{};
+ OptionsModel{m_node};
gArgs.LockSettings([&](util::Settings& settings) {
settings.rw_settings.erase("prune");
});
@@ -49,7 +48,7 @@ void OptionTests::parametersInteraction()
QSettings settings;
settings.setValue("fListen", false);
- OptionsModel{};
+ OptionsModel{m_node};
const bool expected{false};
diff --git a/src/qt/test/optiontests.h b/src/qt/test/optiontests.h
index 39c1612c8f..257a0b65be 100644
--- a/src/qt/test/optiontests.h
+++ b/src/qt/test/optiontests.h
@@ -16,7 +16,7 @@ public:
explicit OptionTests(interfaces::Node& node) : m_node(node) {}
private Q_SLOTS:
- void optionTests();
+ void integerGetArgBug();
void parametersInteraction();
private:
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index 70b7f83872..bc06f0f23b 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -184,7 +184,7 @@ void TestGUI(interfaces::Node& node)
std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other"));
SendCoinsDialog sendCoinsDialog(platformStyle.get());
TransactionView transactionView(platformStyle.get());
- OptionsModel optionsModel;
+ OptionsModel optionsModel(node);
ClientModel clientModel(node, &optionsModel);
WalletContext& context = *node.walletLoader().context();
AddWallet(context, wallet);
diff --git a/src/script/standard.h b/src/script/standard.h
index f0b143c52b..6a15ba4e3d 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -6,6 +6,7 @@
#ifndef BITCOIN_SCRIPT_STANDARD_H
#define BITCOIN_SCRIPT_STANDARD_H
+#include <attributes.h>
#include <pubkey.h>
#include <script/interpreter.h>
#include <uint256.h>
diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
index 82e4e1c90f..b333a9f72d 100644
--- a/src/test/coins_tests.cpp
+++ b/src/test/coins_tests.cpp
@@ -2,7 +2,6 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <attributes.h>
#include <clientversion.h>
#include <coins.h>
#include <script/standard.h>
diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h
index 580105e442..3fc6fa1cd5 100644
--- a/src/test/fuzz/util.h
+++ b/src/test/fuzz/util.h
@@ -6,7 +6,6 @@
#define BITCOIN_TEST_FUZZ_UTIL_H
#include <arith_uint256.h>
-#include <attributes.h>
#include <chainparamsbase.h>
#include <coins.h>
#include <compat.h>
diff --git a/src/util/bip32.h b/src/util/bip32.h
index 8f86f2aaa6..aa4eac3791 100644
--- a/src/util/bip32.h
+++ b/src/util/bip32.h
@@ -5,7 +5,6 @@
#ifndef BITCOIN_UTIL_BIP32_H
#define BITCOIN_UTIL_BIP32_H
-#include <attributes.h>
#include <string>
#include <vector>
diff --git a/src/util/moneystr.h b/src/util/moneystr.h
index 8180604342..3d33bd7f99 100644
--- a/src/util/moneystr.h
+++ b/src/util/moneystr.h
@@ -9,7 +9,6 @@
#ifndef BITCOIN_UTIL_MONEYSTR_H
#define BITCOIN_UTIL_MONEYSTR_H
-#include <attributes.h>
#include <consensus/amount.h>
#include <optional>
diff --git a/src/util/strencodings.h b/src/util/strencodings.h
index ee58383528..9a96bbe67b 100644
--- a/src/util/strencodings.h
+++ b/src/util/strencodings.h
@@ -9,7 +9,6 @@
#ifndef BITCOIN_UTIL_STRENCODINGS_H
#define BITCOIN_UTIL_STRENCODINGS_H
-#include <attributes.h>
#include <span.h>
#include <util/string.h>
diff --git a/src/util/system.h b/src/util/system.h
index 64585cbfac..8db3ab9913 100644
--- a/src/util/system.h
+++ b/src/util/system.h
@@ -14,7 +14,6 @@
#include <config/bitcoin-config.h>
#endif
-#include <attributes.h>
#include <compat.h>
#include <compat/assumptions.h>
#include <fs.h>
diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
index fffab39cc1..613c5b65ef 100644
--- a/src/validationinterface.cpp
+++ b/src/validationinterface.cpp
@@ -5,6 +5,7 @@
#include <validationinterface.h>
+#include <attributes.h>
#include <chain.h>
#include <consensus/validation.h>
#include <logging.h>
diff --git a/src/wallet/coinselection.cpp b/src/wallet/coinselection.cpp
index 0b236e2e48..e710787a26 100644
--- a/src/wallet/coinselection.cpp
+++ b/src/wallet/coinselection.cpp
@@ -328,24 +328,18 @@ std::optional<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups,
******************************************************************************/
void OutputGroup::Insert(const COutput& output, size_t ancestors, size_t descendants, bool positive_only) {
- // Compute the effective value first
- const CAmount coin_fee = output.input_bytes < 0 ? 0 : m_effective_feerate.GetFee(output.input_bytes);
- const CAmount ev = output.txout.nValue - coin_fee;
-
// Filter for positive only here before adding the coin
- if (positive_only && ev <= 0) return;
+ if (positive_only && output.GetEffectiveValue() <= 0) return;
m_outputs.push_back(output);
COutput& coin = m_outputs.back();
- coin.fee = coin_fee;
- fee += coin.fee;
+ fee += coin.GetFee();
coin.long_term_fee = coin.input_bytes < 0 ? 0 : m_long_term_feerate.GetFee(coin.input_bytes);
long_term_fee += coin.long_term_fee;
- coin.effective_value = ev;
- effective_value += coin.effective_value;
+ effective_value += coin.GetEffectiveValue();
m_from_me &= coin.from_me;
m_value += coin.txout.nValue;
@@ -380,8 +374,8 @@ CAmount GetSelectionWaste(const std::set<COutput>& inputs, CAmount change_cost,
CAmount waste = 0;
CAmount selected_effective_value = 0;
for (const COutput& coin : inputs) {
- waste += coin.fee - coin.long_term_fee;
- selected_effective_value += use_effective_value ? coin.effective_value : coin.txout.nValue;
+ waste += coin.GetFee() - coin.long_term_fee;
+ selected_effective_value += use_effective_value ? coin.GetEffectiveValue() : coin.txout.nValue;
}
if (change_cost) {
diff --git a/src/wallet/coinselection.h b/src/wallet/coinselection.h
index 25c672eda0..9135e48104 100644
--- a/src/wallet/coinselection.h
+++ b/src/wallet/coinselection.h
@@ -20,6 +20,14 @@ static constexpr CAmount CHANGE_UPPER{1000000};
/** A UTXO under consideration for use in funding a new transaction. */
struct COutput {
+private:
+ /** The output's value minus fees required to spend it.*/
+ std::optional<CAmount> effective_value;
+
+ /** The fee required to spend this output at the transaction's target feerate. */
+ std::optional<CAmount> fee;
+
+public:
/** The outpoint identifying this UTXO */
COutPoint outpoint;
@@ -55,16 +63,10 @@ struct COutput {
/** Whether the transaction containing this output is sent from the owning wallet */
bool from_me;
- /** The output's value minus fees required to spend it. Initialized as the output's absolute value. */
- CAmount effective_value;
-
- /** The fee required to spend this output at the transaction's target feerate. */
- CAmount fee{0};
-
/** The fee required to spend this output at the consolidation feerate. */
CAmount long_term_fee{0};
- COutput(const COutPoint& outpoint, const CTxOut& txout, int depth, int input_bytes, bool spendable, bool solvable, bool safe, int64_t time, bool from_me)
+ COutput(const COutPoint& outpoint, const CTxOut& txout, int depth, int input_bytes, bool spendable, bool solvable, bool safe, int64_t time, bool from_me, const std::optional<CFeeRate> feerate = std::nullopt)
: outpoint{outpoint},
txout{txout},
depth{depth},
@@ -73,9 +75,22 @@ struct COutput {
solvable{solvable},
safe{safe},
time{time},
- from_me{from_me},
- effective_value{txout.nValue}
- {}
+ from_me{from_me}
+ {
+ if (feerate) {
+ fee = input_bytes < 0 ? 0 : feerate.value().GetFee(input_bytes);
+ effective_value = txout.nValue - fee.value();
+ }
+ }
+
+ COutput(const COutPoint& outpoint, const CTxOut& txout, int depth, int input_bytes, bool spendable, bool solvable, bool safe, int64_t time, bool from_me, const CAmount fees)
+ : COutput(outpoint, txout, depth, input_bytes, spendable, solvable, safe, time, from_me)
+ {
+ // if input_bytes is unknown, then fees should be 0, if input_bytes is known, then the fees should be a positive integer or 0 (input_bytes known and fees = 0 only happens in the tests)
+ assert((input_bytes < 0 && fees == 0) || (input_bytes > 0 && fees >= 0));
+ fee = fees;
+ effective_value = txout.nValue - fee.value();
+ }
std::string ToString() const;
@@ -83,6 +98,18 @@ struct COutput {
{
return outpoint < rhs.outpoint;
}
+
+ CAmount GetFee() const
+ {
+ assert(fee.has_value());
+ return fee.value();
+ }
+
+ CAmount GetEffectiveValue() const
+ {
+ assert(effective_value.has_value());
+ return effective_value.value();
+ }
};
/** Parameters for one iteration of Coin Selection. */
diff --git a/src/wallet/rpc/coins.cpp b/src/wallet/rpc/coins.cpp
index 95296b4c9d..2649fa586c 100644
--- a/src/wallet/rpc/coins.cpp
+++ b/src/wallet/rpc/coins.cpp
@@ -18,28 +18,31 @@
namespace wallet {
static CAmount GetReceived(const CWallet& wallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{
- std::set<CScript> output_scripts;
-
+ std::set<CTxDestination> addresses;
if (by_label) {
// Get the set of addresses assigned to label
- std::string label = LabelFromValue(params[0]);
- for (const auto& address : wallet.GetLabelAddresses(label)) {
- auto output_script{GetScriptForDestination(address)};
- if (wallet.IsMine(output_script)) {
- output_scripts.insert(output_script);
- }
- }
+ addresses = wallet.GetLabelAddresses(LabelFromValue(params[0]));
+ if (addresses.empty()) throw JSONRPCError(RPC_WALLET_ERROR, "Label not found in wallet");
} else {
// Get the address
CTxDestination dest = DecodeDestination(params[0].get_str());
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
}
- CScript script_pub_key = GetScriptForDestination(dest);
- if (!wallet.IsMine(script_pub_key)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Address not found in wallet");
+ addresses.insert(dest);
+ }
+
+ // Filter by own scripts only
+ std::set<CScript> output_scripts;
+ for (const auto& address : addresses) {
+ auto output_script{GetScriptForDestination(address)};
+ if (wallet.IsMine(output_script)) {
+ output_scripts.insert(output_script);
}
- output_scripts.insert(script_pub_key);
+ }
+
+ if (output_scripts.empty()) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Address not found in wallet");
}
// Minimum confirmations
@@ -635,7 +638,7 @@ RPCHelpMan listunspent()
cctl.m_max_depth = nMaxDepth;
cctl.m_include_unsafe_inputs = include_unsafe;
LOCK(pwallet->cs_wallet);
- AvailableCoins(*pwallet, vecOutputs, &cctl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount);
+ AvailableCoinsListUnspent(*pwallet, vecOutputs, &cctl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount);
}
LOCK(pwallet->cs_wallet);
diff --git a/src/wallet/rpc/spend.cpp b/src/wallet/rpc/spend.cpp
index c7b57ba4be..d1a0ba50f6 100644
--- a/src/wallet/rpc/spend.cpp
+++ b/src/wallet/rpc/spend.cpp
@@ -1398,7 +1398,7 @@ RPCHelpMan sendall()
total_input_value += tx->tx->vout[input.prevout.n].nValue;
}
} else {
- AvailableCoins(*pwallet, all_the_utxos, &coin_control, /*nMinimumAmount=*/0);
+ AvailableCoins(*pwallet, all_the_utxos, &coin_control, fee_rate, /*nMinimumAmount=*/0);
for (const COutput& output : all_the_utxos) {
CHECK_NONFATAL(output.input_bytes > 0);
if (send_max && fee_rate.GetFee(output.input_bytes) > output.txout.nValue) {
diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp
index e5fd7b0eb4..fe36cfcc6b 100644
--- a/src/wallet/spend.cpp
+++ b/src/wallet/spend.cpp
@@ -84,7 +84,7 @@ TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *walle
return CalculateMaximumSignedTxSize(tx, wallet, txouts, coin_control);
}
-void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount)
+void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl, std::optional<CFeeRate> feerate, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount)
{
AssertLockHeld(wallet.cs_wallet);
@@ -192,7 +192,7 @@ void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const C
bool spendable = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && (coinControl && coinControl->fAllowWatchOnly && solvable));
int input_bytes = GetTxSpendSize(wallet, wtx, i, (coinControl && coinControl->fAllowWatchOnly));
- vCoins.emplace_back(COutPoint(wtx.GetHash(), i), wtx.tx->vout.at(i), nDepth, input_bytes, spendable, solvable, safeTx, wtx.GetTxTime(), tx_from_me);
+ vCoins.emplace_back(COutPoint(wtx.GetHash(), i), wtx.tx->vout.at(i), nDepth, input_bytes, spendable, solvable, safeTx, wtx.GetTxTime(), tx_from_me, feerate);
// Checks the sum amount of all UTXO's.
if (nMinimumSumAmount != MAX_MONEY) {
@@ -211,13 +211,18 @@ void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const C
}
}
+void AvailableCoinsListUnspent(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount)
+{
+ AvailableCoins(wallet, vCoins, coinControl, /*feerate=*/ std::nullopt, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount);
+}
+
CAmount GetAvailableBalance(const CWallet& wallet, const CCoinControl* coinControl)
{
LOCK(wallet.cs_wallet);
CAmount balance = 0;
std::vector<COutput> vCoins;
- AvailableCoins(wallet, vCoins, coinControl);
+ AvailableCoinsListUnspent(wallet, vCoins, coinControl);
for (const COutput& out : vCoins) {
if (out.spendable) {
balance += out.txout.nValue;
@@ -257,7 +262,7 @@ std::map<CTxDestination, std::vector<COutput>> ListCoins(const CWallet& wallet)
std::map<CTxDestination, std::vector<COutput>> result;
std::vector<COutput> availableCoins;
- AvailableCoins(wallet, availableCoins);
+ AvailableCoinsListUnspent(wallet, availableCoins);
for (const COutput& coin : availableCoins) {
CTxDestination address;
@@ -477,12 +482,11 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, const std::vec
}
/* Set some defaults for depth, spendable, solvable, safe, time, and from_me as these don't matter for preset inputs since no selection is being done. */
- COutput output(outpoint, txout, /*depth=*/ 0, input_bytes, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false);
- output.effective_value = output.txout.nValue - coin_selection_params.m_effective_feerate.GetFee(output.input_bytes);
+ COutput output(outpoint, txout, /*depth=*/ 0, input_bytes, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, coin_selection_params.m_effective_feerate);
if (coin_selection_params.m_subtract_fee_outputs) {
value_to_select -= output.txout.nValue;
} else {
- value_to_select -= output.effective_value;
+ value_to_select -= output.GetEffectiveValue();
}
preset_coins.insert(outpoint);
/* Set ancestors and descendants to 0 as they don't matter for preset inputs since no actual selection is being done.
@@ -788,7 +792,7 @@ static std::optional<CreatedTransactionResult> CreateTransactionInternal(
// Get available coins
std::vector<COutput> vAvailableCoins;
- AvailableCoins(wallet, vAvailableCoins, &coin_control, 1, MAX_MONEY, MAX_MONEY, 0);
+ AvailableCoins(wallet, vAvailableCoins, &coin_control, coin_selection_params.m_effective_feerate, 1, MAX_MONEY, MAX_MONEY, 0);
// Choose coins to use
std::optional<SelectionResult> result = SelectCoins(wallet, vAvailableCoins, /*nTargetValue=*/selection_target, coin_control, coin_selection_params);
diff --git a/src/wallet/spend.h b/src/wallet/spend.h
index 8af712110d..988058a25a 100644
--- a/src/wallet/spend.h
+++ b/src/wallet/spend.h
@@ -37,7 +37,13 @@ TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* walle
/**
* populate vCoins with vector of available COutputs.
*/
-void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
+void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl = nullptr, std::optional<CFeeRate> feerate = std::nullopt, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
+
+/**
+ * Wrapper function for AvailableCoins which skips the `feerate` parameter. Use this function
+ * to list all available coins (e.g. listunspent RPC) while not intending to fund a transaction.
+ */
+void AvailableCoinsListUnspent(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
CAmount GetAvailableBalance(const CWallet& wallet, const CCoinControl* coinControl = nullptr);
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index d851101260..76f28917a4 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -41,7 +41,7 @@ static void add_coin(const CAmount& nValue, int nInput, std::vector<COutput>& se
tx.vout.resize(nInput + 1);
tx.vout[nInput].nValue = nValue;
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
- set.emplace_back(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false);
+ set.emplace_back(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, /*fees=*/ 0);
}
static void add_coin(const CAmount& nValue, int nInput, SelectionResult& result)
@@ -50,7 +50,7 @@ static void add_coin(const CAmount& nValue, int nInput, SelectionResult& result)
tx.vout.resize(nInput + 1);
tx.vout[nInput].nValue = nValue;
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
- COutput output(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false);
+ COutput output(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, /*fees=*/ 0);
OutputGroup group;
group.Insert(output, /*ancestors=*/ 0, /*descendants=*/ 0, /*positive_only=*/ true);
result.AddInput(group);
@@ -62,14 +62,12 @@ static void add_coin(const CAmount& nValue, int nInput, CoinSet& set, CAmount fe
tx.vout.resize(nInput + 1);
tx.vout[nInput].nValue = nValue;
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
- COutput coin(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false);
- coin.effective_value = nValue - fee;
- coin.fee = fee;
+ COutput coin(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ 148, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, fee);
coin.long_term_fee = long_term_fee;
set.insert(coin);
}
-static void add_coin(std::vector<COutput>& coins, CWallet& wallet, const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0, bool spendable = false)
+static void add_coin(std::vector<COutput>& coins, CWallet& wallet, const CAmount& nValue, CFeeRate feerate = CFeeRate(0), int nAge = 6*24, bool fIsFromMe = false, int nInput=0, bool spendable = false)
{
CMutableTransaction tx;
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
@@ -88,7 +86,7 @@ static void add_coin(std::vector<COutput>& coins, CWallet& wallet, const CAmount
auto ret = wallet.mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(txid), std::forward_as_tuple(MakeTransactionRef(std::move(tx)), TxStateInactive{}));
assert(ret.second);
CWalletTx& wtx = (*ret.first).second;
- coins.emplace_back(COutPoint(wtx.GetHash(), nInput), wtx.tx->vout.at(nInput), nAge, GetTxSpendSize(wallet, wtx, nInput), /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, wtx.GetTxTime(), fIsFromMe);
+ coins.emplace_back(COutPoint(wtx.GetHash(), nInput), wtx.tx->vout.at(nInput), nAge, GetTxSpendSize(wallet, wtx, nInput), /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, wtx.GetTxTime(), fIsFromMe, feerate);
}
/** Check if SelectionResult a is equivalent to SelectionResult b.
@@ -311,13 +309,13 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
std::vector<COutput> coins;
- add_coin(coins, *wallet, 1);
+ add_coin(coins, *wallet, 1, coin_selection_params_bnb.m_effective_feerate);
coins.at(0).input_bytes = 40; // Make sure that it has a negative effective value. The next check should assert if this somehow got through. Otherwise it will fail
BOOST_CHECK(!SelectCoinsBnB(GroupCoins(coins), 1 * CENT, coin_selection_params_bnb.m_cost_of_change));
// Test fees subtracted from output:
coins.clear();
- add_coin(coins, *wallet, 1 * CENT);
+ add_coin(coins, *wallet, 1 * CENT, coin_selection_params_bnb.m_effective_feerate);
coins.at(0).input_bytes = 40;
coin_selection_params_bnb.m_subtract_fee_outputs = true;
const auto result9 = SelectCoinsBnB(GroupCoins(coins), 1 * CENT, coin_selection_params_bnb.m_cost_of_change);
@@ -334,9 +332,9 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
std::vector<COutput> coins;
- add_coin(coins, *wallet, 5 * CENT, 6 * 24, false, 0, true);
- add_coin(coins, *wallet, 3 * CENT, 6 * 24, false, 0, true);
- add_coin(coins, *wallet, 2 * CENT, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 5 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 3 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 2 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
CCoinControl coin_control;
coin_control.fAllowOtherInputs = true;
coin_control.Select(coins.at(0).outpoint);
@@ -353,36 +351,49 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
std::vector<COutput> coins;
- add_coin(coins, *wallet, 10 * CENT, 6 * 24, false, 0, true);
- add_coin(coins, *wallet, 9 * CENT, 6 * 24, false, 0, true);
- add_coin(coins, *wallet, 1 * CENT, 6 * 24, false, 0, true);
-
// single coin should be selected when effective fee > long term fee
+ coin_selection_params_bnb.m_effective_feerate = CFeeRate(5000);
+ coin_selection_params_bnb.m_long_term_feerate = CFeeRate(3000);
+
+ add_coin(coins, *wallet, 10 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 9 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 1 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+
expected_result.Clear();
add_coin(10 * CENT, 2, expected_result);
CCoinControl coin_control;
- coin_selection_params_bnb.m_effective_feerate = CFeeRate(5000);
- coin_selection_params_bnb.m_long_term_feerate = CFeeRate(3000);
const auto result11 = SelectCoins(*wallet, coins, 10 * CENT, coin_control, coin_selection_params_bnb);
BOOST_CHECK(EquivalentResult(expected_result, *result11));
+ coins.clear();
// more coins should be selected when effective fee < long term fee
+ coin_selection_params_bnb.m_effective_feerate = CFeeRate(3000);
+ coin_selection_params_bnb.m_long_term_feerate = CFeeRate(5000);
+
+ add_coin(coins, *wallet, 10 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 9 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 1 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+
expected_result.Clear();
add_coin(9 * CENT, 2, expected_result);
add_coin(1 * CENT, 2, expected_result);
- coin_selection_params_bnb.m_effective_feerate = CFeeRate(3000);
- coin_selection_params_bnb.m_long_term_feerate = CFeeRate(5000);
const auto result12 = SelectCoins(*wallet, coins, 10 * CENT, coin_control, coin_selection_params_bnb);
BOOST_CHECK(EquivalentResult(expected_result, *result12));
+ coins.clear();
// pre selected coin should be selected even if disadvantageous
+ coin_selection_params_bnb.m_effective_feerate = CFeeRate(5000);
+ coin_selection_params_bnb.m_long_term_feerate = CFeeRate(3000);
+
+ add_coin(coins, *wallet, 10 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 9 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 1 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+
expected_result.Clear();
add_coin(9 * CENT, 2, expected_result);
add_coin(1 * CENT, 2, expected_result);
coin_control.fAllowOtherInputs = true;
coin_control.Select(coins.at(1).outpoint); // pre select 9 coin
- coin_selection_params_bnb.m_effective_feerate = CFeeRate(5000);
- coin_selection_params_bnb.m_long_term_feerate = CFeeRate(3000);
const auto result13 = SelectCoins(*wallet, coins, 10 * CENT, coin_control, coin_selection_params_bnb);
BOOST_CHECK(EquivalentResult(expected_result, *result13));
}
@@ -409,7 +420,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
// with an empty wallet we can't even pay one cent
BOOST_CHECK(!KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_standard), 1 * CENT, CENT));
- add_coin(coins, *wallet, 1*CENT, 4); // add a new 1 cent coin
+ add_coin(coins, *wallet, 1*CENT, CFeeRate(0), 4); // add a new 1 cent coin
// with a new 1 cent coin, we still can't find a mature 1 cent
BOOST_CHECK(!KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_standard), 1 * CENT, CENT));
@@ -430,7 +441,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
BOOST_CHECK_EQUAL(result2->GetSelectedValue(), 3 * CENT);
add_coin(coins, *wallet, 5*CENT); // add a mature 5 cent coin,
- add_coin(coins, *wallet, 10*CENT, 3, true); // a new 10 cent coin sent from one of our own addresses
+ add_coin(coins, *wallet, 10*CENT, CFeeRate(0), 3, true); // a new 10 cent coin sent from one of our own addresses
add_coin(coins, *wallet, 20*CENT); // and a mature 20 cent coin
// now we have new: 1+10=11 (of which 10 was self-sent), and mature: 2+5+20=27. total = 38
@@ -857,5 +868,40 @@ BOOST_AUTO_TEST_CASE(waste_test)
BOOST_CHECK_EQUAL(0, GetSelectionWaste(selection, /* change cost */ 0, new_target));
}
+BOOST_AUTO_TEST_CASE(effective_value_test)
+{
+ const int input_bytes = 148;
+ const CFeeRate feerate(1000);
+ const CAmount nValue = 10000;
+ const int nInput = 0;
+
+ CMutableTransaction tx;
+ tx.vout.resize(1);
+ tx.vout[nInput].nValue = nValue;
+
+ // standard case, pass feerate in constructor
+ COutput output1(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, input_bytes, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, feerate);
+ const CAmount expected_ev1 = 9852; // 10000 - 148
+ BOOST_CHECK_EQUAL(output1.GetEffectiveValue(), expected_ev1);
+
+ // input bytes unknown (input_bytes = -1), pass feerate in constructor
+ COutput output2(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, feerate);
+ BOOST_CHECK_EQUAL(output2.GetEffectiveValue(), nValue); // The effective value should be equal to the absolute value if input_bytes is -1
+
+ // negative effective value, pass feerate in constructor
+ COutput output3(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, input_bytes, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, CFeeRate(100000));
+ const CAmount expected_ev3 = -4800; // 10000 - 14800
+ BOOST_CHECK_EQUAL(output3.GetEffectiveValue(), expected_ev3);
+
+ // standard case, pass fees in constructor
+ const CAmount fees = 148;
+ COutput output4(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, input_bytes, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, fees);
+ BOOST_CHECK_EQUAL(output4.GetEffectiveValue(), expected_ev1);
+
+ // input bytes unknown (input_bytes = -1), pass fees in constructor
+ COutput output5(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, /*fees=*/ 0);
+ BOOST_CHECK_EQUAL(output5.GetEffectiveValue(), nValue); // The effective value should be equal to the absolute value if input_bytes is -1
+}
+
BOOST_AUTO_TEST_SUITE_END()
} // namespace wallet
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index d4406fd5dd..70863f5464 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -584,7 +584,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoinsTest, ListCoinsTestingSetup)
{
LOCK(wallet->cs_wallet);
std::vector<COutput> available;
- AvailableCoins(*wallet, available);
+ AvailableCoinsListUnspent(*wallet, available);
BOOST_CHECK_EQUAL(available.size(), 2U);
}
for (const auto& group : list) {
@@ -596,7 +596,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoinsTest, ListCoinsTestingSetup)
{
LOCK(wallet->cs_wallet);
std::vector<COutput> available;
- AvailableCoins(*wallet, available);
+ AvailableCoinsListUnspent(*wallet, available);
BOOST_CHECK_EQUAL(available.size(), 0U);
}
// Confirm ListCoins still returns same result as before, despite coins