# ZIP 248: Extensible Transaction Format

Status: Draft

Owners: Jack Grigg, Kris Nuttycombe, Daira-Emma Hopwood, Schell Scivally

Snapshot: a34cfc091434275120ff017c92c3b50d3cef623a

Source date: 2026-10-16T02:56:04+01:00

[Source revision](https://github.com/zcash/zips/commit/a34cfc091434275120ff017c92c3b50d3cef623a)

[Official source](https://zips.z.cash/zip-0248) · [Source file](https://github.com/zcash/zips/blob/a34cfc091434275120ff017c92c3b50d3cef623a/zips/zip-0248.rst) · [Discussion](https://github.com/zcash/zips/pull/1163)

## Document parts

[Part 1](https://zip-tools-web.harpocrates.workers.dev/zip/248) · [Part 2](https://zip-tools-web.harpocrates.workers.dev/zip/248?part=2)

Reading part 1 of 2. Follow all parts for the complete proposal.

Preamble

**ZIP**: 248

**Title**: Extensible Transaction Format

**Owners**: 

- [Jack Grigg](mailto:thestr4d@gmail.com)
- [Kris Nuttycombe](mailto:kris@nutty.land)
- [Daira-Emma Hopwood](mailto:daira@jacaranda.org)
- [Schell Scivally](mailto:efsubenovex@gmail.com)

**Status**: Draft

**Category**: Consensus / Wallet

**Created**: 2025-12-17

**License**: MIT

**Discussions-To**: [https://github.com/zcash/zips/pull/1163](https://github.com/zcash/zips/pull/1163)

**Pull-Request**: [https://github.com/zcash/zips/pull/1156](https://github.com/zcash/zips/pull/1156)

<a id="terminology"></a>

## Terminology

The key words "MUST", "MUST NOT", "SHOULD", and "MAY" in this document are to be interpreted as described in BCP 14<a id="fnref1"></a>
[1](/zip/248?part=2#fn1) when, and only when, they appear in all capitals.

The character § is used when referring to sections of the Zcash Protocol Specification.<a id="fnref2"></a>
[2](/zip/248?part=2#fn2)

The terms "Mainnet" and "Testnet" are to be interpreted as described in § 3.12 ‘Mainnet and Testnet’.<a id="fnref3"></a>
[3](/zip/248?part=2#fn3)

The term "full validator" in this document is to be interpreted as defined in § 3.3 ‘The Block Chain’.<a id="fnref4"></a>
[4](/zip/248?part=2#fn4)

The terms "Orchard protocol", "*Orchard pool*", and "*Ironwood pool*" are to be interpreted as described in ZIP 229.<a id="fnref5"></a>
[5](/zip/248?part=2#fn5) Following the convention in the Zcash Protocol Specification, *slanted text* refers to pool names, to distinguish them from shielded protocols.

The terms below are to be interpreted as follows:

**V7 transaction format**: 

The transaction format defined by this ZIP, identified by transaction version 7. Its version group ID is not yet assigned.

**V7 transaction**: 

A transaction that is encoded using the V7 transaction format.

**pre-V7 transaction format**: 

Any transaction format that precedes the V7 transaction format; that is, any of the V1 to V6 transaction formats.

**transparent transaction value pool**: 

An ephemeral value for the balance of an asset within the scope of a single transaction, which is modified by additions and subtractions in the processing of the effects of transaction bundles. When all of the effects of a transaction are accounted for, each such balance is zero; i.e., the total of additions to the balance equals the total of subtractions from it.

<a id="abstract"></a>

## Abstract

This ZIP proposes an encoding for V7 Zcash transactions that is intended to reduce the impact of future changes to the Zcash transaction format on the Zcash ecosystem. It defines a new typecode-length-value encoding for a sequence of protocol bundles, and a "value balance" map that describes the effect of each bundle on the transparent transaction value pool for each asset. The entries of this map serve the same purpose as the Sapling and Orchard value balance fields have done in the past.

<a id="motivation"></a>

## Motivation

In the past, Zcash network upgrades that changed the transaction format have resulted in substantial disruption for wallets and other third-party clients in the Zcash ecosystem. In order to continue functioning after a network upgrade, clients were required to upgrade their Zcash transaction parsers to read the new format, even if the context in which those parsers were being used didn't need or couldn't make use of newly added transaction data. An example of this is that transparent-only wallets were forced to update their parsers to understand the Sapling and Orchard parts of transactions, even if they would never read or act upon those parts. This has led on occasion to significant problems in the Zcash ecosystem, including situations where funds have been locked and rendered unspendable from transparent-only wallets.

For some kinds of changes to consensus features, it's imperative that every wallet be aware of and be adapted to those changes, and in those cases making a major (breaking) transaction version update as we've done in the past is appropriate. For many new features, however, it is possible for a wallet to continue functioning correctly without having to fully understand a transaction using that feature.

For example, if TZEs were to be added to the protocol, it would be possible for wallets to continue operating with transparent/Sapling/Orchard functionality, ignoring TZE parts. There is substantial precedent for this sort of behavior; transparent-only hardware wallets are currently still important in the Zcash ecosystem, and many wallets didn't begin interacting with Orchard transaction parts until quite a while after Orchard activation.

After this change to transaction encoding, wallets and other third parties will not be required to update their transaction parsers in advance of a network upgrade for the introduction of many (and perhaps most) types of new protocol features. This will enable the Zcash ecosystem to make smaller and more incremental network upgrades without breaking existing wallets.

<a id="privacy-implications"></a>

## Privacy Implications

This change alters the encoding of transactions, but does not alter the information content of the transaction. As such, the only implication of this change is that the use of this transaction format acts as a 1-bit distinguisher that reveals that the wallet that generated the transaction has been updated to be aware of the new format. This information leakage is unavoidable for any transaction format change.

In the future, this change may reduce the amount of information leakage, since transactions created using the proposed TLV format will include bundles only for those protocols for which the transaction modifies chain state. For example, if this transaction format change is deployed in NU8 and a hypothetical NU9 defines a bundle type for TZE components, it will not be possible for a chain observer to distinguish whether or not the wallet that produced an Orchard-only transaction is one that has been updated to understand the TZE component. Under prior practices for changing the transaction format, this would have been distinguishable.

In summary, this proposal provides a net improvement in user privacy in addition to its other benefits.

<a id="requirements"></a>

## Requirements

- The transaction format can be parsed without any knowledge of any Zcash payment protocols.

- Movement of value into and out of the transparent value pools (the ZEC transparent value pool, plus the transparent value pool for each ZSA asset) can be understood with only partial knowledge of the Zcash payment protocols.

- The information content of transactions should not change as part of this ZIP. Other ZIPs activated along with this ZIP may however make use of it in introducing such changes.

- It must be possible for wallets to parse any transaction that is valid within a version group that it understands, even if it doesn't have handling for or understand all of the bundle types that are valid for that version group. In such a situation, however, such a wallet must still be able to accurately describe any transparent movement of funds effected by the transaction, and alert the user if the transaction contains bundles that it does not understand.

- It must be possible for a wallet to correctly construct and sign transactions for a given transaction version group that it understands, even if it doesn't have handling for or understand all of the bundle types that are valid for transactions in that version group.

<a id="non-requirements"></a>

## Non-requirements

<a id="specification"></a>

## Specification

<a id="protocol-bundles"></a>

### Protocol Bundles

This ZIP refines and codifies the concept of "protocol bundles" that emerged from the implementation of the ZIP 225<a id="fnref6"></a>
[6](/zip/248?part=2#fn6) transaction format. It makes bundles first-class objects and defines a registry of bundle type and variant identifiers. Within the period that a given transaction format version is used on the Zcash network, the semantics of the bundle associated with a given `(bundleType, bundleVariant)` pair are fixed.

A **protocol bundle** is a self-contained component of a transaction that implements a specific piece of protocol functionality. Each bundle is identified by a `(bundleType, bundleVariant)` pair, where:

- The **bundle type** identifies the protocol or value pool that the bundle operates on (e.g., Transparent, Sapling, Orchard).

- The **bundle variant** identifies a specific variant of that protocol. New variants of an existing bundle type may be introduced by subsequent ZIPs to evolve a protocol's functionality while preserving the association with the same value pool.

A transaction MUST NOT contain more than one variant of any given bundle type. This constraint is enforced by the map structure of the transaction encoding: the maps `mEffectBundles`, and `mAuthBundles` are each keyed by `bundleType` alone; `mValuePoolDeltas` is keyed by bundle type and asset ID.

Each bundle type/variant pair defines:

- What **effecting data** the bundle contains — the data that determines what state changes the bundle produces (e.g., which notes are spent, which outputs are created, which transparent UTXOs are consumed or produced).

- What **authorizing data** the bundle contains — the proofs and signatures that authorize the state changes specified by the effecting data.

- How the bundle affects the **transparent transaction value pool** — whether the bundle adds value to, removes value from, or has no effect on this ephemeral pool that balances value flows within a transaction.

<a id="rationale-for-effecting-data-and-authorizing-data"></a>

#### Rationale for Effecting Data and Authorizing Data

Click to show/hide

The separation of effecting data from authorizing data serves several purposes:

1. **Transaction identifier stability**: The transaction identifier (txid) is computed only from the effecting data. This means that the txid is determined by *what* the transaction does, not by *how* it is authorized. Third parties cannot change a transaction's identifier by modifying signatures or proofs.

2. **Efficient pruning**: Full nodes that have validated a transaction may prune the authorizing data while retaining the effecting data. Since authorizing data appears at the end of the encoded transaction, pruning is simply truncation.

3. **Partial validation**: A wallet that does not understand a particular bundle type can still compute the transaction identifier by hashing the effecting data opaquely, without needing to parse its internal structure.

<a id="the-transparent-transaction-value-pool"></a>

#### The Transparent Transaction Value Pool

The transparent transaction value pool is an ephemeral concept that exists only within the scope of processing a single transaction. It serves as a balancing mechanism through which value flows between bundles.

Each bundle may contribute a **value pool delta** — a signed value indicating how much the bundle adds to or removes from the transparent transaction value pool for a given asset. A positive delta means the bundle is adding value to the pool (e.g., a shielded spend releasing value), while a negative delta means the bundle is consuming value from the pool (e.g., a shielded output absorbing value, or a transaction fee).

For a valid transaction, the sum of all value pool deltas for each asset MUST equal zero. This ensures that value is neither created nor destroyed — it is only transferred between bundles within the transaction.

This holds for coinbase transactions as well. The part of the block subsidy that the transaction pays out enters the transparent transaction value pool as the value pool delta of the coinbase bundle, and the fees collected from the other transactions in the block enter it as the value pool delta of the fee bundle; the coinbase outputs then consume that value like any other bundle.

<a id="bundle-type-registration"></a>

#### Bundle Type Registration

When a ZIP introduces a new bundle type or a new variant of an existing bundle type, it MUST:

1. Request allocation of a `(bundleType, bundleVariant)` pair in the registry defined below. The `bundleType` and `bundleVariant` are each non-negative integers. If the ZIP introduces a new bundle type, it requests a new `bundleType` value with `bundleVariant` 0. If it introduces a new variant of an existing bundle type, it requests a new `bundleVariant` value for the existing `bundleType`.

2. Specify whether entries for this bundle type are permitted in `mValuePoolDeltas` (the value pool delta map).

3. Specify whether entries for this bundle type are permitted in `mEffectBundles` (the effecting data map).

4. Specify whether entries for this bundle type are permitted in `mAuthBundles` (the authorizing data map).

5. If effecting data is permitted, define the encoding of that data.

6. If authorizing data is permitted, define the encoding of that data.

7. Define the digest algorithm for the bundle's contribution to the transaction identifier, if effecting data is present.

8. Define the digest algorithm for the bundle's contribution to the authorizing data commitment, if authorizing data is present.

A bundle type MUST NOT permit entries in `mAuthBundles` unless it also permits entries in `mEffectBundles`. That is, authorizing data cannot exist without corresponding effecting data for a given bundle type.

Once a `(bundleType, bundleVariant)` pair is assigned for a given transaction version, its semantics are fixed for the lifetime of that transaction version. A subsequent network upgrade may define a new transaction version that reassigns identifiers or changes bundle semantics, but within a single transaction version, bundle type and variant identifiers have stable, unchanging meanings.

A new variant of an existing bundle type MUST affect the same value pool(s) as the original bundle type. This ensures that a client that does not recognize a particular `bundleVariant` can still determine which pool(s) are affected by the bundle and inform its user accordingly.

<a id="v7-transaction-bundle-type-registry"></a>

#### V7 Transaction Bundle Type Registry

The following `(bundleType, bundleVariant)` pairs are registered for the V7 transaction format. All currently-defined values are encoded as single-byte `compactSize` values where they appear in the transaction format.

The `mValuePoolDeltas` column indicates whether or not an entry for this bundle type MAY appear in `mValuePoolDeltas`. For rows where an ❌ is present, the value pool delta for every pool is guaranteed to be zero, and so entries in `mValuePoolDeltas` MUST NOT be present.

The `mEffectBundles` column indicates whether or not an entry for this bundle type MAY appear in `mEffectBundles`. For rows where an ❌ is present, the bundle has no effecting data, and so an entry in `mEffectBundles` MUST NOT be present.

The `mAuthBundles` column indicates whether or not an entry for this bundle type MAY appear in `mAuthBundles`. For rows where an ❌ is present, the bundle has no authorizing data, and so an entry in `mAuthBundles` MUST NOT be present.

| BundleType | BundleVariant | `mValuePoolDeltas` | `mEffectBundles` | `mAuthBundles` | Defining ZIP | Bundle kind |
| --- | --- | --- | --- | --- | --- | --- |
| 0 | 0 | ✅ | ✅ | ✅ | This ZIP | Transparent |
| 1 | 0 | ✅ | ✅ | ❌ | This ZIP | Coinbase |
| 2 | 0 | ✅ | ✅ | ✅ | This ZIP | Sapling |
| 3 | 0 | ✅ | ✅ | ✅ | This ZIP | Orchard |
| 4 | 0 | ✅ | ✅ | ✅ | This ZIP | Ironwood |
| 5 | 0 | ✅ | ❌ | ❌ | ZIP 2002 | Transaction fee |
| 6 | 0 | ✅ | ❌ | ❌ | ZIP 233 | ZIP 233 NSM field |

Additional bundle types or variants MAY be added to this registry via modifications to this ZIP specified in other ZIPs. Such modifications MUST specify all of the information required by the [Bundle Type Registration](#bundle-type-registration) section above.

<a id="potential-future-bundle-types"></a>

#### Potential Future Bundle Types

Click to show/hide

The following bundle kinds have no allocated `(bundleType, bundleVariant)` pair. Where a bundle type is given, the kind is a new variant of that existing type and only its variant identifier is unassigned. Those proposed by a ZIP name it; the rest are provided to illustrate how potential future upgrades might affect the bundle registry:

| BundleType | BundleVariant | `mValuePoolDeltas` | `mEffectBundles` | `mAuthBundles` | Defining ZIP | Bundle kind |
| --- | --- | --- | --- | --- | --- | --- |
| 2 | 1 (provisional) | ✅ | ✅ | ✅ | ZIP 231 | Sapling-post-ZIP 231 |
| 3 | 1 (provisional) | ✅ | ✅ | ✅ | ZIP 231 | Orchard post-ZIP 231 |
| 3 | 2 (provisional) | ✅ | ✅ | ✅ | ZIP 226 | OrchardZSA |
| 4 | 1 (provisional) | ✅ | ✅ | ✅ | ZIP 231 | Ironwood post-ZIP 231 |
|  | 0 | ❌ | ✅ | ✅ | ZIP 270 | Key rotation |
|  | 0 | ✅ | ✅ | ✅ | TBD | Lockbox disbursement |
|  | 0 | ❌ | ✅ | ❌ | ZIP 231 | Memos |
|  | 0 | ✅ | ✅ | ✅ | ZIP 227 | ZSA Issuance |
|  | 0 | ✅ | ✅ | ✅ |  | TZEs |
|  | 0 | ✅ | ✅ | ✅ |  | Pool that only has a long-term storage protocol (PQ, very simple thus insulated from counterfeiting fears, can be used for payments but higher latency for that purpose) |
|  | 0 | ✅ | ✅ | ✅ |  | Tachyon |
|  | 0 | ✅ | ✅ | ❌ |  | Staking |
|  | 0 | ✅ | ✅ | ✅ |  | Unstaking (if it can't be combined with the Staking bundle) |
|  | 0 | ✅ | ✅ | ✅ |  | Post-quantum fast payment protocol |

<a id="transaction-format"></a>

### Transaction Format

| Bytes | Name | Data Type | Description |
| --- | --- | --- | --- |
| **Common Transaction Fields** |  |  |  |
| 4 | `header` | `uint32` | Contains:      - `fOverwintered` flag (bit 31, always set)  - `version` (bits 30 .. 0) – transaction version. |
| 4 | `nVersionGroupId` | `uint32` | Version group ID (nonzero). |
| 4 | `nConsensusBranchId` | `uint32` | Consensus branch ID (nonzero). |
| 4 | `lock_time` | `uint32` | Unix-epoch UTC time or block height, encoded as in Bitcoin. |
| 4 | `nExpiryHeight` | `uint32` | A block height in the range {1 .. 499999999} after which the transaction will expire, or 0 to disable expiry.<a id="fnref7"></a> [7](/zip/248?part=2#fn7) |
| **Transaction transparent value pool balance map** |  |  |  |
| varies | `nValuePoolDeltas` | `compactSize` | Number of entries in the `mValuePoolDeltas` map. |
| varies | `mValuePoolDeltas` | `ValuePoolDelta[nValuePoolDeltas]` | A map describing the change to the transparent value pool produced by each bundle. Only bundles that produces changes to the transparent value balance will have corresponding entries in this map. For bundles that have no data except for a value, such as the ZIP 233 amount, no additional bundle data will be present in the `Bundles` section. |
| **Bundles** |  |  |  |
| varies | `nEffectBundles` | `compactSize` | Number of bundles in the transaction that have per-bundle data. |
| varies | `mEffectBundles` | `BundleData[nEffectBundles]` | A map from bundle identifier to the effecting data of a bundle. |
| varies | `nAuthBundles` | `compactSize` | Number of bundles in the transaction that have per-bundle data. |
| varies | `mAuthBundles` | `BundleData[nAuthBundles]` | A map from bundle identifier to the authorizing data of a bundle. |

`mValuePoolDeltas` is interpreted as a map keyed by the tuple $(\mathsf{BundleType}, \mathsf{AssetUuid})$. Lookups in this map are denoted with the syntax $\mathsf{mValuePoolDeltas}[(\mathsf{BundleType}, \mathsf{AssetUuid})].$ A lookup for a key that is not present yields 0, consistent with the requirement that a record having zero value is elided from the encoding.

`mEffectBundles` and `mAuthBundles` are interpreted as maps keyed by `bundleType`.

Encoding constraints on these maps (uniqueness, ordering, and cross-map consistency) are specified in [Parsing Rules](#parsing-rules).

<a id="valuepooldelta"></a>

### ValuePoolDelta

| Bytes | Name | Data Type | Description |
| --- | --- | --- | --- |
| varies | `bundleType` | `compactSize` | An encoding of the bundle type identifier. |
| varies | `bundleVariant` | `compactSize` | An encoding of the bundle variant identifier. |
| 1 | `assetClass` | `uint8` | An asset class identifier. 0x00 for the ZEC asset, 0x01 for other assets. |
| one of {0, 64} | `assetUuid` | `byte[0] or byte[64]` | If assetClass == 0, the zero-length byte array, otherwise a byte array containing a universally unique 64-byte identifier for the asset. |
| 8 | `value` | `nonzero int64` | The net change to the transparent transaction value pool for the given asset, produced by the bundle with this bundle type identifier. This value MUST be nonzero; if a `ValuePoolDelta` record would have zero value, it MUST be elided from the encoding of `mValuePoolDeltas` instead. |

Let $\mathsf{Zec}$ be a distinguished value representing the ZEC asset. It is used as the asset identifier when $\mathsf{assetClass} = 0$.

Let $\mathsf{AssetUuid}(\mathsf{d})$ be the asset indicated by the `mValuePoolDeltas` entry $\mathsf{d}.$

$\begin{aligned}
\mathsf{AssetUuid}(\mathsf{d}) =
\begin{cases}
   \mathsf{Zec} & \text{if } \mathsf{d}.\mathsf{assetClass} = 0 \\
   \mathsf{d}.\mathsf{assetUuid} & \text{if } \mathsf{d}.\mathsf{assetClass} = 1 \\
   \bot & \text{otherwise}
\end{cases}
\end{aligned}$

$\mathsf{AssetUuid}$ groups the entries of `mValuePoolDeltas` by asset. The values of the entries in each such group are required to sum to zero; this balance rule is specified in [Cross-bundle and chain-context rules](#cross-bundle-and-chain-context-rules).

<a id="bundledata"></a>

### BundleData

| Bytes | Name | Data Type | Description |
| --- | --- | --- | --- |
| varies | `bundleType` | `compactSize` | An encoding of the bundle type identifier. |
| varies | `bundleVariant` | `compactSize` | An encoding of the bundle variant identifier. |
| varies | `nBundleDataLen` | `compactSize` | The length of the `vBundleData` byte array. |
| varies | `vBundleData` | `byte[nBundleDataLen]` | The effecting or authorizing data for the bundle, dependent upon whether the `BundleData` occurs in `mEffectBundles` or `mAuthBundles`. |

<a id="parsing-rules"></a>

### Parsing Rules

The rules in this section are requirements on the byte encoding of a V7 transaction. A byte stream that violates any of these rules is not a well-formed V7 transaction and MUST be rejected by any parser, regardless of which bundle types the parser understands. Enforcement of these rules is what makes it safe for a wallet to parse, identify, and compute the transaction identifier for a transaction containing bundle types that the wallet does not itself implement.

- Each of `mValuePoolDeltas`, `mEffectBundles`, and `mAuthBundles` MUST contain at most one entry per key, and the entries MUST be encoded in strictly increasing key order. For `mValuePoolDeltas` the key order is `(bundleType, assetClass, assetUuid)` compared lexicographically (with `assetUuid` compared as a byte string). For `mEffectBundles` and `mAuthBundles` the key order is `bundleType`.

- Every `ValuePoolDelta` record MUST have a nonzero `value` field. A `ValuePoolDelta` record that would have `value = 0` MUST be elided from the encoding of `mValuePoolDeltas`.

- In every `ValuePoolDelta` record, `assetUuid` MUST be the zero-length byte array if `assetClass = 0`, and MUST be a 64-byte value if `assetClass = 1`. No other value of `assetClass` is permitted.

- For each `bundleType` that appears in any of `mValuePoolDeltas`, `mEffectBundles`, or `mAuthBundles`, all entries for that `bundleType` across all three maps MUST encode the same `bundleVariant`. (This, combined with the constraint that each of `mEffectBundles` and `mAuthBundles` is keyed by `bundleType`, ensures that a transaction uses at most one variant of any given bundle type.)

- For each `bundleType` that appears in `mAuthBundles`, a corresponding entry with the same `bundleVariant` MUST exist in `mEffectBundles`.

- Within the encoding of any bundle's effecting and authorizing data, every per-element sequence in the authorizing data that is described as corresponding to a per-element sequence in the effecting data MUST have exactly the same length as that effecting-data sequence, and the element at each index in the authorizing-data sequence corresponds to the element at the same index in the effecting-data sequence. For the bundle types defined by this ZIP this requires:
  
  
  
  - `vSpendProofsSapling` and `vSpendAuthSigsSapling` each have `nSpendsSapling` elements;
  
  - `vOutputProofsSapling` has `nOutputsSapling` elements;
  
  - in each Orchard protocol bundle, `vSpendAuthSigsOrchard` has `nActionsOrchard` elements, and `proofsOrchard` aggregates exactly `nActionsOrchard` per-action proofs.

A wallet that successfully parses a V7 transaction under these rules is guaranteed to be able to compute the transaction identifier (given, for each bundle type it does not understand, the corresponding 32-byte `bundle_effects_digest` value as described in [Implications for Wallets](#implications-for-wallets)), and to enumerate the transparent value flows of the transaction at the granularity of bundle types.

<a id="consensus-rules"></a>

### Consensus Rules

This ZIP requires the following modifications to the consensus rules in the Zcash Protocol Specification. These rules are additional to the parsing rules above; a transaction that satisfies the parsing rules but violates any of these consensus rules is well-formed but invalid.

Let `CoinbaseBundleId` be the identifier of the coinbase bundle, and `FeeBundleId` be the identifier of the fee bundle. In V7 transactions, $\mathsf{CoinbaseBundleId} = 1$ and $\mathsf{FeeBundleId} = 5$ as defined in the table above.

<a id="bundle-local-rules"></a>

#### Bundle-local rules

The following rules constrain the contents of individual bundles. A wallet or full validator only needs to enforce a given rule in this subsection if it understands the bundle type that the rule applies to.

- A bundle type MUST NOT appear in any map for which the corresponding column of the [V7 Transaction Bundle Type Registry](#v7-transaction-bundle-type-registry) is marked ❌, and MUST NOT appear in any map at all if its row in the registry is marked as Reserved.

- The `assetClass` value for any entry in `mValuePoolDeltas` having `bundleType = FeeBundleId` MUST be 0 (fee amounts are denominated in ZEC and no other asset).

- The `assetClass` value for any entry in `mValuePoolDeltas` having `bundleType = CoinbaseBundleId` MUST be 0 (the block subsidy is denominated in ZEC and no other asset). The same applies to the `blockSubsidy` and `lockboxValue` fields of its effecting data.

- A coinbase transaction MUST NOT spend any transparent output. If a transparent bundle is present in a coinbase transaction, its effecting data MUST have `tx_in_count = 0`.

- For coinbase transactions, the `nSpendsSapling` field of the Sapling bundle's effecting data MUST be 0.

- For coinbase transactions, the `enableSpends` bit of the `flagsOrchard` field of every Orchard protocol bundle MUST be 0.

- In an Orchard bundle, the `enableCrossAddress` bit of `flagsOrchard` MUST be 0; that is, transfers into the *Orchard pool* are restricted to the protocol-level address of the action's spend.<a id="fnref8"></a>
  [8](/zip/248?part=2#fn8) The bit is unrestricted in an Ironwood bundle.

<a id="cross-bundle-and-chain-context-rules"></a>

#### Cross-bundle and chain-context rules

The following rules relate the contents of a bundle to the other bundles of its transaction, or to the block that contains it. They MUST be enforced by full validators.

- For coinbase transactions, the value of $\mathsf{mValuePoolDeltas}[(\mathsf{FeeBundleId}, \mathsf{Zec})]$ MUST be nonnegative. This represents the total transaction fees collected from all other transactions in the block.

- For non-coinbase transactions, the value of $\mathsf{mValuePoolDeltas}[(\mathsf{FeeBundleId}, \mathsf{Zec})]$ MUST be nonpositive. This represents the transaction fee paid by the transaction (expressed as a negative value, since it is removed from the transparent transaction value pool).

- Within the scope of a block, the sum of the fee bundle values MUST equal 0. That is, the fees collected by the coinbase transaction MUST equal the sum of fees paid by all other transactions in the block.

- Exactly one transaction in a block MUST be a coinbase transaction, and it MUST be the first transaction in the block.

- The `blockHeight` field of the coinbase bundle's effecting data MUST equal the height of the block containing the transaction.

- The `blockSubsidy` field of the coinbase bundle's effecting data MUST equal the block subsidy for that block, as defined in § 7.8 'Block Subsidy and Founders' Reward'.<a id="fnref9"></a>
  [9](/zip/248?part=2#fn9)

- The `lockboxValue` field of the coinbase bundle's effecting data MUST equal the total value that the funding streams for that block deposit into the deferred pool, as defined in § 7.10 'Payment of Funding Streams, Deferred Lockbox, and Lockbox Disbursement'.<a id="fnref10"></a>
  [10](/zip/248?part=2#fn10)

- The value of $\mathsf{mValuePoolDeltas}[(\mathsf{CoinbaseBundleId}, \mathsf{Zec})]$ MUST equal the part of the block subsidy that is not deposited into the lockbox.
  
  
  
  
  $\mathsf{mValuePoolDeltas}[(\mathsf{CoinbaseBundleId}, \mathsf{Zec})] = \mathsf{blockSubsidy} - \mathsf{lockboxValue}$

- For every transaction, the sum of value pool delta values in each asset MUST equal 0.
  
  
  
  
  $\forall \mathsf{a}. \sum_{\mathsf{d} \in \mathsf{mValuePoolDeltas} | \mathsf{AssetUuid}(\mathsf{d}) = \mathsf{a}} \mathsf{d.value} = 0$

<a id="sighash-versioning"></a>

### Sighash Versioning

This ZIP introduces sighash algorithm versioning. Where previously each transaction version had a single associated sighash algorithm, going forward it is possible for signers to use any sighash algorithm within the closed set specified for a given transaction version (and made available in consensus via network upgrades).

The sighash version is encoded as a single byte alongside any associated data that the sighash algorithm version requires (for deterministically computing the digest):

`sighashInfo = [sighashVersion] || associatedData`

where `associatedData` is specific to the bundle it appears in.

Each bundle type defines the sighash algorithm versions available to its signers, the `associatedData` that each version requires, and the digest that a signature using it commits to. Those definitions are given for the bundle types registered by this ZIP in [Bundle Definitions](#bundle-definitions).

Version 0 is by convention the "commit to all effecting data" sighash algorithm. Other versions can commit to whatever makes sense for desired functionality within a given transaction version. Consensus rules choose the digest algorithm for each signer based on `sighashVersion`.

Sighash version information is present alongside each signature in the authorizing data of each bundle.

<a id="digest-algorithms"></a>

### Digest Algorithms

All digests are personalized BLAKE2b-256 hashes. In cases where no elements are available for hashing (for example, if there are no transparent transaction inputs), a personalized hash of the empty byte array will be used. The personalization string therefore provides domain separation for the hashes of even empty data fields.

The notation `BLAKE2b-256(personalization_string, [])` is used to refer to hashes constructed in this manner.

<a id="txid-digest"></a>

#### TxId Digest

A new transaction digest algorithm is defined that constructs the identifier for a V7 transaction from a tree of hashes. The overall structure of the hash is as follows:

```
txid_digest
├── header_digest
├── value_pool_deltas_digest
└── effects_bundles_digest
    ├─ (bundle_type_id || bundle_variant || transparent_effects_digest)
    ├─ (bundle_type_id || bundle_variant || coinbase_effects_digest)
    ├─ (bundle_type_id || bundle_variant || sapling_effects_digest)
    │   ├── sapling_spends_digest
    │   │   ├── sapling_spends_compact_digest
    │   │   └── sapling_spends_noncompact_digest
    │   └── sapling_outputs_digest
    │       ├── sapling_outputs_compact_digest
    │       ├── sapling_outputs_memos_digest
    │       └── sapling_outputs_noncompact_digest
    ├─ (bundle_type_id || bundle_variant || orchard_effects_digest)
    │   ├── orchard_actions_compact_digest
    │   ├── orchard_actions_memos_digest
    │   └── orchard_actions_noncompact_digest
    ├─ (bundle_type_id || bundle_variant || ironwood_effects_digest)
    │   ├── ironwood_actions_compact_digest
    │   ├── ironwood_actions_memos_digest
    │   └── ironwood_actions_noncompact_digest
    └─ (bundle_type_id || bundle_variant || unknown_bundle_effects_digest) ...
```

Each node written as `snake_case` in this tree is a BLAKE2b-256 hash of its children, initialized with a personalization string specific to that branch of the tree. Nodes that are not themselves digests are written in `camelCase`. In the specification below, nodes of the tree are presented in depth-first order.

<a id="txid_digest"></a>

##### txid_digest

A BLAKE2b-256 hash of the following values:

```
T.1: header_digest             (32-byte hash output)
T.2: value_pool_deltas_digest  (32-byte hash output)
T.3: effects_bundles_digest    (32-byte hash output)
```

The personalization field of this hash is set to:

```
"ZcashTxHash_" || CONSENSUS_BRANCH_ID
```

`ZcashTxHash_` has 1 underscore character.

As in ZIP 244<a id="fnref11"></a>
[11](/zip/248?part=2#fn11), CONSENSUS_BRANCH_ID is the 4-byte little-endian encoding of the consensus branch ID for the epoch of the block containing the transaction.

<a id="t.1-header_digest"></a>

##### T.1: header_digest

A BLAKE2b-256 hash of the following values:

```
T.1a: version             (4-byte little-endian version identifier including overwintered flag)
T.1b: nVersionGroupId    (4-byte little-endian version group identifier)
T.1c: nConsensusBranchId (4-byte little-endian consensus branch id)
T.1d: lock_time           (4-byte little-endian nLockTime value)
T.1e: nExpiryHeight       (4-byte little-endian block height)
```

The personalization field of this hash is set to:

```
"ZTxIdHeadersHash"
```

<a id="t.2-value_pool_deltas_digest"></a>

##### T.2: value_pool_deltas_digest

A BLAKE2b-256 hash of the concatenated encodings of all entries in `mValuePoolDeltas`, in transaction order. For each entry, the following values are concatenated:

```
T.2a: bundleType    (compactSize encoding)
T.2b: bundleVariant (compactSize encoding)
T.2c: assetClass    (1 byte)
T.2d: assetUuid     (0 or 64 bytes, depending on assetClass)
T.2e: value         (8-byte signed little-endian)
```

The personalization field of this hash is set to:

```
"ZTxIdVPDeltaHash"
```

In the case that the transaction has no value pool delta entries (which would only occur for transactions that have no effect on any value pool), `value_pool_deltas_digest` is:

```
BLAKE2b-256("ZTxIdVPDeltaHash", [])
```

<a id="t.3-effects_bundles_digest"></a>

##### T.3: effects_bundles_digest

A BLAKE2b-256 hash of the concatenated tagged bundle effect digests for all bundles present in `mEffectBundles`, in transaction order. For each bundle, the following values are concatenated:

```
T.3a: bundleType            (compactSize encoding)
T.3b: bundleVariant         (compactSize encoding)
T.3c: bundle_effects_digest (32-byte hash output)
```

where `bundle_effects_digest` is the root hash of the bundle's effecting data tree, as defined for each bundle type in [Bundle Definitions](#bundle-definitions).

The personalization field of this hash is set to:

```
"ZTxIdEffBnd_Hash" (1 underscore character)
```

In the case that the transaction has no effect bundles, `effects_bundles_digest` is:

```
BLAKE2b-256("ZTxIdEffBnd_Hash", [])
```

<a id="v0-signature-digest"></a>

#### v0 Signature Digest

A new per-input transaction digest algorithm is defined that constructs a hash that may be signed by a transaction creator to commit to the effects of the transaction. This follows closely the algorithm from ZIP 244<a id="fnref12"></a>
[12](/zip/248?part=2#fn12).

The digest algorithm used for a given signature is determined by the `sighashVersion` from the signer's `sighashInfo`, as specified in the [Sighash Versioning](#sighash-versioning) section. For sighash version 0 (the only version currently defined for V7 transactions), the digest algorithm is as specified below. Future network upgrades may define additional sighash algorithm versions with divergent behavior.

For transactions that have no transparent inputs, the v0 signature digest is identical to the transaction identifier digest.

For transactions with transparent inputs, the signature digest replaces `effects_bundles_digest` with a `signature_bundles_digest` that incorporates `hash_type`-dependent transparent signing data:

```
signature_digest
├── header_digest
├── value_pool_deltas_digest
└── signature_bundles_digest
```

<a id="signature_digest"></a>

##### signature_digest

A BLAKE2b-256 hash of the following values:

```
S.1: header_digest             (32-byte hash output)
S.2: value_pool_deltas_digest  (32-byte hash output)
S.3: signature_bundles_digest  (32-byte hash output)
```

The personalization field of this hash is set to:

```
"ZcashTxHash_" || CONSENSUS_BRANCH_ID
```

This value has the same personalization as the transaction identifier digest, so that what is being signed in the case that there are no transparent inputs is exactly the transaction id.

<a id="s.3-signature_bundles_digest"></a>

##### S.3: signature_bundles_digest

If the transaction has no transparent inputs, `signature_bundles_digest` is identical to `effects_bundles_digest`.

Otherwise, `signature_bundles_digest` is constructed the same as `effects_bundles_digest`, except that `transparent_effects_digest` is replaced with `transparent_sig_digest`.

<a id="authorizing-data-commitment"></a>

#### Authorizing Data Commitment

A transaction digest algorithm is defined that constructs a digest committing to the authorizing data of a transaction. The overall structure is:

```
auth_digest
└── auth_bundles_digest
    ├─ (bundle_type_id || bundle_variant || transparent_auth_digest)
    ├─ (bundle_type_id || bundle_variant || sapling_auth_digest)
    ├─ (bundle_type_id || bundle_variant || orchard_auth_digest)
    ├─ (bundle_type_id || bundle_variant || ironwood_auth_digest)
    └─ (bundle_type_id || bundle_variant || unknown_bundle_auth_digest) ...
```

<a id="auth_digest"></a>

##### auth_digest

A BLAKE2b-256 hash of the following value:

```
A.1: auth_bundles_digest (32-byte hash output)
```

The personalization field of this hash is set to:

```
"ZTxAuthHash_" || CONSENSUS_BRANCH_ID
```

For transaction versions before V7, a placeholder value consisting of 32 bytes of `0xFF` is used in place of the authorizing data commitment.

<a id="a.1-auth_bundles_digest"></a>

##### A.1: auth_bundles_digest

A BLAKE2b-256 hash of the concatenated tagged bundle auth digests for all bundles present in `mAuthBundles`, in transaction order. For each bundle, the following values are concatenated:

```
A.1a: bundleType          (compactSize encoding)
A.1b: bundleVariant       (compactSize encoding)
A.1c: bundle_auth_digest  (32-byte hash output)
```

The personalization field of this hash is set to:

```
"ZTxAuthBnd__Hash" (2 underscore characters)
```

In the case that the transaction has no auth bundles, `auth_bundles_digest` is:

```
BLAKE2b-256("ZTxAuthBnd__Hash", [])
```

<a id="implications-for-wallets"></a>

### Implications for Wallets

<a id="sending-v7-transactions"></a>

#### Sending V7 transactions

*All* Zcash wallets SHOULD, without undue delay, switch to sending only V7 transactions once they are allowed on the network. This applies to all transactions regardless of whether they use new V7 features.

<a id="support-for-receiving-funds-in-v7-transactions"></a>

#### Support for receiving funds in V7 transactions

Zcash wallets MUST support parsing V7 transactions by the time they are allowed on the network.

Because the V7 transaction format uses a type-length-value encoding for bundles, a wallet is not required to understand the internal encoding of every bundle in order to parse a transaction. However, a wallet that encounters a bundle with an unrecognized `bundleType` SHOULD alert the user that the transaction contains components it does not understand. A wallet that encounters a bundle with a recognized `bundleType` but unrecognized `bundleVariant` SHOULD alert the user that the transaction affects a pool the wallet is aware of, but in a way the wallet does not fully understand.

For bundle types not understood by a wallet, the wallet can compute the transaction identifier so long as it has been provided with the 32-byte `bundle_effects_digest` value for each bundle that it does not understand. This enables partial verification of transactions containing unknown bundle types.

A wallet MUST NOT construct or sign a transaction containing a bundle type or variant that it does not fully understand.

<a id="bundle-definitions"></a>

### Bundle Definitions

The sections above are independent of any particular bundle type. This section defines the bundle types that this ZIP registers: the encoding of their effecting and authorizing data, the sighash algorithm versions available to their signers, and their contributions to the digests defined in [Digest Algorithms](#digest-algorithms). A ZIP that registers a further bundle type defines the same things for it, as required by [Bundle Type Registration](#bundle-type-registration).

<a id="transparent-bundle"></a>

#### Transparent Bundle

The transparent bundle's value pool delta in `mValuePoolDeltas` represents the net value flowing from the transparent inputs to the transparent outputs.

A full validator MUST verify that the ZEC value pool delta for `bundleType = 0` equals the total value of the transparent inputs minus the total value of the transparent outputs. (The input values are not encoded in the transaction itself; they are obtained from the UTXOs being spent.)

This rule applies to coinbase transactions unchanged. A coinbase transaction has no transparent inputs, so its transparent value pool delta is the negation of the total value of its transparent outputs; the value that those outputs consume is contributed to the transparent transaction value pool by the coinbase bundle and the fee bundle.

<a id="transparent-effecting-data"></a>

##### Transparent Effecting Data

The effecting data for the transparent bundle describes the transparent inputs being spent and the transparent outputs being created.

| Bytes | Name | Data Type | Description |
| --- | --- | --- | --- |
| `varies` | `tx_in_count` | `compactSize` | Number of transparent inputs. |
| `varies` | `tx_in_effecting` | `TransparentInputEffecting[tx_in_count]` | Effecting data for each transparent input. |
| `varies` | `tx_out_count` | `compactSize` | Number of transparent outputs. |
| `varies` | `tx_out` | `TransparentOutput[tx_out_count]` | Transparent outputs. |

<a id="transparentinputeffecting"></a>

###### TransparentInputEffecting

| Bytes | Name | Data Type | Description |
| --- | --- | --- | --- |
| `32` | `prevout_hash` | `byte[32]` | The transaction ID of the output being spent. |
| `4` | `prevout_index` | `uint32` | The index of the output being spent within that transaction. |
| `4` | `nSequence` | `uint32` | Sequence number, encoded as in Bitcoin. |

## Citation relationships

Cites: [ZIP 203](https://zip-tools-web.harpocrates.workers.dev/zip/203), [ZIP 212](https://zip-tools-web.harpocrates.workers.dev/zip/212), [ZIP 225](https://zip-tools-web.harpocrates.workers.dev/zip/225), [ZIP 226](https://zip-tools-web.harpocrates.workers.dev/zip/226), [ZIP 227](https://zip-tools-web.harpocrates.workers.dev/zip/227), [ZIP 229](https://zip-tools-web.harpocrates.workers.dev/zip/229), [ZIP 231](https://zip-tools-web.harpocrates.workers.dev/zip/231), [ZIP 233](https://zip-tools-web.harpocrates.workers.dev/zip/233), [ZIP 239](https://zip-tools-web.harpocrates.workers.dev/zip/239), [ZIP 244](https://zip-tools-web.harpocrates.workers.dev/zip/244), [ZIP 270](https://zip-tools-web.harpocrates.workers.dev/zip/270), [ZIP 307](https://zip-tools-web.harpocrates.workers.dev/zip/307), [ZIP 2001](https://zip-tools-web.harpocrates.workers.dev/zip/2001), [ZIP 2002](https://zip-tools-web.harpocrates.workers.dev/zip/2002), [ZIP 2006](https://zip-tools-web.harpocrates.workers.dev/zip/2006)

Cited by: [ZIP 229](https://zip-tools-web.harpocrates.workers.dev/zip/229), [ZIP 231](https://zip-tools-web.harpocrates.workers.dev/zip/231), [ZIP 233](https://zip-tools-web.harpocrates.workers.dev/zip/233), [ZIP 317](https://zip-tools-web.harpocrates.workers.dev/zip/317), [ZIP 2002](https://zip-tools-web.harpocrates.workers.dev/zip/2002)