@@ -554,6 +554,93 @@ static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::
554554 vErrorsRet.push_back (entry);
555555}
556556
557+ UniValue combinerawtransaction (const JSONRPCRequest& request)
558+ {
559+
560+ if (request.fHelp || request.params .size () != 1 )
561+ throw std::runtime_error (
562+ " combinerawtransaction [\" hexstring\" ,...]\n "
563+ " \n Combine multiple partially signed transactions into one transaction.\n "
564+ " The combined transaction may be another partially signed transaction or a \n "
565+ " fully signed transaction."
566+
567+ " \n Arguments:\n "
568+ " 1. \" txs\" (string) A json array of hex strings of partially signed transactions\n "
569+ " [\n "
570+ " \" hexstring\" (string) A transaction hash\n "
571+ " ,...\n "
572+ " ]\n "
573+
574+ " \n Result:\n "
575+ " \" hex\" : \" value\" , (string) The hex-encoded raw transaction with signature(s)\n "
576+
577+ " \n Examples:\n "
578+ + HelpExampleCli (" combinerawtransaction" , " [\" myhex1\" , \" myhex2\" , \" myhex3\" ]" )
579+ );
580+
581+
582+ UniValue txs = request.params [0 ].get_array ();
583+ std::vector<CMutableTransaction> txVariants (txs.size ());
584+
585+ for (unsigned int idx = 0 ; idx < txs.size (); idx++) {
586+ if (!DecodeHexTx (txVariants[idx], txs[idx].get_str (), true )) {
587+ throw JSONRPCError (RPC_DESERIALIZATION_ERROR, strprintf (" TX decode failed for tx %d" , idx));
588+ }
589+ }
590+
591+ if (txVariants.empty ()) {
592+ throw JSONRPCError (RPC_DESERIALIZATION_ERROR, " Missing transactions" );
593+ }
594+
595+ // mergedTx will end up with all the signatures; it
596+ // starts as a clone of the rawtx:
597+ CMutableTransaction mergedTx (txVariants[0 ]);
598+
599+ // Fetch previous transactions (inputs):
600+ CCoinsView viewDummy;
601+ CCoinsViewCache view (&viewDummy);
602+ {
603+ LOCK (cs_main);
604+ LOCK (mempool.cs );
605+ CCoinsViewCache &viewChain = *pcoinsTip;
606+ CCoinsViewMemPool viewMempool (&viewChain, mempool);
607+ view.SetBackend (viewMempool); // temporarily switch cache backend to db+mempool view
608+
609+ for (const CTxIn& txin : mergedTx.vin ) {
610+ view.AccessCoin (txin.prevout ); // Load entries from viewChain into view; can fail.
611+ }
612+
613+ view.SetBackend (viewDummy); // switch back to avoid locking mempool for too long
614+ }
615+
616+ // Use CTransaction for the constant parts of the
617+ // transaction to avoid rehashing.
618+ const CTransaction txConst (mergedTx);
619+ // Sign what we can:
620+ for (unsigned int i = 0 ; i < mergedTx.vin .size (); i++) {
621+ CTxIn& txin = mergedTx.vin [i];
622+ const Coin& coin = view.AccessCoin (txin.prevout );
623+ if (coin.IsSpent ()) {
624+ throw JSONRPCError (RPC_VERIFY_ERROR, " Input not found or already spent" );
625+ }
626+ const CScript& prevPubKey = coin.out .scriptPubKey ;
627+ const CAmount& amount = coin.out .nValue ;
628+
629+ SignatureData sigdata;
630+
631+ // ... and merge in other signatures:
632+ for (const CMutableTransaction& txv : txVariants) {
633+ if (txv.vin .size () > i) {
634+ sigdata = CombineSignatures (prevPubKey, TransactionSignatureChecker (&txConst, i, amount), sigdata, DataFromTransaction (txv, i));
635+ }
636+ }
637+
638+ UpdateTransaction (mergedTx, i, sigdata);
639+ }
640+
641+ return EncodeHexTx (mergedTx);
642+ }
643+
557644UniValue signrawtransaction (const JSONRPCRequest& request)
558645{
559646#ifdef ENABLE_WALLET
@@ -626,26 +713,9 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
626713#endif
627714 RPCTypeCheck (request.params , {UniValue::VSTR, UniValue::VARR, UniValue::VARR, UniValue::VSTR}, true );
628715
629- std::vector<unsigned char > txData (ParseHexV (request.params [0 ], " argument 1" ));
630- CDataStream ssData (txData, SER_NETWORK, PROTOCOL_VERSION);
631- std::vector<CMutableTransaction> txVariants;
632- while (!ssData.empty ()) {
633- try {
634- CMutableTransaction tx;
635- ssData >> tx;
636- txVariants.push_back (tx);
637- }
638- catch (const std::exception&) {
639- throw JSONRPCError (RPC_DESERIALIZATION_ERROR, " TX decode failed" );
640- }
641- }
642-
643- if (txVariants.empty ())
644- throw JSONRPCError (RPC_DESERIALIZATION_ERROR, " Missing transaction" );
645-
646- // mergedTx will end up with all the signatures; it
647- // starts as a clone of the rawtx:
648- CMutableTransaction mergedTx (txVariants[0 ]);
716+ CMutableTransaction mtx;
717+ if (!DecodeHexTx (mtx, request.params [0 ].get_str (), true ))
718+ throw JSONRPCError (RPC_DESERIALIZATION_ERROR, " TX decode failed" );
649719
650720 // Fetch previous transactions (inputs):
651721 CCoinsView viewDummy;
@@ -656,7 +726,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
656726 CCoinsViewMemPool viewMempool (&viewChain, mempool);
657727 view.SetBackend (viewMempool); // temporarily switch cache backend to db+mempool view
658728
659- for (const CTxIn& txin : mergedTx .vin ) {
729+ for (const CTxIn& txin : mtx .vin ) {
660730 view.AccessCoin (txin.prevout ); // Load entries from viewChain into view; can fail.
661731 }
662732
@@ -781,10 +851,10 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
781851
782852 // Use CTransaction for the constant parts of the
783853 // transaction to avoid rehashing.
784- const CTransaction txConst (mergedTx );
854+ const CTransaction txConst (mtx );
785855 // Sign what we can:
786- for (unsigned int i = 0 ; i < mergedTx .vin .size (); i++) {
787- CTxIn& txin = mergedTx .vin [i];
856+ for (unsigned int i = 0 ; i < mtx .vin .size (); i++) {
857+ CTxIn& txin = mtx .vin [i];
788858 const Coin& coin = view.AccessCoin (txin.prevout );
789859 if (coin.IsSpent ()) {
790860 TxInErrorToJSON (txin, vErrors, " Input not found or already spent" );
@@ -795,17 +865,11 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
795865
796866 SignatureData sigdata;
797867 // Only sign SIGHASH_SINGLE if there's a corresponding output:
798- if (!fHashSingle || (i < mergedTx.vout .size ()))
799- ProduceSignature (MutableTransactionSignatureCreator (&keystore, &mergedTx, i, amount, nHashType), prevPubKey, sigdata);
868+ if (!fHashSingle || (i < mtx.vout .size ()))
869+ ProduceSignature (MutableTransactionSignatureCreator (&keystore, &mtx, i, amount, nHashType), prevPubKey, sigdata);
870+ sigdata = CombineSignatures (prevPubKey, TransactionSignatureChecker (&txConst, i, amount), sigdata, DataFromTransaction (mtx, i));
800871
801- // ... and merge in other signatures:
802- for (const CMutableTransaction& txv : txVariants) {
803- if (txv.vin .size () > i) {
804- sigdata = CombineSignatures (prevPubKey, TransactionSignatureChecker (&txConst, i, amount), sigdata, DataFromTransaction (txv, i));
805- }
806- }
807-
808- UpdateTransaction (mergedTx, i, sigdata);
872+ UpdateTransaction (mtx, i, sigdata);
809873
810874 ScriptError serror = SCRIPT_ERR_OK;
811875 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)
815879 bool fComplete = vErrors.empty ();
816880
817881 UniValue result (UniValue::VOBJ);
818- result.push_back (Pair (" hex" , EncodeHexTx (mergedTx )));
882+ result.push_back (Pair (" hex" , EncodeHexTx (mtx )));
819883 result.push_back (Pair (" complete" , fComplete ));
820884 if (!vErrors.empty ()) {
821885 result.push_back (Pair (" errors" , vErrors));
@@ -905,6 +969,7 @@ static const CRPCCommand commands[] =
905969 { " rawtransactions" , " decoderawtransaction" , &decoderawtransaction, true , {" hexstring" } },
906970 { " rawtransactions" , " decodescript" , &decodescript, true , {" hexstring" } },
907971 { " rawtransactions" , " sendrawtransaction" , &sendrawtransaction, false , {" hexstring" ," allowhighfees" } },
972+ { " rawtransactions" , " combinerawtransaction" , &combinerawtransaction, true , {" txs" } },
908973 { " rawtransactions" , " signrawtransaction" , &signrawtransaction, false , {" hexstring" ," prevtxs" ," privkeys" ," sighashtype" } }, /* uses wallet if enabled */
909974
910975 { " blockchain" , " gettxoutproof" , &gettxoutproof, true , {" txids" , " blockhash" } },
0 commit comments