Auto merge of #3086 - str4d:cli-binary-improvements-1, r=str4d
CLI binary improvements Cherry-picked from the following upstream PRs: - bitcoin/bitcoin#5936 - bitcoin/bitcoin#7550 - bitcoin/bitcoin#7989 - bitcoin/bitcoin#7957 - bitcoin/bitcoin#9067 - bitcoin/bitcoin#9220 Excludes any changes that affected the QT code.
This commit is contained in:
@@ -4,3 +4,18 @@ release-notes at release time)
|
|||||||
Notable changes
|
Notable changes
|
||||||
===============
|
===============
|
||||||
|
|
||||||
|
zcash-cli: arguments privacy
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
The RPC command line client gained a new argument, `-stdin`
|
||||||
|
to read extra arguments from standard input, one per line until EOF/Ctrl-D.
|
||||||
|
For example:
|
||||||
|
|
||||||
|
$ src/zcash-cli -stdin walletpassphrase
|
||||||
|
mysecretcode
|
||||||
|
120
|
||||||
|
^D (Ctrl-D)
|
||||||
|
|
||||||
|
It is recommended to use this for sensitive information such as private keys, as
|
||||||
|
command-line arguments can usually be read from the process table by any user on
|
||||||
|
the system.
|
||||||
|
|||||||
@@ -145,5 +145,11 @@ class RawTransactionsTest(BitcoinTestFramework):
|
|||||||
self.sync_all()
|
self.sync_all()
|
||||||
assert_equal(self.nodes[0].getbalance(), bal+Decimal('10.00000000')+Decimal('2.19900000')) #block reward + tx
|
assert_equal(self.nodes[0].getbalance(), bal+Decimal('10.00000000')+Decimal('2.19900000')) #block reward + tx
|
||||||
|
|
||||||
|
inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : 1000}]
|
||||||
|
outputs = { self.nodes[0].getnewaddress() : 1 }
|
||||||
|
rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
|
||||||
|
decrawtx= self.nodes[0].decoderawtransaction(rawtx)
|
||||||
|
assert_equal(decrawtx['vin'][0]['sequence'], 1000)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
RawTransactionsTest().main()
|
RawTransactionsTest().main()
|
||||||
|
|||||||
@@ -19,13 +19,12 @@
|
|||||||
|
|
||||||
#include <univalue.h>
|
#include <univalue.h>
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;
|
static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;
|
||||||
|
static const int CONTINUE_EXECUTION=-1;
|
||||||
|
|
||||||
std::string HelpMessageCli()
|
std::string HelpMessageCli()
|
||||||
{
|
{
|
||||||
string strUsage;
|
std::string strUsage;
|
||||||
strUsage += HelpMessageGroup(_("Options:"));
|
strUsage += HelpMessageGroup(_("Options:"));
|
||||||
strUsage += HelpMessageOpt("-?", _("This help message"));
|
strUsage += HelpMessageOpt("-?", _("This help message"));
|
||||||
strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), "zcash.conf"));
|
strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), "zcash.conf"));
|
||||||
@@ -39,6 +38,7 @@ std::string HelpMessageCli()
|
|||||||
strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections"));
|
strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections"));
|
||||||
strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections"));
|
strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections"));
|
||||||
strUsage += HelpMessageOpt("-rpcclienttimeout=<n>", strprintf(_("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)"), DEFAULT_HTTP_CLIENT_TIMEOUT));
|
strUsage += HelpMessageOpt("-rpcclienttimeout=<n>", strprintf(_("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)"), DEFAULT_HTTP_CLIENT_TIMEOUT));
|
||||||
|
strUsage += HelpMessageOpt("-stdin", _("Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases)"));
|
||||||
|
|
||||||
return strUsage;
|
return strUsage;
|
||||||
}
|
}
|
||||||
@@ -62,8 +62,16 @@ public:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool AppInitRPC(int argc, char* argv[])
|
//
|
||||||
|
// 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 AppInitRPC(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
|
static_assert(CONTINUE_EXECUTION != EXIT_FAILURE,
|
||||||
|
"CONTINUE_EXECUTION should be different from EXIT_FAILURE");
|
||||||
|
static_assert(CONTINUE_EXECUTION != EXIT_SUCCESS,
|
||||||
|
"CONTINUE_EXECUTION should be different from EXIT_SUCCESS");
|
||||||
//
|
//
|
||||||
// Parameters
|
// Parameters
|
||||||
//
|
//
|
||||||
@@ -82,29 +90,33 @@ static bool AppInitRPC(int argc, char* argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stdout, "%s", strUsage.c_str());
|
fprintf(stdout, "%s", strUsage.c_str());
|
||||||
return false;
|
if (argc < 2) {
|
||||||
|
fprintf(stderr, "Error: too few parameters\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
if (!boost::filesystem::is_directory(GetDataDir(false))) {
|
if (!boost::filesystem::is_directory(GetDataDir(false))) {
|
||||||
fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str());
|
fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str());
|
||||||
return false;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
ReadConfigFile(mapArgs, mapMultiArgs);
|
ReadConfigFile(mapArgs, mapMultiArgs);
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
fprintf(stderr,"Error reading configuration file: %s\n", e.what());
|
fprintf(stderr,"Error reading configuration file: %s\n", e.what());
|
||||||
return false;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
// Check for -testnet or -regtest parameter (BaseParams() calls are only valid after this clause)
|
// Check for -testnet or -regtest parameter (BaseParams() calls are only valid after this clause)
|
||||||
if (!SelectBaseParamsFromCommandLine()) {
|
if (!SelectBaseParamsFromCommandLine()) {
|
||||||
fprintf(stderr, "Error: Invalid combination of -regtest and -testnet.\n");
|
fprintf(stderr, "Error: Invalid combination of -regtest and -testnet.\n");
|
||||||
return false;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
if (GetBoolArg("-rpcssl", false))
|
if (GetBoolArg("-rpcssl", false))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Error: SSL mode for RPC (-rpcssl) is no longer supported.\n");
|
fprintf(stderr, "Error: SSL mode for RPC (-rpcssl) is no longer supported.\n");
|
||||||
return false;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
return true;
|
return CONTINUE_EXECUTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -173,7 +185,7 @@ static void http_error_cb(enum evhttp_request_error err, void *ctx)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
UniValue CallRPC(const string& strMethod, const UniValue& params)
|
UniValue CallRPC(const std::string& strMethod, const UniValue& params)
|
||||||
{
|
{
|
||||||
std::string host = GetArg("-rpcconnect", "127.0.0.1");
|
std::string host = GetArg("-rpcconnect", "127.0.0.1");
|
||||||
int port = GetArg("-rpcport", BaseParams().RPCPort());
|
int port = GetArg("-rpcport", BaseParams().RPCPort());
|
||||||
@@ -188,7 +200,7 @@ UniValue CallRPC(const string& strMethod, const UniValue& params)
|
|||||||
HTTPReply response;
|
HTTPReply response;
|
||||||
raii_evhttp_request req = obtain_evhttp_request(http_request_done, (void*)&response);
|
raii_evhttp_request req = obtain_evhttp_request(http_request_done, (void*)&response);
|
||||||
if (req == NULL)
|
if (req == NULL)
|
||||||
throw runtime_error("create http request failed");
|
throw std::runtime_error("create http request failed");
|
||||||
#if LIBEVENT_VERSION_NUMBER >= 0x02010300
|
#if LIBEVENT_VERSION_NUMBER >= 0x02010300
|
||||||
evhttp_request_set_error_cb(req.get(), http_error_cb);
|
evhttp_request_set_error_cb(req.get(), http_error_cb);
|
||||||
#endif
|
#endif
|
||||||
@@ -198,7 +210,7 @@ UniValue CallRPC(const string& strMethod, const UniValue& params)
|
|||||||
if (mapArgs["-rpcpassword"] == "") {
|
if (mapArgs["-rpcpassword"] == "") {
|
||||||
// Try fall back to cookie-based authentication if no password is provided
|
// Try fall back to cookie-based authentication if no password is provided
|
||||||
if (!GetAuthCookie(&strRPCUserColonPass)) {
|
if (!GetAuthCookie(&strRPCUserColonPass)) {
|
||||||
throw runtime_error(strprintf(
|
throw std::runtime_error(strprintf(
|
||||||
_("Could not locate RPC credentials. No authentication cookie could be found,\n"
|
_("Could not locate RPC credentials. No authentication cookie could be found,\n"
|
||||||
"and no rpcpassword is set in the configuration file (%s)."),
|
"and no rpcpassword is set in the configuration file (%s)."),
|
||||||
GetConfigFile().string().c_str()));
|
GetConfigFile().string().c_str()));
|
||||||
@@ -231,26 +243,26 @@ UniValue CallRPC(const string& strMethod, const UniValue& params)
|
|||||||
if (response.status == 0)
|
if (response.status == 0)
|
||||||
throw CConnectionFailed(strprintf("couldn't connect to server: %s (code %d)\n(make sure server is running and you are connecting to the correct RPC port)", http_errorstring(response.error), response.error));
|
throw CConnectionFailed(strprintf("couldn't connect to server: %s (code %d)\n(make sure server is running and you are connecting to the correct RPC port)", http_errorstring(response.error), response.error));
|
||||||
else if (response.status == HTTP_UNAUTHORIZED)
|
else if (response.status == HTTP_UNAUTHORIZED)
|
||||||
throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
|
throw std::runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
|
||||||
else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR)
|
else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR)
|
||||||
throw runtime_error(strprintf("server returned HTTP error %d", response.status));
|
throw std::runtime_error(strprintf("server returned HTTP error %d", response.status));
|
||||||
else if (response.body.empty())
|
else if (response.body.empty())
|
||||||
throw runtime_error("no response from server");
|
throw std::runtime_error("no response from server");
|
||||||
|
|
||||||
// Parse reply
|
// Parse reply
|
||||||
UniValue valReply(UniValue::VSTR);
|
UniValue valReply(UniValue::VSTR);
|
||||||
if (!valReply.read(response.body))
|
if (!valReply.read(response.body))
|
||||||
throw runtime_error("couldn't parse reply from server");
|
throw std::runtime_error("couldn't parse reply from server");
|
||||||
const UniValue& reply = valReply.get_obj();
|
const UniValue& reply = valReply.get_obj();
|
||||||
if (reply.empty())
|
if (reply.empty())
|
||||||
throw runtime_error("expected reply to have result, error and id properties");
|
throw std::runtime_error("expected reply to have result, error and id properties");
|
||||||
|
|
||||||
return reply;
|
return reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CommandLineRPC(int argc, char *argv[])
|
int CommandLineRPC(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
string strPrint;
|
std::string strPrint;
|
||||||
int nRet = 0;
|
int nRet = 0;
|
||||||
try {
|
try {
|
||||||
// Skip switches
|
// Skip switches
|
||||||
@@ -258,15 +270,17 @@ int CommandLineRPC(int argc, char *argv[])
|
|||||||
argc--;
|
argc--;
|
||||||
argv++;
|
argv++;
|
||||||
}
|
}
|
||||||
|
std::vector<std::string> args = std::vector<std::string>(&argv[1], &argv[argc]);
|
||||||
// Method
|
if (GetBoolArg("-stdin", false)) {
|
||||||
if (argc < 2)
|
// Read one arg per line from stdin and append
|
||||||
throw runtime_error("too few parameters");
|
std::string line;
|
||||||
string strMethod = argv[1];
|
while (std::getline(std::cin,line))
|
||||||
|
args.push_back(line);
|
||||||
// Parameters default to strings
|
}
|
||||||
std::vector<std::string> strParams(&argv[2], &argv[argc]);
|
if (args.size() < 1)
|
||||||
UniValue params = RPCConvertValues(strMethod, strParams);
|
throw std::runtime_error("too few parameters (need at least command)");
|
||||||
|
std::string strMethod = args[0];
|
||||||
|
UniValue params = RPCConvertValues(strMethod, std::vector<std::string>(args.begin()+1, args.end()));
|
||||||
|
|
||||||
// Execute and handle connection failures with -rpcwait
|
// Execute and handle connection failures with -rpcwait
|
||||||
const bool fWait = GetBoolArg("-rpcwait", false);
|
const bool fWait = GetBoolArg("-rpcwait", false);
|
||||||
@@ -318,7 +332,7 @@ int CommandLineRPC(int argc, char *argv[])
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
catch (const std::exception& e) {
|
||||||
strPrint = string("error: ") + e.what();
|
strPrint = std::string("error: ") + e.what();
|
||||||
nRet = EXIT_FAILURE;
|
nRet = EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
catch (...) {
|
catch (...) {
|
||||||
@@ -337,12 +351,13 @@ int main(int argc, char* argv[])
|
|||||||
SetupEnvironment();
|
SetupEnvironment();
|
||||||
if (!SetupNetworking()) {
|
if (!SetupNetworking()) {
|
||||||
fprintf(stderr, "Error: Initializing networking failed\n");
|
fprintf(stderr, "Error: Initializing networking failed\n");
|
||||||
exit(1);
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if(!AppInitRPC(argc, argv))
|
int ret = AppInitRPC(argc, argv);
|
||||||
return EXIT_FAILURE;
|
if (ret != CONTINUE_EXECUTION)
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
catch (const std::exception& e) {
|
||||||
PrintExceptionContinue(&e, "AppInitRPC()");
|
PrintExceptionContinue(&e, "AppInitRPC()");
|
||||||
|
|||||||
@@ -22,12 +22,15 @@
|
|||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <boost/assign/list_of.hpp>
|
#include <boost/assign/list_of.hpp>
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
static bool fCreateBlank;
|
static bool fCreateBlank;
|
||||||
static map<string,UniValue> registers;
|
static std::map<std::string,UniValue> registers;
|
||||||
|
static const int CONTINUE_EXECUTION=-1;
|
||||||
|
|
||||||
static bool AppInitRawTx(int argc, char* argv[])
|
//
|
||||||
|
// 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
|
// Parameters
|
||||||
@@ -66,7 +69,7 @@ static bool AppInitRawTx(int argc, char* argv[])
|
|||||||
strUsage = HelpMessageGroup(_("Commands:"));
|
strUsage = HelpMessageGroup(_("Commands:"));
|
||||||
strUsage += HelpMessageOpt("delin=N", _("Delete input N from TX"));
|
strUsage += HelpMessageOpt("delin=N", _("Delete input N from TX"));
|
||||||
strUsage += HelpMessageOpt("delout=N", _("Delete output N from TX"));
|
strUsage += HelpMessageOpt("delout=N", _("Delete output N from TX"));
|
||||||
strUsage += HelpMessageOpt("in=TXID:VOUT", _("Add input to TX"));
|
strUsage += HelpMessageOpt("in=TXID:VOUT(:SEQUENCE_NUMBER)", _("Add input to TX"));
|
||||||
strUsage += HelpMessageOpt("locktime=N", _("Set TX lock time to N"));
|
strUsage += HelpMessageOpt("locktime=N", _("Set TX lock time to N"));
|
||||||
strUsage += HelpMessageOpt("nversion=N", _("Set TX version to N"));
|
strUsage += HelpMessageOpt("nversion=N", _("Set TX version to N"));
|
||||||
strUsage += HelpMessageOpt("outaddr=VALUE:ADDRESS", _("Add address-based output to TX"));
|
strUsage += HelpMessageOpt("outaddr=VALUE:ADDRESS", _("Add address-based output to TX"));
|
||||||
@@ -83,57 +86,61 @@ static bool AppInitRawTx(int argc, char* argv[])
|
|||||||
strUsage += HelpMessageOpt("set=NAME:JSON-STRING", _("Set register NAME to given JSON-STRING"));
|
strUsage += HelpMessageOpt("set=NAME:JSON-STRING", _("Set register NAME to given JSON-STRING"));
|
||||||
fprintf(stdout, "%s", strUsage.c_str());
|
fprintf(stdout, "%s", strUsage.c_str());
|
||||||
|
|
||||||
return false;
|
if (argc < 2) {
|
||||||
|
fprintf(stderr, "Error: too few parameters\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
return true;
|
return CONTINUE_EXECUTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void RegisterSetJson(const string& key, const string& rawJson)
|
static void RegisterSetJson(const std::string& key, const std::string& rawJson)
|
||||||
{
|
{
|
||||||
UniValue val;
|
UniValue val;
|
||||||
if (!val.read(rawJson)) {
|
if (!val.read(rawJson)) {
|
||||||
string strErr = "Cannot parse JSON for key " + key;
|
std::string strErr = "Cannot parse JSON for key " + key;
|
||||||
throw runtime_error(strErr);
|
throw std::runtime_error(strErr);
|
||||||
}
|
}
|
||||||
|
|
||||||
registers[key] = val;
|
registers[key] = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void RegisterSet(const string& strInput)
|
static void RegisterSet(const std::string& strInput)
|
||||||
{
|
{
|
||||||
// separate NAME:VALUE in string
|
// separate NAME:VALUE in string
|
||||||
size_t pos = strInput.find(':');
|
size_t pos = strInput.find(':');
|
||||||
if ((pos == string::npos) ||
|
if ((pos == std::string::npos) ||
|
||||||
(pos == 0) ||
|
(pos == 0) ||
|
||||||
(pos == (strInput.size() - 1)))
|
(pos == (strInput.size() - 1)))
|
||||||
throw runtime_error("Register input requires NAME:VALUE");
|
throw std::runtime_error("Register input requires NAME:VALUE");
|
||||||
|
|
||||||
string key = strInput.substr(0, pos);
|
std::string key = strInput.substr(0, pos);
|
||||||
string valStr = strInput.substr(pos + 1, string::npos);
|
std::string valStr = strInput.substr(pos + 1, std::string::npos);
|
||||||
|
|
||||||
RegisterSetJson(key, valStr);
|
RegisterSetJson(key, valStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void RegisterLoad(const string& strInput)
|
static void RegisterLoad(const std::string& strInput)
|
||||||
{
|
{
|
||||||
// separate NAME:FILENAME in string
|
// separate NAME:FILENAME in string
|
||||||
size_t pos = strInput.find(':');
|
size_t pos = strInput.find(':');
|
||||||
if ((pos == string::npos) ||
|
if ((pos == std::string::npos) ||
|
||||||
(pos == 0) ||
|
(pos == 0) ||
|
||||||
(pos == (strInput.size() - 1)))
|
(pos == (strInput.size() - 1)))
|
||||||
throw runtime_error("Register load requires NAME:FILENAME");
|
throw std::runtime_error("Register load requires NAME:FILENAME");
|
||||||
|
|
||||||
string key = strInput.substr(0, pos);
|
std::string key = strInput.substr(0, pos);
|
||||||
string filename = strInput.substr(pos + 1, string::npos);
|
std::string filename = strInput.substr(pos + 1, std::string::npos);
|
||||||
|
|
||||||
FILE *f = fopen(filename.c_str(), "r");
|
FILE *f = fopen(filename.c_str(), "r");
|
||||||
if (!f) {
|
if (!f) {
|
||||||
string strErr = "Cannot open file " + filename;
|
std::string strErr = "Cannot open file " + filename;
|
||||||
throw runtime_error(strErr);
|
throw std::runtime_error(strErr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// load file chunks into one big buffer
|
// load file chunks into one big buffer
|
||||||
string valStr;
|
std::string valStr;
|
||||||
while ((!feof(f)) && (!ferror(f))) {
|
while ((!feof(f)) && (!ferror(f))) {
|
||||||
char buf[4096];
|
char buf[4096];
|
||||||
int bread = fread(buf, 1, sizeof(buf), f);
|
int bread = fread(buf, 1, sizeof(buf), f);
|
||||||
@@ -147,90 +154,95 @@ static void RegisterLoad(const string& strInput)
|
|||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
string strErr = "Error reading file " + filename;
|
std::string strErr = "Error reading file " + filename;
|
||||||
throw runtime_error(strErr);
|
throw std::runtime_error(strErr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// evaluate as JSON buffer register
|
// evaluate as JSON buffer register
|
||||||
RegisterSetJson(key, valStr);
|
RegisterSetJson(key, valStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void MutateTxVersion(CMutableTransaction& tx, const string& cmdVal)
|
static void MutateTxVersion(CMutableTransaction& tx, const std::string& cmdVal)
|
||||||
{
|
{
|
||||||
int64_t newVersion = atoi64(cmdVal);
|
int64_t newVersion = atoi64(cmdVal);
|
||||||
if (newVersion < CTransaction::SPROUT_MIN_CURRENT_VERSION || newVersion > CTransaction::SPROUT_MAX_CURRENT_VERSION)
|
if (newVersion < CTransaction::SPROUT_MIN_CURRENT_VERSION || newVersion > CTransaction::SPROUT_MAX_CURRENT_VERSION)
|
||||||
throw runtime_error("Invalid TX version requested");
|
throw std::runtime_error("Invalid TX version requested");
|
||||||
|
|
||||||
tx.nVersion = (int) newVersion;
|
tx.nVersion = (int) newVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void MutateTxExpiry(CMutableTransaction& tx, const string& cmdVal)
|
static void MutateTxExpiry(CMutableTransaction& tx, const std::string& cmdVal)
|
||||||
{
|
{
|
||||||
int64_t newExpiry = atoi64(cmdVal);
|
int64_t newExpiry = atoi64(cmdVal);
|
||||||
if (newExpiry >= TX_EXPIRY_HEIGHT_THRESHOLD) {
|
if (newExpiry >= TX_EXPIRY_HEIGHT_THRESHOLD) {
|
||||||
throw runtime_error("Invalid TX expiry requested");
|
throw std::runtime_error("Invalid TX expiry requested");
|
||||||
}
|
}
|
||||||
tx.nExpiryHeight = (int) newExpiry;
|
tx.nExpiryHeight = (int) newExpiry;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void MutateTxLocktime(CMutableTransaction& tx, const string& cmdVal)
|
static void MutateTxLocktime(CMutableTransaction& tx, const std::string& cmdVal)
|
||||||
{
|
{
|
||||||
int64_t newLocktime = atoi64(cmdVal);
|
int64_t newLocktime = atoi64(cmdVal);
|
||||||
if (newLocktime < 0LL || newLocktime > 0xffffffffLL)
|
if (newLocktime < 0LL || newLocktime > 0xffffffffLL)
|
||||||
throw runtime_error("Invalid TX locktime requested");
|
throw std::runtime_error("Invalid TX locktime requested");
|
||||||
|
|
||||||
tx.nLockTime = (unsigned int) newLocktime;
|
tx.nLockTime = (unsigned int) newLocktime;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void MutateTxAddInput(CMutableTransaction& tx, const string& strInput)
|
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
|
// separate TXID:VOUT in string
|
||||||
size_t pos = strInput.find(':');
|
if (vStrInputParts.size()<2)
|
||||||
if ((pos == string::npos) ||
|
throw std::runtime_error("TX input missing separator");
|
||||||
(pos == 0) ||
|
|
||||||
(pos == (strInput.size() - 1)))
|
|
||||||
throw runtime_error("TX input missing separator");
|
|
||||||
|
|
||||||
// extract and validate TXID
|
// extract and validate TXID
|
||||||
string strTxid = strInput.substr(0, pos);
|
std::string strTxid = vStrInputParts[0];
|
||||||
if ((strTxid.size() != 64) || !IsHex(strTxid))
|
if ((strTxid.size() != 64) || !IsHex(strTxid))
|
||||||
throw runtime_error("invalid TX input txid");
|
throw std::runtime_error("invalid TX input txid");
|
||||||
uint256 txid(uint256S(strTxid));
|
uint256 txid(uint256S(strTxid));
|
||||||
|
|
||||||
static const unsigned int minTxOutSz = 9;
|
static const unsigned int minTxOutSz = 9;
|
||||||
static const unsigned int maxVout = MAX_BLOCK_SIZE / minTxOutSz;
|
static const unsigned int maxVout = MAX_BLOCK_SIZE / minTxOutSz;
|
||||||
|
|
||||||
// extract and validate vout
|
// extract and validate vout
|
||||||
string strVout = strInput.substr(pos + 1, string::npos);
|
std::string strVout = vStrInputParts[1];
|
||||||
int vout = atoi(strVout);
|
int vout = atoi(strVout);
|
||||||
if ((vout < 0) || (vout > (int)maxVout))
|
if ((vout < 0) || (vout > (int)maxVout))
|
||||||
throw runtime_error("invalid TX input vout");
|
throw std::runtime_error("invalid TX input vout");
|
||||||
|
|
||||||
|
// extract the optional sequence number
|
||||||
|
uint32_t nSequenceIn=std::numeric_limits<unsigned int>::max();
|
||||||
|
if (vStrInputParts.size() > 2)
|
||||||
|
nSequenceIn = atoi(vStrInputParts[2]);
|
||||||
|
|
||||||
// append to transaction input list
|
// append to transaction input list
|
||||||
CTxIn txin(txid, vout);
|
CTxIn txin(txid, vout, CScript(), nSequenceIn);
|
||||||
tx.vin.push_back(txin);
|
tx.vin.push_back(txin);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void MutateTxAddOutAddr(CMutableTransaction& tx, const string& strInput)
|
static void MutateTxAddOutAddr(CMutableTransaction& tx, const std::string& strInput)
|
||||||
{
|
{
|
||||||
// separate VALUE:ADDRESS in string
|
// separate VALUE:ADDRESS in string
|
||||||
size_t pos = strInput.find(':');
|
size_t pos = strInput.find(':');
|
||||||
if ((pos == string::npos) ||
|
if ((pos == std::string::npos) ||
|
||||||
(pos == 0) ||
|
(pos == 0) ||
|
||||||
(pos == (strInput.size() - 1)))
|
(pos == (strInput.size() - 1)))
|
||||||
throw runtime_error("TX output missing separator");
|
throw std::runtime_error("TX output missing separator");
|
||||||
|
|
||||||
// extract and validate VALUE
|
// extract and validate VALUE
|
||||||
string strValue = strInput.substr(0, pos);
|
std::string strValue = strInput.substr(0, pos);
|
||||||
CAmount value;
|
CAmount value;
|
||||||
if (!ParseMoney(strValue, value))
|
if (!ParseMoney(strValue, value))
|
||||||
throw runtime_error("invalid TX output value");
|
throw std::runtime_error("invalid TX output value");
|
||||||
|
|
||||||
// extract and validate ADDRESS
|
// extract and validate ADDRESS
|
||||||
string strAddr = strInput.substr(pos + 1, string::npos);
|
std::string strAddr = strInput.substr(pos + 1, std::string::npos);
|
||||||
CBitcoinAddress addr(strAddr);
|
CBitcoinAddress addr(strAddr);
|
||||||
if (!addr.IsValid())
|
if (!addr.IsValid())
|
||||||
throw runtime_error("invalid TX output address");
|
throw std::runtime_error("invalid TX output address");
|
||||||
|
|
||||||
// build standard output script via GetScriptForDestination()
|
// build standard output script via GetScriptForDestination()
|
||||||
CScript scriptPubKey = GetScriptForDestination(addr.Get());
|
CScript scriptPubKey = GetScriptForDestination(addr.Get());
|
||||||
@@ -240,22 +252,22 @@ static void MutateTxAddOutAddr(CMutableTransaction& tx, const string& strInput)
|
|||||||
tx.vout.push_back(txout);
|
tx.vout.push_back(txout);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void MutateTxAddOutScript(CMutableTransaction& tx, const string& strInput)
|
static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& strInput)
|
||||||
{
|
{
|
||||||
// separate VALUE:SCRIPT in string
|
// separate VALUE:SCRIPT in string
|
||||||
size_t pos = strInput.find(':');
|
size_t pos = strInput.find(':');
|
||||||
if ((pos == string::npos) ||
|
if ((pos == std::string::npos) ||
|
||||||
(pos == 0))
|
(pos == 0))
|
||||||
throw runtime_error("TX output missing separator");
|
throw std::runtime_error("TX output missing separator");
|
||||||
|
|
||||||
// extract and validate VALUE
|
// extract and validate VALUE
|
||||||
string strValue = strInput.substr(0, pos);
|
std::string strValue = strInput.substr(0, pos);
|
||||||
CAmount value;
|
CAmount value;
|
||||||
if (!ParseMoney(strValue, value))
|
if (!ParseMoney(strValue, value))
|
||||||
throw runtime_error("invalid TX output value");
|
throw std::runtime_error("invalid TX output value");
|
||||||
|
|
||||||
// extract and validate script
|
// extract and validate script
|
||||||
string strScript = strInput.substr(pos + 1, string::npos);
|
std::string strScript = strInput.substr(pos + 1, std::string::npos);
|
||||||
CScript scriptPubKey = ParseScript(strScript); // throws on err
|
CScript scriptPubKey = ParseScript(strScript); // throws on err
|
||||||
|
|
||||||
// construct TxOut, append to transaction output list
|
// construct TxOut, append to transaction output list
|
||||||
@@ -263,26 +275,26 @@ static void MutateTxAddOutScript(CMutableTransaction& tx, const string& strInput
|
|||||||
tx.vout.push_back(txout);
|
tx.vout.push_back(txout);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void MutateTxDelInput(CMutableTransaction& tx, const string& strInIdx)
|
static void MutateTxDelInput(CMutableTransaction& tx, const std::string& strInIdx)
|
||||||
{
|
{
|
||||||
// parse requested deletion index
|
// parse requested deletion index
|
||||||
int inIdx = atoi(strInIdx);
|
int inIdx = atoi(strInIdx);
|
||||||
if (inIdx < 0 || inIdx >= (int)tx.vin.size()) {
|
if (inIdx < 0 || inIdx >= (int)tx.vin.size()) {
|
||||||
string strErr = "Invalid TX input index '" + strInIdx + "'";
|
std::string strErr = "Invalid TX input index '" + strInIdx + "'";
|
||||||
throw runtime_error(strErr.c_str());
|
throw std::runtime_error(strErr.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete input from transaction
|
// delete input from transaction
|
||||||
tx.vin.erase(tx.vin.begin() + inIdx);
|
tx.vin.erase(tx.vin.begin() + inIdx);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void MutateTxDelOutput(CMutableTransaction& tx, const string& strOutIdx)
|
static void MutateTxDelOutput(CMutableTransaction& tx, const std::string& strOutIdx)
|
||||||
{
|
{
|
||||||
// parse requested deletion index
|
// parse requested deletion index
|
||||||
int outIdx = atoi(strOutIdx);
|
int outIdx = atoi(strOutIdx);
|
||||||
if (outIdx < 0 || outIdx >= (int)tx.vout.size()) {
|
if (outIdx < 0 || outIdx >= (int)tx.vout.size()) {
|
||||||
string strErr = "Invalid TX output index '" + strOutIdx + "'";
|
std::string strErr = "Invalid TX output index '" + strOutIdx + "'";
|
||||||
throw runtime_error(strErr.c_str());
|
throw std::runtime_error(strErr.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete output from transaction
|
// delete output from transaction
|
||||||
@@ -302,7 +314,7 @@ static const struct {
|
|||||||
{"SINGLE|ANYONECANPAY", SIGHASH_SINGLE|SIGHASH_ANYONECANPAY},
|
{"SINGLE|ANYONECANPAY", SIGHASH_SINGLE|SIGHASH_ANYONECANPAY},
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool findSighashFlags(int& flags, const string& flagStr)
|
static bool findSighashFlags(int& flags, const std::string& flagStr)
|
||||||
{
|
{
|
||||||
flags = 0;
|
flags = 0;
|
||||||
|
|
||||||
@@ -316,17 +328,17 @@ static bool findSighashFlags(int& flags, const string& flagStr)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint256 ParseHashUO(map<string,UniValue>& o, string strKey)
|
uint256 ParseHashUO(std::map<std::string,UniValue>& o, std::string strKey)
|
||||||
{
|
{
|
||||||
if (!o.count(strKey))
|
if (!o.count(strKey))
|
||||||
return uint256();
|
return uint256();
|
||||||
return ParseHashUV(o[strKey], strKey);
|
return ParseHashUV(o[strKey], strKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<unsigned char> ParseHexUO(map<string,UniValue>& o, string strKey)
|
std::vector<unsigned char> ParseHexUO(std::map<std::string,UniValue>& o, std::string strKey)
|
||||||
{
|
{
|
||||||
if (!o.count(strKey)) {
|
if (!o.count(strKey)) {
|
||||||
vector<unsigned char> emptyVec;
|
std::vector<unsigned char> emptyVec;
|
||||||
return emptyVec;
|
return emptyVec;
|
||||||
}
|
}
|
||||||
return ParseHexUV(o[strKey], strKey);
|
return ParseHexUV(o[strKey], strKey);
|
||||||
@@ -335,41 +347,41 @@ vector<unsigned char> ParseHexUO(map<string,UniValue>& o, string strKey)
|
|||||||
static CAmount AmountFromValue(const UniValue& value)
|
static CAmount AmountFromValue(const UniValue& value)
|
||||||
{
|
{
|
||||||
if (!value.isNum() && !value.isStr())
|
if (!value.isNum() && !value.isStr())
|
||||||
throw runtime_error("Amount is not a number or string");
|
throw std::runtime_error("Amount is not a number or string");
|
||||||
CAmount amount;
|
CAmount amount;
|
||||||
if (!ParseFixedPoint(value.getValStr(), 8, &amount))
|
if (!ParseFixedPoint(value.getValStr(), 8, &amount))
|
||||||
throw runtime_error("Invalid amount");
|
throw std::runtime_error("Invalid amount");
|
||||||
if (!MoneyRange(amount))
|
if (!MoneyRange(amount))
|
||||||
throw runtime_error("Amount out of range");
|
throw std::runtime_error("Amount out of range");
|
||||||
return amount;
|
return amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void MutateTxSign(CMutableTransaction& tx, const string& strInput)
|
static void MutateTxSign(CMutableTransaction& tx, const std::string& strInput)
|
||||||
{
|
{
|
||||||
// separate HEIGHT:SIGHASH-FLAGS in string
|
// separate HEIGHT:SIGHASH-FLAGS in string
|
||||||
size_t pos = strInput.find(':');
|
size_t pos = strInput.find(':');
|
||||||
if ((pos == 0) ||
|
if ((pos == 0) ||
|
||||||
(pos == (strInput.size() - 1)))
|
(pos == (strInput.size() - 1)))
|
||||||
throw runtime_error("Invalid sighash flag separator");
|
throw std::runtime_error("Invalid sighash flag separator");
|
||||||
|
|
||||||
// extract and validate HEIGHT
|
// extract and validate HEIGHT
|
||||||
string strHeight = strInput.substr(0, pos);
|
std::string strHeight = strInput.substr(0, pos);
|
||||||
int nHeight = atoi(strHeight);
|
int nHeight = atoi(strHeight);
|
||||||
if (nHeight <= 0) {
|
if (nHeight <= 0) {
|
||||||
throw runtime_error("invalid height");
|
throw std::runtime_error("invalid height");
|
||||||
}
|
}
|
||||||
|
|
||||||
// extract and validate SIGHASH-FLAGS
|
// extract and validate SIGHASH-FLAGS
|
||||||
int nHashType = SIGHASH_ALL;
|
int nHashType = SIGHASH_ALL;
|
||||||
string flagStr;
|
std::string flagStr;
|
||||||
if (pos != string::npos) {
|
if (pos != std::string::npos) {
|
||||||
flagStr = strInput.substr(pos + 1, string::npos);
|
flagStr = strInput.substr(pos + 1, std::string::npos);
|
||||||
}
|
}
|
||||||
if (flagStr.size() > 0)
|
if (flagStr.size() > 0)
|
||||||
if (!findSighashFlags(nHashType, flagStr))
|
if (!findSighashFlags(nHashType, flagStr))
|
||||||
throw runtime_error("unknown sighash flag/sign option");
|
throw std::runtime_error("unknown sighash flag/sign option");
|
||||||
|
|
||||||
vector<CTransaction> txVariants;
|
std::vector<CTransaction> txVariants;
|
||||||
txVariants.push_back(tx);
|
txVariants.push_back(tx);
|
||||||
|
|
||||||
// mergedTx will end up with all the signatures; it
|
// mergedTx will end up with all the signatures; it
|
||||||
@@ -380,7 +392,7 @@ static void MutateTxSign(CMutableTransaction& tx, const string& strInput)
|
|||||||
CCoinsViewCache view(&viewDummy);
|
CCoinsViewCache view(&viewDummy);
|
||||||
|
|
||||||
if (!registers.count("privatekeys"))
|
if (!registers.count("privatekeys"))
|
||||||
throw runtime_error("privatekeys register variable must be set.");
|
throw std::runtime_error("privatekeys register variable must be set.");
|
||||||
bool fGivenKeys = false;
|
bool fGivenKeys = false;
|
||||||
CBasicKeyStore tempKeystore;
|
CBasicKeyStore tempKeystore;
|
||||||
UniValue keysObj = registers["privatekeys"];
|
UniValue keysObj = registers["privatekeys"];
|
||||||
@@ -388,11 +400,11 @@ static void MutateTxSign(CMutableTransaction& tx, const string& strInput)
|
|||||||
|
|
||||||
for (size_t kidx = 0; kidx < keysObj.size(); kidx++) {
|
for (size_t kidx = 0; kidx < keysObj.size(); kidx++) {
|
||||||
if (!keysObj[kidx].isStr())
|
if (!keysObj[kidx].isStr())
|
||||||
throw runtime_error("privatekey not a string");
|
throw std::runtime_error("privatekey not a std::string");
|
||||||
CBitcoinSecret vchSecret;
|
CBitcoinSecret vchSecret;
|
||||||
bool fGood = vchSecret.SetString(keysObj[kidx].getValStr());
|
bool fGood = vchSecret.SetString(keysObj[kidx].getValStr());
|
||||||
if (!fGood)
|
if (!fGood)
|
||||||
throw runtime_error("privatekey not valid");
|
throw std::runtime_error("privatekey not valid");
|
||||||
|
|
||||||
CKey key = vchSecret.GetKey();
|
CKey key = vchSecret.GetKey();
|
||||||
tempKeystore.AddKey(key);
|
tempKeystore.AddKey(key);
|
||||||
@@ -400,34 +412,34 @@ static void MutateTxSign(CMutableTransaction& tx, const string& strInput)
|
|||||||
|
|
||||||
// Add previous txouts given in the RPC call:
|
// Add previous txouts given in the RPC call:
|
||||||
if (!registers.count("prevtxs"))
|
if (!registers.count("prevtxs"))
|
||||||
throw runtime_error("prevtxs register variable must be set.");
|
throw std::runtime_error("prevtxs register variable must be set.");
|
||||||
UniValue prevtxsObj = registers["prevtxs"];
|
UniValue prevtxsObj = registers["prevtxs"];
|
||||||
{
|
{
|
||||||
for (size_t previdx = 0; previdx < prevtxsObj.size(); previdx++) {
|
for (size_t previdx = 0; previdx < prevtxsObj.size(); previdx++) {
|
||||||
UniValue prevOut = prevtxsObj[previdx];
|
UniValue prevOut = prevtxsObj[previdx];
|
||||||
if (!prevOut.isObject())
|
if (!prevOut.isObject())
|
||||||
throw runtime_error("expected prevtxs internal object");
|
throw std::runtime_error("expected prevtxs internal object");
|
||||||
|
|
||||||
map<string,UniValue::VType> types = boost::assign::map_list_of("txid", UniValue::VSTR)("vout",UniValue::VNUM)("scriptPubKey",UniValue::VSTR);
|
std::map<std::string,UniValue::VType> types = boost::assign::map_list_of("txid", UniValue::VSTR)("vout",UniValue::VNUM)("scriptPubKey",UniValue::VSTR);
|
||||||
if (!prevOut.checkObject(types))
|
if (!prevOut.checkObject(types))
|
||||||
throw runtime_error("prevtxs internal object typecheck fail");
|
throw std::runtime_error("prevtxs internal object typecheck fail");
|
||||||
|
|
||||||
uint256 txid = ParseHashUV(prevOut["txid"], "txid");
|
uint256 txid = ParseHashUV(prevOut["txid"], "txid");
|
||||||
|
|
||||||
int nOut = atoi(prevOut["vout"].getValStr());
|
int nOut = atoi(prevOut["vout"].getValStr());
|
||||||
if (nOut < 0)
|
if (nOut < 0)
|
||||||
throw runtime_error("vout must be positive");
|
throw std::runtime_error("vout must be positive");
|
||||||
|
|
||||||
vector<unsigned char> pkData(ParseHexUV(prevOut["scriptPubKey"], "scriptPubKey"));
|
std::vector<unsigned char> pkData(ParseHexUV(prevOut["scriptPubKey"], "scriptPubKey"));
|
||||||
CScript scriptPubKey(pkData.begin(), pkData.end());
|
CScript scriptPubKey(pkData.begin(), pkData.end());
|
||||||
|
|
||||||
{
|
{
|
||||||
CCoinsModifier coins = view.ModifyCoins(txid);
|
CCoinsModifier coins = view.ModifyCoins(txid);
|
||||||
if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) {
|
if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) {
|
||||||
string err("Previous output scriptPubKey mismatch:\n");
|
std::string err("Previous output scriptPubKey mismatch:\n");
|
||||||
err = err + coins->vout[nOut].scriptPubKey.ToString() + "\nvs:\n"+
|
err = err + coins->vout[nOut].scriptPubKey.ToString() + "\nvs:\n"+
|
||||||
scriptPubKey.ToString();
|
scriptPubKey.ToString();
|
||||||
throw runtime_error(err);
|
throw std::runtime_error(err);
|
||||||
}
|
}
|
||||||
if ((unsigned int)nOut >= coins->vout.size())
|
if ((unsigned int)nOut >= coins->vout.size())
|
||||||
coins->vout.resize(nOut+1);
|
coins->vout.resize(nOut+1);
|
||||||
@@ -443,7 +455,7 @@ static void MutateTxSign(CMutableTransaction& tx, const string& strInput)
|
|||||||
if (fGivenKeys && scriptPubKey.IsPayToScriptHash() &&
|
if (fGivenKeys && scriptPubKey.IsPayToScriptHash() &&
|
||||||
prevOut.exists("redeemScript")) {
|
prevOut.exists("redeemScript")) {
|
||||||
UniValue v = prevOut["redeemScript"];
|
UniValue v = prevOut["redeemScript"];
|
||||||
vector<unsigned char> rsData(ParseHexUV(v, "redeemScript"));
|
std::vector<unsigned char> rsData(ParseHexUV(v, "redeemScript"));
|
||||||
CScript redeemScript(rsData.begin(), rsData.end());
|
CScript redeemScript(rsData.begin(), rsData.end());
|
||||||
tempKeystore.AddCScript(redeemScript);
|
tempKeystore.AddCScript(redeemScript);
|
||||||
}
|
}
|
||||||
@@ -503,8 +515,8 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static void MutateTx(CMutableTransaction& tx, const string& command,
|
static void MutateTx(CMutableTransaction& tx, const std::string& command,
|
||||||
const string& commandVal)
|
const std::string& commandVal)
|
||||||
{
|
{
|
||||||
boost::scoped_ptr<Secp256k1Init> ecc;
|
boost::scoped_ptr<Secp256k1Init> ecc;
|
||||||
|
|
||||||
@@ -539,7 +551,7 @@ static void MutateTx(CMutableTransaction& tx, const string& command,
|
|||||||
RegisterSet(commandVal);
|
RegisterSet(commandVal);
|
||||||
|
|
||||||
else
|
else
|
||||||
throw runtime_error("unknown command");
|
throw std::runtime_error("unknown command");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void OutputTxJSON(const CTransaction& tx)
|
static void OutputTxJSON(const CTransaction& tx)
|
||||||
@@ -547,20 +559,20 @@ static void OutputTxJSON(const CTransaction& tx)
|
|||||||
UniValue entry(UniValue::VOBJ);
|
UniValue entry(UniValue::VOBJ);
|
||||||
TxToUniv(tx, uint256(), entry);
|
TxToUniv(tx, uint256(), entry);
|
||||||
|
|
||||||
string jsonOutput = entry.write(4);
|
std::string jsonOutput = entry.write(4);
|
||||||
fprintf(stdout, "%s\n", jsonOutput.c_str());
|
fprintf(stdout, "%s\n", jsonOutput.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void OutputTxHash(const CTransaction& tx)
|
static void OutputTxHash(const CTransaction& tx)
|
||||||
{
|
{
|
||||||
string strHexHash = tx.GetHash().GetHex(); // the hex-encoded transaction hash (aka the transaction id)
|
std::string strHexHash = tx.GetHash().GetHex(); // the hex-encoded transaction hash (aka the transaction id)
|
||||||
|
|
||||||
fprintf(stdout, "%s\n", strHexHash.c_str());
|
fprintf(stdout, "%s\n", strHexHash.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void OutputTxHex(const CTransaction& tx)
|
static void OutputTxHex(const CTransaction& tx)
|
||||||
{
|
{
|
||||||
string strHex = EncodeHexTx(tx);
|
std::string strHex = EncodeHexTx(tx);
|
||||||
|
|
||||||
fprintf(stdout, "%s\n", strHex.c_str());
|
fprintf(stdout, "%s\n", strHex.c_str());
|
||||||
}
|
}
|
||||||
@@ -575,10 +587,10 @@ static void OutputTx(const CTransaction& tx)
|
|||||||
OutputTxHex(tx);
|
OutputTxHex(tx);
|
||||||
}
|
}
|
||||||
|
|
||||||
static string readStdin()
|
static std::string readStdin()
|
||||||
{
|
{
|
||||||
char buf[4096];
|
char buf[4096];
|
||||||
string ret;
|
std::string ret;
|
||||||
|
|
||||||
while (!feof(stdin)) {
|
while (!feof(stdin)) {
|
||||||
size_t bread = fread(buf, 1, sizeof(buf), stdin);
|
size_t bread = fread(buf, 1, sizeof(buf), stdin);
|
||||||
@@ -588,7 +600,7 @@ static string readStdin()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ferror(stdin))
|
if (ferror(stdin))
|
||||||
throw runtime_error("error reading stdin");
|
throw std::runtime_error("error reading stdin");
|
||||||
|
|
||||||
boost::algorithm::trim_right(ret);
|
boost::algorithm::trim_right(ret);
|
||||||
|
|
||||||
@@ -597,7 +609,7 @@ static string readStdin()
|
|||||||
|
|
||||||
static int CommandLineRawTx(int argc, char* argv[])
|
static int CommandLineRawTx(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
string strPrint;
|
std::string strPrint;
|
||||||
int nRet = 0;
|
int nRet = 0;
|
||||||
try {
|
try {
|
||||||
// Skip switches; Permit common stdin convention "-"
|
// Skip switches; Permit common stdin convention "-"
|
||||||
@@ -613,15 +625,15 @@ static int CommandLineRawTx(int argc, char* argv[])
|
|||||||
if (!fCreateBlank) {
|
if (!fCreateBlank) {
|
||||||
// require at least one param
|
// require at least one param
|
||||||
if (argc < 2)
|
if (argc < 2)
|
||||||
throw runtime_error("too few parameters");
|
throw std::runtime_error("too few parameters");
|
||||||
|
|
||||||
// param: hex-encoded bitcoin transaction
|
// param: hex-encoded bitcoin transaction
|
||||||
string strHexTx(argv[1]);
|
std::string strHexTx(argv[1]);
|
||||||
if (strHexTx == "-") // "-" implies standard input
|
if (strHexTx == "-") // "-" implies standard input
|
||||||
strHexTx = readStdin();
|
strHexTx = readStdin();
|
||||||
|
|
||||||
if (!DecodeHexTx(txDecodeTmp, strHexTx))
|
if (!DecodeHexTx(txDecodeTmp, strHexTx))
|
||||||
throw runtime_error("invalid transaction encoding");
|
throw std::runtime_error("invalid transaction encoding");
|
||||||
|
|
||||||
startArg = 2;
|
startArg = 2;
|
||||||
} else
|
} else
|
||||||
@@ -630,10 +642,10 @@ static int CommandLineRawTx(int argc, char* argv[])
|
|||||||
CMutableTransaction tx(txDecodeTmp);
|
CMutableTransaction tx(txDecodeTmp);
|
||||||
|
|
||||||
for (int i = startArg; i < argc; i++) {
|
for (int i = startArg; i < argc; i++) {
|
||||||
string arg = argv[i];
|
std::string arg = argv[i];
|
||||||
string key, value;
|
std::string key, value;
|
||||||
size_t eqpos = arg.find('=');
|
size_t eqpos = arg.find('=');
|
||||||
if (eqpos == string::npos)
|
if (eqpos == std::string::npos)
|
||||||
key = arg;
|
key = arg;
|
||||||
else {
|
else {
|
||||||
key = arg.substr(0, eqpos);
|
key = arg.substr(0, eqpos);
|
||||||
@@ -650,7 +662,7 @@ static int CommandLineRawTx(int argc, char* argv[])
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
catch (const std::exception& e) {
|
||||||
strPrint = string("error: ") + e.what();
|
strPrint = std::string("error: ") + e.what();
|
||||||
nRet = EXIT_FAILURE;
|
nRet = EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
catch (...) {
|
catch (...) {
|
||||||
@@ -669,8 +681,9 @@ int main(int argc, char* argv[])
|
|||||||
SetupEnvironment();
|
SetupEnvironment();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if(!AppInitRawTx(argc, argv))
|
int ret = AppInitRawTx(argc, argv);
|
||||||
return EXIT_FAILURE;
|
if (ret != CONTINUE_EXECUTION)
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
catch (const std::exception& e) {
|
||||||
PrintExceptionContinue(&e, "AppInitRawTx()");
|
PrintExceptionContinue(&e, "AppInitRawTx()");
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ bool AppInit(int argc, char* argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stdout, "%s", strUsage.c_str());
|
fprintf(stdout, "%s", strUsage.c_str());
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -138,7 +138,7 @@ bool AppInit(int argc, char* argv[])
|
|||||||
if (fCommandLine)
|
if (fCommandLine)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Error: There is no RPC client functionality in zcashd. Use the zcash-cli utility instead.\n");
|
fprintf(stderr, "Error: There is no RPC client functionality in zcashd. Use the zcash-cli utility instead.\n");
|
||||||
exit(1);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
fDaemon = GetBoolArg("-daemon", false);
|
fDaemon = GetBoolArg("-daemon", false);
|
||||||
@@ -195,5 +195,5 @@ int main(int argc, char* argv[])
|
|||||||
// Connect bitcoind signal handlers
|
// Connect bitcoind signal handlers
|
||||||
noui_connect();
|
noui_connect();
|
||||||
|
|
||||||
return (AppInit(argc, argv) ? 0 : 1);
|
return (AppInit(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||||||
{ "getrawtransaction", 1 },
|
{ "getrawtransaction", 1 },
|
||||||
{ "createrawtransaction", 0 },
|
{ "createrawtransaction", 0 },
|
||||||
{ "createrawtransaction", 1 },
|
{ "createrawtransaction", 1 },
|
||||||
|
{ "createrawtransaction", 2 },
|
||||||
{ "signrawtransaction", 1 },
|
{ "signrawtransaction", 1 },
|
||||||
{ "signrawtransaction", 2 },
|
{ "signrawtransaction", 2 },
|
||||||
{ "sendrawtransaction", 1 },
|
{ "sendrawtransaction", 1 },
|
||||||
|
|||||||
@@ -407,9 +407,9 @@ UniValue verifytxoutproof(const UniValue& params, bool fHelp)
|
|||||||
|
|
||||||
UniValue createrawtransaction(const UniValue& params, bool fHelp)
|
UniValue createrawtransaction(const UniValue& params, bool fHelp)
|
||||||
{
|
{
|
||||||
if (fHelp || params.size() != 2)
|
if (fHelp || params.size() < 2 || params.size() > 3)
|
||||||
throw runtime_error(
|
throw runtime_error(
|
||||||
"createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,...}\n"
|
"createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,...} ( locktime )\n"
|
||||||
"\nCreate a transaction spending the given inputs and sending to the given addresses.\n"
|
"\nCreate a transaction spending the given inputs and sending to the given addresses.\n"
|
||||||
"Returns hex-encoded raw transaction.\n"
|
"Returns hex-encoded raw transaction.\n"
|
||||||
"Note that the transaction's inputs are not signed, and\n"
|
"Note that the transaction's inputs are not signed, and\n"
|
||||||
@@ -421,6 +421,7 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp)
|
|||||||
" {\n"
|
" {\n"
|
||||||
" \"txid\":\"id\", (string, required) The transaction id\n"
|
" \"txid\":\"id\", (string, required) The transaction id\n"
|
||||||
" \"vout\":n (numeric, required) The output number\n"
|
" \"vout\":n (numeric, required) The output number\n"
|
||||||
|
" \"sequence\":n (numeric, optional) The sequence number\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" ,...\n"
|
" ,...\n"
|
||||||
" ]\n"
|
" ]\n"
|
||||||
@@ -429,7 +430,7 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp)
|
|||||||
" \"address\": x.xxx (numeric, required) The key is the Zcash address, the value is the " + CURRENCY_UNIT + " amount\n"
|
" \"address\": x.xxx (numeric, required) The key is the Zcash address, the value is the " + CURRENCY_UNIT + " amount\n"
|
||||||
" ,...\n"
|
" ,...\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
"3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n"
|
||||||
"\nResult:\n"
|
"\nResult:\n"
|
||||||
"\"transaction\" (string) hex string of the transaction\n"
|
"\"transaction\" (string) hex string of the transaction\n"
|
||||||
|
|
||||||
@@ -439,7 +440,9 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp)
|
|||||||
);
|
);
|
||||||
|
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
RPCTypeCheck(params, boost::assign::list_of(UniValue::VARR)(UniValue::VOBJ));
|
RPCTypeCheck(params, boost::assign::list_of(UniValue::VARR)(UniValue::VOBJ)(UniValue::VNUM), true);
|
||||||
|
if (params[0].isNull() || params[1].isNull())
|
||||||
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null");
|
||||||
|
|
||||||
UniValue inputs = params[0].get_array();
|
UniValue inputs = params[0].get_array();
|
||||||
UniValue sendTo = params[1].get_obj();
|
UniValue sendTo = params[1].get_obj();
|
||||||
@@ -455,6 +458,13 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (params.size() > 2 && !params[2].isNull()) {
|
||||||
|
int64_t nLockTime = params[2].get_int64();
|
||||||
|
if (nLockTime < 0 || nLockTime > std::numeric_limits<uint32_t>::max())
|
||||||
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range");
|
||||||
|
rawTx.nLockTime = nLockTime;
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t idx = 0; idx < inputs.size(); idx++) {
|
for (size_t idx = 0; idx < inputs.size(); idx++) {
|
||||||
const UniValue& input = inputs[idx];
|
const UniValue& input = inputs[idx];
|
||||||
const UniValue& o = input.get_obj();
|
const UniValue& o = input.get_obj();
|
||||||
@@ -468,7 +478,15 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp)
|
|||||||
if (nOutput < 0)
|
if (nOutput < 0)
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
|
||||||
|
|
||||||
CTxIn in(COutPoint(txid, nOutput));
|
uint32_t nSequence = (rawTx.nLockTime ? std::numeric_limits<uint32_t>::max() - 1 : std::numeric_limits<uint32_t>::max());
|
||||||
|
|
||||||
|
// set the sequence number if passed in the parameters object
|
||||||
|
const UniValue& sequenceObj = find_value(o, "sequence");
|
||||||
|
if (sequenceObj.isNum())
|
||||||
|
nSequence = sequenceObj.get_int();
|
||||||
|
|
||||||
|
CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence);
|
||||||
|
|
||||||
rawTx.vin.push_back(in);
|
rawTx.vin.push_back(in);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,5 +56,18 @@
|
|||||||
"sign=1:ALL",
|
"sign=1:ALL",
|
||||||
"outaddr=0.001:t1Ruz6gK4QPZoPPGpHaieupnnh62mktjQE7"],
|
"outaddr=0.001:t1Ruz6gK4QPZoPPGpHaieupnnh62mktjQE7"],
|
||||||
"output_cmp": "txcreatesign.hex"
|
"output_cmp": "txcreatesign.hex"
|
||||||
|
},
|
||||||
|
{ "exec": "./zcash-tx",
|
||||||
|
"args":
|
||||||
|
["-create",
|
||||||
|
"in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0:4294967293",
|
||||||
|
"outaddr=0.18:t1LmWJddYzkTmTQjZrX7ZkFjmuEu5XKpGKb"],
|
||||||
|
"output_cmp": "txcreatedata_seq0.hex"
|
||||||
|
},
|
||||||
|
{ "exec": "./zcash-tx",
|
||||||
|
"args":
|
||||||
|
["01000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000fdffffff0180a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac00000000",
|
||||||
|
"in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0:1"],
|
||||||
|
"output_cmp": "txcreatedata_seq1.hex"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
1
src/test/data/txcreatedata_seq0.hex
Normal file
1
src/test/data/txcreatedata_seq0.hex
Normal file
@@ -0,0 +1 @@
|
|||||||
|
01000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000fdffffff0180a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac00000000
|
||||||
1
src/test/data/txcreatedata_seq1.hex
Normal file
1
src/test/data/txcreatedata_seq1.hex
Normal file
@@ -0,0 +1 @@
|
|||||||
|
01000000021f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000fdffffff1f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000010000000180a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac00000000
|
||||||
Reference in New Issue
Block a user