Bitcoin Core 28.0.0
P2P Digital Currency
Loading...
Searching...
No Matches
descriptor_parse.cpp
Go to the documentation of this file.
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>
11#include <util/chaintype.h>
12#include <util/strencodings.h>
13
16
18static void TestDescriptor(const Descriptor& desc, FlatSigningProvider& sig_provider, std::string& dummy)
19{
20 // Trivial helpers.
21 (void)desc.IsRange();
22 const bool is_solvable{desc.IsSolvable()};
23 (void)desc.IsSingleType();
24 (void)desc.GetOutputType();
25
26 // Serialization to string representation.
27 (void)desc.ToString();
28 (void)desc.ToPrivateString(sig_provider, dummy);
29 (void)desc.ToNormalizedString(sig_provider, dummy);
30
31 // Serialization to Script.
32 DescriptorCache cache;
33 std::vector<CScript> out_scripts;
34 (void)desc.Expand(0, sig_provider, out_scripts, sig_provider, &cache);
35 (void)desc.ExpandPrivate(0, sig_provider, sig_provider);
36 (void)desc.ExpandFromCache(0, cache, out_scripts, sig_provider);
37
38 // If we could serialize to script we must be able to infer using the same provider.
39 if (!out_scripts.empty()) {
40 assert(InferDescriptor(out_scripts.back(), sig_provider));
41
42 // The ScriptSize() must match the size of the serialized Script. (ScriptSize() is set for all descs but 'combo()'.)
43 const bool is_combo{!desc.IsSingleType()};
44 assert(is_combo || desc.ScriptSize() == out_scripts.back().size());
45 }
46
47 const auto max_sat_maxsig{desc.MaxSatisfactionWeight(true)};
48 const auto max_sat_nonmaxsig{desc.MaxSatisfactionWeight(true)};
49 const auto max_elems{desc.MaxSatisfactionElems()};
50 // We must be able to estimate the max satisfaction size for any solvable descriptor (but combo).
51 const bool is_nontop_or_nonsolvable{!is_solvable || !desc.GetOutputType()};
52 const bool is_input_size_info_set{max_sat_maxsig && max_sat_nonmaxsig && max_elems};
53 assert(is_input_size_info_set || is_nontop_or_nonsolvable);
54}
55
61
67
69{
70 // Key derivation is expensive. Deriving deep derivation paths take a lot of compute and we'd
71 // rather spend time elsewhere in this target, like on the actual descriptor syntax. So rule
72 // out strings which could correspond to a descriptor containing a too large derivation path.
73 if (HasDeepDerivPath(buffer)) return;
74
75 // Some fragments can take a virtually unlimited number of sub-fragments (thresh, multi_a) but
76 // may perform quadratic operations on them. Limit the number of sub-fragments per fragment.
77 if (HasTooManySubFrag(buffer)) return;
78
79 // The script building logic performs quadratic copies in the number of nested wrappers. Limit
80 // the number of nested wrappers per fragment.
81 if (HasTooManyWrappers(buffer)) return;
82
83 const std::string mocked_descriptor{buffer.begin(), buffer.end()};
84 if (const auto descriptor = MOCKED_DESC_CONVERTER.GetDescriptor(mocked_descriptor)) {
85 FlatSigningProvider signing_provider;
86 std::string error;
87 const auto desc = Parse(*descriptor, signing_provider, error);
88 if (desc) TestDescriptor(*desc, signing_provider, error);
89 }
90}
91
93{
94 // See comments above for rationales.
95 if (HasDeepDerivPath(buffer)) return;
96 if (HasTooManySubFrag(buffer)) return;
97 if (HasTooManyWrappers(buffer)) return;
98
99 const std::string descriptor(buffer.begin(), buffer.end());
100 FlatSigningProvider signing_provider;
101 std::string error;
102 for (const bool require_checksum : {true, false}) {
103 const auto desc = Parse(descriptor, signing_provider, error, require_checksum);
104 if (desc) TestDescriptor(*desc, signing_provider, error);
105 }
106}
ECC_Context ecc_context
void SelectParams(const ChainType chain)
Sets the params returned by Params() to those for the given chain type.
Cache for single descriptor's derived extended pubkeys.
Definition descriptor.h:19
RAII class initializing and deinitializing global state for elliptic curve support.
Definition key.h:322
Converts a mocked descriptor string to a valid one.
Definition descriptor.h:21
void Init()
When initializing the target, populate the list of keys.
std::optional< std::string > GetDescriptor(std::string_view mocked_desc) const
Get an actual descriptor string from a descriptor string whose keys were mocked.
static UniValue Parse(std::string_view raw)
Parse string to UniValue or throw runtime_error if string contains invalid JSON.
Definition client.cpp:321
void initialize_mocked_descriptor_parse()
static void TestDescriptor(const Descriptor &desc, FlatSigningProvider &sig_provider, std::string &dummy)
Test a successfully parsed descriptor.
void initialize_descriptor_parse()
MockedDescriptorConverter MOCKED_DESC_CONVERTER
The converter of mocked descriptors, needs to be initialized when the target is.
#define FUZZ_TARGET(...)
Definition fuzz.h:35
std::unique_ptr< Descriptor > InferDescriptor(const CScript &script, const SigningProvider &provider)
Find a descriptor for the specified script, using information from provider where possible.
Interface for parsed descriptor objects.
Definition descriptor.h:98
virtual bool IsSingleType() const =0
Whether this descriptor will return one scriptPubKey or multiple (aka is or is not combo)
virtual std::optional< int64_t > MaxSatisfactionElems() const =0
Get the maximum size number of stack elements for satisfying this descriptor.
virtual bool ToNormalizedString(const SigningProvider &provider, std::string &out, const DescriptorCache *cache=nullptr) const =0
Convert the descriptor to a normalized string.
virtual std::optional< int64_t > MaxSatisfactionWeight(bool use_max_sig) const =0
Get the maximum size of a satisfaction for this descriptor, in weight units.
virtual std::string ToString(bool compat_format=false) const =0
Convert the descriptor back to a string, undoing parsing.
virtual std::optional< OutputType > GetOutputType() const =0
virtual bool Expand(int pos, const SigningProvider &provider, std::vector< CScript > &output_scripts, FlatSigningProvider &out, DescriptorCache *write_cache=nullptr) const =0
Expand a descriptor at a specified position.
virtual bool IsRange() const =0
Whether the expansion of this descriptor depends on the position.
virtual std::optional< int64_t > ScriptSize() const =0
Get the size of the scriptPubKey for this descriptor.
virtual bool IsSolvable() const =0
Whether this descriptor has all information about signing ignoring lack of private keys.
virtual void ExpandPrivate(int pos, const SigningProvider &provider, FlatSigningProvider &out) const =0
Expand the private key for a descriptor at a specified position, if possible.
virtual bool ToPrivateString(const SigningProvider &provider, std::string &out) const =0
Convert the descriptor to a private string.
virtual bool ExpandFromCache(int pos, const DescriptorCache &read_cache, std::vector< CScript > &output_scripts, FlatSigningProvider &out) const =0
Expand a descriptor at a specified position using cached expansion data.
bool HasTooManySubFrag(const FuzzBufferType &buff, const int max_subs, const size_t max_nested_subs)
Whether the buffer, if it represents a valid descriptor, contains a fragment with more sub-fragments ...
bool HasTooManyWrappers(const FuzzBufferType &buff, const int max_wrappers)
Whether the buffer, if it represents a valid descriptor, contains a fragment with more wrappers than ...
bool HasDeepDerivPath(const FuzzBufferType &buff, const int max_depth)
Whether the buffer, if it represents a valid descriptor, contains a derivation path deeper than a giv...
assert(!tx.IsCoinBase())