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) 2015-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 node responses to invalid network messages."""7import random8import time10from test_framework.messages import (11CBlockHeader,12CInv,13MAX_HEADERS_RESULTS,14MAX_INV_SIZE,15MAX_PROTOCOL_MESSAGE_LENGTH,16MSG_TX,17from_hex,18msg_getdata,19msg_headers,20msg_inv,21msg_ping,22msg_version,23ser_string,24)25from test_framework.p2p import (26P2PDataStore,27P2PInterface,28)29from test_framework.test_framework import BitcoinTestFramework30from test_framework.util import (31assert_equal,32)34VALID_DATA_LIMIT = MAX_PROTOCOL_MESSAGE_LENGTH - 5 # Account for the 5-byte length prefix37class msg_unrecognized:38"""Nonsensical message. Modeled after similar types in test_framework.messages."""40msgtype = b'badmsg\x01'42def __init__(self, *, str_data):43self.str_data = str_data.encode() if not isinstance(str_data, bytes) else str_data45def serialize(self):46return ser_string(self.str_data)48def __repr__(self):49return "{}(data={})".format(self.msgtype, self.str_data)52class SenderOfAddrV2(P2PInterface):53def wait_for_sendaddrv2(self):54self.wait_until(lambda: 'sendaddrv2' in self.last_message)57class InvalidMessagesTest(BitcoinTestFramework):58def set_test_params(self):59self.num_nodes = 160self.setup_clean_chain = True61self.extra_args = [["[email protected]"]]63def run_test(self):64self.test_buffer()65self.test_duplicate_version_msg()66self.test_magic_bytes()67self.test_checksum()68self.test_size()69self.test_msgtype()70self.test_addrv2_empty()71self.test_addrv2_no_addresses()72self.test_addrv2_too_long_address()73self.test_addrv2_unrecognized_network()74self.test_oversized_inv_msg()75self.test_oversized_getdata_msg()76self.test_oversized_headers_msg()77self.test_invalid_pow_headers_msg()78self.test_noncontinuous_headers_msg()79self.test_resource_exhaustion()81def test_buffer(self):82self.log.info("Test message with header split across two buffers is received")83conn = self.nodes[0].add_p2p_connection(P2PDataStore())84# After add_p2p_connection both sides have the verack processed.85# However the pong from conn in reply to the ping from the node has not86# been processed and recorded in totalbytesrecv.87# Flush the pong from conn by sending a ping from conn.88conn.sync_with_ping(timeout=1)89# Create valid message90msg = conn.build_message(msg_ping(nonce=12345))91cut_pos = 12 # Chosen at an arbitrary position within the header92# Send message in two pieces93before = self.nodes[0].getnettotals()['totalbytesrecv']94conn.send_raw_message(msg[:cut_pos])95# Wait until node has processed the first half of the message96self.wait_until(lambda: self.nodes[0].getnettotals()['totalbytesrecv'] != before)97middle = self.nodes[0].getnettotals()['totalbytesrecv']98assert_equal(middle, before + cut_pos)99conn.send_raw_message(msg[cut_pos:])100conn.sync_with_ping(timeout=1)101self.nodes[0].disconnect_p2ps()103def test_duplicate_version_msg(self):104self.log.info("Test duplicate version message is ignored")105conn = self.nodes[0].add_p2p_connection(P2PDataStore())106with self.nodes[0].assert_debug_log(['redundant version message from peer']):107conn.send_and_ping(msg_version())108self.nodes[0].disconnect_p2ps()110def test_magic_bytes(self):111# Skip with v2, magic bytes are v1-specific112if self.options.v2transport:113return114self.log.info("Test message with invalid magic bytes disconnects peer")115conn = self.nodes[0].add_p2p_connection(P2PDataStore())116with self.nodes[0].assert_debug_log(['Header error: Wrong MessageStart ffffffff received']):117msg = conn.build_message(msg_unrecognized(str_data="d"))118# modify magic bytes119msg = b'\xff' * 4 + msg[4:]120conn.send_raw_message(msg)121conn.wait_for_disconnect(timeout=1)122self.nodes[0].disconnect_p2ps()124def test_checksum(self):125# Skip with v2, the checksum is v1-specific126if self.options.v2transport:127return128self.log.info("Test message with invalid checksum logs an error")129conn = self.nodes[0].add_p2p_connection(P2PDataStore())130with self.nodes[0].assert_debug_log(['Header error: Wrong checksum (badmsg, 2 bytes), expected 78df0a04 was ffffffff']):131msg = conn.build_message(msg_unrecognized(str_data="d"))132# Checksum is after start bytes (4B), message type (12B), len (4B)133cut_len = 4 + 12 + 4134# modify checksum135msg = msg[:cut_len] + b'\xff' * 4 + msg[cut_len + 4:]136conn.send_raw_message(msg)137conn.sync_with_ping(timeout=1)138# Check that traffic is accounted for (24 bytes header + 2 bytes payload)139assert_equal(self.nodes[0].getpeerinfo()[0]['bytesrecv_per_msg']['*other*'], 26)140self.nodes[0].disconnect_p2ps()142def test_size(self):143self.log.info("Test message with oversized payload disconnects peer")144conn = self.nodes[0].add_p2p_connection(P2PDataStore())145error_msg = (146['V2 transport error: packet too large (4000014 bytes)'] if self.options.v2transport147else ['Header error: Size too large (badmsg, 4000001 bytes)']148)149with self.nodes[0].assert_debug_log(error_msg):150msg = msg_unrecognized(str_data="d" * (VALID_DATA_LIMIT + 1))151msg = conn.build_message(msg)152conn.send_raw_message(msg)153conn.wait_for_disconnect(timeout=1)154self.nodes[0].disconnect_p2ps()156def test_msgtype(self):157self.log.info("Test message with invalid message type logs an error")158conn = self.nodes[0].add_p2p_connection(P2PDataStore())159if self.options.v2transport:160msgtype = 99 # not defined161msg = msg_unrecognized(str_data="d")162contents = msgtype.to_bytes(1, 'big') + msg.serialize()163tmsg = conn.v2_state.v2_enc_packet(contents, ignore=False)164with self.nodes[0].assert_debug_log(['V2 transport error: invalid message type']):165conn.send_raw_message(tmsg)166conn.sync_with_ping(timeout=1)167# Check that traffic is accounted for (20 bytes plus 3 bytes contents)168assert_equal(self.nodes[0].getpeerinfo()[0]['bytesrecv_per_msg']['*other*'], 23)169else:170with self.nodes[0].assert_debug_log(['Header error: Invalid message type']):171msg = msg_unrecognized(str_data="d")172msg = conn.build_message(msg)173# Modify msgtype174msg = msg[:7] + b'\x00' + msg[7 + 1:]175conn.send_raw_message(msg)176conn.sync_with_ping(timeout=1)177# Check that traffic is accounted for (24 bytes header + 2 bytes payload)178assert_equal(self.nodes[0].getpeerinfo()[0]['bytesrecv_per_msg']['*other*'], 26)179self.nodes[0].disconnect_p2ps()181def test_addrv2(self, label, required_log_messages, raw_addrv2):182node = self.nodes[0]183conn = node.add_p2p_connection(SenderOfAddrV2())185# Make sure bitcoind signals support for ADDRv2, otherwise this test186# will bombard an old node with messages it does not recognize which187# will produce unexpected results.188conn.wait_for_sendaddrv2()190self.log.info('Test addrv2: ' + label)192msg = msg_unrecognized(str_data=b'')193msg.msgtype = b'addrv2'194with node.assert_debug_log(required_log_messages):195# override serialize() which would include the length of the data196msg.serialize = lambda: raw_addrv2197conn.send_raw_message(conn.build_message(msg))198conn.sync_with_ping()200node.disconnect_p2ps()202def test_addrv2_empty(self):203self.test_addrv2('empty',204[205'received: addrv2 (0 bytes)',206'ProcessMessages(addrv2, 0 bytes): Exception',207'end of data',208],209b'')211def test_addrv2_no_addresses(self):212self.test_addrv2('no addresses',213[214'received: addrv2 (1 bytes)',215],216bytes.fromhex('00'))218def test_addrv2_too_long_address(self):219self.test_addrv2('too long address',220[221'received: addrv2 (525 bytes)',222'ProcessMessages(addrv2, 525 bytes): Exception',223'Address too long: 513 > 512',224],225bytes.fromhex(226'01' + # number of entries227'61bc6649' + # time, Fri Jan 9 02:54:25 UTC 2009228'00' + # service flags, COMPACTSIZE(NODE_NONE)229'01' + # network type (IPv4)230'fd0102' + # address length (COMPACTSIZE(513))231'ab' * 513 + # address232'208d')) # port234def test_addrv2_unrecognized_network(self):235now_hex = int(time.time()).to_bytes(4, "little").hex()236self.test_addrv2('unrecognized network',237[238'received: addrv2 (25 bytes)',239'9.9.9.9:8333',240'Added 1 addresses',241],242bytes.fromhex(243'02' + # number of entries244# this should be ignored without impeding acceptance of subsequent ones245now_hex + # time246'01' + # service flags, COMPACTSIZE(NODE_NETWORK)247'99' + # network type (unrecognized)248'02' + # address length (COMPACTSIZE(2))249'ab' * 2 + # address250'208d' + # port
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.