Skip to content

Commit 5f65aff

Browse files
author
MacroFake
committed
Merge bitcoin#24178: p2p: Respond to getheaders if we have sufficient chainwork
a35f963 Add test for getheaders behavior (Suhas Daftuar) ef6dbe6 Respond to getheaders if we have sufficient chainwork (Suhas Daftuar) Pull request description: Previously, we would check to see if we were in IBD and ignore getheaders requests accordingly. However, the IBD criteria -- an optimization mostly targeted at behavior when we have peers serving us many blocks we need to download -- is difficult to reason about in edge-case scenarios, such as if the network were to go a long time without any blocks found and nodes are getting restarted during that time. To make things simpler to reason about, just use `nMinimumChainWork` as our anti-DoS threshold for responding to a getheaders request; as long as our chain has that much work, it should be fine to respond to a peer asking for our headers (and this should allow such a peer to request blocks from us if needed). ACKs for top commit: klementtan: crACK a35f963 naumenkogs: ACK a35f963 MarcoFalke: review ACK a35f963 🗯 Tree-SHA512: 131e3872e7fe80382ea9c1ec202d6c2dc59c006355c69000aa3f4ce6bccd02a6c689c8cb8f3542b5d9bc48bfa61edcbd1a78535c0b79018971d02bed2655d284
2 parents bd6c5e4 + a35f963 commit 5f65aff

File tree

2 files changed

+37
-2
lines changed

2 files changed

+37
-2
lines changed

src/net_processing.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3300,9 +3300,23 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
33003300
return;
33013301
}
33023302

3303+
if (fImporting || fReindex) {
3304+
LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d while importing/reindexing\n", pfrom.GetId());
3305+
return;
3306+
}
3307+
33033308
LOCK(cs_main);
3304-
if (m_chainman.ActiveChainstate().IsInitialBlockDownload() && !pfrom.HasPermission(NetPermissionFlags::Download)) {
3305-
LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom.GetId());
3309+
3310+
// Note that if we were to be on a chain that forks from the checkpointed
3311+
// chain, then serving those headers to a peer that has seen the
3312+
// checkpointed chain would cause that peer to disconnect us. Requiring
3313+
// that our chainwork exceed nMinimumChainWork is a protection against
3314+
// being fed a bogus chain when we started up for the first time and
3315+
// getting partitioned off the honest network for serving that chain to
3316+
// others.
3317+
if (m_chainman.ActiveTip() == nullptr ||
3318+
(m_chainman.ActiveTip()->nChainWork < nMinimumChainWork && !pfrom.HasPermission(NetPermissionFlags::Download))) {
3319+
LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because active chain has too little work\n", pfrom.GetId());
33063320
return;
33073321
}
33083322

test/functional/feature_minchainwork.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import time
1919

20+
from test_framework.p2p import P2PInterface, msg_getheaders
2021
from test_framework.test_framework import BitcoinTestFramework
2122
from test_framework.util import assert_equal
2223

@@ -41,6 +42,9 @@ def setup_network(self):
4142
for i in range(self.num_nodes-1):
4243
self.connect_nodes(i+1, i)
4344

45+
# Set clock of node2 2 days ahead, to keep it in IBD during this test.
46+
self.nodes[2].setmocktime(int(time.time()) + 48*60*60)
47+
4448
def run_test(self):
4549
# Start building a chain on node0. node2 shouldn't be able to sync until node1's
4650
# minchainwork is exceeded
@@ -71,6 +75,15 @@ def run_test(self):
7175
assert self.nodes[1].getbestblockhash() != self.nodes[0].getbestblockhash()
7276
assert_equal(self.nodes[2].getblockcount(), starting_blockcount)
7377

78+
self.log.info("Check that getheaders requests to node2 are ignored")
79+
peer = self.nodes[2].add_p2p_connection(P2PInterface())
80+
msg = msg_getheaders()
81+
msg.locator.vHave = [int(self.nodes[2].getbestblockhash(), 16)]
82+
msg.hashstop = 0
83+
peer.send_and_ping(msg)
84+
time.sleep(5)
85+
assert "headers" not in peer.last_message
86+
7487
self.log.info("Generating one more block")
7588
self.generate(self.nodes[0], 1)
7689

@@ -85,5 +98,13 @@ def run_test(self):
8598
self.sync_all()
8699
self.log.info(f"Blockcounts: {[n.getblockcount() for n in self.nodes]}")
87100

101+
self.log.info("Test that getheaders requests to node2 are not ignored")
102+
peer.send_and_ping(msg)
103+
assert "headers" in peer.last_message
104+
105+
# Verify that node2 is in fact still in IBD (otherwise this test may
106+
# not be exercising the logic we want!)
107+
assert_equal(self.nodes[2].getblockchaininfo()['initialblockdownload'], True)
108+
88109
if __name__ == '__main__':
89110
MinimumChainWorkTest().main()

0 commit comments

Comments
 (0)