Skip to content

docs(notes): clarify purpose, serial number, and nullifier terms#3016

Open
BrianSeong99 wants to merge 2 commits into
nextfrom
docs/notes-page-improvements
Open

docs(notes): clarify purpose, serial number, and nullifier terms#3016
BrianSeong99 wants to merge 2 commits into
nextfrom
docs/notes-page-improvements

Conversation

@BrianSeong99

@BrianSeong99 BrianSeong99 commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Addresses #1825.

Summary

Implements the outstanding items from the Notes page review (docs/src/note.md), re-scoped against the current state of the page and aligned with current (v0.15) protocol terminology.

Note: this is a targeted interim fix for the specific gaps flagged in #1825. A full pass over the docs across all major core repos is planned for later in June; broader structural and consistency work will land there rather than here.

Changes

  • Purpose & scope — enumerate the three roles of a note (asset transfer, account notification via note presence, and account state change via the note's storage) instead of implying notes are only for asset transfers.
  • Serial number — replace the one-line explanation with the commitment/nullifier framing: both concepts are introduced first, then the serial number's role in guaranteeing uniqueness and protecting privacy. Uses "commitment" terminology throughout, per earlier feedback on the issue.
  • Nullifier formula — define each term (SERIAL_NUM, SCRIPT_ROOT, STORAGE_COMMITMENT, ASSET_COMMITMENT, METADATA, ATTACHMENTS_COMMITMENT), grounded in Nullifier::new (crates/miden-protocol/src/note/nullifier.rs), each cross-linked to its section.
  • Terminology alignment — fix a remaining "note's hash" reference to "note's commitment", and correct the SWAP tag example from "2 bits" to "1 bit" of the note's type, reflecting the 1-bit NoteType encoding introduced in v0.15.0.

Scope notes

  • The original vault_hash item is obsolete: the nullifier formula was rewritten and vault_hash no longer appears on the page (now ASSET_COMMITMENT et al.). The re-scoped item defines the formula's actual terms, which were previously undefined.
  • Note tag composition is already documented under Note discovery, so it is out of scope here.

@BrianSeong99 BrianSeong99 force-pushed the docs/notes-page-improvements branch 2 times, most recently from c161041 to d6eed41 Compare June 1, 2026 06:38
Address the outstanding items from the Notes page review and align
terminology with the current (v0.15) protocol:

- Purpose & scope: enumerate the three roles of a note (asset
  transfer, account notification via note presence, and account
  state change via the note's storage) rather than implying notes
  are only for asset transfers.
- Serial number: replace the one-line explanation with the
  commitment/nullifier framing, introducing both concepts before
  describing how the serial number guarantees uniqueness and
  protects privacy. Uses "commitment" terminology throughout.
- Nullifier: define each term in the nullifier formula
  (SERIAL_NUM, SCRIPT_ROOT, STORAGE_COMMITMENT, ASSET_COMMITMENT,
  METADATA, ATTACHMENTS_COMMITMENT), grounded in Nullifier::new,
  and fix a remaining "note's hash" reference to "note's commitment".
- Note tag: correct the SWAP tag example from "2 bits" to "1 bit"
  of the note's type, reflecting the NoteType 1-bit encoding
  introduced in v0.15.0.

The original "define vault_hash" item is obsolete: the nullifier
formula was rewritten and vault_hash no longer appears on the page.
Note tag composition is already documented under Note discovery.
@BrianSeong99 BrianSeong99 force-pushed the docs/notes-page-improvements branch from d6eed41 to ad32821 Compare June 1, 2026 06:57
Comment thread docs/src/note.md

- **Transferring assets** between accounts.
- **Notifying an account**, through the mere presence of a note addressed to it.
- **Changing an account's state**, through the data in the note's [storage](#storage), which the consuming account's code can act on.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This makes it sound like notes transfer data to an account passively, and the account acts on them. But the note script is usually the one that "acts on the data in the storage".

I'd also suggest replacing "changing an account's state" with "invoking an account's public procedures" which better captures the idea that a note cannot just arbitrarily change an account, but must go through the account's interface - so note and account both have something to say in this process.

Comment thread docs/src/note.md
Comment on lines +16 to +17
- **Notifying an account**, through the mere presence of a note addressed to it.
- **Changing an account's state**, through the data in the note's [storage](#storage), which the consuming account's code can act on.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that points 2 and 3 are really distinct use cases. Notifying an account essentially only happens when the note is consumed, in which case this is covered by point 3.

Comment thread docs/src/note.md
Comment on lines +67 to +68
- **Commitment**: a commitment to the `Note`'s data (including the serial number) that can be stored publicly without revealing the note's details. It represents the note in the notes database, which is how private notes are recorded on-chain.
- **Nullifier**: a value derived from the `Note`'s data (including the serial number) that uniquely marks the note as consumed, without allowing the note's data to be reconstructed.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • We recently renamed "note commitment" to "note ID".
  • A nullifier is also a commitment to the note, just purposefully computed differently than the note ID, so that one isn't derivable from the other.
  • It doesn't feel ideal that we introduce these important note concepts under "serial number". Seems like we should restructure this to introduce things so they build on top of each other. Serial number is lower in the hierarchy than note ID and nullifier. Maybe we should first introduce serial number and then explain its purpose in the note ID and nullifier section (and linking to this from here so there is a reference). I think a good part of this is already explained in #note-nullifier-ensuring-private-consumption, so I'd suggest extending this to mention that the serial number helps ensure uniqueness.

Comment thread docs/src/note.md
With that context, the serial number serves two purposes:

- **Uniqueness**: the added randomness ensures each note's commitment is unique even when two notes hold identical assets, scripts, and inputs, preventing collisions in the notes database.
- **Privacy**: the serial number is the secret link between a note's commitment and its nullifier. If an attacker learns the serial number of a private note, they can recompute its nullifier from the commitment and detect when the note is spent — linking its creation to its consumption. The serial number of a private note must therefore be kept secret; if leaked, privacy is compromised even when the rest of the note's data remains hidden.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the serial number is the secret link between a note's commitment and its nullifier

I don't find this accurate, as the link between note ID and nullifier is that the same inputs are used, just hashed together differently. The serial number is just one part of this.

If an attacker learns the serial number of a private note, they can recompute its nullifier from the commitment

I'm not sure this works, or maybe I don't understand it. A leaked serial number could compromise privacy, but, afaict, only if certain other key properties are also known like storage and assets commitment. Without these, the nullifier cannot be computed and guessing or deriving them should be pretty hard in most circumstances.

Comment thread docs/src/note.md
- To compute the nullifier, one must know all components of the `Note`: serial_num, script_root, storage_commitment, assets_commitment, metadata, and attachments_commitment.

That means if a `Note` is private and the operator stores only the note's hash, only those with the `Note` details know if this `Note` has been consumed already. Zcash first [introduced](https://zcash.github.io/orchard/design/nullifiers.html#nullifiers) this approach.
That means if a `Note` is private and the operator stores only the note's commitment, only those with the `Note` details know if this `Note` has been consumed already. Zcash first [introduced](https://zcash.github.io/orchard/design/nullifiers.html#nullifiers) this approach.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
That means if a `Note` is private and the operator stores only the note's commitment, only those with the `Note` details know if this `Note` has been consumed already. Zcash first [introduced](https://zcash.github.io/orchard/design/nullifiers.html#nullifiers) this approach.
That means if a `Note` is private and the operator stores only the note's ID, only those with the `Note` details know if this `Note` has been consumed already. Zcash first [introduced](https://zcash.github.io/orchard/design/nullifiers.html#nullifiers) this approach.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants