aboutsummaryrefslogtreecommitdiff
path: root/src/rpc/rawtransaction.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc/rawtransaction.cpp')
-rw-r--r--src/rpc/rawtransaction.cpp170
1 files changed, 116 insertions, 54 deletions
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index b878624df8..934576a391 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -41,7 +41,7 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
// Blockchain contextual information (confirmations and blocktime) is not
// available to code in bitcoin-common, so we query them here and push the
// data into the returned UniValue.
- TxToUniv(tx, uint256(), entry);
+ TxToUniv(tx, uint256(), entry, true, RPCSerializationFlags());
if (!hashBlock.IsNull()) {
entry.push_back(Pair("blockhash", hashBlock.GetHex()));
@@ -137,7 +137,7 @@ UniValue getrawtransaction(const JSONRPCRequest& request)
// Accept either a bool (true) or a num (>=1) to indicate verbose output.
bool fVerbose = false;
- if (request.params.size() > 1) {
+ if (!request.params[1].isNull()) {
if (request.params[1].isNum()) {
if (request.params[1].get_int() != 0) {
fVerbose = true;
@@ -160,13 +160,10 @@ UniValue getrawtransaction(const JSONRPCRequest& request)
: "No such mempool transaction. Use -txindex to enable blockchain transaction queries") +
". Use gettransaction for wallet transactions.");
- std::string strHex = EncodeHexTx(*tx, RPCSerializationFlags());
-
if (!fVerbose)
- return strHex;
+ return EncodeHexTx(*tx, RPCSerializationFlags());
UniValue result(UniValue::VOBJ);
- result.push_back(Pair("hex", strHex));
TxToJSON(*tx, hashBlock, result);
return result;
}
@@ -208,10 +205,10 @@ UniValue gettxoutproof(const JSONRPCRequest& request)
LOCK(cs_main);
- CBlockIndex* pblockindex = NULL;
+ CBlockIndex* pblockindex = nullptr;
uint256 hashBlock;
- if (request.params.size() > 1)
+ if (!request.params[1].isNull())
{
hashBlock = uint256S(request.params[1].get_str());
if (!mapBlockIndex.count(hashBlock))
@@ -228,7 +225,7 @@ UniValue gettxoutproof(const JSONRPCRequest& request)
}
}
- if (pblockindex == NULL)
+ if (pblockindex == nullptr)
{
CTransactionRef tx;
if (!GetTransaction(oneTxid, tx, Params().GetConsensus(), hashBlock, false) || hashBlock.IsNull())
@@ -339,14 +336,14 @@ UniValue createrawtransaction(const JSONRPCRequest& request)
CMutableTransaction rawTx;
- if (request.params.size() > 2 && !request.params[2].isNull()) {
+ if (!request.params[2].isNull()) {
int64_t nLockTime = request.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;
}
- bool rbfOptIn = request.params.size() > 3 ? request.params[3].isTrue() : false;
+ bool rbfOptIn = request.params[3].isTrue();
for (unsigned int idx = 0; idx < inputs.size(); idx++) {
const UniValue& input = inputs[idx];
@@ -412,7 +409,7 @@ UniValue createrawtransaction(const JSONRPCRequest& request)
}
}
- if (request.params.size() > 3 && rbfOptIn != SignalsOptInRBF(rawTx)) {
+ if (!request.params[3].isNull() && rbfOptIn != SignalsOptInRBF(rawTx)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option");
}
@@ -483,7 +480,7 @@ UniValue decoderawtransaction(const JSONRPCRequest& request)
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
UniValue result(UniValue::VOBJ);
- TxToUniv(CTransaction(std::move(mtx)), uint256(), result);
+ TxToUniv(CTransaction(std::move(mtx)), uint256(), result, false);
return result;
}
@@ -554,6 +551,93 @@ static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::
vErrorsRet.push_back(entry);
}
+UniValue combinerawtransaction(const JSONRPCRequest& request)
+{
+
+ if (request.fHelp || request.params.size() != 1)
+ throw std::runtime_error(
+ "combinerawtransaction [\"hexstring\",...]\n"
+ "\nCombine multiple partially signed transactions into one transaction.\n"
+ "The combined transaction may be another partially signed transaction or a \n"
+ "fully signed transaction."
+
+ "\nArguments:\n"
+ "1. \"txs\" (string) A json array of hex strings of partially signed transactions\n"
+ " [\n"
+ " \"hexstring\" (string) A transaction hash\n"
+ " ,...\n"
+ " ]\n"
+
+ "\nResult:\n"
+ "\"hex\" (string) The hex-encoded raw transaction with signature(s)\n"
+
+ "\nExamples:\n"
+ + HelpExampleCli("combinerawtransaction", "[\"myhex1\", \"myhex2\", \"myhex3\"]")
+ );
+
+
+ UniValue txs = request.params[0].get_array();
+ std::vector<CMutableTransaction> txVariants(txs.size());
+
+ for (unsigned int idx = 0; idx < txs.size(); idx++) {
+ if (!DecodeHexTx(txVariants[idx], txs[idx].get_str(), true)) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed for tx %d", idx));
+ }
+ }
+
+ if (txVariants.empty()) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transactions");
+ }
+
+ // mergedTx will end up with all the signatures; it
+ // starts as a clone of the rawtx:
+ CMutableTransaction mergedTx(txVariants[0]);
+
+ // Fetch previous transactions (inputs):
+ CCoinsView viewDummy;
+ CCoinsViewCache view(&viewDummy);
+ {
+ LOCK(cs_main);
+ LOCK(mempool.cs);
+ CCoinsViewCache &viewChain = *pcoinsTip;
+ CCoinsViewMemPool viewMempool(&viewChain, mempool);
+ view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
+
+ for (const CTxIn& txin : mergedTx.vin) {
+ view.AccessCoin(txin.prevout); // Load entries from viewChain into view; can fail.
+ }
+
+ view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long
+ }
+
+ // Use CTransaction for the constant parts of the
+ // transaction to avoid rehashing.
+ const CTransaction txConst(mergedTx);
+ // Sign what we can:
+ for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
+ CTxIn& txin = mergedTx.vin[i];
+ const Coin& coin = view.AccessCoin(txin.prevout);
+ if (coin.IsSpent()) {
+ throw JSONRPCError(RPC_VERIFY_ERROR, "Input not found or already spent");
+ }
+ const CScript& prevPubKey = coin.out.scriptPubKey;
+ const CAmount& amount = coin.out.nValue;
+
+ SignatureData sigdata;
+
+ // ... and merge in other signatures:
+ for (const CMutableTransaction& txv : txVariants) {
+ if (txv.vin.size() > i) {
+ sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(txv, i));
+ }
+ }
+
+ UpdateTransaction(mergedTx, i, sigdata);
+ }
+
+ return EncodeHexTx(mergedTx);
+}
+
UniValue signrawtransaction(const JSONRPCRequest& request)
{
#ifdef ENABLE_WALLET
@@ -620,32 +704,15 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
);
#ifdef ENABLE_WALLET
- LOCK2(cs_main, pwallet ? &pwallet->cs_wallet : NULL);
+ 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);
- std::vector<unsigned char> txData(ParseHexV(request.params[0], "argument 1"));
- CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
- std::vector<CMutableTransaction> txVariants;
- while (!ssData.empty()) {
- try {
- CMutableTransaction tx;
- ssData >> tx;
- txVariants.push_back(tx);
- }
- catch (const std::exception&) {
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
- }
- }
-
- if (txVariants.empty())
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transaction");
-
- // mergedTx will end up with all the signatures; it
- // starts as a clone of the rawtx:
- CMutableTransaction mergedTx(txVariants[0]);
+ CMutableTransaction mtx;
+ if (!DecodeHexTx(mtx, request.params[0].get_str(), true))
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
// Fetch previous transactions (inputs):
CCoinsView viewDummy;
@@ -656,7 +723,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
CCoinsViewMemPool viewMempool(&viewChain, mempool);
view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
- for (const CTxIn& txin : mergedTx.vin) {
+ for (const CTxIn& txin : mtx.vin) {
view.AccessCoin(txin.prevout); // Load entries from viewChain into view; can fail.
}
@@ -665,7 +732,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
bool fGivenKeys = false;
CBasicKeyStore tempKeystore;
- if (request.params.size() > 2 && !request.params[2].isNull()) {
+ if (!request.params[2].isNull()) {
fGivenKeys = true;
UniValue keys = request.params[2].get_array();
for (unsigned int idx = 0; idx < keys.size(); idx++) {
@@ -687,7 +754,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
#endif
// Add previous txouts given in the RPC call:
- if (request.params.size() > 1 && !request.params[1].isNull()) {
+ if (!request.params[1].isNull()) {
UniValue prevTxs = request.params[1].get_array();
for (unsigned int idx = 0; idx < prevTxs.size(); idx++) {
const UniValue& p = prevTxs[idx];
@@ -758,7 +825,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
#endif
int nHashType = SIGHASH_ALL;
- if (request.params.size() > 3 && !request.params[3].isNull()) {
+ if (!request.params[3].isNull()) {
static std::map<std::string, int> mapSigHashValues = {
{std::string("ALL"), int(SIGHASH_ALL)},
{std::string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)},
@@ -781,10 +848,10 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
// Use CTransaction for the constant parts of the
// transaction to avoid rehashing.
- const CTransaction txConst(mergedTx);
+ const CTransaction txConst(mtx);
// Sign what we can:
- for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
- CTxIn& txin = mergedTx.vin[i];
+ for (unsigned int i = 0; i < mtx.vin.size(); i++) {
+ CTxIn& txin = mtx.vin[i];
const Coin& coin = view.AccessCoin(txin.prevout);
if (coin.IsSpent()) {
TxInErrorToJSON(txin, vErrors, "Input not found or already spent");
@@ -795,17 +862,11 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
SignatureData sigdata;
// Only sign SIGHASH_SINGLE if there's a corresponding output:
- if (!fHashSingle || (i < mergedTx.vout.size()))
- ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType), prevPubKey, sigdata);
+ if (!fHashSingle || (i < mtx.vout.size()))
+ ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mtx, i, amount, nHashType), prevPubKey, sigdata);
+ sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(mtx, i));
- // ... and merge in other signatures:
- for (const CMutableTransaction& txv : txVariants) {
- if (txv.vin.size() > i) {
- sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(txv, i));
- }
- }
-
- UpdateTransaction(mergedTx, i, sigdata);
+ UpdateTransaction(mtx, i, sigdata);
ScriptError serror = SCRIPT_ERR_OK;
if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) {
@@ -815,7 +876,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
bool fComplete = vErrors.empty();
UniValue result(UniValue::VOBJ);
- result.push_back(Pair("hex", EncodeHexTx(mergedTx)));
+ result.push_back(Pair("hex", EncodeHexTx(mtx)));
result.push_back(Pair("complete", fComplete));
if (!vErrors.empty()) {
result.push_back(Pair("errors", vErrors));
@@ -858,7 +919,7 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
const uint256& hashTx = tx->GetHash();
CAmount nMaxRawTxFee = maxTxFee;
- if (request.params.size() > 1 && request.params[1].get_bool())
+ if (!request.params[1].isNull() && request.params[1].get_bool())
nMaxRawTxFee = 0;
CCoinsViewCache &view = *pcoinsTip;
@@ -873,7 +934,7 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
CValidationState state;
bool fMissingInputs;
bool fLimitFree = true;
- if (!AcceptToMemoryPool(mempool, state, std::move(tx), fLimitFree, &fMissingInputs, NULL, false, nMaxRawTxFee)) {
+ if (!AcceptToMemoryPool(mempool, state, std::move(tx), fLimitFree, &fMissingInputs, nullptr, false, nMaxRawTxFee)) {
if (state.IsInvalid()) {
throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason()));
} else {
@@ -905,6 +966,7 @@ static const CRPCCommand commands[] =
{ "rawtransactions", "decoderawtransaction", &decoderawtransaction, true, {"hexstring"} },
{ "rawtransactions", "decodescript", &decodescript, true, {"hexstring"} },
{ "rawtransactions", "sendrawtransaction", &sendrawtransaction, false, {"hexstring","allowhighfees"} },
+ { "rawtransactions", "combinerawtransaction", &combinerawtransaction, true, {"txs"} },
{ "rawtransactions", "signrawtransaction", &signrawtransaction, false, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */
{ "blockchain", "gettxoutproof", &gettxoutproof, true, {"txids", "blockhash"} },