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
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 5. 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 fully transfer a note 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 bytes on-chain, where 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 addresses, 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 6, 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 standard security notions for an authenticated encryption scheme.
By decoupling memo data from note ciphertexts, this proposal reduces the v6-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, rectifying the security shortcoming 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.
Use Cases
Authenticated Reply-to addresses
In a peer-to-peer transaction Bob and Alice can split a lunch check. Bob can send a shielded memo to Alice that says "Nice Lunch! It was fun! this is my half + tips. Hugs, Bob.". Bob would show Alice his transaction detail in his wallet and Alice can visibly check hers and verify that the memo contained in that transaction was authored by Bob. That in-person manual verification process is only valid for that one transaction. Anyone that knows Alice's address and the fact that she exchanged messages over Zcash memos with Bob could spoof a shielded memo impersonating Bob. Neither Alice or her wallet have an aid to judge the authorship of any of the received messages.
If Bob's wallet had the capability to prove the authorship of a message, Alice's wallet could help her notice such a spoofing attempt. The rationale of this use case is that application developers can use this mechanism to build a user experience that can help users tell apart trustworthy memos from those whose origin cannot be certainly determined.
It has to be noted that providing a mechanism for wallet applications to attach data that can help prove the origin of a transaction through the memo field will not prevent that spoofers attempt such attacks. Spoofers continue to send memos to attempt to deceive a user. The proving data is not self-sufficient to determine that the author of a shielded memo shall be trusted. Further verification steps would be needed to determine a such a level of trust. This specification enables wallet developers to build a user experience that helps Zcash wallet users to identify authentic memos containing Reply-to addresses from spoofed ones. The definition of such UX is outside of the scope of this ZIP.
Memos as input for application layer
Using unverifiable memo data as input for business logic is unadvisable. Repeatedly, application developers have discussed memos an on-chain data layer for privacy-minded software such as Instant (or ansynchronous) messaging applications or marketplaces. These applications require that the memo information can be trusted and therefore be treated as actionable data by the application business logic. Without such a mechanism the application does not have a way to choose which messages should be trusted and which ones shall be ignored. The ability to attach proofs to memos provides application developers with mechanisms to distinguish plausible user intent from arbitrary memos generated outside the application domain.
Broadcasting a newsletter
The ZecHub DAO runs a "Shielded Newsletter" service 7. Users subscribe by sending a one-time transaction with a subscription fee and a shielded memo that tells the service where to send the newsletter to. As discussed in the motivation section, Memo bundles will help the newsletter service to avoid chain bloat by enabling subscribers to share a single key to decrypt a single memo instead of sending individual memos to each one of them. Additionally, by requiring subscribers to prove the authorship of their subscription memos, the newsletter can verify that the recipient address indicated in the one-time subscription memo is indeed the author of the subscription and not a subscription bot attempting to perform a pseudospoofing attack against the service. Finally, the newsletter service can require one-time unsubscribe messages to attach a proof-of-authorship that proves that the sender owns spend authority over the address that the unsubcription is being requested for.
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 transaction with no memo would be distinguishable from one with a memo.
To mitigate this, a 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.
- Memo chunks within a transaction can be individually pruned from block storage without preventing the transaction from being verified when transmitting block data to 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 can 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 v6 and later transactions, it is not necessary to consider Sprout JoinSplit outputs. The following sections apply to both Sapling and Orchard outputs.
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 transaction may contain a single memo bundle, and a memo bundle may contain at most = 64 memo chunks. This limits the total amount of memo data that can be conveyed within a single transaction to × 256 = 16384 bytes, or 16 KiB.
If the transaction contains any shielded outputs, its memo bundle MUST contain an even number of memo chunks, with a minimum of 2. See [Memo bundle padding] for details.
Memo bundles are encoded in transactions in a prunable manner: each memo chunk can be replaced by its representative digest.
Memo encryption
During transaction construction, each output with memo data is assigned a 32-byte memo key , 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 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 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 v6-onward transactions, the 512-byte memo field is replaced by the 32-byte .
The explicit encodings of the note plaintexts for Sapling and Orchard outputs are specified in ZIP 230 8.
The transaction builder generates a 32-byte salt value from a CSPRNG. A new salt MUST be generated for each memo bundle.
The symmetric encryption key for a memo is derived from its as follows:
The first byte should be added to the documentation of inputs to in § 4.1.2 ‘Pseudo Random Functions’ 9.
If the generated key is 32 bytes, the transaction constructor MAY 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 MAY 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 10.) The memo is split into 256-byte plaintext chunks. Each plaintext chunk is encrypted with ChaCha20Poly1305 11 as follows:
where .
The resulting 272-byte ciphertext is referred to as a memo chunk:
This is a variant of the STREAM construction 12.
- is a big-endian chunk counter starting at zero and incrementing by one for each subsequent chunk within a particular memo.
- is the byte for the final memo chunk, and 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.
The shuffle algorithm SHOULD select each successive chunk by choosing among the remaining lists with probability weighted by remaining list length. That is, given lists with remaining lengths , the next chunk is drawn from list with probability . This can be implemented by generating a uniform random index in and selecting the list such that .
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. 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 .
If consists of 32 bytes, no memo is associated with
this output and memo decryption SHOULD NOT be performed. Otherwise, the recipient
derives encryption_key as above, and then proceeds as follows:
let mutable let mutable let mutable for from up to : if is not pruned: let let if : set set set let let mutable false for from up to : if is not pruned: let if and and not : set set true if then return else return
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 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.
If any chunk of the memo encrypted to has been pruned, the decryption process above returns nothing (as will be set to the wrong counter value followed by ), ensuring that a malformed memo is not returned.
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-v6 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 v6 note ciphertext yields a memo key , and if the key is not the no-memo sentinel, the wallet must scan up to 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
| Bytes | Name | Data Type | Description |
|---|---|---|---|
| 1 | 1 if all chunks have been pruned, otherwise 0. | ||
| 32 | The salt for deriving memo encryption keys, or the memo bundle digest. | ||
| † varies | The number of memo chunks. | ||
| † varies | Bitflags indicating the type of each entry in . | ||
| † varies | A sequence of encrypted memo chunks. |
† These fields are present if and only if .
If , then:
- contains the used to derive memo encryption keys (see [Memo encryption]).
- Each bit of , in little-endian order, indicates the type of the corresponding entry in . A bit value of 0 indicates that the entry will be of type representing an encrypted memo chunk. A bit value of 1 indicates the entry will be a and contains the for a pruned chunk.
If , then:
- contains the as defined in [Transaction sighash].
- The , , and fields will be absent.
Transaction sighash
The memo bundle digest structure is a performance optimization for the case where all memo chunks in a transaction have been pruned.
TODO: finish this as a modification to ZIP 248 13, which defines the v6 transaction digest structure. Note that 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 GetData responses having an value that is nonzero,
or any byte of that is nonzero. A node that has pruned any memo chunks
from a transaction MUST NOT serve that transaction in response to a GetData request;
it SHOULD respond as though the transaction is not available.
Because memo data MAY be pruned from stored blocks, nodes that have pruned memo data are not required to serve complete block data for affected transactions. However, to support reliable memo delivery, it would be beneficial for the Zcash community to establish expectations for how long after block confirmation a node is expected to retain complete (unpruned) memo data. Such a retention policy would allow wallet implementations to make reasonable assumptions about memo availability when recovering from backup or performing initial synchronization.
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.
Changes to the Zcash Protocol Specification
The changes to support memo bundles that affect the definitions of note plaintexts and note ciphertexts, interact with the addition of an field to v6-onward Orchard note plaintexts in order to support ZSAs, which must be merged with the changes in this section.
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 ) consists of
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-v6, or v6-onward.
Each pre-v6 Sapling or Orchard note plaintext (denoted ) consists of
Each v6-onward Sapling or Orchard note plaintext (denoted ) consists of
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-v6 Sapling or Orchard note plaintext".
-
Add a new paragraph at the end of the section:
The encoding of a v6-onward Sapling or Orchard note plaintext consists of:
- A byte 0x03, indicating this version of the encoding of a v6-onward Sapling or Orchard note plaintext.
- 11 bytes specifying .
- 8 bytes specifying .
- 32 bytes specifying .
- 32 bytes specifying .
A value consisting of 32 bytes for 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 in the case of a v6-onward note plaintext.
-
Change
Let .
to
Let be the encoding of a Sapling note plaintext using , , , , and either for a pre-v6 note plaintext or for a v6-onward note plaintext.
replacing "Sapling" with Orchard in the case of § 4.7.3.
In § 4.20.1 ‘Encryption (Sapling and Orchard)’ 20:
-
For v6-onward note ciphertexts, the KDF used to derive the note encryption key MUST use a different BLAKE2b-256 personalization string than for pre-v6 note ciphertexts, to provide domain separation. The personalization string is formed by concatenating a protocol prefix with the 4-byte little-endian encoding of the transaction version group id ():
For v6-onward Sapling note ciphertexts:
\hspace{2em}\mathsf{KDF^{Sapling}}(\mathsf{sharedSecret}, \mathsf{ephemeralKey}) := \mathsf{BLAKE2b\text{-}256}(\text{"Zc_SaplingKD"} \,||\, \mathsf{I2LEOSP}_{32}(\mathsf{nVersionGroupId}), \mathsf{sharedSecret} \,||\, \mathsf{ephemeralKey}) ParseError: Expected 'EOF', got '_' at position 122: …}256}(\text{"Zc_̲SaplingKD"} \,|…
For v6-onward Orchard note ciphertexts:
\hspace{2em}\mathsf{KDF^{Orchard}}(\mathsf{sharedSecret}, \mathsf{ephemeralKey}) := \mathsf{BLAKE2b\text{-}256}(\text{"Zc_OrchardKD"} \,||\, \mathsf{I2LEOSP}_{32}(\mathsf{nVersionGroupId}), \mathsf{sharedSecret} \,||\, \mathsf{ephemeralKey}) ParseError: Expected 'EOF', got '_' at position 122: …}256}(\text{"Zc_̲OrchardKD"} \,|…
This prevents a malicious lightwalletd server from presenting a v6 note ciphertext to a wallet as though it were a partial v5 ciphertext, which could otherwise cause the wallet to decrypt and act on unauthenticated plaintext.
-
Change
Let be the Sapling or Orchard note plaintext. is encoded as defined in § 5.5 ‘Encodings of Note Plaintexts and Memo Fields’.
to
Let be the encoding of the Sapling or Orchard note plaintext (which may be pre-v6 or v6-onward), as defined in § 5.5 ‘Encodings of Note Plaintexts and Memo Fields’.
-
Add another normative note to that section:
- will be of length either 580 or 100 bytes, depending on whether is a pre-v6 or v6-onward note plaintext.
In § 4.20.2 ‘Decryption using an Incoming Viewing Key (Sapling and Orchard)’ 21 and § 4.20.3 ‘Decryption using an Outgoing Viewing Key (Sapling and Orchard)’ 22:
- Replace with .
- Specify that the type of is when decrypting a pre-v6 note ciphertext, or when decrypting a v6-onward note ciphertext. In the latter case, it is used as to decrypt the memo bundle as described in [Memo bundle].
Applicability
All of these changes apply identically to Mainnet and Testnet.
Open issues
Interaction with ZIP 302 10
TBD
Rationale
Memo bundle size restriction
Restricting the total amount of memo data in a bundle, for example to 16 KiB, limits the rate at which the chain size can grow cheaply (from a computational perspective; memo bundles are much easier to produce than proofs or signatures).
The current behaviour for previous transaction versions (no limit on the number of memos) is not altered by this ZIP, because memos in those transactions are tied to individual shielded outputs (incurring their computational cost), and are not natively aggregatable.
Independent fee policy for memo data
By decoupling memos from shielded outputs, this ZIP creates an opportunity to impose economic disincentives on adding memo data to the chain without affecting the shielded value transfer protocols. Because the fee for memo data is independent of the fee for value transfer, it can be adjusted independently — for example, to impose a superlinear cost curve for large amounts of memo data, or to increase memo fees in response to chain growth, without any impact on the cost of ordinary shielded transfers. More generally, this decoupling makes it straightforward to impose restrictions on memo creation that cannot currently be imposed without also affecting value transfer.
Memo chunk size
To understand the effect of memo chunk size, we construct a table showing the total amount of data stored on-chain when encoding 16 KiB of memo data to as many recipients as possible.
Each table entry has the format " @ ()" where is the maximum number of distinct recipients you can have within the memo data limits, is the cost in bytes of that memo data plus memo keys and authentication tags when using a 32-byte memo key, and is the relative overhead compared to pre-ZIP-231 memos.
Calculation details
Let:
- be the limit on total memo data (16384 bytes);
- be the plaintext chunk size (256 or 512 bytes);
- be the maximum length of an individual memo (256 or 512 bytes);
- be the key size (32 bytes);
- be the authentication tag size (16 bytes);
- be the limit on the number of chunks (32 or ).
Then
- ;
- ; and
- .
| Chunk size | Memo size ≤ 256 bytes | Memo size = 512 bytes |
|---|---|---|
| Pre-231 | 32 @ 16384 ( 0.00%) | 32 @ 16384 ( 0.00%) |
| 512 | 32 @ 17920 (+ 9.38%) | 32 @ 17920 (+ 9.38%) |
| 256 | 64 @ 19456 (+18.75%) | 32 @ 18432 (+12.50%) |
| 256 32-out | 32 @ 9728 (-40.63%) |
In the "256 32-out" case you have a distinguisher compared to old transactions, in that you can tell the transaction is sending at most 256 bytes per recipient rather than 512 if it is sending the maximum number of memos. But that's inherently baked into the decision to use a smaller plaintext chunk size (and it is still possible for the chunks to all be a single memo sent to all outputs, or anything in between).
Memo key size
16-byte (128-bit) keys don't meet Zcash's target security level of 125 bits, as argued in 23.
However, for the sake of argument, if we used a 16-byte memo key instead of 32 bytes, the transaction size overhead would become:
| Chunk size | Memo size ≤ 256 bytes | Memo size = 512 bytes |
|---|---|---|
| Pre-231 | 32 @ 16384 ( 0.00%) | 32 @ 16384 ( 0.00%) |
| 512 | 32 @ 17408 (+ 6.25%) | 32 @ 17408 (+ 6.25%) |
| 256 | 64 @ 18432 (+12.50%) | 32 @ 17920 (+ 9.38%) |
| 256 32-out | 32 @ 9216 (-43.75%) |
The decrease in overhead is relatively modest in most cases, but more noticeable for small memos with a 256-byte plaintext chunk.
The benefits of 256-bit keys are:
- They incur only a small transaction size overhead above the minimum key size that would meet the target security level.
- This key length matches what we already use elsewhere for symmetric keys.
Encryption format
Including a per-transaction in the derivation of gives protection against accidental (or intentional) reuse of across multiple transactions. We do not protect against reuse within a transaction; it is up to the transaction builder to ensure that the same is not used to encrypt two different memos (and if they did so, normal clients would either never observe the second memo, or would decrypt parts of each memo and get a nonsensical and potentially insecure "spliced" memo).
We do not include commitments to the shielded outputs in the derivation of for two reasons:
- It would force the transaction builder to fully define all shielded outputs before encrypting the memos, which might prevent potential use cases of PCZTs 24.
- We don't want to unnecessarily prevent the ability to create a transaction with a memo bundle and no shielded outputs, as there may be use cases for, e.g. a fully-transparent transaction with encrypted memo, or a ZSA issuance transaction with exposed memo data using a well-known .
Domain separation of note ciphertexts
V6-onward note ciphertexts use KDF personalization strings that incorporate the transaction version group id (\text{"Zc_SaplingKD"} \,||\, \mathsf{nVersionGroupId} ParseError: Expected 'EOF', got '_' at position 10: \text{"Zc_̲SaplingKD"} \,|… and \text{"Zc_OrchardKD"} \,||\, \mathsf{nVersionGroupId} ParseError: Expected 'EOF', got '_' at position 10: \text{"Zc_̲OrchardKD"} \,|…), providing domain separation from pre-v6 note ciphertexts. This prevents a downgrade attack in which a malicious lightwalletd server presents a v6 note ciphertext as a partial v5 note ciphertext. Without domain separation, a wallet that supports the v5 partial-ciphertext optimization (downloading note ciphertexts without the MAC to save bandwidth) could decrypt a v6 ciphertext without verifying its authentication tag, potentially acting on unauthenticated data. Using the version group id rather than a fixed suffix ensures that domain separation extends automatically to future transaction versions.
Pruned encoding
The separation of memo data from note data, and the new ability to easily store variable-length memo data, opens up an attack vector against node operators for storing arbitrary data. The transaction digest commitments to the memo bundle are structured such that if a node operator is presented with a memo key (i.e. they are given the capability to decrypt a particular memo), they can identify and prune the corresponding memo chunks, while still enabling the transaction to be validated as part of its corresponding block and broadcast over the network.
The transaction encoding permits pruning at the individual chunk level in order to facilitate pruning an individual memo from a transaction without affecting the other memos. This enables node operators to be responsive to, for example, GDPR deletion requests.
Note that broadcasting a partially-pruned transaction means that the pruned chunks no longer contribute to the upper bound on memo data.
The prunable structure does not introduce a censorship axis; memo bundles do not reveal which memo chunks correspond to which memos, and therefore a network adversary cannot selectively censor individual memos. They can censor any/all chunks within specific transactions, however shielded transactions do not reveal their senders, recipients, or amounts, and thus also cannot be individually targeted for censorship.
Deployment
This ZIP is proposed to activate with Network Upgrade 7. 25
Reference implementation
TBD
Alternatives
- An alternative chunk size could be selected. The 256-byte chunk size was chosen because the 16-byte AEAD tag for each chunk introduces a 6.25% per-chunk overhead; smaller chunk sizes would require a higher percentage of the memo bundle data to be devoted to encryption overhead, while larger chunk sizes would tend to bloat the chain.
- The 64-chunk limit could be modified to be either smaller or larger. However, as the smallest Orchard authenticated reply-to address proofs are approximately 4 kilobytes in size, the limit needs to be sufficient to permit this use case.
- The fee algorithm could be modified to be a superlinear function of the number of memo chunks, in order to economically discourage the use of large amounts of memo data.
References
Footnotes
-
Information on BCP 14 — "RFC 2119: Key words for use in RFCs to Indicate Requirement Levels" and "RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words" ↩
-
Zcash Protocol Specification, Version 2025.6.2 [NU6.1] or later ↩
-
Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 3.12: Mainnet and Testnet ↩
-
Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 3.2.1: Note Plaintexts and Memo Fields ↩
-
ZIP 230: Version 6 Transaction Format — Orchard Note Plaintext ↩
-
Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 4.1.2: Pseudo Random Functions ↩
-
Online Authenticated-Encryption and its Nonce-Reuse Misuse-Resistance ↩
-
ZIP 248: Extensible Transaction Format (PR: zcash/zips#1156) ↩
-
ZIP 317: Proportional Transfer Fee Mechanism - Fee calculation ↩
-
Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 5.5: Encodings of Note Plaintexts and Memo Fields ↩
-
Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 4.7.2: Sending Notes (Sapling) ↩
-
Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 4.7.3: Sending Notes (Orchard) ↩
-
Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 4.20.1: Encryption (Sapling and Orchard) ↩
-
Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 4.20.2: Decryption using an Incoming Viewing Key (Sapling and Orchard) ↩
-
Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 4.20.3: Decryption using an Outgoing Viewing Key (Sapling and Orchard) ↩
-
Zcash Protocol Specification, Version 2025.6.2 [NU6.1]. Section 8.7: In-band secret distribution ↩
-
zcash/zips issue #693: Standardize a protocol for creating shielded transactions offline ↩
-
draft-arya-deploy-nu7: Deployment of the NU7 Network Upgrade ↩