/ src / core_read.cpp
core_read.cpp
  1  // Copyright (c) 2009-2022 The Bitcoin Core developers
  2  // Distributed under the MIT software license, see the accompanying
  3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  4  
  5  #include <core_io.h>
  6  
  7  #include <primitives/block.h>
  8  #include <primitives/transaction.h>
  9  #include <script/script.h>
 10  #include <script/sign.h>
 11  #include <serialize.h>
 12  #include <streams.h>
 13  #include <util/result.h>
 14  #include <util/strencodings.h>
 15  
 16  #include <algorithm>
 17  #include <string>
 18  
 19  namespace {
 20  class OpCodeParser
 21  {
 22  private:
 23      std::map<std::string, opcodetype> mapOpNames;
 24  
 25  public:
 26      OpCodeParser()
 27      {
 28          for (unsigned int op = 0; op <= MAX_OPCODE; ++op) {
 29              // Allow OP_RESERVED to get into mapOpNames
 30              if (op < OP_NOP && op != OP_RESERVED) {
 31                  continue;
 32              }
 33  
 34              std::string strName = GetOpName(static_cast<opcodetype>(op));
 35              if (strName == "OP_UNKNOWN") {
 36                  continue;
 37              }
 38              mapOpNames[strName] = static_cast<opcodetype>(op);
 39              // Convenience: OP_ADD and just ADD are both recognized:
 40              if (strName.compare(0, 3, "OP_") == 0) { // strName starts with "OP_"
 41                  mapOpNames[strName.substr(3)] = static_cast<opcodetype>(op);
 42              }
 43          }
 44      }
 45      opcodetype Parse(const std::string& s) const
 46      {
 47          auto it = mapOpNames.find(s);
 48          if (it == mapOpNames.end()) throw std::runtime_error("script parse error: unknown opcode");
 49          return it->second;
 50      }
 51  };
 52  
 53  opcodetype ParseOpCode(const std::string& s)
 54  {
 55      static const OpCodeParser ocp;
 56      return ocp.Parse(s);
 57  }
 58  
 59  } // namespace
 60  
 61  CScript ParseScript(const std::string& s)
 62  {
 63      CScript result;
 64  
 65      std::vector<std::string> words = SplitString(s, " \t\n");
 66  
 67      for (const std::string& w : words) {
 68          if (w.empty()) {
 69              // Empty string, ignore. (SplitString doesn't combine multiple separators)
 70          } else if (std::all_of(w.begin(), w.end(), ::IsDigit) ||
 71                     (w.front() == '-' && w.size() > 1 && std::all_of(w.begin() + 1, w.end(), ::IsDigit)))
 72          {
 73              // Number
 74              const auto num{ToIntegral<int64_t>(w)};
 75  
 76              // limit the range of numbers ParseScript accepts in decimal
 77              // since numbers outside -0xFFFFFFFF...0xFFFFFFFF are illegal in scripts
 78              if (!num.has_value() || num > int64_t{0xffffffff} || num < -1 * int64_t{0xffffffff}) {
 79                  throw std::runtime_error("script parse error: decimal numeric value only allowed in the "
 80                                           "range -0xFFFFFFFF...0xFFFFFFFF");
 81              }
 82  
 83              result << num.value();
 84          } else if (w.substr(0, 2) == "0x" && w.size() > 2 && IsHex(std::string(w.begin() + 2, w.end()))) {
 85              // Raw hex data, inserted NOT pushed onto stack:
 86              std::vector<unsigned char> raw = ParseHex(std::string(w.begin() + 2, w.end()));
 87              result.insert(result.end(), raw.begin(), raw.end());
 88          } else if (w.size() >= 2 && w.front() == '\'' && w.back() == '\'') {
 89              // Single-quoted string, pushed as data. NOTE: this is poor-man's
 90              // parsing, spaces/tabs/newlines in single-quoted strings won't work.
 91              std::vector<unsigned char> value(w.begin() + 1, w.end() - 1);
 92              result << value;
 93          } else {
 94              // opcode, e.g. OP_ADD or ADD:
 95              result << ParseOpCode(w);
 96          }
 97      }
 98  
 99      return result;
100  }
101  
102  // Check that all of the input and output scripts of a transaction contains valid opcodes
103  static bool CheckTxScriptsSanity(const CMutableTransaction& tx)
104  {
105      // Check input scripts for non-coinbase txs
106      if (!CTransaction(tx).IsCoinBase()) {
107          for (unsigned int i = 0; i < tx.vin.size(); i++) {
108              if (!tx.vin[i].scriptSig.HasValidOps() || tx.vin[i].scriptSig.size() > MAX_SCRIPT_SIZE) {
109                  return false;
110              }
111          }
112      }
113      // Check output scripts
114      for (unsigned int i = 0; i < tx.vout.size(); i++) {
115          if (!tx.vout[i].scriptPubKey.HasValidOps() || tx.vout[i].scriptPubKey.size() > MAX_SCRIPT_SIZE) {
116              return false;
117          }
118      }
119  
120      return true;
121  }
122  
123  static bool DecodeTx(CMutableTransaction& tx, const std::vector<unsigned char>& tx_data, bool try_no_witness, bool try_witness)
124  {
125      // General strategy:
126      // - Decode both with extended serialization (which interprets the 0x0001 tag as a marker for
127      //   the presence of witnesses) and with legacy serialization (which interprets the tag as a
128      //   0-input 1-output incomplete transaction).
129      //   - Restricted by try_no_witness (which disables legacy if false) and try_witness (which
130      //     disables extended if false).
131      //   - Ignore serializations that do not fully consume the hex string.
132      // - If neither succeeds, fail.
133      // - If only one succeeds, return that one.
134      // - If both decode attempts succeed:
135      //   - If only one passes the CheckTxScriptsSanity check, return that one.
136      //   - If neither or both pass CheckTxScriptsSanity, return the extended one.
137  
138      CMutableTransaction tx_extended, tx_legacy;
139      bool ok_extended = false, ok_legacy = false;
140  
141      // Try decoding with extended serialization support, and remember if the result successfully
142      // consumes the entire input.
143      if (try_witness) {
144          DataStream ssData(tx_data);
145          try {
146              ssData >> TX_WITH_WITNESS(tx_extended);
147              if (ssData.empty()) ok_extended = true;
148          } catch (const std::exception&) {
149              // Fall through.
150          }
151      }
152  
153      // Optimization: if extended decoding succeeded and the result passes CheckTxScriptsSanity,
154      // don't bother decoding the other way.
155      if (ok_extended && CheckTxScriptsSanity(tx_extended)) {
156          tx = std::move(tx_extended);
157          return true;
158      }
159  
160      // Try decoding with legacy serialization, and remember if the result successfully consumes the entire input.
161      if (try_no_witness) {
162          DataStream ssData(tx_data);
163          try {
164              ssData >> TX_NO_WITNESS(tx_legacy);
165              if (ssData.empty()) ok_legacy = true;
166          } catch (const std::exception&) {
167              // Fall through.
168          }
169      }
170  
171      // If legacy decoding succeeded and passes CheckTxScriptsSanity, that's our answer, as we know
172      // at this point that extended decoding either failed or doesn't pass the sanity check.
173      if (ok_legacy && CheckTxScriptsSanity(tx_legacy)) {
174          tx = std::move(tx_legacy);
175          return true;
176      }
177  
178      // If extended decoding succeeded, and neither decoding passes sanity, return the extended one.
179      if (ok_extended) {
180          tx = std::move(tx_extended);
181          return true;
182      }
183  
184      // If legacy decoding succeeded and extended didn't, return the legacy one.
185      if (ok_legacy) {
186          tx = std::move(tx_legacy);
187          return true;
188      }
189  
190      // If none succeeded, we failed.
191      return false;
192  }
193  
194  bool DecodeHexTx(CMutableTransaction& tx, const std::string& hex_tx, bool try_no_witness, bool try_witness)
195  {
196      if (!IsHex(hex_tx)) {
197          return false;
198      }
199  
200      std::vector<unsigned char> txData(ParseHex(hex_tx));
201      return DecodeTx(tx, txData, try_no_witness, try_witness);
202  }
203  
204  bool DecodeHexBlockHeader(CBlockHeader& header, const std::string& hex_header)
205  {
206      if (!IsHex(hex_header)) return false;
207  
208      const std::vector<unsigned char> header_data{ParseHex(hex_header)};
209      DataStream ser_header{header_data};
210      try {
211          ser_header >> header;
212      } catch (const std::exception&) {
213          return false;
214      }
215      return true;
216  }
217  
218  bool DecodeHexBlk(CBlock& block, const std::string& strHexBlk)
219  {
220      if (!IsHex(strHexBlk))
221          return false;
222  
223      std::vector<unsigned char> blockData(ParseHex(strHexBlk));
224      DataStream ssBlock(blockData);
225      try {
226          ssBlock >> TX_WITH_WITNESS(block);
227      }
228      catch (const std::exception&) {
229          return false;
230      }
231  
232      return true;
233  }
234  
235  bool ParseHashStr(const std::string& strHex, uint256& result)
236  {
237      if ((strHex.size() != 64) || !IsHex(strHex))
238          return false;
239  
240      result.SetHex(strHex);
241      return true;
242  }
243  
244  util::Result<int> SighashFromStr(const std::string& sighash)
245  {
246      static std::map<std::string, int> map_sighash_values = {
247          {std::string("DEFAULT"), int(SIGHASH_DEFAULT)},
248          {std::string("ALL"), int(SIGHASH_ALL)},
249          {std::string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)},
250          {std::string("NONE"), int(SIGHASH_NONE)},
251          {std::string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY)},
252          {std::string("SINGLE"), int(SIGHASH_SINGLE)},
253          {std::string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY)},
254      };
255      const auto& it = map_sighash_values.find(sighash);
256      if (it != map_sighash_values.end()) {
257          return it->second;
258      } else {
259          return util::Error{Untranslated(sighash + " is not a valid sighash parameter.")};
260      }
261  }