@@ -502,6 +502,92 @@ static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::
502502 vErrorsRet.push_back (entry);
503503}
504504
505+ UniValue combinerawtransaction (const JSONRPCRequest& request)
506+ {
507+ if (request.fHelp || request.params .size () != 1 )
508+ throw std::runtime_error (
509+ " combinerawtransaction [\" hexstring\" ,...]\n "
510+ " \n Combine multiple partially signed transactions into one transaction.\n "
511+ " The combined transaction may be another partially signed transaction or a \n "
512+ " fully signed transaction."
513+
514+ " \n Arguments:\n "
515+ " 1. \" txs\" (string) A json array of hex strings of partially signed transactions\n "
516+ " [\n "
517+ " \" hexstring\" (string) A transaction hash\n "
518+ " ,...\n "
519+ " ]\n "
520+
521+ " \n Result:\n "
522+ " \" hex\" : \" value\" , (string) The hex-encoded raw transaction with signature(s)\n "
523+
524+ " \n Examples:\n "
525+ + HelpExampleCli (" combinerawtransaction" , " [\" myhex1\" , \" myhex2\" , \" myhex3\" ]" )
526+ );
527+
528+
529+ UniValue txs = request.params [0 ].get_array ();
530+ std::vector<CMutableTransaction> txVariants (txs.size ());
531+
532+ for (unsigned int idx = 0 ; idx < txs.size (); idx++) {
533+ if (!DecodeHexTx (txVariants[idx], txs[idx].get_str ())) {
534+ throw JSONRPCError (RPC_DESERIALIZATION_ERROR, strprintf (" TX decode failed for tx %d" , idx));
535+ }
536+ }
537+
538+ if (txVariants.empty ()) {
539+ throw JSONRPCError (RPC_DESERIALIZATION_ERROR, " Missing transactions" );
540+ }
541+
542+ // mergedTx will end up with all the signatures; it
543+ // starts as a clone of the rawtx:
544+ CMutableTransaction mergedTx (txVariants[0 ]);
545+
546+ // Fetch previous transactions (inputs):
547+ CCoinsView viewDummy;
548+ CCoinsViewCache view (&viewDummy);
549+ {
550+ LOCK (cs_main);
551+ LOCK (mempool.cs );
552+ CCoinsViewCache &viewChain = *pcoinsTip;
553+ CCoinsViewMemPool viewMempool (&viewChain, mempool);
554+ view.SetBackend (viewMempool); // temporarily switch cache backend to db+mempool view
555+
556+ for (const CTxIn& txin : mergedTx.vin ) {
557+ view.AccessCoin (txin.prevout ); // Load entries from viewChain into view; can fail.
558+ }
559+
560+ view.SetBackend (viewDummy); // switch back to avoid locking mempool for too long
561+ }
562+
563+ // Use CTransaction for the constant parts of the
564+ // transaction to avoid rehashing.
565+ const CTransaction txConst (mergedTx);
566+ // Sign what we can:
567+ for (unsigned int i = 0 ; i < mergedTx.vin .size (); i++) {
568+ CTxIn& txin = mergedTx.vin [i];
569+ const Coin& coin = view.AccessCoin (txin.prevout );
570+ if (coin.IsSpent ()) {
571+ throw JSONRPCError (RPC_VERIFY_ERROR, " Input not found or already spent" );
572+ }
573+ const CScript& prevPubKey = coin.out .scriptPubKey ;
574+ const CAmount& amount = coin.out .nValue ;
575+
576+ SignatureData sigdata;
577+
578+ // ... and merge in other signatures:
579+ for (const CMutableTransaction& txv : txVariants) {
580+ if (txv.vin .size () > i) {
581+ sigdata = CombineSignatures (prevPubKey, TransactionSignatureChecker (&txConst, i, amount), sigdata, DataFromTransaction (txv, i));
582+ }
583+ }
584+
585+ UpdateTransaction (mergedTx, i, sigdata);
586+ }
587+
588+ return EncodeHexTx (mergedTx);
589+ }
590+
505591UniValue signrawtransaction (const JSONRPCRequest& request)
506592{
507593 CWallet * const pwallet = GetWalletForJSONRPCRequest (request);
@@ -570,30 +656,14 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
570656#endif
571657 RPCTypeCheck (request.params , {UniValue::VSTR, UniValue::VARR, UniValue::VARR, UniValue::VSTR}, true );
572658
573- std::vector<unsigned char > txData (ParseHexV (request.params [0 ], " argument 1" ));
574- CDataStream ssData (txData, SER_NETWORK, PROTOCOL_VERSION);
575- std::vector<CMutableTransaction> txVariants;
576- while (!ssData.empty ()) {
577- try {
578- CMutableTransaction tx;
579- ssData >> tx;
580- txVariants.push_back (tx);
581- } catch (const std::exception&) {
582- throw JSONRPCError (RPC_DESERIALIZATION_ERROR, " TX decode failed" );
583- }
584- }
585-
586- if (txVariants.empty ())
587- throw JSONRPCError (RPC_DESERIALIZATION_ERROR, " Missing transaction" );
588-
589- // mergedTx will end up with all the signatures; it
590- // starts as a clone of the rawtx:
591- CMutableTransaction mergedTx (txVariants[0 ]);
659+ CMutableTransaction mtx;
660+ if (!DecodeHexTx (mtx, request.params [0 ].get_str ()))
661+ throw JSONRPCError (RPC_DESERIALIZATION_ERROR, " TX decode failed" );
592662
593663 // Fetch previous transactions (inputs):
594664 std::map<COutPoint, std::pair<CScript, CAmount>> mapPrevOut; // todo: check why do we have this for regtest..
595665 if (Params ().IsRegTestNet ()) {
596- for (const CTxIn &txbase : mergedTx .vin )
666+ for (const CTxIn &txbase : mtx .vin )
597667 {
598668 CTransactionRef tempTx;
599669 uint256 hashBlock;
@@ -611,7 +681,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
611681 CCoinsViewMemPool viewMempool (&viewChain, mempool);
612682 view.SetBackend (viewMempool); // temporarily switch cache backend to db+mempool view
613683
614- for (const CTxIn& txin : mergedTx .vin ) {
684+ for (const CTxIn& txin : mtx .vin ) {
615685 view.AccessCoin (txin.prevout ); // Load entries from viewChain into view; can fail.
616686 }
617687
@@ -732,10 +802,10 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
732802
733803 // Use CTransaction for the constant parts of the
734804 // transaction to avoid rehashing.
735- const CTransaction txConst (mergedTx );
805+ const CTransaction txConst (mtx );
736806 // Sign what we can:
737- for (unsigned int i = 0 ; i < mergedTx .vin .size (); i++) {
738- CTxIn& txin = mergedTx .vin [i];
807+ for (unsigned int i = 0 ; i < mtx .vin .size (); i++) {
808+ CTxIn& txin = mtx .vin [i];
739809 const Coin& coin = view.AccessCoin (txin.prevout );
740810 if (Params ().IsRegTestNet ()) {
741811 if (mapPrevOut.count (txin.prevout ) == 0 && coin.IsSpent ())
@@ -763,18 +833,13 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
763833 }
764834
765835 SignatureData sigdata;
766- SigVersion sigversion = mergedTx .GetRequiredSigVersion ();
836+ SigVersion sigversion = mtx .GetRequiredSigVersion ();
767837 // Only sign SIGHASH_SINGLE if there's a corresponding output:
768- if (!fHashSingle || (i < mergedTx .vout .size ()))
769- ProduceSignature (MutableTransactionSignatureCreator (&keystore, &mergedTx , i, amount, nHashType),
770- prevPubKey , sigdata, sigversion, fColdStake );
838+ if (!fHashSingle || (i < mtx .vout .size ()))
839+ ProduceSignature (MutableTransactionSignatureCreator (&keystore, &mtx , i, amount, nHashType), prevPubKey, sigdata, sigversion, fColdStake );
840+ sigdata = CombineSignatures (prevPubKey, TransactionSignatureChecker (&txConst, i, amount) , sigdata, DataFromTransaction (mtx, i) );
771841
772- // ... and merge in other signatures:
773- for (const CMutableTransaction& txv : txVariants) {
774- sigdata = CombineSignatures (prevPubKey, TransactionSignatureChecker (&txConst, i, amount), sigdata, DataFromTransaction (txv, i));
775- }
776-
777- UpdateTransaction (mergedTx, i, sigdata);
842+ UpdateTransaction (mtx, i, sigdata);
778843
779844 ScriptError serror = SCRIPT_ERR_OK;
780845 if (!VerifyScript (txin.scriptSig , prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS,
@@ -785,7 +850,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
785850 bool fComplete = vErrors.empty ();
786851
787852 UniValue result (UniValue::VOBJ);
788- result.pushKV (" hex" , EncodeHexTx (mergedTx ));
853+ result.pushKV (" hex" , EncodeHexTx (mtx ));
789854 result.pushKV (" complete" , fComplete );
790855 if (!vErrors.empty ()) {
791856 result.pushKV (" errors" , vErrors);
@@ -895,6 +960,7 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
895960static const CRPCCommand commands[] =
896961{ // category name actor (function) okSafe argNames
897962 // --------------------- ------------------------ ----------------------- ------ --------
963+ { " rawtransactions" , " combinerawtransaction" , &combinerawtransaction, true , {" txs" } },
898964 { " rawtransactions" , " createrawtransaction" , &createrawtransaction, true , {" inputs" ," outputs" ," locktime" } },
899965 { " rawtransactions" , " decoderawtransaction" , &decoderawtransaction, true , {" hexstring" } },
900966 { " rawtransactions" , " decodescript" , &decodescript, true , {" hexstring" } },
0 commit comments