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) 2024-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 <consensus/validation.h>6#include <policy/ephemeral_policy.h>7#include <policy/feerate.h>8#include <policy/packages.h>9#include <policy/policy.h>10#include <primitives/transaction.h>11#include <txmempool.h>12#include <util/check.h>13#include <util/hasher.h>15#include <algorithm>16#include <cstdint>17#include <map>18#include <memory>19#include <unordered_set>20#include <utility>21#include <vector>23bool PreCheckEphemeralTx(const CTransaction& tx, CFeeRate dust_relay_rate, CAmount base_fee, CAmount mod_fee, TxValidationState& state)24{25// We never want to give incentives to mine this transaction alone26if ((base_fee != 0 || mod_fee != 0) && !GetDust(tx, dust_relay_rate).empty()) {27return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "dust", "tx with dust output must be 0-fee");28}30return true;31}33bool CheckEphemeralSpends(const Package& package, CFeeRate dust_relay_rate, const CTxMemPool& tx_pool, TxValidationState& out_child_state, Wtxid& out_child_wtxid)34{35if (!Assume(std::ranges::all_of(package, [](const auto& tx){return tx != nullptr;}))) {36// Bail out of spend checks if caller gave us an invalid package37return true;38}40std::map<Txid, CTransactionRef> map_txid_ref;41for (const auto& tx : package) {42map_txid_ref[tx->GetHash()] = tx;43}45for (const auto& tx : package) {46std::unordered_set<Txid, SaltedTxidHasher> processed_parent_set;47std::unordered_set<COutPoint, SaltedOutpointHasher> unspent_parent_dust;49for (const auto& tx_input : tx->vin) {50const Txid& parent_txid{tx_input.prevout.hash};51// Skip parents we've already checked dust for52if (processed_parent_set.contains(parent_txid)) continue;54// We look for an in-package or in-mempool dependency55CTransactionRef parent_ref = nullptr;56if (auto it = map_txid_ref.find(parent_txid); it != map_txid_ref.end()) {57parent_ref = it->second;58} else {59parent_ref = tx_pool.get(parent_txid);60}62// Check for dust on parents63if (parent_ref) {64for (uint32_t out_index = 0; out_index < parent_ref->vout.size(); out_index++) {65const auto& tx_output = parent_ref->vout[out_index];66if (IsDust(tx_output, dust_relay_rate)) {67unspent_parent_dust.insert(COutPoint(parent_txid, out_index));68}69}70}72processed_parent_set.insert(parent_txid);73}75if (unspent_parent_dust.empty()) {76continue;77}79// Now that we have gathered parents' dust, make sure it's spent80// by the child81for (const auto& tx_input : tx->vin) {82unspent_parent_dust.erase(tx_input.prevout);83}85if (!unspent_parent_dust.empty()) {86const Txid& out_child_txid = tx->GetHash();87out_child_wtxid = tx->GetWitnessHash();88out_child_state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "missing-ephemeral-spends",89strprintf("tx %s (wtxid=%s) did not spend parent's ephemeral dust", out_child_txid.ToString(), out_child_wtxid.ToString()));90return false;91}92}94return true;95}