@@ -554,6 +554,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 "
+ " \n Combine multiple partially signed transactions into one transaction.\n "
+ " The combined transaction may be another partially signed transaction or a \n "
+ " fully signed transaction."
+
+ " \n Arguments:\n "
+ " 1. \" txs\" (string) A json array of hex strings of partially signed transactions\n "
+ " [\n "
+ " \" hexstring\" (string) A transaction hash\n "
+ " ,...\n "
+ " ]\n "
+
+ " \n Result:\n "
+ " \" hex\" : \" value\" , (string) The hex-encoded raw transaction with signature(s)\n "
+
+ " \n Examples:\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
@@ -626,26 +713,9 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
#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 +726,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.
}
@@ -781,10 +851,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 +865,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 +879,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));
@@ -905,6 +969,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" } },