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