Coverage Report

Created: 2024-10-21 15:10

/root/bitcoin/src/test/fuzz/descriptor_parse.cpp
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) 2009-2021 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 <chainparams.h>
6
#include <key_io.h>
7
#include <pubkey.h>
8
#include <script/descriptor.h>
9
#include <test/fuzz/fuzz.h>
10
#include <test/fuzz/util/descriptor.h>
11
#include <util/chaintype.h>
12
#include <util/strencodings.h>
13
14
//! The converter of mocked descriptors, needs to be initialized when the target is.
15
MockedDescriptorConverter MOCKED_DESC_CONVERTER;
16
17
/** Test a successfully parsed descriptor. */
18
static void TestDescriptor(const Descriptor& desc, FlatSigningProvider& sig_provider, std::string& dummy, std::optional<bool>& is_ranged, std::optional<bool>& is_solvable)
19
0
{
20
    // Trivial helpers.
21
0
    (void)desc.IsRange();
22
0
    (void)desc.IsSingleType();
23
0
    (void)desc.GetOutputType();
24
25
0
    if (is_ranged.has_value()) {
26
0
        assert(desc.IsRange() == *is_ranged);
27
0
    } else {
28
0
        is_ranged = desc.IsRange();
29
0
    }
30
0
    if (is_solvable.has_value()) {
31
0
        assert(desc.IsSolvable() == *is_solvable);
32
0
    } else {
33
0
        is_solvable = desc.IsSolvable();
34
0
    }
35
36
    // Serialization to string representation.
37
0
    (void)desc.ToString();
38
0
    (void)desc.ToPrivateString(sig_provider, dummy);
39
0
    (void)desc.ToNormalizedString(sig_provider, dummy);
40
41
    // Serialization to Script.
42
0
    DescriptorCache cache;
43
0
    std::vector<CScript> out_scripts;
44
0
    (void)desc.Expand(0, sig_provider, out_scripts, sig_provider, &cache);
45
0
    (void)desc.ExpandPrivate(0, sig_provider, sig_provider);
46
0
    (void)desc.ExpandFromCache(0, cache, out_scripts, sig_provider);
47
48
    // If we could serialize to script we must be able to infer using the same provider.
49
0
    if (!out_scripts.empty()) {
50
0
        assert(InferDescriptor(out_scripts.back(), sig_provider));
51
52
        // The ScriptSize() must match the size of the serialized Script. (ScriptSize() is set for all descs but 'combo()'.)
53
0
        const bool is_combo{!desc.IsSingleType()};
54
0
        assert(is_combo || desc.ScriptSize() == out_scripts.back().size());
55
0
    }
56
57
0
    const auto max_sat_maxsig{desc.MaxSatisfactionWeight(true)};
58
0
    const auto max_sat_nonmaxsig{desc.MaxSatisfactionWeight(true)};
59
0
    const auto max_elems{desc.MaxSatisfactionElems()};
60
    // We must be able to estimate the max satisfaction size for any solvable descriptor (but combo).
61
0
    const bool is_nontop_or_nonsolvable{!*is_solvable || !desc.GetOutputType()};
62
0
    const bool is_input_size_info_set{max_sat_maxsig && max_sat_nonmaxsig && max_elems};
63
0
    assert(is_input_size_info_set || is_nontop_or_nonsolvable);
64
0
}
65
66
void initialize_descriptor_parse()
67
0
{
68
0
    static ECC_Context ecc_context{};
69
0
    SelectParams(ChainType::MAIN);
70
0
}
71
72
void initialize_mocked_descriptor_parse()
73
0
{
74
0
    initialize_descriptor_parse();
75
0
    MOCKED_DESC_CONVERTER.Init();
76
0
}
77
78
FUZZ_TARGET(mocked_descriptor_parse, .init = initialize_mocked_descriptor_parse)
79
0
{
80
    // Key derivation is expensive. Deriving deep derivation paths take a lot of compute and we'd
81
    // rather spend time elsewhere in this target, like on the actual descriptor syntax. So rule
82
    // out strings which could correspond to a descriptor containing a too large derivation path.
83
0
    if (HasDeepDerivPath(buffer)) return;
84
85
    // Some fragments can take a virtually unlimited number of sub-fragments (thresh, multi_a) but
86
    // may perform quadratic operations on them. Limit the number of sub-fragments per fragment.
87
0
    if (HasTooManySubFrag(buffer)) return;
88
89
    // The script building logic performs quadratic copies in the number of nested wrappers. Limit
90
    // the number of nested wrappers per fragment.
91
0
    if (HasTooManyWrappers(buffer)) return;
92
93
0
    const std::string mocked_descriptor{buffer.begin(), buffer.end()};
94
0
    if (const auto descriptor = MOCKED_DESC_CONVERTER.GetDescriptor(mocked_descriptor)) {
95
0
        FlatSigningProvider signing_provider;
96
0
        std::string error;
97
0
        const auto desc = Parse(*descriptor, signing_provider, error);
98
0
        std::optional<bool> is_ranged;
99
0
        std::optional<bool> is_solvable;
100
0
        for (const auto& d : desc) {
101
0
            assert(d);
102
0
            TestDescriptor(*d, signing_provider, error, is_ranged, is_solvable);
103
0
        }
104
0
    }
105
0
}
106
107
FUZZ_TARGET(descriptor_parse, .init = initialize_descriptor_parse)
108
0
{
109
    // See comments above for rationales.
110
0
    if (HasDeepDerivPath(buffer)) return;
111
0
    if (HasTooManySubFrag(buffer)) return;
112
0
    if (HasTooManyWrappers(buffer)) return;
113
114
0
    const std::string descriptor(buffer.begin(), buffer.end());
115
0
    FlatSigningProvider signing_provider;
116
0
    std::string error;
117
0
    for (const bool require_checksum : {true, false}) {
118
0
        const auto desc = Parse(descriptor, signing_provider, error, require_checksum);
119
0
        std::optional<bool> is_ranged;
120
0
        std::optional<bool> is_solvable;
121
0
        for (const auto& d : desc) {
122
0
            assert(d);
123
0
            TestDescriptor(*d, signing_provider, error, is_ranged, is_solvable);
124
0
        }
125
0
    }
126
0
}