solver.cpp raw

   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 <script/solver.h>
   7  
   8  #include <prevector.h>
   9  #include <pubkey.h>
  10  #include <script/interpreter.h>
  11  #include <script/script.h>
  12  
  13  #include <cassert>
  14  #include <string>
  15  
  16  typedef std::vector<unsigned char> valtype;
  17  
  18  std::string GetTxnOutputType(TxoutType t)
  19  {
  20      switch (t) {
  21      case TxoutType::NONSTANDARD: return "nonstandard";
  22      case TxoutType::PUBKEY: return "pubkey";
  23      case TxoutType::PUBKEYHASH: return "pubkeyhash";
  24      case TxoutType::SCRIPTHASH: return "scripthash";
  25      case TxoutType::MULTISIG: return "multisig";
  26      case TxoutType::NULL_DATA: return "nulldata";
  27      case TxoutType::ANCHOR: return "anchor";
  28      case TxoutType::WITNESS_V0_KEYHASH: return "witness_v0_keyhash";
  29      case TxoutType::WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";
  30      case TxoutType::WITNESS_V1_TAPROOT: return "witness_v1_taproot";
  31      case TxoutType::WITNESS_UNKNOWN: return "witness_unknown";
  32      } // no default case, so the compiler can warn about missing cases
  33      assert(false);
  34  }
  35  
  36  static bool MatchPayToPubkey(const CScript& script, valtype& pubkey)
  37  {
  38      if (script.size() == CPubKey::SIZE + 2 && script[0] == CPubKey::SIZE && script.back() == OP_CHECKSIG) {
  39          pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::SIZE + 1);
  40          return CPubKey::ValidSize(pubkey);
  41      }
  42      if (script.size() == CPubKey::COMPRESSED_SIZE + 2 && script[0] == CPubKey::COMPRESSED_SIZE && script.back() == OP_CHECKSIG) {
  43          pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::COMPRESSED_SIZE + 1);
  44          return CPubKey::ValidSize(pubkey);
  45      }
  46      return false;
  47  }
  48  
  49  static bool MatchPayToPubkeyHash(const CScript& script, valtype& pubkeyhash)
  50  {
  51      if (script.size() == 25 && script[0] == OP_DUP && script[1] == OP_HASH160 && script[2] == 20 && script[23] == OP_EQUALVERIFY && script[24] == OP_CHECKSIG) {
  52          pubkeyhash = valtype(script.begin () + 3, script.begin() + 23);
  53          return true;
  54      }
  55      return false;
  56  }
  57  
  58  /** Test for "small positive integer" script opcodes - OP_1 through OP_16. */
  59  static constexpr bool IsSmallInteger(opcodetype opcode)
  60  {
  61      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  static std::optional<int> GetScriptNumber(opcodetype opcode, valtype data, int min, int max)
  67  {
  68      int count;
  69      if (IsSmallInteger(opcode)) {
  70          count = CScript::DecodeOP_N(opcode);
  71      } else if (IsPushdataOp(opcode)) {
  72          if (!CheckMinimalPush(data, opcode)) return {};
  73          try {
  74              count = CScriptNum(data, /* fRequireMinimal = */ true).getint();
  75          } catch (const scriptnum_error&) {
  76              return {};
  77          }
  78      } else {
  79          return {};
  80      }
  81      if (count < min || count > max) return {};
  82      return count;
  83  }
  84  
  85  static bool MatchMultisig(const CScript& script, int& required_sigs, std::vector<valtype>& pubkeys)
  86  {
  87      opcodetype opcode;
  88      valtype data;
  89  
  90      CScript::const_iterator it = script.begin();
  91      if (script.size() < 1 || script.back() != OP_CHECKMULTISIG) return false;
  92  
  93      if (!script.GetOp(it, opcode, data)) return false;
  94      auto req_sigs = GetScriptNumber(opcode, data, 1, MAX_PUBKEYS_PER_MULTISIG);
  95      if (!req_sigs) return false;
  96      required_sigs = *req_sigs;
  97      while (script.GetOp(it, opcode, data) && CPubKey::ValidSize(data)) {
  98          pubkeys.emplace_back(std::move(data));
  99      }
 100      auto num_keys = GetScriptNumber(opcode, data, required_sigs, MAX_PUBKEYS_PER_MULTISIG);
 101      if (!num_keys) return false;
 102      if (pubkeys.size() != static_cast<unsigned long>(*num_keys)) return false;
 103  
 104      return (it + 1 == script.end());
 105  }
 106  
 107  std::optional<std::pair<int, std::vector<std::span<const unsigned char>>>> MatchMultiA(const CScript& script)
 108  {
 109      std::vector<std::span<const unsigned char>> keyspans;
 110  
 111      // Redundant, but very fast and selective test.
 112      if (script.size() == 0 || script[0] != 32 || script.back() != OP_NUMEQUAL) return {};
 113  
 114      // Parse keys
 115      auto it = script.begin();
 116      while (script.end() - it >= 34) {
 117          if (*it != 32) return {};
 118          ++it;
 119          keyspans.emplace_back(&*it, 32);
 120          it += 32;
 121          if (*it != (keyspans.size() == 1 ? OP_CHECKSIG : OP_CHECKSIGADD)) return {};
 122          ++it;
 123      }
 124      if (keyspans.size() == 0 || keyspans.size() > MAX_PUBKEYS_PER_MULTI_A) return {};
 125  
 126      // Parse threshold.
 127      opcodetype opcode;
 128      std::vector<unsigned char> data;
 129      if (!script.GetOp(it, opcode, data)) return {};
 130      if (it == script.end()) return {};
 131      if (*it != OP_NUMEQUAL) return {};
 132      ++it;
 133      if (it != script.end()) return {};
 134      auto threshold = GetScriptNumber(opcode, data, 1, (int)keyspans.size());
 135      if (!threshold) return {};
 136  
 137      // Construct result.
 138      return std::pair{*threshold, std::move(keyspans)};
 139  }
 140  
 141  TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned char>>& vSolutionsRet)
 142  {
 143      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      if (scriptPubKey.IsPayToScriptHash())
 148      {
 149          std::vector<unsigned char> hashBytes(scriptPubKey.begin()+2, scriptPubKey.begin()+22);
 150          vSolutionsRet.push_back(hashBytes);
 151          return TxoutType::SCRIPTHASH;
 152      }
 153  
 154      int witnessversion;
 155      std::vector<unsigned char> witnessprogram;
 156      if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
 157          if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_KEYHASH_SIZE) {
 158              vSolutionsRet.push_back(std::move(witnessprogram));
 159              return TxoutType::WITNESS_V0_KEYHASH;
 160          }
 161          if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE) {
 162              vSolutionsRet.push_back(std::move(witnessprogram));
 163              return TxoutType::WITNESS_V0_SCRIPTHASH;
 164          }
 165          if (witnessversion == 1 && witnessprogram.size() == WITNESS_V1_TAPROOT_SIZE) {
 166              vSolutionsRet.push_back(std::move(witnessprogram));
 167              return TxoutType::WITNESS_V1_TAPROOT;
 168          }
 169          if (scriptPubKey.IsPayToAnchor()) {
 170              return TxoutType::ANCHOR;
 171          }
 172          if (witnessversion != 0) {
 173              vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion});
 174              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      if (scriptPubKey.size() >= 1 && scriptPubKey[0] == OP_RETURN && scriptPubKey.IsPushOnly(scriptPubKey.begin()+1)) {
 186          return TxoutType::NULL_DATA;
 187      }
 188  
 189      std::vector<unsigned char> data;
 190      if (MatchPayToPubkey(scriptPubKey, data)) {
 191          vSolutionsRet.push_back(std::move(data));
 192          return TxoutType::PUBKEY;
 193      }
 194  
 195      if (MatchPayToPubkeyHash(scriptPubKey, data)) {
 196          vSolutionsRet.push_back(std::move(data));
 197          return TxoutType::PUBKEYHASH;
 198      }
 199  
 200      int required;
 201      std::vector<std::vector<unsigned char>> keys;
 202      if (MatchMultisig(scriptPubKey, required, keys)) {
 203          vSolutionsRet.push_back({static_cast<unsigned char>(required)}); // safe as required is in range 1..20
 204          vSolutionsRet.insert(vSolutionsRet.end(), keys.begin(), keys.end());
 205          vSolutionsRet.push_back({static_cast<unsigned char>(keys.size())}); // safe as size is in range 1..20
 206          return TxoutType::MULTISIG;
 207      }
 208  
 209      vSolutionsRet.clear();
 210      return TxoutType::NONSTANDARD;
 211  }
 212  
 213  CScript GetScriptForRawPubKey(const CPubKey& pubKey)
 214  {
 215      return CScript() << std::vector<unsigned char>(pubKey.begin(), pubKey.end()) << OP_CHECKSIG;
 216  }
 217  
 218  CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys)
 219  {
 220      CScript script;
 221  
 222      script << nRequired;
 223      for (const CPubKey& key : keys)
 224          script << ToByteVector(key);
 225      script << keys.size() << OP_CHECKMULTISIG;
 226  
 227      return script;
 228  }
 229