Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/wallet.cpp
Show First 20 Lines • Show All 2,799 Lines • ▼ Show 20 Lines | for (const CTxIn &txin : tx_new->vin) { | ||||
} | } | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
static bool IsCurrentForAntiFeeSniping(interfaces::Chain &chain, | static bool IsCurrentForAntiFeeSniping(interfaces::Chain &chain, | ||||
interfaces::Chain::Lock &locked_chain) { | const BlockHash &block_hash) { | ||||
if (chain.isInitialBlockDownload()) { | 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 (locked_chain.getBlockTime(*locked_chain.getHeight()) < | int64_t block_time; | ||||
(GetTime() - MAX_ANTI_FEE_SNIPING_TIP_AGE)) { | CHECK_NONFATAL(chain.findBlock(block_hash, FoundBlock().time(block_time))); | ||||
if (block_time < (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 &chain, | ||||
GetLocktimeForNewTransaction(interfaces::Chain &chain, | const BlockHash &block_hash, | ||||
interfaces::Chain::Lock &locked_chain) { | int block_height) { | ||||
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(chain, locked_chain)) { | if (IsCurrentForAntiFeeSniping(chain, block_hash)) { | ||||
locktime = height; | locktime = block_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)); | ||||
} | } | ||||
} else { | } else { | ||||
// If our chain is lagging behind, we can't discourage fee sniping nor | // If our chain is lagging behind, we can't discourage fee sniping nor | ||||
// help the privacy of high-latency transactions. To avoid leaking a | // help the privacy of high-latency transactions. To avoid leaking a | ||||
// potentially unique "nLockTime fingerprint", set nLockTime to a | // potentially unique "nLockTime fingerprint", set nLockTime to a | ||||
// constant. | // constant. | ||||
locktime = 0; | locktime = 0; | ||||
} | } | ||||
assert(locktime <= height); | |||||
assert(locktime < LOCKTIME_THRESHOLD); | assert(locktime < LOCKTIME_THRESHOLD); | ||||
return locktime; | return locktime; | ||||
} | } | ||||
OutputType | OutputType | ||||
CWallet::TransactionChangeType(OutputType change_type, | CWallet::TransactionChangeType(OutputType change_type, | ||||
const std::vector<CRecipient> &vecSend) { | const std::vector<CRecipient> &vecSend) { | ||||
// If -changetype is specified, always use that change type. | // If -changetype is specified, always use that change type. | ||||
Show All 40 Lines | bool CWallet::CreateTransactionInternal(interfaces::Chain::Lock &locked_chainIn, | ||||
if (vecSend.empty()) { | if (vecSend.empty()) { | ||||
error = _("Transaction must have at least one recipient"); | error = _("Transaction must have at least one recipient"); | ||||
return false; | return false; | ||||
} | } | ||||
CMutableTransaction txNew; | CMutableTransaction txNew; | ||||
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); | ||||
txNew.nLockTime = GetLocktimeForNewTransaction( | |||||
chain(), GetLastBlockHash(), GetLastBlockHeight()); | |||||
std::vector<COutput> vAvailableCoins; | std::vector<COutput> vAvailableCoins; | ||||
AvailableCoins(*locked_chain, vAvailableCoins, true, &coin_control); | AvailableCoins(*locked_chain, vAvailableCoins, true, &coin_control); | ||||
// Parameters for coin selection, init with dummy | // Parameters for coin selection, init with dummy | ||||
CoinSelectionParams coin_selection_params; | CoinSelectionParams coin_selection_params; | ||||
// Create change script that will be used if we need change | // Create change script that will be used if we need change | ||||
// TODO: pass in scriptChange instead of reservedest so | // TODO: pass in scriptChange instead of reservedest so | ||||
// change transaction isn't always pay-to-bitcoin-address | // change transaction isn't always pay-to-bitcoin-address | ||||
▲ Show 20 Lines • Show All 1,833 Lines • Show Last 20 Lines |