diff --git a/doc/release-notes.md b/doc/release-notes.md
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -3,3 +3,5 @@
This release includes the following features and fixes:
+ - Add `signrawtransactionwithkey` and `signrawtransactionwithwallet` RPCs.
+ These are specialized subsets of the `signrawtransaction` RPC.
\ No newline at end of file
diff --git a/src/Makefile.am b/src/Makefile.am
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -170,6 +170,7 @@
rpc/mining.h \
rpc/misc.h \
rpc/protocol.h \
+ rpc/rawtransaction.h \
rpc/safemode.h \
rpc/server.h \
rpc/tojson.h \
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -68,6 +68,7 @@
<< "importmulti"
<< "signmessagewithprivkey"
<< "signrawtransaction"
+ << "signrawtransactionwithkey"
<< "walletpassphrase"
<< "walletpassphrasechange"
<< "encryptwallet";
diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp
--- a/src/qt/test/rpcnestedtests.cpp
+++ b/src/qt/test/rpcnestedtests.cpp
@@ -108,9 +108,9 @@
RPCConsole::RPCParseCommandLine(result, "signmessagewithprivkey abc,def",
false, &filtered);
QVERIFY(filtered == "signmessagewithprivkey(…)");
- RPCConsole::RPCParseCommandLine(result, "signrawtransaction(abc)", false,
- &filtered);
- QVERIFY(filtered == "signrawtransaction(…)");
+ RPCConsole::RPCParseCommandLine(result, "signrawtransactionwithkey(abc)",
+ false, &filtered);
+ QVERIFY(filtered == "signrawtransactionwithkey(…)");
RPCConsole::RPCParseCommandLine(result, "walletpassphrase(help())", false,
&filtered);
QVERIFY(filtered == "walletpassphrase(…)");
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -90,6 +90,9 @@
{"createrawtransaction", 2, "locktime"},
{"signrawtransaction", 1, "prevtxs"},
{"signrawtransaction", 2, "privkeys"},
+ {"signrawtransactionwithkey", 1, "privkeys"},
+ {"signrawtransactionwithkey", 2, "prevtxs"},
+ {"signrawtransactionwithwallet", 1, "prevtxs"},
{"sendrawtransaction", 1, "allowhighfees"},
{"combinerawtransaction", 0, "txs"},
{"fundrawtransaction", 1, "options"},
diff --git a/src/rpc/rawtransaction.h b/src/rpc/rawtransaction.h
new file mode 100644
--- /dev/null
+++ b/src/rpc/rawtransaction.h
@@ -0,0 +1,17 @@
+// Copyright (c) 2017 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_RPC_RAWTRANSACTION_H
+#define BITCOIN_RPC_RAWTRANSACTION_H
+
+class CBasicKeyStore;
+class CMutableTransaction;
+class UniValue;
+
+/** Sign a transaction with the given keystore and previous transactions */
+UniValue SignTransaction(CMutableTransaction &mtx, const UniValue &prevTxs,
+ CBasicKeyStore *keystore, bool tempKeystore,
+ const UniValue &hashType);
+
+#endif // BITCOIN_RPC_RAWTRANSACTION_H
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -3,6 +3,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include "rpc/rawtransaction.h"
#include "base58.h"
#include "chain.h"
#include "coins.h"
@@ -29,7 +30,6 @@
#include "validation.h"
#ifdef ENABLE_WALLET
#include "wallet/rpcwallet.h"
-#include "wallet/wallet.h"
#endif
#include
@@ -659,8 +659,8 @@
vErrorsRet.push_back(entry);
}
-UniValue combinerawtransaction(const Config &config,
- const JSONRPCRequest &request) {
+static UniValue combinerawtransaction(const Config &config,
+ const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
@@ -758,125 +758,15 @@
return EncodeHexTx(CTransaction(mergedTx));
}
-static UniValue signrawtransaction(const Config &config,
- const JSONRPCRequest &request) {
-#ifdef ENABLE_WALLET
- CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
-#endif
-
- if (request.fHelp || request.params.size() < 1 ||
- request.params.size() > 4) {
- throw std::runtime_error(
- "signrawtransaction \"hexstring\" ( "
- "[{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\","
- "\"redeemScript\":\"hex\"},...] [\"privatekey1\",...] sighashtype "
- ")\n"
- "\nSign inputs for raw transaction (serialized, hex-encoded).\n"
- "The second optional argument (may be null) is an array of "
- "previous transaction outputs that\n"
- "this transaction depends on but may not yet be in the block "
- "chain.\n"
- "The third optional argument (may be null) is an array of "
- "base58-encoded private\n"
- "keys that, if given, will be the only keys used to sign the "
- "transaction.\n"
-#ifdef ENABLE_WALLET
- + HelpRequiringPassphrase(pwallet) +
- "\n"
-#endif
-
- "\nArguments:\n"
- "1. \"hexstring\" (string, required) The transaction hex "
- "string\n"
- "2. \"prevtxs\" (string, optional) An json array of previous "
- "dependent transaction outputs\n"
- " [ (json array of json objects, or 'null' if "
- "none provided)\n"
- " {\n"
- " \"txid\":\"id\", (string, required) The "
- "transaction id\n"
- " \"vout\":n, (numeric, required) The "
- "output number\n"
- " \"scriptPubKey\": \"hex\", (string, required) script "
- "key\n"
- " \"redeemScript\": \"hex\", (string, required for P2SH "
- "or P2WSH) redeem script\n"
- " \"amount\": value (numeric, required) The "
- "amount spent\n"
- " }\n"
- " ,...\n"
- " ]\n"
- "3. \"privkeys\" (string, optional) A json array of "
- "base58-encoded private keys for signing\n"
- " [ (json array of strings, or 'null' if none "
- "provided)\n"
- " \"privatekey\" (string) private key in base58-encoding\n"
- " ,...\n"
- " ]\n"
- "4. \"sighashtype\" (string, optional, default=ALL) The "
- "signature hash type. Must be one of\n"
- " \"ALL\"\n"
- " \"NONE\"\n"
- " \"SINGLE\"\n"
- " \"ALL|ANYONECANPAY\"\n"
- " \"NONE|ANYONECANPAY\"\n"
- " \"SINGLE|ANYONECANPAY\"\n"
- " \"ALL|FORKID\"\n"
- " \"NONE|FORKID\"\n"
- " \"SINGLE|FORKID\"\n"
- " \"ALL|FORKID|ANYONECANPAY\"\n"
- " \"NONE|FORKID|ANYONECANPAY\"\n"
- " \"SINGLE|FORKID|ANYONECANPAY\"\n"
-
- "\nResult:\n"
- "{\n"
- " \"hex\" : \"value\", (string) The hex-encoded raw "
- "transaction with signature(s)\n"
- " \"complete\" : true|false, (boolean) If the transaction has a "
- "complete set of signatures\n"
- " \"errors\" : [ (json array of objects) Script "
- "verification errors (if there are any)\n"
- " {\n"
- " \"txid\" : \"hash\", (string) The hash of the "
- "referenced, previous transaction\n"
- " \"vout\" : n, (numeric) The index of the "
- "output to spent and used as input\n"
- " \"scriptSig\" : \"hex\", (string) The hex-encoded "
- "signature script\n"
- " \"sequence\" : n, (numeric) Script sequence "
- "number\n"
- " \"error\" : \"text\" (string) Verification or "
- "signing error related to the input\n"
- " }\n"
- " ,...\n"
- " ]\n"
- "}\n"
-
- "\nExamples:\n" +
- HelpExampleCli("signrawtransaction", "\"myhex\"") +
- HelpExampleRpc("signrawtransaction", "\"myhex\""));
- }
-
- ObserveSafeMode();
-#ifdef ENABLE_WALLET
- LOCK2(cs_main, pwallet ? &pwallet->cs_wallet : nullptr);
-#else
- LOCK(cs_main);
-#endif
- RPCTypeCheck(
- request.params,
- {UniValue::VSTR, UniValue::VARR, UniValue::VARR, UniValue::VSTR}, true);
-
- CMutableTransaction mtx;
- if (!DecodeHexTx(mtx, request.params[0].get_str())) {
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
- }
-
+UniValue SignTransaction(CMutableTransaction &mtx,
+ const UniValue &prevTxsUnival,
+ CBasicKeyStore *keystore, bool is_temp_keystore,
+ const UniValue &hashType) {
// Fetch previous transactions (inputs):
CCoinsView viewDummy;
CCoinsViewCache view(&viewDummy);
{
- LOCK(g_mempool.cs);
+ LOCK2(cs_main, g_mempool.cs);
CCoinsViewCache &viewChain = *pcoinsTip;
CCoinsViewMemPool viewMempool(&viewChain, g_mempool);
// Temporarily switch cache backend to db+mempool view.
@@ -891,39 +781,10 @@
view.SetBackend(viewDummy);
}
- bool fGivenKeys = false;
- CBasicKeyStore tempKeystore;
- if (request.params.size() > 2 && !request.params[2].isNull()) {
- fGivenKeys = true;
- UniValue keys = request.params[2].get_array();
- for (size_t idx = 0; idx < keys.size(); idx++) {
- UniValue k = keys[idx];
- CBitcoinSecret vchSecret;
- bool fGood = vchSecret.SetString(k.get_str());
- if (!fGood) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
- "Invalid private key");
- }
-
- CKey key = vchSecret.GetKey();
- if (!key.IsValid()) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
- "Private key outside allowed range");
- }
-
- tempKeystore.AddKey(key);
- }
- }
-#ifdef ENABLE_WALLET
- else if (pwallet) {
- EnsureWalletIsUnlocked(pwallet);
- }
-#endif
-
// Add previous txouts given in the RPC call:
- if (request.params.size() > 1 && !request.params[1].isNull()) {
- UniValue prevTxs = request.params[1].get_array();
- for (size_t idx = 0; idx < prevTxs.size(); idx++) {
+ if (!prevTxsUnival.isNull()) {
+ UniValue prevTxs = prevTxsUnival.get_array();
+ for (size_t idx = 0; idx < prevTxs.size(); ++idx) {
const UniValue &p = prevTxs[idx];
if (!p.isObject()) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR,
@@ -989,9 +850,9 @@
}
// If redeemScript given and not using the local wallet (private
- // keys given), add redeemScript to the tempKeystore so it can be
+ // keys given), add redeemScript to the keystore so it can be
// signed:
- if (fGivenKeys && scriptPubKey.IsPayToScriptHash()) {
+ if (is_temp_keystore && scriptPubKey.IsPayToScriptHash()) {
RPCTypeCheckObj(
prevOut, {
{"txid", UniValueType(UniValue::VSTR)},
@@ -1003,21 +864,14 @@
if (!v.isNull()) {
std::vector rsData(ParseHexV(v, "redeemScript"));
CScript redeemScript(rsData.begin(), rsData.end());
- tempKeystore.AddCScript(redeemScript);
+ keystore->AddCScript(redeemScript);
}
}
}
}
-#ifdef ENABLE_WALLET
- const CKeyStore &keystore =
- ((fGivenKeys || !pwallet) ? tempKeystore : *pwallet);
-#else
- const CKeyStore &keystore = tempKeystore;
-#endif
-
SigHashType sigHashType = SigHashType().withForkId();
- if (request.params.size() > 3 && !request.params[3].isNull()) {
+ if (!hashType.isNull()) {
static std::map mapSigHashValues = {
{"ALL", SIGHASH_ALL},
{"ALL|ANYONECANPAY", SIGHASH_ALL | SIGHASH_ANYONECANPAY},
@@ -1035,7 +889,7 @@
{"SINGLE|FORKID|ANYONECANPAY",
SIGHASH_SINGLE | SIGHASH_FORKID | SIGHASH_ANYONECANPAY},
};
- std::string strHashType = request.params[3].get_str();
+ std::string strHashType = hashType.get_str();
if (!mapSigHashValues.count(strHashType)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param");
}
@@ -1070,7 +924,7 @@
if ((sigHashType.getBaseType() != BaseSigHashType::SINGLE) ||
(i < mtx.vout.size())) {
ProduceSignature(MutableTransactionSignatureCreator(
- &keystore, &mtx, i, amount, sigHashType),
+ keystore, &mtx, i, amount, sigHashType),
prevPubKey, sigdata);
}
sigdata = CombineSignatures(
@@ -1108,6 +962,243 @@
return result;
}
+static UniValue signrawtransactionwithkey(const Config &config,
+ const JSONRPCRequest &request) {
+ if (request.fHelp || request.params.size() < 2 ||
+ request.params.size() > 4) {
+ throw std::runtime_error(
+ "signrawtransactionwithkey \"hexstring\" [\"privatekey1\",...] ( "
+ "[{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\","
+ "\"redeemScript\":\"hex\"},...] sighashtype )\n"
+ "\nSign inputs for raw transaction (serialized, hex-encoded).\n"
+ "The second argument is an array of base58-encoded private\n"
+ "keys that will be the only keys used to sign the transaction.\n"
+ "The third optional argument (may be null) is an array of previous "
+ "transaction outputs that\n"
+ "this transaction depends on but may not yet be in the block "
+ "chain.\n"
+
+ "\nArguments:\n"
+ "1. \"hexstring\" (string, required) The "
+ "transaction hex string\n"
+ "2. \"privkeys\" (string, required) A json "
+ "array of base58-encoded private keys for signing\n"
+ " [ (json array of strings)\n"
+ " \"privatekey\" (string) private key in "
+ "base58-encoding\n"
+ " ,...\n"
+ " ]\n"
+ "3. \"prevtxs\" (string, optional) An json "
+ "array of previous dependent transaction outputs\n"
+ " [ (json array of json objects, "
+ "or 'null' if none provided)\n"
+ " {\n"
+ " \"txid\":\"id\", (string, required) The "
+ "transaction id\n"
+ " \"vout\":n, (numeric, required) The "
+ "output number\n"
+ " \"scriptPubKey\": \"hex\", (string, required) script "
+ "key\n"
+ " \"redeemScript\": \"hex\", (string, required for "
+ "P2SH) redeem script\n"
+ " \"amount\": value (numeric, required) The "
+ "amount spent\n"
+ " }\n"
+ " ,...\n"
+ " ]\n"
+ "4. \"sighashtype\" (string, optional, "
+ "default=ALL) The signature hash type. Must be one of\n"
+ " \"ALL|FORKID\"\n"
+ " \"NONE|FORKID\"\n"
+ " \"SINGLE|FORKID\"\n"
+ " \"ALL|FORKID|ANYONECANPAY\"\n"
+ " \"NONE|FORKID|ANYONECANPAY\"\n"
+ " \"SINGLE|FORKID|ANYONECANPAY\"\n"
+
+ "\nResult:\n"
+ "{\n"
+ " \"hex\" : \"value\", (string) The hex-encoded "
+ "raw transaction with signature(s)\n"
+ " \"complete\" : true|false, (boolean) If the "
+ "transaction has a complete set of signatures\n"
+ " \"errors\" : [ (json array of objects) "
+ "Script verification errors (if there are any)\n"
+ " {\n"
+ " \"txid\" : \"hash\", (string) The hash of the "
+ "referenced, previous transaction\n"
+ " \"vout\" : n, (numeric) The index of the "
+ "output to spent and used as input\n"
+ " \"scriptSig\" : \"hex\", (string) The hex-encoded "
+ "signature script\n"
+ " \"sequence\" : n, (numeric) Script sequence "
+ "number\n"
+ " \"error\" : \"text\" (string) Verification or "
+ "signing error related to the input\n"
+ " }\n"
+ " ,...\n"
+ " ]\n"
+ "}\n"
+
+ "\nExamples:\n" +
+ HelpExampleCli("signrawtransactionwithkey", "\"myhex\"") +
+ HelpExampleRpc("signrawtransactionwithkey", "\"myhex\""));
+ }
+
+ RPCTypeCheck(
+ request.params,
+ {UniValue::VSTR, UniValue::VARR, UniValue::VARR, UniValue::VSTR}, true);
+
+ CMutableTransaction mtx;
+ if (!DecodeHexTx(mtx, request.params[0].get_str())) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
+ }
+
+ CBasicKeyStore keystore;
+ const UniValue &keys = request.params[1].get_array();
+ for (size_t idx = 0; idx < keys.size(); ++idx) {
+ UniValue k = keys[idx];
+ CBitcoinSecret vchSecret;
+ if (!vchSecret.SetString(k.get_str())) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
+ "Invalid private key");
+ }
+ CKey key = vchSecret.GetKey();
+ if (!key.IsValid()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
+ "Private key outside allowed range");
+ }
+ keystore.AddKey(key);
+ }
+
+ return SignTransaction(mtx, request.params[2], &keystore, true,
+ request.params[3]);
+}
+
+static UniValue signrawtransaction(const Config &config,
+ const JSONRPCRequest &request) {
+#ifdef ENABLE_WALLET
+ CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
+#endif
+
+ if (request.fHelp || request.params.size() < 1 ||
+ request.params.size() > 4) {
+ throw std::runtime_error(
+ "signrawtransaction \"hexstring\" ( "
+ "[{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\","
+ "\"redeemScript\":\"hex\"},...] [\"privatekey1\",...] sighashtype "
+ ")\n"
+ "\nSign inputs for raw transaction (serialized, hex-encoded).\n"
+ "The second optional argument (may be null) is an array of "
+ "previous transaction outputs that\n"
+ "this transaction depends on but may not yet be in the block "
+ "chain.\n"
+ "The third optional argument (may be null) is an array of "
+ "base58-encoded private\n"
+ "keys that, if given, will be the only keys used to sign the "
+ "transaction.\n"
+#ifdef ENABLE_WALLET
+ + HelpRequiringPassphrase(pwallet) +
+ "\n"
+#endif
+ "\nArguments:\n"
+ "1. \"hexstring\" (string, required) The transaction hex "
+ "string\n"
+ "2. \"prevtxs\" (string, optional) An json array of previous "
+ "dependent transaction outputs\n"
+ " [ (json array of json objects, or 'null' if "
+ "none provided)\n"
+ " {\n"
+ " \"txid\":\"id\", (string, required) The "
+ "transaction id\n"
+ " \"vout\":n, (numeric, required) The "
+ "output number\n"
+ " \"scriptPubKey\": \"hex\", (string, required) script "
+ "key\n"
+ " \"redeemScript\": \"hex\", (string, required for P2SH) "
+ "redeem script\n"
+ " \"amount\": value (numeric, required) The "
+ "amount spent\n"
+ " }\n"
+ " ,...\n"
+ " ]\n"
+ "3. \"privkeys\" (string, optional) A json array of "
+ "base58-encoded private keys for signing\n"
+ " [ (json array of strings, or 'null' if none "
+ "provided)\n"
+ " \"privatekey\" (string) private key in base58-encoding\n"
+ " ,...\n"
+ " ]\n"
+ "4. \"sighashtype\" (string, optional, default=ALL) The "
+ "signature hash type. Must be one of\n"
+ " \"ALL|FORKID\"\n"
+ " \"NONE|FORKID\"\n"
+ " \"SINGLE|FORKID\"\n"
+ " \"ALL|FORKID|ANYONECANPAY\"\n"
+ " \"NONE|FORKID|ANYONECANPAY\"\n"
+ " \"SINGLE|FORKID|ANYONECANPAY\"\n"
+
+ "\nResult:\n"
+ "{\n"
+ " \"hex\" : \"value\", (string) The hex-encoded raw "
+ "transaction with signature(s)\n"
+ " \"complete\" : true|false, (boolean) If the transaction has a "
+ "complete set of signatures\n"
+ " \"errors\" : [ (json array of objects) Script "
+ "verification errors (if there are any)\n"
+ " {\n"
+ " \"txid\" : \"hash\", (string) The hash of the "
+ "referenced, previous transaction\n"
+ " \"vout\" : n, (numeric) The index of the "
+ "output to spent and used as input\n"
+ " \"scriptSig\" : \"hex\", (string) The hex-encoded "
+ "signature script\n"
+ " \"sequence\" : n, (numeric) Script sequence "
+ "number\n"
+ " \"error\" : \"text\" (string) Verification or "
+ "signing error related to the input\n"
+ " }\n"
+ " ,...\n"
+ " ]\n"
+ "}\n"
+
+ "\nExamples:\n" +
+ HelpExampleCli("signrawtransaction", "\"myhex\"") +
+ HelpExampleRpc("signrawtransaction", "\"myhex\""));
+ }
+
+ RPCTypeCheck(
+ request.params,
+ {UniValue::VSTR, UniValue::VARR, UniValue::VARR, UniValue::VSTR}, true);
+
+ // Make a JSONRPCRequest to pass on to the right signrawtransaction* command
+ JSONRPCRequest new_request;
+ new_request.id = request.id;
+ new_request.params.setArray();
+
+ // For signing with private keys
+ if (!request.params[2].isNull()) {
+ new_request.params.push_back(request.params[0]);
+ // Note: the prevtxs and privkeys are reversed for
+ // signrawtransactionwithkey
+ new_request.params.push_back(request.params[2]);
+ new_request.params.push_back(request.params[1]);
+ new_request.params.push_back(request.params[3]);
+ return signrawtransactionwithkey(config, new_request);
+ }
+// Otherwise sign with the wallet which does not take a privkeys parameter
+#ifdef ENABLE_WALLET
+ else {
+ new_request.params.push_back(request.params[0]);
+ new_request.params.push_back(request.params[1]);
+ new_request.params.push_back(request.params[3]);
+ return signrawtransactionwithwallet(config, new_request);
+ }
+#endif
+ // If we have made it this far, then wallet is disabled and no private keys
+ // were given, so fail here.
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "No private keys available.");
+}
+
static UniValue sendrawtransaction(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() < 1 ||
@@ -1203,18 +1294,19 @@
// clang-format off
static const ContextFreeRPCCommand commands[] = {
- // category name actor (function) argNames
- // ------------------- ------------------------ ---------------------- ----------
- { "rawtransactions", "getrawtransaction", getrawtransaction, {"txid","verbose"} },
- { "rawtransactions", "createrawtransaction", createrawtransaction, {"inputs","outputs","locktime"} },
- { "rawtransactions", "decoderawtransaction", decoderawtransaction, {"hexstring"} },
- { "rawtransactions", "decodescript", decodescript, {"hexstring"} },
- { "rawtransactions", "sendrawtransaction", sendrawtransaction, {"hexstring","allowhighfees"} },
- { "rawtransactions", "combinerawtransaction", combinerawtransaction, {"txs"} },
- { "rawtransactions", "signrawtransaction", signrawtransaction, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */
-
- { "blockchain", "gettxoutproof", gettxoutproof, {"txids", "blockhash"} },
- { "blockchain", "verifytxoutproof", verifytxoutproof, {"proof"} },
+ // category name actor (function) argNames
+ // ------------------- ------------------------ ---------------------- ----------
+ { "rawtransactions", "getrawtransaction", getrawtransaction, {"txid","verbose"} },
+ { "rawtransactions", "createrawtransaction", createrawtransaction, {"inputs","outputs","locktime"} },
+ { "rawtransactions", "decoderawtransaction", decoderawtransaction, {"hexstring"} },
+ { "rawtransactions", "decodescript", decodescript, {"hexstring"} },
+ { "rawtransactions", "sendrawtransaction", sendrawtransaction, {"hexstring","allowhighfees"} },
+ { "rawtransactions", "combinerawtransaction", combinerawtransaction, {"txs"} },
+ { "rawtransactions", "signrawtransaction", signrawtransaction, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */
+ { "rawtransactions", "signrawtransactionwithkey", signrawtransactionwithkey, {"hexstring","privkeys","prevtxs","sighashtype"} },
+
+ { "blockchain", "gettxoutproof", gettxoutproof, {"txids", "blockhash"} },
+ { "blockchain", "verifytxoutproof", verifytxoutproof, {"proof"} },
};
// clang-format on
diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -81,18 +81,6 @@
r = CallRPC(std::string("decoderawtransaction ") + rawtx + " extra"),
std::runtime_error);
- BOOST_CHECK_THROW(CallRPC("signrawtransaction"), std::runtime_error);
- BOOST_CHECK_THROW(CallRPC("signrawtransaction null"), std::runtime_error);
- BOOST_CHECK_THROW(CallRPC("signrawtransaction ff00"), std::runtime_error);
- BOOST_CHECK_NO_THROW(CallRPC(std::string("signrawtransaction ") + rawtx));
- BOOST_CHECK_NO_THROW(CallRPC(std::string("signrawtransaction ") + rawtx +
- " null null NONE|FORKID|ANYONECANPAY"));
- BOOST_CHECK_NO_THROW(CallRPC(std::string("signrawtransaction ") + rawtx +
- " [] [] NONE|FORKID|ANYONECANPAY"));
- BOOST_CHECK_THROW(CallRPC(std::string("signrawtransaction ") + rawtx +
- " null null badenum"),
- std::runtime_error);
-
// Only check failure cases for sendrawtransaction, there's no network to
// send to...
BOOST_CHECK_THROW(CallRPC("sendrawtransaction"), std::runtime_error);
@@ -145,11 +133,11 @@
"\"KzsXybp9jX64P5ekX1KUxRQ79Jht9uzW7LorgwE65i5rWACL6LQe\"";
std::string privkey2 =
"\"Kyhdf5LuKTRx4ge69ybABsiUAWjVRK4XGxAKk2FQLp2HjGMy87Z4\"";
- r = CallRPC(std::string("signrawtransaction ") + notsigned + " " + prevout +
- " " + "[]");
+ r = CallRPC(std::string("signrawtransactionwithkey ") + notsigned + " [] " +
+ prevout);
BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == false);
- r = CallRPC(std::string("signrawtransaction ") + notsigned + " " + prevout +
- " " + "[" + privkey1 + "," + privkey2 + "]");
+ r = CallRPC(std::string("signrawtransactionwithkey ") + notsigned + " [" +
+ privkey1 + "," + privkey2 + "] " + prevout);
BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == true);
}
@@ -180,8 +168,8 @@
bool exceptionThrownDueToMissingAmount = false,
errorWasMissingAmount = false;
try {
- r = CallRPC(std::string("signrawtransaction ") + notsigned + " " +
- prevout + " " + "[" + privkey1 + "," + privkey2 + "]");
+ r = CallRPC(std::string("signrawtransactionwithkey ") + notsigned +
+ " [" + privkey1 + "," + privkey2 + "] " + prevout);
} catch (const std::runtime_error &e) {
exceptionThrownDueToMissingAmount = true;
if (std::string(e.what()).find("amount") != std::string::npos) {
diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h
--- a/src/wallet/rpcwallet.h
+++ b/src/wallet/rpcwallet.h
@@ -11,6 +11,8 @@
class CWallet;
class JSONRPCRequest;
class CWallet;
+class UniValue;
+class Config;
void RegisterWalletRPCCommands(CRPCTable &t);
@@ -26,4 +28,6 @@
void EnsureWalletIsUnlocked(CWallet *);
bool EnsureWalletIsAvailable(CWallet *, bool avoidException);
+UniValue signrawtransactionwithwallet(const Config &config,
+ const JSONRPCRequest &request);
#endif // BITCOIN_WALLET_RPCWALLET_H
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -15,6 +15,7 @@
#include "policy/policy.h"
#include "rpc/mining.h"
#include "rpc/misc.h"
+#include "rpc/rawtransaction.h"
#include "rpc/safemode.h"
#include "rpc/server.h"
#include "timedata.h"
@@ -3610,6 +3611,100 @@
return result;
}
+UniValue signrawtransactionwithwallet(const Config &config,
+ const JSONRPCRequest &request) {
+ CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
+ return NullUniValue;
+ }
+
+ if (request.fHelp || request.params.size() < 1 ||
+ request.params.size() > 3) {
+ throw std::runtime_error(
+ "signrawtransactionwithwallet \"hexstring\" ( "
+ "[{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\","
+ "\"redeemScript\":\"hex\"},...] sighashtype )\n"
+ "\nSign inputs for raw transaction (serialized, hex-encoded).\n"
+ "The second optional argument (may be null) is an array of "
+ "previous transaction outputs that\n"
+ "this transaction depends on but may not yet be in the block "
+ "chain.\n" +
+ HelpRequiringPassphrase(pwallet) +
+ "\n"
+
+ "\nArguments:\n"
+ "1. \"hexstring\" (string, required) The "
+ "transaction hex string\n"
+ "2. \"prevtxs\" (string, optional) An json "
+ "array of previous dependent transaction outputs\n"
+ " [ (json array of json objects, "
+ "or 'null' if none provided)\n"
+ " {\n"
+ " \"txid\":\"id\", (string, required) The "
+ "transaction id\n"
+ " \"vout\":n, (numeric, required) The "
+ "output number\n"
+ " \"scriptPubKey\": \"hex\", (string, required) script "
+ "key\n"
+ " \"redeemScript\": \"hex\", (string, required for "
+ "P2SH) redeem script\n"
+ " \"amount\": value (numeric, required) The "
+ "amount spent\n"
+ " }\n"
+ " ,...\n"
+ " ]\n"
+ "3. \"sighashtype\" (string, optional, "
+ "default=ALL) The signature hash type. Must be one of\n"
+ " \"ALL|FORKID\"\n"
+ " \"NONE|FORKID\"\n"
+ " \"SINGLE|FORKID\"\n"
+ " \"ALL|FORKID|ANYONECANPAY\"\n"
+ " \"NONE|FORKID|ANYONECANPAY\"\n"
+ " \"SINGLE|FORKID|ANYONECANPAY\"\n"
+
+ "\nResult:\n"
+ "{\n"
+ " \"hex\" : \"value\", (string) The hex-encoded "
+ "raw transaction with signature(s)\n"
+ " \"complete\" : true|false, (boolean) If the "
+ "transaction has a complete set of signatures\n"
+ " \"errors\" : [ (json array of objects) "
+ "Script verification errors (if there are any)\n"
+ " {\n"
+ " \"txid\" : \"hash\", (string) The hash of the "
+ "referenced, previous transaction\n"
+ " \"vout\" : n, (numeric) The index of the "
+ "output to spent and used as input\n"
+ " \"scriptSig\" : \"hex\", (string) The hex-encoded "
+ "signature script\n"
+ " \"sequence\" : n, (numeric) Script sequence "
+ "number\n"
+ " \"error\" : \"text\" (string) Verification or "
+ "signing error related to the input\n"
+ " }\n"
+ " ,...\n"
+ " ]\n"
+ "}\n"
+
+ "\nExamples:\n" +
+ HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") +
+ HelpExampleRpc("signrawtransactionwithwallet", "\"myhex\""));
+ }
+
+ RPCTypeCheck(request.params,
+ {UniValue::VSTR, UniValue::VARR, UniValue::VSTR}, true);
+
+ CMutableTransaction mtx;
+ if (!DecodeHexTx(mtx, request.params[0].get_str())) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
+ }
+
+ // Sign the transaction
+ LOCK2(cs_main, pwallet->cs_wallet);
+ return SignTransaction(mtx, request.params[1], pwallet, false,
+ request.params[2]);
+}
+
static UniValue generate(const Config &config, const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
@@ -3745,52 +3840,53 @@
// clang-format off
static const ContextFreeRPCCommand commands[] = {
- // category name actor (function) argNames
- // ------------------- ------------------------ ---------------------- ----------
- { "rawtransactions", "fundrawtransaction", fundrawtransaction, {"hexstring","options"} },
- { "hidden", "resendwallettransactions", resendwallettransactions, {} },
- { "wallet", "abandontransaction", abandontransaction, {"txid"} },
- { "wallet", "addmultisigaddress", addmultisigaddress, {"nrequired","keys","label|account"} },
- { "wallet", "backupwallet", backupwallet, {"destination"} },
- { "wallet", "encryptwallet", encryptwallet, {"passphrase"} },
- { "wallet", "getaccountaddress", getlabeladdress, {"account"} },
- { "wallet", "getlabeladdress", getlabeladdress, {"label"} },
- { "wallet", "getaccount", getaccount, {"address"} },
- { "wallet", "getaddressesbyaccount", getaddressesbyaccount, {"account"} },
- { "wallet", "getbalance", getbalance, {"account","minconf","include_watchonly"} },
- { "wallet", "getnewaddress", getnewaddress, {"label|account"} },
- { "wallet", "getrawchangeaddress", getrawchangeaddress, {} },
- { "wallet", "getreceivedbylabel", getreceivedbylabel, {"label","minconf"} },
- { "wallet", "getreceivedbyaccount", getreceivedbylabel, {"account","minconf"} },
- { "wallet", "getreceivedbyaddress", getreceivedbyaddress, {"address","minconf"} },
- { "wallet", "gettransaction", gettransaction, {"txid","include_watchonly"} },
- { "wallet", "getunconfirmedbalance", getunconfirmedbalance, {} },
- { "wallet", "getwalletinfo", getwalletinfo, {} },
- { "wallet", "keypoolrefill", keypoolrefill, {"newsize"} },
- { "wallet", "listaccounts", listaccounts, {"minconf","include_watchonly"} },
- { "wallet", "listaddressgroupings", listaddressgroupings, {} },
- { "wallet", "listlockunspent", listlockunspent, {} },
- { "wallet", "listreceivedbylabel", listreceivedbylabel, {"minconf","include_empty","include_watchonly"} },
- { "wallet", "listreceivedbyaccount", listreceivedbylabel, {"minconf","include_empty","include_watchonly"} },
- { "wallet", "listreceivedbyaddress", listreceivedbyaddress, {"minconf","include_empty","include_watchonly","address_filter"} },
- { "wallet", "listsinceblock", listsinceblock, {"blockhash","target_confirmations","include_watchonly"} },
- { "wallet", "listtransactions", listtransactions, {"account","count","skip","include_watchonly"} },
- { "wallet", "listunspent", listunspent, {"minconf","maxconf","addresses","include_unsafe","query_options"} },
- { "wallet", "listwallets", listwallets, {} },
- { "wallet", "lockunspent", lockunspent, {"unlock","transactions"} },
- { "wallet", "move", movecmd, {"fromaccount","toaccount","amount","minconf","comment"} },
- { "wallet", "rescanblockchain", rescanblockchain, {"start_height", "stop_height"} },
- { "wallet", "sendfrom", sendfrom, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} },
- { "wallet", "sendmany", sendmany, {"fromaccount","amounts","minconf","comment","subtractfeefrom"} },
- { "wallet", "sendtoaddress", sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount"} },
- { "wallet", "setlabel", setlabel, {"address","label"} },
- { "wallet", "setaccount", setlabel, {"address","account"} },
- { "wallet", "settxfee", settxfee, {"amount"} },
- { "wallet", "signmessage", signmessage, {"address","message"} },
- { "wallet", "walletlock", walletlock, {} },
- { "wallet", "walletpassphrasechange", walletpassphrasechange, {"oldpassphrase","newpassphrase"} },
- { "wallet", "walletpassphrase", walletpassphrase, {"passphrase","timeout"} },
- { "generating", "generate", generate, {"nblocks","maxtries"} },
+ // category name actor (function) argNames
+ // ------------------- ------------------------ ---------------------- ----------
+ { "rawtransactions", "fundrawtransaction", fundrawtransaction, {"hexstring","options"} },
+ { "hidden", "resendwallettransactions", resendwallettransactions, {} },
+ { "wallet", "abandontransaction", abandontransaction, {"txid"} },
+ { "wallet", "addmultisigaddress", addmultisigaddress, {"nrequired","keys","label|account"} },
+ { "wallet", "backupwallet", backupwallet, {"destination"} },
+ { "wallet", "encryptwallet", encryptwallet, {"passphrase"} },
+ { "wallet", "getaccountaddress", getlabeladdress, {"account"} },
+ { "wallet", "getlabeladdress", getlabeladdress, {"label"} },
+ { "wallet", "getaccount", getaccount, {"address"} },
+ { "wallet", "getaddressesbyaccount", getaddressesbyaccount, {"account"} },
+ { "wallet", "getbalance", getbalance, {"account","minconf","include_watchonly"} },
+ { "wallet", "getnewaddress", getnewaddress, {"label|account"} },
+ { "wallet", "getrawchangeaddress", getrawchangeaddress, {} },
+ { "wallet", "getreceivedbylabel", getreceivedbylabel, {"label","minconf"} },
+ { "wallet", "getreceivedbyaccount", getreceivedbylabel, {"account","minconf"} },
+ { "wallet", "getreceivedbyaddress", getreceivedbyaddress, {"address","minconf"} },
+ { "wallet", "gettransaction", gettransaction, {"txid","include_watchonly"} },
+ { "wallet", "getunconfirmedbalance", getunconfirmedbalance, {} },
+ { "wallet", "getwalletinfo", getwalletinfo, {} },
+ { "wallet", "keypoolrefill", keypoolrefill, {"newsize"} },
+ { "wallet", "listaccounts", listaccounts, {"minconf","include_watchonly"} },
+ { "wallet", "listaddressgroupings", listaddressgroupings, {} },
+ { "wallet", "listlockunspent", listlockunspent, {} },
+ { "wallet", "listreceivedbylabel", listreceivedbylabel, {"minconf","include_empty","include_watchonly"} },
+ { "wallet", "listreceivedbyaccount", listreceivedbylabel, {"minconf","include_empty","include_watchonly"} },
+ { "wallet", "listreceivedbyaddress", listreceivedbyaddress, {"minconf","include_empty","include_watchonly","address_filter"} },
+ { "wallet", "listsinceblock", listsinceblock, {"blockhash","target_confirmations","include_watchonly"} },
+ { "wallet", "listtransactions", listtransactions, {"account","count","skip","include_watchonly"} },
+ { "wallet", "listunspent", listunspent, {"minconf","maxconf","addresses","include_unsafe","query_options"} },
+ { "wallet", "listwallets", listwallets, {} },
+ { "wallet", "lockunspent", lockunspent, {"unlock","transactions"} },
+ { "wallet", "move", movecmd, {"fromaccount","toaccount","amount","minconf","comment"} },
+ { "wallet", "rescanblockchain", rescanblockchain, {"start_height", "stop_height"} },
+ { "wallet", "sendfrom", sendfrom, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} },
+ { "wallet", "sendmany", sendmany, {"fromaccount","amounts","minconf","comment","subtractfeefrom"} },
+ { "wallet", "sendtoaddress", sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount"} },
+ { "wallet", "setlabel", setlabel, {"address","label"} },
+ { "wallet", "setaccount", setlabel, {"address","account"} },
+ { "wallet", "settxfee", settxfee, {"amount"} },
+ { "wallet", "signmessage", signmessage, {"address","message"} },
+ { "wallet", "signrawtransactionwithwallet", signrawtransactionwithwallet, {"hextring","prevtxs","sighashtype"} },
+ { "wallet", "walletlock", walletlock, {} },
+ { "wallet", "walletpassphrasechange", walletpassphrasechange, {"oldpassphrase","newpassphrase"} },
+ { "wallet", "walletpassphrase", walletpassphrase, {"passphrase","timeout"} },
+ { "generating", "generate", generate, {"nblocks","maxtries"} },
};
// clang-format on
diff --git a/test/functional/abc-high_priority_transaction.py b/test/functional/abc-high_priority_transaction.py
--- a/test/functional/abc-high_priority_transaction.py
+++ b/test/functional/abc-high_priority_transaction.py
@@ -29,8 +29,8 @@
change = t['amount'] - fee
outputs[addr] = satoshi_round(change)
rawtx = node.createrawtransaction(inputs, outputs)
- signresult = node.signrawtransaction(
- rawtx, None, None, "NONE|FORKID")
+ signresult = node.signrawtransactionwithwallet(
+ rawtx, None, "NONE|FORKID")
txid = node.sendrawtransaction(signresult["hex"], True)
txids.append(txid)
return txids
diff --git a/test/functional/rpc_signrawtransaction.py b/test/functional/rpc_signrawtransaction.py
--- a/test/functional/rpc_signrawtransaction.py
+++ b/test/functional/rpc_signrawtransaction.py
@@ -2,6 +2,7 @@
# Copyright (c) 2015-2016 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test transaction signing using the signrawtransactionwithwallet RPC."""
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error
@@ -35,7 +36,8 @@
outputs = {'mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB': 0.1}
rawTx = self.nodes[0].createrawtransaction(inputs, outputs)
- rawTxSigned = self.nodes[0].signrawtransaction(rawTx, inputs, privKeys)
+ rawTxSigned = self.nodes[0].signrawtransactionwithkey(
+ rawTx, privKeys, inputs)
# 1) The transaction has a complete set of signatures
assert 'complete' in rawTxSigned
@@ -92,8 +94,8 @@
assert_raises_rpc_error(-22, "TX decode failed",
self.nodes[0].decoderawtransaction, rawTx + "00")
- rawTxSigned = self.nodes[0].signrawtransaction(
- rawTx, scripts, privKeys)
+ rawTxSigned = self.nodes[0].signrawtransactionwithkey(
+ rawTx, privKeys, scripts)
# 3) The transaction has no complete set of signatures
assert 'complete' in rawTxSigned
diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py
--- a/test/functional/test_framework/blocktools.py
+++ b/test/functional/test_framework/blocktools.py
@@ -194,8 +194,8 @@
CScript([OP_DUP, OP_HASH160, addrHash, OP_EQUALVERIFY, OP_CHECKSIG])))
# Create a proper fee for the transaction to be mined
ctx.vout[1].nValue -= int(fee_multiplier * node.calculate_fee(ctx))
- signresult = node.signrawtransaction(
- ToHex(ctx), None, None, "NONE|FORKID")
+ signresult = node.signrawtransactionwithwallet(
+ ToHex(ctx), None, "NONE|FORKID")
txid = node.sendrawtransaction(signresult["hex"], True)
txids.append(txid)
return txids
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -576,7 +576,7 @@
outputs[to_node.getnewaddress()] = float(amount)
rawtx = from_node.createrawtransaction(inputs, outputs)
- signresult = from_node.signrawtransaction(rawtx)
+ signresult = from_node.signrawtransactionwithwallet(rawtx)
txid = from_node.sendrawtransaction(signresult["hex"], True)
return (txid, signresult["hex"], fee)
@@ -630,7 +630,8 @@
newtx = rawtx[0:92]
newtx = newtx + txouts
newtx = newtx + rawtx[94:]
- signresult = node.signrawtransaction(newtx, None, None, "NONE|FORKID")
+ signresult = node.signrawtransactionwithwallet(
+ newtx, None, "NONE|FORKID")
txid = node.sendrawtransaction(signresult["hex"], True)
txids.append(txid)
return txids
diff --git a/test/functional/wallet_txn_clone.py b/test/functional/wallet_txn_clone.py
--- a/test/functional/wallet_txn_clone.py
+++ b/test/functional/wallet_txn_clone.py
@@ -83,8 +83,8 @@
# Use a different signature hash type to sign. This creates an equivalent but malleated clone.
# Don't send the clone anywhere yet
- tx1_clone = self.nodes[0].signrawtransaction(
- clone_raw, None, None, "ALL|FORKID|ANYONECANPAY")
+ tx1_clone = self.nodes[0].signrawtransactionwithwallet(
+ clone_raw, None, "ALL|FORKID|ANYONECANPAY")
assert_equal(tx1_clone["complete"], True)
# Have node0 mine a block, if requested: