mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-03-25 06:55:32 +01:00
Merge #9725: CValidationInterface Cleanups
b1a6d4cTake a CTransactionRef in AddToWalletIfInvolvingMe to avoid a copy (Matt Corallo)1c95e2fUse std::shared_ptr instead of boost::shared_ptr in ScriptForMining (Matt Corallo)91f1e6cRemove dead-code tracking of requests for blocks we generated (Matt Corallo)acad82fAdd override to functions using CValidationInterface methods (Matt Corallo)e6d5e6cHold cs_wallet for whole block [dis]connection processing (Matt Corallo)461e49fSyncTransaction->TxAddedToMempool/BlockConnected/Disconnected (Matt Corallo)f404334Handle SyncTransaction in ActivateBestChain instead of ConnectTrace (Matt Corallo)a147687Keep conflictedTxs in ConnectTrace per-block (Matt Corallo)d3167baHandle conflicted transactions directly in ConnectTrace (Matt Corallo)29e6e23Make ConnectTrace::blocksConnected private, hide behind accessors (Matt Corallo)822000cAdd pblock to connectTrace at the end of ConnectTip, not start (Matt Corallo)f5e9a01Include missing #include in zmqnotificationinterface.h (Matt Corallo) Tree-SHA512: 8893d47559da3b28d2ef7359768547cba8a4b43b6f891d80f5848f995a84b1517bfb0f706fdc8cd43f09a1350349eb440d9724a59363ab517dfcc4fcb31b2018
This commit is contained in:
@@ -154,39 +154,6 @@ namespace {
|
||||
std::set<int> setDirtyFileInfo;
|
||||
} // anon namespace
|
||||
|
||||
/* Use this class to start tracking transactions that are removed from the
|
||||
* mempool and pass all those transactions through SyncTransaction when the
|
||||
* object goes out of scope. This is currently only used to call SyncTransaction
|
||||
* on conflicts removed from the mempool during block connection. Applied in
|
||||
* ActivateBestChain around ActivateBestStep which in turn calls:
|
||||
* ConnectTip->removeForBlock->removeConflicts
|
||||
*/
|
||||
class MemPoolConflictRemovalTracker
|
||||
{
|
||||
private:
|
||||
std::vector<CTransactionRef> conflictedTxs;
|
||||
CTxMemPool &pool;
|
||||
|
||||
public:
|
||||
MemPoolConflictRemovalTracker(CTxMemPool &_pool) : pool(_pool) {
|
||||
pool.NotifyEntryRemoved.connect(boost::bind(&MemPoolConflictRemovalTracker::NotifyEntryRemoved, this, _1, _2));
|
||||
}
|
||||
|
||||
void NotifyEntryRemoved(CTransactionRef txRemoved, MemPoolRemovalReason reason) {
|
||||
if (reason == MemPoolRemovalReason::CONFLICT) {
|
||||
conflictedTxs.push_back(txRemoved);
|
||||
}
|
||||
}
|
||||
|
||||
~MemPoolConflictRemovalTracker() {
|
||||
pool.NotifyEntryRemoved.disconnect(boost::bind(&MemPoolConflictRemovalTracker::NotifyEntryRemoved, this, _1, _2));
|
||||
for (const auto& tx : conflictedTxs) {
|
||||
GetMainSignals().SyncTransaction(*tx, NULL, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK);
|
||||
}
|
||||
conflictedTxs.clear();
|
||||
}
|
||||
};
|
||||
|
||||
CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator)
|
||||
{
|
||||
// Find the first block the caller has in the main chain
|
||||
@@ -982,7 +949,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
|
||||
}
|
||||
}
|
||||
|
||||
GetMainSignals().SyncTransaction(tx, NULL, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK);
|
||||
GetMainSignals().TransactionAddedToMempool(ptx);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -2153,7 +2120,8 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
|
||||
CBlockIndex *pindexDelete = chainActive.Tip();
|
||||
assert(pindexDelete);
|
||||
// Read block from disk.
|
||||
CBlock block;
|
||||
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
|
||||
CBlock& block = *pblock;
|
||||
if (!ReadBlockFromDisk(block, pindexDelete, chainparams.GetConsensus()))
|
||||
return AbortNode(state, "Failed to read block");
|
||||
// Apply the block atomically to the chain state.
|
||||
@@ -2195,9 +2163,7 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
|
||||
UpdateTip(pindexDelete->pprev, chainparams);
|
||||
// Let wallets know transactions went from 1-confirmed to
|
||||
// 0-confirmed or conflicted:
|
||||
for (const auto& tx : block.vtx) {
|
||||
GetMainSignals().SyncTransaction(*tx, pindexDelete->pprev, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK);
|
||||
}
|
||||
GetMainSignals().BlockDisconnected(pblock);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2207,36 +2173,92 @@ static int64_t nTimeFlush = 0;
|
||||
static int64_t nTimeChainState = 0;
|
||||
static int64_t nTimePostConnect = 0;
|
||||
|
||||
struct PerBlockConnectTrace {
|
||||
CBlockIndex* pindex = NULL;
|
||||
std::shared_ptr<const CBlock> pblock;
|
||||
std::shared_ptr<std::vector<CTransactionRef>> conflictedTxs;
|
||||
PerBlockConnectTrace() : conflictedTxs(std::make_shared<std::vector<CTransactionRef>>()) {}
|
||||
};
|
||||
/**
|
||||
* Used to track blocks whose transactions were applied to the UTXO state as a
|
||||
* part of a single ActivateBestChainStep call.
|
||||
*
|
||||
* This class also tracks transactions that are removed from the mempool as
|
||||
* conflicts (per block) and can be used to pass all those transactions
|
||||
* through SyncTransaction.
|
||||
*
|
||||
* This class assumes (and asserts) that the conflicted transactions for a given
|
||||
* block are added via mempool callbacks prior to the BlockConnected() associated
|
||||
* with those transactions. If any transactions are marked conflicted, it is
|
||||
* assumed that an associated block will always be added.
|
||||
*
|
||||
* This class is single-use, once you call GetBlocksConnected() you have to throw
|
||||
* it away and make a new one.
|
||||
*/
|
||||
struct ConnectTrace {
|
||||
std::vector<std::pair<CBlockIndex*, std::shared_ptr<const CBlock> > > blocksConnected;
|
||||
class ConnectTrace {
|
||||
private:
|
||||
std::vector<PerBlockConnectTrace> blocksConnected;
|
||||
CTxMemPool &pool;
|
||||
|
||||
public:
|
||||
ConnectTrace(CTxMemPool &_pool) : blocksConnected(1), pool(_pool) {
|
||||
pool.NotifyEntryRemoved.connect(boost::bind(&ConnectTrace::NotifyEntryRemoved, this, _1, _2));
|
||||
}
|
||||
|
||||
~ConnectTrace() {
|
||||
pool.NotifyEntryRemoved.disconnect(boost::bind(&ConnectTrace::NotifyEntryRemoved, this, _1, _2));
|
||||
}
|
||||
|
||||
void BlockConnected(CBlockIndex* pindex, std::shared_ptr<const CBlock> pblock) {
|
||||
assert(!blocksConnected.back().pindex);
|
||||
assert(pindex);
|
||||
assert(pblock);
|
||||
blocksConnected.back().pindex = pindex;
|
||||
blocksConnected.back().pblock = std::move(pblock);
|
||||
blocksConnected.emplace_back();
|
||||
}
|
||||
|
||||
std::vector<PerBlockConnectTrace>& GetBlocksConnected() {
|
||||
// We always keep one extra block at the end of our list because
|
||||
// blocks are added after all the conflicted transactions have
|
||||
// been filled in. Thus, the last entry should always be an empty
|
||||
// one waiting for the transactions from the next block. We pop
|
||||
// the last entry here to make sure the list we return is sane.
|
||||
assert(!blocksConnected.back().pindex);
|
||||
assert(blocksConnected.back().conflictedTxs->empty());
|
||||
blocksConnected.pop_back();
|
||||
return blocksConnected;
|
||||
}
|
||||
|
||||
void NotifyEntryRemoved(CTransactionRef txRemoved, MemPoolRemovalReason reason) {
|
||||
assert(!blocksConnected.back().pindex);
|
||||
if (reason == MemPoolRemovalReason::CONFLICT) {
|
||||
blocksConnected.back().conflictedTxs->emplace_back(std::move(txRemoved));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock
|
||||
* corresponding to pindexNew, to bypass loading it again from disk.
|
||||
*
|
||||
* The block is always added to connectTrace (either after loading from disk or by copying
|
||||
* pblock) - if that is not intended, care must be taken to remove the last entry in
|
||||
* blocksConnected in case of failure.
|
||||
* 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)
|
||||
{
|
||||
assert(pindexNew->pprev == chainActive.Tip());
|
||||
// Read block from disk.
|
||||
int64_t nTime1 = GetTimeMicros();
|
||||
std::shared_ptr<const CBlock> pthisBlock;
|
||||
if (!pblock) {
|
||||
std::shared_ptr<CBlock> pblockNew = std::make_shared<CBlock>();
|
||||
connectTrace.blocksConnected.emplace_back(pindexNew, pblockNew);
|
||||
if (!ReadBlockFromDisk(*pblockNew, pindexNew, chainparams.GetConsensus()))
|
||||
return AbortNode(state, "Failed to read block");
|
||||
pthisBlock = pblockNew;
|
||||
} else {
|
||||
connectTrace.blocksConnected.emplace_back(pindexNew, pblock);
|
||||
pthisBlock = pblock;
|
||||
}
|
||||
const CBlock& blockConnecting = *connectTrace.blocksConnected.back().second;
|
||||
const CBlock& blockConnecting = *pthisBlock;
|
||||
// Apply the block atomically to the chain state.
|
||||
int64_t nTime2 = GetTimeMicros(); nTimeReadFromDisk += nTime2 - nTime1;
|
||||
int64_t nTime3;
|
||||
@@ -2270,6 +2292,8 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
|
||||
int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
|
||||
LogPrint(BCLog::BENCH, " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001);
|
||||
LogPrint(BCLog::BENCH, "- Connect block: %.2fms [%.2fs]\n", (nTime6 - nTime1) * 0.001, nTimeTotal * 0.000001);
|
||||
|
||||
connectTrace.BlockConnected(pindexNew, std::move(pthisBlock));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2388,8 +2412,6 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
|
||||
state = CValidationState();
|
||||
fInvalidFound = true;
|
||||
fContinue = false;
|
||||
// If we didn't actually connect the block, don't notify listeners about it
|
||||
connectTrace.blocksConnected.pop_back();
|
||||
break;
|
||||
} else {
|
||||
// A system error occurred (disk space, database error, ...).
|
||||
@@ -2461,18 +2483,11 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
|
||||
break;
|
||||
|
||||
const CBlockIndex *pindexFork;
|
||||
ConnectTrace connectTrace;
|
||||
bool fInitialDownload;
|
||||
{
|
||||
LOCK(cs_main);
|
||||
{ // TODO: Temporarily ensure that mempool removals are notified before
|
||||
// connected transactions. This shouldn't matter, but the abandoned
|
||||
// state of transactions in our wallet is currently cleared when we
|
||||
// receive another notification and there is a race condition where
|
||||
// notification of a connected conflict might cause an outside process
|
||||
// to abandon a transaction and then have it inadvertently cleared by
|
||||
// the notification that the conflicted transaction was evicted.
|
||||
MemPoolConflictRemovalTracker mrt(mempool);
|
||||
ConnectTrace connectTrace(mempool); // Destructed before cs_main is unlocked
|
||||
|
||||
CBlockIndex *pindexOldTip = chainActive.Tip();
|
||||
if (pindexMostWork == NULL) {
|
||||
pindexMostWork = FindMostWorkChain();
|
||||
@@ -2495,16 +2510,9 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
|
||||
pindexFork = chainActive.FindFork(pindexOldTip);
|
||||
fInitialDownload = IsInitialBlockDownload();
|
||||
|
||||
// throw all transactions though the signal-interface
|
||||
|
||||
} // MemPoolConflictRemovalTracker destroyed and conflict evictions are notified
|
||||
|
||||
// Transactions in the connected block are notified
|
||||
for (const auto& pair : connectTrace.blocksConnected) {
|
||||
assert(pair.second);
|
||||
const CBlock& block = *(pair.second);
|
||||
for (unsigned int i = 0; i < block.vtx.size(); i++)
|
||||
GetMainSignals().SyncTransaction(*block.vtx[i], pair.first, i);
|
||||
for (const PerBlockConnectTrace& trace : connectTrace.GetBlocksConnected()) {
|
||||
assert(trace.pblock && trace.pindex);
|
||||
GetMainSignals().BlockConnected(trace.pblock, trace.pindex, *trace.conflictedTxs);
|
||||
}
|
||||
}
|
||||
// When we reach this point, we switched to a new tip (stored in pindexNewTip).
|
||||
|
||||
Reference in New Issue
Block a user