Coverage Report

Created: 2026-06-09 19:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/root/bitcoin/src/test/fuzz/descriptor_parse.cpp
Line
Count
Source
1
// Copyright (c) 2009-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 <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()) {
  Branch (25:9): [True: 0, False: 0]
26
0
        assert(desc.IsRange() == *is_ranged);
  Branch (26:9): [True: 0, False: 0]
27
0
    } else {
28
0
        is_ranged = desc.IsRange();
29
0
    }
30
0
    if (is_solvable.has_value()) {
  Branch (30:9): [True: 0, False: 0]
31
0
        assert(desc.IsSolvable() == *is_solvable);
  Branch (31:9): [True: 0, False: 0]
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()) {
  Branch (49:9): [True: 0, False: 0]
50
0
        assert(InferDescriptor(out_scripts.back(), sig_provider));
  Branch (50:9): [True: 0, False: 0]
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());
  Branch (54:9): [True: 0, False: 0]
  Branch (54:9): [True: 0, False: 0]
  Branch (54:9): [True: 0, False: 0]
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()};
  Branch (61:41): [True: 0, False: 0]
  Branch (61:58): [True: 0, False: 0]
62
0
    const bool is_input_size_info_set{max_sat_maxsig && max_sat_nonmaxsig && max_elems};
  Branch (62:39): [True: 0, False: 0]
  Branch (62:57): [True: 0, False: 0]
  Branch (62:78): [True: 0, False: 0]
63
0
    assert(is_input_size_info_set || is_nontop_or_nonsolvable);
  Branch (63:5): [True: 0, False: 0]
  Branch (63:5): [True: 0, False: 0]
  Branch (63:5): [True: 0, False: 0]
64
65
0
    auto max_key_expr = desc.GetMaxKeyExpr();
66
0
    auto key_count = desc.GetKeyCount();
67
0
    assert((max_key_expr == 0 && key_count == 0) || max_key_expr + 1 == key_count);
  Branch (67:5): [True: 0, False: 0]
  Branch (67:5): [True: 0, False: 0]
  Branch (67:5): [True: 0, False: 0]
  Branch (67:5): [True: 0, False: 0]
68
0
}
69
70
void initialize_descriptor_parse()
71
0
{
72
0
    static ECC_Context ecc_context{};
73
0
    SelectParams(ChainType::MAIN);
74
0
}
75
76
void initialize_mocked_descriptor_parse()
77
0
{
78
0
    initialize_descriptor_parse();
79
0
    MOCKED_DESC_CONVERTER.Init();
80
0
}
81
82
FUZZ_TARGET(mocked_descriptor_parse, .init = initialize_mocked_descriptor_parse)
83
0
{
84
0
    const std::string mocked_descriptor{buffer.begin(), buffer.end()};
85
0
    if (const auto descriptor = MOCKED_DESC_CONVERTER.GetDescriptor(mocked_descriptor)) {
  Branch (85:20): [True: 0, False: 0]
86
0
        if (IsTooExpensive(MakeUCharSpan(*descriptor))) return;
  Branch (86:13): [True: 0, False: 0]
87
0
        FlatSigningProvider signing_provider;
88
0
        std::string error;
89
0
        const auto desc = Parse(*descriptor, signing_provider, error);
90
0
        std::optional<bool> is_ranged;
91
0
        std::optional<bool> is_solvable;
92
0
        for (const auto& d : desc) {
  Branch (92:28): [True: 0, False: 0]
93
0
            assert(d);
  Branch (93:13): [True: 0, False: 0]
94
0
            TestDescriptor(*d, signing_provider, error, is_ranged, is_solvable);
95
0
        }
96
0
    }
97
0
}
98
99
FUZZ_TARGET(descriptor_parse, .init = initialize_descriptor_parse)
100
0
{
101
0
    if (IsTooExpensive(buffer)) return;
  Branch (101:9): [True: 0, False: 0]
102
103
0
    const std::string descriptor(buffer.begin(), buffer.end());
104
0
    FlatSigningProvider signing_provider;
105
0
    std::string error;
106
0
    for (const bool require_checksum : {true, false}) {
  Branch (106:38): [True: 0, False: 0]
107
0
        const auto desc = Parse(descriptor, signing_provider, error, require_checksum);
108
0
        std::optional<bool> is_ranged;
109
0
        std::optional<bool> is_solvable;
110
0
        for (const auto& d : desc) {
  Branch (110:28): [True: 0, False: 0]
111
0
            assert(d);
  Branch (111:13): [True: 0, False: 0]
112
0
            TestDescriptor(*d, signing_provider, error, is_ranged, is_solvable);
113
0
        }
114
0
    }
115
0
}