Page MenuHomePhabricator

D15130.id44227.diff
No OneTemporary

D15130.id44227.diff

diff --git a/src/avalanche/test/processor_tests.cpp b/src/avalanche/test/processor_tests.cpp
--- a/src/avalanche/test/processor_tests.cpp
+++ b/src/avalanche/test/processor_tests.cpp
@@ -2212,7 +2212,8 @@
LOCK(cs_main);
BOOST_CHECK(chainstate.AcceptBlock(config, block, state,
/*fRequested=*/true, /*dbp=*/nullptr,
- /*fNewBlock=*/nullptr));
+ /*fNewBlock=*/nullptr,
+ /*min_pow_checked=*/true));
blockindex = chainman->m_blockman.LookupBlockIndex(blockhash);
BOOST_CHECK(blockindex);
diff --git a/src/bitcoin-chainstate.cpp b/src/bitcoin-chainstate.cpp
--- a/src/bitcoin-chainstate.cpp
+++ b/src/bitcoin-chainstate.cpp
@@ -206,6 +206,7 @@
RegisterSharedValidationInterface(sc);
bool accepted = chainman.ProcessNewBlock(config, blockptr,
/*force_processing=*/true,
+ /*min_pow_checked=*/true,
/*new_block=*/&new_block);
UnregisterSharedValidationInterface(sc);
if (!new_block && accepted) {
@@ -222,6 +223,11 @@
std::cerr << "Initial value. Block has not yet been rejected"
<< std::endl;
break;
+ case BlockValidationResult::BLOCK_HEADER_LOW_WORK:
+ std::cerr
+ << "the block header may be on a too-little-work chain"
+ << std::endl;
+ break;
case BlockValidationResult::BLOCK_CONSENSUS:
std::cerr << "Invalid by consensus rules (excluding any below "
"reasons)"
diff --git a/src/consensus/validation.h b/src/consensus/validation.h
--- a/src/consensus/validation.h
+++ b/src/consensus/validation.h
@@ -70,6 +70,8 @@
BLOCK_TIME_FUTURE,
//! the block failed to meet one of our checkpoints
BLOCK_CHECKPOINT,
+ //! the block header may be on a too-little-work chain
+ BLOCK_HEADER_LOW_WORK
};
/**
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -1248,7 +1248,7 @@
/** Process a new block. Perform any post-processing housekeeping */
void ProcessBlock(const Config &config, CNode &node,
const std::shared_ptr<const CBlock> &block,
- bool force_processing);
+ bool force_processing, bool min_pow_checked);
/** Relay map. */
typedef std::map<TxId, CTransactionRef> MapRelay;
@@ -2361,6 +2361,10 @@
switch (state.GetResult()) {
case BlockValidationResult::BLOCK_RESULT_UNSET:
break;
+ case BlockValidationResult::BLOCK_HEADER_LOW_WORK:
+ // We didn't try to process the block because the header chain may
+ // have too little work.
+ break;
// The node is providing invalid data:
case BlockValidationResult::BLOCK_CONSENSUS:
case BlockValidationResult::BLOCK_MUTATED:
@@ -3787,8 +3791,8 @@
// Now process all the headers.
BlockValidationState state;
- if (!m_chainman.ProcessNewBlockHeaders(config, headers, state,
- &pindexLast)) {
+ if (!m_chainman.ProcessNewBlockHeaders(
+ config, headers, /*min_pow_checked=*/true, state, &pindexLast)) {
if (state.IsInvalid()) {
MaybePunishNodeForBlock(pfrom.GetId(), state, via_compact_block,
"invalid header received");
@@ -4178,9 +4182,11 @@
void PeerManagerImpl::ProcessBlock(const Config &config, CNode &node,
const std::shared_ptr<const CBlock> &block,
- bool force_processing) {
+ bool force_processing,
+ bool min_pow_checked) {
bool new_block{false};
- m_chainman.ProcessNewBlock(config, block, force_processing, &new_block);
+ m_chainman.ProcessNewBlock(config, block, force_processing, min_pow_checked,
+ &new_block);
if (new_block) {
node.m_last_block_time = GetTime<std::chrono::seconds>();
} else {
@@ -5295,8 +5301,10 @@
{
LOCK(cs_main);
- if (!m_chainman.m_blockman.LookupBlockIndex(
- cmpctblock.header.hashPrevBlock)) {
+ const CBlockIndex *prev_block =
+ m_chainman.m_blockman.LookupBlockIndex(
+ cmpctblock.header.hashPrevBlock);
+ if (!prev_block) {
// Doesn't connect (or is genesis), instead of DoSing in
// AcceptBlockHeader, request deeper headers
if (!m_chainman.ActiveChainstate().IsInitialBlockDownload()) {
@@ -5305,6 +5313,16 @@
}
return;
}
+ if (prev_block->nChainWork +
+ CalculateHeadersWork({cmpctblock.header}) <
+ GetAntiDoSWorkThreshold()) {
+ // If we get a low-work header in a compact block, we can ignore
+ // it.
+ LogPrint(BCLog::NET,
+ "Ignoring low-work compact block from peer %d\n",
+ pfrom.GetId());
+ return;
+ }
if (!m_chainman.m_blockman.LookupBlockIndex(
cmpctblock.header.GetHash())) {
@@ -5315,7 +5333,8 @@
const CBlockIndex *pindex = nullptr;
BlockValidationState state;
if (!m_chainman.ProcessNewBlockHeaders(config, {cmpctblock.header},
- state, &pindex)) {
+ /*min_pow_checked=*/true, state,
+ &pindex)) {
if (state.IsInvalid()) {
MaybePunishNodeForBlock(pfrom.GetId(), state,
/*via_compact_block*/ true,
@@ -5524,7 +5543,8 @@
// we have a chain with at least nMinimumChainWork), and we ignore
// compact blocks with less work than our tip, it is safe to treat
// reconstructed compact blocks as having been requested.
- ProcessBlock(config, pfrom, pblock, /*force_processing=*/true);
+ ProcessBlock(config, pfrom, pblock, /*force_processing=*/true,
+ /*min_pow_checked=*/true);
// hold cs_main for CBlockIndex::IsValid()
LOCK(cs_main);
if (pindex->IsValid(BlockValidity::TRANSACTIONS)) {
@@ -5625,7 +5645,8 @@
// disk-space attacks), but this should be safe due to the
// protections in the compact block handler -- see related comment
// in compact block optimistic reconstruction handling.
- ProcessBlock(config, pfrom, pblock, /*force_processing=*/true);
+ ProcessBlock(config, pfrom, pblock, /*force_processing=*/true,
+ /*min_pow_checked=*/true);
}
return;
}
@@ -5688,6 +5709,7 @@
pfrom.HasPermission(NetPermissionFlags::NoBan) &&
!m_chainman.ActiveChainstate().IsInitialBlockDownload();
const BlockHash hash = pblock->GetHash();
+ bool min_pow_checked = false;
{
LOCK(cs_main);
// Always process the block if we requested it, since we may
@@ -5698,8 +5720,18 @@
// which peers send us compact blocks, so the race between here and
// cs_main in ProcessNewBlock is fine.
mapBlockSource.emplace(hash, std::make_pair(pfrom.GetId(), true));
+
+ // Check work on this block against our anti-dos thresholds.
+ const CBlockIndex *prev_block =
+ m_chainman.m_blockman.LookupBlockIndex(pblock->hashPrevBlock);
+ if (prev_block &&
+ prev_block->nChainWork +
+ CalculateHeadersWork({pblock->GetBlockHeader()}) >=
+ GetAntiDoSWorkThreshold()) {
+ min_pow_checked = true;
+ }
}
- ProcessBlock(config, pfrom, pblock, forceProcessing);
+ ProcessBlock(config, pfrom, pblock, forceProcessing, min_pow_checked);
return;
}
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -157,7 +157,9 @@
std::shared_ptr<const CBlock> shared_pblock =
std::make_shared<const CBlock>(block);
- if (!chainman.ProcessNewBlock(config, shared_pblock, true, nullptr)) {
+ if (!chainman.ProcessNewBlock(config, shared_pblock,
+ /*force_processing=*/true,
+ /*min_pow_checked=*/true, nullptr)) {
throw JSONRPCError(RPC_INTERNAL_ERROR,
"ProcessNewBlock, block not accepted");
}
@@ -1193,10 +1195,10 @@
auto sc =
std::make_shared<submitblock_StateCatcher>(block.GetHash());
RegisterSharedValidationInterface(sc);
- bool accepted =
- chainman.ProcessNewBlock(config, blockptr,
- /* fForceProcessing */ true,
- /* fNewBlock */ &new_block);
+ bool accepted = chainman.ProcessNewBlock(config, blockptr,
+ /*force_processing=*/true,
+ /*min_pow_checked=*/true,
+ /*new_block=*/&new_block);
UnregisterSharedValidationInterface(sc);
if (!new_block && accepted) {
return "duplicate";
@@ -1246,7 +1248,8 @@
}
BlockValidationState state;
- chainman.ProcessNewBlockHeaders(config, {h}, state);
+ chainman.ProcessNewBlockHeaders(config, {h},
+ /*min_pow_checked=*/true, state);
if (state.IsValid()) {
return NullUniValue;
}
diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp
--- a/src/test/blockfilter_index_tests.cpp
+++ b/src/test/blockfilter_index_tests.cpp
@@ -108,7 +108,7 @@
BlockValidationState state;
if (!Assert(m_node.chainman)
- ->ProcessNewBlockHeaders(GetConfig(), {header}, state,
+ ->ProcessNewBlockHeaders(GetConfig(), {header}, true, state,
&pindex)) {
return false;
}
@@ -197,8 +197,9 @@
uint256 chainA_last_header = last_header;
for (size_t i = 0; i < 2; i++) {
const auto &block = chainA[i];
- BOOST_REQUIRE(Assert(m_node.chainman)
- ->ProcessNewBlock(GetConfig(), block, true, nullptr));
+ BOOST_REQUIRE(
+ Assert(m_node.chainman)
+ ->ProcessNewBlock(GetConfig(), block, true, true, nullptr));
}
for (size_t i = 0; i < 2; i++) {
const auto &block = chainA[i];
@@ -217,8 +218,9 @@
uint256 chainB_last_header = last_header;
for (size_t i = 0; i < 3; i++) {
const auto &block = chainB[i];
- BOOST_REQUIRE(Assert(m_node.chainman)
- ->ProcessNewBlock(GetConfig(), block, true, nullptr));
+ BOOST_REQUIRE(
+ Assert(m_node.chainman)
+ ->ProcessNewBlock(GetConfig(), block, true, true, nullptr));
}
for (size_t i = 0; i < 3; i++) {
const auto &block = chainB[i];
@@ -251,8 +253,9 @@
// Reorg back to chain A.
for (size_t i = 2; i < 4; i++) {
const auto &block = chainA[i];
- BOOST_REQUIRE(Assert(m_node.chainman)
- ->ProcessNewBlock(GetConfig(), block, true, nullptr));
+ BOOST_REQUIRE(
+ Assert(m_node.chainman)
+ ->ProcessNewBlock(GetConfig(), block, true, true, nullptr));
}
// Check that chain A and B blocks can be retrieved.
diff --git a/src/test/checkpoints_tests.cpp b/src/test/checkpoints_tests.cpp
--- a/src/test/checkpoints_tests.cpp
+++ b/src/test/checkpoints_tests.cpp
@@ -91,9 +91,9 @@
{
BlockValidationState state;
- BOOST_CHECK(
- Assert(m_node.chainman)
- ->ProcessNewBlockHeaders(config, {headerG}, state, &pindex));
+ BOOST_CHECK(Assert(m_node.chainman)
+ ->ProcessNewBlockHeaders(config, {headerG}, true, state,
+ &pindex));
pindex = nullptr;
}
@@ -157,9 +157,9 @@
// Headers A and AA should be accepted
{
BlockValidationState state;
- BOOST_CHECK(
- Assert(m_node.chainman)
- ->ProcessNewBlockHeaders(config, {headerA}, state, &pindex));
+ BOOST_CHECK(Assert(m_node.chainman)
+ ->ProcessNewBlockHeaders(config, {headerA}, true, state,
+ &pindex));
BOOST_CHECK(state.IsValid());
BOOST_CHECK(pindex != nullptr);
pindex = nullptr;
@@ -167,9 +167,9 @@
{
BlockValidationState state;
- BOOST_CHECK(
- Assert(m_node.chainman)
- ->ProcessNewBlockHeaders(config, {headerAA}, state, &pindex));
+ BOOST_CHECK(Assert(m_node.chainman)
+ ->ProcessNewBlockHeaders(config, {headerAA}, true,
+ state, &pindex));
BOOST_CHECK(state.IsValid());
BOOST_CHECK(pindex != nullptr);
pindex = nullptr;
@@ -178,9 +178,9 @@
// Header B should be rejected
{
BlockValidationState state;
- BOOST_CHECK(
- !Assert(m_node.chainman)
- ->ProcessNewBlockHeaders(config, {headerB}, state, &pindex));
+ BOOST_CHECK(!Assert(m_node.chainman)
+ ->ProcessNewBlockHeaders(config, {headerB}, true,
+ state, &pindex));
BOOST_CHECK(state.IsInvalid());
BOOST_CHECK(state.GetRejectReason() == "bad-fork-prior-to-checkpoint");
BOOST_CHECK(pindex == nullptr);
@@ -196,9 +196,9 @@
// Header AB should be rejected
{
BlockValidationState state;
- BOOST_CHECK(
- !Assert(m_node.chainman)
- ->ProcessNewBlockHeaders(config, {headerAB}, state, &pindex));
+ BOOST_CHECK(!Assert(m_node.chainman)
+ ->ProcessNewBlockHeaders(config, {headerAB}, true,
+ state, &pindex));
BOOST_CHECK(state.IsInvalid());
BOOST_CHECK(state.GetRejectReason() == "checkpoint mismatch");
BOOST_CHECK(pindex == nullptr);
diff --git a/src/test/coinstatsindex_tests.cpp b/src/test/coinstatsindex_tests.cpp
--- a/src/test/coinstatsindex_tests.cpp
+++ b/src/test/coinstatsindex_tests.cpp
@@ -109,7 +109,7 @@
BOOST_CHECK(CheckBlock(
block, state, config.GetChainParams().GetConsensus(), options));
BOOST_CHECK(chainstate.AcceptBlock(config, new_block, state, true,
- nullptr, nullptr));
+ nullptr, nullptr, true));
// Get the block index (not returned by AcceptBlock since D2127)
auto it{m_node.chainman->m_blockman.m_block_index.find(
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -756,7 +756,7 @@
std::make_shared<const CBlock>(*pblock);
BOOST_CHECK(
Assert(m_node.chainman)
- ->ProcessNewBlock(config, shared_pblock, true, nullptr));
+ ->ProcessNewBlock(config, shared_pblock, true, true, nullptr));
pblock->hashPrevBlock = pblock->GetHash();
}
diff --git a/src/test/util/mining.cpp b/src/test/util/mining.cpp
--- a/src/test/util/mining.cpp
+++ b/src/test/util/mining.cpp
@@ -40,8 +40,8 @@
assert(block->nNonce);
}
- bool processed{
- Assert(node.chainman)->ProcessNewBlock(config, block, true, nullptr)};
+ bool processed{Assert(node.chainman)
+ ->ProcessNewBlock(config, block, true, true, nullptr)};
assert(processed);
return CTxIn{block->vtx[0]->GetId(), 0};
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -366,7 +366,7 @@
std::shared_ptr<const CBlock> shared_pblock =
std::make_shared<const CBlock>(block);
Assert(m_node.chainman)
- ->ProcessNewBlock(GetConfig(), shared_pblock, true, nullptr);
+ ->ProcessNewBlock(GetConfig(), shared_pblock, true, true, nullptr);
return block;
}
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -189,14 +189,14 @@
// Process all the headers so we understand the toplogy of the chain
BOOST_CHECK(Assert(m_node.chainman)
- ->ProcessNewBlockHeaders(config, headers, state));
+ ->ProcessNewBlockHeaders(config, headers, true, state));
// Connect the genesis block and drain any outstanding events
BOOST_CHECK(Assert(m_node.chainman)
->ProcessNewBlock(
config,
std::make_shared<CBlock>(chainParams.GenesisBlock()),
- true, &ignored));
+ true, true, &ignored));
SyncWithValidationInterfaceQueue();
// subscribe to events (this subscriber will validate event ordering)
@@ -220,16 +220,16 @@
for (int j = 0; j < 1000; j++) {
auto block = blocks[insecure.randrange(blocks.size() - 1)];
Assert(m_node.chainman)
- ->ProcessNewBlock(config, block, true, &tlignored);
+ ->ProcessNewBlock(config, block, true, true, &tlignored);
}
// to make sure that eventually we process the full chain - do it
// here
for (auto block : blocks) {
if (block->vtx.size() == 1) {
- bool processed =
- Assert(m_node.chainman)
- ->ProcessNewBlock(config, block, true, &tlignored);
+ bool processed = Assert(m_node.chainman)
+ ->ProcessNewBlock(config, block, true,
+ true, &tlignored);
assert(processed);
}
}
@@ -261,7 +261,7 @@
bool newBlock;
BOOST_CHECK(chainman.ProcessNewBlock(
config, std::make_shared<CBlock>(chainParams.GenesisBlock()), true,
- &newBlock));
+ true, &newBlock));
// Generate an invalid block with a valid header
const std::shared_ptr<const CBlock> pblock =
@@ -271,7 +271,7 @@
const CBlockIndex *pindexBadBlock;
BlockValidationState state;
BOOST_CHECK(chainman.ProcessNewBlockHeaders(
- config, {pblock->GetBlockHeader()}, state, &pindexBadBlock));
+ config, {pblock->GetBlockHeader()}, true, state, &pindexBadBlock));
// In order to force the invalid block to be finalized, we set the chain
// tip manually. This does not happen under normal conditions. Rewind
@@ -285,7 +285,8 @@
activeChainstate.m_chain.SetTip(pindex->pprev);
// Process the block. It should be found invalid and finalization reverted.
- bool processed = chainman.ProcessNewBlock(config, pblock, true, &newBlock);
+ bool processed =
+ chainman.ProcessNewBlock(config, pblock, true, true, &newBlock);
assert(processed);
BOOST_CHECK(newBlock);
{
@@ -321,8 +322,9 @@
bool ignored;
auto ProcessBlock = [&](std::shared_ptr<const CBlock> block) -> bool {
return Assert(m_node.chainman)
- ->ProcessNewBlock(config, block, /* fForceProcessing */ true,
- /* fNewBlock */ &ignored);
+ ->ProcessNewBlock(config, block, /*force_processing=*/true,
+ /*min_pow_checked=*/true,
+ /*new_block=*/&ignored);
};
// Process all mined blocks
diff --git a/src/test/validation_chainstate_tests.cpp b/src/test/validation_chainstate_tests.cpp
--- a/src/test/validation_chainstate_tests.cpp
+++ b/src/test/validation_chainstate_tests.cpp
@@ -146,7 +146,7 @@
*pblock, state, config.GetChainParams().GetConsensus(), options);
BOOST_CHECK(checked);
bool accepted = background_cs.AcceptBlock(config, pblock, state, true,
- nullptr, &newblock);
+ nullptr, &newblock, true);
BOOST_CHECK(accepted);
}
// UpdateTip is called here
diff --git a/src/validation.h b/src/validation.h
--- a/src/validation.h
+++ b/src/validation.h
@@ -922,8 +922,8 @@
bool AcceptBlock(const Config &config,
const std::shared_ptr<const CBlock> &pblock,
BlockValidationState &state, bool fRequested,
- const FlatFilePos *dbp, bool *fNewBlock)
- EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ const FlatFilePos *dbp, bool *fNewBlock,
+ bool min_pow_checked) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
// Block (dis)connection on a given view:
DisconnectResult DisconnectBlock(const CBlock &block,
@@ -1225,9 +1225,13 @@
* If a block header hasn't already been seen, call CheckBlockHeader on it,
* ensure that it doesn't descend from an invalid block, and then add it to
* m_block_index.
+ * Caller must set min_pow_checked=true in order to add a new header to the
+ * block index (permanent memory storage), indicating that the header is
+ * known to be part of a sufficiently high-work chain (anti-dos check).
*/
bool AcceptBlockHeader(const Config &config, const CBlockHeader &block,
- BlockValidationState &state, CBlockIndex **ppindex)
+ BlockValidationState &state, CBlockIndex **ppindex,
+ bool min_pow_checked)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
friend Chainstate;
@@ -1403,15 +1407,20 @@
* @param[in] config The global config.
* @param[in] block The block we want to process.
* @param[in] force_processing Process this block even if unrequested;
- * used for non-network block sources.
+ * used for non-network block sources.
+ * @param[in] min_pow_checked True if proof-of-work anti-DoS checks have
+ * been done by caller for headers chain
+ * (note: only affects headers acceptance; if
+ * block header is already present in block
+ * index then this parameter has no effect)
* @param[out] new_block A boolean which is set to indicate if the block
* was first received via this call.
* @returns If the block was processed, independently of block validity
*/
bool ProcessNewBlock(const Config &config,
const std::shared_ptr<const CBlock> &block,
- bool force_processing, bool *new_block)
- LOCKS_EXCLUDED(cs_main);
+ bool force_processing, bool min_pow_checked,
+ bool *new_block) LOCKS_EXCLUDED(cs_main);
/**
* Process incoming block headers.
@@ -1420,6 +1429,8 @@
*
* @param[in] config The config.
* @param[in] block The block headers themselves.
+ * @param[in] min_pow_checked True if proof-of-work anti-DoS checks have
+ * been done by caller for headers chain
* @param[out] state This may be set to an Error state if any error
* occurred processing them.
* @param[out] ppindex If set, the pointer will be set to point to the
@@ -1429,6 +1440,7 @@
*/
bool ProcessNewBlockHeaders(const Config &config,
const std::vector<CBlockHeader> &block,
+ bool min_pow_checked,
BlockValidationState &state,
const CBlockIndex **ppindex = nullptr)
LOCKS_EXCLUDED(cs_main);
diff --git a/src/validation.cpp b/src/validation.cpp
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -4120,7 +4120,8 @@
bool ChainstateManager::AcceptBlockHeader(const Config &config,
const CBlockHeader &block,
BlockValidationState &state,
- CBlockIndex **ppindex) {
+ CBlockIndex **ppindex,
+ bool min_pow_checked) {
AssertLockHeld(cs_main);
const CChainParams &chainparams = config.GetChainParams();
@@ -4228,7 +4229,14 @@
}
}
}
-
+ 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) {
@@ -4241,14 +4249,16 @@
// Exposed wrapper for AcceptBlockHeader
bool ChainstateManager::ProcessNewBlockHeaders(
const Config &config, const std::vector<CBlockHeader> &headers,
- BlockValidationState &state, const CBlockIndex **ppindex) {
+ bool min_pow_checked, BlockValidationState &state,
+ const CBlockIndex **ppindex) {
AssertLockNotHeld(cs_main);
{
LOCK(cs_main);
for (const CBlockHeader &header : headers) {
// Use a temp pindex instead of ppindex to avoid a const_cast
CBlockIndex *pindex = nullptr;
- bool accepted = AcceptBlockHeader(config, header, state, &pindex);
+ bool accepted = AcceptBlockHeader(config, header, state, &pindex,
+ min_pow_checked);
ActiveChainstate().CheckBlockIndex();
if (!accepted) {
@@ -4286,12 +4296,15 @@
* from our peers.
* @param[in] dbp If non-null, the disk position of the block.
* @param[in,out] fNewBlock True if block was first received via this call.
+ * @param[in] min_pow_checked True if proof-of-work anti-DoS checks have
+ * been done by caller for headers chain
* @return True if the block is accepted as a valid block and written to disk.
*/
bool Chainstate::AcceptBlock(const Config &config,
const std::shared_ptr<const CBlock> &pblock,
BlockValidationState &state, bool fRequested,
- const FlatFilePos *dbp, bool *fNewBlock) {
+ const FlatFilePos *dbp, bool *fNewBlock,
+ bool min_pow_checked) {
AssertLockHeld(cs_main);
const CBlock &block = *pblock;
@@ -4301,8 +4314,8 @@
CBlockIndex *pindex = nullptr;
- bool accepted_header{
- m_chainman.AcceptBlockHeader(config, block, state, &pindex)};
+ bool accepted_header{m_chainman.AcceptBlockHeader(
+ config, block, state, &pindex, min_pow_checked)};
CheckBlockIndex();
if (!accepted_header) {
@@ -4449,7 +4462,7 @@
bool ChainstateManager::ProcessNewBlock(
const Config &config, const std::shared_ptr<const CBlock> &block,
- bool force_processing, bool *new_block) {
+ bool force_processing, bool min_pow_checked, bool *new_block) {
AssertLockNotHeld(cs_main);
{
@@ -4478,8 +4491,9 @@
BlockValidationOptions(config));
if (ret) {
// Store to disk
- ret = ActiveChainstate().AcceptBlock(
- config, block, state, force_processing, nullptr, new_block);
+ ret = ActiveChainstate().AcceptBlock(config, block, state,
+ force_processing, nullptr,
+ new_block, min_pow_checked);
}
if (!ret) {
@@ -5195,7 +5209,7 @@
if (!pindex || !pindex->nStatus.hasData()) {
BlockValidationState state;
if (AcceptBlock(config, pblock, state, true, dbp,
- nullptr)) {
+ nullptr, true)) {
nLoaded++;
}
if (state.IsError()) {
@@ -5266,7 +5280,7 @@
LOCK(cs_main);
BlockValidationState dummy;
if (AcceptBlock(config, pblockrecursive, dummy,
- true, &it->second, nullptr)) {
+ true, &it->second, nullptr, true)) {
nLoaded++;
queue.push_back(pblockrecursive->GetHash());
}
diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py
--- a/test/functional/feature_block.py
+++ b/test/functional/feature_block.py
@@ -1166,7 +1166,7 @@
blocks2 = []
for i in range(89, LARGE_REORG_SIZE + 89):
blocks2.append(self.next_block(f"alt{i}"))
- self.send_blocks(blocks2, False, force_send=True)
+ self.send_blocks(blocks2, False, force_send=False)
# extend alt chain to trigger re-org
block = self.next_block(f"alt{chain1_tip + 1}")
diff --git a/test/functional/p2p_compactblocks.py b/test/functional/p2p_compactblocks.py
--- a/test/functional/p2p_compactblocks.py
+++ b/test/functional/p2p_compactblocks.py
@@ -657,6 +657,29 @@
)
assert "blocktxn" not in test_node.last_message
+ def test_low_work_compactblocks(self, test_node):
+ # A compactblock with insufficient work won't get its header included
+ node = self.nodes[0]
+ hashPrevBlock = int(node.getblockhash(node.getblockcount() - 150), 16)
+ block = self.build_block_on_tip(node)
+ block.hashPrevBlock = hashPrevBlock
+ block.solve()
+
+ comp_block = HeaderAndShortIDs()
+ comp_block.initialize_from_block(block)
+ with self.nodes[0].assert_debug_log(
+ ["Ignoring low-work compact block from peer 0"]
+ ):
+ test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
+
+ tips = node.getchaintips()
+ found = False
+ for x in tips:
+ if x["hash"] == block.hash:
+ found = True
+ break
+ assert not found
+
def test_compactblocks_not_at_tip(self, test_node):
node = self.nodes[0]
# Test that requesting old compactblocks doesn't work.
@@ -885,6 +908,9 @@
self.log.info("Testing compactblock requests/announcements not at chain tip...")
self.test_compactblocks_not_at_tip(self.test_node)
+ self.log.info("Testing handling of low-work compact blocks...")
+ self.test_low_work_compactblocks(self.test_node)
+
self.log.info("Testing handling of incorrect blocktxn responses...")
self.test_incorrect_blocktxn_response(self.test_node)
diff --git a/test/functional/p2p_unrequested_blocks.py b/test/functional/p2p_unrequested_blocks.py
--- a/test/functional/p2p_unrequested_blocks.py
+++ b/test/functional/p2p_unrequested_blocks.py
@@ -80,6 +80,13 @@
def setup_network(self):
self.setup_nodes()
+ def check_hash_in_chaintips(self, node, blockhash):
+ tips = node.getchaintips()
+ for x in tips:
+ if x["hash"] == blockhash:
+ return True
+ return False
+
def run_test(self):
test_node = self.nodes[0].add_p2p_connection(P2PInterface())
min_work_node = self.nodes[1].add_p2p_connection(P2PInterface())
@@ -97,10 +104,21 @@
blocks_h2[i].solve()
block_time += 1
test_node.send_and_ping(msg_block(blocks_h2[0]))
- min_work_node.send_and_ping(msg_block(blocks_h2[1]))
+
+ with self.nodes[1].assert_debug_log(
+ expected_msgs=[
+ f"AcceptBlockHeader: not adding new block header {blocks_h2[1].hash}, missing anti-dos proof-of-work validation"
+ ]
+ ):
+ min_work_node.send_and_ping(msg_block(blocks_h2[1]))
assert_equal(self.nodes[0].getblockcount(), 2)
assert_equal(self.nodes[1].getblockcount(), 1)
+
+ # Ensure that the header of the second block was also not accepted by node1
+ assert_equal(
+ self.check_hash_in_chaintips(self.nodes[1], blocks_h2[1].hash), False
+ )
self.log.info(
"First height 2 block accepted by node0; correctly rejected by node1"
)
diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py
--- a/test/functional/rpc_blockchain.py
+++ b/test/functional/rpc_blockchain.py
@@ -445,8 +445,10 @@
# (Previously this was broken based on setting
# `rpc/blockchain.cpp:latestblock` incorrectly.)
#
- b20hash = node.getblockhash(20)
- b20 = node.getblock(b20hash)
+ # choose something vaguely near our tip
+ fork_height = current_height - 100
+ fork_hash = node.getblockhash(fork_height)
+ fork_block = node.getblock(fork_hash)
def solve_and_send_block(prevhash, height, time):
b = create_block(prevhash, create_coinbase(height), time)
@@ -454,10 +456,12 @@
peer.send_and_ping(msg_block(b))
return b
- b21f = solve_and_send_block(int(b20hash, 16), 21, b20["time"] + 1)
- b22f = solve_and_send_block(b21f.sha256, 22, b21f.nTime + 1)
+ b1 = solve_and_send_block(
+ int(fork_hash, 16), fork_height + 1, fork_block["time"] + 1
+ )
+ b2 = solve_and_send_block(b1.sha256, fork_height + 1, b1.nTime + 1)
- node.invalidateblock(b22f.hash)
+ node.invalidateblock(b2.hash)
def assert_waitforheight(height, timeout=2):
assert_equal(

File Metadata

Mime Type
text/plain
Expires
Thu, Feb 6, 15:51 (17 h, 5 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5082642
Default Alt Text
D15130.id44227.diff (34 KB)

Event Timeline