@@ -170,6 +170,87 @@ bool CheckFinalTxAtTip(const CBlockIndex& active_chain_tip, const CTransaction&
170
170
return IsFinalTx (tx, nBlockHeight, nBlockTime);
171
171
}
172
172
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
+
173
254
bool CheckSequenceLocksAtTip (CBlockIndex* tip,
174
255
const CCoinsView& coins_view,
175
256
const CTransaction& tx,
0 commit comments