Skip to content

Commit 6ab8470

Browse files
committed
Merge bitcoin/bitcoin#25960: p2p: Headers-sync followups
94af3e4 Fix typo from PR25717 (Suhas Daftuar) e5982ec Bypass headers anti-DoS checks for NoBan peers (Suhas Daftuar) 132ed7e Move headerssync logging to BCLog::NET (Suhas Daftuar) Pull request description: Remove BCLog::HEADERSSYNC and move all headerssync logging to BCLog::NET. Bypass headers anti-DoS checks for NoBan peers Also fix a typo that was introduced in PR25717. ACKs for top commit: Sjors: tACK 94af3e4 ajtowns: ACK 94af3e4 sipa: ACK 94af3e4 naumenkogs: ACK 94af3e4 w0xlt: ACK bitcoin/bitcoin@94af3e4 Tree-SHA512: 612d594eddace977359bcc8234b2093d273fd50662f4ac70cb90903d28fb831f6e1aecff51a4ef6c0bb0f6fb5d1aa7ff1eb8798fac5ac142783788f3080717dc
2 parents 8343420 + 94af3e4 commit 6ab8470

File tree

6 files changed

+46
-23
lines changed

6 files changed

+46
-23
lines changed

src/headerssync.cpp

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ HeadersSyncState::HeadersSyncState(NodeId id, const Consensus::Params& consensus
4242
// could try again, if necessary, to sync a longer chain).
4343
m_max_commitments = 6*(Ticks<std::chrono::seconds>(GetAdjustedTime() - NodeSeconds{std::chrono::seconds{chain_start->GetMedianTimePast()}}) + MAX_FUTURE_BLOCK_TIME) / HEADER_COMMITMENT_PERIOD;
4444

45-
LogPrint(BCLog::HEADERSSYNC, "Initial headers sync started with peer=%d: height=%i, max_commitments=%i, min_work=%s\n", m_id, m_current_height, m_max_commitments, m_minimum_required_work.ToString());
45+
LogPrint(BCLog::NET, "Initial headers sync started with peer=%d: height=%i, max_commitments=%i, min_work=%s\n", m_id, m_current_height, m_max_commitments, m_minimum_required_work.ToString());
4646
}
4747

4848
/** Free any memory in use, and mark this object as no longer usable. This is
@@ -92,7 +92,7 @@ HeadersSyncState::ProcessingResult HeadersSyncState::ProcessNextHeaders(const
9292
// If we're in PRESYNC and we get a non-full headers
9393
// message, then the peer's chain has ended and definitely doesn't
9494
// have enough work, so we can stop our sync.
95-
LogPrint(BCLog::HEADERSSYNC, "Initial headers sync aborted with peer=%d: incomplete headers message at height=%i (presync phase)\n", m_id, m_current_height);
95+
LogPrint(BCLog::NET, "Initial headers sync aborted with peer=%d: incomplete headers message at height=%i (presync phase)\n", m_id, m_current_height);
9696
}
9797
}
9898
} else if (m_download_state == State::REDOWNLOAD) {
@@ -118,7 +118,7 @@ HeadersSyncState::ProcessingResult HeadersSyncState::ProcessNextHeaders(const
118118
// If we hit our target blockhash, then all remaining headers will be
119119
// returned and we can clear any leftover internal state.
120120
if (m_redownloaded_headers.empty() && m_process_all_remaining_headers) {
121-
LogPrint(BCLog::HEADERSSYNC, "Initial headers sync complete with peer=%d: releasing all at height=%i (redownload phase)\n", m_id, m_redownload_buffer_last_height);
121+
LogPrint(BCLog::NET, "Initial headers sync complete with peer=%d: releasing all at height=%i (redownload phase)\n", m_id, m_redownload_buffer_last_height);
122122
} else if (full_headers_message) {
123123
// If the headers message is full, we need to request more.
124124
ret.request_more = true;
@@ -127,7 +127,7 @@ HeadersSyncState::ProcessingResult HeadersSyncState::ProcessNextHeaders(const
127127
// declining to serve us that full chain again. Give up.
128128
// Note that there's no more processing to be done with these
129129
// headers, so we can still return success.
130-
LogPrint(BCLog::HEADERSSYNC, "Initial headers sync aborted with peer=%d: incomplete headers message at height=%i (redownload phase)\n", m_id, m_redownload_buffer_last_height);
130+
LogPrint(BCLog::NET, "Initial headers sync aborted with peer=%d: incomplete headers message at height=%i (redownload phase)\n", m_id, m_redownload_buffer_last_height);
131131
}
132132
}
133133
}
@@ -150,7 +150,7 @@ bool HeadersSyncState::ValidateAndStoreHeadersCommitments(const std::vector<CBlo
150150
// This might be benign -- perhaps our peer reorged away from the chain
151151
// they were on. Give up on this sync for now (likely we will start a
152152
// new sync with a new starting point).
153-
LogPrint(BCLog::HEADERSSYNC, "Initial headers sync aborted with peer=%d: non-continuous headers at height=%i (presync phase)\n", m_id, m_current_height);
153+
LogPrint(BCLog::NET, "Initial headers sync aborted with peer=%d: non-continuous headers at height=%i (presync phase)\n", m_id, m_current_height);
154154
return false;
155155
}
156156

@@ -169,7 +169,7 @@ bool HeadersSyncState::ValidateAndStoreHeadersCommitments(const std::vector<CBlo
169169
m_redownload_buffer_last_hash = m_chain_start->GetBlockHash();
170170
m_redownload_chain_work = m_chain_start->nChainWork;
171171
m_download_state = State::REDOWNLOAD;
172-
LogPrint(BCLog::HEADERSSYNC, "Initial headers sync transition with peer=%d: reached sufficient work at height=%i, redownloading from height=%i\n", m_id, m_current_height, m_redownload_buffer_last_height);
172+
LogPrint(BCLog::NET, "Initial headers sync transition with peer=%d: reached sufficient work at height=%i, redownloading from height=%i\n", m_id, m_current_height, m_redownload_buffer_last_height);
173173
}
174174
return true;
175175
}
@@ -188,7 +188,7 @@ bool HeadersSyncState::ValidateAndProcessSingleHeader(const CBlockHeader& curren
188188
// adjustment maximum.
189189
if (!PermittedDifficultyTransition(m_consensus_params, next_height,
190190
m_last_header_received.nBits, current.nBits)) {
191-
LogPrint(BCLog::HEADERSSYNC, "Initial headers sync aborted with peer=%d: invalid difficulty transition at height=%i (presync phase)\n", m_id, next_height);
191+
LogPrint(BCLog::NET, "Initial headers sync aborted with peer=%d: invalid difficulty transition at height=%i (presync phase)\n", m_id, next_height);
192192
return false;
193193
}
194194

@@ -200,7 +200,7 @@ bool HeadersSyncState::ValidateAndProcessSingleHeader(const CBlockHeader& curren
200200
// It's possible the chain grew since we started the sync; so
201201
// potentially we could succeed in syncing the peer's chain if we
202202
// try again later.
203-
LogPrint(BCLog::HEADERSSYNC, "Initial headers sync aborted with peer=%d: exceeded max commitments at height=%i (presync phase)\n", m_id, next_height);
203+
LogPrint(BCLog::NET, "Initial headers sync aborted with peer=%d: exceeded max commitments at height=%i (presync phase)\n", m_id, next_height);
204204
return false;
205205
}
206206
}
@@ -222,7 +222,7 @@ bool HeadersSyncState::ValidateAndStoreRedownloadedHeader(const CBlockHeader& he
222222
// Ensure that we're working on a header that connects to the chain we're
223223
// downloading.
224224
if (header.hashPrevBlock != m_redownload_buffer_last_hash) {
225-
LogPrint(BCLog::HEADERSSYNC, "Initial headers sync aborted with peer=%d: non-continuous headers at height=%i (redownload phase)\n", m_id, next_height);
225+
LogPrint(BCLog::NET, "Initial headers sync aborted with peer=%d: non-continuous headers at height=%i (redownload phase)\n", m_id, next_height);
226226
return false;
227227
}
228228

@@ -236,7 +236,7 @@ bool HeadersSyncState::ValidateAndStoreRedownloadedHeader(const CBlockHeader& he
236236

237237
if (!PermittedDifficultyTransition(m_consensus_params, next_height,
238238
previous_nBits, header.nBits)) {
239-
LogPrint(BCLog::HEADERSSYNC, "Initial headers sync aborted with peer=%d: invalid difficulty transition at height=%i (redownload phase)\n", m_id, next_height);
239+
LogPrint(BCLog::NET, "Initial headers sync aborted with peer=%d: invalid difficulty transition at height=%i (redownload phase)\n", m_id, next_height);
240240
return false;
241241
}
242242

@@ -255,7 +255,7 @@ bool HeadersSyncState::ValidateAndStoreRedownloadedHeader(const CBlockHeader& he
255255
// target blockhash just because we ran out of commitments.
256256
if (!m_process_all_remaining_headers && next_height % HEADER_COMMITMENT_PERIOD == m_commit_offset) {
257257
if (m_header_commitments.size() == 0) {
258-
LogPrint(BCLog::HEADERSSYNC, "Initial headers sync aborted with peer=%d: commitment overrun at height=%i (redownload phase)\n", m_id, next_height);
258+
LogPrint(BCLog::NET, "Initial headers sync aborted with peer=%d: commitment overrun at height=%i (redownload phase)\n", m_id, next_height);
259259
// Somehow our peer managed to feed us a different chain and
260260
// we've run out of commitments.
261261
return false;
@@ -264,7 +264,7 @@ bool HeadersSyncState::ValidateAndStoreRedownloadedHeader(const CBlockHeader& he
264264
bool expected_commitment = m_header_commitments.front();
265265
m_header_commitments.pop_front();
266266
if (commitment != expected_commitment) {
267-
LogPrint(BCLog::HEADERSSYNC, "Initial headers sync aborted with peer=%d: commitment mismatch at height=%i (redownload phase)\n", m_id, next_height);
267+
LogPrint(BCLog::NET, "Initial headers sync aborted with peer=%d: commitment mismatch at height=%i (redownload phase)\n", m_id, next_height);
268268
return false;
269269
}
270270
}

src/logging.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,6 @@ const CLogCategoryDesc LogCategories[] =
165165
#endif
166166
{BCLog::UTIL, "util"},
167167
{BCLog::BLOCKSTORE, "blockstorage"},
168-
{BCLog::HEADERSSYNC, "headerssync"},
169168
{BCLog::ALL, "1"},
170169
{BCLog::ALL, "all"},
171170
};
@@ -264,8 +263,6 @@ std::string LogCategoryToStr(BCLog::LogFlags category)
264263
return "util";
265264
case BCLog::LogFlags::BLOCKSTORE:
266265
return "blockstorage";
267-
case BCLog::LogFlags::HEADERSSYNC:
268-
return "headerssync";
269266
case BCLog::LogFlags::ALL:
270267
return "all";
271268
}

src/logging.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ namespace BCLog {
6565
#endif
6666
UTIL = (1 << 25),
6767
BLOCKSTORE = (1 << 26),
68-
HEADERSSYNC = (1 << 27),
6968
ALL = ~(uint32_t)0,
7069
};
7170
enum class Level {

src/net_processing.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2820,6 +2820,13 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, Peer& peer,
28202820
}
28212821
}
28222822

2823+
// If our peer has NetPermissionFlags::NoBan privileges, then bypass our
2824+
// anti-DoS logic (this saves bandwidth when we connect to a trusted peer
2825+
// on startup).
2826+
if (pfrom.HasPermission(NetPermissionFlags::NoBan)) {
2827+
already_validated_work = true;
2828+
}
2829+
28232830
// At this point, the headers connect to something in our block index.
28242831
// Do anti-DoS checks to determine if we should process or store for later
28252832
// processing.

src/validation.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3718,7 +3718,7 @@ void ChainstateManager::ReportHeadersPresync(const arith_uint256& work, int64_t
37183718
{
37193719
LOCK(cs_main);
37203720
// Don't report headers presync progress if we already have a post-minchainwork header chain.
3721-
// This means we lose reporting for potentially legimate, but unlikely, deep reorgs, but
3721+
// This means we lose reporting for potentially legitimate, but unlikely, deep reorgs, but
37223722
// prevent attackers that spam low-work headers from filling our logs.
37233723
if (m_best_header->nChainWork >= UintToArith256(GetConsensus().nMinimumChainWork)) return;
37243724
// Rate limit headers presync updates to 4 per second, as these are not subject to DoS

test/functional/p2p_headers_sync_with_minchainwork.py

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@
2828
class RejectLowDifficultyHeadersTest(BitcoinTestFramework):
2929
def set_test_params(self):
3030
self.setup_clean_chain = True
31-
self.num_nodes = 3
31+
self.num_nodes = 4
3232
# Node0 has no required chainwork; node1 requires 15 blocks on top of the genesis block; node2 requires 2047
33-
self.extra_args = [["-minimumchainwork=0x0", "-checkblockindex=0"], ["-minimumchainwork=0x1f", "-checkblockindex=0"], ["-minimumchainwork=0x1000", "-checkblockindex=0"]]
33+
self.extra_args = [["-minimumchainwork=0x0", "-checkblockindex=0"], ["-minimumchainwork=0x1f", "-checkblockindex=0"], ["-minimumchainwork=0x1000", "-checkblockindex=0"], ["-minimumchainwork=0x1000", "-checkblockindex=0", "[email protected]"]]
3434

3535
def setup_network(self):
3636
self.setup_nodes()
@@ -40,17 +40,34 @@ def setup_network(self):
4040
def disconnect_all(self):
4141
self.disconnect_nodes(0, 1)
4242
self.disconnect_nodes(0, 2)
43+
self.disconnect_nodes(0, 3)
4344

4445
def reconnect_all(self):
4546
self.connect_nodes(0, 1)
4647
self.connect_nodes(0, 2)
48+
self.connect_nodes(0, 3)
4749

4850
def test_chains_sync_when_long_enough(self):
4951
self.log.info("Generate blocks on the node with no required chainwork, and verify nodes 1 and 2 have no new headers in their headers tree")
5052
with self.nodes[1].assert_debug_log(expected_msgs=["[net] Ignoring low-work chain (height=14)"]), self.nodes[2].assert_debug_log(expected_msgs=["[net] Ignoring low-work chain (height=14)"]):
5153
self.generate(self.nodes[0], NODE1_BLOCKS_REQUIRED-1, sync_fun=self.no_op)
5254

53-
for node in self.nodes[1:]:
55+
# Node3 should always allow headers due to noban permissions
56+
self.log.info("Check that node3 will sync headers (due to noban permissions)")
57+
58+
def check_node3_chaintips(num_tips, tip_hash, height):
59+
node3_chaintips = self.nodes[3].getchaintips()
60+
assert(len(node3_chaintips) == num_tips)
61+
assert {
62+
'height': height,
63+
'hash': tip_hash,
64+
'branchlen': height,
65+
'status': 'headers-only',
66+
} in node3_chaintips
67+
68+
check_node3_chaintips(2, self.nodes[0].getbestblockhash(), NODE1_BLOCKS_REQUIRED-1)
69+
70+
for node in self.nodes[1:3]:
5471
chaintips = node.getchaintips()
5572
assert(len(chaintips) == 1)
5673
assert {
@@ -63,7 +80,7 @@ def test_chains_sync_when_long_enough(self):
6380
self.log.info("Generate more blocks to satisfy node1's minchainwork requirement, and verify node2 still has no new headers in headers tree")
6481
with self.nodes[2].assert_debug_log(expected_msgs=["[net] Ignoring low-work chain (height=15)"]):
6582
self.generate(self.nodes[0], NODE1_BLOCKS_REQUIRED - self.nodes[0].getblockcount(), sync_fun=self.no_op)
66-
self.sync_blocks(self.nodes[0:2])
83+
self.sync_blocks(self.nodes[0:2]) # node3 will sync headers (noban permissions) but not blocks (due to minchainwork)
6784

6885
assert {
6986
'height': 0,
@@ -74,10 +91,13 @@ def test_chains_sync_when_long_enough(self):
7491

7592
assert(len(self.nodes[2].getchaintips()) == 1)
7693

77-
self.log.info("Generate long chain for node0/node1")
94+
self.log.info("Check that node3 accepted these headers as well")
95+
check_node3_chaintips(2, self.nodes[0].getbestblockhash(), NODE1_BLOCKS_REQUIRED)
96+
97+
self.log.info("Generate long chain for node0/node1/node3")
7898
self.generate(self.nodes[0], NODE2_BLOCKS_REQUIRED-self.nodes[0].getblockcount(), sync_fun=self.no_op)
7999

80-
self.log.info("Verify that node2 will sync the chain when it gets long enough")
100+
self.log.info("Verify that node2 and node3 will sync the chain when it gets long enough")
81101
self.sync_blocks()
82102

83103
def test_peerinfo_includes_headers_presync_height(self):

0 commit comments

Comments
 (0)