@@ -157,11 +157,89 @@ bool CheckFinalTxAtTip(const CBlockIndex& active_chain_tip, const CTransaction&
157
157
return IsFinalTx (tx, nBlockHeight, nBlockTime);
158
158
}
159
159
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
+
160
241
bool CheckSequenceLocksAtTip (CBlockIndex* tip,
161
- const CCoinsView& coins_view,
162
- const CTransaction& tx,
163
- LockPoints* lp,
164
- bool useExistingLockPoints)
242
+ const LockPoints& lock_points)
165
243
{
166
244
assert (tip != nullptr );
167
245
@@ -175,61 +253,7 @@ bool CheckSequenceLocksAtTip(CBlockIndex* tip,
175
253
// *next* block, we need to use one more than active_chainstate.m_chain.Height()
176
254
index.nHeight = tip->nHeight + 1 ;
177
255
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 });
233
257
}
234
258
235
259
// Returns the script flags which should be checked for a given block
@@ -315,20 +339,23 @@ void Chainstate::MaybeUpdateMempoolForReorg(
315
339
316
340
// The transaction must be final.
317
341
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 ();
321
344
// 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
+ }
332
359
}
333
360
334
361
// If the transaction spends any coinbase outputs, it must be mature.
@@ -732,7 +759,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
732
759
}
733
760
}
734
761
735
- LockPoints lp;
736
762
m_view.SetBackend (m_viewmempool);
737
763
738
764
const CCoinsViewCache& coins_cache = m_active_chainstate.CoinsTip ();
@@ -774,7 +800,8 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
774
800
// be mined yet.
775
801
// Pass in m_view which has all of the relevant inputs cached. Note that, since m_view's
776
802
// 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)) {
778
805
return state.Invalid (TxValidationResult::TX_PREMATURE_SPEND, " non-BIP68-final" );
779
806
}
780
807
@@ -810,7 +837,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
810
837
}
811
838
812
839
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 () ));
814
841
ws.m_vsize = entry->GetTxSize ();
815
842
816
843
if (nSigOpsCost > MAX_STANDARD_TX_SIGOPS_COST)
0 commit comments