Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/wallet.cpp
Show First 20 Lines • Show All 3,547 Lines • ▼ Show 20 Lines | bool CWallet::NewKeyPool() { | ||||
} | } | ||||
setInternalKeyPool.clear(); | setInternalKeyPool.clear(); | ||||
for (int64_t nIndex : setExternalKeyPool) { | for (int64_t nIndex : setExternalKeyPool) { | ||||
batch.ErasePool(nIndex); | batch.ErasePool(nIndex); | ||||
} | } | ||||
setExternalKeyPool.clear(); | setExternalKeyPool.clear(); | ||||
for (int64_t nIndex : set_pre_split_keypool) { | |||||
batch.ErasePool(nIndex); | |||||
} | |||||
set_pre_split_keypool.clear(); | |||||
m_pool_key_to_index.clear(); | m_pool_key_to_index.clear(); | ||||
if (!TopUpKeyPool()) { | if (!TopUpKeyPool()) { | ||||
return false; | return false; | ||||
} | } | ||||
LogPrintf("CWallet::NewKeyPool rewrote keypool\n"); | LogPrintf("CWallet::NewKeyPool rewrote keypool\n"); | ||||
return true; | return true; | ||||
} | } | ||||
size_t CWallet::KeypoolCountExternalKeys() { | size_t CWallet::KeypoolCountExternalKeys() { | ||||
// setExternalKeyPool | // setExternalKeyPool | ||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
return setExternalKeyPool.size(); | return setExternalKeyPool.size() + set_pre_split_keypool.size(); | ||||
} | } | ||||
void CWallet::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool) { | void CWallet::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool) { | ||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
if (keypool.fInternal) { | if (keypool.m_pre_split) { | ||||
set_pre_split_keypool.insert(nIndex); | |||||
} else if (keypool.fInternal) { | |||||
setInternalKeyPool.insert(nIndex); | setInternalKeyPool.insert(nIndex); | ||||
} else { | } else { | ||||
setExternalKeyPool.insert(nIndex); | setExternalKeyPool.insert(nIndex); | ||||
} | } | ||||
m_max_keypool_index = std::max(m_max_keypool_index, nIndex); | m_max_keypool_index = std::max(m_max_keypool_index, nIndex); | ||||
m_pool_key_to_index[keypool.vchPubKey.GetID()] = nIndex; | m_pool_key_to_index[keypool.vchPubKey.GetID()] = nIndex; | ||||
// If no metadata exists yet, create a default with the pool key's | // If no metadata exists yet, create a default with the pool key's | ||||
▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | for (int64_t i = missingInternal + missingExternal; i--;) { | ||||
setExternalKeyPool.insert(index); | setExternalKeyPool.insert(index); | ||||
} | } | ||||
m_pool_key_to_index[pubkey.GetID()] = index; | m_pool_key_to_index[pubkey.GetID()] = index; | ||||
} | } | ||||
if (missingInternal + missingExternal > 0) { | if (missingInternal + missingExternal > 0) { | ||||
LogPrintf( | LogPrintf( | ||||
"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(), | |||||
setInternalKeyPool.size()); | setInternalKeyPool.size()); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
void CWallet::ReserveKeyFromKeyPool(int64_t &nIndex, CKeyPool &keypool, | void CWallet::ReserveKeyFromKeyPool(int64_t &nIndex, CKeyPool &keypool, | ||||
bool fRequestedInternal) { | bool fRequestedInternal) { | ||||
nIndex = -1; | nIndex = -1; | ||||
keypool.vchPubKey = CPubKey(); | keypool.vchPubKey = CPubKey(); | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
if (!IsLocked()) { | if (!IsLocked()) { | ||||
TopUpKeyPool(); | TopUpKeyPool(); | ||||
} | } | ||||
bool fReturningInternal = IsHDEnabled() && | bool fReturningInternal = IsHDEnabled() && | ||||
CanSupportFeature(FEATURE_HD_SPLIT) && | CanSupportFeature(FEATURE_HD_SPLIT) && | ||||
fRequestedInternal; | fRequestedInternal; | ||||
std::set<int64_t> &setKeyPool = | std::set<int64_t> &setKeyPool = | ||||
fReturningInternal ? setInternalKeyPool : setExternalKeyPool; | set_pre_split_keypool.empty() | ||||
? (fReturningInternal ? setInternalKeyPool : setExternalKeyPool) | |||||
: set_pre_split_keypool; | |||||
// Get the oldest key | // Get the oldest key | ||||
if (setKeyPool.empty()) { | if (setKeyPool.empty()) { | ||||
return; | return; | ||||
} | } | ||||
WalletBatch batch(*database); | WalletBatch batch(*database); | ||||
auto it = setKeyPool.begin(); | auto it = setKeyPool.begin(); | ||||
nIndex = *it; | nIndex = *it; | ||||
setKeyPool.erase(it); | setKeyPool.erase(it); | ||||
if (!batch.ReadPool(nIndex, keypool)) { | if (!batch.ReadPool(nIndex, keypool)) { | ||||
throw std::runtime_error(std::string(__func__) + ": read failed"); | throw std::runtime_error(std::string(__func__) + ": read failed"); | ||||
} | } | ||||
if (!HaveKey(keypool.vchPubKey.GetID())) { | if (!HaveKey(keypool.vchPubKey.GetID())) { | ||||
throw std::runtime_error(std::string(__func__) + | throw std::runtime_error(std::string(__func__) + | ||||
": unknown key in key pool"); | ": unknown key in key pool"); | ||||
} | } | ||||
if (keypool.fInternal != fReturningInternal) { | // If the key was pre-split keypool, we don't care about what type it is | ||||
if (set_pre_split_keypool.size() == 0 && | |||||
keypool.fInternal != fReturningInternal) { | |||||
throw std::runtime_error(std::string(__func__) + | throw std::runtime_error(std::string(__func__) + | ||||
": keypool entry misclassified"); | ": keypool entry misclassified"); | ||||
} | } | ||||
assert(keypool.vchPubKey.IsValid()); | assert(keypool.vchPubKey.IsValid()); | ||||
m_pool_key_to_index.erase(keypool.vchPubKey.GetID()); | m_pool_key_to_index.erase(keypool.vchPubKey.GetID()); | ||||
LogPrintf("keypool reserve %d\n", nIndex); | LogPrintf("keypool reserve %d\n", nIndex); | ||||
} | } | ||||
void CWallet::KeepKey(int64_t nIndex) { | void CWallet::KeepKey(int64_t nIndex) { | ||||
// Remove from key pool. | // Remove from key pool. | ||||
WalletBatch batch(*database); | WalletBatch batch(*database); | ||||
batch.ErasePool(nIndex); | batch.ErasePool(nIndex); | ||||
LogPrintf("keypool keep %d\n", nIndex); | LogPrintf("keypool keep %d\n", nIndex); | ||||
} | } | ||||
void CWallet::ReturnKey(int64_t nIndex, bool fInternal, const CPubKey &pubkey) { | void CWallet::ReturnKey(int64_t nIndex, bool fInternal, const CPubKey &pubkey) { | ||||
// Return to key pool | // Return to key pool | ||||
{ | { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
if (fInternal) { | if (fInternal) { | ||||
setInternalKeyPool.insert(nIndex); | setInternalKeyPool.insert(nIndex); | ||||
} else if (!set_pre_split_keypool.empty()) { | |||||
set_pre_split_keypool.insert(nIndex); | |||||
} else { | } else { | ||||
setExternalKeyPool.insert(nIndex); | setExternalKeyPool.insert(nIndex); | ||||
} | } | ||||
m_pool_key_to_index[pubkey.GetID()] = nIndex; | m_pool_key_to_index[pubkey.GetID()] = nIndex; | ||||
} | } | ||||
LogPrintf("keypool return %d\n", nIndex); | LogPrintf("keypool return %d\n", nIndex); | ||||
} | } | ||||
Show All 40 Lines | int64_t CWallet::GetOldestKeyPoolTime() { | ||||
WalletBatch batch(*database); | WalletBatch batch(*database); | ||||
// load oldest key from keypool, get time and return | // load oldest key from keypool, get time and return | ||||
int64_t oldestKey = GetOldestKeyTimeInPool(setExternalKeyPool, batch); | int64_t oldestKey = GetOldestKeyTimeInPool(setExternalKeyPool, batch); | ||||
if (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT)) { | if (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT)) { | ||||
oldestKey = std::max(GetOldestKeyTimeInPool(setInternalKeyPool, batch), | oldestKey = std::max(GetOldestKeyTimeInPool(setInternalKeyPool, batch), | ||||
oldestKey); | oldestKey); | ||||
if (!set_pre_split_keypool.empty()) { | |||||
oldestKey = | |||||
std::max(GetOldestKeyTimeInPool(set_pre_split_keypool, batch), | |||||
oldestKey); | |||||
} | |||||
} | } | ||||
return oldestKey; | return oldestKey; | ||||
} | } | ||||
std::map<CTxDestination, Amount> CWallet::GetAddressBalances() { | std::map<CTxDestination, Amount> CWallet::GetAddressBalances() { | ||||
std::map<CTxDestination, Amount> balances; | std::map<CTxDestination, Amount> balances; | ||||
▲ Show 20 Lines • Show All 192 Lines • ▼ Show 20 Lines | void CReserveKey::ReturnKey() { | ||||
nIndex = -1; | nIndex = -1; | ||||
vchPubKey = CPubKey(); | vchPubKey = CPubKey(); | ||||
} | } | ||||
void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id) { | void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id) { | ||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
bool internal = setInternalKeyPool.count(keypool_id); | bool internal = setInternalKeyPool.count(keypool_id); | ||||
if (!internal) { | if (!internal) { | ||||
assert(setExternalKeyPool.count(keypool_id)); | assert(setExternalKeyPool.count(keypool_id) || | ||||
set_pre_split_keypool.count(keypool_id)); | |||||
} | } | ||||
std::set<int64_t> *setKeyPool = | std::set<int64_t> *setKeyPool = | ||||
internal ? &setInternalKeyPool : &setExternalKeyPool; | internal ? &setInternalKeyPool | ||||
: (set_pre_split_keypool.empty() ? &setExternalKeyPool | |||||
: &set_pre_split_keypool); | |||||
auto it = setKeyPool->begin(); | auto it = setKeyPool->begin(); | ||||
WalletBatch batch(*database); | WalletBatch batch(*database); | ||||
while (it != std::end(*setKeyPool)) { | while (it != std::end(*setKeyPool)) { | ||||
const int64_t &index = *(it); | const int64_t &index = *(it); | ||||
if (index > keypool_id) { | if (index > keypool_id) { | ||||
// set*KeyPool is ordered | // set*KeyPool is ordered | ||||
break; | break; | ||||
▲ Show 20 Lines • Show All 301 Lines • ▼ Show 20 Lines | if (salvage_wallet) { | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
return WalletBatch::VerifyDatabaseFile(wallet_path, warning_string, | return WalletBatch::VerifyDatabaseFile(wallet_path, warning_string, | ||||
error_string); | error_string); | ||||
} | } | ||||
void CWallet::MarkPreSplitKeys() { | |||||
WalletBatch batch(*database); | |||||
for (auto it = setExternalKeyPool.begin(); | |||||
it != setExternalKeyPool.end();) { | |||||
int64_t index = *it; | |||||
CKeyPool keypool; | |||||
if (!batch.ReadPool(index, keypool)) { | |||||
throw std::runtime_error(std::string(__func__) + | |||||
": read keypool entry failed"); | |||||
} | |||||
keypool.m_pre_split = true; | |||||
if (!batch.WritePool(index, keypool)) { | |||||
throw std::runtime_error(std::string(__func__) + | |||||
": writing modified keypool entry failed"); | |||||
} | |||||
set_pre_split_keypool.insert(index); | |||||
it = setExternalKeyPool.erase(it); | |||||
} | |||||
} | |||||
std::shared_ptr<CWallet> | std::shared_ptr<CWallet> | ||||
CWallet::CreateWalletFromFile(const CChainParams &chainParams, | CWallet::CreateWalletFromFile(const CChainParams &chainParams, | ||||
const std::string &name, const fs::path &path) { | const std::string &name, const fs::path &path) { | ||||
const std::string &walletFile = name; | const std::string &walletFile = name; | ||||
// Needed to restore wallet transaction meta data after -zapwallettxes | // Needed to restore wallet transaction meta data after -zapwallettxes | ||||
std::vector<CWalletTx> vWtx; | std::vector<CWalletTx> vWtx; | ||||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | if (nLoadWalletRet != DBErrors::LOAD_OK) { | ||||
} else { | } else { | ||||
InitError(strprintf(_("Error loading %s"), walletFile)); | InitError(strprintf(_("Error loading %s"), walletFile)); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
} | } | ||||
uiInterface.LoadWallet(walletInstance); | uiInterface.LoadWallet(walletInstance); | ||||
int prev_version = walletInstance->nWalletVersion; | |||||
if (gArgs.GetBoolArg("-upgradewallet", fFirstRun)) { | if (gArgs.GetBoolArg("-upgradewallet", fFirstRun)) { | ||||
int nMaxVersion = gArgs.GetArg("-upgradewallet", 0); | int nMaxVersion = gArgs.GetArg("-upgradewallet", 0); | ||||
// The -upgradewallet without argument case | // The -upgradewallet without argument case | ||||
if (nMaxVersion == 0) { | if (nMaxVersion == 0) { | ||||
LogPrintf("Performing wallet upgrade to %i\n", FEATURE_LATEST); | LogPrintf("Performing wallet upgrade to %i\n", FEATURE_LATEST); | ||||
nMaxVersion = CLIENT_VERSION; | nMaxVersion = CLIENT_VERSION; | ||||
// permanently upgrade the wallet immediately | // permanently upgrade the wallet immediately | ||||
walletInstance->SetMinVersion(FEATURE_LATEST); | walletInstance->SetMinVersion(FEATURE_LATEST); | ||||
} else { | } else { | ||||
LogPrintf("Allowing wallet upgrade up to %i\n", nMaxVersion); | LogPrintf("Allowing wallet upgrade up to %i\n", nMaxVersion); | ||||
} | } | ||||
if (nMaxVersion < walletInstance->GetVersion()) { | if (nMaxVersion < walletInstance->GetVersion()) { | ||||
InitError(_("Cannot downgrade wallet")); | InitError(_("Cannot downgrade wallet")); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
walletInstance->SetMaxVersion(nMaxVersion); | walletInstance->SetMaxVersion(nMaxVersion); | ||||
} | } | ||||
// Upgrade to HD if explicit upgrade | |||||
if (gArgs.GetBoolArg("-upgradewallet", false)) { | |||||
LOCK(walletInstance->cs_wallet); | |||||
bool hd_upgrade = false; | |||||
bool split_upgrade = false; | |||||
if (walletInstance->CanSupportFeature(FEATURE_HD) && | |||||
!walletInstance->IsHDEnabled()) { | |||||
LogPrintf("Upgrading wallet to HD\n"); | |||||
walletInstance->SetMinVersion(FEATURE_HD); | |||||
// generate a new master key | |||||
CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey(); | |||||
walletInstance->SetHDMasterKey(masterPubKey); | |||||
hd_upgrade = true; | |||||
} | |||||
// Upgrade to HD chain split if necessary | |||||
if (walletInstance->CanSupportFeature(FEATURE_HD_SPLIT)) { | |||||
LogPrintf("Upgrading wallet to use HD chain split\n"); | |||||
walletInstance->SetMinVersion(FEATURE_HD_SPLIT); | |||||
split_upgrade = FEATURE_HD_SPLIT > prev_version; | |||||
} | |||||
// Mark all keys currently in the keypool as pre-split | |||||
if (split_upgrade) { | |||||
walletInstance->MarkPreSplitKeys(); | |||||
} | |||||
// Regenerate the keypool if upgraded to HD | |||||
if (hd_upgrade) { | |||||
if (!walletInstance->TopUpKeyPool()) { | |||||
InitError(_("Unable to generate keys") += "\n"); | |||||
return nullptr; | |||||
} | |||||
} | |||||
} | |||||
if (fFirstRun) { | if (fFirstRun) { | ||||
// Ensure this wallet.dat can only be opened by clients supporting | // Ensure this wallet.dat can only be opened by clients supporting | ||||
// HD with chain split and expects no default key. | // HD with chain split and expects no default key. | ||||
if (!gArgs.GetBoolArg("-usehd", true)) { | if (!gArgs.GetBoolArg("-usehd", true)) { | ||||
InitError(strprintf(_("Error creating %s: You can't create non-HD " | InitError(strprintf(_("Error creating %s: You can't create non-HD " | ||||
"wallets with this version."), | "wallets with this version."), | ||||
walletFile)); | walletFile)); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
walletInstance->SetMinVersion(FEATURE_NO_DEFAULT_KEY); | walletInstance->SetMinVersion(FEATURE_LATEST); | ||||
// Generate a new master key. | // Generate a new master key. | ||||
CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey(); | CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey(); | ||||
walletInstance->SetHDMasterKey(masterPubKey); | walletInstance->SetHDMasterKey(masterPubKey); | ||||
// Top up the keypool | // Top up the keypool | ||||
if (!walletInstance->TopUpKeyPool()) { | if (!walletInstance->TopUpKeyPool()) { | ||||
InitError(_("Unable to generate initial keys") += "\n"); | InitError(_("Unable to generate initial keys") += "\n"); | ||||
▲ Show 20 Lines • Show All 193 Lines • ▼ Show 20 Lines | |||||
bool CWallet::BackupWallet(const std::string &strDest) { | bool CWallet::BackupWallet(const std::string &strDest) { | ||||
return database->Backup(strDest); | return database->Backup(strDest); | ||||
} | } | ||||
CKeyPool::CKeyPool() { | CKeyPool::CKeyPool() { | ||||
nTime = GetTime(); | nTime = GetTime(); | ||||
fInternal = false; | fInternal = false; | ||||
m_pre_split = false; | |||||
} | } | ||||
CKeyPool::CKeyPool(const CPubKey &vchPubKeyIn, bool internalIn) { | CKeyPool::CKeyPool(const CPubKey &vchPubKeyIn, bool internalIn) { | ||||
nTime = GetTime(); | nTime = GetTime(); | ||||
vchPubKey = vchPubKeyIn; | vchPubKey = vchPubKeyIn; | ||||
fInternal = internalIn; | fInternal = internalIn; | ||||
m_pre_split = false; | |||||
} | } | ||||
CWalletKey::CWalletKey(int64_t nExpires) { | CWalletKey::CWalletKey(int64_t nExpires) { | ||||
nTimeCreated = (nExpires ? GetTime() : 0); | nTimeCreated = (nExpires ? GetTime() : 0); | ||||
nTimeExpires = nExpires; | nTimeExpires = nExpires; | ||||
} | } | ||||
void CMerkleTx::SetMerkleBranch(const CBlockIndex *pindex, int posInBlock) { | void CMerkleTx::SetMerkleBranch(const CBlockIndex *pindex, int posInBlock) { | ||||
▲ Show 20 Lines • Show All 150 Lines • Show Last 20 Lines |