@@ -157,11 +157,89 @@ bool CheckFinalTxAtTip(const CBlockIndex& active_chain_tip, const CTransaction&
157157 return IsFinalTx (tx, nBlockHeight, nBlockTime);
158158}
159159
160+ namespace {
161+ /* *
162+ * A helper which calculates heights of inputs of a given transaction.
163+ *
164+ * @param[in] tip The current chain tip. If an input belongs to a mempool
165+ * transaction, we assume it will be confirmed in the next block.
166+ * @param[in] coins Any CCoinsView that provides access to the relevant coins.
167+ * @param[in] tx The transaction being evaluated.
168+ *
169+ * @returns A vector of input heights or nullopt, in case of an error.
170+ */
171+ std::optional<std::vector<int >> CalculatePrevHeights (
172+ const CBlockIndex& tip,
173+ const CCoinsView& coins,
174+ const CTransaction& tx)
175+ {
176+ std::vector<int > prev_heights;
177+ prev_heights.resize (tx.vin .size ());
178+ for (size_t i = 0 ; i < tx.vin .size (); ++i) {
179+ const CTxIn& txin = tx.vin [i];
180+ Coin coin;
181+ if (!coins.GetCoin (txin.prevout , coin)) {
182+ LogPrintf (" ERROR: %s: Missing input %d in transaction \' %s\'\n " , __func__, i, tx.GetHash ().GetHex ());
183+ return std::nullopt ;
184+ }
185+ if (coin.nHeight == MEMPOOL_HEIGHT) {
186+ // Assume all mempool transaction confirm in the next block.
187+ prev_heights[i] = tip.nHeight + 1 ;
188+ } else {
189+ prev_heights[i] = coin.nHeight ;
190+ }
191+ }
192+ return prev_heights;
193+ }
194+ } // namespace
195+
196+ std::optional<LockPoints> CalculateLockPointsAtTip (
197+ CBlockIndex* tip,
198+ const CCoinsView& coins_view,
199+ const CTransaction& tx)
200+ {
201+ assert (tip);
202+
203+ auto prev_heights{CalculatePrevHeights (*tip, coins_view, tx)};
204+ if (!prev_heights.has_value ()) return std::nullopt ;
205+
206+ CBlockIndex next_tip;
207+ next_tip.pprev = tip;
208+ // When SequenceLocks() is called within ConnectBlock(), the height
209+ // of the block *being* evaluated is what is used.
210+ // Thus if we want to know if a transaction can be part of the
211+ // *next* block, we need to use one more than active_chainstate.m_chain.Height()
212+ next_tip.nHeight = tip->nHeight + 1 ;
213+ const auto [min_height, min_time] = CalculateSequenceLocks (tx, STANDARD_LOCKTIME_VERIFY_FLAGS, prev_heights.value (), next_tip);
214+
215+ // Also store the hash of the block with the highest height of
216+ // all the blocks which have sequence locked prevouts.
217+ // This hash needs to still be on the chain
218+ // for these LockPoint calculations to be valid
219+ // Note: It is impossible to correctly calculate a maxInputBlock
220+ // if any of the sequence locked inputs depend on unconfirmed txs,
221+ // except in the special case where the relative lock time/height
222+ // is 0, which is equivalent to no sequence lock. Since we assume
223+ // input height of tip+1 for mempool txs and test the resulting
224+ // min_height and min_time from CalculateSequenceLocks against tip+1.
225+ int max_input_height{0 };
226+ for (const int height : prev_heights.value ()) {
227+ // Can ignore mempool inputs since we'll fail if they had non-zero locks
228+ if (height != next_tip.nHeight ) {
229+ max_input_height = std::max (max_input_height, height);
230+ }
231+ }
232+
233+ // tip->GetAncestor(max_input_height) should never return a nullptr
234+ // because max_input_height is always less than the tip height.
235+ // It would, however, be a bad bug to continue execution, since a
236+ // LockPoints object with the maxInputBlock member set to nullptr
237+ // signifies no relative lock time.
238+ return LockPoints{min_height, min_time, Assert (tip->GetAncestor (max_input_height))};
239+ }
240+
160241bool CheckSequenceLocksAtTip (CBlockIndex* tip,
161- const CCoinsView& coins_view,
162- const CTransaction& tx,
163- LockPoints* lp,
164- bool useExistingLockPoints)
242+ const LockPoints& lock_points)
165243{
166244 assert (tip != nullptr );
167245
@@ -175,61 +253,7 @@ bool CheckSequenceLocksAtTip(CBlockIndex* tip,
175253 // *next* block, we need to use one more than active_chainstate.m_chain.Height()
176254 index.nHeight = tip->nHeight + 1 ;
177255
178- std::pair<int , int64_t > lockPair;
179- if (useExistingLockPoints) {
180- assert (lp);
181- lockPair.first = lp->height ;
182- lockPair.second = lp->time ;
183- }
184- else {
185- std::vector<int > prevheights;
186- prevheights.resize (tx.vin .size ());
187- for (size_t txinIndex = 0 ; txinIndex < tx.vin .size (); txinIndex++) {
188- const CTxIn& txin = tx.vin [txinIndex];
189- Coin coin;
190- if (!coins_view.GetCoin (txin.prevout , coin)) {
191- return error (" %s: Missing input" , __func__);
192- }
193- if (coin.nHeight == MEMPOOL_HEIGHT) {
194- // Assume all mempool transaction confirm in the next block
195- prevheights[txinIndex] = tip->nHeight + 1 ;
196- } else {
197- prevheights[txinIndex] = coin.nHeight ;
198- }
199- }
200- lockPair = CalculateSequenceLocks (tx, STANDARD_LOCKTIME_VERIFY_FLAGS, prevheights, index);
201- if (lp) {
202- lp->height = lockPair.first ;
203- lp->time = lockPair.second ;
204- // Also store the hash of the block with the highest height of
205- // all the blocks which have sequence locked prevouts.
206- // This hash needs to still be on the chain
207- // for these LockPoint calculations to be valid
208- // Note: It is impossible to correctly calculate a maxInputBlock
209- // if any of the sequence locked inputs depend on unconfirmed txs,
210- // except in the special case where the relative lock time/height
211- // is 0, which is equivalent to no sequence lock. Since we assume
212- // input height of tip+1 for mempool txs and test the resulting
213- // lockPair from CalculateSequenceLocks against tip+1. We know
214- // EvaluateSequenceLocks will fail if there was a non-zero sequence
215- // lock on a mempool input, so we can use the return value of
216- // CheckSequenceLocksAtTip to indicate the LockPoints validity
217- int maxInputHeight = 0 ;
218- for (const int height : prevheights) {
219- // Can ignore mempool inputs since we'll fail if they had non-zero locks
220- if (height != tip->nHeight +1 ) {
221- maxInputHeight = std::max (maxInputHeight, height);
222- }
223- }
224- // tip->GetAncestor(maxInputHeight) should never return a nullptr
225- // because maxInputHeight is always less than the tip height.
226- // It would, however, be a bad bug to continue execution, since a
227- // LockPoints object with the maxInputBlock member set to nullptr
228- // signifies no relative lock time.
229- lp->maxInputBlock = Assert (tip->GetAncestor (maxInputHeight));
230- }
231- }
232- return EvaluateSequenceLocks (index, lockPair);
256+ return EvaluateSequenceLocks (index, {lock_points.height , lock_points.time });
233257}
234258
235259// Returns the script flags which should be checked for a given block
@@ -315,20 +339,23 @@ void Chainstate::MaybeUpdateMempoolForReorg(
315339
316340 // The transaction must be final.
317341 if (!CheckFinalTxAtTip (*Assert (m_chain.Tip ()), tx)) return true ;
318- LockPoints lp = it->GetLockPoints ();
319- const bool validLP{TestLockPointValidity (m_chain, lp)};
320- CCoinsViewMemPool view_mempool (&CoinsTip (), *m_mempool);
342+
343+ const LockPoints& lp = it->GetLockPoints ();
321344 // CheckSequenceLocksAtTip checks if the transaction will be final in the next block to be
322- // created on top of the new chain. We use useExistingLockPoints=false so that, instead of
323- // using the information in lp (which might now refer to a block that no longer exists in
324- // the chain), it will update lp to contain LockPoints relevant to the new chain.
325- if (!CheckSequenceLocksAtTip (m_chain.Tip (), view_mempool, tx, &lp, validLP)) {
326- // If CheckSequenceLocksAtTip fails, remove the tx and don't depend on the LockPoints.
327- return true ;
328- } else if (!validLP) {
329- // If CheckSequenceLocksAtTip succeeded, it also updated the LockPoints.
330- // Now update the mempool entry lockpoints as well.
331- m_mempool->mapTx .modify (it, [&lp](CTxMemPoolEntry& e) { e.UpdateLockPoints (lp); });
345+ // created on top of the new chain.
346+ if (TestLockPointValidity (m_chain, lp)) {
347+ if (!CheckSequenceLocksAtTip (m_chain.Tip (), lp)) {
348+ return true ;
349+ }
350+ } else {
351+ const CCoinsViewMemPool view_mempool{&CoinsTip (), *m_mempool};
352+ const std::optional<LockPoints> new_lock_points{CalculateLockPointsAtTip (m_chain.Tip (), view_mempool, tx)};
353+ if (new_lock_points.has_value () && CheckSequenceLocksAtTip (m_chain.Tip (), *new_lock_points)) {
354+ // Now update the mempool entry lockpoints as well.
355+ m_mempool->mapTx .modify (it, [&new_lock_points](CTxMemPoolEntry& e) { e.UpdateLockPoints (*new_lock_points); });
356+ } else {
357+ return true ;
358+ }
332359 }
333360
334361 // If the transaction spends any coinbase outputs, it must be mature.
@@ -732,7 +759,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
732759 }
733760 }
734761
735- LockPoints lp;
736762 m_view.SetBackend (m_viewmempool);
737763
738764 const CCoinsViewCache& coins_cache = m_active_chainstate.CoinsTip ();
@@ -774,7 +800,8 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
774800 // be mined yet.
775801 // Pass in m_view which has all of the relevant inputs cached. Note that, since m_view's
776802 // backend was removed, it no longer pulls coins from the mempool.
777- if (!CheckSequenceLocksAtTip (m_active_chainstate.m_chain .Tip (), m_view, tx, &lp)) {
803+ const std::optional<LockPoints> lock_points{CalculateLockPointsAtTip (m_active_chainstate.m_chain .Tip (), m_view, tx)};
804+ if (!lock_points.has_value () || !CheckSequenceLocksAtTip (m_active_chainstate.m_chain .Tip (), *lock_points)) {
778805 return state.Invalid (TxValidationResult::TX_PREMATURE_SPEND, " non-BIP68-final" );
779806 }
780807
@@ -810,7 +837,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
810837 }
811838
812839 entry.reset (new CTxMemPoolEntry (ptx, ws.m_base_fees , nAcceptTime, m_active_chainstate.m_chain .Height (),
813- fSpendsCoinbase , nSigOpsCost, lp ));
840+ fSpendsCoinbase , nSigOpsCost, lock_points. value () ));
814841 ws.m_vsize = entry->GetTxSize ();
815842
816843 if (nSigOpsCost > MAX_STANDARD_TX_SIGOPS_COST)
0 commit comments