Skip to content

Commit 75a227e

Browse files
author
MarcoFalke
committed
Merge bitcoin#23683: bug fix: valid but different LockPoints after a reorg
b4adc5a [bugfix] update lockpoints correctly during reorg (glozow) b6002b0 MOVEONLY: update_lock_points to txmempool.h (glozow) Pull request description: I introduced a bug in bitcoin#22677 (sorry! 😅) Mempool entries cache `LockPoints`, containing the first height/blockhash/`CBlockIndex*` at which the transaction becomes valid. During a reorg, we re-check timelocks on all mempool entries using `CheckSequenceLocks(useExistingLockPoints=false)` and remove any now-invalid entries. `CheckSequenceLocks()` also mutates the `LockPoints` passed in, and we update valid entries' `LockPoints` using `update_lock_points`. Thus, `update_lock_points(lp)` needs to be called right after `CheckSequenceLocks(lp)`, otherwise we lose the data in `lp`. I incorrectly assumed they could be called in separate loops. The incorrect behavior introduced is: if we have a reorg in which a timelocked mempool transaction is still valid but becomes valid at a different block, the cached `LockPoints` will be incorrect. This PR fixes the bug, adds a test, and adds an assertion at the end of `removeForReorg()` to check that all mempool entries' lockpoints are valid. You can reproduce the bug by running the test added in the [test] commit on the code before the [bugfix] commit. ACKs for top commit: jnewbery: ACK b4adc5a vasild: ACK b4adc5a mzumsande: Code Review ACK b4adc5a hebasto: ACK b4adc5a MarcoFalke: re-ACK b4adc5a 🏁 Tree-SHA512: 16b59f6ff8140d0229079ca1c6b04f2f4a00a2e49931275150e4f3fe5ac4ec109698b083fa6b223ba9511f328271cc1ab081263669d5da020af7fee83c13e401
2 parents d69af93 + b4adc5a commit 75a227e

File tree

3 files changed

+13
-14
lines changed

3 files changed

+13
-14
lines changed

src/txmempool.cpp

+1-14
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,6 @@ struct update_fee_delta
6464
int64_t feeDelta;
6565
};
6666

67-
struct update_lock_points
68-
{
69-
explicit update_lock_points(const LockPoints& _lp) : lp(_lp) { }
70-
71-
void operator() (CTxMemPoolEntry &e) { e.UpdateLockPoints(lp); }
72-
73-
private:
74-
const LockPoints& lp;
75-
};
76-
7767
bool TestLockPointValidity(CChain& active_chain, const LockPoints& lp)
7868
{
7969
AssertLockHeld(cs_main);
@@ -649,10 +639,7 @@ void CTxMemPool::removeForReorg(CChain& chain, std::function<bool(txiter)> check
649639
}
650640
RemoveStaged(setAllRemoves, false, MemPoolRemovalReason::REORG);
651641
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
652-
const LockPoints lp{it->GetLockPoints()};
653-
if (!TestLockPointValidity(chain, lp)) {
654-
mapTx.modify(it, update_lock_points(lp));
655-
}
642+
assert(TestLockPointValidity(chain, it->GetLockPoints()));
656643
}
657644
}
658645

src/txmempool.h

+10
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,16 @@ class CompareTxMemPoolEntryByAncestorFee
312312
}
313313
};
314314

315+
struct update_lock_points
316+
{
317+
explicit update_lock_points(const LockPoints& _lp) : lp(_lp) { }
318+
319+
void operator() (CTxMemPoolEntry &e) { e.UpdateLockPoints(lp); }
320+
321+
private:
322+
const LockPoints& lp;
323+
};
324+
315325
// Multi_index tag names
316326
struct descendant_score {};
317327
struct entry_time {};

src/validation.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,8 @@ void CChainState::MaybeUpdateMempoolForReorg(
375375
}
376376
}
377377
}
378+
// CheckSequenceLocks updates lp. Update the mempool entry LockPoints.
379+
if (!validLP) m_mempool->mapTx.modify(it, update_lock_points(lp));
378380
return should_remove;
379381
};
380382

0 commit comments

Comments
 (0)