Inside Bitcoin
Opening the selected file…
Inside Bitcoin
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) 2011-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 <arith_uint256.h>6#include <consensus/validation.h>7#include <node/txorphanage.h>8#include <policy/policy.h>9#include <primitives/transaction.h>10#include <pubkey.h>11#include <script/sign.h>12#include <script/signingprovider.h>13#include <test/util/common.h>14#include <test/util/random.h>15#include <test/util/setup_common.h>16#include <test/util/time.h>17#include <test/util/transaction_utils.h>19#include <array>20#include <cstdint>22#include <boost/test/unit_test.hpp>24BOOST_FIXTURE_TEST_SUITE(orphanage_tests, BasicTestingSetup)26static void MakeNewKeyWithFastRandomContext(CKey& key, FastRandomContext& rand_ctx)27{28std::vector<unsigned char> keydata;29keydata = rand_ctx.randbytes(32);30key.Set(keydata.data(), keydata.data() + keydata.size(), /*fCompressedIn=*/true);31assert(key.IsValid());32}34// Creates a transaction with 2 outputs. Spends all outpoints. If outpoints is empty, spends a random one.35static CTransactionRef MakeTransactionSpending(const std::vector<COutPoint>& outpoints, FastRandomContext& det_rand)36{37CKey key;38MakeNewKeyWithFastRandomContext(key, det_rand);39CMutableTransaction tx;40// If no outpoints are given, create a random one.41if (outpoints.empty()) {42tx.vin.emplace_back(Txid::FromUint256(det_rand.rand256()), 0);43} else {44for (const auto& outpoint : outpoints) {45tx.vin.emplace_back(outpoint);46}47}48// Ensure txid != wtxid49tx.vin[0].scriptWitness.stack.push_back({1});50tx.vout.resize(2);51tx.vout[0].nValue = CENT;52tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));53tx.vout[1].nValue = 3 * CENT;54tx.vout[1].scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(key.GetPubKey()));55return MakeTransactionRef(tx);56}58// Make another (not necessarily valid) tx with the same txid but different wtxid.59static CTransactionRef MakeMutation(const CTransactionRef& ptx)60{61CMutableTransaction tx(*ptx);62tx.vin[0].scriptWitness.stack.push_back({5});63auto mutated_tx = MakeTransactionRef(tx);64assert(ptx->GetHash() == mutated_tx->GetHash());65return mutated_tx;66}68static bool EqualTxns(const std::set<CTransactionRef>& set_txns, const std::vector<CTransactionRef>& vec_txns)69{70if (vec_txns.size() != set_txns.size()) return false;71for (const auto& tx : vec_txns) {72if (!set_txns.contains(tx)) return false;73}74return true;75}77BOOST_AUTO_TEST_CASE(peer_dos_limits)78{79FastRandomContext det_rand{true};81// Construct transactions to use. They must all be the same size.82static constexpr unsigned int NUM_TXNS_CREATED = 100;83static constexpr int64_t TX_SIZE{469};84static constexpr int64_t TOTAL_SIZE = NUM_TXNS_CREATED * TX_SIZE;86std::vector<CTransactionRef> txns;87txns.reserve(NUM_TXNS_CREATED);88// All transactions are the same size.89for (unsigned int i{0}; i < NUM_TXNS_CREATED; ++i) {90auto ptx = MakeTransactionSpending({}, det_rand);91txns.emplace_back(ptx);92BOOST_CHECK_EQUAL(TX_SIZE, GetTransactionWeight(*ptx));93}95// Single peer: eviction is triggered if either limit is hit96{97// Test announcement limits98NodeId peer{8};99auto orphanage_low_ann = node::MakeTxOrphanage(/*max_global_latency_score=*/1, /*reserved_peer_usage=*/TX_SIZE * 10);100auto orphanage_low_mem = node::MakeTxOrphanage(/*max_global_latency_score=*/10, /*reserved_peer_usage=*/TX_SIZE);102// Add the first transaction103orphanage_low_ann->AddTx(txns.at(0), peer);104orphanage_low_mem->AddTx(txns.at(0), peer);106// Add more. One of the limits is exceeded, so LimitOrphans evicts 1.107orphanage_low_ann->AddTx(txns.at(1), peer);108orphanage_low_mem->AddTx(txns.at(1), peer);110// The older transaction is evicted.111BOOST_CHECK(!orphanage_low_ann->HaveTx(txns.at(0)->GetWitnessHash()));112BOOST_CHECK(!orphanage_low_mem->HaveTx(txns.at(0)->GetWitnessHash()));113BOOST_CHECK(orphanage_low_ann->HaveTx(txns.at(1)->GetWitnessHash()));114BOOST_CHECK(orphanage_low_mem->HaveTx(txns.at(1)->GetWitnessHash()));116orphanage_low_ann->SanityCheck();117orphanage_low_mem->SanityCheck();118}120// Single peer: latency score includes inputs121{122// Test latency score limits123NodeId peer{10};124auto orphanage_low_ann = node::MakeTxOrphanage(/*max_global_latency_score=*/5, /*reserved_peer_usage=*/TX_SIZE * 1000);126// Add the first transaction127orphanage_low_ann->AddTx(txns.at(0), peer);129// Add 1 more transaction with 45 inputs. Even though there are only 2 announcements, this pushes the orphanage above its maximum latency score.130std::vector<COutPoint> outpoints_45;131for (unsigned int j{0}; j < 45; ++j) {132outpoints_45.emplace_back(Txid::FromUint256(det_rand.rand256()), j);133}134auto ptx = MakeTransactionSpending(outpoints_45, det_rand);135orphanage_low_ann->AddTx(ptx, peer);137// The older transaction is evicted.138BOOST_CHECK(!orphanage_low_ann->HaveTx(txns.at(0)->GetWitnessHash()));139BOOST_CHECK(orphanage_low_ann->HaveTx(ptx->GetWitnessHash()));141orphanage_low_ann->SanityCheck();142}144// Single peer: eviction order is FIFO on non-reconsiderable, then reconsiderable orphans.145{146// Construct parent + child pairs147std::vector<CTransactionRef> parents;148std::vector<CTransactionRef> children;149for (unsigned int i{0}; i < 10; ++i) {150CTransactionRef parent = MakeTransactionSpending({}, det_rand);151CTransactionRef child = MakeTransactionSpending({{parent->GetHash(), 0}}, det_rand);152parents.emplace_back(parent);153children.emplace_back(child);154}156// Test announcement limits157NodeId peer{9};158auto orphanage = node::MakeTxOrphanage(/*max_global_latency_score=*/3, /*reserved_peer_usage=*/TX_SIZE * 10);160// First add a tx which will be made reconsiderable.161orphanage->AddTx(children.at(0), peer);163// Then add 2 more orphans... not oversize yet.164orphanage->AddTx(children.at(1), peer);165orphanage->AddTx(children.at(2), peer);167// Make child0 ready to reconsider168const std::vector<std::pair<Wtxid, NodeId>> expected_set_c0{std::make_pair(children.at(0)->GetWitnessHash(), peer)};169BOOST_CHECK(orphanage->AddChildrenToWorkSet(*parents.at(0), det_rand) == expected_set_c0);170BOOST_CHECK(orphanage->HaveTxToReconsider(peer));172// Add 1 more orphan, causing the orphanage to be oversize. child1 is evicted.173orphanage->AddTx(children.at(3), peer);174BOOST_CHECK(orphanage->HaveTx(children.at(0)->GetWitnessHash()));175BOOST_CHECK(!orphanage->HaveTx(children.at(1)->GetWitnessHash()));176BOOST_CHECK(orphanage->HaveTx(children.at(2)->GetWitnessHash()));177BOOST_CHECK(orphanage->HaveTx(children.at(3)->GetWitnessHash()));178orphanage->SanityCheck();180// Add 1 more... child2 is evicted.181orphanage->AddTx(children.at(4), peer);182BOOST_CHECK(orphanage->HaveTx(children.at(0)->GetWitnessHash()));183BOOST_CHECK(!orphanage->HaveTx(children.at(2)->GetWitnessHash()));184BOOST_CHECK(orphanage->HaveTx(children.at(3)->GetWitnessHash()));185BOOST_CHECK(orphanage->HaveTx(children.at(4)->GetWitnessHash()));187// Eviction order is FIFO within the orphans that are read188const std::vector<std::pair<Wtxid, NodeId>> expected_set_c4{std::make_pair(children.at(4)->GetWitnessHash(), peer)};189BOOST_CHECK(orphanage->AddChildrenToWorkSet(*parents.at(4), det_rand) == expected_set_c4);190const std::vector<std::pair<Wtxid, NodeId>> expected_set_c3{std::make_pair(children.at(3)->GetWitnessHash(), peer)};191BOOST_CHECK(orphanage->AddChildrenToWorkSet(*parents.at(3), det_rand) == expected_set_c3);193// child5 is evicted immediately because it is the only non-reconsiderable orphan.194orphanage->AddTx(children.at(5), peer);195BOOST_CHECK(orphanage->HaveTx(children.at(0)->GetWitnessHash()));196BOOST_CHECK(orphanage->HaveTx(children.at(3)->GetWitnessHash()));197BOOST_CHECK(orphanage->HaveTx(children.at(4)->GetWitnessHash()));198BOOST_CHECK(!orphanage->HaveTx(children.at(5)->GetWitnessHash()));200// Transactions are marked non-reconsiderable again when returned through GetTxToReconsider201BOOST_CHECK_EQUAL(orphanage->GetTxToReconsider(peer), children.at(0));202orphanage->AddTx(children.at(6), peer);203BOOST_CHECK(!orphanage->HaveTx(children.at(0)->GetWitnessHash()));204BOOST_CHECK(orphanage->HaveTx(children.at(3)->GetWitnessHash()));205BOOST_CHECK(orphanage->HaveTx(children.at(4)->GetWitnessHash()));206BOOST_CHECK(orphanage->HaveTx(children.at(6)->GetWitnessHash()));208// The first transaction returned from GetTxToReconsider is the older one, not the one that was marked for209// reconsideration earlier.210BOOST_CHECK_EQUAL(orphanage->GetTxToReconsider(peer), children.at(3));211BOOST_CHECK_EQUAL(orphanage->GetTxToReconsider(peer), children.at(4));213orphanage->SanityCheck();214}216// Multiple peers: when limit is exceeded, we choose the DoSiest peer and evict their oldest transaction.217{218NodeId peer_dosy{0};219NodeId peer1{1};220NodeId peer2{2};222unsigned int max_announcements = 60;223// Set a high per-peer reservation so announcement limit is always hit first.224auto orphanage = node::MakeTxOrphanage(max_announcements, TOTAL_SIZE * 10);226// No evictions happen before the global limit is reached.227for (unsigned int i{0}; i < max_announcements; ++i) {228orphanage->AddTx(txns.at(i), peer_dosy);229}230orphanage->SanityCheck();231BOOST_CHECK_EQUAL(orphanage->AnnouncementsFromPeer(peer_dosy), max_announcements);232BOOST_CHECK_EQUAL(orphanage->AnnouncementsFromPeer(peer1), 0);233BOOST_CHECK_EQUAL(orphanage->AnnouncementsFromPeer(peer2), 0);235// Add 10 unique transactions from peer1.236// LimitOrphans should evict from peer_dosy, because that's the one exceeding announcement limits.237unsigned int num_from_peer1 = 10;238for (unsigned int i{0}; i < num_from_peer1; ++i) {239orphanage->AddTx(txns.at(max_announcements + i), peer1);240// The announcement limit per peer has halved, but LimitOrphans does not evict beyond what is necessary to241// bring the total announcements within its global limit.242BOOST_CHECK(orphanage->AnnouncementsFromPeer(peer_dosy) > orphanage->MaxPeerLatencyScore());244BOOST_CHECK_EQUAL(orphanage->AnnouncementsFromPeer(peer1), i + 1);245BOOST_CHECK_EQUAL(orphanage->AnnouncementsFromPeer(peer_dosy), max_announcements - i - 1);247// Evictions are FIFO within a peer, so the ith transaction sent by peer_dosy is the one that was evicted.248BOOST_CHECK(!orphanage->HaveTx(txns.at(i)->GetWitnessHash()));249}250// Add 10 transactions that are duplicates of the ones sent by peer_dosy. We need to add 10 because the first 10
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.