diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index ab2e5ecf..dce23106 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -196,6 +196,7 @@ def option_passed(option_without_dashes): 'txpropagate', 'wallet', 'walletbackup', + 'zapwallettxes', # Disabled('mempool_limit', "FAILS"), Disabled('mempool_reorg', "FAILS"), @@ -208,7 +209,6 @@ def option_passed(option_without_dashes): Disabled('invalidtxrequest', "FAILS"), Disabled('merkle_blocks', "FAILS"), Disabled('miningtest', "FAILS"), - Disabled('zapwallettxes', "FAILS"), Disabled('sendheaders', "FAILS"), Disabled('signrawtransactions', "FAILS"), # diff --git a/qa/rpc-tests/getchaintips.py b/qa/rpc-tests/getchaintips.py index 060574cf..8f4d0bf7 100755 --- a/qa/rpc-tests/getchaintips.py +++ b/qa/rpc-tests/getchaintips.py @@ -10,6 +10,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal +from test_framework.util import * class GetChainTipsTest (BitcoinTestFramework): @@ -42,9 +43,18 @@ def run_test (self): assert_equal (longTip['height'], 220) assert_equal (tips[0]['status'], 'active') - # Join the network halves and check that we now have two tips - # (at least at the nodes that previously had the short chain). - self.join_network () + stop_nodes(self.nodes) + wait_bitcoinds() + self.is_network_split = False + self.nodes = self.setup_nodes() + connect_nodes_bi(self.nodes, 0, 1) + connect_nodes_bi(self.nodes, 0, 2) + connect_nodes_bi(self.nodes, 0, 3) + connect_nodes_bi(self.nodes, 1, 2) + connect_nodes_bi(self.nodes, 1, 3) + connect_nodes_bi(self.nodes, 2, 3) + self.sync_blocks() + self.sync_all() tips = self.nodes[0].getchaintips () assert_equal (len (tips), 2) diff --git a/qa/rpc-tests/wallet.py b/qa/rpc-tests/wallet.py index 3d50e2c0..b7eaad4f 100755 --- a/qa/rpc-tests/wallet.py +++ b/qa/rpc-tests/wallet.py @@ -422,27 +422,13 @@ def run_test (self): {"address": self.nodes[2].getaddressforms(p2shAddress2)["bitcoincash"]}, {"label": ""}) + ''' #check if wallet or blochchain maintenance changes the balance self.sync_all() blocks = self.nodes[0].generate(2) self.sync_all() balance_nodes = [self.nodes[i].getbalance() for i in range(3)] block_count = self.nodes[0].getblockcount() - ''' - - # Check modes: - # - True: unicode escaped as \u.... - # - False: unicode directly as UTF-8 - ''' - for mode in [True, False]: - self.nodes[0].ensure_ascii = mode - # unicode check: Basic Multilingual Plane, Supplementary Plane respectively - for s in [u'рыба', u'𝅘𝅥𝅯']: - addr = self.nodes[0].getaccountaddress(s) - label = self.nodes[0].getaccount(addr) - assert_equal(label, s) - assert(s in self.nodes[0].listaccounts().keys()) - self.nodes[0].ensure_ascii = True # restore to default # maintenance tests @@ -464,12 +450,17 @@ def run_test (self): time.sleep(0.1) assert_equal(balance_nodes, [self.nodes[i].getbalance() for i in range(3)]) + ''' # Exercise listsinceblock with the last two blocks coinbase_tx_1 = self.nodes[0].listsinceblock(blocks[0]) assert_equal(coinbase_tx_1["lastblock"], blocks[1]) assert_equal(len(coinbase_tx_1["transactions"]), 1) + assert_equal(len(coinbase_tx_1["transactions"]), 2) + print(coinbase_tx_1["transactions"][0]) assert_equal(coinbase_tx_1["transactions"][0]["blockhash"], blocks[1]) assert_equal(coinbase_tx_1["transactions"][0]["satoshi"], Decimal('2500000000')) + assert_equal(coinbase_tx_1["transactions"][0]["amount"], Decimal('50.000000')) + print(self.nodes[0].listsinceblock(blocks[1])["transactions"]) assert_equal(len(self.nodes[0].listsinceblock(blocks[1])["transactions"]), 0) ''' diff --git a/src/blockgeneration/blockgeneration.cpp b/src/blockgeneration/blockgeneration.cpp index 5a350494..4e78f415 100644 --- a/src/blockgeneration/blockgeneration.cpp +++ b/src/blockgeneration/blockgeneration.cpp @@ -74,6 +74,7 @@ void ThreadMiner(void *parg, bool shutdownOnly) if (minerThreads != nullptr) { minerThreads->interrupt_all(); + minerThreads->join_all(); delete minerThreads; minerThreads = nullptr; return; @@ -108,6 +109,7 @@ void ThreadMinter(void *parg, bool shutdownOnly) if (minterThreads != nullptr) { minterThreads->interrupt_all(); + minterThreads->join_all(); delete minterThreads; minterThreads = nullptr; return; diff --git a/src/chain/tx.cpp b/src/chain/tx.cpp index 7ee2c33f..a2fabfdc 100644 --- a/src/chain/tx.cpp +++ b/src/chain/tx.cpp @@ -203,10 +203,8 @@ unsigned int CTransaction::CalculateModifiedSize(unsigned int nTxSize) const std::string CTransaction::ToString() const { std::string str; - str += strprintf( - "CTransaction(hash=%s, ver=%d, nTime=%u, vin.size=%u, vout.size=%u, nLockTime=%u, serviceReferenceHash=%s)\n", - GetHash().ToString().substr(0, 10), nVersion, nTime, vin.size(), vout.size(), nLockTime, - serviceReferenceHash.GetHex().c_str()); + str += strprintf("CTransaction(hash=%s, ver=%d, nTime=%u, vin.size=%u, vout.size=%u, nLockTime=%u)\n", + GetHash().ToString().substr(0, 10), nVersion, nTime, vin.size(), vout.size(), nLockTime); for (unsigned int i = 0; i < vin.size(); i++) str += " " + vin[i].ToString() + "\n"; for (unsigned int i = 0; i < vout.size(); i++) @@ -387,10 +385,10 @@ bool GetTransaction(const uint256 &hash, if (fAllowSlow) // use coin database to locate block that contains transaction, and scan it { - const Coin &coin = AccessByTxid(*(pnetMan->getChainActive()->pcoinsTip), hash); - if (!coin.IsSpent()) + CoinAccessor coin(*(pnetMan->getChainActive()->pcoinsTip), hash); + if (!coin->IsSpent()) { - pindexSlow = pnetMan->getChainActive()->chainActive[coin.nHeight]; + pindexSlow = pnetMan->getChainActive()->chainActive[coin->nHeight]; } } diff --git a/src/coins.cpp b/src/coins.cpp index 7e072f29..6ab1bbd9 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -23,10 +23,12 @@ #include "consensus/consensus.h" #include "memusage.h" #include "random.h" +#include "undo.h" #include "util/logger.h" #include "util/util.h" #include +static const Coin emptyCoin; bool CCoinsView::GetCoin(const COutPoint &outpoint, Coin &coin) const { return false; } bool CCoinsView::HaveCoin(const COutPoint &outpoint) const { return false; } @@ -64,17 +66,22 @@ CCoinsViewCache::CCoinsViewCache(CCoinsView *baseIn) : CCoinsViewBacked(baseIn), size_t CCoinsViewCache::DynamicMemoryUsage() const { - LOCK(cs_utxo); + READLOCK(cs_utxo); return memusage::DynamicUsage(cacheCoins) + cachedCoinsUsage; } +size_t CCoinsViewCache::_DynamicMemoryUsage() const { return memusage::DynamicUsage(cacheCoins) + cachedCoinsUsage; } size_t CCoinsViewCache::ResetCachedCoinUsage() const { - LOCK(cs_utxo); + bool drifted = false; size_t newCachedCoinsUsage = 0; - for (CCoinsMap::iterator it = cacheCoins.begin(); it != cacheCoins.end(); it++) - newCachedCoinsUsage += it->second.coin.DynamicMemoryUsage(); - if (cachedCoinsUsage != newCachedCoinsUsage) + { + READLOCK(cs_utxo); + for (CCoinsMap::iterator it = cacheCoins.begin(); it != cacheCoins.end(); it++) + newCachedCoinsUsage += it->second.coin.DynamicMemoryUsage(); + drifted = (cachedCoinsUsage != newCachedCoinsUsage); + } + if (drifted) { error( "Resetting: cachedCoinsUsage has drifted - before %lld after %lld", cachedCoinsUsage, newCachedCoinsUsage); @@ -83,15 +90,26 @@ size_t CCoinsViewCache::ResetCachedCoinUsage() const return newCachedCoinsUsage; } -CCoinsMap::iterator CCoinsViewCache::FetchCoin(const COutPoint &outpoint) const +CCoinsMap::iterator CCoinsViewCache::FetchCoin(const COutPoint &outpoint, CDeferredSharedLocker *lock) const { - LOCK(cs_utxo); - CCoinsMap::iterator it = cacheCoins.find(outpoint); - if (it != cacheCoins.end()) - return it; + // When fetching a coin, we only need the shared lock if the coin exists in the cache. + // So we have the Locker object take the shared lock and return with the read lock held if the coin was in cache. + { + if (lock) + lock->lock_shared(); + CCoinsMap::iterator it = cacheCoins.find(outpoint); + if (it != cacheCoins.end()) + return it; + if (lock) + lock->unlock(); + } Coin tmp; if (!base->GetCoin(outpoint, tmp)) return cacheCoins.end(); + + // But if the coin is NOT in the cache, we need to grab the exclusive lock in order to modify the cache + if (lock) + lock->lock(); CCoinsMap::iterator ret = cacheCoins .emplace(std::piecewise_construct, std::forward_as_tuple(outpoint), std::forward_as_tuple(std::move(tmp))) @@ -112,8 +130,8 @@ CCoinsMap::iterator CCoinsViewCache::FetchCoin(const COutPoint &outpoint) const bool CCoinsViewCache::GetCoin(const COutPoint &outpoint, Coin &coin) const { - LOCK(cs_utxo); - CCoinsMap::const_iterator it = FetchCoin(outpoint); + CDeferredSharedLocker lock(cs_utxo); + CCoinsMap::const_iterator it = FetchCoin(outpoint, &lock); if (it != cacheCoins.end()) { coin = it->second.coin; @@ -124,7 +142,7 @@ bool CCoinsViewCache::GetCoin(const COutPoint &outpoint, Coin &coin) const void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin &&coin, bool possible_overwrite) { - LOCK(cs_utxo); + WRITELOCK(cs_utxo); assert(!coin.IsSpent()); if (coin.out.scriptPubKey.IsUnspendable()) return; @@ -152,24 +170,10 @@ void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin &&coin, bool possi nBestCoinHeight = it->second.coin.nHeight; } -void AddCoins(CCoinsViewCache &cache, const CTransaction &tx, int nHeight) -{ - bool fCoinbase = tx.IsCoinBase(); - bool fCoinStake = tx.IsCoinStake(); - uint64_t nTime = tx.nTime; - const uint256 &txid = tx.GetHash(); - for (size_t i = 0; i < tx.vout.size(); ++i) - { - // Pass fCoinbase as the possible_overwrite flag to AddCoin, in order to correctly - // deal with the pre-BIP30 occurrances of duplicate coinbase transactions. - cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase, fCoinStake, nTime), fCoinbase); - } -} - void CCoinsViewCache::SpendCoin(const COutPoint &outpoint, Coin *moveout) { - LOCK(cs_utxo); - CCoinsMap::iterator it = FetchCoin(outpoint); + WRITELOCK(cs_utxo); + CCoinsMap::iterator it = FetchCoin(outpoint, nullptr); if (it == cacheCoins.end()) return; cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage(); @@ -188,15 +192,13 @@ void CCoinsViewCache::SpendCoin(const COutPoint &outpoint, Coin *moveout) } } -static const Coin coinEmpty; - -const Coin &CCoinsViewCache::AccessCoin(const COutPoint &outpoint) const +const Coin &CCoinsViewCache::_AccessCoin(const COutPoint &outpoint) const { - LOCK(cs_utxo); - CCoinsMap::const_iterator it = FetchCoin(outpoint); + AssertLockHeld(cs_utxo); + CCoinsMap::const_iterator it = FetchCoin(outpoint, nullptr); if (it == cacheCoins.end()) { - return coinEmpty; + return emptyCoin; } else { @@ -206,21 +208,21 @@ const Coin &CCoinsViewCache::AccessCoin(const COutPoint &outpoint) const bool CCoinsViewCache::HaveCoin(const COutPoint &outpoint) const { - LOCK(cs_utxo); - CCoinsMap::const_iterator it = FetchCoin(outpoint); + CDeferredSharedLocker lock(cs_utxo); + CCoinsMap::const_iterator it = FetchCoin(outpoint, &lock); return (it != cacheCoins.end() && !it->second.coin.IsSpent()); } bool CCoinsViewCache::HaveCoinInCache(const COutPoint &outpoint) const { - LOCK(cs_utxo); + READLOCK(cs_utxo); CCoinsMap::const_iterator it = cacheCoins.find(outpoint); return it != cacheCoins.end(); } uint256 CCoinsViewCache::GetBestBlock() const { - LOCK(cs_utxo); + READLOCK(cs_utxo); if (hashBlock.IsNull()) hashBlock = base->GetBestBlock(); return hashBlock; @@ -228,7 +230,7 @@ uint256 CCoinsViewCache::GetBestBlock() const void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) { - LOCK(cs_utxo); + WRITELOCK(cs_utxo); hashBlock = hashBlockIn; } @@ -237,7 +239,7 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint64_t nBestCoinHeightIn, size_t &nChildCachedCoinsUsage) { - LOCK(cs_utxo); + WRITELOCK(cs_utxo); for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) { if (it->second.flags & CCoinsCacheEntry::DIRTY) @@ -309,14 +311,14 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, bool CCoinsViewCache::Flush() { - LOCK(cs_utxo); + WRITELOCK(cs_utxo); bool fOk = base->BatchWrite(cacheCoins, hashBlock, nBestCoinHeight, cachedCoinsUsage); return fOk; } void CCoinsViewCache::Trim(size_t nTrimSize) const { - LOCK(cs_utxo); + WRITELOCK(cs_utxo); uint64_t nTrimmed = 0; uint64_t nTrimmedByHeight = 0; @@ -326,7 +328,7 @@ void CCoinsViewCache::Trim(size_t nTrimSize) const // if we've already walked the nTrimHeight all the way back as far as we can go and there is nothing to trim // then no need to check further. This should be the typical state after a block sync is completed and there is // enough dbcache to hold all the coins from recent transactions in memory. - if (nTrimHeight == 0 && DynamicMemoryUsage() <= nTrimSize) + if (nTrimHeight == 0 && _DynamicMemoryUsage() <= nTrimSize) return; // Begin first Trim loop. This loop will trim coins from cache by the coin height, removing the oldest coins first. @@ -335,14 +337,14 @@ void CCoinsViewCache::Trim(size_t nTrimSize) const bool fDone = false; uint64_t nSmallestDelta = 50; // number of blocks to adjust trim height by CCoinsMap::iterator iter = cacheCoins.begin(); - while (!fDone && DynamicMemoryUsage() > nTrimSize) + while (!fDone && _DynamicMemoryUsage() > nTrimSize) { LogPrint("COINDB", "cacheCoinsUsage at start: %d total dynamic usage: %d trim to size: %d nBestCoinHeight: %d " "trim height:%d\n", - cachedCoinsUsage, DynamicMemoryUsage(), nTrimSize, nBestCoinHeight, nTrimHeight); + cachedCoinsUsage, _DynamicMemoryUsage(), nTrimSize, nBestCoinHeight, nTrimHeight); iter = cacheCoins.begin(); - while (DynamicMemoryUsage() > nTrimSize) + while (_DynamicMemoryUsage() > nTrimSize) { if (iter == cacheCoins.end()) { @@ -363,7 +365,7 @@ void CCoinsViewCache::Trim(size_t nTrimSize) const } // Gradually increase the nTrimHeight if we didn't trim enought entries. - if (fDone && DynamicMemoryUsage() > nTrimSize && nTrimHeightDelta > nSmallestDelta) + if (fDone && _DynamicMemoryUsage() > nTrimSize && nTrimHeightDelta > nSmallestDelta) { if (nTrimHeightDelta <= nSmallestDelta * 100) nTrimHeightDelta = @@ -388,7 +390,7 @@ void CCoinsViewCache::Trim(size_t nTrimSize) const // If trimming by coin height failed to find any or enough coins to trim then trim the cache by ignoring // coin height. While this is not ideal we still have to trim to keep the cache from growing unbounded. iter = cacheCoins.begin(); - while (DynamicMemoryUsage() > nTrimSize) + while (_DynamicMemoryUsage() > nTrimSize) { if (iter == cacheCoins.end()) break; @@ -427,7 +429,7 @@ void CCoinsViewCache::Trim(size_t nTrimSize) const void CCoinsViewCache::Uncache(const COutPoint &hash) { - LOCK(cs_utxo); + WRITELOCK(cs_utxo); CCoinsMap::iterator it = cacheCoins.find(hash); // only uncache coins that are not dirty. @@ -446,26 +448,25 @@ void CCoinsViewCache::UncacheTx(const CTransaction &tx) unsigned int CCoinsViewCache::GetCacheSize() const { - LOCK(cs_utxo); + READLOCK(cs_utxo); return cacheCoins.size(); } CAmount CCoinsViewCache::GetValueIn(const CTransaction &tx) const { - LOCK(cs_utxo); + READLOCK(cs_utxo); if (tx.IsCoinBase()) return 0; CAmount nResult = 0; for (unsigned int i = 0; i < tx.vin.size(); i++) - nResult += AccessCoin(tx.vin[i].prevout).out.nValue; + nResult += _AccessCoin(tx.vin[i].prevout).out.nValue; return nResult; } bool CCoinsViewCache::HaveInputs(const CTransaction &tx) const { - LOCK(cs_utxo); if (!tx.IsCoinBase()) { for (unsigned int i = 0; i < tx.vin.size(); i++) @@ -481,17 +482,17 @@ bool CCoinsViewCache::HaveInputs(const CTransaction &tx) const double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight, CAmount &inChainInputValue) const { - LOCK(cs_utxo); + READLOCK(cs_utxo); inChainInputValue = 0; if (tx.IsCoinBase()) return 0.0; double dResult = 0.0; BOOST_FOREACH (const CTxIn &txin, tx.vin) { - const Coin &coin = AccessCoin(txin.prevout); + const Coin &coin = _AccessCoin(txin.prevout); if (coin.IsSpent()) continue; - if (coin.nHeight <= nHeight) + if ((int64_t)coin.nHeight <= (int64_t)nHeight) { dResult += coin.out.nValue * (nHeight - coin.nHeight); inChainInputValue += coin.out.nValue; @@ -505,15 +506,100 @@ CCoinsViewCursor::~CCoinsViewCursor() {} static const size_t nMaxOutputsPerBlock = DEFAULT_LARGEST_TRANSACTION / ::GetSerializeSize(CTxOut(), SER_NETWORK, PROTOCOL_VERSION); -const Coin &AccessByTxid(const CCoinsViewCache &view, const uint256 &txid) +CoinAccessor::CoinAccessor(const CCoinsViewCache &view, const uint256 &txid) : cache(&view), lock(cache->csCacheInsert) { + EnterCritical("CCoinsViewCache.cs_utxo", __FILE__, __LINE__, (void *)(&cache->cs_utxo)); + cache->cs_utxo.lock_shared(); COutPoint iter(txid, 0); + coin = &emptyCoin; while (iter.n < nMaxOutputsPerBlock) { - const Coin &alternate = view.AccessCoin(iter); + const Coin &alternate = view._AccessCoin(iter); if (!alternate.IsSpent()) - return alternate; + { + coin = &alternate; + return; + } ++iter.n; } - return coinEmpty; +} + +CoinAccessor::CoinAccessor(const CCoinsViewCache &cacheObj, const COutPoint &output) + : cache(&cacheObj), lock(cache->csCacheInsert) +{ + EnterCritical("CCoinsViewCache.cs_utxo", __FILE__, __LINE__, (void *)(&cache->cs_utxo)); + cache->cs_utxo.lock_shared(); + it = cache->FetchCoin(output, &lock); + if (it != cache->cacheCoins.end()) + coin = &it->second.coin; + else + coin = &emptyCoin; +} + +CoinAccessor::~CoinAccessor() +{ + coin = nullptr; + cache->cs_utxo.unlock_shared(); + LeaveCritical(); +} + + +CoinModifier::CoinModifier(const CCoinsViewCache &cacheObj, const COutPoint &output) : cache(&cacheObj) +{ + EnterCritical("CCoinsViewCache.cs_utxo", __FILE__, __LINE__, (void *)(&cache->cs_utxo)); + cache->cs_utxo.lock(); + it = cache->FetchCoin(output, nullptr); + if (it != cache->cacheCoins.end()) + coin = &it->second.coin; + else + coin = &emptyCoin; +} + +CoinModifier::~CoinModifier() +{ + coin = nullptr; + cache->cs_utxo.unlock(); + LeaveCritical(); +} + +void AddCoins(CCoinsViewCache &cache, const CTransaction &tx, int nHeight) +{ + bool fCoinbase = tx.IsCoinBase(); + bool fCoinStake = tx.IsCoinStake(); + uint64_t nTime = tx.nTime; + const uint256 &txid = tx.GetHash(); + for (size_t i = 0; i < tx.vout.size(); ++i) + { + // Pass fCoinbase as the possible_overwrite flag to AddCoin, in order to correctly + // deal with the pre-BIP30 occurrances of duplicate coinbase transactions. + cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase, fCoinStake, nTime), fCoinbase); + } +} + +void SpendCoins(const CTransaction &tx, CCoinsViewCache &inputs, CTxUndo &txundo) +{ + // mark inputs spent + if (!tx.IsCoinBase()) + { + txundo.vprevout.reserve(tx.vin.size()); + for (const CTxIn &txin : tx.vin) + { + txundo.vprevout.emplace_back(); + inputs.SpendCoin(txin.prevout, &txundo.vprevout.back()); + } + } +} + +void UpdateCoins(const CTransaction &tx, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight) +{ + // mark inputs spent + SpendCoins(tx, inputs, txundo); + // add outputs + AddCoins(inputs, tx, nHeight); +} + +void UpdateCoins(const CTransaction &tx, CCoinsViewCache &inputs, int nHeight) +{ + CTxUndo txundo; + UpdateCoins(tx, inputs, txundo, nHeight); } diff --git a/src/coins.h b/src/coins.h index c930e8fd..0cd2083c 100644 --- a/src/coins.h +++ b/src/coins.h @@ -48,6 +48,7 @@ #include +class CTxUndo; struct CCoinsStats { int nHeight; @@ -79,12 +80,12 @@ class Coin CTxOut out; //! whether containing transaction was a coinbase - uint32_t fCoinBase : 1; + uint8_t fCoinBase : 1; - uint32_t fCoinStake : 1; + uint8_t fCoinStake : 1; //! at which height this containing transaction was included in the active block chain - uint32_t nHeight : 31; + uint32_t nHeight; uint64_t nTime; @@ -101,14 +102,14 @@ class Coin void Clear() { out.SetNull(); - fCoinBase = false; - fCoinStake = false; + fCoinBase = 0; + fCoinStake = 0; nHeight = 0; nTime = 0; } //! empty constructor - Coin() : fCoinBase(false), nHeight(0) {} + Coin() { Clear(); } bool IsCoinBase() const { return fCoinBase; } bool IsCoinStake() const { return fCoinStake; } template @@ -134,7 +135,7 @@ class Coin template void Unserialize(Stream &s) { - uint32_t code = 0; + uint8_t code = 0; ::Unserialize(s, VARINT(code)); fCoinBase = code & 1; fCoinStake = code & 2; @@ -201,7 +202,7 @@ class CCoinsViewCursor class CCoinsView { public: - mutable CCriticalSection cs_utxo; + mutable CSharedCriticalSection cs_utxo; //! Retrieve the Coin (unspent transaction output) for a given outpoint. virtual bool GetCoin(const COutPoint &outpoint, Coin &coin) const; @@ -254,6 +255,9 @@ class CCoinsViewBacked : public CCoinsView /** CCoinsView that adds a memory cache for transactions to another CCoinsView */ class CCoinsViewCache : public CCoinsViewBacked { + friend class CoinAccessor; + friend class CoinModifier; + protected: /** * Make mutable so that we can "fill the cache" even from Get-methods @@ -262,7 +266,7 @@ class CCoinsViewCache : public CCoinsViewBacked mutable uint256 hashBlock; mutable uint64_t nBestCoinHeight; mutable CCoinsMap cacheCoins; - + mutable CSharedCriticalSection csCacheInsert; /* Cached dynamic memory usage for the inner Coin objects. */ mutable size_t cachedCoinsUsage; @@ -292,7 +296,7 @@ class CCoinsViewCache : public CCoinsViewBacked * more efficient than GetCoin. Modifications to other cache entries are * allowed while accessing the returned pointer. */ - const Coin &AccessCoin(const COutPoint &output) const; + const Coin &_AccessCoin(const COutPoint &output) const; /** * Add a coin. Set potential_overwrite to true if a non-pruned version may @@ -343,6 +347,7 @@ class CCoinsViewCache : public CCoinsViewBacked //! Calculate the size of the cache (in bytes) size_t DynamicMemoryUsage() const; + size_t _DynamicMemoryUsage() const; //! Recalculate and Reset the size of cachedCoinsUsage size_t ResetCachedCoinUsage() const; @@ -368,7 +373,7 @@ class CCoinsViewCache : public CCoinsViewBacked double GetPriority(const CTransaction &tx, int nHeight, CAmount &inChainInputValue) const; private: - CCoinsMap::iterator FetchCoin(const COutPoint &outpoint) const; + CCoinsMap::iterator FetchCoin(const COutPoint &outpoint, CDeferredSharedLocker *lock) const; /** * By making the copy constructor private, we prevent accidentally using it when one intends to create a cache on @@ -377,13 +382,55 @@ class CCoinsViewCache : public CCoinsViewBacked CCoinsViewCache(const CCoinsViewCache &); }; +class CoinModifier +{ +protected: + const CCoinsViewCache *cache; + CCoinsMap::const_iterator it; + const Coin *coin; + +public: + operator bool() const { return coin != nullptr; } + const Coin *operator->() { return coin; } + const Coin &operator*() { return *coin; } + CoinModifier(const CCoinsViewCache &cacheObj, const COutPoint &output); + ~CoinModifier(); + friend class CCoinsViewCache; +}; + + +/** + * A reference to an immutable cache entry. This class holds the appropriate lock for you + * while you access the underlying data. + */ +class CoinAccessor +{ +protected: + const CCoinsViewCache *cache; + CCoinsMap::const_iterator it; + const Coin *coin; + CDeferredSharedLocker lock; + +public: + operator bool() const { return coin != nullptr; } + const Coin *operator->() { return coin; } + const Coin &operator*() { return *coin; } + CoinAccessor(const CCoinsViewCache &cacheObj, const COutPoint &output); + // finds the first unspent output in this tx (slow) + CoinAccessor(const CCoinsViewCache &cacheObj, const uint256 &txid); + ~CoinAccessor(); + friend class CCoinsViewCache; +}; + //! Utility function to add all of a transaction's outputs to a cache. // It assumes that overwrites are only possible for coinbase transactions, // TODO: pass in a boolean to limit these possible overwrites to known // (pre-BIP34) cases. void AddCoins(CCoinsViewCache &cache, const CTransaction &tx, int nHeight); -//! Utility function to find any unspent output with a given txid. -const Coin &AccessByTxid(const CCoinsViewCache &cache, const uint256 &txid); +/** Apply the effects of this transaction on the UTXO set represented by view */ +void UpdateCoins(const CTransaction &tx, CCoinsViewCache &inputs, int nHeight); +void UpdateCoins(const CTransaction &tx, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight); + #endif // BITCOIN_COINS_H diff --git a/src/eccoind.cpp b/src/eccoind.cpp index 879bef90..22a5e4dc 100644 --- a/src/eccoind.cpp +++ b/src/eccoind.cpp @@ -177,7 +177,7 @@ bool AppInit(int argc, char *argv[]) { WaitForShutdown(&threadGroup); } - Shutdown(); + Shutdown(threadGroup); return fRet; } diff --git a/src/init.cpp b/src/init.cpp index e66704d9..ebaae30b 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -187,7 +187,7 @@ void Interrupt(thread_group &threadGroup) InterruptScriptCheck(); } -void Shutdown() +void Shutdown(thread_group &threadGroup) { LogPrintf("%s: In progress...\n", __func__); static CCriticalSection cs_Shutdown; @@ -196,6 +196,7 @@ void Shutdown() { return; } + threadGroup.join_all(); /// Note: Shutdown() must be able to handle cases in which AppInit2() failed part of the way, /// for example if the data directory was found to be locked. @@ -204,6 +205,16 @@ void Shutdown() RenameThread("bitcoin-shutoff"); mempool.AddTransactionsUpdated(1); + { + LOCK(cs_main); + if (pnetMan->getChainActive()->pcoinsTip != nullptr) + { + // Flush state and clear cache completely to release as much memory as possible before continuing. + FlushStateToDisk(); + pnetMan->getChainActive()->pcoinsTip->Clear(); + } + } + StopHTTPRPC(); StopRPC(); StopHTTPServer(); @@ -239,24 +250,18 @@ void Shutdown() { LOCK(cs_main); - if (pnetMan) + if (pnetMan->getChainActive()->pcoinsTip != nullptr) { - if (pnetMan->getChainActive()->pcoinsTip != nullptr) - { - FlushStateToDisk(); - } - pnetMan->getChainActive()->pcoinsTip.reset(); - pnetMan->getChainActive()->pcoinsTip = nullptr; + FlushStateToDisk(); } + pnetMan->getChainActive()->pcoinsTip.reset(); + pnetMan->getChainActive()->pcoinsTip = nullptr; pcoinscatcher.reset(); pcoinscatcher = nullptr; pcoinsdbview.reset(); pcoinsdbview = nullptr; - if (pnetMan) - { - pnetMan->getChainActive()->pblocktree.reset(); - pnetMan->getChainActive()->pblocktree = nullptr; - } + pnetMan->getChainActive()->pblocktree.reset(); + pnetMan->getChainActive()->pblocktree = nullptr; } if (pwalletMain) @@ -1663,7 +1668,7 @@ bool AppInit2(thread_group &threadGroup) // ********************************************************* Step 12: finished SetRPCWarmupFinished(); - LogPrintf("Done loading"); + LogPrintf("Done loading\n"); if (pwalletMain) diff --git a/src/init.h b/src/init.h index c289c801..e4daa57d 100644 --- a/src/init.h +++ b/src/init.h @@ -35,7 +35,7 @@ void StartShutdown(); bool ShutdownRequested(); /** Interrupt threads */ void Interrupt(thread_group &threadGroup); -void Shutdown(); +void Shutdown(thread_group &threadGroup); //! Initialize the logging infrastructure void InitLogging(); //! Parameter interaction: change current parameters depending on various rules diff --git a/src/main.cpp b/src/main.cpp index d6b85451..15dea05b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -442,11 +442,9 @@ unsigned int GetP2SHSigOpCount(const CTransaction &tx, const CCoinsViewCache &in unsigned int nSigOps = 0; for (unsigned int i = 0; i < tx.vin.size(); i++) { - const CTxOut &prevout = inputs.AccessCoin(tx.vin[i].prevout).out; - if (prevout.scriptPubKey.IsPayToScriptHash()) - { - nSigOps += prevout.scriptPubKey.GetSigOpCount(tx.vin[i].scriptSig); - } + CoinAccessor coin(inputs, tx.vin[i].prevout); + if (coin && coin->out.scriptPubKey.IsPayToScriptHash()) + nSigOps += coin->out.scriptPubKey.GetSigOpCount(tx.vin[i].scriptSig); } return nSigOps; } @@ -621,8 +619,8 @@ bool AcceptToMemoryPoolWorker(CTxMemPool &pool, bool fSpendsCoinbase = false; for (auto const &txin : tx.vin) { - const Coin coin = view.AccessCoin(txin.prevout); - if (coin.IsCoinBase()) + CoinAccessor coin(view, txin.prevout); + if (coin->IsCoinBase()) { fSpendsCoinbase = true; break; @@ -905,28 +903,6 @@ bool ReadBlockFromDisk(CBlock &block, const CBlockIndex *pindex, const Consensus return true; } -void UpdateCoins(const CTransaction &tx, CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight) -{ - // mark inputs spent - if (!tx.IsCoinBase()) - { - txundo.vprevout.reserve(tx.vin.size()); - for (auto const &txin : tx.vin) - { - txundo.vprevout.emplace_back(); - inputs.SpendCoin(txin.prevout, &txundo.vprevout.back()); - } - } - // add outputs - AddCoins(inputs, tx, nHeight); -} - -void UpdateCoins(const CTransaction &tx, CValidationState &state, CCoinsViewCache &inputs, int nHeight) -{ - CTxUndo txundo; - UpdateCoins(tx, state, inputs, txundo, nHeight); -} - bool CScriptCheck::operator()() { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; @@ -959,7 +935,8 @@ bool CheckTxInputs(const CTransaction &tx, CValidationState &state, const CCoins for (uint64_t i = 0; i < tx.vin.size(); i++) { const COutPoint &prevout = tx.vin[i].prevout; - const Coin &coin = inputs.AccessCoin(prevout); + Coin coin; + inputs.GetCoin(prevout, coin); // Make a copy so I don't hold the utxo lock assert(!coin.IsSpent()); // If prev is coinbase or coinstake, check that it's matured @@ -1044,16 +1021,16 @@ bool CheckInputs(const CTransaction &tx, for (unsigned int i = 0; i < tx.vin.size(); i++) { const COutPoint &prevout = tx.vin[i].prevout; - const Coin &coin = inputs.AccessCoin(prevout); - assert(!coin.IsSpent()); + CoinAccessor coin(inputs, prevout); + assert(!coin->IsSpent()); // We very carefully only pass in things to CScriptCheck which // are clearly committed. This provides // a sanity check that our caching is not introducing consensus // failures through additional data in, eg, the coins being // spent being checked as a part of CScriptCheck. - const CScript &scriptPubKey = coin.out.scriptPubKey; - const CAmount amount = coin.out.nValue; + const CScript &scriptPubKey = coin->out.scriptPubKey; + const CAmount amount = coin->out.nValue; // Verify signature CScriptCheck check(scriptPubKey, amount, tx, i, flags, cacheStore); @@ -1147,97 +1124,110 @@ void static FlushBlockFile(bool fFinalize = false) */ bool FlushStateToDisk(CValidationState &state, FlushStateMode mode) { - LOCK2(cs_main, cs_LastBlockFile); static int64_t nLastWrite = 0; static int64_t nLastFlush = 0; static int64_t nLastSetChain = 0; - try - { - int64_t nNow = GetTimeMicros(); - // Avoid writing/flushing immediately after startup. - if (nLastWrite == 0) + int64_t nNow = GetTimeMicros(); + // Avoid writing/flushing immediately after startup. + if (nLastWrite == 0) + { + nLastWrite = nNow; + } + if (nLastFlush == 0) + { + nLastFlush = nNow; + } + if (nLastSetChain == 0) + { + nLastSetChain = nNow; + } + size_t cacheSize = pnetMan->getChainActive()->pcoinsTip->DynamicMemoryUsage(); + static int64_t nSizeAfterLastFlush = 0; + // The cache is close to the limit. Try to flush and trim. + bool fCacheCritical = ((mode == FLUSH_STATE_IF_NEEDED) && (cacheSize > nCoinCacheUsage * 0.995)) || + (cacheSize - nSizeAfterLastFlush > (int64_t)nMaxCacheIncreaseSinceLastFlush); + // It's been a while since we wrote the block index to disk. Do this frequently, so we don't need to redownload + // after a crash. + bool fPeriodicWrite = + mode == FLUSH_STATE_PERIODIC && nNow > nLastWrite + (int64_t)DATABASE_WRITE_INTERVAL * 1000000; + // It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage. + bool fPeriodicFlush = + mode == FLUSH_STATE_PERIODIC && nNow > nLastFlush + (int64_t)DATABASE_FLUSH_INTERVAL * 1000000; + // Combine all conditions that result in a full cache flush. + bool fDoFullFlush = (mode == FLUSH_STATE_ALWAYS) || fCacheCritical || fPeriodicFlush; + // Write blocks and block index to disk. + if (fDoFullFlush || fPeriodicWrite) + { + // Depend on nMinDiskSpace to ensure we can write block index + if (!CheckDiskSpace(0)) { - nLastWrite = nNow; + return state.Error("out of disk space"); } - if (nLastFlush == 0) + FlushBlockFile(); + // Then update all block file information (which may refer to block and undo files). { - nLastFlush = nNow; - } - if (nLastSetChain == 0) - { - nLastSetChain = nNow; - } - size_t cacheSize = pnetMan->getChainActive()->pcoinsTip->DynamicMemoryUsage(); - // The cache is large and close to the limit, but we have time now (not in the middle of a block processing). - bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize * (10.0 / 9) > nCoinCacheUsage; - // The cache is over the limit, we have to write now. - bool fCacheCritical = mode == FLUSH_STATE_IF_NEEDED && cacheSize > nCoinCacheUsage; - // It's been a while since we wrote the block index to disk. Do this frequently, so we don't need to redownload - // after a crash. - bool fPeriodicWrite = - mode == FLUSH_STATE_PERIODIC && nNow > nLastWrite + (int64_t)DATABASE_WRITE_INTERVAL * 1000000; - // It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage. - bool fPeriodicFlush = - mode == FLUSH_STATE_PERIODIC && nNow > nLastFlush + (int64_t)DATABASE_FLUSH_INTERVAL * 1000000; - // Combine all conditions that result in a full cache flush. - bool fDoFullFlush = (mode == FLUSH_STATE_ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush; - // Write blocks and block index to disk. - if (fDoFullFlush || fPeriodicWrite) - { - // Depend on nMinDiskSpace to ensure we can write block index - if (!CheckDiskSpace(0)) - return state.Error("out of disk space"); - // First make sure all block and undo data is flushed to disk. - FlushBlockFile(); - // Then update all block file information (which may refer to block and undo files). + std::vector > vFiles; + vFiles.reserve(setDirtyFileInfo.size()); + for (std::set::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end();) { - std::vector > vFiles; - vFiles.reserve(setDirtyFileInfo.size()); - for (std::set::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end();) - { - vFiles.push_back(std::make_pair(*it, &vinfoBlockFile[*it])); - setDirtyFileInfo.erase(it++); - } - std::vector vBlocks; - vBlocks.reserve(setDirtyBlockIndex.size()); - for (std::set::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end();) - { - vBlocks.push_back(*it); - setDirtyBlockIndex.erase(it++); - } - if (!pnetMan->getChainActive()->pblocktree->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) - { - return AbortNode(state, "Files to write to block index database"); - } + vFiles.push_back(std::make_pair(*it, &vinfoBlockFile[*it])); + setDirtyFileInfo.erase(it++); + } + std::vector vBlocks; + vBlocks.reserve(setDirtyBlockIndex.size()); + for (std::set::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end();) + { + vBlocks.push_back(*it); + setDirtyBlockIndex.erase(it++); + } + if (!pnetMan->getChainActive()->pblocktree->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) + { + return AbortNode(state, "Files to write to block index database"); } - nLastWrite = nNow; } - // Flush best chain related state. This can only be done if the blocks / block index write was also done. - if (fDoFullFlush) + nLastWrite = nNow; + } + // Flush best chain related state. This can only be done if the blocks / block index write was also done. + if (fDoFullFlush) + { + // Typical Coin structures on disk are around 48 bytes in size. + // Pushing a new one to the database can cause it to be written + // twice (once in the log, and once in the tables). This is already + // an overestimation, as most will delete an existing entry or + // overwrite one. Still, use a conservative safety factor of 2. + if (!CheckDiskSpace(48 * 2 * 2 * pnetMan->getChainActive()->pcoinsTip->GetCacheSize())) { - // Typical CCoins structures on disk are around 128 bytes in size. - // Pushing a new one to the database can cause it to be written - // twice (once in the log, and once in the tables). This is already - // an overestimation, as most will delete an existing entry or - // overwrite one. Still, use a conservative safety factor of 2. - if (!CheckDiskSpace(128 * 2 * 2 * pnetMan->getChainActive()->pcoinsTip->GetCacheSize())) - return state.Error("out of disk space"); - // Flush the chainstate (which may refer to block index entries). - if (!pnetMan->getChainActive()->pcoinsTip->Flush()) - return AbortNode(state, "Failed to write to coin database"); - nLastFlush = nNow; + return state.Error("out of disk space"); + } + // Flush the chainstate (which may refer to block index entries). + if (!pnetMan->getChainActive()->pcoinsTip->Flush()) + { + return AbortNode(state, "Failed to write to coin database"); } - if (fDoFullFlush || ((mode == FLUSH_STATE_ALWAYS || mode == FLUSH_STATE_PERIODIC) && - nNow > nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000)) + nLastFlush = nNow; + // Trim, but never trim more than nMaxCacheIncreaseSinceLastFlush + size_t nTrimSize = nCoinCacheUsage * .90; + if (nCoinCacheUsage - nMaxCacheIncreaseSinceLastFlush > nTrimSize) { - // Update best block in wallet (so we can detect restored wallets). - GetMainSignals().SetBestChain(pnetMan->getChainActive()->chainActive.GetLocator()); - nLastSetChain = nNow; + nTrimSize = nCoinCacheUsage - nMaxCacheIncreaseSinceLastFlush; } + pnetMan->getChainActive()->pcoinsTip->Trim(nTrimSize); + nSizeAfterLastFlush = pnetMan->getChainActive()->pcoinsTip->DynamicMemoryUsage(); } - catch (const std::runtime_error &e) + if (fDoFullFlush || ((mode == FLUSH_STATE_ALWAYS || mode == FLUSH_STATE_PERIODIC) && + nNow > nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000)) { - return AbortNode(state, std::string("System error while flushing: ") + e.what()); + // Update best block in wallet (so we can detect restored wallets). + GetMainSignals().SetBestChain(pnetMan->getChainActive()->chainActive.GetLocator()); + nLastSetChain = nNow; + } + + // As a safeguard, periodically check and correct any drift in the value of cachedCoinsUsage. While a + // correction should never be needed, resetting the value allows the node to continue operating, and only + // an error is reported if the new and old values do not match. + if (fPeriodicFlush) + { + pnetMan->getChainActive()->pcoinsTip->ResetCachedCoinUsage(); } return true; } @@ -1251,6 +1241,10 @@ void FlushStateToDisk() /** Delete all entries in setBlockIndexCandidates that are worse than the current tip. */ void PruneBlockIndexCandidates() { + AssertLockHeld(cs_main); + if (setBlockIndexCandidates.empty()) + return; // nothing to prune + // Note that we can't delete the current block itself, as we may need to return to it later in case a // reorganization to a better block fails. std::set::iterator it = setBlockIndexCandidates.begin(); @@ -1259,8 +1253,6 @@ void PruneBlockIndexCandidates() { setBlockIndexCandidates.erase(it++); } - // Either the current tip or a successor of it we're working towards is left in setBlockIndexCandidates. - assert(!setBlockIndexCandidates.empty()); } diff --git a/src/main.h b/src/main.h index ebc2d127..32a4b583 100644 --- a/src/main.h +++ b/src/main.h @@ -342,14 +342,6 @@ bool CheckInputs(const CTransaction &tx, bool cacheStore, std::vector *pvChecks = nullptr); -/** Apply the effects of this transaction on the UTXO set represented by view */ -void UpdateCoins(const CTransaction &tx, CValidationState &state, CCoinsViewCache &inputs, int nHeight); -void UpdateCoins(const CTransaction &tx, - CValidationState &state, - CCoinsViewCache &inputs, - CTxUndo &txundo, - int nHeight); - /** * Check if transaction is final and can be included in a block with the * specified height and time. Consensus critical. diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index ff7ec046..da01ac11 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -152,7 +152,8 @@ bool AreInputsStandard(const CTransaction &tx, const CCoinsViewCache &mapInputs) for (unsigned int i = 0; i < tx.vin.size(); i++) { - const CTxOut &prev = mapInputs.AccessCoin(tx.vin[i].prevout).out; + CoinAccessor coin(mapInputs, tx.vin[i].prevout); + const CTxOut &prev = coin->out; std::vector > vSolutions; txnouttype whichType; // get the scriptPubKey corresponding to this input: diff --git a/src/processblock.cpp b/src/processblock.cpp index 9297d3c0..1e57847b 100644 --- a/src/processblock.cpp +++ b/src/processblock.cpp @@ -150,6 +150,12 @@ bool ProcessNewBlock(CValidationState &state, bool fForceProcessing, CDiskBlockPos *dbp) { + if (!CheckBlockHeader(*pblock, state, true)) + { + // block header is bad + // demerit the sender + return error("%s: CheckBlockHeader FAILED", __func__); + } // Preliminary checks bool checked = CheckBlock(*pblock, state); @@ -174,7 +180,12 @@ bool ProcessNewBlock(CValidationState &state, } if (!ActivateBestChain(state, chainparams, pblock)) - return error("%s: ActivateBestChain failed", __func__); + { + if (state.IsInvalid() || state.IsError()) + return error("%s: ActivateBestChain failed", __func__); + else + return false; + } return true; } @@ -217,8 +228,10 @@ bool DisconnectTip(CValidationState &state, const Consensus::Params &consensusPa int64_t nStart = GetTimeMicros(); { CCoinsViewCache view(pnetMan->getChainActive()->pcoinsTip.get()); - if (!DisconnectBlock(block, state, pindexDelete, view)) + if (DisconnectBlock(block, pindexDelete, view) != DISCONNECT_OK) + { return error("DisconnectTip(): DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString()); + } assert(view.Flush()); } LogPrint("bench", "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); @@ -276,6 +289,7 @@ bool ConnectTip(CValidationState &state, CBlockIndex *pindexNew, const CBlock *pblock) { + AssertLockHeld(cs_main); assert(pindexNew->pprev == pnetMan->getChainActive()->chainActive.Tip()); // Read block from disk. int64_t nTime1 = GetTimeMicros(); @@ -461,7 +475,8 @@ bool ActivateBestChainStep(CValidationState &state, std::vector vpindexToConnect; bool fContinue = true; int nHeight = pindexFork ? pindexFork->nHeight : -1; - while (fContinue && nHeight != pindexMostWork->nHeight) + bool fBlock = true; + while (fContinue && nHeight < pindexMostWork->nHeight) { // Don't iterate the entire list of potential improvements toward the best tip, as we likely only need // a few blocks along the way. @@ -477,16 +492,18 @@ bool ActivateBestChainStep(CValidationState &state, nHeight = nTargetHeight; // Connect new blocks. - BOOST_REVERSE_FOREACH (CBlockIndex *pindexConnect, vpindexToConnect) + CBlockIndex *pindexNewTip = nullptr; + for (auto i = vpindexToConnect.rbegin(); i != vpindexToConnect.rend(); i++) { - if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : nullptr)) + CBlockIndex *pindexConnect = *i; + if (!ConnectTip( + state, chainparams, pindexConnect, pindexConnect == pindexMostWork && fBlock ? pblock : nullptr)) { if (state.IsInvalid()) { // The block violates a consensus rule. if (!state.CorruptionPossible()) InvalidChainFound(vpindexToConnect.back()); - state = CValidationState(); fInvalidFound = true; fContinue = false; break; @@ -499,6 +516,14 @@ bool ActivateBestChainStep(CValidationState &state, } else { + pindexNewTip = pindexConnect; + if (!pnetMan->getChainActive()->IsInitialBlockDownload()) + { + // Notify external zmq listeners about the new tip. + GetMainSignals().UpdatedBlockTip(pindexConnect); + } + BlockNotifyCallback(pnetMan->getChainActive()->IsInitialBlockDownload(), pindexNewTip); + PruneBlockIndexCandidates(); if (!pindexOldTip || pnetMan->getChainActive()->chainActive.Tip()->nChainWork > pindexOldTip->nChainWork) @@ -509,6 +534,51 @@ bool ActivateBestChainStep(CValidationState &state, } } } + if (fInvalidFound) + break; // stop processing more blocks if the last one was invalid. + + if (fContinue) + { + pindexMostWork = FindMostWorkChain(); + if (!pindexMostWork) + return false; + } + fBlock = false; // read next blocks from disk + } + + // Relay Inventory + CBlockIndex *pindexNewTip = pnetMan->getChainActive()->chainActive.Tip(); + if (pindexFork != pindexNewTip) + { + if (!pnetMan->getChainActive()->IsInitialBlockDownload()) + { + // Find the hashes of all blocks that weren't previously in the best chain. + std::vector vHashes; + CBlockIndex *pindexToAnnounce = pindexNewTip; + while (pindexToAnnounce != pindexFork) + { + vHashes.push_back(pindexToAnnounce->GetBlockHash()); + pindexToAnnounce = pindexToAnnounce->pprev; + if (vHashes.size() == MAX_BLOCKS_TO_ANNOUNCE) + { + // Limit announcements in case of a huge reorganization. + // Rely on the peer's synchronization mechanism in that case. + break; + } + } + + // Relay inventory, but don't relay old inventory during initial block download. + const int nNewHeight = pindexNewTip->nHeight; + g_connman->ForEachNode([nNewHeight, &vHashes](CNode *pnode) { + if (nNewHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 0)) + { + for (const uint256 &hash : boost::adaptors::reverse(vHashes)) + { + pnode->PushBlockHash(hash); + } + } + }); + } } if (fBlocksDisconnected) @@ -521,9 +591,14 @@ bool ActivateBestChainStep(CValidationState &state, mempool.check(pnetMan->getChainActive()->pcoinsTip.get()); // Callbacks/notifications for a new best chain. if (fInvalidFound) + { CheckForkWarningConditionsOnNewFork(vpindexToConnect.back()); + return false; + } else + { CheckForkWarningConditions(); + } return true; } @@ -534,7 +609,9 @@ bool ActivateBestChainStep(CValidationState &state, */ bool ActivateBestChain(CValidationState &state, const CNetworkTemplate &chainparams, const CBlock *pblock) { - CBlockIndex *pindexMostWork = NULL; + CBlockIndex *pindexMostWork = nullptr; + LOCK(cs_main); + do { if (shutdown_threads.load()) @@ -542,79 +619,30 @@ bool ActivateBestChain(CValidationState &state, const CNetworkTemplate &chainpar break; } - if (ShutdownRequested()) - break; - - CBlockIndex *pindexNewTip = NULL; - const CBlockIndex *pindexFork; - bool fInitialDownload; + pindexMostWork = FindMostWorkChain(); + if (!pindexMostWork) { - LOCK(cs_main); - - CBlockIndex *pindexOldTip = pnetMan->getChainActive()->chainActive.Tip(); - pindexMostWork = FindMostWorkChain(); + return true; + } - // Whether we have anything to do at all. - if (pindexMostWork == nullptr || pindexMostWork == pnetMan->getChainActive()->chainActive.Tip()) + // Whether we have anything to do at all. + if (pnetMan->getChainActive()->chainActive.Tip() != nullptr) + { + if (pindexMostWork->nChainWork <= pnetMan->getChainActive()->chainActive.Tip()->nChainWork) return true; - - if (!ActivateBestChainStep(state, chainparams, pindexMostWork, - pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullptr)) - { - return false; - } - - pindexNewTip = pnetMan->getChainActive()->chainActive.Tip(); - pindexFork = pnetMan->getChainActive()->chainActive.FindFork(pindexOldTip); - fInitialDownload = pnetMan->getChainActive()->IsInitialBlockDownload(); } - // When we reach this point, we switched to a new tip (stored in pindexNewTip). - - // Notifications/callbacks that can run without cs_main - // Always notify the UI if a new block tip was connected - if (pindexFork != pindexNewTip) + if (!ActivateBestChainStep(state, chainparams, pindexMostWork, + pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullptr)) { - BlockNotifyCallback(fInitialDownload, pindexNewTip); - - if (!fInitialDownload) - { - // Find the hashes of all blocks that weren't previously in the best chain. - std::vector vHashes; - CBlockIndex *pindexToAnnounce = pindexNewTip; - while (pindexToAnnounce != pindexFork) - { - vHashes.push_back(pindexToAnnounce->GetBlockHash()); - pindexToAnnounce = pindexToAnnounce->pprev; - if (vHashes.size() == MAX_BLOCKS_TO_ANNOUNCE) - { - // Limit announcements in case of a huge reorganization. - // Rely on the peer's synchronization mechanism in that case. - break; - } - } - // Relay inventory, but don't relay old inventory during initial block - // download. - const int nNewHeight = pindexNewTip->nHeight; - g_connman->ForEachNode([nNewHeight, &vHashes](CNode *pnode) { - if (nNewHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 0)) - { - for (const uint256 &hash : boost::adaptors::reverse(vHashes)) - { - pnode->PushBlockHash(hash); - } - } - }); - // Notify external listeners about the new tip. - if (!vHashes.empty()) - { - GetMainSignals().UpdatedBlockTip(pindexNewTip); - } - } + return false; } - } while (pindexMostWork != pnetMan->getChainActive()->chainActive.Tip()); + pindexMostWork = FindMostWorkChain(); + if (!pindexMostWork) + return false; + pblock = nullptr; + } while (pindexMostWork->nChainWork > pnetMan->getChainActive()->chainActive.Tip()->nChainWork); CheckBlockIndex(chainparams.GetConsensus()); - - // Write changes periodically to disk, after relay. + // Write changes periodically to disk if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC)) { return false; @@ -1142,24 +1170,8 @@ bool ConnectBlock(const CBlock &block, } unsigned int flags = SCRIPT_VERIFY_P2SH; - - // Start enforcing the DERSIG (BIP66) rules, for block.nVersion=3 blocks, - // when 75% of the network has upgraded: - if (block.nVersion >= 3 && IsSuperMajority(3, pindex->pprev, - chainparams.GetConsensus().nMajorityEnforceBlockUpgrade, chainparams.GetConsensus())) - { - flags |= SCRIPT_VERIFY_DERSIG; - } - - // Start enforcing CHECKLOCKTIMEVERIFY, (BIP65) for block.nVersion=4 - // blocks, when 75% of the network has upgraded: - if (block.nVersion >= 4 && IsSuperMajority(4, pindex->pprev, - chainparams.GetConsensus().nMajorityEnforceBlockUpgrade, chainparams.GetConsensus())) - { - flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY; - } - - // Start enforcing BIP68 (sequence locks) and BIP112 (CHECKSEQUENCEVERIFY) using versionbits logic. + flags |= SCRIPT_VERIFY_DERSIG; + flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY; int nLockTimeFlags = 0; flags |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY; nLockTimeFlags |= LOCKTIME_VERIFY_SEQUENCE; @@ -1187,6 +1199,7 @@ bool ConnectBlock(const CBlock &block, } else { + // PoW block blockundo.vtxundo.reserve(block.vtx.size() - 1); } for (unsigned int i = 0; i < block.vtx.size(); i++) @@ -1216,7 +1229,7 @@ bool ConnectBlock(const CBlock &block, prevheights.resize(tx.vin.size()); for (size_t j = 0; j < tx.vin.size(); j++) { - prevheights[j] = view.AccessCoin(tx.vin[j].prevout).nHeight; + prevheights[j] = CoinAccessor(view, tx.vin[j].prevout)->nHeight; } if (!SequenceLocks(tx, nLockTimeFlags, &prevheights, *pindex)) @@ -1260,7 +1273,8 @@ bool ConnectBlock(const CBlock &block, { blockundo.vtxundo.push_back(CTxUndo()); } - UpdateCoins(tx, state, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight); + bool useDummy = (i == 0 && tx.IsCoinStake() == false); + UpdateCoins(tx, view, useDummy ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight); vPos.push_back(std::make_pair(tx.GetHash(), pos)); pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); @@ -1414,7 +1428,7 @@ bool ConnectBlock(const CBlock &block, * @param out The out point that corresponds to the tx input. * @return True on success. */ -DisconnectResult ApplyTxInUndo(Coin &&undo, CCoinsViewCache &view, const COutPoint &out) +int ApplyTxInUndo(Coin &&undo, CCoinsViewCache &view, const COutPoint &out) { bool fClean = true; if (view.HaveCoin(out)) @@ -1427,11 +1441,13 @@ DisconnectResult ApplyTxInUndo(Coin &&undo, CCoinsViewCache &view, const COutPoi // information only in undo records for the last spend of a transactions // outputs. This implies that it must be present for some other output of the same tx. - const Coin &alternate = AccessByTxid(view, out.hash); - if (!alternate.IsSpent()) + CoinAccessor alternate(view, out.hash); + if (!alternate->IsSpent()) { - undo.nHeight = alternate.nHeight; - undo.fCoinBase = alternate.fCoinBase; + undo.nHeight = alternate->nHeight; + undo.fCoinBase = alternate->fCoinBase; + undo.fCoinStake = alternate->fCoinStake; + undo.nTime = alternate->nTime; } else { @@ -1442,40 +1458,71 @@ DisconnectResult ApplyTxInUndo(Coin &&undo, CCoinsViewCache &view, const COutPoi return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN; } - -int DisconnectBlock(const CBlock &block, - CValidationState &state, - const CBlockIndex *pindex, - CCoinsViewCache &view, - bool *pfClean) +/** Undo the effects of this block (with given index) on the UTXO set represented by coins. + * When UNCLEAN or FAILED is returned, view is left in an indeterminate state. */ +DisconnectResult DisconnectBlock(const CBlock &block, const CBlockIndex *pindex, CCoinsViewCache &view) { assert(pindex->GetBlockHash() == view.GetBestBlock()); - if (pfClean) - *pfClean = false; - bool fClean = true; CBlockUndo blockUndo; CDiskBlockPos pos = pindex->GetUndoPos(); if (pos.IsNull()) { - return error("DisconnectBlock(): no undo data available"); + error("DisconnectBlock(): no undo data available"); + return DISCONNECT_FAILED; } if (!UndoReadFromDisk(blockUndo, pos, pindex->pprev->GetBlockHash())) { - return error("DisconnectBlock(): failure reading undo data"); + error("DisconnectBlock(): failure reading undo data"); + return DISCONNECT_FAILED; } - - if (blockUndo.vtxundo.size() + 1 != block.vtx.size()) + if (blockUndo.vtxundo.size() + 1 != block.vtx.size() && block.IsProofOfWork()) { - return error("DisconnectBlock(): block and undo data inconsistent"); + error("DisconnectBlock(): block and undo data inconsistent, PoW"); + return DISCONNECT_FAILED; } + if (blockUndo.vtxundo.size() != block.vtx.size() && block.IsProofOfStake()) + { + error("DisconnectBlock(): block and undo data inconsistent, PoS"); + return DISCONNECT_FAILED; + } + // undo transactions in reverse of the OTI algorithm order (so add inputs first, then remove outputs) - // undo transactions in reverse order - for (int i = block.vtx.size() - 1; i >= 0; i--) + // restore inputs + unsigned int start = 1; + if (block.IsProofOfStake()) + { + start = 0; + } + for (unsigned int i = start; i < block.vtx.size(); i++) // i=1 to skip the coinbase, it has no inputs { const CTransaction &tx = *(block.vtx[i]); + CTxUndo &txundo = blockUndo.vtxundo[i - start]; + if (txundo.vprevout.size() != tx.vin.size()) + { + error("DisconnectBlock(): transaction and undo data inconsistent"); + return DISCONNECT_FAILED; + } + for (unsigned int j = tx.vin.size(); j-- > 0;) + { + const COutPoint &out = tx.vin[j].prevout; + int res = ApplyTxInUndo(std::move(txundo.vprevout[j]), view, out); + if (res == DISCONNECT_FAILED) + { + error("DisconnectBlock(): ApplyTxInUndo failed"); + return DISCONNECT_FAILED; + } + fClean = fClean && res != DISCONNECT_UNCLEAN; + } + // At this point, all of txundo.vprevout should have been moved out. + } + + // remove outputs + for (unsigned int j = 0; j < block.vtx.size(); j++) + { + const CTransaction &tx = *(block.vtx[j]); uint256 hash = tx.GetHash(); // Check that all outputs are available and match the outputs in the block itself exactly. @@ -1488,37 +1535,15 @@ int DisconnectBlock(const CBlock &block, view.SpendCoin(out, &coin); if (tx.vout[o] != coin.out) { + error("DisconnectBlock(): transaction output mismatch"); fClean = false; // transaction output mismatch } } } - // restore inputs - if (!tx.IsCoinBase()) // not coinbases - { - CTxUndo &txundo = blockUndo.vtxundo[i - 1]; - if (txundo.vprevout.size() != tx.vin.size()) - { - return error("DisconnectBlock(): transaction and undo data inconsistent"); - } - for (unsigned int j = tx.vin.size(); j-- > 0;) - { - const COutPoint &out = tx.vin[j].prevout; - int res = ApplyTxInUndo(std::move(txundo.vprevout[j]), view, out); - if (res == DISCONNECT_FAILED) - return DISCONNECT_FAILED; - fClean = fClean && res != DISCONNECT_UNCLEAN; - } - } } // move best block pointer to prevout block view.SetBestBlock(pindex->pprev->GetBlockHash()); - if (pfClean) - { - *pfClean = fClean; - return true; - } - - return fClean; + return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN; } diff --git a/src/processblock.h b/src/processblock.h index 4f2737a0..b62020f7 100644 --- a/src/processblock.h +++ b/src/processblock.h @@ -81,11 +81,7 @@ bool ConnectBlock(const CBlock &block, * In case pfClean is provided, operation will try to be tolerant about errors, and *pfClean * will be true if no problems were found. Otherwise, the return value will be false in case * of problems. Note that in any case, coins may be modified. */ -int DisconnectBlock(const CBlock &block, - CValidationState &state, - const CBlockIndex *pindex, - CCoinsViewCache &coins, - bool *pfClean = nullptr); +DisconnectResult DisconnectBlock(const CBlock &block, const CBlockIndex *pindex, CCoinsViewCache &coins); /** Find the best known block, and make it the tip of the block chain */ bool ActivateBestChain(CValidationState &state, const CNetworkTemplate &chainparams, const CBlock *pblock = nullptr); diff --git a/src/rpc/rpcblockchain.cpp b/src/rpc/rpcblockchain.cpp index 95c08bf5..eb8b43bc 100644 --- a/src/rpc/rpcblockchain.cpp +++ b/src/rpc/rpcblockchain.cpp @@ -627,7 +627,7 @@ UniValue gettxout(const UniValue ¶ms, bool fHelp) if ((unsigned int)coin.nHeight == MEMPOOL_HEIGHT) ret.push_back(Pair("confirmations", 0)); else - ret.push_back(Pair("confirmations", pindex->nHeight - coin.nHeight + 1)); + ret.push_back(Pair("confirmations", (int64_t)(pindex->nHeight - coin.nHeight + 1))); ret.push_back(Pair("value", ValueFromAmount(coin.out.nValue))); UniValue o(UniValue::VOBJ); ScriptPubKeyToJSON(coin.out.scriptPubKey, o, true); diff --git a/src/rpc/rpcrawtransaction.cpp b/src/rpc/rpcrawtransaction.cpp index ff33c27a..9fa66879 100644 --- a/src/rpc/rpcrawtransaction.cpp +++ b/src/rpc/rpcrawtransaction.cpp @@ -274,10 +274,11 @@ UniValue gettxoutproof(const UniValue ¶ms, bool fHelp) } else { - const Coin &coin = AccessByTxid(*(pnetMan->getChainActive()->pcoinsTip), oneTxid); - if (!coin.IsSpent() && coin.nHeight > 0 && coin.nHeight <= pnetMan->getChainActive()->chainActive.Height()) + CoinAccessor coin(*pnetMan->getChainActive()->pcoinsTip, oneTxid); + if (coin && !coin->IsSpent() && coin->nHeight > 0 && + coin->nHeight <= pnetMan->getChainActive()->chainActive.Height()) { - pblockindex = pnetMan->getChainActive()->chainActive[coin.nHeight]; + pblockindex = pnetMan->getChainActive()->chainActive[coin->nHeight]; } } @@ -685,9 +686,11 @@ UniValue signrawtransaction(const UniValue ¶ms, bool fHelp) CCoinsViewMemPool viewMempool(&viewChain, mempool); view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view - for (auto const &txin : mergedTx.vin) + WRITELOCK(view.cs_utxo); + for (const CTxIn &txin : mergedTx.vin) { - view.AccessCoin(txin.prevout); // Load entries from viewChain into view; can fail. + // Load entries from viewChain into view; can fail. + view._AccessCoin(txin.prevout); } view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long @@ -741,15 +744,16 @@ UniValue signrawtransaction(const UniValue ¶ms, bool fHelp) std::vector pkData(ParseHexO(prevOut, "scriptPubKey")); CScript scriptPubKey(pkData.begin(), pkData.end()); + Coin newcoin; { - const Coin &coin = view.AccessCoin(out); - if (!coin.IsSpent() && coin.out.scriptPubKey != scriptPubKey) + CoinAccessor coin(view, out); + + if (!coin->IsSpent() && coin->out.scriptPubKey != scriptPubKey) { std::string err("Previous output scriptPubKey mismatch:\n"); - err = err + ScriptToAsmStr(coin.out.scriptPubKey) + "\nvs:\n" + ScriptToAsmStr(scriptPubKey); + err = err + ScriptToAsmStr(coin->out.scriptPubKey) + "\nvs:\n" + ScriptToAsmStr(scriptPubKey); throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err); } - Coin newcoin; newcoin.out.scriptPubKey = scriptPubKey; newcoin.out.nValue = 0; if (prevOut.exists("amount")) @@ -757,8 +761,8 @@ UniValue signrawtransaction(const UniValue ¶ms, bool fHelp) newcoin.out.nValue = AmountFromValue(find_value(prevOut, "amount")); } newcoin.nHeight = 1; - view.AddCoin(out, std::move(newcoin), true); } + view.AddCoin(out, std::move(newcoin), true); // if redeemScript given and not using the local wallet (private keys // given), add redeemScript to the tempKeystore so it can be signed: @@ -803,13 +807,13 @@ UniValue signrawtransaction(const UniValue ¶ms, bool fHelp) for (unsigned int i = 0; i < mergedTx.vin.size(); i++) { CTxIn &txin = mergedTx.vin[i]; - const Coin &coin = view.AccessCoin(txin.prevout); - if (coin.IsSpent()) + CoinAccessor coin(view, txin.prevout); + if (coin->IsSpent()) { TxInErrorToJSON(txin, vErrors, "Input not found or already spent"); continue; } - const CScript &prevPubKey = coin.out.scriptPubKey; + const CScript &prevPubKey = coin->out.scriptPubKey; txin.scriptSig.clear(); // Only sign SIGHASH_SINGLE if there's a corresponding output: @@ -880,8 +884,8 @@ UniValue sendrawtransaction(const UniValue ¶ms, bool fHelp) bool fHaveChain = false; for (size_t o = 0; !fHaveChain && o < tx->vout.size(); o++) { - const Coin &existingCoin = view.AccessCoin(COutPoint(txid, o)); - fHaveChain = !existingCoin.IsSpent(); + CoinAccessor existingCoin(view, COutPoint(txid, o)); + fHaveChain = !existingCoin->IsSpent(); } bool fHaveMempool = mempool.exists(txid); if (!fHaveMempool && !fHaveChain) diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index 8ee355c2..a376e38b 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -166,13 +166,13 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) Coin &coin = result[COutPoint(txid, 0)]; if ((insecure_rand() % 500) == 0) { - const Coin &entry = AccessByTxid(*stack.back(), txid); - BOOST_CHECK(coin == entry); + CoinAccessor entry(*stack.back(), txid); + BOOST_CHECK(coin == *entry); } else { - LOCK(stack.back()->cs_utxo); - const Coin &entry = stack.back()->AccessCoin(COutPoint(txid, 0)); + WRITELOCK(stack.back()->cs_utxo); + const Coin &entry = stack.back()->_AccessCoin(COutPoint(txid, 0)); BOOST_CHECK(coin == entry); } @@ -228,8 +228,8 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) bool have = stack.back()->HaveCoin(it->first); bool isspent = true; { - LOCK(stack.back()->cs_utxo); - const Coin &coin = stack.back()->AccessCoin(it->first); + WRITELOCK(stack.back()->cs_utxo); + const Coin &coin = stack.back()->_AccessCoin(it->first); isspent = coin.IsSpent(); BOOST_CHECK(have == !coin.IsSpent()); BOOST_CHECK(coin == it->second); @@ -504,7 +504,10 @@ void CheckAccessCoin(CAmount base_value, char expected_flags) { SingleEntryCacheTest test(base_value, cache_value, cache_flags); - test.cache.AccessCoin(OUTPOINT); + { + WRITELOCK(test.cache.cs_utxo); + test.cache._AccessCoin(OUTPOINT); + } test.cache.SelfTest(); CAmount result_value; diff --git a/src/txdb.cpp b/src/txdb.cpp index 965f8975..f9ccde0e 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -90,7 +90,7 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint64_t nBestCoinHeight, size_t &nChildCachedCoinsUsage) { - LOCK(cs_utxo); + WRITELOCK(cs_utxo); CDBBatch batch(db); size_t count = 0; size_t changed = 0; diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 8db88ee9..270e483c 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -21,6 +21,7 @@ #include "txmempool.h" #include "clientversion.h" +#include "coins.h" #include "consensus/consensus.h" #include "consensus/validation.h" #include "main.h" @@ -733,11 +734,11 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash); if (it2 != mapTx.end()) continue; - const Coin &coin = pcoins->AccessCoin(txin.prevout); + CoinAccessor coin(*pcoins, txin.prevout); if (nCheckFrequency != 0) - assert(!coin.IsSpent()); - if (coin.IsSpent() || - (coin.IsCoinBase() && ((signed long)nMemPoolHeight) - coin.nHeight < COINBASE_MATURITY)) + assert(!coin->IsSpent()); + if (coin->IsSpent() || + (coin->IsCoinBase() && ((signed long)nMemPoolHeight) - coin->nHeight < COINBASE_MATURITY)) { transactionsToRemove.push_back(tx); break; @@ -918,7 +919,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const { CValidationState state; assert(CheckInputs(tx, state, mempoolDuplicate, false, 0, false, NULL)); - UpdateCoins(tx, state, mempoolDuplicate, 1000000); + UpdateCoins(tx, mempoolDuplicate, 1000000); } } unsigned int stepsSinceLastRemove = 0; @@ -936,7 +937,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const else { assert(CheckInputs(entry->GetTx(), state, mempoolDuplicate, false, 0, false, NULL)); - UpdateCoins(entry->GetTx(), state, mempoolDuplicate, 1000000); + UpdateCoins(entry->GetTx(), mempoolDuplicate, 1000000); stepsSinceLastRemove = 0; } } diff --git a/src/verifydb.cpp b/src/verifydb.cpp index 715650ed..c3839d33 100644 --- a/src/verifydb.cpp +++ b/src/verifydb.cpp @@ -82,12 +82,14 @@ bool CVerifyDB::VerifyDB(const CNetworkTemplate &chainparams, CCoinsView *coinsv (coins.DynamicMemoryUsage() + pnetMan->getChainActive()->pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage) { - bool fClean = true; - if (!DisconnectBlock(block, state, pindex, coins, &fClean)) + DisconnectResult res = DisconnectBlock(block, pindex, coins); + if (res == DISCONNECT_FAILED) + { return error("VerifyDB(): *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); + } pindexState = pindex->pprev; - if (!fClean) + if (res == DISCONNECT_UNCLEAN) { nGoodTransactions = 0; pindexFailure = pindex; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index d83b4997..e339d3dd 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3571,7 +3571,7 @@ bool CWallet::InitLoadWallet() if (gArgs.GetBoolArg("-zapwallettxes", false)) { - LogPrintf("Zapping all transactions from wallet..."); + LogPrintf("Zapping all transactions from wallet...\n"); CWallet *tempWallet = new CWallet(walletFile); DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx); diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index f15dd5ba..9b192481 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -640,7 +640,7 @@ DBErrors CWalletDB::FindWalletTx(CWallet *pwallet, std::vector &vTxHash int nMinVersion = 0; if (Read((std::string) "minversion", nMinVersion)) { - if (nMinVersion > CLIENT_VERSION) + if (nMinVersion > WALLET_VERSION) return DB_TOO_NEW; pwallet->LoadMinVersion(nMinVersion); }