From 47237cd1938058b29fdec242c3a37611e255fda0 Mon Sep 17 00:00:00 2001 From: Ava Chow Date: Thu, 22 May 2025 17:29:21 -0700 Subject: [PATCH] wallet, rpc: Output wallet flags in getwalletinfo --- src/wallet/rpc/wallet.cpp | 25 ++++++++++++++++++++++--- src/wallet/wallet.h | 26 ++++++++++++++++++-------- test/functional/wallet_avoidreuse.py | 2 ++ test/functional/wallet_createwallet.py | 1 + 4 files changed, 43 insertions(+), 11 deletions(-) diff --git a/src/wallet/rpc/wallet.cpp b/src/wallet/rpc/wallet.cpp index c2db094fba5..ac371ab004e 100644 --- a/src/wallet/rpc/wallet.cpp +++ b/src/wallet/rpc/wallet.cpp @@ -62,6 +62,10 @@ static RPCHelpMan getwalletinfo() {RPCResult::Type::BOOL, "external_signer", "whether this wallet is configured to use an external signer such as a hardware wallet"}, {RPCResult::Type::BOOL, "blank", "Whether this wallet intentionally does not contain any keys, scripts, or descriptors"}, {RPCResult::Type::NUM_TIME, "birthtime", /*optional=*/true, "The start time for blocks scanning. It could be modified by (re)importing any descriptor with an earlier timestamp."}, + {RPCResult::Type::ARR, "flags", "The flags currently set on the wallet", + { + {RPCResult::Type::STR, "flag", "The name of the flag"}, + }}, RESULT_LAST_PROCESSED_BLOCK, }}, }, @@ -121,6 +125,21 @@ static RPCHelpMan getwalletinfo() obj.pushKV("birthtime", birthtime); } + // Push known flags + UniValue flags(UniValue::VARR); + uint64_t wallet_flags = pwallet->GetWalletFlags(); + for (uint64_t i = 0; i < 64; ++i) { + uint64_t flag = uint64_t{1} << i; + if (flag & wallet_flags) { + if (flag & KNOWN_WALLET_FLAGS) { + flags.push_back(WALLET_FLAG_TO_STRING.at(WalletFlags{flag})); + } else { + flags.push_back(strprintf("unknown_flag_%u", i)); + } + } + } + obj.pushKV("flags", flags); + AppendLastProcessedBlock(obj, *pwallet); return obj; }, @@ -263,7 +282,7 @@ static RPCHelpMan loadwallet() static RPCHelpMan setwalletflag() { std::string flags; - for (auto& it : WALLET_FLAG_MAP) + for (auto& it : STRING_TO_WALLET_FLAG) if (it.second & MUTABLE_WALLET_FLAGS) flags += (flags == "" ? "" : ", ") + it.first; @@ -294,11 +313,11 @@ static RPCHelpMan setwalletflag() std::string flag_str = request.params[0].get_str(); bool value = request.params[1].isNull() || request.params[1].get_bool(); - if (!WALLET_FLAG_MAP.count(flag_str)) { + if (!STRING_TO_WALLET_FLAG.count(flag_str)) { throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Unknown wallet flag: %s", flag_str)); } - auto flag = WALLET_FLAG_MAP.at(flag_str); + auto flag = STRING_TO_WALLET_FLAG.at(flag_str); if (!(flag & MUTABLE_WALLET_FLAGS)) { throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Wallet flag is immutable: %s", flag_str)); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 989db8e79df..51386678687 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -160,14 +160,24 @@ static constexpr uint64_t KNOWN_WALLET_FLAGS = static constexpr uint64_t MUTABLE_WALLET_FLAGS = WALLET_FLAG_AVOID_REUSE; -static const std::map WALLET_FLAG_MAP{ - {"avoid_reuse", WALLET_FLAG_AVOID_REUSE}, - {"blank", WALLET_FLAG_BLANK_WALLET}, - {"key_origin_metadata", WALLET_FLAG_KEY_ORIGIN_METADATA}, - {"last_hardened_xpub_cached", WALLET_FLAG_LAST_HARDENED_XPUB_CACHED}, - {"disable_private_keys", WALLET_FLAG_DISABLE_PRIVATE_KEYS}, - {"descriptor_wallet", WALLET_FLAG_DESCRIPTORS}, - {"external_signer", WALLET_FLAG_EXTERNAL_SIGNER} +static const std::map WALLET_FLAG_TO_STRING{ + {WALLET_FLAG_AVOID_REUSE, "avoid_reuse"}, + {WALLET_FLAG_BLANK_WALLET, "blank"}, + {WALLET_FLAG_KEY_ORIGIN_METADATA, "key_origin_metadata"}, + {WALLET_FLAG_LAST_HARDENED_XPUB_CACHED, "last_hardened_xpub_cached"}, + {WALLET_FLAG_DISABLE_PRIVATE_KEYS, "disable_private_keys"}, + {WALLET_FLAG_DESCRIPTORS, "descriptor_wallet"}, + {WALLET_FLAG_EXTERNAL_SIGNER, "external_signer"} +}; + +static const std::map STRING_TO_WALLET_FLAG{ + {WALLET_FLAG_TO_STRING.at(WALLET_FLAG_AVOID_REUSE), WALLET_FLAG_AVOID_REUSE}, + {WALLET_FLAG_TO_STRING.at(WALLET_FLAG_BLANK_WALLET), WALLET_FLAG_BLANK_WALLET}, + {WALLET_FLAG_TO_STRING.at(WALLET_FLAG_KEY_ORIGIN_METADATA), WALLET_FLAG_KEY_ORIGIN_METADATA}, + {WALLET_FLAG_TO_STRING.at(WALLET_FLAG_LAST_HARDENED_XPUB_CACHED), WALLET_FLAG_LAST_HARDENED_XPUB_CACHED}, + {WALLET_FLAG_TO_STRING.at(WALLET_FLAG_DISABLE_PRIVATE_KEYS), WALLET_FLAG_DISABLE_PRIVATE_KEYS}, + {WALLET_FLAG_TO_STRING.at(WALLET_FLAG_DESCRIPTORS), WALLET_FLAG_DESCRIPTORS}, + {WALLET_FLAG_TO_STRING.at(WALLET_FLAG_EXTERNAL_SIGNER), WALLET_FLAG_EXTERNAL_SIGNER} }; /** A wrapper to reserve an address from a wallet diff --git a/test/functional/wallet_avoidreuse.py b/test/functional/wallet_avoidreuse.py index 2ae153a937e..44fa16ff64c 100755 --- a/test/functional/wallet_avoidreuse.py +++ b/test/functional/wallet_avoidreuse.py @@ -104,7 +104,9 @@ class AvoidReuseTest(BitcoinTestFramework): # Flags should be node1.avoid_reuse=false, node2.avoid_reuse=true assert_equal(self.nodes[0].getwalletinfo()["avoid_reuse"], False) + assert_equal(sorted(self.nodes[0].getwalletinfo()["flags"]), sorted(["descriptor_wallet", "last_hardened_xpub_cached"])) assert_equal(self.nodes[1].getwalletinfo()["avoid_reuse"], True) + assert_equal(sorted(self.nodes[1].getwalletinfo()["flags"]), sorted(["descriptor_wallet", "last_hardened_xpub_cached", "avoid_reuse"])) self.restart_node(1) self.connect_nodes(0, 1) diff --git a/test/functional/wallet_createwallet.py b/test/functional/wallet_createwallet.py index 312d22fce48..a2e7aae33ea 100755 --- a/test/functional/wallet_createwallet.py +++ b/test/functional/wallet_createwallet.py @@ -44,6 +44,7 @@ class CreateWalletTest(BitcoinTestFramework): assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w1.getrawchangeaddress) import_res = w1.importdescriptors([{"desc": w0.getaddressinfo(address1)['desc'], "timestamp": "now"}]) assert_equal(import_res[0]["success"], True) + assert_equal(sorted(w1.getwalletinfo()["flags"]), sorted(["last_hardened_xpub_cached", "descriptor_wallet", "disable_private_keys"])) self.log.info('Test that private keys cannot be imported') privkey, pubkey = generate_keypair(wif=True)