@@ -154,39 +154,6 @@ namespace {
154
154
std::set<int > setDirtyFileInfo;
155
155
} // anon namespace
156
156
157
- /* Use this class to start tracking transactions that are removed from the
158
- * mempool and pass all those transactions through SyncTransaction when the
159
- * object goes out of scope. This is currently only used to call SyncTransaction
160
- * on conflicts removed from the mempool during block connection. Applied in
161
- * ActivateBestChain around ActivateBestStep which in turn calls:
162
- * ConnectTip->removeForBlock->removeConflicts
163
- */
164
- class MemPoolConflictRemovalTracker
165
- {
166
- private:
167
- std::vector<CTransactionRef> conflictedTxs;
168
- CTxMemPool &pool;
169
-
170
- public:
171
- MemPoolConflictRemovalTracker (CTxMemPool &_pool) : pool(_pool) {
172
- pool.NotifyEntryRemoved .connect (boost::bind (&MemPoolConflictRemovalTracker::NotifyEntryRemoved, this , _1, _2));
173
- }
174
-
175
- void NotifyEntryRemoved (CTransactionRef txRemoved, MemPoolRemovalReason reason) {
176
- if (reason == MemPoolRemovalReason::CONFLICT) {
177
- conflictedTxs.push_back (txRemoved);
178
- }
179
- }
180
-
181
- ~MemPoolConflictRemovalTracker () {
182
- pool.NotifyEntryRemoved .disconnect (boost::bind (&MemPoolConflictRemovalTracker::NotifyEntryRemoved, this , _1, _2));
183
- for (const auto & tx : conflictedTxs) {
184
- GetMainSignals ().SyncTransaction (*tx, NULL , CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK);
185
- }
186
- conflictedTxs.clear ();
187
- }
188
- };
189
-
190
157
CBlockIndex* FindForkInGlobalIndex (const CChain& chain, const CBlockLocator& locator)
191
158
{
192
159
// Find the first block the caller has in the main chain
@@ -982,7 +949,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
982
949
}
983
950
}
984
951
985
- GetMainSignals ().SyncTransaction (tx, NULL , CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK );
952
+ GetMainSignals ().TransactionAddedToMempool (ptx );
986
953
987
954
return true ;
988
955
}
@@ -2153,7 +2120,8 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
2153
2120
CBlockIndex *pindexDelete = chainActive.Tip ();
2154
2121
assert (pindexDelete);
2155
2122
// Read block from disk.
2156
- CBlock block;
2123
+ std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
2124
+ CBlock& block = *pblock;
2157
2125
if (!ReadBlockFromDisk (block, pindexDelete, chainparams.GetConsensus ()))
2158
2126
return AbortNode (state, " Failed to read block" );
2159
2127
// Apply the block atomically to the chain state.
@@ -2195,9 +2163,7 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
2195
2163
UpdateTip (pindexDelete->pprev , chainparams);
2196
2164
// Let wallets know transactions went from 1-confirmed to
2197
2165
// 0-confirmed or conflicted:
2198
- for (const auto & tx : block.vtx ) {
2199
- GetMainSignals ().SyncTransaction (*tx, pindexDelete->pprev , CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK);
2200
- }
2166
+ GetMainSignals ().BlockDisconnected (pblock);
2201
2167
return true ;
2202
2168
}
2203
2169
@@ -2207,36 +2173,92 @@ static int64_t nTimeFlush = 0;
2207
2173
static int64_t nTimeChainState = 0 ;
2208
2174
static int64_t nTimePostConnect = 0 ;
2209
2175
2176
+ struct PerBlockConnectTrace {
2177
+ CBlockIndex* pindex = NULL ;
2178
+ std::shared_ptr<const CBlock> pblock;
2179
+ std::shared_ptr<std::vector<CTransactionRef>> conflictedTxs;
2180
+ PerBlockConnectTrace () : conflictedTxs(std::make_shared<std::vector<CTransactionRef>>()) {}
2181
+ };
2210
2182
/* *
2211
2183
* Used to track blocks whose transactions were applied to the UTXO state as a
2212
2184
* part of a single ActivateBestChainStep call.
2185
+ *
2186
+ * This class also tracks transactions that are removed from the mempool as
2187
+ * conflicts (per block) and can be used to pass all those transactions
2188
+ * through SyncTransaction.
2189
+ *
2190
+ * This class assumes (and asserts) that the conflicted transactions for a given
2191
+ * block are added via mempool callbacks prior to the BlockConnected() associated
2192
+ * with those transactions. If any transactions are marked conflicted, it is
2193
+ * assumed that an associated block will always be added.
2194
+ *
2195
+ * This class is single-use, once you call GetBlocksConnected() you have to throw
2196
+ * it away and make a new one.
2213
2197
*/
2214
- struct ConnectTrace {
2215
- std::vector<std::pair<CBlockIndex*, std::shared_ptr<const CBlock> > > blocksConnected;
2198
+ class ConnectTrace {
2199
+ private:
2200
+ std::vector<PerBlockConnectTrace> blocksConnected;
2201
+ CTxMemPool &pool;
2202
+
2203
+ public:
2204
+ ConnectTrace (CTxMemPool &_pool) : blocksConnected(1 ), pool(_pool) {
2205
+ pool.NotifyEntryRemoved .connect (boost::bind (&ConnectTrace::NotifyEntryRemoved, this , _1, _2));
2206
+ }
2207
+
2208
+ ~ConnectTrace () {
2209
+ pool.NotifyEntryRemoved .disconnect (boost::bind (&ConnectTrace::NotifyEntryRemoved, this , _1, _2));
2210
+ }
2211
+
2212
+ void BlockConnected (CBlockIndex* pindex, std::shared_ptr<const CBlock> pblock) {
2213
+ assert (!blocksConnected.back ().pindex );
2214
+ assert (pindex);
2215
+ assert (pblock);
2216
+ blocksConnected.back ().pindex = pindex;
2217
+ blocksConnected.back ().pblock = std::move (pblock);
2218
+ blocksConnected.emplace_back ();
2219
+ }
2220
+
2221
+ std::vector<PerBlockConnectTrace>& GetBlocksConnected () {
2222
+ // We always keep one extra block at the end of our list because
2223
+ // blocks are added after all the conflicted transactions have
2224
+ // been filled in. Thus, the last entry should always be an empty
2225
+ // one waiting for the transactions from the next block. We pop
2226
+ // the last entry here to make sure the list we return is sane.
2227
+ assert (!blocksConnected.back ().pindex );
2228
+ assert (blocksConnected.back ().conflictedTxs ->empty ());
2229
+ blocksConnected.pop_back ();
2230
+ return blocksConnected;
2231
+ }
2232
+
2233
+ void NotifyEntryRemoved (CTransactionRef txRemoved, MemPoolRemovalReason reason) {
2234
+ assert (!blocksConnected.back ().pindex );
2235
+ if (reason == MemPoolRemovalReason::CONFLICT) {
2236
+ blocksConnected.back ().conflictedTxs ->emplace_back (std::move (txRemoved));
2237
+ }
2238
+ }
2216
2239
};
2217
2240
2218
2241
/* *
2219
2242
* Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock
2220
2243
* corresponding to pindexNew, to bypass loading it again from disk.
2221
2244
*
2222
- * The block is always added to connectTrace (either after loading from disk or by copying
2223
- * pblock) - if that is not intended, care must be taken to remove the last entry in
2224
- * blocksConnected in case of failure.
2245
+ * The block is added to connectTrace if connection succeeds.
2225
2246
*/
2226
2247
bool static ConnectTip (CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace)
2227
2248
{
2228
2249
assert (pindexNew->pprev == chainActive.Tip ());
2229
2250
// Read block from disk.
2230
2251
int64_t nTime1 = GetTimeMicros ();
2252
+ std::shared_ptr<const CBlock> pthisBlock;
2231
2253
if (!pblock) {
2232
2254
std::shared_ptr<CBlock> pblockNew = std::make_shared<CBlock>();
2233
- connectTrace.blocksConnected .emplace_back (pindexNew, pblockNew);
2234
2255
if (!ReadBlockFromDisk (*pblockNew, pindexNew, chainparams.GetConsensus ()))
2235
2256
return AbortNode (state, " Failed to read block" );
2257
+ pthisBlock = pblockNew;
2236
2258
} else {
2237
- connectTrace. blocksConnected . emplace_back (pindexNew, pblock) ;
2259
+ pthisBlock = pblock;
2238
2260
}
2239
- const CBlock& blockConnecting = *connectTrace. blocksConnected . back (). second ;
2261
+ const CBlock& blockConnecting = *pthisBlock ;
2240
2262
// Apply the block atomically to the chain state.
2241
2263
int64_t nTime2 = GetTimeMicros (); nTimeReadFromDisk += nTime2 - nTime1;
2242
2264
int64_t nTime3;
@@ -2270,6 +2292,8 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
2270
2292
int64_t nTime6 = GetTimeMicros (); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
2271
2293
LogPrint (BCLog::BENCH, " - Connect postprocess: %.2fms [%.2fs]\n " , (nTime6 - nTime5) * 0.001 , nTimePostConnect * 0.000001 );
2272
2294
LogPrint (BCLog::BENCH, " - Connect block: %.2fms [%.2fs]\n " , (nTime6 - nTime1) * 0.001 , nTimeTotal * 0.000001 );
2295
+
2296
+ connectTrace.BlockConnected (pindexNew, std::move (pthisBlock));
2273
2297
return true ;
2274
2298
}
2275
2299
@@ -2388,8 +2412,6 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
2388
2412
state = CValidationState ();
2389
2413
fInvalidFound = true ;
2390
2414
fContinue = false ;
2391
- // If we didn't actually connect the block, don't notify listeners about it
2392
- connectTrace.blocksConnected .pop_back ();
2393
2415
break ;
2394
2416
} else {
2395
2417
// A system error occurred (disk space, database error, ...).
@@ -2461,18 +2483,11 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
2461
2483
break ;
2462
2484
2463
2485
const CBlockIndex *pindexFork;
2464
- ConnectTrace connectTrace;
2465
2486
bool fInitialDownload ;
2466
2487
{
2467
2488
LOCK (cs_main);
2468
- { // TODO: Temporarily ensure that mempool removals are notified before
2469
- // connected transactions. This shouldn't matter, but the abandoned
2470
- // state of transactions in our wallet is currently cleared when we
2471
- // receive another notification and there is a race condition where
2472
- // notification of a connected conflict might cause an outside process
2473
- // to abandon a transaction and then have it inadvertently cleared by
2474
- // the notification that the conflicted transaction was evicted.
2475
- MemPoolConflictRemovalTracker mrt (mempool);
2489
+ ConnectTrace connectTrace (mempool); // Destructed before cs_main is unlocked
2490
+
2476
2491
CBlockIndex *pindexOldTip = chainActive.Tip ();
2477
2492
if (pindexMostWork == NULL ) {
2478
2493
pindexMostWork = FindMostWorkChain ();
@@ -2495,16 +2510,9 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
2495
2510
pindexFork = chainActive.FindFork (pindexOldTip);
2496
2511
fInitialDownload = IsInitialBlockDownload ();
2497
2512
2498
- // throw all transactions though the signal-interface
2499
-
2500
- } // MemPoolConflictRemovalTracker destroyed and conflict evictions are notified
2501
-
2502
- // Transactions in the connected block are notified
2503
- for (const auto & pair : connectTrace.blocksConnected ) {
2504
- assert (pair.second );
2505
- const CBlock& block = *(pair.second );
2506
- for (unsigned int i = 0 ; i < block.vtx .size (); i++)
2507
- GetMainSignals ().SyncTransaction (*block.vtx [i], pair.first , i);
2513
+ for (const PerBlockConnectTrace& trace : connectTrace.GetBlocksConnected ()) {
2514
+ assert (trace.pblock && trace.pindex );
2515
+ GetMainSignals ().BlockConnected (trace.pblock , trace.pindex , *trace.conflictedTxs );
2508
2516
}
2509
2517
}
2510
2518
// When we reach this point, we switched to a new tip (stored in pindexNewTip).
0 commit comments