/ src / script / bitcoinconsensus.cpp
bitcoinconsensus.cpp
  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 <script/bitcoinconsensus.h>
  7  
  8  #include <primitives/transaction.h>
  9  #include <pubkey.h>
 10  #include <script/interpreter.h>
 11  
 12  namespace {
 13  
 14  /** A class that deserializes a single CTransaction one time. */
 15  class TxInputStream
 16  {
 17  public:
 18      TxInputStream(const unsigned char *txTo, size_t txToLen) :
 19      m_data(txTo),
 20      m_remaining(txToLen)
 21      {}
 22  
 23      void read(Span<std::byte> dst)
 24      {
 25          if (dst.size() > m_remaining) {
 26              throw std::ios_base::failure(std::string(__func__) + ": end of data");
 27          }
 28  
 29          if (dst.data() == nullptr) {
 30              throw std::ios_base::failure(std::string(__func__) + ": bad destination buffer");
 31          }
 32  
 33          if (m_data == nullptr) {
 34              throw std::ios_base::failure(std::string(__func__) + ": bad source buffer");
 35          }
 36  
 37          memcpy(dst.data(), m_data, dst.size());
 38          m_remaining -= dst.size();
 39          m_data += dst.size();
 40      }
 41  
 42      template<typename T>
 43      TxInputStream& operator>>(T&& obj)
 44      {
 45          ::Unserialize(*this, obj);
 46          return *this;
 47      }
 48  
 49  private:
 50      const unsigned char* m_data;
 51      size_t m_remaining;
 52  };
 53  
 54  inline int set_error(bitcoinconsensus_error* ret, bitcoinconsensus_error serror)
 55  {
 56      if (ret)
 57          *ret = serror;
 58      return 0;
 59  }
 60  
 61  } // namespace
 62  
 63  /** Check that all specified flags are part of the libconsensus interface. */
 64  static bool verify_flags(unsigned int flags)
 65  {
 66      return (flags & ~(bitcoinconsensus_SCRIPT_FLAGS_VERIFY_ALL)) == 0;
 67  }
 68  
 69  static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, CAmount amount,
 70                                      const unsigned char *txTo        , unsigned int txToLen,
 71                                      const UTXO *spentOutputs, unsigned int spentOutputsLen,
 72                                      unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err)
 73  {
 74      if (!verify_flags(flags)) {
 75          return set_error(err, bitcoinconsensus_ERR_INVALID_FLAGS);
 76      }
 77  
 78      if (flags & bitcoinconsensus_SCRIPT_FLAGS_VERIFY_TAPROOT && spentOutputs == nullptr) {
 79          return set_error(err, bitcoinconsensus_ERR_SPENT_OUTPUTS_REQUIRED);
 80      }
 81  
 82      try {
 83          TxInputStream stream(txTo, txToLen);
 84          CTransaction tx(deserialize, TX_WITH_WITNESS, stream);
 85  
 86          std::vector<CTxOut> spent_outputs;
 87          if (spentOutputs != nullptr) {
 88              if (spentOutputsLen != tx.vin.size()) {
 89                  return set_error(err, bitcoinconsensus_ERR_SPENT_OUTPUTS_MISMATCH);
 90              }
 91              for (size_t i = 0; i < spentOutputsLen; i++) {
 92                  CScript spk = CScript(spentOutputs[i].scriptPubKey, spentOutputs[i].scriptPubKey + spentOutputs[i].scriptPubKeySize);
 93                  const CAmount& value = spentOutputs[i].value;
 94                  CTxOut tx_out = CTxOut(value, spk);
 95                  spent_outputs.push_back(tx_out);
 96              }
 97          }
 98  
 99          if (nIn >= tx.vin.size())
100              return set_error(err, bitcoinconsensus_ERR_TX_INDEX);
101          if (GetSerializeSize(TX_WITH_WITNESS(tx)) != txToLen)
102              return set_error(err, bitcoinconsensus_ERR_TX_SIZE_MISMATCH);
103  
104          // Regardless of the verification result, the tx did not error.
105          set_error(err, bitcoinconsensus_ERR_OK);
106  
107          PrecomputedTransactionData txdata(tx);
108  
109          if (spentOutputs != nullptr && flags & bitcoinconsensus_SCRIPT_FLAGS_VERIFY_TAPROOT) {
110              txdata.Init(tx, std::move(spent_outputs));
111          }
112  
113          return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), &tx.vin[nIn].scriptWitness, flags, TransactionSignatureChecker(&tx, nIn, amount, txdata, MissingDataBehavior::FAIL), nullptr);
114      } catch (const std::exception&) {
115          return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing
116      }
117  }
118  
119  int bitcoinconsensus_verify_script_with_spent_outputs(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, int64_t amount,
120                                      const unsigned char *txTo        , unsigned int txToLen,
121                                      const UTXO *spentOutputs, unsigned int spentOutputsLen,
122                                      unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err)
123  {
124      CAmount am(amount);
125      return ::verify_script(scriptPubKey, scriptPubKeyLen, am, txTo, txToLen, spentOutputs, spentOutputsLen, nIn, flags, err);
126  }
127  
128  int bitcoinconsensus_verify_script_with_amount(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, int64_t amount,
129                                      const unsigned char *txTo        , unsigned int txToLen,
130                                      unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err)
131  {
132      CAmount am(amount);
133      UTXO *spentOutputs = nullptr;
134      unsigned int spentOutputsLen = 0;
135      return ::verify_script(scriptPubKey, scriptPubKeyLen, am, txTo, txToLen, spentOutputs, spentOutputsLen, nIn, flags, err);
136  }
137  
138  
139  int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen,
140                                     const unsigned char *txTo        , unsigned int txToLen,
141                                     unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err)
142  {
143      if (flags & bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS) {
144          return set_error(err, bitcoinconsensus_ERR_AMOUNT_REQUIRED);
145      }
146  
147      CAmount am(0);
148      UTXO *spentOutputs = nullptr;
149      unsigned int spentOutputsLen = 0;
150      return ::verify_script(scriptPubKey, scriptPubKeyLen, am, txTo, txToLen, spentOutputs, spentOutputsLen, nIn, flags, err);
151  }
152  
153  unsigned int bitcoinconsensus_version()
154  {
155      // Just use the API version for now
156      return BITCOINCONSENSUS_API_VER;
157  }