LCOV - code coverage report
Current view: top level - src/test/fuzz - descriptor_parse.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 105 105 100.0 %
Date: 2023-10-05 15:40:34 Functions: 20 20 100.0 %
Branches: 108 168 64.3 %

           Branch data     Line data    Source code
       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 <util/chaintype.h>
      11                 :            : #include <util/strencodings.h>
      12                 :            : 
      13                 :            : //! Types are raw (un)compressed pubkeys, raw xonly pubkeys, raw privkeys (WIF), xpubs, xprvs.
      14                 :            : static constexpr uint8_t KEY_TYPES_COUNT{6};
      15                 :            : //! How many keys we'll generate in total.
      16                 :            : static constexpr size_t TOTAL_KEYS_GENERATED{std::numeric_limits<uint8_t>::max() + 1};
      17                 :            : 
      18                 :            : /**
      19                 :            :  * Converts a mocked descriptor string to a valid one. Every key in a mocked descriptor key is
      20                 :            :  * represented by 2 hex characters preceded by the '%' character. We parse the two hex characters
      21                 :            :  * as an index in a list of pre-generated keys. This list contains keys of the various types
      22                 :            :  * accepted in descriptor keys expressions.
      23                 :            :  */
      24                 :            : class MockedDescriptorConverter {
      25                 :            :     //! 256 keys of various types.
      26                 :            :     std::array<std::string, TOTAL_KEYS_GENERATED> keys_str;
      27                 :            : 
      28                 :            : public:
      29                 :            :     // We derive the type of key to generate from the 1-byte id parsed from hex.
      30                 :        428 :     bool IdIsCompPubKey(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 0; }
      31                 :        514 :     bool IdIsUnCompPubKey(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 1; }
      32                 :        256 :     bool IdIsXOnlyPubKey(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 2; }
      33                 :        127 :     bool IdIsConstPrivKey(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 3; }
      34                 :            :     bool IdIsXpub(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 4; }
      35                 :         84 :     bool IdIsXprv(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 5; }
      36                 :            : 
      37                 :            :     //! When initializing the target, populate the list of keys.
      38                 :          1 :     void Init() {
      39                 :            :         // The data to use as a private key or a seed for an xprv.
      40                 :          1 :         std::array<std::byte, 32> key_data{std::byte{1}};
      41                 :            :         // Generate keys of all kinds and store them in the keys array.
      42         [ +  + ]:        257 :         for (size_t i{0}; i < TOTAL_KEYS_GENERATED; i++) {
      43                 :        256 :             key_data[31] = std::byte(i);
      44                 :            : 
      45                 :            :             // If this is a "raw" key, generate a normal privkey. Otherwise generate
      46                 :            :             // an extended one.
      47   [ +  +  +  +  :        256 :             if (IdIsCompPubKey(i) || IdIsUnCompPubKey(i) || IdIsXOnlyPubKey(i) || IdIsConstPrivKey(i)) {
             +  +  +  + ]
      48                 :        172 :                 CKey privkey;
      49   [ +  -  +  -  :        172 :                 privkey.Set(UCharCast(key_data.begin()), UCharCast(key_data.end()), !IdIsUnCompPubKey(i));
             +  -  +  - ]
      50   [ +  -  +  +  :        172 :                 if (IdIsCompPubKey(i) || IdIsUnCompPubKey(i)) {
             +  -  +  + ]
      51         [ +  - ]:         86 :                     CPubKey pubkey{privkey.GetPubKey()};
      52   [ +  -  +  - ]:         86 :                     keys_str[i] = HexStr(pubkey);
      53   [ +  -  +  + ]:        172 :                 } else if (IdIsXOnlyPubKey(i)) {
      54   [ +  -  +  - ]:         43 :                     const XOnlyPubKey pubkey{privkey.GetPubKey()};
      55   [ +  -  +  - ]:         43 :                     keys_str[i] = HexStr(pubkey);
      56                 :         43 :                 } else {
      57         [ +  - ]:         43 :                     keys_str[i] = EncodeSecret(privkey);
      58                 :            :                 }
      59                 :        172 :             } else {
      60                 :         84 :                 CExtKey ext_privkey;
      61   [ +  -  +  - ]:         84 :                 ext_privkey.SetSeed(key_data);
      62   [ +  -  +  + ]:         84 :                 if (IdIsXprv(i)) {
      63         [ +  - ]:         42 :                     keys_str[i] = EncodeExtKey(ext_privkey);
      64                 :         42 :                 } else {
      65         [ +  - ]:         42 :                     const CExtPubKey ext_pubkey{ext_privkey.Neuter()};
      66         [ +  - ]:         42 :                     keys_str[i] = EncodeExtPubKey(ext_pubkey);
      67                 :            :                 }
      68                 :         84 :             }
      69                 :        256 :         }
      70                 :          1 :     }
      71                 :            : 
      72                 :            :     //! Parse an id in the keys vectors from a 2-characters hex string.
      73                 :       6632 :     std::optional<uint8_t> IdxFromHex(std::string_view hex_characters) const {
      74         [ -  + ]:       6632 :         if (hex_characters.size() != 2) return {};
      75                 :       6632 :         auto idx = ParseHex(hex_characters);
      76         [ +  + ]:       6632 :         if (idx.size() != 1) return {};
      77         [ +  - ]:       6619 :         return idx[0];
      78                 :       6632 :     }
      79                 :            : 
      80                 :            :     //! Get an actual descriptor string from a descriptor string whose keys were mocked.
      81                 :        881 :     std::optional<std::string> GetDescriptor(std::string_view mocked_desc) const {
      82                 :            :         // The smallest fragment would be "pk(%00)"
      83         [ +  + ]:        881 :         if (mocked_desc.size() < 7) return {};
      84                 :            : 
      85                 :            :         // The actual descriptor string to be returned.
      86                 :        880 :         std::string desc;
      87         [ +  - ]:        880 :         desc.reserve(mocked_desc.size());
      88                 :            : 
      89                 :            :         // Replace all occurrences of '%' followed by two hex characters with the corresponding key.
      90         [ +  + ]:     297484 :         for (size_t i = 0; i < mocked_desc.size();) {
      91         [ +  + ]:     296618 :             if (mocked_desc[i] == '%') {
      92         [ +  + ]:       6633 :                 if (i + 3 >= mocked_desc.size()) return {};
      93   [ +  -  +  -  :       6632 :                 if (const auto idx = IdxFromHex(mocked_desc.substr(i + 1, 2))) {
                   +  + ]
      94   [ +  -  +  - ]:       6619 :                     desc += keys_str[*idx];
      95                 :       6619 :                     i += 3;
      96                 :       6619 :                 } else {
      97                 :         13 :                     return {};
      98                 :            :                 }
      99                 :       6619 :             } else {
     100         [ +  - ]:     289985 :                 desc += mocked_desc[i++];
     101                 :            :             }
     102                 :            :         }
     103                 :            : 
     104         [ +  - ]:        866 :         return desc;
     105                 :        881 :     }
     106                 :            : };
     107                 :            : 
     108                 :            : //! The converter of mocked descriptors, needs to be initialized when the target is.
     109                 :        173 : MockedDescriptorConverter MOCKED_DESC_CONVERTER;
     110                 :            : 
     111                 :            : /** Test a successfully parsed descriptor. */
     112                 :        587 : static void TestDescriptor(const Descriptor& desc, FlatSigningProvider& sig_provider, std::string& dummy)
     113                 :            : {
     114                 :            :     // Trivial helpers.
     115                 :        587 :     (void)desc.IsRange();
     116                 :        587 :     const bool is_solvable{desc.IsSolvable()};
     117                 :        587 :     (void)desc.IsSingleType();
     118                 :        587 :     (void)desc.GetOutputType();
     119                 :            : 
     120                 :            :     // Serialization to string representation.
     121                 :        587 :     (void)desc.ToString();
     122                 :        587 :     (void)desc.ToPrivateString(sig_provider, dummy);
     123                 :        587 :     (void)desc.ToNormalizedString(sig_provider, dummy);
     124                 :            : 
     125                 :            :     // Serialization to Script.
     126                 :        587 :     DescriptorCache cache;
     127                 :        587 :     std::vector<CScript> out_scripts;
     128         [ +  - ]:        587 :     (void)desc.Expand(0, sig_provider, out_scripts, sig_provider, &cache);
     129         [ +  - ]:        587 :     (void)desc.ExpandPrivate(0, sig_provider, sig_provider);
     130         [ +  - ]:        587 :     (void)desc.ExpandFromCache(0, cache, out_scripts, sig_provider);
     131                 :            : 
     132                 :            :     // If we could serialize to script we must be able to infer using the same provider.
     133         [ +  + ]:        587 :     if (!out_scripts.empty()) {
     134   [ +  -  +  - ]:        550 :         assert(InferDescriptor(out_scripts.back(), sig_provider));
     135                 :            : 
     136                 :            :         // The ScriptSize() must match the size of the serialized Script. (ScriptSize() is set for all descs but 'combo()'.)
     137         [ +  - ]:        550 :         const bool is_combo{!desc.IsSingleType()};
     138   [ +  +  +  -  :        550 :         assert(is_combo || desc.ScriptSize() == out_scripts.back().size());
          +  -  +  -  -  
                      + ]
     139                 :        550 :     }
     140                 :            : 
     141         [ +  - ]:        587 :     const auto max_sat_maxsig{desc.MaxSatisfactionWeight(true)};
     142         [ +  - ]:        587 :     const auto max_sat_nonmaxsig{desc.MaxSatisfactionWeight(true)};
     143         [ +  - ]:        587 :     const auto max_elems{desc.MaxSatisfactionElems()};
     144                 :            :     // We must be able to estimate the max satisfaction size for any solvable descriptor (but combo).
     145   [ +  +  +  - ]:        587 :     const bool is_nontop_or_nonsolvable{!is_solvable || !desc.GetOutputType()};
     146   [ +  +  -  + ]:        587 :     const bool is_input_size_info_set{max_sat_maxsig && max_sat_nonmaxsig && max_elems};
     147   [ +  +  +  - ]:        587 :     assert(is_input_size_info_set || is_nontop_or_nonsolvable);
     148                 :        587 : }
     149                 :            : 
     150                 :          2 : void initialize_descriptor_parse()
     151                 :            : {
     152                 :          2 :     ECC_Start();
     153                 :          2 :     SelectParams(ChainType::MAIN);
     154                 :          2 : }
     155                 :            : 
     156                 :          1 : void initialize_mocked_descriptor_parse()
     157                 :            : {
     158                 :          1 :     initialize_descriptor_parse();
     159                 :          1 :     MOCKED_DESC_CONVERTER.Init();
     160                 :          1 : }
     161                 :            : 
     162   [ +  -  -  + ]:       1227 : FUZZ_TARGET(mocked_descriptor_parse, .init = initialize_mocked_descriptor_parse)
     163                 :            : {
     164         [ +  - ]:        881 :     const std::string mocked_descriptor{buffer.begin(), buffer.end()};
     165   [ +  -  +  + ]:       1747 :     if (const auto descriptor = MOCKED_DESC_CONVERTER.GetDescriptor(mocked_descriptor)) {
     166                 :        866 :         FlatSigningProvider signing_provider;
     167                 :        866 :         std::string error;
     168   [ +  -  +  - ]:        866 :         const auto desc = Parse(*descriptor, signing_provider, error);
     169   [ +  +  +  -  :        866 :         if (desc) TestDescriptor(*desc, signing_provider, error);
                   +  - ]
     170                 :        866 :     }
     171                 :        881 : }
     172                 :            : 
     173   [ +  -  -  + ]:       1396 : FUZZ_TARGET(descriptor_parse, .init = initialize_descriptor_parse)
     174                 :            : {
     175         [ +  - ]:       1050 :     const std::string descriptor(buffer.begin(), buffer.end());
     176                 :       1050 :     FlatSigningProvider signing_provider;
     177                 :       1050 :     std::string error;
     178         [ +  + ]:       3150 :     for (const bool require_checksum : {true, false}) {
     179         [ +  - ]:       2100 :         const auto desc = Parse(descriptor, signing_provider, error, require_checksum);
     180   [ +  +  +  -  :       2100 :         if (desc) TestDescriptor(*desc, signing_provider, error);
                   +  - ]
     181                 :       2100 :     }
     182                 :       1050 : }

Generated by: LCOV version 1.14