Skip to content
Bitcoin Roots

v31.1-roots.1 · Commit 3: [PATCH 03/17] feat(policy): preserve cluster accounting and repair dynamic dust

src/test/fuzz/tx_pool.cpp

Before: src/test/fuzz/tx_pool.cpp · After: src/test/fuzz/tx_pool.cpp

File metadata
index bb15552723afa9485549d4379543bee4f6eca3b4..b0589bd50f00cfc959cb9cc15d08fbcaf1fa37e7 100644

Download original patch · Release notes

Diff rows 1–145 of 145. Line numbers refer to the original before/after files.

+ Added− RemovedUnmarked lines are context.

Scroll the code area horizontally to read long lines. Before is on the left; after is on the right. Unified view fits narrow screens more easily.

src/test/fuzz/tx_pool.cpp CHANGED
@@ -21,6 +21,11 @@
21
  #include <validation.h>
22
  #include <validationinterface.h>
23
 
 
 
 
 
 
24
  using node::BlockAssembler;
25
  using node::NodeContext;
26
  using util::ToString;
@@ -463,4 +468,134 @@ FUZZ_TARGET(tx_pool, .init = initialize_tx_pool)
463
  }
464
  Finish(fuzzed_data_provider, tx_pool, chainstate);
465
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
466
  } // namespace
 
21
  #include <validation.h>
22
  #include <validationinterface.h>
23
 
24
+ #include <boost/multiprecision/cpp_int.hpp>
25
+
26
+ #include <algorithm>
27
+ #include <array>
28
+
29
  using node::BlockAssembler;
30
  using node::NodeContext;
31
  using util::ToString;
 
468
  }
469
  Finish(fuzzed_data_provider, tx_pool, chainstate);
470
  }
471
+ FUZZ_TARGET(subdust_fee_penalty, .init = initialize_tx_pool)
472
+ {
473
+ SeedRandomStateForTest(SeedRand::ZEROS);
474
+ FuzzedDataProvider provider{buffer.data(), buffer.size()};
475
+ const auto& node{g_setup->m_node};
476
+ auto& chainstate{static_cast<DummyChainState&>(node.chainman->ActiveChainstate())};
477
+ SetMockTime(chainstate.m_chain.Tip()->GetMedianTimePast() + 1);
478
+
479
+ using Wide = boost::multiprecision::int128_t;
480
+ const auto clamp_amount = [](Wide amount) -> CAmount {
481
+ return std::clamp(amount, Wide{INT64_MIN}, Wide{INT64_MAX}).convert_to<CAmount>();
482
+ };
483
+ // All these outputs are below 1,000 bytes including the spend estimate.
484
+ // Thus an arbitrary nonnegative int64 rate has a representable threshold.
485
+ // Larger scripts/rates require a separate threshold-overflow decision.
486
+ const std::array<CScript, 5> scripts{
487
+ CScript{} << OP_1 << std::vector<unsigned char>(32, 1),
488
+ CScript{} << OP_0 << std::vector<unsigned char>(20, 1),
489
+ CScript{} << OP_1 << std::vector<unsigned char>{0x4e, 0x73},
490
+ CScript{} << OP_DUP << OP_HASH160 << std::vector<unsigned char>(20, 1) << OP_EQUALVERIFY << OP_CHECKSIG,
491
+ CScript{} << OP_RETURN << std::vector<unsigned char>(16, 1),
492
+ };
493
+ const CAmount rate{provider.ConsumeIntegralInRange<CAmount>(0, INT64_MAX)};
494
+ const CAmount fee{provider.ConsumeIntegralInRange<CAmount>(0, 1'000'000)};
495
+ const CAmount delta{provider.ConsumeIntegralInRange<CAmount>(-MAX_MONEY, MAX_MONEY)};
496
+ CMutableTransaction mutable_tx;
497
+ mutable_tx.vin.emplace_back(g_outpoints_coinbase_init_mature.front());
498
+ mutable_tx.vin[0].scriptWitness.stack = {WITNESS_STACK_ELEM_OP_TRUE};
499
+ CAmount output_value{0};
500
+ const auto output_count{provider.ConsumeIntegralInRange<unsigned>(1, 16)};
501
+ for (unsigned i{0}; i < output_count; ++i) {
502
+ const CAmount value{provider.ConsumeIntegralInRange<CAmount>(0, 1000)};
503
+ mutable_tx.vout.emplace_back(value, provider.PickValueInArray(scripts));
504
+ output_value += value;
505
+ }
506
+ mutable_tx.vout.emplace_back(50 * COIN - fee - output_value, P2WSH_OP_TRUE);
507
+ const auto tx{MakeTransactionRef(mutable_tx)};
508
+
509
+ Wide deficit{0};
510
+ for (const auto& output : tx->vout) {
511
+ CAmount threshold{0};
512
+ if (!output.scriptPubKey.IsUnspendable()) {
513
+ int witness_version;
514
+ std::vector<unsigned char> program;
515
+ const auto bytes{GetSerializeSize(output) + (output.scriptPubKey.IsWitnessProgram(witness_version, program) ? 67 : 148)};
516
+ threshold = ((Wide{rate} * bytes + 999) / 1000).convert_to<CAmount>();
517
+ }
518
+ Assert(GetDustThreshold(output, CFeeRate{rate}) == threshold);
519
+ deficit += std::max<CAmount>(0, threshold - output.nValue);
520
+ }
521
+ const CAmount penalty{clamp_amount(deficit)};
522
+ const auto previous_mempool{chainstate.GetMempool()};
523
+ for (const bool enabled : {false, true}) {
524
+ auto options{MemPoolOptionsForTest(node)};
525
+ options.require_standard = false;
526
+ options.datacarrier_fullcount = false;
527
+ options.accept_non_std_datacarrier = true;
528
+ options.subdustfeepenalty = enabled;
529
+ options.dust_relay_feerate = CFeeRate{rate};
530
+ options.min_relay_feerate = CFeeRate{1000};
531
+ options.check_ratio = 0;
532
+ bilingual_str error;
533
+ CTxMemPool pool{options, error};
534
+ Assert(error.empty());
535
+ chainstate.SetMempool(&pool);
536
+ pool.PrioritiseTransaction(tx->GetHash(), delta);
537
+ const CAmount modified{clamp_amount(Wide{fee} + delta - (enabled ? penalty : 0))};
538
+ const auto result{WITH_LOCK(cs_main, return AcceptToMemoryPool(chainstate, tx, GetTime(), /*bypass_limits=*/false, /*test_accept=*/false))};
539
+ // These valid v2 fixtures have only the relay floor as an admission gate.
540
+ Assert((result.m_result_type == MempoolAcceptResult::ResultType::VALID) == (modified >= GetVirtualTransactionSize(*tx)));
541
+ if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
542
+ LOCK(pool.cs);
543
+ const auto entry{Assert(pool.GetEntry(tx->GetHash()))};
544
+ Assert(entry->GetFee() == fee);
545
+ Assert(entry->GetModifiedFee() == modified);
546
+ Assert(pool.GetMainChunkFeerate(*entry).fee == modified);
547
+ Assert(pool.CalculateAncestorData(*entry) == std::make_tuple(size_t{1}, static_cast<size_t>(entry->GetTxSize()), modified));
548
+ } else {
549
+ Assert(result.m_state.GetRejectReason() == "min relay fee not met");
550
+ }
551
+ node.validation_signals->SyncWithValidationInterfaceQueue();
552
+ chainstate.SetMempool(previous_mempool);
553
+ }
554
+ }
555
+ FUZZ_TARGET(dynamic_dust_fee)
556
+ {
557
+ FuzzedDataProvider provider{buffer.data(), buffer.size()};
558
+ const CAmount fee{provider.ConsumeIntegral<CAmount>()};
559
+ const int32_t size{provider.ConsumeIntegralInRange<int32_t>(1, INT32_MAX)};
560
+ const int multiplier{provider.ConsumeIntegralInRange<int>(1, INT32_MAX)};
561
+ using Wide = boost::multiprecision::int128_t;
562
+ const Wide maximum{std::numeric_limits<CAmount>::max()};
563
+ const Wide minimum{std::numeric_limits<CAmount>::min()};
564
+ const Wide product{Wide{fee} * 1000};
565
+ Wide per_kvb{product / size};
566
+ if (product < 0 && product % size != 0) --per_kvb;
567
+ per_kvb = std::max(minimum, std::min(maximum, per_kvb));
568
+ const CFeeRate estimate{fee, size};
569
+ Assert(estimate.GetFeePerK() == static_cast<CAmount>(per_kvb));
570
+ const Wide scaled{std::max(Wide{0}, per_kvb) * multiplier / 1000};
571
+ Assert(ScaleDustFee(estimate, multiplier).GetFeePerK() == static_cast<CAmount>(std::min(maximum, scaled)));
572
+ }
573
+
574
+ FUZZ_TARGET(carrier_policy_weight)
575
+ {
576
+ SeedRandomStateForTest(SeedRand::ZEROS);
577
+ FuzzedDataProvider provider{buffer.data(), buffer.size()};
578
+ const unsigned int cost{provider.ConsumeIntegral<unsigned int>()};
579
+ CMutableTransaction tx;
580
+ tx.vin.resize(1);
581
+ uint64_t data_bytes{0};
582
+ const auto outputs{provider.ConsumeIntegralInRange<unsigned>(1, 16)};
583
+ for (unsigned i{0}; i < outputs; ++i) {
584
+ const auto bytes{provider.ConsumeBytes<unsigned char>(provider.ConsumeIntegralInRange<unsigned>(0, 4096))};
585
+ const CScript script{CScript{} << OP_RETURN << bytes};
586
+ data_bytes += script.size();
587
+ tx.vout.emplace_back(0, script);
588
+ }
589
+ CCoinsView view;
590
+ CCoinsViewCache cache{&view};
591
+ const auto ref{MakeTransactionRef(tx)};
592
+ const uint64_t extra{cost > WITNESS_SCALE_FACTOR ? data_bytes * (cost - WITNESS_SCALE_FACTOR) : 0};
593
+ const auto expected{static_cast<int32_t>(std::min<uint64_t>(extra, INT32_MAX))};
594
+ Assert(CalculateExtraTxWeight(*ref, cache, cost) == expected);
595
+ const CTxMemPoolEntry entry{ref, 123, 0, 1, 0, false, 0, {}, expected};
596
+ Assert(entry.GetFee() == 123);
597
+ Assert(entry.GetTxWeight() == GetTransactionWeight(*ref));
598
+ Assert(entry.GetAdjustedWeight() == GetTransactionWeight(*ref) + int64_t{expected});
599
+ Assert(entry.GetTxSize() == (entry.GetAdjustedWeight() + 3) / 4);
600
+ }
601
  } // namespace