Notes (Encrypted UTXOs)
Nullmask uses a UTXO model for the shielded account state. Each UTXO is called a note — an encrypted container holding value, token type, and owner information.
Note Structure
Definition 4: Note
A Nullmask Note is a tuple (rk_hash,value,coin_id,rk_trapdoor,value_trapdoor,nfs_hash) where:
rk_hash: hash of the Receiving Key that owns the note
value: note value (token amount)
coin_id: token address (zero address for native ETH)
rk_trapdoor: masking factor of note owner
value_trapdoor: masking factor of note value
nfs_hash: hash of all nullifiers from the shielded action that created this note
The Role of nfs_hash
For notes created via deposits, nfs_hash is set to a unique deposit index. For notes created by shielded actions, it is the Poseidon2 hash of all 6 input note nullifiers. Including this hash serves three purposes:
Uniqueness — Guarantees note commitment and nullifier uniqueness; prevents faerie gold attacks
Recursion — Enables recursive proving: a shielded action proof can verify proofs of the actions that funded it
Compliance — Enables Private Proof of Innocence and other compliance mechanisms
Note Commitment
Algorithm 4: Note Commitment
Input: A note (rk_hash,value,coin_id,rk_trapdoor,value_trapdoor,nfs_hash)
Output: Note commitment cm
rk_commitment←Poseidon2T4(rk_hash,rk_trapdoor)
value_commitment←Poseidon2T4(value,coin_id,value_trapdoor)
Return Poseidon2T4(rk_commitment,value_commitment,nfs_hash)
The commitment is a single field element that is stored in the on-chain Merkle tree. It hides the note's contents while enabling verification of note ownership and value in ZK proofs.
Note Nullifier
Definition 5: Note Nullifier
For a note with commitment cm belonging to a Viewing Key whose nullifying key is nk, the note nullifier is:
nf:=Poseidon2T4(cm,nk)
When a note is spent, its nullifier is published on-chain. The contract maintains a set of all spent nullifiers and rejects any transaction that attempts to spend a note whose nullifier has already been recorded. This prevents double-spending without revealing which note was spent.
Spendable Notes
A spendable note extends the basic note with Merkle tree context:
To spend a note in a ZK proof, the circuit:
Computes the note commitment from the plaintext fields
Verifies the Merkle path from the commitment to the tree root
Computes the note nullifier from the commitment and nullifying key
Asserts the note owner matches the sender's receiving key hash
Asserts the sender's
pnkequalsPoseidon2(nk)(proves ownership of nullifying key)
Unified 6-Note Funding
All shielded actions take an array of 6 SpendableNote inputs. Notes are categorized by their coin_id:
Notes matching the action asset contribute to the action value pool
Notes matching the fee asset contribute to the fee value pool
If action asset equals fee asset, both pools are combined
Unused slots are filled with dummy notes (all fields zero). Dummy notes produce a nullifier of 0, which the contract skips during nullifier recording.
Nullifiers Hash
All 6 nullifiers (including zeros from dummies) are hashed together:
nullifiers_hash=Poseidon2T4(nf1,nf2,nf3,nf4,nf5,nf6)
This hash is included in all output note commitments, binding them to the specific set of input notes that funded them.
Commitment and Nullifier Diagram

Last updated