|
20 | 20 | #include <policy/rbf.h>
|
21 | 21 | #include <primitives/transaction.h>
|
22 | 22 | #include <psbt.h>
|
23 |
| -#include <rpc/rawtransaction.h> |
| 23 | +#include <rpc/rawtransaction_util.h> |
24 | 24 | #include <rpc/server.h>
|
25 | 25 | #include <rpc/util.h>
|
26 | 26 | #include <script/script.h>
|
@@ -359,119 +359,6 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request)
|
359 | 359 | return res;
|
360 | 360 | }
|
361 | 361 |
|
362 |
| -CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf) |
363 |
| -{ |
364 |
| - if (inputs_in.isNull() || outputs_in.isNull()) |
365 |
| - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null"); |
366 |
| - |
367 |
| - UniValue inputs = inputs_in.get_array(); |
368 |
| - const bool outputs_is_obj = outputs_in.isObject(); |
369 |
| - UniValue outputs = outputs_is_obj ? outputs_in.get_obj() : outputs_in.get_array(); |
370 |
| - |
371 |
| - CMutableTransaction rawTx; |
372 |
| - |
373 |
| - if (!locktime.isNull()) { |
374 |
| - int64_t nLockTime = locktime.get_int64(); |
375 |
| - if (nLockTime < 0 || nLockTime > LOCKTIME_MAX) |
376 |
| - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range"); |
377 |
| - rawTx.nLockTime = nLockTime; |
378 |
| - } |
379 |
| - |
380 |
| - bool rbfOptIn = rbf.isTrue(); |
381 |
| - |
382 |
| - for (unsigned int idx = 0; idx < inputs.size(); idx++) { |
383 |
| - const UniValue& input = inputs[idx]; |
384 |
| - const UniValue& o = input.get_obj(); |
385 |
| - |
386 |
| - uint256 txid = ParseHashO(o, "txid"); |
387 |
| - |
388 |
| - const UniValue& vout_v = find_value(o, "vout"); |
389 |
| - if (!vout_v.isNum()) |
390 |
| - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key"); |
391 |
| - int nOutput = vout_v.get_int(); |
392 |
| - if (nOutput < 0) |
393 |
| - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); |
394 |
| - |
395 |
| - uint32_t nSequence; |
396 |
| - if (rbfOptIn) { |
397 |
| - nSequence = MAX_BIP125_RBF_SEQUENCE; /* CTxIn::SEQUENCE_FINAL - 2 */ |
398 |
| - } else if (rawTx.nLockTime) { |
399 |
| - nSequence = CTxIn::SEQUENCE_FINAL - 1; |
400 |
| - } else { |
401 |
| - nSequence = CTxIn::SEQUENCE_FINAL; |
402 |
| - } |
403 |
| - |
404 |
| - // set the sequence number if passed in the parameters object |
405 |
| - const UniValue& sequenceObj = find_value(o, "sequence"); |
406 |
| - if (sequenceObj.isNum()) { |
407 |
| - int64_t seqNr64 = sequenceObj.get_int64(); |
408 |
| - if (seqNr64 < 0 || seqNr64 > CTxIn::SEQUENCE_FINAL) { |
409 |
| - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range"); |
410 |
| - } else { |
411 |
| - nSequence = (uint32_t)seqNr64; |
412 |
| - } |
413 |
| - } |
414 |
| - |
415 |
| - CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence); |
416 |
| - |
417 |
| - rawTx.vin.push_back(in); |
418 |
| - } |
419 |
| - |
420 |
| - if (!outputs_is_obj) { |
421 |
| - // Translate array of key-value pairs into dict |
422 |
| - UniValue outputs_dict = UniValue(UniValue::VOBJ); |
423 |
| - for (size_t i = 0; i < outputs.size(); ++i) { |
424 |
| - const UniValue& output = outputs[i]; |
425 |
| - if (!output.isObject()) { |
426 |
| - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair not an object as expected"); |
427 |
| - } |
428 |
| - if (output.size() != 1) { |
429 |
| - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair must contain exactly one key"); |
430 |
| - } |
431 |
| - outputs_dict.pushKVs(output); |
432 |
| - } |
433 |
| - outputs = std::move(outputs_dict); |
434 |
| - } |
435 |
| - |
436 |
| - // Duplicate checking |
437 |
| - std::set<CTxDestination> destinations; |
438 |
| - bool has_data{false}; |
439 |
| - |
440 |
| - for (const std::string& name_ : outputs.getKeys()) { |
441 |
| - if (name_ == "data") { |
442 |
| - if (has_data) { |
443 |
| - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, duplicate key: data"); |
444 |
| - } |
445 |
| - has_data = true; |
446 |
| - std::vector<unsigned char> data = ParseHexV(outputs[name_].getValStr(), "Data"); |
447 |
| - |
448 |
| - CTxOut out(0, CScript() << OP_RETURN << data); |
449 |
| - rawTx.vout.push_back(out); |
450 |
| - } else { |
451 |
| - CTxDestination destination = DecodeDestination(name_); |
452 |
| - if (!IsValidDestination(destination)) { |
453 |
| - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + name_); |
454 |
| - } |
455 |
| - |
456 |
| - if (!destinations.insert(destination).second) { |
457 |
| - throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_); |
458 |
| - } |
459 |
| - |
460 |
| - CScript scriptPubKey = GetScriptForDestination(destination); |
461 |
| - CAmount nAmount = AmountFromValue(outputs[name_]); |
462 |
| - |
463 |
| - CTxOut out(nAmount, scriptPubKey); |
464 |
| - rawTx.vout.push_back(out); |
465 |
| - } |
466 |
| - } |
467 |
| - |
468 |
| - if (!rbf.isNull() && rawTx.vin.size() > 0 && rbfOptIn != SignalsOptInRBF(CTransaction(rawTx))) { |
469 |
| - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option"); |
470 |
| - } |
471 |
| - |
472 |
| - return rawTx; |
473 |
| -} |
474 |
| - |
475 | 362 | static UniValue createrawtransaction(const JSONRPCRequest& request)
|
476 | 363 | {
|
477 | 364 | if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) {
|
@@ -717,23 +604,6 @@ static UniValue decodescript(const JSONRPCRequest& request)
|
717 | 604 | return r;
|
718 | 605 | }
|
719 | 606 |
|
720 |
| -/** Pushes a JSON object for script verification or signing errors to vErrorsRet. */ |
721 |
| -static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::string& strMessage) |
722 |
| -{ |
723 |
| - UniValue entry(UniValue::VOBJ); |
724 |
| - entry.pushKV("txid", txin.prevout.hash.ToString()); |
725 |
| - entry.pushKV("vout", (uint64_t)txin.prevout.n); |
726 |
| - UniValue witness(UniValue::VARR); |
727 |
| - for (unsigned int i = 0; i < txin.scriptWitness.stack.size(); i++) { |
728 |
| - witness.push_back(HexStr(txin.scriptWitness.stack[i].begin(), txin.scriptWitness.stack[i].end())); |
729 |
| - } |
730 |
| - entry.pushKV("witness", witness); |
731 |
| - entry.pushKV("scriptSig", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())); |
732 |
| - entry.pushKV("sequence", (uint64_t)txin.nSequence); |
733 |
| - entry.pushKV("error", strMessage); |
734 |
| - vErrorsRet.push_back(entry); |
735 |
| -} |
736 |
| - |
737 | 607 | static UniValue combinerawtransaction(const JSONRPCRequest& request)
|
738 | 608 | {
|
739 | 609 | if (request.fHelp || request.params.size() != 1)
|
@@ -818,152 +688,6 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request)
|
818 | 688 | return EncodeHexTx(CTransaction(mergedTx));
|
819 | 689 | }
|
820 | 690 |
|
821 |
| -// TODO(https://github.com/bitcoin/bitcoin/pull/10973#discussion_r267084237): |
822 |
| -// This function is called from both wallet and node rpcs |
823 |
| -// (signrawtransactionwithwallet and signrawtransactionwithkey). It should be |
824 |
| -// moved to a util file so wallet code doesn't need to link against node code. |
825 |
| -// Also the dependency on interfaces::Chain should be removed, so |
826 |
| -// signrawtransactionwithkey doesn't need access to a Chain instance. |
827 |
| -UniValue SignTransaction(interfaces::Chain& chain, CMutableTransaction& mtx, const UniValue& prevTxsUnival, CBasicKeyStore *keystore, bool is_temp_keystore, const UniValue& hashType) |
828 |
| -{ |
829 |
| - // Fetch previous transactions (inputs): |
830 |
| - std::map<COutPoint, Coin> coins; |
831 |
| - for (const CTxIn& txin : mtx.vin) { |
832 |
| - coins[txin.prevout]; // Create empty map entry keyed by prevout. |
833 |
| - } |
834 |
| - chain.findCoins(coins); |
835 |
| - |
836 |
| - // Add previous txouts given in the RPC call: |
837 |
| - if (!prevTxsUnival.isNull()) { |
838 |
| - UniValue prevTxs = prevTxsUnival.get_array(); |
839 |
| - for (unsigned int idx = 0; idx < prevTxs.size(); ++idx) { |
840 |
| - const UniValue& p = prevTxs[idx]; |
841 |
| - if (!p.isObject()) { |
842 |
| - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}"); |
843 |
| - } |
844 |
| - |
845 |
| - UniValue prevOut = p.get_obj(); |
846 |
| - |
847 |
| - RPCTypeCheckObj(prevOut, |
848 |
| - { |
849 |
| - {"txid", UniValueType(UniValue::VSTR)}, |
850 |
| - {"vout", UniValueType(UniValue::VNUM)}, |
851 |
| - {"scriptPubKey", UniValueType(UniValue::VSTR)}, |
852 |
| - }); |
853 |
| - |
854 |
| - uint256 txid = ParseHashO(prevOut, "txid"); |
855 |
| - |
856 |
| - int nOut = find_value(prevOut, "vout").get_int(); |
857 |
| - if (nOut < 0) { |
858 |
| - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive"); |
859 |
| - } |
860 |
| - |
861 |
| - COutPoint out(txid, nOut); |
862 |
| - std::vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey")); |
863 |
| - CScript scriptPubKey(pkData.begin(), pkData.end()); |
864 |
| - |
865 |
| - { |
866 |
| - auto coin = coins.find(out); |
867 |
| - if (coin != coins.end() && !coin->second.IsSpent() && coin->second.out.scriptPubKey != scriptPubKey) { |
868 |
| - std::string err("Previous output scriptPubKey mismatch:\n"); |
869 |
| - err = err + ScriptToAsmStr(coin->second.out.scriptPubKey) + "\nvs:\n"+ |
870 |
| - ScriptToAsmStr(scriptPubKey); |
871 |
| - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err); |
872 |
| - } |
873 |
| - Coin newcoin; |
874 |
| - newcoin.out.scriptPubKey = scriptPubKey; |
875 |
| - newcoin.out.nValue = MAX_MONEY; |
876 |
| - if (prevOut.exists("amount")) { |
877 |
| - newcoin.out.nValue = AmountFromValue(find_value(prevOut, "amount")); |
878 |
| - } |
879 |
| - newcoin.nHeight = 1; |
880 |
| - coins[out] = std::move(newcoin); |
881 |
| - } |
882 |
| - |
883 |
| - // if redeemScript and private keys were given, add redeemScript to the keystore so it can be signed |
884 |
| - if (is_temp_keystore && (scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash())) { |
885 |
| - RPCTypeCheckObj(prevOut, |
886 |
| - { |
887 |
| - {"redeemScript", UniValueType(UniValue::VSTR)}, |
888 |
| - {"witnessScript", UniValueType(UniValue::VSTR)}, |
889 |
| - }, true); |
890 |
| - UniValue rs = find_value(prevOut, "redeemScript"); |
891 |
| - if (!rs.isNull()) { |
892 |
| - std::vector<unsigned char> rsData(ParseHexV(rs, "redeemScript")); |
893 |
| - CScript redeemScript(rsData.begin(), rsData.end()); |
894 |
| - keystore->AddCScript(redeemScript); |
895 |
| - // Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH). |
896 |
| - // This is only for compatibility, it is encouraged to use the explicit witnessScript field instead. |
897 |
| - keystore->AddCScript(GetScriptForWitness(redeemScript)); |
898 |
| - } |
899 |
| - UniValue ws = find_value(prevOut, "witnessScript"); |
900 |
| - if (!ws.isNull()) { |
901 |
| - std::vector<unsigned char> wsData(ParseHexV(ws, "witnessScript")); |
902 |
| - CScript witnessScript(wsData.begin(), wsData.end()); |
903 |
| - keystore->AddCScript(witnessScript); |
904 |
| - // Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH). |
905 |
| - keystore->AddCScript(GetScriptForWitness(witnessScript)); |
906 |
| - } |
907 |
| - } |
908 |
| - } |
909 |
| - } |
910 |
| - |
911 |
| - int nHashType = ParseSighashString(hashType); |
912 |
| - |
913 |
| - bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE); |
914 |
| - |
915 |
| - // Script verification errors |
916 |
| - UniValue vErrors(UniValue::VARR); |
917 |
| - |
918 |
| - // Use CTransaction for the constant parts of the |
919 |
| - // transaction to avoid rehashing. |
920 |
| - const CTransaction txConst(mtx); |
921 |
| - // Sign what we can: |
922 |
| - for (unsigned int i = 0; i < mtx.vin.size(); i++) { |
923 |
| - CTxIn& txin = mtx.vin[i]; |
924 |
| - auto coin = coins.find(txin.prevout); |
925 |
| - if (coin == coins.end() || coin->second.IsSpent()) { |
926 |
| - TxInErrorToJSON(txin, vErrors, "Input not found or already spent"); |
927 |
| - continue; |
928 |
| - } |
929 |
| - const CScript& prevPubKey = coin->second.out.scriptPubKey; |
930 |
| - const CAmount& amount = coin->second.out.nValue; |
931 |
| - |
932 |
| - SignatureData sigdata = DataFromTransaction(mtx, i, coin->second.out); |
933 |
| - // Only sign SIGHASH_SINGLE if there's a corresponding output: |
934 |
| - if (!fHashSingle || (i < mtx.vout.size())) { |
935 |
| - ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata); |
936 |
| - } |
937 |
| - |
938 |
| - UpdateInput(txin, sigdata); |
939 |
| - |
940 |
| - // amount must be specified for valid segwit signature |
941 |
| - if (amount == MAX_MONEY && !txin.scriptWitness.IsNull()) { |
942 |
| - throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing amount for %s", coin->second.out.ToString())); |
943 |
| - } |
944 |
| - |
945 |
| - ScriptError serror = SCRIPT_ERR_OK; |
946 |
| - if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) { |
947 |
| - if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) { |
948 |
| - // Unable to sign input and verification failed (possible attempt to partially sign). |
949 |
| - TxInErrorToJSON(txin, vErrors, "Unable to sign input, invalid stack size (possibly missing key)"); |
950 |
| - } else { |
951 |
| - TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); |
952 |
| - } |
953 |
| - } |
954 |
| - } |
955 |
| - bool fComplete = vErrors.empty(); |
956 |
| - |
957 |
| - UniValue result(UniValue::VOBJ); |
958 |
| - result.pushKV("hex", EncodeHexTx(CTransaction(mtx))); |
959 |
| - result.pushKV("complete", fComplete); |
960 |
| - if (!vErrors.empty()) { |
961 |
| - result.pushKV("errors", vErrors); |
962 |
| - } |
963 |
| - |
964 |
| - return result; |
965 |
| -} |
966 |
| - |
967 | 691 | static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
|
968 | 692 | {
|
969 | 693 | if (request.fHelp || request.params.size() < 2 || request.params.size() > 4)
|
|
0 commit comments