aboutsummaryrefslogtreecommitdiff
path: root/src/rpc
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc')
-rw-r--r--src/rpc/blockchain.cpp39
-rw-r--r--src/rpc/mining.cpp3
-rw-r--r--src/rpc/rawtransaction.cpp40
-rw-r--r--src/rpc/rawtransaction_util.cpp3
-rw-r--r--src/rpc/util.cpp38
-rw-r--r--src/rpc/util.h5
6 files changed, 86 insertions, 42 deletions
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 50c4589d9f..9f3748cb65 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -2247,41 +2247,12 @@ UniValue scantxoutset(const JSONRPCRequest& request)
// loop through the scan objects
for (const UniValue& scanobject : request.params[1].get_array().getValues()) {
- std::string desc_str;
- std::pair<int64_t, int64_t> range = {0, 1000};
- if (scanobject.isStr()) {
- desc_str = scanobject.get_str();
- } else if (scanobject.isObject()) {
- UniValue desc_uni = find_value(scanobject, "desc");
- if (desc_uni.isNull()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Descriptor needs to be provided in scan object");
- desc_str = desc_uni.get_str();
- UniValue range_uni = find_value(scanobject, "range");
- if (!range_uni.isNull()) {
- range = ParseDescriptorRange(range_uni);
- }
- } else {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan object needs to be either a string or an object");
- }
-
FlatSigningProvider provider;
- auto desc = Parse(desc_str, provider);
- if (!desc) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid descriptor '%s'", desc_str));
- }
- if (!desc->IsRange()) {
- range.first = 0;
- range.second = 0;
- }
- for (int i = range.first; i <= range.second; ++i) {
- std::vector<CScript> scripts;
- if (!desc->Expand(i, provider, scripts, provider)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys: '%s'", desc_str));
- }
- for (const auto& script : scripts) {
- std::string inferred = InferDescriptor(script, provider)->ToString();
- needles.emplace(script);
- descriptors.emplace(std::move(script), std::move(inferred));
- }
+ auto scripts = EvalDescriptorStringOrObject(scanobject, provider);
+ for (const auto& script : scripts) {
+ std::string inferred = InferDescriptor(script, provider)->ToString();
+ needles.emplace(script);
+ descriptors.emplace(std::move(script), std::move(inferred));
}
}
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 1dfaecc951..6636a8a29a 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -468,7 +468,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
nTransactionsUpdatedLastLP = nTransactionsUpdatedLast;
}
- // Release the wallet and main lock while waiting
+ // Release lock while waiting
LEAVE_CRITICAL_SECTION(cs_main);
{
checktxtime = std::chrono::steady_clock::now() + std::chrono::minutes(1);
@@ -479,6 +479,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
if (g_best_block_cv.wait_until(lock, checktxtime) == std::cv_status::timeout)
{
// Timeout: Check transactions for update
+ // without holding ::mempool.cs to avoid deadlocks
if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP)
break;
checktxtime += std::chrono::seconds(10);
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 9da24afe79..966ff3fedc 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -1495,12 +1495,19 @@ UniValue converttopsbt(const JSONRPCRequest& request)
UniValue utxoupdatepsbt(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 1) {
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
throw std::runtime_error(
RPCHelpMan{"utxoupdatepsbt",
- "\nUpdates a PSBT with witness UTXOs retrieved from the UTXO set or the mempool.\n",
+ "\nUpdates all segwit inputs and outputs in a PSBT with data from output descriptors, the UTXO set or the mempool.\n",
{
- {"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"}
+ {"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"},
+ {"descriptors", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "An array of either strings or objects", {
+ {"", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "An output descriptor"},
+ {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "An object with an output descriptor and extra information", {
+ {"desc", RPCArg::Type::STR, RPCArg::Optional::NO, "An output descriptor"},
+ {"range", RPCArg::Type::RANGE, "1000", "Up to what index HD chains should be explored (either end or [begin,end])"},
+ }},
+ }},
},
RPCResult {
" \"psbt\" (string) The base64-encoded partially signed transaction with inputs updated\n"
@@ -1510,7 +1517,7 @@ UniValue utxoupdatepsbt(const JSONRPCRequest& request)
}}.ToString());
}
- RPCTypeCheck(request.params, {UniValue::VSTR}, true);
+ RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR}, true);
// Unserialize the transactions
PartiallySignedTransaction psbtx;
@@ -1519,6 +1526,17 @@ UniValue utxoupdatepsbt(const JSONRPCRequest& request)
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
}
+ // Parse descriptors, if any.
+ FlatSigningProvider provider;
+ if (!request.params[1].isNull()) {
+ auto descs = request.params[1].get_array();
+ for (size_t i = 0; i < descs.size(); ++i) {
+ EvalDescriptorStringOrObject(descs[i], provider);
+ }
+ }
+ // We don't actually need private keys further on; hide them as a precaution.
+ HidingSigningProvider public_provider(&provider, /* nosign */ true, /* nobip32derivs */ false);
+
// Fetch previous transactions (inputs):
CCoinsView viewDummy;
CCoinsViewCache view(&viewDummy);
@@ -1545,11 +1563,19 @@ UniValue utxoupdatepsbt(const JSONRPCRequest& request)
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) {
+ if (IsSegWitOutput(provider, coin.out.scriptPubKey)) {
input.witness_utxo = coin.out;
}
+
+ // Update script/keypath information using descriptor data.
+ // Note that SignPSBTInput does a lot more than just constructing ECDSA signatures
+ // we don't actually care about those here, in fact.
+ SignPSBTInput(public_provider, psbtx, i, /* sighash_type */ 1);
+ }
+
+ // Update script/keypath information using descriptor data.
+ for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) {
+ UpdatePSBTOutput(public_provider, psbtx, i);
}
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp
index 9c4cdc3a90..14fcb628eb 100644
--- a/src/rpc/rawtransaction_util.cpp
+++ b/src/rpc/rawtransaction_util.cpp
@@ -221,6 +221,9 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival
// Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
keystore->AddCScript(GetScriptForWitness(witnessScript));
}
+ if (rs.isNull() && ws.isNull()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing redeemScript/witnessScript");
+ }
}
}
}
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index 4642cf16b1..67ccb225b5 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -6,6 +6,7 @@
#include <keystore.h>
#include <outputtype.h>
#include <rpc/util.h>
+#include <script/descriptor.h>
#include <tinyformat.h>
#include <util/strencodings.h>
@@ -697,3 +698,40 @@ std::pair<int64_t, int64_t> ParseDescriptorRange(const UniValue& value)
}
return {low, high};
}
+
+std::vector<CScript> EvalDescriptorStringOrObject(const UniValue& scanobject, FlatSigningProvider& provider)
+{
+ std::string desc_str;
+ std::pair<int64_t, int64_t> range = {0, 1000};
+ if (scanobject.isStr()) {
+ desc_str = scanobject.get_str();
+ } else if (scanobject.isObject()) {
+ UniValue desc_uni = find_value(scanobject, "desc");
+ if (desc_uni.isNull()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Descriptor needs to be provided in scan object");
+ desc_str = desc_uni.get_str();
+ UniValue range_uni = find_value(scanobject, "range");
+ if (!range_uni.isNull()) {
+ range = ParseDescriptorRange(range_uni);
+ }
+ } else {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan object needs to be either a string or an object");
+ }
+
+ auto desc = Parse(desc_str, provider);
+ if (!desc) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid descriptor '%s'", desc_str));
+ }
+ if (!desc->IsRange()) {
+ range.first = 0;
+ range.second = 0;
+ }
+ std::vector<CScript> ret;
+ for (int i = range.first; i <= range.second; ++i) {
+ std::vector<CScript> scripts;
+ if (!desc->Expand(i, provider, scripts, provider)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys: '%s'", desc_str));
+ }
+ std::move(scripts.begin(), scripts.end(), std::back_inserter(ret));
+ }
+ return ret;
+}
diff --git a/src/rpc/util.h b/src/rpc/util.h
index 0eb2fef5c3..8762281d73 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -9,6 +9,8 @@
#include <outputtype.h>
#include <pubkey.h>
#include <rpc/protocol.h>
+#include <script/script.h>
+#include <script/sign.h>
#include <script/standard.h>
#include <univalue.h>
@@ -84,6 +86,9 @@ UniValue JSONRPCTransactionError(TransactionError terr, const std::string& err_s
//! Parse a JSON range specified as int64, or [int64, int64]
std::pair<int64_t, int64_t> ParseDescriptorRange(const UniValue& value);
+/** Evaluate a descriptor given as a string, or as a {"desc":...,"range":...} object, with default range of 1000. */
+std::vector<CScript> EvalDescriptorStringOrObject(const UniValue& scanobject, FlatSigningProvider& provider);
+
struct RPCArg {
enum class Type {
OBJ,