draft-arya-jvff-p2p-quic-transport
Version 2 Zcash P2P Network Protocol
- Draft
Contents
Preamble
- ZIP
- XXX
- Title
- Version 2 Zcash P2P Network Protocol
- Status
- Draft
- Category
- Network
- Created
- 2026-07-31
- License
- MIT
- Discussions-To
- https://github.com/zcash/zips/issues/352
Terminology
The key words "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", "MAY", and "RECOMMENDED" in this document are to be interpreted as described in BCP 14 1 when, and only when, they appear in all capitals.
The terms "Mainnet" and "Testnet" are to be interpreted as described in section 3.12 of the Zcash Protocol Specification. 2
The term "network upgrade" is to be interpreted as described in ZIP 200. 3
The term "block chain" in this document is to be interpreted as described in section 3.3 of the Zcash Protocol Specification. 4
The term "legacy protocol" in this document refers to the Zcash P2P network protocol specified in ZIP 204 5.
peer : A network node that participates in the Zcash P2P protocol by maintaining connections to other peers over one or more supported transports and exchanging protocol data over streams.
transport : A concrete mechanism, such as QUIC 6 or Tor onion services 7, that carries connections between peers and provides the stream layer of this protocol (see Stream Layer and Transports).
connection : A session between two peers over a supported transport, over which P2P streams are exchanged.
initiator : The peer that initiated a connection (the transport-level client).
responder : The peer that accepted a connection (the transport-level server). In the context of a single request stream, responder instead denotes the peer serving the request, irrespective of which peer accepted the connection (see Request Streams); which sense is meant is indicated by context.
requester : The peer that opened a request stream and sends the request on it. Either the initiator or the responder of a connection may be the requester on a given request stream.
stream : An ordered, reliable byte stream within a connection, provided by the transport. Streams are typed by their first byte (see Stream Layer).
request stream : A bidirectional stream carrying a single request and its response.
announcement stream : A long-lived unidirectional stream carrying unsolicited announcements (blocks, transactions, or peer addresses).
record : A length-delimited unit of data on an announcement or handshake stream.
protocol version : A 32-bit integer identifying the set of protocol features supported by a node. Higher values indicate support for more recent features.
Abstract
This ZIP specifies version 2 of the Zcash peer-to-peer network protocol, a successor to the protocol specified in ZIP 204 5. It specifies the protocol's transport-neutral stream layer, the QUIC and Tor transports that realize it, the connection handshake, stream and record types, and relay behavior, and is intended to be sufficient for an implementor to build a conformant Zcash network peer.
Motivation
ZIP 204 5 specifies the current Zcash P2P network protocol, whose message-passing architecture is inherited from Bitcoin: a single unencrypted TCP bytestream carrying framed messages with implicit correlation between requests and responses. This ZIP proposes a successor protocol that replaces that architecture with multiplexed streams carried over an arbitrary transport that provides a secure channel between peers and a means of identifying the network and addressing peers. The protocol is specified against that abstract transport interface; this ZIP defines two concrete transports, one based on QUIC 6 and one carried over Tor onion services 7. The motivations for this change are:
-
Transport security. The legacy transport was unencrypted and unauthenticated, exposing all P2P traffic to passive network observers. Every transport of this protocol provides an encrypted channel: the QUIC transport integrates TLS 1.3 8 9, and the Tor transport inherits the encryption of onion service connections. (Bitcoin addressed the same gap with a bespoke encrypted transport, BIP 324 10; this protocol provides equivalent protection using standardized protocols.)
-
Multiplexing without head-of-line blocking. On the legacy transport, a single 2 MiB
blockmessage stalled all other traffic on the connection. Streams allow concurrent block downloads, transaction relay, and control traffic to proceed independently — on the QUIC transport, at both the application and packet-loss level. -
Explicit request correlation. Legacy responses (
block,tx,headers,notfound) carried no request identifiers, forcing implementations to correlate them heuristically. In this protocol, each request occupies its own stream, so correlation, cancellation, and per-request timeouts are structural. -
Simplification. Transport-level mechanisms that the legacy protocol carried in the application layer — payload checksums, network magic, keep-alives and latency measurement, flow control — are provided by the transport and are deleted from the application protocol.
No backwards compatibility with the legacy protocol is provided; this ZIP replaces it entirely. See Deployment.
This ZIP does not specify the encoding of transactions and block headers; for those, refer to the Zcash Protocol Specification 11 12. The encoding of a whole block in terms of its header and transactions is part of the peer-to-peer protocol rather than the consensus protocol 12, and is specified in Serialized Blocks.
Changes from the Legacy Protocol
This section is non-normative. Relative to the legacy protocol specified in ZIP 204 5, this ZIP:
- Replaces network magic bytes with transport-level network identification (for the QUIC transport, ALPN protocol identifiers; for the Tor transport, an in-band network identifier; see QUIC Transport and Tor Transport).
- Removes per-message headers entirely: the 12-byte command string is replaced
by a 1-byte stream type, and the length and
SHA-256dchecksum fields are subsumed by QUIC framing and authenticated encryption. - Replaces the
version/verackhandshake with a singleinitrecord exchange on a dedicated handshake stream (see Connection Handshake). Thetimestamp,addr_recv, andaddr_fromfields are removed. - Removes
pingandpong; QUIC provides keep-alives and RTT measurement. - Removes
reject; QUIC application error codes are used instead (see Application Error Codes). - Removes
inv,getdata, andnotfound; announcements are carried on dedicated unidirectional streams, and object requests carry per-item results. - Removes the inventory-walk
getblocksmessage; headers-first synchronization is the baseline synchronization method, and new request streams support checkpoint-based bulk synchronization:get-hashes(known hashes and sync-cost metadata),get-block-range(anchored block streaming),get-tree-roots(note commitment tree roots, verified against header commitments), andget-object(content-addressed synchronization artifacts). See Synchronization and 13. - Removes the deprecated
alertmessage. - Removes BIP 37 Bloom filtering (
filterload,filteradd,filterclear) and theNODE_BLOOMservice flag. - Removes the legacy
addrmessage andCAddress/CServiceencodings; theaddrv2address encoding of ZIP 155 14 is the only address format. - Retains divided block relay — header-based block announcement and compact
block relay — recast onto announcement streams and request streams, with a
block's coinbase transaction and transaction IDs additionally available in
get-headersresponses (see Block Relay).
Specification
This specification is organized bottom-up. Transports defines how connections are established and secured (QUIC and Tor); Stream Layer defines the typed streams that carry all application data, and Data Types and Encoding the shared encodings. Connection Handshake and Protocol Versioning cover connection setup. Request Stream Types and Announcement Stream Types specify the individual request and announcement formats, and Block Relay, Synchronization, Transaction Relay, and Address Relay the behaviors built on them.
Network Parameters
Networks
There are three Zcash networks: Mainnet, Testnet, and Regtest. Every connection is bound to exactly one network, determined during connection establishment as each transport defines (see Transports); this replaces the network magic bytes of the legacy protocol.
DNS Seeds
The DNS seed hostnames used for initial peer discovery on each network are those listed in ZIP 204 15. Regtest does not use DNS seeds or hardcoded seed nodes.
Peer Discovery
A node discovers peers through the following mechanisms:
-
Persisted addresses. A node SHOULD persist the peer addresses it learns (see Address Relay) across restarts, and on startup SHOULD attempt connections to previously learned addresses before consulting seeds.
-
DNS seeding. When the node has no usable persisted addresses — on first start, or when too few persisted addresses yield successful connections — it queries the DNS seed hostnames listed above for A and AAAA records, and attempts connections to the resulting addresses on the default port of the QUIC transport.
-
Hardcoded seed nodes. If DNS seeding fails or is insufficient, the node MAY fall back to a compiled-in list of seed node addresses. DNS seeding is preferred over hardcoded seed nodes.
-
Address relay. Connected peers exchange address information using
get-addrrequest streams and address announcement streams (seeget-addrand Address Announcements). This mechanism operates independently of the above.
DNS seeding yields IP addresses only, and so bootstraps only IP-based
transports such as QUIC. Addresses of peers reachable over overlay transports —
such as the TORV3 addresses of the Tor transport (see
Tor Transport) — are learned through address relay or provided
by local configuration.
Transports
The protocol semantics in this document are defined against the abstract stream layer of Stream Layer. A transport is a concrete mechanism that carries connections between peers and realizes that stream layer. This ZIP defines two transports: one based on QUIC (see QUIC Transport), and one carried over Tor onion services (see Tor Transport). Future revisions may define additional transports — a transport carrying the stream layer over the Nym mixnet is anticipated (see Deployment) — and a node MAY support several transports simultaneously. The protocol semantics on a connection are identical regardless of its transport.
Every transport MUST provide:
- a secure channel between the two peers: confidentiality and integrity for all application data;
- identification of the network (see Networks) during connection establishment, such that a connection between peers on different networks fails before any application data is exchanged;
- a form of address by which peers reachable over the transport are identified, encodable as a network address record (see Network Address Record);
- the stream operations and guarantees of Transport Requirements.
Connection Management
The transport provides keep-alives and round-trip time measurement; the legacy
ping/pong messages have no equivalent in this protocol.
- A node SHOULD use an idle timeout of at least 120 seconds, and SHOULD use the transport's keep-alive mechanism to keep connections it wishes to retain from idling out. The specific values are implementation-defined.
- A node MUST close a connection on which the application handshake (see Connection Handshake) has not completed within a bounded time, measured from the establishment of the transport connection so that it covers everything a peer sends beforehand — including the Tor transport's connection preamble (see Tor Transport). The specific timeout is implementation-defined; values between 3 and 60 seconds were used by legacy implementations. Without such a timeout, a peer that connects and then sends nothing holds a connection slot indefinitely, and the transport's keep-alive mechanism — answered by the transport stack rather than by the application — will report the connection healthy.
- A node SHOULD bound the number of concurrent inbound connections it accepts, and SHOULD bound the share of them originating from any one transport, so that inbound slots on a transport whose addresses are free to generate cannot displace the rest.
- Per-request timeouts are implementation-defined and are applied per stream (see Request Streams).
The specific connection limits are implementation-defined; the legacy reference values in ZIP 204 5 remain reasonable defaults.
A node SHOULD maintain at most one connection to a given remote address. Self-connection detection via the handshake nonce is specified in Init Record.
QUIC Transport
The QUIC transport uses QUIC version 1 6 over UDP, secured with TLS 1.3 8 as specified by RFC 9001 9.
- A node MUST NOT use 0-RTT (early data). Note: a peer that attempts to use
0-RTT MUST have its connection closed — a responder MUST NOT enable the
acceptance of early data, and a node whose peer nevertheless attempts to
use 0-RTT MUST close the connection with the
PROTOCOL_ERRORerror code. (0-RTT data is replayable by an attacker.) - QUIC datagrams 16 are not used by this protocol. A node MUST ignore
the peer's
max_datagram_frame_sizetransport parameter and MUST NOT send DATAGRAM frames. - A node MAY support connection migration as specified in RFC 9000. Migration links the node's traffic across the old and new network paths; a node for which that linkage is a privacy concern SHOULD establish a fresh connection instead of migrating.
- The address validation and amplification limits of RFC 9000 (including Retry packets) apply; a responder under load SHOULD use Retry to validate client addresses before committing state.
- QUIC's loss recovery provides round-trip time measurement, and QUIC PING
frames provide keep-alives. A node using the QUIC transport SHOULD advertise
a
max_idle_timeouttransport parameter consistent with the idle timeout of Connection Management.
Network Identification
The network of a QUIC connection is identified by an Application-Layer Protocol Negotiation (ALPN) 17 protocol identifier, negotiated in the QUIC-TLS handshake:
| Network | ALPN Identifier |
|---|---|
| Mainnet | zcash/main |
| Testnet | zcash/test |
| Regtest | zcash/regtest |
A node MUST offer exactly the ALPN identifier of the network it operates on, and MUST NOT complete a connection on which ALPN negotiation did not select that identifier.
Incompatible future revisions of this transport will be assigned new ALPN identifiers.
Default Ports
The QUIC transport uses the default port numbers of the legacy protocol, as specified in ZIP 204 18, as UDP port numbers.
Certificates
Connections are encrypted but endpoints are not authenticated: the goal is protection against passive network observers, not endpoint identity.
- A node presents a self-signed X.509 certificate, or a raw public key 19, for an Ed25519 or ECDSA P-256 key. The key MAY be ephemeral or persistent, at the node's option; a node concerned about cross-connection linkability SHOULD use per-connection ephemeral keys.
- A node MUST be prepared to accept both certificate encodings (X.509 and raw public key) and both key types from its peer.
- A node MUST NOT require the peer's certificate to chain to any certification authority, and MUST NOT reject a connection on the basis of certificate contents (subject names, expiry, or extensions).
- Client certificates are not requested: the responder MUST NOT require TLS client authentication.
Bulk Transfer Performance
Bulk synchronization moves hundreds of gigabytes over the request streams of this protocol, and its throughput is governed by transport configuration rather than by the protocol itself. The following applies to nodes that serve or perform bulk synchronization (see 13):
- A node SHOULD size its flow control windows to the bandwidth-delay product it intends to sustain: at least 16 MiB of per-stream receive window and 64 MiB of connection receive window are RECOMMENDED. Default windows tuned for interactive traffic cap bulk throughput at a small fraction of link capacity once round-trip time is non-negligible.
- A node serving bulk streams SHOULD use a paced, model-based congestion controller suited to high bandwidth-delay paths; loss-triggered controllers collapse under modest jitter at these transfer sizes.
- Implementations SHOULD use platform UDP batching and segmentation offloads where available; per-packet processing, not cryptography, is the dominant CPU cost of QUIC bulk transfer.
- One QUIC connection is typically processed by one core, bounding per-connection throughput. Synchronizing nodes obtain aggregate throughput by downloading from several peers concurrently rather than by tuning a single connection (see 13).
Stream Layer Mapping
The abstract stream layer of Transport Requirements maps onto QUIC as follows:
| Stream layer concept | QUIC realization |
|---|---|
| Bidirectional / unidirectional stream | QUIC bidirectional / unidirectional streams. |
| Finishing a stream direction | A STREAM frame with the FIN bit. |
| Resetting a stream with an error code | RESET_STREAM carrying the application error code. |
| Cancelling a peer's sending with an error code | STOP_SENDING carrying the application error code. |
| Closing the connection with an error code | CONNECTION_CLOSE (application variant) carrying the application error code. |
| Per-stream backpressure | QUIC stream and connection flow control. |
| Stream concurrency limits | initial_max_streams_bidi, initial_max_streams_uni, and MAX_STREAMS frames. |
| Keep-alive | QUIC PING frames. |
Tor Transport
The Tor transport carries connections over the Tor network 20 as connections to version 3 onion services 7. It serves traffic for which the metadata protection of onion routing outweighs its latency cost (see Deployment and Security and Privacy Considerations), and allows a node to participate in the network without exposing an IP address.
- A node that accepts inbound connections over this transport runs a version 3
onion service and advertises its address as a
TORV3network address record (see Network Address Record); the record'sportfield is the onion service virtual port, which SHOULD be the default port of the network (see Default Ports). - The initiator connects to the responder's onion service through the Tor network. The initiator needs no onion service of its own and is anonymous to the responder.
- The secure channel is provided by Tor itself: onion service connections are
end-to-end encrypted, and the connection is authenticated to the responder's
onion address (the Ed25519 key that the
TORV3address encodes). TLS is not used inside the connection, and the initiator is deliberately not authenticated (cf. Certificates). - The transport provides a single ordered bytestream per connection; the stream layer is realized on top of it by the framing layer of Stream Framing.
Note: The underlying Tor connection is a single TCP-like bytestream, so loss or congestion on it stalls all streams of the connection; the framing layer removes head-of-line blocking between streams at the application level only (see Deployment for transport selection).
Connection Preamble
Immediately after the connection is established, each peer sends a preamble, before any frames:
| Size | Field | Description |
|---|---|---|
| varies | network |
Network identifier string (CompactSize-prefixed; see below). |
| varies | initial_max_data |
Initial connection-level flow control credit in bytes (CompactSize). |
| varies | initial_max_stream_data |
Initial per-stream flow control credit in bytes (CompactSize). |
| varies | initial_max_streams_bidi |
Initial limit on the peer's concurrent bidirectional streams (CompactSize). |
| varies | initial_max_streams_uni |
Initial limit on the peer's concurrent unidirectional streams (CompactSize). |
The network string is the identifier of the node's network, using the same
values as the QUIC transport's ALPN identifiers (see
Network Identification): zcash/main, zcash/test,
or zcash/regtest. A node MUST close the connection if the peer's network
string differs from its own; this replaces ALPN-based network identification,
which is unavailable without TLS.
The flow control fields play the role of the corresponding QUIC transport
parameters: they are the initial credit available to the peer for the
corresponding limit (see Stream Framing). A node MUST allow
an initial_max_stream_data of at least 2,228,224 bytes (the maximum record
payload length plus framing headroom), and SHOULD allow the stream concurrency
minimums of Transport Requirements.
The preamble precedes every frame, and therefore precedes the record payload limit of Records and the malformed-frame rule of Stream Framing; neither bounds it. Its fields are bounded here instead, and a node MUST enforce these bounds while parsing the preamble, before allocating storage for a field:
- The
networkstring MUST NOT exceed 16 bytes. - Each of the four flow control fields MUST NOT exceed 2^62 − 1, the corresponding limit in QUIC 6.
- A node MUST close the connection immediately, without sending frames, if a preamble field exceeds its bound, if the preamble is not complete within the handshake timeout below, or if a CompactSize in the preamble is not canonically encoded (see Data Types and Encoding).
The handshake timeout of
Connection Management runs from the establishment of
the transport connection, so it covers the preamble as well as the init
record; an initiator that opens a connection and stalls mid-preamble is closed
by it. This matters most for this transport: onion service addresses are free
to generate and effectively unbannable (see
Misbehavior and Banning), so a connection slot held
open at no cost is the resource an attacker is actually consuming.
Incompatible future revisions of this transport will be assigned new network identifier strings.
Stream Framing
The stream layer is realized over the connection's ordered bytestream by a framing layer whose stream semantics — stream states, flow control, and error signalling — are modeled on those of QUIC 6.
Stream IDs are integers. The two least significant bits of a stream ID encode
its kind, as in QUIC: 0x0 initiator-opened bidirectional, 0x1
responder-opened bidirectional, 0x2 initiator-opened unidirectional, 0x3
responder-opened unidirectional. Successive streams of each kind are opened
with sequentially increasing stream IDs; a stream is opened implicitly by the
first frame that its opener sends referencing its ID.
Each frame is a 1-byte frame type followed by type-dependent fields. Integer fields, including stream IDs, are CompactSize-encoded.
| Type | Frame | Fields | Semantics |
|---|---|---|---|
0x00 |
PING |
8-byte opaque value | Keep-alive and RTT measurement; the receiver MUST respond with PONG. |
0x01 |
PONG |
8-byte echoed value | Response to PING. |
0x02 |
STREAM |
stream ID, flags (1 byte), length, data | Appends data to the stream. Flag bit 0 (FIN) finishes the sender's direction of the stream. |
0x03 |
RESET_STREAM |
stream ID, error code | Resets the sender's direction of the stream (see Transport Requirements). |
0x04 |
STOP_SENDING |
stream ID, error code | Requests that the peer stop sending on the stream. |
0x05 |
MAX_DATA |
maximum | Raises the connection-level flow control limit (cumulative bytes of stream data). |
0x06 |
MAX_STREAM_DATA |
stream ID, maximum | Raises the flow control limit of one stream (cumulative bytes). |
0x07 |
MAX_STREAMS_BIDI |
count | Raises the limit on the peer's cumulative count of opened bidirectional streams. |
0x08 |
MAX_STREAMS_UNI |
count | Raises the limit on the peer's cumulative count of opened unidirectional streams. |
0x09 |
CLOSE |
error code | Closes the connection with an application error code; the sender then closes the underlying connection. |
Flow control limits are cumulative, as in QUIC: a limit counts total bytes
sent on a stream (respectively, on all streams; respectively, total streams
opened) since the beginning of the connection, and each MAX_* frame
communicates a new absolute limit. Limits never decrease; a MAX_* frame with
a lower value than a previously communicated limit is ignored. A node MUST NOT
exceed a limit communicated by its peer; a peer that does so is a connection
error of type PROTOCOL_ERROR (equally, a frame that is malformed or has an
unrecognized frame type).
The length of a STREAM frame MUST NOT exceed 65,536 bytes; larger
application writes are split across multiple STREAM frames, allowing frames
of other streams to be interleaved between them.
The abstract stream layer maps onto this framing as follows:
| Stream layer concept | Framing realization |
|---|---|
| Bidirectional / unidirectional stream | Stream ID kinds 0x0/0x1 and 0x2/0x3. |
| Finishing a stream direction | A STREAM frame with the FIN flag. |
| Resetting a stream with an error code | RESET_STREAM carrying the application error code. |
| Cancelling a peer's sending with an error code | STOP_SENDING carrying the application error code. |
| Closing the connection with an error code | CLOSE carrying the application error code. |
| Per-stream backpressure | MAX_DATA and MAX_STREAM_DATA credit. |
| Stream concurrency limits | Preamble initial limits and MAX_STREAMS_* frames. |
| Keep-alive | PING frames. |
Stream Layer
All application data is carried on streams provided by the transport. The first byte of every stream is a stream type that determines the format and semantics of the remaining stream data.
Transport Requirements
The stream layer that every transport provides consists of bidirectional streams (both peers can send) and unidirectional streams (only the opener can send), with the following operations and guarantees:
- Either peer can open streams of either kind at any time. The receiver of a stream can tell which peer opened it and of which kind it is.
- Data written to a stream is delivered reliably and in order within that stream. Streams are mutually independent: delay or loss on one stream MUST NOT prevent delivery of data on other streams.
- A sender can finish its sending direction of a stream, signalling that no more data will be sent on it.
- A peer can reset a stream it is sending on, abandoning it with an application error code (see Application Error Codes), and can cancel the peer's sending direction of a stream, requesting with an application error code that the peer stop sending.
- Either peer can close the connection with an application error code.
- The receiver of a stream can apply per-stream backpressure to bound how much data it must buffer.
- Each peer can bound the number of streams of each kind that the other peer may have concurrently open. A node SHOULD allow at least 32 concurrent bidirectional streams, and SHOULD allow at least 8 concurrent unidirectional streams (sufficient for the announcement streams plus headroom for future types).
How each transport realizes these operations is specified in the corresponding transport section (for QUIC, see Stream Layer Mapping).
Stream Types
| Code | Name | Kind | Description |
|---|---|---|---|
0x00 |
Handshake | Bidirectional, initiator only | Connection handshake and control (see Connection Handshake). |
0x01 |
get-headers |
Bidirectional | Request block headers, optionally with transaction IDs. |
0x02 |
get-blocks |
Bidirectional | Request full blocks. |
0x03 |
get-tx |
Bidirectional | Request transactions. |
0x04 |
get-addr |
Bidirectional | Request peer addresses. |
0x05 |
get-mempool |
Bidirectional | Subscribe to mempool contents. |
0x06 |
get-hashes |
Bidirectional | Request best-chain block hashes and per-block sync-cost metadata. |
0x07 |
get-block-range |
Bidirectional | Stream a contiguous range of blocks, verified against an anchor hash. |
0x08 |
get-tree-roots |
Bidirectional | Request per-block note commitment tree roots for a height range. |
0x09 |
get-object |
Bidirectional | Request a range of a content-addressed synchronization artifact. |
0x10 |
Block announcements | Unidirectional | Announce new blocks (see Block Announcements). |
0x11 |
Transaction announcements | Unidirectional | Announce new transactions (see Transaction Announcements). |
0x12 |
Address announcements | Unidirectional | Gossip peer addresses (see Address Announcements). |
Stream type 0x00 and the ranges 0x01–0x0F and 0x10–0x1F are reserved
for the handshake, request stream types, and announcement stream types
respectively; future revisions are expected to assign new stream types from the
matching range.
To refuse a stream opened by its peer, a node cancels the peer's sending direction of that stream with the applicable error code and, if the stream is bidirectional, also resets its own sending direction with the same code; refusing a unidirectional stream involves only the cancellation.
A node receiving a stream whose type byte it does not recognize MUST refuse
that stream with the UNSUPPORTED_STREAM_TYPE error code (see
Application Error Codes) and MUST NOT treat it as a
connection error or assign a misbehavior penalty. This allows future stream
types to be deployed without version gating.
A stream that is finished by its opener before a complete type byte has been
received is a connection error of type PROTOCOL_ERROR.
Request Streams
A request stream is a bidirectional stream carrying exactly one request and its response:
- The requester opens a bidirectional stream, writes the stream type byte followed by the request in the format defined for that type, and finishes its sending direction.
- The responder writes the response in the format defined for that type and
finishes its sending direction. (For a stream type that defines an
open-ended response, the responder instead keeps its sending direction open;
see
get-mempool.)
Either peer may open request streams; there is no client/server asymmetry beyond the rules stated for individual stream types.
- A responder MAY begin serving a request as soon as the request is syntactically complete, without waiting for the requester to finish its sending direction.
- A responder that cannot or will not serve a request MAY reset its sending
direction of the stream with the
REFUSEDerror code instead of sending a response. - A requester that no longer needs a response SHOULD cancel the responder's
sending direction with the
CANCELLEDerror code; the responder SHOULD then reset the stream and abandon work on the request. - A reset or refused request stream is never resumed or reused. A requester that retries a request after a reset does so by opening a new stream, and the next byte the requesting side sends — the first byte of that new stream — MUST be the stream type byte, as for any other stream.
- A node SHOULD apply an implementation-defined timeout to each outstanding request, cancelling the stream if the response does not complete in time.
- A node MUST NOT send more than one request on a stream. Unless the stream
type defines an open-ended response, any data following a complete request
or response on a stream is a connection error of type
PROTOCOL_ERROR. - A request or response that does not conform to the format defined for its
stream type — including an unrecognized
resultvalue in a response — is a connection error of typePROTOCOL_ERROR, unless other handling is specified for the stream type.
A node bounds the number of concurrent requests a peer may have outstanding using the transport's stream concurrency limits (see Transport Requirements).
Announcement Streams
An announcement stream is a long-lived unidirectional stream carrying a sequence of records for one announcement topic.
- After the connection handshake completes, each peer SHOULD open one announcement stream of each type it intends to send announcements on, and keep it open for the life of the connection.
- A node MUST NOT open more than one announcement stream of a given type in
the same direction at a time. A second concurrent stream of the same type is
a connection error of type
PROTOCOL_ERROR. If an announcement stream is reset or finished, the sender MAY open a replacement. - Announcements are best-effort: if transport backpressure prevents a record from being written promptly, the sender MAY drop the announcement rather than queueing it indefinitely.
- A record on an announcement stream that does not conform to the format
defined for its stream type — a nonconforming record — is a connection
error of type
PROTOCOL_ERROR, unless other handling is specified for the stream type. (New record formats are introduced under new protocol versions, so an unrecognized format is a violation, not a newer peer.)
Records
Each record on an announcement or handshake stream is encoded as a fixed-size 4-byte length prefix followed by that many bytes of payload:
| Size | Field | Description |
|---|---|---|
| 4 | length |
Payload length in bytes (uint32, little-endian). |
| varies | payload |
Record payload; format depends on the stream type. |
The maximum length of a record payload — and of any individually
length-prefixed element in a request or response, such as a serialized block —
is 2,097,152 bytes (2 MiB). A node MUST treat a length prefix exceeding this
limit as a connection error of type FLOOD.
A record MUST NOT be processed until it is complete. A stream that is finished
in the middle of a record is a connection error of type PROTOCOL_ERROR.
Application Error Codes
The following application error codes are used when closing a connection, resetting a stream, or cancelling a peer's sending direction (see Transport Requirements):
| Code | Constant | Usage |
|---|---|---|
0x00 |
NO_ERROR |
Graceful connection close. |
0x01 |
PROTOCOL_ERROR |
The peer violated this specification. |
0x02 |
UNSUPPORTED_STREAM_TYPE |
Stream refusal: unrecognized stream type. |
0x03 |
OBSOLETE |
The peer's protocol version is below the minimum for the current network epoch (see Network Upgrade Epoch Enforcement). |
0x04 |
SELF_CONNECTION |
The connection is to the node itself (see Init Record). |
0x05 |
FLOOD |
The peer exceeded a size or rate limit. |
0x06 |
MISBEHAVIOR |
The peer's misbehavior score reached the ban threshold (see Misbehavior and Banning). |
0x07 |
CANCELLED |
Stop-sending/reset: the request is no longer wanted. |
0x08 |
REFUSED |
Stream reset: the responder declines to serve this request. |
0x09 |
INTERNAL_ERROR |
The sender encountered an internal error. |
Where this document states that an event is a connection error of a given type, a node detecting that event MUST close the connection with the corresponding error code.
A node receiving an error code it does not recognize MUST treat it as
INTERNAL_ERROR.
Data Types and Encoding
The integer types and byte order, the CompactSize variable-length integer encoding (including its canonicity requirement), and the string encoding are as specified in ZIP 204 21. As there, a node MUST reject non-canonical CompactSize encodings.
Every CompactSize count and length in this protocol is peer-supplied and may be as large as 2^64 − 1 on the wire, whatever bound this document places on its legitimate values. A node MUST check a count or length against its specified bound before using it, and MUST NOT allocate storage in proportion to a declared count before the corresponding data has been received. Where this document requires a bound on a sum of two counts, the node MUST also bound each addend, since a sum of two unbounded values can wrap and satisfy the bound on the sum. Likewise, a node computing an offset or a remaining length from peer-supplied values MUST perform the arithmetic in a way that cannot overflow or underflow.
Serialized Blocks
The Zcash Protocol Specification defines the encodings of block headers and
transactions, but leaves the encoding of a whole block to the peer-to-peer
protocol 12. A serialized block is encoded as follows,
unchanged from the payload of the legacy block message:
| Size | Field | Description |
|---|---|---|
| varies | header |
The serialized block header 12 (including the Equihash solution). |
| varies | tx_count |
Number of transactions in the block (CompactSize). |
| varies | txns |
tx_count serialized transactions 11, in block order. |
Network Address Record
Peer addresses use the addrv2 address record specified in ZIP 155
14; it is the only address encoding in this protocol. The record
fields (time, services, networkID, sizeAddr, addr, port), the
network IDs (IPV4, IPV6, TORV3, I2P, CJDNS), the 512-byte addr
limit, the per-network-ID length and encoding rules, and the prohibition on
gossiping addresses with unrecognized network IDs are all as specified in
ZIP 155.
The port field is interpreted per transport: for QUIC endpoints it is the
UDP port, and for TORV3 addresses it is the onion service virtual port (see
Tor Transport). It MUST be 0 if not relevant for the
network.
The time field of a relayed record is attacker-suppliable and
unauthenticated: it is the relaying peer's claim about when the address was
last seen, and nothing binds it to reality. Because it is the freshness
signal an address manager ranks on, an unvalidated time set far in the
future makes an attacker's addresses permanently the freshest entries of
whatever buckets they occupy, and so a preferred choice at every outbound
peer selection — an eclipse vector 22 that operates from inside the
buckets, where the bucketing of
Address Book Management affords no protection. A
node therefore:
- MUST clamp the
timeof a relayed record before storing it: atimemore than 10 minutes in the future, or equal to 0, is replaced by the current time less 5 days. - SHOULD NOT relay, and SHOULD eventually evict, a record whose clamped
timeis more than 30 days in the past. - SHOULD NOT make the relayed
timethe primary key of outbound peer selection, and SHOULD prefer its own record of successful connections to an address over any peer's claim about it.
These are the protections legacy implementations applied in code; they are stated here because the field is load-bearing for peer selection.
Note: Each network ID implies the transport by which the address is
reachable: IPV4, IPV6, and CJDNS addresses identify QUIC endpoints, and
TORV3 addresses identify onion services of the Tor transport. I2P
addresses are not reachable via either transport defined in this ZIP; they
remain defined for gossip so that nodes can learn them ahead of a future
revision that defines an I2P-capable transport.
Transaction References
Transactions are identified in announcements and requests by transaction
references. Per ZIP 239 23, transactions with version ≥ 5 are relayed
by wtxid (the txid followed by the authorizing data commitment auth_digest),
and transactions with version ≤ 4 by txid. A transaction reference is encoded
as a 1-byte type followed by a type-dependent identifier:
| Type | Name | Total Size | Description |
|---|---|---|---|
0x01 |
TXID |
33 bytes | Transaction with version ≤ 4, identified by its 32-byte txid. |
0x02 |
WTXID |
65 bytes | Transaction with version ≥ 5, identified by its 64-byte wtxid (txid followed by auth_digest). See ZIP 239 23. |
0x03 |
SHORTID |
39 bytes | Transaction within a specific block, identified by a 32-byte block hash followed by a 6-byte short transaction ID (see Short Transaction IDs). Only valid in get-tx requests. |
A transaction reference with an unrecognized type is a connection error of
type PROTOCOL_ERROR. Using the wrong reference type for a transaction's
version is subject to a misbehavior penalty only where the sender of the
reference necessarily holds the transaction and so knows its version:
in announcement stream records and get-mempool response records (see
Misbehavior and Banning). A get-tx requester,
by contrast, may not hold the transaction it is requesting, and can have
derived the reference type from data supplied by another peer (see
Requesting Missing Transactions); a
responder receiving a get-tx reference whose type does not match the
referenced transaction's version MUST answer that reference with a
not-found result, and MUST NOT assign a misbehavior penalty for it.
Service Flags
Service flags are advertised in the services field of init records and
network address records. Bit numbering is as in ZIP 204