@@ -170,6 +170,87 @@ bool CheckFinalTxAtTip(const CBlockIndex& active_chain_tip, const CTransaction&
170170 return IsFinalTx (tx, nBlockHeight, nBlockTime);
171171}
172172
173+ namespace {
174+ /* *
175+ * A helper which calculates heights of inputs of a given transaction.
176+ *
177+ * @param[in] tip The current chain tip. If an input belongs to a mempool
178+ * transaction, we assume it will be confirmed in the next block.
179+ * @param[in] coins Any CCoinsView that provides access to the relevant coins.
180+ * @param[in] tx The transaction being evaluated.
181+ *
182+ * @returns A vector of input heights or nullopt, in case of an error.
183+ */
184+ std::optional<std::vector<int >> CalculatePrevHeights (
185+ const CBlockIndex& tip,
186+ const CCoinsView& coins,
187+ const CTransaction& tx)
188+ {
189+ std::vector<int > prev_heights;
190+ prev_heights.resize (tx.vin .size ());
191+ for (size_t i = 0 ; i < tx.vin .size (); ++i) {
192+ const CTxIn& txin = tx.vin [i];
193+ Coin coin;
194+ if (!coins.GetCoin (txin.prevout , coin)) {
195+ LogPrintf (" ERROR: %s: Missing input %d in transaction \' %s\'\n " , __func__, i, tx.GetHash ().GetHex ());
196+ return std::nullopt ;
197+ }
198+ if (coin.nHeight == MEMPOOL_HEIGHT) {
199+ // Assume all mempool transaction confirm in the next block.
200+ prev_heights[i] = tip.nHeight + 1 ;
201+ } else {
202+ prev_heights[i] = coin.nHeight ;
203+ }
204+ }
205+ return prev_heights;
206+ }
207+ } // namespace
208+
209+ std::optional<LockPoints> CalculateLockPointsAtTip (
210+ CBlockIndex* tip,
211+ const CCoinsView& coins_view,
212+ const CTransaction& tx)
213+ {
214+ assert (tip);
215+
216+ auto prev_heights{CalculatePrevHeights (*tip, coins_view, tx)};
217+ if (!prev_heights.has_value ()) return std::nullopt ;
218+
219+ CBlockIndex next_tip;
220+ next_tip.pprev = tip;
221+ // When SequenceLocks() is called within ConnectBlock(), the height
222+ // of the block *being* evaluated is what is used.
223+ // Thus if we want to know if a transaction can be part of the
224+ // *next* block, we need to use one more than active_chainstate.m_chain.Height()
225+ next_tip.nHeight = tip->nHeight + 1 ;
226+ const auto [min_height, min_time] = CalculateSequenceLocks (tx, STANDARD_LOCKTIME_VERIFY_FLAGS, prev_heights.value (), next_tip);
227+
228+ // Also store the hash of the block with the highest height of
229+ // all the blocks which have sequence locked prevouts.
230+ // This hash needs to still be on the chain
231+ // for these LockPoint calculations to be valid
232+ // Note: It is impossible to correctly calculate a maxInputBlock
233+ // if any of the sequence locked inputs depend on unconfirmed txs,
234+ // except in the special case where the relative lock time/height
235+ // is 0, which is equivalent to no sequence lock. Since we assume
236+ // input height of tip+1 for mempool txs and test the resulting
237+ // min_height and min_time from CalculateSequenceLocks against tip+1.
238+ int max_input_height{0 };
239+ for (const int height : prev_heights.value ()) {
240+ // Can ignore mempool inputs since we'll fail if they had non-zero locks
241+ if (height != next_tip.nHeight ) {
242+ max_input_height = std::max (max_input_height, height);
243+ }
244+ }
245+
246+ // tip->GetAncestor(max_input_height) should never return a nullptr
247+ // because max_input_height is always less than the tip height.
248+ // It would, however, be a bad bug to continue execution, since a
249+ // LockPoints object with the maxInputBlock member set to nullptr
250+ // signifies no relative lock time.
251+ return LockPoints{min_height, min_time, Assert (tip->GetAncestor (max_input_height))};
252+ }
253+
173254bool CheckSequenceLocksAtTip (CBlockIndex* tip,
174255 const CCoinsView& coins_view,
175256 const CTransaction& tx,
0 commit comments