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", "")
|
+ 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);
|
LOCK2(cs_main, pwallet->cs_wallet);
|
||||||
|
|
||||||
// Parse the label first so we don't generate a key if there's an error
|
// 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", "")
|
+ 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);
|
LOCK2(cs_main, pwallet->cs_wallet);
|
||||||
|
|
||||||
if (!pwallet->IsLocked()) {
|
if (!pwallet->IsLocked()) {
|
||||||
|
@ -2499,6 +2507,10 @@ static UniValue keypoolrefill(const JSONRPCRequest& request)
|
||||||
+ HelpExampleRpc("keypoolrefill", "")
|
+ 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);
|
LOCK2(cs_main, pwallet->cs_wallet);
|
||||||
|
|
||||||
// 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool
|
// 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool
|
||||||
|
@ -2996,6 +3008,7 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
|
||||||
" \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\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"
|
" \"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"
|
" \"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"
|
"}\n"
|
||||||
"\nExamples:\n"
|
"\nExamples:\n"
|
||||||
+ HelpExampleCli("getwalletinfo", "")
|
+ HelpExampleCli("getwalletinfo", "")
|
||||||
|
@ -3031,6 +3044,7 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
|
||||||
obj.pushKV("hdseedid", seed_id.GetHex());
|
obj.pushKV("hdseedid", seed_id.GetHex());
|
||||||
obj.pushKV("hdmasterkeyid", seed_id.GetHex());
|
obj.pushKV("hdmasterkeyid", seed_id.GetHex());
|
||||||
}
|
}
|
||||||
|
obj.pushKV("private_keys_enabled", !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -164,6 +164,7 @@ const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
|
||||||
|
|
||||||
CPubKey CWallet::GenerateNewKey(WalletBatch &batch, bool internal)
|
CPubKey CWallet::GenerateNewKey(WalletBatch &batch, bool internal)
|
||||||
{
|
{
|
||||||
|
assert(!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
|
||||||
AssertLockHeld(cs_wallet); // mapKeyMetadata
|
AssertLockHeld(cs_wallet); // mapKeyMetadata
|
||||||
bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets
|
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()
|
CPubKey CWallet::GenerateNewSeed()
|
||||||
{
|
{
|
||||||
|
assert(!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
|
||||||
CKey key;
|
CKey key;
|
||||||
key.MakeNewKey(true);
|
key.MakeNewKey(true);
|
||||||
return DeriveNewSeed(key);
|
return DeriveNewSeed(key);
|
||||||
|
@ -1539,13 +1541,19 @@ bool CWallet::IsWalletFlagSet(uint64_t flag)
|
||||||
return (m_wallet_flags & 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);
|
LOCK(cs_wallet);
|
||||||
m_wallet_flags = overwriteFlags;
|
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)) {
|
if (!memonly && !WalletBatch(*database).WriteWalletFlags(m_wallet_flags)) {
|
||||||
throw std::runtime_error(std::string(__func__) + ": writing wallet flags failed");
|
throw std::runtime_error(std::string(__func__) + ": writing wallet flags failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t CWalletTx::GetTxTime() const
|
int64_t CWalletTx::GetTxTime() const
|
||||||
|
@ -2793,6 +2801,10 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac
|
||||||
// post-backup change.
|
// post-backup change.
|
||||||
|
|
||||||
// Reserve a new key pair from key pool
|
// 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;
|
CPubKey vchPubKey;
|
||||||
bool ret;
|
bool ret;
|
||||||
ret = reservekey.GetReservedKey(vchPubKey, true);
|
ret = reservekey.GetReservedKey(vchPubKey, true);
|
||||||
|
@ -3193,7 +3205,7 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
|
||||||
{
|
{
|
||||||
LOCK(cs_KeyStore);
|
LOCK(cs_KeyStore);
|
||||||
// This wallet is in its first run if all of these are empty
|
// 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)
|
if (nLoadWalletRet != DBErrors::LOAD_OK)
|
||||||
|
@ -3317,6 +3329,9 @@ const std::string& CWallet::GetLabelName(const CScript& scriptPubKey) const
|
||||||
*/
|
*/
|
||||||
bool CWallet::NewKeyPool()
|
bool CWallet::NewKeyPool()
|
||||||
{
|
{
|
||||||
|
if (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
{
|
{
|
||||||
LOCK(cs_wallet);
|
LOCK(cs_wallet);
|
||||||
WalletBatch batch(*database);
|
WalletBatch batch(*database);
|
||||||
|
@ -3375,6 +3390,9 @@ void CWallet::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool)
|
||||||
|
|
||||||
bool CWallet::TopUpKeyPool(unsigned int kpSize)
|
bool CWallet::TopUpKeyPool(unsigned int kpSize)
|
||||||
{
|
{
|
||||||
|
if (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
{
|
{
|
||||||
LOCK(cs_wallet);
|
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)
|
bool CWallet::GetKeyFromPool(CPubKey& result, bool internal)
|
||||||
{
|
{
|
||||||
|
if (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
CKeyPool keypool;
|
CKeyPool keypool;
|
||||||
{
|
{
|
||||||
LOCK(cs_wallet);
|
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);
|
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;
|
const std::string& walletFile = name;
|
||||||
|
|
||||||
|
@ -4163,18 +4185,33 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
|
||||||
}
|
}
|
||||||
walletInstance->SetMinVersion(FEATURE_LATEST);
|
walletInstance->SetMinVersion(FEATURE_LATEST);
|
||||||
|
|
||||||
|
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
|
// generate a new seed
|
||||||
CPubKey seed = walletInstance->GenerateNewSeed();
|
CPubKey seed = walletInstance->GenerateNewSeed();
|
||||||
if (!walletInstance->SetHDSeed(seed))
|
if (!walletInstance->SetHDSeed(seed)) {
|
||||||
throw std::runtime_error(std::string(__func__) + ": Storing HD seed failed");
|
throw std::runtime_error(std::string(__func__) + ": Storing HD seed failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Top up the keypool
|
// Top up the keypool
|
||||||
if (!walletInstance->TopUpKeyPool()) {
|
if (!walletInstance->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !walletInstance->TopUpKeyPool()) {
|
||||||
InitError(_("Unable to generate initial keys") += "\n");
|
InitError(_("Unable to generate initial keys") += "\n");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
walletInstance->ChainStateFlushed(chainActive.GetLocator());
|
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")) {
|
} else if (gArgs.IsArgSet("-usehd")) {
|
||||||
bool useHD = gArgs.GetBoolArg("-usehd", true);
|
bool useHD = gArgs.GetBoolArg("-usehd", true);
|
||||||
if (walletInstance->IsHDEnabled() && !useHD) {
|
if (walletInstance->IsHDEnabled() && !useHD) {
|
||||||
|
|
|
@ -113,6 +113,16 @@ constexpr OutputType DEFAULT_ADDRESS_TYPE{OutputType::P2SH_SEGWIT};
|
||||||
//! Default for -changetype
|
//! Default for -changetype
|
||||||
constexpr OutputType DEFAULT_CHANGE_TYPE{OutputType::CHANGE_AUTO};
|
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 */
|
/** A key pool entry */
|
||||||
class CKeyPool
|
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);
|
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 */
|
/* 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
|
* Wallet post-init setup
|
||||||
|
@ -1198,8 +1208,9 @@ public:
|
||||||
/** check if a certain wallet flag is set */
|
/** check if a certain wallet flag is set */
|
||||||
bool IsWalletFlagSet(uint64_t flag);
|
bool IsWalletFlagSet(uint64_t flag);
|
||||||
|
|
||||||
/** overwrite all flags by the given uint64_t */
|
/** overwrite all flags by the given uint64_t
|
||||||
void SetWalletFlags(uint64_t overwriteFlags, bool memOnly);
|
returns false if unknown, non-tolerable flags are present */
|
||||||
|
bool SetWalletFlags(uint64_t overwriteFlags, bool memOnly);
|
||||||
};
|
};
|
||||||
|
|
||||||
/** A key allocated from the key pool. */
|
/** A key allocated from the key pool. */
|
||||||
|
|
|
@ -513,7 +513,10 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
|
||||||
} else if (strType == "flags") {
|
} else if (strType == "flags") {
|
||||||
uint64_t flags;
|
uint64_t flags;
|
||||||
ssValue >> 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") {
|
} else if (strType != "bestblock" && strType != "bestblock_nomerkle") {
|
||||||
wss.m_unknown_records++;
|
wss.m_unknown_records++;
|
||||||
}
|
}
|
||||||
|
@ -574,10 +577,12 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
|
||||||
{
|
{
|
||||||
// losing keys is considered a catastrophic error, anything else
|
// losing keys is considered a catastrophic error, anything else
|
||||||
// we assume the user can live with:
|
// we assume the user can live with:
|
||||||
if (IsKeyType(strType) || strType == "defaultkey")
|
if (IsKeyType(strType) || strType == "defaultkey") {
|
||||||
result = DBErrors::CORRUPT;
|
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.
|
// 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.
|
fNoncriticalErrors = true; // ... but do warn the user there is something wrong.
|
||||||
if (strType == "tx")
|
if (strType == "tx")
|
||||||
|
|
Loading…
Add table
Reference in a new issue