mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-26 03:03:22 -03:00
refactor: Add CalculateLockPointsAtTip()
function
This commit is contained in:
parent
78aee0fe2c
commit
3bc434f459
2 changed files with 104 additions and 0 deletions
|
@ -170,6 +170,87 @@ bool CheckFinalTxAtTip(const CBlockIndex& active_chain_tip, const CTransaction&
|
|||
return IsFinalTx(tx, nBlockHeight, nBlockTime);
|
||||
}
|
||||
|
||||
namespace {
|
||||
/**
|
||||
* A helper which calculates heights of inputs of a given transaction.
|
||||
*
|
||||
* @param[in] tip The current chain tip. If an input belongs to a mempool
|
||||
* transaction, we assume it will be confirmed in the next block.
|
||||
* @param[in] coins Any CCoinsView that provides access to the relevant coins.
|
||||
* @param[in] tx The transaction being evaluated.
|
||||
*
|
||||
* @returns A vector of input heights or nullopt, in case of an error.
|
||||
*/
|
||||
std::optional<std::vector<int>> CalculatePrevHeights(
|
||||
const CBlockIndex& tip,
|
||||
const CCoinsView& coins,
|
||||
const CTransaction& tx)
|
||||
{
|
||||
std::vector<int> prev_heights;
|
||||
prev_heights.resize(tx.vin.size());
|
||||
for (size_t i = 0; i < tx.vin.size(); ++i) {
|
||||
const CTxIn& txin = tx.vin[i];
|
||||
Coin coin;
|
||||
if (!coins.GetCoin(txin.prevout, coin)) {
|
||||
LogPrintf("ERROR: %s: Missing input %d in transaction \'%s\'\n", __func__, i, tx.GetHash().GetHex());
|
||||
return std::nullopt;
|
||||
}
|
||||
if (coin.nHeight == MEMPOOL_HEIGHT) {
|
||||
// Assume all mempool transaction confirm in the next block.
|
||||
prev_heights[i] = tip.nHeight + 1;
|
||||
} else {
|
||||
prev_heights[i] = coin.nHeight;
|
||||
}
|
||||
}
|
||||
return prev_heights;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::optional<LockPoints> CalculateLockPointsAtTip(
|
||||
CBlockIndex* tip,
|
||||
const CCoinsView& coins_view,
|
||||
const CTransaction& tx)
|
||||
{
|
||||
assert(tip);
|
||||
|
||||
auto prev_heights{CalculatePrevHeights(*tip, coins_view, tx)};
|
||||
if (!prev_heights.has_value()) return std::nullopt;
|
||||
|
||||
CBlockIndex next_tip;
|
||||
next_tip.pprev = tip;
|
||||
// When SequenceLocks() is called within ConnectBlock(), the height
|
||||
// of the block *being* evaluated is what is used.
|
||||
// Thus if we want to know if a transaction can be part of the
|
||||
// *next* block, we need to use one more than active_chainstate.m_chain.Height()
|
||||
next_tip.nHeight = tip->nHeight + 1;
|
||||
const auto [min_height, min_time] = CalculateSequenceLocks(tx, STANDARD_LOCKTIME_VERIFY_FLAGS, prev_heights.value(), next_tip);
|
||||
|
||||
// Also store the hash of the block with the highest height of
|
||||
// all the blocks which have sequence locked prevouts.
|
||||
// This hash needs to still be on the chain
|
||||
// for these LockPoint calculations to be valid
|
||||
// Note: It is impossible to correctly calculate a maxInputBlock
|
||||
// if any of the sequence locked inputs depend on unconfirmed txs,
|
||||
// except in the special case where the relative lock time/height
|
||||
// is 0, which is equivalent to no sequence lock. Since we assume
|
||||
// input height of tip+1 for mempool txs and test the resulting
|
||||
// min_height and min_time from CalculateSequenceLocks against tip+1.
|
||||
int max_input_height{0};
|
||||
for (const int height : prev_heights.value()) {
|
||||
// Can ignore mempool inputs since we'll fail if they had non-zero locks
|
||||
if (height != next_tip.nHeight) {
|
||||
max_input_height = std::max(max_input_height, height);
|
||||
}
|
||||
}
|
||||
|
||||
// tip->GetAncestor(max_input_height) should never return a nullptr
|
||||
// because max_input_height is always less than the tip height.
|
||||
// It would, however, be a bad bug to continue execution, since a
|
||||
// LockPoints object with the maxInputBlock member set to nullptr
|
||||
// signifies no relative lock time.
|
||||
return LockPoints{min_height, min_time, Assert(tip->GetAncestor(max_input_height))};
|
||||
}
|
||||
|
||||
bool CheckSequenceLocksAtTip(CBlockIndex* tip,
|
||||
const CCoinsView& coins_view,
|
||||
const CTransaction& tx,
|
||||
|
|
|
@ -244,6 +244,29 @@ PackageMempoolAcceptResult ProcessNewPackage(Chainstate& active_chainstate, CTxM
|
|||
*/
|
||||
bool CheckFinalTxAtTip(const CBlockIndex& active_chain_tip, const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||
|
||||
/**
|
||||
* Calculate LockPoints required to check if transaction will be BIP68 final in the next block
|
||||
* to be created on top of tip.
|
||||
*
|
||||
* @param[in] tip Chain tip for which tx sequence locks are calculated. For
|
||||
* example, the tip of the current active chain.
|
||||
* @param[in] coins_view Any CCoinsView that provides access to the relevant coins for
|
||||
* checking sequence locks. For example, it can be a CCoinsViewCache
|
||||
* that isn't connected to anything but contains all the relevant
|
||||
* coins, or a CCoinsViewMemPool that is connected to the
|
||||
* mempool and chainstate UTXO set. In the latter case, the caller
|
||||
* is responsible for holding the appropriate locks to ensure that
|
||||
* calls to GetCoin() return correct coins.
|
||||
* @param[in] tx The transaction being evaluated.
|
||||
*
|
||||
* @returns The resulting height and time calculated and the hash of the block needed for
|
||||
* calculation, or std::nullopt if there is an error.
|
||||
*/
|
||||
std::optional<LockPoints> CalculateLockPointsAtTip(
|
||||
CBlockIndex* tip,
|
||||
const CCoinsView& coins_view,
|
||||
const CTransaction& tx);
|
||||
|
||||
/**
|
||||
* Check if transaction will be BIP68 final in the next block to be created on top of tip.
|
||||
* @param[in] tip Chain tip to check tx sequence locks against. For example,
|
||||
|
|
Loading…
Add table
Reference in a new issue