Changeset View
Changeset View
Standalone View
Standalone View
src/rpc/rawtransaction.cpp
// Copyright (c) 2010 Satoshi Nakamoto | // Copyright (c) 2010 Satoshi Nakamoto | ||||
// Copyright (c) 2009-2016 The Bitcoin Core developers | // Copyright (c) 2009-2016 The Bitcoin Core developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#include "rpc/rawtransaction.h" | |||||
#include "base58.h" | #include "base58.h" | ||||
#include "chain.h" | #include "chain.h" | ||||
#include "coins.h" | #include "coins.h" | ||||
#include "config.h" | #include "config.h" | ||||
#include "consensus/validation.h" | #include "consensus/validation.h" | ||||
#include "core_io.h" | #include "core_io.h" | ||||
#include "dstencode.h" | #include "dstencode.h" | ||||
#include "init.h" | #include "init.h" | ||||
#include "keystore.h" | #include "keystore.h" | ||||
#include "merkleblock.h" | #include "merkleblock.h" | ||||
#include "net.h" | #include "net.h" | ||||
#include "policy/policy.h" | #include "policy/policy.h" | ||||
#include "primitives/transaction.h" | #include "primitives/transaction.h" | ||||
#include "rpc/safemode.h" | #include "rpc/safemode.h" | ||||
#include "rpc/server.h" | #include "rpc/server.h" | ||||
#include "rpc/tojson.h" | #include "rpc/tojson.h" | ||||
#include "script/script.h" | #include "script/script.h" | ||||
#include "script/script_error.h" | #include "script/script_error.h" | ||||
#include "script/sign.h" | #include "script/sign.h" | ||||
#include "script/standard.h" | #include "script/standard.h" | ||||
#include "txmempool.h" | #include "txmempool.h" | ||||
#include "uint256.h" | #include "uint256.h" | ||||
#include "util.h" | |||||
#include "utilstrencodings.h" | #include "utilstrencodings.h" | ||||
#include "validation.h" | #include "validation.h" | ||||
#ifdef ENABLE_WALLET | #ifdef ENABLE_WALLET | ||||
#include "wallet/rpcwallet.h" | #include "wallet/rpcwallet.h" | ||||
#include "wallet/wallet.h" | |||||
#endif | #endif | ||||
#include <cstdint> | #include <cstdint> | ||||
#include <univalue.h> | #include <univalue.h> | ||||
void ScriptPubKeyToJSON(const Config &config, const CScript &scriptPubKey, | void ScriptPubKeyToJSON(const Config &config, const CScript &scriptPubKey, | ||||
UniValue &out, bool fIncludeHex) { | UniValue &out, bool fIncludeHex) { | ||||
▲ Show 20 Lines • Show All 676 Lines • ▼ Show 20 Lines | static void TxInErrorToJSON(const CTxIn &txin, UniValue &vErrorsRet, | ||||
entry.pushKV("vout", uint64_t(txin.prevout.GetN())); | entry.pushKV("vout", uint64_t(txin.prevout.GetN())); | ||||
entry.pushKV("scriptSig", | entry.pushKV("scriptSig", | ||||
HexStr(txin.scriptSig.begin(), txin.scriptSig.end())); | HexStr(txin.scriptSig.begin(), txin.scriptSig.end())); | ||||
entry.pushKV("sequence", uint64_t(txin.nSequence)); | entry.pushKV("sequence", uint64_t(txin.nSequence)); | ||||
entry.pushKV("error", strMessage); | entry.pushKV("error", strMessage); | ||||
vErrorsRet.push_back(entry); | vErrorsRet.push_back(entry); | ||||
} | } | ||||
UniValue combinerawtransaction(const Config &config, | static UniValue combinerawtransaction(const Config &config, | ||||
const JSONRPCRequest &request) { | const JSONRPCRequest &request) { | ||||
if (request.fHelp || request.params.size() != 1) { | if (request.fHelp || request.params.size() != 1) { | ||||
throw std::runtime_error( | throw std::runtime_error( | ||||
"combinerawtransaction [\"hexstring\",...]\n" | "combinerawtransaction [\"hexstring\",...]\n" | ||||
"\nCombine multiple partially signed transactions into one " | "\nCombine multiple partially signed transactions into one " | ||||
"transaction.\n" | "transaction.\n" | ||||
"The combined transaction may be another partially signed " | "The combined transaction may be another partially signed " | ||||
"transaction or a \n" | "transaction or a \n" | ||||
▲ Show 20 Lines • Show All 81 Lines • ▼ Show 20 Lines | for (size_t i = 0; i < mergedTx.vin.size(); i++) { | ||||
} | } | ||||
UpdateTransaction(mergedTx, i, sigdata); | UpdateTransaction(mergedTx, i, sigdata); | ||||
} | } | ||||
return EncodeHexTx(CTransaction(mergedTx)); | return EncodeHexTx(CTransaction(mergedTx)); | ||||
} | } | ||||
static UniValue signrawtransaction(const Config &config, | UniValue SignTransaction(CMutableTransaction &mtx, | ||||
const JSONRPCRequest &request) { | const UniValue &prevTxsUnival, | ||||
#ifdef ENABLE_WALLET | CBasicKeyStore *keystore, bool is_temp_keystore, | ||||
CWallet *const pwallet = GetWalletForJSONRPCRequest(request); | const UniValue &hashType) { | ||||
#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"); | |||||
} | |||||
// Fetch previous transactions (inputs): | // Fetch previous transactions (inputs): | ||||
CCoinsView viewDummy; | CCoinsView viewDummy; | ||||
CCoinsViewCache view(&viewDummy); | CCoinsViewCache view(&viewDummy); | ||||
{ | { | ||||
LOCK(g_mempool.cs); | LOCK2(cs_main, g_mempool.cs); | ||||
CCoinsViewCache &viewChain = *pcoinsTip; | CCoinsViewCache &viewChain = *pcoinsTip; | ||||
CCoinsViewMemPool viewMempool(&viewChain, g_mempool); | CCoinsViewMemPool viewMempool(&viewChain, g_mempool); | ||||
// Temporarily switch cache backend to db+mempool view. | // Temporarily switch cache backend to db+mempool view. | ||||
view.SetBackend(viewMempool); | view.SetBackend(viewMempool); | ||||
for (const CTxIn &txin : mtx.vin) { | for (const CTxIn &txin : mtx.vin) { | ||||
// Load entries from viewChain into view; can fail. | // Load entries from viewChain into view; can fail. | ||||
view.AccessCoin(txin.prevout); | view.AccessCoin(txin.prevout); | ||||
} | } | ||||
// Switch back to avoid locking mempool for too long. | // Switch back to avoid locking mempool for too long. | ||||
view.SetBackend(viewDummy); | 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: | // Add previous txouts given in the RPC call: | ||||
if (request.params.size() > 1 && !request.params[1].isNull()) { | if (!prevTxsUnival.isNull()) { | ||||
UniValue prevTxs = request.params[1].get_array(); | UniValue prevTxs = prevTxsUnival.get_array(); | ||||
for (size_t idx = 0; idx < prevTxs.size(); idx++) { | for (size_t idx = 0; idx < prevTxs.size(); ++idx) { | ||||
const UniValue &p = prevTxs[idx]; | const UniValue &p = prevTxs[idx]; | ||||
if (!p.isObject()) { | if (!p.isObject()) { | ||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, | throw JSONRPCError(RPC_DESERIALIZATION_ERROR, | ||||
"expected object with " | "expected object with " | ||||
"{\"txid'\",\"vout\",\"scriptPubKey\"}"); | "{\"txid'\",\"vout\",\"scriptPubKey\"}"); | ||||
} | } | ||||
UniValue prevOut = p.get_obj(); | UniValue prevOut = p.get_obj(); | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | if (!prevTxsUnival.isNull()) { | ||||
// eg getbalance returns "3.14152" rather than 3.14152 | // eg getbalance returns "3.14152" rather than 3.14152 | ||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing amount"); | throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing amount"); | ||||
} | } | ||||
view.AddCoin(out, Coin(txout, 1, false), true); | view.AddCoin(out, Coin(txout, 1, false), true); | ||||
} | } | ||||
// If redeemScript given and not using the local wallet (private | // 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: | // signed: | ||||
if (fGivenKeys && scriptPubKey.IsPayToScriptHash()) { | if (is_temp_keystore && (scriptPubKey.IsPayToScriptHash())) { | ||||
RPCTypeCheckObj( | RPCTypeCheckObj( | ||||
prevOut, | prevOut, | ||||
{ | { | ||||
{"txid", UniValueType(UniValue::VSTR)}, | {"txid", UniValueType(UniValue::VSTR)}, | ||||
{"vout", UniValueType(UniValue::VNUM)}, | {"vout", UniValueType(UniValue::VNUM)}, | ||||
{"scriptPubKey", UniValueType(UniValue::VSTR)}, | {"scriptPubKey", UniValueType(UniValue::VSTR)}, | ||||
{"redeemScript", UniValueType(UniValue::VSTR)}, | {"redeemScript", UniValueType(UniValue::VSTR)}, | ||||
}); | }); | ||||
UniValue v = find_value(prevOut, "redeemScript"); | UniValue v = find_value(prevOut, "redeemScript"); | ||||
if (!v.isNull()) { | if (!v.isNull()) { | ||||
std::vector<uint8_t> rsData(ParseHexV(v, "redeemScript")); | std::vector<uint8_t> rsData(ParseHexV(v, "redeemScript")); | ||||
CScript redeemScript(rsData.begin(), rsData.end()); | 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(); | SigHashType sigHashType = SigHashType().withForkId(); | ||||
if (request.params.size() > 3 && !request.params[3].isNull()) { | if (!hashType.isNull()) { | ||||
static std::map<std::string, int> mapSigHashValues = { | static std::map<std::string, int> mapSigHashValues = { | ||||
{"ALL", SIGHASH_ALL}, | {"ALL", SIGHASH_ALL}, | ||||
{"ALL|ANYONECANPAY", SIGHASH_ALL | SIGHASH_ANYONECANPAY}, | {"ALL|ANYONECANPAY", SIGHASH_ALL | SIGHASH_ANYONECANPAY}, | ||||
{"ALL|FORKID", SIGHASH_ALL | SIGHASH_FORKID}, | {"ALL|FORKID", SIGHASH_ALL | SIGHASH_FORKID}, | ||||
{"ALL|FORKID|ANYONECANPAY", | {"ALL|FORKID|ANYONECANPAY", | ||||
SIGHASH_ALL | SIGHASH_FORKID | SIGHASH_ANYONECANPAY}, | SIGHASH_ALL | SIGHASH_FORKID | SIGHASH_ANYONECANPAY}, | ||||
{"NONE", SIGHASH_NONE}, | {"NONE", SIGHASH_NONE}, | ||||
{"NONE|ANYONECANPAY", SIGHASH_NONE | SIGHASH_ANYONECANPAY}, | {"NONE|ANYONECANPAY", SIGHASH_NONE | SIGHASH_ANYONECANPAY}, | ||||
{"NONE|FORKID", SIGHASH_NONE | SIGHASH_FORKID}, | {"NONE|FORKID", SIGHASH_NONE | SIGHASH_FORKID}, | ||||
{"NONE|FORKID|ANYONECANPAY", | {"NONE|FORKID|ANYONECANPAY", | ||||
SIGHASH_NONE | SIGHASH_FORKID | SIGHASH_ANYONECANPAY}, | SIGHASH_NONE | SIGHASH_FORKID | SIGHASH_ANYONECANPAY}, | ||||
{"SINGLE", SIGHASH_SINGLE}, | {"SINGLE", SIGHASH_SINGLE}, | ||||
{"SINGLE|ANYONECANPAY", SIGHASH_SINGLE | SIGHASH_ANYONECANPAY}, | {"SINGLE|ANYONECANPAY", SIGHASH_SINGLE | SIGHASH_ANYONECANPAY}, | ||||
{"SINGLE|FORKID", SIGHASH_SINGLE | SIGHASH_FORKID}, | {"SINGLE|FORKID", SIGHASH_SINGLE | SIGHASH_FORKID}, | ||||
{"SINGLE|FORKID|ANYONECANPAY", | {"SINGLE|FORKID|ANYONECANPAY", | ||||
SIGHASH_SINGLE | SIGHASH_FORKID | SIGHASH_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)) { | if (!mapSigHashValues.count(strHashType)) { | ||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param"); | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param"); | ||||
} | } | ||||
sigHashType = SigHashType(mapSigHashValues[strHashType]); | sigHashType = SigHashType(mapSigHashValues[strHashType]); | ||||
if (!sigHashType.hasForkId()) { | if (!sigHashType.hasForkId()) { | ||||
throw JSONRPCError(RPC_INVALID_PARAMETER, | throw JSONRPCError(RPC_INVALID_PARAMETER, | ||||
"Signature must use SIGHASH_FORKID"); | "Signature must use SIGHASH_FORKID"); | ||||
Show All 11 Lines | for (size_t i = 0; i < mtx.vin.size(); i++) { | ||||
CTxIn &txin = mtx.vin[i]; | CTxIn &txin = mtx.vin[i]; | ||||
const Coin &coin = view.AccessCoin(txin.prevout); | const Coin &coin = view.AccessCoin(txin.prevout); | ||||
if (coin.IsSpent()) { | if (coin.IsSpent()) { | ||||
TxInErrorToJSON(txin, vErrors, "Input not found or already spent"); | TxInErrorToJSON(txin, vErrors, "Input not found or already spent"); | ||||
continue; | continue; | ||||
} | } | ||||
const CScript &prevPubKey = coin.GetTxOut().scriptPubKey; | const CScript &prevPubKey = coin.GetTxOut().scriptPubKey; | ||||
const Amount amount = coin.GetTxOut().nValue; | const Amount &amount = coin.GetTxOut().nValue; | ||||
SignatureData sigdata; | SignatureData sigdata; | ||||
// Only sign SIGHASH_SINGLE if there's a corresponding output: | // Only sign SIGHASH_SINGLE if there's a corresponding output: | ||||
if ((sigHashType.getBaseType() != BaseSigHashType::SINGLE) || | if ((sigHashType.getBaseType() != BaseSigHashType::SINGLE) || | ||||
(i < mtx.vout.size())) { | (i < mtx.vout.size())) { | ||||
ProduceSignature(MutableTransactionSignatureCreator( | ProduceSignature(MutableTransactionSignatureCreator( | ||||
&keystore, &mtx, i, amount, sigHashType), | keystore, &mtx, i, amount, sigHashType), | ||||
prevPubKey, sigdata); | prevPubKey, sigdata); | ||||
} | } | ||||
sigdata = CombineSignatures( | sigdata = CombineSignatures( | ||||
prevPubKey, TransactionSignatureChecker(&txConst, i, amount), | prevPubKey, TransactionSignatureChecker(&txConst, i, amount), | ||||
sigdata, DataFromTransaction(mtx, i)); | sigdata, DataFromTransaction(mtx, i)); | ||||
UpdateTransaction(mtx, i, sigdata); | UpdateTransaction(mtx, i, sigdata); | ||||
ScriptError serror = SCRIPT_ERR_OK; | ScriptError serror = SCRIPT_ERR_OK; | ||||
if (!VerifyScript( | if (!VerifyScript( | ||||
txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, | txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, | ||||
TransactionSignatureChecker(&txConst, i, amount), &serror)) { | TransactionSignatureChecker(&txConst, i, amount), &serror)) { | ||||
if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) { | |||||
// Unable to sign input and verification failed (possible | |||||
// attempt to partially sign). | |||||
TxInErrorToJSON(txin, vErrors, "Unable to sign input, invalid " | |||||
"stack size (possibly missing " | |||||
"key)"); | |||||
} else { | |||||
TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); | TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); | ||||
} | } | ||||
} | } | ||||
} | |||||
bool fComplete = vErrors.empty(); | bool fComplete = vErrors.empty(); | ||||
UniValue result(UniValue::VOBJ); | UniValue result(UniValue::VOBJ); | ||||
result.pushKV("hex", EncodeHexTx(CTransaction(mtx))); | result.pushKV("hex", EncodeHexTx(CTransaction(mtx))); | ||||
result.pushKV("complete", fComplete); | result.pushKV("complete", fComplete); | ||||
if (!vErrors.empty()) { | if (!vErrors.empty()) { | ||||
result.pushKV("errors", vErrors); | result.pushKV("errors", vErrors); | ||||
} | } | ||||
return result; | 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\"\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("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" | |||||
"\nDEPRECATED. Sign 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\"\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\"")); | |||||
if (!IsDeprecatedRPCEnabled(gArgs, "signrawtransaction")) { | |||||
throw JSONRPCError( | |||||
RPC_METHOD_DEPRECATED, | |||||
"signrawtransaction is deprecated and will be fully removed in " | |||||
"v0.19. " | |||||
"To use signrawtransaction in v0.18, restart bitcoind with " | |||||
"-deprecatedrpc=signrawtransaction.\n" | |||||
"Projects should transition to using signrawtransactionwithkey and " | |||||
"signrawtransactionwithwallet before upgrading to v0.19"); | |||||
} | |||||
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, | static UniValue sendrawtransaction(const Config &config, | ||||
const JSONRPCRequest &request) { | const JSONRPCRequest &request) { | ||||
if (request.fHelp || request.params.size() < 1 || | if (request.fHelp || request.params.size() < 1 || | ||||
request.params.size() > 2) { | request.params.size() > 2) { | ||||
throw std::runtime_error( | throw std::runtime_error( | ||||
"sendrawtransaction \"hexstring\" ( allowhighfees )\n" | "sendrawtransaction \"hexstring\" ( allowhighfees )\n" | ||||
"\nSubmits raw transaction (serialized, hex-encoded) to local node " | "\nSubmits raw transaction (serialized, hex-encoded) to local node " | ||||
"and network.\n" | "and network.\n" | ||||
▲ Show 20 Lines • Show All 79 Lines • ▼ Show 20 Lines | static UniValue sendrawtransaction(const Config &config, | ||||
CInv inv(MSG_TX, txid); | CInv inv(MSG_TX, txid); | ||||
g_connman->ForEachNode([&inv](CNode *pnode) { pnode->PushInventory(inv); }); | g_connman->ForEachNode([&inv](CNode *pnode) { pnode->PushInventory(inv); }); | ||||
return txid.GetHex(); | return txid.GetHex(); | ||||
} | } | ||||
// clang-format off | // clang-format off | ||||
static const ContextFreeRPCCommand commands[] = { | static const ContextFreeRPCCommand commands[] = { | ||||
// category name actor (function) argNames | // category name actor (function) argNames | ||||
// ------------------- ------------------------ ---------------------- ---------- | // ------------------- ------------------------ ---------------------- ---------- | ||||
{ "rawtransactions", "getrawtransaction", getrawtransaction, {"txid","verbose"} }, | { "rawtransactions", "getrawtransaction", getrawtransaction, {"txid","verbose"} }, | ||||
{ "rawtransactions", "createrawtransaction", createrawtransaction, {"inputs","outputs","locktime"} }, | { "rawtransactions", "createrawtransaction", createrawtransaction, {"inputs","outputs","locktime"} }, | ||||
{ "rawtransactions", "decoderawtransaction", decoderawtransaction, {"hexstring"} }, | { "rawtransactions", "decoderawtransaction", decoderawtransaction, {"hexstring"} }, | ||||
{ "rawtransactions", "decodescript", decodescript, {"hexstring"} }, | { "rawtransactions", "decodescript", decodescript, {"hexstring"} }, | ||||
{ "rawtransactions", "sendrawtransaction", sendrawtransaction, {"hexstring","allowhighfees"} }, | { "rawtransactions", "sendrawtransaction", sendrawtransaction, {"hexstring","allowhighfees"} }, | ||||
{ "rawtransactions", "combinerawtransaction", combinerawtransaction, {"txs"} }, | { "rawtransactions", "combinerawtransaction", combinerawtransaction, {"txs"} }, | ||||
{ "rawtransactions", "signrawtransaction", signrawtransaction, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */ | { "rawtransactions", "signrawtransaction", signrawtransaction, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */ | ||||
{ "rawtransactions", "signrawtransactionwithkey", signrawtransactionwithkey, {"hexstring","privkeys","prevtxs","sighashtype"} }, | |||||
{ "blockchain", "gettxoutproof", gettxoutproof, {"txids", "blockhash"} }, | { "blockchain", "gettxoutproof", gettxoutproof, {"txids", "blockhash"} }, | ||||
{ "blockchain", "verifytxoutproof", verifytxoutproof, {"proof"} }, | { "blockchain", "verifytxoutproof", verifytxoutproof, {"proof"} }, | ||||
}; | }; | ||||
// clang-format on | // clang-format on | ||||
void RegisterRawTransactionRPCCommands(CRPCTable &t) { | void RegisterRawTransactionRPCCommands(CRPCTable &t) { | ||||
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) { | for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) { | ||||
t.appendCommand(commands[vcidx].name, &commands[vcidx]); | t.appendCommand(commands[vcidx].name, &commands[vcidx]); | ||||
} | } | ||||
} | } |