diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/chain.cpp | 7 | ||||
-rw-r--r-- | src/chain.h | 4 | ||||
-rw-r--r-- | src/checkpoints.cpp | 32 | ||||
-rw-r--r-- | src/checkpoints.h | 27 | ||||
-rw-r--r-- | src/coins.h | 4 | ||||
-rw-r--r-- | src/init.cpp | 11 | ||||
-rw-r--r-- | src/interfaces/chain.cpp | 18 | ||||
-rw-r--r-- | src/interfaces/chain.h | 17 | ||||
-rw-r--r-- | src/primitives/transaction.h | 4 | ||||
-rw-r--r-- | src/qt/clientmodel.cpp | 1 | ||||
-rw-r--r-- | src/qt/guiutil.cpp | 5 | ||||
-rw-r--r-- | src/qt/guiutil.h | 5 | ||||
-rw-r--r-- | src/qt/intro.cpp | 13 | ||||
-rw-r--r-- | src/qt/intro.h | 5 | ||||
-rw-r--r-- | src/qt/optionsmodel.cpp | 5 | ||||
-rw-r--r-- | src/rpc/blockchain.cpp | 3 | ||||
-rw-r--r-- | src/rpc/rawtransaction.cpp | 10 | ||||
-rw-r--r-- | src/rpc/rawtransaction_util.cpp | 12 | ||||
-rw-r--r-- | src/rpc/rawtransaction_util.h | 22 | ||||
-rw-r--r-- | src/script/ismine.h | 29 | ||||
-rw-r--r-- | src/test/skiplist_tests.cpp | 46 | ||||
-rw-r--r-- | src/ui_interface.cpp | 4 | ||||
-rw-r--r-- | src/ui_interface.h | 3 | ||||
-rw-r--r-- | src/validation.cpp | 36 | ||||
-rw-r--r-- | src/wallet/rpcwallet.cpp | 22 | ||||
-rw-r--r-- | src/wallet/test/coinselector_tests.cpp | 5 | ||||
-rw-r--r-- | src/wallet/wallet.cpp | 101 | ||||
-rw-r--r-- | src/wallet/wallet.h | 47 |
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; } |