Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/wallet.cpp
Show First 20 Lines • Show All 1,854 Lines • ▼ Show 20 Lines | CWallet::ScanResult CWallet::ScanForWalletTransactions( | ||||
WalletLogPrintf("Rescan started from block %s...\n", | WalletLogPrintf("Rescan started from block %s...\n", | ||||
start_block.ToString()); | start_block.ToString()); | ||||
{ | { | ||||
fAbortRescan = false; | fAbortRescan = false; | ||||
// Show rescan progress in GUI as dialog or on splashscreen, if -rescan | // Show rescan progress in GUI as dialog or on splashscreen, if -rescan | ||||
// on startup. | // on startup. | ||||
ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), | ShowProgress( | ||||
strprintf("%s " + _("Rescanning...").translated, GetDisplayName()), | |||||
0); | 0); | ||||
BlockHash tip_hash; | BlockHash tip_hash; | ||||
// The way the 'block_height' is initialized is just a workaround for | // The way the 'block_height' is initialized is just a workaround for | ||||
// the gcc bug #47679 since version 4.6.0. | // the gcc bug #47679 since version 4.6.0. | ||||
Optional<int> block_height = MakeOptional(false, int()); | Optional<int> block_height = MakeOptional(false, int()); | ||||
double progress_begin; | double progress_begin; | ||||
double progress_end; | double progress_end; | ||||
{ | { | ||||
auto locked_chain = chain().lock(); | auto locked_chain = chain().lock(); | ||||
if (Optional<int> tip_height = locked_chain->getHeight()) { | if (Optional<int> tip_height = locked_chain->getHeight()) { | ||||
tip_hash = locked_chain->getBlockHash(*tip_height); | tip_hash = locked_chain->getBlockHash(*tip_height); | ||||
} | } | ||||
block_height = locked_chain->getBlockHeight(block_hash); | block_height = locked_chain->getBlockHeight(block_hash); | ||||
progress_begin = chain().guessVerificationProgress(block_hash); | progress_begin = chain().guessVerificationProgress(block_hash); | ||||
progress_end = chain().guessVerificationProgress( | progress_end = chain().guessVerificationProgress( | ||||
stop_block.IsNull() ? tip_hash : stop_block); | stop_block.IsNull() ? tip_hash : stop_block); | ||||
} | } | ||||
double progress_current = progress_begin; | double progress_current = progress_begin; | ||||
while (block_height && !fAbortRescan && !chain().shutdownRequested()) { | while (block_height && !fAbortRescan && !chain().shutdownRequested()) { | ||||
if (*block_height % 100 == 0 && | if (*block_height % 100 == 0 && | ||||
progress_end - progress_begin > 0.0) { | progress_end - progress_begin > 0.0) { | ||||
ShowProgress( | ShowProgress( | ||||
strprintf("%s " + _("Rescanning..."), GetDisplayName()), | strprintf("%s " + _("Rescanning...").translated, | ||||
GetDisplayName()), | |||||
std::max( | std::max( | ||||
1, | 1, | ||||
std::min(99, (int)((progress_current - progress_begin) / | std::min(99, (int)((progress_current - progress_begin) / | ||||
(progress_end - progress_begin) * | (progress_end - progress_begin) * | ||||
100)))); | 100)))); | ||||
} | } | ||||
if (GetTime() >= nNow + 60) { | if (GetTime() >= nNow + 60) { | ||||
nNow = GetTime(); | nNow = GetTime(); | ||||
▲ Show 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | WalletLogPrintf("Rescan started from block %s...\n", | ||||
if (stop_block.IsNull() && prev_tip_hash != tip_hash) { | if (stop_block.IsNull() && prev_tip_hash != tip_hash) { | ||||
// in case the tip has changed, update progress max | // in case the tip has changed, update progress max | ||||
progress_end = chain().guessVerificationProgress(tip_hash); | progress_end = chain().guessVerificationProgress(tip_hash); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// Hide progress dialog in GUI. | // Hide progress dialog in GUI. | ||||
ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), | ShowProgress( | ||||
strprintf("%s " + _("Rescanning...").translated, GetDisplayName()), | |||||
100); | 100); | ||||
if (block_height && fAbortRescan) { | if (block_height && fAbortRescan) { | ||||
WalletLogPrintf("Rescan aborted at block %d. Progress=%f\n", | WalletLogPrintf("Rescan aborted at block %d. Progress=%f\n", | ||||
*block_height, progress_current); | *block_height, progress_current); | ||||
result.status = ScanResult::USER_ABORT; | result.status = ScanResult::USER_ABORT; | ||||
} else if (block_height && chain().shutdownRequested()) { | } else if (block_height && chain().shutdownRequested()) { | ||||
WalletLogPrintf("Rescan interrupted by shutdown request at block " | WalletLogPrintf("Rescan interrupted by shutdown request at block " | ||||
"%d. Progress=%f\n", | "%d. Progress=%f\n", | ||||
*block_height, progress_current); | *block_height, progress_current); | ||||
▲ Show 20 Lines • Show All 1,076 Lines • ▼ Show 20 Lines | bool CWallet::CreateTransaction(interfaces::Chain::Lock &locked_chainIn, | ||||
Amount &nFeeRet, int &nChangePosInOut, | Amount &nFeeRet, int &nChangePosInOut, | ||||
std::string &strFailReason, | std::string &strFailReason, | ||||
const CCoinControl &coinControl, bool sign) { | const CCoinControl &coinControl, bool sign) { | ||||
Amount nValue = Amount::zero(); | Amount nValue = Amount::zero(); | ||||
int nChangePosRequest = nChangePosInOut; | int nChangePosRequest = nChangePosInOut; | ||||
unsigned int nSubtractFeeFromAmount = 0; | unsigned int nSubtractFeeFromAmount = 0; | ||||
for (const auto &recipient : vecSend) { | for (const auto &recipient : vecSend) { | ||||
if (nValue < Amount::zero() || recipient.nAmount < Amount::zero()) { | if (nValue < Amount::zero() || recipient.nAmount < Amount::zero()) { | ||||
strFailReason = _("Transaction amounts must not be negative"); | strFailReason = | ||||
_("Transaction amounts must not be negative").translated; | |||||
return false; | return false; | ||||
} | } | ||||
nValue += recipient.nAmount; | nValue += recipient.nAmount; | ||||
if (recipient.fSubtractFeeFromAmount) { | if (recipient.fSubtractFeeFromAmount) { | ||||
nSubtractFeeFromAmount++; | nSubtractFeeFromAmount++; | ||||
} | } | ||||
} | } | ||||
if (vecSend.empty()) { | if (vecSend.empty()) { | ||||
strFailReason = _("Transaction must have at least one recipient"); | strFailReason = | ||||
_("Transaction must have at least one recipient").translated; | |||||
return false; | return false; | ||||
} | } | ||||
CMutableTransaction txNew; | CMutableTransaction txNew; | ||||
txNew.nLockTime = GetLocktimeForNewTransaction(chain(), locked_chainIn); | txNew.nLockTime = GetLocktimeForNewTransaction(chain(), locked_chainIn); | ||||
{ | { | ||||
Show All 25 Lines | txNew.nLockTime = GetLocktimeForNewTransaction(chain(), locked_chainIn); | ||||
// reused the old key, it would be possible to add code to look | // reused the old key, it would be possible to add code to look | ||||
// for and rediscover unknown transactions that were written | // for and rediscover unknown transactions that were written | ||||
// with keys of ours to recover post-backup change. | // with keys of ours to recover 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)) { | if (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { | ||||
strFailReason = | strFailReason = | ||||
_("Can't generate a change-address key. Private keys " | _("Can't generate a change-address key. Private keys " | ||||
"are disabled for this wallet."); | "are disabled for this wallet.") | ||||
.translated; | |||||
return false; | return false; | ||||
} | } | ||||
CPubKey vchPubKey; | CPubKey vchPubKey; | ||||
bool ret; | bool ret; | ||||
ret = reservekey.GetReservedKey(vchPubKey, true); | ret = reservekey.GetReservedKey(vchPubKey, true); | ||||
if (!ret) { | if (!ret) { | ||||
strFailReason = | strFailReason = | ||||
_("Keypool ran out, please call keypoolrefill first"); | _("Keypool ran out, please call keypoolrefill first") | ||||
.translated; | |||||
return false; | return false; | ||||
} | } | ||||
const OutputType change_type = TransactionChangeType( | const OutputType change_type = TransactionChangeType( | ||||
coinControl.m_change_type ? *coinControl.m_change_type | coinControl.m_change_type ? *coinControl.m_change_type | ||||
: m_default_change_type, | : m_default_change_type, | ||||
vecSend); | vecSend); | ||||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | txNew.nLockTime = GetLocktimeForNewTransaction(chain(), locked_chainIn); | ||||
// BnB right now | // BnB right now | ||||
coin_selection_params.tx_noinputs_size += | coin_selection_params.tx_noinputs_size += | ||||
::GetSerializeSize(txout, PROTOCOL_VERSION); | ::GetSerializeSize(txout, PROTOCOL_VERSION); | ||||
if (IsDust(txout, chain().relayDustFee())) { | if (IsDust(txout, chain().relayDustFee())) { | ||||
if (recipient.fSubtractFeeFromAmount && | if (recipient.fSubtractFeeFromAmount && | ||||
nFeeRet > Amount::zero()) { | nFeeRet > Amount::zero()) { | ||||
if (txout.nValue < Amount::zero()) { | if (txout.nValue < Amount::zero()) { | ||||
strFailReason = _("The transaction amount is " | strFailReason = _("The transaction amount is too " | ||||
"too small to pay the fee"); | "small to pay the fee") | ||||
.translated; | |||||
} else { | } else { | ||||
strFailReason = | strFailReason = | ||||
_("The transaction amount is too small to " | _("The transaction amount is too small to " | ||||
"send after the fee has been deducted"); | "send after the fee has been deducted") | ||||
.translated; | |||||
} | } | ||||
} else { | } else { | ||||
strFailReason = _("Transaction amount too small"); | strFailReason = | ||||
_("Transaction amount too small").translated; | |||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
txNew.vout.push_back(txout); | txNew.vout.push_back(txout); | ||||
} | } | ||||
Show All 10 Lines | txNew.nLockTime = GetLocktimeForNewTransaction(chain(), locked_chainIn); | ||||
nValueIn, coinControl, coin_selection_params, | nValueIn, coinControl, coin_selection_params, | ||||
bnb_used)) { | bnb_used)) { | ||||
// If BnB was used, it was the first pass. No longer the | // If BnB was used, it was the first pass. No longer the | ||||
// first pass and continue loop with knapsack. | // first pass and continue loop with knapsack. | ||||
if (bnb_used) { | if (bnb_used) { | ||||
coin_selection_params.use_bnb = false; | coin_selection_params.use_bnb = false; | ||||
continue; | continue; | ||||
} else { | } else { | ||||
strFailReason = _("Insufficient funds"); | strFailReason = _("Insufficient funds").translated; | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
const Amount nChange = nValueIn - nValueToSelect; | const Amount nChange = nValueIn - nValueToSelect; | ||||
if (nChange > Amount::zero()) { | if (nChange > Amount::zero()) { | ||||
// Fill a vout to ourself. | // Fill a vout to ourself. | ||||
CTxOut newTxOut(nChange, scriptChange); | CTxOut newTxOut(nChange, scriptChange); | ||||
// Never create dust outputs; if we would, just add the dust to | // Never create dust outputs; if we would, just add the dust to | ||||
// the fee. | // the fee. | ||||
// The nChange when BnB is used is always going to go to fees. | // The nChange when BnB is used is always going to go to fees. | ||||
if (IsDust(newTxOut, dustRelayFee) || bnb_used) { | if (IsDust(newTxOut, dustRelayFee) || bnb_used) { | ||||
nChangePosInOut = -1; | nChangePosInOut = -1; | ||||
nFeeRet += nChange; | nFeeRet += nChange; | ||||
} else { | } else { | ||||
if (nChangePosInOut == -1) { | if (nChangePosInOut == -1) { | ||||
// Insert change txn at random position: | // Insert change txn at random position: | ||||
nChangePosInOut = GetRandInt(txNew.vout.size() + 1); | nChangePosInOut = GetRandInt(txNew.vout.size() + 1); | ||||
} else if ((unsigned int)nChangePosInOut > | } else if ((unsigned int)nChangePosInOut > | ||||
txNew.vout.size()) { | txNew.vout.size()) { | ||||
strFailReason = _("Change index out of range"); | strFailReason = | ||||
_("Change index out of range").translated; | |||||
return false; | return false; | ||||
} | } | ||||
std::vector<CTxOut>::iterator position = | std::vector<CTxOut>::iterator position = | ||||
txNew.vout.begin() + nChangePosInOut; | txNew.vout.begin() + nChangePosInOut; | ||||
txNew.vout.insert(position, newTxOut); | txNew.vout.insert(position, newTxOut); | ||||
} | } | ||||
} else { | } else { | ||||
nChangePosInOut = -1; | nChangePosInOut = -1; | ||||
} | } | ||||
// Dummy fill vin for maximum size estimation | // Dummy fill vin for maximum size estimation | ||||
// | // | ||||
for (const auto &coin : setCoins) { | for (const auto &coin : setCoins) { | ||||
txNew.vin.push_back(CTxIn(coin.outpoint, CScript())); | txNew.vin.push_back(CTxIn(coin.outpoint, CScript())); | ||||
} | } | ||||
CTransaction txNewConst(txNew); | CTransaction txNewConst(txNew); | ||||
int nBytes = CalculateMaximumSignedTxSize( | int nBytes = CalculateMaximumSignedTxSize( | ||||
txNewConst, this, coinControl.fAllowWatchOnly); | txNewConst, this, coinControl.fAllowWatchOnly); | ||||
if (nBytes < 0) { | if (nBytes < 0) { | ||||
strFailReason = _("Signing transaction failed"); | strFailReason = _("Signing transaction failed").translated; | ||||
return false; | return false; | ||||
} | } | ||||
Amount nFeeNeeded = GetMinimumFee(*this, nBytes, coinControl); | Amount nFeeNeeded = GetMinimumFee(*this, nBytes, coinControl); | ||||
// If we made it here and we aren't even able to meet the relay fee | // If we made it here and we aren't even able to meet the relay fee | ||||
// on the next pass, give up because we must be at the maximum | // on the next pass, give up because we must be at the maximum | ||||
// allowed fee. | // allowed fee. | ||||
if (nFeeNeeded < chain().relayMinFee().GetFee(nBytes)) { | if (nFeeNeeded < chain().relayMinFee().GetFee(nBytes)) { | ||||
strFailReason = _("Transaction too large for fee policy"); | strFailReason = | ||||
_("Transaction too large for fee policy").translated; | |||||
return false; | return false; | ||||
} | } | ||||
if (nFeeRet >= nFeeNeeded) { | if (nFeeRet >= nFeeNeeded) { | ||||
// Reduce fee to only the needed amount if possible. This | // Reduce fee to only the needed amount if possible. This | ||||
// prevents potential overpayment in fees if the coins selected | // prevents potential overpayment in fees if the coins selected | ||||
// to meet nFeeNeeded result in a transaction that requires less | // to meet nFeeNeeded result in a transaction that requires less | ||||
// fee than the prior iteration. | // fee than the prior iteration. | ||||
Show All 34 Lines | txNew.nLockTime = GetLocktimeForNewTransaction(chain(), locked_chainIn); | ||||
// Done, enough fee included. | // Done, enough fee included. | ||||
break; | break; | ||||
} else if (!pick_new_inputs) { | } else if (!pick_new_inputs) { | ||||
// This shouldn't happen, we should have had enough excess fee | // This shouldn't happen, we should have had enough excess fee | ||||
// to pay for the new output and still meet nFeeNeeded. | // to pay for the new output and still meet nFeeNeeded. | ||||
// Or we should have just subtracted fee from recipients and | // Or we should have just subtracted fee from recipients and | ||||
// nFeeNeeded should not have changed. | // nFeeNeeded should not have changed. | ||||
strFailReason = | strFailReason = | ||||
_("Transaction fee and change calculation failed"); | _("Transaction fee and change calculation failed") | ||||
.translated; | |||||
return false; | return false; | ||||
} | } | ||||
// Try to reduce change to include necessary fee. | // Try to reduce change to include necessary fee. | ||||
if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { | if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { | ||||
Amount additionalFeeNeeded = nFeeNeeded - nFeeRet; | Amount additionalFeeNeeded = nFeeNeeded - nFeeRet; | ||||
std::vector<CTxOut>::iterator change_position = | std::vector<CTxOut>::iterator change_position = | ||||
txNew.vout.begin() + nChangePosInOut; | txNew.vout.begin() + nChangePosInOut; | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | txNew.nLockTime = GetLocktimeForNewTransaction(chain(), locked_chainIn); | ||||
const CScript &scriptPubKey = coin.txout.scriptPubKey; | const CScript &scriptPubKey = coin.txout.scriptPubKey; | ||||
SignatureData sigdata; | SignatureData sigdata; | ||||
if (!ProduceSignature( | if (!ProduceSignature( | ||||
*this, | *this, | ||||
MutableTransactionSignatureCreator( | MutableTransactionSignatureCreator( | ||||
&txNew, nIn, coin.txout.nValue, sigHashType), | &txNew, nIn, coin.txout.nValue, sigHashType), | ||||
scriptPubKey, sigdata)) { | scriptPubKey, sigdata)) { | ||||
strFailReason = _("Signing transaction failed"); | strFailReason = _("Signing transaction failed").translated; | ||||
return false; | return false; | ||||
} | } | ||||
UpdateInput(txNew.vin.at(nIn), sigdata); | UpdateInput(txNew.vin.at(nIn), sigdata); | ||||
nIn++; | nIn++; | ||||
} | } | ||||
} | } | ||||
// Return the constructed transaction data. | // Return the constructed transaction data. | ||||
tx = MakeTransactionRef(std::move(txNew)); | tx = MakeTransactionRef(std::move(txNew)); | ||||
// Limit size. | // Limit size. | ||||
if (tx->GetTotalSize() >= MAX_STANDARD_TX_SIZE) { | if (tx->GetTotalSize() >= MAX_STANDARD_TX_SIZE) { | ||||
strFailReason = _("Transaction too large"); | strFailReason = _("Transaction too large").translated; | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
if (gArgs.GetBoolArg("-walletrejectlongchains", | if (gArgs.GetBoolArg("-walletrejectlongchains", | ||||
DEFAULT_WALLET_REJECT_LONG_CHAINS)) { | DEFAULT_WALLET_REJECT_LONG_CHAINS)) { | ||||
// Lastly, ensure this tx will pass the mempool's chain limits | // Lastly, ensure this tx will pass the mempool's chain limits | ||||
if (!chain().checkChainLimits(tx)) { | if (!chain().checkChainLimits(tx)) { | ||||
strFailReason = _("Transaction has too long of a mempool chain"); | strFailReason = | ||||
_("Transaction has too long of a mempool chain").translated; | |||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
/** | /** | ||||
▲ Show 20 Lines • Show All 1,025 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)) { | ||||
chain.initMessage(_("Zapping all transactions from wallet...")); | chain.initMessage( | ||||
_("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( | chain.initError( | ||||
strprintf(_("Error loading %s: Wallet corrupted"), walletFile)); | strprintf(_("Error loading %s: Wallet corrupted").translated, | ||||
walletFile)); | |||||
return nullptr; | return nullptr; | ||||
} | } | ||||
} | } | ||||
chain.initMessage(_("Loading wallet...")); | 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( | chain.initError( | ||||
strprintf(_("Error loading %s: Wallet corrupted"), walletFile)); | strprintf(_("Error loading %s: Wallet corrupted").translated, | ||||
walletFile)); | |||||
return nullptr; | return nullptr; | ||||
} | } | ||||
if (nLoadWalletRet == DBErrors::NONCRITICAL_ERROR) { | if (nLoadWalletRet == DBErrors::NONCRITICAL_ERROR) { | ||||
chain.initError(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."), | .translated, | ||||
walletFile)); | walletFile)); | ||||
} else if (nLoadWalletRet == DBErrors::TOO_NEW) { | } else if (nLoadWalletRet == DBErrors::TOO_NEW) { | ||||
chain.initError(strprintf( | chain.initError(strprintf( | ||||
_("Error loading %s: Wallet requires newer version of %s"), | _("Error loading %s: Wallet requires newer version of %s") | ||||
.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( | chain.initError(strprintf( | ||||
_("Wallet needed to be rewritten: restart %s to complete"), | _("Wallet needed to be rewritten: restart %s to complete") | ||||
.translated, | |||||
PACKAGE_NAME)); | PACKAGE_NAME)); | ||||
return nullptr; | return nullptr; | ||||
} else { | } else { | ||||
chain.initError(strprintf(_("Error loading %s"), walletFile)); | chain.initError( | ||||
strprintf(_("Error loading %s").translated, 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()) { | ||||
chain.initError(_("Cannot downgrade wallet")); | chain.initError(_("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->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) { | ||||
chain.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.") | ||||
.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")); | chain.initError(_("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); | ||||
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()) { | ||||
chain.initError(_("Unable to generate initial keys")); | chain.initError(_("Unable to generate initial keys").translated); | ||||
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->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( | chain.initError( | ||||
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, | |||||
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( | chain.initWarning( | ||||
strprintf(_("Warning: Private keys detected in wallet " | strprintf(_("Warning: Private keys detected in wallet " | ||||
"{%s} with disabled private keys"), | "{%s} with disabled private keys") | ||||
.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( | chain.initError( | ||||
AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", ""))); | AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", ""))); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
if (n > HIGH_TX_FEE_PER_KB) { | if (n > HIGH_TX_FEE_PER_KB) { | ||||
chain.initWarning( | chain.initWarning( | ||||
AmountHighWarn("-mintxfee") + " " + | AmountHighWarn("-mintxfee") + " " + | ||||
_("This is the minimum transaction fee you pay on " | _("This is the minimum transaction fee you pay on " | ||||
"every transaction.")); | "every transaction.") | ||||
.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( | chain.initError(strprintf( | ||||
strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'"), | _("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( | chain.initWarning( | ||||
AmountHighWarn("-fallbackfee") + " " + | 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.") | ||||
.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 | // 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)) { | ||||
chain.initError( | chain.initError( | ||||
AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", ""))); | AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", ""))); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
if (nFeePerK > HIGH_TX_FEE_PER_KB) { | if (nFeePerK > HIGH_TX_FEE_PER_KB) { | ||||
chain.initWarning( | chain.initWarning( | ||||
AmountHighWarn("-paytxfee") + " " + | 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.") | ||||
.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( | chain.initError(strprintf( | ||||
_("Invalid amount for -paytxfee=<amount>: '%s' " | _("Invalid amount for -paytxfee=<amount>: '%s' " | ||||
"(must be at least %s)"), | "(must be at least %s)") | ||||
.translated, | |||||
gArgs.GetArg("-paytxfee", ""), chain.relayMinFee().ToString())); | gArgs.GetArg("-paytxfee", ""), chain.relayMinFee().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; | ||||
Show All 40 Lines | if (tip_height && *tip_height != rescan_height) { | ||||
rescan_height != block_height) { | rescan_height != block_height) { | ||||
--block_height; | --block_height; | ||||
} | } | ||||
if (rescan_height != block_height) { | if (rescan_height != block_height) { | ||||
chain.initError( | chain.initError( | ||||
_("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); | |||||
return nullptr; | return nullptr; | ||||
} | } | ||||
} | } | ||||
chain.initMessage(_("Rescanning...")); | 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); | ||||
// 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 10 Lines | if (tip_height && *tip_height != rescan_height) { | ||||
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( | chain.initError( | ||||
_("Failed to rescan the wallet during initialization")); | _("Failed to rescan the wallet during initialization") | ||||
.translated); | |||||
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->getTipLocator()); | walletInstance->ChainStateFlushed(locked_chain->getTipLocator()); | ||||
walletInstance->database->IncrementUpdateCounter(); | walletInstance->database->IncrementUpdateCounter(); | ||||
▲ Show 20 Lines • Show All 194 Lines • Show Last 20 Lines |