ZIP: XXX
Title: Version 2 Zcash P2P Network Protocol
Owners: Arya <[email protected]>
        Janito <[email protected]>
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:

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:

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:

  1. 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.

  2. 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.

  3. 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.

  4. Address relay. Connected peers exchange address information using get-addr request streams and address announcement streams (see get-addr and 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:

Connection Management

The transport provides keep-alives and round-trip time measurement; the legacy ping/pong messages have no equivalent in this protocol.

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.

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.

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):

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.

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 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:

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 0x010x0F and 0x100x1F 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:

  1. 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.
  2. 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 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.

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 264 − 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:

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

Name Bit Description
NODE_NETWORK 0 The node is capable of serving the complete block chain.
NODE_TREE_ROOTS 3 The node maintains a per-block note commitment tree root index and serves get-tree-roots requests.
NODE_SYNC_ARTIFACTS 4 The node serves content-addressed synchronization artifacts via get-object requests.
NODE_NETWORK_LIMITED 10 The node is capable of serving at least the most recent 2,304 blocks (approximately two days).

A node that does not hold the complete block chain — because it synchronized from a state snapshot and has not finished backfilling, or because it prunes historical blocks — MUST NOT advertise NODE_NETWORK; it SHOULD advertise NODE_NETWORK_LIMITED if it can serve at least the most recent 2,304 blocks of its best chain. A node selecting peers to request historical blocks from uses these flags (see 13).

Bits 24–31 remain reserved for temporary experiments, as in ZIP 204 24.

A node MUST ignore service bits that it does not recognize, and SHOULD preserve them when relaying address records (see Address Relay).

The legacy NODE_BLOOM flag (bit 2) is retired along with BIP 37 Bloom filtering, and MUST NOT be advertised.

Connection Handshake

Peers perform an application handshake immediately after the transport connection is established.

Handshake Sequence

  1. The initiator opens a bidirectional stream with stream type 0x00 (the handshake stream) and sends an init record.
  2. The responder sends its own init record on the same stream.
  3. The handshake is complete for a peer once it has both sent and received an init record. The negotiated protocol version is min(local_version, remote_version).

The two init records are independent: the responder MAY send its init record as soon as it observes the handshake stream’s type byte, without waiting for the initiator’s init record to arrive.

Only the initiator may open the handshake stream, and a connection has exactly one: a handshake stream opened by the responder, or a second handshake stream, is a connection error of type PROTOCOL_ERROR.

A node MUST NOT open any other stream before it has sent its init record, and MUST NOT send announcement records or requests before the handshake is complete. A node MAY buffer streams received from a peer before the handshake completes, or MAY refuse them with REFUSED (see Stream Types); it MUST NOT process them before the handshake completes.

The handshake stream remains open for the life of the connection. Future record kinds on the handshake stream may be defined by later revisions; a node MUST ignore handshake-stream records whose kind it does not recognize. Finishing or resetting the handshake stream signals intent to disconnect; a node observing this SHOULD close the connection with NO_ERROR.

Records on the handshake stream use the encoding of Records. The record payload begins with a 1-byte record kind; kind 0x00 is the init record.

Init Record

The init record payload (following the kind byte) has the following format:

Size Field Description
4 version The sender’s advertised protocol version (uint32).
varies services Service flags (CompactSize-encoded uint64).
8 nonce Random nonce for self-connection detection (uint64).
varies user_agent User agent string (CompactSize-prefixed, max 256 bytes).
4 start_height Best block height known to the sender (uint32).
1 relay Whether the sender wants transaction relay (uint8; 0 or 1).
1 announce Whether the sender requests high-bandwidth compact block announcements (uint8; 0 or 1). See Compact Block Relay.
1 full_ids Whether the sender requests full transaction IDs in compact block announcements (uint8; 0 or 1). See Full Transaction IDs.

All fields are mandatory. The legacy timestamp, addr_recv, and addr_from fields are removed.

The nonce field is used for self-connection detection. If a node receives an init record containing a nonce it recently sent in its own init records, it MUST close the connection with the SELF_CONNECTION error code.

If relay is 0, the peer MUST NOT open a transaction announcement stream to the sender, and SHOULD NOT announce transactions to it by any other means.

There is no mechanism for changing the value of the relay, announce, or full_ids fields during the life of a connection; a node that wants to change them disconnects and performs a new handshake.

Handshake Validation

A receiving node MUST validate the init record as follows:

Except where another error code is specified above, a node MUST close the connection with the PROTOCOL_ERROR error code if any of these checks fail.

Handshake Example (non-normative)

Connection establishment over the QUIC transport:

Initiator                                     Responder
   ═══ QUIC handshake (ALPN "zcash/main") ═══
   ── open stream 0x00: type byte, init ────▶
   ◀──────── same stream: init ──────────────
        (handshake is complete for each peer once it
         has both sent and received an init record)
   ── open announcement streams 0x10..0x12 ─▶
   ◀─ open announcement streams 0x10..0x12 ──
   ...requests and announcements flow in both directions...

Protocol Versioning

Protocol versions are 32-bit integers, advertised in the version field of the init record. The negotiated protocol version of a connection is the minimum of the two peers' advertised versions. The negotiated protocol version determines whether the peer meets the minimum version requirements for the current network epoch (see Network Upgrade Epoch Enforcement), and gates any features that later revisions of this protocol introduce under new protocol versions.

Protocol versions inhabit a single numbering space shared with the legacy protocol: a protocol version is assigned to a (network, network upgrade) pair, not to a P2P protocol revision, and denotes the same version whether it is advertised in a legacy version message or in an init record. The association of protocol versions with network upgrades, and the procedure by which future network upgrades are assigned protocol versions, are specified in ZIP 204 25 and are not duplicated here.

At the time of writing, the current protocol version — advertised by nodes implementing the legacy protocol — is 170160 (PROTOCOL_VERSION). The protocol version from which the protocol specified by this ZIP is deployed has not yet been assigned; it will be the protocol version that ZIP 204’s assignment procedure 25 assigns to the network upgrade that deploys this protocol (see Deployment). That version is the minimum peer protocol version of this protocol: every node implementing this ZIP necessarily advertises at least that version, and version-gated features of the legacy protocol (such as MSG_WTX relay and addrv2 support) are unconditionally in effect.

Network Upgrade Epoch Enforcement

Each network upgrade defines a minimum protocol version. When a network upgrade activates (as defined in ZIP 200 3), a node MUST disconnect any peer whose negotiated protocol version is less than the protocol version associated with the current epoch, using the OBSOLETE error code.

The protocol versions associated with network upgrades on each network are tabulated in ZIP 204 26.

In the preference window before an upgrade’s activation, a node SHOULD preferentially connect to peers advertising the upcoming epoch’s protocol version, as specified in ZIP 201 27.

Request Stream Types

This section defines each request stream type. For each type, the request and response formats and their semantics are specified. All requests and responses follow the lifecycle of Request Streams.

get-headers

Stream type: 0x01

Request:

Size Field Description
varies locator_count Number of block locator hashes (CompactSize).
varies locator_hashes Block locator hashes (each 32 bytes), from highest to lowest height.
32 hash_stop Hash of the last desired header, or all zeros to request as many as possible.
1 tx_ids Whether each returned header should be accompanied by the block’s coinbase transaction and transaction IDs (uint8; 0 or 1).

Response:

Size Field Description
varies count Number of entries (CompactSize).
varies entries count entries, each encoded as follows.

Each entry:

Size Field Description
varies header The block header, encoded as a CompactSize length prefix followed by the serialized header 12 (including the Equihash solution).
1 has_txs Present only if the request had tx_ids = 1. 0x01: the coinbase transaction and transaction IDs follow. 0x00: nothing further follows for this entry.
varies coinbase Present only if has_txs is 0x01: the block’s coinbase transaction, encoded as a CompactSize length prefix followed by the serialized transaction 11.
varies ids_count Present only if has_txs is 0x01: the number of transaction IDs (CompactSize).
varies ids Present only if has_txs is 0x01: the full transaction IDs (64 bytes each; see Full Transaction IDs) of the block’s transactions other than the coinbase transaction, in block order.

Requests block headers starting after the first block locator hash found in the responder’s best chain, up to and including hash_stop or 160 headers (MAX_HEADERS_RESULTS), whichever comes first. The locator_count MUST NOT exceed 101, and tx_ids MUST be 0 or 1.

If none of the locator hashes are found in the responder’s best chain, headers start at height 1 (the block after the genesis block). If locator_count is 0, the response consists solely of the entry for the header whose hash is hash_stop, if the responder has it. A response with count = 0 is valid and indicates that the responder has no headers to return.

If tx_ids is 1, each entry additionally identifies the block’s transactions: the coinbase transaction in full (it is never otherwise relayed), and every other transaction by its full transaction ID, letting the requester fetch only the transactions it is missing via get-tx (see Relay Protocol). A responder that cannot supply a returned header’s transactions sets has_txs to 0x00 for that entry, and the requester falls back to get-blocks.

A node performing bulk synchronization SHOULD set tx_ids to 0: the transaction-ID form pays off only near the chain tip, where the requester already holds most of a block’s transactions. Because the ids array is not individually length-prefixed, the record payload limit of Records does not bound it, and a tx_ids = 1 request over large historical blocks would otherwise compel a response of hundreds of megabytes from a request of a few dozen bytes. Both directions are therefore bounded:

The response count MUST NOT exceed 160, and MUST NOT exceed the number of headers the request could have selected. The headers MUST form a contiguous chain (each header’s hashPrevBlock must match the hash of the preceding header). A node receiving a response with more than 160 headers, or with non-contiguous headers, SHOULD discard the response and assign a misbehavior penalty (see Misbehavior and Banning); this is the handling of such responses, in place of the connection error that Request Streams would otherwise prescribe.

The legacy always-zero transaction count that followed each header in headers messages is removed.

get-blocks

Stream type: 0x02

Request:

Size Field Description
varies count Number of block hashes (CompactSize).
varies hashes count block hashes (each 32 bytes).

Response: For each requested hash, in request order:

Size Field Description
1 result 0x00: a full block follows. 0x02: not found (nothing follows for this entry).
varies object If result is 0x00: a CompactSize length prefix followed by the serialized block (see Serialized Blocks).

Requests full blocks by hash; the hashes to request are learned from headers, announcements, or get-hashes responses. The count MUST NOT exceed 128. Compact blocks cannot be requested (see Compact Block Relay).

Each delivered block MUST be the block that was requested: the header of the block delivered for an entry MUST hash to that entry’s requested hash. A delivered block that does not is a connection error of type PROTOCOL_ERROR — the check is by hashing alone, so a violation is never attributable to a different chain view. Without this rule a peer could answer every entry with some other valid block, choosing which of two competing blocks the requester sees while remaining syntactically conformant.

A count of 128 bounds the number of blocks but not their size, and blocks differ in size by three orders of magnitude across the chain’s history. A responder SHOULD therefore also apply a byte budget, and MAY finish its sending direction after any complete entry, having answered fewer entries than were requested; the requester re-requests the remainder from any peer. For bulk synchronization, get-block-range — which is byte-bounded by construction — is the appropriate primitive (see 13).

get-tx

Stream type: 0x03

Request:

Size Field Description
varies count Number of transaction references (CompactSize).
varies txrefs count transaction references (see Transaction References). All three reference types are permitted.

Response: For each requested reference, in request order:

Size Field Description
1 result 0x00: a transaction follows. 0x02: not found (nothing follows for this entry).
varies tx If result is 0x00: a CompactSize length prefix followed by the serialized transaction 11.

Requests transactions by reference. The count MUST NOT exceed 50,000.

Each delivered transaction MUST be the transaction that was requested: it MUST match the corresponding entry’s reference, under the identifier that reference names (txid, wtxid, or the short transaction ID relative to the named block). A delivered transaction that does not is a connection error of type PROTOCOL_ERROR.

As with get-blocks, the count bound does not bound the response in bytes: 50,000 references occupy a request of a few megabytes but could name transactions totalling far more, an amplification the count limit alone does not address. A responder SHOULD apply a byte budget to the response, and MAY finish its sending direction after any complete entry; the requester re-requests what it still needs. A requester SHOULD size a request to the bytes it is prepared to receive rather than to the count limit.

SHORTID references identify a transaction within a block relayed by a compact block; their handling is specified in Requesting Missing Transactions.

get-addr

Stream type: 0x04

Request: Empty (the stream is finished immediately after the stream type byte).

Response:

Size Field Description
varies count Number of address records (CompactSize).
varies addresses count network address records (see Network Address Record).

Requests peer addresses from the remote node. The response count MUST NOT exceed 1000; a node receiving a larger response SHOULD discard the response and assign a misbehavior penalty (see Misbehavior and Banning); this is the handling of such responses, in place of the connection error that Request Streams would otherwise prescribe.

Each returned record carries the address’s last-known service flags in its services field (see Service Flags and Network Address Record). A responder SHOULD set that field to the services it last observed the address advertise in an init record, or the services the record was relayed to it with if it has never connected to the address. The requester MAY use the field to select candidate peers offering the services it needs (for example, NODE_SYNC_ARTIFACTS or NODE_TREE_ROOTS), but relayed service flags are an unauthenticated claim: the peer’s actual services are established only by the services field of its own init record upon connecting, and a node MUST NOT treat an address book entry’s flags as more than a selection hint.

To impede address-based fingerprinting attacks, a node SHOULD send get-addr only on outbound connections, at most once per connection, and SHOULD only answer get-addr requests on inbound connections (resetting its sending direction of the stream with REFUSED otherwise).

get-mempool

Stream type: 0x05

Request: Empty (the stream is finished immediately after the stream type byte).

Response: Open-ended. The response is a sequence of records (see Records), each a single transaction reference (see Transaction References) of type TXID or WTXID; each field of a reference is fixed in size for its type. SHORTID references MUST NOT be used.

Subscribes to the contents of the peer’s transaction memory pool. The responder first sends a record for each transaction currently in its mempool (the snapshot; no snapshot records indicate an empty mempool). It then keeps its sending direction open, and sends further records referencing transactions as they are accepted into its mempool, until the stream or connection ends. The requester fetches transactions of interest with get-tx.

This is the one stream type whose response is open-ended (see Request Streams): the responder finishing its sending direction ends the subscription (for example, on shutdown), and a requester that no longer wants the subscription cancels the responder’s sending direction with CANCELLED. A node MUST NOT open more than one concurrent get-mempool stream to the same peer; a second concurrent subscription is a connection error of type PROTOCOL_ERROR.

Records after the snapshot SHOULD be subject to the same trickling delay as transaction announcements (see Trickling). The responder MAY omit references it has already sent to the same peer — in the snapshot, in an earlier record, or on a transaction announcement stream — and the requester MUST tolerate duplicate references.

Serving the snapshot costs the responder a full serialization of its mempool for a request of one byte, so a node SHOULD rate-limit get-mempool per peer — a peer that repeatedly cancels and re-opens the stream is requesting that work again each time — and SHOULD decline the request from a peer for which it has set relay = 0 in its init record, since such a peer has declined transaction relay in the other direction. A node concerned about topology inference SHOULD apply the trickling delay of Trickling to the post-snapshot records, as above; without it the subscription reports each transaction’s arrival at the responder without delay, which is exactly what trickling exists to obscure.

get-hashes

Stream type: 0x06

Request:

Size Field Description
4 start_height Height of the first requested block hash (uint32, little-endian).
4 count Maximum number of block hashes requested (uint32, little-endian).

Response:

Size Field Description
varies count Number of entries (CompactSize).
varies entries count entries, each encoded as follows.

Entry k, for k from 0 to count − 1, describes the block at height start_height + k of the responder’s best chain:

Size Field Description
32 hash Hash of the block at height start_height + k.
varies size The block’s serialized size in bytes (see Serialized Blocks) (CompactSize).
varies txs Number of transactions in the block, including the coinbase transaction (CompactSize).
varies txouts Number of transparent outputs created by the block’s transactions, including coinbase outputs (CompactSize).
varies notes Number of note commitments added by the block: two per JoinSplit description, plus one per Sapling Output description, plus one per Orchard-protocol Action description (in the Orchard or, from NU6.3, the Ironwood pool) (CompactSize).

Requests the hashes of count consecutive blocks of the responder’s best chain starting at start_height, together with per-block sync-cost metadata. A requester that needs coarser granularity (for example, the spacing of its checkpoints; see 13) aggregates the per-block values itself.

The size, txs, txouts, and notes fields are scheduling hints: they let a synchronizing node estimate, before downloading, the download volume and the validation and note-commitment-tree work in each range of the chain, and so divide download work across peers and interleave transfer with computation to reach the tip fastest (see 13). Cumulative txouts values additionally locate each block’s bits within the spentness-hint bitmap of 13. They are deterministic functions of the block — for a given hash, every honest responder returns identical values. A node MUST NOT rely on them for any consensus or validity purpose.

The requested count MUST NOT exceed 50,000, and the greatest requested height (start_height + count − 1) MUST NOT exceed 0xFFFFFFFF.

The response count MUST NOT exceed the requested count, MAY be less, and MAY be 0: a node omits requested heights above its chain tip, and SHOULD NOT include entries for blocks within 100 blocks of its chain tip (which could still be affected by a chain reorganization; the same margin as the reorganization rule of Checkpointed Synchronization). Truncation MUST be a prefix: if fewer entries are returned than were requested, the returned entries MUST correspond to the lowest requested heights, so that the requester can re-request the missing tail.

Responses are not self-authenticating: a node MUST NOT rely on the returned hashes for any security-relevant purpose until they are verified, per the untrusted-inputs rule of Checkpointed Synchronization. The metadata, by contrast, is checkable after the fact: once a node has downloaded an entry’s block, it SHOULD verify any hints it relied upon, and SHOULD assign a misbehavior penalty (see Misbehavior and Banning) if txs or notes do not match the downloaded block. Those two fields are determined by the block hash alone, so a mismatch is not attributable to a different chain view.

size is not. A block’s serialized size depends on its authorizing data — notably scriptSig lengths — which the txid, and hence the block hash, does not commit to (see get-block-range and ZIP 244 28). A node that obtained the hints from one peer and the blocks from another therefore MUST NOT penalize a size mismatch unless it has verified the delivered blocks' authorizing data commitment; otherwise a peer that pads the blocks it serves would cause the penalty to fall on the honest peer that served the hints, which is an eviction primitive rather than a defense. A size mismatch on unverified blocks is instead grounds to re-fetch the blocks.

get-block-range

Stream type: 0x07

Request:

Size Field Description
32 final_hash Hash of the highest block in the requested range.
4 count Maximum number of blocks requested (uint32, little-endian). MUST NOT exceed 65,536.
4 max_bytes Maximum total serialized size of the delivered blocks, in bytes (uint32, little-endian). MUST NOT exceed 67,108,864 (64 MiB).

Response:

Size Field Description
1 result 0x00: blocks follow. 0x02: not found (the responder does not have the block with hash final_hash; nothing follows).
varies blocks A sequence of serialized blocks, each encoded as a CompactSize length prefix followed by the serialized block (see Serialized Blocks), in descending height order: the first block is the block whose hash is final_hash, and each subsequent block is the parent of the previous one.

get-block-range is the bulk block download primitive: where get-blocks requests individual blocks by hash, get-block-range streams a contiguous chain of blocks identified by a single anchor — typically a hash the requester has verified against a trusted commitment, a validated header, or an already-verified block (see 13).

The responder streams blocks until it has delivered count blocks, or until delivering the next block would bring the total serialized size of the delivered blocks above max_bytes, whichever comes first — except that it MUST be willing to deliver the first block regardless of max_bytes, so that a maximum-size block is always retrievable. A response that exceeds either bound — more than count blocks, or any block after the first whose delivery brings the total serialized size above max_bytes — is a connection error of type FLOOD; both bounds are exact, so an honest responder never exceeds them. Because block sizes vary by three orders of magnitude across the chain’s history, max_bytes — not count — is what bounds the work a stream commits one peer to; scheduling recommendations are given in 13.

Delivery is in descending height order so that every block is verifiable on arrival: the first delivered block’s header MUST hash to final_hash, each subsequent delivered block’s header MUST hash to the hashPrevBlock of the previously delivered block, and each delivered block’s transactions MUST match its header’s transaction merkle root, and each delivered block’s serialized size MUST NOT exceed the maximum block size permitted by the consensus rules. A delivered block that violates these rules is a connection error of type PROTOCOL_ERROR — the rules are checkable by hashing alone, so a violation is never attributable to a different chain view. A requester with a trusted anchor therefore needs no download handles for the range’s interior blocks and can assign blame exactly.

These checks bind each delivered block to the committed chain, but they do not bind all of its bytes. The transaction merkle root commits to txids, and from NU5 a v5 transaction’s txid excludes its authorizing datascriptSigs, proofs, and signatures — which is committed separately by hashAuthDataRoot within the header’s hashBlockCommitments (see ZIP 244 28). Recovering the authorizing data commitment from the header requires the chain history root at the parent block, which arrival-time checking does not have, so this check is necessarily deferred to validation time. A responder can consequently deliver blocks that satisfy every rule above while carrying arbitrary authorizing data, padded up to the element size limit.

A requester therefore MUST NOT store a delivered block, serve it to another peer, or use it to regenerate a synchronization artifact until its authorizing data commitment has been checked against its header. A block whose authorizing data commitment fails that check SHOULD incur a misbehavior penalty of 100 points against the peer that delivered it: the delivered block matches the committed chain by txid, so the discrepancy is attributable to that peer and not to a different chain view. A requester SHOULD also bound the bytes it accepts on a stream by max_bytes, independently of the block count, since a block may be padded well beyond the size its hints predicted.

The responder serves the ancestor chain of the block with hash final_hash, whether or not that block is in its best chain. It MAY also finish its sending direction early, before either request bound is reached (for example, to bound the resources committed to one stream). Truncation for any reason is resumable, since the requester knows the next expected hash (the hashPrevBlock of the last delivered block) and MAY re-request the remainder — from the same or a different peer — using it as final_hash; a stream truncated by max_bytes thereby self-chunks a long span into byte-bounded, individually verified units even when the requester has no per-block size information. A requester that no longer wants the remainder cancels the responder’s sending direction with CANCELLED; delivered blocks remain verified and usable. Transport flow control bounds the buffering on both sides.

get-tree-roots

Stream type: 0x08

Request:

Size Field Description
4 start_height Height of the first requested entry (uint32, little-endian).
32 final_hash Hash of the block at the highest requested height, start_height + count − 1 (see below).
4 count Maximum number of entries requested (uint32, little-endian). MUST NOT exceed 4,000.

Response:

Size Field Description
varies count Number of entries (CompactSize).
varies entries count entries, each encoded as follows.

Entry k, for k from 0 to count − 1, describes the block at height start_height + k of the chain identified by final_hash — the block with hash final_hash and its ancestors:

Size Field Description
32 sapling_root Root of the Sapling note commitment tree after this block.
32 orchard_root Root of the Orchard note commitment tree after this block.
32 ironwood_root Root of the Ironwood note commitment tree after this block.
varies sapling_txs Number of transactions in this block with Sapling components, as counted by the block’s chain history tree leaf (CompactSize; see ZIP 221 29).
varies orchard_txs Number of transactions in this block with Orchard-pool components (CompactSize).
varies ironwood_txs Number of transactions in this block with Ironwood-pool components (CompactSize).
32 auth_data_root The block’s authorizing data commitment hashAuthDataRoot (see ZIP 244 28).

For a pool that is not active at the entry’s height, the corresponding root field is 32 zero bytes and the corresponding count is 0.

Requests the per-block data from which a synchronizing node can reproduce each block’s note commitment tree roots and chain history tree without recomputing the trees from the blocks' note commitments — the dominant CPU cost of validating historical blocks under checkpoint-based synchronization.

The final_hash field anchors the request to a specific chain: the requester sets it to the hash — taken from headers it has already authenticated (see 13) — of the block at the highest requested height, and the requested entries describe that block and its ancestors, which final_hash identifies uniquely. A responder whose best chain does not contain the block with hash final_hash at height start_height + count − 1 MUST refuse the stream with REFUSED rather than serve entries for different blocks. An honest peer with a divergent chain view therefore refuses rather than serving mismatching entries, so an entry that fails verification is never attributable to a different chain view (cf. the tolerance rule of Checkpointed Synchronization, which this anchoring makes unnecessary here).

The entries are not self-authenticating: a node MUST NOT rely on any part of an entry for any purpose until it has verified that part against the chain’s header commitments, as specified in 13. Which commitment applies depends on the height, and changes at Heartwood rather than at NU5: between Sapling and Heartwood activation the header field hashFinalSaplingRoot is the block’s Sapling root and is compared directly; from Heartwood activation ZIP 221 29 repurposes that field to carry the chain history root (hashChainHistoryRoot, and from NU5 hashBlockCommitments, which binds it together with the authorizing data commitment of ZIP 244 28), and entries are verified by reconstructing the chain history tree.

Not every field of an entry is authenticated at every height. The chain history tree leaf defines no Ironwood field, so no header commits to an ironwood_root; and reconstruction verifies an entry only once the following block’s header has been checked against it, so the highest entry of a response is not yet verified when the response completes. A node MUST NOT rely on an unverified root, and recomputes the corresponding tree instead.

An entry that fails verification against an authenticated header SHOULD incur a misbehavior penalty (see Misbehavior and Banning). A node MUST NOT assign that penalty on the basis of a check that does not apply at the entry’s height: in particular, comparing a supplied Sapling root against hashFinalSaplingRoot above Heartwood activation compares it against an unrelated commitment and would penalize every honest responder.

Serving get-tree-roots requires a per-block root index that not every node maintains (in particular, a node that itself synchronized without computing historical trees may not have one). A node that maintains the index advertises the NODE_TREE_ROOTS service flag (see Service Flags). The response MAY contain fewer entries than requested, and MAY be empty; truncation MUST be a prefix (the returned entries MUST correspond to the lowest requested heights). A node without the index refuses the stream with REFUSED. The response count MUST NOT exceed the requested count.

get-object

Stream type: 0x09

Request:

Size Field Description
32 hash SHA-256 hash of the requested object.
8 offset Byte offset into the object at which to start (uint64, little-endian). MUST NOT exceed 248 − 1.
4 length Maximum number of bytes requested (uint32, little-endian). MUST NOT exceed 33,554,432 (32 MiB).

Response:

Size Field Description
1 result 0x00: the object is available; its size and data follow. 0x02: not found (nothing follows).
varies size Total size of the object in bytes (CompactSize).
varies data The object’s bytes from offset, up to length bytes or the end of the object, whichever comes first.

Requests a byte range of a synchronization artifact: an immutable, content-addressed object identified by the SHA-256 hash of its contents. The protocol does not interpret artifact contents; artifacts are named by trusted commitments and synchronization metadata (for example, known-hash chunk files and state snapshot pieces; see 13), and a node serves whichever artifacts it holds or can reproduce deterministically from its chain state. A node that serves artifacts advertises the NODE_SYNC_ARTIFACTS service flag (see Service Flags).

The responder MAY finish its sending direction early, having delivered fewer than the requested bytes; the requester detects the shortfall from size and MAY re-request the remainder from any peer, since object bytes are position-addressed and identical everywhere. An object is verified by hashing its complete contents; piece sizing conventions that keep each piece independently fetchable and verifiable are specified in 13.

The number of bytes returned is determined as follows, and a responder MUST evaluate the two cases in this order:

  1. If offset is greater than or equal to the object’s size, the response is result = 0x00, the object’s size, and no data.
  2. Otherwise the responder returns at most min(length, size − offset) bytes, starting at offset.

Evaluating the bound as min(offset + length, size) is not equivalent: the sum can wrap, yielding an end position below offset and a byte count that underflows to an enormous value. offset is bounded above for the same reason, and a responder MUST reject a request whose offset exceeds that bound as a connection error of type PROTOCOL_ERROR.

A response carrying more than length bytes of data, or data extending beyond the end of the object as given by its own size field, is a connection error of type FLOOD.

The same discipline applies to the requester. A response’s size is peer-supplied and unauthenticated until the assembled object is hashed: a requester MUST NOT compute a remaining length as size − received without first checking that size is at least received, and a peer reporting a size inconsistent with a previous response for the same hash, or below the bytes it has already delivered, is a connection error of type PROTOCOL_ERROR.

Object data is verified only by hashing the complete object, so misdelivery is attributable only when a single peer delivered every byte: a node that has fetched all of an object’s bytes from one peer, and finds that they do not hash to the requested hash, SHOULD assign that peer a misbehavior penalty (see Misbehavior and Banning), so that a peer answering with well-formed garbage is deselected rather than reassigned the same work indefinitely. A node that assembled an object from ranges delivered by more than one peer MUST NOT assign a misbehavior penalty when the assembled object fails verification — any of the contributing peers could be at fault — and SHOULD instead discard the data and re-fetch the object with each candidate peer serving the whole object, which isolates a misbehaving peer. Disagreement between peers about an object’s size is resolved by the hash check, not penalized.

Announcement Stream Types

Block Announcements

Stream type: 0x10

Each record payload on a block announcement stream has the following format:

Size Field Description
1 kind 0x00: header announcement. 0x01: compact block announcement.
varies payload For kind = 0x00: a serialized block header 12. For kind = 0x01: a compact block (see Compact Block Encoding).

Blocks are announced immediately; they are not subject to the trickling delay applied to transactions.

A node announces a new block to a given peer in one of two ways:

  1. If the peer requested high-bandwidth compact block announcements (the announce field of its init record was 1), by sending a compact block announcement (kind = 0x01).
  2. Otherwise, by sending header announcements (kind = 0x00) for the new block and for any intermediate blocks the peer has not yet been sent, up to a small implementation-defined limit. This follows the design of BIP 130 30.

A node MUST NOT send compact block announcements to a peer whose init record had announce = 0. Header announcements MAY be sent to any peer, regardless of its announce preference; in particular, if a block’s compact block encoding would exceed the maximum record payload length (see Records), the sender announces that block with a header announcement instead.

A node SHOULD NOT send a header announcement for a block it has not fully validated, except as the oversize substitute above for a compact block announcement to a peer that requested high-bandwidth announcements. Correspondingly, the pre-validation penalty exemption of Relay Protocol applies to announcements of either kind received on a connection on which the receiving node requested high-bandwidth announcements (its init record had announce = 1); a header announcement received where the receiver’s announce was 0 carries no such exemption, and an announced block that fails full validation is subject to the rules of Misbehavior and Banning.

An announcing node SHOULD only announce a block if it expects the peer to be able to connect the announced header to the peer’s known chain (for example, because the parent block was previously announced to or by that peer). A node receiving an announcement whose header does not connect to its known chain SHOULD recover by sending a get-headers request, and MUST NOT assign a misbehavior penalty solely because a header does not connect.

Transaction Announcements

Stream type: 0x11

Each record payload on a transaction announcement stream is a single transaction reference (see Transaction References) of type TXID or WTXID. SHORTID references MUST NOT be used in announcements.

A node MUST NOT open a transaction announcement stream to a peer whose init record had relay = 0.

Announcements are subject to the trickling delay of Trickling. Interested peers fetch announced transactions with get-tx requests.

Address Announcements

Stream type: 0x12

Each record payload on an address announcement stream is a single network address record (see Network Address Record).

Address announcements are subject to the rate limits and address book management practices of Address Relay.

Block Relay

Divided Block Relay

This protocol divides block headers and block transactions, rather than transferring monolithic block messages as the legacy protocol did:

Both mechanisms are unconditional; the per-connection preferences are carried in the announce and full_ids fields of the init record (see Init Record).

Compact Block Relay

Compact block relay is adapted from BIP 152 31. This section specifies the protocol flows and the Zcash-specific differences from BIP 152; BIP 152 provides the design rationale.

The differences from BIP 152 are:

Compact Block Encoding

A compact block relays a block as its header plus a compact representation of its transactions:

Size Field Description
varies header The block header, encoded as a CompactSize length prefix followed by the serialized header 12 (including the Equihash solution).
8 nonce Nonce for short transaction ID computation (uint64, little-endian). If ids_kind is 1, this field SHOULD be 0 and MUST be ignored.
1 ids_kind 0 if ids contains short transaction IDs; 1 if it contains full transaction IDs (uint8).
varies ids_count Number of transaction IDs (CompactSize).
varies ids If ids_kind is 0: short transaction IDs, 6 bytes each, little-endian (see Short Transaction IDs). If ids_kind is 1: full transaction IDs, 64 bytes each (see Full Transaction IDs).
varies prefilled_count Number of prefilled transactions (CompactSize).
varies prefilled_txns Prefilled transactions (see below).

Each transaction in the block, in block order, is represented either by a transaction ID in ids (short or full, according to ids_kind) or by a full serialized transaction in prefilled_txns; the total number of transactions in the block is ids_count + prefilled_count. Each of ids_count and prefilled_count MUST NOT exceed 65,536, and their sum MUST NOT exceed 65,536; a compact block violating any of these is a connection error of type FLOOD. Bounding the two counts individually is required as well as bounding the sum: each is an independent CompactSize that may be as large as 264 − 1 on the wire, and a sum of two such values can wrap to a small number that satisfies a bound the addends do not.

A node MUST set ids_kind to 1 in compact blocks sent to a peer that set full_ids = 1 in its init record, and SHOULD set ids_kind to 0 otherwise. A compact block with an ids_kind value other than 0 or 1 is a nonconforming record (see Announcement Streams).

Each entry of prefilled_txns has the following format:

Size Field Description
varies index Differentially encoded index of this transaction within the block (CompactSize; see below).
varies tx A full serialized transaction, encoded as a CompactSize length prefix followed by the serialized transaction 11.

Prefilled transaction indexes are differentially encoded as in BIP 152 31. A compact block in which any absolute index is greater than or equal to ids_count + prefilled_count, or in which indexes overflow or are not strictly increasing, is a nonconforming record (see Announcement Streams). The bound is the block’s own transaction count, not the protocol maximum: an index below 65,536 can still lie outside a small block, and a reconstructor sized to the block’s actual transaction count would then write out of bounds.

The coinbase transaction MUST be prefilled. A sender SHOULD additionally prefill any transaction that it predicts the receiver does not have.

Short Transaction IDs

The short transaction ID of a transaction, relative to a given compact block, is computed as specified in BIP 152 31, with two substitutions:

  1. The input to SipHash-2–4 is the transaction identifier used for relay of the transaction: for a transaction with version ≥ 5, its 64-byte wtxid as defined in ZIP 239 23 (the txid followed by the auth_digest); for a transaction with version ≤ 4, its 32-byte txid.
  2. The header hashed together with the nonce field to derive the SipHash keys is the serialized Zcash block header 12 (as it appears in the header field, without the length prefix).

The nonce SHOULD be chosen uniformly at random by the sender of a compact block, so that short ID collisions between blocks and senders are independent.

Full Transaction IDs

The full transaction ID of a transaction is the 64-byte value consisting of the transaction’s txid followed by its auth_digest. For a transaction with version ≥ 5, this is the wtxid used for relay, as defined in ZIP 239 23. For a transaction with version ≤ 4, the auth_digest is the placeholder value consisting of 32 bytes of 0xFF defined in ZIP 244 28.

Matching against full transaction IDs is exact — they are not subject to collisions and involve no nonce. A node unable to do short-ID matching (for example, a pruned node) requests full transaction IDs by setting full_ids = 1 in its init record, at the cost of larger compact blocks.

Requesting Missing Transactions

Transactions that could not be matched while reconstructing a block from a compact block are requested with get-tx requests, rather than with dedicated messages as in BIP 152 (getblocktxn and blocktxn, which identify transactions by index within the block):

A node that has sent a peer a compact block, or a get-headers entry carrying transaction IDs, MUST be able, for a reasonable implementation-defined time thereafter, to serve that block’s transactions to the same peer via get-tx — including by SHORTID references in the compact block case, and irrespective of whether those transactions remain in its mempool.

Relay Protocol

A node follows the BIP 152 31 protocol flows:

Upon receiving a compact block — or the equivalent header, coinbase transaction, and transaction IDs from a get-headers response with tx_ids = 1 — a node attempts to reconstruct the block by matching each transaction ID against the transactions it already holds (in its mempool or otherwise) — computing short transaction IDs for the held transactions if the IDs are short (ids_kind = 0), or comparing full transaction IDs directly otherwise:

An incorrectly reconstructed block fails validation of the block’s merkle root and authorizing data commitment, so short ID matching does not weaken consensus enforcement.

Compact Block Relay Example (non-normative)

A new block reaches peer B (high-bandwidth mode) and peer C (low-bandwidth mode) from peer A:

A ── block announcements ────▶ B : compact block (header, short
                                   IDs, prefilled coinbase)
     B matches the short IDs against transactions it
     holds; two are unknown
B ── get-tx ─────────────────▶ A : [SHORTID, SHORTID]
A ── response ───────────────▶ B : the two transactions
     B reconstructs the block and validates it

A ── block announcements ────▶ C : header announcement
C ── get-headers ────────────▶ A : locator, hash_stop = block
                                   hash, tx_ids = 1
A ── response ───────────────▶ C : header, coinbase, full
                                   transaction IDs
     C matches the IDs, fetches missing transactions
     via get-tx, reconstructs, and validates

Synchronization

Headers-First Synchronization

Nodes synchronize the block chain using a headers-first approach; it is the baseline synchronization method of this protocol, which every node supports. It is the full-validation method: it assumes no trusted data beyond the consensus rules and the genesis block. (An alternative method for nodes with trusted checkpoint data is specified in Checkpointed Synchronization; recommended concrete synchronization strategies are specified in 13.)

  1. The synchronizing node sends a get-headers request with a block locator (typically with tx_ids = 0; see get-headers).
  2. The remote peer responds with up to 160 headers.
  3. The synchronizing node validates the headers and requests full blocks via get-blocks requests.
  4. Steps 1–3 repeat until the node is synchronized with the network.

The following rules apply to headers-first synchronization:

Checkpointed Synchronization

A node MAY synchronize spans of the historical chain against trusted commitments: checkpoint data — bindings of block heights to block hashes — obtained through a channel the node already trusts, such as its own binary or local configuration. The bulk primitives of this protocol — get-hashes, get-block-range (whose descending delivery authenticates every block against a committed anchor on arrival), get-tree-roots, and get-object — exist to serve such procedures; a recommended concrete strategy is specified in 13. Any checkpoint-based synchronization procedure MUST obey the following rules.

The commitment scheme itself — checkpoint spacing, any chunking, and the hash or signature scheme binding it — is local to the node and out of scope for this protocol.

Block Download Parameters

For headers-first synchronization and near-tip block fetching via get-blocks, the block download parameters of ZIP 204 32 — the download window, the per-peer in-transit limit, and the stalling timeout — apply unchanged. On a stall, the node MAY re-request the block from an alternative peer, cancelling the original request stream with CANCELLED.

Bulk transfer via get-block-range and get-object is not governed by those parameters: in-flight volume is bounded in bytes by transport flow control and by the synchronizing node’s scheduling policy, for which 13 gives recommendations (work unit sizing, per-peer budgets, and stall detection).

Transaction Relay

Announcement-Based Relay

Transaction relay follows an announcement-based protocol:

  1. A node with a new transaction sends a transaction reference on its transaction announcement stream to each peer (subject to trickling).
  2. Peers that want the transaction request it with a get-tx request.
  3. The originating node serves the transaction in the get-tx response.

Announcements use TXID and WTXID references according to transaction version, as specified in Transaction References.

Trickling

To impede network topology inference, transaction announcements SHOULD NOT be sent immediately but SHOULD instead be “trickled” at random intervals. The specific parameters are implementation-defined; for the reference values used by legacy implementations, see ZIP 204 33.

Transaction Expiry

The avoidance of relaying transactions that are about to expire (TX_EXPIRING_SOON_THRESHOLD) is unchanged from the legacy protocol; see ZIP 204 33.

Mempool Policy

Orphan transaction handling and the minimum relay fee are local mempool policy, unchanged from the legacy protocol; see ZIP 204 33.

Address Relay

Address relay allows nodes to discover peers by propagating network address records through the network, via address announcement streams and get-addr requests.

Rate Limiting

Address records SHOULD be subject to rate limiting to prevent address flooding. The specific mechanism is implementation-defined; for the token-bucket reference values used by zcashd, see ZIP 204 34.

A node MAY additionally apply backpressure on the peer’s address announcement stream to bound the rate at which it receives address records.

Address Book Management

Relayed addresses are unauthenticated, attacker-suppliable data; an address book that one source can fill leads to eclipse 22. A node SHOULD segment its address book by IP range, in the manner of the Bitcoin Core address manager design:

A node SHOULD also diversify its outbound connections across groups: for reference, Bitcoin Core and zcashd make at most one outbound connection per group. Outbound peer selection SHOULD NOT be biased toward the addresses most recently received, since those are the easiest for an attacker to have planted.

Addresses of overlay networks (TORV3, I2P) have no IP-range structure and are free to generate, so bucketing cannot bound an attacker’s share of them. A node SHOULD treat each overlay network as a separate bounded segment of its address book, and a node supporting both IP-based and overlay transports SHOULD retain a minimum number of outbound connections on IP-based transports.

Address Broadcasting

The specific broadcast intervals are implementation-defined; for the reference values used by zcashd, see ZIP 204 34.

How a node determines its own externally reachable addresses is out of scope for this ZIP; the legacy addr_recv-based self-discovery is removed (see Init Record).

Misbehavior and Banning

Peer misbehavior is handled by two mechanisms, split by severity. Unambiguous violations of “MUST”-level requirements — malformed records, invalid stream usage, exceeded hard limits — are connection errors: the node closes the connection immediately with the indicated error code (PROTOCOL_ERROR or FLOOD unless otherwise specified), with no score kept. Violations for which immediate disconnection would be disproportionate, or which an honest-but-buggy peer or a peer on a different chain could commit, instead accumulate a per-peer misbehavior score that leads to disconnection and banning only when it crosses a threshold.

Every misbehavior penalty — including any implementation-defined penalty assigned under the final row of the table below — is constrained by the following principle: a node MUST NOT assign a misbehavior penalty unless the violation is provable from the received data together with data the node has independently authenticated, does not depend on the node’s own chain view, mempool contents, clock, or local policy, and could not be committed by a conformant peer acting on honest but divergent state. Behavior that fails this test — however suspicious — is instead handled by discarding the data, refusing or cancelling streams, rate limits and backpressure, or disconnection without a ban. Penalties assigned on weaker evidence are worse than none: they let an attacker who can arrange the appearance of misbehavior induce honest nodes to ban one another, narrowing each victim’s peer set toward the attacker’s own nodes.

The misbehavior score works as follows:

Points Violation
20 get-headers response with more than 160 headers.
20 Non-contiguous headers in a get-headers response.
20 get-addr response with more than 1000 address records.
20 get-hashes metadata (txs or notes) that does not match the downloaded block (see get-hashes).
100 A get-tree-roots entry that fails verification against authenticated header commitments (see get-tree-roots).
100 A block delivered by get-block-range whose authorizing data commitment fails verification against its header (see get-block-range).
100 A get-object object, delivered entirely by the peer, that fails verification against the requested content hash (see get-object).
100 Announcing a v5 transaction with a TXID reference, or a v4-or-earlier transaction with a WTXID reference, in an announcement stream record or a get-mempool response record (see Transaction References and ZIP 239 23). Wrong-typed references in get-tx requests are exempt.
varies Transaction, block, or header validation failure that is provable under the principle above: an invalid signature or proof, malformed structure, invalid proof of work, or a commitment mismatch, established with full context. The penalty is determined by the severity of the validation error, subject to the exemptions below.

Validation-failure penalties apply only where the invalidity proves that this peer misbehaved. In particular, a node MUST NOT assign a misbehavior penalty for:

Score thresholds and banning work as follows:

Bans are keyed by network address, and are only as strong as the cost of acquiring a new address. Banning is a meaningful deterrent for IP-based transports; for overlay networks whose addresses are free to generate (such as Tor onion services), it excludes only the banned address, and inbound connection limits — not ban lists — bound the node’s exposure (see Address Book Management).

Address granularity cuts both ways even on IP transports. A single IPv6 operator typically controls at least a /64, so per-address IPv6 bans are nearly free to evade; a node MAY key IPv6 bans and scores by prefix (a /64 is a reasonable default). Conversely, a single IPv4 address behind carrier-grade NAT may be shared by many independent users, so a ban can exclude bystanders — a reason to keep ban durations bounded rather than to widen their scope.

Security and Privacy Considerations

Scope of transport encryption. Transport encryption protects the confidentiality and integrity of each connection against passive network observers. Peers are deliberately not authenticated (see Certificates), so an active attacker in a position to intercept a connection can terminate the encryption with its own certificate and read or modify that connection’s traffic. The structural defense against an active attacker seeking to control a node’s view of the network is redundancy: connections to multiple, independently discovered peers.

Denial of service. QUIC’s address validation and amplification limits, including Retry packets, bound the cost that spoofed-source floods can impose (see QUIC Transport). At the application layer, resource consumption is bounded by the record payload limit (see Records), the stream concurrency limits (see Transport Requirements), the per-request count limits of each request stream type, and address rate limiting (see Address Relay); exceeding a hard size or rate limit is a connection error of type FLOOD.

A count limit is not by itself a bound on work, because the per-item size varies by orders of magnitude: get-block-range and get-object are therefore bounded in bytes as well (max_bytes and length), and for get-blocks, get-tx, and get-headers the responder applies its own byte budget and may finish early, with the requester re-requesting the remainder. The general shape of the defense is that a responder is never obliged to spend unbounded bandwidth, disk reads, or memory on one cheap request, and that every peer-supplied count or length is checked against its bound before it is used to size an allocation (see Data Types and Encoding).

Fingerprinting and linkability. A persistent TLS key would allow a node to be recognized across connections and network locations; a node concerned about this uses per-connection ephemeral keys (see Certificates). Connection migration links a node’s traffic across network paths (see QUIC Transport). The user_agent field of the init record is a fingerprinting vector; a node MAY advertise a generic user agent string. The get-addr policy (see get-addr) impedes fingerprinting of a node’s address book, and trickling (see Trickling) impedes inference of the network topology and of the origin of a transaction from announcement timing.

Traffic analysis. QUIC encrypts payloads but conceals neither packet timing and volume nor the IP addresses of the communicating peers. In particular, an adversary observing a node’s traffic may still be able to identify it as the originator of a transaction. The Tor transport (see Tor Transport) defends against this adversary when it is local — an observer of the node’s network, or the remote peer itself, learns neither the node’s IP address nor which peers it communicates with — but not against a global adversary correlating traffic timing across the Tor network. The anticipated Nym mixnet transport (see Deployment) is directed at that stronger adversary.

Length leakage. Encryption conceals the content of application data but not its length: neither TLS over TCP nor QUIC pads application data by default, so encrypted packet sizes leak plaintext lengths. (QUIC defines PADDING frames 6, but an implementation must use them explicitly.) The Tor transport is coarser — Tor pads relay cells to a 509-byte body — so leakage there is granular rather than byte-exact. To remove the sharpest instance of this channel, every request format in this protocol whose fields are position-fixed (get-hashes, get-block-range, get-tree-roots, get-object, and the empty get-addr and get-mempool requests) uses fixed-length integer encodings: every request of a given type has a single wire length, smaller than a Tor cell body, so its encrypted size reveals at most the stream type and never the field values, and there is no way to misimplement the encoding so that it leaks lengths but still interoperates. Requests that carry variable-length lists (get-headers, get-blocks, get-tx) and responses necessarily reveal their gross size; a node MAY additionally use QUIC PADDING frames to obscure those lengths, at a bandwidth cost this specification does not mandate.

Tor. On a Tor transport connection the initiator is anonymous — it reveals no network address to the responder or to network observers — so a node SHOULD prefer the Tor transport (where available) for the traffic most sensitive to linkage, above all announcing transactions it originated. The responder’s onion address is a stable, linkable identifier, and onion addresses are free to generate, which weakens address-based banning and address book protections for TORV3 addresses (see Misbehavior and Banning and Address Book Management).

Mixnets. The two kinds of traffic most sensitive to linkage are mempool transaction propagation — announcing a transaction links the announcing node to it, and a get-mempool subscription reveals interest in the mempool’s contents — and address book queries, whose get-addr request/response pattern is a fingerprinting and mapping primitive. A node implementer SHOULD carry both over a mixnet transport once one is available (the anticipated Nym transport; see Deployment), and in the interim SHOULD carry them over the Tor transport where available, per the preference above. Mixnet delay and cover traffic address the timing correlation that onion routing alone does not.

Synchronization. Headers-first synchronization trusts proof of work: forging a history requires outspending the honest chain’s accumulated work over the forged span, which an eclipsing attacker with sufficient hash power can in principle do. Checkpointed synchronization trusts the node’s commitment, which shares a trust base with its binary: below its checkpoints, an attacker who cannot break the block hash function cannot cause acceptance of a false history. Under either method, an attacker controlling a node’s connections can withhold data and stall synchronization; the mitigations are redundancy and re-requesting from alternative peers (see Block Download Parameters).

Eclipse attacks. An attacker that controls all of a node’s connections controls its view of the block chain and mempool. The structural defenses are segmentation of the address book by IP range and diversity in outbound peer selection (see Address Book Management and 22), persisted addresses (see Peer Discovery), and rate-limited, validated address relay (see Address Relay). Overlay addresses such as onion services deserve particular caution here, since an attacker can generate them without limit (see Address Book Management).

Deployment

This ZIP replaces the legacy protocol without a compatibility bridge: nodes implementing this protocol do not interoperate with nodes implementing the legacy protocol. Deployment is coordinated through the network upgrade mechanism 3: the protocol version from which this protocol is in effect will be assigned to a network upgrade, and the epoch enforcement of ZIP 201 27 retires legacy-protocol peers at activation, as with any other network upgrade.

Because there is no compatibility bridge, an implementation deploying this protocol is expected to also implement the legacy protocol during the transition: before activation, a node participates in the network over the legacy protocol while also listening on the QUIC transport, so that DNS seeders and upgraded peers can discover and exercise QUIC endpoints ahead of activation. The QUIC transport uses UDP where the legacy transport uses TCP, so both can be served concurrently on the same port number and address. At activation, epoch enforcement retires legacy-protocol connections, and the node continues on this protocol alone.

DNS seeders are expected to probe and serve the QUIC endpoints of nodes implementing this ZIP.

The protocol semantics are specified against the abstract stream layer of Stream Layer; QUIC and Tor are the transports defined by this ZIP. Future revisions are expected to define additional transports to be used simultaneously with them; in particular, a transport carrying the stream layer over the Nym mixnet 36 is anticipated, providing network-level metadata protection (resistance to traffic analysis of which peers are communicating) stronger than onion routing provides.

Note: The transports are complementary rather than alternatives: a node is expected to use QUIC for latency-critical bulk relay and synchronization, and Tor (eventually Nym) where anonymity matters more than latency, selected per connection according to the traffic it will carry.

Formal Model

A TLA+ formal specification of the legacy protocol’s connection lifecycle is available at 37; it has not yet been updated for the protocol specified here.

References


  1. Information on BCP 14 — “RFC 2119: Key words for use in RFCs to Indicate Requirement Levels” and “RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words”  ↩︎

  2. Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 3.12: Mainnet and Testnet  ↩︎

  3. ZIP 200: Network Upgrade Mechanism  ↩︎

  4. Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 3.3: The Block Chain  ↩︎

  5. ZIP 204: Zcash P2P Network Protocol  ↩︎

  6. RFC 9000: QUIC: A UDP-Based Multiplexed and Secure Transport  ↩︎

  7. Tor Rendezvous Specification — Version 3 Onion Services  ↩︎

  8. RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3  ↩︎

  9. RFC 9001: Using TLS to Secure QUIC  ↩︎

  10. BIP 324: Version 2 P2P Encrypted Transport Protocol  ↩︎

  11. Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 7.1: Transaction Encoding and Consensus  ↩︎

  12. Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 7.6: Block Header Encoding and Consensus  ↩︎

  13. Draft ZIP: Block Chain Synchronization  ↩︎

  14. ZIP 155: addrv2 message  ↩︎

  15. ZIP 204: Zcash P2P Network Protocol — DNS Seeds  ↩︎

  16. RFC 9221: An Unreliable Datagram Extension to QUIC  ↩︎

  17. RFC 7301: Transport Layer Security (TLS) Application-Layer Protocol Negotiation Extension  ↩︎

  18. ZIP 204: Zcash P2P Network Protocol — Default Ports  ↩︎

  19. RFC 7250: Using Raw Public Keys in Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS)  ↩︎

  20. The Tor Project  ↩︎

  21. ZIP 204: Zcash P2P Network Protocol — Data Types and Encoding  ↩︎

  22. Ethan Heilman, Alison Kendler, Aviv Zohar, Sharon Goldberg. Eclipse Attacks on Bitcoin’s Peer-to-Peer Network. 24th USENIX Security Symposium, 2015.  ↩︎

  23. ZIP 239: Relay of Version 5 Transactions  ↩︎

  24. bit \(k\) is the bit with numeric weight \(2^k\kern-0.05em\textsf{.}\)  ↩︎

  25. ZIP 204: Zcash P2P Network Protocol — Assigning Protocol Versions to Network Upgrades  ↩︎

  26. ZIP 204: Zcash P2P Network Protocol — Network Upgrade Epoch Enforcement  ↩︎

  27. ZIP 201: Network Peer Management for Overwinter  ↩︎

  28. ZIP 244: Transaction Identifier Non-Malleability  ↩︎

  29. ZIP 221: FlyClient - Consensus-Layer Changes  ↩︎

  30. BIP 130: sendheaders message  ↩︎

  31. BIP 152: Compact Block Relay  ↩︎

  32. ZIP 204: Zcash P2P Network Protocol — Block Download Parameters  ↩︎

  33. ZIP 204: Zcash P2P Network Protocol — Transaction Relay  ↩︎

  34. ZIP 204: Zcash P2P Network Protocol — Address Relay  ↩︎

  35. ZIP 204: Zcash P2P Network Protocol — Misbehavior and Banning  ↩︎

  36. The Nym Mixnet  ↩︎

  37. Zcash P2P Protocol TLA+ Specification  ↩︎