Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/scriptpubkeyman.cpp
// Copyright (c) 2019 The Bitcoin Core developers | // Copyright (c) 2019 The Bitcoin Core developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#include <chainparams.h> | #include <chainparams.h> | ||||
#include <config.h> | #include <config.h> | ||||
#include <key_io.h> | #include <key_io.h> | ||||
#include <outputtype.h> | #include <outputtype.h> | ||||
#include <script/descriptor.h> | #include <script/descriptor.h> | ||||
#include <script/sign.h> | #include <script/sign.h> | ||||
#include <util/bip32.h> | #include <util/bip32.h> | ||||
#include <util/strencodings.h> | #include <util/strencodings.h> | ||||
#include <util/string.h> | #include <util/string.h> | ||||
#include <util/translation.h> | #include <util/translation.h> | ||||
#include <wallet/scriptpubkeyman.h> | #include <wallet/scriptpubkeyman.h> | ||||
//! Value for the first BIP 32 hardened derivation. Can be used as a bit mask | |||||
//! and as a value. See BIP 32 for more details. | |||||
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000; | |||||
bool LegacyScriptPubKeyMan::GetNewDestination(const OutputType type, | bool LegacyScriptPubKeyMan::GetNewDestination(const OutputType type, | ||||
CTxDestination &dest, | CTxDestination &dest, | ||||
std::string &error) { | std::string &error) { | ||||
LOCK(cs_KeyStore); | LOCK(cs_KeyStore); | ||||
error.clear(); | error.clear(); | ||||
// Generate a new key that is added to wallet | // Generate a new key that is added to wallet | ||||
CPubKey new_key; | CPubKey new_key; | ||||
▲ Show 20 Lines • Show All 224 Lines • ▼ Show 20 Lines | bool LegacyScriptPubKeyMan::GetReservedDestination(const OutputType type, | ||||
if (!ReserveKeyFromKeyPool(index, keypool, internal)) { | if (!ReserveKeyFromKeyPool(index, keypool, internal)) { | ||||
return false; | return false; | ||||
} | } | ||||
address = GetDestinationForKey(keypool.vchPubKey, type); | address = GetDestinationForKey(keypool.vchPubKey, type); | ||||
return true; | return true; | ||||
} | } | ||||
bool LegacyScriptPubKeyMan::TopUpInactiveHDChain(const CKeyID seed_id, | |||||
int64_t index, bool internal) { | |||||
LOCK(cs_KeyStore); | |||||
if (m_storage.IsLocked()) { | |||||
return false; | |||||
} | |||||
auto it = m_inactive_hd_chains.find(seed_id); | |||||
if (it == m_inactive_hd_chains.end()) { | |||||
return false; | |||||
} | |||||
CHDChain &chain = it->second; | |||||
// Top up key pool | |||||
int64_t target_size = | |||||
std::max(gArgs.GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t)1); | |||||
// "size" of the keypools. Not really the size, actually the difference | |||||
// between index and the chain counter Since chain counter is 1 based and | |||||
// index is 0 based, one of them needs to be offset by 1. | |||||
int64_t kp_size = | |||||
(internal ? chain.nInternalChainCounter : chain.nExternalChainCounter) - | |||||
(index + 1); | |||||
// make sure the keypool fits the user-selected target (-keypool) | |||||
int64_t missing = std::max(target_size - kp_size, (int64_t)0); | |||||
if (missing > 0) { | |||||
WalletBatch batch(m_storage.GetDatabase()); | |||||
for (int64_t i = missing; i > 0; --i) { | |||||
GenerateNewKey(batch, chain, internal); | |||||
} | |||||
if (internal) { | |||||
WalletLogPrintf("inactive seed with id %s added %d internal keys\n", | |||||
HexStr(seed_id), missing); | |||||
} else { | |||||
WalletLogPrintf("inactive seed with id %s added %d keys\n", | |||||
HexStr(seed_id), missing); | |||||
} | |||||
} | |||||
return true; | |||||
} | |||||
void LegacyScriptPubKeyMan::MarkUnusedAddresses(const CScript &script) { | void LegacyScriptPubKeyMan::MarkUnusedAddresses(const CScript &script) { | ||||
LOCK(cs_KeyStore); | LOCK(cs_KeyStore); | ||||
// extract addresses and check if they match with an unused keypool key | // extract addresses and check if they match with an unused keypool key | ||||
for (const auto &keyid : GetAffectedKeys(script, *this)) { | for (const auto &keyid : GetAffectedKeys(script, *this)) { | ||||
std::map<CKeyID, int64_t>::const_iterator mi = | std::map<CKeyID, int64_t>::const_iterator mi = | ||||
m_pool_key_to_index.find(keyid); | m_pool_key_to_index.find(keyid); | ||||
if (mi != m_pool_key_to_index.end()) { | if (mi != m_pool_key_to_index.end()) { | ||||
WalletLogPrintf("%s: Detected a used keypool key, mark all keypool " | WalletLogPrintf("%s: Detected a used keypool key, mark all keypool " | ||||
"key up to this key as used\n", | "keys up to this key as used\n", | ||||
__func__); | __func__); | ||||
MarkReserveKeysAsUsed(mi->second); | MarkReserveKeysAsUsed(mi->second); | ||||
if (!TopUp()) { | if (!TopUp()) { | ||||
WalletLogPrintf( | WalletLogPrintf( | ||||
"%s: Topping up keypool failed (locked wallet)\n", | "%s: Topping up keypool failed (locked wallet)\n", | ||||
__func__); | __func__); | ||||
} | } | ||||
} | } | ||||
// Find the key's metadata and check if it's seed id (if it has one) is | |||||
// inactive, i.e. it is not the current m_hd_chain seed id. If so, TopUp | |||||
// the inactive hd chain | |||||
auto it = mapKeyMetadata.find(keyid); | |||||
if (it != mapKeyMetadata.end()) { | |||||
CKeyMetadata meta = it->second; | |||||
if (!meta.hd_seed_id.IsNull() && | |||||
meta.hd_seed_id != m_hd_chain.seed_id) { | |||||
bool internal = | |||||
(meta.key_origin.path[1] & ~BIP32_HARDENED_KEY_LIMIT) != 0; | |||||
int64_t index = | |||||
meta.key_origin.path[2] & ~BIP32_HARDENED_KEY_LIMIT; | |||||
if (!TopUpInactiveHDChain(meta.hd_seed_id, index, internal)) { | |||||
WalletLogPrintf("%s: Adding inactive seed keys failed\n", | |||||
__func__); | |||||
} | |||||
} | |||||
} | |||||
} | } | ||||
} | } | ||||
void LegacyScriptPubKeyMan::UpgradeKeyMetadata() { | void LegacyScriptPubKeyMan::UpgradeKeyMetadata() { | ||||
LOCK(cs_KeyStore); | LOCK(cs_KeyStore); | ||||
if (m_storage.IsLocked() || | if (m_storage.IsLocked() || | ||||
m_storage.IsWalletFlagSet(WALLET_FLAG_KEY_ORIGIN_METADATA)) { | m_storage.IsWalletFlagSet(WALLET_FLAG_KEY_ORIGIN_METADATA)) { | ||||
return; | return; | ||||
Show All 39 Lines | bool LegacyScriptPubKeyMan::SetupGeneration(bool force) { | ||||
SetHDSeed(GenerateNewSeed()); | SetHDSeed(GenerateNewSeed()); | ||||
if (!NewKeyPool()) { | if (!NewKeyPool()) { | ||||
return false; | return false; | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool LegacyScriptPubKeyMan::IsHDEnabled() const { | bool LegacyScriptPubKeyMan::IsHDEnabled() const { | ||||
return !hdChain.seed_id.IsNull(); | return !m_hd_chain.seed_id.IsNull(); | ||||
} | } | ||||
bool LegacyScriptPubKeyMan::CanGetAddresses(bool internal) const { | bool LegacyScriptPubKeyMan::CanGetAddresses(bool internal) const { | ||||
LOCK(cs_KeyStore); | LOCK(cs_KeyStore); | ||||
// Check if the keypool has keys | // Check if the keypool has keys | ||||
bool keypool_has_keys; | bool keypool_has_keys; | ||||
if (internal && m_storage.CanSupportFeature(FEATURE_HD_SPLIT)) { | if (internal && m_storage.CanSupportFeature(FEATURE_HD_SPLIT)) { | ||||
keypool_has_keys = setInternalKeyPool.size() > 0; | keypool_has_keys = setInternalKeyPool.size() > 0; | ||||
▲ Show 20 Lines • Show All 467 Lines • ▼ Show 20 Lines | |||||
bool LegacyScriptPubKeyMan::AddWatchOnly(const CScript &dest, | bool LegacyScriptPubKeyMan::AddWatchOnly(const CScript &dest, | ||||
int64_t nCreateTime) { | int64_t nCreateTime) { | ||||
m_script_metadata[CScriptID(dest)].nCreateTime = nCreateTime; | m_script_metadata[CScriptID(dest)].nCreateTime = nCreateTime; | ||||
return AddWatchOnly(dest); | return AddWatchOnly(dest); | ||||
} | } | ||||
void LegacyScriptPubKeyMan::SetHDChain(const CHDChain &chain, bool memonly) { | void LegacyScriptPubKeyMan::SetHDChain(const CHDChain &chain, bool memonly) { | ||||
LOCK(cs_KeyStore); | LOCK(cs_KeyStore); | ||||
if (!memonly && !WalletBatch(m_storage.GetDatabase()).WriteHDChain(chain)) { | // memonly == true means we are loading the wallet file | ||||
// memonly == false means that the chain is actually being changed | |||||
if (!memonly) { | |||||
// Store the new chain | |||||
if (!WalletBatch(m_storage.GetDatabase()).WriteHDChain(chain)) { | |||||
throw std::runtime_error(std::string(__func__) + | throw std::runtime_error(std::string(__func__) + | ||||
": writing chain failed"); | ": writing chain failed"); | ||||
} | } | ||||
// When there's an old chain, add it as an inactive chain as we are now | |||||
// rotating hd chains | |||||
if (!m_hd_chain.seed_id.IsNull()) { | |||||
AddInactiveHDChain(m_hd_chain); | |||||
} | |||||
} | |||||
m_hd_chain = chain; | |||||
} | |||||
hdChain = chain; | void LegacyScriptPubKeyMan::AddInactiveHDChain(const CHDChain &chain) { | ||||
LOCK(cs_KeyStore); | |||||
assert(!chain.seed_id.IsNull()); | |||||
m_inactive_hd_chains[chain.seed_id] = chain; | |||||
} | } | ||||
bool LegacyScriptPubKeyMan::HaveKey(const CKeyID &address) const { | bool LegacyScriptPubKeyMan::HaveKey(const CKeyID &address) const { | ||||
LOCK(cs_KeyStore); | LOCK(cs_KeyStore); | ||||
if (!m_storage.HasEncryptionKeys()) { | if (!m_storage.HasEncryptionKeys()) { | ||||
return FillableSigningProvider::HaveKey(address); | return FillableSigningProvider::HaveKey(address); | ||||
} | } | ||||
return mapCryptedKeys.count(address) > 0; | return mapCryptedKeys.count(address) > 0; | ||||
▲ Show 20 Lines • Show All 63 Lines • ▼ Show 20 Lines | if (mi != mapCryptedKeys.end()) { | ||||
return true; | return true; | ||||
} | } | ||||
// Check for watch-only pubkeys | // Check for watch-only pubkeys | ||||
return GetWatchPubKey(address, vchPubKeyOut); | return GetWatchPubKey(address, vchPubKeyOut); | ||||
} | } | ||||
CPubKey LegacyScriptPubKeyMan::GenerateNewKey(WalletBatch &batch, | CPubKey LegacyScriptPubKeyMan::GenerateNewKey(WalletBatch &batch, | ||||
CHDChain &hd_chain, | |||||
bool internal) { | bool internal) { | ||||
assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); | assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); | ||||
assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET)); | assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET)); | ||||
AssertLockHeld(cs_KeyStore); | AssertLockHeld(cs_KeyStore); | ||||
// default to compressed public keys if we want 0.6.0 wallets | // default to compressed public keys if we want 0.6.0 wallets | ||||
bool fCompressed = m_storage.CanSupportFeature(FEATURE_COMPRPUBKEY); | bool fCompressed = m_storage.CanSupportFeature(FEATURE_COMPRPUBKEY); | ||||
CKey secret; | CKey secret; | ||||
// Create new metadata | // Create new metadata | ||||
int64_t nCreationTime = GetTime(); | int64_t nCreationTime = GetTime(); | ||||
CKeyMetadata metadata(nCreationTime); | CKeyMetadata metadata(nCreationTime); | ||||
// use HD key derivation if HD was enabled during wallet creation and a seed | // use HD key derivation if HD was enabled during wallet creation and a seed | ||||
// is present | // is present | ||||
if (IsHDEnabled()) { | if (IsHDEnabled()) { | ||||
DeriveNewChildKey( | DeriveNewChildKey( | ||||
batch, metadata, secret, | batch, metadata, secret, hd_chain, | ||||
(m_storage.CanSupportFeature(FEATURE_HD_SPLIT) ? internal : false)); | (m_storage.CanSupportFeature(FEATURE_HD_SPLIT) ? internal : false)); | ||||
} else { | } else { | ||||
secret.MakeNewKey(fCompressed); | secret.MakeNewKey(fCompressed); | ||||
} | } | ||||
// Compressed public keys were introduced in version 0.6.0 | // Compressed public keys were introduced in version 0.6.0 | ||||
if (fCompressed) { | if (fCompressed) { | ||||
m_storage.SetMinVersion(FEATURE_COMPRPUBKEY); | m_storage.SetMinVersion(FEATURE_COMPRPUBKEY); | ||||
} | } | ||||
CPubKey pubkey = secret.GetPubKey(); | CPubKey pubkey = secret.GetPubKey(); | ||||
assert(secret.VerifyPubKey(pubkey)); | assert(secret.VerifyPubKey(pubkey)); | ||||
mapKeyMetadata[pubkey.GetID()] = metadata; | mapKeyMetadata[pubkey.GetID()] = metadata; | ||||
UpdateTimeFirstKey(nCreationTime); | UpdateTimeFirstKey(nCreationTime); | ||||
if (!AddKeyPubKeyWithDB(batch, secret, pubkey)) { | if (!AddKeyPubKeyWithDB(batch, secret, pubkey)) { | ||||
throw std::runtime_error(std::string(__func__) + ": AddKey failed"); | throw std::runtime_error(std::string(__func__) + ": AddKey failed"); | ||||
} | } | ||||
return pubkey; | return pubkey; | ||||
} | } | ||||
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000; | |||||
void LegacyScriptPubKeyMan::DeriveNewChildKey(WalletBatch &batch, | void LegacyScriptPubKeyMan::DeriveNewChildKey(WalletBatch &batch, | ||||
CKeyMetadata &metadata, | CKeyMetadata &metadata, | ||||
CKey &secret, bool internal) { | CKey &secret, CHDChain &hd_chain, | ||||
bool internal) { | |||||
// for now we use a fixed keypath scheme of m/0'/0'/k | // for now we use a fixed keypath scheme of m/0'/0'/k | ||||
// seed (256bit) | // seed (256bit) | ||||
CKey seed; | CKey seed; | ||||
// hd master key | // hd master key | ||||
CExtKey masterKey; | CExtKey masterKey; | ||||
// key at m/0' | // key at m/0' | ||||
CExtKey accountKey; | CExtKey accountKey; | ||||
// key at m/0'/0' (external) or m/0'/1' (internal) | // key at m/0'/0' (external) or m/0'/1' (internal) | ||||
CExtKey chainChildKey; | CExtKey chainChildKey; | ||||
// key at m/0'/0'/<n>' | // key at m/0'/0'/<n>' | ||||
CExtKey childKey; | CExtKey childKey; | ||||
// try to get the seed | // try to get the seed | ||||
if (!GetKey(hdChain.seed_id, seed)) { | if (!GetKey(hd_chain.seed_id, seed)) { | ||||
throw std::runtime_error(std::string(__func__) + ": seed not found"); | throw std::runtime_error(std::string(__func__) + ": seed not found"); | ||||
} | } | ||||
masterKey.SetSeed(seed.begin(), seed.size()); | masterKey.SetSeed(seed.begin(), seed.size()); | ||||
// derive m/0' | // derive m/0' | ||||
// use hardened derivation (child keys >= 0x80000000 are hardened after | // use hardened derivation (child keys >= 0x80000000 are hardened after | ||||
// bip32) | // bip32) | ||||
masterKey.Derive(accountKey, BIP32_HARDENED_KEY_LIMIT); | masterKey.Derive(accountKey, BIP32_HARDENED_KEY_LIMIT); | ||||
// derive m/0'/0' (external chain) OR m/0'/1' (internal chain) | // derive m/0'/0' (external chain) OR m/0'/1' (internal chain) | ||||
assert(internal ? m_storage.CanSupportFeature(FEATURE_HD_SPLIT) : true); | assert(internal ? m_storage.CanSupportFeature(FEATURE_HD_SPLIT) : true); | ||||
accountKey.Derive(chainChildKey, | accountKey.Derive(chainChildKey, | ||||
BIP32_HARDENED_KEY_LIMIT + (internal ? 1 : 0)); | BIP32_HARDENED_KEY_LIMIT + (internal ? 1 : 0)); | ||||
// derive child key at next index, skip keys already known to the wallet | // derive child key at next index, skip keys already known to the wallet | ||||
do { | do { | ||||
// always derive hardened keys | // always derive hardened keys | ||||
// childIndex | BIP32_HARDENED_KEY_LIMIT = derive childIndex in hardened | // childIndex | BIP32_HARDENED_KEY_LIMIT = derive childIndex in hardened | ||||
// child-index-range | // child-index-range | ||||
// example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649 | // example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649 | ||||
if (internal) { | if (internal) { | ||||
chainChildKey.Derive(childKey, hdChain.nInternalChainCounter | | chainChildKey.Derive(childKey, hd_chain.nInternalChainCounter | | ||||
BIP32_HARDENED_KEY_LIMIT); | BIP32_HARDENED_KEY_LIMIT); | ||||
metadata.hdKeypath = | metadata.hdKeypath = | ||||
"m/0'/1'/" + ToString(hdChain.nInternalChainCounter) + "'"; | "m/0'/1'/" + ToString(hd_chain.nInternalChainCounter) + "'"; | ||||
metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT); | metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT); | ||||
metadata.key_origin.path.push_back(1 | BIP32_HARDENED_KEY_LIMIT); | metadata.key_origin.path.push_back(1 | BIP32_HARDENED_KEY_LIMIT); | ||||
metadata.key_origin.path.push_back(hdChain.nInternalChainCounter | | metadata.key_origin.path.push_back(hd_chain.nInternalChainCounter | | ||||
BIP32_HARDENED_KEY_LIMIT); | BIP32_HARDENED_KEY_LIMIT); | ||||
hdChain.nInternalChainCounter++; | hd_chain.nInternalChainCounter++; | ||||
} else { | } else { | ||||
chainChildKey.Derive(childKey, hdChain.nExternalChainCounter | | chainChildKey.Derive(childKey, hd_chain.nExternalChainCounter | | ||||
BIP32_HARDENED_KEY_LIMIT); | BIP32_HARDENED_KEY_LIMIT); | ||||
metadata.hdKeypath = | metadata.hdKeypath = | ||||
"m/0'/0'/" + ToString(hdChain.nExternalChainCounter) + "'"; | "m/0'/0'/" + ToString(hd_chain.nExternalChainCounter) + "'"; | ||||
metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT); | metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT); | ||||
metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT); | metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT); | ||||
metadata.key_origin.path.push_back(hdChain.nExternalChainCounter | | metadata.key_origin.path.push_back(hd_chain.nExternalChainCounter | | ||||
BIP32_HARDENED_KEY_LIMIT); | BIP32_HARDENED_KEY_LIMIT); | ||||
hdChain.nExternalChainCounter++; | hd_chain.nExternalChainCounter++; | ||||
} | } | ||||
} while (HaveKey(childKey.key.GetPubKey().GetID())); | } while (HaveKey(childKey.key.GetPubKey().GetID())); | ||||
secret = childKey.key; | secret = childKey.key; | ||||
metadata.hd_seed_id = hdChain.seed_id; | metadata.hd_seed_id = hd_chain.seed_id; | ||||
CKeyID master_id = masterKey.key.GetPubKey().GetID(); | CKeyID master_id = masterKey.key.GetPubKey().GetID(); | ||||
std::copy(master_id.begin(), master_id.begin() + 4, | std::copy(master_id.begin(), master_id.begin() + 4, | ||||
metadata.key_origin.fingerprint); | metadata.key_origin.fingerprint); | ||||
metadata.has_key_origin = true; | metadata.has_key_origin = true; | ||||
// update the chain model in the database | // update the chain model in the database | ||||
if (!batch.WriteHDChain(hdChain)) { | if (hd_chain.seed_id == m_hd_chain.seed_id && | ||||
!batch.WriteHDChain(hd_chain)) { | |||||
throw std::runtime_error(std::string(__func__) + | throw std::runtime_error(std::string(__func__) + | ||||
": Writing HD chain model failed"); | ": writing HD chain model failed"); | ||||
} | } | ||||
} | } | ||||
void LegacyScriptPubKeyMan::LoadKeyPool(int64_t nIndex, | void LegacyScriptPubKeyMan::LoadKeyPool(int64_t nIndex, | ||||
const CKeyPool &keypool) { | const CKeyPool &keypool) { | ||||
LOCK(cs_KeyStore); | LOCK(cs_KeyStore); | ||||
if (keypool.m_pre_split) { | if (keypool.m_pre_split) { | ||||
set_pre_split_keypool.insert(nIndex); | set_pre_split_keypool.insert(nIndex); | ||||
▲ Show 20 Lines • Show All 140 Lines • ▼ Show 20 Lines | if (!CanGenerateKeys()) { | ||||
} | } | ||||
bool internal = false; | bool internal = false; | ||||
WalletBatch batch(m_storage.GetDatabase()); | WalletBatch batch(m_storage.GetDatabase()); | ||||
for (int64_t i = missingInternal + missingExternal; i--;) { | for (int64_t i = missingInternal + missingExternal; i--;) { | ||||
if (i < missingInternal) { | if (i < missingInternal) { | ||||
internal = true; | internal = true; | ||||
} | } | ||||
CPubKey pubkey(GenerateNewKey(batch, internal)); | CPubKey pubkey(GenerateNewKey(batch, m_hd_chain, internal)); | ||||
AddKeypoolPubkeyWithDB(pubkey, internal, batch); | AddKeypoolPubkeyWithDB(pubkey, internal, batch); | ||||
} | } | ||||
if (missingInternal + missingExternal > 0) { | if (missingInternal + missingExternal > 0) { | ||||
WalletLogPrintf( | WalletLogPrintf( | ||||
"keypool added %d keys (%d internal), size=%u (%u internal)\n", | "keypool added %d keys (%d internal), size=%u (%u internal)\n", | ||||
missingInternal + missingExternal, missingInternal, | missingInternal + missingExternal, missingInternal, | ||||
setInternalKeyPool.size() + setExternalKeyPool.size() + | setInternalKeyPool.size() + setExternalKeyPool.size() + | ||||
set_pre_split_keypool.size(), | set_pre_split_keypool.size(), | ||||
▲ Show 20 Lines • Show All 68 Lines • ▼ Show 20 Lines | bool LegacyScriptPubKeyMan::GetKeyFromPool(CPubKey &result, | ||||
LOCK(cs_KeyStore); | LOCK(cs_KeyStore); | ||||
int64_t nIndex; | int64_t nIndex; | ||||
if (!ReserveKeyFromKeyPool(nIndex, keypool, internal) && | if (!ReserveKeyFromKeyPool(nIndex, keypool, internal) && | ||||
!m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { | !m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { | ||||
if (m_storage.IsLocked()) { | if (m_storage.IsLocked()) { | ||||
return false; | return false; | ||||
} | } | ||||
WalletBatch batch(m_storage.GetDatabase()); | WalletBatch batch(m_storage.GetDatabase()); | ||||
result = GenerateNewKey(batch, internal); | result = GenerateNewKey(batch, m_hd_chain, internal); | ||||
return true; | return true; | ||||
} | } | ||||
KeepDestination(nIndex, type); | KeepDestination(nIndex, type); | ||||
result = keypool.vchPubKey; | result = keypool.vchPubKey; | ||||
return true; | return true; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 1,005 Lines • Show Last 20 Lines |