LCOV - code coverage report
Current view: top level - src/script - descriptor.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 147 284 51.8 %
Date: 2025-02-23 09:33:43 Functions: 21 50 42.0 %

          Line data    Source code
       1             : // Copyright (c) 2018 The Bitcoin Core developers
       2             : // Copyright (c) 2022 The PIVX Core developers
       3             : // Distributed under the MIT software license, see the accompanying
       4             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       5             : 
       6             : #include <script/descriptor.h>
       7             : 
       8             : #include <key_io.h>
       9             : #include <pubkey.h>
      10             : #include <script/script.h>
      11             : #include <script/standard.h>
      12             : 
      13             : #include <span.h>
      14             : #include <util/system.h>
      15             : #include <utilstrencodings.h>
      16             : 
      17             : #include <memory>
      18             : #include <string>
      19             : #include <vector>
      20             : 
      21             : namespace {
      22             : 
      23             : ////////////////////////////////////////////////////////////////////////////
      24             : // Internal representation                                                //
      25             : ////////////////////////////////////////////////////////////////////////////
      26             : 
      27             : typedef std::vector<uint32_t> KeyPath;
      28             : 
      29           0 : std::string FormatKeyPath(const KeyPath& path)
      30             : {
      31           0 :     std::string ret;
      32           0 :     for (auto i : path) {
      33           0 :         ret += strprintf("/%i", (i << 1) >> 1);
      34           0 :         if (i >> 31) ret += '\'';
      35             :     }
      36           0 :     return ret;
      37             : }
      38             : 
      39             : /** Interface for public key objects in descriptors. */
      40          32 : struct PubkeyProvider
      41             : {
      42           0 :     virtual ~PubkeyProvider() = default;
      43             : 
      44             :     /** Derive a public key. */
      45             :     virtual bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& out) const = 0;
      46             : 
      47             :     /** Whether this represent multiple public keys at different positions. */
      48             :     virtual bool IsRange() const = 0;
      49             : 
      50             :     /** Get the size of the generated public key(s) in bytes (33 or 65). */
      51             :     virtual size_t GetSize() const = 0;
      52             : 
      53             :     /** Get the descriptor string form. */
      54             :     virtual std::string ToString() const = 0;
      55             : 
      56             :     /** Get the descriptor string form including private data (if available in arg). */
      57             :     virtual bool ToPrivateString(const SigningProvider& arg, std::string& out) const = 0;
      58             : };
      59             : 
      60             : /** An object representing a parsed constant public key in a descriptor. */
      61           0 : class ConstPubkeyProvider final : public PubkeyProvider
      62             : {
      63             :     CPubKey m_pubkey;
      64             : 
      65             : public:
      66           7 :     explicit ConstPubkeyProvider(const CPubKey& pubkey) : m_pubkey(pubkey) {}
      67           7 :     bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& out) const override
      68             :     {
      69           7 :         out = m_pubkey;
      70           7 :         return true;
      71             :     }
      72           7 :     bool IsRange() const override { return false; }
      73           0 :     size_t GetSize() const override { return m_pubkey.size(); }
      74           0 :     std::string ToString() const override { return HexStr(m_pubkey); }
      75           0 :     bool ToPrivateString(const SigningProvider& arg, std::string& ret) const override
      76             :     {
      77           0 :         CKey key;
      78           0 :         if (!arg.GetKey(m_pubkey.GetID(), key)) return false;
      79           0 :         ret = KeyIO::EncodeSecret(key);
      80           0 :         return true;
      81             :     }
      82             : };
      83             : 
      84             : enum class DeriveType {
      85             :     NO,
      86             :     UNHARDENED,
      87             :     HARDENED,
      88             : };
      89             : 
      90             : /** An object representing a parsed extended public key in a descriptor. */
      91             : class BIP32PubkeyProvider final : public PubkeyProvider
      92             : {
      93             :     CExtPubKey m_extkey;
      94             :     KeyPath m_path;
      95             :     DeriveType m_derive;
      96             : 
      97        9012 :     bool GetExtKey(const SigningProvider& arg, CExtKey& ret) const
      98             :     {
      99       18024 :         CKey key;
     100        9012 :         if (!arg.GetKey(m_extkey.pubkey.GetID(), key)) return false;
     101        9012 :         ret.nDepth = m_extkey.nDepth;
     102        9012 :         std::copy(m_extkey.vchFingerprint, m_extkey.vchFingerprint + 4, ret.vchFingerprint);
     103        9012 :         ret.nChild = m_extkey.nChild;
     104        9012 :         ret.chaincode = m_extkey.chaincode;
     105        9012 :         ret.key = key;
     106             :         return true;
     107             :     }
     108             : 
     109       15020 :     bool IsHardened() const
     110             :     {
     111       15020 :         if (m_derive == DeriveType::HARDENED) return true;
     112       21046 :         for (auto entry : m_path) {
     113       15038 :             if (entry >> 31) return true;
     114             :         }
     115        6008 :         return false;
     116             :     }
     117             : 
     118             : public:
     119          25 :     BIP32PubkeyProvider(const CExtPubKey& extkey, KeyPath path, DeriveType derive) : m_extkey(extkey), m_path(std::move(path)), m_derive(derive) {}
     120          25 :     bool IsRange() const override { return m_derive != DeriveType::NO; }
     121           0 :     size_t GetSize() const override { return 33; }
     122       15020 :     bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& out) const override
     123             :     {
     124       15020 :         if (IsHardened()) {
     125       18024 :             CExtKey key;
     126        9012 :             if (!GetExtKey(arg, key)) return false;
     127       27045 :             for (auto entry : m_path) {
     128       18033 :                 key.Derive(key, entry);
     129             :             }
     130        9012 :             if (m_derive == DeriveType::UNHARDENED) key.Derive(key, pos);
     131        9012 :             if (m_derive == DeriveType::HARDENED) key.Derive(key, pos | 0x80000000UL);
     132        9012 :             out = key.Neuter().pubkey;
     133             :         } else {
     134             :             // TODO: optimize by caching
     135        6008 :             CExtPubKey key = m_extkey;
     136       18030 :             for (auto entry : m_path) {
     137       12022 :                 key.Derive(key, entry);
     138             :             }
     139        6008 :             if (m_derive == DeriveType::UNHARDENED) key.Derive(key, pos);
     140        6008 :             assert(m_derive != DeriveType::HARDENED);
     141        6008 :             out = key.pubkey;
     142             :         }
     143             :         return true;
     144             :     }
     145           0 :     std::string ToString() const override
     146             :     {
     147           0 :         std::string ret = KeyIO::EncodeExtPubKey(m_extkey) + FormatKeyPath(m_path);
     148           0 :         if (IsRange()) {
     149           0 :             ret += "/*";
     150           0 :             if (m_derive == DeriveType::HARDENED) ret += '\'';
     151             :         }
     152           0 :         return ret;
     153             :     }
     154           0 :     bool ToPrivateString(const SigningProvider& arg, std::string& out) const override
     155             :     {
     156           0 :         CExtKey key;
     157           0 :         if (!GetExtKey(arg, key)) return false;
     158           0 :         out = KeyIO::EncodeExtKey(key) + FormatKeyPath(m_path);
     159           0 :         if (IsRange()) {
     160           0 :             out += "/*";
     161           0 :             if (m_derive == DeriveType::HARDENED) out += '\'';
     162             :         }
     163             :         return true;
     164             :     }
     165             : };
     166             : 
     167             : /** A parsed addr(A) descriptor. */
     168           0 : class AddressDescriptor final : public Descriptor
     169             : {
     170             :     CTxDestination m_destination;
     171             : 
     172             : public:
     173           5 :     explicit AddressDescriptor(CTxDestination destination) : m_destination(std::move(destination)) {}
     174             : 
     175           5 :     bool IsRange() const override { return false; }
     176           0 :     std::string ToString() const override { return "addr(" + EncodeDestination(m_destination) + ")"; }
     177           0 :     bool ToPrivateString(const SigningProvider& arg, std::string& out) const override { out = ToString(); return true; }
     178           5 :     bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
     179             :     {
     180          15 :         output_scripts = std::vector<CScript>{GetScriptForDestination(m_destination)};
     181           5 :         return true;
     182             :     }
     183             : };
     184             : 
     185             : /** A parsed raw(H) descriptor. */
     186             : class RawDescriptor final : public Descriptor
     187             : {
     188             :     CScript m_script;
     189             : 
     190             : public:
     191           0 :     explicit RawDescriptor(CScript script) : m_script(std::move(script)) {}
     192             : 
     193           0 :     bool IsRange() const override { return false; }
     194           0 :     std::string ToString() const override { return "raw(" + HexStr(m_script) + ")"; }
     195           0 :     bool ToPrivateString(const SigningProvider& arg, std::string& out) const override { out = ToString(); return true; }
     196           0 :     bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
     197             :     {
     198           0 :         output_scripts = std::vector<CScript>{m_script};
     199           0 :         return true;
     200             :     }
     201             : };
     202             : 
     203             : /** A parsed pk(P), pkh(P), or wpkh(P) descriptor. */
     204           0 : class SingleKeyDescriptor final : public Descriptor
     205             : {
     206             :     const std::function<CScript(const CPubKey&)> m_script_fn;
     207             :     const std::string m_fn_name;
     208             :     std::unique_ptr<PubkeyProvider> m_provider;
     209             : 
     210             : public:
     211           6 :     SingleKeyDescriptor(std::unique_ptr<PubkeyProvider> prov, const std::function<CScript(const CPubKey&)>& fn, const std::string& name) : m_script_fn(fn), m_fn_name(name), m_provider(std::move(prov)) {}
     212             : 
     213           3 :     bool IsRange() const override { return m_provider->IsRange(); }
     214           0 :     std::string ToString() const override { return m_fn_name + "(" + m_provider->ToString() + ")"; }
     215           0 :     bool ToPrivateString(const SigningProvider& arg, std::string& out) const override
     216             :     {
     217           0 :         std::string ret;
     218           0 :         if (!m_provider->ToPrivateString(arg, ret)) return false;
     219           0 :         out = m_fn_name + "(" + std::move(ret) + ")";
     220           0 :         return true;
     221             :     }
     222           3 :     bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
     223             :     {
     224           3 :         CPubKey key;
     225           3 :         if (!m_provider->GetPubKey(pos, arg, key)) return false;
     226           9 :         output_scripts = std::vector<CScript>{m_script_fn(key)};
     227           3 :         out.pubkeys.emplace(key.GetID(), std::move(key));
     228           3 :         return true;
     229             :     }
     230             : };
     231             : 
     232           6 : CScript P2PKHGetScript(const CPubKey& pubkey) { return GetScriptForDestination(pubkey.GetID()); }
     233           0 : CScript P2PKGetScript(const CPubKey& pubkey) { return GetScriptForRawPubKey(pubkey); }
     234             : 
     235             : /** A parsed multi(...) descriptor. */
     236             : class MultisigDescriptor : public Descriptor
     237             : {
     238             :     int m_threshold;
     239             :     std::vector<std::unique_ptr<PubkeyProvider>> m_providers;
     240             : 
     241             : public:
     242           0 :     MultisigDescriptor(int threshold, std::vector<std::unique_ptr<PubkeyProvider>> providers) : m_threshold(threshold), m_providers(std::move(providers)) {}
     243             : 
     244           0 :     bool IsRange() const override
     245             :     {
     246           0 :         for (const auto& p : m_providers) {
     247           0 :             if (p->IsRange()) return true;
     248             :         }
     249           0 :         return false;
     250             :     }
     251             : 
     252           0 :     std::string ToString() const override
     253             :     {
     254           0 :         std::string ret = strprintf("multi(%i", m_threshold);
     255           0 :         for (const auto& p : m_providers) {
     256           0 :             ret += "," + p->ToString();
     257             :         }
     258           0 :         return std::move(ret) + ")";
     259             :     }
     260             : 
     261           0 :     bool ToPrivateString(const SigningProvider& arg, std::string& out) const override
     262             :     {
     263           0 :         std::string ret = strprintf("multi(%i", m_threshold);
     264           0 :         for (const auto& p : m_providers) {
     265           0 :             std::string sub;
     266           0 :             if (!p->ToPrivateString(arg, sub)) return false;
     267           0 :             ret += "," + std::move(sub);
     268             :         }
     269           0 :         out = std::move(ret) + ")";
     270           0 :         return true;
     271             :     }
     272             : 
     273           0 :     bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
     274             :     {
     275           0 :         std::vector<CPubKey> pubkeys;
     276           0 :         pubkeys.reserve(m_providers.size());
     277           0 :         for (const auto& p : m_providers) {
     278           0 :             CPubKey key;
     279           0 :             if (!p->GetPubKey(pos, arg, key)) return false;
     280           0 :             pubkeys.push_back(key);
     281             :         }
     282           0 :         for (const CPubKey& key : pubkeys) {
     283           0 :             out.pubkeys.emplace(key.GetID(), std::move(key));
     284             :         }
     285           0 :         output_scripts = std::vector<CScript>{GetScriptForMultisig(m_threshold, pubkeys)};
     286           0 :         return true;
     287             :     }
     288             : };
     289             : 
     290             : /** A parsed sh(S) or wsh(S) descriptor. */
     291             : class ConvertorDescriptor : public Descriptor
     292             : {
     293             :     const std::function<CScript(const CScript&)> m_convert_fn;
     294             :     const std::string m_fn_name;
     295             :     std::unique_ptr<Descriptor> m_descriptor;
     296             : 
     297             : public:
     298           0 :     ConvertorDescriptor(std::unique_ptr<Descriptor> descriptor, const std::function<CScript(const CScript&)>& fn, const std::string& name) : m_convert_fn(fn), m_fn_name(name), m_descriptor(std::move(descriptor)) {}
     299             : 
     300           0 :     bool IsRange() const override { return m_descriptor->IsRange(); }
     301           0 :     std::string ToString() const override { return m_fn_name + "(" + m_descriptor->ToString() + ")"; }
     302           0 :     bool ToPrivateString(const SigningProvider& arg, std::string& out) const override
     303             :     {
     304           0 :         std::string ret;
     305           0 :         if (!m_descriptor->ToPrivateString(arg, ret)) return false;
     306           0 :         out = m_fn_name + "(" + std::move(ret) + ")";
     307           0 :         return true;
     308             :     }
     309           0 :     bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
     310             :     {
     311           0 :         std::vector<CScript> sub;
     312           0 :         if (!m_descriptor->Expand(pos, arg, sub, out)) return false;
     313           0 :         output_scripts.clear();
     314           0 :         for (const auto& script : sub) {
     315           0 :             CScriptID id(script);
     316           0 :             out.scripts.emplace(CScriptID(script), script);
     317           0 :             output_scripts.push_back(m_convert_fn(script));
     318             :         }
     319           0 :         return true;
     320             :     }
     321             : };
     322             : 
     323           0 : CScript ConvertP2SH(const CScript& script) { return GetScriptForDestination(CScriptID(script)); }
     324             : 
     325             : /** A parsed combo(P) descriptor. */
     326             : class ComboDescriptor final : public Descriptor
     327             : {
     328             :     std::unique_ptr<PubkeyProvider> m_provider;
     329             : 
     330             : public:
     331          29 :     explicit ComboDescriptor(std::unique_ptr<PubkeyProvider> provider) : m_provider(std::move(provider)) {}
     332             : 
     333          29 :     bool IsRange() const override { return m_provider->IsRange(); }
     334           0 :     std::string ToString() const override { return "combo(" + m_provider->ToString() + ")"; }
     335           0 :     bool ToPrivateString(const SigningProvider& arg, std::string& out) const override
     336             :     {
     337           0 :         std::string ret;
     338           0 :         if (!m_provider->ToPrivateString(arg, ret)) return false;
     339           0 :         out = "combo(" + std::move(ret) + ")";
     340           0 :         return true;
     341             :     }
     342       15024 :     bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
     343             :     {
     344       15024 :         CPubKey key;
     345       15024 :         if (!m_provider->GetPubKey(pos, arg, key)) return false;
     346       15024 :         CKeyID keyid = key.GetID();
     347       15024 :         {
     348       15024 :             CScript p2pk = GetScriptForRawPubKey(key);
     349       30048 :             CScript p2pkh = GetScriptForDestination(keyid);
     350       60096 :             output_scripts = std::vector<CScript>{std::move(p2pk), std::move(p2pkh)};
     351       15024 :             out.pubkeys.emplace(keyid, key);
     352             :         }
     353       15024 :         return true;
     354             :     }
     355             : };
     356             : 
     357             : ////////////////////////////////////////////////////////////////////////////
     358             : // Parser                                                                 //
     359             : ////////////////////////////////////////////////////////////////////////////
     360             : 
     361             : enum class ParseScriptContext {
     362             :     TOP,
     363             :     P2SH,
     364             : };
     365             : 
     366             : /** Parse a constant. If successful, sp is updated to skip the constant and return true. */
     367           0 : bool Const(const std::string& str, Span<const char>& sp)
     368             : {
     369           0 :     if ((size_t)sp.size() >= str.size() && std::equal(str.begin(), str.end(), sp.begin())) {
     370           0 :         sp = sp.subspan(str.size());
     371           0 :         return true;
     372             :     }
     373             :     return false;
     374             : }
     375             : 
     376             : /** Parse a function call. If successful, sp is updated to be the function's argument(s). */
     377         123 : bool Func(const std::string& str, Span<const char>& sp)
     378             : {
     379         123 :     if ((size_t)sp.size() >= str.size() + 2 && sp[str.size()] == '(' && sp[sp.size() - 1] == ')' && std::equal(str.begin(), str.end(), sp.begin())) {
     380          37 :         sp = sp.subspan(str.size() + 1, sp.size() - str.size() - 2);
     381          37 :         return true;
     382             :     }
     383             :     return false;
     384             : }
     385             : 
     386             : /** Return the expression that sp begins with, and update sp to skip it. */
     387          37 : Span<const char> Expr(Span<const char>& sp)
     388             : {
     389          37 :     int level = 0;
     390          37 :     auto it = sp.begin();
     391        3912 :     while (it != sp.end()) {
     392        3875 :         if (*it == '(') {
     393          37 :             ++level;
     394        3838 :         } else if (level && *it == ')') {
     395          37 :             --level;
     396        3801 :         } else if (level == 0 && (*it == ')' || *it == ',')) {
     397             :             break;
     398             :         }
     399        3875 :         ++it;
     400             :     }
     401          37 :     Span<const char> ret = sp.first(it - sp.begin());
     402          37 :     sp = sp.subspan(it - sp.begin());
     403          37 :     return ret;
     404             : }
     405             : 
     406             : /** Split a string on every instance of sep, returning a vector. */
     407          32 : std::vector<Span<const char>> Split(const Span<const char>& sp, char sep)
     408             : {
     409          32 :     std::vector<Span<const char>> ret;
     410          32 :     auto it = sp.begin();
     411          32 :     auto start = it;
     412        3489 :     while (it != sp.end()) {
     413        3457 :         if (*it == sep) {
     414          75 :             ret.emplace_back(start, it);
     415          75 :             start = it + 1;
     416             :         }
     417        3457 :         ++it;
     418             :     }
     419          32 :     ret.emplace_back(start, it);
     420          32 :     return ret;
     421             : }
     422             : 
     423             : /** Parse a key path, being passed a split list of elements (the first element is ignored). */
     424          25 : bool ParseKeyPath(const std::vector<Span<const char>>& split, KeyPath& out)
     425             : {
     426          90 :     for (size_t i = 1; i < split.size(); ++i) {
     427          65 :         Span<const char> elem = split[i];
     428          65 :         bool hardened = false;
     429          65 :         if (elem.size() > 0 && (elem[elem.size() - 1] == '\'' || elem[elem.size() - 1] == 'h')) {
     430          26 :             elem = elem.first(elem.size() - 1);
     431          26 :             hardened = true;
     432             :         }
     433          65 :         uint32_t p;
     434         195 :         if (!ParseUInt32(std::string(elem.begin(), elem.end()), &p) || p > 0x7FFFFFFFUL) return false;
     435          65 :         out.push_back(p | (((uint32_t)hardened) << 31));
     436             :     }
     437             :     return true;
     438             : }
     439             : 
     440          32 : std::unique_ptr<PubkeyProvider> ParsePubkey(const Span<const char>& sp, bool permit_uncompressed, FlatSigningProvider& out)
     441             : {
     442          64 :     auto split = Split(sp, '/');
     443          96 :     std::string str(split[0].begin(), split[0].end());
     444          32 :     if (split.size() == 1) {
     445           7 :         if (IsHex(str)) {
     446           7 :             std::vector<unsigned char> data = ParseHex(str);
     447           7 :             CPubKey pubkey(data);
     448           7 :             if (pubkey.IsFullyValid() && (permit_uncompressed || pubkey.IsCompressed())) return std::make_unique<ConstPubkeyProvider>(pubkey);
     449             :         }
     450           0 :         CKey key = KeyIO::DecodeSecret(str);
     451           0 :         if (key.IsValid() && (permit_uncompressed || key.IsCompressed())) {
     452           0 :             CPubKey pubkey = key.GetPubKey();
     453           0 :             out.keys.emplace(pubkey.GetID(), key);
     454           0 :             return std::make_unique<ConstPubkeyProvider>(pubkey);
     455             :         }
     456             :     }
     457          57 :     CExtKey extkey = KeyIO::DecodeExtKey(str);
     458          25 :     CExtPubKey extpubkey = KeyIO::DecodeExtPubKey(str);
     459          25 :     if (!extkey.key.IsValid() && !extpubkey.pubkey.IsValid()) return nullptr;
     460          50 :     KeyPath path;
     461          25 :     DeriveType type = DeriveType::NO;
     462          25 :     if (split.back() == MakeSpan("*").first(1)) {
     463           6 :         split.pop_back();
     464           6 :         type = DeriveType::UNHARDENED;
     465          19 :     } else if (split.back() == MakeSpan("*'").first(2) || split.back() == MakeSpan("*h").first(2)) {
     466           4 :         split.pop_back();
     467           4 :         type = DeriveType::HARDENED;
     468             :     }
     469          50 :     if (!ParseKeyPath(split, path)) return nullptr;
     470          25 :     if (extkey.key.IsValid()) {
     471          20 :         extpubkey = extkey.Neuter();
     472          40 :         out.keys.emplace(extpubkey.pubkey.GetID(), extkey.key);
     473             :     }
     474          25 :     return std::make_unique<BIP32PubkeyProvider>(extpubkey, std::move(path), type);
     475             : }
     476             : 
     477             : /** Parse a script in a particular context. */
     478          37 : std::unique_ptr<Descriptor> ParseScript(Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out)
     479             : {
     480          37 :     auto expr = Expr(sp);
     481          37 :     if (Func("pk", expr)) {
     482           0 :         auto pubkey = ParsePubkey(expr, true, out);
     483           0 :         if (!pubkey) return nullptr;
     484           0 :         return std::make_unique<SingleKeyDescriptor>(std::move(pubkey), P2PKGetScript, "pk");
     485             :     }
     486          37 :     if (Func("pkh", expr)) {
     487           6 :         auto pubkey = ParsePubkey(expr, true, out);
     488           6 :         if (!pubkey) return nullptr;
     489           3 :         return std::make_unique<SingleKeyDescriptor>(std::move(pubkey), P2PKHGetScript, "pkh");
     490             :     }
     491          73 :     if (ctx == ParseScriptContext::TOP && Func("combo", expr)) {
     492          58 :         auto pubkey = ParsePubkey(expr, true, out);
     493          58 :         if (!pubkey) return nullptr;
     494          29 :         return std::make_unique<ComboDescriptor>(std::move(pubkey));
     495             :     }
     496           5 :     if (Func("multi", expr)) {
     497           0 :         auto threshold = Expr(expr);
     498           0 :         uint32_t thres;
     499           0 :         std::vector<std::unique_ptr<PubkeyProvider>> providers;
     500           0 :         if (!ParseUInt32(std::string(threshold.begin(), threshold.end()), &thres)) return nullptr;
     501             :         size_t script_size = 0;
     502           0 :         while (expr.size()) {
     503           0 :             if (!Const(",", expr)) return nullptr;
     504           0 :             auto arg = Expr(expr);
     505           0 :             auto pk = ParsePubkey(arg, true, out);
     506           0 :             if (!pk) return nullptr;
     507           0 :             script_size += pk->GetSize() + 1;
     508           0 :             providers.emplace_back(std::move(pk));
     509             :         }
     510           0 :         if (providers.size() < 1 || providers.size() > 16 || thres < 1 || thres > providers.size()) return nullptr;
     511           0 :         if (ctx == ParseScriptContext::TOP) {
     512           0 :             if (providers.size() > 3) return nullptr; // Not more than 3 pubkeys for raw multisig
     513             :         }
     514           0 :         if (ctx == ParseScriptContext::P2SH) {
     515           0 :             if (script_size + 3 > 520) return nullptr; // Enforce P2SH script size limit
     516             :         }
     517           0 :         return std::make_unique<MultisigDescriptor>(thres, std::move(providers));
     518             :     }
     519          15 :     if (ctx == ParseScriptContext::TOP && Func("sh", expr)) {
     520           0 :         auto desc = ParseScript(expr, ParseScriptContext::P2SH, out);
     521           0 :         if (!desc || expr.size()) return nullptr;
     522           0 :         return std::make_unique<ConvertorDescriptor>(std::move(desc), ConvertP2SH, "sh");
     523             :     }
     524          10 :     if (ctx == ParseScriptContext::TOP && Func("addr", expr)) {
     525          15 :         CTxDestination dest = DecodeDestination(std::string(expr.begin(), expr.end()));
     526          10 :         if (!IsValidDestination(dest)) return nullptr;
     527           5 :         return std::make_unique<AddressDescriptor>(std::move(dest));
     528             :     }
     529           0 :     if (ctx == ParseScriptContext::TOP && Func("raw", expr)) {
     530           0 :         std::string str(expr.begin(), expr.end());
     531           0 :         if (!IsHex(str)) return nullptr;
     532           0 :         auto bytes = ParseHex(str);
     533           0 :         return std::make_unique<RawDescriptor>(CScript(bytes.begin(), bytes.end()));
     534             :     }
     535          37 :     return nullptr;
     536             : }
     537             : 
     538             : } // namespace
     539             : 
     540          37 : std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out)
     541             : {
     542          37 :     Span<const char> sp(descriptor.data(), descriptor.size());
     543          37 :     auto ret = ParseScript(sp, ParseScriptContext::TOP, out);
     544          37 :     if (sp.size() == 0 && ret) return ret;
     545          37 :     return nullptr;
     546             : }

Generated by: LCOV version 1.14