Skip to content
ZIPs.tools

ZIP 231

Memo Bundles

  • Draft
Proposal metadata
ID
231
Status
Draft
Category
Consensus / Wallet
Owners
  • Jack Grigg
  • Kris Nuttycombe
  • Daira-Emma Hopwood
  • Arya Solhi
Created
2024-04-26
License
MIT
Contents
  1. Terminology
  2. Abstract
  3. Motivation
  4. Privacy Implications
  5. Requirements
  6. Non-requirements
  7. Specification
  8. Changes to ZIP 248
  9. Sapling-post-ZIP 231 Bundle
  10. Orchard-protocol-post-ZIP 231 Bundle
  11. Memo bundle
  12. Memo encryption
  13. Memo decryption
  14. Encoding in transactions
  15. Transaction Digest
  16. Changes to ZIP 317 14
  17. Network protocol
  18. Light client protocol
  19. Changes to the Zcash Protocol Specification
Preamble
ZIP
231
Title
Memo Bundles
Credits
  • Sean Bowe
  • Nate Wilcox
Status
Draft
Category
Consensus / Wallet
Created
2024-04-26
License
MIT

Terminology

The key words "MUST", "MUST NOT", "SHOULD", and "MAY" in this document are to be interpreted as described in BCP 14 1 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. 2

The character § is used when referring to sections of the Zcash Protocol Specification. 3

The terms "Mainnet" and "Testnet" are to be interpreted as described in § 3.12 ‘Mainnet and Testnet’. 4

The terms "V7 transaction format", "V7 transaction", and "pre-V7 transaction format" are to be interpreted as defined in ZIP 248. 5 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. 6 The term "Orchard protocol bundle" is to be interpreted as defined in ZIP 248. 5

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.

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 7. 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×n512 \times n bytes on-chain, where nn 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 8, 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 9.

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.

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.

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.

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.

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.

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

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.

Sapling-post-ZIP 231 Bundle

The Sapling-post-ZIP 231 bundle replaces the Sapling bundle defined in ZIP 248 5. 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 Kmemo\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 5, the anchor is not part of the effecting data; it appears in the authorizing data below.

SaplingSpendEffecting is unchanged from ZIP 248 5.

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 uu-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 Kmemo\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 5.

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 nSpendsSapling>0\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 nSpendsSapling+nOutputsSapling>0\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.

Orchard-protocol-post-ZIP 231 Bundle

The Orchard-protocol-post-ZIP 231 bundle replaces the Orchard protocol bundle defined in ZIP 248 5, 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 Kmemo\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 00.
  • The field flagsOrchard is present if and only if nActionsOrchard>0\mathtt{nActionsOrchard} > 0.
  • For coinbase transactions, the enableSpends bit MUST be set to 00.
  • In an Orchard bundle, the enableCrossAddress bit MUST be set to 00. The bit is unrestricted in an Ironwood bundle.

As in ZIP 248 5, 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 xx-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 Kmemo\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 5.

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⋅nActionsOrchard2720 + 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 nActionsOrchard>0\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.

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 memo_chunk_limit\mathsf{memo\_chunk\_limit} = 64 memo chunks. This limits the total amount of memo data that can be conveyed within a single transaction to memo_chunk_limit\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.

Memo encryption

During transaction construction, each output with memo data is assigned a 32-byte memo key Kmemo\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 0xFF\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 Kmemo\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 Kmemo\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 salt\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 Kmemo\mathsf{K^{memo}} as follows:

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

The first byte 0xE0\mathtt{0xE0} should be added to the documentation of inputs to PRFexpand\mathsf{PRF^{expand}} in § 4.1.2 ‘Pseudo Random Functions’ 10.

If the generated key encryption_key\mathsf{encryption\_key} is 32 0xFF\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 11.) The memo is split into 256-byte plaintext chunks. Each plaintext chunk is encrypted with ChaCha20Poly1305 12 as follows:

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

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

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

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

This is a variant of the STREAM construction 13.

  • counter\mathsf{counter} is a big-endian chunk counter starting at zero and incrementing by one for each subsequent chunk within a particular memo.
  • is_final_chunk\mathsf{is\_final\_chunk} is the byte 0x01\mathtt{0x01} for the final memo chunk, and 0x00\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 NN lists with remaining lengths l1,l2,…,lNl_1, l_2, \ldots, l_N, the next chunk is drawn from list ii with probability li/∑jljl_i / \sum_j l_j. This can be implemented by generating a uniform random index rr in [0,∑jlj)[0, \sum_j l_j) and selecting the list ii such that ∑j<ilj≤r<∑j≤ilj\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.

Memo decryption

When a recipient decrypts a shielded output, they obtain a memo key Kmemo\mathsf{K^{memo}}. If Kmemo\mathsf{K^{memo}} consists of 32 0xFF\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. fAllPruned=1\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 memo  ⦂  B∗←[]\mathsf{memo} \;{\small ⦂}\; \mathbb{B}^* \leftarrow [] \\ \hspace{1.5em} let mutable counter  ⦂  N←0\mathsf{counter} \;{\small ⦂}\; \mathbb{N} \leftarrow 0 \\ \hspace{1.5em} let mutable potential_last_chunk_index  ⦂  N←0\mathsf{potential\_last\_chunk\_index} \;{\small ⦂}\; \mathbb{N} \leftarrow 0 \\ \hspace{1.5em} for ii from 00 up to nMemoChunks−1\mathtt{nMemoChunks}-1: \\ \hspace{3.0em} let nonce=I2BEOSP88(counter) ∣∣ [0x00]\mathsf{nonce} = \mathsf{I2BEOSP}_{88}(\mathsf{counter}) \,||\, [\mathtt{0x00}] \\ \hspace{3.0em} let P=IETF_AEAD_CHACHA20_POLY1305.Decrypt(encryption_key,nonce,vMemoChunks[i])P = \mathsf{IETF\_AEAD\_CHACHA20\_POLY1305.Decrypt}(\mathsf{encryption\_key}, \mathsf{nonce}, \mathtt{vMemoChunks}[i]) \\ \hspace{3.0em} if P≠⊥P \neq \bot: \\ \hspace{4.5em} set memo←memo ∣∣ P\mathsf{memo} \leftarrow \mathsf{memo} \,||\, P \\ \hspace{4.5em} set counter←counter+1\mathsf{counter} \leftarrow \mathsf{counter} + 1 \\ \hspace{4.5em} set potential_last_chunk_index←i+1\mathsf{potential\_last\_chunk\_index} \leftarrow i + 1 \\  \,\\ \hspace{1.5em} let final_nonce=I2BEOSP88(counter) ∣∣ [0x01]\mathsf{final\_nonce} = \mathsf{I2BEOSP}_{88}(\mathsf{counter}) \,||\, [\mathtt{0x01}] \\ \hspace{1.5em} let mutable success←\mathsf{success} \leftarrow \kern0.05em false \\ \hspace{1.5em} for ii from 00 up to nMemoChunks−1\mathtt{nMemoChunks}-1: \\ \hspace{3.0em} let P=IETF_AEAD_CHACHA20_POLY1305.Decrypt(encryption_key,final_nonce,vMemoChunks[i])P = \mathsf{IETF\_AEAD\_CHACHA20\_POLY1305.Decrypt}(\mathsf{encryption\_key}, \mathsf{final\_nonce}, \mathtt{vMemoChunks}[i]) \\ \hspace{3.0em} if i≥potential_last_chunk_indexi \geq \mathsf{potential\_last\_chunk\_index} and P≠⊥P \neq \bot and not success\mathsf{success}: \\ \hspace{4.5em} set memo←memo ∣∣ P\mathsf{memo} \leftarrow \mathsf{memo} \,||\, P \\ \hspace{4.5em} set success←\mathsf{success} \leftarrow \kern0.05em true \\  \,\\ \hspace{1.5em} if success\mathsf{success} then return memo\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 potential_last_chunk_index\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 Kmemo\mathsf{K^{memo}}, and if the key is not the no-memo sentinel, the wallet must scan up to memo_chunk_limit\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.


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 fAllPruned\mathtt{fAllPruned} uint8\mathtt{uint8} 1 if the memo bundle has been pruned, otherwise 0.
32 saltOrHash\mathtt{saltOrHash} byte[32]\mathtt{byte}[32] The salt for deriving memo encryption keys, or the memo bundle digest.
† varies nMemoChunks\mathtt{nMemoChunks} compactSize\mathtt{compactSize} The number of memo chunks.
† varies vMemoChunks\mathtt{vMemoChunks} MemoChunk[nMemoChunks]\mathtt{MemoChunk}[\mathtt{nMemoChunks}] A sequence of encrypted memo chunks.

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

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

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

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

  • saltOrHash\mathtt{saltOrHash} contains the memo_bundle_digest\mathsf{memo\_bundle\_digest} as defined in [Transaction Digest].
  • The nMemoChunks\mathtt{nMemoChunks} and vMemoChunks\mathtt{vMemoChunks} fields will be absent.

Transaction Digest

The memo bundle contributes to the transaction identifier via the effects_bundles_digest defined in ZIP 248 5.

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

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

For pruned chunks, the memo_chunk_digest\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 5, which defines the V7 transaction digest structure. Note that fAllPruned\mathtt{fAllPruned} MUST NOT contribute to the transaction digest, as it is not part of the committed transaction data.

Changes to ZIP 317 14

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 15 for details.

Network protocol

Nodes MUST reject tx messages having an fAllPruned\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.

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.

Changes to the Zcash Protocol Specification

Changes to the algorithms for encryption and decryption are specified below. Note that 16 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 np\mathbf{np}) consists of

    (leadByte  ⦂  BY,d  ⦂  B[ℓd],rseed  ⦂  BY[32],memo  ⦂  BY[512])\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 np\mathbf{np}) consists of

    (leadByte  ⦂  BY,d  ⦂  B[ℓd],rseed  ⦂  BY[32],memo  ⦂  BY[512])\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 np\mathbf{np}) consists of

    (leadByte  ⦂  BY,d  ⦂  B[ℓd],rseed  ⦂  BY[32],Kmemo  ⦂  BY[32])\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’ 17:

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

    (8-bit leadByte88-bit d64-bit v256-bit rseed32-byte Kmemo\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 d\mathsf{d}.
    • 8 bytes specifying v\mathsf{v}.
    • 32 bytes specifying rseed\mathsf{rseed}.
    • 32 bytes specifying Kmemo\mathsf{K^{memo}}.

    A value consisting of 32 0xFF\mathtt{0xFF} bytes for Kmemo\mathsf{K^{memo}} is used to indicate that there is no memo for this note plaintext.

In § 4.7.2 ‘Sending Notes (Sapling)’ 18 and § 4.7.3 ‘Sending Notes (Orchard)’ 19:

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

  • Change

    Let np=(leadByte,d,v,rseed,memo)\mathbf{np} = (\mathsf{leadByte}, \mathsf{d}, \mathsf{v}, \mathsf{rseed}, \mathsf{memo}).

    to

    Let np\mathbf{np} be the encoding of a Sapling note plaintext using leadByte\mathsf{leadByte}, d\mathsf{d}, v\mathsf{v}, rseed\mathsf{rseed}, and either memo\mathsf{memo} for a pre-V7 note plaintext or Kmemo\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)’ 20:

Citation graph

Hover or tap a node to see its title and citations.
Loading citation graph…
ZIP 231: Memo Bundles · ZIPs.tools