@@ -2174,22 +2174,35 @@ static int64_t nTimeFlush = 0;
2174
2174
static int64_t nTimeChainState = 0 ;
2175
2175
static int64_t nTimePostConnect = 0 ;
2176
2176
2177
+ struct PerBlockConnectTrace {
2178
+ CBlockIndex* pindex = NULL ;
2179
+ std::shared_ptr<const CBlock> pblock;
2180
+ std::shared_ptr<std::vector<CTransactionRef>> conflictedTxs;
2181
+ PerBlockConnectTrace () : conflictedTxs(std::make_shared<std::vector<CTransactionRef>>()) {}
2182
+ };
2177
2183
/* *
2178
2184
* Used to track blocks whose transactions were applied to the UTXO state as a
2179
2185
* part of a single ActivateBestChainStep call.
2180
2186
*
2181
2187
* This class also tracks transactions that are removed from the mempool as
2182
2188
* conflicts (per block) and can be used to pass all those transactions
2183
2189
* through SyncTransaction.
2190
+ *
2191
+ * This class assumes (and asserts) that the conflicted transactions for a given
2192
+ * block are added via mempool callbacks prior to the BlockConnected() associated
2193
+ * with those transactions. If any transactions are marked conflicted, it is
2194
+ * assumed that an associated block will always be added.
2195
+ *
2196
+ * This class is single-use, once you call GetBlocksConnected() you have to throw
2197
+ * it away and make a new one.
2184
2198
*/
2185
2199
class ConnectTrace {
2186
2200
private:
2187
- std::vector<std::pair<CBlockIndex*, std::shared_ptr<const CBlock> > > blocksConnected;
2188
- std::vector<std::vector<CTransactionRef> > conflictedTxs;
2201
+ std::vector<PerBlockConnectTrace> blocksConnected;
2189
2202
CTxMemPool &pool;
2190
2203
2191
2204
public:
2192
- ConnectTrace (CTxMemPool &_pool) : conflictedTxs (1 ), pool(_pool) {
2205
+ ConnectTrace (CTxMemPool &_pool) : blocksConnected (1 ), pool(_pool) {
2193
2206
pool.NotifyEntryRemoved .connect (boost::bind (&ConnectTrace::NotifyEntryRemoved, this , _1, _2));
2194
2207
}
2195
2208
@@ -2198,29 +2211,32 @@ class ConnectTrace {
2198
2211
}
2199
2212
2200
2213
void BlockConnected (CBlockIndex* pindex, std::shared_ptr<const CBlock> pblock) {
2201
- blocksConnected.emplace_back (pindex, std::move (pblock));
2202
- conflictedTxs.emplace_back ();
2203
- }
2204
-
2205
- std::vector<std::pair<CBlockIndex*, std::shared_ptr<const CBlock> > >& GetBlocksConnected () {
2214
+ assert (!blocksConnected.back ().pindex );
2215
+ assert (pindex);
2216
+ assert (pblock);
2217
+ blocksConnected.back ().pindex = pindex;
2218
+ blocksConnected.back ().pblock = std::move (pblock);
2219
+ blocksConnected.emplace_back ();
2220
+ }
2221
+
2222
+ std::vector<PerBlockConnectTrace>& GetBlocksConnected () {
2223
+ // We always keep one extra block at the end of our list because
2224
+ // blocks are added after all the conflicted transactions have
2225
+ // been filled in. Thus, the last entry should always be an empty
2226
+ // one waiting for the transactions from the next block. We pop
2227
+ // the last entry here to make sure the list we return is sane.
2228
+ assert (!blocksConnected.back ().pindex );
2229
+ assert (blocksConnected.back ().conflictedTxs ->empty ());
2230
+ blocksConnected.pop_back ();
2206
2231
return blocksConnected;
2207
2232
}
2208
2233
2209
2234
void NotifyEntryRemoved (CTransactionRef txRemoved, MemPoolRemovalReason reason) {
2235
+ assert (!blocksConnected.back ().pindex );
2210
2236
if (reason == MemPoolRemovalReason::CONFLICT) {
2211
- conflictedTxs .back ().push_back ( txRemoved);
2237
+ blocksConnected .back ().conflictedTxs -> emplace_back ( std::move ( txRemoved) );
2212
2238
}
2213
2239
}
2214
-
2215
- void CallSyncTransactionOnConflictedTransactions () {
2216
- for (const auto & txRemovedForBlock : conflictedTxs) {
2217
- for (const auto & tx : txRemovedForBlock) {
2218
- GetMainSignals ().SyncTransaction (*tx, NULL , CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK);
2219
- }
2220
- }
2221
- conflictedTxs.clear ();
2222
- conflictedTxs.emplace_back ();
2223
- }
2224
2240
};
2225
2241
2226
2242
/* *
@@ -2495,9 +2511,6 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
2495
2511
pindexFork = chainActive.FindFork (pindexOldTip);
2496
2512
fInitialDownload = IsInitialBlockDownload ();
2497
2513
2498
- // throw all transactions though the signal-interface
2499
- connectTrace.CallSyncTransactionOnConflictedTransactions ();
2500
-
2501
2514
// TODO: Temporarily ensure that mempool removals are notified before
2502
2515
// connected transactions. This shouldn't matter, but the abandoned
2503
2516
// state of transactions in our wallet is currently cleared when we
@@ -2506,12 +2519,21 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
2506
2519
// to abandon a transaction and then have it inadvertently cleared by
2507
2520
// the notification that the conflicted transaction was evicted.
2508
2521
2522
+ // throw all transactions though the signal-interface
2523
+ auto blocksConnected = connectTrace.GetBlocksConnected ();
2524
+ for (const PerBlockConnectTrace& trace : blocksConnected) {
2525
+ assert (trace.conflictedTxs );
2526
+ for (const auto & tx : *trace.conflictedTxs ) {
2527
+ GetMainSignals ().SyncTransaction (*tx, NULL , CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK);
2528
+ }
2529
+ }
2530
+
2509
2531
// Transactions in the connected block are notified
2510
- for (const auto & pair : connectTrace. GetBlocksConnected () ) {
2511
- assert (pair. second );
2512
- const CBlock& block = *(pair. second );
2532
+ for (const PerBlockConnectTrace& trace : blocksConnected ) {
2533
+ assert (trace. pblock && trace. pindex );
2534
+ const CBlock& block = *(trace. pblock );
2513
2535
for (unsigned int i = 0 ; i < block.vtx .size (); i++)
2514
- GetMainSignals ().SyncTransaction (*block.vtx [i], pair. first , i);
2536
+ GetMainSignals ().SyncTransaction (*block.vtx [i], trace. pindex , i);
2515
2537
}
2516
2538
}
2517
2539
// When we reach this point, we switched to a new tip (stored in pindexNewTip).
0 commit comments