Handle conflicted transactions directly in ConnectTrace

This commit is contained in:
Matt Corallo 2017-03-06 17:19:22 -05:00
parent 29e6e231c8
commit d3167ba9bb

View file

@ -154,39 +154,6 @@ namespace {
std::set<int> setDirtyFileInfo; std::set<int> setDirtyFileInfo;
} // anon namespace } // 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) CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator)
{ {
// Find the first block the caller has in the main chain // Find the first block the caller has in the main chain
@ -2210,12 +2177,26 @@ static int64_t nTimePostConnect = 0;
/** /**
* Used to track blocks whose transactions were applied to the UTXO state as a * Used to track blocks whose transactions were applied to the UTXO state as a
* part of a single ActivateBestChainStep call. * part of a single ActivateBestChainStep call.
*
* This class also tracks transactions that are removed from the mempool as
* conflicts and can be used to pass all those transactions through
* SyncTransaction.
*/ */
struct ConnectTrace { class ConnectTrace {
private: private:
std::vector<std::pair<CBlockIndex*, std::shared_ptr<const CBlock> > > blocksConnected; std::vector<std::pair<CBlockIndex*, std::shared_ptr<const CBlock> > > blocksConnected;
std::vector<CTransactionRef> conflictedTxs;
CTxMemPool &pool;
public: public:
ConnectTrace(CTxMemPool &_pool) : 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) { void BlockConnected(CBlockIndex* pindex, std::shared_ptr<const CBlock> pblock) {
blocksConnected.emplace_back(pindex, std::move(pblock)); blocksConnected.emplace_back(pindex, std::move(pblock));
} }
@ -2223,6 +2204,19 @@ public:
std::vector<std::pair<CBlockIndex*, std::shared_ptr<const CBlock> > >& GetBlocksConnected() { std::vector<std::pair<CBlockIndex*, std::shared_ptr<const CBlock> > >& GetBlocksConnected() {
return blocksConnected; return blocksConnected;
} }
void NotifyEntryRemoved(CTransactionRef txRemoved, MemPoolRemovalReason reason) {
if (reason == MemPoolRemovalReason::CONFLICT) {
conflictedTxs.push_back(txRemoved);
}
}
void CallSyncTransactionOnConflictedTransactions() {
for (const auto& tx : conflictedTxs) {
GetMainSignals().SyncTransaction(*tx, NULL, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK);
}
conflictedTxs.clear();
}
}; };
/** /**
@ -2470,18 +2464,11 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
break; break;
const CBlockIndex *pindexFork; const CBlockIndex *pindexFork;
ConnectTrace connectTrace;
bool fInitialDownload; bool fInitialDownload;
{ {
LOCK(cs_main); LOCK(cs_main);
{ // TODO: Temporarily ensure that mempool removals are notified before ConnectTrace connectTrace(mempool); // Destructed before cs_main is unlocked
// 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);
CBlockIndex *pindexOldTip = chainActive.Tip(); CBlockIndex *pindexOldTip = chainActive.Tip();
if (pindexMostWork == NULL) { if (pindexMostWork == NULL) {
pindexMostWork = FindMostWorkChain(); pindexMostWork = FindMostWorkChain();
@ -2505,8 +2492,15 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
fInitialDownload = IsInitialBlockDownload(); fInitialDownload = IsInitialBlockDownload();
// throw all transactions though the signal-interface // throw all transactions though the signal-interface
connectTrace.CallSyncTransactionOnConflictedTransactions();
} // MemPoolConflictRemovalTracker destroyed and conflict evictions are notified // 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.
// Transactions in the connected block are notified // Transactions in the connected block are notified
for (const auto& pair : connectTrace.GetBlocksConnected()) { for (const auto& pair : connectTrace.GetBlocksConnected()) {