Skip to content

Commit 75db62b

Browse files
committed
refactor: Move calculation logic out from CheckSequenceLocksAtTip()
1 parent 3bc434f commit 75db62b

File tree

3 files changed

+27
-90
lines changed

3 files changed

+27
-90
lines changed

src/test/miner_tests.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ struct MinerTestingSetup : public TestingSetup {
3636
bool TestSequenceLocks(const CTransaction& tx, CTxMemPool& tx_mempool) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
3737
{
3838
CCoinsViewMemPool view_mempool{&m_node.chainman->ActiveChainstate().CoinsTip(), tx_mempool};
39-
return CheckSequenceLocksAtTip(m_node.chainman->ActiveChain().Tip(), view_mempool, tx);
39+
CBlockIndex* tip{m_node.chainman->ActiveChain().Tip()};
40+
const std::optional<LockPoints> lock_points{CalculateLockPointsAtTip(tip, view_mempool, tx)};
41+
return lock_points.has_value() && CheckSequenceLocksAtTip(tip, *lock_points);
4042
}
4143
CTxMemPool& MakeMempool()
4244
{

src/validation.cpp

Lines changed: 21 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -252,10 +252,7 @@ std::optional<LockPoints> CalculateLockPointsAtTip(
252252
}
253253

254254
bool CheckSequenceLocksAtTip(CBlockIndex* tip,
255-
const CCoinsView& coins_view,
256-
const CTransaction& tx,
257-
LockPoints* lp,
258-
bool useExistingLockPoints)
255+
const LockPoints& lock_points)
259256
{
260257
assert(tip != nullptr);
261258

@@ -269,61 +266,7 @@ bool CheckSequenceLocksAtTip(CBlockIndex* tip,
269266
// *next* block, we need to use one more than active_chainstate.m_chain.Height()
270267
index.nHeight = tip->nHeight + 1;
271268

272-
std::pair<int, int64_t> lockPair;
273-
if (useExistingLockPoints) {
274-
assert(lp);
275-
lockPair.first = lp->height;
276-
lockPair.second = lp->time;
277-
}
278-
else {
279-
std::vector<int> prevheights;
280-
prevheights.resize(tx.vin.size());
281-
for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) {
282-
const CTxIn& txin = tx.vin[txinIndex];
283-
Coin coin;
284-
if (!coins_view.GetCoin(txin.prevout, coin)) {
285-
return error("%s: Missing input", __func__);
286-
}
287-
if (coin.nHeight == MEMPOOL_HEIGHT) {
288-
// Assume all mempool transaction confirm in the next block
289-
prevheights[txinIndex] = tip->nHeight + 1;
290-
} else {
291-
prevheights[txinIndex] = coin.nHeight;
292-
}
293-
}
294-
lockPair = CalculateSequenceLocks(tx, STANDARD_LOCKTIME_VERIFY_FLAGS, prevheights, index);
295-
if (lp) {
296-
lp->height = lockPair.first;
297-
lp->time = lockPair.second;
298-
// Also store the hash of the block with the highest height of
299-
// all the blocks which have sequence locked prevouts.
300-
// This hash needs to still be on the chain
301-
// for these LockPoint calculations to be valid
302-
// Note: It is impossible to correctly calculate a maxInputBlock
303-
// if any of the sequence locked inputs depend on unconfirmed txs,
304-
// except in the special case where the relative lock time/height
305-
// is 0, which is equivalent to no sequence lock. Since we assume
306-
// input height of tip+1 for mempool txs and test the resulting
307-
// lockPair from CalculateSequenceLocks against tip+1. We know
308-
// EvaluateSequenceLocks will fail if there was a non-zero sequence
309-
// lock on a mempool input, so we can use the return value of
310-
// CheckSequenceLocksAtTip to indicate the LockPoints validity
311-
int maxInputHeight = 0;
312-
for (const int height : prevheights) {
313-
// Can ignore mempool inputs since we'll fail if they had non-zero locks
314-
if (height != tip->nHeight+1) {
315-
maxInputHeight = std::max(maxInputHeight, height);
316-
}
317-
}
318-
// tip->GetAncestor(maxInputHeight) should never return a nullptr
319-
// because maxInputHeight is always less than the tip height.
320-
// It would, however, be a bad bug to continue execution, since a
321-
// LockPoints object with the maxInputBlock member set to nullptr
322-
// signifies no relative lock time.
323-
lp->maxInputBlock = Assert(tip->GetAncestor(maxInputHeight));
324-
}
325-
}
326-
return EvaluateSequenceLocks(index, lockPair);
269+
return EvaluateSequenceLocks(index, {lock_points.height, lock_points.time});
327270
}
328271

329272
// Returns the script flags which should be checked for a given block
@@ -409,20 +352,23 @@ void Chainstate::MaybeUpdateMempoolForReorg(
409352

410353
// The transaction must be final.
411354
if (!CheckFinalTxAtTip(*Assert(m_chain.Tip()), tx)) return true;
412-
LockPoints lp = it->GetLockPoints();
413-
const bool validLP{TestLockPointValidity(m_chain, lp)};
414-
CCoinsViewMemPool view_mempool(&CoinsTip(), *m_mempool);
355+
356+
const LockPoints& lp = it->GetLockPoints();
415357
// CheckSequenceLocksAtTip checks if the transaction will be final in the next block to be
416-
// created on top of the new chain. We use useExistingLockPoints=false so that, instead of
417-
// using the information in lp (which might now refer to a block that no longer exists in
418-
// the chain), it will update lp to contain LockPoints relevant to the new chain.
419-
if (!CheckSequenceLocksAtTip(m_chain.Tip(), view_mempool, tx, &lp, validLP)) {
420-
// If CheckSequenceLocksAtTip fails, remove the tx and don't depend on the LockPoints.
421-
return true;
422-
} else if (!validLP) {
423-
// If CheckSequenceLocksAtTip succeeded, it also updated the LockPoints.
424-
// Now update the mempool entry lockpoints as well.
425-
m_mempool->mapTx.modify(it, [&lp](CTxMemPoolEntry& e) { e.UpdateLockPoints(lp); });
358+
// created on top of the new chain.
359+
if (TestLockPointValidity(m_chain, lp)) {
360+
if (!CheckSequenceLocksAtTip(m_chain.Tip(), lp)) {
361+
return true;
362+
}
363+
} else {
364+
const CCoinsViewMemPool view_mempool{&CoinsTip(), *m_mempool};
365+
const std::optional<LockPoints> new_lock_points{CalculateLockPointsAtTip(m_chain.Tip(), view_mempool, tx)};
366+
if (new_lock_points.has_value() && CheckSequenceLocksAtTip(m_chain.Tip(), *new_lock_points)) {
367+
// Now update the mempool entry lockpoints as well.
368+
m_mempool->mapTx.modify(it, [&new_lock_points](CTxMemPoolEntry& e) { e.UpdateLockPoints(*new_lock_points); });
369+
} else {
370+
return true;
371+
}
426372
}
427373

428374
// If the transaction spends any coinbase outputs, it must be mature.
@@ -824,7 +770,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
824770
}
825771
}
826772

827-
LockPoints lp;
828773
m_view.SetBackend(m_viewmempool);
829774

830775
const CCoinsViewCache& coins_cache = m_active_chainstate.CoinsTip();
@@ -866,7 +811,8 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
866811
// be mined yet.
867812
// Pass in m_view which has all of the relevant inputs cached. Note that, since m_view's
868813
// backend was removed, it no longer pulls coins from the mempool.
869-
if (!CheckSequenceLocksAtTip(m_active_chainstate.m_chain.Tip(), m_view, tx, &lp)) {
814+
const std::optional<LockPoints> lock_points{CalculateLockPointsAtTip(m_active_chainstate.m_chain.Tip(), m_view, tx)};
815+
if (!lock_points.has_value() || !CheckSequenceLocksAtTip(m_active_chainstate.m_chain.Tip(), *lock_points)) {
870816
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-BIP68-final");
871817
}
872818

@@ -902,7 +848,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
902848
}
903849

904850
entry.reset(new CTxMemPoolEntry(ptx, ws.m_base_fees, nAcceptTime, m_active_chainstate.m_chain.Height(),
905-
fSpendsCoinbase, nSigOpsCost, lp));
851+
fSpendsCoinbase, nSigOpsCost, lock_points.value()));
906852
ws.m_vsize = entry->GetTxSize();
907853

908854
if (nSigOpsCost > MAX_STANDARD_TX_SIGOPS_COST)

src/validation.h

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -271,24 +271,13 @@ std::optional<LockPoints> CalculateLockPointsAtTip(
271271
* Check if transaction will be BIP68 final in the next block to be created on top of tip.
272272
* @param[in] tip Chain tip to check tx sequence locks against. For example,
273273
* the tip of the current active chain.
274-
* @param[in] coins_view Any CCoinsView that provides access to the relevant coins for
275-
* checking sequence locks. For example, it can be a CCoinsViewCache
276-
* that isn't connected to anything but contains all the relevant
277-
* coins, or a CCoinsViewMemPool that is connected to the
278-
* mempool and chainstate UTXO set. In the latter case, the caller is
279-
* responsible for holding the appropriate locks to ensure that
280-
* calls to GetCoin() return correct coins.
274+
* @param[in] lock_points LockPoints containing the height and time at which this
275+
* transaction is final.
281276
* Simulates calling SequenceLocks() with data from the tip passed in.
282-
* Optionally stores in LockPoints the resulting height and time calculated and the hash
283-
* of the block needed for calculation or skips the calculation and uses the LockPoints
284-
* passed in for evaluation.
285277
* The LockPoints should not be considered valid if CheckSequenceLocksAtTip returns false.
286278
*/
287279
bool CheckSequenceLocksAtTip(CBlockIndex* tip,
288-
const CCoinsView& coins_view,
289-
const CTransaction& tx,
290-
LockPoints* lp = nullptr,
291-
bool useExistingLockPoints = false);
280+
const LockPoints& lock_points);
292281

293282
/**
294283
* Closure representing one script verification

0 commit comments

Comments
 (0)