46 std::vector<COutPoint> outpoints;
47 outpoints.reserve(200'000);
50 for (uint8_t i = 0; i < 4; i++) {
56 std::vector<CTransactionRef> tx_history;
67 tx_mut.
vin.reserve(num_in);
68 for (uint32_t i = 0; i < num_in; i++) {
69 auto& prevout =
PickValue(fuzzed_data_provider, outpoints);
74 tx_mut.
vout.reserve(num_out);
75 for (uint32_t i = 0; i < num_out; i++) {
80 for (uint32_t i = 0; i < num_out; i++) {
81 outpoints.emplace_back(new_tx->GetHash(), i);
86 tx_history.push_back(tx);
88 const auto wtxid{tx->GetWitnessHash()};
92 if (ptx_potential_parent) {
94 orphanage->AddChildrenToWorkSet(*ptx_potential_parent, orphanage_rng);
98 for (
const auto& child : orphanage->GetChildrenFromSamePeer(ptx_potential_parent, peer_id)) {
99 assert(std::any_of(child->vin.cbegin(), child->vin.cend(), [&](
const auto& input) {
100 return input.prevout.hash == ptx_potential_parent->GetHash();
109 const auto total_bytes_start{orphanage->TotalOrphanUsage()};
110 const auto total_peer_bytes_start{orphanage->UsageByPeer(peer_id)};
114 fuzzed_data_provider,
119 Assert(orphanage->HaveTx(ref->GetWitnessHash()));
124 bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
125 bool have_tx_and_peer = orphanage->HaveTxFromPeer(wtxid, peer_id);
129 bool add_tx = orphanage->AddTx(tx, peer_id);
131 Assert(!have_tx || !add_tx);
133 Assert(!have_tx_and_peer || !add_tx);
140 if (orphanage->UsageByPeer(peer_id) > total_peer_bytes_start) {
141 Assert(orphanage->HaveTxFromPeer(wtxid, peer_id));
146 Assert(orphanage->TotalOrphanUsage() <= total_bytes_start);
155 bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
156 bool have_tx_and_peer = orphanage->HaveTxFromPeer(tx->GetWitnessHash(), peer_id);
159 bool added_announcer = orphanage->AddAnnouncer(tx->GetWitnessHash(), peer_id);
161 Assert(have_tx || !added_announcer);
163 Assert(!have_tx_and_peer || !added_announcer);
167 Assert(orphanage->TotalOrphanUsage() <= total_bytes_start);
171 bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
172 bool have_tx_and_peer{orphanage->HaveTxFromPeer(wtxid, peer_id)};
175 auto bytes_from_peer_before{orphanage->UsageByPeer(peer_id)};
176 Assert(have_tx == orphanage->EraseTx(tx->GetWitnessHash()));
179 if (!have_tx_and_peer) {
180 Assert(orphanage->UsageByPeer(peer_id) == bytes_from_peer_before);
184 have_tx = orphanage->HaveTx(tx->GetWitnessHash());
185 have_tx_and_peer = orphanage->HaveTxFromPeer(wtxid, peer_id);
188 Assert(!have_tx && !have_tx_and_peer && !orphanage->EraseTx(wtxid));
192 orphanage->EraseForPeer(peer_id);
193 Assert(!orphanage->HaveTxFromPeer(tx->GetWitnessHash(), peer_id));
194 Assert(orphanage->UsageByPeer(peer_id) == 0);
200 for (
int i{0}; i < num_txs; ++i) {
201 auto& tx_to_remove =
PickValue(fuzzed_data_provider, tx_history);
202 block.
vtx.push_back(tx_to_remove);
204 orphanage->EraseForBlock(block);
205 for (
const auto& tx_removed : block.
vtx) {
206 Assert(!orphanage->HaveTx(tx_removed->GetWitnessHash()));
207 Assert(!orphanage->HaveTxFromPeer(tx_removed->GetWitnessHash(), peer_id));
214 if (!ptx_potential_parent || fuzzed_data_provider.
ConsumeBool()) {
215 ptx_potential_parent = tx;
218 const bool have_tx{orphanage->HaveTx(tx->GetWitnessHash())};
219 const bool get_tx_nonnull{orphanage->GetTx(tx->GetWitnessHash()) !=
nullptr};
220 Assert(have_tx == get_tx_nonnull);
222 orphanage->SanityCheck();
234 const unsigned int MAX_PEERS = 125;
237 std::bitset<MAX_PEERS> protected_peers;
238 for (
unsigned int i = 0; i < num_peers; i++) {
239 protected_peers.set(i, fuzzed_data_provider.
ConsumeBool());
243 const unsigned int global_latency_score_limit = fuzzed_data_provider.
ConsumeIntegralInRange<
unsigned int>(num_peers, 6'000);
244 const int64_t per_peer_weight_reservation = fuzzed_data_provider.
ConsumeIntegralInRange<int64_t>(1, 4'040'000);
250 const unsigned int honest_latency_limit = global_latency_score_limit / num_peers;
252 const int64_t honest_mem_limit = per_peer_weight_reservation;
254 std::vector<COutPoint> outpoints;
255 outpoints.reserve(400);
258 for (uint8_t i = 0; i < 4; i++) {
263 std::set<Wtxid> protected_wtxids;
274 tx_mut.
vin.reserve(num_in);
275 for (uint32_t i = 0; i < num_in; i++) {
276 auto& prevout =
PickValue(fuzzed_data_provider, outpoints);
281 tx_mut.
vout.reserve(num_out);
282 for (uint32_t i = 0; i < num_out; i++) {
285 tx_mut.
vout.emplace_back(0,
CScript() << OP_RETURN << std::vector<unsigned char>(payload_size));
292 for (uint32_t i = 0; i < num_out; i++) {
293 outpoints.emplace_back(new_tx->GetHash(), i);
298 const auto wtxid{tx->GetWitnessHash()};
308 const bool peer_is_protected{protected_peers[peer_id]};
311 fuzzed_data_provider,
313 bool have_tx_and_peer = orphanage->HaveTxFromPeer(wtxid, peer_id);
314 if (peer_is_protected && !have_tx_and_peer &&
315 (orphanage->UsageByPeer(peer_id) + tx_weight > honest_mem_limit ||
316 orphanage->LatencyScoreFromPeer(peer_id) + (tx->vin.size() / 10) + 1 > honest_latency_limit)) {
319 orphanage->AddTx(tx, peer_id);
320 if (peer_is_protected && orphanage->HaveTxFromPeer(wtxid, peer_id)) {
321 protected_wtxids.insert(wtxid);
326 bool have_tx_and_peer = orphanage->HaveTxFromPeer(tx->GetWitnessHash(), peer_id);
329 if (peer_is_protected && !have_tx_and_peer &&
330 (orphanage->UsageByPeer(peer_id) + tx_weight > honest_mem_limit ||
331 orphanage->LatencyScoreFromPeer(peer_id) + (tx->vin.size() / 10) + 1 > honest_latency_limit)) {
334 orphanage->AddAnnouncer(tx->GetWitnessHash(), peer_id);
335 if (peer_is_protected && orphanage->HaveTxFromPeer(wtxid, peer_id)) {
336 protected_wtxids.insert(wtxid);
342 if (protected_wtxids.count(tx->GetWitnessHash())) {
343 protected_wtxids.erase(wtxid);
345 orphanage->EraseTx(wtxid);
346 Assert(!orphanage->HaveTx(wtxid));
349 if (!protected_peers[peer_id]) {
350 orphanage->EraseForPeer(peer_id);
351 Assert(orphanage->UsageByPeer(peer_id) == 0);
352 Assert(orphanage->LatencyScoreFromPeer(peer_id) == 0);
353 Assert(orphanage->AnnouncementsFromPeer(peer_id) == 0);
360 orphanage->SanityCheck();
362 for (
const auto& wtxid : protected_wtxids) {
363 Assert(orphanage->HaveTx(wtxid));
382 static constexpr unsigned NUM_TX = 16;
385 static constexpr unsigned NUM_PEERS = 16;
388 static constexpr unsigned MAX_ANN = 64;
400 std::vector<unsigned> txorder(NUM_TX);
401 std::iota(txorder.begin(), txorder.end(),
unsigned{0});
402 std::shuffle(txorder.begin(), txorder.end(), rng);
404 std::vector<std::pair<unsigned, unsigned>> deps;
405 deps.reserve((NUM_TX * (NUM_TX - 1)) / 2);
406 for (
unsigned p = 0; p < NUM_TX - 1; ++p) {
407 for (
unsigned c = p + 1; c < NUM_TX; ++c) {
408 deps.emplace_back(c, p);
411 std::shuffle(deps.begin(), deps.end(), rng);
414 std::set<Wtxid> wtxids;
415 std::vector<CTransactionRef> txn(NUM_TX);
417 for (
unsigned t = 0;
t < NUM_TX; ++
t) {
428 for (
unsigned output = 0; output < num_outputs; ++output) {
431 tx.
vout.emplace_back(
CAmount{0}, std::move(scriptpubkey));
434 for (
auto& [child, parent] : deps) {
436 auto& partx = txn[txorder[parent]];
437 assert(partx->version == 1);
439 tx.
vin.emplace_back(outpoint);
444 if (tx.
vin.empty()) {
446 tx.
vin.emplace_back(outpoint);
455 input.scriptWitness.stack.resize(1);
456 input.scriptWitness.stack[0].resize(rng.
randrange(100));
458 input.scriptWitness.stack.resize(0);
463 wtxids.insert(txn[txorder[
t]]->GetWitnessHash());
483 struct SimAnnouncement
487 bool reconsider{
false};
488 SimAnnouncement(
unsigned tx_in,
NodeId announcer_in,
bool reconsider_in) noexcept :
489 tx(tx_in), announcer(announcer_in), reconsider(reconsider_in) {}
494 std::vector<SimAnnouncement> sim_announcements;
501 auto read_tx_peer_fn = [&]() -> std::pair<unsigned, NodeId> {
503 return {code % NUM_TX, code / NUM_TX};
506 auto have_tx_fn = [&](
unsigned tx) ->
bool {
507 for (
auto& ann : sim_announcements) {
508 if (ann.tx == tx)
return true;
513 auto count_peers_fn = [&]() ->
unsigned {
514 std::bitset<NUM_PEERS> mask;
515 for (
auto& ann : sim_announcements) {
516 mask.set(ann.announcer);
521 auto have_reconsiderable_fn = [&](
unsigned tx) ->
bool {
522 for (
auto& ann : sim_announcements) {
523 if (ann.reconsider && ann.tx == tx)
return true;
528 auto have_reconsider_fn = [&](
NodeId peer) ->
bool {
529 for (
auto& ann : sim_announcements) {
530 if (ann.reconsider && ann.announcer == peer)
return true;
535 auto find_announce_wtxid_fn = [&](
const Wtxid& wtxid,
NodeId peer) -> std::vector<SimAnnouncement>::iterator {
536 for (
auto it = sim_announcements.begin(); it != sim_announcements.end(); ++it) {
537 if (txn[it->tx]->GetWitnessHash() == wtxid && it->announcer == peer)
return it;
539 return sim_announcements.end();
542 auto find_announce_fn = [&](
unsigned tx,
NodeId peer) {
543 for (
auto it = sim_announcements.begin(); it != sim_announcements.end(); ++it) {
544 if (it->tx == tx && it->announcer == peer)
return it;
546 return sim_announcements.end();
549 auto dos_score_fn = [&](
NodeId peer, int32_t max_count, int32_t max_usage) ->
FeeFrac {
552 for (
auto& ann : sim_announcements) {
553 if (ann.announcer != peer)
continue;
554 count += 1 + (txn[ann.tx]->vin.size() / 10);
567 if (sim_announcements.size() < MAX_ANN &&
command-- == 0) {
569 auto [tx, peer] = read_tx_peer_fn();
570 bool added = real->AddTx(txn[tx], peer);
571 bool sim_have_tx = have_tx_fn(tx);
572 assert(added == !sim_have_tx);
573 if (find_announce_fn(tx, peer) == sim_announcements.end()) {
574 sim_announcements.emplace_back(tx, peer,
false);
577 }
else if (sim_announcements.size() < MAX_ANN &&
command-- == 0) {
579 auto [tx, peer] = read_tx_peer_fn();
580 bool added = real->AddAnnouncer(txn[tx]->GetWitnessHash(), peer);
581 bool sim_have_tx = have_tx_fn(tx);
582 auto sim_it = find_announce_fn(tx, peer);
583 assert(added == (sim_it == sim_announcements.end() && sim_have_tx));
585 sim_announcements.emplace_back(tx, peer,
false);
590 auto tx = read_tx_fn();
591 bool erased = real->EraseTx(txn[tx]->GetWitnessHash());
592 bool sim_have = have_tx_fn(tx);
593 assert(erased == sim_have);
594 std::erase_if(sim_announcements, [&](
auto& ann) {
return ann.tx == tx; });
598 auto peer = read_peer_fn();
599 real->EraseForPeer(peer);
600 std::erase_if(sim_announcements, [&](
auto& ann) {
return ann.announcer == peer; });
606 std::set<COutPoint> spent;
607 for (
unsigned tx = 0; tx < NUM_TX; ++tx) {
608 if ((pattern >> tx) & 1) {
609 block.
vtx.emplace_back(txn[tx]);
610 for (
auto& txin : block.
vtx.back()->vin) {
611 spent.insert(txin.prevout);
615 std::shuffle(block.
vtx.begin(), block.
vtx.end(), rng);
616 real->EraseForBlock(block);
617 std::erase_if(sim_announcements, [&](
auto& ann) {
618 for (
auto& txin : txn[ann.tx]->vin) {
619 if (spent.count(txin.prevout)) return true;
626 auto tx = read_tx_fn();
628 auto added = real->AddChildrenToWorkSet(*txn[tx], rand_ctx);
630 std::set<Wtxid> child_wtxids;
631 for (
unsigned child_tx = 0; child_tx < NUM_TX; ++child_tx) {
632 if (!have_tx_fn(child_tx))
continue;
633 if (have_reconsiderable_fn(child_tx))
continue;
634 bool child_of =
false;
635 for (
auto& txin : txn[child_tx]->vin) {
636 if (txin.prevout.hash == txn[tx]->GetHash()) {
642 child_wtxids.insert(txn[child_tx]->GetWitnessHash());
645 for (
auto& [wtxid, peer] : added) {
647 auto child_wtxid_it = child_wtxids.find(wtxid);
648 assert(child_wtxid_it != child_wtxids.end());
650 auto sim_ann_it = find_announce_wtxid_fn(wtxid, peer);
651 assert(sim_ann_it != sim_announcements.end());
653 assert(sim_ann_it->reconsider ==
false);
655 sim_ann_it->reconsider =
true;
657 child_wtxids.erase(wtxid);
662 assert(child_wtxids.empty());
666 auto peer = read_peer_fn();
667 auto result = real->GetTxToReconsider(peer);
671 auto sim_ann_it = find_announce_wtxid_fn(result->GetWitnessHash(), peer);
672 assert(sim_ann_it != sim_announcements.end());
673 assert(sim_ann_it->announcer == peer);
674 assert(sim_ann_it->reconsider);
676 sim_ann_it->reconsider =
false;
680 assert(!have_reconsider_fn(peer));
686 const auto max_ann = max_global_latency_score / std::max<unsigned>(1, count_peers_fn());
687 const auto max_mem = reserved_peer_usage;
692 for (
unsigned tx = 0; tx < NUM_TX; ++tx) {
693 if (have_tx_fn(tx)) {
695 total_latency_score += txn[tx]->vin.size() / 10;
698 auto num_peers = count_peers_fn();
699 bool oversized = (total_usage > reserved_peer_usage * num_peers) ||
700 (total_latency_score > real->MaxGlobalLatencyScore());
701 if (!oversized)
break;
704 unsigned worst_peer = unsigned(-1);
705 for (
unsigned peer = 0; peer < NUM_PEERS; ++peer) {
706 auto dos_score = dos_score_fn(peer, max_ann, max_mem);
709 if (dos_score >= worst_dos_score) {
710 worst_dos_score = dos_score;
714 assert(worst_peer !=
unsigned(-1));
718 for (
int reconsider = 0; reconsider < 2; ++reconsider) {
719 for (
auto it = sim_announcements.begin(); it != sim_announcements.end(); ++it) {
720 if (it->announcer != worst_peer || it->reconsider != reconsider)
continue;
721 sim_announcements.erase(it);
731 assert(real->TotalLatencyScore() <= real->MaxGlobalLatencyScore());
732 assert(real->TotalOrphanUsage() <= real->MaxGlobalUsage());
742 auto all_orphans = real->GetOrphanTransactions();
744 std::vector<node::TxOrphanage::Usage> usage_by_peer(NUM_PEERS);
746 std::vector<node::TxOrphanage::Count> count_by_peer(NUM_PEERS);
748 for (
unsigned tx = 0; tx < NUM_TX; ++tx) {
749 bool sim_have_tx = have_tx_fn(tx);
752 total_latency_score += txn[tx]->vin.size() / 10;
754 unique_orphans += sim_have_tx;
755 auto orphans_it = std::find_if(all_orphans.begin(), all_orphans.end(), [&](
auto& orph) { return orph.tx->GetWitnessHash() == txn[tx]->GetWitnessHash(); });
757 assert((orphans_it != all_orphans.end()) == sim_have_tx);
759 bool have_tx = real->HaveTx(txn[tx]->GetWitnessHash());
760 assert(have_tx == sim_have_tx);
762 auto txref = real->GetTx(txn[tx]->GetWitnessHash());
763 assert(!!txref == sim_have_tx);
764 if (sim_have_tx)
assert(txref->GetWitnessHash() == txn[tx]->GetWitnessHash());
766 for (
NodeId peer = 0; peer < NUM_PEERS; ++peer) {
767 auto it_sim_ann = find_announce_fn(tx, peer);
768 bool sim_have_ann = it_sim_ann != sim_announcements.end();
770 count_by_peer[peer] += sim_have_ann;
772 if (sim_have_ann)
assert(sim_have_tx);
773 if (sim_have_tx)
assert(orphans_it->announcers.count(peer) == sim_have_ann);
775 bool have_ann = real->HaveTxFromPeer(txn[tx]->GetWitnessHash(), peer);
776 assert(sim_have_ann == have_ann);
778 auto children_from_peer = real->GetChildrenFromSamePeer(txn[tx], peer);
779 auto it = children_from_peer.rbegin();
780 for (
int phase = 0; phase < 2; ++phase) {
782 for (
auto& ann : sim_announcements) {
783 if (ann.announcer != peer)
continue;
784 if (ann.reconsider != (phase == 1))
continue;
785 bool matching_parent{
false};
786 for (
const auto& vin : txn[ann.tx]->vin) {
787 if (vin.prevout.hash == txn[tx]->GetHash()) matching_parent =
true;
789 if (!matching_parent)
continue;
791 assert(it != children_from_peer.rend());
792 assert((*it)->GetWitnessHash() == txn[ann.tx]->GetWitnessHash());
796 assert(it == children_from_peer.rend());
800 assert(orphan_usage == real->TotalOrphanUsage());
801 for (
NodeId peer = 0; peer < NUM_PEERS; ++peer) {
802 bool sim_have_reconsider = have_reconsider_fn(peer);
804 bool have_reconsider = real->HaveTxToReconsider(peer);
805 assert(have_reconsider == sim_have_reconsider);
807 assert(usage_by_peer[peer] == real->UsageByPeer(peer));
809 assert(count_by_peer[peer] == real->AnnouncementsFromPeer(peer));
812 assert(sim_announcements.size() == real->CountAnnouncements());
814 assert(unique_orphans == real->CountUniqueOrphans());
816 assert(max_global_latency_score == real->MaxGlobalLatencyScore());
818 assert(reserved_peer_usage == real->ReservedPeerUsage());
820 auto present_peers = count_peers_fn();
821 assert(max_global_latency_score / std::max<unsigned>(1, present_peers) == real->MaxPeerLatencyScore());
823 assert(reserved_peer_usage * std::max<unsigned>(1, present_peers) == real->MaxGlobalUsage());
825 assert(real->TotalLatencyScore() == total_latency_score);
int64_t CAmount
Amount in satoshis (Can be negative)
#define Assert(val)
Identity function.
std::vector< CTransactionRef > vtx
An outpoint - a combination of a transaction hash and an index n into its vout.
Serialized script, used inside transaction inputs and outputs.
The basic transaction that is broadcasted on the network and contained in blocks.
static const uint32_t SEQUENCE_FINAL
Setting nSequence to this value for every input in a transaction disables nLockTime/IsFinalTx().
T ConsumeIntegralInRange(T min, T max)
I randrange(I range) noexcept
Generate a random integer in the range [0..range), with range > 0.
uint256 rand256() noexcept
generate a random uint256.
bool randbool() noexcept
Generate a random boolean.
void resize(size_type new_size)
transaction_identifier represents the two canonical transaction identifier types (txid,...
static transaction_identifier FromUint256(const uint256 &id)
static int32_t GetTransactionWeight(const CTransaction &tx)
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
std::unique_ptr< TxOrphanage > MakeTxOrphanage() noexcept
Create a new TxOrphanage instance.
static constexpr int32_t MAX_STANDARD_TX_WEIGHT
The maximum weight for transactions we're willing to relay/mine.
static CTransactionRef MakeTransactionRef(Tx &&txIn)
std::shared_ptr< const CTransaction > CTransactionRef
std::unique_ptr< T > MakeNoLogFileContext(const ChainType chain_type=ChainType::REGTEST, TestOpts opts={})
Make a test setup that has disk access to the debug.log file disabled.
A mutable version of CTransaction.
std::vector< CTxOut > vout
Data structure storing a fee and size, ordered by increasing fee/size.
int64_t ConsumeTime(FuzzedDataProvider &fuzzed_data_provider, const std::optional< int64_t > &min, const std::optional< int64_t > &max) noexcept
auto & PickValue(FuzzedDataProvider &fuzzed_data_provider, Collection &col)
uint256 ConsumeUInt256(FuzzedDataProvider &fuzzed_data_provider) noexcept
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
void SeedRandomStateForTest(SeedRand seedtype)
Seed the global RNG state for testing and log the seed value.
@ ZEROS
Seed with a compile time constant of zeros.
void initialize_orphanage()
FUZZ_TARGET(txorphan,.init=initialize_orphanage)
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.