aboutsummaryrefslogtreecommitdiff
path: root/src/rpc
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc')
-rw-r--r--src/rpc/client.cpp1
-rw-r--r--src/rpc/mining.cpp28
-rw-r--r--src/rpc/misc.cpp45
-rw-r--r--src/rpc/rawtransaction.cpp360
4 files changed, 413 insertions, 21 deletions
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index c5694e6d55..1cdc9f87a7 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -112,6 +112,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "createpsbt", 2, "locktime" },
{ "createpsbt", 3, "replaceable" },
{ "combinepsbt", 0, "txs"},
+ { "joinpsbts", 0, "txs"},
{ "finalizepsbt", 1, "extract"},
{ "converttopsbt", 1, "permitsigdata"},
{ "converttopsbt", 2, "iswitness"},
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 66a3b1048e..6625a03bbd 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -187,36 +187,36 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
static UniValue getmininginfo(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 0)
+ if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
RPCHelpMan{"getmininginfo",
"\nReturns a json object containing mining-related information.",
{},
RPCResult{
- "{\n"
- " \"blocks\": nnn, (numeric) The current block\n"
- " \"currentblockweight\": nnn, (numeric) The last block weight\n"
- " \"currentblocktx\": nnn, (numeric) The last block transaction\n"
- " \"difficulty\": xxx.xxxxx (numeric) The current difficulty\n"
- " \"networkhashps\": nnn, (numeric) The network hashes per second\n"
- " \"pooledtx\": n (numeric) The size of the mempool\n"
- " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n"
- " \"warnings\": \"...\" (string) any network and blockchain warnings\n"
- "}\n"
+ "{\n"
+ " \"blocks\": nnn, (numeric) The current block\n"
+ " \"currentblockweight\": nnn, (numeric, optional) The block weight of the last assembled block (only present if a block was ever assembled)\n"
+ " \"currentblocktx\": nnn, (numeric, optional) The number of block transactions of the last assembled block (only present if a block was ever assembled)\n"
+ " \"difficulty\": xxx.xxxxx (numeric) The current difficulty\n"
+ " \"networkhashps\": nnn, (numeric) The network hashes per second\n"
+ " \"pooledtx\": n (numeric) The size of the mempool\n"
+ " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n"
+ " \"warnings\": \"...\" (string) any network and blockchain warnings\n"
+ "}\n"
},
RPCExamples{
HelpExampleCli("getmininginfo", "")
+ HelpExampleRpc("getmininginfo", "")
},
}.ToString());
-
+ }
LOCK(cs_main);
UniValue obj(UniValue::VOBJ);
obj.pushKV("blocks", (int)chainActive.Height());
- obj.pushKV("currentblockweight", (uint64_t)nLastBlockWeight);
- obj.pushKV("currentblocktx", (uint64_t)nLastBlockTx);
+ if (BlockAssembler::m_last_block_weight) obj.pushKV("currentblockweight", *BlockAssembler::m_last_block_weight);
+ if (BlockAssembler::m_last_block_num_txs) obj.pushKV("currentblocktx", *BlockAssembler::m_last_block_num_txs);
obj.pushKV("difficulty", (double)GetDifficulty(chainActive.Tip()));
obj.pushKV("networkhashps", getnetworkhashps(request));
obj.pushKV("pooledtx", (uint64_t)mempool.size());
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 784adfa969..999a307e2b 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -143,6 +143,46 @@ static UniValue createmultisig(const JSONRPCRequest& request)
return result;
}
+UniValue getdescriptorinfo(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1) {
+ throw std::runtime_error(
+ RPCHelpMan{"getdescriptorinfo",
+ {"\nAnalyses a descriptor.\n"},
+ {
+ {"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor."},
+ },
+ RPCResult{
+ "{\n"
+ " \"descriptor\" : \"desc\", (string) The descriptor in canonical form, without private keys\n"
+ " \"isrange\" : true|false, (boolean) Whether the descriptor is ranged\n"
+ " \"issolvable\" : true|false, (boolean) Whether the descriptor is solvable\n"
+ " \"hasprivatekeys\" : true|false, (boolean) Whether the input descriptor contained at least one private key\n"
+ "}\n"
+ },
+ RPCExamples{
+ "Analyse a descriptor\n" +
+ HelpExampleCli("getdescriptorinfo", "\"wpkh([d34db33f/84h/0h/0h]0279be667ef9dcbbac55a06295Ce870b07029Bfcdb2dce28d959f2815b16f81798)\"")
+ }}.ToString()
+ );
+ }
+
+ RPCTypeCheck(request.params, {UniValue::VSTR});
+
+ FlatSigningProvider provider;
+ auto desc = Parse(request.params[0].get_str(), provider);
+ if (!desc) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid descriptor"));
+ }
+
+ UniValue result(UniValue::VOBJ);
+ result.pushKV("descriptor", desc->ToString());
+ result.pushKV("isrange", desc->IsRange());
+ result.pushKV("issolvable", desc->IsSolvable());
+ result.pushKV("hasprivatekeys", provider.keys.size() > 0);
+ return result;
+}
+
UniValue deriveaddresses(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.empty() || request.params.size() > 3) {
@@ -167,7 +207,7 @@ UniValue deriveaddresses(const JSONRPCRequest& request)
},
RPCExamples{
"First three native segwit receive addresses\n" +
- HelpExampleCli("deriveaddresses", "\"wpkh([d34db33f/84h/0h/0h]xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8PhqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKEu3oaqMSzhSrHMxyyoEAmUHQbY/0/*)\" 0 2")
+ HelpExampleCli("deriveaddresses", "\"wpkh([d34db33f/84h/0h/0h]xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8PhqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKEu3oaqMSzhSrHMxyyoEAmUHQbY/0/*)#trd0mf0l\" 0 2")
}}.ToString()
);
}
@@ -193,7 +233,7 @@ UniValue deriveaddresses(const JSONRPCRequest& request)
}
FlatSigningProvider provider;
- auto desc = Parse(desc_str, provider);
+ auto desc = Parse(desc_str, provider, /* require_checksum = */ true);
if (!desc) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid descriptor"));
}
@@ -564,6 +604,7 @@ static const CRPCCommand commands[] =
{ "util", "validateaddress", &validateaddress, {"address"} },
{ "util", "createmultisig", &createmultisig, {"nrequired","keys","address_type"} },
{ "util", "deriveaddresses", &deriveaddresses, {"descriptor", "begin", "end"} },
+ { "util", "getdescriptorinfo", &getdescriptorinfo, {"descriptor"} },
{ "util", "verifymessage", &verifymessage, {"address","signature","message"} },
{ "util", "signmessagewithprivkey", &signmessagewithprivkey, {"privkey","message"} },
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 44cac14228..5a714a137e 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -7,6 +7,7 @@
#include <coins.h>
#include <compat/byteswap.h>
#include <consensus/validation.h>
+#include <consensus/tx_verify.h>
#include <core_io.h>
#include <index/txindex.h>
#include <init.h>
@@ -26,10 +27,13 @@
#include <script/sign.h>
#include <script/standard.h>
#include <uint256.h>
+#include <util/bip32.h>
#include <util/strencodings.h>
#include <validation.h>
#include <validationinterface.h>
+
+#include <numeric>
#include <stdint.h>
#include <univalue.h>
@@ -855,15 +859,25 @@ UniValue SignTransaction(interfaces::Chain& chain, CMutableTransaction& mtx, con
RPCTypeCheckObj(prevOut,
{
{"redeemScript", UniValueType(UniValue::VSTR)},
- });
- UniValue v = find_value(prevOut, "redeemScript");
- if (!v.isNull()) {
- std::vector<unsigned char> rsData(ParseHexV(v, "redeemScript"));
+ {"witnessScript", UniValueType(UniValue::VSTR)},
+ }, true);
+ UniValue rs = find_value(prevOut, "redeemScript");
+ if (!rs.isNull()) {
+ std::vector<unsigned char> rsData(ParseHexV(rs, "redeemScript"));
CScript redeemScript(rsData.begin(), rsData.end());
keystore->AddCScript(redeemScript);
// Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
+ // This is only for compatibility, it is encouraged to use the explicit witnessScript field instead.
keystore->AddCScript(GetScriptForWitness(redeemScript));
}
+ UniValue ws = find_value(prevOut, "witnessScript");
+ if (!ws.isNull()) {
+ std::vector<unsigned char> wsData(ParseHexV(ws, "witnessScript"));
+ CScript witnessScript(wsData.begin(), wsData.end());
+ keystore->AddCScript(witnessScript);
+ // Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
+ keystore->AddCScript(GetScriptForWitness(witnessScript));
+ }
}
}
}
@@ -948,7 +962,8 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
{"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
{"scriptPubKey", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "script key"},
- {"redeemScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2SH or P2WSH) redeem script"},
+ {"redeemScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2SH) redeem script"},
+ {"witnessScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2WSH or P2SH-P2WSH) witness script"},
{"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The amount spent"},
},
},
@@ -1692,6 +1707,338 @@ UniValue converttopsbt(const JSONRPCRequest& request)
return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
}
+UniValue utxoupdatepsbt(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1) {
+ throw std::runtime_error(
+ RPCHelpMan{"utxoupdatepsbt",
+ "\nUpdates a PSBT with witness UTXOs retrieved from the UTXO set or the mempool.\n",
+ {
+ {"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"}
+ },
+ RPCResult {
+ " \"psbt\" (string) The base64-encoded partially signed transaction with inputs updated\n"
+ },
+ RPCExamples {
+ HelpExampleCli("utxoupdatepsbt", "\"psbt\"")
+ }}.ToString());
+ }
+
+ RPCTypeCheck(request.params, {UniValue::VSTR}, true);
+
+ // Unserialize the transactions
+ PartiallySignedTransaction psbtx;
+ std::string error;
+ if (!DecodeBase64PSBT(psbtx, request.params[0].get_str(), error)) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
+ }
+
+ // Fetch previous transactions (inputs):
+ CCoinsView viewDummy;
+ CCoinsViewCache view(&viewDummy);
+ {
+ LOCK2(cs_main, mempool.cs);
+ CCoinsViewCache &viewChain = *pcoinsTip;
+ CCoinsViewMemPool viewMempool(&viewChain, mempool);
+ view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
+
+ for (const CTxIn& txin : psbtx.tx->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
+ }
+
+ // Fill the inputs
+ for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
+ PSBTInput& input = psbtx.inputs.at(i);
+
+ if (input.non_witness_utxo || !input.witness_utxo.IsNull()) {
+ continue;
+ }
+
+ const Coin& coin = view.AccessCoin(psbtx.tx->vin[i].prevout);
+
+ std::vector<std::vector<unsigned char>> solutions_data;
+ txnouttype which_type = Solver(coin.out.scriptPubKey, solutions_data);
+ if (which_type == TX_WITNESS_V0_SCRIPTHASH || which_type == TX_WITNESS_V0_KEYHASH || which_type == TX_WITNESS_UNKNOWN) {
+ input.witness_utxo = coin.out;
+ }
+ }
+
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << psbtx;
+ return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
+}
+
+UniValue joinpsbts(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1) {
+ throw std::runtime_error(
+ RPCHelpMan{"joinpsbts",
+ "\nJoins multiple distinct PSBTs with different inputs and outputs into one PSBT with inputs and outputs from all of the PSBTs\n"
+ "No input in any of the PSBTs can be in more than one of the PSBTs.\n",
+ {
+ {"txs", RPCArg::Type::ARR, RPCArg::Optional::NO, "A json array of base64 strings of partially signed transactions",
+ {
+ {"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"}
+ }}
+ },
+ RPCResult {
+ " \"psbt\" (string) The base64-encoded partially signed transaction\n"
+ },
+ RPCExamples {
+ HelpExampleCli("joinpsbts", "\"psbt\"")
+ }}.ToString());
+ }
+
+ RPCTypeCheck(request.params, {UniValue::VARR}, true);
+
+ // Unserialize the transactions
+ std::vector<PartiallySignedTransaction> psbtxs;
+ UniValue txs = request.params[0].get_array();
+
+ if (txs.size() <= 1) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "At least two PSBTs are required to join PSBTs.");
+ }
+
+ int32_t best_version = 1;
+ uint32_t best_locktime = 0xffffffff;
+ for (unsigned int i = 0; i < txs.size(); ++i) {
+ PartiallySignedTransaction psbtx;
+ std::string error;
+ if (!DecodeBase64PSBT(psbtx, txs[i].get_str(), error)) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
+ }
+ psbtxs.push_back(psbtx);
+ // Choose the highest version number
+ if (psbtx.tx->nVersion > best_version) {
+ best_version = psbtx.tx->nVersion;
+ }
+ // Choose the lowest lock time
+ if (psbtx.tx->nLockTime < best_locktime) {
+ best_locktime = psbtx.tx->nLockTime;
+ }
+ }
+
+ // Create a blank psbt where everything will be added
+ PartiallySignedTransaction merged_psbt;
+ merged_psbt.tx = CMutableTransaction();
+ merged_psbt.tx->nVersion = best_version;
+ merged_psbt.tx->nLockTime = best_locktime;
+
+ // Merge
+ for (auto& psbt : psbtxs) {
+ for (unsigned int i = 0; i < psbt.tx->vin.size(); ++i) {
+ if (!merged_psbt.AddInput(psbt.tx->vin[i], psbt.inputs[i])) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Input %s:%d exists in multiple PSBTs", psbt.tx->vin[i].prevout.hash.ToString().c_str(), psbt.tx->vin[i].prevout.n));
+ }
+ }
+ for (unsigned int i = 0; i < psbt.tx->vout.size(); ++i) {
+ merged_psbt.AddOutput(psbt.tx->vout[i], psbt.outputs[i]);
+ }
+ merged_psbt.unknown.insert(psbt.unknown.begin(), psbt.unknown.end());
+ }
+
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << merged_psbt;
+ return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
+}
+
+UniValue analyzepsbt(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1) {
+ throw std::runtime_error(
+ RPCHelpMan{"analyzepsbt",
+ "\nAnalyzes and provides information about the current status of a PSBT and its inputs\n",
+ {
+ {"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"}
+ },
+ RPCResult {
+ "{\n"
+ " \"inputs\" : [ (array of json objects)\n"
+ " {\n"
+ " \"has_utxo\" : true|false (boolean) Whether a UTXO is provided\n"
+ " \"is_final\" : true|false (boolean) Whether the input is finalized\n"
+ " \"missing\" : { (json object, optional) Things that are missing that are required to complete this input\n"
+ " \"pubkeys\" : [ (array)\n"
+ " \"keyid\" (string) Public key ID, hash160 of the public key, of a public key whose BIP 32 derivation path is missing\n"
+ " ]\n"
+ " \"signatures\" : [ (array)\n"
+ " \"keyid\" (string) Public key ID, hash160 of the public key, of a public key whose signature is missing\n"
+ " ]\n"
+ " \"redeemscript\" : \"hash\" (string) Hash160 of the redeemScript that is missing\n"
+ " \"witnessscript\" : \"hash\" (string) SHA256 of the witnessScript that is missing\n"
+ " }\n"
+ " \"next\" : \"role\" (string) Role of the next person that this input needs to go to\n"
+ " }\n"
+ " ,...\n"
+ " ]\n"
+ " \"estimated_vsize\" : vsize (numeric) Estimated vsize of the final signed transaction\n"
+ " \"estimated_feerate\" : feerate (numeric, optional) Estimated feerate of the final signed transaction. Shown only if all UTXO slots in the PSBT have been filled.\n"
+ " \"fee\" : fee (numeric, optional) The transaction fee paid. Shown only if all UTXO slots in the PSBT have been filled.\n"
+ " \"next\" : \"role\" (string) Role of the next person that this psbt needs to go to\n"
+ "}\n"
+ },
+ RPCExamples {
+ HelpExampleCli("analyzepsbt", "\"psbt\"")
+ }}.ToString());
+ }
+
+ RPCTypeCheck(request.params, {UniValue::VSTR});
+
+ // Unserialize the transaction
+ PartiallySignedTransaction psbtx;
+ std::string error;
+ if (!DecodeBase64PSBT(psbtx, request.params[0].get_str(), error)) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
+ }
+
+ // Go through each input and build status
+ UniValue result(UniValue::VOBJ);
+ UniValue inputs_result(UniValue::VARR);
+ bool calc_fee = true;
+ bool all_final = true;
+ bool only_missing_sigs = true;
+ bool only_missing_final = false;
+ CAmount in_amt = 0;
+ for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
+ PSBTInput& input = psbtx.inputs[i];
+ UniValue input_univ(UniValue::VOBJ);
+ UniValue missing(UniValue::VOBJ);
+
+ // Check for a UTXO
+ CTxOut utxo;
+ if (psbtx.GetInputUTXO(utxo, i)) {
+ in_amt += utxo.nValue;
+ input_univ.pushKV("has_utxo", true);
+ } else {
+ input_univ.pushKV("has_utxo", false);
+ input_univ.pushKV("is_final", false);
+ input_univ.pushKV("next", "updater");
+ calc_fee = false;
+ }
+
+ // Check if it is final
+ if (!utxo.IsNull() && !PSBTInputSigned(input)) {
+ input_univ.pushKV("is_final", false);
+ all_final = false;
+
+ // Figure out what is missing
+ SignatureData outdata;
+ bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, &outdata);
+
+ // Things are missing
+ if (!complete) {
+ if (!outdata.missing_pubkeys.empty()) {
+ // Missing pubkeys
+ UniValue missing_pubkeys_univ(UniValue::VARR);
+ for (const CKeyID& pubkey : outdata.missing_pubkeys) {
+ missing_pubkeys_univ.push_back(HexStr(pubkey));
+ }
+ missing.pushKV("pubkeys", missing_pubkeys_univ);
+ }
+ if (!outdata.missing_redeem_script.IsNull()) {
+ // Missing redeemScript
+ missing.pushKV("redeemscript", HexStr(outdata.missing_redeem_script));
+ }
+ if (!outdata.missing_witness_script.IsNull()) {
+ // Missing witnessScript
+ missing.pushKV("witnessscript", HexStr(outdata.missing_witness_script));
+ }
+ if (!outdata.missing_sigs.empty()) {
+ // Missing sigs
+ UniValue missing_sigs_univ(UniValue::VARR);
+ for (const CKeyID& pubkey : outdata.missing_sigs) {
+ missing_sigs_univ.push_back(HexStr(pubkey));
+ }
+ missing.pushKV("signatures", missing_sigs_univ);
+ }
+ input_univ.pushKV("missing", missing);
+
+ // If we are only missing signatures and nothing else, then next is signer
+ if (outdata.missing_pubkeys.empty() && outdata.missing_redeem_script.IsNull() && outdata.missing_witness_script.IsNull() && !outdata.missing_sigs.empty()) {
+ input_univ.pushKV("next", "signer");
+ } else {
+ only_missing_sigs = false;
+ input_univ.pushKV("next", "updater");
+ }
+ } else {
+ only_missing_final = true;
+ input_univ.pushKV("next", "finalizer");
+ }
+ } else if (!utxo.IsNull()){
+ input_univ.pushKV("is_final", true);
+ }
+ inputs_result.push_back(input_univ);
+ }
+ result.pushKV("inputs", inputs_result);
+
+ if (all_final) {
+ only_missing_sigs = false;
+ result.pushKV("next", "extractor");
+ }
+ if (calc_fee) {
+ // Get the output amount
+ CAmount out_amt = std::accumulate(psbtx.tx->vout.begin(), psbtx.tx->vout.end(), 0,
+ [](int a, const CTxOut& b) {
+ return a += b.nValue;
+ }
+ );
+
+ // Get the fee
+ CAmount fee = in_amt - out_amt;
+
+ // Estimate the size
+ CMutableTransaction mtx(*psbtx.tx);
+ CCoinsView view_dummy;
+ CCoinsViewCache view(&view_dummy);
+ bool success = true;
+
+ for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
+ PSBTInput& input = psbtx.inputs[i];
+ if (SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, nullptr, true)) {
+ mtx.vin[i].scriptSig = input.final_script_sig;
+ mtx.vin[i].scriptWitness = input.final_script_witness;
+
+ Coin newcoin;
+ if (!psbtx.GetInputUTXO(newcoin.out, i)) {
+ success = false;
+ break;
+ }
+ newcoin.nHeight = 1;
+ view.AddCoin(psbtx.tx->vin[i].prevout, std::move(newcoin), true);
+ } else {
+ success = false;
+ break;
+ }
+ }
+
+ if (success) {
+ CTransaction ctx = CTransaction(mtx);
+ size_t size = GetVirtualTransactionSize(ctx, GetTransactionSigOpCost(ctx, view, STANDARD_SCRIPT_VERIFY_FLAGS));
+ result.pushKV("estimated_vsize", (int)size);
+ // Estimate fee rate
+ CFeeRate feerate(fee, size);
+ result.pushKV("estimated_feerate", feerate.ToString());
+ }
+ result.pushKV("fee", ValueFromAmount(fee));
+
+ if (only_missing_sigs) {
+ result.pushKV("next", "signer");
+ } else if (only_missing_final) {
+ result.pushKV("next", "finalizer");
+ } else if (all_final) {
+ result.pushKV("next", "extractor");
+ } else {
+ result.pushKV("next", "updater");
+ }
+ } else {
+ result.pushKV("next", "updater");
+ }
+ return result;
+}
+
// clang-format off
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
@@ -1710,6 +2057,9 @@ static const CRPCCommand commands[] =
{ "rawtransactions", "finalizepsbt", &finalizepsbt, {"psbt", "extract"} },
{ "rawtransactions", "createpsbt", &createpsbt, {"inputs","outputs","locktime","replaceable"} },
{ "rawtransactions", "converttopsbt", &converttopsbt, {"hexstring","permitsigdata","iswitness"} },
+ { "rawtransactions", "utxoupdatepsbt", &utxoupdatepsbt, {"psbt"} },
+ { "rawtransactions", "joinpsbts", &joinpsbts, {"txs"} },
+ { "rawtransactions", "analyzepsbt", &analyzepsbt, {"psbt"} },
{ "blockchain", "gettxoutproof", &gettxoutproof, {"txids", "blockhash"} },
{ "blockchain", "verifytxoutproof", &verifytxoutproof, {"proof"} },