limenka-tx.cpp raw

   1  // Copyright (c) 2009-2022 The Limenka 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 <limenka-build-config.h> // IWYU pragma: keep
   6  
   7  #include <chainparamsbase.h>
   8  #include <clientversion.h>
   9  #include <coins.h>
  10  #include <common/args.h>
  11  #include <common/system.h>
  12  #include <compat/compat.h>
  13  #include <consensus/amount.h>
  14  #include <consensus/consensus.h>
  15  #include <core_io.h>
  16  #include <key_io.h>
  17  #include <policy/policy.h>
  18  #include <primitives/transaction.h>
  19  #include <script/script.h>
  20  #include <script/sign.h>
  21  #include <script/signingprovider.h>
  22  #include <univalue.h>
  23  #include <util/exception.h>
  24  #include <util/fs.h>
  25  #include <util/moneystr.h>
  26  #include <util/rbf.h>
  27  #include <util/strencodings.h>
  28  #include <util/string.h>
  29  #include <util/translation.h>
  30  
  31  #include <cstdio>
  32  #include <functional>
  33  #include <memory>
  34  
  35  using util::SplitString;
  36  using util::ToString;
  37  using util::TrimString;
  38  using util::TrimStringView;
  39  
  40  static bool fCreateBlank;
  41  static std::map<std::string,UniValue> registers;
  42  static const int CONTINUE_EXECUTION=-1;
  43  
  44  const TranslateFn G_TRANSLATION_FUN{nullptr};
  45  
  46  static void SetupLimenkaTxArgs(ArgsManager &argsman)
  47  {
  48      SetupHelpOptions(argsman);
  49  
  50      argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
  51      argsman.AddArg("-create", "Create new, empty TX.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
  52      argsman.AddArg("-json", "Select JSON output", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
  53      argsman.AddArg("-txid", "Output only the hex-encoded transaction id of the resultant transaction.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
  54      SetupChainParamsBaseOptions(argsman);
  55  
  56      argsman.AddArg("delin=N", "Delete input N from TX", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
  57      argsman.AddArg("delout=N", "Delete output N from TX", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
  58      argsman.AddArg("in=TXID:VOUT(:SEQUENCE_NUMBER)", "Add input to TX", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
  59      argsman.AddArg("locktime=N", "Set TX lock time to N", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
  60      argsman.AddArg("nversion=N", "Set TX version to N", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
  61      argsman.AddArg("outaddr=VALUE:ADDRESS", "Add address-based output to TX", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
  62      argsman.AddArg("outdata=[VALUE:]DATA", "Add data-based output to TX", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
  63      argsman.AddArg("outmultisig=VALUE:REQUIRED:PUBKEYS:PUBKEY1:PUBKEY2:....[:FLAGS]", "Add Pay To n-of-m Multi-sig output to TX. n = REQUIRED, m = PUBKEYS. "
  64          "Optionally add the \"W\" flag to produce a pay-to-witness-script-hash output. "
  65          "Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
  66      argsman.AddArg("outpubkey=VALUE:PUBKEY[:FLAGS]", "Add pay-to-pubkey output to TX. "
  67          "Optionally add the \"W\" flag to produce a pay-to-witness-pubkey-hash output. "
  68          "Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
  69      argsman.AddArg("outscript=VALUE:SCRIPT[:FLAGS]", "Add raw script output to TX. "
  70          "Optionally add the \"W\" flag to produce a pay-to-witness-script-hash output. "
  71          "Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
  72      argsman.AddArg("replaceable(=N)", "Sets Replace-By-Fee (RBF) opt-in sequence number for input N. "
  73          "If N is not provided, the command attempts to opt-in all available inputs for RBF. "
  74          "If the transaction has no inputs, this option is ignored.", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
  75      argsman.AddArg("sign=SIGHASH-FLAGS", "Add zero or more signatures to transaction. "
  76          "This command requires JSON registers:"
  77          "prevtxs=JSON object, "
  78          "privatekeys=JSON object. "
  79          "See signrawtransactionwithkey docs for format of sighash flags, JSON objects.", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
  80  
  81      argsman.AddArg("load=NAME:FILENAME", "Load JSON file FILENAME into register NAME", ArgsManager::ALLOW_ANY, OptionsCategory::REGISTER_COMMANDS);
  82      argsman.AddArg("set=NAME:JSON-STRING", "Set register NAME to given JSON-STRING", ArgsManager::ALLOW_ANY, OptionsCategory::REGISTER_COMMANDS);
  83  }
  84  
  85  //
  86  // This function returns either one of EXIT_ codes when it's expected to stop the process or
  87  // CONTINUE_EXECUTION when it's expected to continue further.
  88  //
  89  static int AppInitRawTx(int argc, char* argv[])
  90  {
  91      SetupLimenkaTxArgs(gArgs);
  92      std::string error;
  93      if (!gArgs.ParseParameters(argc, argv, error)) {
  94          tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error);
  95          return EXIT_FAILURE;
  96      }
  97  
  98      // Check for chain settings (Params() calls are only valid after this clause)
  99      try {
 100          SelectParams(gArgs.GetChainType());
 101      } catch (const std::exception& e) {
 102          tfm::format(std::cerr, "Error: %s\n", e.what());
 103          return EXIT_FAILURE;
 104      }
 105  
 106      fCreateBlank = gArgs.GetBoolArg("-create", false);
 107  
 108      if (argc < 2 || HelpRequested(gArgs) || gArgs.GetBoolArg("-version", false)) {
 109          // First part of help message is specific to this utility
 110          std::string strUsage = CLIENT_NAME " limenka-tx utility version " + FormatFullVersion() + "\n";
 111  
 112          if (gArgs.GetBoolArg("-version", false)) {
 113              strUsage += FormatParagraph(LicenseInfo());
 114          } else {
 115              strUsage += "\n"
 116                  "The limenka-tx tool is used for creating and modifying limenka transactions.\n\n"
 117                  "limenka-tx can be used with \"<hex-tx> [commands]\" to update a hex-encoded limenka transaction, or with \"-create [commands]\" to create a hex-encoded limenka transaction.\n"
 118                  "\n"
 119                  "Usage: limenka-tx [options] <hex-tx> [commands]\n"
 120                  "or:    limenka-tx [options] -create [commands]\n"
 121                  "\n";
 122              strUsage += gArgs.GetHelpMessage();
 123          }
 124  
 125          tfm::format(std::cout, "%s", strUsage);
 126  
 127          if (argc < 2) {
 128              tfm::format(std::cerr, "Error: too few parameters\n");
 129              return EXIT_FAILURE;
 130          }
 131          return EXIT_SUCCESS;
 132      }
 133      return CONTINUE_EXECUTION;
 134  }
 135  
 136  static void RegisterSetJson(const std::string& key, const std::string& rawJson)
 137  {
 138      UniValue val;
 139      if (!val.read(rawJson)) {
 140          std::string strErr = "Cannot parse JSON for key " + key;
 141          throw std::runtime_error(strErr);
 142      }
 143  
 144      registers[key] = val;
 145  }
 146  
 147  static void RegisterSet(const std::string& strInput)
 148  {
 149      // separate NAME:VALUE in string
 150      size_t pos = strInput.find(':');
 151      if ((pos == std::string::npos) ||
 152          (pos == 0) ||
 153          (pos == (strInput.size() - 1)))
 154          throw std::runtime_error("Register input requires NAME:VALUE");
 155  
 156      std::string key = strInput.substr(0, pos);
 157      std::string valStr = strInput.substr(pos + 1, std::string::npos);
 158  
 159      RegisterSetJson(key, valStr);
 160  }
 161  
 162  static void RegisterLoad(const std::string& strInput)
 163  {
 164      // separate NAME:FILENAME in string
 165      size_t pos = strInput.find(':');
 166      if ((pos == std::string::npos) ||
 167          (pos == 0) ||
 168          (pos == (strInput.size() - 1)))
 169          throw std::runtime_error("Register load requires NAME:FILENAME");
 170  
 171      std::string key = strInput.substr(0, pos);
 172      std::string filename = strInput.substr(pos + 1, std::string::npos);
 173  
 174      FILE *f = fsbridge::fopen(filename.c_str(), "r");
 175      if (!f) {
 176          std::string strErr = "Cannot open file " + filename;
 177          throw std::runtime_error(strErr);
 178      }
 179  
 180      // load file chunks into one big buffer
 181      std::string valStr;
 182      while ((!feof(f)) && (!ferror(f))) {
 183          char buf[4096];
 184          int bread = fread(buf, 1, sizeof(buf), f);
 185          if (bread <= 0)
 186              break;
 187  
 188          valStr.insert(valStr.size(), buf, bread);
 189      }
 190  
 191      int error = ferror(f);
 192      fclose(f);
 193  
 194      if (error) {
 195          std::string strErr = "Error reading file " + filename;
 196          throw std::runtime_error(strErr);
 197      }
 198  
 199      // evaluate as JSON buffer register
 200      RegisterSetJson(key, valStr);
 201  }
 202  
 203  static CAmount ExtractAndValidateValue(const std::string& strValue)
 204  {
 205      if (std::optional<CAmount> parsed = ParseMoney(strValue)) {
 206          return parsed.value();
 207      } else {
 208          throw std::runtime_error("invalid TX output value");
 209      }
 210  }
 211  
 212  static void MutateTxVersion(CMutableTransaction& tx, const std::string& cmdVal)
 213  {
 214      uint32_t newVersion;
 215      if (!ParseUInt32(cmdVal, &newVersion) || newVersion < 1 || newVersion > TX_MAX_STANDARD_VERSION) {
 216          throw std::runtime_error("Invalid TX version requested: '" + cmdVal + "'");
 217      }
 218  
 219      tx.version = newVersion;
 220  }
 221  
 222  static void MutateTxLocktime(CMutableTransaction& tx, const std::string& cmdVal)
 223  {
 224      int64_t newLocktime;
 225      if (!ParseInt64(cmdVal, &newLocktime) || newLocktime < 0LL || newLocktime > 0xffffffffLL)
 226          throw std::runtime_error("Invalid TX locktime requested: '" + cmdVal + "'");
 227  
 228      tx.nLockTime = (unsigned int) newLocktime;
 229  }
 230  
 231  static void MutateTxRBFOptIn(CMutableTransaction& tx, const std::string& strInIdx)
 232  {
 233      // parse requested index
 234      int64_t inIdx = -1;
 235      if (strInIdx != "" && (!ParseInt64(strInIdx, &inIdx) || inIdx < 0 || inIdx >= static_cast<int64_t>(tx.vin.size()))) {
 236          throw std::runtime_error("Invalid TX input index '" + strInIdx + "'");
 237      }
 238  
 239      // set the nSequence to MAX_INT - 2 (= RBF opt in flag)
 240      int cnt = 0;
 241      for (CTxIn& txin : tx.vin) {
 242          if (strInIdx == "" || cnt == inIdx) {
 243              if (txin.nSequence > MAX_BIP125_RBF_SEQUENCE) {
 244                  txin.nSequence = MAX_BIP125_RBF_SEQUENCE;
 245              }
 246          }
 247          ++cnt;
 248      }
 249  }
 250  
 251  template <typename T>
 252  static T TrimAndParse(const std::string& int_str, const std::string& err)
 253  {
 254      const auto parsed{ToIntegral<T>(TrimStringView(int_str))};
 255      if (!parsed.has_value()) {
 256          throw std::runtime_error(err + " '" + int_str + "'");
 257      }
 258      return parsed.value();
 259  }
 260  
 261  static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInput)
 262  {
 263      std::vector<std::string> vStrInputParts = SplitString(strInput, ':');
 264  
 265      // separate TXID:VOUT in string
 266      if (vStrInputParts.size()<2)
 267          throw std::runtime_error("TX input missing separator");
 268  
 269      // extract and validate TXID
 270      auto txid{Txid::FromHex(vStrInputParts[0])};
 271      if (!txid) {
 272          throw std::runtime_error("invalid TX input txid");
 273      }
 274  
 275      static const unsigned int minTxOutSz = 9;
 276      static const unsigned int maxVout = MAX_BLOCK_WEIGHT / (WITNESS_SCALE_FACTOR * minTxOutSz);
 277  
 278      // extract and validate vout
 279      const std::string& strVout = vStrInputParts[1];
 280      int64_t vout;
 281      if (!ParseInt64(strVout, &vout) || vout < 0 || vout > static_cast<int64_t>(maxVout))
 282          throw std::runtime_error("invalid TX input vout '" + strVout + "'");
 283  
 284      // extract the optional sequence number
 285      uint32_t nSequenceIn = CTxIn::SEQUENCE_FINAL;
 286      if (vStrInputParts.size() > 2) {
 287          nSequenceIn = TrimAndParse<uint32_t>(vStrInputParts.at(2), "invalid TX sequence id");
 288      }
 289  
 290      // append to transaction input list
 291      CTxIn txin(*txid, vout, CScript(), nSequenceIn);
 292      tx.vin.push_back(txin);
 293  }
 294  
 295  static void MutateTxAddOutAddr(CMutableTransaction& tx, const std::string& strInput)
 296  {
 297      // Separate into VALUE:ADDRESS
 298      std::vector<std::string> vStrInputParts = SplitString(strInput, ':');
 299  
 300      if (vStrInputParts.size() != 2)
 301          throw std::runtime_error("TX output missing or too many separators");
 302  
 303      // Extract and validate VALUE
 304      CAmount value = ExtractAndValidateValue(vStrInputParts[0]);
 305  
 306      // extract and validate ADDRESS
 307      const std::string& strAddr = vStrInputParts[1];
 308      CTxDestination destination = DecodeDestination(strAddr);
 309      if (!IsValidDestination(destination)) {
 310          throw std::runtime_error("invalid TX output address");
 311      }
 312      CScript scriptPubKey = GetScriptForDestination(destination);
 313  
 314      // construct TxOut, append to transaction output list
 315      CTxOut txout(value, scriptPubKey);
 316      tx.vout.push_back(txout);
 317  }
 318  
 319  static void MutateTxAddOutPubKey(CMutableTransaction& tx, const std::string& strInput)
 320  {
 321      // Separate into VALUE:PUBKEY[:FLAGS]
 322      std::vector<std::string> vStrInputParts = SplitString(strInput, ':');
 323  
 324      if (vStrInputParts.size() < 2 || vStrInputParts.size() > 3)
 325          throw std::runtime_error("TX output missing or too many separators");
 326  
 327      // Extract and validate VALUE
 328      CAmount value = ExtractAndValidateValue(vStrInputParts[0]);
 329  
 330      // Extract and validate PUBKEY
 331      CPubKey pubkey(ParseHex(vStrInputParts[1]));
 332      if (!pubkey.IsFullyValid())
 333          throw std::runtime_error("invalid TX output pubkey");
 334      CScript scriptPubKey = GetScriptForRawPubKey(pubkey);
 335  
 336      // Extract and validate FLAGS
 337      bool bSegWit = false;
 338      bool bScriptHash = false;
 339      if (vStrInputParts.size() == 3) {
 340          const std::string& flags = vStrInputParts[2];
 341          bSegWit = (flags.find('W') != std::string::npos);
 342          bScriptHash = (flags.find('S') != std::string::npos);
 343      }
 344  
 345      if (bSegWit) {
 346          if (!pubkey.IsCompressed()) {
 347              throw std::runtime_error("Uncompressed pubkeys are not useable for SegWit outputs");
 348          }
 349          // Build a P2WPKH script
 350          scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(pubkey));
 351      }
 352      if (bScriptHash) {
 353          // Get the ID for the script, and then construct a P2SH destination for it.
 354          scriptPubKey = GetScriptForDestination(ScriptHash(scriptPubKey));
 355      }
 356  
 357      // construct TxOut, append to transaction output list
 358      CTxOut txout(value, scriptPubKey);
 359      tx.vout.push_back(txout);
 360  }
 361  
 362  static void MutateTxAddOutMultiSig(CMutableTransaction& tx, const std::string& strInput)
 363  {
 364      // Separate into VALUE:REQUIRED:NUMKEYS:PUBKEY1:PUBKEY2:....[:FLAGS]
 365      std::vector<std::string> vStrInputParts = SplitString(strInput, ':');
 366  
 367      // Check that there are enough parameters
 368      if (vStrInputParts.size()<3)
 369          throw std::runtime_error("Not enough multisig parameters");
 370  
 371      // Extract and validate VALUE
 372      CAmount value = ExtractAndValidateValue(vStrInputParts[0]);
 373  
 374      // Extract REQUIRED
 375      const uint32_t required{TrimAndParse<uint32_t>(vStrInputParts.at(1), "invalid multisig required number")};
 376  
 377      // Extract NUMKEYS
 378      const uint32_t numkeys{TrimAndParse<uint32_t>(vStrInputParts.at(2), "invalid multisig total number")};
 379  
 380      // Validate there are the correct number of pubkeys
 381      if (vStrInputParts.size() < numkeys + 3)
 382          throw std::runtime_error("incorrect number of multisig pubkeys");
 383  
 384      if (required < 1 || required > MAX_PUBKEYS_PER_MULTISIG || numkeys < 1 || numkeys > MAX_PUBKEYS_PER_MULTISIG || numkeys < required)
 385          throw std::runtime_error("multisig parameter mismatch. Required " \
 386                              + ToString(required) + " of " + ToString(numkeys) + "signatures.");
 387  
 388      // extract and validate PUBKEYs
 389      std::vector<CPubKey> pubkeys;
 390      for(int pos = 1; pos <= int(numkeys); pos++) {
 391          CPubKey pubkey(ParseHex(vStrInputParts[pos + 2]));
 392          if (!pubkey.IsFullyValid())
 393              throw std::runtime_error("invalid TX output pubkey");
 394          pubkeys.push_back(pubkey);
 395      }
 396  
 397      // Extract FLAGS
 398      bool bSegWit = false;
 399      bool bScriptHash = false;
 400      if (vStrInputParts.size() == numkeys + 4) {
 401          const std::string& flags = vStrInputParts.back();
 402          bSegWit = (flags.find('W') != std::string::npos);
 403          bScriptHash = (flags.find('S') != std::string::npos);
 404      }
 405      else if (vStrInputParts.size() > numkeys + 4) {
 406          // Validate that there were no more parameters passed
 407          throw std::runtime_error("Too many parameters");
 408      }
 409  
 410      CScript scriptPubKey = GetScriptForMultisig(required, pubkeys);
 411  
 412      if (bSegWit) {
 413          for (const CPubKey& pubkey : pubkeys) {
 414              if (!pubkey.IsCompressed()) {
 415                  throw std::runtime_error("Uncompressed pubkeys are not useable for SegWit outputs");
 416              }
 417          }
 418          // Build a P2WSH with the multisig script
 419          scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(scriptPubKey));
 420      }
 421      if (bScriptHash) {
 422          if (scriptPubKey.size() > MAX_SCRIPT_ELEMENT_SIZE) {
 423              throw std::runtime_error(strprintf(
 424                          "redeemScript exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_ELEMENT_SIZE));
 425          }
 426          // Get the ID for the script, and then construct a P2SH destination for it.
 427          scriptPubKey = GetScriptForDestination(ScriptHash(scriptPubKey));
 428      }
 429  
 430      // construct TxOut, append to transaction output list
 431      CTxOut txout(value, scriptPubKey);
 432      tx.vout.push_back(txout);
 433  }
 434  
 435  static void MutateTxAddOutData(CMutableTransaction& tx, const std::string& strInput)
 436  {
 437      CAmount value = 0;
 438  
 439      // separate [VALUE:]DATA in string
 440      size_t pos = strInput.find(':');
 441  
 442      if (pos==0)
 443          throw std::runtime_error("TX output value not specified");
 444  
 445      if (pos == std::string::npos) {
 446          pos = 0;
 447      } else {
 448          // Extract and validate VALUE
 449          value = ExtractAndValidateValue(strInput.substr(0, pos));
 450          ++pos;
 451      }
 452  
 453      // extract and validate DATA
 454      const std::string strData{strInput.substr(pos, std::string::npos)};
 455  
 456      if (!IsHex(strData))
 457          throw std::runtime_error("invalid TX output data");
 458  
 459      std::vector<unsigned char> data = ParseHex(strData);
 460  
 461      CTxOut txout(value, CScript() << OP_RETURN << data);
 462      tx.vout.push_back(txout);
 463  }
 464  
 465  static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& strInput)
 466  {
 467      // separate VALUE:SCRIPT[:FLAGS]
 468      std::vector<std::string> vStrInputParts = SplitString(strInput, ':');
 469      if (vStrInputParts.size() < 2)
 470          throw std::runtime_error("TX output missing separator");
 471  
 472      // Extract and validate VALUE
 473      CAmount value = ExtractAndValidateValue(vStrInputParts[0]);
 474  
 475      // extract and validate script
 476      const std::string& strScript = vStrInputParts[1];
 477      CScript scriptPubKey = ParseScript(strScript);
 478  
 479      // Extract FLAGS
 480      bool bSegWit = false;
 481      bool bScriptHash = false;
 482      if (vStrInputParts.size() == 3) {
 483          const std::string& flags = vStrInputParts.back();
 484          bSegWit = (flags.find('W') != std::string::npos);
 485          bScriptHash = (flags.find('S') != std::string::npos);
 486      }
 487  
 488      if (scriptPubKey.size() > MAX_SCRIPT_SIZE) {
 489          throw std::runtime_error(strprintf(
 490                      "script exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_SIZE));
 491      }
 492  
 493      if (bSegWit) {
 494          scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(scriptPubKey));
 495      }
 496      if (bScriptHash) {
 497          if (scriptPubKey.size() > MAX_SCRIPT_ELEMENT_SIZE) {
 498              throw std::runtime_error(strprintf(
 499                          "redeemScript exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_ELEMENT_SIZE));
 500          }
 501          scriptPubKey = GetScriptForDestination(ScriptHash(scriptPubKey));
 502      }
 503  
 504      // construct TxOut, append to transaction output list
 505      CTxOut txout(value, scriptPubKey);
 506      tx.vout.push_back(txout);
 507  }
 508  
 509  static void MutateTxDelInput(CMutableTransaction& tx, const std::string& strInIdx)
 510  {
 511      // parse requested deletion index
 512      int64_t inIdx;
 513      if (!ParseInt64(strInIdx, &inIdx) || inIdx < 0 || inIdx >= static_cast<int64_t>(tx.vin.size())) {
 514          throw std::runtime_error("Invalid TX input index '" + strInIdx + "'");
 515      }
 516  
 517      // delete input from transaction
 518      tx.vin.erase(tx.vin.begin() + inIdx);
 519  }
 520  
 521  static void MutateTxDelOutput(CMutableTransaction& tx, const std::string& strOutIdx)
 522  {
 523      // parse requested deletion index
 524      int64_t outIdx;
 525      if (!ParseInt64(strOutIdx, &outIdx) || outIdx < 0 || outIdx >= static_cast<int64_t>(tx.vout.size())) {
 526          throw std::runtime_error("Invalid TX output index '" + strOutIdx + "'");
 527      }
 528  
 529      // delete output from transaction
 530      tx.vout.erase(tx.vout.begin() + outIdx);
 531  }
 532  
 533  static const unsigned int N_SIGHASH_OPTS = 7;
 534  static const struct {
 535      const char *flagStr;
 536      int flags;
 537  } sighashOptions[N_SIGHASH_OPTS] = {
 538      {"DEFAULT", SIGHASH_DEFAULT},
 539      {"ALL", SIGHASH_ALL},
 540      {"NONE", SIGHASH_NONE},
 541      {"SINGLE", SIGHASH_SINGLE},
 542      {"ALL|ANYONECANPAY", SIGHASH_ALL|SIGHASH_ANYONECANPAY},
 543      {"NONE|ANYONECANPAY", SIGHASH_NONE|SIGHASH_ANYONECANPAY},
 544      {"SINGLE|ANYONECANPAY", SIGHASH_SINGLE|SIGHASH_ANYONECANPAY},
 545  };
 546  
 547  static bool findSighashFlags(int& flags, const std::string& flagStr)
 548  {
 549      flags = 0;
 550  
 551      for (unsigned int i = 0; i < N_SIGHASH_OPTS; i++) {
 552          if (flagStr == sighashOptions[i].flagStr) {
 553              flags = sighashOptions[i].flags;
 554              return true;
 555          }
 556      }
 557  
 558      return false;
 559  }
 560  
 561  static CAmount AmountFromValue(const UniValue& value)
 562  {
 563      if (!value.isNum() && !value.isStr())
 564          throw std::runtime_error("Amount is not a number or string");
 565      int64_t amount_i64;
 566      if (!ParseFixedPoint(value.getValStr(), 8, &amount_i64))
 567          throw std::runtime_error("Invalid amount");
 568      CAmount amount{amount_i64};
 569      if (!MoneyRange(amount))
 570          throw std::runtime_error("Amount out of range");
 571      return amount;
 572  }
 573  
 574  static std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strName)
 575  {
 576      std::string strHex;
 577      if (v.isStr())
 578          strHex = v.getValStr();
 579      if (!IsHex(strHex))
 580          throw std::runtime_error(strName + " must be hexadecimal string (not '" + strHex + "')");
 581      return ParseHex(strHex);
 582  }
 583  
 584  static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
 585  {
 586      int nHashType = SIGHASH_ALL;
 587  
 588      if (flagStr.size() > 0)
 589          if (!findSighashFlags(nHashType, flagStr))
 590              throw std::runtime_error("unknown sighash flag/sign option");
 591  
 592      // mergedTx will end up with all the signatures; it
 593      // starts as a clone of the raw tx:
 594      CMutableTransaction mergedTx{tx};
 595      const CMutableTransaction txv{tx};
 596      CCoinsView viewDummy;
 597      CCoinsViewCache view(&viewDummy);
 598  
 599      if (!registers.count("privatekeys"))
 600          throw std::runtime_error("privatekeys register variable must be set.");
 601      FillableSigningProvider tempKeystore;
 602      UniValue keysObj = registers["privatekeys"];
 603  
 604      for (unsigned int kidx = 0; kidx < keysObj.size(); kidx++) {
 605          if (!keysObj[kidx].isStr())
 606              throw std::runtime_error("privatekey not a std::string");
 607          CKey key = DecodeSecret(keysObj[kidx].getValStr());
 608          if (!key.IsValid()) {
 609              throw std::runtime_error("privatekey not valid");
 610          }
 611          tempKeystore.AddKey(key);
 612      }
 613  
 614      // Add previous txouts given in the RPC call:
 615      if (!registers.count("prevtxs"))
 616          throw std::runtime_error("prevtxs register variable must be set.");
 617      UniValue prevtxsObj = registers["prevtxs"];
 618      {
 619          for (unsigned int previdx = 0; previdx < prevtxsObj.size(); previdx++) {
 620              const UniValue& prevOut = prevtxsObj[previdx];
 621              if (!prevOut.isObject())
 622                  throw std::runtime_error("expected prevtxs internal object");
 623  
 624              std::map<std::string, UniValue::VType> types = {
 625                  {"txid", UniValue::VSTR},
 626                  {"vout", UniValue::VNUM},
 627                  {"scriptPubKey", UniValue::VSTR},
 628              };
 629              if (!prevOut.checkObject(types))
 630                  throw std::runtime_error("prevtxs internal object typecheck fail");
 631  
 632              auto txid{Txid::FromHex(prevOut["txid"].get_str())};
 633              if (!txid) {
 634                  throw std::runtime_error("txid must be hexadecimal string (not '" + prevOut["txid"].get_str() + "')");
 635              }
 636  
 637              const int nOut = prevOut["vout"].getInt<int>();
 638              if (nOut < 0)
 639                  throw std::runtime_error("vout cannot be negative");
 640  
 641              COutPoint out(*txid, nOut);
 642              std::vector<unsigned char> pkData(ParseHexUV(prevOut["scriptPubKey"], "scriptPubKey"));
 643              CScript scriptPubKey(pkData.begin(), pkData.end());
 644  
 645              {
 646                  const Coin& coin = view.AccessCoin(out);
 647                  if (!coin.IsSpent() && coin.out.scriptPubKey != scriptPubKey) {
 648                      std::string err("Previous output scriptPubKey mismatch:\n");
 649                      err = err + ScriptToAsmStr(coin.out.scriptPubKey) + "\nvs:\n"+
 650                          ScriptToAsmStr(scriptPubKey);
 651                      throw std::runtime_error(err);
 652                  }
 653                  Coin newcoin;
 654                  newcoin.out.scriptPubKey = scriptPubKey;
 655                  newcoin.out.nValue = MAX_MONEY;
 656                  if (prevOut.exists("amount")) {
 657                      newcoin.out.nValue = AmountFromValue(prevOut["amount"]);
 658                  }
 659                  newcoin.nHeight = 1;
 660                  view.AddCoin(out, std::move(newcoin), true);
 661              }
 662  
 663              // if redeemScript given and private keys given,
 664              // add redeemScript to the tempKeystore so it can be signed:
 665              if ((scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash()) &&
 666                  prevOut.exists("redeemScript")) {
 667                  UniValue v = prevOut["redeemScript"];
 668                  std::vector<unsigned char> rsData(ParseHexUV(v, "redeemScript"));
 669                  CScript redeemScript(rsData.begin(), rsData.end());
 670                  tempKeystore.AddCScript(redeemScript);
 671              }
 672          }
 673      }
 674  
 675      const FillableSigningProvider& keystore = tempKeystore;
 676  
 677      bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
 678  
 679      // Sign what we can:
 680      for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
 681          CTxIn& txin = mergedTx.vin[i];
 682          const Coin& coin = view.AccessCoin(txin.prevout);
 683          if (coin.IsSpent()) {
 684              continue;
 685          }
 686          const CScript& prevPubKey = coin.out.scriptPubKey;
 687          const CAmount& amount = coin.out.nValue;
 688  
 689          SignatureData sigdata = DataFromTransaction(mergedTx, i, coin.out);
 690          // Only sign SIGHASH_SINGLE if there's a corresponding output:
 691          if (!fHashSingle || (i < mergedTx.vout.size()))
 692              ProduceSignature(keystore, MutableTransactionSignatureCreator(mergedTx, i, amount, nHashType), prevPubKey, sigdata);
 693  
 694          if (amount == MAX_MONEY && !sigdata.scriptWitness.IsNull()) {
 695              throw std::runtime_error(strprintf("Missing amount for CTxOut with scriptPubKey=%s", HexStr(prevPubKey)));
 696          }
 697  
 698          UpdateInput(txin, sigdata);
 699      }
 700  
 701      tx = mergedTx;
 702  }
 703  
 704  static void MutateTx(CMutableTransaction& tx, const std::string& command,
 705                       const std::string& commandVal)
 706  {
 707      std::unique_ptr<ECC_Context> ecc;
 708  
 709      if (command == "nversion")
 710          MutateTxVersion(tx, commandVal);
 711      else if (command == "locktime")
 712          MutateTxLocktime(tx, commandVal);
 713      else if (command == "replaceable") {
 714          MutateTxRBFOptIn(tx, commandVal);
 715      }
 716  
 717      else if (command == "delin")
 718          MutateTxDelInput(tx, commandVal);
 719      else if (command == "in")
 720          MutateTxAddInput(tx, commandVal);
 721  
 722      else if (command == "delout")
 723          MutateTxDelOutput(tx, commandVal);
 724      else if (command == "outaddr")
 725          MutateTxAddOutAddr(tx, commandVal);
 726      else if (command == "outpubkey") {
 727          ecc.reset(new ECC_Context());
 728          MutateTxAddOutPubKey(tx, commandVal);
 729      } else if (command == "outmultisig") {
 730          ecc.reset(new ECC_Context());
 731          MutateTxAddOutMultiSig(tx, commandVal);
 732      } else if (command == "outscript")
 733          MutateTxAddOutScript(tx, commandVal);
 734      else if (command == "outdata")
 735          MutateTxAddOutData(tx, commandVal);
 736  
 737      else if (command == "sign") {
 738          ecc.reset(new ECC_Context());
 739          MutateTxSign(tx, commandVal);
 740      }
 741  
 742      else if (command == "load")
 743          RegisterLoad(commandVal);
 744  
 745      else if (command == "set")
 746          RegisterSet(commandVal);
 747  
 748      else
 749          throw std::runtime_error("unknown command");
 750  }
 751  
 752  static void OutputTxJSON(const CTransaction& tx)
 753  {
 754      UniValue entry(UniValue::VOBJ);
 755      TxToUniv(tx, /*block_hash=*/uint256(), entry);
 756  
 757      std::string jsonOutput = entry.write(4);
 758      tfm::format(std::cout, "%s\n", jsonOutput);
 759  }
 760  
 761  static void OutputTxHash(const CTransaction& tx)
 762  {
 763      std::string strHexHash = tx.GetHash().GetHex(); // the hex-encoded transaction hash (aka the transaction id)
 764  
 765      tfm::format(std::cout, "%s\n", strHexHash);
 766  }
 767  
 768  static void OutputTxHex(const CTransaction& tx)
 769  {
 770      std::string strHex = EncodeHexTx(tx);
 771  
 772      tfm::format(std::cout, "%s\n", strHex);
 773  }
 774  
 775  static void OutputTx(const CTransaction& tx)
 776  {
 777      if (gArgs.GetBoolArg("-json", false))
 778          OutputTxJSON(tx);
 779      else if (gArgs.GetBoolArg("-txid", false))
 780          OutputTxHash(tx);
 781      else
 782          OutputTxHex(tx);
 783  }
 784  
 785  static std::string readStdin()
 786  {
 787      char buf[4096];
 788      std::string ret;
 789  
 790      while (!feof(stdin)) {
 791          size_t bread = fread(buf, 1, sizeof(buf), stdin);
 792          ret.append(buf, bread);
 793          if (bread < sizeof(buf))
 794              break;
 795      }
 796  
 797      if (ferror(stdin))
 798          throw std::runtime_error("error reading stdin");
 799  
 800      return TrimString(ret);
 801  }
 802  
 803  static int CommandLineRawTx(int argc, char* argv[])
 804  {
 805      std::string strPrint;
 806      int nRet = 0;
 807      try {
 808          // Skip switches; Permit common stdin convention "-"
 809          while (argc > 1 && IsSwitchChar(argv[1][0]) &&
 810                 (argv[1][1] != 0)) {
 811              argc--;
 812              argv++;
 813          }
 814  
 815          CMutableTransaction tx;
 816          int startArg;
 817  
 818          if (!fCreateBlank) {
 819              // require at least one param
 820              if (argc < 2)
 821                  throw std::runtime_error("too few parameters");
 822  
 823              // param: hex-encoded limenka transaction
 824              std::string strHexTx(argv[1]);
 825              if (strHexTx == "-")                 // "-" implies standard input
 826                  strHexTx = readStdin();
 827  
 828              if (!DecodeHexTx(tx, strHexTx, true))
 829                  throw std::runtime_error("invalid transaction encoding");
 830  
 831              startArg = 2;
 832          } else
 833              startArg = 1;
 834  
 835          for (int i = startArg; i < argc; i++) {
 836              std::string arg = argv[i];
 837              std::string key, value;
 838              size_t eqpos = arg.find('=');
 839              if (eqpos == std::string::npos)
 840                  key = arg;
 841              else {
 842                  key = arg.substr(0, eqpos);
 843                  value = arg.substr(eqpos + 1);
 844              }
 845  
 846              MutateTx(tx, key, value);
 847          }
 848  
 849          OutputTx(CTransaction(tx));
 850      }
 851      catch (const std::exception& e) {
 852          strPrint = std::string("error: ") + e.what();
 853          nRet = EXIT_FAILURE;
 854      }
 855      catch (...) {
 856          PrintExceptionContinue(nullptr, "CommandLineRawTx()");
 857          throw;
 858      }
 859  
 860      if (strPrint != "") {
 861          tfm::format(nRet == 0 ? std::cout : std::cerr, "%s\n", strPrint);
 862      }
 863      return nRet;
 864  }
 865  
 866  MAIN_FUNCTION
 867  {
 868      SetupEnvironment();
 869  
 870      try {
 871          int ret = AppInitRawTx(argc, argv);
 872          if (ret != CONTINUE_EXECUTION)
 873              return ret;
 874      }
 875      catch (const std::exception& e) {
 876          PrintExceptionContinue(&e, "AppInitRawTx()");
 877          return EXIT_FAILURE;
 878      } catch (...) {
 879          PrintExceptionContinue(nullptr, "AppInitRawTx()");
 880          return EXIT_FAILURE;
 881      }
 882  
 883      int ret = EXIT_FAILURE;
 884      try {
 885          ret = CommandLineRawTx(argc, argv);
 886      }
 887      catch (const std::exception& e) {
 888          PrintExceptionContinue(&e, "CommandLineRawTx()");
 889      } catch (...) {
 890          PrintExceptionContinue(nullptr, "CommandLineRawTx()");
 891      }
 892      return ret;
 893  }
 894