Handle conflicted transactions directly in ConnectTrace
This commit is contained in:
parent
29e6e231c8
commit
d3167ba9bb
1 changed files with 38 additions and 44 deletions
|
@ -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
|
||||
|
@ -2210,12 +2177,26 @@ static int64_t nTimePostConnect = 0;
|
|||
/**
|
||||
* 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 and can be used to pass all those transactions through
|
||||
* SyncTransaction.
|
||||
*/
|
||||
struct ConnectTrace {
|
||||
class ConnectTrace {
|
||||
private:
|
||||
std::vector<std::pair<CBlockIndex*, std::shared_ptr<const CBlock> > > blocksConnected;
|
||||
std::vector<CTransactionRef> conflictedTxs;
|
||||
CTxMemPool &pool;
|
||||
|
||||
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) {
|
||||
blocksConnected.emplace_back(pindex, std::move(pblock));
|
||||
}
|
||||
|
@ -2223,6 +2204,19 @@ public:
|
|||
std::vector<std::pair<CBlockIndex*, std::shared_ptr<const CBlock> > >& GetBlocksConnected() {
|
||||
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;
|
||||
|
||||
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();
|
||||
|
@ -2505,8 +2492,15 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
|
|||
fInitialDownload = IsInitialBlockDownload();
|
||||
|
||||
// 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
|
||||
for (const auto& pair : connectTrace.GetBlocksConnected()) {
|
||||
|
|
Loading…
Reference in a new issue