Skip to content

Commit 00ce854

Browse files
committed
Merge bitcoin/bitcoin#24171: p2p: Sync chain more readily from inbound peers during IBD
48262a0 Add functional test for block sync from inbound peers (Suhas Daftuar) 0569b5c Sync chain more readily from inbound peers during IBD (Suhas Daftuar) Pull request description: When in IBD, if the honest chain is only known by inbound peers, then we must eventually sync from them in order to learn it. This change allows us to perform initial headers sync and fetch blocks from inbound peers, if we have no blocks in flight. The restriction on having no blocks in flight means that we will naturally throttle our block downloads to any such inbound peers that we may be downloading from, until we leave IBD. This is a tradeoff between preferring outbound peers for most of our block download, versus making sure we always eventually will get blocks we need that are only known by inbound peers even during IBD, as otherwise we may be stuck in IBD indefinitely (which could have cascading failure on the network, if a large fraction of the network managed to get stuck in IBD). Note that the test in the second commit fails on master, without the first commit. ACKs for top commit: ajtowns: ACK 48262a0 sipa: ACK 48262a0 Tree-SHA512: ffad3a05fa9a32a92226843c9128f52c275e8d51930fde7368badc340227f2ed680561c4c9f2937b4e3bd722474464849ec9b624f912f5e380ce98d71b55764d
2 parents 1f63b46 + 48262a0 commit 00ce854

File tree

3 files changed

+62
-3
lines changed

3 files changed

+62
-3
lines changed

src/net_processing.cpp

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4717,10 +4717,31 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
47174717
if (m_chainman.m_best_header == nullptr) {
47184718
m_chainman.m_best_header = m_chainman.ActiveChain().Tip();
47194719
}
4720-
bool fFetch = state.fPreferredDownload || (m_num_preferred_download_peers == 0 && !pto->fClient && !pto->IsAddrFetchConn()); // Download if this is a nice peer, or we have no nice peers and this one might do.
4720+
4721+
// Determine whether we might try initial headers sync or parallel
4722+
// block download from this peer -- this mostly affects behavior while
4723+
// in IBD (once out of IBD, we sync from all peers).
4724+
bool sync_blocks_and_headers_from_peer = false;
4725+
if (state.fPreferredDownload) {
4726+
sync_blocks_and_headers_from_peer = true;
4727+
} else if (!pto->fClient && !pto->IsAddrFetchConn()) {
4728+
// Typically this is an inbound peer. If we don't have any outbound
4729+
// peers, or if we aren't downloading any blocks from such peers,
4730+
// then allow block downloads from this peer, too.
4731+
// We prefer downloading blocks from outbound peers to avoid
4732+
// putting undue load on (say) some home user who is just making
4733+
// outbound connections to the network, but if our only source of
4734+
// the latest blocks is from an inbound peer, we have to be sure to
4735+
// eventually download it (and not just wait indefinitely for an
4736+
// outbound peer to have it).
4737+
if (m_num_preferred_download_peers == 0 || mapBlocksInFlight.empty()) {
4738+
sync_blocks_and_headers_from_peer = true;
4739+
}
4740+
}
4741+
47214742
if (!state.fSyncStarted && !pto->fClient && !fImporting && !fReindex) {
47224743
// Only actively request headers from a single peer, unless we're close to today.
4723-
if ((nSyncStarted == 0 && fFetch) || m_chainman.m_best_header->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) {
4744+
if ((nSyncStarted == 0 && sync_blocks_and_headers_from_peer) || m_chainman.m_best_header->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) {
47244745
state.fSyncStarted = true;
47254746
state.m_headers_sync_timeout = current_time + HEADERS_DOWNLOAD_TIMEOUT_BASE +
47264747
(
@@ -5093,7 +5114,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
50935114
// Message: getdata (blocks)
50945115
//
50955116
std::vector<CInv> vGetData;
5096-
if (!pto->fClient && ((fFetch && !pto->m_limited_node) || !m_chainman.ActiveChainstate().IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
5117+
if (!pto->fClient && ((sync_blocks_and_headers_from_peer && !pto->m_limited_node) || !m_chainman.ActiveChainstate().IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
50975118
std::vector<const CBlockIndex*> vToDownload;
50985119
NodeId staller = -1;
50995120
FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller);

test/functional/p2p_block_sync.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2022 The Bitcoin Core developers
3+
# Distributed under the MIT software license, see the accompanying
4+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
"""Test block download
6+
7+
Ensure that even in IBD, we'll eventually sync chain from inbound peers
8+
(whether we have only inbound peers or both inbound and outbound peers).
9+
"""
10+
11+
from test_framework.test_framework import BitcoinTestFramework
12+
13+
class BlockSyncTest(BitcoinTestFramework):
14+
15+
def set_test_params(self):
16+
self.setup_clean_chain = True
17+
self.num_nodes = 3
18+
19+
def setup_network(self):
20+
self.setup_nodes()
21+
# Construct a network:
22+
# node0 -> node1 -> node2
23+
# So node1 has both an inbound and outbound peer.
24+
# In our test, we will mine a block on node0, and ensure that it makes
25+
# to to both node1 and node2.
26+
self.connect_nodes(0, 1)
27+
self.connect_nodes(1, 2)
28+
29+
def run_test(self):
30+
self.log.info("Setup network: node0->node1->node2")
31+
self.log.info("Mining one block on node0 and verify all nodes sync")
32+
self.generate(self.nodes[0], 1)
33+
self.log.info("Success!")
34+
35+
36+
if __name__ == '__main__':
37+
BlockSyncTest().main()

test/functional/test_runner.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@
157157
'wallet_avoidreuse.py --descriptors',
158158
'mempool_reorg.py',
159159
'mempool_persist.py',
160+
'p2p_block_sync.py',
160161
'wallet_multiwallet.py --legacy-wallet',
161162
'wallet_multiwallet.py --descriptors',
162163
'wallet_multiwallet.py --usecli',

0 commit comments

Comments
 (0)