Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/wallet.cpp
Show First 20 Lines • Show All 135 Lines • ▼ Show 20 Lines | void UnloadWallet(std::shared_ptr<CWallet> &&wallet) { | ||||
} | } | ||||
} | } | ||||
static const size_t OUTPUT_GROUP_MAX_ENTRIES = 10; | static const size_t OUTPUT_GROUP_MAX_ENTRIES = 10; | ||||
std::shared_ptr<CWallet> LoadWallet(const CChainParams &chainParams, | std::shared_ptr<CWallet> LoadWallet(const CChainParams &chainParams, | ||||
interfaces::Chain &chain, | interfaces::Chain &chain, | ||||
const WalletLocation &location, | const WalletLocation &location, | ||||
std::string &error, std::string &warning) { | std::string &error, | ||||
if (!CWallet::Verify(chainParams, chain, location, false, error, warning)) { | std::vector<std::string> &warnings) { | ||||
if (!CWallet::Verify(chainParams, chain, location, false, error, | |||||
warnings)) { | |||||
error = "Wallet file verification failed: " + error; | error = "Wallet file verification failed: " + error; | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
std::shared_ptr<CWallet> wallet = | std::shared_ptr<CWallet> wallet = CWallet::CreateWalletFromFile( | ||||
CWallet::CreateWalletFromFile(chainParams, chain, location); | chainParams, chain, location, error, warnings); | ||||
if (!wallet) { | if (!wallet) { | ||||
error = "Wallet loading failed."; | error = "Wallet loading failed: " + error; | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
AddWallet(wallet); | AddWallet(wallet); | ||||
wallet->postInitProcess(); | wallet->postInitProcess(); | ||||
return wallet; | return wallet; | ||||
} | } | ||||
std::shared_ptr<CWallet> LoadWallet(const CChainParams &chainParams, | std::shared_ptr<CWallet> LoadWallet(const CChainParams &chainParams, | ||||
interfaces::Chain &chain, | interfaces::Chain &chain, | ||||
const std::string &name, std::string &error, | const std::string &name, std::string &error, | ||||
std::string &warning) { | std::vector<std::string> &warnings) { | ||||
return LoadWallet(chainParams, chain, WalletLocation(name), error, warning); | return LoadWallet(chainParams, chain, WalletLocation(name), error, | ||||
warnings); | |||||
} | } | ||||
WalletCreationStatus | WalletCreationStatus CreateWallet(const CChainParams ¶ms, | ||||
CreateWallet(const CChainParams ¶ms, interfaces::Chain &chain, | interfaces::Chain &chain, | ||||
const SecureString &passphrase, uint64_t wallet_creation_flags, | const SecureString &passphrase, | ||||
const std::string &name, std::string &error, std::string &warning, | uint64_t wallet_creation_flags, | ||||
const std::string &name, std::string &error, | |||||
std::vector<std::string> &warnings, | |||||
std::shared_ptr<CWallet> &result) { | std::shared_ptr<CWallet> &result) { | ||||
// Indicate that the wallet is actually supposed to be blank and not just | // Indicate that the wallet is actually supposed to be blank and not just | ||||
// blank to make it encrypted | // blank to make it encrypted | ||||
bool create_blank = (wallet_creation_flags & WALLET_FLAG_BLANK_WALLET); | bool create_blank = (wallet_creation_flags & WALLET_FLAG_BLANK_WALLET); | ||||
// Born encrypted wallets need to be created blank first. | // Born encrypted wallets need to be created blank first. | ||||
if (!passphrase.empty()) { | if (!passphrase.empty()) { | ||||
wallet_creation_flags |= WALLET_FLAG_BLANK_WALLET; | wallet_creation_flags |= WALLET_FLAG_BLANK_WALLET; | ||||
} | } | ||||
// Check the wallet file location | // Check the wallet file location | ||||
WalletLocation location(name); | WalletLocation location(name); | ||||
if (location.Exists()) { | if (location.Exists()) { | ||||
error = "Wallet " + location.GetName() + " already exists."; | error = "Wallet " + location.GetName() + " already exists."; | ||||
return WalletCreationStatus::CREATION_FAILED; | return WalletCreationStatus::CREATION_FAILED; | ||||
} | } | ||||
// Wallet::Verify will check if we're trying to create a wallet with a | // Wallet::Verify will check if we're trying to create a wallet with a | ||||
// duplicate name. | // duplicate name. | ||||
std::string wallet_error; | if (!CWallet::Verify(params, chain, location, false, error, warnings)) { | ||||
if (!CWallet::Verify(params, chain, location, false, wallet_error, | error = "Wallet file verification failed: " + error; | ||||
warning)) { | |||||
error = "Wallet file verification failed: " + wallet_error; | |||||
return WalletCreationStatus::CREATION_FAILED; | return WalletCreationStatus::CREATION_FAILED; | ||||
} | } | ||||
// Do not allow a passphrase when private keys are disabled | // Do not allow a passphrase when private keys are disabled | ||||
if (!passphrase.empty() && | if (!passphrase.empty() && | ||||
(wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { | (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { | ||||
error = "Passphrase provided but private keys are disabled. A " | error = "Passphrase provided but private keys are disabled. A " | ||||
"passphrase is only used to encrypt private keys, so cannot be " | "passphrase is only used to encrypt private keys, so cannot be " | ||||
"used for wallets with private keys disabled."; | "used for wallets with private keys disabled."; | ||||
return WalletCreationStatus::CREATION_FAILED; | return WalletCreationStatus::CREATION_FAILED; | ||||
} | } | ||||
// Make the wallet | // Make the wallet | ||||
std::shared_ptr<CWallet> wallet = CWallet::CreateWalletFromFile( | std::shared_ptr<CWallet> wallet = CWallet::CreateWalletFromFile( | ||||
params, chain, location, wallet_creation_flags); | params, chain, location, error, warnings, wallet_creation_flags); | ||||
if (!wallet) { | if (!wallet) { | ||||
error = "Wallet creation failed"; | error = "Wallet creation failed: " + error; | ||||
return WalletCreationStatus::CREATION_FAILED; | return WalletCreationStatus::CREATION_FAILED; | ||||
} | } | ||||
// Encrypt the wallet | // Encrypt the wallet | ||||
if (!passphrase.empty() && | if (!passphrase.empty() && | ||||
!(wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { | !(wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { | ||||
if (!wallet->EncryptWallet(passphrase)) { | if (!wallet->EncryptWallet(passphrase)) { | ||||
error = "Error: Wallet created but failed to encrypt."; | error = "Error: Wallet created but failed to encrypt."; | ||||
▲ Show 20 Lines • Show All 4,390 Lines • ▼ Show 20 Lines | for (const auto &address : mapAddressBook) { | ||||
} | } | ||||
} | } | ||||
} | } | ||||
return values; | return values; | ||||
} | } | ||||
bool CWallet::Verify(const CChainParams &chainParams, interfaces::Chain &chain, | bool CWallet::Verify(const CChainParams &chainParams, interfaces::Chain &chain, | ||||
const WalletLocation &location, bool salvage_wallet, | const WalletLocation &location, bool salvage_wallet, | ||||
std::string &error_string, std::string &warning_string) { | std::string &error_string, | ||||
std::vector<std::string> &warnings) { | |||||
// Do some checking on wallet path. It should be either a: | // Do some checking on wallet path. It should be either a: | ||||
// | // | ||||
// 1. Path where a directory can be created. | // 1. Path where a directory can be created. | ||||
// 2. Path to an existing directory. | // 2. Path to an existing directory. | ||||
// 3. Path to a symlink to a directory. | // 3. Path to a symlink to a directory. | ||||
// 4. For backwards compatibility, the name of a data file in -walletdir. | // 4. For backwards compatibility, the name of a data file in -walletdir. | ||||
LOCK(cs_wallets); | LOCK(cs_wallets); | ||||
const fs::path &wallet_path = location.GetPath(); | const fs::path &wallet_path = location.GetPath(); | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | if (salvage_wallet) { | ||||
auto locked_chain = dummyWallet.LockChain(); | auto locked_chain = dummyWallet.LockChain(); | ||||
if (!WalletBatch::Recover( | if (!WalletBatch::Recover( | ||||
wallet_path, static_cast<void *>(&dummyWallet), | wallet_path, static_cast<void *>(&dummyWallet), | ||||
WalletBatch::RecoverKeysOnlyFilter, backup_filename)) { | WalletBatch::RecoverKeysOnlyFilter, backup_filename)) { | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
return WalletBatch::VerifyDatabaseFile(wallet_path, warning_string, | return WalletBatch::VerifyDatabaseFile(wallet_path, warnings, error_string); | ||||
error_string); | |||||
} | } | ||||
void CWallet::MarkPreSplitKeys() { | void CWallet::MarkPreSplitKeys() { | ||||
WalletBatch batch(*database); | WalletBatch batch(*database); | ||||
for (auto it = setExternalKeyPool.begin(); | for (auto it = setExternalKeyPool.begin(); | ||||
it != setExternalKeyPool.end();) { | it != setExternalKeyPool.end();) { | ||||
int64_t index = *it; | int64_t index = *it; | ||||
CKeyPool keypool; | CKeyPool keypool; | ||||
if (!batch.ReadPool(index, keypool)) { | if (!batch.ReadPool(index, keypool)) { | ||||
throw std::runtime_error(std::string(__func__) + | throw std::runtime_error(std::string(__func__) + | ||||
": read keypool entry failed"); | ": read keypool entry failed"); | ||||
} | } | ||||
keypool.m_pre_split = true; | keypool.m_pre_split = true; | ||||
if (!batch.WritePool(index, keypool)) { | if (!batch.WritePool(index, keypool)) { | ||||
throw std::runtime_error(std::string(__func__) + | throw std::runtime_error(std::string(__func__) + | ||||
": writing modified keypool entry failed"); | ": writing modified keypool entry failed"); | ||||
} | } | ||||
set_pre_split_keypool.insert(index); | set_pre_split_keypool.insert(index); | ||||
it = setExternalKeyPool.erase(it); | it = setExternalKeyPool.erase(it); | ||||
} | } | ||||
} | } | ||||
std::shared_ptr<CWallet> CWallet::CreateWalletFromFile( | 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, std::string &error, | ||||
std::vector<std::string> &warnings, uint64_t wallet_creation_flags) { | |||||
const std::string walletFile = | const std::string walletFile = | ||||
WalletDataFilePath(location.GetPath()).string(); | WalletDataFilePath(location.GetPath()).string(); | ||||
// 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)) { | ||||
chain.initMessage( | chain.initMessage( | ||||
_("Zapping all transactions from wallet...").translated); | _("Zapping all transactions from wallet...").translated); | ||||
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) { | ||||
chain.initError( | error = strprintf( | ||||
strprintf(_("Error loading %s: Wallet corrupted").translated, | _("Error loading %s: Wallet corrupted").translated, walletFile); | ||||
walletFile)); | |||||
return nullptr; | return nullptr; | ||||
} | } | ||||
} | } | ||||
chain.initMessage(_("Loading wallet...").translated); | chain.initMessage(_("Loading wallet...").translated); | ||||
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) { | ||||
chain.initError( | error = strprintf( | ||||
strprintf(_("Error loading %s: Wallet corrupted").translated, | _("Error loading %s: Wallet corrupted").translated, walletFile); | ||||
walletFile)); | |||||
return nullptr; | return nullptr; | ||||
} | } | ||||
if (nLoadWalletRet == DBErrors::NONCRITICAL_ERROR) { | if (nLoadWalletRet == DBErrors::NONCRITICAL_ERROR) { | ||||
chain.initError(strprintf( | warnings.push_back(strprintf( | ||||
_("Error reading %s! All keys read correctly, but transaction " | _("Error reading %s! All keys read correctly, but transaction " | ||||
"data or address book entries might be missing or incorrect.") | "data" | ||||
Fabien: Layout | |||||
" or address book entries might be missing or incorrect.") | |||||
.translated, | .translated, | ||||
walletFile)); | walletFile)); | ||||
} else if (nLoadWalletRet == DBErrors::TOO_NEW) { | } else if (nLoadWalletRet == DBErrors::TOO_NEW) { | ||||
chain.initError(strprintf( | error = strprintf( | ||||
_("Error loading %s: Wallet requires newer version of %s") | _("Error loading %s: Wallet requires newer version of %s") | ||||
.translated, | .translated, | ||||
walletFile, PACKAGE_NAME)); | walletFile, PACKAGE_NAME); | ||||
return nullptr; | return nullptr; | ||||
} else if (nLoadWalletRet == DBErrors::NEED_REWRITE) { | } else if (nLoadWalletRet == DBErrors::NEED_REWRITE) { | ||||
chain.initError(strprintf( | error = strprintf( | ||||
_("Wallet needed to be rewritten: restart %s to complete") | _("Wallet needed to be rewritten: restart %s to complete") | ||||
.translated, | .translated, | ||||
PACKAGE_NAME)); | PACKAGE_NAME); | ||||
return nullptr; | return nullptr; | ||||
} else { | } else { | ||||
chain.initError( | error = strprintf(_("Error loading %s").translated, walletFile); | ||||
strprintf(_("Error loading %s").translated, walletFile)); | |||||
return nullptr; | return nullptr; | ||||
} | } | ||||
} | } | ||||
int prev_version = walletInstance->GetVersion(); | int prev_version = walletInstance->GetVersion(); | ||||
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()) { | ||||
chain.initError(_("Cannot downgrade wallet").translated); | error = _("Cannot downgrade wallet").translated; | ||||
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->GetVersion(); | int max_version = walletInstance->GetVersion(); | ||||
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) { | ||||
chain.initError( | error = | ||||
_("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.") | ||||
.translated); | .translated; | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
bool hd_upgrade = false; | bool hd_upgrade = false; | ||||
bool split_upgrade = false; | bool split_upgrade = false; | ||||
if (walletInstance->CanSupportFeature(FEATURE_HD) && | if (walletInstance->CanSupportFeature(FEATURE_HD) && | ||||
!walletInstance->IsHDEnabled()) { | !walletInstance->IsHDEnabled()) { | ||||
walletInstance->WalletLogPrintf("Upgrading wallet to HD\n"); | walletInstance->WalletLogPrintf("Upgrading wallet to HD\n"); | ||||
Show All 13 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()) { | ||||
chain.initError(_("Unable to generate keys").translated); | error = _("Unable to generate keys").translated; | ||||
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); | ||||
walletInstance->SetWalletFlags(wallet_creation_flags, false); | walletInstance->SetWalletFlags(wallet_creation_flags, false); | ||||
if (!(wallet_creation_flags & | if (!(wallet_creation_flags & | ||||
(WALLET_FLAG_DISABLE_PRIVATE_KEYS | WALLET_FLAG_BLANK_WALLET))) { | (WALLET_FLAG_DISABLE_PRIVATE_KEYS | WALLET_FLAG_BLANK_WALLET))) { | ||||
// generate a new seed | // generate a new seed | ||||
CPubKey seed = walletInstance->GenerateNewSeed(); | CPubKey seed = walletInstance->GenerateNewSeed(); | ||||
walletInstance->SetHDSeed(seed); | walletInstance->SetHDSeed(seed); | ||||
} | } | ||||
// Top up the keypool | // Top up the keypool | ||||
if (walletInstance->CanGenerateKeys() && | if (walletInstance->CanGenerateKeys() && | ||||
!walletInstance->TopUpKeyPool()) { | !walletInstance->TopUpKeyPool()) { | ||||
chain.initError(_("Unable to generate initial keys").translated); | error = _("Unable to generate initial keys").translated; | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
auto locked_chain = chain.lock(); | auto locked_chain = chain.lock(); | ||||
walletInstance->ChainStateFlushed(locked_chain->getTipLocator()); | walletInstance->ChainStateFlushed(locked_chain->getTipLocator()); | ||||
} 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 | ||||
chain.initError( | error = strprintf(_("Error loading %s: Private keys can only be " | ||||
strprintf(_("Error loading %s: Private keys can only be " | |||||
"disabled during creation") | "disabled during creation") | ||||
.translated, | .translated, | ||||
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()) { | ||||
chain.initWarning( | warnings.push_back( | ||||
strprintf(_("Warning: Private keys detected in wallet " | strprintf(_("Warning: Private keys detected in wallet {%s} " | ||||
"{%s} with disabled private keys") | "with disabled private keys") | ||||
.translated, | .translated, | ||||
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()) { | ||||
chain.initError( | error = AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", "")) | ||||
AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", "")) | .translated; | ||||
.translated); | |||||
return nullptr; | return nullptr; | ||||
} | } | ||||
if (n > HIGH_TX_FEE_PER_KB) { | if (n > HIGH_TX_FEE_PER_KB) { | ||||
chain.initWarning( | warnings.push_back(AmountHighWarn("-mintxfee").translated + " " + | ||||
AmountHighWarn("-mintxfee").translated + " " + | _("This is the minimum transaction fee you pay " | ||||
_("This is the minimum transaction fee you pay on " | "on every transaction.") | ||||
"every transaction.") | |||||
.translated); | .translated); | ||||
} | } | ||||
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)) { | ||||
chain.initError(strprintf( | error = strprintf( | ||||
_("Invalid amount for -fallbackfee=<amount>: '%s'").translated, | _("Invalid amount for -fallbackfee=<amount>: '%s'").translated, | ||||
gArgs.GetArg("-fallbackfee", ""))); | gArgs.GetArg("-fallbackfee", "")); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
if (nFeePerK > HIGH_TX_FEE_PER_KB) { | if (nFeePerK > HIGH_TX_FEE_PER_KB) { | ||||
chain.initWarning( | warnings.push_back(AmountHighWarn("-fallbackfee").translated + " " + | ||||
AmountHighWarn("-fallbackfee").translated + " " + | _("This is the transaction fee you may pay when " | ||||
_("This is the transaction fee you may pay when fee " | "fee estimates are not available.") | ||||
"estimates are not available.") | |||||
.translated); | .translated); | ||||
} | } | ||||
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 value | // Disable fallback fee in case value was set to 0, enable if non-null value | ||||
walletInstance->m_allow_fallback_fee = | walletInstance->m_allow_fallback_fee = | ||||
walletInstance->m_fallback_fee.GetFeePerK() != Amount::zero(); | walletInstance->m_fallback_fee.GetFeePerK() != 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)) { | ||||
chain.initError( | error = AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", "")) | ||||
AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", "")) | .translated; | ||||
.translated); | |||||
return nullptr; | return nullptr; | ||||
} | } | ||||
if (nFeePerK > HIGH_TX_FEE_PER_KB) { | if (nFeePerK > HIGH_TX_FEE_PER_KB) { | ||||
chain.initWarning( | warnings.push_back(AmountHighWarn("-paytxfee").translated + " " + | ||||
AmountHighWarn("-paytxfee").translated + " " + | _("This is the transaction fee you will pay if " | ||||
_("This is the transaction fee you will pay if you " | "you send a transaction.") | ||||
"send a transaction.") | |||||
.translated); | .translated); | ||||
} | } | ||||
walletInstance->m_pay_tx_fee = CFeeRate(nFeePerK, 1000); | walletInstance->m_pay_tx_fee = CFeeRate(nFeePerK, 1000); | ||||
if (walletInstance->m_pay_tx_fee < chain.relayMinFee()) { | if (walletInstance->m_pay_tx_fee < chain.relayMinFee()) { | ||||
chain.initError(strprintf( | error = strprintf(_("Invalid amount for -paytxfee=<amount>: '%s' " | ||||
_("Invalid amount for -paytxfee=<amount>: '%s' " | |||||
"(must be at least %s)") | "(must be at least %s)") | ||||
.translated, | .translated, | ||||
gArgs.GetArg("-paytxfee", ""), chain.relayMinFee().ToString())); | gArgs.GetArg("-paytxfee", ""), | ||||
chain.relayMinFee().ToString()); | |||||
return nullptr; | return nullptr; | ||||
} | } | ||||
} | } | ||||
if (gArgs.IsArgSet("-maxtxfee")) { | if (gArgs.IsArgSet("-maxtxfee")) { | ||||
Amount nMaxFee = Amount::zero(); | Amount nMaxFee = Amount::zero(); | ||||
if (!ParseMoney(gArgs.GetArg("-maxtxfee", ""), nMaxFee)) { | if (!ParseMoney(gArgs.GetArg("-maxtxfee", ""), nMaxFee)) { | ||||
chain.initError( | error = AmountErrMsg("maxtxfee", gArgs.GetArg("-maxtxfee", "")) | ||||
AmountErrMsg("maxtxfee", gArgs.GetArg("-maxtxfee", "")) | .translated; | ||||
.translated); | |||||
return nullptr; | return nullptr; | ||||
} | } | ||||
if (nMaxFee > HIGH_MAX_TX_FEE) { | if (nMaxFee > HIGH_MAX_TX_FEE) { | ||||
chain.initWarning(_("-maxtxfee is set very high! Fees this large " | warnings.push_back(_("-maxtxfee is set very high! Fees this large " | ||||
"could be paid on a single transaction.") | "could be paid on a single transaction.") | ||||
.translated); | .translated); | ||||
} | } | ||||
if (CFeeRate(nMaxFee, 1000) < chain.relayMinFee()) { | if (CFeeRate(nMaxFee, 1000) < chain.relayMinFee()) { | ||||
chain.initError(strprintf( | error = strprintf( | ||||
_("Invalid amount for -maxtxfee=<amount>: '%s' (must be at " | _("Invalid amount for -maxtxfee=<amount>: '%s' (must be at " | ||||
"least the minrelay fee of %s to prevent stuck transactions)") | "least the minrelay fee of %s to prevent stuck transactions)") | ||||
.translated, | .translated, | ||||
gArgs.GetArg("-maxtxfee", ""), chain.relayMinFee().ToString())); | gArgs.GetArg("-maxtxfee", ""), chain.relayMinFee().ToString()); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
walletInstance->m_default_max_tx_fee = nMaxFee; | walletInstance->m_default_max_tx_fee = nMaxFee; | ||||
} | } | ||||
if (chain.relayMinFee().GetFeePerK() > HIGH_TX_FEE_PER_KB) { | if (chain.relayMinFee().GetFeePerK() > HIGH_TX_FEE_PER_KB) { | ||||
chain.initWarning( | warnings.push_back( | ||||
AmountHighWarn("-minrelaytxfee").translated + " " + | AmountHighWarn("-minrelaytxfee").translated + " " + | ||||
_("The wallet will avoid paying less than the minimum relay fee.") | _("The wallet will avoid paying less than the minimum relay fee.") | ||||
.translated); | .translated); | ||||
} | } | ||||
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); | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 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) { | ||||
chain.initError( | error = _("Prune: last wallet synchronisation goes beyond " | ||||
_("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)") | ||||
.translated); | .translated; | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
} | } | ||||
chain.initMessage(_("Rescanning...").translated); | chain.initMessage(_("Rescanning...").translated); | ||||
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); | ||||
Show All 13 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)) { | ||||
chain.initError( | error = _("Failed to rescan the wallet during initialization") | ||||
_("Failed to rescan the wallet during initialization") | .translated; | ||||
.translated); | |||||
return nullptr; | return nullptr; | ||||
} | } | ||||
} | } | ||||
walletInstance->ChainStateFlushed(locked_chain->getTipLocator()); | walletInstance->ChainStateFlushed(locked_chain->getTipLocator()); | ||||
walletInstance->database->IncrementUpdateCounter(); | walletInstance->database->IncrementUpdateCounter(); | ||||
// Restore wallet transaction metadata after -zapwallettxes=1 | // Restore wallet transaction metadata after -zapwallettxes=1 | ||||
if (gArgs.GetBoolArg("-zapwallettxes", false) && | if (gArgs.GetBoolArg("-zapwallettxes", false) && | ||||
▲ Show 20 Lines • Show All 385 Lines • Show Last 20 Lines |
Layout