|
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