Add option to disable private keys during internal wallet creation
This commit is contained in:
parent
9995a602a6
commit
cebefba085
4 changed files with 95 additions and 28 deletions
|
@ -160,6 +160,10 @@ static UniValue getnewaddress(const JSONRPCRequest& request)
|
|||
+ HelpExampleRpc("getnewaddress", "")
|
||||
);
|
||||
|
||||
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
|
||||
}
|
||||
|
||||
LOCK2(cs_main, pwallet->cs_wallet);
|
||||
|
||||
// Parse the label first so we don't generate a key if there's an error
|
||||
|
@ -267,6 +271,10 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request)
|
|||
+ HelpExampleRpc("getrawchangeaddress", "")
|
||||
);
|
||||
|
||||
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
|
||||
}
|
||||
|
||||
LOCK2(cs_main, pwallet->cs_wallet);
|
||||
|
||||
if (!pwallet->IsLocked()) {
|
||||
|
@ -2499,6 +2507,10 @@ static UniValue keypoolrefill(const JSONRPCRequest& request)
|
|||
+ HelpExampleRpc("keypoolrefill", "")
|
||||
);
|
||||
|
||||
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
|
||||
}
|
||||
|
||||
LOCK2(cs_main, pwallet->cs_wallet);
|
||||
|
||||
// 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool
|
||||
|
@ -2983,19 +2995,20 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
|
|||
"Returns an object containing various wallet state info.\n"
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"walletname\": xxxxx, (string) the wallet name\n"
|
||||
" \"walletversion\": xxxxx, (numeric) the wallet version\n"
|
||||
" \"balance\": xxxxxxx, (numeric) the total confirmed balance of the wallet in " + CURRENCY_UNIT + "\n"
|
||||
" \"unconfirmed_balance\": xxx, (numeric) the total unconfirmed balance of the wallet in " + CURRENCY_UNIT + "\n"
|
||||
" \"immature_balance\": xxxxxx, (numeric) the total immature balance of the wallet in " + CURRENCY_UNIT + "\n"
|
||||
" \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\n"
|
||||
" \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since Unix epoch) of the oldest pre-generated key in the key pool\n"
|
||||
" \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated (only counts external keys)\n"
|
||||
" \"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"
|
||||
" \"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"
|
||||
" \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n"
|
||||
" \"hdseedid\": \"<hash160>\" (string, optional) the Hash160 of the HD seed (only present when HD is enabled)\n"
|
||||
" \"hdmasterkeyid\": \"<hash160>\" (string, optional) alias for hdseedid retained for backwards-compatibility. Will be removed in V0.18.\n"
|
||||
" \"walletname\": xxxxx, (string) the wallet name\n"
|
||||
" \"walletversion\": xxxxx, (numeric) the wallet version\n"
|
||||
" \"balance\": xxxxxxx, (numeric) the total confirmed balance of the wallet in " + CURRENCY_UNIT + "\n"
|
||||
" \"unconfirmed_balance\": xxx, (numeric) the total unconfirmed balance of the wallet in " + CURRENCY_UNIT + "\n"
|
||||
" \"immature_balance\": xxxxxx, (numeric) the total immature balance of the wallet in " + CURRENCY_UNIT + "\n"
|
||||
" \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\n"
|
||||
" \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since Unix epoch) of the oldest pre-generated key in the key pool\n"
|
||||
" \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated (only counts external keys)\n"
|
||||
" \"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"
|
||||
" \"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"
|
||||
" \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n"
|
||||
" \"hdseedid\": \"<hash160>\" (string, optional) the Hash160 of the HD seed (only present when HD is enabled)\n"
|
||||
" \"hdmasterkeyid\": \"<hash160>\" (string, optional) alias for hdseedid retained for backwards-compatibility. Will be removed in V0.18.\n"
|
||||
" \"private_keys_enabled\": true|false (boolean) false if privatekeys are disabled for this wallet (enforced watch-only wallet)\n"
|
||||
"}\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("getwalletinfo", "")
|
||||
|
@ -3031,6 +3044,7 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
|
|||
obj.pushKV("hdseedid", seed_id.GetHex());
|
||||
obj.pushKV("hdmasterkeyid", seed_id.GetHex());
|
||||
}
|
||||
obj.pushKV("private_keys_enabled", !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
|
|
@ -164,6 +164,7 @@ const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
|
|||
|
||||
CPubKey CWallet::GenerateNewKey(WalletBatch &batch, bool internal)
|
||||
{
|
||||
assert(!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
|
||||
AssertLockHeld(cs_wallet); // mapKeyMetadata
|
||||
bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets
|
||||
|
||||
|
@ -1465,6 +1466,7 @@ CAmount CWallet::GetChange(const CTransaction& tx) const
|
|||
|
||||
CPubKey CWallet::GenerateNewSeed()
|
||||
{
|
||||
assert(!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
|
||||
CKey key;
|
||||
key.MakeNewKey(true);
|
||||
return DeriveNewSeed(key);
|
||||
|
@ -1539,13 +1541,19 @@ bool CWallet::IsWalletFlagSet(uint64_t flag)
|
|||
return (m_wallet_flags & flag);
|
||||
}
|
||||
|
||||
void CWallet::SetWalletFlags(uint64_t overwriteFlags, bool memonly)
|
||||
bool CWallet::SetWalletFlags(uint64_t overwriteFlags, bool memonly)
|
||||
{
|
||||
LOCK(cs_wallet);
|
||||
m_wallet_flags = overwriteFlags;
|
||||
if (((overwriteFlags & g_known_wallet_flags) >> 32) ^ (overwriteFlags >> 32)) {
|
||||
// contains unknown non-tolerable wallet flags
|
||||
return false;
|
||||
}
|
||||
if (!memonly && !WalletBatch(*database).WriteWalletFlags(m_wallet_flags)) {
|
||||
throw std::runtime_error(std::string(__func__) + ": writing wallet flags failed");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int64_t CWalletTx::GetTxTime() const
|
||||
|
@ -2793,6 +2801,10 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac
|
|||
// post-backup change.
|
||||
|
||||
// Reserve a new key pair from key pool
|
||||
if (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
|
||||
strFailReason = _("Can't generate a change-address key. Private keys are disabled for this wallet.");
|
||||
return false;
|
||||
}
|
||||
CPubKey vchPubKey;
|
||||
bool ret;
|
||||
ret = reservekey.GetReservedKey(vchPubKey, true);
|
||||
|
@ -3193,7 +3205,7 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
|
|||
{
|
||||
LOCK(cs_KeyStore);
|
||||
// This wallet is in its first run if all of these are empty
|
||||
fFirstRunRet = mapKeys.empty() && mapCryptedKeys.empty() && mapWatchKeys.empty() && setWatchOnly.empty() && mapScripts.empty();
|
||||
fFirstRunRet = mapKeys.empty() && mapCryptedKeys.empty() && mapWatchKeys.empty() && setWatchOnly.empty() && mapScripts.empty() && !IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
|
||||
}
|
||||
|
||||
if (nLoadWalletRet != DBErrors::LOAD_OK)
|
||||
|
@ -3317,6 +3329,9 @@ const std::string& CWallet::GetLabelName(const CScript& scriptPubKey) const
|
|||
*/
|
||||
bool CWallet::NewKeyPool()
|
||||
{
|
||||
if (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
|
||||
return false;
|
||||
}
|
||||
{
|
||||
LOCK(cs_wallet);
|
||||
WalletBatch batch(*database);
|
||||
|
@ -3375,6 +3390,9 @@ void CWallet::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool)
|
|||
|
||||
bool CWallet::TopUpKeyPool(unsigned int kpSize)
|
||||
{
|
||||
if (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
|
||||
return false;
|
||||
}
|
||||
{
|
||||
LOCK(cs_wallet);
|
||||
|
||||
|
@ -3499,6 +3517,10 @@ void CWallet::ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey)
|
|||
|
||||
bool CWallet::GetKeyFromPool(CPubKey& result, bool internal)
|
||||
{
|
||||
if (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CKeyPool keypool;
|
||||
{
|
||||
LOCK(cs_wallet);
|
||||
|
@ -4038,7 +4060,7 @@ bool CWallet::Verify(std::string wallet_file, bool salvage_wallet, std::string&
|
|||
return WalletBatch::VerifyDatabaseFile(wallet_path, warning_string, error_string);
|
||||
}
|
||||
|
||||
std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name, const fs::path& path)
|
||||
std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name, const fs::path& path, uint64_t wallet_creation_flags)
|
||||
{
|
||||
const std::string& walletFile = name;
|
||||
|
||||
|
@ -4163,18 +4185,33 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
|
|||
}
|
||||
walletInstance->SetMinVersion(FEATURE_LATEST);
|
||||
|
||||
// generate a new seed
|
||||
CPubKey seed = walletInstance->GenerateNewSeed();
|
||||
if (!walletInstance->SetHDSeed(seed))
|
||||
throw std::runtime_error(std::string(__func__) + ": Storing HD seed failed");
|
||||
if ((wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
|
||||
//selective allow to set flags
|
||||
walletInstance->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
|
||||
} else {
|
||||
// generate a new seed
|
||||
CPubKey seed = walletInstance->GenerateNewSeed();
|
||||
if (!walletInstance->SetHDSeed(seed)) {
|
||||
throw std::runtime_error(std::string(__func__) + ": Storing HD seed failed");
|
||||
}
|
||||
}
|
||||
|
||||
// Top up the keypool
|
||||
if (!walletInstance->TopUpKeyPool()) {
|
||||
if (!walletInstance->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !walletInstance->TopUpKeyPool()) {
|
||||
InitError(_("Unable to generate initial keys") += "\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
walletInstance->ChainStateFlushed(chainActive.GetLocator());
|
||||
} else if (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS) {
|
||||
// Make it impossible to disable private keys after creation
|
||||
InitError(strprintf(_("Error loading %s: Private keys can only be disabled during creation"), walletFile));
|
||||
return NULL;
|
||||
} else if (walletInstance->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
|
||||
LOCK(walletInstance->cs_KeyStore);
|
||||
if (!walletInstance->mapKeys.empty() || !walletInstance->mapCryptedKeys.empty()) {
|
||||
InitWarning(strprintf(_("Warning: Private keys detected in wallet {%s} with disabled private keys"), walletFile));
|
||||
}
|
||||
} else if (gArgs.IsArgSet("-usehd")) {
|
||||
bool useHD = gArgs.GetBoolArg("-usehd", true);
|
||||
if (walletInstance->IsHDEnabled() && !useHD) {
|
||||
|
|
|
@ -113,6 +113,16 @@ constexpr OutputType DEFAULT_ADDRESS_TYPE{OutputType::P2SH_SEGWIT};
|
|||
//! Default for -changetype
|
||||
constexpr OutputType DEFAULT_CHANGE_TYPE{OutputType::CHANGE_AUTO};
|
||||
|
||||
enum WalletFlags : uint64_t {
|
||||
// wallet flags in the upper section (> 1 << 31) will lead to not opening the wallet if flag is unknown
|
||||
// unkown wallet flags in the lower section <= (1 << 31) will be tolerated
|
||||
|
||||
// will enforce the rule that the wallet can't contain any private keys (only watch-only/pubkeys)
|
||||
WALLET_FLAG_DISABLE_PRIVATE_KEYS = (1ULL << 32),
|
||||
};
|
||||
|
||||
static constexpr uint64_t g_known_wallet_flags = WALLET_FLAG_DISABLE_PRIVATE_KEYS;
|
||||
|
||||
/** A key pool entry */
|
||||
class CKeyPool
|
||||
{
|
||||
|
@ -1132,7 +1142,7 @@ public:
|
|||
static bool Verify(std::string wallet_file, bool salvage_wallet, std::string& error_string, std::string& warning_string);
|
||||
|
||||
/* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */
|
||||
static std::shared_ptr<CWallet> CreateWalletFromFile(const std::string& name, const fs::path& path);
|
||||
static std::shared_ptr<CWallet> CreateWalletFromFile(const std::string& name, const fs::path& path, uint64_t wallet_creation_flags = 0);
|
||||
|
||||
/**
|
||||
* Wallet post-init setup
|
||||
|
@ -1198,8 +1208,9 @@ public:
|
|||
/** check if a certain wallet flag is set */
|
||||
bool IsWalletFlagSet(uint64_t flag);
|
||||
|
||||
/** overwrite all flags by the given uint64_t */
|
||||
void SetWalletFlags(uint64_t overwriteFlags, bool memOnly);
|
||||
/** overwrite all flags by the given uint64_t
|
||||
returns false if unknown, non-tolerable flags are present */
|
||||
bool SetWalletFlags(uint64_t overwriteFlags, bool memOnly);
|
||||
};
|
||||
|
||||
/** A key allocated from the key pool. */
|
||||
|
|
|
@ -513,7 +513,10 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
|
|||
} else if (strType == "flags") {
|
||||
uint64_t flags;
|
||||
ssValue >> flags;
|
||||
pwallet->SetWalletFlags(flags, true);
|
||||
if (!pwallet->SetWalletFlags(flags, true)) {
|
||||
strErr = "Error reading wallet database: Unknown non-tolerable wallet flags found";
|
||||
return false;
|
||||
}
|
||||
} else if (strType != "bestblock" && strType != "bestblock_nomerkle") {
|
||||
wss.m_unknown_records++;
|
||||
}
|
||||
|
@ -574,10 +577,12 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
|
|||
{
|
||||
// losing keys is considered a catastrophic error, anything else
|
||||
// we assume the user can live with:
|
||||
if (IsKeyType(strType) || strType == "defaultkey")
|
||||
if (IsKeyType(strType) || strType == "defaultkey") {
|
||||
result = DBErrors::CORRUPT;
|
||||
else
|
||||
{
|
||||
} else if(strType == "flags") {
|
||||
// reading the wallet flags can only fail if unknown flags are present
|
||||
result = DBErrors::TOO_NEW;
|
||||
} else {
|
||||
// Leave other errors alone, if we try to fix them we might make things worse.
|
||||
fNoncriticalErrors = true; // ... but do warn the user there is something wrong.
|
||||
if (strType == "tx")
|
||||
|
|
Loading…
Reference in a new issue