Branch data Line data Source code
1 : : // Copyright (c) 2022-present The Bitcoin Core developers
2 : : // Distributed under the MIT software license, see the accompanying
3 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 : :
5 : : #include <node/block_template_manager.h>
6 : : #include <policy/fees/estimator_man.h>
7 : : #include <test/fuzz/FuzzedDataProvider.h>
8 : : #include <test/fuzz/fuzz.h>
9 : : #include <test/fuzz/util.h>
10 : : #include <test/util/setup_common.h>
11 : : #include <test/util/time.h>
12 : : #include <test/util/txmempool.h>
13 : : #include <util/expected.h>
14 : : #include <util/fees.h>
15 : : #include <validation.h>
16 : : #include <wallet/coincontrol.h>
17 : : #include <wallet/fees.h>
18 : : #include <wallet/test/util.h>
19 : : #include <wallet/wallet.h>
20 : :
21 : : #include <optional>
22 : :
23 : : namespace wallet {
24 : : namespace {
25 : :
26 : : struct FeeEstimatorManTestingSetup : public TestingSetup {
27 [ + - ]: 1 : FeeEstimatorManTestingSetup(const ChainType chain_type, TestOpts opts) : TestingSetup{chain_type, opts}
28 : : {
29 : 1 : }
30 : :
31 : 1 : ~FeeEstimatorManTestingSetup()
32 : : {
33 [ + - ]: 1 : m_node.fee_estimator_man.reset();
34 : 1 : }
35 : :
36 : 153 : void SetFeeEstimatorMan(std::unique_ptr<FeeRateEstimatorManager> fee_estimator_man)
37 : : {
38 : 153 : m_node.fee_estimator_man = std::move(fee_estimator_man);
39 : : }
40 : : };
41 : :
42 : : FeeEstimatorManTestingSetup* g_setup;
43 : :
44 : : class FuzzedFeeEstimatorMan : public FeeRateEstimatorManager
45 : : {
46 : : FuzzedDataProvider& fuzzed_data_provider;
47 : :
48 : : public:
49 : 153 : FuzzedFeeEstimatorMan(FuzzedDataProvider& provider, const CTxMemPool& mempool, ChainstateManager& chainman)
50 [ + - ]: 459 : : FeeRateEstimatorManager(fs::path{}, false, fs::path{}, mempool, chainman), fuzzed_data_provider(provider) {}
51 : :
52 : 295 : util::Expected<FeeRateEstimation, FeeRateEstimationError> GetFeeRateEstimate(int confTarget, bool conservative) const override
53 : : {
54 : 295 : FeePerVSize feerate(ConsumeMoney(fuzzed_data_provider, /*max=*/1'000'000), fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(1000, 1000000));
55 : 295 : return FeeRateEstimation{FeeRateEstimatorType::BLOCK_POLICY, feerate, fuzzed_data_provider.ConsumeIntegralInRange<int>(2, 1004)};
56 : : }
57 : 0 : util::Expected<FeeRateEstimation, FeeRateEstimationError> GetFeeRateEstimate(FeeRateEstimatorType type, int confTarget, bool conservative) const override
58 : : {
59 : 0 : auto res = GetFeeRateEstimate(confTarget, conservative);
60 [ # # ]: 0 : if (res) res->feerate_estimator = type;
61 : 0 : return res;
62 : : }
63 : 153 : unsigned int MaximumTarget() const override
64 : : {
65 : 153 : return fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(1, 1004);
66 : : }
67 : : };
68 : :
69 : 1 : void initialize_setup()
70 : : {
71 [ + - + - : 1 : static const auto testing_setup = MakeNoLogFileContext<FeeEstimatorManTestingSetup>();
+ - ]
72 : 1 : g_setup = testing_setup.get();
73 : 1 : }
74 : :
75 [ + - ]: 629 : FUZZ_TARGET(wallet_fees, .init = initialize_setup)
76 : : {
77 : 153 : SeedRandomStateForTest(SeedRand::ZEROS);
78 : 153 : FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
79 : 153 : FakeNodeClock clock{ConsumeTime(fuzzed_data_provider)};
80 : 153 : auto& node{g_setup->m_node};
81 [ + - ]: 153 : Chainstate* chainstate = &node.chainman->ActiveChainstate();
82 : :
83 : 153 : bilingual_str error;
84 : 153 : CTxMemPool::Options mempool_opts{
85 : 153 : .incremental_relay_feerate = CFeeRate{ConsumeMoney(fuzzed_data_provider, 1'000'000)},
86 : 153 : .min_relay_feerate = CFeeRate{ConsumeMoney(fuzzed_data_provider, 1'000'000)},
87 : 153 : .dust_relay_feerate = CFeeRate{ConsumeMoney(fuzzed_data_provider, 1'000'000)}
88 [ + - ]: 153 : };
89 [ + - ]: 153 : node.block_template_manager.reset();
90 [ + - ]: 153 : node.mempool = std::make_unique<CTxMemPool>(mempool_opts, error);
91 [ + - ]: 153 : g_setup->CreateBlockTemplateManager();
92 [ + - ]: 153 : std::unique_ptr<FeeRateEstimatorManager> fee_estimator_man = std::make_unique<FuzzedFeeEstimatorMan>(fuzzed_data_provider, *node.mempool, *node.chainman);
93 : 153 : g_setup->SetFeeEstimatorMan(std::move(fee_estimator_man));
94 [ + + ]: 153 : auto target_feerate{CFeeRate{ConsumeMoney(fuzzed_data_provider, /*max=*/1'000'000)}};
95 [ + + ]: 61 : if (target_feerate > node.mempool->m_opts.incremental_relay_feerate &&
96 [ + + ]: 61 : target_feerate > node.mempool->m_opts.min_relay_feerate) {
97 [ + - ]: 42 : MockMempoolMinFee(target_feerate, *node.mempool);
98 : : }
99 [ + - + - ]: 153 : std::unique_ptr<CWallet> wallet_ptr{std::make_unique<CWallet>(node.chain.get(), "", CreateMockableWalletDatabase())};
100 [ + - ]: 153 : CWallet& wallet{*wallet_ptr};
101 : 153 : {
102 [ + - ]: 153 : LOCK(wallet.cs_wallet);
103 [ - + + - ]: 306 : wallet.SetLastBlockProcessed(chainstate->m_chain.Height(), chainstate->m_chain.Tip()->GetBlockHash());
104 : 0 : }
105 : :
106 [ + + ]: 153 : if (fuzzed_data_provider.ConsumeBool()) {
107 : 17 : wallet.m_fallback_fee = CFeeRate{ConsumeMoney(fuzzed_data_provider, /*max=*/COIN)};
108 : : }
109 : :
110 [ + + ]: 153 : if (fuzzed_data_provider.ConsumeBool()) {
111 : 12 : wallet.m_discard_rate = CFeeRate{ConsumeMoney(fuzzed_data_provider, /*max=*/COIN)};
112 : : }
113 [ + - ]: 153 : (void)GetDiscardRate(wallet);
114 : :
115 : 153 : const auto tx_bytes{fuzzed_data_provider.ConsumeIntegralInRange(0, std::numeric_limits<int32_t>::max())};
116 [ + + ]: 153 : if (fuzzed_data_provider.ConsumeBool()) {
117 : 14 : wallet.m_min_fee = CFeeRate{ConsumeMoney(fuzzed_data_provider, /*max=*/COIN)};
118 : : }
119 : :
120 [ + - ]: 153 : (void)GetRequiredFee(wallet, tx_bytes);
121 [ + - ]: 153 : (void)GetRequiredFeeRate(wallet);
122 : :
123 [ + - ]: 153 : CCoinControl coin_control;
124 [ + + ]: 153 : if (fuzzed_data_provider.ConsumeBool()) {
125 [ - + ]: 11 : coin_control.m_feerate = CFeeRate{ConsumeMoney(fuzzed_data_provider, /*max=*/COIN)};
126 : : }
127 [ + + ]: 153 : if (fuzzed_data_provider.ConsumeBool()) {
128 : 9 : coin_control.m_confirm_target = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, 999'000);
129 : : }
130 [ + + ]: 153 : if (fuzzed_data_provider.ConsumeBool()) {
131 [ + + ]: 13 : coin_control.m_fee_mode = fuzzed_data_provider.ConsumeBool() ? FeeEstimateMode::CONSERVATIVE : FeeEstimateMode::ECONOMICAL;
132 : : }
133 : 153 : FeeReason fee_reason{FeeReason::FEE_RATE_ESTIMATOR};
134 [ + + ]: 153 : if (fuzzed_data_provider.ConsumeBool()) {
135 : 6 : fee_reason = fuzzed_data_provider.PickValueInArray({FeeReason::FEE_RATE_ESTIMATOR, FeeReason::MEMPOOL_MIN, FeeReason::USER_SPECIFIED, FeeReason::FALLBACK, FeeReason::REQUIRED});
136 : : }
137 : 153 : std::optional<int> returned_target;
138 [ + + ]: 153 : if (fuzzed_data_provider.ConsumeBool()) {
139 : 5 : returned_target = fuzzed_data_provider.ConsumeIntegralInRange<int>(1, 999'000);
140 : : }
141 : 153 : MinimumFeeRateResult min_fee_rate{
142 [ + - ]: 153 : CFeeRate{ConsumeMoney(fuzzed_data_provider, /*max=*/COIN)},
143 : : fee_reason,
144 [ + - ]: 153 : returned_target};
145 [ + - ]: 153 : (void)GetMinimumFeeRate(wallet, coin_control);
146 [ + - ]: 153 : (void)GetMinimumFee(min_fee_rate, tx_bytes);
147 [ + - ]: 459 : }
148 : : } // namespace
149 : : } // namespace wallet
|