Applied Zcash

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)
  1. The app selects a binding domain D. D is 16 bytes. D is unique to the app and to the version of its record format.
  2. The app gives each type of record a kind. The kind is one byte.
  3. The app encodes the record as bytes. These bytes are the fields.
  4. 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
  1. The wallet builds a usual Orchard-protocol output with this rseed. ZIP 212 and ZIP 2005 calculate esk, ψ and rcm from rseed and ρ. ρ 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:

  1. calculates rseed from the record;
  2. sets ρ = nf_old;
  3. calculates ψ and rcm from rseed and ρ;
  4. calculates the note commitment;
  5. 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

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.