# ZIP 231: Memo Bundles

Status: Draft

Owners: Jack Grigg, Kris Nuttycombe, Daira-Emma Hopwood, Arya Solhi

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-0231) · [Source file](https://github.com/zcash/zips/blob/a34cfc091434275120ff017c92c3b50d3cef623a/zips/zip-0231.md) · [Discussion](https://github.com/zcash/zips/issues/627)

## Document parts

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

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

Preamble

**ZIP**: 231

**Title**: Memo Bundles

**Owners**: 

- [Jack Grigg](mailto:thestr4d@gmail.com)
- [Kris Nuttycombe](mailto:kris@nutty.land)
- [Daira-Emma Hopwood](mailto:daira@jacaranda.org)
- [Arya Solhi](mailto:arya@zfnd.org)

**Credits**: 

- Sean Bowe
- Nate Wilcox

**Status**: Draft

**Category**: Consensus / Wallet

**Created**: 2024-04-26

**License**: MIT

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

<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="user-content-fnref-bcp14"></a>
[1](/zip/231?part=2#user-content-fn-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. <a id="user-content-fnref-zip-0200"></a>
[2](/zip/231?part=2#user-content-fn-zip-0200)

The character § is used when referring to sections of the Zcash Protocol
Specification. <a id="user-content-fnref-protocol"></a>
[3](/zip/231?part=2#user-content-fn-protocol)

The terms "Mainnet" and "Testnet" are to be interpreted as described in
§ 3.12 ‘Mainnet and Testnet’. <a id="user-content-fnref-protocol-networks"></a>
[4](/zip/231?part=2#user-content-fn-protocol-networks)

The terms "V7 transaction format", "V7 transaction", and "pre-V7 transaction
format" are to be interpreted as defined in ZIP 248. <a id="user-content-fnref-zip-0248"></a>
[5](/zip/231?part=2#user-content-fn-zip-0248) A note
plaintext or note ciphertext is described as "V7-onward" or "pre-V7" according
to the format of the transaction that contains it.

The terms "Orchard protocol", "*Orchard pool*", and "*Ironwood pool*" are to be
interpreted as described in ZIP 229. <a id="user-content-fnref-zip-0229"></a>
[6](/zip/231?part=2#user-content-fn-zip-0229) The term "Orchard protocol
bundle" is to be interpreted as defined in ZIP 248. <a id="user-content-fnref-zip-0248-2"></a>
[5](/zip/231?part=2#user-content-fn-zip-0248)

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

## Abstract

Currently, the memo associated with a shielded output is fixed to 512 bytes of data.

This ZIP proposes to decouple memo data from outputs by introducing a per-transaction
memo bundle. Each shielded output carries a 32-byte memo key rather than an inline
512-byte memo field. The memo key is used to derive a symmetric encryption key that
decrypts the output's memo from the bundle, which consists of individually encrypted
256-byte chunks. This will:

- reduce the memo data of a transaction with two shielded outputs from a minimum
  of 1024 bytes to approximately 576 bytes;

- allow larger memos, with a proportionate increase in fees;

- enable memo data to be shared between multiple recipients of a transaction;

- enable memo data to be pruned from a stored transaction without impairing the
  ability of a consensus node to validate the transaction.

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

## Motivation

In Zcash transaction versions v2 to v5 inclusive, each Sapling or Orchard
shielded output contains a ciphertext comprised of a 52-byte note plaintext and
corresponding 512-byte memo field, with a 16-byte authentication tag
<a id="user-content-fnref-protocol-noteptconcept"></a>
[7](/zip/231?part=2#user-content-fn-protocol-noteptconcept). Recipients can only decrypt the outputs sent to
them, and thus can also only observe the memo fields included with the outputs
they can decrypt.

An ordinary transaction is one that is padded to 2 inputs and 2 outputs (or 2
Orchard actions), so as to make transfers that spend a note without change
indistinguishable from an ordinary transaction with change. Such a transaction
will consume 1024 bytes of block space for memo data. An Orchard transfer that
spends a larger number of notes will incur a memo data cost of $512 \times n$
bytes on-chain, where $n$ is the number of notes being spent as inputs.
Ordinarily, most of this data space is wasted, as most wallets support only a
single 512-byte memo to be sent to the recipient of the transfer. Virtually all
transactions with shielded components consume at least 1024 bytes of block
space for memo data, and between half and 3/4 of that data payload is
ordinarily waste. Adopting this ZIP will eliminate a significant source of
chain bloat.

Beyond reducing chain bloat, decoupling memos from shielded outputs allows for
some important use cases to be served. The shielded transaction protocol always
hides the sender(s) (that is, the addresses corresponding to the keys used to
spend the input notes) from all of the recipients. For certain kinds of
transactions, it is desirable to make one or more sender addresses available to
one or more recipients (for example, a reply address). In such circumstances it
is important to authenticate the sender address(es), to give the recipient a
guarantee that the address is controlled by a sender of the transaction;
failure to authenticate this address can enable phishing attacks. These
Authenticated Reply Addresses require zero-knowledge proofs, and for the
Orchard protocol these proofs are too large to fit into the existing 512-byte
memo field. This ZIP makes the maximum size of memo data large enough to
support the inclusion of such proofs.

Another motivation is that it is desirable, for clients with more stringent
bandwidth constraints, to be able to transmit encrypted notes to the client
without including the encrypted memo data. In the current light client protocol
<a id="user-content-fnref-zip-0307"></a>
[8](/zip/231?part=2#user-content-fn-zip-0307), this is done by truncating the note ciphertext to just the part
that encrypts the note plaintext, removing the 512-byte encrypted memo and the
16-byte AEAD authentication tag. Because the authentication tag is not
transmitted to the light client, the decryption performed by the client does
not meet the standard IND-CCA2 ∧ INT-CTXT security notion for an authenticated
encryption scheme <a id="user-content-fnref-bn2000"></a>
[9](/zip/231?part=2#user-content-fn-bn2000).

By decoupling memo data from note ciphertexts, this proposal reduces the
V7-onward note ciphertext to 100 bytes (excluding any other changes to the note
plaintext proposed by other ZIPs). This makes it practical for the light client
protocol to transmit complete note ciphertexts, including the AEAD tag, in
exchange for a relatively minor bandwidth increase compared to the current
truncated ciphertexts. This allows light clients to perform fully authenticated
decryption, ensuring security against chosen ciphertext attacks and simplifying
the security argument.

Instead of the memo data, this ZIP proposes that it is possible to indicate
whether a memo is present for the recipient. When using the light client
protocol, a recipient need not download full transaction information if this
indication tells them that they have not received any memo in the transaction.

In addition to reducing chain bloat by eliminating wasted memo space, this ZIP
defines a mechanism by which validating nodes may reduce their long-term
storage requirements by pruning memo data, without impairing their ability to
validate the entire chain.

Finally, at present, it is not possible to transmit the same memo data to
multiple transaction recipients without redundantly encoding that data, and
sending memo data greater than 512 bytes requires sending multiple outputs; the
problem is compounded when attempting to send more than 512 bytes to each
recipient. By separating memo data from the decryption capability for those
memos, it admits a greater variety of applications that utilize memo data,
while decreasing the amount of data that needs to be stored on-chain overall.

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

## Privacy Implications

Prior to the activation of this ZIP, every shielded output has an associated
memo field. A chain observer can therefore infer a likely 1:1 correlation
between transaction recipients and memo payloads. The maximum number of
distinct memos is precisely known, and the bounds on possible memo sizes are
very small (between 0 and 512 bytes). It is possible to send additional data to
a single recipient by adding (potentially zero-valued) outputs; on-chain this is
indistinguishable from sending more memos to more recipients or (in the case of
Orchard) spending more input notes.

After the activation of this ZIP, recipient count and memo count are decoupled.
It becomes possible to construct transactions for which there are many more
recipients than distinct memos, or vice versa. Without mitigation, this could
result in more observably distinct patterns relating the number of recipients
to the number of possible memos. In particular, a V7 transaction with no memo
would be distinguishable from one with a memo.

To mitigate this, a V7 transaction with any shielded outputs is required to
include at least 2 memo chunks in its memo bundle and pad the memo to a
multiple of 2 chunks (see [Memo bundle padding]). This ensures that the common
use case of sending a transaction without including explicit memo data is
indistinguishable from sending a transaction with a short memo, as is currently
supported by all wallets in common use.

If a wallet includes an authenticated reply-to address, however, this may be
distinguishable from other ordinary wallet behaviour because the memo data
(including the reply-to address) necessarily exceeds 512 bytes. While it would
be possible to require transactions be padded to at least 16 chunks to
ameliorate this potential distinguisher, this introduces an unreasonable amount
of waste in the case of ordinary transactions.

On the flip side, a chain observer now only knows upper bounds on the amount
of memo data being conveyed, and the number of possible distinct memos.
They have no information about how memo data is split among the recipients.
This can provide a privacy improvement in some situations. For example, it is
not possible to distinguish between many recipients receiving many small
memos, and the same set of recipients receiving one large shared memo.

In summary, when sending large amounts of memo data, the change introduced by
this ZIP eliminates a potential distinguisher along one axis in exchange for a
potential distinguisher along another.

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

## Requirements

- Standard Zcash shielded transactions continue to include a single 512-byte
  memo.

- The mechanism should be able to reproduce the existing functionality where
  each output may include a corresponding 512 bytes of memo data that is
  decryptable only by the recipient of that output.

- Recipients can receive memo data that is greater than 512 bytes in length.

- Multiple recipients, across any of the shielded pools, can be given the
  capability to view the same memo data.

- The exact number and exact lengths of distinct decryptable memos should not
  be revealed, even to the transaction recipients, although an upper bound on
  the total length of memo data that the observer does not have the capability
  to view will be leaked to transaction recipients, and the overall maximum
  possible length of memo data will be revealed on-chain.

- A recipient can determine whether or not they have been given the capability
  to view any memo solely by decrypting the note ciphertext.

- The memo bundle of a transaction can be pruned from block storage without
  preventing the transaction from being verified when that pruned block data
  is received by peers.

- The ciphertext of the note alone can be decrypted using a scheme that meets
  a standard security notion for authenticated encryption, without more than a
  small constant overhead in ciphertext size.

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

## Non-requirements

- Recipients do not need to be able to receive multiple memos per note. This
  capability could however be enabled under the existing proposal by "chaining"
  memos, including the decryption key for another memo within the memo that
  is decryptable by a recipient.

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

## Specification

Since this proposal is defined only for V7 transactions, it is not
necessary to consider Sprout JoinSplit outputs. The following sections apply to
Sapling outputs, and to the outputs of Orchard protocol actions in either the
*Orchard pool* or the *Ironwood pool*, in V7 transactions.

<a id="changes-to-zip-248"></a>

### Changes to ZIP 248

This ZIP proposes to register a new bundle type for Memos, and new versions of
the Sapling bundle type and of each Orchard protocol bundle type, in the
V7 transaction bundle type registry defined in ZIP 248 <a id="user-content-fnref-zip-0248-3"></a>
[5](/zip/231?part=2#user-content-fn-zip-0248):

| BundleType | BundleVariant | `mValuePoolDeltas` | `mEffectBundles` | `mAuthBundles` | Bundle kind |
| --- | --- | --- | --- | --- | --- |
| TBD | 0 | ❌ | ✅ | ❌ | Memos |
| 2 | TBD | ✅ | ✅ | ✅ | Sapling-post-ZIP 231 |
| 3 | TBD | ✅ | ✅ | ✅ | Orchard post-ZIP 231 |
| 4 | TBD | ✅ | ✅ | ✅ | Ironwood post-ZIP 231 |

The Memos bundle has no value pool deltas (memo data does not involve value
transfers) and no authorizing data. The effecting data consists of the encoded
memo bundle as defined in [Encoding in transactions].

The Sapling-post-ZIP 231 entry is a new variant of the existing Sapling bundle
type (`bundleType` 2). The Orchard post-ZIP 231 and Ironwood post-ZIP 231
entries are new variants of the two bundle types that use the Orchard protocol
bundle encoding — the Orchard bundle (`bundleType` 3) and the Ironwood bundle
(`bundleType` 4) — and share one encoding between them, as in ZIP 248. Because
ZIP 248 requires that each bundle map is keyed by `bundleType` alone, a
transaction that includes a Sapling-post-ZIP 231 bundle cannot also include a
pre-ZIP 231 Sapling bundle, and likewise for each Orchard protocol bundle
type.

If this ZIP is activated in the same network upgrade as ZIP 248, the encodings
of the Sapling bundle and of the Orchard protocol bundles described in this ZIP
will be used for variant 0 of bundle types 2, 3, and 4, and ZIP 248's
definitions of those bundles must be updated accordingly. If this ZIP is
activated in a network upgrade AFTER the network upgrade in which ZIP 248 is
activated, the encodings it describes will be assigned distinct bundle variant
identifiers for their respective bundle types.

<a id="sapling-post-zip-231-bundle"></a>

### Sapling-post-ZIP 231 Bundle

The Sapling-post-ZIP 231 bundle replaces the Sapling bundle defined in
ZIP 248 <a id="user-content-fnref-zip-0248-4"></a>
[5](/zip/231?part=2#user-content-fn-zip-0248). The only change is that the note plaintext in each
Sapling output is shortened: the 512-byte memo field is replaced by a
32-byte $\mathsf{K^{memo}}$, reducing `encCiphertext` from 580 bytes to
100 bytes.

#### Sapling-post-ZIP 231 Effecting Data

The effecting data for the Sapling-post-ZIP 231 bundle describes the Sapling
spends and outputs.

| Bytes | Name | Data Type | Description |
| --- | --- | --- | --- |
| varies | `nSpendsSapling` | `compactSize` | Number of Sapling Spend descriptions. |
| 96 * nSpendsSapling | `vSpendsSapling` | `SaplingSpendEffecting[nSpendsSapling]` | Effecting data for each Sapling Spend. |
| varies | `nOutputsSapling` | `compactSize` | Number of Sapling Output descriptions. |
| 276 * nOutputsSapling | `vOutputsSapling` | `SaplingOutputPostZIP231[nOutputsSapling]` | Sapling Output descriptions. |

As in ZIP 248 <a id="user-content-fnref-zip-0248-5"></a>
[5](/zip/231?part=2#user-content-fn-zip-0248), the anchor is not part of the effecting data; it
appears in the authorizing data below.

`SaplingSpendEffecting` is unchanged from ZIP 248 <a id="user-content-fnref-zip-0248-6"></a>
[5](/zip/231?part=2#user-content-fn-zip-0248).

##### SaplingOutputPostZIP231

| Bytes | Name | Data Type | Description |
| --- | --- | --- | --- |
| 32 | `cv` | `byte[32]` | A value commitment to the net value of the output note. |
| 32 | `cmu` | `byte[32]` | The $u$-coordinate of the note commitment for the output note. |
| 32 | `ephemeralKey` | `byte[32]` | An encoding of an ephemeral Jubjub public key. |
| 100 | `encCiphertext` | `byte[100]` | The encrypted contents of the note plaintext, which contains $\mathsf{K^{memo}}$ in place of the 512-byte memo field. |
| 80 | `outCiphertext` | `byte[80]` | The encrypted contents of the byte string created by concatenation of the transmission key with the ephemeral secret key. |

#### Sapling-post-ZIP 231 Authorizing Data

The authorizing data is unchanged from the Sapling bundle defined in
ZIP 248 <a id="user-content-fnref-zip-0248-7"></a>
[5](/zip/231?part=2#user-content-fn-zip-0248).

| Bytes | Name | Data Type | Description |
| --- | --- | --- | --- |
| 32 | `anchorSapling` | `byte[32]` | A root of the Sapling note commitment tree at some block height in the past. |
| 192 * nSpendsSapling | `vSpendProofsSapling` | `byte[192 * nSpendsSapling]` | Encodings of the zk-SNARK proofs for each Sapling Spend. |
| 64 * nSpendsSapling | `vSpendAuthSigsSapling` | `byte[64 * nSpendsSapling]` | Authorizing signatures for each Sapling Spend. |
| 192 * nOutputsSapling | `vOutputProofsSapling` | `byte[192 * nOutputsSapling]` | Encodings of the zk-SNARK proofs for each Sapling Output. |
| 64 | `bindingSigSapling` | `byte[64]` | A Sapling binding signature on the SIGHASH transaction hash. |

- The field `anchorSapling` is present if and only if
  $\mathtt{nSpendsSapling} > 0$.

- The values of `nSpendsSapling` and `nOutputsSapling` are not re-encoded in
  the authorizing data; they are taken from the corresponding effecting data.

- The field `bindingSigSapling` is present if and only if
  $\mathtt{nSpendsSapling} + \mathtt{nOutputsSapling} > 0$.

- The elements of `vSpendProofsSapling` and `vSpendAuthSigsSapling` have a
  1:1 correspondence to the elements of `vSpendsSapling` in the effecting data
  and MUST be ordered such that the element at a given index corresponds to the
  `SaplingSpendEffecting` at the same index.

- The elements of `vOutputProofsSapling` have a 1:1 correspondence to the
  elements of `vOutputsSapling` in the effecting data and MUST be ordered such
  that the proof at a given index corresponds to the `SaplingOutputPostZIP231`
  at the same index.

<a id="orchard-protocol-post-zip-231-bundle"></a>

### Orchard-protocol-post-ZIP 231 Bundle

The Orchard-protocol-post-ZIP 231 bundle replaces the Orchard protocol bundle
defined in ZIP 248 <a id="user-content-fnref-zip-0248-8"></a>
[5](/zip/231?part=2#user-content-fn-zip-0248), and like it is shared by the Orchard and
Ironwood bundle types. As with Sapling, the only change is that the note
plaintext in each Orchard action is shortened: the 512-byte memo field is
replaced by a 32-byte $\mathsf{K^{memo}}$, reducing `encCiphertext` from
580 bytes to 100 bytes.

#### Orchard-protocol-post-ZIP 231 Effecting Data

The effecting data for an Orchard-protocol-post-ZIP 231 bundle describes the
Orchard actions.

| Bytes | Name | Data Type | Description |
| --- | --- | --- | --- |
| varies | `nActionsOrchard` | `compactSize` | The number of Orchard Action descriptions. |
| 340 * nActionsOrchard | `vActionsOrchard` | `OrchardActionPostZIP231[nActionsOrchard]` | Effecting data for each Orchard Action. |
| 1 | `flagsOrchard` | `byte` | An 8-bit value representing a set of flags. Ordered from LSB to MSB: `enableSpends`, `enableOutputs`, `enableCrossAddress`. The remaining bits are set to $0$. |

- The field `flagsOrchard` is present if and only if
  $\mathtt{nActionsOrchard} > 0$.

- For coinbase transactions, the `enableSpends` bit MUST be set to $0$.

- In an Orchard bundle, the `enableCrossAddress` bit MUST be set to $0$. The bit
  is unrestricted in an Ironwood bundle.

As in ZIP 248 <a id="user-content-fnref-zip-0248-9"></a>
[5](/zip/231?part=2#user-content-fn-zip-0248), the anchor is not part of the effecting data; it
appears in the authorizing data below.

##### OrchardActionPostZIP231

| Bytes | Name | Data Type | Description |
| --- | --- | --- | --- |
| 32 | `cv` | `byte[32]` | A value commitment to the net value of the input note minus the output note. |
| 32 | `nullifier` | `byte[32]` | The nullifier of the input note. |
| 32 | `rk` | `byte[32]` | The randomized validating key for this Action. |
| 32 | `cmx` | `byte[32]` | The $x$-coordinate of the note commitment for the output note. |
| 32 | `ephemeralKey` | `byte[32]` | An encoding of an ephemeral Pallas public key. |
| 100 | `encCiphertext` | `byte[100]` | The encrypted contents of the note plaintext, which contains $\mathsf{K^{memo}}$ in place of the 512-byte memo field. |
| 80 | `outCiphertext` | `byte[80]` | The encrypted contents of the byte string created by concatenation of the transmission key with the ephemeral secret key. |

#### Orchard-protocol-post-ZIP 231 Authorizing Data

The authorizing data is unchanged from the Orchard protocol bundle defined in
ZIP 248 <a id="user-content-fnref-zip-0248-10"></a>
[5](/zip/231?part=2#user-content-fn-zip-0248).

| Bytes | Name | Data Type | Description |
| --- | --- | --- | --- |
| 32 | `anchorOrchard` | `byte[32]` | A root of the note commitment tree of the bundle's pool, at some block height in the past. |
| varies | `sizeProofsOrchard` | `compactSize` | Length in bytes of `proofsOrchard`. Value is $2720 + 2272 \cdot \mathtt{nActionsOrchard}$. |
| sizeProofsOrchard | `proofsOrchard` | `byte[sizeProofsOrchard]` | Encoding of aggregated zk-SNARK proofs for Orchard Actions. |
| 64 * nActionsOrchard | `vSpendAuthSigsOrchard` | `byte[64 * nActionsOrchard]` | Authorizing signatures for each Orchard Action. |
| 64 | `bindingSigOrchard` | `byte[64]` | An Orchard binding signature on the SIGHASH transaction hash. |

- The value of `nActionsOrchard` is not re-encoded in the authorizing data; it
  is taken from the corresponding effecting data.

- The fields `anchorOrchard`, `sizeProofsOrchard`, `proofsOrchard`, and
  `bindingSigOrchard` are present if and only if $\mathtt{nActionsOrchard} > 0$.

- The proofs aggregated in `proofsOrchard`, and the elements of
  `vSpendAuthSigsOrchard`, each have a 1:1 correspondence to the elements of
  `vActionsOrchard` in the effecting data and MUST be ordered such that the
  proof or signature at a given index corresponds to the
  `OrchardActionPostZIP231` at the same index.

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

### Memo bundle

A memo bundle consists of a sequence of 272-byte memo chunks, each encrypting
a 256-byte chunk of memo plaintext. These memo chunks represent zero or more
encrypted memos.

Each V7 transaction may contain a single memo bundle, and a memo bundle may contain
at most $\mathsf{memo\_chunk\_limit}$ = 64 memo chunks. This limits the total amount
of memo data that can be conveyed within a single transaction to
$\mathsf{memo\_chunk\_limit}$ × 256 = 16384 bytes, or 16 KiB.

Memo bundles are encoded in transactions in a prunable manner: the entire
memo bundle can be replaced by a single digest.

<a id="memo-encryption"></a>

### Memo encryption

During transaction construction, each output with memo data is assigned a
32-byte memo key $\mathsf{K^{memo}}$, as follows:

- If an output is intended to be publicly decryptable, it is assigned the memo
  key consisting of 32 $mathtt{0x00}$ bytes.

- If an output has no memo data, it is assigned the memo key consisting of 32
  $\mathtt{0xFF}$ bytes.

- Otherwise, a 32-byte key SHOULD be generated randomly in a way that excludes
  the above-mentioned reserved values from the valid range of the generation
  function.

A single memo key MUST NOT be used to encrypt more than one memo within a
single transaction. To share a memo among multiple recipients, the same
$\mathsf{K^{memo}}$ value is placed in each recipient's note plaintext; this
does not violate the above constraint because all recipients decrypt the same
memo.

In note plaintexts of V7 transactions, the 512-byte memo field
is replaced by the 32-byte $\mathsf{K^{memo}}$.

The explicit encodings of the note plaintexts for Sapling and Orchard outputs
are specified in [Changes to the Zcash Protocol Specification].

The transaction builder generates a 32-byte salt value $\mathsf{salt}$ from a CSPRNG.
A new salt MUST be generated for each memo bundle.

The symmetric encryption key for a memo is derived from its $\mathsf{K^{memo}}$ as follows:

$\hspace{2em}\mathsf{encryption\_key} = \mathsf{PRF^{expand}_{K^{memo}}}([\mathtt{0xE0}] \,||\, \mathsf{salt})$

The first byte $\mathtt{0xE0}$ should be added to the documentation of inputs to
$\mathsf{PRF^{expand}}$ in § 4.1.2 ‘Pseudo Random Functions’ <a id="user-content-fnref-protocol-abstractprfs"></a>
[10](/zip/231?part=2#user-content-fn-protocol-abstractprfs).

If the generated key $\mathsf{encryption\_key}$ is 32 $\mathtt{0xFF}$ bytes,
the transaction constructor SHOULD repeat this procedure with a different salt, in
order to avoid the recipient misinterpreting the output as having no memo data.
Since that has negligible probability, it alternatively could omit this check.

Each memo MUST have a length that is a multiple of 256 bytes. (For UTF-8 text
memos, padding is performed at the application level as specified in ZIP 302
<a id="user-content-fnref-zip-0302"></a>
[11](/zip/231?part=2#user-content-fn-zip-0302).) The memo is split into 256-byte plaintext chunks. Each plaintext
chunk is encrypted with ChaCha20Poly1305 <a id="user-content-fnref-rfc-8439"></a>
[12](/zip/231?part=2#user-content-fn-rfc-8439) as follows:

$\hspace{2em}\mathsf{IETF\_AEAD\_CHACHA20\_POLY1305}(\mathsf{encryption\_key}, \mathsf{nonce}, \mathsf{memo\_chunk})$

where $\mathsf{nonce} = \mathsf{I2BEOSP}_{88}(\mathsf{counter}) \,||\, [\mathsf{is\_final\_chunk}]$.

The resulting 272-byte ciphertext is referred to as a memo chunk:

- $\mathsf{MemoChunk} := \mathbb{B}^{{\kern-0.05em\tiny\mathbb{Y}}[272]}$

This is a variant of the STREAM construction <a id="user-content-fnref-stream"></a>
[13](/zip/231?part=2#user-content-fn-stream).

- $\mathsf{counter}$ is a big-endian chunk counter starting at zero and incrementing
  by one for each subsequent chunk within a particular memo.

- $\mathsf{is\_final\_chunk}$ is the byte $\mathtt{0x01}$ for the final memo chunk, and
  $\mathtt{0x00}$ for all preceding chunks.

Finally, the encrypted memo chunks for all memos are combined into a single
sequence using an order-preserving shuffle. Memo chunks from different memos MAY
be interleaved in any order, but memo chunks from the same memo MUST have the
same relative order.

Given a set of memos, each broken into a sequence of chunks, the shuffle
algorithm SHOULD select each successive chunk by choosing among the sequences
with probability weighted by each sequence's remaining length. That is,
given $N$ lists with remaining lengths $l_1, l_2, \ldots, l_N$, the next chunk
is drawn from list $i$ with probability $l_i / \sum_j l_j$. This can be
implemented by generating a uniform random index $r$ in $[0, \sum_j l_j)$ and
selecting the list $i$ such that $\sum_{j<i} l_j \leq r < \sum_{j \leq i} l_j$.

The shuffle algorithm MUST ensure that a memo recipient learns only (a) the
number of chunks in their own memo and (b) the total number of chunks they
cannot decrypt, but gains no information about how the undecryptable chunks are
distributed among other memos. The above weighting satisfies this property.

The following diagram shows an example shuffle for three memos:

```
[
    (memo_a, 0),
    (memo_b, 0),
    (memo_a, 1),
    (memo_c, 0),
    (memo_c, 1),
    (memo_a, 2),
]
```

#### Memo bundle padding

If the transaction contains any shielded outputs, the memo bundle MUST contain
an even number of memo chunks, with a minimum of 2. If the number of memo
chunks resulting from the encryption and shuffle steps above does not satisfy
this requirement, the transaction builder MUST append padding chunks generated
by encrypting random memo data using a random memo key generated using a CSPRNG
until the total is even and at least 2, **prior to shuffling**. Padding chunks
generated in this fashion will be indistinguishable from real encrypted memo
chunks to an observer who does not hold the memo key for those chunks.

<a id="memo-decryption"></a>

### Memo decryption

When a recipient decrypts a shielded output, they obtain a memo key $\mathsf{K^{memo}}$.
If $\mathsf{K^{memo}}$ consists of 32 $\mathtt{0xFF}$ bytes, no memo is associated with
this output and memo decryption SHOULD NOT be performed. If the memo bundle has
been pruned (i.e. $\mathtt{fAllPruned} = 1$), memo decryption cannot be
attempted, as no memo chunk data is available. Otherwise, the recipient derives
`encryption_key` as above, and then proceeds as follows:

$\hspace{1.5em}$ let mutable $\mathsf{memo} \;{\small ⦂}\; \mathbb{B}^* \leftarrow [] \\$
$\hspace{1.5em}$ let mutable $\mathsf{counter} \;{\small ⦂}\; \mathbb{N} \leftarrow 0 \\$
$\hspace{1.5em}$ let mutable $\mathsf{potential\_last\_chunk\_index} \;{\small ⦂}\; \mathbb{N} \leftarrow 0 \\$
$\hspace{1.5em}$ for $i$ from $0$ up to $\mathtt{nMemoChunks}-1$: $\\$
$\hspace{3.0em}$     let $\mathsf{nonce} = \mathsf{I2BEOSP}_{88}(\mathsf{counter}) \,||\, [\mathtt{0x00}] \\$
$\hspace{3.0em}$     let $P = \mathsf{IETF\_AEAD\_CHACHA20\_POLY1305.Decrypt}(\mathsf{encryption\_key}, \mathsf{nonce}, \mathtt{vMemoChunks}[i]) \\$
$\hspace{3.0em}$     if $P \neq \bot$: $\\$
$\hspace{4.5em}$         set $\mathsf{memo} \leftarrow \mathsf{memo} \,||\, P \\$
$\hspace{4.5em}$         set $\mathsf{counter} \leftarrow \mathsf{counter} + 1 \\$
$\hspace{4.5em}$         set $\mathsf{potential\_last\_chunk\_index} \leftarrow i + 1 \\$
$\,\\$
$\hspace{1.5em}$ let $\mathsf{final\_nonce} = \mathsf{I2BEOSP}_{88}(\mathsf{counter}) \,||\, [\mathtt{0x01}] \\$
$\hspace{1.5em}$ let mutable $\mathsf{success} \leftarrow \kern0.05em$ false $\\$
$\hspace{1.5em}$ for $i$ from $0$ up to $\mathtt{nMemoChunks}-1$: $\\$
$\hspace{3.0em}$     let $P = \mathsf{IETF\_AEAD\_CHACHA20\_POLY1305.Decrypt}(\mathsf{encryption\_key}, \mathsf{final\_nonce}, \mathtt{vMemoChunks}[i]) \\$
$\hspace{3.0em}$     if $i \geq \mathsf{potential\_last\_chunk\_index}$ and $P \neq \bot$ and not $\mathsf{success}$: $\\$
$\hspace{4.5em}$         set $\mathsf{memo} \leftarrow \mathsf{memo} \,||\, P \\$
$\hspace{4.5em}$         set $\mathsf{success} \leftarrow \kern0.05em$ true $\\$
$\,\\$
$\hspace{1.5em}$ if $\mathsf{success}$ then return $\mathsf{memo}$ else return $\bot \\$

This algorithm relies on the order-preserving shuffle invariant from [Memo encryption]:
the final chunk of a memo always appears after all of its non-final chunks. The
$\mathsf{potential\_last\_chunk\_index}$ guard ensures that a chunk is only accepted as
the final chunk if it appears at or after the position following the last successfully
decrypted non-final chunk.

Wallet implementations SHOULD defer memo bundle decryption to an asynchronous
process that is independent of the trial decryption of note ciphertexts.

Rationale for deferred memo decryption

In pre-V7 transactions performed by full-node wallets, trial decryption
of each shielded output performs constant work regardless of whether the output
belongs to the wallet: the AEAD decryption of the full 580-byte note ciphertext
costs the same whether it succeeds or fails.

With memo bundles, successful trial decryption of a 100-byte V7-onward note
ciphertext yields a memo key $\mathsf{K^{memo}}$, and if the key is not the
no-memo sentinel, the wallet must scan up to $\mathsf{memo\_chunk\_limit}$ memo
chunks to recover the memo. This additional work — potentially up to 64 AEAD
operations — creates a timing differential between successful and unsuccessful
trial decryptions that could be exploited by an adversary to determine whether
a node's wallet recognized a particular output.

Deferring memo bundle decryption decorrelates the observable timing of block
processing from whether any notes were successfully decrypted, making it more
difficult for an adversary to attribute memo decryption work to a specific
transaction.

<a id="encoding-in-transactions"></a>

### Encoding in transactions

The following describes the effecting data for the memo bundle. This data
appears in `mEffectBundles` with the memo bundle type identifier.

| Bytes | Name | Data Type | Description |
| --- | --- | --- | --- |
| 1 | $\mathtt{fAllPruned}$ | $\mathtt{uint8}$ | 1 if the memo bundle has been pruned, otherwise 0. |
| 32 | $\mathtt{saltOrHash}$ | $\mathtt{byte}[32]$ | The salt for deriving memo encryption keys, or the memo bundle digest. |
| † varies | $\mathtt{nMemoChunks}$ | $\mathtt{compactSize}$ | The number of memo chunks. |
| † varies | $\mathtt{vMemoChunks}$ | $\mathtt{MemoChunk}[\mathtt{nMemoChunks}]$ | A sequence of encrypted memo chunks. |

† These fields are present if and only if $\mathtt{fAllPruned} = 0$.

If $\mathtt{fAllPruned} = 0$, then:

- $\mathtt{saltOrHash}$ contains the $\mathsf{salt}$ used to derive memo encryption keys
  (see [Memo encryption]).

If $\mathtt{fAllPruned} = 1$, then:

- $\mathtt{saltOrHash}$ contains the $\mathsf{memo\_bundle\_digest}$ as defined in
  [Transaction Digest].

- The $\mathtt{nMemoChunks}$ and $\mathtt{vMemoChunks}$ fields will be absent.

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

### Transaction Digest

The memo bundle contributes to the transaction identifier via the
`effects_bundles_digest` defined in ZIP 248 <a id="user-content-fnref-zip-0248-11"></a>
[5](/zip/231?part=2#user-content-fn-zip-0248).

The memo bundle's effect digest is computed as follows:

$\mathsf{memo\_chunk\_digest}[i] = \mathsf{BLAKE2b\text{-}256}(\texttt{"ZTxIdMemoChunkHs"}, \mathtt{vMemoChunks}[i]) \\$
$\mathsf{memo\_bundle\_digest} = \mathsf{BLAKE2b\text{-}256}(\texttt{"ZTxIdMemoBundHsh"}, \mathsf{concat}(\mathsf{memo\_chunk\_digests}))$

For pruned chunks, the $\mathsf{memo\_chunk\_digest}$ stored in the transaction
encoding is used directly.

The memo bundle digest is used in place of the full memo bundle when
the bundle has been pruned.

TODO: finish this as a modification to ZIP 248 <a id="user-content-fnref-zip-0248-12"></a>
[5](/zip/231?part=2#user-content-fn-zip-0248), which defines
the V7 transaction digest structure. Note that $\mathtt{fAllPruned}$ MUST NOT
contribute to the transaction digest, as it is not part of the committed
transaction data.

<a id="changes-to-zip-317-14"></a>

### Changes to ZIP 317 <a id="user-content-fnref-zip-0317"></a>
[14](/zip/231?part=2#user-content-fn-zip-0317)

The conventional fee in ZEC is altered such that a memo bundle may contain two
free chunks if there are any shielded outputs in the transaction. Any memo chunk
beyond this requires `marginal_fee`. See the Fee calculation section of ZIP 317
<a id="user-content-fnref-zip-0317-fee-calculation"></a>
[15](/zip/231?part=2#user-content-fn-zip-0317-fee-calculation) for details.

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

### Network protocol

Nodes MUST reject `tx` messages having an $\mathtt{fAllPruned}$ value that is
nonzero. A node that has pruned the memo bundle from a transaction MUST NOT serve
that transaction in response to a `getdata` message for `MSG_TX` or `MSG_WTX`; it SHOULD respond as though
the transaction is not available.

Decoupling memo data from note data and making it independently prunable helps
to mitigate the long-term storage costs that memo data imposes on node
operators, while preserving the ability of every node to validate the full
transaction chain using the memo bundle digest.

<a id="light-client-protocol"></a>

### Light client protocol

Light client servers SHOULD include memo bundle pruning status in the compact
transaction format, so that clients can avoid attempting to retrieve memo data
from a server that has pruned it.

<a id="changes-to-the-zcash-protocol-specification"></a>

### Changes to the Zcash Protocol Specification

Changes to the algorithms for encryption and decryption are specified below.
Note that <a id="user-content-fnref-zip-2005"></a>
[16](/zip/231?part=2#user-content-fn-zip-2005) also updates these algorithms, but the merge is trivial.

In § 3.2.1 ‘Note Plaintexts and Memo Fields’:

- Change
  
  
  
  
  > Each Sapling or Orchard note plaintext (denoted $\mathbf{np}$) consists of
  > 
  > 
  > 
  > 
  > $\hspace{2em}(\mathsf{leadByte} \;{\small ⦂}\; \mathbb{B}^{{\kern-0.05em\tiny\mathbb{Y}}}, \mathsf{d} \;{\small ⦂}\; \mathbb{B}^{[\ell_{\mathsf{d}}]}, \mathsf{rseed} \;{\small ⦂}\; \mathbb{B}^{{\kern-0.05em\tiny\mathbb{Y}}[32]}, \mathsf{memo} \;{\small ⦂}\; \mathbb{B}^{{\kern-0.05em\tiny\mathbb{Y}}[512]})$
  
  
  
  
  to
  
  
  
  
  > The form of a Sapling or Orchard note plaintext depends on the version of
  > the transaction in which it will be included; specifically whether that
  > version is pre-V7, or V7-onward.
  > 
  > 
  > 
  > 
  > Each pre-V7 Sapling or Orchard note plaintext (denoted $\mathbf{np}$) consists of
  > 
  > 
  > 
  > 
  > $\hspace{2em}(\mathsf{leadByte} \;{\small ⦂}\; \mathbb{B}^{{\kern-0.05em\tiny\mathbb{Y}}}, \mathsf{d} \;{\small ⦂}\; \mathbb{B}^{[\ell_{\mathsf{d}}]}, \mathsf{rseed} \;{\small ⦂}\; \mathbb{B}^{{\kern-0.05em\tiny\mathbb{Y}}[32]}, \mathsf{memo} \;{\small ⦂}\; \mathbb{B}^{{\kern-0.05em\tiny\mathbb{Y}}[512]})$
  > 
  > 
  > 
  > 
  > Each V7-onward Sapling or Orchard note plaintext (denoted $\mathbf{np}$) consists of
  > 
  > 
  > 
  > 
  > $\hspace{2em}(\mathsf{leadByte} \;{\small ⦂}\; \mathbb{B}^{{\kern-0.05em\tiny\mathbb{Y}}}, \mathsf{d} \;{\small ⦂}\; \mathbb{B}^{[\ell_{\mathsf{d}}]}, \mathsf{rseed} \;⦂\; \mathbb{B}^{{\kern-0.05em\tiny\mathbb{Y}}[32]}, \mathsf{K^{memo}} \;{\small ⦂}\; \mathbb{B}^{{\kern-0.05em\tiny\mathbb{Y}}[32]})$

In § 5.5 ‘Encodings of Note Plaintexts and Memo Fields’ <a id="user-content-fnref-protocol-noteptencoding"></a>
[17](/zip/231?part=2#user-content-fn-protocol-noteptencoding):

- Change the paragraph that describes "The encoding of a Sapling or Orchard note plaintext"
  to refer to "The encoding of a pre-V7 Sapling or Orchard note plaintext".

- Add a new paragraph at the end of the section:
  
  
  
  
  > The encoding of a V7-onward Sapling or Orchard note plaintext consists of:
  > 
  > 
  > 
  > 
  > $\begin{array}{|c|c|c|c|c|} \hline \raisebox{0.6ex}{\mathstrut} \text{8-bit } \mathsf{leadByte} & \text{88-bit } \mathsf{d} & \text{64-bit } \mathsf{v} & \text{256-bit } \mathsf{rseed} & \text{32-byte } \mathsf{K^{memo}} \\\hline \end{array}$
  > 
  > 
  > 
  > 
  > 
  > - A byte {{MBLEADBYTE}}, indicating this version of the encoding of an
  >   V7-onward Sapling or Orchard note plaintext.
  > 
  > - 11 bytes specifying $\mathsf{d}$.
  > 
  > - 8 bytes specifying $\mathsf{v}$.
  > 
  > - 32 bytes specifying $\mathsf{rseed}$.
  > 
  > - 32 bytes specifying $\mathsf{K^{memo}}$.
  > 
  > 
  > 
  > 
  > 
  > A value consisting of 32 $\mathtt{0xFF}$ bytes for $\mathsf{K^{memo}}$ is used
  > to indicate that there is no memo for this note plaintext.

In § 4.7.2 ‘Sending Notes (Sapling)’ <a id="user-content-fnref-protocol-saplingsend"></a>
[18](/zip/231?part=2#user-content-fn-protocol-saplingsend) and
§ 4.7.3 ‘Sending Notes (Orchard)’ <a id="user-content-fnref-protocol-orchardsend"></a>
[19](/zip/231?part=2#user-content-fn-protocol-orchardsend):

- Add a reference to this ZIP specifying the construction of the memo bundle and
  derivation of $\mathsf{K^{memo}}$ in the case of a V7-onward note
  plaintext.

- Change
  
  
  
  
  > Let $\mathbf{np} = (\mathsf{leadByte}, \mathsf{d}, \mathsf{v}, \mathsf{rseed}, \mathsf{memo})$.
  
  
  
  
  to
  
  
  
  
  > Let $\mathbf{np}$ be the encoding of a Sapling note plaintext using $\mathsf{leadByte}$, $\mathsf{d}$,
  > $\mathsf{v}$, $\mathsf{rseed}$, and either $\mathsf{memo}$ for a pre-V7 note plaintext or
  > $\mathsf{K^{memo}}$ for a V7-onward note plaintext.
  
  
  
  
  replacing "Sapling" with Orchard in the case of § 4.7.3.

In § 4.20.1 ‘Encryption (Sapling and Orchard)’ <a id="user-content-fnref-protocol-saplingandorchardinband"></a>
[20](/zip/231?part=2#user-content-fn-protocol-saplingandorchardinband):

## Citation relationships

Cites: [ZIP 200](https://zip-tools-web.harpocrates.workers.dev/zip/200), [ZIP 229](https://zip-tools-web.harpocrates.workers.dev/zip/229), [ZIP 248](https://zip-tools-web.harpocrates.workers.dev/zip/248), [ZIP 259](https://zip-tools-web.harpocrates.workers.dev/zip/259), [ZIP 302](https://zip-tools-web.harpocrates.workers.dev/zip/302), [ZIP 307](https://zip-tools-web.harpocrates.workers.dev/zip/307), [ZIP 317](https://zip-tools-web.harpocrates.workers.dev/zip/317), [ZIP 2005](https://zip-tools-web.harpocrates.workers.dev/zip/2005)

Cited by: [ZIP 230](https://zip-tools-web.harpocrates.workers.dev/zip/230), [ZIP 248](https://zip-tools-web.harpocrates.workers.dev/zip/248), [ZIP 317](https://zip-tools-web.harpocrates.workers.dev/zip/317), [ZIP 374](https://zip-tools-web.harpocrates.workers.dev/zip/374), [ZIP 2005](https://zip-tools-web.harpocrates.workers.dev/zip/2005)