Skip to content

Commit f4ce573

Browse files
committed
merge bitcoin#22340: Use legacy relaying to download blocks in blocks-only mode
1 parent 73b8f84 commit f4ce573

File tree

3 files changed

+141
-1
lines changed

3 files changed

+141
-1
lines changed

src/net_processing.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1052,6 +1052,12 @@ bool PeerManagerImpl::MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, co
10521052
void PeerManagerImpl::MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid)
10531053
{
10541054
AssertLockHeld(cs_main);
1055+
1056+
// Never request high-bandwidth mode from peers if we're blocks-only. Our
1057+
// mempool will not contain the transactions necessary to reconstruct the
1058+
// compact block.
1059+
if (m_ignore_incoming_txs) return;
1060+
10551061
CNodeState* nodestate = State(nodeid);
10561062
if (!nodestate || !nodestate->fSupportsDesiredCmpctVersion) {
10571063
// Never ask from peers who can't provide desired version.
@@ -2897,7 +2903,11 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
28972903
pindexLast->GetBlockHash().ToString(), pindexLast->nHeight);
28982904
}
28992905
if (vGetData.size() > 0) {
2900-
if (nodestate->fSupportsDesiredCmpctVersion && vGetData.size() == 1 && mapBlocksInFlight.size() == 1 && pindexLast->pprev->IsValid(BLOCK_VALID_CHAIN)) {
2906+
if (!m_ignore_incoming_txs &&
2907+
nodestate->fSupportsDesiredCmpctVersion &&
2908+
vGetData.size() == 1 &&
2909+
mapBlocksInFlight.size() == 1 &&
2910+
pindexLast->pprev->IsValid(BLOCK_VALID_CHAIN)) {
29012911
// In any case, we want to download using a compact block, not a regular one
29022912
vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash);
29032913
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2021-2021 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 that a node in blocksonly mode does not request compact blocks."""
6+
7+
from test_framework.messages import (
8+
MSG_BLOCK,
9+
MSG_CMPCT_BLOCK,
10+
CBlock,
11+
CBlockHeader,
12+
CInv,
13+
from_hex,
14+
msg_block,
15+
msg_getdata,
16+
msg_headers,
17+
msg_sendcmpct,
18+
)
19+
from test_framework.p2p import P2PInterface
20+
from test_framework.test_framework import BitcoinTestFramework
21+
from test_framework.util import assert_equal
22+
23+
24+
class P2PCompactBlocksBlocksOnly(BitcoinTestFramework):
25+
def set_test_params(self):
26+
self.extra_args = [["-blocksonly"], [], [], []]
27+
self.num_nodes = 4
28+
29+
def setup_network(self):
30+
self.setup_nodes()
31+
# Start network with everyone disconnected
32+
self.sync_all()
33+
34+
def build_block_on_tip(self):
35+
blockhash = self.nodes[2].generate(1)[0]
36+
block_hex = self.nodes[2].getblock(blockhash=blockhash, verbosity=0)
37+
block = from_hex(CBlock(), block_hex)
38+
block.rehash()
39+
return block
40+
41+
def run_test(self):
42+
# Nodes will only request hb compact blocks mode when they're out of IBD
43+
for node in self.nodes:
44+
assert not node.getblockchaininfo()['initialblockdownload']
45+
46+
p2p_conn_blocksonly = self.nodes[0].add_p2p_connection(P2PInterface())
47+
p2p_conn_high_bw = self.nodes[1].add_p2p_connection(P2PInterface())
48+
p2p_conn_low_bw = self.nodes[3].add_p2p_connection(P2PInterface())
49+
for conn in [p2p_conn_blocksonly, p2p_conn_high_bw, p2p_conn_low_bw]:
50+
assert_equal(conn.message_count['sendcmpct'], 1)
51+
conn.send_and_ping(msg_sendcmpct(announce=False, version=1))
52+
53+
# Nodes:
54+
# 0 -> blocksonly
55+
# 1 -> high bandwidth
56+
# 2 -> miner
57+
# 3 -> low bandwidth
58+
#
59+
# Topology:
60+
# p2p_conn_blocksonly ---> node0
61+
# p2p_conn_high_bw ---> node1
62+
# p2p_conn_low_bw ---> node3
63+
# node2 (no connections)
64+
#
65+
# node2 produces blocks that are passed to the rest of the nodes
66+
# through the respective p2p connections.
67+
68+
self.log.info("Test that -blocksonly nodes do not select peers for BIP152 high bandwidth mode")
69+
70+
block0 = self.build_block_on_tip()
71+
72+
# A -blocksonly node should not request BIP152 high bandwidth mode upon
73+
# receiving a new valid block at the tip.
74+
p2p_conn_blocksonly.send_and_ping(msg_block(block0))
75+
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block0.sha256)
76+
assert_equal(p2p_conn_blocksonly.message_count['sendcmpct'], 1)
77+
assert_equal(p2p_conn_blocksonly.last_message['sendcmpct'].announce, False)
78+
79+
# A normal node participating in transaction relay should request BIP152
80+
# high bandwidth mode upon receiving a new valid block at the tip.
81+
p2p_conn_high_bw.send_and_ping(msg_block(block0))
82+
assert_equal(int(self.nodes[1].getbestblockhash(), 16), block0.sha256)
83+
p2p_conn_high_bw.wait_until(lambda: p2p_conn_high_bw.message_count['sendcmpct'] == 2)
84+
assert_equal(p2p_conn_high_bw.last_message['sendcmpct'].announce, True)
85+
86+
# Don't send a block from the p2p_conn_low_bw so the low bandwidth node
87+
# doesn't select it for BIP152 high bandwidth relay.
88+
self.nodes[3].submitblock(block0.serialize().hex())
89+
90+
self.log.info("Test that -blocksonly nodes send getdata(BLOCK) instead"
91+
" of getdata(CMPCT) in BIP152 low bandwidth mode")
92+
93+
block1 = self.build_block_on_tip()
94+
95+
p2p_conn_blocksonly.send_message(msg_headers(headers=[CBlockHeader(block1)]))
96+
p2p_conn_blocksonly.sync_send_with_ping()
97+
assert_equal(p2p_conn_blocksonly.last_message['getdata'].inv, [CInv(MSG_BLOCK, block1.sha256)])
98+
99+
p2p_conn_high_bw.send_message(msg_headers(headers=[CBlockHeader(block1)]))
100+
p2p_conn_high_bw.sync_send_with_ping()
101+
assert_equal(p2p_conn_high_bw.last_message['getdata'].inv, [CInv(MSG_CMPCT_BLOCK, block1.sha256)])
102+
103+
self.log.info("Test that getdata(CMPCT) is still sent on BIP152 low bandwidth connections"
104+
" when no -blocksonly nodes are involved")
105+
106+
p2p_conn_low_bw.send_and_ping(msg_headers(headers=[CBlockHeader(block1)]))
107+
p2p_conn_low_bw.sync_with_ping()
108+
assert_equal(p2p_conn_low_bw.last_message['getdata'].inv, [CInv(MSG_CMPCT_BLOCK, block1.sha256)])
109+
110+
self.log.info("Test that -blocksonly nodes still serve compact blocks")
111+
112+
def test_for_cmpctblock(block):
113+
if 'cmpctblock' not in p2p_conn_blocksonly.last_message:
114+
return False
115+
return p2p_conn_blocksonly.last_message['cmpctblock'].header_and_shortids.header.rehash() == block.sha256
116+
117+
p2p_conn_blocksonly.send_message(msg_getdata([CInv(MSG_CMPCT_BLOCK, block0.sha256)]))
118+
p2p_conn_blocksonly.wait_until(lambda: test_for_cmpctblock(block0))
119+
120+
# Request BIP152 high bandwidth mode from the -blocksonly node.
121+
p2p_conn_blocksonly.send_and_ping(msg_sendcmpct(announce=True, version=1))
122+
123+
block2 = self.build_block_on_tip()
124+
self.nodes[0].submitblock(block1.serialize().hex())
125+
self.nodes[0].submitblock(block2.serialize().hex())
126+
p2p_conn_blocksonly.wait_until(lambda: test_for_cmpctblock(block2))
127+
128+
if __name__ == '__main__':
129+
P2PCompactBlocksBlocksOnly().main()

test/functional/test_runner.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@
273273
'wallet_listdescriptors.py --descriptors',
274274
'p2p_leak.py',
275275
'p2p_compactblocks.py',
276+
'p2p_compactblocks_blocksonly.py',
276277
'p2p_connect_to_devnet.py',
277278
'feature_sporks.py',
278279
'rpc_getblockstats.py',

0 commit comments

Comments
 (0)