LCOV - code coverage report
Current view: top level - src/script - solver.cpp (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 98.4 % 126 124
Test Date: 2025-09-10 04:04:06 Functions: 100.0 % 9 9
Branches: 77.7 % 256 199

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 2009-2010 Satoshi Nakamoto
       2                 :             : // Copyright (c) 2009-present The Bitcoin 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 <pubkey.h>
       7                 :             : #include <script/interpreter.h>
       8                 :             : #include <script/script.h>
       9                 :             : #include <script/solver.h>
      10                 :             : #include <span.h>
      11                 :             : 
      12                 :             : #include <algorithm>
      13                 :             : #include <cassert>
      14                 :             : #include <string>
      15                 :             : 
      16                 :             : typedef std::vector<unsigned char> valtype;
      17                 :             : 
      18                 :     1078986 : std::string GetTxnOutputType(TxoutType t)
      19                 :             : {
      20   [ +  +  +  +  :     1078986 :     switch (t) {
          +  +  +  +  +  
                +  +  - ]
      21                 :      749939 :     case TxoutType::NONSTANDARD: return "nonstandard";
      22                 :       24916 :     case TxoutType::PUBKEY: return "pubkey";
      23                 :       21140 :     case TxoutType::PUBKEYHASH: return "pubkeyhash";
      24                 :      112140 :     case TxoutType::SCRIPTHASH: return "scripthash";
      25                 :       22415 :     case TxoutType::MULTISIG: return "multisig";
      26                 :        4376 :     case TxoutType::NULL_DATA: return "nulldata";
      27                 :        1804 :     case TxoutType::ANCHOR: return "anchor";
      28                 :       50745 :     case TxoutType::WITNESS_V0_KEYHASH: return "witness_v0_keyhash";
      29                 :       58337 :     case TxoutType::WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";
      30                 :       12589 :     case TxoutType::WITNESS_V1_TAPROOT: return "witness_v1_taproot";
      31                 :       20585 :     case TxoutType::WITNESS_UNKNOWN: return "witness_unknown";
      32                 :             :     } // no default case, so the compiler can warn about missing cases
      33                 :           0 :     assert(false);
      34                 :             : }
      35                 :             : 
      36                 :     3670227 : static bool MatchPayToPubkey(const CScript& script, valtype& pubkey)
      37                 :             : {
      38   [ +  +  +  +  :     3670227 :     if (script.size() == CPubKey::SIZE + 2 && script[0] == CPubKey::SIZE && script.back() == OP_CHECKSIG) {
             +  +  +  + ]
      39                 :       47122 :         pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::SIZE + 1);
      40                 :       23561 :         return CPubKey::ValidSize(pubkey);
      41                 :             :     }
      42   [ +  +  +  +  :     4659939 :     if (script.size() == CPubKey::COMPRESSED_SIZE + 2 && script[0] == CPubKey::COMPRESSED_SIZE && script.back() == OP_CHECKSIG) {
          +  +  +  +  +  
                +  +  + ]
      43                 :      180442 :         pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::COMPRESSED_SIZE + 1);
      44                 :       90221 :         return CPubKey::ValidSize(pubkey);
      45                 :             :     }
      46                 :             :     return false;
      47                 :             : }
      48                 :             : 
      49                 :     3565538 : static bool MatchPayToPubkeyHash(const CScript& script, valtype& pubkeyhash)
      50                 :             : {
      51   [ +  +  +  +  :     5540915 :     if (script.size() == 25 && script[0] == OP_DUP && script[1] == OP_HASH160 && script[2] == 20 && script[23] == OP_EQUALVERIFY && script[24] == OP_CHECKSIG) {
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
             +  +  +  +  
                      + ]
      52                 :      452566 :         pubkeyhash = valtype(script.begin () + 3, script.begin() + 23);
      53                 :      226283 :         return true;
      54                 :             :     }
      55                 :             :     return false;
      56                 :             : }
      57                 :             : 
      58                 :             : /** Test for "small positive integer" script opcodes - OP_1 through OP_16. */
      59                 :      269260 : static constexpr bool IsSmallInteger(opcodetype opcode)
      60                 :             : {
      61                 :      269260 :     return opcode >= OP_1 && opcode <= OP_16;
      62                 :             : }
      63                 :             : 
      64                 :             : /** Retrieve a minimally-encoded number in range [min,max] from an (opcode, data) pair,
      65                 :             :  *  whether it's OP_n or through a push. */
      66                 :      269260 : static std::optional<int> GetScriptNumber(opcodetype opcode, valtype data, int min, int max)
      67                 :             : {
      68                 :      269260 :     int count;
      69         [ +  + ]:      269260 :     if (IsSmallInteger(opcode)) {
      70                 :      173887 :         count = CScript::DecodeOP_N(opcode);
      71         [ +  + ]:       95373 :     } else if (IsPushdataOp(opcode)) {
      72         [ +  + ]:       66242 :         if (!CheckMinimalPush(data, opcode)) return {};
      73                 :       61026 :         try {
      74         [ +  + ]:       61026 :             count = CScriptNum(data, /* fRequireMinimal = */ true).getint();
      75         [ -  + ]:       21274 :         } catch (const scriptnum_error&) {
      76                 :       21274 :             return {};
      77                 :       21274 :         }
      78                 :             :     } else {
      79                 :       29131 :         return {};
      80                 :             :     }
      81   [ +  +  +  + ]:      213639 :     if (count < min || count > max) return {};
      82                 :      194526 :     return count;
      83                 :             : }
      84                 :             : 
      85                 :     3339255 : static bool MatchMultisig(const CScript& script, int& required_sigs, std::vector<valtype>& pubkeys)
      86                 :             : {
      87                 :     3339255 :     opcodetype opcode;
      88                 :     3339255 :     valtype data;
      89                 :             : 
      90         [ +  + ]:     3339255 :     CScript::const_iterator it = script.begin();
      91   [ +  +  +  +  :     6799811 :     if (script.size() < 1 || script.back() != OP_CHECKMULTISIG) return false;
             +  +  +  + ]
      92                 :             : 
      93   [ +  -  +  + ]:      160039 :     if (!script.GetOp(it, opcode, data)) return false;
      94   [ +  -  +  - ]:      154777 :     auto req_sigs = GetScriptNumber(opcode, data, 1, MAX_PUBKEYS_PER_MULTISIG);
      95         [ +  + ]:      154777 :     if (!req_sigs) return false;
      96                 :      110001 :     required_sigs = *req_sigs;
      97   [ +  -  +  +  :      559847 :     while (script.GetOp(it, opcode, data) && CPubKey::ValidSize(data)) {
                   +  + ]
      98         [ +  - ]:      449846 :         pubkeys.emplace_back(std::move(data));
      99                 :             :     }
     100   [ +  -  +  - ]:      110001 :     auto num_keys = GetScriptNumber(opcode, data, required_sigs, MAX_PUBKEYS_PER_MULTISIG);
     101         [ +  + ]:      110001 :     if (!num_keys) return false;
     102   [ -  +  +  + ]:       80043 :     if (pubkeys.size() != static_cast<unsigned long>(*num_keys)) return false;
     103                 :             : 
     104                 :       76857 :     return (it + 1 == script.end());
     105                 :     3339255 : }
     106                 :             : 
     107                 :       20233 : std::optional<std::pair<int, std::vector<std::span<const unsigned char>>>> MatchMultiA(const CScript& script)
     108                 :             : {
     109                 :       20233 :     std::vector<std::span<const unsigned char>> keyspans;
     110                 :             : 
     111                 :             :     // Redundant, but very fast and selective test.
     112   [ +  +  +  -  :       64446 :     if (script.size() == 0 || script[0] != 32 || script.back() != OP_NUMEQUAL) return {};
          +  +  +  +  +  
                +  +  + ]
     113                 :             : 
     114                 :             :     // Parse keys
     115                 :        4482 :     auto it = script.begin();
     116         [ +  + ]:      563755 :     while (script.end() - it >= 34) {
     117         [ -  + ]:      559273 :         if (*it != 32) return {};
     118         [ +  - ]:      559273 :         ++it;
     119         [ +  - ]:      559273 :         keyspans.emplace_back(&*it, 32);
     120         [ -  + ]:      559273 :         it += 32;
     121   [ -  +  +  +  :     1114064 :         if (*it != (keyspans.size() == 1 ? OP_CHECKSIG : OP_CHECKSIGADD)) return {};
                   -  + ]
     122                 :      559273 :         ++it;
     123                 :             :     }
     124   [ -  +  +  -  :        4482 :     if (keyspans.size() == 0 || keyspans.size() > MAX_PUBKEYS_PER_MULTI_A) return {};
                   +  - ]
     125                 :             : 
     126                 :             :     // Parse threshold.
     127                 :        4482 :     opcodetype opcode;
     128                 :        4482 :     std::vector<unsigned char> data;
     129   [ +  -  -  + ]:        4482 :     if (!script.GetOp(it, opcode, data)) return {};
     130         [ -  + ]:        4482 :     if (it == script.end()) return {};
     131         [ -  + ]:        4482 :     if (*it != OP_NUMEQUAL) return {};
     132         [ -  + ]:        4482 :     ++it;
     133         [ -  + ]:        4482 :     if (it != script.end()) return {};
     134   [ -  +  +  -  :        4482 :     auto threshold = GetScriptNumber(opcode, data, 1, (int)keyspans.size());
                   +  - ]
     135         [ -  + ]:        4482 :     if (!threshold) return {};
     136                 :             : 
     137                 :             :     // Construct result.
     138                 :        4482 :     return std::pair{*threshold, std::move(keyspans)};
     139                 :       24715 : }
     140                 :             : 
     141                 :    14832393 : TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned char>>& vSolutionsRet)
     142                 :             : {
     143                 :    14832393 :     vSolutionsRet.clear();
     144                 :             : 
     145                 :             :     // Shortcut for pay-to-script-hash, which are more constrained than the other types:
     146                 :             :     // it is always OP_HASH160 20 [20 byte hash] OP_EQUAL
     147         [ +  + ]:    14832393 :     if (scriptPubKey.IsPayToScriptHash())
     148                 :             :     {
     149         [ +  + ]:     1136454 :         std::vector<unsigned char> hashBytes(scriptPubKey.begin()+2, scriptPubKey.begin()+22);
     150         [ +  - ]:      568227 :         vSolutionsRet.push_back(hashBytes);
     151                 :      568227 :         return TxoutType::SCRIPTHASH;
     152                 :      568227 :     }
     153                 :             : 
     154                 :    14264166 :     int witnessversion;
     155                 :    14264166 :     std::vector<unsigned char> witnessprogram;
     156   [ +  -  +  + ]:    14264166 :     if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
     157   [ +  +  +  + ]:    20952631 :         if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_KEYHASH_SIZE) {
     158         [ +  - ]:      248481 :             vSolutionsRet.push_back(std::move(witnessprogram));
     159                 :             :             return TxoutType::WITNESS_V0_KEYHASH;
     160                 :             :         }
     161   [ +  +  +  + ]:    20455669 :         if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE) {
     162         [ +  - ]:    10126370 :             vSolutionsRet.push_back(std::move(witnessprogram));
     163                 :             :             return TxoutType::WITNESS_V0_SCRIPTHASH;
     164                 :             :         }
     165   [ +  +  +  + ]:      308484 :         if (witnessversion == 1 && witnessprogram.size() == WITNESS_V1_TAPROOT_SIZE) {
     166         [ +  - ]:      101236 :             vSolutionsRet.push_back(std::move(witnessprogram));
     167                 :             :             return TxoutType::WITNESS_V1_TAPROOT;
     168                 :             :         }
     169   [ +  -  +  + ]:       96422 :         if (scriptPubKey.IsPayToAnchor()) {
     170                 :             :             return TxoutType::ANCHOR;
     171                 :             :         }
     172         [ +  + ]:       94113 :         if (witnessversion != 0) {
     173   [ +  -  +  - ]:      177684 :             vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion});
     174         [ +  - ]:    14264166 :             vSolutionsRet.push_back(std::move(witnessprogram));
     175                 :             :             return TxoutType::WITNESS_UNKNOWN;
     176                 :             :         }
     177                 :             :         return TxoutType::NONSTANDARD;
     178                 :             :     }
     179                 :             : 
     180                 :             :     // Provably prunable, data-carrying output
     181                 :             :     //
     182                 :             :     // So long as script passes the IsUnspendable() test and all but the first
     183                 :             :     // byte passes the IsPushOnly() test we don't care what exactly is in the
     184                 :             :     // script.
     185   [ +  +  +  +  :     7542630 :     if (scriptPubKey.size() >= 1 && scriptPubKey[0] == OP_RETURN && scriptPubKey.IsPushOnly(scriptPubKey.begin()+1)) {
          +  +  +  +  +  
                -  +  + ]
     186                 :             :         return TxoutType::NULL_DATA;
     187                 :             :     }
     188                 :             : 
     189                 :     3670227 :     std::vector<unsigned char> data;
     190   [ +  -  +  + ]:     3670227 :     if (MatchPayToPubkey(scriptPubKey, data)) {
     191         [ +  - ]:      104689 :         vSolutionsRet.push_back(std::move(data));
     192                 :             :         return TxoutType::PUBKEY;
     193                 :             :     }
     194                 :             : 
     195   [ +  -  +  + ]:     3565538 :     if (MatchPayToPubkeyHash(scriptPubKey, data)) {
     196         [ +  - ]:     3670227 :         vSolutionsRet.push_back(std::move(data));
     197                 :             :         return TxoutType::PUBKEYHASH;
     198                 :             :     }
     199                 :             : 
     200                 :     3339255 :     int required;
     201                 :     3339255 :     std::vector<std::vector<unsigned char>> keys;
     202   [ +  -  +  + ]:     3339255 :     if (MatchMultisig(scriptPubKey, required, keys)) {
     203   [ +  -  +  - ]:      153404 :         vSolutionsRet.push_back({static_cast<unsigned char>(required)}); // safe as required is in range 1..20
     204         [ +  - ]:       76702 :         vSolutionsRet.insert(vSolutionsRet.end(), keys.begin(), keys.end());
     205   [ -  +  +  - ]:      153404 :         vSolutionsRet.push_back({static_cast<unsigned char>(keys.size())}); // safe as size is in range 1..20
     206                 :       76702 :         return TxoutType::MULTISIG;
     207                 :             :     }
     208                 :             : 
     209                 :     3262553 :     vSolutionsRet.clear();
     210                 :     3262553 :     return TxoutType::NONSTANDARD;
     211                 :    21273648 : }
     212                 :             : 
     213                 :        6409 : CScript GetScriptForRawPubKey(const CPubKey& pubKey)
     214                 :             : {
     215   [ +  -  +  - ]:       12818 :     return CScript() << std::vector<unsigned char>(pubKey.begin(), pubKey.end()) << OP_CHECKSIG;
     216                 :             : }
     217                 :             : 
     218                 :        9012 : CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys)
     219                 :             : {
     220                 :        9012 :     CScript script;
     221                 :             : 
     222         [ +  - ]:        9012 :     script << nRequired;
     223         [ +  + ]:       92982 :     for (const CPubKey& key : keys)
     224         [ +  - ]:      167940 :         script << ToByteVector(key);
     225   [ -  +  +  -  :        9012 :     script << keys.size() << OP_CHECKMULTISIG;
                   +  - ]
     226                 :             : 
     227                 :        9012 :     return script;
     228                 :           0 : }
        

Generated by: LCOV version 2.0-1