Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ Released ZIPs
<tr> <td>252</td> <td class="left"><a href="zips/zip-0252.rst">Deployment of the NU5 Network Upgrade</a></td> <td>Final</td>
<tr> <td>253</td> <td class="left"><a href="zips/zip-0253.md">Deployment of the NU6 Network Upgrade</a></td> <td>Final</td>
<tr> <td>255</td> <td class="left"><a href="zips/zip-0255.md">Deployment of the NU6.1 Network Upgrade</a></td> <td>Proposed</td>
<tr> <td>256</td> <td class="left"><a href="zips/zip-0256.md">Deployment of Consensus Bug Fixes Between NU6.1 and NU6.2</a></td> <td>Proposed</td>
<tr> <td>271</td> <td class="left"><a href="zips/zip-0271.md">Dev Fund Extension and One-Time Disbursement</a></td> <td>Proposed</td>
<tr> <td>300</td> <td class="left"><a href="zips/zip-0300.rst">Cross-chain Atomic Transactions</a></td> <td>Proposed</td>
<tr> <td>301</td> <td class="left"><a href="zips/zip-0301.rst">Zcash Stratum Protocol</a></td> <td>Active</td>
Expand Down Expand Up @@ -333,6 +334,7 @@ Index of ZIPs
<tr> <td>253</td> <td class="left"><a href="zips/zip-0253.md">Deployment of the NU6 Network Upgrade</a></td> <td>Final</td>
<tr> <td><strike>254</strike></td> <td class="left"><strike><a href="zips/zip-0254.md">Deployment of the NU7 Network Upgrade (Withdrawn)</a></strike></td> <td>Withdrawn</td>
<tr> <td>255</td> <td class="left"><a href="zips/zip-0255.md">Deployment of the NU6.1 Network Upgrade</a></td> <td>Proposed</td>
<tr> <td>256</td> <td class="left"><a href="zips/zip-0256.md">Deployment of Consensus Bug Fixes Between NU6.1 and NU6.2</a></td> <td>Proposed</td>
<tr> <td><span class="reserved">260</span></td> <td class="left"><a class="reserved" href="zips/zip-0260.md">Extending Block Messages with Additional Authentication Data</a></td> <td>Reserved</td>
<tr> <td><span class="reserved">270</span></td> <td class="left"><a class="reserved" href="zips/zip-0270.md">Key Rotation for Tracked Signing Keys</a></td> <td>Reserved</td>
<tr> <td>271</td> <td class="left"><a href="zips/zip-0271.md">Dev Fund Extension and One-Time Disbursement</a></td> <td>Proposed</td>
Expand Down
332 changes: 332 additions & 0 deletions zips/zip-0256.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,332 @@

ZIP: 256
Title: Deployment of Consensus Bug Fixes Between NU6.1 and NU6.2
Owners: Daira-Emma Hopwood <daira@jacaranda.org>
Status: Proposed
Category: Consensus / Network
Created: 2026-06-09
License: MIT
Discussions-To: <https://github.com/zcash/zips/issues/1294>


# Terminology

The key words "MUST" and "MUST NOT" in this document are to be interpreted as
described in BCP 14 [^BCP14] when, and only when, they appear in all capitals.

The term "network upgrade" in this document is to be interpreted as described
in ZIP 200. [^zip-0200]

The character § is used when referring to sections of the Zcash Protocol
Specification. [^protocol]

The terms "Mainnet" and "Testnet" are to be interpreted as described in
§ 3.12 ‘Mainnet and Testnet’. [^protocol-networks]


# Abstract

This ZIP retrospectively documents the deployment and specification of
several consensus and denial-of-service bug fixes that were applied to the
Zcash node implementations, after the activation of NU6.1 [^zip-0255], up to
but not including the Orchard Temporary Vulnerability Mitigation and the
NU6.2 network upgrade. The latter are documented in ZIP 257. [^zip-0257].


# Motivation

Between the activation of NU6.1 and that of NU6.2, several consensus enforcement
bugs and denial-of-service vulnerabilities were reported and mitigated in the
zcashd and zebra full node implementations. This period corresponded to a sudden
spike in vulnerability reports, precipitated by the widespread use of capable AI
models for vulnerability discovery and mitigation.

These vulnerabilities were fixed and deployed in zcashd releases v6.12.0 to
v6.12.4 inclusive, and in zebra releases v4.2.0 to v4.5.1 inclusive.

This ZIP records the nature of those fixes, the releases in which they were
deployed, and corresponding specification changes and clarifications, so that
the behaviour of the network across this period is fully documented.


# Specification

## Consensus rule change

This is the one fix in this ZIP that changes a consensus rule. It was deployed
as a constricting consensus rule change ("soft fork"); the corresponding
specification change is given under
[Zcash Protocol Specification changes](#zcashprotocolspecificationchanges) below.

- **Orchard identity $\mathtt{rk}$.** An Orchard action with $\mathtt{rk}$
encoding the zero point on the Pallas curve caused a panic during proof
verification. Both zcashd and zebra now reject $\mathtt{rk}$ encoding
$\mathcal{O}_{\mathbb{P}}$. This is a constricting consensus rule change,
since the specification previously permitted $\mathtt{rk}$ to be the zero
point. It is enforced by zcashd from v6.12.1, by zebra from v4.3.1, and
reflected in the `orchard` crate from version 0.13.0. [^ZODL-v6.12.1-fixes]

## Consensus node implementation conformance fixes

Each of the following makes an implementation enforce a rule the protocol
specification already defines, restoring agreement of zcashd and zebra to
the specification and to each other.

No consensus rule change is introduced for these implementation flaws.
Clarifying specification notes relating to two of these flaws are given under
[Zcash Protocol Specification changes](#zcashprotocolspecificationchanges) below.

- **Sprout transaction verification.** A flaw in zcashd's verification of
Sprout proofs on block connection could have allowed invalid Sprout
transactions to be accepted. Fixed in zcashd v6.12.0 [^zcashd-v6.12.0-PR].
[^ZODL-v6.12.0-fixes]

- **Orchard invalid $\mathtt{epk}$.** An Orchard action with $\mathtt{epk}$
encoding the zero point —or any other 32-byte string that does not encode
a valid Pallas curve point— was accepted by zcashd but rejected by zebra as
required by § 4.6 [^protocol-actiondesc], a potential consensus split. zcashd
now requires $\mathtt{epk}$ to decode to a valid non-zero Pallas point.
This was partially fixed in zcashd v6.12.1 (rejecting the zero Pallas
point) [^ZODL-v6.12.1-fixes]; the fix was extended to all invalid encodings in
v6.12.2 [^zcashd-v6.12.2-PR].

- **Sapling v4 $\mathtt{valueBalanceSapling}$ normalization.** The v4
transaction deserializer accepted a transaction with no Spend descriptions and
no Output descriptions, but $\mathtt{valueBalanceSapling}$ encoding a non-zero
value. The value was normalized to zero during deserialization, so the
consensus check that should have rejected it ran against the normalized zero
and never fired. zebra rejected the encoding at deserialization as required by
§ 7.1.2 [^protocol-txnconsensus], so the malformed encoding would have caused
a consensus split. zcashd now rejects it at deserialization, matching zebra.
Fixed in zcashd v6.12.2 [^zcashd-v6.12.2-PR].

- **NU5-onward block-body poisoning.** In NU5 and later, the authorizing data of
v5 transactions is committed by the header's $\mathtt{hashBlockCommitments}$
(via $\mathtt{hashAuthDataRoot}$), not by $\mathtt{hashMerkleRoot}$. A
body-derived rejection firing before the auth-commitment check could
permanently mark an honest header invalid. zcashd now pre-checks
$\mathtt{hashBlockCommitments}$ before any body-mutable check on the
active-tip path, and treats the sidechain case as body-replaceable. This
closes the class of vulnerabilities, including the `bad-blk-sigops`,
`bad-cb-length`, and `bad-blk-length` trigger paths. Initial fix in zcashd
v6.12.2 [^zcashd-v6.12.2-PR]; structurally closed in v6.12.4 [^zcashd-v6.12.4-PR].
[^GHSA-ghc3-g8w4-whf9]

- **Chain value-pool accounting.** A duplicate block header could silently
reset chain value pool balance-tracking fields, disabling ZIP 209 [^zip-0209]
turnstile enforcement. A variation on the attack could persist corrupted
per-block chain value pool deltas. zcashd v6.12.1 onward defers pool-value
initialization until after the duplicate-data check, adds a chain-supply
checkpoint at NU6.1 activation covering all value pools, recomputes shielded
chain value pool deltas (since the checkpoint) from block data on startup,
and tightens the turnstile and lockbox checks [^zcashd-v6.12.1-PR]. [^ZODL-v6.12.1-fixes]

- **P2SH input sigop count.** zebra's P2SH input signature-operation count did
not match zcashd; this was corrected to restore agreement. Fixed in zebra
v4.5.1 [^Zebra-v4.5.1-changelog].

- **Input ordering.** zebra did not preserve the order of inputs in
`spent_outputs` for transactions spending a mix of chain and mempool outputs,
and so an input could be matched against the wrong spent output during
validation. Fixed in zebra v4.2.0 [^Zebra-v4.2.0-changelog].

- **Other zebra validity fixes.** zebra accepted coinbase transactions
containing Sapling spends; did not validate transparent input/output alignment
before script verification; and could treat a v5 transaction as verified on
the basis of its mined transaction id alone. These were corrected in zebra
v4.4.0 [^Zebra-v4.4.0-changelog].

## Denial-of-service and node-crash hardening

The following issues allowed remotely triggered crashes or unbounded resource use.
They affect node liveness but do not change which blocks or transactions are valid.

### Zcashd

- **Coinbase shielded value-balance crash.** A coinbase transaction with a
positive Sapling or Orchard value balance would desynchronize chain-supply
accounting from pool balances in `ConnectBlock`, triggering a node abort and a
crash loop on restart. Fixed in zcashd v6.12.4. [^GHSA-ghc3-g8w4-whf9]

- **Out-of-range pool-value delta.** A block whose aggregate per-pool value
delta was out of monetary range left the sending peer unbanned, and re-wrote
the body to disk on each replay. Fixed in zcashd v6.12.4. [^GHSA-ghc3-g8w4-whf9]

- **Use-after-free in `ConnectBlock`.** CVE-2024-52911, a use-after-free in the
script verification path inherited from Bitcoin Core [^CVE-2024-52911]. Fixed
in zcashd v6.12.3.

- **Integer-overflow and value-range hardening.** Chain value pool balance
accumulation was hardened against undefined behaviour from signed integer
overflow. Value range exceptions are now caught as consensus rejections.
Fixed in zcashd v6.12.1. [^ZODL-v6.12.1-fixes]

### Zebra

- **Unbounded preallocation and duplicate-delivery DoS.** The fix for this
issue added caps on `Vec::with_capacity` reservations driven by peer-supplied
counts; added caps on `block::Hash` and `CountedHeader` allocation; and removed
rejected block hashes from `SentHashes` so that honest re-deliveries are not
short-circuited. Fixed in zebra v4.5.0. [^Zebra-v4.5.0-changelog]

## Zcash Protocol Specification changes

One fix changes a consensus rule: excluding $\mathsf{rk} = \mathcal{O}_{\mathbb{P}}$
in Orchard Action descriptions, deployed as a constricting consensus rule change
(§ 4.6 below).

The remaining validity fixes make implementations enforce rules that the
specification already defined. Two of those rules are clarified by normative
notes below.

The denial-of-service hardening changes have no specification effect.

### § 7.1.2 ‘Transaction Consensus Rules’

In reference to this rule:

> If $\mathsf{effectiveVersion} = 4$ and there are no Spend descriptions or
> Output descriptions, then $\mathtt{valueBalanceSapling}$ MUST be $0$.

add the note:

> zcashd prior to v6.12.1 misimplemented the rule that requires
> $\mathtt{valueBalanceSapling} = 0$ for a v4 transaction with no Spend
> descriptions and no Output descriptions. It incorrectly normalized the
> value to $0$ when no Spend or Output descriptions were present, with the
> consequence that the failure case for this rule was never reached. For
> clarification, $\mathtt{valueBalanceSapling}$ refers to the value as
> encoded in the transaction, which is not normalized in this way; not to
> $\mathsf{v^{balanceSapling}}$.

### § 4.6 ‘Action Descriptions’

**Exclude the zero point for $\mathsf{rk}$.** In the list of consensus rules, add:

> $\mathsf{rk}$ MUST NOT be $\mathcal{O}_{\mathbb{P}}$.

Correspondingly, replace the non-normative note

> $\mathsf{cv}$ and $\mathsf{rk}$ can be the zero point $\mathcal{O}_{\mathbb{P}}$.
> $\mathsf{epk}$ cannot be $\mathcal{O}_{\mathbb{P}}$.

with

> $\mathsf{cv}$ can be the zero point $\mathcal{O}_{\mathbb{P}}$. $\mathsf{rk}$
> and $\mathsf{epk}$ cannot be $\mathcal{O}_{\mathbb{P}}$.

This is a soft fork — it only removes transactions from the valid set, and is
safe to deploy even if nodes upgrade at different times. It was chosen in
response to a panic that an $\mathtt{rk}$ encoding the zero point caused during
proof verification, in both zcashd and zebra. $\mathsf{rk} = \mathcal{O}_{\mathbb{P}}$
would have been harmless had it been handled correctly, but excluding it was the
simpler and safer response under the circumstances. It is enforced by zcashd
from v6.12.1 and by zebra from v4.3.1.

**Clarification ($\mathtt{ephemeralKey}$ encoding).** The existing consensus
rule requires each Action description field to be a canonical encoding of its
declared type. For $\mathsf{epk}$ encoded as $\mathtt{ephemeralKey}$, the
declared type is $\mathsf{KA^{Orchard}.Public}$, i.e. $\mathbb{P}^{*}$ (the
non-zero Pallas points). Add the note:

> A valid encoding of $\mathbb{P}^{*}$ for $\mathsf{epk}$ excludes all of:
> the zero encoding; any $32$-byte string whose $x$-coordinate is non-canonical
> (numerically $\geq q_{\mathbb{P}}$, the Pallas base field modulus); and any
> canonical $x$-coordinate for which no Pallas curve point exists. An Action
> description with $\mathtt{ephemeralKey}$ falling into any of these categories
> is invalid. (See § 5.4.5.5 for the definition of $\mathsf{KA^{Orchard}.Public}$.)

### § 7.5 ‘Action Description Encoding and Consensus’

§ 7.5 specifies the encoding of an Action description and defers its other
consensus rules to § 4.6. The restrictions above are expressed in § 4.6 in terms
of curve points. To help implementors that validate during deserialization, add
a non-normative note in this section observing that $\mathcal{O}_{\mathbb{P}}$ is
encoded as the all-zeros $32$-byte string, which the § 4.6 rules exclude for both
$\mathtt{rk}$ and $\mathtt{ephemeralKey}$.

## Deployment

These fixes were enforced immediately on upgrade in each release below; none
was gated on a block height or network-upgrade activation.

| Release | Date | Fixes |
| -------------- | ---------- | ----- |
| zcashd v6.12.0 | 2026-03-27 | Sprout transaction verification |
| zcashd v6.12.1 | 2026-04-17 | Orchard zero $\mathtt{rk}$ / $\mathtt{ephemeralKey}$; chain value-pool accounting; integer-overflow / value-range hardening |
| zcashd v6.12.2 | 2026-05-06 | Sapling `valueBalanceSapling` normalization; strengthened $\mathtt{ephemeralKey}$ check; block-body poisoning (initial) |
| zcashd v6.12.3 | 2026-05-07 | CVE-2024-52911 use-after-free |
| zcashd v6.12.4 | 2026-06-01 | block-body poisoning (structural); coinbase value-balance crash; out-of-range pool-value delta |
| zebra v4.2.0 | 2026-03-12 | input-ordering validity fix |
| zebra v4.3.1 | 2026-04-17 | Orchard zero $\mathtt{rk}$ / $\mathtt{ephemeralKey}$ |
| zebra v4.4.0 | 2026-05-01 | coinbase Sapling spend; transparent input/output alignment; v5-verification |
| zebra v4.5.0 | 2026-05-28 | preallocation / duplicate-delivery DoS |
| zebra v4.5.1 | 2026-05-29 | P2SH input sigop count |

## Backward compatibility

Most of these fixes tighten validity checks or harden the node, rejecting inputs
that were already invalid under the specification but were previously accepted
(or mishandled) by one or both full node implementations. Honest blocks and
transactions remain valid, so upgraded nodes do not diverge from each other.
Where one implementation previously accepted a malformed encoding, the fix
restores agreement with the other (for example the Sapling
$\mathtt{valueBalanceSapling}$ and Orchard $\mathtt{ephemeralKey}$ cases, where
the specification already required the relevant encodings to be rejected).

The exclusion of Orchard $\mathtt{rk}$ = $\mathcal{O}_{\mathbb{P}}$ is a soft
fork: it removes transactions from the valid set rather than adding any, so it
cannot cause an upgraded node to reject a block that conforming miners produce.
$\mathtt{rk}$ is the spend authorization validating key randomized by $\alpha$.
When, as specified, $\alpha$ is generated uniformly at random,
$\mathtt{rk}$ = $\mathcal{O}_{\mathbb{P}}$ only with negligible probability —
so in practice no honest transaction is affected.

The denial-of-service hardening changes do not affect block or transaction
validity.

# References

[^BCP14]: [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"](https://www.rfc-editor.org/info/bcp14)

[^protocol]: [Zcash Protocol Specification, Version 2025.6.3 or later](protocol/protocol.pdf)

[^protocol-networks]: [Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 3.12: Mainnet and Testnet](protocol/protocol.pdf#networks)

[^protocol-txnconsensus]: [Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 7.1.2: Transaction Consensus Rules](protocol/protocol.pdf#txnconsensus)

[^protocol-actiondesc]: [Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 4.6: Action Descriptions](protocol/protocol.pdf#actiondesc)

[^zip-0200]: [ZIP 200: Network Upgrade Mechanism](zip-0200.rst)

[^zip-0201]: [ZIP 201: Network Peer Management for Overwinter](zip-0201.rst)

[^zip-0209]: [ZIP 209: Prohibit Negative Shielded Chain Value Pool Balances](zip-0209.rst)

[^zip-0255]: [ZIP 255: Deployment of the NU6.1 Network Upgrade](zip-0255.md)

[^zip-0257]: [ZIP 257: Deployment of the Orchard Temporary Vulnerability Mitigation and NU6.2 Network Upgrade](zip-0257.md)

[^GHSA-ghc3-g8w4-whf9]: [Urgent Zcash hotfixes v6.12.4 & v6.20.0 (zcashd, affected v5.0.0–v6.13.0)](https://github.com/zcash/zcash/security/advisories/GHSA-ghc3-g8w4-whf9)

[^zcashd-v6.12.0-PR]: [zcashd v6.12.0 post-release merge (zcash/zcash#7133)](https://github.com/zcash/zcash/pull/7133)

[^zcashd-v6.12.1-PR]: [zcashd v6.12.1 post-release merge (zcash/zcash#7141)](https://github.com/zcash/zcash/pull/7141)

[^zcashd-v6.12.2-PR]: [Hotfix v6.12.2 (zcash/zcash#7157)](https://github.com/zcash/zcash/pull/7157)

[^zcashd-v6.12.4-PR]: [Zcashd release v6.20.0, which published the v6.12.4 security fixes (zcash/zcash#7174)](https://github.com/zcash/zcash/pull/7174)

[^Zebra-v4.2.0-changelog]: [Zebra CHANGELOG — Zebra 4.2.0 (2026-03-12)](https://github.com/ZcashFoundation/zebra/blob/main/CHANGELOG.md#zebra-420---2026-03-12)

[^Zebra-v4.4.0-changelog]: [Zebra CHANGELOG — Zebra 4.4.0 (2026-05-01)](https://github.com/ZcashFoundation/zebra/blob/main/CHANGELOG.md#zebra-440---2026-05-01)

[^Zebra-v4.5.0-changelog]: [Zebra CHANGELOG — Zebra 4.5.0 (2026-05-28)](https://github.com/ZcashFoundation/zebra/blob/main/CHANGELOG.md#zebra-450---2026-05-28)

[^Zebra-v4.5.1-changelog]: [Zebra CHANGELOG — Zebra 4.5.1 (2026-05-29)](https://github.com/ZcashFoundation/zebra/blob/main/CHANGELOG.md#zebra-451---2026-05-29)

[^ZODL-v6.12.1-fixes]: [Several Zcash Vulnerabilities Successfully Remediated (Zcash Open Development Lab, 2026-04-17; zcashd v6.12.1, zebra v4.3.1)](https://zodl.com/zcashd-zebra-april-2026-disclosure/)

[^ZODL-v6.12.0-fixes]: [Zcash Vulnerability Successfully Remediated (Zcash Open Development Lab, 2026-03-31; zcashd v6.12.0)](https://zodl.com/zcashd-sprout-verification-vulnerability/)

[^CVE-2024-52911]: [Bitcoin Core disclosure of CVE-2024-52911](https://bitcoincore.org/en/2026/05/05/disclose-cve-2024-52911/)
Loading