Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/wallet.cpp
Show First 20 Lines • Show All 4,430 Lines • ▼ Show 20 Lines | std::shared_ptr<CWallet> CWallet::CreateWalletFromFile( | ||||
const CChainParams &chainParams, interfaces::Chain &chain, | const CChainParams &chainParams, interfaces::Chain &chain, | ||||
const WalletLocation &location, uint64_t wallet_creation_flags) { | const WalletLocation &location, uint64_t wallet_creation_flags) { | ||||
const std::string &walletFile = location.GetName(); | const std::string &walletFile = location.GetName(); | ||||
// 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; | ||||
if (gArgs.GetBoolArg("-zapwallettxes", false)) { | if (gArgs.GetBoolArg("-zapwallettxes", false)) { | ||||
uiInterface.InitMessage(_("Zapping all transactions from wallet...")); | chain.initMessage(_("Zapping all transactions from wallet...")); | ||||
std::unique_ptr<CWallet> tempWallet = std::make_unique<CWallet>( | std::unique_ptr<CWallet> tempWallet = std::make_unique<CWallet>( | ||||
chainParams, chain, location, | chainParams, chain, location, | ||||
WalletDatabase::Create(location.GetPath())); | WalletDatabase::Create(location.GetPath())); | ||||
DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx); | DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx); | ||||
if (nZapWalletRet != DBErrors::LOAD_OK) { | if (nZapWalletRet != DBErrors::LOAD_OK) { | ||||
InitError( | chain.initError( | ||||
strprintf(_("Error loading %s: Wallet corrupted"), walletFile)); | strprintf(_("Error loading %s: Wallet corrupted"), walletFile)); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
} | } | ||||
uiInterface.InitMessage(_("Loading wallet...")); | chain.initMessage(_("Loading wallet...")); | ||||
int64_t nStart = GetTimeMillis(); | int64_t nStart = GetTimeMillis(); | ||||
bool fFirstRun = true; | bool fFirstRun = true; | ||||
// TODO: Can't use std::make_shared because we need a custom deleter but | // TODO: Can't use std::make_shared because we need a custom deleter but | ||||
// should be possible to use std::allocate_shared. | // should be possible to use std::allocate_shared. | ||||
std::shared_ptr<CWallet> walletInstance( | std::shared_ptr<CWallet> walletInstance( | ||||
new CWallet(chainParams, chain, location, | new CWallet(chainParams, chain, location, | ||||
WalletDatabase::Create(location.GetPath())), | WalletDatabase::Create(location.GetPath())), | ||||
ReleaseWallet); | ReleaseWallet); | ||||
DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun); | DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun); | ||||
if (nLoadWalletRet != DBErrors::LOAD_OK) { | if (nLoadWalletRet != DBErrors::LOAD_OK) { | ||||
if (nLoadWalletRet == DBErrors::CORRUPT) { | if (nLoadWalletRet == DBErrors::CORRUPT) { | ||||
InitError( | chain.initError( | ||||
strprintf(_("Error loading %s: Wallet corrupted"), walletFile)); | strprintf(_("Error loading %s: Wallet corrupted"), walletFile)); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
if (nLoadWalletRet == DBErrors::NONCRITICAL_ERROR) { | if (nLoadWalletRet == DBErrors::NONCRITICAL_ERROR) { | ||||
InitWarning(strprintf( | chain.initError(strprintf( | ||||
_("Error reading %s! All keys read correctly, but transaction " | _("Error reading %s! All keys read correctly, but transaction " | ||||
"data" | "data" | ||||
" or address book entries might be missing or incorrect."), | " or address book entries might be missing or incorrect."), | ||||
walletFile)); | walletFile)); | ||||
} else if (nLoadWalletRet == DBErrors::TOO_NEW) { | } else if (nLoadWalletRet == DBErrors::TOO_NEW) { | ||||
InitError(strprintf( | chain.initError(strprintf( | ||||
_("Error loading %s: Wallet requires newer version of %s"), | _("Error loading %s: Wallet requires newer version of %s"), | ||||
walletFile, PACKAGE_NAME)); | walletFile, PACKAGE_NAME)); | ||||
return nullptr; | return nullptr; | ||||
} else if (nLoadWalletRet == DBErrors::NEED_REWRITE) { | } else if (nLoadWalletRet == DBErrors::NEED_REWRITE) { | ||||
InitError(strprintf( | chain.initError(strprintf( | ||||
_("Wallet needed to be rewritten: restart %s to complete"), | _("Wallet needed to be rewritten: restart %s to complete"), | ||||
PACKAGE_NAME)); | PACKAGE_NAME)); | ||||
return nullptr; | return nullptr; | ||||
} else { | } else { | ||||
InitError(strprintf(_("Error loading %s"), walletFile)); | chain.initError(strprintf(_("Error loading %s"), walletFile)); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
} | } | ||||
int prev_version = walletInstance->nWalletVersion; | 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) { | ||||
walletInstance->WalletLogPrintf("Performing wallet upgrade to %i\n", | walletInstance->WalletLogPrintf("Performing wallet upgrade to %i\n", | ||||
FEATURE_LATEST); | FEATURE_LATEST); | ||||
nMaxVersion = FEATURE_LATEST; | nMaxVersion = FEATURE_LATEST; | ||||
// permanently upgrade the wallet immediately | // permanently upgrade the wallet immediately | ||||
walletInstance->SetMinVersion(FEATURE_LATEST); | walletInstance->SetMinVersion(FEATURE_LATEST); | ||||
} else { | } else { | ||||
walletInstance->WalletLogPrintf( | walletInstance->WalletLogPrintf( | ||||
"Allowing wallet upgrade up to %i\n", nMaxVersion); | "Allowing wallet upgrade up to %i\n", nMaxVersion); | ||||
} | } | ||||
if (nMaxVersion < walletInstance->GetVersion()) { | if (nMaxVersion < walletInstance->GetVersion()) { | ||||
InitError(_("Cannot downgrade wallet")); | chain.initError(_("Cannot downgrade wallet")); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
walletInstance->SetMaxVersion(nMaxVersion); | walletInstance->SetMaxVersion(nMaxVersion); | ||||
} | } | ||||
// Upgrade to HD if explicit upgrade | // Upgrade to HD if explicit upgrade | ||||
if (gArgs.GetBoolArg("-upgradewallet", false)) { | if (gArgs.GetBoolArg("-upgradewallet", false)) { | ||||
LOCK(walletInstance->cs_wallet); | LOCK(walletInstance->cs_wallet); | ||||
// Do not upgrade versions to any version between HD_SPLIT and | // Do not upgrade versions to any version between HD_SPLIT and | ||||
// FEATURE_PRE_SPLIT_KEYPOOL unless already supporting HD_SPLIT | // FEATURE_PRE_SPLIT_KEYPOOL unless already supporting HD_SPLIT | ||||
int max_version = walletInstance->nWalletVersion; | int max_version = walletInstance->nWalletVersion; | ||||
if (!walletInstance->CanSupportFeature(FEATURE_HD_SPLIT) && | if (!walletInstance->CanSupportFeature(FEATURE_HD_SPLIT) && | ||||
max_version >= FEATURE_HD_SPLIT && | max_version >= FEATURE_HD_SPLIT && | ||||
max_version < FEATURE_PRE_SPLIT_KEYPOOL) { | max_version < FEATURE_PRE_SPLIT_KEYPOOL) { | ||||
InitError( | chain.initError( | ||||
_("Cannot upgrade a non HD split wallet without upgrading to " | _("Cannot upgrade a non HD split wallet without upgrading to " | ||||
"support pre split keypool. Please use -upgradewallet=200300 " | "support pre split keypool. Please use -upgradewallet=200300 " | ||||
"or -upgradewallet with no version specified.")); | "or -upgradewallet with no version specified.")); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
bool hd_upgrade = false; | bool hd_upgrade = false; | ||||
bool split_upgrade = false; | bool split_upgrade = false; | ||||
Show All 16 Lines | if (gArgs.GetBoolArg("-upgradewallet", false)) { | ||||
} | } | ||||
// Mark all keys currently in the keypool as pre-split | // Mark all keys currently in the keypool as pre-split | ||||
if (split_upgrade) { | if (split_upgrade) { | ||||
walletInstance->MarkPreSplitKeys(); | walletInstance->MarkPreSplitKeys(); | ||||
} | } | ||||
// Regenerate the keypool if upgraded to HD | // Regenerate the keypool if upgraded to HD | ||||
if (hd_upgrade) { | if (hd_upgrade) { | ||||
if (!walletInstance->TopUpKeyPool()) { | if (!walletInstance->TopUpKeyPool()) { | ||||
InitError(_("Unable to generate keys")); | chain.initError(_("Unable to generate keys")); | ||||
return nullptr; | 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. | ||||
walletInstance->SetMinVersion(FEATURE_LATEST); | walletInstance->SetMinVersion(FEATURE_LATEST); | ||||
if ((wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { | if ((wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { | ||||
// selective allow to set flags | // selective allow to set flags | ||||
walletInstance->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS); | walletInstance->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS); | ||||
} else if (wallet_creation_flags & WALLET_FLAG_BLANK_WALLET) { | } else if (wallet_creation_flags & WALLET_FLAG_BLANK_WALLET) { | ||||
walletInstance->SetWalletFlag(WALLET_FLAG_BLANK_WALLET); | walletInstance->SetWalletFlag(WALLET_FLAG_BLANK_WALLET); | ||||
} else { | } else { | ||||
// generate a new seed | // generate a new seed | ||||
CPubKey seed = walletInstance->GenerateNewSeed(); | CPubKey seed = walletInstance->GenerateNewSeed(); | ||||
walletInstance->SetHDSeed(seed); | walletInstance->SetHDSeed(seed); | ||||
} // Otherwise, do not generate a new seed | } // Otherwise, do not generate a new seed | ||||
// Top up the keypool | // Top up the keypool | ||||
if (walletInstance->CanGenerateKeys() && | if (walletInstance->CanGenerateKeys() && | ||||
!walletInstance->TopUpKeyPool()) { | !walletInstance->TopUpKeyPool()) { | ||||
InitError(_("Unable to generate initial keys")); | chain.initError(_("Unable to generate initial keys")); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
// Temporary. Removed in upcoming lock cleanup | // Temporary. Removed in upcoming lock cleanup | ||||
auto locked_chain = chain.assumeLocked(); | auto locked_chain = chain.assumeLocked(); | ||||
walletInstance->ChainStateFlushed(locked_chain->getLocator()); | walletInstance->ChainStateFlushed(locked_chain->getLocator()); | ||||
} else if (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS) { | } else if (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS) { | ||||
// Make it impossible to disable private keys after creation | // Make it impossible to disable private keys after creation | ||||
InitError(strprintf(_("Error loading %s: Private keys can only be " | chain.initError( | ||||
strprintf(_("Error loading %s: Private keys can only be " | |||||
"disabled during creation"), | "disabled during creation"), | ||||
walletFile)); | walletFile)); | ||||
return nullptr; | return nullptr; | ||||
} else if (walletInstance->IsWalletFlagSet( | } else if (walletInstance->IsWalletFlagSet( | ||||
WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { | WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { | ||||
LOCK(walletInstance->cs_KeyStore); | LOCK(walletInstance->cs_KeyStore); | ||||
if (!walletInstance->mapKeys.empty() || | if (!walletInstance->mapKeys.empty() || | ||||
!walletInstance->mapCryptedKeys.empty()) { | !walletInstance->mapCryptedKeys.empty()) { | ||||
InitWarning(strprintf(_("Warning: Private keys detected in wallet " | chain.initWarning( | ||||
strprintf(_("Warning: Private keys detected in wallet " | |||||
"{%s} with disabled private keys"), | "{%s} with disabled private keys"), | ||||
walletFile)); | walletFile)); | ||||
} | } | ||||
} | } | ||||
if (gArgs.IsArgSet("-mintxfee")) { | if (gArgs.IsArgSet("-mintxfee")) { | ||||
Amount n = Amount::zero(); | Amount n = Amount::zero(); | ||||
if (!ParseMoney(gArgs.GetArg("-mintxfee", ""), n) || | if (!ParseMoney(gArgs.GetArg("-mintxfee", ""), n) || | ||||
n == Amount::zero()) { | n == Amount::zero()) { | ||||
InitError(AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", ""))); | chain.initError( | ||||
AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", ""))); | |||||
return nullptr; | return nullptr; | ||||
} | } | ||||
if (n > HIGH_TX_FEE_PER_KB) { | if (n > HIGH_TX_FEE_PER_KB) { | ||||
InitWarning(AmountHighWarn("-mintxfee") + " " + | chain.initWarning( | ||||
AmountHighWarn("-mintxfee") + " " + | |||||
_("This is the minimum transaction fee you pay on " | _("This is the minimum transaction fee you pay on " | ||||
"every transaction.")); | "every transaction.")); | ||||
} | } | ||||
walletInstance->m_min_fee = CFeeRate(n); | walletInstance->m_min_fee = CFeeRate(n); | ||||
} | } | ||||
if (gArgs.IsArgSet("-fallbackfee")) { | if (gArgs.IsArgSet("-fallbackfee")) { | ||||
Amount nFeePerK = Amount::zero(); | Amount nFeePerK = Amount::zero(); | ||||
if (!ParseMoney(gArgs.GetArg("-fallbackfee", ""), nFeePerK)) { | if (!ParseMoney(gArgs.GetArg("-fallbackfee", ""), nFeePerK)) { | ||||
InitError( | chain.initError( | ||||
strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'"), | strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'"), | ||||
gArgs.GetArg("-fallbackfee", ""))); | gArgs.GetArg("-fallbackfee", ""))); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
if (nFeePerK > HIGH_TX_FEE_PER_KB) { | if (nFeePerK > HIGH_TX_FEE_PER_KB) { | ||||
InitWarning(AmountHighWarn("-fallbackfee") + " " + | chain.initWarning( | ||||
AmountHighWarn("-fallbackfee") + " " + | |||||
_("This is the transaction fee you may pay when fee " | _("This is the transaction fee you may pay when fee " | ||||
"estimates are not available.")); | "estimates are not available.")); | ||||
} | } | ||||
walletInstance->m_fallback_fee = CFeeRate(nFeePerK); | walletInstance->m_fallback_fee = CFeeRate(nFeePerK); | ||||
// disable fallback fee in case value was set to 0, enable if non-null | // disable fallback fee in case value was set to 0, enable if non-null | ||||
// value | // value | ||||
walletInstance->m_allow_fallback_fee = (nFeePerK != Amount::zero()); | walletInstance->m_allow_fallback_fee = (nFeePerK != Amount::zero()); | ||||
} | } | ||||
if (gArgs.IsArgSet("-paytxfee")) { | if (gArgs.IsArgSet("-paytxfee")) { | ||||
Amount nFeePerK = Amount::zero(); | Amount nFeePerK = Amount::zero(); | ||||
if (!ParseMoney(gArgs.GetArg("-paytxfee", ""), nFeePerK)) { | if (!ParseMoney(gArgs.GetArg("-paytxfee", ""), nFeePerK)) { | ||||
InitError(AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", ""))); | chain.initError( | ||||
AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", ""))); | |||||
return nullptr; | return nullptr; | ||||
} | } | ||||
if (nFeePerK > HIGH_TX_FEE_PER_KB) { | if (nFeePerK > HIGH_TX_FEE_PER_KB) { | ||||
InitWarning(AmountHighWarn("-paytxfee") + " " + | chain.initWarning( | ||||
AmountHighWarn("-paytxfee") + " " + | |||||
_("This is the transaction fee you will pay if you " | _("This is the transaction fee you will pay if you " | ||||
"send a transaction.")); | "send a transaction.")); | ||||
} | } | ||||
walletInstance->m_pay_tx_fee = CFeeRate(nFeePerK, 1000); | walletInstance->m_pay_tx_fee = CFeeRate(nFeePerK, 1000); | ||||
if (walletInstance->m_pay_tx_fee < ::minRelayTxFee) { | if (walletInstance->m_pay_tx_fee < ::minRelayTxFee) { | ||||
InitError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s' " | chain.initError(strprintf( | ||||
_("Invalid amount for -paytxfee=<amount>: '%s' " | |||||
"(must be at least %s)"), | "(must be at least %s)"), | ||||
gArgs.GetArg("-paytxfee", ""), | gArgs.GetArg("-paytxfee", ""), ::minRelayTxFee.ToString())); | ||||
::minRelayTxFee.ToString())); | |||||
return nullptr; | return nullptr; | ||||
} | } | ||||
} | } | ||||
walletInstance->m_spend_zero_conf_change = | walletInstance->m_spend_zero_conf_change = | ||||
gArgs.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); | gArgs.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); | ||||
walletInstance->m_default_address_type = DEFAULT_ADDRESS_TYPE; | walletInstance->m_default_address_type = DEFAULT_ADDRESS_TYPE; | ||||
walletInstance->m_default_change_type = DEFAULT_CHANGE_TYPE; | walletInstance->m_default_change_type = DEFAULT_CHANGE_TYPE; | ||||
Show All 36 Lines | if (tip_height && *tip_height != rescan_height) { | ||||
int block_height = *tip_height; | int block_height = *tip_height; | ||||
while (block_height > 0 && | while (block_height > 0 && | ||||
locked_chain->haveBlockOnDisk(block_height - 1) && | locked_chain->haveBlockOnDisk(block_height - 1) && | ||||
rescan_height != block_height) { | rescan_height != block_height) { | ||||
--block_height; | --block_height; | ||||
} | } | ||||
if (rescan_height != block_height) { | if (rescan_height != block_height) { | ||||
InitError(_("Prune: last wallet synchronisation goes beyond " | chain.initError( | ||||
_("Prune: last wallet synchronisation goes beyond " | |||||
"pruned data. You need to -reindex (download the " | "pruned data. You need to -reindex (download the " | ||||
"whole blockchain again in case of pruned node)")); | "whole blockchain again in case of pruned node)")); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
} | } | ||||
uiInterface.InitMessage(_("Rescanning...")); | chain.initMessage(_("Rescanning...")); | ||||
walletInstance->WalletLogPrintf( | walletInstance->WalletLogPrintf( | ||||
"Rescanning last %i blocks (from block %i)...\n", | "Rescanning last %i blocks (from block %i)...\n", | ||||
*tip_height - rescan_height, rescan_height); | *tip_height - rescan_height, rescan_height); | ||||
// No need to read and scan block if block was created before our wallet | // No need to read and scan block if block was created before our wallet | ||||
// birthday (as adjusted for block time variability) | // birthday (as adjusted for block time variability) | ||||
if (walletInstance->nTimeFirstKey) { | if (walletInstance->nTimeFirstKey) { | ||||
if (Optional<int> first_block = | if (Optional<int> first_block = | ||||
Show All 9 Lines | if (tip_height && *tip_height != rescan_height) { | ||||
WalletRescanReserver reserver(walletInstance.get()); | WalletRescanReserver reserver(walletInstance.get()); | ||||
if (!reserver.reserve() || | if (!reserver.reserve() || | ||||
(ScanResult::SUCCESS != | (ScanResult::SUCCESS != | ||||
walletInstance | walletInstance | ||||
->ScanForWalletTransactions( | ->ScanForWalletTransactions( | ||||
locked_chain->getBlockHash(rescan_height), BlockHash(), | locked_chain->getBlockHash(rescan_height), BlockHash(), | ||||
reserver, true /* update */) | reserver, true /* update */) | ||||
.status)) { | .status)) { | ||||
InitError( | chain.initError( | ||||
_("Failed to rescan the wallet during initialization")); | _("Failed to rescan the wallet during initialization")); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
} | } | ||||
walletInstance->WalletLogPrintf("Rescan completed in %15dms\n", | walletInstance->WalletLogPrintf("Rescan completed in %15dms\n", | ||||
GetTimeMillis() - nStart); | GetTimeMillis() - nStart); | ||||
walletInstance->ChainStateFlushed(locked_chain->getLocator()); | walletInstance->ChainStateFlushed(locked_chain->getLocator()); | ||||
walletInstance->database->IncrementUpdateCounter(); | walletInstance->database->IncrementUpdateCounter(); | ||||
▲ Show 20 Lines • Show All 201 Lines • Show Last 20 Lines |