RPC/Wallet: Convert walletprocesspsbt to use options parameter

This commit is contained in:
Andrew Chow 2021-07-20 21:24:56 -04:00 committed by Luke Dashjr
parent c2612273ef
commit f43f992b73
4 changed files with 67 additions and 17 deletions

View File

@ -162,7 +162,10 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "walletcreatefundedpsbt", 3, "replaceable"},
{ "walletcreatefundedpsbt", 3, "solving_data"},
{ "walletcreatefundedpsbt", 4, "bip32derivs" },
{ "walletprocesspsbt", 1, "options" },
{ "walletprocesspsbt", 1, "sign" },
{ "walletprocesspsbt", 1, "bip32derivs" },
{ "walletprocesspsbt", 1, "finalize" },
{ "walletprocesspsbt", 3, "bip32derivs" },
{ "walletprocesspsbt", 4, "finalize" },
{ "descriptorprocesspsbt", 1, "descriptors"},

View File

@ -1552,17 +1552,25 @@ RPCHelpMan walletprocesspsbt()
HELP_REQUIRING_PASSPHRASE,
{
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction base64 string"},
{"sign", RPCArg::Type::BOOL, RPCArg::Default{true}, "Also sign the transaction when updating (requires wallet to be unlocked)"},
{"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"},
{"options|sign", {RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Type::BOOL}, RPCArg::Optional::OMITTED, "",
{
{"sign", RPCArg::Type::BOOL, RPCArg::Default{true}, "Also sign the transaction when updating (requires wallet to be unlocked)", RPCArgOptions{.also_positional = true}},
{"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\"",
RPCArgOptions{.also_positional = true}},
{"bip32derivs", RPCArg::Type::BOOL, RPCArg::Default{true}, "Include BIP 32 derivation paths for public keys if we know them", RPCArgOptions{.also_positional = true}},
{"finalize", RPCArg::Type::BOOL, RPCArg::Default{true}, "Also finalize inputs if possible", RPCArgOptions{.also_positional = true}},
},
RPCArgOptions{.oneline_description="options"}},
{"sighashtype", RPCArg::Type::STR, RPCArg::Default{"DEFAULT"}, "for backwards compatibility", RPCArgOptions{.hidden=true}},
{"bip32derivs", RPCArg::Type::BOOL, RPCArg::Default{true}, "for backwards compatibility", RPCArgOptions{.hidden=true}},
{"finalize", RPCArg::Type::BOOL, RPCArg::Default{true}, "for backwards compatibility", RPCArgOptions{.hidden=true}},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@ -1592,13 +1600,47 @@ RPCHelpMan walletprocesspsbt()
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
}
// Get the sighash type
int nHashType = ParseSighashString(request.params[2]);
// Get options
bool sign = true;
bool bip32derivs = true;
bool finalize = true;
int nHashType = ParseSighashString(NullUniValue); // Use ParseSighashString default
if (request.params[1].isBool() || request.params[1].isNull()) {
// Old style positional parameters
sign = request.params[1].isNull() ? true : request.params[1].get_bool();
nHashType = ParseSighashString(request.params[2]);
bip32derivs = request.params[3].isNull() ? true : request.params[3].get_bool();
finalize = request.params[4].isNull() ? true : request.params[4].get_bool();
} else {
// New style options are in an object
UniValue options = request.params[1];
RPCTypeCheckObj(options,
{
{"sign", UniValueType(UniValue::VBOOL)},
{"bip32derivs", UniValueType(UniValue::VBOOL)},
{"finalize", UniValueType(UniValue::VBOOL)},
{"sighashtype", UniValueType(UniValue::VSTR)},
},
true, true);
if (options.exists("sign")) {
sign = options["sign"].get_bool();
}
if (options.exists("bip32derivs")) {
bip32derivs = options["bip32derivs"].get_bool();
}
if (options.exists("finalize")) {
finalize = options["finalize"].get_bool();
}
if (options.exists("sighashtype")) {
nHashType = ParseSighashString(options["sighashtype"]);
}
if (request.params.size() > 2) {
// Same behaviour as too many args passed normally
throw std::runtime_error(self.ToString());
}
}
// Fill transaction with our data and also sign
bool sign = request.params[1].isNull() ? true : request.params[1].get_bool();
bool bip32derivs = request.params[3].isNull() ? true : request.params[3].get_bool();
bool finalize = request.params[4].isNull() ? true : request.params[4].get_bool();
bool complete = true;
if (sign) EnsureWalletIsUnlocked(*pwallet);

View File

@ -65,7 +65,7 @@ class HelpRpcTest(BitcoinTestFramework):
mapping_server = self.nodes[0].help("dump_all_command_conversions")
# Filter all RPCs whether they need conversion
mapping_server_conversion = [tuple(m[:3]) for m in mapping_server if not m[3]]
mapping_server_conversion = set(tuple(m[:3]) for m in mapping_server if not m[3])
# Only check if all RPC methods have been compiled (i.e. wallet is enabled)
if self.is_wallet_compiled() and sorted(mapping_client) != sorted(mapping_server_conversion):

View File

@ -216,6 +216,7 @@ class PSBTTest(BitcoinTestFramework):
# Sign the transaction but don't finalize
processed_psbt = self.nodes[0].walletprocesspsbt(psbt=psbtx, finalize=False)
assert_equal(processed_psbt, self.nodes[0].walletprocesspsbt(psbtx, {"finalize": False}))
assert "hex" not in processed_psbt
signed_psbt = processed_psbt['psbt']
@ -225,6 +226,7 @@ class PSBTTest(BitcoinTestFramework):
# Alternative method: sign AND finalize in one command
processed_finalized_psbt = self.nodes[0].walletprocesspsbt(psbt=psbtx, finalize=True)
assert_equal(processed_finalized_psbt, self.nodes[0].walletprocesspsbt(psbtx, {"finalize": True}))
finalized_psbt = processed_finalized_psbt['psbt']
finalized_psbt_hex = processed_finalized_psbt['hex']
assert signed_psbt != finalized_psbt
@ -430,11 +432,13 @@ class PSBTTest(BitcoinTestFramework):
# Update psbts, should only have data for one input and not the other
psbt1 = self.nodes[1].walletprocesspsbt(psbt_orig, False, "ALL")['psbt']
assert_equal(psbt1, self.nodes[1].walletprocesspsbt(psbt_orig, {"sign": False, "sighashtype": "ALL"})["psbt"])
psbt1_decoded = self.nodes[0].decodepsbt(psbt1)
assert psbt1_decoded['inputs'][0] and not psbt1_decoded['inputs'][1]
# Check that BIP32 path was added
assert "bip32_derivs" in psbt1_decoded['inputs'][0]
psbt2 = self.nodes[2].walletprocesspsbt(psbt_orig, False, "ALL", False)['psbt']
assert_equal(psbt2, self.nodes[2].walletprocesspsbt(psbt_orig, {"sign": False, "sighashtype": "ALL", "bip32derivs": False})["psbt"])
psbt2_decoded = self.nodes[0].decodepsbt(psbt2)
assert not psbt2_decoded['inputs'][0] and psbt2_decoded['inputs'][1]
# Check that BIP32 paths were not added
@ -679,6 +683,7 @@ class PSBTTest(BitcoinTestFramework):
# After update with wallet, only needs signing
updated = self.nodes[1].walletprocesspsbt(psbt, False, 'ALL', True)['psbt']
assert_equal(updated, self.nodes[1].walletprocesspsbt(psbt, {"sign": False, "sighashtype": 'ALL', "bip32derivs": True})["psbt"])
analyzed = self.nodes[0].analyzepsbt(updated)
assert analyzed['inputs'][0]['has_utxo'] and not analyzed['inputs'][0]['is_final'] and analyzed['inputs'][0]['next'] == 'signer' and analyzed['next'] == 'signer' and analyzed['inputs'][0]['missing']['signatures'][0] == addrinfo['embedded']['witness_program']