Skip to content

Commit 0b57d54

Browse files
knstachow101
andcommitted
Merge bitcoin#26184: test: p2p: check that headers message with invalid proof-of-work disconnects peer
7726712 test: p2p: check that headers message with invalid proof-of-work disconnects peer (Sebastian Falbesoner) Pull request description: One of the earliest anti-DoS checks done after receiving and deserializing a `headers` message from a peer is verifying whether the proof-of-work is valid (called in method `PeerManagerImpl::ProcessHeadersMessage`): https://github.com/bitcoin/bitcoin/blob/f227e153e80c8c50c30d76e1ac638d7206c7ff61/src/net_processing.cpp#L2752-L2762 The called method `PeerManagerImpl::CheckHeadersPoW` calls `Misbehaving` with a score of 100, i.e. leading to an immediate disconnect of the peer: https://github.com/bitcoin/bitcoin/blob/f227e153e80c8c50c30d76e1ac638d7206c7ff61/src/net_processing.cpp#L2368-L2372 This PR adds a simple test for both the misbehaving log and the resulting disconnect. For creating a block header with invalid proof-of-work, we first create one that is accepted by the node (the difficulty field `nBits` is copied from the genesis block) and based on that the nonce is modified until we have block header hash prefix that is too high to fulfill even the minimum difficulty. ACKs for top commit: Sjors: ACK 7726712 achow101: ACK 7726712 brunoerg: crACK 7726712 furszy: Code review ACK 7726712 with a non-blocking speedup. Tree-SHA512: 680aa7939158d1dc672b90aa6554ba2b3a92584b6d3bcb0227776035858429feb8bc66eed18b47de0fe56df7d9b3ddaee231aaeaa360136603b9ad4b19e6ac11 Co-authored-by: Andrew Chow <github@achow101.com>
1 parent fbf1796 commit 0b57d54

2 files changed

Lines changed: 41 additions & 2 deletions

File tree

test/functional/p2p_headers_sync_with_minchainwork.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/usr/bin/env python3
2-
# Copyright (c) 2019-2021 The Bitcoin Core developers
2+
# Copyright (c) 2019-present The Bitcoin Core developers
33
# Distributed under the MIT software license, see the accompanying
44
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
55
"""Test that we reject low difficulty headers to prevent our block tree from filling up with useless bloat"""
@@ -23,6 +23,8 @@
2323

2424
from test_framework.util import assert_equal
2525

26+
import time
27+
2628
NODE1_BLOCKS_REQUIRED = 15
2729
NODE2_BLOCKS_REQUIRED = 2047
2830

@@ -50,6 +52,10 @@ def reconnect_all(self):
5052
self.connect_nodes(0, 2)
5153
self.connect_nodes(0, 3)
5254

55+
def mocktime_all(self, time):
56+
for n in self.nodes:
57+
n.setmocktime(time)
58+
5359
def test_chains_sync_when_long_enough(self):
5460
# The Dash test framework pins mocktime to TIME_GENESIS_BLOCK when
5561
# setup_clean_chain is set, which makes the genesis tip look "recent"
@@ -170,7 +176,9 @@ def test_large_reorgs_can_succeed(self):
170176

171177
self.reconnect_all()
172178

179+
self.mocktime_all(int(time.time())) # Temporarily hold time to avoid internal timeouts
173180
self.sync_blocks(timeout=300) # Ensure tips eventually agree
181+
self.mocktime_all(0)
174182

175183

176184
def run_test(self):

test/functional/p2p_invalid_messages.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@
1515
MAX_HEADERS_UNCOMPRESSED_RESULT,
1616
MAX_INV_SIZE,
1717
MAX_PROTOCOL_MESSAGE_LENGTH,
18+
MSG_TX,
1819
msg_getdata,
1920
msg_headers,
2021
msg_headers2,
2122
msg_inv,
22-
MSG_TX,
2323
msg_version,
2424
from_hex,
2525
)
@@ -63,6 +63,7 @@ def run_test(self):
6363
self.test_oversized_inv_msg()
6464
self.test_oversized_getdata_msg()
6565
self.test_oversized_headers_msg()
66+
self.test_invalid_pow_headers_msg()
6667
self.test_noncontinuous_headers_msg()
6768
self.test_resource_exhaustion()
6869

@@ -189,6 +190,36 @@ def test_oversized_headers2_msg(self):
189190
size = MAX_HEADERS_COMPRESSED_RESULT + 1
190191
self.test_oversized_msg(msg_headers2([CBlockHeader()] * size), size)
191192

193+
def test_invalid_pow_headers_msg(self):
194+
self.log.info("Test headers message with invalid proof-of-work is logged as misbehaving and disconnects peer")
195+
blockheader_tip_hash = self.nodes[0].getbestblockhash()
196+
blockheader_tip = from_hex(CBlockHeader(), self.nodes[0].getblockheader(blockheader_tip_hash, False))
197+
198+
# send valid headers message first
199+
assert_equal(self.nodes[0].getblockchaininfo()['headers'], 0)
200+
blockheader = CBlockHeader()
201+
blockheader.hashPrevBlock = int(blockheader_tip_hash, 16)
202+
blockheader.nTime = blockheader_tip.nTime + 150
203+
blockheader.nBits = blockheader_tip.nBits
204+
blockheader.rehash()
205+
while not blockheader.hash.startswith('0'):
206+
blockheader.nNonce += 1
207+
blockheader.rehash()
208+
peer = self.nodes[0].add_p2p_connection(P2PInterface())
209+
peer.send_and_ping(msg_headers([blockheader]))
210+
assert_equal(self.nodes[0].getblockchaininfo()['headers'], 1)
211+
chaintips = self.nodes[0].getchaintips()
212+
assert_equal(chaintips[0]['status'], 'headers-only')
213+
assert_equal(chaintips[0]['hash'], blockheader.hash)
214+
215+
# invalidate PoW
216+
while not blockheader.hash.startswith('f'):
217+
blockheader.nNonce += 1
218+
blockheader.rehash()
219+
with self.nodes[0].assert_debug_log(['Misbehaving', 'header with invalid proof of work']):
220+
peer.send_message(msg_headers([blockheader]))
221+
peer.wait_for_disconnect()
222+
192223
def test_noncontinuous_headers_msg(self):
193224
self.log.info("Test headers message with non-continuous headers sequence is logged as misbehaving")
194225
block_hashes = self.generate(self.nodes[0], 10)

0 commit comments

Comments
 (0)