Browse Source

Merge #9208: Improve DisconnectTip performance

c1235e3 Add RecursiveDynamicUsage overload for std::shared_ptr (Russell Yanofsky)
71f1903 Store disconnected block transactions outside mempool during reorg (Suhas Daftuar)
9decd64 [qa] Relax assumptions on mempool behavior during reorg (Suhas Daftuar)

Tree-SHA512: c160ad853a5cd060d0307af7606a0c77907497ed7033c9599b95e73d83f68fdfcd4214bd8a83db1c5b7a58022722b9de1ed2e6ea2e02f38a7b6c717f079dd0c6
0.15
Wladimir J. van der Laan 7 years ago
parent
commit
acd9957b72
No known key found for this signature in database
GPG Key ID: 1E4AED62986CD25D
  1. 5
      src/core_memusage.h
  2. 2
      src/txmempool.cpp
  3. 99
      src/txmempool.h
  4. 125
      src/validation.cpp
  5. 2
      src/validation.h
  6. 15
      test/functional/pruning.py

5
src/core_memusage.h

@ -63,4 +63,9 @@ static inline size_t RecursiveDynamicUsage(const CBlockLocator& locator) {
return memusage::DynamicUsage(locator.vHave); return memusage::DynamicUsage(locator.vHave);
} }
template<typename X>
static inline size_t RecursiveDynamicUsage(const std::shared_ptr<X>& p) {
return p ? memusage::DynamicUsage(p) + RecursiveDynamicUsage(*p) : 0;
}
#endif // BITCOIN_CORE_MEMUSAGE_H #endif // BITCOIN_CORE_MEMUSAGE_H

2
src/txmempool.cpp

@ -24,7 +24,7 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFe
spendsCoinbase(_spendsCoinbase), sigOpCost(_sigOpsCost), lockPoints(lp) spendsCoinbase(_spendsCoinbase), sigOpCost(_sigOpsCost), lockPoints(lp)
{ {
nTxWeight = GetTransactionWeight(*tx); nTxWeight = GetTransactionWeight(*tx);
nUsageSize = RecursiveDynamicUsage(*tx) + memusage::DynamicUsage(tx); nUsageSize = RecursiveDynamicUsage(tx);
nCountWithDescendants = 1; nCountWithDescendants = 1;
nSizeWithDescendants = GetTxSize(); nSizeWithDescendants = GetTxSize();

99
src/txmempool.h

@ -24,6 +24,7 @@
#include "boost/multi_index_container.hpp" #include "boost/multi_index_container.hpp"
#include "boost/multi_index/ordered_index.hpp" #include "boost/multi_index/ordered_index.hpp"
#include "boost/multi_index/hashed_index.hpp" #include "boost/multi_index/hashed_index.hpp"
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/signals2/signal.hpp> #include <boost/signals2/signal.hpp>
@ -185,7 +186,7 @@ private:
const LockPoints& lp; const LockPoints& lp;
}; };
// extracts a TxMemPoolEntry's transaction hash // extracts a transaction hash from CTxMempoolEntry or CTransactionRef
struct mempoolentry_txid struct mempoolentry_txid
{ {
typedef uint256 result_type; typedef uint256 result_type;
@ -193,6 +194,11 @@ struct mempoolentry_txid
{ {
return entry.GetTx().GetHash(); return entry.GetTx().GetHash();
} }
result_type operator() (const CTransactionRef& tx) const
{
return tx->GetHash();
}
}; };
/** \class CompareTxMemPoolEntryByDescendantScore /** \class CompareTxMemPoolEntryByDescendantScore
@ -662,4 +668,95 @@ public:
bool HaveCoins(const uint256 &txid) const; bool HaveCoins(const uint256 &txid) const;
}; };
/**
* DisconnectedBlockTransactions
* During the reorg, it's desirable to re-add previously confirmed transactions
* to the mempool, so that anything not re-confirmed in the new chain is
* available to be mined. However, it's more efficient to wait until the reorg
* is complete and process all still-unconfirmed transactions at that time,
* since we expect most confirmed transactions to (typically) still be
* confirmed in the new chain, and re-accepting to the memory pool is expensive
* (and therefore better to not do in the middle of reorg-processing).
* Instead, store the disconnected transactions (in order!) as we go, remove any
* that are included in blocks in the new chain, and then process the remaining
* still-unconfirmed transactions at the end.
*/
// multi_index tag names
struct txid_index {};
struct insertion_order {};
struct DisconnectedBlockTransactions {
typedef boost::multi_index_container<
CTransactionRef,
boost::multi_index::indexed_by<
// sorted by txid
boost::multi_index::hashed_unique<
boost::multi_index::tag<txid_index>,
mempoolentry_txid,
SaltedTxidHasher
>,
// sorted by order in the blockchain
boost::multi_index::sequenced<
boost::multi_index::tag<insertion_order>
>
>
> indexed_disconnected_transactions;
// It's almost certainly a logic bug if we don't clear out queuedTx before
// destruction, as we add to it while disconnecting blocks, and then we
// need to re-process remaining transactions to ensure mempool consistency.
// For now, assert() that we've emptied out this object on destruction.
// This assert() can always be removed if the reorg-processing code were
// to be refactored such that this assumption is no longer true (for
// instance if there was some other way we cleaned up the mempool after a
// reorg, besides draining this object).
~DisconnectedBlockTransactions() { assert(queuedTx.empty()); }
indexed_disconnected_transactions queuedTx;
uint64_t cachedInnerUsage = 0;
// Estimate the overhead of queuedTx to be 6 pointers + an allocation, as
// no exact formula for boost::multi_index_contained is implemented.
size_t DynamicMemoryUsage() const {
return memusage::MallocUsage(sizeof(CTransactionRef) + 6 * sizeof(void*)) * queuedTx.size() + cachedInnerUsage;
}
void addTransaction(const CTransactionRef& tx)
{
queuedTx.insert(tx);
cachedInnerUsage += RecursiveDynamicUsage(tx);
}
// Remove entries based on txid_index, and update memory usage.
void removeForBlock(const std::vector<CTransactionRef>& vtx)
{
// Short-circuit in the common case of a block being added to the tip
if (queuedTx.empty()) {
return;
}
for (auto const &tx : vtx) {
auto it = queuedTx.find(tx->GetHash());
if (it != queuedTx.end()) {
cachedInnerUsage -= RecursiveDynamicUsage(*it);
queuedTx.erase(it);
}
}
}
// Remove an entry by insertion_order index, and update memory usage.
void removeEntry(indexed_disconnected_transactions::index<insertion_order>::type::iterator entry)
{
cachedInnerUsage -= RecursiveDynamicUsage(*entry);
queuedTx.get<insertion_order>().erase(entry);
}
void clear()
{
cachedInnerUsage = 0;
queuedTx.clear();
}
};
#endif // BITCOIN_TXMEMPOOL_H #endif // BITCOIN_TXMEMPOOL_H

125
src/validation.cpp

@ -342,6 +342,56 @@ static bool IsCurrentForFeeEstimation()
return true; return true;
} }
/* Make mempool consistent after a reorg, by re-adding or recursively erasing
* disconnected block transactions from the mempool, and also removing any
* other transactions from the mempool that are no longer valid given the new
* tip/height.
*
* Note: we assume that disconnectpool only contains transactions that are NOT
* confirmed in the current chain nor already in the mempool (otherwise,
* in-mempool descendants of such transactions would be removed).
*
* Passing fAddToMempool=false will skip trying to add the transactions back,
* and instead just erase from the mempool as needed.
*/
void UpdateMempoolForReorg(DisconnectedBlockTransactions &disconnectpool, bool fAddToMempool)
{
AssertLockHeld(cs_main);
std::vector<uint256> vHashUpdate;
// disconnectpool's insertion_order index sorts the entries from
// oldest to newest, but the oldest entry will be the last tx from the
// latest mined block that was disconnected.
// Iterate disconnectpool in reverse, so that we add transactions
// back to the mempool starting with the earliest transaction that had
// been previously seen in a block.
auto it = disconnectpool.queuedTx.get<insertion_order>().rbegin();
while (it != disconnectpool.queuedTx.get<insertion_order>().rend()) {
// ignore validation errors in resurrected transactions
CValidationState stateDummy;
if (!fAddToMempool || (*it)->IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, *it, false, NULL, NULL, true)) {
// If the transaction doesn't make it in to the mempool, remove any
// transactions that depend on it (which would now be orphans).
mempool.removeRecursive(**it, MemPoolRemovalReason::REORG);
} else if (mempool.exists((*it)->GetHash())) {
vHashUpdate.push_back((*it)->GetHash());
}
++it;
}
disconnectpool.queuedTx.clear();
// AcceptToMemoryPool/addUnchecked all assume that new mempool entries have
// no in-mempool children, which is generally not true when adding
// previously-confirmed transactions back to the mempool.
// UpdateTransactionsFromBlock finds descendants of any transactions in
// the disconnectpool that were added back and cleans up the mempool state.
mempool.UpdateTransactionsFromBlock(vHashUpdate);
// We also need to remove any now-immature transactions
mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS);
// Re-limit mempool size, in case we added any transactions
LimitMempoolSize(mempool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
}
bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx, bool fLimitFree, bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx, bool fLimitFree,
bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced, bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
bool fOverrideMempoolLimit, const CAmount& nAbsurdFee, std::vector<uint256>& vHashTxnToUncache) bool fOverrideMempoolLimit, const CAmount& nAbsurdFee, std::vector<uint256>& vHashTxnToUncache)
@ -1877,8 +1927,17 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) {
} }
/** Disconnect chainActive's tip. You probably want to call mempool.removeForReorg and manually re-limit mempool size after this, with cs_main held. */ /** Disconnect chainActive's tip.
bool static DisconnectTip(CValidationState& state, const CChainParams& chainparams, bool fBare = false) * After calling, the mempool will be in an inconsistent state, with
* transactions from disconnected blocks being added to disconnectpool. You
* should make the mempool consistent again by calling UpdateMempoolForReorg.
* with cs_main held.
*
* If disconnectpool is NULL, then no disconnected transactions are added to
* disconnectpool (note that the caller is responsible for mempool consistency
* in any case).
*/
bool static DisconnectTip(CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions *disconnectpool)
{ {
CBlockIndex *pindexDelete = chainActive.Tip(); CBlockIndex *pindexDelete = chainActive.Tip();
assert(pindexDelete); assert(pindexDelete);
@ -1901,25 +1960,17 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED)) if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED))
return false; return false;
if (!fBare) { if (disconnectpool) {
// Resurrect mempool transactions from the disconnected block. // Save transactions to re-add to mempool at end of reorg
std::vector<uint256> vHashUpdate; for (auto it = block.vtx.rbegin(); it != block.vtx.rend(); ++it) {
for (const auto& it : block.vtx) { disconnectpool->addTransaction(*it);
const CTransaction& tx = *it; }
// ignore validation errors in resurrected transactions while (disconnectpool->DynamicMemoryUsage() > MAX_DISCONNECTED_TX_POOL_SIZE * 1000) {
CValidationState stateDummy; // Drop the earliest entry, and remove its children from the mempool.
if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, it, false, NULL, NULL, true)) { auto it = disconnectpool->queuedTx.get<insertion_order>().begin();
mempool.removeRecursive(tx, MemPoolRemovalReason::REORG); mempool.removeRecursive(**it, MemPoolRemovalReason::REORG);
} else if (mempool.exists(tx.GetHash())) { disconnectpool->removeEntry(it);
vHashUpdate.push_back(tx.GetHash());
}
} }
// AcceptToMemoryPool/addUnchecked all assume that new mempool entries have
// no in-mempool children, which is generally not true when adding
// previously-confirmed transactions back to the mempool.
// UpdateTransactionsFromBlock finds descendants of any transactions in this
// block that were added back and cleans up the mempool state.
mempool.UpdateTransactionsFromBlock(vHashUpdate);
} }
// Update chainActive and related variables. // Update chainActive and related variables.
@ -2007,7 +2058,7 @@ public:
* *
* The block is added to connectTrace if connection succeeds. * The block is added to connectTrace if connection succeeds.
*/ */
bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace) bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool)
{ {
assert(pindexNew->pprev == chainActive.Tip()); assert(pindexNew->pprev == chainActive.Tip());
// Read block from disk. // Read block from disk.
@ -2049,6 +2100,7 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001); LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001);
// Remove conflicting transactions from the mempool.; // Remove conflicting transactions from the mempool.;
mempool.removeForBlock(blockConnecting.vtx, pindexNew->nHeight); mempool.removeForBlock(blockConnecting.vtx, pindexNew->nHeight);
disconnectpool.removeForBlock(blockConnecting.vtx);
// Update chainActive & related variables. // Update chainActive & related variables.
UpdateTip(pindexNew, chainparams); UpdateTip(pindexNew, chainparams);
@ -2142,9 +2194,14 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
// Disconnect active blocks which are no longer in the best chain. // Disconnect active blocks which are no longer in the best chain.
bool fBlocksDisconnected = false; bool fBlocksDisconnected = false;
DisconnectedBlockTransactions disconnectpool;
while (chainActive.Tip() && chainActive.Tip() != pindexFork) { while (chainActive.Tip() && chainActive.Tip() != pindexFork) {
if (!DisconnectTip(state, chainparams)) if (!DisconnectTip(state, chainparams, &disconnectpool)) {
// This is likely a fatal error, but keep the mempool consistent,
// just in case. Only remove from the mempool in this case.
UpdateMempoolForReorg(disconnectpool, false);
return false; return false;
}
fBlocksDisconnected = true; fBlocksDisconnected = true;
} }
@ -2167,7 +2224,7 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
// Connect new blocks. // Connect new blocks.
BOOST_REVERSE_FOREACH(CBlockIndex *pindexConnect, vpindexToConnect) { BOOST_REVERSE_FOREACH(CBlockIndex *pindexConnect, vpindexToConnect) {
if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace)) { if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace, disconnectpool)) {
if (state.IsInvalid()) { if (state.IsInvalid()) {
// The block violates a consensus rule. // The block violates a consensus rule.
if (!state.CorruptionPossible()) if (!state.CorruptionPossible())
@ -2178,6 +2235,9 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
break; break;
} else { } else {
// A system error occurred (disk space, database error, ...). // A system error occurred (disk space, database error, ...).
// Make the mempool consistent with the current tip, just in case
// any observers try to use it before shutdown.
UpdateMempoolForReorg(disconnectpool, false);
return false; return false;
} }
} else { } else {
@ -2192,8 +2252,9 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
} }
if (fBlocksDisconnected) { if (fBlocksDisconnected) {
mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); // If any blocks were disconnected, disconnectpool may be non empty. Add
LimitMempoolSize(mempool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); // any disconnected transactions back to the mempool.
UpdateMempoolForReorg(disconnectpool, true);
} }
mempool.check(pcoinsTip); mempool.check(pcoinsTip);
@ -2342,6 +2403,7 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C
setDirtyBlockIndex.insert(pindex); setDirtyBlockIndex.insert(pindex);
setBlockIndexCandidates.erase(pindex); setBlockIndexCandidates.erase(pindex);
DisconnectedBlockTransactions disconnectpool;
while (chainActive.Contains(pindex)) { while (chainActive.Contains(pindex)) {
CBlockIndex *pindexWalk = chainActive.Tip(); CBlockIndex *pindexWalk = chainActive.Tip();
pindexWalk->nStatus |= BLOCK_FAILED_CHILD; pindexWalk->nStatus |= BLOCK_FAILED_CHILD;
@ -2349,13 +2411,17 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C
setBlockIndexCandidates.erase(pindexWalk); setBlockIndexCandidates.erase(pindexWalk);
// ActivateBestChain considers blocks already in chainActive // ActivateBestChain considers blocks already in chainActive
// unconditionally valid already, so force disconnect away from it. // unconditionally valid already, so force disconnect away from it.
if (!DisconnectTip(state, chainparams)) { if (!DisconnectTip(state, chainparams, &disconnectpool)) {
mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); // It's probably hopeless to try to make the mempool consistent
// here if DisconnectTip failed, but we can try.
UpdateMempoolForReorg(disconnectpool, false);
return false; return false;
} }
} }
LimitMempoolSize(mempool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); // DisconnectTip will add transactions to disconnectpool; try to add these
// back to the mempool.
UpdateMempoolForReorg(disconnectpool, true);
// The resulting new best tip may not be in setBlockIndexCandidates anymore, so // The resulting new best tip may not be in setBlockIndexCandidates anymore, so
// add it again. // add it again.
@ -2368,7 +2434,6 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C
} }
InvalidChainFound(pindex); InvalidChainFound(pindex);
mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS);
uiInterface.NotifyBlockTip(IsInitialBlockDownload(), pindex->pprev); uiInterface.NotifyBlockTip(IsInitialBlockDownload(), pindex->pprev);
return true; return true;
} }
@ -3482,7 +3547,7 @@ bool RewindBlockIndex(const CChainParams& params)
// of the blockchain). // of the blockchain).
break; break;
} }
if (!DisconnectTip(state, params, true)) { if (!DisconnectTip(state, params, NULL)) {
return error("RewindBlockIndex: unable to disconnect block at height %i", pindex->nHeight); return error("RewindBlockIndex: unable to disconnect block at height %i", pindex->nHeight);
} }
// Occasionally flush state to disk. // Occasionally flush state to disk.

2
src/validation.h

@ -70,6 +70,8 @@ static const unsigned int DEFAULT_DESCENDANT_LIMIT = 25;
static const unsigned int DEFAULT_DESCENDANT_SIZE_LIMIT = 101; static const unsigned int DEFAULT_DESCENDANT_SIZE_LIMIT = 101;
/** Default for -mempoolexpiry, expiration time for mempool transactions in hours */ /** Default for -mempoolexpiry, expiration time for mempool transactions in hours */
static const unsigned int DEFAULT_MEMPOOL_EXPIRY = 336; static const unsigned int DEFAULT_MEMPOOL_EXPIRY = 336;
/** Maximum kilobytes for transactions to store for processing during reorg */
static const unsigned int MAX_DISCONNECTED_TX_POOL_SIZE = 20000;
/** The maximum size of a blk?????.dat file (since 0.8) */ /** The maximum size of a blk?????.dat file (since 0.8) */
static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB
/** The pre-allocation chunk size for blk?????.dat files (since 0.8) */ /** The pre-allocation chunk size for blk?????.dat files (since 0.8) */

15
test/functional/pruning.py

@ -34,10 +34,11 @@ class PruneTest(BitcoinTestFramework):
# Create nodes 0 and 1 to mine. # Create nodes 0 and 1 to mine.
# Create node 2 to test pruning. # Create node 2 to test pruning.
self.full_node_default_args = ["-maxreceivebuffer=20000","-blockmaxsize=999000", "-checkblocks=5", "-limitdescendantcount=100", "-limitdescendantsize=5000", "-limitancestorcount=100", "-limitancestorsize=5000" ]
# Create nodes 3 and 4 to test manual pruning (they will be re-started with manual pruning later) # Create nodes 3 and 4 to test manual pruning (they will be re-started with manual pruning later)
# Create nodes 5 to test wallet in prune mode, but do not connect # Create nodes 5 to test wallet in prune mode, but do not connect
self.extra_args = [["-maxreceivebuffer=20000", "-blockmaxsize=999000", "-checkblocks=5"], self.extra_args = [self.full_node_default_args,
["-maxreceivebuffer=20000", "-blockmaxsize=999000", "-checkblocks=5"], self.full_node_default_args,
["-maxreceivebuffer=20000", "-prune=550"], ["-maxreceivebuffer=20000", "-prune=550"],
["-maxreceivebuffer=20000", "-blockmaxsize=999000"], ["-maxreceivebuffer=20000", "-blockmaxsize=999000"],
["-maxreceivebuffer=20000", "-blockmaxsize=999000"], ["-maxreceivebuffer=20000", "-blockmaxsize=999000"],
@ -97,12 +98,15 @@ class PruneTest(BitcoinTestFramework):
# Node 2 stays connected, so it hears about the stale blocks and then reorg's when node0 reconnects # Node 2 stays connected, so it hears about the stale blocks and then reorg's when node0 reconnects
# Stopping node 0 also clears its mempool, so it doesn't have node1's transactions to accidentally mine # Stopping node 0 also clears its mempool, so it doesn't have node1's transactions to accidentally mine
self.stop_node(0) self.stop_node(0)
self.nodes[0]=start_node(0, self.options.tmpdir, ["-maxreceivebuffer=20000","-blockmaxsize=999000", "-checkblocks=5"], timewait=900) self.nodes[0]=start_node(0, self.options.tmpdir, self.full_node_default_args, timewait=900)
# Mine 24 blocks in node 1 # Mine 24 blocks in node 1
for i in range(24): for i in range(24):
if j == 0: if j == 0:
mine_large_block(self.nodes[1], self.utxo_cache_1) mine_large_block(self.nodes[1], self.utxo_cache_1)
else: else:
# Add node1's wallet transactions back to the mempool, to
# avoid the mined blocks from being too small.
self.nodes[1].resendwallettransactions()
self.nodes[1].generate(1) #tx's already in mempool from previous disconnects self.nodes[1].generate(1) #tx's already in mempool from previous disconnects
# Reorg back with 25 block chain from node 0 # Reorg back with 25 block chain from node 0
@ -159,6 +163,11 @@ class PruneTest(BitcoinTestFramework):
self.log.info("Usage possibly still high bc of stale blocks in block files: %d" % calc_usage(self.prunedir)) self.log.info("Usage possibly still high bc of stale blocks in block files: %d" % calc_usage(self.prunedir))
self.log.info("Mine 220 more blocks so we have requisite history (some blocks will be big and cause pruning of previous chain)") self.log.info("Mine 220 more blocks so we have requisite history (some blocks will be big and cause pruning of previous chain)")
# Get node0's wallet transactions back in its mempool, to avoid the
# mined blocks from being too small.
self.nodes[0].resendwallettransactions()
for i in range(22): for i in range(22):
# This can be slow, so do this in multiple RPC calls to avoid # This can be slow, so do this in multiple RPC calls to avoid
# RPC timeouts. # RPC timeouts.

Loading…
Cancel
Save