aboutsummaryrefslogtreecommitdiff
path: root/src/validation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/validation.cpp')
-rw-r--r--src/validation.cpp105
1 files changed, 76 insertions, 29 deletions
diff --git a/src/validation.cpp b/src/validation.cpp
index d276cea2f7..a2a339a5a1 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -449,7 +449,7 @@ public:
/** Whether we allow transactions to replace mempool transactions by BIP125 rules. If false,
* any transaction spending the same inputs as a transaction in the mempool is considered
* a conflict. */
- const bool m_allow_bip125_replacement;
+ const bool m_allow_replacement;
/** When true, the mempool will not be trimmed when individual transactions are submitted in
* Finalize(). Instead, limits should be enforced at the end to ensure the package is not
* partially submitted.
@@ -469,7 +469,7 @@ public:
/* m_bypass_limits */ bypass_limits,
/* m_coins_to_uncache */ coins_to_uncache,
/* m_test_accept */ test_accept,
- /* m_allow_bip125_replacement */ true,
+ /* m_allow_replacement */ true,
/* m_package_submission */ false,
/* m_package_feerates */ false,
};
@@ -483,7 +483,7 @@ public:
/* m_bypass_limits */ false,
/* m_coins_to_uncache */ coins_to_uncache,
/* m_test_accept */ true,
- /* m_allow_bip125_replacement */ false,
+ /* m_allow_replacement */ false,
/* m_package_submission */ false, // not submitting to mempool
/* m_package_feerates */ false,
};
@@ -497,7 +497,7 @@ public:
/* m_bypass_limits */ false,
/* m_coins_to_uncache */ coins_to_uncache,
/* m_test_accept */ false,
- /* m_allow_bip125_replacement */ false,
+ /* m_allow_replacement */ false,
/* m_package_submission */ true,
/* m_package_feerates */ true,
};
@@ -510,7 +510,7 @@ public:
/* m_bypass_limits */ false,
/* m_coins_to_uncache */ package_args.m_coins_to_uncache,
/* m_test_accept */ package_args.m_test_accept,
- /* m_allow_bip125_replacement */ true,
+ /* m_allow_replacement */ true,
/* m_package_submission */ false,
/* m_package_feerates */ false, // only 1 transaction
};
@@ -524,7 +524,7 @@ public:
bool bypass_limits,
std::vector<COutPoint>& coins_to_uncache,
bool test_accept,
- bool allow_bip125_replacement,
+ bool allow_replacement,
bool package_submission,
bool package_feerates)
: m_chainparams{chainparams},
@@ -532,7 +532,7 @@ public:
m_bypass_limits{bypass_limits},
m_coins_to_uncache{coins_to_uncache},
m_test_accept{test_accept},
- m_allow_bip125_replacement{allow_bip125_replacement},
+ m_allow_replacement{allow_replacement},
m_package_submission{package_submission},
m_package_feerates{package_feerates}
{
@@ -731,7 +731,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
{
const CTransaction* ptxConflicting = m_pool.GetConflictTx(txin.prevout);
if (ptxConflicting) {
- if (!args.m_allow_bip125_replacement) {
+ if (!args.m_allow_replacement) {
// Transaction conflicts with a mempool tx, but we're not allowing replacements.
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "bip125-replacement-disallowed");
}
@@ -861,8 +861,8 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// Specifically, the subset of RBF transactions which we allow despite chain limits are those which
// conflict directly with exactly one other transaction (but may evict children of said transaction),
// and which are not adding any new mempool dependencies. Note that the "no new mempool dependencies"
- // check is accomplished later, so we don't bother doing anything about it here, but if BIP 125 is
- // amended, we may need to move that check to here instead of removing it wholesale.
+ // check is accomplished later, so we don't bother doing anything about it here, but if our
+ // policy changes, we may need to move that check to here instead of removing it wholesale.
//
// Such transactions are clearly not merging any existing packages, so we are only concerned with
// ensuring that (a) no package is growing past the package size (not count) limits and (b) we are
@@ -929,7 +929,7 @@ bool MemPoolAccept::ReplacementChecks(Workspace& ws)
TxValidationState& state = ws.m_state;
CFeeRate newFeeRate(ws.m_modified_fees, ws.m_vsize);
- // The replacement transaction must have a higher feerate than its direct conflicts.
+ // Enforce Rule #6. The replacement transaction must have a higher feerate than its direct conflicts.
// - The motivation for this check is to ensure that the replacement transaction is preferable for
// block-inclusion, compared to what would be removed from the mempool.
// - This logic predates ancestor feerate-based transaction selection, which is why it doesn't
@@ -942,18 +942,18 @@ bool MemPoolAccept::ReplacementChecks(Workspace& ws)
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "insufficient fee", *err_string);
}
- // Calculate all conflicting entries and enforce BIP125 Rule #5.
+ // Calculate all conflicting entries and enforce Rule #5.
if (const auto err_string{GetEntriesForConflicts(tx, m_pool, ws.m_iters_conflicting, ws.m_all_conflicting)}) {
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY,
"too many potential replacements", *err_string);
}
- // Enforce BIP125 Rule #2.
+ // Enforce Rule #2.
if (const auto err_string{HasNoNewUnconfirmed(tx, m_pool, ws.m_iters_conflicting)}) {
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY,
"replacement-adds-unconfirmed", *err_string);
}
// Check if it's economically rational to mine this transaction rather than the ones it
- // replaces and pays for its own relay fees. Enforce BIP125 Rules #3 and #4.
+ // replaces and pays for its own relay fees. Enforce Rules #3 and #4.
for (CTxMemPool::txiter it : ws.m_all_conflicting) {
ws.m_conflicting_fees += it->GetModifiedFee();
ws.m_conflicting_size += it->GetTxSize();
@@ -1224,7 +1224,7 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
// package to spend. Since we already checked conflicts in the package and we don't allow
// replacements, we don't need to track the coins spent. Note that this logic will need to be
// updated if package replace-by-fee is allowed in the future.
- assert(!args.m_allow_bip125_replacement);
+ assert(!args.m_allow_replacement);
m_viewmempool.PackageAddTransaction(ws.m_ptx);
}
@@ -2944,7 +2944,7 @@ static bool NotifyHeaderTip(CChainState& chainstate) LOCKS_EXCLUDED(cs_main) {
}
// Send block tip changed notifications without cs_main
if (fNotify) {
- uiInterface.NotifyHeaderTip(GetSynchronizationState(fInitialBlockDownload), pindexHeader);
+ uiInterface.NotifyHeaderTip(GetSynchronizationState(fInitialBlockDownload), pindexHeader->nHeight, pindexHeader->nTime, false);
}
return fNotify;
}
@@ -3432,6 +3432,22 @@ std::vector<unsigned char> ChainstateManager::GenerateCoinbaseCommitment(CBlock&
return commitment;
}
+bool HasValidProofOfWork(const std::vector<CBlockHeader>& headers, const Consensus::Params& consensusParams)
+{
+ return std::all_of(headers.cbegin(), headers.cend(),
+ [&](const auto& header) { return CheckProofOfWork(header.GetHash(), header.nBits, consensusParams);});
+}
+
+arith_uint256 CalculateHeadersWork(const std::vector<CBlockHeader>& headers)
+{
+ arith_uint256 total_work{0};
+ for (const CBlockHeader& header : headers) {
+ CBlockIndex dummy(header);
+ total_work += GetBlockProof(dummy);
+ }
+ return total_work;
+}
+
/** Context-dependent validity checks.
* By "context", we mean only the previous block headers, but not the UTXO
* set; UTXO-related validity checks are done in ConnectBlock().
@@ -3441,7 +3457,7 @@ std::vector<unsigned char> ChainstateManager::GenerateCoinbaseCommitment(CBlock&
* in ConnectBlock().
* Note that -reindex-chainstate skips the validation that happens here!
*/
-static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidationState& state, BlockManager& blockman, const ChainstateManager& chainman, const CBlockIndex* pindexPrev, int64_t nAdjustedTime) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
+static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidationState& state, BlockManager& blockman, const ChainstateManager& chainman, const CBlockIndex* pindexPrev, NodeClock::time_point now) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
AssertLockHeld(::cs_main);
assert(pindexPrev != nullptr);
@@ -3469,8 +3485,9 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio
return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "time-too-old", "block's timestamp is too early");
// Check timestamp
- if (block.GetBlockTime() > nAdjustedTime + MAX_FUTURE_BLOCK_TIME)
+ if (block.Time() > now + std::chrono::seconds{MAX_FUTURE_BLOCK_TIME}) {
return state.Invalid(BlockValidationResult::BLOCK_TIME_FUTURE, "time-too-new", "block timestamp too far in the future");
+ }
// Reject blocks with outdated version
if ((block.nVersion < 2 && DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_HEIGHTINCB)) ||
@@ -3571,9 +3588,10 @@ static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& stat
return true;
}
-bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValidationState& state, CBlockIndex** ppindex)
+bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValidationState& state, CBlockIndex** ppindex, bool min_pow_checked)
{
AssertLockHeld(cs_main);
+
// Check for duplicate
uint256 hash = block.GetHash();
BlockMap::iterator miSelf{m_blockman.m_block_index.find(hash)};
@@ -3607,7 +3625,7 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
LogPrint(BCLog::VALIDATION, "%s: %s prev block invalid\n", __func__, hash.ToString());
return state.Invalid(BlockValidationResult::BLOCK_INVALID_PREV, "bad-prevblk");
}
- if (!ContextualCheckBlockHeader(block, state, m_blockman, *this, pindexPrev, m_adjusted_time_callback())) {
+ if (!ContextualCheckBlockHeader(block, state, m_blockman, *this, pindexPrev, m_options.adjusted_time_callback())) {
LogPrint(BCLog::VALIDATION, "%s: Consensus::ContextualCheckBlockHeader: %s, %s\n", __func__, hash.ToString(), state.ToString());
return false;
}
@@ -3651,6 +3669,10 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
}
}
}
+ if (!min_pow_checked) {
+ LogPrint(BCLog::VALIDATION, "%s: not adding new block header %s, missing anti-dos proof-of-work validation\n", __func__, hash.ToString());
+ return state.Invalid(BlockValidationResult::BLOCK_HEADER_LOW_WORK, "too-little-chainwork");
+ }
CBlockIndex* pindex{m_blockman.AddToBlockIndex(block, m_best_header)};
if (ppindex)
@@ -3660,14 +3682,14 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
}
// Exposed wrapper for AcceptBlockHeader
-bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, BlockValidationState& state, const CBlockIndex** ppindex)
+bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, bool min_pow_checked, BlockValidationState& state, const CBlockIndex** ppindex)
{
AssertLockNotHeld(cs_main);
{
LOCK(cs_main);
for (const CBlockHeader& header : headers) {
CBlockIndex *pindex = nullptr; // Use a temp pindex instead of ppindex to avoid a const_cast
- bool accepted{AcceptBlockHeader(header, state, &pindex)};
+ bool accepted{AcceptBlockHeader(header, state, &pindex, min_pow_checked)};
ActiveChainstate().CheckBlockIndex();
if (!accepted) {
@@ -3689,8 +3711,33 @@ bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>&
return true;
}
+void ChainstateManager::ReportHeadersPresync(const arith_uint256& work, int64_t height, int64_t timestamp)
+{
+ AssertLockNotHeld(cs_main);
+ const auto& chainstate = ActiveChainstate();
+ {
+ LOCK(cs_main);
+ // Don't report headers presync progress if we already have a post-minchainwork header chain.
+ // This means we lose reporting for potentially legimate, but unlikely, deep reorgs, but
+ // prevent attackers that spam low-work headers from filling our logs.
+ if (m_best_header->nChainWork >= UintToArith256(GetConsensus().nMinimumChainWork)) return;
+ // Rate limit headers presync updates to 4 per second, as these are not subject to DoS
+ // protection.
+ auto now = std::chrono::steady_clock::now();
+ if (now < m_last_presync_update + std::chrono::milliseconds{250}) return;
+ m_last_presync_update = now;
+ }
+ bool initial_download = chainstate.IsInitialBlockDownload();
+ uiInterface.NotifyHeaderTip(GetSynchronizationState(initial_download), height, timestamp, /*presync=*/true);
+ if (initial_download) {
+ const int64_t blocks_left{(GetTime() - timestamp) / GetConsensus().nPowTargetSpacing};
+ const double progress{100.0 * height / (height + blocks_left)};
+ LogPrintf("Pre-synchronizing blockheaders, height: %d (~%.2f%%)\n", height, progress);
+ }
+}
+
/** Store block on disk. If dbp is non-nullptr, the file is known to already reside on disk */
-bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockValidationState& state, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock)
+bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockValidationState& state, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock, bool min_pow_checked)
{
const CBlock& block = *pblock;
@@ -3700,7 +3747,7 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
CBlockIndex *pindexDummy = nullptr;
CBlockIndex *&pindex = ppindex ? *ppindex : pindexDummy;
- bool accepted_header{m_chainman.AcceptBlockHeader(block, state, &pindex)};
+ bool accepted_header{m_chainman.AcceptBlockHeader(block, state, &pindex, min_pow_checked)};
CheckBlockIndex();
if (!accepted_header)
@@ -3773,7 +3820,7 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
return true;
}
-bool ChainstateManager::ProcessNewBlock(const std::shared_ptr<const CBlock>& block, bool force_processing, bool* new_block)
+bool ChainstateManager::ProcessNewBlock(const std::shared_ptr<const CBlock>& block, bool force_processing, bool min_pow_checked, bool* new_block)
{
AssertLockNotHeld(cs_main);
@@ -3794,7 +3841,7 @@ bool ChainstateManager::ProcessNewBlock(const std::shared_ptr<const CBlock>& blo
bool ret = CheckBlock(*block, state, GetConsensus());
if (ret) {
// Store to disk
- ret = ActiveChainstate().AcceptBlock(block, state, &pindex, force_processing, nullptr, new_block);
+ ret = ActiveChainstate().AcceptBlock(block, state, &pindex, force_processing, nullptr, new_block, min_pow_checked);
}
if (!ret) {
GetMainSignals().BlockChecked(*block, state);
@@ -3831,7 +3878,7 @@ bool TestBlockValidity(BlockValidationState& state,
CChainState& chainstate,
const CBlock& block,
CBlockIndex* pindexPrev,
- const std::function<int64_t()>& adjusted_time_callback,
+ const std::function<NodeClock::time_point()>& adjusted_time_callback,
bool fCheckPOW,
bool fCheckMerkleRoot)
{
@@ -4331,7 +4378,7 @@ void CChainState::LoadExternalBlockFile(
const CBlockIndex* pindex = m_blockman.LookupBlockIndex(hash);
if (!pindex || (pindex->nStatus & BLOCK_HAVE_DATA) == 0) {
BlockValidationState state;
- if (AcceptBlock(pblock, state, nullptr, true, dbp, nullptr)) {
+ if (AcceptBlock(pblock, state, nullptr, true, dbp, nullptr, true)) {
nLoaded++;
}
if (state.IsError()) {
@@ -4369,7 +4416,7 @@ void CChainState::LoadExternalBlockFile(
head.ToString());
LOCK(cs_main);
BlockValidationState dummy;
- if (AcceptBlock(pblockrecursive, dummy, nullptr, true, &it->second, nullptr)) {
+ if (AcceptBlock(pblockrecursive, dummy, nullptr, true, &it->second, nullptr, true)) {
nLoaded++;
queue.push_back(pblockrecursive->GetHash());
}