> For the complete documentation index, see [llms.txt](https://protocol.bsvblockchain.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://protocol.bsvblockchain.org/peer-to-peer-networking/sv-node-wire-protocol.md).

# SV Node Wire Protocol

Message framing and message types of the SV Node P2P wire protocol (non-consensus)

{% hint style="warning" %}
This page documents a **network transport**, not the protocol. Nothing on this page affects whether a transaction or block is valid — see [Peer-to-Peer Networking](/peer-to-peer-networking/p2p.md) for why. The message *payloads* for `tx` and `block` carry the consensus serialization specified in [Transactions](/specification/transactions.md) and [Blocks](/specification/blocks.md); this page specifies only the envelope that carries them between peers.
{% endhint %}

The reference node exchanges data with its peers over TCP using a message-based protocol descended from the original Bitcoin wire protocol. This page specifies the message framing, the connection handshake, the catalogue of message types, and the BSV-specific extensions that support unbounded block sizes and higher throughput.

**Ground truth.** The message framing, message-type names, protocol version, and size limits below derive from [`src/protocol.h`](https://github.com/bitcoin-sv/bitcoin-sv/blob/879fc8b42168dd0e608dafd51b39c6dabad37d4d/src/protocol.h) and [`src/protocol.cpp`](https://github.com/bitcoin-sv/bitcoin-sv/blob/879fc8b42168dd0e608dafd51b39c6dabad37d4d/src/protocol.cpp), the protocol-version constants from [`src/version.h`](https://github.com/bitcoin-sv/bitcoin-sv/blob/879fc8b42168dd0e608dafd51b39c6dabad37d4d/src/version.h), and the network magic values from [`src/chainparams.cpp`](https://github.com/bitcoin-sv/bitcoin-sv/blob/879fc8b42168dd0e608dafd51b39c6dabad37d4d/src/chainparams.cpp). The BSV-specific message extensions are also specified in the [`bitcoin-sv-specs/protocol`](https://github.com/bitcoin-sv-specs/protocol/tree/master/p2p) documents referenced inline. All citations are pinned to release **v1.2.2** (commit `879fc8b`).

## Conventions

* **Little-endian** unless stated otherwise: integer header and payload fields are serialized least-significant byte first. The one exception is the network port inside an address, which is big-endian (network byte order).
* **`char[n]`** fields are fixed-width and NULL-padded.
* **`var_int`** (CompactSize) is the variable-length integer used for counts and lengths, identical to the encoding used throughout the consensus serialization — see [CompactSize](/specification/transactions.md#compactsize-varint).
* **`var_str`** is a `var_int` length followed by that many bytes of UTF-8 text.
* **`var_bytes`** is a `var_int` length followed by that many raw bytes.

## Message structure

Every message is a fixed-size header followed by a payload. There are two header forms: the **basic** 24-byte header used by all ordinary messages, and the **extended** header used to carry payloads larger than 4 GiB (see [Extended messages](#extended-large-messages)).

### Basic header

| Field size | Name       | Data type   | Description                                                                        |
| ---------- | ---------- | ----------- | ---------------------------------------------------------------------------------- |
| 4          | `magic`    | `char[4]`   | Network magic value identifying the network (see [Network magic](#network-magic)). |
| 12         | `command`  | `char[12]`  | Message type identifier, ASCII, NULL-padded (e.g. `version`, `tx`, `block`).       |
| 4          | `length`   | `uint32_t`  | Byte length of `payload`.                                                          |
| 4          | `checksum` | `char[4]`   | First 4 bytes of the double-SHA-256 of `payload`.                                  |
| `length`   | `payload`  | `uint8_t[]` | The message body.                                                                  |

The header is 24 bytes (`MESSAGE_START_SIZE` 4 + `COMMAND_SIZE` 12 + `length` 4 + `CHECKSUM_SIZE` 4). Because `length` is a `uint32_t`, a basic message payload is limited to `0xFFFFFFFF` (4 GiB − 1) bytes; larger payloads use the [extended header](#extended-large-messages).

### Network magic

The 4-byte magic prefix identifies which network a message belongs to and frames the start of each message on the stream.

| Network                    | Magic (bytes, in order) |
| -------------------------- | ----------------------- |
| Mainnet                    | `e3 e1 f3 e8`           |
| Testnet                    | `f4 e5 f3 f4`           |
| STN (scaling test network) | `fb ce c4 f9`           |
| Regtest                    | `da b5 bf fa`           |

## Protocol version

The current P2P protocol version is **70016** (`PROTOCOL_VERSION`). Each peer advertises its version in the [`version`](#the-handshake) message during the handshake, and behaviour is gated on the negotiated minimum of the two peers' versions. Notable version thresholds:

| Version | Meaning                                                                                                                                                                         |
| ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 70016   | `EXTENDED_PAYLOAD_VERSION` — peer understands [extended (large) messages](#extended-large-messages). A node must not send extended-format messages to peers below this version. |
| 70015   | `INVALID_CB_NO_BAN_VERSION`                                                                                                                                                     |
| 70014   | `SHORT_IDS_BLOCKS_VERSION` — compact blocks.                                                                                                                                    |
| 70013   | `FEEFILTER_VERSION` — `feefilter` message.                                                                                                                                      |
| 70012   | `SENDHEADERS_VERSION` — `sendheaders` message.                                                                                                                                  |
| 70011   | `NO_BLOOM_VERSION` — bloom filtering no longer advertised by default.                                                                                                           |
| 31800   | `GETHEADERS_VERSION` = `MIN_PEER_PROTO_VERSION`; peers below this are not accepted.                                                                                             |

## The handshake

A connection begins with a version handshake before any other message is exchanged:

1. Each side sends a `version` message describing itself.
2. Each side replies with `verack` to acknowledge the peer's `version`.
3. Only after both `verack`s may other message types be sent. Optionally, [`protoconf`](#protoconf) is sent immediately after `verack` to advertise relaxed limits.

### `version` message

| Field size | Name             | Data type   | Description                                                                                                                      |
| ---------- | ---------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------- |
| 4          | `version`        | `int32_t`   | Protocol version of the sending node.                                                                                            |
| 8          | `services`       | `uint64_t`  | Bitfield of services offered (see [Service flags](#service-flags)).                                                              |
| 8          | `timestamp`      | `int64_t`   | Current UNIX time in seconds.                                                                                                    |
| 26         | `addr_recv`      | `net_addr`  | Address of the receiving node (as seen by the sender).                                                                           |
| 26         | `addr_from`      | `net_addr`  | Address of the sending node.                                                                                                     |
| 8          | `nonce`          | `uint64_t`  | Random nonce; used to detect connections to self.                                                                                |
| 1+         | `user_agent`     | `var_str`   | Human-readable client identifier.                                                                                                |
| 4          | `start_height`   | `int32_t`   | Height of the sender's best block.                                                                                               |
| 1          | `relay`          | `bool`      | Whether the peer should relay transactions (BIP 37).                                                                             |
| 1+         | `association_id` | `var_bytes` | Association identifier for [multi-streams](#multi-streams). Optional; absence signals a peer that does not support associations. |

The `association_id` field is a BSV addition appended after `relay`; see [Multi-streams](#multi-streams). The `version` message uses the **26-byte** `net_addr` form (services + IP + port, with **no** timestamp).

### `net_addr`

Network addresses appear in `version` and `addr` messages.

| Field size | Name       | Data type  | Description                                                                             |
| ---------- | ---------- | ---------- | --------------------------------------------------------------------------------------- |
| 4          | `time`     | `uint32_t` | UNIX timestamp the address was last seen. **Present in `addr` only**, not in `version`. |
| 8          | `services` | `uint64_t` | Service bits advertised by that address.                                                |
| 16         | `ip`       | `char[16]` | IPv6 address, or an IPv4 address mapped into IPv6 (`::ffff:a.b.c.d`).                   |
| 2          | `port`     | `uint16_t` | TCP port, **big-endian** (network byte order).                                          |

### Service flags

| Bit      | Name                | Meaning                                                                                           |
| -------- | ------------------- | ------------------------------------------------------------------------------------------------- |
| `1 << 0` | `NODE_NETWORK`      | Node serves the full blockchain. Set by all reference nodes; unset by SPV/light clients.          |
| `1 << 1` | `NODE_GETUTXO`      | BIP 64 `getutxo`. Not supported by the reference node.                                            |
| `1 << 2` | `NODE_BLOOM`        | Willing to serve bloom-filtered connections. No longer advertised by default as of version 70011. |
| `1 << 4` | `NODE_XTHIN`        | Xtreme Thinblocks support.                                                                        |
| `1 << 5` | `NODE_BITCOIN_CASH` | Legacy UAHF-era separation bit.                                                                   |

Service bits are unauthenticated advertisements; implementations must be robust against peers that advertise services they do not actually provide.

## Message types

The reference node recognises the following message types (`allNetMessageTypes` in `protocol.cpp`). Message types not on this list are ignored, which is what allows optional messages like `protoconf` to be introduced without a protocol-version bump.

### Connection and control

| Command         | Description                                                               |
| --------------- | ------------------------------------------------------------------------- |
| `version`       | Opening handshake; advertises version, services, and address.             |
| `verack`        | Acknowledges a received `version`.                                        |
| `ping` / `pong` | Liveness check; `pong` echoes the `ping` nonce (BIP 31, version ≥ 60000). |
| `reject`        | Reports rejection of a previous message.                                  |
| `protoconf`     | Advertises relaxed protocol limits (see [protoconf](#protoconf)).         |
| `createstrm`    | Opens an additional [stream](#multi-streams) within an association.       |
| `streamack`     | Acknowledges a `createstrm`.                                              |

### Peer discovery

| Command   | Description                                                |
| --------- | ---------------------------------------------------------- |
| `getaddr` | Requests known peer addresses.                             |
| `addr`    | Advertises peer addresses (each a `net_addr` with `time`). |

### Inventory and data transfer

| Command     | Description                                                                              |
| ----------- | ---------------------------------------------------------------------------------------- |
| `inv`       | Announces available objects by type and hash.                                            |
| `getdata`   | Requests objects previously announced in an `inv`.                                       |
| `notfound`  | Reports that requested objects are unavailable.                                          |
| `tx`        | A transaction.                                                                           |
| `block`     | A full block.                                                                            |
| `mempool`   | Requests the peer's mempool contents (see [mempool message](#extended-mempool-message)). |
| `feefilter` | Requests that the peer not relay transactions below a fee rate (version ≥ 70013).        |

An `inv`/`getdata`/`notfound` entry pairs a 4-byte type with a 32-byte hash. The object types are:

| Value | Name                 | Object                                |
| ----- | -------------------- | ------------------------------------- |
| 1     | `MSG_TX`             | Transaction.                          |
| 2     | `MSG_BLOCK`          | Block.                                |
| 3     | `MSG_FILTERED_BLOCK` | Bloom-filtered block (`merkleblock`). |
| 4     | `MSG_CMPCT_BLOCK`    | Compact block.                        |
| 5     | `MSG_DATAREF_TX`     | Dataref transaction.                  |

### Headers and block synchronisation

| Command       | Description                                                                              |
| ------------- | ---------------------------------------------------------------------------------------- |
| `getblocks`   | Requests an `inv` of block hashes from a locator.                                        |
| `getheaders`  | Requests block headers from a locator.                                                   |
| `headers`     | Delivers block headers.                                                                  |
| `sendheaders` | Requests that new blocks be announced via `headers` rather than `inv` (version ≥ 70012). |
| `gethdrsen`   | Requests enriched headers.                                                               |
| `hdrsen`      | Delivers enriched headers.                                                               |
| `sendhdrsen`  | Requests enriched-header announcements.                                                  |

### Compact blocks

| Command       | Description                                                  |
| ------------- | ------------------------------------------------------------ |
| `sendcmpct`   | Negotiates compact-block relay (version ≥ 70014).            |
| `cmpctblock`  | A compact block (short transaction IDs).                     |
| `getblocktxn` | Requests the full transactions missing from a compact block. |
| `blocktxn`    | Delivers the requested transactions.                         |

### Bloom filtering (BIP 37)

| Command       | Description                                                                            |
| ------------- | -------------------------------------------------------------------------------------- |
| `filterload`  | Installs a bloom filter on the connection.                                             |
| `filteradd`   | Adds an element to the current filter.                                                 |
| `filterclear` | Removes the current filter.                                                            |
| `merkleblock` | A block reduced to the transactions matching the filter, plus a partial Merkle branch. |

### Large-message framing

| Command  | Description                                                                          |
| -------- | ------------------------------------------------------------------------------------ |
| `extmsg` | Envelope indicating an [extended (large) payload](#extended-large-messages) follows. |

{% hint style="info" %}
The reference node also defines message types for features **outside the wire protocol proper** — Miner ID (`revokemid`), node authentication (`authch`, `authresp`), dataref transactions (`datareftx`), and double-spend detection (`dsdetected`). These are not part of the core wire protocol and are not documented here.
{% endhint %}

## BSV extensions

The following extensions distinguish the BSV wire protocol from the original Bitcoin protocol. All are designed for backward compatibility: a peer that does not understand an extension either negotiates it away during the handshake or simply ignores the unknown message.

### Extended (large) messages

The basic header's `length` field is a `uint32_t`, capping a basic payload at 4 GiB. To carry blocks (and potentially other objects) larger than that, an **extended header** is used. It is signalled by the special command `extmsg`, after which the real command and a 64-bit length follow before the payload.

| Field size   | Name          | Data type   | Description                                                                                                    |
| ------------ | ------------- | ----------- | -------------------------------------------------------------------------------------------------------------- |
| 4            | `magic`       | `char[4]`   | Network magic.                                                                                                 |
| 12           | `command`     | `char[12]`  | Set to `extmsg`.                                                                                               |
| 4            | `length`      | `uint32_t`  | Set to `0xFFFFFFFF` to mark an extended message. The real length is in `ext_length`.                           |
| 4            | `checksum`    | `char[4]`   | Set to `0x00000000` and **not checked**; computing a checksum over multi-gigabyte payloads has little utility. |
| 12           | `ext_command` | `char[12]`  | The real message type (e.g. `block`).                                                                          |
| 8            | `ext_length`  | `uint64_t`  | The real payload length.                                                                                       |
| `ext_length` | `payload`     | `uint8_t[]` | The message body.                                                                                              |

Extended messages require protocol version **70016** (`EXTENDED_PAYLOAD_VERSION`). A node must not send an extended-format message to a peer advertising a lower version, or it risks being banned. See the [large-message specification](https://github.com/bitcoin-sv-specs/protocol/blob/master/p2p/large_messages.md).

### `protoconf`

By default a peer will accept messages up to `DEFAULT_MAX_PROTOCOL_RECV_PAYLOAD_LENGTH` (2 MiB), falling back to `LEGACY_MAX_PROTOCOL_PAYLOAD_LENGTH` (1 MiB) for peers that predate this message. The optional `protoconf` message lets a peer advertise **relaxed** limits so larger non-block messages can be exchanged.

| Field size | Name                   | Data type  | Description                                                                                                                                |
| ---------- | ---------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| variable   | `numberOfFields`       | `var_int`  | Number of fields that follow; also serves as the message version. Must be ≥ 1 (version 0 is invalid); this specification is version 2.     |
| 4          | `maxRecvPayloadLength` | `uint32_t` | Maximum non-block message payload the sender is willing to receive. Minimum (and default) 1 MiB; advertising less is a protocol violation. |
| variable   | `streamPolicies`       | `var_str`  | Comma-separated list of supported [stream policies](#multi-streams).                                                                       |

Rules of use:

* `protoconf` may only be sent **after** `verack`, at most **once** per connection, and should be sent immediately after `verack`.
* It can only **relax** limits, never tighten them below the defaults — this preserves compatibility with peers that ignore the message.
* The `protoconf` payload itself is capped at 1 MiB.
* Advertising `maxRecvPayloadLength` also lifts the legacy `MAX_INV_SZ` (50 000) cap on `inv`/`getdata` element counts; those are then bounded only by the advertised payload length.

See the [protoconf specification](https://github.com/bitcoin-sv-specs/protocol/blob/master/p2p/protoconf.md).

### Multi-streams

A single TCP connection to a peer suffers head-of-line blocking: a newly-arrived block can be stuck behind a large batch of transactions already being transmitted. Multi-streams lets peers open several TCP connections (**streams**) grouped into one logical **association**, and dedicate streams to particular traffic categories (for example, a high-priority stream for blocks).

An association is identified by the `association_id` a peer includes in its `version` message. If the responding node echoes the same `association_id`, both peers support associations and further streams can be opened.

**`createstrm`** — sent as the first message on a new connection (in place of `version`) to attach it as a stream to an existing association:

| Field size | Name             | Data type   | Description                                  |
| ---------- | ---------------- | ----------- | -------------------------------------------- |
| 1+         | `association_id` | `var_bytes` | The association this stream joins.           |
| 1          | `stream_type`    | `byte`      | Stream category (1–4).                       |
| 0+         | `stream_policy`  | `var_str`   | (Optional) name of the stream policy to use. |

**`streamack`** — the response confirming the stream, echoing the association ID and stream type:

| Field size | Name             | Data type   | Description                         |
| ---------- | ---------------- | ----------- | ----------------------------------- |
| 1+         | `association_id` | `var_bytes` | The association being acknowledged. |
| 1          | `stream_type`    | `byte`      | The stream type being acknowledged. |

A failed `createstrm` yields a `reject` and the connection is closed.

Two stream policies are implemented: **`Default`** (no additional streams; behaves like a single traditional connection) and **`BlockPriority`** (one extra stream carrying block and control messages, so they are prioritised over bulk transaction traffic). Supported policy names are exchanged via the `streamPolicies` field of [`protoconf`](#protoconf). See the [multi-streams specification](https://github.com/bitcoin-sv-specs/protocol/blob/master/p2p/multistreams.md).

### Extended `mempool` message

The `mempool` message may carry an optional **age** field. A node responding to a `mempool` request restricts the transactions it reports to those that have been in its mempool for at least the given number of seconds.

| Field size | Name  | Data type  | Description                                                                     |
| ---------- | ----- | ---------- | ------------------------------------------------------------------------------- |
| 0+         | `age` | `uint64_t` | Minimum mempool age (seconds) for a transaction to be included in the response. |

Old nodes continue to use the field-less form unchanged. See the [mempool message specification](https://github.com/bitcoin-sv-specs/protocol/blob/master/p2p/mempool.md).

## Message-size limits

The reference node applies per-message size limits as a transport-layer defence. These are **policy limits on the network layer**, not consensus rules; exceeding them causes a peer to be disconnected or banned but says nothing about the validity of any transaction or block.

| Limit                                      | Value                             | Applies to                                                                                                                    |
| ------------------------------------------ | --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `LEGACY_MAX_PROTOCOL_PAYLOAD_LENGTH`       | 1 MiB                             | Peers that do not support `protoconf`; also the `protoconf` payload cap.                                                      |
| `DEFAULT_MAX_PROTOCOL_RECV_PAYLOAD_LENGTH` | 2 MiB                             | Default cap for non-block messages, overridable upward via `protoconf`.                                                       |
| `MAX_PROTOCOL_RECV_PAYLOAD_LENGTH`         | 1 GiB                             | Ceiling for the configurable receive limit.                                                                                   |
| Block-like messages                        | Max block size                    | `block`, `cmpctblock`, `blocktxn`, `hdrsen` are bounded by the configured maximum block size, not the protocol payload limit. |
| `tx`                                       | Max post-Genesis transaction size | A `tx` (and `datareftx`) is bounded by the consensus maximum transaction size.                                                |

Because block-like and transaction messages are exempt from the general payload limit, block and transaction propagation scales with the configured block size rather than a fixed network cap.
