LCOV - code coverage report
Current view: top level - src/test/fuzz/util - descriptor.cpp (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 69.3 % 88 61
Test Date: 2024-09-01 05:20:30 Functions: 83.3 % 6 5
Branches: 53.4 % 146 78

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 2023-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 <test/fuzz/util/descriptor.h>
       6                 :             : 
       7                 :             : #include <ranges>
       8                 :             : #include <stack>
       9                 :             : 
      10                 :           0 : void MockedDescriptorConverter::Init() {
      11                 :             :     // The data to use as a private key or a seed for an xprv.
      12                 :           0 :     std::array<std::byte, 32> key_data{std::byte{1}};
      13                 :             :     // Generate keys of all kinds and store them in the keys array.
      14         [ #  # ]:           0 :     for (size_t i{0}; i < TOTAL_KEYS_GENERATED; i++) {
      15                 :           0 :         key_data[31] = std::byte(i);
      16                 :             : 
      17                 :             :         // If this is a "raw" key, generate a normal privkey. Otherwise generate
      18                 :             :         // an extended one.
      19   [ #  #  #  #  :           0 :         if (IdIsCompPubKey(i) || IdIsUnCompPubKey(i) || IdIsXOnlyPubKey(i) || IdIsConstPrivKey(i)) {
             #  #  #  # ]
      20                 :           0 :             CKey privkey;
      21   [ #  #  #  # ]:           0 :             privkey.Set(key_data.begin(), key_data.end(), !IdIsUnCompPubKey(i));
      22   [ #  #  #  #  :           0 :             if (IdIsCompPubKey(i) || IdIsUnCompPubKey(i)) {
             #  #  #  # ]
      23         [ #  # ]:           0 :                 CPubKey pubkey{privkey.GetPubKey()};
      24   [ #  #  #  # ]:           0 :                 keys_str[i] = HexStr(pubkey);
      25   [ #  #  #  # ]:           0 :             } else if (IdIsXOnlyPubKey(i)) {
      26   [ #  #  #  # ]:           0 :                 const XOnlyPubKey pubkey{privkey.GetPubKey()};
      27   [ #  #  #  # ]:           0 :                 keys_str[i] = HexStr(pubkey);
      28                 :           0 :             } else {
      29         [ #  # ]:           0 :                 keys_str[i] = EncodeSecret(privkey);
      30                 :             :             }
      31                 :           0 :         } else {
      32                 :           0 :             CExtKey ext_privkey;
      33   [ #  #  #  # ]:           0 :             ext_privkey.SetSeed(key_data);
      34   [ #  #  #  # ]:           0 :             if (IdIsXprv(i)) {
      35         [ #  # ]:           0 :                 keys_str[i] = EncodeExtKey(ext_privkey);
      36                 :           0 :             } else {
      37         [ #  # ]:           0 :                 const CExtPubKey ext_pubkey{ext_privkey.Neuter()};
      38         [ #  # ]:           0 :                 keys_str[i] = EncodeExtPubKey(ext_pubkey);
      39                 :           0 :             }
      40                 :           0 :         }
      41                 :           0 :     }
      42                 :           0 : }
      43                 :             : 
      44                 :      419517 : std::optional<uint8_t> MockedDescriptorConverter::IdxFromHex(std::string_view hex_characters) const {
      45         [ -  + ]:      419517 :     if (hex_characters.size() != 2) return {};
      46                 :      419517 :     auto idx = ParseHex(hex_characters);
      47         [ +  + ]:      419517 :     if (idx.size() != 1) return {};
      48                 :      418399 :     return idx[0];
      49                 :      419517 : }
      50                 :             : 
      51                 :       23784 : std::optional<std::string> MockedDescriptorConverter::GetDescriptor(std::string_view mocked_desc) const {
      52                 :             :     // The smallest fragment would be "pk(%00)"
      53         [ +  + ]:       23784 :     if (mocked_desc.size() < 7) return {};
      54                 :             : 
      55                 :             :     // The actual descriptor string to be returned.
      56                 :       23319 :     std::string desc;
      57         [ +  - ]:       23319 :     desc.reserve(mocked_desc.size());
      58                 :             : 
      59                 :             :     // Replace all occurrences of '%' followed by two hex characters with the corresponding key.
      60   [ +  +  +  + ]:    22092255 :     for (size_t i = 0; i < mocked_desc.size();) {
      61         [ +  + ]:    22068936 :         if (mocked_desc[i] == '%') {
      62         [ +  + ]:      419576 :             if (i + 3 >= mocked_desc.size()) return {};
      63   [ +  -  +  -  :      839034 :             if (const auto idx = IdxFromHex(mocked_desc.substr(i + 1, 2))) {
             +  +  +  + ]
      64         [ +  - ]:      418399 :                 desc += keys_str[*idx];
      65                 :      418399 :                 i += 3;
      66                 :      418399 :             } else {
      67                 :        1118 :                 return {};
      68                 :             :             }
      69                 :      418399 :         } else {
      70         [ +  - ]:    21649360 :             desc += mocked_desc[i++];
      71                 :             :         }
      72                 :             :     }
      73                 :             : 
      74                 :       22142 :     return desc;
      75                 :       23784 : }
      76                 :             : 
      77                 :       28268 : bool HasDeepDerivPath(const FuzzBufferType& buff, const int max_depth)
      78                 :             : {
      79                 :       28268 :     auto depth{0};
      80   [ +  +  +  + ]:    47830100 :     for (const auto& ch: buff) {
      81         [ +  + ]:    47801832 :         if (ch == ',') {
      82                 :             :             // A comma is always present between two key expressions, so we use that as a delimiter.
      83                 :      901302 :             depth = 0;
      84         [ +  + ]:    47801832 :         } else if (ch == '/') {
      85         [ +  + ]:      167555 :             if (++depth > max_depth) return true;
      86                 :      166983 :         }
      87         [ +  + ]:    47801832 :     }
      88                 :       27696 :     return false;
      89                 :       28268 : }
      90                 :             : 
      91                 :        7726 : bool HasTooManySubFrag(const FuzzBufferType& buff, const int max_subs, const size_t max_nested_subs)
      92                 :             : {
      93                 :             :     // We use a stack because there may be many nested sub-frags.
      94                 :        7726 :     std::stack<int> counts;
      95   [ +  +  +  + ]:    27335829 :     for (const auto& ch: buff) {
      96                 :             :         // The fuzzer may generate an input with a ton of parentheses. Rule out pathological cases.
      97   [ +  -  +  + ]:    27328103 :         if (counts.size() > max_nested_subs) return true;
      98                 :             : 
      99         [ +  + ]:    27328099 :         if (ch == '(') {
     100                 :             :             // A new fragment was opened, create a new sub-count for it and start as one since any fragment with
     101                 :             :             // parentheses has at least one sub.
     102         [ -  + ]:      473884 :             counts.push(1);
     103   [ +  +  +  -  :    27328099 :         } else if (ch == ',' && !counts.empty()) {
                   +  + ]
     104                 :             :             // When encountering a comma, account for an additional sub in the last opened fragment. If it exceeds the
     105                 :             :             // limit, bail.
     106   [ +  -  +  + ]:      744161 :             if (++counts.top() > max_subs) return true;
     107   [ +  +  +  -  :    26854179 :         } else if (ch == ')' && !counts.empty()) {
                   +  + ]
     108                 :             :             // Fragment closed! Drop its sub count and resume to counting the number of subs for its parent.
     109         [ +  - ]:      290099 :             counts.pop();
     110                 :      290099 :         }
     111         [ +  + ]:    27328103 :     }
     112                 :        7686 :     return false;
     113                 :        7726 : }
     114                 :             : 
     115                 :        7686 : bool HasTooManyWrappers(const FuzzBufferType& buff, const int max_wrappers)
     116                 :             : {
     117                 :             :     // The number of nested wrappers. Nested wrappers are always characters which follow each other so we don't have to
     118                 :             :     // use a stack as we do above when counting the number of sub-fragments.
     119                 :        7686 :     std::optional<int> count;
     120                 :             : 
     121                 :             :     // We want to detect nested wrappers. A wrapper is a character prepended to a fragment, separated by a colon. There
     122                 :             :     // may be more than one wrapper, in which case the colon is not repeated. For instance `jjjjj:pk()`.  To count
     123                 :             :     // wrappers we iterate in reverse and use the colon to detect the end of a wrapper expression and count how many
     124                 :             :     // characters there are since the beginning of the expression. We stop counting when we encounter a character
     125                 :             :     // indicating the beginning of a new expression.
     126   [ +  +  +  + ]:    16630744 :     for (const auto ch: buff | std::views::reverse) {
     127                 :             :         // A colon, start counting.
     128         [ +  + ]:    16623058 :         if (ch == ':') {
     129                 :             :             // The colon itself is not a wrapper so we start at 0.
     130                 :      315598 :             count = 0;
     131         [ +  + ]:    16623058 :         } else if (count) {
     132                 :             :             // If we are counting wrappers, stop when we crossed the beginning of the wrapper expression. Otherwise keep
     133                 :             :             // counting and bail if we reached the limit.
     134                 :             :             // A wrapper may only ever occur as the first sub of a descriptor/miniscript expression ('('), as the
     135                 :             :             // first Taproot leaf in a pair ('{') or as the nth sub in each case (',').
     136   [ +  +  +  +  :     2247950 :             if (ch == ',' || ch == '(' || ch == '{') {
                   +  + ]
     137                 :      314134 :                 count.reset();
     138         [ +  + ]:     2247950 :             } else if (++*count > max_wrappers) {
     139                 :         342 :                 return true;
     140                 :             :             }
     141                 :     2247608 :         }
     142         [ +  + ]:    16623058 :     }
     143                 :             : 
     144                 :        7344 :     return false;
     145                 :        7686 : }
        

Generated by: LCOV version 2.0-1