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:
ishaanam
2022-08-03 19:32:59 -04:00
parent 49d07ea9a1
commit fb2a3a70e8
5 changed files with 96 additions and 8 deletions

View File

@@ -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
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;
@@ -242,9 +243,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.
@@ -1697,7 +1699,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;
@@ -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)
{
static const CRPCCommand commands[]{
@@ -1931,6 +2011,7 @@ void RegisterRawTransactionRPCCommands(CRPCTable& t)
{"rawtransactions", &createpsbt},
{"rawtransactions", &converttopsbt},
{"rawtransactions", &utxoupdatepsbt},
{"rawtransactions", &descriptorprocesspsbt},
{"rawtransactions", &joinpsbts},
{"rawtransactions", &analyzepsbt},
};