Skip to content
ZIPs.tools

ZIP 248

Extensible Transaction Format

  • Draft
Proposal metadata
ID
248
Status
Draft
Category
Consensus / Wallet
Owners
  • Jack Grigg
  • Kris Nuttycombe
  • Daira-Emma Hopwood
  • Schell Scivally
Created
2025-12-17
License
MIT
Contents
  1. Rationale
  2. Deployment
  3. Reference implementation
  4. Open issues
  5. Notes from design sessions
  6. Strawman
  7. Strawman II
  8. Questions
  9. TODO
  10. References
TransparentOutput
Bytes Name Data Type Description
8 value int64 The value of the output in zatoshi.
varies scriptPubKeyLen compactSize Length of the scriptPubKey.
scriptPubKeyLen scriptPubKey byte[scriptPubKeyLen] The script that must be satisfied to spend this output.
Transparent Authorizing Data

The authorizing data for the transparent bundle contains the scripts that authorize spending of the referenced transparent inputs.

Bytes Name Data Type Description
varies tx_in_auth TransparentInputAuth[tx_in_count] Authorizing data for each transparent input. The number of entries MUST equal tx_in_count from the effecting data.
TransparentInputAuth
Bytes Name Data Type Description
varies sighashInfo TransparentSighashInfo The sighash version and associated information for this input, as defined in Transparent Sighash Information (TransparentSighashInfo).
varies scriptSigLen compactSize Length of the scriptSig.
scriptSigLen scriptSig byte[scriptSigLen] The script satisfying the conditions of the referenced output's scriptPubKey.
Transparent Sighash Information (TransparentSighashInfo)
Bytes Name Data Type Description
varies sizeSighashInfo compactSize The size in bytes of sighashInfo.
sizeSighashInfo sighashInfo byte[sizeSighashInfo] The sighash version and associated information, as defined in Sighash Versioning.
Transparent Sighash Algorithms

For the transparent bundle at bundleVariant = 0, sighash version 0 is the only version defined, and its associatedData is the empty byte string. A signature made with it commits to the digest defined in S.3.0: transparent_sig_digest.

Transparent Digests
T.3.0: transparent_effects_digest

In the case that transparent inputs or outputs are present, the transparent effects digest is a BLAKE2b-256 hash of the following values:

T.3.0a: prevouts_digest  (32-byte hash)
T.3.0b: sequence_digest  (32-byte hash)
T.3.0c: outputs_digest   (32-byte hash)

The personalization field of this hash is set to:

"ZTxIdTranspaHash"

In the case that the transaction has no transparent components, transparent_effects_digest is:

BLAKE2b-256("ZTxIdTranspaHash", [])
T.3.0a: prevouts_digest

A BLAKE2b-256 hash of the field encoding of all (prevout_hash, prevout_index) pairs from the transparent effecting data.

The personalization field of this hash is set to:

"ZTxIdPrevoutHash"

In the case that the transaction has transparent outputs but no transparent inputs, prevouts_digest is:

BLAKE2b-256("ZTxIdPrevoutHash", [])
T.3.0b: sequence_digest

A BLAKE2b-256 hash of the 32-bit little-endian representation of all nSequence field values from the transparent effecting data.

The personalization field of this hash is set to:

"ZTxIdSequencHash"

In the case that the transaction has transparent outputs but no transparent inputs, sequence_digest is:

BLAKE2b-256("ZTxIdSequencHash", [])
T.3.0c: outputs_digest

A BLAKE2b-256 hash of the concatenated field encodings of all transparent outputs. The field encoding of each output consists of the encoded output value (8-byte little endian) followed by the scriptPubKey byte array (with leading compactSize length).

The personalization field of this hash is set to:

"ZTxIdOutputsHash"

In the case that the transaction has transparent inputs but no transparent outputs, outputs_digest is:

BLAKE2b-256("ZTxIdOutputsHash", [])
S.3.0: transparent_sig_digest

This digest is a BLAKE2b-256 hash of the following values:

S.3.0a: hash_type                (1 byte)
S.3.0b: prevouts_sig_digest      (32-byte hash)
S.3.0c: amounts_sig_digest       (32-byte hash)
S.3.0d: scriptpubkeys_sig_digest (32-byte hash)
S.3.0e: sequence_sig_digest      (32-byte hash)
S.3.0f: outputs_sig_digest       (32-byte hash)
S.3.0g: txin_sig_digest          (32-byte hash)

The personalization field of this hash is set to:

"ZTxIdTranspaHash"
S.3.0a: hash_type

An 8-bit unsigned value. The SIGHASH encodings from the legacy script system are used: one of SIGHASH_ALL (0x01), SIGHASH_NONE (0x02), or SIGHASH_SINGLE (0x03), optionally combined with SIGHASH_ANYONECANPAY (0x80).

The following restrictions apply:

  • Using any undefined hash_type (not 0x01, 0x02, 0x03, 0x81, 0x82, or 0x83) causes validation failure.
  • Using SIGHASH_SINGLE without a corresponding output at the same index causes validation failure.
S.3.0b: prevouts_sig_digest

If the SIGHASH_ANYONECANPAY flag is not set, identical to prevouts_digest (T.3.0a).

Otherwise:

BLAKE2b-256("ZTxIdPrevoutHash", [])
S.3.0c: amounts_sig_digest

If the SIGHASH_ANYONECANPAY flag is not set, a BLAKE2b-256 hash of the concatenation of the 8-byte signed little-endian representations of all value fields for the coins spent by the transparent inputs to the transaction.

The personalization field of this hash is set to:

"ZTxTrAmountsHash"

If the SIGHASH_ANYONECANPAY flag is set:

BLAKE2b-256("ZTxTrAmountsHash", [])
S.3.0d: scriptpubkeys_sig_digest

If the SIGHASH_ANYONECANPAY flag is not set, a BLAKE2b-256 hash of the concatenation of the field encodings (each including a leading compactSize) of all scriptPubKey fields for the coins spent by the transparent inputs.

The personalization field of this hash is set to:

"ZTxTrScriptsHash"

If the SIGHASH_ANYONECANPAY flag is set:

BLAKE2b-256("ZTxTrScriptsHash", [])
S.3.0e: sequence_sig_digest

Identical to sequence_digest (T.3.0b) regardless of hash_type.

S.3.0f: outputs_sig_digest

If the sighash type is neither SIGHASH_SINGLE nor SIGHASH_NONE, identical to outputs_digest (T.3.0c).

If the sighash type is SIGHASH_SINGLE and a transparent output exists at the same index as the input being signed, a hash of that output's encoding.

Otherwise:

BLAKE2b-256("ZTxIdOutputsHash", [])
S.3.0g: txin_sig_digest

For signatures over a transparent input, a BLAKE2b-256 hash of:

S.3.0g.i:   prevout      (36 bytes: 32-byte hash + 4-byte index)
S.3.0g.ii:  value        (8-byte signed little-endian)
S.3.0g.iii: scriptPubKey (with compactSize length prefix)
S.3.0g.iv:  nSequence    (4-byte unsigned little-endian)

The personalization field of this hash is set to:

"Zcash___TxInHash" (3 underscores)

For signatures over a Sapling Spend or Orchard Action:

BLAKE2b-256("Zcash___TxInHash", [])
A.1.0: transparent_auth_digest

In the case that the transaction contains transparent inputs, this is a BLAKE2b-256 hash of the following concatenated values for each transparent input:

A.1.0a: TransparentSighashInfo (field encoding bytes)
A.1.0b: scriptSig              (field encoding bytes, with compactSize length prefix)

The field encoding of TransparentSighashInfo is defined in Transparent Sighash Information (TransparentSighashInfo).

The personalization field of this hash is set to:

"ZTxAuthTransHash"

In the case that the transaction has no transparent inputs:

BLAKE2b-256("ZTxAuthTransHash", [])

Coinbase Bundle

A transaction is a coinbase transaction if and only if mEffectBundles contains an entry with bundleType = 1. The coinbase bundle replaces the otherwise-unspendable transparent input that identified a coinbase transaction in previous transaction versions, and carries the block height that that input was required to encode.

The block subsidy is the whole of the new issuance for the block, which consensus splits between the miner, the funding streams that pay to an address, and the funding streams that deposit into the deferred pool (the "lockbox") 13. Only the part that is not deposited into the lockbox is paid out by this transaction, so the coinbase bundle's value pool delta in mValuePoolDeltas is the block subsidy less the lockbox deposit. The fees collected from the other transactions in the block are contributed separately, as the value pool delta of the fee bundle.

The bundle's own effecting data carries the two components, as blockSubsidy and lockboxValue, so that the split is recoverable from the transaction rather than only their difference.

The coinbase bundle has no authorizing data.

Coinbase Effecting Data
Bytes Name Data Type Description
4 blockHeight uint32 The height of the block in which the transaction is mined.
8 blockSubsidy uint64 The block subsidy for that block, in zatoshis.
8 lockboxValue uint64 The part of that block subsidy deposited into the lockbox, in zatoshis.
varies coinbaseDataLen compactSize Length of the coinbaseData byte array.
coinbaseDataLen coinbaseData byte[coinbaseDataLen] Data chosen by the miner. Consensus assigns no meaning to its contents.
  • blockHeight MUST be in the range {1 .. 499999999}.
  • lockboxValue MUST be in the range {0 .. blockSubsidy}.
  • coinbaseDataLen MUST be at most 94.
Coinbase Digests
T.3.1: coinbase_effects_digest

A BLAKE2b-256 hash of the following values:

T.3.1a: blockHeight   (4-byte little-endian block height)
T.3.1b: blockSubsidy  (8-byte unsigned little-endian)
T.3.1c: lockboxValue  (8-byte unsigned little-endian)
T.3.1d: coinbaseData  (byte array with leading ``compactSize`` length)

The personalization field of this hash is set to:

"ZTxIdCoinbasHash"

This digest is present only for coinbase transactions; a transaction that has no coinbase bundle contributes no entry for it to effects_bundles_digest.

Sapling Bundle

Sapling Effecting Data

The effecting data for the Sapling bundle describes the Sapling spends and outputs. Unlike the V5 transaction format defined in ZIP 22514, the value balance is not included here; it appears in mValuePoolDeltas instead.

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.
756 * nOutputsSapling vOutputsSapling SaplingOutput[nOutputsSapling] Sapling Output descriptions.

The anchor is not part of the effecting data; it appears in Sapling Authorizing Data.

SaplingSpendEffecting
Bytes Name Data Type Description
32 cv byte[32] A value commitment to the net value of the input note.
32 nullifier byte[32] The nullifier of the input note.
32 rk byte[32] The randomized validating key for this Spend.
SaplingOutput

This is identical to OutputDescriptionV5 as defined in ZIP 22515.

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 ⁣u\!-coordinate of the note commitment for the output note.
32 ephemeralKey byte[32] An encoding of an ephemeral Jubjub public key.
580 encCiphertext byte[580] The encrypted contents of the note plaintext.
80 outCiphertext byte[80] The encrypted contents of the byte string created by concatenation of the transmission key with the ephemeral secret key.
Sapling Authorizing Data

The authorizing data for the Sapling bundle contains the proofs and signatures that authorize the spends and validate the outputs.

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.
varies vSpendAuthSigsSapling SaplingSignature[nSpendsSapling] Authorizing signatures for each Sapling Spend.
192 * nOutputsSapling vOutputProofsSapling byte[192 * nOutputsSapling] Encodings of the zk-SNARK proofs for each Sapling Output.
varies bindingSigSapling SaplingSignature A Sapling binding signature on the SIGHASH transaction hash.
  • The field anchorSapling is present if and only if nSpendsSapling>0\mathtt{nSpendsSapling} > 0. A Sapling bundle with outputs but no spends references no anchor.
  • 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.
Sapling Signature (SaplingSignature)
Bytes Name Data Type Description
varies sizeSighashInfo compactSize The size in bytes of sighashInfo.
sizeSighashInfo sighashInfo byte[sizeSighashInfo] The sighash version and associated information, as defined in Sighash Versioning.
64 signature byte[64] An encoding of a RedJubjub signature, which may be either a spendAuthSig or bindingSig depending on context.

The 1:1 correspondences between Sapling authorizing-data elements and the elements of the corresponding effecting data are required by the parsing rules in Parsing Rules.

Sapling Sighash Algorithms

For the Sapling bundle at bundleVariant = 0, sighash version 0 is the only version defined, and its associatedData is the empty byte string. A Sapling spendAuthSig or bindingSig made with it commits to the digest defined in v0 Signature Digest, computed with hash_type = SIGHASH_ALL (0x01).

Sapling Digests
T.3.2: sapling_effects_digest

In the case that Sapling spends or outputs are present, the Sapling effects digest is a BLAKE2b-256 hash of the following values:

T.3.2a: sapling_spends_digest   (32-byte hash)
T.3.2b: sapling_outputs_digest  (32-byte hash)

The personalization field of this hash is set to:

"ZTxIdSaplingH_v7"

Note that unlike ZIP 244, neither the value balance nor the anchor is included here. The value balance is committed via value_pool_deltas_digest, and the anchor via sapling_auth_digest; the personalization differs from ZIP 244's ZTxIdSaplingHash because what is directly hashed has changed.

In the case that the transaction has no Sapling spends or outputs, sapling_effects_digest is:

BLAKE2b-256("ZTxIdSaplingH_v7", [])
T.3.2a: sapling_spends_digest

In the case that Sapling spends are present, this digest is a BLAKE2b-256 hash of the following values:

T.3.2a.i:  sapling_spends_compact_digest    (32-byte hash)
T.3.2a.ii: sapling_spends_noncompact_digest (32-byte hash)

The personalization field of this hash is set to:

"ZTxIdSSpendsHash"

In the case that the transaction has Sapling outputs but no Sapling spends, sapling_spends_digest is:

BLAKE2b-256("ZTxIdSSpendsHash", [])
T.3.2a.i: sapling_spends_compact_digest

A BLAKE2b-256 hash of the field encoding of all nullifier field values of Sapling spends belonging to the transaction.

The personalization field of this hash is set to:

"ZTxIdSSpendCHash"
T.3.2a.ii: sapling_spends_noncompact_digest

A BLAKE2b-256 hash of the non-nullifier information for all Sapling spends belonging to the transaction. For each spend, the following elements are included in the hash:

T.3.2a.ii.1: cv     (32 bytes)
T.3.2a.ii.2: anchor (32 bytes)
T.3.2a.ii.3: rk     (32 bytes)

The anchor is hashed for each spend (even though it is shared in the encoding).

The personalization field of this hash is set to:

"ZTxIdSSpendNHash"
T.3.2b: sapling_outputs_digest

In the case that Sapling outputs are present, this digest is a BLAKE2b-256 hash of the following values:

T.3.2b.i:   sapling_outputs_compact_digest    (32-byte hash)
T.3.2b.ii:  sapling_outputs_memos_digest      (32-byte hash)
T.3.2b.iii: sapling_outputs_noncompact_digest (32-byte hash)

The personalization field of this hash is set to:

"ZTxIdSOutputHash"

In the case that the transaction has Sapling spends but no Sapling outputs, sapling_outputs_digest is:

BLAKE2b-256("ZTxIdSOutputHash", [])
T.3.2b.i: sapling_outputs_compact_digest

A BLAKE2b-256 hash of the subset of Sapling output information included in the ZIP 30716 CompactBlock format for all Sapling outputs belonging to the transaction. For each output, the following elements are included:

T.3.2b.i.1: cmu                  (32 bytes)
T.3.2b.i.2: ephemeralKey         (32 bytes)
T.3.2b.i.3: encCiphertext[..52]  (first 52 bytes)

The personalization field of this hash is set to:

"ZTxIdSOutC__Hash" (2 underscore characters)
T.3.2b.ii: sapling_outputs_memos_digest

A BLAKE2b-256 hash of the memo field data for all Sapling outputs belonging to the transaction. For each output:

T.3.2b.ii.1: encCiphertext[52..564] (512 bytes, encrypted memo)

The personalization field of this hash is set to:

"ZTxIdSOutM__Hash" (2 underscore characters)
T.3.2b.iii: sapling_outputs_noncompact_digest

A BLAKE2b-256 hash of the remaining Sapling output information not included in the CompactBlock format. For each output:

T.3.2b.iii.1: cv                    (32 bytes)
T.3.2b.iii.2: encCiphertext[564..]  (post-memo AEAD tag, 16 bytes)
T.3.2b.iii.3: outCiphertext         (80 bytes)

The personalization field of this hash is set to:

"ZTxIdSOutN__Hash" (2 underscore characters)
A.1.2: sapling_auth_digest

In the case that Sapling spends or outputs are present, this is a BLAKE2b-256 hash of the following concatenated values:

A.1.2a: anchorSapling          (32 bytes, present iff nSpendsSapling > 0)
A.1.2b: vSpendProofsSapling    (192 bytes per spend)
A.1.2c: vSpendAuthSigsSapling  (SaplingSignature field encoding per spend)
A.1.2d: vOutputProofsSapling   (192 bytes per output)
A.1.2e: bindingSigSapling      (SaplingSignature field encoding)

The SaplingSignature field encoding is defined in Sapling Signature (SaplingSignature) and includes sighashInfo.

The personalization field of this hash is set to:

"ZTxAuthSapliH_v7"

The personalization differs from ZIP 244's ZTxAuthSapliHash because this digest now also commits to the anchor.

In the case that the transaction has no Sapling spends or outputs:

BLAKE2b-256("ZTxAuthSapliH_v7", [])

Orchard Protocol Bundles

The Orchard protocol supports two value pools, each with its own note commitment tree, nullifier set, and chain value pool balance. Each is acted upon by its own bundle type: the Orchard bundle, bundleType = 3, acts on the Orchard pool, and the Ironwood bundle, bundleType = 4, acts on the Ironwood pool.

Both bundle types use the encoding defined in this section. Where a field is described below as belonging to the bundle's pool, that pool is the Orchard pool for an Orchard bundle and the Ironwood pool for an Ironwood bundle.

The bits of flagsOrchard have the same meaning for both pools, and so are named without the Orchard suffix they carried in the V5 transaction format, following ZIP 22917. Their values are constrained per pool by the Bundle-local rules.

Orchard Protocol Effecting Data

The effecting data for an Orchard protocol bundle describes the Orchard actions. Unlike the V5 transaction format defined in ZIP 22518, the value balance is not included here; it appears in mValuePoolDeltas instead.

Bytes Name Data Type Description
varies nActionsOrchard compactSize The number of Orchard Action descriptions.
820 * nActionsOrchard vActionsOrchard OrchardActionEffecting[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
  • enableCrossAddress19
  • The remaining bits are set to 0 ⁣0\!.
  • The field flagsOrchard is present if and only if nActionsOrchard>0\mathtt{nActionsOrchard} > 0.

The anchor is not part of the effecting data; it appears in Orchard Protocol Authorizing Data.

OrchardActionEffecting
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 ⁣x\!-coordinate of the note commitment for the output note.
32 ephemeralKey byte[32] An encoding of an ephemeral Pallas public key.
580 encCiphertext byte[580] The encrypted contents of the note plaintext.
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 Authorizing Data

The authorizing data for an Orchard protocol bundle contains the proofs and signatures that authorize the actions.

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⋅nActionsOrchard ⁣2720 + 2272 \cdot \mathtt{nActionsOrchard}\!.
sizeProofsOrchard proofsOrchard byte[sizeProofsOrchard] Encoding of aggregated zk-SNARK proofs for Orchard Actions.
varies vSpendAuthSigsOrchard OrchardSignature[nActionsOrchard] Authorizing signatures for each Orchard Action.
varies bindingSigOrchard OrchardSignature 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.
Orchard Signature (OrchardSignature)
Bytes Name Data Type Description
varies sizeSighashInfo compactSize The size in bytes of sighashInfo.
sizeSighashInfo sighashInfo byte[sizeSighashInfo] The sighash version and associated information, as defined in Sighash Versioning.
64 signature byte[64] An encoding of a RedPallas signature, which may be either a spendAuthSig or bindingSig depending on context.

The 1:1 correspondences between the authorizing-data elements of an Orchard protocol bundle and the elements of the corresponding effecting data are required by the parsing rules in Parsing Rules.

Orchard Protocol Sighash Algorithms

For the Orchard and Ironwood bundles at bundleVariant = 0, sighash version 0 is the only version defined, and its associatedData is the empty byte string. An Orchard spendAuthSig or bindingSig made with it commits to the digest defined in v0 Signature Digest, computed with hash_type = SIGHASH_ALL (0x01).

Orchard Protocol Digests
T.3.3: orchard_effects_digest

The digest defined here and in its child sections is the Orchard bundle's instance of a digest shape shared with the Ironwood bundle; T.3.4 gives the Ironwood instance.

In the case that Orchard actions are present, the Orchard effects digest is a BLAKE2b-256 hash of the following values:

T.3.3a: orchard_actions_compact_digest    (32-byte hash)
T.3.3b: orchard_actions_memos_digest      (32-byte hash)
T.3.3c: orchard_actions_noncompact_digest (32-byte hash)
T.3.3d: flagsOrchard                      (1 byte)

The personalization field of this hash is set to:

"ZTxIdOrchardH_v7"

Note that unlike ZIP 244, neither the value balance nor the anchor is included here. The value balance is committed via value_pool_deltas_digest, and the anchor via orchard_auth_digest; the personalization differs from ZIP 244's ZTxIdOrchardHash because what is directly hashed has changed.

In the case that the transaction has no Orchard actions, orchard_effects_digest is:

BLAKE2b-256("ZTxIdOrchardH_v7", [])
T.3.3a: orchard_actions_compact_digest

A BLAKE2b-256 hash of the subset of Orchard action information intended for inclusion in the CompactBlock format. For each action:

T.3.3a.i:   nullifier            (32 bytes)
T.3.3a.ii:  cmx                  (32 bytes)
T.3.3a.iii: ephemeralKey         (32 bytes)
T.3.3a.iv:  encCiphertext[..52]  (first 52 bytes)

The personalization field of this hash is set to:

"ZTxIdOrcActCHash"
T.3.3b: orchard_actions_memos_digest

A BLAKE2b-256 hash of the memo field data for all Orchard actions. For each action:

T.3.3b.i: encCiphertext[52..564] (512 bytes, encrypted memo)

The personalization field of this hash is set to:

"ZTxIdOrcActMHash"
T.3.3c: orchard_actions_noncompact_digest

A BLAKE2b-256 hash of the remaining Orchard action information not intended for inclusion in the CompactBlock format. For each action:

T.3.3c.i:   cv                   (32 bytes)
T.3.3c.ii:  rk                   (32 bytes)
T.3.3c.iii: encCiphertext[564..] (post-memo AEAD tag, 16 bytes)
T.3.3c.iv:  outCiphertext        (80 bytes)

The personalization field of this hash is set to:

"ZTxIdOrcActNHash"
T.3.4: ironwood_effects_digest

The Ironwood bundle uses the same effecting data encoding as the Orchard bundle, and ironwood_effects_digest is computed over that data exactly as orchard_effects_digest and its children are computed (T.3.3), except that each personalization string is replaced as follows:

Digest Orchard bundle Ironwood bundle
effects digest ZTxIdOrchardH_v7 ZTxIdIronwd_Hash
actions compact digest ZTxIdOrcActCHash ZTxIdIrnActCHash
actions memos digest ZTxIdOrcActMHash ZTxIdIrnActMHash
actions noncompact digest ZTxIdOrcActNHash ZTxIdIrnActNHash

The personalization strings differ so that the digest of a bundle acting on one pool cannot be reused as the digest of a bundle acting on the other.

In the case that the transaction has no Ironwood actions, ironwood_effects_digest is:

BLAKE2b-256("ZTxIdIronwd_Hash", [])
A.1.3: orchard_auth_digest

In the case that Orchard actions are present, this is a BLAKE2b-256 hash of the following concatenated values:

A.1.3a: anchorOrchard         (32 bytes)
A.1.3b: proofsOrchard         (aggregated proofs)
A.1.3c: vSpendAuthSigsOrchard (OrchardSignature field encoding per action)
A.1.3d: bindingSigOrchard     (OrchardSignature field encoding)

The OrchardSignature field encoding is defined in Orchard Signature (OrchardSignature) and includes sighashInfo.

The personalization field of this hash is set to:

"ZTxAuthOrchaH_v7"

The personalization differs from ZIP 244's ZTxAuthOrchaHash because this digest now also commits to the anchor.

In the case that the transaction has no Orchard actions:

BLAKE2b-256("ZTxAuthOrchaH_v7", [])
A.1.4: ironwood_auth_digest

The Ironwood bundle uses the same authorizing data encoding as the Orchard bundle, and ironwood_auth_digest is computed over that data exactly as orchard_auth_digest (A.1.3), including its commitment to the anchor, except that the personalization field of the hash is set to:

"ZTxAuthIrnwdHash"

In the case that the transaction has no Ironwood actions:

BLAKE2b-256("ZTxAuthIrnwdHash", [])

Rationale

Effecting data bundles and authorizing data bundles are stored separately in the transaction format so that the authorizing data may be pruned by straightforward truncation of the encoded representation of the transaction.

Representing the coinbase as a bundle makes the value pool delta balance rule universal. Previously the coinbase transaction was the sole exception: its deltas summed to the negation of the block subsidy rather than to zero, because the subsidy entered the transparent transaction value pool implicitly. With the subsidy carried in the coinbase bundle's value pool delta, every transaction balances to zero in every asset, and a wallet can enumerate the value flows of a coinbase transaction using the same rule it applies to any other transaction.

Carrying blockSubsidy and lockboxValue in the effecting data states both components of the issuance in the bundle they belong to, and commits them to the transaction identifier along with the rest of that data. The value pool delta is their difference, so without them a client could see how much the coinbase pays out but not how much was issued or how much went to the lockbox. Deriving them instead is not open to every client: the subsidy is not a function of the block height alone once issuance depends on accumulated chain state, so evaluating the issuance schedule would require a client to track that state.

The Orchard and Ironwood bundles share one encoding because the Orchard pool and the Ironwood pool are two pools of the same shielded protocol. The action encoding, proving system, authorization, and note encryption are common to both; the pools differ in their note commitment trees, nullifier sets, and chain value pool balances.

Each pool gets its own bundle type rather than a variant of a single type because a variant is required to affect the same value pool(s) as the type it varies, so that a client encountering an unrecognized variant still knows which pools the bundle touches. Two pools therefore cannot share a bundle type, and separate types also keep the two value pool deltas separable, which a client needs in order to report the two chain value pool balances independently.

The Sapling and Orchard protocol anchors are authorizing data rather than effecting data, following ZIP 22920. An anchor selects the note commitment tree state that the bundle's proofs are verified against, which is a property of how the spends are authorized rather than of what the transaction does. Placing it there lets a bundle be re-anchored to a more recent root without changing the transaction identifier, while the proofs still bind to the anchor actually used.

The coinbase bundle also gives the coinbase metadata a place of its own. In previous transaction versions that metadata was encoded as the scriptSig of a single transparent input whose previous output reference pointed at nothing, so a coinbase transaction had to be recognized by that unspendable reference, and the block height had to be encoded as a script push. The coinbase bundle encodes the height directly, and its presence identifies the transaction. The coinbaseData limit of 94 bytes holds the encoded size of that field to what the previous encoding spent on the same content. A coinbase scriptSig was limited to 100 bytes, of which the leading push of the block height took 5; of the 95 bytes that remained, the compactSize length prefix of coinbaseData now takes one.

Neither the previous output reference nor the sequence number of the input being replaced is carried over. Both were artifacts of encoding the coinbase metadata as a transparent input: the reference identified no output, and the sequence number had no effect on the validity of a transaction that spends nothing.

Deployment

Reference implementation

Open issues

Notes from design sessions

This section should be removed as soon as all the considerations described here are accounted for in ZIP.

Wallets or consensus-dependent applications that send transactions, might do something wrong that compromises user funds or privacy if they do not take into account consensus changes in an upgrade; therefore, only a subset of consensus changes can be safely adapted to using this mechanism.

In particular, consensus rules may change in such a way that a wallet doing what it has done in the past causes risk of loss of funds.

An example of this was ZIP 21221. In that case the existing mechanisms failed to prevent loss of funds because in practice, wallets updated the consensus branch ID without updating note encryption. We made the mistake of requiring wallets to change their behaviour for an existing transaction version. Except for certain cases involving severe security flaws, we should avoid doing that again.

If a wallet needs to actively do something differently (for example, advertising addresses in a new format or creating an output with a TZE precondition) in order to be affected by a new feature, then it is reasonably safe for it to ignore the feature as long as it can still parse transactions and, and create and sign transactions that don't make use of those features.

It is okay that such a wallet might not be able to see funds that depend on new features, as long as they do not create such funds themselves.

Loss of funds is unacceptable. Temporary inaccessibility of funds in certain circumstances can be okay -- provided that this potential inaccessibility and the circumstances where it can occur is documented and an explicit design decision.

Strawman

Modify how we approach transaction format evolution, such that (after one more change to transaction encoding) it is possible for a wallet that has not adopted a parser for a given transaction format to continue to function after an additive change to the transaction format. Another way to state this is that we should make it possible to make "semver-compatible" transaction format changes.

  • @str4d: We could use a TLV approach where each "bundle" has a value balance. The NSM burn amount field could be its own bundle, the explicit fee data could be its own bundle and the consensus rule could be that all value balances sum to zero.
    • generically, you want a value balance vector, where you have zero or more value balances moving between bundles in other assets.
  • @nuttycom: You could have pre-ZSA and post-ZSA Orchard bundles.

Strawman II

Treat bundles as individually versioned.

  • Each bundle is registered with an ID relative to a tx version group ID.
  • The bundle ID encoding also has some flag bits indicating how it interacts with the tx as a whole.
  • Transactions then have two "bundle maps":
    • The first encodes how value moves between the different bundles.
    • The second encodes data specific to a bundle (e.g. how value moves within a bundle)
    • Bundles that don't have any data would just appear in the first map, and bundles that don't produce or consume value would just appear in the second map.
  • We can re-interpret various other facets of transactions as "bundles"
    • Explicit fees are a bundle that never produces value
    • NSM field similarly never produces value
    • ZSA burns would be split into "value balance out of Orchard pool" and "value balance being removed from ZSA issuance"
    • The coinbase subsidy is a bundle that only produces value; see Coinbase Bundle.
  • Privacy effect is minimal
    • We already follow a bundle approach with a transparent transaction value pool, for the turnstiles. This leans into it, while preserving the bundle boundary within which we implement each privacy protocol.
    • Some combinations of bundles would be permitted by the tx format that were not previously permitted.
      • However, we can still restrict which combinations of bundles can be mined in the consensus rules.

Sketch of the format:

  • Transaction version (like now)
    • Version
    • Version group ID
  • Transaction header
    • Expiry height etc
    • Likely need some kind of key-value map here to allow additional fields to be added, or maybe version the header to allow evolution?
  • Transparent transaction value pool "traffic map"
    • Option 1: BundleVariantID -> (valueBalance, AssetId -> valueBalance)
      • Key: Bundle variant ID
      • Value:
        • ZEC valueBalance
        • CompactSize len(generalizedValueBalances)
        • Zero or more generalized value balances
          • AssetId (not AssetBase because those are protocol-specific, and we want generalized value balances to be understandable independently of protocol changes)
          • valueBalance
    • Option 2: (BundleVariantID, Option[AssetId]) -> valueBalance
      • Key: Bundle variant ID encoded as u8 || { Option[AssetId] }
      • Value: valueBalance
    • Option 3: BundleVariantID -> Option[AssetId] -> valueBalance
      • Key: Bundle variant ID
      • Value:
        • Map containing one or more generalized value balances
          • { Option[AssetId] }
          • valueBalance
  • Sequence of bundles (maybe with a length prefix?)
    • Bundle variant ID
      • Maybe flag bits, either in the variant ID or next to it, that indicate how an opaquely-parsing wallet should interpret the bundle, e.g.:
        • A bit that says whether or not the bundle interacts with the transparent transaction value pool (which memo bundles would not have).
          • Counterpoint: The traffic map already specifies whether a given bundle does have an interaction with the transparent tx value pool for this tx. This is different from whether that kind of bundle can interact with the transparent tx value pool, but it the latter needed?
        • A bit that says whether the bundle has any other effect than what is specified in the traffic map.
          • Counterpoint: We should split apart effecting and authorizing data in the encoding, and then a bundle must be assumed effecting iff it has non-null effecting data.
    • CompactSize len(effectingData)
    • effectingData
    • CompactSize len(authorizingData)
    • authorizingData
      • effectingData and authorizingData would be opaque to the initial parser.
      • Parsers that support parsing (tx_version, bundle_version) know how to interpret its internals

Questions

  • Is it okay for fee calculations to be opaque to wallet parsers, as long as the fee amounts can be calculated in consensus?
    • Yes:
      • When receiving, all you care about is knowing the actual fee amount; you see that in the fee bundle's value balance.
      • When sending, you need to understand all bundles you are including, and then you can calculate the fee.
  • Can wallets still compute the txid and wtxid of an arbitrary transaction?
    • Yes, provided that effecting and authorizing data is separated. Then they can hash the effecting data even without understanding it to compute the txid, and they can hash the authorizing data even without understanding it to compute the authorizing data commitment part of the wtxid (ZIP 23922).
    • However, this reintroduces a more direct linkage between the [w]txid computation and the transaction encoding. It's arguably fine, and potentially simpler -- since the [w]txid computation need not change at all for most protocol changes.
    • If the hashing uses flat hashes over the effectingData and authorizingData of each bundle (which it has to because the internal structure is not known), then it might be more difficult to do Merkle proofs over subsets of the data within a bundle. We haven't used that so far; is it really needed?
    • Should the authorizing data be entirely separate from the effecting data, and encoded at the end of the transaction in a batch, so that pruning is simply truncation?
  • The light wallet protocol will be updated to allow the client to specify the set of bundle types that the client understands. In the case that this information is provided, the light client server will then send the root hashes for each bundle type that the client does not understand when returning raw transaction data, so that the light client can correctly recompute and validate the txid; also, the compact transactions can be pruned to exclude bundles of bundle types. that the client will not understand.

TODO

Rename assetDigest to assetUuid in ZIP 227

References


  1. 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↩︎

  2. Zcash Protocol Specification, Version 2025.6.3 [NU6.1] or later↩︎

  3. Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 3.12: Mainnet and Testnet↩︎

  4. Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 3.3: The Block Chain↩︎

  5. ZIP 229: Version 6 Transaction Format↩︎

  6. ZIP 225: Version 5 Transaction Format↩︎

  7. ZIP 203: Transaction Expiry↩︎

  8. ZIP 2006: Restricting Transfers into the Orchard Pool↩︎

  9. Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 7.8: Block Subsidy and Founders' Reward↩︎

  10. Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 7.10: Payment of Funding Streams, Deferred Lockbox, and Lockbox Disbursement↩︎

  11. ZIP 244: Transaction Identifier Non-Malleability↩︎

  12. ZIP 244: Transaction Identifier Non-Malleability↩︎

  13. ZIP 2001: Lockbox Funding Streams↩︎

  14. ZIP 225: Version 5 Transaction Format↩︎

  15. ZIP 225: Version 5 Transaction Format↩︎

  16. ZIP 307: Light Client Protocol for Payment Detection↩︎

  17. ZIP 229: Version 6 Transaction Format↩︎

  18. ZIP 225: Version 5 Transaction Format↩︎

  19. ZIP 2006: Restricting Transfers into the Orchard Pool↩︎

  20. ZIP 229: Version 6 Transaction Format↩︎

  21. ZIP 212: Allow Recipient to Derive Ephemeral Secret from Note Plaintext↩︎

  22. ZIP 239: Relay of Version 5 Transactions↩︎

Citation graph

Hover or tap a node to see its title and citations.
Loading citation graph…