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.
1#!/usr/bin/env python32# Copyright (c) 2020-present The Bitcoin Core developers3# Distributed under the MIT software license, see the accompanying4# file COPYING or http://www.opensource.org/licenses/mit-license.php.5"""Test the getblockfrompeer RPC."""7from test_framework.messages import (8CBlock,9from_hex,10msg_headers,11NODE_WITNESS,12)13from test_framework.p2p import (14P2P_SERVICES,15P2PInterface,16)17from test_framework.test_framework import BitcoinTestFramework18from test_framework.util import (19assert_equal,20assert_raises_rpc_error,21JSONRPCException,22)25class GetBlockFromPeerTest(BitcoinTestFramework):26def set_test_params(self):27self.num_nodes = 328self.extra_args = [29[],30[],31["-fastprune", "-prune=1"]32]34def setup_network(self):35self.setup_nodes()37def check_for_block(self, node, hash):38try:39self.nodes[node].getblock(hash)40return True41except JSONRPCException:42return False44def run_test(self):45self.log.info("Mine 4 blocks on Node 0")46self.generate(self.nodes[0], 4, sync_fun=self.no_op)47assert_equal(self.nodes[0].getblockcount(), 204)49self.log.info("Mine competing 3 blocks on Node 1")50self.generate(self.nodes[1], 3, sync_fun=self.no_op)51assert_equal(self.nodes[1].getblockcount(), 203)52short_tip = self.nodes[1].getbestblockhash()54self.log.info("Connect nodes to sync headers")55self.connect_nodes(0, 1)56self.sync_blocks(self.nodes[0:2])58self.log.info("Node 0 should only have the header for node 1's block 3")59x = next(filter(lambda x: x['hash'] == short_tip, self.nodes[0].getchaintips()))60assert_equal(x['status'], "headers-only")61assert_raises_rpc_error(-1, "Block not available (not fully downloaded)", self.nodes[0].getblock, short_tip)63self.log.info("Fetch block from node 1")64peers = self.nodes[0].getpeerinfo()65assert_equal(len(peers), 1)66peer_0_peer_1_id = peers[0]["id"]68self.log.info("Arguments must be valid")69assert_raises_rpc_error(-8, "hash must be of length 64 (not 4, for '1234')", self.nodes[0].getblockfrompeer, "1234", peer_0_peer_1_id)71# cli handles wrong types differently72if not self.options.usecli:73assert_raises_rpc_error(-3, "JSON value of type number is not of expected type string", self.nodes[0].getblockfrompeer, 1234, peer_0_peer_1_id)74assert_raises_rpc_error(-3, "JSON value of type string is not of expected type number", self.nodes[0].getblockfrompeer, short_tip, "0")76self.log.info("We must already have the header")77assert_raises_rpc_error(-1, "Block header missing", self.nodes[0].getblockfrompeer, "00" * 32, 0)79self.log.info("Non-existent peer generates error")80for peer_id in [-1, peer_0_peer_1_id + 1]:81assert_raises_rpc_error(-1, "Peer does not exist", self.nodes[0].getblockfrompeer, short_tip, peer_id)83self.log.info("Fetching from pre-segwit peer generates error")84self.nodes[0].add_p2p_connection(P2PInterface(), services=P2P_SERVICES & ~NODE_WITNESS)85peers = self.nodes[0].getpeerinfo()86assert_equal(len(peers), 2)87presegwit_peer_id = peers[1]["id"]88assert_raises_rpc_error(-1, "Pre-SegWit peer", self.nodes[0].getblockfrompeer, short_tip, presegwit_peer_id)90self.log.info("Successful fetch")91result = self.nodes[0].getblockfrompeer(short_tip, peer_0_peer_1_id)92self.wait_until(lambda: self.check_for_block(node=0, hash=short_tip), timeout=1)93assert_equal(result, {})95self.log.info("Don't fetch blocks we already have")96assert_raises_rpc_error(-1, "Block already downloaded", self.nodes[0].getblockfrompeer, short_tip, peer_0_peer_1_id)98self.log.info("Don't fetch blocks while the node has not synced past it yet")99# For this test we need node 1 in prune mode and as a side effect this also disconnects100# the nodes which is also necessary for the rest of the test.101self.restart_node(1, ["-prune=550"])103# Generate a block on the disconnected node that the pruning node is not connected to104blockhash = self.generate(self.nodes[0], 1, sync_fun=self.no_op)[0]105block_hex = self.nodes[0].getblock(blockhash=blockhash, verbosity=0)106block = from_hex(CBlock(), block_hex)108# Connect a P2PInterface to the pruning node and have it submit only the header of the109# block that the pruning node has not seen110node1_interface = self.nodes[1].add_p2p_connection(P2PInterface())111node1_interface.send_and_ping(msg_headers([block]))113# Get the peer id of the P2PInterface from the pruning node114node1_peers = self.nodes[1].getpeerinfo()115assert_equal(len(node1_peers), 1)116node1_interface_id = node1_peers[0]["id"]118# Trying to fetch this block from the P2PInterface should not be possible119error_msg = "In prune mode, only blocks that the node has already synced previously can be fetched from a peer"120assert_raises_rpc_error(-1, error_msg, self.nodes[1].getblockfrompeer, blockhash, node1_interface_id)122self.log.info("Connect pruned node")123self.connect_nodes(0, 2)124pruned_node = self.nodes[2]125self.sync_blocks([self.nodes[0], pruned_node])127# We need to generate more blocks to be able to prune128self.generate(self.nodes[0], 400, sync_fun=self.no_op)129self.sync_blocks([self.nodes[0], pruned_node])130pruneheight = pruned_node.pruneblockchain(300)131assert_equal(pruneheight, 249)132# Ensure the block is actually pruned133pruned_block = self.nodes[0].getblockhash(2)134assert_raises_rpc_error(-1, "Block not available (pruned data)", pruned_node.getblock, pruned_block)136self.log.info("Fetch pruned block")137peers = pruned_node.getpeerinfo()138assert_equal(len(peers), 1)139pruned_node_peer_0_id = peers[0]["id"]140result = pruned_node.getblockfrompeer(pruned_block, pruned_node_peer_0_id)141self.wait_until(lambda: self.check_for_block(node=2, hash=pruned_block), timeout=1)142assert_equal(result, {})144self.log.info("Fetched block persists after next pruning event")145self.generate(self.nodes[0], 250, sync_fun=self.no_op)146self.sync_blocks([self.nodes[0], pruned_node])147pruneheight += 252148assert_equal(pruned_node.pruneblockchain(700), pruneheight)149assert_equal(pruned_node.getblock(pruned_block)["hash"], "196ee3a1a6db2353965081c48ef8e6b031cb2115d084bec6fec937e91a2c6277")151self.log.info("Fetched block can be pruned again when prune height exceeds the height of the tip at the time when the block was fetched")152self.generate(self.nodes[0], 250, sync_fun=self.no_op)153self.sync_blocks([self.nodes[0], pruned_node])154pruneheight += 251155assert_equal(pruned_node.pruneblockchain(1000), pruneheight)156assert_raises_rpc_error(-1, "Block not available (pruned data)", pruned_node.getblock, pruned_block)159if __name__ == '__main__':160GetBlockFromPeerTest(__file__).main()
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.