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 character § is used when referring to sections of the Zcash Protocol Specification. 2
The terms below are to be interpreted as follows:
- Pool snapshot
- A pair consisting of the Orchard note commitment tree root and the nullifier non-membership tree root, both computed as of the end of a specified block height. The pool snapshot defines the set of notes eligible for claims.
- Nullifier non-membership tree
- An Indexed Merkle Tree (IMT) whose leaves represent contiguous intervals between consecutive revealed nullifiers. A Merkle path to a leaf, together with a range check, proves that a given nullifier does not appear in the set of revealed nullifiers at the snapshot height. See [Nullifier Non-Membership Tree].
- Alternate nullifier
- A value derived from an Orchard note and a nullifier domain, with similar cryptographic properties to the note's standard nullifier but unlinkable to it. A note has exactly one alternate nullifier for each nullifier domain. See [Alternate Nullifier Derivation].
- Nullifier domain
- A unique identifier, encoded as an element of , that scopes alternate nullifiers to a particular application instance. Two claims in the same domain for the same note produce the same alternate nullifier; claims in different domains are unlinkable.
- Claim
- A zero-knowledge proof, together with a spend authorization signature, demonstrating that the prover holds an unspent Orchard note with a committed value at a given pool snapshot, and revealing the note's alternate nullifier in a specified domain.
- Dummy signed note
- A synthetic Orchard note with value 1 zatoshi that does not exist in any on-chain note commitment tree. It is constructed solely for obtaining a spend authorization signature from the holder's key. Its value does not contribute to the claimed balance. See [Wallet Signing] and [Dummy Signed Note].
Abstract
This ZIP specifies a mechanism for proving ownership of unspent Orchard notes at a snapshot of the shielded pool, without revealing the notes' standard nullifiers or linking claims to past or future on-chain spending.
The mechanism introduces two primitives: a nullifier non-membership tree (an Indexed Merkle Tree of intervals between consecutive revealed nullifiers) that enables efficient in-circuit proofs that a note was unspent at the snapshot height, and alternate nullifiers (domain-separated values derived from a note's standard nullifier using Poseidon) that prevent double-claiming within an application domain while remaining unlinkable across domains and to on-chain activity.
A Claim circuit combines these primitives with standard Orchard note commitment integrity, Merkle membership, spend authority, and diversified address integrity checks to produce a single proof per note. An optional multi-note batching extension allows proving aggregate balance across several notes in one proof.
Possible applications include private air drops, private proof-of-balance, and private stake-weighted polling. Application-specific protocols (such as coinholder voting) are specified in separate ZIPs that reference this one.
Motivation
Several applications in the Zcash ecosystem require a holder to demonstrate that they controlled a certain balance at a point in time, without revealing which specific notes they held or linking the demonstration to their on-chain transaction history. Examples include:
- Air drops: distributing tokens proportional to shielded holdings.
- Stake-weighted polling: weighting votes by ZEC balance.
- Proof-of-balance: demonstrating solvency or collateral without revealing addresses.
The naive approach of revealing standard nullifiers would link the claim to future spending, destroying the privacy that Zcash's shielded transactions are designed to provide. An alternative approach of actually spending the claimed notes (e.g., to oneself) with an anchor at the snapshot height has the disadvantage that the same notes cannot be used in concurrent applications, and it permanently commits the holder to the claim by recording it on the main chain.
Domain-separated alternate nullifiers resolve both problems: they are unlinkable to the standard nullifiers (even after the note is later spent on-chain), and they are scoped to a specific application instance so that the same notes can participate in independent, concurrent applications without interference.
The concept of alternate nullifiers and nullifier non-membership trees for proof-of-balance was first described by Daira-Emma Hopwood and Jack Grigg in a 2023 draft. 3 This ZIP builds on that work with a concrete instantiation informed by the implementation experience of the Zcash coinholder voting system.
A key practical consideration is that many holders custody their spending keys on hardware wallets such as Keystone. Without a delegation mechanism, hardware wallet users must either export their spending keys to software (negating the security benefit of hardware custody) or perform many more hardware wallet signing interactions (one per protocol action rather than a single delegation) each with limited ability to verify the signing context on screen. This ZIP therefore specifies a PCZT-based signing flow (see [Wallet Signing]) that works with any hardware wallet supporting Orchard PCZT signing today, requiring no firmware changes.
Privacy Implications
Unlinkability to standard nullifiers. The alternate nullifier for a note is computed as a Poseidon hash keyed by the nullifier deriving key , which is secret. An observer who sees both (published during a claim) and the standard nullifier (published when the note is later spent on the Zcash chain) cannot link them without knowledge of . This property holds under the assumption that Poseidon is a pseudorandom function when keyed with a uniformly random field element (see [Security Argument]).
Cross-domain unlinkability. If the same note participates in two independent applications with different nullifier domains and , the resulting alternate nullifiers and are unlinkable. This follows from the PRF property: distinct inputs produce outputs that are computationally indistinguishable from independent random values.
Spend authority prevents viewing-key delegation. The circuit constrains correct derivation of , and the protocol requires a valid spend authorization signature under (see [Spend Authorization Signature]), ensuring that a party with only a viewing key cannot make claims on behalf of the holder.
Non-membership tree queries. Obtaining a Merkle path in the nullifier non-membership tree requires querying a data source that holds the full tree. A naive query reveals which path is being requested, potentially linking the querier to a nullifier range. Private information retrieval (PIR) techniques can mitigate this leakage 4; their specification is out of scope for this document.
Hardware wallet device exposure. When the PCZT-based hardware wallet signing flow (see [Wallet Signing]) is used, the device observes the PCZT but learns no information about the holder's real Orchard balance or delegation context. The PCZT contains a 1-zatoshi dummy note with no on-chain counterpart; the holder's actual note commitments, values, and nullifiers are never transmitted to the device. The application commitment, alternate nullifiers, and usage identifier are committed via the dummy note's nullifier inside the ZKP circuit and do not appear as plaintext fields in the PCZT. The output address (application hotkey) is visible on the device but is freshly generated per usage instance and is not linked to the holder's on-chain Orchard addresses. An attacker with physical access to the device during signing learns only that the holder is participating in a proof-of-balance instance and the declared balance.
Requirements
- A holder can prove ownership of an unspent Orchard note at a pool snapshot without revealing the note's standard nullifier.
- Double-claiming the same note within the same nullifier domain is detectable.
- Claims are unlinkable to the holder's past or future on-chain spends, and to their claims in other nullifier domains.
- The holder proves spend authority (not merely viewing access).
- The construction allows a holder to participate in multiple independent applications using the same notes, provided each application uses a distinct nullifier domain.
Non-requirements
- Privacy-preserving retrieval of non-membership Merkle paths (e.g., via PIR) is out of scope.
- Value denomination conversions (e.g., converting zatoshi to application-specific units) are out of scope.
- Transaction-level encoding of claims is out of scope; this ZIP specifies only the proof statement and its verification.
Specification
Pool Snapshot
A pool snapshot at block height is a pair where:
- is the root of the Orchard note commitment tree as of the end of block . 5
- is the root of the nullifier non-membership tree constructed from the set of all Orchard nullifiers revealed on the consensus chain as of the end of block .
The pool snapshot MUST be deterministically computable from the consensus chain state at height . Any party with access to the chain state MUST be able to independently verify both roots.
Nullifier Non-Membership Tree
The nullifier non-membership tree is a Merkle tree whose leaves are commitments to the gaps between consecutive revealed nullifiers, enabling efficient in-circuit proofs that a given nullifier is absent from the revealed set.
The relationship between the sorted nullifier set and the tree is illustrated below:
/ \ ..
/ \ /
gcm_0 gcm_1 gcm_2 <-- Gap commitments (tree leaves)
/\ /\ /\
|(l_0, w_0)| |(l_1, w_1)| |(l_2, w_2) .. <-- Gap encodings (low, width)
s_0| |s_1| |s_2| .. <-- Sorted nullifier set + sentinels
Each gap between consecutive nullifiers is encoded as a pair, then committed via (see [Hash Function]) to produce a tree leaf.
Construction
Let be the union of the set of all Orchard nullifiers revealed on the consensus chain as of block height and the sentinel values defined in [Sentinel Initialization]. Sort in ascending order. Because sentinel initialization is mandatory, is non-empty.
For each pair of consecutive elements where and , compute a gap encoding:
- : the first value in the gap
- : the number of additional values in the gap beyond
The gap encoding represents the closed interval , which contains exactly the field elements between and exclusive. The corresponding tree leaf is .
After processing all consecutive pairs, if , a terminal gap MUST be added with:
This ensures that all unrevealed values above the largest element of are represented in the tree.
Rationale for (low, width) encoding
An alternative encoding stores pairs directly, as proposed in 3. The encoding precomputes during tree construction, saving one field subtraction in the in-circuit interval check. Since tree construction is performed once per snapshot while the circuit is evaluated per claim, this trade-off favors prover efficiency.
Hash Function
The non-membership tree uses Poseidon 6 with the instantiation (width , rate 2) over the Pallas base field for all hashing:
- Gap commitment: , a 2-input Poseidon hash (one permutation).
- Internal node hash: , a 2-input Poseidon hash (one permutation), where and are determined by the node's position bit at each level.
Both functions use with . This is the same Poseidon instantiation used by Orchard for nullifier derivation 6.
Rationale for Poseidon over Sinsemilla
The Orchard note commitment tree uses Sinsemilla 7 for Merkle hashing. The non-membership tree uses Poseidon instead because Poseidon operates natively on field elements, making it significantly more efficient inside Halo 2 arithmetic circuits. Sinsemilla is optimized for bitstring inputs and incurs overhead when hashing field elements. Since the non-membership tree is a new data structure not constrained by backwards compatibility, the more circuit-efficient choice is appropriate.
Poseidon2 was considered but not adopted because no audited implementation was available at the time of design. Poseidon already has an audited implementation used by Orchard for nullifier derivation.
Tree Depth
MUST be large enough to accommodate the number of gap leaves. Each nullifier in the pool adds at most one leaf (by splitting an existing gap), so the tree requires capacity for at least leaves, where is the size of the Orchard nullifier set and 17 is the number of sentinels for the Pallas base field. Concretely:
A depth of is RECOMMENDED. This supports up to million leaves. As of early 2026, the Zcash Orchard pool contains roughly 51 million nullifiers, so depth 29 provides about one order of magnitude of headroom.
Unused leaf positions MUST be filled with the canonical empty leaf value . This is non-overlapping with every valid gap encoding because the sentinel set always includes (see [Sentinel Initialization]), so can never appear in a valid gap.
Sentinel Initialization
Before inserting any real nullifiers, the non-membership tree MUST be initialized with sentinel values that partition into intervals each of width strictly less than . The sentinel set MUST include the value .
For the Pallas base field (), implementations MUST insert 17 sentinels at:
This guarantees the width bound required by the in-circuit range checks:
- For , the interval between consecutive sentinels has width exactly .
- The final tail interval has width .
The width bound is critical for the soundness of the in-circuit range checks. If any interval had width , the range check could overflow, allowing a prover to falsely claim non-membership.
These sentinel values are constants defined by this specification.
Nullifier Domains
A nullifier domain is an element of that defines a double-claim detection scope. Each independent claim context (such as a specific air-drop event, a single poll, or an individual voting round) MUST use its own domain.
Applications MUST ensure that the domain value is unique across independent claim contexts. Two claims that share the same domain and the same underlying note will produce the same alternate nullifier, which is the mechanism for double-claim prevention.
Applications SHOULD derive the domain deterministically by hashing a protocol identifier with an application-specific instance value. The protocol identifier SHOULD be the application's ZIP number, encoded as a little-endian Pallas base field element, which guarantees cross-application uniqueness. The instance value (such as a round or event identifier) ensures uniqueness across instances of the same application.
Applications MAY define their own domain derivation scheme provided it satisfies the uniqueness requirement above.
Alternate Nullifier Derivation
Given a nullifier domain and an Orchard note with standard nullifier , the alternate nullifier is computed as:
where:
- is the nullifier deriving key from the holder's full viewing key.
- is the nullifier domain (see [Nullifier Domains]).
- is the note's standard Orchard nullifier, computed as .
This is a 3-input Poseidon hash. Using the standard instantiation (width , rate 2), this requires two permutations.
Properties
Deterministic. The same note in the same domain always produces the same alternate nullifier, enabling double-claim detection.
Unlinkable to standard nullifier. An adversary who observes and later observes (when the note is spent on-chain) cannot link them without knowledge of .
Cross-domain unlinkable. Alternate nullifiers for the same note in different domains are computationally indistinguishable from independent random values.
Collision resistant. Distinct notes in the same domain produce distinct alternate nullifiers, under the collision resistance of Poseidon.
Security Argument
The security of the alternate nullifier derivation relies on Poseidon being a pseudorandom function (PRF) when keyed with a uniformly random element of .
Unlinkability. Model as a PRF keyed by . An adversary who does not know sees outputs that are indistinguishable from random. Given and , the adversary cannot verify the relationship without . The nullifier deriving key is a 255-bit value derived from the spending key and is never revealed on-chain.
Cross-domain unlinkability. For two domains , the tuples and are distinct inputs to the PRF. Under the PRF assumption, their outputs are jointly indistinguishable from independent random values.
Collision resistance. If , then and are distinct Poseidon inputs, so their outputs collide with negligible probability under Poseidon's collision resistance.
Rationale for divergence from the Orchard-native construction
The earlier draft 3 proposed an alternate nullifier derivation structurally parallel to the standard Orchard nullifier:
That construction has the advantage of sharing more circuit infrastructure with the standard nullifier check.
The Poseidon-based construction adopted here was chosen for simplicity: it requires only a single Poseidon hash rather than an additional fixed-base scalar multiplication, and it reuses the standard nullifier that the circuit already computes for the non-membership check. The security assumption is comparable, as both constructions rely on the hardness of distinguishing -keyed evaluations from random.
The current Zcash coinholder voting system was designed and implemented around this construction. A future revision of this ZIP may revisit the choice as part of a broader protocol revision. For example, if hardware wallet firmware support (e.g., a Keystone testnet byte for governance signing) restructures the spend authority flow in a way that favors tighter integration with the standard Orchard nullifier derivation.
Claim Circuit
A valid instance of a Claim proof assures the following statement.
Public Inputs
Given a primary input:
Auxiliary Inputs
the prover knows an auxiliary input:
such that the following conditions hold:
Conditions
Note commitment integrity. .
This is identical to the corresponding check in an Orchard Action statement. 8
Merkle path validity for . is a valid Merkle path of depth , as defined in § 4.9 'Merkle Path Validity' 9, from to the anchor .
Value commitment integrity. .
Nullifier derivation. .
Spend authority. .
Diversified address integrity. Let and . Then or .
Nullifier non-membership. Let (see [Hash Function]). is a valid Merkle path of depth from to the anchor , using (see [Hash Function]) at each level.
Additionally, let . Both of the following range checks MUST hold:
These together enforce over , proving that the standard nullifier falls within an interval of unrevealed values.
Derivation of the interval check
To prove :
-
Check 1 proves : if , the field subtraction wraps to a value , failing the range check. This relies on the sentinel initialization ensuring and therefore all legitimate offsets are small.
-
Check 2 proves : substituting, . This is in if and only if , i.e., .
Alternate nullifier integrity. (see [Alternate Nullifier Derivation]).
Circuit Implementation Notes
The first six conditions (note commitment integrity through diversified address integrity) are adapted from the corresponding Orchard Action statement checks. In particular, note-commitment Merkle membership uses as in the Orchard Action statement. 8 Implementations SHOULD share circuit gadgets with an Orchard implementation to minimize new code requiring review.
The nullifier non-membership check requires a Poseidon-based Merkle path verification (29 levels, 30 total Poseidon calls per note) and two 250-bit range checks.
The alternate nullifier integrity check is a single Poseidon hash (two permutations at width 3).
Spend Authorization Signature
Each Claim proof MUST be accompanied by a spend authorization signature , which is a signature under the randomized key that is a public input to the circuit.
The signature MUST be computed over a sighash that binds the signature to the specific claim, ensuring that it cannot be replayed for a different set of public inputs. Applications MAY achieve this binding directly (by including the public inputs in the sighash) or indirectly (through values that the Claim circuit constrains to be consistent with the public inputs).
The concrete sighash derivation is application-defined. The [Wallet Signing] section specifies a PCZT-based signing flow that reuses the standard ZIP 244 10 sighash, enabling compatibility with existing hardware wallets.
Out-of-Circuit Verification
A verifier that receives a Claim proof together with a spend authorization signature MUST perform the following checks:
-
Verify against the public inputs .
-
Verify as a valid signature on the sighash (see [Spend Authorization Signature]), under the randomized key .
-
Verify that corresponds to the Orchard note commitment tree root at the declared snapshot height.
-
Verify that corresponds to the nullifier non-membership tree root at the declared snapshot height.
-
Verify that is valid for the current application instance.
-
Verify that has not been previously accepted in the same nullifier domain. If it has, reject the claim as a double-claim.
Multi-Note Batching
The Claim circuit defined in [Claim Circuit] proves a statement about a single note. Applications that require proving aggregate balance across multiple notes MAY use the following batching extension. Applications that use the PCZT-based signing flow (see [Wallet Signing]) MUST use the batched circuit, as the dummy signed note (see [Dummy Signed Note]) requires binding to the claimed note slots.
Batched Claim Circuit
A batched Claim circuit for notes modifies the single-note circuit as follows:
- The public inputs , , , and are shared across all notes.
- Each note contributes its own value commitment and alternate nullifier as public inputs.
- A single (derived from the shared and ) is constrained to own all notes, ensuring they belong to the same holder.
- All note commitment integrity, value commitment integrity, Merkle path, nullifier derivation, non-membership, and alternate nullifier checks are performed independently per note.
Dummy Signed Note
When the PCZT-based signing flow (see [Wallet Signing]) is used, the batched circuit MUST include an additional slot for the dummy signed note alongside the claimed note slots. This slot has the following additional public input:
and the following additional auxiliary inputs:
The following conditions MUST hold for the dummy signed note:
Signed note commitment integrity. .
The value is fixed to 1 (zatoshi) to match the PCZT value used for hardware wallet display (see [Wallet Signing]). The dummy signed note's value does not contribute to the claimed balance.
Signed note nullifier derivation. .
Rho binding.
where is the extracted note commitment of the -th claimed note slot (real or dummy). This binds the hardware wallet's signature to the exact set of notes being claimed and the application context.
Spend authority. .
This is the same shared across the claimed note slots.
Diversified address integrity. where is derived from the shared and as in the claimed note conditions.
Note Padding
To avoid leaking the number of notes a holder is claiming, the circuit SHOULD accept a fixed number of note slots (for example, ). Unused slots are filled with dummy notes (Orchard notes with ).
Dummy notes use the same Merkle path validity check as the standard Orchard Action circuit 8: if , the Merkle path check is skipped.
All other conditions are enforced unconditionally:
- Dummy notes commit to value 0 with a fresh .
- Dummy notes are derived from the same full viewing key as real notes (using a distinct diversifier index per slot).
- Dummy notes MUST provide a valid non-membership proof against . Because dummy nullifiers are random field elements, they fall within an unrevealed interval with overwhelming probability.
- The alternate nullifier is derived and published for every slot. The application verifier MAY ignore alternate nullifiers corresponding to zero-value slots.
Sub-Circuit Instantiation
When the Claim circuit is used as a sub-circuit of an application-specific circuit, the application circuit has direct access to the private witnesses including the note value . In this case, the value commitment MAY be omitted from the public inputs if the application circuit binds to the note values through its own mechanism. For example, an application circuit that converts note values to application-specific units (such as vote weight) may bind to directly within the circuit and expose the converted result through its own public outputs. All other conditions of the Claim circuit MUST still be enforced.
Application Requirements
An application instantiating this ZIP MUST define the following:
- An application hotkey: a key pair generated locally by the wallet, freshly generated per proof-of-balance instance, and not linked to the holder's on-chain Orchard addresses. When the PCZT-based signing flow is used, the output is addressed to this key (see [Wallet Signing]). All subsequent application-level operations use the hotkey in place of the holder's spending key.
- An application verifier: the component that accepts or rejects claims by performing the checks in [Out-of-Circuit Verification]. It MUST maintain a persistent set of accepted alternate nullifiers per domain for double-claim detection.
- A nullifier domain derivation, as specified in [Nullifier Domains].
- An application commitment (): a value that commits to application-specific context, included in the rho binding (see [Dummy Signed Note]). For example, a voting protocol might commit to the delegated vote weight and recipient address.
- An instance identifier (): a unique identifier for the proof-of-balance instance, included in the rho binding (see [Dummy Signed Note]).
- A sighash derivation: the method for computing the sighash for the spend authorization signature (see [Spend Authorization Signature]). Applications using the PCZT-based signing flow use the ZIP 244 10 sighash.
Wallet Signing
The signing flow proceeds in five steps:
- The wallet generates an application hotkey on the local device.
- The wallet constructs a dummy signed note whose nullifier commits to the delegation context.
- The wallet builds a PCZT containing the dummy signed note.
- The hardware wallet device signs the PCZT and returns the signature.
- The wallet extracts the signature and assembles the delegation submission.
Each interaction with the hardware wallet device delegates up to Orchard notes (we utilize the default batch size of 5). A holder with more than notes repeats the flow for each batch, producing a separate application commitment per batch. The holder MAY choose to delegate fewer batches than their full note set, claiming only the balance covered by the delegated batches.
Dummy Signed Note Construction
The wallet constructs a dummy Orchard note as follows:
-
Address. The signed note's address is derived from the holder's full viewing key at diversifier index 0 with external scope: . A fixed index is used because the address is a private witness inside the ZKP circuit and is never publicly transmitted.
-
Nullifier commitment. The signed note's is set to the value specified by the rho binding condition in [Dummy Signed Note]. Because the dummy note has no on-chain existence, can be freely chosen; this design binds the hardware wallet's signature to the exact set of notes being claimed and the application context via the ZKP circuit, avoiding the need for a custom signing protocol.
-
Value. The note value MUST be set to 1 zatoshi (0.00000001 ZEC) in the PCZT, matching the value used in the signed note commitment integrity condition (see [Dummy Signed Note]).
-
Rseed. A fresh random is sampled for the note.
-
Note construction. The note is constructed via standard Orchard note construction using the address, value (1 zatoshi), rho, and rseed above.
For fewer than real notes, the wallet pads the remaining slots with zero-value dummy notes at diversifier indices (external scope) from the holder's full viewing key. These padding notes enter the commitment via their values but do not correspond to real on-chain notes.
PCZT Construction
The wallet constructs a PCZT as a single-action Orchard transaction:
Spend Side
- The single spend consumes the dummy signed note constructed above.
- A dummy Merkle authentication path (all-zero siblings, position 0) is used. The path is not verified on-chain; spend authorization is established by the ZKP circuit.
- The Orchard bundle builder generates the spend authorization randomizer and the randomized verification key internally.
Output Side
-
A single output of 1 zatoshi is addressed to the application hotkey.
-
The output memo SHOULD contain a human-readable delegation description. For example, a voting application might use:
"I am authorizing this hotkey managed by my wallet to participatein {instance_name} with {amount}.{frac} ZEC."where
{instance_name}identifies the proof-of-balance instance and{amount}.{frac}is the holder's eligible ZEC balance.
ZIP-32 Derivation
The spend MUST include a ZIP 32 11 derivation path so that the hardware wallet device can derive the correct spending key:
where is 133 for Zcash mainnet.
Finalization
The wallet applies the PCZT Creator and IoFinalizer roles. The IoFinalizer computes the ZIP 244 10 transaction identifier (sighash) that the hardware wallet device will sign.
Device Display
During signing, the hardware wallet device displays the PCZT as a standard Orchard transaction. The user sees fields such as:
Amount: 0.00000001 ZEC
Fee: 0 ZEC
Orchard
From
#1 0.00000001 ZEC Mine
<shielded>
To
#1 0.00000001 ZEC
{application hotkey address}
Memo: I am authorizing this hotkey managed by my
wallet to participate in {instance_name} with
{amount}.{frac} ZEC
The 0.00000001 ZEC amount (1 zatoshi) and 0 ZEC fee confirm that no real funds are being transferred. The "To" address matches the application hotkey address displayed in the wallet application. The memo provides human-readable context for what the user is authorizing.
The hardware wallet device has no awareness of application semantics. It interprets the PCZT identically to any other Orchard transaction.
Signature Extraction and Submission
After receiving the signed PCZT from the hardware wallet device, the wallet:
-
Parses the signed PCZT and reads the
spend_auth_sigfield from the relevant action. -
Recomputes the sighash that the device signed. This is the ZIP 244 transaction identifier computed over the PCZT.
-
Assembles the delegation submission by combining the hardware wallet signature and sighash with the Claim proof and other public inputs. The submission format is application-defined.
The delegation submission is sent to the application verifier. The wallet does not need the hardware wallet device again for the remainder of the usage instance; all subsequent operations use the application hotkey.
Rationale
Why Alternate Nullifiers Instead of Actual Spends
The most obvious approach to proof-of-balance (actually spending the notes to oneself at the snapshot anchor) fails because it consumes the notes on-chain. A holder who participates in one application cannot reuse the same notes in a concurrent application. Alternate nullifiers avoid this by operating entirely off the main chain: the standard nullifiers are never revealed, and the notes remain spendable.
Why Sentinel Initialization
Without sentinels, the initial non-membership tree would contain a single leaf spanning the entire field , with width . The in-circuit range checks use 250-bit windows (25 limbs of the 10-bit lookup table). An interval wider than would allow the upper-bound range check to wrap, enabling a prover to falsely claim non-membership for a value outside the interval.
Sentinels partition the field into intervals each narrower than at initialization. Real nullifier insertions only split intervals into smaller ones, so the width bound is maintained permanently.
Why 1 Zatoshi Instead of 0
The dummy signed note uses a value of 1 zatoshi (0.00000001 ZEC) in the PCZT rather than 0. When an Orchard Action has a 0-value note, Keystone's suppress the display of transaction fields (amount, fee, addresses, memo), presenting the user with insufficient information to make an informed signing decision. Setting the value to 1 zatoshi causes the device to render all fields normally.
The Batched Claim circuit uses the 1-zatoshi value in the signed note commitment integrity check (see [Dummy Signed Note]), matching the PCZT value. The dummy signed note's value does not contribute to the claimed balance.
Why a Dummy Note Instead of a Real Note
A holder's real Orchard notes are not spent or consumed during delegation. The dummy signed note is constructed specifically for the proof-of-balance flow and never appears in any on-chain note commitment tree. This design has three advantages:
- No fund risk. Because the proof-of-balance transaction is never broadcast to the Zcash mainchain and the signed note has no on-chain existence, there is no scenario in which the delegation signing could result in loss of funds.
- Note reusability. Because real notes are never consumed on mainchain, they remain fully spendable and available for other applications that use the proof-of-balance mechanism. Alternate nullifiers are domain-separated by , so the same notes can participate in concurrent proof-of-balance instances without conflict.
- PCZT compatibility. The dummy note reuses the standard Orchard Action structure, allowing the PCZT to pass through the hardware wallet's existing PCZT parser and signer without modification.
Why the Dummy Note's ρ Provides Non-Replayability
Because the dummy signed note does not correspond to any on-chain note, its inputs can be freely chosen. The design sets to a Poseidon hash of the delegated note commitments, the application commitment, and the usage identifier. Because enters the note commitment (and therefore the sighash), the hardware wallet's signature is cryptographically bound to the exact delegation context. An attacker cannot replay the signature for a different set of notes, a different application commitment, or a different usage instance.
This binding is enforced by the Batched Claim circuit's rho binding condition (see [Dummy Signed Note]), which the application verifier checks. The hardware wallet device does not need to understand the binding; it is sufficient that the device signs the sighash derived from the PCZT containing the committed . No custom signing protocol is required — the standard Orchard PCZT flow is reused, and all existing hardware wallets that support Orchard signing are compatible without firmware changes.
Why ZIP 244 Sighash
The PCZT uses the standard ZIP 244 10 transaction identifier as the sighash. This is the only sighash format that existing hardware wallet Orchard signing implementations can produce. Using an application-specific sighash would require firmware changes, which this specification explicitly avoids.
The ZIP 244 sighash commits to the full Orchard bundle structure (including the dummy note's commitment, which embeds the bound ), providing the necessary cryptographic binding without a custom signature scheme.
Why Not a Custom Signing Protocol
A future version of this specification could define a custom signing protocol purpose-built for proof-of-balance, rather than reusing the standard Orchard PCZT format. This would require ecosystem-wide coordination (firmware updates to all participating hardware wallets) but could yield several improvements:
- Binding application actions to the signature. Many use cases want to prove balance and take an action in a single step (e.g., casting a vote). A custom signing protocol could commit to the application action directly in the signature, reducing the number of ZKPs required by downstream protocols.
- Removing the hotkey indirection. With a richer signing format, the holder could authorize actions directly from the hardware wallet without delegating to an application hotkey, simplifying the trust model.
- Improved on-device display. A custom format could present application-aware context (e.g., "Vote on proposal X with Y ZEC") rather than the generic Orchard transaction fields shown today.
This specification intentionally avoids a custom signing protocol to maximize compatibility with existing wallets. The trade-off is accepted: downstream protocols may require additional ZKPs or hotkey indirection that a purpose-built signing format could eliminate.
Deployment
This ZIP does not specify a consensus change. Deployment considerations are application-specific.
The hardware wallet signing flow specified in [Wallet Signing] requires no firmware changes and no changes to Zcash mainchain consensus rules. Any hardware wallet that supports Orchard PCZT signing can participate immediately.
Software wallets that hold the spending key directly do not need the PCZT construction; they follow the same Claim proof specification but sign the application-defined sighash directly.
Reference implementation
A reference implementation of the Claim circuit (including the non-membership tree, alternate nullifier derivation, and multi-note batching for ) is available at valargroup/voting-circuits.
Open issues
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.3 [NU6.1] or later ↩
-
Draft ZIP: Air drops, Proof-of-Balance, and Stake-weighted Polling ↩ ↩2 ↩3
-
Draft ZIP: Private Information Retrieval for Nullifier Exclusion Proofs ↩
-
Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 3.1: Note Commitment Trees ↩
-
Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 5.4.2: Pseudo Random Functions ↩ ↩2
-
Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 5.4.1.9: Sinsemilla Hash Function ↩
-
Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 4.17.4: Action Statement (Orchard) ↩ ↩2 ↩3
-
Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 4.9: Merkle Path Validity ↩