Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
251self.log.info("Verify getheaders with null locator and invalid hashstop does not return headers.")252block = create_block(int(tip["hash"], 16), height=tip["height"] + 1, ntime=tip["mediantime"] + 1)253block.solve()254test_node.send_header_for_blocks([block])255test_node.clear_block_announcements()256test_node.send_get_headers(locator=[], hashstop=block.hash_int)257test_node.sync_with_ping()258assert_equal(test_node.block_announced, False)259inv_node.clear_block_announcements()260test_node.send_without_ping(msg_block(block))261inv_node.check_last_inv_announcement(inv=[block.hash_int])263def test_nonnull_locators(self, test_node, inv_node):264tip = int(self.nodes[0].getbestblockhash(), 16)266# PART 1267# 1. Mine a block; expect inv announcements each time268self.log.info("Part 1: headers don't start before sendheaders message...")269for i in range(4):270self.log.debug("Part 1.{}: starting...".format(i))271old_tip = tip272tip = self.mine_blocks(1)273inv_node.check_last_inv_announcement(inv=[tip])274test_node.check_last_inv_announcement(inv=[tip])275# Try a few different responses; none should affect next announcement276if i == 0:277# first request the block278test_node.send_get_data([tip])279test_node.wait_for_block(tip)280elif i == 1:281# next try requesting header and block282test_node.send_get_headers(locator=[old_tip], hashstop=tip)283test_node.send_get_data([tip])284test_node.wait_for_block(tip)285test_node.clear_block_announcements() # since we requested headers...286elif i == 2:287# this time announce own block via headers288inv_node.clear_block_announcements()289height = self.nodes[0].getblockcount()290last_time = self.nodes[0].getblock(self.nodes[0].getbestblockhash())['time']291block_time = last_time + 1292new_block = create_block(tip, height=height + 1, ntime=block_time)293new_block.solve()294test_node.send_header_for_blocks([new_block])295test_node.wait_for_getdata([new_block.hash_int])296test_node.send_and_ping(msg_block(new_block)) # make sure this block is processed297inv_node.wait_until(lambda: inv_node.block_announced)298inv_node.clear_block_announcements()299test_node.clear_block_announcements()301self.log.info("Part 1: success!")302self.log.info("Part 2: announce blocks with headers after sendheaders message...")303# PART 2304# 2. Send a sendheaders message and test that headers announcements305# commence and keep working.306test_node.send_without_ping(msg_sendheaders())307prev_tip = int(self.nodes[0].getbestblockhash(), 16)308test_node.send_get_headers(locator=[prev_tip], hashstop=0)309test_node.sync_with_ping()311# Now that we've synced headers, headers announcements should work312tip = self.mine_blocks(1)313expected_hash = tip314inv_node.check_last_inv_announcement(inv=[tip])315test_node.check_last_headers_announcement(headers=[tip])317height = self.nodes[0].getblockcount() + 1318block_time += 10 # Advance far enough ahead319for i in range(10):320self.log.debug("Part 2.{}: starting...".format(i))321# Mine i blocks, and alternate announcing either via322# inv (of tip) or via headers. After each, new blocks323# mined by the node should successfully be announced324# with block header, even though the blocks are never requested325for j in range(2):326self.log.debug("Part 2.{}.{}: starting...".format(i, j))327blocks = []328for _ in range(i + 1):329blocks.append(create_block(tip, height=height, ntime=block_time))330blocks[-1].solve()331tip = blocks[-1].hash_int332block_time += 1333height += 1334if j == 0:335# Announce via inv336test_node.send_block_inv(tip)337if i == 0:338test_node.wait_for_getheaders(block_hash=expected_hash)339else:340assert "getheaders" not in test_node.last_message341# Should have received a getheaders now342test_node.send_header_for_blocks(blocks)343# Test that duplicate inv's won't result in duplicate344# getdata requests, or duplicate headers announcements345[inv_node.send_block_inv(x.hash_int) for x in blocks]346test_node.wait_for_getdata([x.hash_int for x in blocks])347inv_node.sync_with_ping()348else:349# Announce via headers350test_node.send_header_for_blocks(blocks)351test_node.wait_for_getdata([x.hash_int for x in blocks])352# Test that duplicate headers won't result in duplicate353# getdata requests (the check is further down)354inv_node.send_header_for_blocks(blocks)355inv_node.sync_with_ping()356[test_node.send_without_ping(msg_block(x)) for x in blocks]357test_node.sync_with_ping()358inv_node.sync_with_ping()359# This block should not be announced to the inv node (since it also360# broadcast it)361assert "inv" not in inv_node.last_message362assert "headers" not in inv_node.last_message363tip = self.mine_blocks(1)364inv_node.check_last_inv_announcement(inv=[tip])365test_node.check_last_headers_announcement(headers=[tip])366height += 1367block_time += 1369self.log.info("Part 2: success!")371self.log.info("Part 3: headers announcements can stop after large reorg, and resume after headers/inv from peer...")373# PART 3. Headers announcements can stop after large reorg, and resume after374# getheaders or inv from peer.375for j in range(2):376self.log.debug("Part 3.{}: starting...".format(j))377# First try mining a reorg that can propagate with header announcement378new_block_hashes = self.mine_reorg(length=7)379tip = new_block_hashes[-1]380inv_node.check_last_inv_announcement(inv=[tip])381test_node.check_last_headers_announcement(headers=new_block_hashes)383block_time += 8385# Mine a too-large reorg, which should be announced with a single inv386new_block_hashes = self.mine_reorg(length=8)387tip = new_block_hashes[-1]388inv_node.check_last_inv_announcement(inv=[tip])389test_node.check_last_inv_announcement(inv=[tip])391block_time += 9393fork_point = self.nodes[0].getblock("%064x" % new_block_hashes[0])["previousblockhash"]394fork_point = int(fork_point, 16)396# Use getblocks/getdata397test_node.send_getblocks(locator=[fork_point])398test_node.check_last_inv_announcement(inv=new_block_hashes)399test_node.send_get_data(new_block_hashes)400test_node.wait_for_block(new_block_hashes[-1])402for i in range(3):403self.log.debug("Part 3.{}.{}: starting...".format(j, i))405# Mine another block, still should get only an inv406tip = self.mine_blocks(1)407inv_node.check_last_inv_announcement(inv=[tip])408test_node.check_last_inv_announcement(inv=[tip])409if i == 0:410# Just get the data -- shouldn't cause headers announcements to resume411test_node.send_get_data([tip])412test_node.wait_for_block(tip)413elif i == 1:414# Send a getheaders message that shouldn't trigger headers announcements415# to resume (best header sent will be too old)416test_node.send_get_headers(locator=[fork_point], hashstop=new_block_hashes[1])417test_node.send_get_data([tip])418test_node.wait_for_block(tip)419elif i == 2:420# This time, try sending either a getheaders to trigger resumption421# of headers announcements, or mine a new block and inv it, also422# triggering resumption of headers announcements.423test_node.send_get_data([tip])424test_node.wait_for_block(tip)425if j == 0:426test_node.send_get_headers(locator=[tip], hashstop=0)427test_node.sync_with_ping()428else:429test_node.send_block_inv(tip)430test_node.sync_with_ping()431# New blocks should now be announced with header432tip = self.mine_blocks(1)433inv_node.check_last_inv_announcement(inv=[tip])434test_node.check_last_headers_announcement(headers=[tip])436self.log.info("Part 3: success!")438self.log.info("Part 4: Testing direct fetch behavior...")439tip = self.mine_blocks(1)440height = self.nodes[0].getblockcount() + 1441last_time = self.nodes[0].getblock(self.nodes[0].getbestblockhash())['time']442block_time = last_time + 1444# Create 2 blocks. Send the blocks, then send the headers.445blocks = []446for _ in range(2):447blocks.append(create_block(tip, height=height, ntime=block_time))448blocks[-1].solve()449tip = blocks[-1].hash_int450block_time += 1451height += 1452inv_node.send_without_ping(msg_block(blocks[-1]))454inv_node.sync_with_ping() # Make sure blocks are processed455test_node.last_message.pop("getdata", None)456test_node.send_header_for_blocks(blocks)457test_node.sync_with_ping()458# should not have received any getdata messages459with p2p_lock:460assert "getdata" not in test_node.last_message462# This time, direct fetch should work463blocks = []464for _ in range(3):465blocks.append(create_block(tip, height=height, ntime=block_time))466blocks[-1].solve()467tip = blocks[-1].hash_int468block_time += 1469height += 1471test_node.send_header_for_blocks(blocks)472test_node.sync_with_ping()473test_node.wait_for_getdata([x.hash_int for x in blocks], timeout=DIRECT_FETCH_RESPONSE_TIME)475[test_node.send_without_ping(msg_block(x)) for x in blocks]477test_node.sync_with_ping()479# Now announce a header that forks the last two blocks480tip = blocks[0].hash_int481height -= 2482blocks = []484# Create extra blocks for later485for _ in range(20):486blocks.append(create_block(tip, height=height, ntime=block_time))487blocks[-1].solve()488tip = blocks[-1].hash_int489block_time += 1490height += 1492# Announcing one block on fork should not trigger direct fetch493# (less work than tip)494test_node.last_message.pop("getdata", None)495test_node.send_header_for_blocks(blocks[0:1])496test_node.sync_with_ping()497with p2p_lock:498assert "getdata" not in test_node.last_message500# Announcing one more block on fork should trigger direct fetch for
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.