LCOV - code coverage report
Current view: top level - src/script - solver.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 132 134 98.5 %
Date: 2023-10-05 15:40:34 Functions: 10 10 100.0 %
Branches: 200 303 66.0 %

           Branch data     Line data    Source code
       1                 :            : // Copyright (c) 2009-2010 Satoshi Nakamoto
       2                 :            : // Copyright (c) 2009-2022 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                 :     609976 : std::string GetTxnOutputType(TxoutType t)
      19                 :            : {
      20   [ +  +  +  +  :     609976 :     switch (t) {
          +  +  +  +  +  
                   +  - ]
      21         [ +  - ]:     455673 :     case TxoutType::NONSTANDARD: return "nonstandard";
      22         [ +  - ]:       9808 :     case TxoutType::PUBKEY: return "pubkey";
      23         [ +  - ]:      11303 :     case TxoutType::PUBKEYHASH: return "pubkeyhash";
      24         [ +  - ]:      51993 :     case TxoutType::SCRIPTHASH: return "scripthash";
      25         [ +  - ]:       9305 :     case TxoutType::MULTISIG: return "multisig";
      26         [ +  - ]:       2445 :     case TxoutType::NULL_DATA: return "nulldata";
      27         [ +  - ]:      29768 :     case TxoutType::WITNESS_V0_KEYHASH: return "witness_v0_keyhash";
      28         [ +  - ]:      25314 :     case TxoutType::WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";
      29         [ +  - ]:       5320 :     case TxoutType::WITNESS_V1_TAPROOT: return "witness_v1_taproot";
      30         [ +  - ]:       9047 :     case TxoutType::WITNESS_UNKNOWN: return "witness_unknown";
      31                 :            :     } // no default case, so the compiler can warn about missing cases
      32                 :          0 :     assert(false);
      33                 :     609976 : }
      34                 :            : 
      35                 :    1953714 : static bool MatchPayToPubkey(const CScript& script, valtype& pubkey)
      36                 :            : {
      37   [ +  +  +  +  :    1953714 :     if (script.size() == CPubKey::SIZE + 2 && script[0] == CPubKey::SIZE && script.back() == OP_CHECKSIG) {
                   +  + ]
      38         [ +  - ]:       5496 :         pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::SIZE + 1);
      39                 :       5496 :         return CPubKey::ValidSize(pubkey);
      40                 :            :     }
      41   [ +  +  +  +  :    1948218 :     if (script.size() == CPubKey::COMPRESSED_SIZE + 2 && script[0] == CPubKey::COMPRESSED_SIZE && script.back() == OP_CHECKSIG) {
                   +  + ]
      42         [ -  + ]:      27034 :         pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::COMPRESSED_SIZE + 1);
      43                 :      27034 :         return CPubKey::ValidSize(pubkey);
      44                 :            :     }
      45                 :    1921184 :     return false;
      46                 :    1953714 : }
      47                 :            : 
      48                 :    1924362 : static bool MatchPayToPubkeyHash(const CScript& script, valtype& pubkeyhash)
      49                 :            : {
      50   [ +  +  +  +  :    1924362 :     if (script.size() == 25 && script[0] == OP_DUP && script[1] == OP_HASH160 && script[2] == 20 && script[23] == OP_EQUALVERIFY && script[24] == OP_CHECKSIG) {
          +  +  +  +  +  
                +  +  + ]
      51         [ -  + ]:      44339 :         pubkeyhash = valtype(script.begin () + 3, script.begin() + 23);
      52                 :      44339 :         return true;
      53                 :            :     }
      54                 :    1880023 :     return false;
      55                 :    1924362 : }
      56                 :            : 
      57                 :            : /** Test for "small positive integer" script opcodes - OP_1 through OP_16. */
      58                 :      91007 : static constexpr bool IsSmallInteger(opcodetype opcode)
      59                 :            : {
      60         [ +  + ]:      91007 :     return opcode >= OP_1 && opcode <= OP_16;
      61                 :            : }
      62                 :            : 
      63                 :            : /** Retrieve a minimally-encoded number in range [min,max] from an (opcode, data) pair,
      64                 :            :  *  whether it's OP_n or through a push. */
      65                 :      91007 : static std::optional<int> GetScriptNumber(opcodetype opcode, valtype data, int min, int max)
      66                 :            : {
      67                 :            :     int count;
      68         [ +  + ]:      91007 :     if (IsSmallInteger(opcode)) {
      69                 :      61880 :         count = CScript::DecodeOP_N(opcode);
      70         [ +  + ]:      91007 :     } else if (IsPushdataOp(opcode)) {
      71         [ +  + ]:      18225 :         if (!CheckMinimalPush(data, opcode)) return {};
      72                 :            :         try {
      73   [ +  +  +  - ]:      15801 :             count = CScriptNum(data, /* fRequireMinimal = */ true).getint();
      74         [ +  - ]:      15801 :         } catch (const scriptnum_error&) {
      75                 :       8046 :             return {};
      76                 :       8046 :         }
      77                 :       7755 :     } else {
      78                 :      10902 :         return {};
      79                 :            :     }
      80   [ +  +  +  + ]:      69635 :     if (count < min || count > max) return {};
      81                 :      63766 :     return count;
      82                 :      99053 : }
      83                 :            : 
      84                 :    1880023 : static bool MatchMultisig(const CScript& script, int& required_sigs, std::vector<valtype>& pubkeys)
      85                 :            : {
      86                 :            :     opcodetype opcode;
      87                 :    1880023 :     valtype data;
      88                 :            : 
      89         [ +  - ]:    1880023 :     CScript::const_iterator it = script.begin();
      90   [ +  -  +  +  :    1880023 :     if (script.size() < 1 || script.back() != OP_CHECKMULTISIG) return false;
             +  -  +  + ]
      91                 :            : 
      92   [ +  -  +  + ]:      57647 :     if (!script.GetOp(it, opcode, data)) return false;
      93   [ +  -  +  - ]:      54427 :     auto req_sigs = GetScriptNumber(opcode, data, 1, MAX_PUBKEYS_PER_MULTISIG);
      94         [ +  + ]:      54427 :     if (!req_sigs) return false;
      95                 :      36492 :     required_sigs = *req_sigs;
      96   [ +  -  +  +  :     235146 :     while (script.GetOp(it, opcode, data) && CPubKey::ValidSize(data)) {
             +  -  +  + ]
      97         [ +  - ]:      81412 :         pubkeys.emplace_back(std::move(data));
      98                 :            :     }
      99   [ +  -  +  - ]:      36492 :     auto num_keys = GetScriptNumber(opcode, data, required_sigs, MAX_PUBKEYS_PER_MULTISIG);
     100         [ +  + ]:      36492 :     if (!num_keys) return false;
     101         [ +  + ]:      27186 :     if (pubkeys.size() != static_cast<unsigned long>(*num_keys)) return false;
     102                 :            : 
     103   [ +  -  +  -  :      26605 :     return (it + 1 == script.end());
                   +  - ]
     104                 :    1880023 : }
     105                 :            : 
     106                 :         88 : std::optional<std::pair<int, std::vector<Span<const unsigned char>>>> MatchMultiA(const CScript& script)
     107                 :            : {
     108                 :         88 :     std::vector<Span<const unsigned char>> keyspans;
     109                 :            : 
     110                 :            :     // Redundant, but very fast and selective test.
     111   [ +  -  +  -  :         88 :     if (script.size() == 0 || script[0] != 32 || script.back() != OP_NUMEQUAL) return {};
          +  -  +  -  +  
                -  +  - ]
     112                 :            : 
     113                 :            :     // Parse keys
     114         [ +  - ]:         88 :     auto it = script.begin();
     115   [ +  -  +  -  :        682 :     while (script.end() - it >= 34) {
                   +  + ]
     116   [ +  -  +  - ]:        594 :         if (*it != 32) return {};
     117         [ +  - ]:        594 :         ++it;
     118   [ +  -  +  - ]:        594 :         keyspans.emplace_back(&*it, 32);
     119         [ +  - ]:        594 :         it += 32;
     120   [ +  -  +  - ]:        594 :         if (*it != (keyspans.size() == 1 ? OP_CHECKSIG : OP_CHECKSIGADD)) return {};
     121         [ +  - ]:        594 :         ++it;
     122                 :            :     }
     123   [ +  -  -  + ]:         88 :     if (keyspans.size() == 0 || keyspans.size() > MAX_PUBKEYS_PER_MULTI_A) return {};
     124                 :            : 
     125                 :            :     // Parse threshold.
     126                 :            :     opcodetype opcode;
     127                 :         88 :     std::vector<unsigned char> data;
     128   [ +  -  +  - ]:         88 :     if (!script.GetOp(it, opcode, data)) return {};
     129   [ +  -  +  -  :         88 :     if (it == script.end()) return {};
                   +  - ]
     130   [ +  -  +  - ]:         88 :     if (*it != OP_NUMEQUAL) return {};
     131         [ +  - ]:         88 :     ++it;
     132   [ +  -  +  -  :         88 :     if (it != script.end()) return {};
                   +  - ]
     133   [ +  -  +  - ]:         88 :     auto threshold = GetScriptNumber(opcode, data, 1, (int)keyspans.size());
     134         [ +  - ]:         88 :     if (!threshold) return {};
     135                 :            : 
     136                 :            :     // Construct result.
     137   [ +  -  +  -  :         88 :     return std::pair{*threshold, std::move(keyspans)};
                   -  + ]
     138                 :         88 : }
     139                 :            : 
     140                 :   11657080 : TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned char>>& vSolutionsRet)
     141                 :            : {
     142                 :   11657080 :     vSolutionsRet.clear();
     143                 :            : 
     144                 :            :     // Shortcut for pay-to-script-hash, which are more constrained than the other types:
     145                 :            :     // it is always OP_HASH160 20 [20 byte hash] OP_EQUAL
     146         [ +  + ]:   11657080 :     if (scriptPubKey.IsPayToScriptHash())
     147                 :            :     {
     148         [ +  - ]:     211720 :         std::vector<unsigned char> hashBytes(scriptPubKey.begin()+2, scriptPubKey.begin()+22);
     149         [ +  - ]:     211720 :         vSolutionsRet.push_back(hashBytes);
     150                 :     211720 :         return TxoutType::SCRIPTHASH;
     151                 :     211720 :     }
     152                 :            : 
     153                 :            :     int witnessversion;
     154                 :   11445360 :     std::vector<unsigned char> witnessprogram;
     155   [ +  -  +  + ]:   11445360 :     if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
     156   [ +  +  +  + ]:    9471877 :         if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_KEYHASH_SIZE) {
     157         [ +  - ]:     128364 :             vSolutionsRet.push_back(std::move(witnessprogram));
     158                 :     128364 :             return TxoutType::WITNESS_V0_KEYHASH;
     159                 :            :         }
     160   [ +  +  +  + ]:    9343513 :         if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE) {
     161         [ +  - ]:    9266380 :             vSolutionsRet.push_back(std::move(witnessprogram));
     162                 :    9266380 :             return TxoutType::WITNESS_V0_SCRIPTHASH;
     163                 :            :         }
     164   [ +  +  +  + ]:      77133 :         if (witnessversion == 1 && witnessprogram.size() == WITNESS_V1_TAPROOT_SIZE) {
     165         [ +  - ]:      14451 :             vSolutionsRet.push_back(std::move(witnessprogram));
     166                 :      14451 :             return TxoutType::WITNESS_V1_TAPROOT;
     167                 :            :         }
     168         [ +  + ]:      62682 :         if (witnessversion != 0) {
     169   [ +  -  +  - ]:      61584 :             vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion});
     170         [ +  - ]:      61584 :             vSolutionsRet.push_back(std::move(witnessprogram));
     171                 :      61584 :             return TxoutType::WITNESS_UNKNOWN;
     172                 :            :         }
     173                 :       1098 :         return TxoutType::NONSTANDARD;
     174                 :            :     }
     175                 :            : 
     176                 :            :     // Provably prunable, data-carrying output
     177                 :            :     //
     178                 :            :     // So long as script passes the IsUnspendable() test and all but the first
     179                 :            :     // byte passes the IsPushOnly() test we don't care what exactly is in the
     180                 :            :     // script.
     181   [ +  -  +  +  :    2001142 :     if (scriptPubKey.size() >= 1 && scriptPubKey[0] == OP_RETURN && scriptPubKey.IsPushOnly(scriptPubKey.begin()+1)) {
          +  -  +  +  +  
          -  +  -  +  -  
                   +  + ]
     182                 :      19769 :         return TxoutType::NULL_DATA;
     183                 :            :     }
     184                 :            : 
     185                 :    1953714 :     std::vector<unsigned char> data;
     186   [ +  -  +  + ]:    1953714 :     if (MatchPayToPubkey(scriptPubKey, data)) {
     187         [ +  - ]:      29352 :         vSolutionsRet.push_back(std::move(data));
     188                 :      29352 :         return TxoutType::PUBKEY;
     189                 :            :     }
     190                 :            : 
     191   [ +  -  +  + ]:    1924362 :     if (MatchPayToPubkeyHash(scriptPubKey, data)) {
     192         [ +  - ]:      44339 :         vSolutionsRet.push_back(std::move(data));
     193                 :      44339 :         return TxoutType::PUBKEYHASH;
     194                 :            :     }
     195                 :            : 
     196                 :            :     int required;
     197                 :    1880023 :     std::vector<std::vector<unsigned char>> keys;
     198   [ +  -  +  + ]:    1880023 :     if (MatchMultisig(scriptPubKey, required, keys)) {
     199   [ +  -  +  - ]:      26590 :         vSolutionsRet.push_back({static_cast<unsigned char>(required)}); // safe as required is in range 1..20
     200         [ +  - ]:      26590 :         vSolutionsRet.insert(vSolutionsRet.end(), keys.begin(), keys.end());
     201   [ +  -  -  + ]:      26590 :         vSolutionsRet.push_back({static_cast<unsigned char>(keys.size())}); // safe as size is in range 1..20
     202                 :      26590 :         return TxoutType::MULTISIG;
     203                 :            :     }
     204                 :            : 
     205                 :    1853433 :     vSolutionsRet.clear();
     206                 :    1853433 :     return TxoutType::NONSTANDARD;
     207                 :   11657080 : }
     208                 :            : 
     209                 :        129 : CScript GetScriptForRawPubKey(const CPubKey& pubKey)
     210                 :            : {
     211   [ +  -  +  -  :        129 :     return CScript() << std::vector<unsigned char>(pubKey.begin(), pubKey.end()) << OP_CHECKSIG;
          -  +  +  -  +  
                -  +  - ]
     212                 :          0 : }
     213                 :            : 
     214                 :        477 : CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys)
     215                 :            : {
     216                 :        477 :     CScript script;
     217                 :            : 
     218         [ +  - ]:        477 :     script << nRequired;
     219         [ +  + ]:       5508 :     for (const CPubKey& key : keys)
     220   [ +  -  +  - ]:       5031 :         script << ToByteVector(key);
     221   [ +  -  +  - ]:        477 :     script << keys.size() << OP_CHECKMULTISIG;
     222                 :            : 
     223                 :        477 :     return script;
     224         [ +  - ]:        477 : }

Generated by: LCOV version 1.14