Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/wallet.cpp
Show First 20 Lines • Show All 1,863 Lines • ▼ Show 20 Lines | WalletLogPrintf("Rescan started from block %s...\n", | ||||
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 && !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..."), 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) * | ||||
▲ Show 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | WalletLogPrintf("Rescan started from block %s...\n", | ||||
// Hide progress dialog in GUI. | // Hide progress dialog in GUI. | ||||
ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), | ShowProgress(strprintf("%s " + _("Rescanning..."), 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.value_or(0), progress_current); | block_height.value_or(0), progress_current); | ||||
result.status = ScanResult::USER_ABORT; | result.status = ScanResult::USER_ABORT; | ||||
} else if (block_height && 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.value_or(0), progress_current); | block_height.value_or(0), progress_current); | ||||
result.status = ScanResult::USER_ABORT; | result.status = ScanResult::USER_ABORT; | ||||
} | } | ||||
} | } | ||||
return result; | return result; | ||||
▲ Show 20 Lines • Show All 486 Lines • ▼ Show 20 Lines | |||||
void CWallet::AvailableCoins(interfaces::Chain::Lock &locked_chain, | void CWallet::AvailableCoins(interfaces::Chain::Lock &locked_chain, | ||||
std::vector<COutput> &vCoins, bool fOnlySafe, | std::vector<COutput> &vCoins, bool fOnlySafe, | ||||
const CCoinControl *coinControl, | const CCoinControl *coinControl, | ||||
const Amount nMinimumAmount, | const Amount nMinimumAmount, | ||||
const Amount nMaximumAmount, | const Amount nMaximumAmount, | ||||
const Amount nMinimumSumAmount, | const Amount nMinimumSumAmount, | ||||
const uint64_t nMaximumCount, const int nMinDepth, | const uint64_t nMaximumCount, const int nMinDepth, | ||||
const int nMaxDepth) const { | const int nMaxDepth) const { | ||||
AssertLockHeld(cs_main); | |||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
vCoins.clear(); | vCoins.clear(); | ||||
Amount nTotal = Amount::zero(); | Amount nTotal = Amount::zero(); | ||||
const Consensus::Params params = Params().GetConsensus(); | const Consensus::Params params = Params().GetConsensus(); | ||||
for (const auto &entry : mapWallet) { | for (const auto &entry : mapWallet) { | ||||
▲ Show 20 Lines • Show All 104 Lines • ▼ Show 20 Lines | for (const auto &entry : mapWallet) { | ||||
return; | return; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
std::map<CTxDestination, std::vector<COutput>> | std::map<CTxDestination, std::vector<COutput>> | ||||
CWallet::ListCoins(interfaces::Chain::Lock &locked_chain) const { | CWallet::ListCoins(interfaces::Chain::Lock &locked_chain) const { | ||||
AssertLockHeld(cs_main); | |||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
std::map<CTxDestination, std::vector<COutput>> result; | std::map<CTxDestination, std::vector<COutput>> result; | ||||
std::vector<COutput> availableCoins; | std::vector<COutput> availableCoins; | ||||
AvailableCoins(locked_chain, availableCoins); | AvailableCoins(locked_chain, availableCoins); | ||||
for (const auto &coin : availableCoins) { | for (const auto &coin : availableCoins) { | ||||
▲ Show 20 Lines • Show All 348 Lines • ▼ Show 20 Lines | for (const CTxIn &txin : tx_new->vin) { | ||||
LockCoin(txin.prevout); | LockCoin(txin.prevout); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
static bool IsCurrentForAntiFeeSniping(interfaces::Chain::Lock &locked_chain) { | static bool IsCurrentForAntiFeeSniping(interfaces::Chain &chain, | ||||
if (IsInitialBlockDownload()) { | interfaces::Chain::Lock &locked_chain) { | ||||
if (chain.isInitialBlockDownload()) { | |||||
return false; | return false; | ||||
} | } | ||||
// in seconds | // in seconds | ||||
constexpr int64_t MAX_ANTI_FEE_SNIPING_TIP_AGE = 8 * 60 * 60; | constexpr int64_t MAX_ANTI_FEE_SNIPING_TIP_AGE = 8 * 60 * 60; | ||||
if (ChainActive().Tip()->GetBlockTime() < | if (locked_chain.getBlockTime(*locked_chain.getHeight()) < | ||||
(GetTime() - MAX_ANTI_FEE_SNIPING_TIP_AGE)) { | (GetTime() - MAX_ANTI_FEE_SNIPING_TIP_AGE)) { | ||||
return false; | return false; | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
/** | /** | ||||
* Return a height-based locktime for new transactions (uses the height of the | * Return a height-based locktime for new transactions (uses the height of the | ||||
* current chain tip unless we are not synced with the current chain | * current chain tip unless we are not synced with the current chain | ||||
*/ | */ | ||||
static uint32_t | static uint32_t | ||||
GetLocktimeForNewTransaction(interfaces::Chain::Lock &locked_chain) { | GetLocktimeForNewTransaction(interfaces::Chain &chain, | ||||
interfaces::Chain::Lock &locked_chain) { | |||||
uint32_t const height = locked_chain.getHeight().value_or(-1); | uint32_t const height = locked_chain.getHeight().value_or(-1); | ||||
uint32_t locktime; | uint32_t locktime; | ||||
// Discourage fee sniping. | // Discourage fee sniping. | ||||
// | // | ||||
// For a large miner the value of the transactions in the best block and | // For a large miner the value of the transactions in the best block and | ||||
// the mempool can exceed the cost of deliberately attempting to mine two | // the mempool can exceed the cost of deliberately attempting to mine two | ||||
// blocks to orphan the current best block. By setting nLockTime such that | // blocks to orphan the current best block. By setting nLockTime such that | ||||
// only the next block can include the transaction, we discourage this | // only the next block can include the transaction, we discourage this | ||||
// practice as the height restricted and limited blocksize gives miners | // practice as the height restricted and limited blocksize gives miners | ||||
// considering fee sniping fewer options for pulling off this attack. | // considering fee sniping fewer options for pulling off this attack. | ||||
// | // | ||||
// A simple way to think about this is from the wallet's point of view we | // A simple way to think about this is from the wallet's point of view we | ||||
// always want the blockchain to move forward. By setting nLockTime this | // always want the blockchain to move forward. By setting nLockTime this | ||||
// way we're basically making the statement that we only want this | // way we're basically making the statement that we only want this | ||||
// transaction to appear in the next block; we don't want to potentially | // transaction to appear in the next block; we don't want to potentially | ||||
// encourage reorgs by allowing transactions to appear at lower heights | // encourage reorgs by allowing transactions to appear at lower heights | ||||
// than the next block in forks of the best chain. | // than the next block in forks of the best chain. | ||||
// | // | ||||
// Of course, the subsidy is high enough, and transaction volume low | // Of course, the subsidy is high enough, and transaction volume low | ||||
// enough, that fee sniping isn't a problem yet, but by implementing a fix | // enough, that fee sniping isn't a problem yet, but by implementing a fix | ||||
// now we ensure code won't be written that makes assumptions about | // now we ensure code won't be written that makes assumptions about | ||||
// nLockTime that preclude a fix later. | // nLockTime that preclude a fix later. | ||||
if (IsCurrentForAntiFeeSniping(locked_chain)) { | if (IsCurrentForAntiFeeSniping(chain, locked_chain)) { | ||||
locktime = height; | locktime = height; | ||||
// Secondly occasionally randomly pick a nLockTime even further back, so | // Secondly occasionally randomly pick a nLockTime even further back, so | ||||
// that transactions that are delayed after signing for whatever reason, | // that transactions that are delayed after signing for whatever reason, | ||||
// e.g. high-latency mix networks and some CoinJoin implementations, | // e.g. high-latency mix networks and some CoinJoin implementations, | ||||
// have better privacy. | // have better privacy. | ||||
if (GetRandInt(10) == 0) { | if (GetRandInt(10) == 0) { | ||||
locktime = std::max(0, int(locktime) - GetRandInt(100)); | locktime = std::max(0, int(locktime) - GetRandInt(100)); | ||||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | bool CWallet::CreateTransaction(interfaces::Chain::Lock &locked_chainIn, | ||||
if (vecSend.empty()) { | if (vecSend.empty()) { | ||||
strFailReason = _("Transaction must have at least one recipient"); | strFailReason = _("Transaction must have at least one recipient"); | ||||
return false; | return false; | ||||
} | } | ||||
CMutableTransaction txNew; | CMutableTransaction txNew; | ||||
txNew.nLockTime = GetLocktimeForNewTransaction(locked_chainIn); | txNew.nLockTime = GetLocktimeForNewTransaction(chain(), locked_chainIn); | ||||
{ | { | ||||
std::set<CInputCoin> setCoins; | std::set<CInputCoin> setCoins; | ||||
auto locked_chain = chain().lock(); | auto locked_chain = chain().lock(); | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
std::vector<COutput> vAvailableCoins; | std::vector<COutput> vAvailableCoins; | ||||
AvailableCoins(*locked_chain, vAvailableCoins, true, &coinControl); | AvailableCoins(*locked_chain, vAvailableCoins, true, &coinControl); | ||||
▲ Show 20 Lines • Show All 93 Lines • ▼ Show 20 Lines | txNew.nLockTime = GetLocktimeForNewTransaction(chain(), locked_chainIn); | ||||
} | } | ||||
} | } | ||||
// Include the fee cost for outputs. Note this is only used for | // Include the fee cost for outputs. Note this is only used for | ||||
// 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, dustRelayFee)) { | 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 small to pay the fee"); | "too small to pay the fee"); | ||||
} else { | } else { | ||||
strFailReason = | strFailReason = | ||||
_("The transaction amount is too small to " | _("The transaction amount is too small to " | ||||
▲ Show 20 Lines • Show All 77 Lines • ▼ Show 20 Lines | txNew.nLockTime = GetLocktimeForNewTransaction(chain(), locked_chainIn); | ||||
} | } | ||||
Amount nFeeNeeded = | Amount nFeeNeeded = | ||||
GetMinimumFee(*this, nBytes, coinControl, g_mempool); | GetMinimumFee(*this, nBytes, coinControl, g_mempool); | ||||
// 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 < ::minRelayTxFee.GetFee(nBytes)) { | if (nFeeNeeded < chain().relayMinFee().GetFee(nBytes)) { | ||||
strFailReason = _("Transaction too large for fee policy"); | strFailReason = _("Transaction too large for fee policy"); | ||||
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 | ||||
▲ Show 20 Lines • Show All 1,385 Lines • ▼ Show 20 Lines | if (gArgs.IsArgSet("-paytxfee")) { | ||||
} | } | ||||
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.")); | ||||
} | } | ||||
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 < 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)"), | ||||
gArgs.GetArg("-paytxfee", ""), ::minRelayTxFee.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; | ||||
walletInstance->m_default_change_type = DEFAULT_CHANGE_TYPE; | walletInstance->m_default_change_type = DEFAULT_CHANGE_TYPE; | ||||
▲ Show 20 Lines • Show All 166 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
int CMerkleTx::GetDepthInMainChain( | int CMerkleTx::GetDepthInMainChain( | ||||
interfaces::Chain::Lock &locked_chain) const { | interfaces::Chain::Lock &locked_chain) const { | ||||
if (hashUnset()) { | if (hashUnset()) { | ||||
return 0; | return 0; | ||||
} | } | ||||
AssertLockHeld(cs_main); | |||||
return locked_chain.getBlockDepth(hashBlock) * (nIndex == -1 ? -1 : 1); | return locked_chain.getBlockDepth(hashBlock) * (nIndex == -1 ? -1 : 1); | ||||
} | } | ||||
int CMerkleTx::GetBlocksToMaturity( | int CMerkleTx::GetBlocksToMaturity( | ||||
interfaces::Chain::Lock &locked_chain) const { | interfaces::Chain::Lock &locked_chain) const { | ||||
if (!IsCoinBase()) { | if (!IsCoinBase()) { | ||||
return 0; | return 0; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 100 Lines • Show Last 20 Lines |