mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-03-29 18:05:58 +02:00
Merge bitcoin/bitcoin#25796: rpc: add descriptorprocesspsbt rpc
1bce12acd3test: add test for `descriptorprocesspsbt` RPC (ishaanam)fb2a3a70e8rpc: add descriptorprocesspsbt rpc (ishaanam) Pull request description: This PR implements an RPC called `descriptorprocesspsbt`. This RPC is based off of `walletprocesspsbt`, but instead of interacting with the wallet to update, sign and finalize a psbt, it instead accepts an array of output descriptors and uses that information along with information from the mempool, txindex, and the utxo set to do so. `utxoupdatepsbt` also updates a psbt in this manner, but doesn't sign or finalize it. Because of this overlap, a helper function that is added in this PR is called by both `utxoupdatepsbt` and `descriptorprocesspsbt`. Whether or not the helper function signs a psbt is dictated by if the HidingSigningProvider passed to it contains any private information. There is also a test added in this PR for this new RPC that uses p2wsh, p2wpkh, and legacy outputs. Edit: see https://github.com/bitcoin/bitcoin/pull/25796#issuecomment-1228830963 ACKs for top commit: achow101: re-ACK1bce12acd3instagibbs: reACK1bce12acd3Tree-SHA512: e1d0334739943e71f2ee68b4db7637ebe725da62e7aa4be071f71c7196d2a5970a31ece96d91e372d34454cde8509e95ab0eebd2c8edb94f7d5a781a84f8fc5d
This commit is contained in:
@@ -170,8 +170,9 @@ static std::vector<RPCArg> CreateTxDoc()
|
||||
};
|
||||
}
|
||||
|
||||
// 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)
|
||||
// Update PSBT with information from the mempool, the UTXO set, the txindex, and the provided descriptors.
|
||||
// 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
|
||||
PartiallySignedTransaction psbtx;
|
||||
@@ -240,9 +241,10 @@ PartiallySignedTransaction ProcessPSBT(const std::string& psbt_string, const std
|
||||
}
|
||||
|
||||
// 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(provider, psbtx, /*index=*/i, &txdata, /*sighash=*/1);
|
||||
// Note that SignPSBTInput does a lot more than just constructing ECDSA signatures.
|
||||
// We only actually care about those if our signing provider doesn't hide private
|
||||
// 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.
|
||||
@@ -1695,7 +1697,9 @@ static RPCHelpMan utxoupdatepsbt()
|
||||
const PartiallySignedTransaction& psbtx = ProcessPSBT(
|
||||
request.params[0].get_str(),
|
||||
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);
|
||||
ssTx << psbtx;
|
||||
@@ -1914,6 +1918,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)
|
||||
{
|
||||
static const CRPCCommand commands[]{
|
||||
@@ -1929,6 +2009,7 @@ void RegisterRawTransactionRPCCommands(CRPCTable& t)
|
||||
{"rawtransactions", &createpsbt},
|
||||
{"rawtransactions", &converttopsbt},
|
||||
{"rawtransactions", &utxoupdatepsbt},
|
||||
{"rawtransactions", &descriptorprocesspsbt},
|
||||
{"rawtransactions", &joinpsbts},
|
||||
{"rawtransactions", &analyzepsbt},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user