Pattern
Bound notes
An app puts a hash of its data in the rseed of a note. ZIP 212 calculates ψ and rcm from
rseed. Thus, the note commitment cmx is a commitment to the data.
Standard wallets can decrypt a bound note. Anyone with the data can calculate cmx again and
compare it with the chain. The pattern operates with the consensus rules of today.
Problem
Many apps must keep a public record on the chain. Anyone must be able to examine the record. Examples are a name registry, a decision market and a timestamp service.
A memo is a weak place for a record. The memo is in the note ciphertext, outside the note commitment. To read a memo, a verifier needs a viewing key. Compact blocks hold only the first part of each ciphertext.
The note commitment is a strong place for a record. Consensus adds each note commitment to the note commitment tree. Consensus accepts each nullifier one time, so each note can be spent one time only. A record in the note commitment gets these two properties.
Mechanism
kind ─┐
fields ─┼─ BLAKE2b-256(D, …) ──→ rseed ─┐
prev ─┘ ├─ ZIP 212, ZIP 2005 ──→ esk, ψ, rcm
ρ = nf_old ┘
recipient, value, ρ, ψ, rcm ──→ NoteCommit ──→ cmx (on the chain)
- The app selects a binding domain
D.Dis 16 bytes.Dis unique to the app and to the version of its record format. - The app gives each type of record a kind. The kind is one byte.
- The app encodes the record as bytes. These bytes are the fields.
- The app calculates
rseed:
rseed = BLAKE2b-256(D, kind ‖ len(fields) ‖ fields ‖ prev)
kind 1 byte
len(fields) 8 bytes, little-endian
prev 32 bytes: the cmx of the predecessor, or 32 zero bytes
-
The wallet builds a usual Orchard-protocol output with this
rseed. ZIP 212 and ZIP 2005 calculate esk, ψ and rcm fromrseedand ρ. ρ is the nullifier of the note that the same action spends.
The length prefix makes the encoding injective. Two different records always give two different inputs to the hash.
Chains
A chain is a sequence of bound notes. The first note is the genesis note. Each note after it is the successor of
the note before it. prev is the cmx of the predecessor. For the genesis note,
prev is 32 zero bytes.
The transaction of a successor must spend its predecessor. Consensus accepts each nullifier one time. Thus, one transaction at most extends each tip. The chain is an append-only record.
Verification
A verifier knows the recipient address, the value, the lead byte of the note plaintext and the record. The verifier
reads nf_old and cmx from the action. Then the verifier:
- calculates
rseedfrom the record; - sets ρ =
nf_old; - calculates ψ and rcm from
rseedand ρ; - calculates the note commitment;
- compares its x-coordinate with
cmx.
This check needs only public data. To check lineage, the verifier also needs the nullifier key nk of
the account that holds the chain. With nk, the verifier calculates the nullifier of the predecessor
and finds it in the transaction of the successor. An app with a public chain publishes its full viewing key.
Authority
Anyone can send a bound note to a published address. Thus, each app must define which notes count. This rule is
sufficient: the app pins a genesis note by its transaction ID and cmx. A note counts only if it
descends from that genesis note. A false note costs its sender a fee, and the record stays the same.
Privacy
rseed is a function of the record. A person who knows or guesses the record can calculate
rseed, and then esk. With esk, that person can decrypt the note and its memo. Thus, a bound note to a
published address is public. For a public registry, this is the correct result.
To keep a bound note private, put a secret of 256 bits or more in the fields. Examples are the hash of a secret key and a random blinding value.
Cost
- Fee. A usual record transaction spends the tip and one funding note. It makes the new note and a change note. That is two actions. The ZIP 317 fee for two actions is 10,000 zatoshis (0.0001 ZEC).
- Wallets. Each wallet tries to decrypt each action on the chain. Keep the number of records small. See Polite actions.
- Verifiers. One BLAKE2b hash and one Sinsemilla commitment for each note. The demo below does this work in your browser.
-
Builders. The transaction builder must accept an
rseedfrom its caller. Theorchardcrate samplesrseedat random. A patch addsadd_output_with_rseed. The patch is pending upstream. A PCZT (ZIP 374) carries a bound output in its usualrseedfield.
Spec
The full spec is a draft ZIP. It includes the binding domain rules, the amendment to ZIP 212, the lineage check and the security analysis. This site will publish the draft after review.
Demo: a timestamp registry
The registry proves that a document existed at a block height. Each stamp is a zero-value note to the registry address.
D "TimeStamp_v1____"
kind 1 (stamp)
fields BLAKE2b-256("TimeStamp_doc_v1", document)
prev the cmx of the previous stamp
The demo uses the orchard crate, compiled to WebAssembly. The registry key is a demo key. The ρ of the
first stamp is a demo value. Each next stamp spends the previous stamp in the same action. Thus, its ρ is the
nullifier of the previous stamp.
Next stamp
Chain
The chain is empty. Add a stamp.
Verify a stamp
The WASM module is loading.