Inside Bitcoin
Opening the selected file…
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2022-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <policy/truc_policy.h>7#include <coins.h>8#include <consensus/amount.h>9#include <tinyformat.h>10#include <util/check.h>12#include <algorithm>13#include <numeric>14#include <vector>16/** Helper for PackageTRUCChecks: Returns a vector containing the indices of transactions (within17* package) that are direct parents of ptx. */18std::vector<size_t> FindInPackageParents(const Package& package, const CTransactionRef& ptx)19{20std::vector<size_t> in_package_parents;22std::set<Txid> possible_parents;23for (auto &input : ptx->vin) {24possible_parents.insert(input.prevout.hash);25}27for (size_t i{0}; i < package.size(); ++i) {28const auto& tx = package.at(i);29// We assume the package is sorted, so that we don't need to continue30// looking past the transaction itself.31if (&(*tx) == &(*ptx)) break;32if (possible_parents.contains(tx->GetHash())) {33in_package_parents.push_back(i);34}35}36return in_package_parents;37}39/** Helper for PackageTRUCChecks, storing info for a mempool or package parent. */40struct ParentInfo {41/** Txid used to identify this parent by prevout */42const Txid& m_txid;43/** Wtxid used for debug string */44const Wtxid& m_wtxid;45/** version used to check inheritance of TRUC and non-TRUC */46decltype(CTransaction::version) m_version;47/** If parent is in mempool, whether it has any descendants in mempool. */48bool m_has_mempool_descendant;50ParentInfo() = delete;51ParentInfo(const Txid& txid, const Wtxid& wtxid, decltype(CTransaction::version) version, bool has_mempool_descendant) :52m_txid{txid}, m_wtxid{wtxid}, m_version{version},53m_has_mempool_descendant{has_mempool_descendant}54{}55};57std::optional<std::string> PackageTRUCChecks(const CTxMemPool& pool, const CTransactionRef& ptx, int64_t vsize,58const Package& package,59const std::vector<CTxMemPoolEntry::CTxMemPoolEntryRef>& mempool_parents)60{61AssertLockHeld(pool.cs);62// This function is specialized for these limits, and must be reimplemented if they ever change.63static_assert(TRUC_ANCESTOR_LIMIT == 2);64static_assert(TRUC_DESCENDANT_LIMIT == 2);66const auto in_package_parents{FindInPackageParents(package, ptx)};68// Now we have all parents, so we can start checking TRUC rules.69if (ptx->version == TRUC_VERSION) {70// SingleTRUCChecks should have checked this already.71if (!Assume(vsize <= TRUC_MAX_VSIZE)) {72return strprintf("version=3 tx %s (wtxid=%s) is too big: %u > %u virtual bytes",73ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(), vsize, TRUC_MAX_VSIZE);74}76if (mempool_parents.size() + in_package_parents.size() + 1 > TRUC_ANCESTOR_LIMIT) {77return strprintf("tx %s (wtxid=%s) would have too many ancestors",78ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString());79}81if (mempool_parents.size()) {82if (pool.GetAncestorCount(mempool_parents[0]) + in_package_parents.size() + 1 > TRUC_ANCESTOR_LIMIT) {83return strprintf("tx %s (wtxid=%s) would have too many ancestors",84ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString());85}86}88const bool has_parent{mempool_parents.size() + in_package_parents.size() > 0};89if (has_parent) {90// A TRUC child cannot be too large.91if (vsize > TRUC_CHILD_MAX_VSIZE) {92return strprintf("version=3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes",93ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(),94vsize, TRUC_CHILD_MAX_VSIZE);95}97// Exactly 1 parent exists, either in mempool or package. Find it.98const auto parent_info = [&] {99if (mempool_parents.size() > 0) {100const auto& mempool_parent = &mempool_parents[0].get();101return ParentInfo{mempool_parent->GetTx().GetHash(),102mempool_parent->GetTx().GetWitnessHash(),103mempool_parent->GetTx().version,104/*has_mempool_descendant=*/pool.GetDescendantCount(*mempool_parent) > 1};105} else {106auto& parent_index = in_package_parents.front();107auto& package_parent = package.at(parent_index);108return ParentInfo{package_parent->GetHash(),109package_parent->GetWitnessHash(),110package_parent->version,111/*has_mempool_descendant=*/false};112}113}();115// If there is a parent, it must have the right version.116if (parent_info.m_version != TRUC_VERSION) {117return strprintf("version=3 tx %s (wtxid=%s) cannot spend from non-version=3 tx %s (wtxid=%s)",118ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(),119parent_info.m_txid.ToString(), parent_info.m_wtxid.ToString());120}122for (const auto& package_tx : package) {123// Skip same tx.124if (&(*package_tx) == &(*ptx)) continue;126for (auto& input : package_tx->vin) {127// Fail if we find another tx with the same parent. We don't check whether the128// sibling is to-be-replaced (done in SingleTRUCChecks) because these transactions129// are within the same package.130if (input.prevout.hash == parent_info.m_txid) {131return strprintf("tx %s (wtxid=%s) would exceed descendant count limit",132parent_info.m_txid.ToString(),133parent_info.m_wtxid.ToString());134}136// This tx can't have both a parent and an in-package child.137if (input.prevout.hash == ptx->GetHash()) {138return strprintf("tx %s (wtxid=%s) would have too many ancestors",139package_tx->GetHash().ToString(), package_tx->GetWitnessHash().ToString());140}141}142}144if (parent_info.m_has_mempool_descendant) {145return strprintf("tx %s (wtxid=%s) would exceed descendant count limit",146parent_info.m_txid.ToString(), parent_info.m_wtxid.ToString());147}148}149} else {150// Non-TRUC transactions cannot have TRUC parents.151for (auto it : mempool_parents) {152if (it.get().GetTx().version == TRUC_VERSION) {153return strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)",154ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(),155it.get().GetSharedTx()->GetHash().ToString(), it.get().GetSharedTx()->GetWitnessHash().ToString());156}157}158for (const auto& index: in_package_parents) {159if (package.at(index)->version == TRUC_VERSION) {160return strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)",161ptx->GetHash().ToString(),162ptx->GetWitnessHash().ToString(),163package.at(index)->GetHash().ToString(),164package.at(index)->GetWitnessHash().ToString());165}166}167}168return std::nullopt;169}171std::optional<std::pair<std::string, CTransactionRef>> SingleTRUCChecks(const CTxMemPool& pool, const CTransactionRef& ptx,172const std::vector<CTxMemPoolEntry::CTxMemPoolEntryRef>& mempool_parents,173const std::set<Txid>& direct_conflicts,174int64_t vsize)175{176AssertLockHeld(pool.cs);177// Check TRUC and non-TRUC inheritance.178for (const auto& entry_ref : mempool_parents) {179const auto& entry = &entry_ref.get();180if (ptx->version != TRUC_VERSION && entry->GetTx().version == TRUC_VERSION) {181return std::make_pair(strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)",182ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(),183entry->GetSharedTx()->GetHash().ToString(), entry->GetSharedTx()->GetWitnessHash().ToString()),184nullptr);185} else if (ptx->version == TRUC_VERSION && entry->GetTx().version != TRUC_VERSION) {186return std::make_pair(strprintf("version=3 tx %s (wtxid=%s) cannot spend from non-version=3 tx %s (wtxid=%s)",187ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(),188entry->GetSharedTx()->GetHash().ToString(), entry->GetSharedTx()->GetWitnessHash().ToString()),189nullptr);190}191}193// This function is specialized for these limits, and must be reimplemented if they ever change.194static_assert(TRUC_ANCESTOR_LIMIT == 2);195static_assert(TRUC_DESCENDANT_LIMIT == 2);197// The rest of the rules only apply to transactions with version=3.198if (ptx->version != TRUC_VERSION) return std::nullopt;200if (vsize > TRUC_MAX_VSIZE) {201return std::make_pair(strprintf("version=3 tx %s (wtxid=%s) is too big: %u > %u virtual bytes",202ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(), vsize, TRUC_MAX_VSIZE),203nullptr);204}206// Check that TRUC_ANCESTOR_LIMIT would not be violated.207if (mempool_parents.size() + 1 > TRUC_ANCESTOR_LIMIT) {208return std::make_pair(strprintf("tx %s (wtxid=%s) would have too many ancestors",209ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString()),210nullptr);211}213// Remaining checks only pertain to transactions with unconfirmed ancestors.214if (mempool_parents.size() > 0) {215// Ensure that the in-mempool parent doesn't have any additional216// ancestors, as that would also be a violation.217if (pool.GetAncestorCount(mempool_parents[0]) + 1 > TRUC_ANCESTOR_LIMIT) {218return std::make_pair(strprintf("tx %s (wtxid=%s) would have too many ancestors",219ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString()),220nullptr);221}222// If this transaction spends TRUC parents, it cannot be too large.223if (vsize > TRUC_CHILD_MAX_VSIZE) {224return std::make_pair(strprintf("version=3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes",225ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(), vsize, TRUC_CHILD_MAX_VSIZE),226nullptr);227}229// Check the descendant counts of in-mempool parents.230const auto& parent_entry = mempool_parents[0].get();231// If there are any parents, this is the only child allowed. The parent cannot have any232// other descendants. We handle the possibility of multiple children as that case is233// possible through a reorg.234CTxMemPool::setEntries descendants;235auto parent_it = pool.CalculateDescendants(parent_entry, descendants);236descendants.erase(parent_it);237// Don't double-count a transaction that is going to be replaced. This logic assumes that238// any descendant of the TRUC transaction is a direct child, which makes sense because a239// TRUC transaction can only have 1 descendant.240const bool child_will_be_replaced = !descendants.empty() &&241std::any_of(descendants.cbegin(), descendants.cend(),242[&direct_conflicts](const CTxMemPool::txiter& child){return direct_conflicts.contains(child->GetTx().GetHash());});243if (pool.GetDescendantCount(parent_entry) + 1 > TRUC_DESCENDANT_LIMIT && !child_will_be_replaced) {244// Allow sibling eviction for TRUC transaction: if another child already exists, even if245// we don't conflict inputs with it, consider evicting it under RBF rules. We rely on TRUC rules246// only permitting 1 descendant, as otherwise we would need to have logic for deciding247// which descendant to evict. Skip if this isn't true, e.g. if the transaction has248// multiple children or the sibling also has descendants due to a reorg.249const bool consider_sibling_eviction{pool.GetDescendantCount(parent_entry) == 2 &&250pool.GetAncestorCount(**descendants.begin()) == 2};