rawtransaction_util.cpp raw

   1  // Copyright (c) 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 <rpc/rawtransaction_util.h>
   7  
   8  #include <coins.h>
   9  #include <consensus/amount.h>
  10  #include <core_io.h>
  11  #include <key_io.h>
  12  #include <policy/policy.h>
  13  #include <primitives/transaction.h>
  14  #include <rpc/request.h>
  15  #include <rpc/util.h>
  16  #include <script/sign.h>
  17  #include <script/signingprovider.h>
  18  #include <tinyformat.h>
  19  #include <univalue.h>
  20  #include <util/check.h>
  21  #include <util/rbf.h>
  22  #include <util/string.h>
  23  #include <util/strencodings.h>
  24  #include <util/translation.h>
  25  
  26  void AddInputs(CMutableTransaction& rawTx, const UniValue& inputs_in, std::optional<bool> rbf)
  27  {
  28      UniValue inputs;
  29      if (inputs_in.isNull()) {
  30          inputs = UniValue::VARR;
  31      } else {
  32          inputs = inputs_in.get_array();
  33      }
  34  
  35      for (unsigned int idx = 0; idx < inputs.size(); idx++) {
  36          const UniValue& input = inputs[idx];
  37          const UniValue& o = input.get_obj();
  38  
  39          Txid txid = Txid::FromUint256(ParseHashO(o, "txid"));
  40  
  41          const UniValue& vout_v = o.find_value("vout");
  42          if (!vout_v.isNum())
  43              throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
  44          int nOutput = vout_v.getInt<int>();
  45          if (nOutput < 0)
  46              throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
  47  
  48          uint32_t nSequence;
  49  
  50          if (rbf.value_or(true)) {
  51              nSequence = MAX_BIP125_RBF_SEQUENCE; /* CTxIn::SEQUENCE_FINAL - 2 */
  52          } else if (rawTx.nLockTime) {
  53              nSequence = CTxIn::MAX_SEQUENCE_NONFINAL; /* CTxIn::SEQUENCE_FINAL - 1 */
  54          } else {
  55              nSequence = CTxIn::SEQUENCE_FINAL;
  56          }
  57  
  58          // set the sequence number if passed in the parameters object
  59          const UniValue& sequenceObj = o.find_value("sequence");
  60          if (sequenceObj.isNum()) {
  61              int64_t seqNr64 = sequenceObj.getInt<int64_t>();
  62              if (seqNr64 < 0 || seqNr64 > CTxIn::SEQUENCE_FINAL) {
  63                  throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range");
  64              } else {
  65                  nSequence = (uint32_t)seqNr64;
  66              }
  67          }
  68  
  69          CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence);
  70  
  71          rawTx.vin.push_back(in);
  72      }
  73  }
  74  
  75  UniValue NormalizeOutputs(const UniValue& outputs_in)
  76  {
  77      if (outputs_in.isNull()) {
  78          throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output argument must be non-null");
  79      }
  80  
  81      const bool outputs_is_obj = outputs_in.isObject();
  82      UniValue outputs = outputs_is_obj ? outputs_in.get_obj() : outputs_in.get_array();
  83  
  84      if (!outputs_is_obj) {
  85          // Translate array of key-value pairs into dict
  86          UniValue outputs_dict = UniValue(UniValue::VOBJ);
  87          for (size_t i = 0; i < outputs.size(); ++i) {
  88              const UniValue& output = outputs[i];
  89              if (!output.isObject()) {
  90                  throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair not an object as expected");
  91              }
  92              if (output.size() != 1) {
  93                  throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair must contain exactly one key");
  94              }
  95              outputs_dict.pushKVs(output);
  96          }
  97          outputs = std::move(outputs_dict);
  98      }
  99      return outputs;
 100  }
 101  
 102  std::vector<std::pair<CTxDestination, CAmount>> ParseOutputs(const UniValue& outputs)
 103  {
 104      // Duplicate checking
 105      std::set<CTxDestination> destinations;
 106      std::vector<std::pair<CTxDestination, CAmount>> parsed_outputs;
 107      bool has_data{false};
 108      for (const std::string& name_ : outputs.getKeys()) {
 109          if (name_ == "data") {
 110              if (has_data) {
 111                  throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, duplicate key: data");
 112              }
 113              has_data = true;
 114              std::vector<unsigned char> data = ParseHexV(outputs[name_].getValStr(), "Data");
 115              CTxDestination destination{CNoDestination{CScript() << OP_RETURN << data}};
 116              CAmount amount{0};
 117              parsed_outputs.emplace_back(destination, amount);
 118          } else {
 119              CTxDestination destination{DecodeDestination(name_)};
 120              CAmount amount{AmountFromValue(outputs[name_])};
 121              if (!IsValidDestination(destination)) {
 122                  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + name_);
 123              }
 124  
 125              if (!destinations.insert(destination).second) {
 126                  throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_);
 127              }
 128              parsed_outputs.emplace_back(destination, amount);
 129          }
 130      }
 131      return parsed_outputs;
 132  }
 133  
 134  void AddOutputs(CMutableTransaction& rawTx, const UniValue& outputs_in)
 135  {
 136      UniValue outputs(UniValue::VOBJ);
 137      outputs = NormalizeOutputs(outputs_in);
 138  
 139      std::vector<std::pair<CTxDestination, CAmount>> parsed_outputs = ParseOutputs(outputs);
 140      for (const auto& [destination, nAmount] : parsed_outputs) {
 141          CScript scriptPubKey = GetScriptForDestination(destination);
 142  
 143          CTxOut out(nAmount, scriptPubKey);
 144          rawTx.vout.push_back(out);
 145      }
 146  }
 147  
 148  CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, std::optional<bool> rbf, const uint32_t version)
 149  {
 150      CMutableTransaction rawTx;
 151  
 152      if (!locktime.isNull()) {
 153          int64_t nLockTime = locktime.getInt<int64_t>();
 154          if (nLockTime < 0 || nLockTime > LOCKTIME_MAX)
 155              throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range");
 156          rawTx.nLockTime = nLockTime;
 157      }
 158  
 159      if (version < TX_MIN_STANDARD_VERSION || version > TX_MAX_STANDARD_VERSION) {
 160          throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, version out of range(%d~%d)", TX_MIN_STANDARD_VERSION, TX_MAX_STANDARD_VERSION));
 161      }
 162      rawTx.version = version;
 163  
 164      AddInputs(rawTx, inputs_in, rbf);
 165      AddOutputs(rawTx, outputs_in);
 166  
 167      if (rbf.has_value() && rbf.value() && rawTx.vin.size() > 0 && !SignalsOptInRBF(CTransaction(rawTx))) {
 168          throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option");
 169      }
 170  
 171      return rawTx;
 172  }
 173  
 174  /** Pushes a JSON object for script verification or signing errors to vErrorsRet. */
 175  static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::string& strMessage)
 176  {
 177      UniValue entry(UniValue::VOBJ);
 178      entry.pushKV("txid", txin.prevout.hash.ToString());
 179      entry.pushKV("vout", txin.prevout.n);
 180      UniValue witness(UniValue::VARR);
 181      for (unsigned int i = 0; i < txin.scriptWitness.stack.size(); i++) {
 182          witness.push_back(HexStr(txin.scriptWitness.stack[i]));
 183      }
 184      entry.pushKV("witness", std::move(witness));
 185      entry.pushKV("scriptSig", HexStr(txin.scriptSig));
 186      entry.pushKV("sequence", txin.nSequence);
 187      entry.pushKV("error", strMessage);
 188      vErrorsRet.push_back(std::move(entry));
 189  }
 190  
 191  void ParsePrevouts(const UniValue& prevTxsUnival, FlatSigningProvider* keystore, std::map<COutPoint, Coin>& coins)
 192  {
 193      if (!prevTxsUnival.isNull()) {
 194          const UniValue& prevTxs = prevTxsUnival.get_array();
 195          for (unsigned int idx = 0; idx < prevTxs.size(); ++idx) {
 196              const UniValue& p = prevTxs[idx];
 197              if (!p.isObject()) {
 198                  throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}");
 199              }
 200  
 201              const UniValue& prevOut = p.get_obj();
 202  
 203              RPCTypeCheckObj(prevOut,
 204                  {
 205                      {"txid", UniValueType(UniValue::VSTR)},
 206                      {"vout", UniValueType(UniValue::VNUM)},
 207                      {"scriptPubKey", UniValueType(UniValue::VSTR)},
 208                  });
 209  
 210              Txid txid = Txid::FromUint256(ParseHashO(prevOut, "txid"));
 211  
 212              int nOut = prevOut.find_value("vout").getInt<int>();
 213              if (nOut < 0) {
 214                  throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout cannot be negative");
 215              }
 216  
 217              COutPoint out(txid, nOut);
 218              std::vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey"));
 219              CScript scriptPubKey(pkData.begin(), pkData.end());
 220  
 221              {
 222                  auto coin = coins.find(out);
 223                  if (coin != coins.end() && !coin->second.IsSpent() && coin->second.out.scriptPubKey != scriptPubKey) {
 224                      std::string err("Previous output scriptPubKey mismatch:\n");
 225                      err = err + ScriptToAsmStr(coin->second.out.scriptPubKey) + "\nvs:\n"+
 226                          ScriptToAsmStr(scriptPubKey);
 227                      throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
 228                  }
 229                  Coin newcoin;
 230                  newcoin.out.scriptPubKey = scriptPubKey;
 231                  newcoin.out.nValue = MAX_MONEY;
 232                  if (prevOut.exists("amount")) {
 233                      newcoin.out.nValue = AmountFromValue(prevOut.find_value("amount"));
 234                  }
 235                  newcoin.nHeight = 1;
 236                  coins[out] = std::move(newcoin);
 237              }
 238  
 239              // if redeemScript and private keys were given, add redeemScript to the keystore so it can be signed
 240              const bool is_p2sh = scriptPubKey.IsPayToScriptHash();
 241              const bool is_p2wsh = scriptPubKey.IsPayToWitnessScriptHash();
 242              if (keystore && (is_p2sh || is_p2wsh)) {
 243                  RPCTypeCheckObj(prevOut,
 244                      {
 245                          {"redeemScript", UniValueType(UniValue::VSTR)},
 246                          {"witnessScript", UniValueType(UniValue::VSTR)},
 247                      }, true);
 248                  const UniValue& rs{prevOut.find_value("redeemScript")};
 249                  const UniValue& ws{prevOut.find_value("witnessScript")};
 250                  if (rs.isNull() && ws.isNull()) {
 251                      throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing redeemScript/witnessScript");
 252                  }
 253  
 254                  // work from witnessScript when possible
 255                  std::vector<unsigned char> scriptData(!ws.isNull() ? ParseHexV(ws, "witnessScript") : ParseHexV(rs, "redeemScript"));
 256                  CScript script(scriptData.begin(), scriptData.end());
 257                  keystore->scripts.emplace(CScriptID(script), script);
 258                  // Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
 259                  // This is done for redeemScript only for compatibility, it is encouraged to use the explicit witnessScript field instead.
 260                  CScript witness_output_script{GetScriptForDestination(WitnessV0ScriptHash(script))};
 261                  keystore->scripts.emplace(CScriptID(witness_output_script), witness_output_script);
 262  
 263                  if (!ws.isNull() && !rs.isNull()) {
 264                      // if both witnessScript and redeemScript are provided,
 265                      // they should either be the same (for backwards compat),
 266                      // or the redeemScript should be the encoded form of
 267                      // the witnessScript (ie, for p2sh-p2wsh)
 268                      if (ws.get_str() != rs.get_str()) {
 269                          std::vector<unsigned char> redeemScriptData(ParseHexV(rs, "redeemScript"));
 270                          CScript redeemScript(redeemScriptData.begin(), redeemScriptData.end());
 271                          if (redeemScript != witness_output_script) {
 272                              throw JSONRPCError(RPC_INVALID_PARAMETER, "redeemScript does not correspond to witnessScript");
 273                          }
 274                      }
 275                  }
 276  
 277                  if (is_p2sh) {
 278                      const CTxDestination p2sh{ScriptHash(script)};
 279                      const CTxDestination p2sh_p2wsh{ScriptHash(witness_output_script)};
 280                      if (scriptPubKey == GetScriptForDestination(p2sh)) {
 281                          // traditional p2sh; arguably an error if
 282                          // we got here with rs.IsNull(), because
 283                          // that means the p2sh script was specified
 284                          // via witnessScript param, but for now
 285                          // we'll just quietly accept it
 286                      } else if (scriptPubKey == GetScriptForDestination(p2sh_p2wsh)) {
 287                          // p2wsh encoded as p2sh; ideally the witness
 288                          // script was specified in the witnessScript
 289                          // param, but also support specifying it via
 290                          // redeemScript param for backwards compat
 291                          // (in which case ws.IsNull() == true)
 292                      } else {
 293                          // otherwise, can't generate scriptPubKey from
 294                          // either script, so we got unusable parameters
 295                          throw JSONRPCError(RPC_INVALID_PARAMETER, "redeemScript/witnessScript does not match scriptPubKey");
 296                      }
 297                  } else if (is_p2wsh) {
 298                      // plain p2wsh; could throw an error if script
 299                      // was specified by redeemScript rather than
 300                      // witnessScript (ie, ws.IsNull() == true), but
 301                      // accept it for backwards compat
 302                      const CTxDestination p2wsh{WitnessV0ScriptHash(script)};
 303                      if (scriptPubKey != GetScriptForDestination(p2wsh)) {
 304                          throw JSONRPCError(RPC_INVALID_PARAMETER, "redeemScript/witnessScript does not match scriptPubKey");
 305                      }
 306                  }
 307              }
 308          }
 309      }
 310  }
 311  
 312  void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType, UniValue& result)
 313  {
 314      std::optional<int> nHashType = ParseSighashString(hashType);
 315      if (!nHashType) {
 316          nHashType = SIGHASH_DEFAULT;
 317      }
 318  
 319      // Script verification errors
 320      std::map<int, bilingual_str> input_errors;
 321  
 322      bool complete = SignTransaction(mtx, keystore, coins, {.sighash_type = *nHashType}, input_errors);
 323      SignTransactionResultToJSON(mtx, complete, coins, input_errors, result);
 324  }
 325  
 326  void SignTransactionResultToJSON(CMutableTransaction& mtx, bool complete, const std::map<COutPoint, Coin>& coins, const std::map<int, bilingual_str>& input_errors, UniValue& result)
 327  {
 328      // Make errors UniValue
 329      UniValue vErrors(UniValue::VARR);
 330      for (const auto& err_pair : input_errors) {
 331          if (err_pair.second.original == "Missing amount") {
 332              // This particular error needs to be an exception for some reason
 333              throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing amount for %s", coins.at(mtx.vin.at(err_pair.first).prevout).out.ToString()));
 334          }
 335          TxInErrorToJSON(mtx.vin.at(err_pair.first), vErrors, err_pair.second.original);
 336      }
 337  
 338      result.pushKV("hex", EncodeHexTx(CTransaction(mtx)));
 339      result.pushKV("complete", complete);
 340      if (!vErrors.empty()) {
 341          if (result.exists("errors")) {
 342              vErrors.push_backV(result["errors"].getValues());
 343          }
 344          result.pushKV("errors", std::move(vErrors));
 345      }
 346  }
 347  
 348  std::vector<RPCResult> TxDoc(const TxDocOptions& opts)
 349  {
 350      CHECK_NONFATAL(!opts.fee_doc || opts.fee);
 351      CHECK_NONFATAL(!opts.prevout_doc || opts.prevout);
 352      CHECK_NONFATAL(!opts.vin_item_doc || opts.vin_inner_elision);
 353      CHECK_NONFATAL(opts.elision_mode != ElisionMode::WithSummary || opts.elision_summary.has_value());
 354  
 355      const std::string fee_doc{opts.fee_doc.value_or(
 356          "transaction fee in " + CURRENCY_UNIT + ", omitted if block undo data is not available")};
 357      const std::string prevout_doc{opts.prevout_doc.value_or(
 358          "The previous output, omitted if block undo data is not available")};
 359      const std::string vin_item_doc{opts.vin_item_doc.value_or("utxo being spent")};
 360  
 361      auto vin_inner = std::vector<RPCResult>{
 362          {RPCResult::Type::STR_HEX, "coinbase", /*optional=*/true, "The coinbase value (only if coinbase transaction)"},
 363          {RPCResult::Type::STR_HEX, "txid", /*optional=*/true, "The transaction id (if not coinbase transaction)"},
 364          {RPCResult::Type::NUM, "vout", /*optional=*/true, "The output number (if not coinbase transaction)"},
 365          {RPCResult::Type::OBJ, "scriptSig", /*optional=*/true, "The script (if not coinbase transaction)",
 366          {
 367              {RPCResult::Type::STR, "asm", "Disassembly of the signature script"},
 368              {RPCResult::Type::STR_HEX, "hex", "The raw signature script bytes, hex-encoded"},
 369          }},
 370          {RPCResult::Type::ARR, "txinwitness", /*optional=*/true, "",
 371          {
 372              {RPCResult::Type::STR_HEX, "hex", "hex-encoded witness data (if any)"},
 373          }},
 374      };
 375      if (opts.prevout) {
 376          vin_inner.emplace_back(
 377              RPCResult::Type::OBJ, "prevout", opts.prevout_optional, prevout_doc,
 378              std::vector<RPCResult>{
 379                  {RPCResult::Type::BOOL, "generated", "Coinbase or not"},
 380                  {RPCResult::Type::NUM, "height", "The height of the prevout"},
 381                  {RPCResult::Type::STR_AMOUNT, "value", "The value in " + CURRENCY_UNIT},
 382                  {RPCResult::Type::OBJ, "scriptPubKey", "", ScriptPubKeyDoc()},
 383              }
 384          );
 385      }
 386      vin_inner.emplace_back(RPCResult::Type::NUM, "sequence", "The script sequence number");
 387  
 388      if (opts.vin_inner_elision) {
 389          vin_inner = ElideGroup(std::move(vin_inner), *opts.vin_inner_elision);
 390          if (opts.prevout) {
 391              // prevout remains visible even when other fields are elided
 392              std::vector<RPCResult> new_vin;
 393              new_vin.reserve(vin_inner.size());
 394              for (const auto& r : vin_inner) {
 395                  if (r.m_key_name == "prevout") {
 396                      RPCResultOptions unopts = r.m_opts;
 397                      unopts.print_elision = HelpElisionNone{};
 398                      new_vin.emplace_back(r, std::move(unopts));
 399                  } else {
 400                      new_vin.push_back(r);
 401                  }
 402              }
 403              vin_inner = std::move(new_vin);
 404          }
 405      }
 406  
 407      auto fields = std::vector<RPCResult>{
 408          {RPCResult::Type::STR_HEX, "txid", opts.txid_field_doc},
 409          {RPCResult::Type::STR_HEX, "hash", "The transaction hash (differs from txid for witness transactions)"},
 410          {RPCResult::Type::NUM, "size", "The serialized transaction size"},
 411          {RPCResult::Type::NUM, "vsize", "The virtual transaction size (differs from size for witness transactions)"},
 412          {RPCResult::Type::NUM, "weight", "The transaction's weight (between vsize*4-3 and vsize*4)"},
 413          {RPCResult::Type::NUM, "version", "The version"},
 414          {RPCResult::Type::NUM_TIME, "locktime", "The lock time"},
 415          {RPCResult::Type::ARR, "vin", "",
 416          {
 417              {RPCResult::Type::OBJ, "", opts.vin_inner_elision ? vin_item_doc : "", std::move(vin_inner)},
 418          }},
 419          {RPCResult::Type::ARR, "vout", "",
 420          {
 421              {RPCResult::Type::OBJ, "", "", Cat(
 422                  {
 423                      {RPCResult::Type::STR_AMOUNT, "value", "The value in " + CURRENCY_UNIT},
 424                      {RPCResult::Type::NUM, "n", "index"},
 425                      {RPCResult::Type::OBJ, "scriptPubKey", "", ScriptPubKeyDoc()},
 426                  },
 427                  opts.wallet ?
 428                      std::vector<RPCResult>{{RPCResult::Type::BOOL, "ischange", /*optional=*/true, "Output script is change (only present if true)"}} :
 429                      std::vector<RPCResult>{}
 430              )},
 431          }},
 432      };
 433  
 434      if (opts.fee) fields.emplace_back(RPCResult::Type::NUM, "fee", /*optional=*/true, fee_doc);
 435      if (opts.hex) fields.emplace_back(RPCResult::Type::STR_HEX, "hex", "The hex-encoded transaction data");
 436  
 437      if (opts.elision_mode != ElisionMode::None) {
 438          const bool silent = opts.elision_mode == ElisionMode::Silent;
 439          std::vector<RPCResult> new_fields;
 440          new_fields.reserve(fields.size());
 441          bool first = true;
 442          for (const auto& f : fields) {
 443              if (!silent && f.m_key_name == "fee") {
 444                  new_fields.push_back(f);
 445                  continue;
 446              }
 447              if (f.m_key_name == "vin" && opts.vin_inner_elision) {
 448                  new_fields.push_back(f);
 449                  continue;
 450              }
 451              if (!silent && first) {
 452                  RPCResultOptions eopts = f.m_opts;
 453                  eopts.print_elision = opts.elision_summary.value_or("");
 454                  new_fields.emplace_back(f, std::move(eopts));
 455                  first = false;
 456              } else {
 457                  RPCResultOptions eopts = f.m_opts;
 458                  eopts.print_elision = HelpElisionSkip{};
 459                  new_fields.emplace_back(f, std::move(eopts));
 460              }
 461          }
 462          fields = std::move(new_fields);
 463      }
 464  
 465      return fields;
 466  }
 467