Skip to content

Commit 6b6e854

Browse files
committed
Merge #9662: Add createwallet "disableprivatekeys" option: a sane mode for watchonly-wallets
a3fa4d6 QA: Fix bug in -usecli logic that converts booleans to non-lowercase strings (Jonas Schnelli) 4704e5f [QA] add createwallet disableprivatekey test (Jonas Schnelli) c7b8f34 [Qt] Disable creating receive addresses when private keys are disabled (Jonas Schnelli) 2f15c2b Add disable privatekeys option to createwallet (Jonas Schnelli) cebefba Add option to disable private keys during internal wallet creation (Jonas Schnelli) 9995a60 Add facility to store wallet flags (64 bits) (Jonas Schnelli) Pull request description: This mode ('createwallet {"disableprivatekeys": true}') is intended for a sane pure watch-only mode, ideal for a use-case where one likes to use Bitcoin-Core in conjunction with a hardware-wallet or another solutions for cold-storage. Since we have support for custom change addresses in `fundrawtransaction`, pure watch-only wallets including coin-selection are possible and do make sense for some use cases. This new mode disables all forms of private key generation and ensure that no mix between hot and cold keys are possible. Tree-SHA512: 3ebe7e8d54c4d4e5f790c348d4c292d456f573960a5b04d69ca5ef43a9217c7e7671761c6968cdc56f9a8bc235f3badd358576651af9f10855a0eb731f3fc508
2 parents aba2e66 + a3fa4d6 commit 6b6e854

File tree

15 files changed

+203
-32
lines changed

15 files changed

+203
-32
lines changed

src/interfaces/wallet.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,7 @@ class WalletImpl : public Wallet
426426
}
427427
unsigned int getConfirmTarget() override { return m_wallet.m_confirm_target; }
428428
bool hdEnabled() override { return m_wallet.IsHDEnabled(); }
429+
bool IsWalletFlagSet(uint64_t flag) override { return m_wallet.IsWalletFlagSet(flag); }
429430
OutputType getDefaultAddressType() override { return m_wallet.m_default_address_type; }
430431
OutputType getDefaultChangeType() override { return m_wallet.m_default_change_type; }
431432
std::unique_ptr<Handler> handleUnload(UnloadFn fn) override

src/interfaces/wallet.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,9 @@ class Wallet
236236
// Return whether HD enabled.
237237
virtual bool hdEnabled() = 0;
238238

239+
// check if a certain wallet flag is set.
240+
virtual bool IsWalletFlagSet(uint64_t flag) = 0;
241+
239242
// Get default address type.
240243
virtual OutputType getDefaultAddressType() = 0;
241244

src/qt/receivecoinsdialog.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ void ReceiveCoinsDialog::setModel(WalletModel *_model)
9999
} else {
100100
ui->useBech32->setCheckState(Qt::Unchecked);
101101
}
102+
103+
// eventually disable the main receive button if private key operations are disabled
104+
ui->receiveButton->setEnabled(!model->privateKeysDisabled());
102105
}
103106
}
104107

src/qt/walletmodel.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,11 @@ bool WalletModel::isWalletEnabled()
558558
return !gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET);
559559
}
560560

561+
bool WalletModel::privateKeysDisabled() const
562+
{
563+
return m_wallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
564+
}
565+
561566
QString WalletModel::getWalletName() const
562567
{
563568
return QString::fromStdString(m_wallet->getWalletName());

src/qt/walletmodel.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ class WalletModel : public QObject
197197
bool bumpFee(uint256 hash);
198198

199199
static bool isWalletEnabled();
200+
bool privateKeysDisabled() const;
200201

201202
interfaces::Node& node() const { return m_node; }
202203
interfaces::Wallet& wallet() const { return *m_wallet; }

src/rpc/client.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
172172
{ "echojson", 9, "arg9" },
173173
{ "rescanblockchain", 0, "start_height"},
174174
{ "rescanblockchain", 1, "stop_height"},
175+
{ "createwallet", 1, "disable_private_keys"},
175176
};
176177

177178
class CRPCConvertTable

src/wallet/rpcwallet.cpp

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,10 @@ static UniValue getnewaddress(const JSONRPCRequest& request)
161161
+ HelpExampleRpc("getnewaddress", "")
162162
);
163163

164+
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
165+
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
166+
}
167+
164168
LOCK2(cs_main, pwallet->cs_wallet);
165169

166170
// Parse the label first so we don't generate a key if there's an error
@@ -268,6 +272,10 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request)
268272
+ HelpExampleRpc("getrawchangeaddress", "")
269273
);
270274

275+
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
276+
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
277+
}
278+
271279
LOCK2(cs_main, pwallet->cs_wallet);
272280

273281
if (!pwallet->IsLocked()) {
@@ -2506,6 +2514,10 @@ static UniValue keypoolrefill(const JSONRPCRequest& request)
25062514
+ HelpExampleRpc("keypoolrefill", "")
25072515
);
25082516

2517+
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
2518+
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
2519+
}
2520+
25092521
LOCK2(cs_main, pwallet->cs_wallet);
25102522

25112523
// 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool
@@ -2990,19 +3002,20 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
29903002
"Returns an object containing various wallet state info.\n"
29913003
"\nResult:\n"
29923004
"{\n"
2993-
" \"walletname\": xxxxx, (string) the wallet name\n"
2994-
" \"walletversion\": xxxxx, (numeric) the wallet version\n"
2995-
" \"balance\": xxxxxxx, (numeric) the total confirmed balance of the wallet in " + CURRENCY_UNIT + "\n"
2996-
" \"unconfirmed_balance\": xxx, (numeric) the total unconfirmed balance of the wallet in " + CURRENCY_UNIT + "\n"
2997-
" \"immature_balance\": xxxxxx, (numeric) the total immature balance of the wallet in " + CURRENCY_UNIT + "\n"
2998-
" \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\n"
2999-
" \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since Unix epoch) of the oldest pre-generated key in the key pool\n"
3000-
" \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated (only counts external keys)\n"
3001-
" \"keypoolsize_hd_internal\": xxxx, (numeric) how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used)\n"
3002-
" \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n"
3003-
" \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n"
3004-
" \"hdseedid\": \"<hash160>\" (string, optional) the Hash160 of the HD seed (only present when HD is enabled)\n"
3005-
" \"hdmasterkeyid\": \"<hash160>\" (string, optional) alias for hdseedid retained for backwards-compatibility. Will be removed in V0.18.\n"
3005+
" \"walletname\": xxxxx, (string) the wallet name\n"
3006+
" \"walletversion\": xxxxx, (numeric) the wallet version\n"
3007+
" \"balance\": xxxxxxx, (numeric) the total confirmed balance of the wallet in " + CURRENCY_UNIT + "\n"
3008+
" \"unconfirmed_balance\": xxx, (numeric) the total unconfirmed balance of the wallet in " + CURRENCY_UNIT + "\n"
3009+
" \"immature_balance\": xxxxxx, (numeric) the total immature balance of the wallet in " + CURRENCY_UNIT + "\n"
3010+
" \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\n"
3011+
" \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since Unix epoch) of the oldest pre-generated key in the key pool\n"
3012+
" \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated (only counts external keys)\n"
3013+
" \"keypoolsize_hd_internal\": xxxx, (numeric) how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used)\n"
3014+
" \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n"
3015+
" \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n"
3016+
" \"hdseedid\": \"<hash160>\" (string, optional) the Hash160 of the HD seed (only present when HD is enabled)\n"
3017+
" \"hdmasterkeyid\": \"<hash160>\" (string, optional) alias for hdseedid retained for backwards-compatibility. Will be removed in V0.18.\n"
3018+
" \"private_keys_enabled\": true|false (boolean) false if privatekeys are disabled for this wallet (enforced watch-only wallet)\n"
30063019
"}\n"
30073020
"\nExamples:\n"
30083021
+ HelpExampleCli("getwalletinfo", "")
@@ -3038,6 +3051,7 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
30383051
obj.pushKV("hdseedid", seed_id.GetHex());
30393052
obj.pushKV("hdmasterkeyid", seed_id.GetHex());
30403053
}
3054+
obj.pushKV("private_keys_enabled", !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
30413055
return obj;
30423056
}
30433057

@@ -3128,12 +3142,13 @@ static UniValue loadwallet(const JSONRPCRequest& request)
31283142

31293143
static UniValue createwallet(const JSONRPCRequest& request)
31303144
{
3131-
if (request.fHelp || request.params.size() != 1) {
3145+
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
31323146
throw std::runtime_error(
3133-
"createwallet \"wallet_name\"\n"
3147+
"createwallet \"wallet_name\" ( disable_private_keys )\n"
31343148
"\nCreates and loads a new wallet.\n"
31353149
"\nArguments:\n"
3136-
"1. \"wallet_name\" (string, required) The name for the new wallet. If this is a path, the wallet will be created at the path location.\n"
3150+
"1. \"wallet_name\" (string, required) The name for the new wallet. If this is a path, the wallet will be created at the path location.\n"
3151+
"2. disable_private_keys (boolean, optional, default: false) Disable the possibility of private keys (only watchonlys are possible in this mode).\n"
31373152
"\nResult:\n"
31383153
"{\n"
31393154
" \"name\" : <wallet_name>, (string) The wallet name if created successfully. If the wallet was created using a full path, the wallet_name will be the full path.\n"
@@ -3148,6 +3163,11 @@ static UniValue createwallet(const JSONRPCRequest& request)
31483163
std::string error;
31493164
std::string warning;
31503165

3166+
bool disable_privatekeys = false;
3167+
if (!request.params[1].isNull()) {
3168+
disable_privatekeys = request.params[1].get_bool();
3169+
}
3170+
31513171
fs::path wallet_path = fs::absolute(wallet_name, GetWalletDir());
31523172
if (fs::symlink_status(wallet_path).type() != fs::file_not_found) {
31533173
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet " + wallet_name + " already exists.");
@@ -3158,7 +3178,7 @@ static UniValue createwallet(const JSONRPCRequest& request)
31583178
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet file verification failed: " + error);
31593179
}
31603180

3161-
std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(wallet_name, fs::absolute(wallet_name, GetWalletDir()));
3181+
std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(wallet_name, fs::absolute(wallet_name, GetWalletDir()), (disable_privatekeys ? (uint64_t)WALLET_FLAG_DISABLE_PRIVATE_KEYS : 0));
31623182
if (!wallet) {
31633183
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet creation failed.");
31643184
}
@@ -4756,7 +4776,7 @@ static const CRPCCommand commands[] =
47564776
{ "hidden", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} },
47574777
{ "wallet", "backupwallet", &backupwallet, {"destination"} },
47584778
{ "wallet", "bumpfee", &bumpfee, {"txid", "options"} },
4759-
{ "wallet", "createwallet", &createwallet, {"wallet_name"} },
4779+
{ "wallet", "createwallet", &createwallet, {"wallet_name", "disable_private_keys"} },
47604780
{ "wallet", "dumpprivkey", &dumpprivkey, {"address"} },
47614781
{ "wallet", "dumpwallet", &dumpwallet, {"filename"} },
47624782
{ "wallet", "encryptwallet", &encryptwallet, {"passphrase"} },

src/wallet/test/wallet_tests.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,4 +365,13 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
365365
BOOST_CHECK_EQUAL(list.begin()->second.size(), 2U);
366366
}
367367

368+
BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys, TestChain100Setup)
369+
{
370+
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>("dummy", WalletDatabase::CreateDummy());
371+
wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
372+
BOOST_CHECK(!wallet->TopUpKeyPool(1000));
373+
CPubKey pubkey;
374+
BOOST_CHECK(!wallet->GetKeyFromPool(pubkey, false));
375+
}
376+
368377
BOOST_AUTO_TEST_SUITE_END()

src/wallet/wallet.cpp

Lines changed: 66 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
164164

165165
CPubKey CWallet::GenerateNewKey(WalletBatch &batch, bool internal)
166166
{
167+
assert(!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
167168
AssertLockHeld(cs_wallet); // mapKeyMetadata
168169
bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets
169170

@@ -1444,6 +1445,7 @@ CAmount CWallet::GetChange(const CTransaction& tx) const
14441445

14451446
CPubKey CWallet::GenerateNewSeed()
14461447
{
1448+
assert(!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
14471449
CKey key;
14481450
key.MakeNewKey(true);
14491451
return DeriveNewSeed(key);
@@ -1505,6 +1507,34 @@ bool CWallet::IsHDEnabled() const
15051507
return !hdChain.seed_id.IsNull();
15061508
}
15071509

1510+
void CWallet::SetWalletFlag(uint64_t flags)
1511+
{
1512+
LOCK(cs_wallet);
1513+
m_wallet_flags |= flags;
1514+
if (!WalletBatch(*database).WriteWalletFlags(m_wallet_flags))
1515+
throw std::runtime_error(std::string(__func__) + ": writing wallet flags failed");
1516+
}
1517+
1518+
bool CWallet::IsWalletFlagSet(uint64_t flag)
1519+
{
1520+
return (m_wallet_flags & flag);
1521+
}
1522+
1523+
bool CWallet::SetWalletFlags(uint64_t overwriteFlags, bool memonly)
1524+
{
1525+
LOCK(cs_wallet);
1526+
m_wallet_flags = overwriteFlags;
1527+
if (((overwriteFlags & g_known_wallet_flags) >> 32) ^ (overwriteFlags >> 32)) {
1528+
// contains unknown non-tolerable wallet flags
1529+
return false;
1530+
}
1531+
if (!memonly && !WalletBatch(*database).WriteWalletFlags(m_wallet_flags)) {
1532+
throw std::runtime_error(std::string(__func__) + ": writing wallet flags failed");
1533+
}
1534+
1535+
return true;
1536+
}
1537+
15081538
int64_t CWalletTx::GetTxTime() const
15091539
{
15101540
int64_t n = nTimeSmart;
@@ -2720,6 +2750,10 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac
27202750
// post-backup change.
27212751

27222752
// Reserve a new key pair from key pool
2753+
if (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
2754+
strFailReason = _("Can't generate a change-address key. Private keys are disabled for this wallet.");
2755+
return false;
2756+
}
27232757
CPubKey vchPubKey;
27242758
bool ret;
27252759
ret = reservekey.GetReservedKey(vchPubKey, true);
@@ -3120,7 +3154,7 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
31203154
{
31213155
LOCK(cs_KeyStore);
31223156
// This wallet is in its first run if all of these are empty
3123-
fFirstRunRet = mapKeys.empty() && mapCryptedKeys.empty() && mapWatchKeys.empty() && setWatchOnly.empty() && mapScripts.empty();
3157+
fFirstRunRet = mapKeys.empty() && mapCryptedKeys.empty() && mapWatchKeys.empty() && setWatchOnly.empty() && mapScripts.empty() && !IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
31243158
}
31253159

31263160
if (nLoadWalletRet != DBErrors::LOAD_OK)
@@ -3244,6 +3278,9 @@ const std::string& CWallet::GetLabelName(const CScript& scriptPubKey) const
32443278
*/
32453279
bool CWallet::NewKeyPool()
32463280
{
3281+
if (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
3282+
return false;
3283+
}
32473284
{
32483285
LOCK(cs_wallet);
32493286
WalletBatch batch(*database);
@@ -3302,6 +3339,9 @@ void CWallet::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool)
33023339

33033340
bool CWallet::TopUpKeyPool(unsigned int kpSize)
33043341
{
3342+
if (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
3343+
return false;
3344+
}
33053345
{
33063346
LOCK(cs_wallet);
33073347

@@ -3426,6 +3466,10 @@ void CWallet::ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey)
34263466

34273467
bool CWallet::GetKeyFromPool(CPubKey& result, bool internal)
34283468
{
3469+
if (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
3470+
return false;
3471+
}
3472+
34293473
CKeyPool keypool;
34303474
{
34313475
LOCK(cs_wallet);
@@ -3965,7 +4009,7 @@ bool CWallet::Verify(std::string wallet_file, bool salvage_wallet, std::string&
39654009
return WalletBatch::VerifyDatabaseFile(wallet_path, warning_string, error_string);
39664010
}
39674011

3968-
std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name, const fs::path& path)
4012+
std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name, const fs::path& path, uint64_t wallet_creation_flags)
39694013
{
39704014
const std::string& walletFile = name;
39714015

@@ -4090,18 +4134,33 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
40904134
}
40914135
walletInstance->SetMinVersion(FEATURE_LATEST);
40924136

4093-
// generate a new seed
4094-
CPubKey seed = walletInstance->GenerateNewSeed();
4095-
if (!walletInstance->SetHDSeed(seed))
4096-
throw std::runtime_error(std::string(__func__) + ": Storing HD seed failed");
4137+
if ((wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
4138+
//selective allow to set flags
4139+
walletInstance->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
4140+
} else {
4141+
// generate a new seed
4142+
CPubKey seed = walletInstance->GenerateNewSeed();
4143+
if (!walletInstance->SetHDSeed(seed)) {
4144+
throw std::runtime_error(std::string(__func__) + ": Storing HD seed failed");
4145+
}
4146+
}
40974147

40984148
// Top up the keypool
4099-
if (!walletInstance->TopUpKeyPool()) {
4149+
if (!walletInstance->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !walletInstance->TopUpKeyPool()) {
41004150
InitError(_("Unable to generate initial keys") += "\n");
41014151
return nullptr;
41024152
}
41034153

41044154
walletInstance->ChainStateFlushed(chainActive.GetLocator());
4155+
} else if (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS) {
4156+
// Make it impossible to disable private keys after creation
4157+
InitError(strprintf(_("Error loading %s: Private keys can only be disabled during creation"), walletFile));
4158+
return NULL;
4159+
} else if (walletInstance->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
4160+
LOCK(walletInstance->cs_KeyStore);
4161+
if (!walletInstance->mapKeys.empty() || !walletInstance->mapCryptedKeys.empty()) {
4162+
InitWarning(strprintf(_("Warning: Private keys detected in wallet {%s} with disabled private keys"), walletFile));
4163+
}
41054164
} else if (gArgs.IsArgSet("-usehd")) {
41064165
bool useHD = gArgs.GetBoolArg("-usehd", true);
41074166
if (walletInstance->IsHDEnabled() && !useHD) {

src/wallet/wallet.h

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,16 @@ constexpr OutputType DEFAULT_ADDRESS_TYPE{OutputType::P2SH_SEGWIT};
100100
//! Default for -changetype
101101
constexpr OutputType DEFAULT_CHANGE_TYPE{OutputType::CHANGE_AUTO};
102102

103+
enum WalletFlags : uint64_t {
104+
// wallet flags in the upper section (> 1 << 31) will lead to not opening the wallet if flag is unknown
105+
// unkown wallet flags in the lower section <= (1 << 31) will be tolerated
106+
107+
// will enforce the rule that the wallet can't contain any private keys (only watch-only/pubkeys)
108+
WALLET_FLAG_DISABLE_PRIVATE_KEYS = (1ULL << 32),
109+
};
110+
111+
static constexpr uint64_t g_known_wallet_flags = WALLET_FLAG_DISABLE_PRIVATE_KEYS;
112+
103113
/** A key pool entry */
104114
class CKeyPool
105115
{
@@ -726,6 +736,7 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface
726736
std::set<int64_t> set_pre_split_keypool;
727737
int64_t m_max_keypool_index = 0;
728738
std::map<CKeyID, int64_t> m_pool_key_to_index;
739+
std::atomic<uint64_t> m_wallet_flags{0};
729740

730741
int64_t nTimeFirstKey = 0;
731742

@@ -1132,7 +1143,7 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface
11321143
static bool Verify(std::string wallet_file, bool salvage_wallet, std::string& error_string, std::string& warning_string);
11331144

11341145
/* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */
1135-
static std::shared_ptr<CWallet> CreateWalletFromFile(const std::string& name, const fs::path& path);
1146+
static std::shared_ptr<CWallet> CreateWalletFromFile(const std::string& name, const fs::path& path, uint64_t wallet_creation_flags = 0);
11361147

11371148
/**
11381149
* Wallet post-init setup
@@ -1185,6 +1196,16 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface
11851196

11861197
/** Whether a given output is spendable by this wallet */
11871198
bool OutputEligibleForSpending(const COutput& output, const CoinEligibilityFilter& eligibility_filter) const;
1199+
1200+
/** set a single wallet flag */
1201+
void SetWalletFlag(uint64_t flags);
1202+
1203+
/** check if a certain wallet flag is set */
1204+
bool IsWalletFlagSet(uint64_t flag);
1205+
1206+
/** overwrite all flags by the given uint64_t
1207+
returns false if unknown, non-tolerable flags are present */
1208+
bool SetWalletFlags(uint64_t overwriteFlags, bool memOnly);
11881209
};
11891210

11901211
/** A key allocated from the key pool. */

0 commit comments

Comments
 (0)