aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Chow <github@achow101.com>2023-04-12 13:02:11 -0400
committerAndrew Chow <github@achow101.com>2023-04-12 13:09:23 -0400
commit6a167325f0d74ed1167705e30e86e5b8449582f1 (patch)
tree2c5baaa657e17336582691e21fa085b4ba42a09a
parent7f4ab67e7be615b2425a85fb65872fc2327f58c3 (diff)
parent7ccdd741fe1544c13b2a9b7baa5c5727e84d6e55 (diff)
downloadbitcoin-6a167325f0d74ed1167705e30e86e5b8449582f1.tar.xz
Merge bitcoin/bitcoin#27279: Add "warnings", deprecate "warning" in {create,load,unload,restore}wallet
7ccdd741fe1544c13b2a9b7baa5c5727e84d6e55 test: fix importmulti/importdescriptors assertion (Jon Atack) 19d888ce407f44d90785c456a1a3e2a6870e9245 rpc: move WALLET_FLAG_CAVEATS to the compilation unit of its caller (Jon Atack) 01df011ca2bf46ee4c988b03a130eea6df692325 doc: release note for wallet RPCs "warning" field deprecation (Jon Atack) 9ea8b3739a863b0ad87593639476b3cd712ff0dc test: createwallet "warning" field deprecation test (Jon Atack) 645d7f75ac1b40e4ea88119b3711f89943d35d6c rpc: deprecate "warning" field in {create,load,unload,restore}wallet (Jon Atack) 2f4a926e95e0379397859c3ba1b5711be5f09925 test: add test coverage for "warnings" field in createwallet (Jon Atack) 4a1e479ca612056761e6247dd5b715dcd6824413 rpc: add "warnings" field to RPCs {create,load,unload,restore}wallet (Jon Atack) 079d8cdda8eeebe199fb6592fca2630c37662731 rpc: extract wallet "warnings" fields to a util helper (Jon Atack) f73782a9032a462a71569e9424db9bf9eeababf3 doc: fix/improve warning helps in {create,load,unload,restore}wallet (Jon Atack) Pull request description: Based on discussion and concept ACKed in #27138, add a `warnings` field to RPCs createwallet, loadwallet, unloadwallet, and restorewallet as a JSON array of strings to replace the `warning` string field in these 4 RPCs. The idea is to more gracefully handle multiple warning messages and for consistency with other wallet RPCs. Then, deprecate the latter fields, which represent all the remaining RPC `warning` fields. The first commit https://github.com/bitcoin/bitcoin/pull/27279/commits/f73782a9032a462a71569e9424db9bf9eeababf3 implements https://github.com/bitcoin/bitcoin/pull/27138#issuecomment-1474789198 as an alternative to #27138. One of those two could potentially be backported to our currently supported releases. ACKs for top commit: achow101: ACK 7ccdd741fe1544c13b2a9b7baa5c5727e84d6e55 1440000bytes: utACK https://github.com/bitcoin/bitcoin/pull/27279/commits/7ccdd741fe1544c13b2a9b7baa5c5727e84d6e55 vasild: ACK 7ccdd741fe1544c13b2a9b7baa5c5727e84d6e55 pinheadmz: re-ACK 7ccdd741fe1544c13b2a9b7baa5c5727e84d6e55 Tree-SHA512: 314e0a4c41fa383d95e2817bfacf359d449e460529d235c3eb902851e2f4eacbabe646d9a5a4beabc4964cdfabf6397ed8301366a58d344a2f787f83b75e9d64
-rw-r--r--doc/release-notes-27279.md10
-rw-r--r--src/rpc/output_script.cpp2
-rw-r--r--src/rpc/util.cpp23
-rw-r--r--src/rpc/util.h9
-rw-r--r--src/wallet/rpc/addresses.cpp2
-rw-r--r--src/wallet/rpc/backup.cpp15
-rw-r--r--src/wallet/rpc/wallet.cpp45
-rw-r--r--src/wallet/wallet.cpp7
-rw-r--r--src/wallet/wallet.h2
-rwxr-xr-xtest/functional/wallet_backwards_compatibility.py7
-rwxr-xr-xtest/functional/wallet_createwallet.py31
11 files changed, 125 insertions, 28 deletions
diff --git a/doc/release-notes-27279.md b/doc/release-notes-27279.md
new file mode 100644
index 0000000000..b664aee755
--- /dev/null
+++ b/doc/release-notes-27279.md
@@ -0,0 +1,10 @@
+Wallet
+------
+
+- In the createwallet, loadwallet, unloadwallet, and restorewallet RPCs, the
+ "warning" string field is deprecated in favor of a "warnings" field that
+ returns a JSON array of strings to better handle multiple warning messages and
+ for consistency with other wallet RPCs. The "warning" field will be fully
+ removed from these RPCs in v26. It can be temporarily re-enabled during the
+ deprecation period by launching bitcoind with the configuration option
+ `-deprecatedrpc=walletwarningfield`. (#27279)
diff --git a/src/rpc/output_script.cpp b/src/rpc/output_script.cpp
index bb04f58424..990ec3ab0c 100644
--- a/src/rpc/output_script.cpp
+++ b/src/rpc/output_script.cpp
@@ -162,7 +162,7 @@ static RPCHelpMan createmultisig()
// Only warns if the user has explicitly chosen an address type we cannot generate
warnings.push_back("Unable to make chosen address type, please ensure no uncompressed public keys are present.");
}
- if (!warnings.empty()) result.pushKV("warnings", warnings);
+ PushWarnings(warnings, result);
return result;
},
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index ee9e3544a4..f95ac4cb4b 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -1174,3 +1174,26 @@ UniValue GetServicesNames(ServiceFlags services)
return servicesNames;
}
+
+/** Convert a vector of bilingual strings to a UniValue::VARR containing their original untranslated values. */
+[[nodiscard]] static UniValue BilingualStringsToUniValue(const std::vector<bilingual_str>& bilingual_strings)
+{
+ CHECK_NONFATAL(!bilingual_strings.empty());
+ UniValue result{UniValue::VARR};
+ for (const auto& s : bilingual_strings) {
+ result.push_back(s.original);
+ }
+ return result;
+}
+
+void PushWarnings(const UniValue& warnings, UniValue& obj)
+{
+ if (warnings.empty()) return;
+ obj.pushKV("warnings", warnings);
+}
+
+void PushWarnings(const std::vector<bilingual_str>& warnings, UniValue& obj)
+{
+ if (warnings.empty()) return;
+ obj.pushKV("warnings", BilingualStringsToUniValue(warnings));
+}
diff --git a/src/rpc/util.h b/src/rpc/util.h
index e3783c8f76..bb5c30a2f4 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -381,4 +381,13 @@ private:
const RPCExamples m_examples;
};
+/**
+ * Push warning messages to an RPC "warnings" field as a JSON array of strings.
+ *
+ * @param[in] warnings Warning messages to push.
+ * @param[out] obj UniValue object to push the warnings array object to.
+ */
+void PushWarnings(const UniValue& warnings, UniValue& obj);
+void PushWarnings(const std::vector<bilingual_str>& warnings, UniValue& obj);
+
#endif // BITCOIN_RPC_UTIL_H
diff --git a/src/wallet/rpc/addresses.cpp b/src/wallet/rpc/addresses.cpp
index 838d66c1e1..633f606747 100644
--- a/src/wallet/rpc/addresses.cpp
+++ b/src/wallet/rpc/addresses.cpp
@@ -300,7 +300,7 @@ RPCHelpMan addmultisigaddress()
// Only warns if the user has explicitly chosen an address type we cannot generate
warnings.push_back("Unable to make chosen address type, please ensure no uncompressed public keys are present.");
}
- if (!warnings.empty()) result.pushKV("warnings", warnings);
+ PushWarnings(warnings, result);
return result;
},
diff --git a/src/wallet/rpc/backup.cpp b/src/wallet/rpc/backup.cpp
index 31a8954259..d9712f05d2 100644
--- a/src/wallet/rpc/backup.cpp
+++ b/src/wallet/rpc/backup.cpp
@@ -1225,7 +1225,7 @@ static UniValue ProcessImport(CWallet& wallet, const UniValue& data, const int64
result.pushKV("error", JSONRPCError(RPC_MISC_ERROR, "Missing required fields"));
}
- if (warnings.size()) result.pushKV("warnings", warnings);
+ PushWarnings(warnings, result);
return result;
}
@@ -1579,7 +1579,7 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
result.pushKV("success", UniValue(false));
result.pushKV("error", e);
}
- if (warnings.size()) result.pushKV("warnings", warnings);
+ PushWarnings(warnings, result);
return result;
}
@@ -1903,7 +1903,11 @@ RPCHelpMan restorewallet()
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR, "name", "The wallet name if restored successfully."},
- {RPCResult::Type::STR, "warning", "Warning message if wallet was not loaded cleanly."},
+ {RPCResult::Type::STR, "warning", /*optional=*/true, "Warning messages, if any, related to restoring the wallet. Multiple messages will be delimited by newlines. (DEPRECATED, returned only if config option -deprecatedrpc=walletwarningfield is passed.)"},
+ {RPCResult::Type::ARR, "warnings", /*optional=*/true, "Warning messages, if any, related to restoring the wallet.",
+ {
+ {RPCResult::Type::STR, "", ""},
+ }},
}
},
RPCExamples{
@@ -1933,7 +1937,10 @@ RPCHelpMan restorewallet()
UniValue obj(UniValue::VOBJ);
obj.pushKV("name", wallet->GetName());
- obj.pushKV("warning", Join(warnings, Untranslated("\n")).original);
+ if (wallet->chain().rpcEnableDeprecated("walletwarningfield")) {
+ obj.pushKV("warning", Join(warnings, Untranslated("\n")).original);
+ }
+ PushWarnings(warnings, obj);
return obj;
diff --git a/src/wallet/rpc/wallet.cpp b/src/wallet/rpc/wallet.cpp
index 16595267b4..ea3507bc75 100644
--- a/src/wallet/rpc/wallet.cpp
+++ b/src/wallet/rpc/wallet.cpp
@@ -13,6 +13,7 @@
#include <wallet/rpc/wallet.h>
#include <wallet/rpc/util.h>
#include <wallet/wallet.h>
+#include <wallet/walletutil.h>
#include <optional>
@@ -20,6 +21,14 @@
namespace wallet {
+
+static const std::map<uint64_t, std::string> WALLET_FLAG_CAVEATS{
+ {WALLET_FLAG_AVOID_REUSE,
+ "You need to rescan the blockchain in order to correctly mark used "
+ "destinations in the past. Until this is done, some destinations may "
+ "be considered unused, even if the opposite is the case."},
+};
+
/** Checks if a CKey is in the given CWallet compressed or otherwise*/
bool HaveKey(const SigningProvider& wallet, const CKey& key)
{
@@ -207,7 +216,11 @@ static RPCHelpMan loadwallet()
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR, "name", "The wallet name if loaded successfully."},
- {RPCResult::Type::STR, "warning", "Warning message if wallet was not loaded cleanly."},
+ {RPCResult::Type::STR, "warning", /*optional=*/true, "Warning messages, if any, related to loading the wallet. Multiple messages will be delimited by newlines. (DEPRECATED, returned only if config option -deprecatedrpc=walletwarningfield is passed.)"},
+ {RPCResult::Type::ARR, "warnings", /*optional=*/true, "Warning messages, if any, related to loading the wallet.",
+ {
+ {RPCResult::Type::STR, "", ""},
+ }},
}
},
RPCExamples{
@@ -240,7 +253,10 @@ static RPCHelpMan loadwallet()
UniValue obj(UniValue::VOBJ);
obj.pushKV("name", wallet->GetName());
- obj.pushKV("warning", Join(warnings, Untranslated("\n")).original);
+ if (wallet->chain().rpcEnableDeprecated("walletwarningfield")) {
+ obj.pushKV("warning", Join(warnings, Untranslated("\n")).original);
+ }
+ PushWarnings(warnings, obj);
return obj;
},
@@ -335,7 +351,11 @@ static RPCHelpMan createwallet()
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR, "name", "The wallet name if created successfully. If the wallet was created using a full path, the wallet_name will be the full path."},
- {RPCResult::Type::STR, "warning", "Warning message if wallet was not loaded cleanly."},
+ {RPCResult::Type::STR, "warning", /*optional=*/true, "Warning messages, if any, related to creating the wallet. Multiple messages will be delimited by newlines. (DEPRECATED, returned only if config option -deprecatedrpc=walletwarningfield is passed.)"},
+ {RPCResult::Type::ARR, "warnings", /*optional=*/true, "Warning messages, if any, related to creating the wallet.",
+ {
+ {RPCResult::Type::STR, "", ""},
+ }},
}
},
RPCExamples{
@@ -405,7 +425,10 @@ static RPCHelpMan createwallet()
UniValue obj(UniValue::VOBJ);
obj.pushKV("name", wallet->GetName());
- obj.pushKV("warning", Join(warnings, Untranslated("\n")).original);
+ if (wallet->chain().rpcEnableDeprecated("walletwarningfield")) {
+ obj.pushKV("warning", Join(warnings, Untranslated("\n")).original);
+ }
+ PushWarnings(warnings, obj);
return obj;
},
@@ -422,7 +445,11 @@ static RPCHelpMan unloadwallet()
{"load_on_startup", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
},
RPCResult{RPCResult::Type::OBJ, "", "", {
- {RPCResult::Type::STR, "warning", "Warning message if wallet was not unloaded cleanly."},
+ {RPCResult::Type::STR, "warning", /*optional=*/true, "Warning messages, if any, related to unloading the wallet. Multiple messages will be delimited by newlines. (DEPRECATED, returned only if config option -deprecatedrpc=walletwarningfield is passed.)"},
+ {RPCResult::Type::ARR, "warnings", /*optional=*/true, "Warning messages, if any, related to unloading the wallet.",
+ {
+ {RPCResult::Type::STR, "", ""},
+ }},
}},
RPCExamples{
HelpExampleCli("unloadwallet", "wallet_name")
@@ -460,11 +487,13 @@ static RPCHelpMan unloadwallet()
throw JSONRPCError(RPC_MISC_ERROR, "Requested wallet already unloaded");
}
}
+ UniValue result(UniValue::VOBJ);
+ if (wallet->chain().rpcEnableDeprecated("walletwarningfield")) {
+ result.pushKV("warning", Join(warnings, Untranslated("\n")).original);
+ }
+ PushWarnings(warnings, result);
UnloadWallet(std::move(wallet));
-
- UniValue result(UniValue::VOBJ);
- result.pushKV("warning", Join(warnings, Untranslated("\n")).original);
return result;
},
};
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 6df0d12df6..4e8c0c0e5e 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -52,13 +52,6 @@
using interfaces::FoundBlock;
namespace wallet {
-const std::map<uint64_t,std::string> WALLET_FLAG_CAVEATS{
- {WALLET_FLAG_AVOID_REUSE,
- "You need to rescan the blockchain in order to correctly mark used "
- "destinations in the past. Until this is done, some destinations may "
- "be considered unused, even if the opposite is the case."
- },
-};
bool AddWalletSetting(interfaces::Chain& chain, const std::string& wallet_name)
{
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 586f215499..581a6bd9cb 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -146,8 +146,6 @@ static const std::map<std::string,WalletFlags> WALLET_FLAG_MAP{
{"external_signer", WALLET_FLAG_EXTERNAL_SIGNER}
};
-extern const std::map<uint64_t,std::string> WALLET_FLAG_CAVEATS;
-
/** A wrapper to reserve an address from a wallet
*
* ReserveDestination is used to reserve an address.
diff --git a/test/functional/wallet_backwards_compatibility.py b/test/functional/wallet_backwards_compatibility.py
index 76aac3e486..a6401a76c1 100755
--- a/test/functional/wallet_backwards_compatibility.py
+++ b/test/functional/wallet_backwards_compatibility.py
@@ -265,7 +265,12 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
)
load_res = node_master.loadwallet("u1_v16")
# Make sure this wallet opens without warnings. See https://github.com/bitcoin/bitcoin/pull/19054
- assert_equal(load_res['warning'], '')
+ if int(node_master.getnetworkinfo()["version"]) >= 249900:
+ # loadwallet#warnings (added in v25) -- only present if there is a warning
+ assert "warnings" not in load_res
+ else:
+ # loadwallet#warning (deprecated in v25) -- always present, but empty string if no warning
+ assert_equal(load_res["warning"], '')
wallet = node_master.get_wallet_rpc("u1_v16")
info = wallet.getaddressinfo(v16_addr)
descriptor = f"wpkh([{info['hdmasterfingerprint']}{hdkeypath[1:]}]{v16_pubkey})"
diff --git a/test/functional/wallet_createwallet.py b/test/functional/wallet_createwallet.py
index 22c491441b..58cc6befbd 100755
--- a/test/functional/wallet_createwallet.py
+++ b/test/functional/wallet_createwallet.py
@@ -15,12 +15,17 @@ from test_framework.util import (
)
from test_framework.wallet_util import bytes_to_wif, generate_wif_key
+EMPTY_PASSPHRASE_MSG = "Empty string given as passphrase, wallet will not be encrypted."
+LEGACY_WALLET_MSG = "Wallet created successfully. The legacy wallet type is being deprecated and support for creating and opening legacy wallets will be removed in the future."
+
+
class CreateWalletTest(BitcoinTestFramework):
def add_options(self, parser):
self.add_wallet_options(parser)
def set_test_params(self):
self.num_nodes = 1
+ self.extra_args = [["-deprecatedrpc=walletwarningfield"]]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -55,7 +60,7 @@ class CreateWalletTest(BitcoinTestFramework):
else:
result = w1.importmulti([{'scriptPubKey': {'address': key_to_p2wpkh(eckey.get_pubkey().get_bytes())}, 'timestamp': 'now', 'keys': [privkey]}])
assert not result[0]['success']
- assert 'warning' not in result[0]
+ assert 'warnings' not in result[0]
assert_equal(result[0]['error']['code'], -4)
assert_equal(result[0]['error']['message'], 'Cannot import private keys to a wallet with private keys disabled')
@@ -159,7 +164,9 @@ class CreateWalletTest(BitcoinTestFramework):
assert_equal(walletinfo['keypoolsize_hd_internal'], keys)
# Allow empty passphrase, but there should be a warning
resp = self.nodes[0].createwallet(wallet_name='w7', disable_private_keys=False, blank=False, passphrase='')
- assert 'Empty string given as passphrase, wallet will not be encrypted.' in resp['warning']
+ assert_equal(resp["warning"], EMPTY_PASSPHRASE_MSG if self.options.descriptors else f"{EMPTY_PASSPHRASE_MSG}\n{LEGACY_WALLET_MSG}")
+ assert_equal(resp["warnings"], [EMPTY_PASSPHRASE_MSG] if self.options.descriptors else [EMPTY_PASSPHRASE_MSG, LEGACY_WALLET_MSG])
+
w7 = node.get_wallet_rpc('w7')
assert_raises_rpc_error(-15, 'Error: running with an unencrypted wallet, but walletpassphrase was called.', w7.walletpassphrase, '', 60)
@@ -174,8 +181,24 @@ class CreateWalletTest(BitcoinTestFramework):
if self.is_bdb_compiled():
self.log.info("Test legacy wallet deprecation")
- res = self.nodes[0].createwallet(wallet_name="legacy_w0", descriptors=False, passphrase=None)
- assert_equal(res["warning"], "Wallet created successfully. The legacy wallet type is being deprecated and support for creating and opening legacy wallets will be removed in the future.")
+ result = self.nodes[0].createwallet(wallet_name="legacy_w0", descriptors=False, passphrase=None)
+ assert_equal(result, {
+ "name": "legacy_w0",
+ "warning": LEGACY_WALLET_MSG,
+ "warnings": [LEGACY_WALLET_MSG],
+ })
+ result = self.nodes[0].createwallet(wallet_name="legacy_w1", descriptors=False, passphrase="")
+ assert_equal(result, {
+ "name": "legacy_w1",
+ "warning": f"{EMPTY_PASSPHRASE_MSG}\n{LEGACY_WALLET_MSG}",
+ "warnings": [EMPTY_PASSPHRASE_MSG, LEGACY_WALLET_MSG],
+ })
+
+ self.log.info('Test "warning" field deprecation, i.e. not returned without -deprecatedrpc=walletwarningfield')
+ self.restart_node(0, extra_args=[])
+ result = self.nodes[0].createwallet(wallet_name="w7_again", disable_private_keys=False, blank=False, passphrase="")
+ assert "warning" not in result
+
if __name__ == '__main__':
CreateWalletTest().main()