Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F12944882
D15130.id44227.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
34 KB
Subscribers
None
D15130.id44227.diff
View Options
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
Details
Attached
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)
Attached To
D15130: Require callers of AcceptBlockHeader() to perform anti-dos checks
Event Timeline
Log In to Comment