Changeset View
Changeset View
Standalone View
Standalone View
src/validation.cpp
Show First 20 Lines • Show All 507 Lines • ▼ Show 20 Lines | for (const auto &txin : tx.vin) { | ||||
return state.DoS(10, false, REJECT_INVALID, | return state.DoS(10, false, REJECT_INVALID, | ||||
"bad-txns-prevout-null"); | "bad-txns-prevout-null"); | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
static void LimitMempoolSize(CTxMemPool &pool, size_t limit, | |||||
unsigned long age) { | |||||
int expired = pool.Expire(GetTime() - age); | |||||
if (expired != 0) { | |||||
LogPrint(BCLog::MEMPOOL, | |||||
"Expired %i transactions from the memory pool\n", expired); | |||||
} | |||||
std::vector<COutPoint> vNoSpendsRemaining; | |||||
pool.TrimToSize(limit, &vNoSpendsRemaining); | |||||
for (const COutPoint &removed : vNoSpendsRemaining) { | |||||
pcoinsTip->Uncache(removed); | |||||
} | |||||
} | |||||
/** Convert CValidationState to a human-readable message for logging */ | /** Convert CValidationState to a human-readable message for logging */ | ||||
std::string FormatStateMessage(const CValidationState &state) { | std::string FormatStateMessage(const CValidationState &state) { | ||||
return strprintf( | return strprintf( | ||||
"%s%s (code %i)", state.GetRejectReason(), | "%s%s (code %i)", state.GetRejectReason(), | ||||
state.GetDebugMessage().empty() ? "" : ", " + state.GetDebugMessage(), | state.GetDebugMessage().empty() ? "" : ", " + state.GetDebugMessage(), | ||||
state.GetRejectCode()); | state.GetRejectCode()); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
// Command-line argument "-replayprotectionactivationtime=<timestamp>" will | // Command-line argument "-replayprotectionactivationtime=<timestamp>" will | ||||
// cause the node to switch to replay protected SigHash ForkID value when the | // cause the node to switch to replay protected SigHash ForkID value when the | ||||
// median timestamp of the previous 11 blocks is greater than or equal to | // median timestamp of the previous 11 blocks is greater than or equal to | ||||
// <timestamp>. Defaults to the pre-defined timestamp when not set. | // <timestamp>. Defaults to the pre-defined timestamp when not set. | ||||
static bool IsReplayProtectionEnabled(const Config &config, | static bool IsReplayProtectionEnabled(const Config &config, | ||||
int64_t nMedianTimePast) { | int64_t nMedianTimePast) { | ||||
return nMedianTimePast >= gArgs.GetArg("-replayprotectionactivationtime", | return nMedianTimePast >= | ||||
config.GetChainParams() | gArgs.GetArg( | ||||
.GetConsensus() | "-replayprotectionactivationtime", | ||||
.greatWallActivationTime); | config.GetChainParams().GetConsensus().greatWallActivationTime); | ||||
jasonbcox: This keeps getting linted for everyone touching this file. I say leave it in at this point. | |||||
} | } | ||||
static bool IsReplayProtectionEnabled(const Config &config, | static bool IsReplayProtectionEnabled(const Config &config, | ||||
const CBlockIndex *pindexPrev) { | const CBlockIndex *pindexPrev) { | ||||
if (pindexPrev == nullptr) { | if (pindexPrev == nullptr) { | ||||
return false; | return false; | ||||
} | } | ||||
return IsReplayProtectionEnabled(config, pindexPrev->GetMedianTimePast()); | return IsReplayProtectionEnabled(config, pindexPrev->GetMedianTimePast()); | ||||
} | } | ||||
static bool IsReplayProtectionEnabledForCurrentBlock(const Config &config) { | static bool IsReplayProtectionEnabledForCurrentBlock(const Config &config) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
return IsReplayProtectionEnabled(config, chainActive.Tip()); | return IsReplayProtectionEnabled(config, chainActive.Tip()); | ||||
} | } | ||||
/** | |||||
* Make mempool consistent after a reorg, by re-adding or recursively erasing | |||||
* disconnected block transactions from the mempool, and also removing any other | |||||
* transactions from the mempool that are no longer valid given the new | |||||
* tip/height. | |||||
* | |||||
* Note: we assume that disconnectpool only contains transactions that are NOT | |||||
* confirmed in the current chain nor already in the mempool (otherwise, | |||||
* in-mempool descendants of such transactions would be removed). | |||||
* | |||||
* Passing fAddToMempool=false will skip trying to add the transactions back, | |||||
* and instead just erase from the mempool as needed. | |||||
*/ | |||||
void UpdateMempoolForReorg(const Config &config, | |||||
DisconnectedBlockTransactions &disconnectpool, | |||||
bool fAddToMempool) { | |||||
AssertLockHeld(cs_main); | |||||
std::vector<uint256> vHashUpdate; | |||||
// disconnectpool's insertion_order index sorts the entries from oldest to | |||||
// newest, but the oldest entry will be the last tx from the latest mined | |||||
// block that was disconnected. | |||||
// Iterate disconnectpool in reverse, so that we add transactions back to | |||||
// the mempool starting with the earliest transaction that had been | |||||
// previously seen in a block. | |||||
auto it = disconnectpool.queuedTx.get<insertion_order>().rbegin(); | |||||
while (it != disconnectpool.queuedTx.get<insertion_order>().rend()) { | |||||
// ignore validation errors in resurrected transactions | |||||
CValidationState stateDummy; | |||||
if (!fAddToMempool || (*it)->IsCoinBase() || | |||||
!AcceptToMemoryPool(config, mempool, stateDummy, *it, false, | |||||
nullptr, true)) { | |||||
// If the transaction doesn't make it in to the mempool, remove any | |||||
// transactions that depend on it (which would now be orphans). | |||||
mempool.removeRecursive(**it, MemPoolRemovalReason::REORG); | |||||
} else if (mempool.exists((*it)->GetId())) { | |||||
vHashUpdate.push_back((*it)->GetId()); | |||||
} | |||||
++it; | |||||
} | |||||
disconnectpool.queuedTx.clear(); | |||||
// AcceptToMemoryPool/addUnchecked all assume that new mempool entries have | |||||
// no in-mempool children, which is generally not true when adding | |||||
// previously-confirmed transactions back to the mempool. | |||||
// UpdateTransactionsFromBlock finds descendants of any transactions in the | |||||
// disconnectpool that were added back and cleans up the mempool state. | |||||
mempool.UpdateTransactionsFromBlock(vHashUpdate); | |||||
// We also need to remove any now-immature transactions | |||||
mempool.removeForReorg(config, pcoinsTip, chainActive.Tip()->nHeight + 1, | |||||
STANDARD_LOCKTIME_VERIFY_FLAGS); | |||||
// Re-limit mempool size, in case we added any transactions | |||||
LimitMempoolSize( | |||||
mempool, | |||||
gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, | |||||
gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); | |||||
} | |||||
// Used to avoid mempool polluting consensus critical paths if CCoinsViewMempool | // Used to avoid mempool polluting consensus critical paths if CCoinsViewMempool | ||||
// were somehow broken and returning the wrong scriptPubKeys | // were somehow broken and returning the wrong scriptPubKeys | ||||
static bool | static bool | ||||
CheckInputsFromMempoolAndCache(const CTransaction &tx, CValidationState &state, | CheckInputsFromMempoolAndCache(const CTransaction &tx, CValidationState &state, | ||||
const CCoinsViewCache &view, CTxMemPool &pool, | const CCoinsViewCache &view, CTxMemPool &pool, | ||||
const uint32_t flags, bool cacheSigStore, | const uint32_t flags, bool cacheSigStore, | ||||
PrecomputedTransactionData &txdata) { | PrecomputedTransactionData &txdata) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
▲ Show 20 Lines • Show All 361 Lines • ▼ Show 20 Lines | // Check for conflicts with in-memory transactions | ||||
bool validForFeeEstimation = | bool validForFeeEstimation = | ||||
IsCurrentForFeeEstimation() && pool.HasNoInputsOf(tx); | IsCurrentForFeeEstimation() && pool.HasNoInputsOf(tx); | ||||
// Store transaction in memory. | // Store transaction in memory. | ||||
pool.addUnchecked(txid, entry, setAncestors, validForFeeEstimation); | pool.addUnchecked(txid, entry, setAncestors, validForFeeEstimation); | ||||
// Trim mempool and check if tx was trimmed. | // Trim mempool and check if tx was trimmed. | ||||
if (!fOverrideMempoolLimit) { | if (!fOverrideMempoolLimit) { | ||||
LimitMempoolSize( | pool.LimitSize( | ||||
pool, | |||||
gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, | gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, | ||||
gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * | gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * | ||||
60); | 60); | ||||
if (!pool.exists(txid)) { | if (!pool.exists(txid)) { | ||||
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, | return state.DoS(0, false, REJECT_INSUFFICIENTFEE, | ||||
"mempool full"); | "mempool full"); | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 1,460 Lines • ▼ Show 20 Lines | static void UpdateTip(const Config &config, CBlockIndex *pindexNew) { | ||||
} | } | ||||
LogPrintf("\n"); | LogPrintf("\n"); | ||||
} | } | ||||
/** | /** | ||||
* Disconnect chainActive's tip. | * Disconnect chainActive's tip. | ||||
* After calling, the mempool will be in an inconsistent state, with | * After calling, the mempool will be in an inconsistent state, with | ||||
* transactions from disconnected blocks being added to disconnectpool. You | * transactions from disconnected blocks being added to disconnectpool. You | ||||
* should make the mempool consistent again by calling UpdateMempoolForReorg. | * should make the mempool consistent again by calling updateMempoolForReorg. | ||||
* with cs_main held. | * with cs_main held. | ||||
* | * | ||||
* If disconnectpool is nullptr, then no disconnected transactions are added to | * If disconnectpool is nullptr, then no disconnected transactions are added to | ||||
* disconnectpool (note that the caller is responsible for mempool consistency | * disconnectpool (note that the caller is responsible for mempool consistency | ||||
* in any case). | * in any case). | ||||
*/ | */ | ||||
static bool DisconnectTip(const Config &config, CValidationState &state, | static bool DisconnectTip(const Config &config, CValidationState &state, | ||||
DisconnectedBlockTransactions *disconnectpool) { | DisconnectedBlockTransactions *disconnectpool) { | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | if ((IsReplayProtectionEnabled(config, pindexDelete) && | ||||
// beneficial so we don't try to reuse its content at the end of the | // beneficial so we don't try to reuse its content at the end of the | ||||
// reorg, which we know will fail. | // reorg, which we know will fail. | ||||
if (disconnectpool) { | if (disconnectpool) { | ||||
disconnectpool->clear(); | disconnectpool->clear(); | ||||
} | } | ||||
} | } | ||||
if (disconnectpool) { | if (disconnectpool) { | ||||
// Save transactions to re-add to mempool at end of reorg | disconnectpool->addForBlock(block.vtx); | ||||
for (const auto &tx : boost::adaptors::reverse(block.vtx)) { | |||||
disconnectpool->addTransaction(tx); | |||||
} | |||||
while (disconnectpool->DynamicMemoryUsage() > | |||||
MAX_DISCONNECTED_TX_POOL_SIZE) { | |||||
// Drop the earliest entry, and remove its children from the | |||||
// mempool. | |||||
auto it = disconnectpool->queuedTx.get<insertion_order>().begin(); | |||||
mempool.removeRecursive(**it, MemPoolRemovalReason::REORG); | |||||
disconnectpool->removeEntry(it); | |||||
} | |||||
} | } | ||||
// Update chainActive and related variables. | // Update chainActive and related variables. | ||||
UpdateTip(config, pindexDelete->pprev); | UpdateTip(config, pindexDelete->pprev); | ||||
// Let wallets know transactions went from 1-confirmed to | // Let wallets know transactions went from 1-confirmed to | ||||
// 0-confirmed or conflicted: | // 0-confirmed or conflicted: | ||||
GetMainSignals().BlockDisconnected(pblock); | GetMainSignals().BlockDisconnected(pblock); | ||||
return true; | return true; | ||||
▲ Show 20 Lines • Show All 275 Lines • ▼ Show 20 Lines | static bool ActivateBestChainStep(const Config &config, CValidationState &state, | ||||
// Disconnect active blocks which are no longer in the best chain. | // Disconnect active blocks which are no longer in the best chain. | ||||
bool fBlocksDisconnected = false; | bool fBlocksDisconnected = false; | ||||
DisconnectedBlockTransactions disconnectpool; | DisconnectedBlockTransactions disconnectpool; | ||||
while (chainActive.Tip() && chainActive.Tip() != pindexFork) { | while (chainActive.Tip() && chainActive.Tip() != pindexFork) { | ||||
if (!DisconnectTip(config, state, &disconnectpool)) { | if (!DisconnectTip(config, state, &disconnectpool)) { | ||||
// This is likely a fatal error, but keep the mempool consistent, | // This is likely a fatal error, but keep the mempool consistent, | ||||
// just in case. Only remove from the mempool in this case. | // just in case. Only remove from the mempool in this case. | ||||
UpdateMempoolForReorg(config, disconnectpool, false); | disconnectpool.updateMempoolForReorg(config, false); | ||||
return false; | return false; | ||||
} | } | ||||
fBlocksDisconnected = true; | fBlocksDisconnected = true; | ||||
} | } | ||||
// Build list of new blocks to connect. | // Build list of new blocks to connect. | ||||
std::vector<CBlockIndex *> vpindexToConnect; | std::vector<CBlockIndex *> vpindexToConnect; | ||||
Show All 31 Lines | while (fContinue && nHeight != pindexMostWork->nHeight) { | ||||
fInvalidFound = true; | fInvalidFound = true; | ||||
fContinue = false; | fContinue = false; | ||||
break; | break; | ||||
} | } | ||||
// A system error occurred (disk space, database error, ...). | // A system error occurred (disk space, database error, ...). | ||||
// Make the mempool consistent with the current tip, just in | // Make the mempool consistent with the current tip, just in | ||||
// case any observers try to use it before shutdown. | // case any observers try to use it before shutdown. | ||||
UpdateMempoolForReorg(config, disconnectpool, false); | disconnectpool.updateMempoolForReorg(config, false); | ||||
return false; | return false; | ||||
} else { | } else { | ||||
PruneBlockIndexCandidates(); | PruneBlockIndexCandidates(); | ||||
if (!pindexOldTip || | if (!pindexOldTip || | ||||
chainActive.Tip()->nChainWork > pindexOldTip->nChainWork) { | chainActive.Tip()->nChainWork > pindexOldTip->nChainWork) { | ||||
// We're in a better position than we were. Return | // We're in a better position than we were. Return | ||||
// temporarily to release the lock. | // temporarily to release the lock. | ||||
fContinue = false; | fContinue = false; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (fBlocksDisconnected) { | if (fBlocksDisconnected) { | ||||
// If any blocks were disconnected, disconnectpool may be non empty. Add | // If any blocks were disconnected, disconnectpool may be non empty. Add | ||||
// any disconnected transactions back to the mempool. | // any disconnected transactions back to the mempool. | ||||
UpdateMempoolForReorg(config, disconnectpool, true); | disconnectpool.updateMempoolForReorg(config, true); | ||||
} | } | ||||
mempool.check(pcoinsTip); | mempool.check(pcoinsTip); | ||||
// Callbacks/notifications for a new best chain. | // Callbacks/notifications for a new best chain. | ||||
if (fInvalidFound) { | if (fInvalidFound) { | ||||
CheckForkWarningConditionsOnNewFork(vpindexToConnect.back()); | CheckForkWarningConditionsOnNewFork(vpindexToConnect.back()); | ||||
} else { | } else { | ||||
CheckForkWarningConditions(); | CheckForkWarningConditions(); | ||||
▲ Show 20 Lines • Show All 167 Lines • ▼ Show 20 Lines | while (chainActive.Contains(pindex)) { | ||||
pindexWalk->nStatus = pindexWalk->nStatus.withFailedParent(); | pindexWalk->nStatus = pindexWalk->nStatus.withFailedParent(); | ||||
setDirtyBlockIndex.insert(pindexWalk); | setDirtyBlockIndex.insert(pindexWalk); | ||||
setBlockIndexCandidates.erase(pindexWalk); | setBlockIndexCandidates.erase(pindexWalk); | ||||
// ActivateBestChain considers blocks already in chainActive | // ActivateBestChain considers blocks already in chainActive | ||||
// unconditionally valid already, so force disconnect away from it. | // unconditionally valid already, so force disconnect away from it. | ||||
if (!DisconnectTip(config, state, &disconnectpool)) { | if (!DisconnectTip(config, state, &disconnectpool)) { | ||||
// It's probably hopeless to try to make the mempool consistent | // It's probably hopeless to try to make the mempool consistent | ||||
// here if DisconnectTip failed, but we can try. | // here if DisconnectTip failed, but we can try. | ||||
UpdateMempoolForReorg(config, disconnectpool, false); | disconnectpool.updateMempoolForReorg(config, false); | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
// DisconnectTip will add transactions to disconnectpool; try to add these | // DisconnectTip will add transactions to disconnectpool; try to add these | ||||
// back to the mempool. | // back to the mempool. | ||||
UpdateMempoolForReorg(config, disconnectpool, true); | disconnectpool.updateMempoolForReorg(config, true); | ||||
// The resulting new best tip may not be in setBlockIndexCandidates anymore, | // The resulting new best tip may not be in setBlockIndexCandidates anymore, | ||||
// so add it again. | // so add it again. | ||||
for (const std::pair<const uint256, CBlockIndex *> &it : mapBlockIndex) { | for (const std::pair<const uint256, CBlockIndex *> &it : mapBlockIndex) { | ||||
CBlockIndex *i = it.second; | CBlockIndex *i = it.second; | ||||
if (i->IsValid(BlockValidity::TRANSACTIONS) && i->nChainTx && | if (i->IsValid(BlockValidity::TRANSACTIONS) && i->nChainTx && | ||||
!setBlockIndexCandidates.value_comp()(i, chainActive.Tip())) { | !setBlockIndexCandidates.value_comp()(i, chainActive.Tip())) { | ||||
setBlockIndexCandidates.insert(i); | setBlockIndexCandidates.insert(i); | ||||
▲ Show 20 Lines • Show All 2,407 Lines • Show Last 20 Lines |
This keeps getting linted for everyone touching this file. I say leave it in at this point.