limenkaconsensus.cpp raw

   1  // Copyright (c) 2009-2010 Satoshi Nakamoto
   2  // Copyright (c) 2009-2022 The Limenka 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/limenkaconsensus.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(limenkaconsensus_error* ret, limenkaconsensus_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 & ~(limenkaconsensus_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, limenkaconsensus_error* err)
  73  {
  74      if (!verify_flags(flags)) {
  75          return set_error(err, limenkaconsensus_ERR_INVALID_FLAGS);
  76      }
  77  
  78      if (flags & limenkaconsensus_SCRIPT_FLAGS_VERIFY_TAPROOT && spentOutputs == nullptr) {
  79          return set_error(err, limenkaconsensus_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, limenkaconsensus_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, limenkaconsensus_ERR_TX_INDEX);
 101          if (GetSerializeSize(TX_WITH_WITNESS(tx)) != txToLen)
 102              return set_error(err, limenkaconsensus_ERR_TX_SIZE_MISMATCH);
 103  
 104          // Regardless of the verification result, the tx did not error.
 105          set_error(err, limenkaconsensus_ERR_OK);
 106  
 107          PrecomputedTransactionData txdata(tx);
 108  
 109          if (spentOutputs != nullptr && flags & limenkaconsensus_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, limenkaconsensus_ERR_TX_DESERIALIZE); // Error deserializing
 116      }
 117  }
 118  
 119  int limenkaconsensus_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, limenkaconsensus_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 limenkaconsensus_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, limenkaconsensus_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 limenkaconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen,
 140                                     const unsigned char *txTo        , unsigned int txToLen,
 141                                     unsigned int nIn, unsigned int flags, limenkaconsensus_error* err)
 142  {
 143      if (flags & limenkaconsensus_SCRIPT_FLAGS_VERIFY_WITNESS) {
 144          return set_error(err, limenkaconsensus_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 limenkaconsensus_version()
 154  {
 155      // Just use the API version for now
 156      return LIMENKACONSENSUS_API_VER;
 157  }
 158