LCOV - code coverage report
Current view: top level - src - pivx-tx.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 247 440 56.1 %
Date: 2025-02-23 09:33:43 Functions: 21 26 80.8 %

          Line data    Source code
       1             : // Copyright (c) 2009-2014 The Bitcoin developers
       2             : // Copyright (c) 2015-2021 The PIVX Core developers
       3             : // Distributed under the MIT/X11 software license, see the accompanying
       4             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       5             : 
       6             : #if defined(HAVE_CONFIG_H)
       7             : #include "config/pivx-config.h"
       8             : #endif
       9             : 
      10             : #include "clientversion.h"
      11             : #include "coins.h"
      12             : #include "core_io.h"
      13             : #include "keystore.h"
      14             : #include "key_io.h"
      15             : #include "policy/policy.h"
      16             : #include "primitives/transaction.h"
      17             : #include "script/script.h"
      18             : #include "script/sign.h"
      19             : #include <univalue.h>
      20             : #include "util/system.h"
      21             : #include "utilmoneystr.h"
      22             : #include "utilstrencodings.h"
      23             : 
      24             : #include <stdio.h>
      25             : 
      26             : #include <boost/algorithm/string.hpp>
      27             : 
      28             : static bool fCreateBlank;
      29             : static std::map<std::string, UniValue> registers;
      30             : static const int CONTINUE_EXECUTION=-1;
      31             : 
      32             : //
      33             : // This function returns either one of EXIT_ codes when it's expected to stop the process or
      34             : // CONTINUE_EXECUTION when it's expected to continue further.
      35             : //
      36          22 : static int AppInitRawTx(int argc, char* argv[])
      37             : {
      38             :     //
      39             :     // Parameters
      40             :     //
      41          22 :     gArgs.ParseParameters(argc, argv);
      42             : 
      43             :     // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
      44          22 :     try {
      45          22 :         SelectParams(gArgs.GetChainName());
      46           0 :     } catch(const std::exception& e) {
      47           0 :         fprintf(stderr, "Error: %s\n", e.what());
      48           0 :         return EXIT_FAILURE;
      49             :     }
      50             : 
      51          22 :     fCreateBlank = gArgs.GetBoolArg("-create", false);
      52             : 
      53          88 :     if (argc < 2 || gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") || gArgs.IsArgSet("-help")) {
      54             :         // First part of help message is specific to this utility
      55           0 :         std::string strUsage = PACKAGE_NAME " pivx-tx utility version " + FormatFullVersion() + "\n\n" +
      56           0 :                                "Usage:  pivx-tx [options] <hex-tx> [commands]  Update hex-encoded pivx transaction\n" +
      57             :                                "or:     pivx-tx [options] -create [commands]   Create hex-encoded pivx transaction\n" +
      58           0 :                                "\n";
      59             : 
      60           0 :         fprintf(stdout, "%s", strUsage.c_str());
      61             : 
      62           0 :         strUsage = HelpMessageGroup("Options:");
      63           0 :         strUsage += HelpMessageOpt("-?", "This help message");
      64           0 :         strUsage += HelpMessageOpt("-create", "Create new, empty TX.");
      65           0 :         strUsage += HelpMessageOpt("-json", "Select JSON output");
      66           0 :         strUsage += HelpMessageOpt("-txid", "Output only the hex-encoded transaction id of the resultant transaction.");
      67           0 :         strUsage += HelpMessageOpt("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly.");
      68           0 :         strUsage += HelpMessageOpt("-testnet", "Use the test network");
      69             : 
      70           0 :         fprintf(stdout, "%s", strUsage.c_str());
      71             : 
      72             : 
      73           0 :         strUsage = HelpMessageGroup("Commands:");
      74           0 :         strUsage += HelpMessageOpt("delin=N", "Delete input N from TX");
      75           0 :         strUsage += HelpMessageOpt("delout=N", "Delete output N from TX");
      76           0 :         strUsage += HelpMessageOpt("in=TXID:VOUT", "Add input to TX");
      77           0 :         strUsage += HelpMessageOpt("locktime=N", "Set TX lock time to N");
      78           0 :         strUsage += HelpMessageOpt("nversion=N", "Set TX version to N");
      79           0 :         strUsage += HelpMessageOpt("outaddr=VALUE:ADDRESS", "Add address-based output to TX");
      80           0 :         strUsage += HelpMessageOpt("outscript=VALUE:SCRIPT", "Add raw script output to TX");
      81           0 :         strUsage += HelpMessageOpt("sign=SIGHASH-FLAGS", "Add zero or more signatures to transaction. "
      82             :             "This command requires JSON registers:"
      83             :             "prevtxs=JSON object, "
      84             :             "privatekeys=JSON object. "
      85           0 :             "See signrawtransaction docs for format of sighash flags, JSON objects.");
      86           0 :         fprintf(stdout, "%s", strUsage.c_str());
      87             : 
      88           0 :         strUsage = HelpMessageGroup("Register Commands:");
      89           0 :         strUsage += HelpMessageOpt("load=NAME:FILENAME", "Load JSON file FILENAME into register NAME");
      90           0 :         strUsage += HelpMessageOpt("set=NAME:JSON-STRING", "Set register NAME to given JSON-STRING");
      91           0 :         fprintf(stdout, "%s", strUsage.c_str());
      92             : 
      93           0 :         if (argc < 2) {
      94           0 :             fprintf(stderr, "Error: too few parameters\n");
      95             :             return EXIT_FAILURE;
      96             :         }
      97             :         return EXIT_SUCCESS;
      98             :     }
      99             :     return CONTINUE_EXECUTION;
     100             : }
     101             : 
     102           2 : static void RegisterSetJson(const std::string& key, const std::string& rawJson)
     103             : {
     104           4 :     UniValue val;
     105           2 :     if (!val.read(rawJson)) {
     106           0 :         std::string strErr = "Cannot parse JSON for key " + key;
     107           0 :         throw std::runtime_error(strErr);
     108             :     }
     109             : 
     110           2 :     registers[key] = val;
     111           2 : }
     112             : 
     113           2 : static void RegisterSet(const std::string& strInput)
     114             : {
     115             :     // separate NAME:VALUE in string
     116           2 :     size_t pos = strInput.find(':');
     117           2 :     if ((pos == std::string::npos) ||
     118           2 :         (pos == 0) ||
     119           2 :         (pos == (strInput.size() - 1)))
     120           0 :         throw std::runtime_error("Register input requires NAME:VALUE");
     121             : 
     122           2 :     std::string key = strInput.substr(0, pos);
     123           4 :     std::string valStr = strInput.substr(pos + 1, std::string::npos);
     124             : 
     125           2 :     RegisterSetJson(key, valStr);
     126           2 : }
     127             : 
     128           0 : static void RegisterLoad(const std::string& strInput)
     129             : {
     130             :     // separate NAME:FILENAME in string
     131           0 :     size_t pos = strInput.find(':');
     132           0 :     if ((pos == std::string::npos) ||
     133           0 :         (pos == 0) ||
     134           0 :         (pos == (strInput.size() - 1)))
     135           0 :         throw std::runtime_error("Register load requires NAME:FILENAME");
     136             : 
     137           0 :     std::string key = strInput.substr(0, pos);
     138           0 :     std::string filename = strInput.substr(pos + 1, std::string::npos);
     139             : 
     140           0 :     FILE* f = fopen(filename.c_str(), "r");
     141           0 :     if (!f) {
     142           0 :         std::string strErr = "Cannot open file " + filename;
     143           0 :         throw std::runtime_error(strErr);
     144             :     }
     145             : 
     146             :     // load file chunks into one big buffer
     147           0 :     std::string valStr;
     148           0 :     while ((!feof(f)) && (!ferror(f))) {
     149           0 :         char buf[4096];
     150           0 :         int bread = fread(buf, 1, sizeof(buf), f);
     151           0 :         if (bread <= 0)
     152             :             break;
     153             : 
     154           0 :         valStr.insert(valStr.size(), buf, bread);
     155             :     }
     156             : 
     157           0 :     if (ferror(f)) {
     158           0 :         std::string strErr = "Error reading file " + filename;
     159           0 :         throw std::runtime_error(strErr);
     160             :     }
     161             : 
     162           0 :     fclose(f);
     163             : 
     164             :     // evaluate as JSON buffer register
     165           0 :     RegisterSetJson(key, valStr);
     166           0 : }
     167             : 
     168           9 : static CAmount ExtractAndValidateValue(const std::string& strValue)
     169             : {
     170           9 :     CAmount value;
     171           9 :     if (!ParseMoney(strValue, value))
     172           0 :         throw std::runtime_error("invalid TX output value");
     173           9 :     return value;
     174             : }
     175             : 
     176           3 : static void MutateTxVersion(CMutableTransaction& tx, const std::string& cmdVal)
     177             : {
     178           3 :     int64_t newVersion;
     179           3 :     if (!ParseInt64(cmdVal, &newVersion) || newVersion < 1 || newVersion >= CTransaction::TxVersion::TOOHIGH)
     180           0 :         throw std::runtime_error("Invalid TX version requested: '" + cmdVal + "'");
     181             : 
     182           3 :     tx.nVersion = (int)newVersion;
     183           3 : }
     184             : 
     185           2 : static void MutateTxLocktime(CMutableTransaction& tx, const std::string& cmdVal)
     186             : {
     187           2 :     int64_t newLocktime;
     188           2 :     if (!ParseInt64(cmdVal, &newLocktime) || newLocktime < 0LL || newLocktime > 0xffffffffLL)
     189           0 :         throw std::runtime_error("Invalid TX locktime requested: '" + cmdVal + "'");
     190             : 
     191           2 :     tx.nLockTime = (unsigned int)newLocktime;
     192           2 : }
     193             : 
     194           7 : static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInput)
     195             : {
     196           7 :     std::vector<std::string> vStrInputParts;
     197           7 :     boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
     198             : 
     199             :     // separate TXID:VOUT in string
     200           7 :     if (vStrInputParts.size()<2)
     201           0 :         throw std::runtime_error("TX input missing separator");
     202             : 
     203             :     // extract and validate TXID
     204           7 :     uint256 txid;
     205           7 :     if (!ParseHashStr(vStrInputParts[0], txid)) {
     206           0 :         throw std::runtime_error("invalid TX input txid");
     207             :     }
     208             : 
     209           7 :     static const unsigned int minTxOutSz = 9;
     210           7 :     unsigned int nMaxSize = MAX_BLOCK_SIZE_LEGACY;
     211           7 :     static const unsigned int maxVout = nMaxSize / minTxOutSz;
     212             : 
     213             :     // extract and validate vout
     214           7 :     const std::string& strVout = vStrInputParts[1];
     215           7 :     int64_t vout;
     216           7 :     if (!ParseInt64(strVout, &vout) || vout < 0 || vout > static_cast<int64_t>(maxVout))
     217           0 :         throw std::runtime_error("invalid TX input vout '" + strVout + "'");
     218             : 
     219             :     // extract the optional sequence number
     220           7 :     uint32_t nSequenceIn = CTxIn::SEQUENCE_FINAL;
     221           7 :     if (vStrInputParts.size() > 2)
     222           0 :         nSequenceIn = std::stoul(vStrInputParts[2]);
     223             : 
     224             :     // append to transaction input list
     225          14 :     tx.vin.emplace_back(txid, vout, CScript(), nSequenceIn);
     226           7 : }
     227             : 
     228           7 : static void MutateTxAddOutAddr(CMutableTransaction& tx, const std::string& strInput)
     229             : {
     230             :     // Separate into VALUE:ADDRESS
     231           7 :     std::vector<std::string> vStrInputParts;
     232           9 :     boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
     233             : 
     234           7 :     if (vStrInputParts.size() != 2)
     235           2 :         throw std::runtime_error("TX output missing or too many separators");
     236             : 
     237             :     // Extract and validate VALUE
     238           5 :     CAmount value = ExtractAndValidateValue(vStrInputParts[0]);
     239             : 
     240             :     // extract and validate ADDRESS
     241          10 :     std::string strAddr = vStrInputParts[1];
     242          10 :     CTxDestination destination = DecodeDestination(strAddr);
     243           5 :     if (!IsValidDestination(destination)) {
     244           0 :         throw std::runtime_error("invalid TX output address");
     245             :     }
     246          10 :     CScript scriptPubKey = GetScriptForDestination(destination);
     247             : 
     248             :     // construct TxOut, append to transaction output list
     249           5 :     tx.vout.emplace_back(value, scriptPubKey);
     250           5 : }
     251             : 
     252           0 : static void MutateTxAddOutPubKey(CMutableTransaction& tx, const std::string& strInput)
     253             : {
     254             :     // Separate into VALUE:PUBKEY[:FLAGS]
     255           0 :     std::vector<std::string> vStrInputParts;
     256           0 :     boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
     257             : 
     258           0 :     if (vStrInputParts.size() < 2 || vStrInputParts.size() > 3)
     259           0 :         throw std::runtime_error("TX output missing or too many separators");
     260             : 
     261             :     // Extract and validate VALUE
     262           0 :     CAmount value = ExtractAndValidateValue(vStrInputParts[0]);
     263             : 
     264             :     // Extract and validate PUBKEY
     265           0 :     CPubKey pubkey(ParseHex(vStrInputParts[1]));
     266           0 :     if (!pubkey.IsFullyValid())
     267           0 :         throw std::runtime_error("invalid TX output pubkey");
     268           0 :     CScript scriptPubKey = GetScriptForRawPubKey(pubkey);
     269             : 
     270             :     // Extract and validate FLAGS
     271           0 :     bool bScriptHash = false;
     272           0 :     if (vStrInputParts.size() == 3) {
     273           0 :         std::string flags = vStrInputParts[2];
     274           0 :         bScriptHash = (flags.find('S') != std::string::npos);
     275             :     }
     276           0 :     if (bScriptHash) {
     277             :         // Get the ID for the script, and then construct a P2SH destination for it.
     278           0 :         scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey));
     279             :     }
     280             : 
     281             :     // construct TxOut, append to transaction output list
     282           0 :     tx.vout.emplace_back(value, scriptPubKey);
     283           0 : }
     284             : 
     285           0 : static void MutateTxAddOutMultiSig(CMutableTransaction& tx, const std::string& strInput)
     286             : {
     287             :     // Separate into VALUE:REQUIRED:NUMKEYS:PUBKEY1:PUBKEY2:....[:FLAGS]
     288           0 :     std::vector<std::string> vStrInputParts;
     289           0 :     boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
     290             : 
     291             :     // Check that there are enough parameters
     292           0 :     if (vStrInputParts.size() < 3)
     293           0 :         throw std::runtime_error("Not enough multisig parameters");
     294             : 
     295             :     // Extract and validate VALUE
     296           0 :     CAmount value = ExtractAndValidateValue(vStrInputParts[0]);
     297             : 
     298             :     // Extract REQUIRED
     299           0 :     uint32_t required = stoul(vStrInputParts[1]);
     300             : 
     301             :     // Extract NUMKEYS
     302           0 :     uint32_t numkeys = stoul(vStrInputParts[2]);
     303             : 
     304             :     // Validate there are the correct number of pubkeys
     305           0 :     if (vStrInputParts.size() < numkeys + 3)
     306           0 :         throw std::runtime_error("incorrect number of multisig pubkeys");
     307             : 
     308           0 :     if (required < 1 || required > MAX_PUBKEYS_PER_MULTISIG || numkeys < 1 || numkeys > MAX_PUBKEYS_PER_MULTISIG || numkeys < required)
     309           0 :         throw std::runtime_error("multisig parameter mismatch. Required " \
     310           0 :                             + std::to_string(required) + " of " + std::to_string(numkeys) + "signatures.");
     311             : 
     312             :     // extract and validate PUBKEYs
     313           0 :     std::vector<CPubKey> pubkeys;
     314           0 :     for(int pos = 1; pos <= int(numkeys); pos++) {
     315           0 :         CPubKey pubkey(ParseHex(vStrInputParts[pos + 2]));
     316           0 :         if (!pubkey.IsFullyValid())
     317           0 :             throw std::runtime_error("invalid TX output pubkey");
     318           0 :         pubkeys.push_back(pubkey);
     319             :     }
     320             : 
     321             :     // Extract FLAGS
     322           0 :     bool bScriptHash = false;
     323           0 :     if (vStrInputParts.size() == numkeys + 4) {
     324           0 :         std::string flags = vStrInputParts.back();
     325           0 :         bScriptHash = (flags.find('S') != std::string::npos);
     326             :     }
     327           0 :     else if (vStrInputParts.size() > numkeys + 4) {
     328             :         // Validate that there were no more parameters passed
     329           0 :         throw std::runtime_error("Too many parameters");
     330             :     }
     331             : 
     332           0 :     CScript scriptPubKey = GetScriptForMultisig(required, pubkeys);
     333             : 
     334           0 :     if (bScriptHash) {
     335           0 :         if (scriptPubKey.size() > MAX_SCRIPT_ELEMENT_SIZE) {
     336           0 :             throw std::runtime_error(strprintf(
     337           0 :                         "redeemScript exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_ELEMENT_SIZE));
     338             :         }
     339             :         // Get the ID for the script, and then construct a P2SH destination for it.
     340           0 :         scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey));
     341             :     }
     342             : 
     343             :     // construct TxOut, append to transaction output list
     344           0 :     tx.vout.emplace_back(value, scriptPubKey);
     345           0 : }
     346             : 
     347           4 : static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& strInput)
     348             : {
     349             :     // separate VALUE:SCRIPT
     350           4 :     std::vector<std::string> vStrInputParts;
     351           4 :     boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
     352           4 :     if (vStrInputParts.size() < 2)
     353           0 :         throw std::runtime_error("TX output missing separator");
     354             : 
     355             :     // Extract and validate VALUE
     356           4 :     CAmount value = ExtractAndValidateValue(vStrInputParts[0]);
     357             : 
     358             :     // extract and validate script
     359           8 :     std::string strScript = vStrInputParts[1];
     360          12 :     CScript scriptPubKey = ParseScript(strScript);
     361             : 
     362           4 :     if (scriptPubKey.size() > MAX_SCRIPT_SIZE) {
     363           0 :         throw std::runtime_error(strprintf(
     364           0 :                     "script exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_SIZE));
     365             :     }
     366             : 
     367             :     // construct TxOut, append to transaction output list
     368           4 :     tx.vout.emplace_back(value, scriptPubKey);
     369           4 : }
     370             : 
     371           3 : static void MutateTxDelInput(CMutableTransaction& tx, const std::string& strInIdx)
     372             : {
     373             :     // parse requested deletion index
     374           3 :     int inIdx = atoi(strInIdx);
     375           3 :     if (inIdx < 0 || inIdx >= (int)tx.vin.size()) {
     376           1 :         std::string strErr = "Invalid TX input index '" + strInIdx + "'";
     377           1 :         throw std::runtime_error(strErr.c_str());
     378             :     }
     379             : 
     380             :     // delete input from transaction
     381           2 :     tx.vin.erase(tx.vin.begin() + inIdx);
     382           2 : }
     383             : 
     384           3 : static void MutateTxDelOutput(CMutableTransaction& tx, const std::string& strOutIdx)
     385             : {
     386             :     // parse requested deletion index
     387           3 :     int outIdx = atoi(strOutIdx);
     388           3 :     if (outIdx < 0 || outIdx >= (int)tx.vout.size()) {
     389           1 :         std::string strErr = "Invalid TX output index '" + strOutIdx + "'";
     390           1 :         throw std::runtime_error(strErr.c_str());
     391             :     }
     392             : 
     393             :     // delete output from transaction
     394           2 :     tx.vout.erase(tx.vout.begin() + outIdx);
     395           2 : }
     396             : 
     397             : static const unsigned int N_SIGHASH_OPTS = 6;
     398             : static const struct {
     399             :     const char* flagStr;
     400             :     int flags;
     401             : } sighashOptions[N_SIGHASH_OPTS] = {
     402             :     {"ALL", SIGHASH_ALL},
     403             :     {"NONE", SIGHASH_NONE},
     404             :     {"SINGLE", SIGHASH_SINGLE},
     405             :     {"ALL|ANYONECANPAY", SIGHASH_ALL | SIGHASH_ANYONECANPAY},
     406             :     {"NONE|ANYONECANPAY", SIGHASH_NONE | SIGHASH_ANYONECANPAY},
     407             :     {"SINGLE|ANYONECANPAY", SIGHASH_SINGLE | SIGHASH_ANYONECANPAY},
     408             : };
     409             : 
     410           1 : static bool findSighashFlags(int& flags, const std::string& flagStr)
     411             : {
     412           1 :     flags = 0;
     413             : 
     414           1 :     for (unsigned int i = 0; i < N_SIGHASH_OPTS; i++) {
     415           1 :         if (flagStr == sighashOptions[i].flagStr) {
     416           1 :             flags = sighashOptions[i].flags;
     417           1 :             return true;
     418             :         }
     419             :     }
     420             : 
     421             :     return false;
     422             : }
     423             : 
     424           0 : static inline int64_t roundint64(double d)
     425             : {
     426           0 :     return (int64_t)(d > 0 ? d + 0.5 : d - 0.5);
     427             : }
     428             : 
     429           0 : static CAmount AmountFromValue(const UniValue& value)
     430             : {
     431           0 :     if (!value.isNum() && !value.isStr())
     432           0 :         throw std::runtime_error("Amount is not a number or string");
     433           0 :     double dAmount = value.get_real();
     434           0 :     if (dAmount <= 0.0 || dAmount > 21000000.0)
     435           0 :         throw std::runtime_error("Invalid amount");
     436           0 :     CAmount nAmount = roundint64(dAmount * COIN);
     437           0 :     if (!Params().GetConsensus().MoneyRange(nAmount))
     438           0 :         throw std::runtime_error("Amount out of range");
     439           0 :     return nAmount;
     440             : }
     441             : 
     442           1 : static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
     443             : {
     444           1 :     int nHashType = SIGHASH_ALL;
     445             : 
     446           1 :     if (flagStr.size() > 0)
     447           1 :         if (!findSighashFlags(nHashType, flagStr))
     448           0 :             throw std::runtime_error("unknown sighash flag/sign option");
     449             : 
     450           2 :     std::vector<CTransaction> txVariants;
     451           2 :     txVariants.push_back(tx);
     452             : 
     453             :     // mergedTx will end up with all the signatures; it
     454             :     // starts as a clone of the raw tx:
     455           1 :     CMutableTransaction mergedTx(txVariants[0]);
     456           1 :     bool fComplete = true;
     457           1 :     CCoinsView viewDummy;
     458           2 :     CCoinsViewCache view(&viewDummy);
     459             : 
     460           2 :     if (!registers.count("privatekeys"))
     461           0 :         throw std::runtime_error("privatekeys register variable must be set.");
     462           1 :     bool fGivenKeys = false;
     463           2 :     CBasicKeyStore tempKeystore;
     464           2 :     UniValue keysObj = registers["privatekeys"];
     465           1 :     fGivenKeys = true;
     466             : 
     467           2 :     for (unsigned int kidx = 0; kidx < keysObj.size(); kidx++) {
     468           1 :         if (!keysObj[kidx].isStr())
     469           0 :             throw std::runtime_error("privatekey not a string");
     470           2 :         CKey key = KeyIO::DecodeSecret(keysObj[kidx].getValStr());
     471           1 :         if (!key.IsValid()) {
     472           0 :             throw std::runtime_error("privatekey not valid");
     473             :         }
     474           1 :         tempKeystore.AddKey(key);
     475             :     }
     476             : 
     477             :     // Add previous txouts given in the RPC call:
     478           2 :     if (!registers.count("prevtxs"))
     479           0 :         throw std::runtime_error("prevtxs register variable must be set.");
     480           2 :     UniValue prevtxsObj = registers["prevtxs"];
     481           1 :     {
     482           2 :         for (unsigned int previdx = 0; previdx < prevtxsObj.size(); previdx++) {
     483           1 :             UniValue prevOut = prevtxsObj[previdx];
     484           1 :             if (!prevOut.isObject())
     485           0 :                 throw std::runtime_error("expected prevtxs internal object");
     486             : 
     487           1 :             std::map<std::string, UniValue::VType> types = {
     488           1 :                 {"txid", UniValue::VSTR},
     489           1 :                 {"vout", UniValue::VNUM},
     490           1 :                 {"scriptPubKey", UniValue::VSTR}
     491           5 :             };
     492           1 :             if (!prevOut.checkObject(types))
     493           0 :                 throw std::runtime_error("prevtxs internal object typecheck fail");
     494             : 
     495           2 :             uint256 txid = ParseHashUV(prevOut["txid"], "txid");
     496             : 
     497           1 :             int nOut = atoi(prevOut["vout"].getValStr());
     498           1 :             if (nOut < 0)
     499           0 :                 throw std::runtime_error("vout must be positive");
     500             : 
     501           1 :             COutPoint out(txid, nOut);
     502           3 :             std::vector<unsigned char> pkData(ParseHexUV(prevOut["scriptPubKey"], "scriptPubKey"));
     503           2 :             CScript scriptPubKey(pkData.begin(), pkData.end());
     504             : 
     505           1 :             {
     506           1 :                 const Coin& coin = view.AccessCoin(out);
     507           1 :                 if (!coin.IsSpent() && coin.out.scriptPubKey != scriptPubKey) {
     508           0 :                     std::string err("Previous output scriptPubKey mismatch:\n");
     509           0 :                     err = err + ScriptToAsmStr(coin.out.scriptPubKey) + "\nvs:\n"+
     510           0 :                         ScriptToAsmStr(scriptPubKey);
     511           0 :                     throw std::runtime_error(err);
     512             :                 }
     513             : 
     514           1 :                 Coin newcoin;
     515           1 :                 newcoin.out.scriptPubKey = scriptPubKey;
     516           1 :                 newcoin.out.nValue = 0;
     517           1 :                 newcoin.nHeight = 1;
     518           2 :                 if (prevOut.exists("amount")) {
     519           0 :                     newcoin.out.nValue = AmountFromValue(prevOut["amount"]);
     520             :                 }
     521             :             }
     522             : 
     523             :             // if redeemScript given and private keys given,
     524             :             // add redeemScript to the tempKeystore so it can be signed:
     525           2 :             if (fGivenKeys && scriptPubKey.IsPayToScriptHash() &&
     526           1 :                 prevOut.exists("redeemScript")) {
     527           0 :                 UniValue v = prevOut["redeemScript"];
     528           0 :                 std::vector<unsigned char> rsData(ParseHexUV(v, "redeemScript"));
     529           0 :                 CScript redeemScript(rsData.begin(), rsData.end());
     530           0 :                 tempKeystore.AddCScript(redeemScript);
     531             :             }
     532             :         }
     533             :     }
     534             : 
     535           1 :     const CKeyStore& keystore = tempKeystore;
     536             : 
     537           1 :     bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
     538             : 
     539             :     // Sign what we can:
     540           2 :     for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
     541           1 :         CTxIn& txin = mergedTx.vin[i];
     542           1 :         const Coin& coin = view.AccessCoin(txin.prevout);
     543           1 :         if (coin.IsSpent()) {
     544           1 :             fComplete = false;
     545           1 :             continue;
     546             :         }
     547             : 
     548           0 :         const CScript& prevPubKey = coin.out.scriptPubKey;
     549           0 :         const CAmount& amount = coin.out.nValue;
     550             : 
     551           0 :         SignatureData sigdata;
     552           0 :         SigVersion sigversion =  mergedTx.GetRequiredSigVersion();
     553             :         // Only sign SIGHASH_SINGLE if there's a corresponding output:
     554           0 :         if (!fHashSingle || (i < mergedTx.vout.size()))
     555           0 :             ProduceSignature(
     556           0 :                     MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType),
     557             :                     prevPubKey,
     558             :                     sigdata,
     559             :                     sigversion,
     560             :                     false // no cold stake
     561             :             );
     562             : 
     563             :         // ... and merge in other signatures:
     564           0 :         for (const CTransaction& txv : txVariants) {
     565           0 :             sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata, DataFromTransaction(txv, i));
     566             :         }
     567           0 :         UpdateTransaction(mergedTx, i, sigdata);
     568           0 :         if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS,
     569           0 :                 MutableTransactionSignatureChecker(&mergedTx, i, amount), sigversion))
     570           0 :             fComplete = false;
     571             :     }
     572             : 
     573           1 :     if (fComplete) {
     574             :         // do nothing... for now
     575             :         // perhaps store this for later optional JSON output
     576             :     }
     577             : 
     578           1 :     tx = mergedTx;
     579           1 : }
     580             : 
     581             : class Secp256k1Init
     582             : {
     583             :     ECCVerifyHandle globalVerifyHandle;
     584             : 
     585             : public:
     586           1 :     Secp256k1Init() {
     587           1 :         ECC_Start();
     588           1 :     }
     589           1 :     ~Secp256k1Init() {
     590           1 :         ECC_Stop();
     591             :     }
     592             : };
     593             : 
     594          32 : static void MutateTx(CMutableTransaction& tx, const std::string& command, const std::string& commandVal)
     595             : {
     596          64 :     std::unique_ptr<Secp256k1Init> ecc;
     597          32 :     if (command == "nversion") {
     598           3 :         MutateTxVersion(tx, commandVal);
     599          29 :     } else if (command == "locktime") {
     600           2 :         MutateTxLocktime(tx, commandVal);
     601          27 :     } else if (command == "delin") {
     602           3 :         MutateTxDelInput(tx, commandVal);
     603          24 :     } else if (command == "in") {
     604           7 :         MutateTxAddInput(tx, commandVal);
     605          17 :     } else if (command == "delout") {
     606           3 :         MutateTxDelOutput(tx, commandVal);
     607          14 :     } else if (command == "outaddr") {
     608           7 :         MutateTxAddOutAddr(tx, commandVal);
     609           7 :     } else if (command == "outpubkey") {
     610           0 :         ecc.reset(new Secp256k1Init());
     611           0 :         MutateTxAddOutPubKey(tx, commandVal);
     612           7 :     } else if (command == "outmultisig") {
     613           0 :         ecc.reset(new Secp256k1Init());
     614           0 :         MutateTxAddOutMultiSig(tx, commandVal);
     615           7 :     } else if (command == "outscript") {
     616           4 :         MutateTxAddOutScript(tx, commandVal);
     617           3 :     } else if (command == "sign") {
     618           1 :         ecc.reset(new Secp256k1Init());
     619           1 :         MutateTxSign(tx, commandVal);
     620           2 :     } else if (command == "load") {
     621           0 :         RegisterLoad(commandVal);
     622           2 :     } else if (command == "set") {
     623           2 :         RegisterSet(commandVal);
     624             :     } else {
     625           0 :         throw std::runtime_error("unknown command");
     626             :     }
     627          28 : }
     628             : 
     629           8 : static void OutputTxJSON(const CTransaction& tx)
     630             : {
     631           8 :     UniValue entry(UniValue::VOBJ);
     632           8 :     TxToUniv(tx, UINT256_ZERO, entry);
     633             : 
     634          16 :     std::string jsonOutput = entry.write(4);
     635           8 :     fprintf(stdout, "%s\n", jsonOutput.c_str());
     636           8 : }
     637             : 
     638           0 : static void OutputTxHash(const CTransaction& tx)
     639             : {
     640           0 :     std::string strHexHash = tx.GetHash().GetHex(); // the hex-encoded transaction hash (aka the transaction id)
     641             : 
     642           0 :     fprintf(stdout, "%s\n", strHexHash.c_str());
     643           0 : }
     644             : 
     645          10 : static void OutputTxHex(const CTransaction& tx)
     646             : {
     647          10 :     std::string strHex = EncodeHexTx(tx);
     648             : 
     649          10 :     fprintf(stdout, "%s\n", strHex.c_str());
     650          10 : }
     651             : 
     652          18 : static void OutputTx(const CTransaction& tx)
     653             : {
     654          18 :     if (gArgs.GetBoolArg("-json", false))
     655           8 :         OutputTxJSON(tx);
     656          10 :     else if (gArgs.GetBoolArg("-txid", false))
     657           0 :         OutputTxHash(tx);
     658             :     else
     659          10 :         OutputTxHex(tx);
     660          18 : }
     661             : 
     662           9 : static std::string readStdin()
     663             : {
     664           9 :     char buf[4096];
     665           9 :     std::string ret;
     666             : 
     667          17 :     while (!feof(stdin)) {
     668          17 :         size_t bread = fread(buf, 1, sizeof(buf), stdin);
     669          17 :         ret.append(buf, bread);
     670          17 :         if (bread < sizeof(buf))
     671             :             break;
     672             :     }
     673             : 
     674           9 :     if (ferror(stdin))
     675           0 :         throw std::runtime_error("error reading stdin");
     676             : 
     677           9 :     boost::algorithm::trim_right(ret);
     678             : 
     679           9 :     return ret;
     680             : }
     681             : 
     682          22 : static int CommandLineRawTx(int argc, char* argv[])
     683             : {
     684          22 :     std::string strPrint;
     685          22 :     int nRet = 0;
     686          41 :     try {
     687             :         // Skip switches; Permit common stdin convention "-"
     688          41 :         while (argc > 1 && IsSwitchChar(argv[1][0]) &&
     689          28 :                (argv[1][1] != 0)) {
     690          19 :             argc--;
     691          19 :             argv++;
     692             :         }
     693             : 
     694          22 :         CMutableTransaction tx;
     695          22 :         int startArg;
     696             : 
     697          22 :         if (!fCreateBlank) {
     698             :             // require at least one param
     699          11 :             if (argc < 2)
     700           0 :                 throw std::runtime_error("too few parameters");
     701             : 
     702             :             // param: hex-encoded pivx transaction
     703          22 :             std::string strHexTx(argv[1]);
     704          11 :             if (strHexTx == "-") // "-" implies standard input
     705           9 :                 strHexTx = readStdin();
     706             : 
     707          11 :             if (!DecodeHexTx(tx, strHexTx))
     708           0 :                 throw std::runtime_error("invalid transaction encoding");
     709             : 
     710          11 :             startArg = 2;
     711             :         } else
     712             :             startArg = 1;
     713             : 
     714          50 :         for (int i = startArg; i < argc; i++) {
     715          64 :             std::string arg = argv[i];
     716          68 :             std::string key, value;
     717          32 :             size_t eqpos = arg.find('=');
     718          32 :             if (eqpos == std::string::npos)
     719           0 :                 key = arg;
     720             :             else {
     721          32 :                 key = arg.substr(0, eqpos);
     722          32 :                 value = arg.substr(eqpos + 1);
     723             :             }
     724             : 
     725          32 :             MutateTx(tx, key, value);
     726             :         }
     727             : 
     728          22 :         OutputTx(tx);
     729             :     }
     730             : 
     731           0 :     catch (const boost::thread_interrupted&) {
     732           0 :         throw;
     733           4 :     } catch (const std::exception& e) {
     734           4 :         strPrint = std::string("error: ") + e.what();
     735           4 :         nRet = EXIT_FAILURE;
     736           0 :     } catch (...) {
     737           0 :         PrintExceptionContinue(nullptr, "CommandLineRawTx()");
     738           0 :         throw;
     739             :     }
     740             : 
     741          22 :     if (strPrint != "") {
     742           4 :         fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
     743             :     }
     744          26 :     return nRet;
     745             : }
     746             : 
     747          22 : int main(int argc, char* argv[])
     748             : {
     749          22 :     SetupEnvironment();
     750             : 
     751          22 :     try {
     752          22 :         int ret = AppInitRawTx(argc, argv);
     753          22 :         if (ret != CONTINUE_EXECUTION)
     754             :             return ret;
     755           0 :     } catch (const std::exception& e) {
     756           0 :         PrintExceptionContinue(&e, "AppInitRawTx()");
     757           0 :         return EXIT_FAILURE;
     758           0 :     } catch (...) {
     759           0 :         PrintExceptionContinue(nullptr, "AppInitRawTx()");
     760           0 :         return EXIT_FAILURE;
     761             :     }
     762             : 
     763          22 :     int ret = EXIT_FAILURE;
     764          22 :     try {
     765          22 :         ret = CommandLineRawTx(argc, argv);
     766           0 :     } catch (const std::exception& e) {
     767           0 :         PrintExceptionContinue(&e, "CommandLineRawTx()");
     768           0 :     } catch (...) {
     769           0 :         PrintExceptionContinue(nullptr, "CommandLineRawTx()");
     770             :     }
     771             :     return ret;
     772             : }

Generated by: LCOV version 1.14