aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-rw-r--r--src/chain.cpp7
-rw-r--r--src/chain.h4
-rw-r--r--src/checkpoints.cpp32
-rw-r--r--src/checkpoints.h27
-rw-r--r--src/coins.h4
-rw-r--r--src/init.cpp11
-rw-r--r--src/interfaces/chain.cpp18
-rw-r--r--src/interfaces/chain.h17
-rw-r--r--src/primitives/transaction.h4
-rw-r--r--src/qt/clientmodel.cpp1
-rw-r--r--src/qt/guiutil.cpp5
-rw-r--r--src/qt/guiutil.h5
-rw-r--r--src/qt/intro.cpp13
-rw-r--r--src/qt/intro.h5
-rw-r--r--src/qt/optionsmodel.cpp5
-rw-r--r--src/rpc/blockchain.cpp3
-rw-r--r--src/rpc/rawtransaction.cpp10
-rw-r--r--src/rpc/rawtransaction_util.cpp12
-rw-r--r--src/rpc/rawtransaction_util.h22
-rw-r--r--src/script/ismine.h29
-rw-r--r--src/test/skiplist_tests.cpp46
-rw-r--r--src/ui_interface.cpp4
-rw-r--r--src/ui_interface.h3
-rw-r--r--src/validation.cpp36
-rw-r--r--src/wallet/rpcwallet.cpp22
-rw-r--r--src/wallet/test/coinselector_tests.cpp5
-rw-r--r--src/wallet/wallet.cpp101
-rw-r--r--src/wallet/wallet.h47
29 files changed, 217 insertions, 283 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 059ed18134..4fe5898829 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -113,7 +113,6 @@ BITCOIN_CORE_H = \
chainparams.h \
chainparamsbase.h \
chainparamsseeds.h \
- checkpoints.h \
checkqueue.h \
clientversion.h \
coins.h \
@@ -258,7 +257,6 @@ libbitcoin_server_a_SOURCES = \
blockencodings.cpp \
blockfilter.cpp \
chain.cpp \
- checkpoints.cpp \
consensus/tx_verify.cpp \
flatfile.cpp \
httprpc.cpp \
diff --git a/src/chain.cpp b/src/chain.cpp
index d462f94ab5..5520d8149a 100644
--- a/src/chain.cpp
+++ b/src/chain.cpp
@@ -59,10 +59,11 @@ const CBlockIndex *CChain::FindFork(const CBlockIndex *pindex) const {
return pindex;
}
-CBlockIndex* CChain::FindEarliestAtLeast(int64_t nTime) const
+CBlockIndex* CChain::FindEarliestAtLeast(int64_t nTime, int height) const
{
- std::vector<CBlockIndex*>::const_iterator lower = std::lower_bound(vChain.begin(), vChain.end(), nTime,
- [](CBlockIndex* pBlock, const int64_t& time) -> bool { return pBlock->GetBlockTimeMax() < time; });
+ std::pair<int64_t, int> blockparams = std::make_pair(nTime, height);
+ std::vector<CBlockIndex*>::const_iterator lower = std::lower_bound(vChain.begin(), vChain.end(), blockparams,
+ [](CBlockIndex* pBlock, const std::pair<int64_t, int>& blockparams) -> bool { return pBlock->GetBlockTimeMax() < blockparams.first || pBlock->nHeight < blockparams.second; });
return (lower == vChain.end() ? nullptr : *lower);
}
diff --git a/src/chain.h b/src/chain.h
index 2b6d2d082c..dd9cc2a598 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -465,8 +465,8 @@ public:
/** Find the last common block between this chain and a block index entry. */
const CBlockIndex *FindFork(const CBlockIndex *pindex) const;
- /** Find the earliest block with timestamp equal or greater than the given. */
- CBlockIndex* FindEarliestAtLeast(int64_t nTime) const;
+ /** Find the earliest block with timestamp equal or greater than the given time and height equal or greater than the given height. */
+ CBlockIndex* FindEarliestAtLeast(int64_t nTime, int height) const;
};
#endif // BITCOIN_CHAIN_H
diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp
deleted file mode 100644
index ad5edfeb39..0000000000
--- a/src/checkpoints.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) 2009-2018 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#include <checkpoints.h>
-
-#include <chain.h>
-#include <chainparams.h>
-#include <reverse_iterator.h>
-#include <validation.h>
-
-#include <stdint.h>
-
-
-namespace Checkpoints {
-
- CBlockIndex* GetLastCheckpoint(const CCheckpointData& data)
- {
- const MapCheckpoints& checkpoints = data.mapCheckpoints;
-
- for (const MapCheckpoints::value_type& i : reverse_iterate(checkpoints))
- {
- const uint256& hash = i.second;
- CBlockIndex* pindex = LookupBlockIndex(hash);
- if (pindex) {
- return pindex;
- }
- }
- return nullptr;
- }
-
-} // namespace Checkpoints
diff --git a/src/checkpoints.h b/src/checkpoints.h
deleted file mode 100644
index a25e97e469..0000000000
--- a/src/checkpoints.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 2009-2018 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#ifndef BITCOIN_CHECKPOINTS_H
-#define BITCOIN_CHECKPOINTS_H
-
-#include <uint256.h>
-
-#include <map>
-
-class CBlockIndex;
-struct CCheckpointData;
-
-/**
- * Block-chain checkpoints are compiled-in sanity checks.
- * They are updated every release or three.
- */
-namespace Checkpoints
-{
-
-//! Returns last CBlockIndex* that is a checkpoint
-CBlockIndex* GetLastCheckpoint(const CCheckpointData& data);
-
-} //namespace Checkpoints
-
-#endif // BITCOIN_CHECKPOINTS_H
diff --git a/src/coins.h b/src/coins.h
index d39ebf9062..482e233e8c 100644
--- a/src/coins.h
+++ b/src/coins.h
@@ -294,6 +294,10 @@ public:
bool HaveInputs(const CTransaction& tx) const;
private:
+ /**
+ * @note this is marked const, but may actually append to `cacheCoins`, increasing
+ * memory usage.
+ */
CCoinsMap::iterator FetchCoin(const COutPoint &outpoint) const;
};
diff --git a/src/init.cpp b/src/init.cpp
index 70459994c7..c639763432 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -15,7 +15,6 @@
#include <blockfilter.h>
#include <chain.h>
#include <chainparams.h>
-#include <checkpoints.h>
#include <compat/sanity.h>
#include <consensus/validation.h>
#include <fs.h>
@@ -346,14 +345,15 @@ static void registerSignalHandler(int signal, void(*handler)(int))
}
#endif
+static boost::signals2::connection rpc_notify_block_change_connection;
static void OnRPCStarted()
{
- uiInterface.NotifyBlockTip_connect(&RPCNotifyBlockChange);
+ rpc_notify_block_change_connection = uiInterface.NotifyBlockTip_connect(&RPCNotifyBlockChange);
}
static void OnRPCStopped()
{
- uiInterface.NotifyBlockTip_disconnect(&RPCNotifyBlockChange);
+ rpc_notify_block_change_connection.disconnect();
RPCNotifyBlockChange(false, nullptr);
g_best_block_cv.notify_all();
LogPrint(BCLog::RPC, "RPC stopped.\n");
@@ -1734,8 +1734,9 @@ bool AppInitMain(InitInterfaces& interfaces)
// Either install a handler to notify us when genesis activates, or set fHaveGenesis directly.
// No locking, as this happens before any background thread is started.
+ boost::signals2::connection block_notify_genesis_wait_connection;
if (chainActive.Tip() == nullptr) {
- uiInterface.NotifyBlockTip_connect(BlockNotifyGenesisWait);
+ block_notify_genesis_wait_connection = uiInterface.NotifyBlockTip_connect(BlockNotifyGenesisWait);
} else {
fHaveGenesis = true;
}
@@ -1759,7 +1760,7 @@ bool AppInitMain(InitInterfaces& interfaces)
while (!fHaveGenesis && !ShutdownRequested()) {
g_genesis_wait_cv.wait_for(lock, std::chrono::milliseconds(500));
}
- uiInterface.NotifyBlockTip_disconnect(BlockNotifyGenesisWait);
+ block_notify_genesis_wait_connection.disconnect();
}
if (ShutdownRequested()) {
diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp
index b61a51b235..4f2eb924a2 100644
--- a/src/interfaces/chain.cpp
+++ b/src/interfaces/chain.cpp
@@ -84,29 +84,15 @@ class LockImpl : public Chain::Lock
CBlockIndex* block = ::chainActive[height];
return block && ((block->nStatus & BLOCK_HAVE_DATA) != 0) && block->nTx > 0;
}
- Optional<int> findFirstBlockWithTime(int64_t time, uint256* hash) override
+ Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height, uint256* hash) override
{
- CBlockIndex* block = ::chainActive.FindEarliestAtLeast(time);
+ CBlockIndex* block = ::chainActive.FindEarliestAtLeast(time, height);
if (block) {
if (hash) *hash = block->GetBlockHash();
return block->nHeight;
}
return nullopt;
}
- Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height) override
- {
- // TODO: Could update CChain::FindEarliestAtLeast() to take a height
- // parameter and use it with std::lower_bound() to make this
- // implementation more efficient and allow combining
- // findFirstBlockWithTime and findFirstBlockWithTimeAndHeight into one
- // method.
- for (CBlockIndex* block = ::chainActive[height]; block; block = ::chainActive.Next(block)) {
- if (block->GetBlockTime() >= time) {
- return block->nHeight;
- }
- }
- return nullopt;
- }
Optional<int> findPruned(int start_height, Optional<int> stop_height) override
{
if (::fPruneMode) {
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
index 17d7b6d8f1..180991526b 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -105,20 +105,11 @@ public:
virtual bool haveBlockOnDisk(int height) = 0;
//! Return height of the first block in the chain with timestamp equal
- //! or greater than the given time, or nullopt if there is no block with
- //! a high enough timestamp. Also return the block hash as an optional
- //! output parameter (to avoid the cost of a second lookup in case this
- //! information is needed.)
- virtual Optional<int> findFirstBlockWithTime(int64_t time, uint256* hash) = 0;
-
- //! Return height of the first block in the chain with timestamp equal
//! or greater than the given time and height equal or greater than the
- //! given height, or nullopt if there is no such block.
- //!
- //! Calling this with height 0 is equivalent to calling
- //! findFirstBlockWithTime, but less efficient because it requires a
- //! linear instead of a binary search.
- virtual Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height) = 0;
+ //! given height, or nullopt if there is no block with a high enough
+ //! timestamp and height. Also return the block hash as an optional output parameter
+ //! (to avoid the cost of a second lookup in case this information is needed.)
+ virtual Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height, uint256* hash) = 0;
//! Return height of last block in the specified range which is pruned, or
//! nullopt if no block in the range is pruned. Range is inclusive.
diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h
index f6f8e31363..aad991e2f1 100644
--- a/src/primitives/transaction.h
+++ b/src/primitives/transaction.h
@@ -222,6 +222,10 @@ inline void UnserializeTransaction(TxType& tx, Stream& s) {
for (size_t i = 0; i < tx.vin.size(); i++) {
s >> tx.vin[i].scriptWitness.stack;
}
+ if (!tx.HasWitness()) {
+ /* It's illegal to encode witnesses when all witness stacks are empty. */
+ throw std::ios_base::failure("Superfluous witness record");
+ }
}
if (flags) {
/* Unknown flag in the serialization */
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index 27b4c182f9..88a35534c2 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -11,7 +11,6 @@
#include <chain.h>
#include <chainparams.h>
-#include <checkpoints.h>
#include <clientversion.h>
#include <interfaces/handler.h>
#include <interfaces/node.h>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index ba0a5abdf3..6e76555979 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -244,6 +244,11 @@ QList<QModelIndex> getEntryData(QAbstractItemView *view, int column)
return view->selectionModel()->selectedRows(column);
}
+QString getDefaultDataDirectory()
+{
+ return boostPathToQString(GetDefaultDataDir());
+}
+
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir,
const QString &filter,
QString *selectedSuffixOut)
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index cbec73a882..bea4a83494 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -79,6 +79,11 @@ namespace GUIUtil
void setClipboard(const QString& str);
+ /**
+ * Determine default data directory for operating system.
+ */
+ QString getDefaultDataDirectory();
+
/** Get save filename, mimics QFileDialog::getSaveFileName, except that it appends a default suffix
when no suffix is provided by the user.
diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp
index 499af9fa07..c595361934 100644
--- a/src/qt/intro.cpp
+++ b/src/qt/intro.cpp
@@ -168,7 +168,7 @@ QString Intro::getDataDirectory()
void Intro::setDataDirectory(const QString &dataDir)
{
ui->dataDirectory->setText(dataDir);
- if(dataDir == getDefaultDataDirectory())
+ if(dataDir == GUIUtil::getDefaultDataDirectory())
{
ui->dataDirDefault->setChecked(true);
ui->dataDirectory->setEnabled(false);
@@ -180,11 +180,6 @@ void Intro::setDataDirectory(const QString &dataDir)
}
}
-QString Intro::getDefaultDataDirectory()
-{
- return GUIUtil::boostPathToQString(GetDefaultDataDir());
-}
-
bool Intro::pickDataDirectory(interfaces::Node& node)
{
QSettings settings;
@@ -193,7 +188,7 @@ bool Intro::pickDataDirectory(interfaces::Node& node)
if(!gArgs.GetArg("-datadir", "").empty())
return true;
/* 1) Default data directory for operating system */
- QString dataDir = getDefaultDataDirectory();
+ QString dataDir = GUIUtil::getDefaultDataDirectory();
/* 2) Allow QSettings to override default dir */
dataDir = settings.value("strDataDir", dataDir).toString();
@@ -239,7 +234,7 @@ bool Intro::pickDataDirectory(interfaces::Node& node)
* override -datadir in the bitcoin.conf file in the default data directory
* (to be consistent with bitcoind behavior)
*/
- if(dataDir != getDefaultDataDirectory()) {
+ if(dataDir != GUIUtil::getDefaultDataDirectory()) {
node.softSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting
}
return true;
@@ -293,7 +288,7 @@ void Intro::on_ellipsisButton_clicked()
void Intro::on_dataDirDefault_clicked()
{
- setDataDirectory(getDefaultDataDirectory());
+ setDataDirectory(GUIUtil::getDefaultDataDirectory());
}
void Intro::on_dataDirCustom_clicked()
diff --git a/src/qt/intro.h b/src/qt/intro.h
index b537c94f7d..c3b26808d4 100644
--- a/src/qt/intro.h
+++ b/src/qt/intro.h
@@ -48,11 +48,6 @@ public:
*/
static bool pickDataDirectory(interfaces::Node& node);
- /**
- * Determine default data directory for operating system.
- */
- static QString getDefaultDataDirectory();
-
Q_SIGNALS:
void requestCheck();
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index 62496a57f4..5b4fb4cc18 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -17,7 +17,6 @@
#include <net.h>
#include <netbase.h>
#include <txdb.h> // for -dbcache defaults
-#include <qt/intro.h>
#include <QNetworkProxy>
#include <QSettings>
@@ -110,7 +109,7 @@ void OptionsModel::Init(bool resetSettings)
addOverriddenOption("-par");
if (!settings.contains("strDataDir"))
- settings.setValue("strDataDir", Intro::getDefaultDataDirectory());
+ settings.setValue("strDataDir", GUIUtil::getDefaultDataDirectory());
// Wallet
#ifdef ENABLE_WALLET
@@ -187,7 +186,7 @@ void OptionsModel::Reset()
BackupSettings(GetDataDir(true) / "guisettings.ini.bak", settings);
// Save the strDataDir setting
- QString dataDir = Intro::getDefaultDataDirectory();
+ QString dataDir = GUIUtil::getDefaultDataDirectory();
dataDir = settings.value("strDataDir", dataDir).toString();
// Remove all entries from our QSettings object
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index c85c2bcc92..672fc69673 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -10,7 +10,6 @@
#include <blockfilter.h>
#include <chain.h>
#include <chainparams.h>
-#include <checkpoints.h>
#include <coins.h>
#include <consensus/validation.h>
#include <core_io.h>
@@ -1013,7 +1012,7 @@ static UniValue pruneblockchain(const JSONRPCRequest& request)
// too low to be a block time (corresponds to timestamp from Sep 2001).
if (heightParam > 1000000000) {
// Add a 2 hour buffer to include blocks which might have had old timestamps
- CBlockIndex* pindex = chainActive.FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW);
+ CBlockIndex* pindex = chainActive.FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW, 0);
if (!pindex) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Could not find block with at least the specified timestamp.");
}
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 1a7216ceeb..78d7bbc80c 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -15,6 +15,7 @@
#include <key_io.h>
#include <keystore.h>
#include <merkleblock.h>
+#include <node/coin.h>
#include <node/psbt.h>
#include <node/transaction.h>
#include <policy/policy.h>
@@ -770,7 +771,14 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
keystore.AddKey(key);
}
- return SignTransaction(*g_rpc_interfaces->chain, mtx, request.params[2], &keystore, true, request.params[3]);
+ // Fetch previous transactions (inputs):
+ std::map<COutPoint, Coin> coins;
+ for (const CTxIn& txin : mtx.vin) {
+ coins[txin.prevout]; // Create empty map entry keyed by prevout.
+ }
+ FindCoins(coins);
+
+ return SignTransaction(mtx, request.params[2], &keystore, coins, true, request.params[3]);
}
static UniValue sendrawtransaction(const JSONRPCRequest& request)
diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp
index 728fc62e25..8af491977c 100644
--- a/src/rpc/rawtransaction_util.cpp
+++ b/src/rpc/rawtransaction_util.cpp
@@ -149,18 +149,8 @@ static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::
vErrorsRet.push_back(entry);
}
-// TODO(https://github.com/bitcoin/bitcoin/pull/10973#discussion_r267084237):
-// The dependency on interfaces::Chain should be removed, so
-// signrawtransactionwithkey doesn't need access to a Chain instance.
-UniValue SignTransaction(interfaces::Chain& chain, CMutableTransaction& mtx, const UniValue& prevTxsUnival, CBasicKeyStore *keystore, bool is_temp_keystore, const UniValue& hashType)
+UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival, CBasicKeyStore* keystore, std::map<COutPoint, Coin>& coins, bool is_temp_keystore, const UniValue& hashType)
{
- // Fetch previous transactions (inputs):
- std::map<COutPoint, Coin> coins;
- for (const CTxIn& txin : mtx.vin) {
- coins[txin.prevout]; // Create empty map entry keyed by prevout.
- }
- chain.findCoins(coins);
-
// Add previous txouts given in the RPC call:
if (!prevTxsUnival.isNull()) {
UniValue prevTxs = prevTxsUnival.get_array();
diff --git a/src/rpc/rawtransaction_util.h b/src/rpc/rawtransaction_util.h
index 5529dedbd4..c115d33a77 100644
--- a/src/rpc/rawtransaction_util.h
+++ b/src/rpc/rawtransaction_util.h
@@ -5,16 +5,26 @@
#ifndef BITCOIN_RPC_RAWTRANSACTION_UTIL_H
#define BITCOIN_RPC_RAWTRANSACTION_UTIL_H
+#include <map>
+
class CBasicKeyStore;
class UniValue;
struct CMutableTransaction;
+class Coin;
+class COutPoint;
-namespace interfaces {
-class Chain;
-} // namespace interfaces
-
-/** Sign a transaction with the given keystore and previous transactions */
-UniValue SignTransaction(interfaces::Chain& chain, CMutableTransaction& mtx, const UniValue& prevTxs, CBasicKeyStore *keystore, bool tempKeystore, const UniValue& hashType);
+/**
+ * Sign a transaction with the given keystore and previous transactions
+ *
+ * @param mtx The transaction to-be-signed
+ * @param prevTxs Array of previous txns outputs that tx depends on but may not yet be in the block chain
+ * @param keystore Temporary keystore containing signing keys
+ * @param coins Map of unspent outputs - coins in mempool and current chain UTXO set, may be extended by previous txns outputs after call
+ * @param tempKeystore Whether to use temporary keystore
+ * @param hashType The signature hash type
+ * @returns JSON object with details of signed transaction
+ */
+UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxs, CBasicKeyStore* keystore, std::map<COutPoint, Coin>& coins, bool tempKeystore, const UniValue& hashType);
/** Create a transaction from univalue parameters */
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf);
diff --git a/src/script/ismine.h b/src/script/ismine.h
index 601e70f709..55e28e225a 100644
--- a/src/script/ismine.h
+++ b/src/script/ismine.h
@@ -9,6 +9,7 @@
#include <script/standard.h>
#include <stdint.h>
+#include <bitset>
class CKeyStore;
class CScript;
@@ -16,10 +17,11 @@ class CScript;
/** IsMine() return codes */
enum isminetype
{
- ISMINE_NO = 0,
- ISMINE_WATCH_ONLY = 1,
- ISMINE_SPENDABLE = 2,
- ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE
+ ISMINE_NO = 0,
+ ISMINE_WATCH_ONLY = 1 << 0,
+ ISMINE_SPENDABLE = 1 << 1,
+ ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE,
+ ISMINE_ENUM_ELEMENTS,
};
/** used for bitflags of isminetype */
typedef uint8_t isminefilter;
@@ -27,4 +29,23 @@ typedef uint8_t isminefilter;
isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest);
+/**
+ * Cachable amount subdivided into watchonly and spendable parts.
+ */
+struct CachableAmount
+{
+ // NO and ALL are never (supposed to be) cached
+ std::bitset<ISMINE_ENUM_ELEMENTS> m_cached;
+ CAmount m_value[ISMINE_ENUM_ELEMENTS];
+ inline void Reset()
+ {
+ m_cached.reset();
+ }
+ void Set(isminefilter filter, CAmount value)
+ {
+ m_cached.set(filter);
+ m_value[filter] = value;
+ }
+};
+
#endif // BITCOIN_SCRIPT_ISMINE_H
diff --git a/src/test/skiplist_tests.cpp b/src/test/skiplist_tests.cpp
index fcd92da8ca..3d39dfdb75 100644
--- a/src/test/skiplist_tests.cpp
+++ b/src/test/skiplist_tests.cpp
@@ -136,7 +136,7 @@ BOOST_AUTO_TEST_CASE(findearliestatleast_test)
// Pick a random element in vBlocksMain.
int r = InsecureRandRange(vBlocksMain.size());
int64_t test_time = vBlocksMain[r].nTime;
- CBlockIndex *ret = chain.FindEarliestAtLeast(test_time);
+ CBlockIndex* ret = chain.FindEarliestAtLeast(test_time, 0);
BOOST_CHECK(ret->nTimeMax >= test_time);
BOOST_CHECK((ret->pprev==nullptr) || ret->pprev->nTimeMax < test_time);
BOOST_CHECK(vBlocksMain[r].GetAncestor(ret->nHeight) == ret);
@@ -158,22 +158,34 @@ BOOST_AUTO_TEST_CASE(findearliestatleast_edge_test)
CChain chain;
chain.SetTip(&blocks.back());
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(50)->nHeight, 0);
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(100)->nHeight, 0);
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(150)->nHeight, 3);
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(200)->nHeight, 3);
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(250)->nHeight, 6);
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(300)->nHeight, 6);
- BOOST_CHECK(!chain.FindEarliestAtLeast(350));
-
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0)->nHeight, 0);
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(-1)->nHeight, 0);
-
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(std::numeric_limits<int64_t>::min())->nHeight, 0);
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(-int64_t(std::numeric_limits<unsigned int>::max()) - 1)->nHeight, 0);
- BOOST_CHECK(!chain.FindEarliestAtLeast(std::numeric_limits<int64_t>::max()));
- BOOST_CHECK(!chain.FindEarliestAtLeast(std::numeric_limits<unsigned int>::max()));
- BOOST_CHECK(!chain.FindEarliestAtLeast(int64_t(std::numeric_limits<unsigned int>::max()) + 1));
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(50, 0)->nHeight, 0);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(100, 0)->nHeight, 0);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(150, 0)->nHeight, 3);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(200, 0)->nHeight, 3);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(250, 0)->nHeight, 6);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(300, 0)->nHeight, 6);
+ BOOST_CHECK(!chain.FindEarliestAtLeast(350, 0));
+
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0, 0)->nHeight, 0);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(-1, 0)->nHeight, 0);
+
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(std::numeric_limits<int64_t>::min(), 0)->nHeight, 0);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(-int64_t(std::numeric_limits<unsigned int>::max()) - 1, 0)->nHeight, 0);
+ BOOST_CHECK(!chain.FindEarliestAtLeast(std::numeric_limits<int64_t>::max(), 0));
+ BOOST_CHECK(!chain.FindEarliestAtLeast(std::numeric_limits<unsigned int>::max(), 0));
+ BOOST_CHECK(!chain.FindEarliestAtLeast(int64_t(std::numeric_limits<unsigned int>::max()) + 1, 0));
+
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0, -1)->nHeight, 0);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0, 0)->nHeight, 0);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0, 3)->nHeight, 3);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0, 8)->nHeight, 8);
+ BOOST_CHECK(!chain.FindEarliestAtLeast(0, 9));
+
+ CBlockIndex* ret1 = chain.FindEarliestAtLeast(100, 2);
+ BOOST_CHECK(ret1->nTimeMax >= 100 && ret1->nHeight == 2);
+ BOOST_CHECK(!chain.FindEarliestAtLeast(300, 9));
+ CBlockIndex* ret2 = chain.FindEarliestAtLeast(200, 4);
+ BOOST_CHECK(ret2->nTimeMax >= 200 && ret2->nHeight == 4);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/ui_interface.cpp b/src/ui_interface.cpp
index c084c4e0e2..31a95486d7 100644
--- a/src/ui_interface.cpp
+++ b/src/ui_interface.cpp
@@ -28,10 +28,6 @@ struct UISignals {
boost::signals2::connection CClientUIInterface::signal_name##_connect(std::function<signal_name##Sig> fn) \
{ \
return g_ui_signals.signal_name.connect(fn); \
- } \
- void CClientUIInterface::signal_name##_disconnect(std::function<signal_name##Sig> fn) \
- { \
- return g_ui_signals.signal_name.disconnect(&fn); \
}
ADD_SIGNALS_IMPL_WRAPPER(ThreadSafeMessageBox);
diff --git a/src/ui_interface.h b/src/ui_interface.h
index 60d85bc142..d408f6f889 100644
--- a/src/ui_interface.h
+++ b/src/ui_interface.h
@@ -81,8 +81,7 @@ public:
#define ADD_SIGNALS_DECL_WRAPPER(signal_name, rtype, ...) \
rtype signal_name(__VA_ARGS__); \
using signal_name##Sig = rtype(__VA_ARGS__); \
- boost::signals2::connection signal_name##_connect(std::function<signal_name##Sig> fn); \
- void signal_name##_disconnect(std::function<signal_name##Sig> fn);
+ boost::signals2::connection signal_name##_connect(std::function<signal_name##Sig> fn);
/** Show message box. */
ADD_SIGNALS_DECL_WRAPPER(ThreadSafeMessageBox, bool, const std::string& message, const std::string& caption, unsigned int style);
diff --git a/src/validation.cpp b/src/validation.cpp
index be6257ea28..6bbce9d99a 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -8,7 +8,6 @@
#include <arith_uint256.h>
#include <chain.h>
#include <chainparams.h>
-#include <checkpoints.h>
#include <checkqueue.h>
#include <consensus/consensus.h>
#include <consensus/merkle.h>
@@ -37,6 +36,7 @@
#include <txdb.h>
#include <txmempool.h>
#include <ui_interface.h>
+#include <uint256.h>
#include <undo.h>
#include <util/moneystr.h>
#include <util/rbf.h>
@@ -562,6 +562,13 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationSt
return CheckInputs(tx, state, view, true, flags, cacheSigStore, true, txdata);
}
+/**
+ * @param[out] coins_to_uncache Return any outpoints which were not previously present in the
+ * coins cache, but were added as a result of validating the tx
+ * for mempool acceptance. This allows the caller to optionally
+ * remove the cache additions if the associated transaction ends
+ * up being rejected by the mempool.
+ */
static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx,
bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
bool bypass_limits, const CAmount& nAbsurdFee, std::vector<COutPoint>& coins_to_uncache, bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
@@ -657,6 +664,10 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
if (!pcoinsTip->HaveCoinInCache(txin.prevout)) {
coins_to_uncache.push_back(txin.prevout);
}
+
+ // Note: this call may add txin.prevout to the coins cache
+ // (pcoinsTip.cacheCoins) by way of FetchCoin(). It should be removed
+ // later (via coins_to_uncache) if this tx turns out to be invalid.
if (!view.HaveCoin(txin.prevout)) {
// Are inputs missing because we already have the tx?
for (size_t out = 0; out < tx.vout.size(); out++) {
@@ -978,6 +989,11 @@ static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPo
std::vector<COutPoint> coins_to_uncache;
bool res = AcceptToMemoryPoolWorker(chainparams, pool, state, tx, pfMissingInputs, nAcceptTime, plTxnReplaced, bypass_limits, nAbsurdFee, coins_to_uncache, test_accept);
if (!res) {
+ // Remove coins that were not present in the coins cache before calling ATMPW;
+ // this is to prevent memory DoS in case we receive a large number of
+ // invalid transactions that attempt to overrun the in-memory coins cache
+ // (`CCoinsViewCache::cacheCoins`).
+
for (const COutPoint& hashTx : coins_to_uncache)
pcoinsTip->Uncache(hashTx);
}
@@ -3188,6 +3204,22 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
return commitment;
}
+//! Returns last CBlockIndex* that is a checkpoint
+static CBlockIndex* GetLastCheckpoint(const CCheckpointData& data)
+{
+ const MapCheckpoints& checkpoints = data.mapCheckpoints;
+
+ for (const MapCheckpoints::value_type& i : reverse_iterate(checkpoints))
+ {
+ const uint256& hash = i.second;
+ CBlockIndex* pindex = LookupBlockIndex(hash);
+ if (pindex) {
+ return pindex;
+ }
+ }
+ return nullptr;
+}
+
/** Context-dependent validity checks.
* By "context", we mean only the previous block headers, but not the UTXO
* set; UTXO-related validity checks are done in ConnectBlock().
@@ -3212,7 +3244,7 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationSta
// Don't accept any forks from the main chain prior to last checkpoint.
// GetLastCheckpoint finds the last checkpoint in MapCheckpoints that's in our
// MapBlockIndex.
- CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(params.Checkpoints());
+ CBlockIndex* pcheckpoint = GetLastCheckpoint(params.Checkpoints());
if (pcheckpoint && nHeight < pcheckpoint->nHeight)
return state.DoS(100, error("%s: forked chain older than last checkpoint (height %d)", __func__, nHeight), REJECT_CHECKPOINT, "bad-fork-prior-to-checkpoint");
}
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 0d804dcc10..5a22508c4b 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -3150,7 +3150,14 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
- return SignTransaction(pwallet->chain(), mtx, request.params[1], pwallet, false, request.params[2]);
+ // Fetch previous transactions (inputs):
+ std::map<COutPoint, Coin> coins;
+ for (const CTxIn& txin : mtx.vin) {
+ coins[txin.prevout]; // Create empty map entry keyed by prevout.
+ }
+ pwallet->chain().findCoins(coins);
+
+ return SignTransaction(mtx, request.params[1], pwallet, coins, false, request.params[2]);
}
static UniValue bumpfee(const JSONRPCRequest& request)
@@ -3683,9 +3690,20 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request)
// Find all addresses that have the given label
UniValue ret(UniValue::VOBJ);
+ std::set<std::string> addresses;
for (const std::pair<const CTxDestination, CAddressBookData>& item : pwallet->mapAddressBook) {
if (item.second.name == label) {
- ret.pushKV(EncodeDestination(item.first), AddressBookDataToJSON(item.second, false));
+ std::string address = EncodeDestination(item.first);
+ // CWallet::mapAddressBook is not expected to contain duplicate
+ // address strings, but build a separate set as a precaution just in
+ // case it does.
+ bool unique = addresses.emplace(address).second;
+ assert(unique);
+ // UniValue::pushKV checks if the key exists in O(N)
+ // and since duplicate addresses are unexpected (checked with
+ // std::set in O(log(N))), UniValue::__pushKV is used instead,
+ // which currently is O(1).
+ ret.__pushKV(address, AddressBookDataToJSON(item.second, false));
}
}
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index b7ca746f8b..34b9770e8b 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -69,8 +69,7 @@ static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = fa
std::unique_ptr<CWalletTx> wtx = MakeUnique<CWalletTx>(&testWallet, MakeTransactionRef(std::move(tx)));
if (fIsFromMe)
{
- wtx->fDebitCached = true;
- wtx->nDebitCached = 1;
+ wtx->m_amounts[CWalletTx::DEBIT].Set(ISMINE_SPENDABLE, 1);
}
COutput output(wtx.get(), nInput, nAge, true /* spendable */, true /* solvable */, true /* safe */);
vCoins.push_back(output);
@@ -115,7 +114,7 @@ inline std::vector<OutputGroup>& GroupCoins(const std::vector<COutput>& coins)
{
static std::vector<OutputGroup> static_groups;
static_groups.clear();
- for (auto& coin : coins) static_groups.emplace_back(coin.GetInputCoin(), coin.nDepth, coin.tx->fDebitCached && coin.tx->nDebitCached == 1 /* HACK: we can't figure out the is_me flag so we use the conditions defined above; perhaps set safe to false for !fIsFromMe in add_coin() */, 0, 0);
+ for (auto& coin : coins) static_groups.emplace_back(coin.GetInputCoin(), coin.nDepth, coin.tx->m_amounts[CWalletTx::DEBIT].m_cached[ISMINE_SPENDABLE] && coin.tx->m_amounts[CWalletTx::DEBIT].m_value[ISMINE_SPENDABLE] == 1 /* HACK: we can't figure out the is_me flag so we use the conditions defined above; perhaps set safe to false for !fIsFromMe in add_coin() */, 0, 0);
return static_groups;
}
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 4737e2f6f4..c07468d255 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -1,12 +1,11 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <wallet/wallet.h>
#include <chain.h>
-#include <checkpoints.h>
#include <consensus/consensus.h>
#include <consensus/validation.h>
#include <fs.h>
@@ -1729,7 +1728,7 @@ int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& r
uint256 start_block;
{
auto locked_chain = chain().lock();
- const Optional<int> start_height = locked_chain->findFirstBlockWithTime(startTime - TIMESTAMP_WINDOW, &start_block);
+ const Optional<int> start_height = locked_chain->findFirstBlockWithTimeAndHeight(startTime - TIMESTAMP_WINDOW, 0, &start_block);
const Optional<int> tip_height = locked_chain->getHeight();
WalletLogPrintf("%s: Rescanning last %i blocks\n", __func__, tip_height && start_height ? *tip_height - *start_height + 1 : 0);
}
@@ -1932,33 +1931,26 @@ std::set<uint256> CWalletTx::GetConflicts() const
return result;
}
+CAmount CWalletTx::GetCachableAmount(AmountType type, const isminefilter& filter, bool recalculate) const
+{
+ auto& amount = m_amounts[type];
+ if (recalculate || !amount.m_cached[filter]) {
+ amount.Set(filter, type == DEBIT ? pwallet->GetDebit(*tx, filter) : pwallet->GetCredit(*tx, filter));
+ }
+ return amount.m_value[filter];
+}
+
CAmount CWalletTx::GetDebit(const isminefilter& filter) const
{
if (tx->vin.empty())
return 0;
CAmount debit = 0;
- if(filter & ISMINE_SPENDABLE)
- {
- if (fDebitCached)
- debit += nDebitCached;
- else
- {
- nDebitCached = pwallet->GetDebit(*tx, ISMINE_SPENDABLE);
- fDebitCached = true;
- debit += nDebitCached;
- }
+ if (filter & ISMINE_SPENDABLE) {
+ debit += GetCachableAmount(DEBIT, ISMINE_SPENDABLE);
}
- if(filter & ISMINE_WATCH_ONLY)
- {
- if(fWatchDebitCached)
- debit += nWatchDebitCached;
- else
- {
- nWatchDebitCached = pwallet->GetDebit(*tx, ISMINE_WATCH_ONLY);
- fWatchDebitCached = true;
- debit += nWatchDebitCached;
- }
+ if (filter & ISMINE_WATCH_ONLY) {
+ debit += GetCachableAmount(DEBIT, ISMINE_WATCH_ONLY);
}
return debit;
}
@@ -1970,28 +1962,12 @@ CAmount CWalletTx::GetCredit(interfaces::Chain::Lock& locked_chain, const ismine
return 0;
CAmount credit = 0;
- if (filter & ISMINE_SPENDABLE)
- {
+ if (filter & ISMINE_SPENDABLE) {
// GetBalance can assume transactions in mapWallet won't change
- if (fCreditCached)
- credit += nCreditCached;
- else
- {
- nCreditCached = pwallet->GetCredit(*tx, ISMINE_SPENDABLE);
- fCreditCached = true;
- credit += nCreditCached;
- }
+ credit += GetCachableAmount(CREDIT, ISMINE_SPENDABLE);
}
- if (filter & ISMINE_WATCH_ONLY)
- {
- if (fWatchCreditCached)
- credit += nWatchCreditCached;
- else
- {
- nWatchCreditCached = pwallet->GetCredit(*tx, ISMINE_WATCH_ONLY);
- fWatchCreditCached = true;
- credit += nWatchCreditCached;
- }
+ if (filter & ISMINE_WATCH_ONLY) {
+ credit += GetCachableAmount(CREDIT, ISMINE_WATCH_ONLY);
}
return credit;
}
@@ -1999,11 +1975,7 @@ CAmount CWalletTx::GetCredit(interfaces::Chain::Lock& locked_chain, const ismine
CAmount CWalletTx::GetImmatureCredit(interfaces::Chain::Lock& locked_chain, bool fUseCache) const
{
if (IsImmatureCoinBase(locked_chain) && IsInMainChain(locked_chain)) {
- if (fUseCache && fImmatureCreditCached)
- return nImmatureCreditCached;
- nImmatureCreditCached = pwallet->GetCredit(*tx, ISMINE_SPENDABLE);
- fImmatureCreditCached = true;
- return nImmatureCreditCached;
+ return GetCachableAmount(IMMATURE_CREDIT, ISMINE_SPENDABLE, !fUseCache);
}
return 0;
@@ -2014,23 +1986,15 @@ CAmount CWalletTx::GetAvailableCredit(interfaces::Chain::Lock& locked_chain, boo
if (pwallet == nullptr)
return 0;
+ // Avoid caching ismine for NO or ALL cases (could remove this check and simplify in the future).
+ bool allow_cache = filter == ISMINE_SPENDABLE || filter == ISMINE_WATCH_ONLY;
+
// Must wait until coinbase is safely deep enough in the chain before valuing it
if (IsImmatureCoinBase(locked_chain))
return 0;
- CAmount* cache = nullptr;
- bool* cache_used = nullptr;
-
- if (filter == ISMINE_SPENDABLE) {
- cache = &nAvailableCreditCached;
- cache_used = &fAvailableCreditCached;
- } else if (filter == ISMINE_WATCH_ONLY) {
- cache = &nAvailableWatchCreditCached;
- cache_used = &fAvailableWatchCreditCached;
- }
-
- if (fUseCache && cache_used && *cache_used) {
- return *cache;
+ if (fUseCache && allow_cache && m_amounts[AVAILABLE_CREDIT].m_cached[filter]) {
+ return m_amounts[AVAILABLE_CREDIT].m_value[filter];
}
CAmount nCredit = 0;
@@ -2046,22 +2010,17 @@ CAmount CWalletTx::GetAvailableCredit(interfaces::Chain::Lock& locked_chain, boo
}
}
- if (cache) {
- *cache = nCredit;
- assert(cache_used);
- *cache_used = true;
+ if (allow_cache) {
+ m_amounts[AVAILABLE_CREDIT].Set(filter, nCredit);
}
+
return nCredit;
}
CAmount CWalletTx::GetImmatureWatchOnlyCredit(interfaces::Chain::Lock& locked_chain, const bool fUseCache) const
{
if (IsImmatureCoinBase(locked_chain) && IsInMainChain(locked_chain)) {
- if (fUseCache && fImmatureWatchCreditCached)
- return nImmatureWatchCreditCached;
- nImmatureWatchCreditCached = pwallet->GetCredit(*tx, ISMINE_WATCH_ONLY);
- fImmatureWatchCreditCached = true;
- return nImmatureWatchCreditCached;
+ return GetCachableAmount(IMMATURE_CREDIT, ISMINE_WATCH_ONLY, !fUseCache);
}
return 0;
@@ -4258,7 +4217,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
// No need to read and scan block if block was created before
// our wallet birthday (as adjusted for block time variability)
if (walletInstance->nTimeFirstKey) {
- if (Optional<int> first_block = locked_chain->findFirstBlockWithTimeAndHeight(walletInstance->nTimeFirstKey - TIMESTAMP_WINDOW, rescan_height)) {
+ if (Optional<int> first_block = locked_chain->findFirstBlockWithTimeAndHeight(walletInstance->nTimeFirstKey - TIMESTAMP_WINDOW, rescan_height, nullptr)) {
rescan_height = *first_block;
}
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 62ba0aa962..80a4e37bc7 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -369,24 +369,11 @@ public:
std::multimap<int64_t, CWalletTx*>::const_iterator m_it_wtxOrdered;
// memory only
- mutable bool fDebitCached;
- mutable bool fCreditCached;
- mutable bool fImmatureCreditCached;
- mutable bool fAvailableCreditCached;
- mutable bool fWatchDebitCached;
- mutable bool fWatchCreditCached;
- mutable bool fImmatureWatchCreditCached;
- mutable bool fAvailableWatchCreditCached;
+ enum AmountType { DEBIT, CREDIT, IMMATURE_CREDIT, AVAILABLE_CREDIT, AMOUNTTYPE_ENUM_ELEMENTS };
+ CAmount GetCachableAmount(AmountType type, const isminefilter& filter, bool recalculate = false) const;
+ mutable CachableAmount m_amounts[AMOUNTTYPE_ENUM_ELEMENTS];
mutable bool fChangeCached;
mutable bool fInMempool;
- mutable CAmount nDebitCached;
- mutable CAmount nCreditCached;
- mutable CAmount nImmatureCreditCached;
- mutable CAmount nAvailableCreditCached;
- mutable CAmount nWatchDebitCached;
- mutable CAmount nWatchCreditCached;
- mutable CAmount nImmatureWatchCreditCached;
- mutable CAmount nAvailableWatchCreditCached;
mutable CAmount nChangeCached;
CWalletTx(const CWallet* pwalletIn, CTransactionRef arg) : CMerkleTx(std::move(arg))
@@ -403,24 +390,8 @@ public:
nTimeReceived = 0;
nTimeSmart = 0;
fFromMe = false;
- fDebitCached = false;
- fCreditCached = false;
- fImmatureCreditCached = false;
- fAvailableCreditCached = false;
- fWatchDebitCached = false;
- fWatchCreditCached = false;
- fImmatureWatchCreditCached = false;
- fAvailableWatchCreditCached = false;
fChangeCached = false;
fInMempool = false;
- nDebitCached = 0;
- nCreditCached = 0;
- nImmatureCreditCached = 0;
- nAvailableCreditCached = 0;
- nWatchDebitCached = 0;
- nWatchCreditCached = 0;
- nAvailableWatchCreditCached = 0;
- nImmatureWatchCreditCached = 0;
nChangeCached = 0;
nOrderPos = -1;
}
@@ -464,14 +435,10 @@ public:
//! make sure balances are recalculated
void MarkDirty()
{
- fCreditCached = false;
- fAvailableCreditCached = false;
- fImmatureCreditCached = false;
- fWatchDebitCached = false;
- fWatchCreditCached = false;
- fAvailableWatchCreditCached = false;
- fImmatureWatchCreditCached = false;
- fDebitCached = false;
+ m_amounts[DEBIT].Reset();
+ m_amounts[CREDIT].Reset();
+ m_amounts[IMMATURE_CREDIT].Reset();
+ m_amounts[AVAILABLE_CREDIT].Reset();
fChangeCached = false;
}