draft-arya-jvff-p2p-quic-transport
Version 2 Zcash P2P Network Protocol
- Draft
Contents
Block Relay
Divided Block Relay
This protocol divides block headers and block transactions, rather than
transferring monolithic block messages as the legacy protocol did:
- Block headers are gossiped directly on block announcement streams, as in BIP 130 30.
- Block transactions are relayed separately from headers using compact block
relay (adapted from BIP 152 31): a block is transferred as its
header plus identifiers of its transactions, and only the transactions the
receiver is missing are requested and transferred, using
get-txrequests.
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:
- There is no
sendcmpctmessage. Compact block relay is unconditional, and the high-bandwidth announcement preference is carried in theannouncefield of theinitrecord (see Init Record). (A future change to the compact block encoding would be deployed under a new protocol version.) - A compact block cannot be requested: BIP 152's
getdatawithMSG_CMPCT_BLOCKhas no equivalent, and compact blocks occur only as high-bandwidth announcements. A peer announced to in low-bandwidth mode instead requests the block's coinbase transaction and transaction IDs viaget-headerswithtx_ids = 1(see Relay Protocol). - A compact block can identify the block's transactions either by short
transaction IDs or by full transaction IDs, at the receiver's option (the
full_idsfield of theinitrecord; see Full Transaction IDs). BIP 152 supports only short transaction IDs. - There are no
getblocktxnandblocktxnmessages. Missing transactions are requested withget-txrequests (usingSHORTID,TXID, orWTXIDreferences) and delivered in the corresponding responses; see Requesting Missing Transactions. - The
headerfield of a compact block uses the Zcash block header encoding 12, including the Equihash solution, rather than the 80-byte Bitcoin header. - The input to the short transaction ID computation is based on the wtxid as defined in ZIP 239 23 for version 5 transactions, rather than on either the txid (BIP 152 version 1) or the BIP 141 wtxid (BIP 152 version 2); see Short Transaction IDs.
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
2^64 − 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:
- 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. - The header hashed together with the
noncefield to derive the SipHash keys is the serialized Zcash block header 12 (as it appears in theheaderfield, 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):
- If the compact block had
ids_kind = 0, each unmatched short transaction ID is requested as aSHORTIDtransaction reference containing the block hash and the short transaction ID copied from the compact block. Because short transaction IDs depend on thenonceof the compact block they appeared in, the responding node interprets them using thenonceof the compact block it most recently sent to the requesting peer for the identified block. A node receiving aSHORTIDreference for a block for which it has not recently sent that peer a compact block, or whose short transaction ID matches no transaction — or more than one transaction — in the identified block, SHOULD answer that reference with a not-found result. - If the compact block had
ids_kind = 1— and likewise for each unmatched full transaction ID from aget-headersresponse withtx_ids = 1(seeget-headers) — each unmatched full transaction ID is requested as an ordinary transaction reference: aTXIDreference containing the txid if theauth_digesthalf of the full transaction ID is the 32-byte0xFFplaceholder (indicating a pre-v5 transaction), or aWTXIDreference containing the full transaction ID otherwise.
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:
- High-bandwidth mode (the receiver's
initrecord hadannounce = 1): the sender announces a new block by sending a compact block announcement directly. To minimize propagation latency, the sender MAY do so as soon as it has validated the block header (checking proof of work and difficulty), before fully validating the block. Because a high-bandwidth announcement may legitimately precede full validation by the announcing peer, a node MUST NOT assign a misbehavior penalty for a high-bandwidth announcement — a compact block announcement, or a header announcement substituted for one (see Block Announcements) — of a block that fails full validation, provided the block's header is valid (including its proof of work); the same exemption covers the block's transactions served under Requesting Missing Transactions. A node SHOULD request high-bandwidth announcements from at most 3 peers, preferring the peers that most recently announced blocks to it first; changing the selection requires a new connection (see Init Record). - Low-bandwidth mode (the receiver's
initrecord hadannounce = 0): blocks are announced to the receiver with header announcements (see Block Announcements). The receiver MAY then request the block's coinbase transaction and transaction IDs — aget-headersrequest withtx_ids = 1whosehash_stopis the announced block hash — and reconstruct the block, fetching the transactions it is missing withget-tx; or it MAY request the full block viaget-blocks. The receiver SHOULD use the transaction-ID form only for blocks close to its chain tip (BIP 152 31 recommends within 5 blocks), and request full blocks otherwise.
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:
- If all transactions are available, the node reconstructs and validates the block as if it had been received as a full block.
- If one or more transactions are missing, or (for short transaction IDs) if
two or more held transactions collide on the same short ID, the node
requests the unresolved transactions with a
get-txrequest (see Requesting Missing Transactions), and completes reconstruction as the response arrives. - If any requested transaction is answered with a not-found result, or if
reconstruction fails after the responses arrive (for example, because a
short ID collision caused a wrong match and the reconstructed block is
invalid), the node SHOULD fall back to requesting the full block via
get-blocks, and MUST NOT assign a misbehavior penalty solely because reconstruction failed.
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.)
- The synchronizing node sends a
get-headersrequest with a block locator (typically withtx_ids = 0; seeget-headers). - The remote peer responds with up to 160 headers.
- The synchronizing node validates the headers and requests full blocks via
get-blocksrequests. - Steps 1–3 repeat until the node is synchronized with the network.
The following rules apply to headers-first synchronization:
- A node MUST validate each received header — including its proof of work and its difficulty adjustment — before requesting the corresponding block and before extending its header chain with it.
- Received headers, even with valid proof of work, establish only that work was expended: a node MUST NOT treat a block as part of its best chain until the block itself has been validated under the consensus rules. Until then, headers serve to select and schedule block downloads.
- A node MUST NOT assign a misbehavior penalty solely because a peer's headers do not connect to its known chain or reflect a different best chain (see Block Announcements); penalties apply to provably invalid headers and blocks, per Misbehavior and Banning.
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.
- Validated advancement. A node MUST NOT advance its validated chain except through blocks that it has either validated under the consensus rules, or authenticated by an unbroken hash chain terminating in a checkpoint bound by a trusted commitment. The extent to which consensus checks may be abbreviated for checkpoint-authenticated blocks is a matter of local policy.
- Untrusted inputs. Peer-supplied data — hashes, headers, blocks — MUST NOT be relied upon for any security-relevant purpose until verified under the previous rule; until then it MAY be used only to select and schedule downloads. Data received from peers MUST NOT be incorporated into a node's trusted commitments.
- Tolerance of divergent views. A node MUST NOT assign a misbehavior
penalty to a peer solely because the peer's
get-hashesresponses fail verification against the node's commitments, or reflect a different best chain; such responses are discarded and MAY be retried with other peers. - Reorganization margin. Commitment-based authentication MUST NOT be
applied within 100 blocks of the node's view of the network chain tip; the
chain near the tip is synchronized headers-first. The responder-side
margin of
get-hashesis the same depth, measured the same way, so that a responder's hints and a requester's authentication agree about which blocks are near enough to the tip to be unsettled. - Headers-first fallback. A node MUST be capable of completing
synchronization using headers-first synchronization alone, and MUST fall
back to it where its commitments end or where too few peers serve
get-hashes.
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:
- A node with a new transaction sends a transaction reference on its transaction announcement stream to each peer (subject to trickling).
- Peers that want the transaction request it with a
get-txrequest. - The originating node serves the transaction in the
get-txresponse.
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:
- Define an address's group as an IP-range prefix of its address — for reference, Bitcoin Core and zcashd use the /16 prefix for IPv4 and the /32 prefix for IPv6 — and, for a relayed address, its source group as the group of the peer that relayed it.
- Partition the address book into a bounded number of bounded-size buckets, and assign each address to a bucket determined by its group and its source group (keyed with a node-local secret), so that addresses from any one source group can occupy only a bounded fraction of the buckets. When a bucket is full, evict within the bucket; do not let insertions grow the book or displace addresses in other buckets.
- Keep addresses learned from independently trusted mechanisms (DNS seeding, local configuration) distinguishable from relayed addresses, and do not let relayed addresses evict them.
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:
- A node SHOULD maintain a misbehavior score for each peer. Scores SHOULD be keyed by network address and persist across connections for a period on the order of the ban duration: several penalties in this protocol are detected only after a delay (sync-cost metadata is checked once the corresponding block has been downloaded; tree-root entries verify one block behind), so a purely per-connection score would let a peer shed its score — or escape a deferred penalty entirely — by reconnecting. A penalty detected after the offending connection has closed SHOULD be applied to the address's score.
- When the node detects one of the violations listed below, it adds the listed number of points to the peer's score:
| 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:
- Expiry. A transaction that is expired, or expires too soon, relative to the receiver's chain tip: relay and trickling delays make expiry races possible between honest peers (see Transaction Expiry).
- Local policy. Rejection under local mempool policy — fee requirements, orphan handling, conflicts with mempool contents, or resource limits (see Mempool Policy).
- Missing or moved state. A transaction whose inputs are unknown to the receiver or already spent in its current view, or any object the receiver lacks the context to validate.
- Content of requested objects. Consensus invalidity of a block,
transaction, or artifact that the node itself requested by hash or
reference (
get-blocks,get-block-range,get-tx,get-object): the responder served exactly the bytes the identifier names, and blame for the object's existence lies with the peer that announced it, under the rules above. (Delivered data that does not match the requested identifier is covered by each stream type's own rules.) - Block reconstruction service. Transactions served in fulfilment of the serving obligation of Requesting Missing Transactions (see the exemption in Relay Protocol).
Score thresholds and banning work as follows:
- When a peer's score reaches or exceeds the node's ban threshold, the node
SHOULD close the connection with the
MISBEHAVIORerror code and ban the peer's network address. - While an address is banned, the node SHOULD NOT initiate connections to it,
SHOULD refuse inbound connections from it, and SHOULD NOT include it in
get-addrresponses or address announcements. - The ban threshold and ban duration are implementation-defined; for the reference values used by legacy implementations, see ZIP 204 35.
- Whitelisted peers accumulate misbehavior scores but are exempt from banning and from the threshold-triggered disconnection.
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
Footnotes
-
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" ↩
-
Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 3.12: Mainnet and Testnet ↩
-
Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 3.3: The Block Chain ↩
-
RFC 9000: QUIC: A UDP-Based Multiplexed and Secure Transport ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Tor Rendezvous Specification — Version 3 Onion Services ↩ ↩2 ↩3
-
RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3 ↩ ↩2
-
Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 7.1: Transaction Encoding and Consensus ↩ ↩2 ↩3 ↩4 ↩5
-
Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 7.6: Block Header Encoding and Consensus ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9
-
Draft ZIP: Block Chain Synchronization ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 ↩12 ↩13 ↩14 ↩15 ↩16 ↩17
-
RFC 7301: Transport Layer Security (TLS) Application-Layer Protocol Negotiation Extension ↩
-
RFC 7250: Using Raw Public Keys in Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS) ↩
-
ZIP 204: Zcash P2P Network Protocol — Data Types and Encoding ↩
-
Ethan Heilman, Alison Kendler, Aviv Zohar, Sharon Goldberg. Eclipse Attacks on Bitcoin's Peer-to-Peer Network. 24th USENIX Security Symposium, 2015. ↩ ↩2 ↩3
-
bit is the bit with numeric weight . ↩
-
ZIP 204: Zcash P2P Network Protocol — Assigning Protocol Versions to Network Upgrades ↩ ↩2
-
ZIP 204: Zcash P2P Network Protocol — Network Upgrade Epoch Enforcement ↩
-
ZIP 244: Transaction Identifier Non-Malleability ↩ ↩2 ↩3 ↩4 ↩5
-
ZIP 204: Zcash P2P Network Protocol — Block Download Parameters ↩
-
ZIP 204: Zcash P2P Network Protocol — Transaction Relay ↩ ↩2 ↩3
-
ZIP 204: Zcash P2P Network Protocol — Misbehavior and Banning ↩