mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-06-25 16:31:29 +02:00
rpc: add descriptorprocesspsbt rpc
This RPC can be the Updater, Signer, and optionally the Input Finalizer for a psbt, and has no interaction with the Bitcoin Core wallet.
This commit is contained in:
parent
49d07ea9a1
commit
fb2a3a70e8
@ -133,6 +133,9 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||||||
{ "walletprocesspsbt", 1, "sign" },
|
{ "walletprocesspsbt", 1, "sign" },
|
||||||
{ "walletprocesspsbt", 3, "bip32derivs" },
|
{ "walletprocesspsbt", 3, "bip32derivs" },
|
||||||
{ "walletprocesspsbt", 4, "finalize" },
|
{ "walletprocesspsbt", 4, "finalize" },
|
||||||
|
{ "descriptorprocesspsbt", 1, "descriptors"},
|
||||||
|
{ "descriptorprocesspsbt", 3, "bip32derivs" },
|
||||||
|
{ "descriptorprocesspsbt", 4, "finalize" },
|
||||||
{ "createpsbt", 0, "inputs" },
|
{ "createpsbt", 0, "inputs" },
|
||||||
{ "createpsbt", 1, "outputs" },
|
{ "createpsbt", 1, "outputs" },
|
||||||
{ "createpsbt", 2, "locktime" },
|
{ "createpsbt", 2, "locktime" },
|
||||||
|
@ -172,8 +172,9 @@ static std::vector<RPCArg> CreateTxDoc()
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update PSBT with information from the mempool, the UTXO set, the txindex, and the provided descriptors
|
// Update PSBT with information from the mempool, the UTXO set, the txindex, and the provided descriptors.
|
||||||
PartiallySignedTransaction ProcessPSBT(const std::string& psbt_string, const std::any& context, const HidingSigningProvider& provider)
|
// Optionally, sign the inputs that we can using information from the descriptors.
|
||||||
|
PartiallySignedTransaction ProcessPSBT(const std::string& psbt_string, const std::any& context, const HidingSigningProvider& provider, int sighash_type, bool finalize)
|
||||||
{
|
{
|
||||||
// Unserialize the transactions
|
// Unserialize the transactions
|
||||||
PartiallySignedTransaction psbtx;
|
PartiallySignedTransaction psbtx;
|
||||||
@ -242,9 +243,10 @@ PartiallySignedTransaction ProcessPSBT(const std::string& psbt_string, const std
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update script/keypath information using descriptor data.
|
// Update script/keypath information using descriptor data.
|
||||||
// Note that SignPSBTInput does a lot more than just constructing ECDSA signatures
|
// Note that SignPSBTInput does a lot more than just constructing ECDSA signatures.
|
||||||
// we don't actually care about those here, in fact.
|
// We only actually care about those if our signing provider doesn't hide private
|
||||||
SignPSBTInput(provider, psbtx, /*index=*/i, &txdata, /*sighash=*/1);
|
// information, as is the case with `descriptorprocesspsbt`
|
||||||
|
SignPSBTInput(provider, psbtx, /*index=*/i, &txdata, sighash_type, /*out_sigdata=*/nullptr, finalize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update script/keypath information using descriptor data.
|
// Update script/keypath information using descriptor data.
|
||||||
@ -1697,7 +1699,9 @@ static RPCHelpMan utxoupdatepsbt()
|
|||||||
const PartiallySignedTransaction& psbtx = ProcessPSBT(
|
const PartiallySignedTransaction& psbtx = ProcessPSBT(
|
||||||
request.params[0].get_str(),
|
request.params[0].get_str(),
|
||||||
request.context,
|
request.context,
|
||||||
HidingSigningProvider(&provider, /*hide_secret=*/true, /*hide_origin=*/false));
|
HidingSigningProvider(&provider, /*hide_secret=*/true, /*hide_origin=*/false),
|
||||||
|
/*sighash_type=*/SIGHASH_ALL,
|
||||||
|
/*finalize=*/false);
|
||||||
|
|
||||||
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
|
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
|
||||||
ssTx << psbtx;
|
ssTx << psbtx;
|
||||||
@ -1916,6 +1920,82 @@ static RPCHelpMan analyzepsbt()
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RPCHelpMan descriptorprocesspsbt()
|
||||||
|
{
|
||||||
|
return RPCHelpMan{"descriptorprocesspsbt",
|
||||||
|
"\nUpdate all segwit inputs in a PSBT with information from output descriptors, the UTXO set or the mempool. \n"
|
||||||
|
"Then, sign the inputs we are able to with information from the output descriptors. ",
|
||||||
|
{
|
||||||
|
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction base64 string"},
|
||||||
|
{"descriptors", RPCArg::Type::ARR, RPCArg::Optional::NO, "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, RPCArg::Default{1000}, "Up to what index HD chains should be explored (either end or [begin,end])"},
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
{"sighashtype", RPCArg::Type::STR, RPCArg::Default{"DEFAULT for Taproot, ALL otherwise"}, "The signature hash type to sign with if not specified by the PSBT. Must be one of\n"
|
||||||
|
" \"DEFAULT\"\n"
|
||||||
|
" \"ALL\"\n"
|
||||||
|
" \"NONE\"\n"
|
||||||
|
" \"SINGLE\"\n"
|
||||||
|
" \"ALL|ANYONECANPAY\"\n"
|
||||||
|
" \"NONE|ANYONECANPAY\"\n"
|
||||||
|
" \"SINGLE|ANYONECANPAY\""},
|
||||||
|
{"bip32derivs", RPCArg::Type::BOOL, RPCArg::Default{true}, "Include BIP 32 derivation paths for public keys if we know them"},
|
||||||
|
{"finalize", RPCArg::Type::BOOL, RPCArg::Default{true}, "Also finalize inputs if possible"},
|
||||||
|
},
|
||||||
|
RPCResult{
|
||||||
|
RPCResult::Type::OBJ, "", "",
|
||||||
|
{
|
||||||
|
{RPCResult::Type::STR, "psbt", "The base64-encoded partially signed transaction"},
|
||||||
|
{RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RPCExamples{
|
||||||
|
HelpExampleCli("descriptorprocesspsbt", "\"psbt\" \"[\\\"descriptor1\\\", \\\"descriptor2\\\"]\"") +
|
||||||
|
HelpExampleCli("descriptorprocesspsbt", "\"psbt\" \"[{\\\"desc\\\":\\\"mydescriptor\\\", \\\"range\\\":21}]\"")
|
||||||
|
},
|
||||||
|
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||||
|
{
|
||||||
|
// Add descriptor information to a signing provider
|
||||||
|
FlatSigningProvider provider;
|
||||||
|
|
||||||
|
auto descs = request.params[1].get_array();
|
||||||
|
for (size_t i = 0; i < descs.size(); ++i) {
|
||||||
|
EvalDescriptorStringOrObject(descs[i], provider, /*expand_priv=*/true);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sighash_type = ParseSighashString(request.params[2]);
|
||||||
|
bool bip32derivs = request.params[3].isNull() ? true : request.params[3].get_bool();
|
||||||
|
bool finalize = request.params[4].isNull() ? true : request.params[4].get_bool();
|
||||||
|
|
||||||
|
const PartiallySignedTransaction& psbtx = ProcessPSBT(
|
||||||
|
request.params[0].get_str(),
|
||||||
|
request.context,
|
||||||
|
HidingSigningProvider(&provider, /*hide_secret=*/false, !bip32derivs),
|
||||||
|
sighash_type,
|
||||||
|
finalize);
|
||||||
|
|
||||||
|
// Check whether or not all of the inputs are now signed
|
||||||
|
bool complete = true;
|
||||||
|
for (const auto& input : psbtx.inputs) {
|
||||||
|
complete &= PSBTInputSigned(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
|
||||||
|
ssTx << psbtx;
|
||||||
|
|
||||||
|
UniValue result(UniValue::VOBJ);
|
||||||
|
|
||||||
|
result.pushKV("psbt", EncodeBase64(ssTx));
|
||||||
|
result.pushKV("complete", complete);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void RegisterRawTransactionRPCCommands(CRPCTable& t)
|
void RegisterRawTransactionRPCCommands(CRPCTable& t)
|
||||||
{
|
{
|
||||||
static const CRPCCommand commands[]{
|
static const CRPCCommand commands[]{
|
||||||
@ -1931,6 +2011,7 @@ void RegisterRawTransactionRPCCommands(CRPCTable& t)
|
|||||||
{"rawtransactions", &createpsbt},
|
{"rawtransactions", &createpsbt},
|
||||||
{"rawtransactions", &converttopsbt},
|
{"rawtransactions", &converttopsbt},
|
||||||
{"rawtransactions", &utxoupdatepsbt},
|
{"rawtransactions", &utxoupdatepsbt},
|
||||||
|
{"rawtransactions", &descriptorprocesspsbt},
|
||||||
{"rawtransactions", &joinpsbts},
|
{"rawtransactions", &joinpsbts},
|
||||||
{"rawtransactions", &analyzepsbt},
|
{"rawtransactions", &analyzepsbt},
|
||||||
};
|
};
|
||||||
|
@ -1126,7 +1126,7 @@ std::pair<int64_t, int64_t> ParseDescriptorRange(const UniValue& value)
|
|||||||
return {low, high};
|
return {low, high};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<CScript> EvalDescriptorStringOrObject(const UniValue& scanobject, FlatSigningProvider& provider)
|
std::vector<CScript> EvalDescriptorStringOrObject(const UniValue& scanobject, FlatSigningProvider& provider, const bool expand_priv)
|
||||||
{
|
{
|
||||||
std::string desc_str;
|
std::string desc_str;
|
||||||
std::pair<int64_t, int64_t> range = {0, 1000};
|
std::pair<int64_t, int64_t> range = {0, 1000};
|
||||||
@ -1159,6 +1159,9 @@ std::vector<CScript> EvalDescriptorStringOrObject(const UniValue& scanobject, Fl
|
|||||||
if (!desc->Expand(i, provider, scripts, provider)) {
|
if (!desc->Expand(i, provider, scripts, provider)) {
|
||||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys: '%s'", desc_str));
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys: '%s'", desc_str));
|
||||||
}
|
}
|
||||||
|
if (expand_priv) {
|
||||||
|
desc->ExpandPrivate(/*pos=*/i, provider, /*out=*/provider);
|
||||||
|
}
|
||||||
std::move(scripts.begin(), scripts.end(), std::back_inserter(ret));
|
std::move(scripts.begin(), scripts.end(), std::back_inserter(ret));
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -110,7 +110,7 @@ UniValue JSONRPCTransactionError(TransactionError terr, const std::string& err_s
|
|||||||
std::pair<int64_t, int64_t> ParseDescriptorRange(const UniValue& value);
|
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. */
|
/** 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);
|
std::vector<CScript> EvalDescriptorStringOrObject(const UniValue& scanobject, FlatSigningProvider& provider, const bool expand_priv = false);
|
||||||
|
|
||||||
/** Returns, given services flags, a list of humanly readable (known) network services */
|
/** Returns, given services flags, a list of humanly readable (known) network services */
|
||||||
UniValue GetServicesNames(ServiceFlags services);
|
UniValue GetServicesNames(ServiceFlags services);
|
||||||
|
@ -97,6 +97,7 @@ const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
|
|||||||
"decoderawtransaction",
|
"decoderawtransaction",
|
||||||
"decodescript",
|
"decodescript",
|
||||||
"deriveaddresses",
|
"deriveaddresses",
|
||||||
|
"descriptorprocesspsbt",
|
||||||
"disconnectnode",
|
"disconnectnode",
|
||||||
"echo",
|
"echo",
|
||||||
"echojson",
|
"echojson",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user