dash/src/dash-tx.cpp
MarcoFalke 0fcb967e4d
Merge #13112: Throw an error for unknown args
903055730b Test gArgs erroring on unknown args (Andrew Chow)
4f8704d57f Give an error and exit if there are unknown parameters (Andrew Chow)
174f7c8080 Use a struct for arguments and nested map for categories (Andrew Chow)

Pull request description:

  Following #13190, gArgs is aware of all of the command line arguments. This PR has gArgs check whether the arguments provided are actually valid arguments. When an unknown argument is encountered, an error is printed to stderr and the program exist.

  Since gArgs is used for everything that has command line arguments, `bitcoind`, `bitcoin-cli`, `bitcoin-qt`, `bitcoin-tx`, and `bench_bitcoin` are all effected by this change and all now have the same argument checking behavior.

  Closes #1044

Tree-SHA512: 388201319a7d6493204bb5433da47e8e6c8266882e809f6df45f86d925f1f320f2fd13edb3e57ffc6a37415dfdfc689f83929452bca224229783accb367032e7
2021-06-28 02:00:50 +03:00

819 lines
27 KiB
C++

// Copyright (c) 2009-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
#include <config/dash-config.h>
#endif
#include <clientversion.h>
#include <coins.h>
#include <consensus/consensus.h>
#include <core_io.h>
#include <key_io.h>
#include <keystore.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
#include <script/script.h>
#include <script/sign.h>
#include <univalue.h>
#include <util/system.h>
#include <util/moneystr.h>
#include <util/strencodings.h>
#include <memory>
#include <stdio.h>
#include <boost/algorithm/string.hpp>
#include <stacktraces.h>
static bool fCreateBlank;
static std::map<std::string,UniValue> registers;
static const int CONTINUE_EXECUTION=-1;
static void SetupBitcoinTxArgs()
{
gArgs.AddArg("-?", "This help message", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-create", "Create new, empty TX.", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-json", "Select JSON output", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-txid", "Output only the hex-encoded transaction id of the resultant transaction.", false, OptionsCategory::OPTIONS);
SetupChainParamsBaseOptions();
gArgs.AddArg("delin=N", "Delete input N from TX", false, OptionsCategory::COMMANDS);
gArgs.AddArg("delout=N", "Delete output N from TX", false, OptionsCategory::COMMANDS);
gArgs.AddArg("in=TXID:VOUT(:SEQUENCE_NUMBER)", "Add input to TX", false, OptionsCategory::COMMANDS);
gArgs.AddArg("locktime=N", "Set TX lock time to N", false, OptionsCategory::COMMANDS);
gArgs.AddArg("nversion=N", "Set TX version to N", false, OptionsCategory::COMMANDS);
gArgs.AddArg("outaddr=VALUE:ADDRESS", "Add address-based output to TX", false, OptionsCategory::COMMANDS);
gArgs.AddArg("outdata=[VALUE:]DATA", "Add data-based output to TX", false, OptionsCategory::COMMANDS);
gArgs.AddArg("outmultisig=VALUE:REQUIRED:PUBKEYS:PUBKEY1:PUBKEY2:....[:FLAGS]", "Add Pay To n-of-m Multi-sig output to TX. n = REQUIRED, m = PUBKEYS. "
"Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.", false, OptionsCategory::COMMANDS);
gArgs.AddArg("outpubkey=VALUE:PUBKEY[:FLAGS]", "Add pay-to-pubkey output to TX. "
"Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.", false, OptionsCategory::COMMANDS);
gArgs.AddArg("outscript=VALUE:SCRIPT[:FLAGS]", "Add raw script output to TX. "
"Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.", false, OptionsCategory::COMMANDS);
gArgs.AddArg("sign=SIGHASH-FLAGS", "Add zero or more signatures to transaction. "
"This command requires JSON registers:"
"prevtxs=JSON object, "
"privatekeys=JSON object. "
"See signrawtransaction docs for format of sighash flags, JSON objects.", false, OptionsCategory::COMMANDS);
gArgs.AddArg("load=NAME:FILENAME", "Load JSON file FILENAME into register NAME", false, OptionsCategory::REGISTER_COMMANDS);
gArgs.AddArg("set=NAME:JSON-STRING", "Set register NAME to given JSON-STRING", false, OptionsCategory::REGISTER_COMMANDS);
// Hidden
gArgs.AddArg("-h", "", false, OptionsCategory::HIDDEN);
gArgs.AddArg("-help", "", false, OptionsCategory::HIDDEN);
}
//
// This function returns either one of EXIT_ codes when it's expected to stop the process or
// CONTINUE_EXECUTION when it's expected to continue further.
//
static int AppInitRawTx(int argc, char* argv[])
{
//
// Parameters
//
SetupBitcoinTxArgs();
std::string error;
if (!gArgs.ParseParameters(argc, argv, error)) {
fprintf(stderr, "Error parsing command line arguments: %s\n", error.c_str());
return EXIT_FAILURE;
}
if (gArgs.IsArgSet("-printcrashinfo")) {
std::cout << GetCrashInfoStrFromSerializedStr(gArgs.GetArg("-printcrashinfo", "")) << std::endl;
return true;
}
// Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
try {
SelectParams(gArgs.GetChainName());
} catch (const std::exception& e) {
fprintf(stderr, "Error: %s\n", e.what());
return EXIT_FAILURE;
}
fCreateBlank = gArgs.GetBoolArg("-create", false);
if (argc < 2 || HelpRequested(gArgs)) {
// First part of help message is specific to this utility
std::string strUsage = strprintf("%s dash-tx utility version", PACKAGE_NAME) + " " + FormatFullVersion() + "\n\n" +
"Usage:\n"
" dash-tx [options] <hex-tx> [commands] Update hex-encoded dash transaction\n" +
" dash-tx [options] -create [commands] Create hex-encoded dash transaction\n" +
"\n";
strUsage += gArgs.GetHelpMessage();
fprintf(stdout, "%s", strUsage.c_str());
if (argc < 2) {
fprintf(stderr, "Error: too few parameters\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
return CONTINUE_EXECUTION;
}
static void RegisterSetJson(const std::string& key, const std::string& rawJson)
{
UniValue val;
if (!val.read(rawJson)) {
std::string strErr = "Cannot parse JSON for key " + key;
throw std::runtime_error(strErr);
}
registers[key] = val;
}
static void RegisterSet(const std::string& strInput)
{
// separate NAME:VALUE in string
size_t pos = strInput.find(':');
if ((pos == std::string::npos) ||
(pos == 0) ||
(pos == (strInput.size() - 1)))
throw std::runtime_error("Register input requires NAME:VALUE");
std::string key = strInput.substr(0, pos);
std::string valStr = strInput.substr(pos + 1, std::string::npos);
RegisterSetJson(key, valStr);
}
static void RegisterLoad(const std::string& strInput)
{
// separate NAME:FILENAME in string
size_t pos = strInput.find(':');
if ((pos == std::string::npos) ||
(pos == 0) ||
(pos == (strInput.size() - 1)))
throw std::runtime_error("Register load requires NAME:FILENAME");
std::string key = strInput.substr(0, pos);
std::string filename = strInput.substr(pos + 1, std::string::npos);
FILE *f = fopen(filename.c_str(), "r");
if (!f) {
std::string strErr = "Cannot open file " + filename;
throw std::runtime_error(strErr);
}
// load file chunks into one big buffer
std::string valStr;
while ((!feof(f)) && (!ferror(f))) {
char buf[4096];
int bread = fread(buf, 1, sizeof(buf), f);
if (bread <= 0)
break;
valStr.insert(valStr.size(), buf, bread);
}
int error = ferror(f);
fclose(f);
if (error) {
std::string strErr = "Error reading file " + filename;
throw std::runtime_error(strErr);
}
// evaluate as JSON buffer register
RegisterSetJson(key, valStr);
}
static CAmount ExtractAndValidateValue(const std::string& strValue)
{
CAmount value;
if (!ParseMoney(strValue, value))
throw std::runtime_error("invalid TX output value");
return value;
}
static void MutateTxVersion(CMutableTransaction& tx, const std::string& cmdVal)
{
int64_t newVersion;
if (!ParseInt64(cmdVal, &newVersion) || newVersion < 1 || newVersion > CTransaction::MAX_STANDARD_VERSION)
throw std::runtime_error("Invalid TX version requested: '" + cmdVal + "'");
tx.nVersion = (int) newVersion;
}
static void MutateTxLocktime(CMutableTransaction& tx, const std::string& cmdVal)
{
int64_t newLocktime;
if (!ParseInt64(cmdVal, &newLocktime) || newLocktime < 0LL || newLocktime > 0xffffffffLL)
throw std::runtime_error("Invalid TX locktime requested: '" + cmdVal + "'");
tx.nLockTime = (unsigned int) newLocktime;
}
static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInput)
{
std::vector<std::string> vStrInputParts;
boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
// separate TXID:VOUT in string
if (vStrInputParts.size()<2)
throw std::runtime_error("TX input missing separator");
// extract and validate TXID
std::string strTxid = vStrInputParts[0];
if ((strTxid.size() != 64) || !IsHex(strTxid))
throw std::runtime_error("invalid TX input txid");
uint256 txid(uint256S(strTxid));
static const unsigned int minTxOutSz = 9;
static const unsigned int maxVout = MaxBlockSize() / minTxOutSz;
// extract and validate vout
const std::string& strVout = vStrInputParts[1];
int64_t vout;
if (!ParseInt64(strVout, &vout) || vout < 0 || vout > static_cast<int64_t>(maxVout))
throw std::runtime_error("invalid TX input vout '" + strVout + "'");
// extract the optional sequence number
uint32_t nSequenceIn=std::numeric_limits<unsigned int>::max();
if (vStrInputParts.size() > 2)
nSequenceIn = std::stoul(vStrInputParts[2]);
// append to transaction input list
CTxIn txin(txid, vout, CScript(), nSequenceIn);
tx.vin.push_back(txin);
}
static void MutateTxAddOutAddr(CMutableTransaction& tx, const std::string& strInput)
{
// Separate into VALUE:ADDRESS
std::vector<std::string> vStrInputParts;
boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
if (vStrInputParts.size() != 2)
throw std::runtime_error("TX output missing or too many separators");
// Extract and validate VALUE
CAmount value = ExtractAndValidateValue(vStrInputParts[0]);
// extract and validate ADDRESS
std::string strAddr = vStrInputParts[1];
CTxDestination destination = DecodeDestination(strAddr);
if (!IsValidDestination(destination)) {
throw std::runtime_error("invalid TX output address");
}
CScript scriptPubKey = GetScriptForDestination(destination);
// construct TxOut, append to transaction output list
CTxOut txout(value, scriptPubKey);
tx.vout.push_back(txout);
}
static void MutateTxAddOutPubKey(CMutableTransaction& tx, const std::string& strInput)
{
// Separate into VALUE:PUBKEY[:FLAGS]
std::vector<std::string> vStrInputParts;
boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
if (vStrInputParts.size() < 2 || vStrInputParts.size() > 3)
throw std::runtime_error("TX output missing or too many separators");
// Extract and validate VALUE
CAmount value = ExtractAndValidateValue(vStrInputParts[0]);
// Extract and validate PUBKEY
CPubKey pubkey(ParseHex(vStrInputParts[1]));
if (!pubkey.IsFullyValid())
throw std::runtime_error("invalid TX output pubkey");
CScript scriptPubKey = GetScriptForRawPubKey(pubkey);
// Extract and validate FLAGS
bool bScriptHash = false;
if (vStrInputParts.size() == 3) {
std::string flags = vStrInputParts[2];
bScriptHash = (flags.find('S') != std::string::npos);
}
if (bScriptHash) {
// Get the ID for the script, and then construct a P2SH destination for it.
scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey));
}
// construct TxOut, append to transaction output list
CTxOut txout(value, scriptPubKey);
tx.vout.push_back(txout);
}
static void MutateTxAddOutMultiSig(CMutableTransaction& tx, const std::string& strInput)
{
// Separate into VALUE:REQUIRED:NUMKEYS:PUBKEY1:PUBKEY2:....[:FLAGS]
std::vector<std::string> vStrInputParts;
boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
// Check that there are enough parameters
if (vStrInputParts.size()<3)
throw std::runtime_error("Not enough multisig parameters");
// Extract and validate VALUE
CAmount value = ExtractAndValidateValue(vStrInputParts[0]);
// Extract REQUIRED
uint32_t required = stoul(vStrInputParts[1]);
// Extract NUMKEYS
uint32_t numkeys = stoul(vStrInputParts[2]);
// Validate there are the correct number of pubkeys
if (vStrInputParts.size() < numkeys + 3)
throw std::runtime_error("incorrect number of multisig pubkeys");
if (required < 1 || required > 20 || numkeys < 1 || numkeys > 20 || numkeys < required)
throw std::runtime_error("multisig parameter mismatch. Required " \
+ std::to_string(required) + " of " + std::to_string(numkeys) + "signatures.");
// extract and validate PUBKEYs
std::vector<CPubKey> pubkeys;
for(int pos = 1; pos <= int(numkeys); pos++) {
CPubKey pubkey(ParseHex(vStrInputParts[pos + 2]));
if (!pubkey.IsFullyValid())
throw std::runtime_error("invalid TX output pubkey");
pubkeys.push_back(pubkey);
}
// Extract FLAGS
bool bScriptHash = false;
if (vStrInputParts.size() == numkeys + 4) {
std::string flags = vStrInputParts.back();
bScriptHash = (flags.find('S') != std::string::npos);
}
else if (vStrInputParts.size() > numkeys + 4) {
// Validate that there were no more parameters passed
throw std::runtime_error("Too many parameters");
}
CScript scriptPubKey = GetScriptForMultisig(required, pubkeys);
if (bScriptHash) {
if (scriptPubKey.size() > MAX_SCRIPT_ELEMENT_SIZE) {
throw std::runtime_error(strprintf(
"redeemScript exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_ELEMENT_SIZE));
}
// Get the ID for the script, and then construct a P2SH destination for it.
scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey));
}
// construct TxOut, append to transaction output list
CTxOut txout(value, scriptPubKey);
tx.vout.push_back(txout);
}
static void MutateTxAddOutData(CMutableTransaction& tx, const std::string& strInput)
{
CAmount value = 0;
// separate [VALUE:]DATA in string
size_t pos = strInput.find(':');
if (pos==0)
throw std::runtime_error("TX output value not specified");
if (pos != std::string::npos) {
// Extract and validate VALUE
value = ExtractAndValidateValue(strInput.substr(0, pos));
}
// extract and validate DATA
std::string strData = strInput.substr(pos + 1, std::string::npos);
if (!IsHex(strData))
throw std::runtime_error("invalid TX output data");
std::vector<unsigned char> data = ParseHex(strData);
CTxOut txout(value, CScript() << OP_RETURN << data);
tx.vout.push_back(txout);
}
static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& strInput)
{
// separate VALUE:SCRIPT[:FLAGS]
std::vector<std::string> vStrInputParts;
boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
if (vStrInputParts.size() < 2)
throw std::runtime_error("TX output missing separator");
// Extract and validate VALUE
CAmount value = ExtractAndValidateValue(vStrInputParts[0]);
// extract and validate script
std::string strScript = vStrInputParts[1];
CScript scriptPubKey = ParseScript(strScript);
// Extract FLAGS
bool bScriptHash = false;
if (vStrInputParts.size() == 3) {
std::string flags = vStrInputParts.back();
bScriptHash = (flags.find('S') != std::string::npos);
}
if (scriptPubKey.size() > MAX_SCRIPT_SIZE) {
throw std::runtime_error(strprintf(
"script exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_SIZE));
}
if (bScriptHash) {
if (scriptPubKey.size() > MAX_SCRIPT_ELEMENT_SIZE) {
throw std::runtime_error(strprintf(
"redeemScript exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_ELEMENT_SIZE));
}
scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey));
}
// construct TxOut, append to transaction output list
CTxOut txout(value, scriptPubKey);
tx.vout.push_back(txout);
}
static void MutateTxDelInput(CMutableTransaction& tx, const std::string& strInIdx)
{
// parse requested deletion index
int64_t inIdx;
if (!ParseInt64(strInIdx, &inIdx) || inIdx < 0 || inIdx >= static_cast<int64_t>(tx.vin.size())) {
throw std::runtime_error("Invalid TX input index '" + strInIdx + "'");
}
// delete input from transaction
tx.vin.erase(tx.vin.begin() + inIdx);
}
static void MutateTxDelOutput(CMutableTransaction& tx, const std::string& strOutIdx)
{
// parse requested deletion index
int64_t outIdx;
if (!ParseInt64(strOutIdx, &outIdx) || outIdx < 0 || outIdx >= static_cast<int64_t>(tx.vout.size())) {
throw std::runtime_error("Invalid TX output index '" + strOutIdx + "'");
}
// delete output from transaction
tx.vout.erase(tx.vout.begin() + outIdx);
}
static const unsigned int N_SIGHASH_OPTS = 6;
static const struct {
const char *flagStr;
int flags;
} sighashOptions[N_SIGHASH_OPTS] = {
{"ALL", SIGHASH_ALL},
{"NONE", SIGHASH_NONE},
{"SINGLE", SIGHASH_SINGLE},
{"ALL|ANYONECANPAY", SIGHASH_ALL|SIGHASH_ANYONECANPAY},
{"NONE|ANYONECANPAY", SIGHASH_NONE|SIGHASH_ANYONECANPAY},
{"SINGLE|ANYONECANPAY", SIGHASH_SINGLE|SIGHASH_ANYONECANPAY},
};
static bool findSighashFlags(int& flags, const std::string& flagStr)
{
flags = 0;
for (unsigned int i = 0; i < N_SIGHASH_OPTS; i++) {
if (flagStr == sighashOptions[i].flagStr) {
flags = sighashOptions[i].flags;
return true;
}
}
return false;
}
static CAmount AmountFromValue(const UniValue& value)
{
if (!value.isNum() && !value.isStr())
throw std::runtime_error("Amount is not a number or string");
CAmount amount;
if (!ParseFixedPoint(value.getValStr(), 8, &amount))
throw std::runtime_error("Invalid amount");
if (!MoneyRange(amount))
throw std::runtime_error("Amount out of range");
return amount;
}
static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
{
int nHashType = SIGHASH_ALL;
if (flagStr.size() > 0)
if (!findSighashFlags(nHashType, flagStr))
throw std::runtime_error("unknown sighash flag/sign option");
// mergedTx will end up with all the signatures; it
// starts as a clone of the raw tx:
CMutableTransaction mergedTx{tx};
const CMutableTransaction txv{tx};
CCoinsView viewDummy;
CCoinsViewCache view(&viewDummy);
if (!registers.count("privatekeys"))
throw std::runtime_error("privatekeys register variable must be set.");
CBasicKeyStore tempKeystore;
UniValue keysObj = registers["privatekeys"];
for (unsigned int kidx = 0; kidx < keysObj.size(); kidx++) {
if (!keysObj[kidx].isStr())
throw std::runtime_error("privatekey not a std::string");
CKey key = DecodeSecret(keysObj[kidx].getValStr());
if (!key.IsValid()) {
throw std::runtime_error("privatekey not valid");
}
tempKeystore.AddKey(key);
}
// Add previous txouts given in the RPC call:
if (!registers.count("prevtxs"))
throw std::runtime_error("prevtxs register variable must be set.");
UniValue prevtxsObj = registers["prevtxs"];
{
for (unsigned int previdx = 0; previdx < prevtxsObj.size(); previdx++) {
UniValue prevOut = prevtxsObj[previdx];
if (!prevOut.isObject())
throw std::runtime_error("expected prevtxs internal object");
std::map<std::string, UniValue::VType> types = {
{"txid", UniValue::VSTR},
{"vout", UniValue::VNUM},
{"scriptPubKey", UniValue::VSTR},
};
if (!prevOut.checkObject(types))
throw std::runtime_error("prevtxs internal object typecheck fail");
uint256 txid = ParseHashStr(prevOut["txid"].get_str(), "txid");
const int nOut = prevOut["vout"].get_int();
if (nOut < 0)
throw std::runtime_error("vout must be positive");
COutPoint out(txid, nOut);
std::vector<unsigned char> pkData(ParseHexUV(prevOut["scriptPubKey"], "scriptPubKey"));
CScript scriptPubKey(pkData.begin(), pkData.end());
{
const Coin& coin = view.AccessCoin(out);
if (!coin.IsSpent() && coin.out.scriptPubKey != scriptPubKey) {
std::string err("Previous output scriptPubKey mismatch:\n");
err = err + ScriptToAsmStr(coin.out.scriptPubKey) + "\nvs:\n"+
ScriptToAsmStr(scriptPubKey);
throw std::runtime_error(err);
}
Coin newcoin;
newcoin.out.scriptPubKey = scriptPubKey;
newcoin.out.nValue = 0; // we don't know the actual output value
if (prevOut.exists("amount")) {
newcoin.out.nValue = AmountFromValue(prevOut["amount"]);
}
newcoin.nHeight = 1;
view.AddCoin(out, std::move(newcoin), true);
}
// if redeemScript given and private keys given,
// add redeemScript to the tempKeystore so it can be signed:
if (scriptPubKey.IsPayToScriptHash() &&
prevOut.exists("redeemScript")) {
UniValue v = prevOut["redeemScript"];
std::vector<unsigned char> rsData(ParseHexUV(v, "redeemScript"));
CScript redeemScript(rsData.begin(), rsData.end());
tempKeystore.AddCScript(redeemScript);
}
}
}
const CKeyStore& keystore = tempKeystore;
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
// Sign what we can:
for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
const CTxIn& txin = mergedTx.vin[i];
const Coin& coin = view.AccessCoin(txin.prevout);
if (coin.IsSpent()) {
continue;
}
const CScript& prevPubKey = coin.out.scriptPubKey;
const CAmount& amount = coin.out.nValue;
SignatureData sigdata;
// Only sign SIGHASH_SINGLE if there's a corresponding output:
if (!fHashSingle || (i < mergedTx.vout.size()))
ProduceSignature(keystore, MutableTransactionSignatureCreator(&mergedTx, i, amount, nHashType), prevPubKey, sigdata);
// ... and merge in other signatures:
sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata, DataFromTransaction(txv, i));
UpdateTransaction(mergedTx, i, sigdata);
}
tx = mergedTx;
}
class Secp256k1Init
{
ECCVerifyHandle globalVerifyHandle;
public:
Secp256k1Init() {
ECC_Start();
}
~Secp256k1Init() {
ECC_Stop();
}
};
static void MutateTx(CMutableTransaction& tx, const std::string& command,
const std::string& commandVal)
{
std::unique_ptr<Secp256k1Init> ecc;
if (command == "nversion")
MutateTxVersion(tx, commandVal);
else if (command == "locktime")
MutateTxLocktime(tx, commandVal);
else if (command == "delin")
MutateTxDelInput(tx, commandVal);
else if (command == "in")
MutateTxAddInput(tx, commandVal);
else if (command == "delout")
MutateTxDelOutput(tx, commandVal);
else if (command == "outaddr")
MutateTxAddOutAddr(tx, commandVal);
else if (command == "outpubkey") {
ecc.reset(new Secp256k1Init());
MutateTxAddOutPubKey(tx, commandVal);
} else if (command == "outmultisig") {
ecc.reset(new Secp256k1Init());
MutateTxAddOutMultiSig(tx, commandVal);
} else if (command == "outscript")
MutateTxAddOutScript(tx, commandVal);
else if (command == "outdata")
MutateTxAddOutData(tx, commandVal);
else if (command == "sign") {
ecc.reset(new Secp256k1Init());
MutateTxSign(tx, commandVal);
}
else if (command == "load")
RegisterLoad(commandVal);
else if (command == "set")
RegisterSet(commandVal);
else
throw std::runtime_error("unknown command");
}
static void OutputTxJSON(const CTransaction& tx)
{
UniValue entry(UniValue::VOBJ);
TxToUniv(tx, uint256(), entry);
std::string jsonOutput = entry.write(4);
fprintf(stdout, "%s\n", jsonOutput.c_str());
}
static void OutputTxHash(const CTransaction& tx)
{
std::string strHexHash = tx.GetHash().GetHex(); // the hex-encoded transaction hash (aka the transaction id)
fprintf(stdout, "%s\n", strHexHash.c_str());
}
static void OutputTxHex(const CTransaction& tx)
{
std::string strHex = EncodeHexTx(tx);
fprintf(stdout, "%s\n", strHex.c_str());
}
static void OutputTx(const CTransaction& tx)
{
if (gArgs.GetBoolArg("-json", false))
OutputTxJSON(tx);
else if (gArgs.GetBoolArg("-txid", false))
OutputTxHash(tx);
else
OutputTxHex(tx);
}
static std::string readStdin()
{
char buf[4096];
std::string ret;
while (!feof(stdin)) {
size_t bread = fread(buf, 1, sizeof(buf), stdin);
ret.append(buf, bread);
if (bread < sizeof(buf))
break;
}
if (ferror(stdin))
throw std::runtime_error("error reading stdin");
boost::algorithm::trim_right(ret);
return ret;
}
static int CommandLineRawTx(int argc, char* argv[])
{
std::string strPrint;
int nRet = 0;
try {
// Skip switches; Permit common stdin convention "-"
while (argc > 1 && IsSwitchChar(argv[1][0]) &&
(argv[1][1] != 0)) {
argc--;
argv++;
}
CMutableTransaction tx;
int startArg;
if (!fCreateBlank) {
// require at least one param
if (argc < 2)
throw std::runtime_error("too few parameters");
// param: hex-encoded dash transaction
std::string strHexTx(argv[1]);
if (strHexTx == "-") // "-" implies standard input
strHexTx = readStdin();
if (!DecodeHexTx(tx, strHexTx))
throw std::runtime_error("invalid transaction encoding");
startArg = 2;
} else
startArg = 1;
for (int i = startArg; i < argc; i++) {
std::string arg = argv[i];
std::string key, value;
size_t eqpos = arg.find('=');
if (eqpos == std::string::npos)
key = arg;
else {
key = arg.substr(0, eqpos);
value = arg.substr(eqpos + 1);
}
MutateTx(tx, key, value);
}
OutputTx(tx);
}
catch (const boost::thread_interrupted&) {
throw;
}
catch (const std::exception& e) {
strPrint = std::string("error: ") + e.what();
nRet = EXIT_FAILURE;
}
catch (...) {
PrintExceptionContinue(std::current_exception(), "CommandLineRawTx()");
throw;
}
if (strPrint != "") {
fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
}
return nRet;
}
int main(int argc, char* argv[])
{
RegisterPrettyTerminateHander();
RegisterPrettySignalHandlers();
SetupEnvironment();
try {
int ret = AppInitRawTx(argc, argv);
if (ret != CONTINUE_EXECUTION)
return ret;
}
catch (const std::exception& e) {
PrintExceptionContinue(std::current_exception(), "AppInitRawTx()");
return EXIT_FAILURE;
}
int ret = EXIT_FAILURE;
try {
ret = CommandLineRawTx(argc, argv);
} catch (...) {
PrintExceptionContinue(std::current_exception(), "CommandLineRawTx()");
}
return ret;
}