Owner: @David Rusu
Certain use-cases like Native Zones require notes which anyone can spend. This document describes the pattern we intend to use to support this use-case.
In order to spend a note, you must be able to construct the note nullifier. The note nullifier is a function of the nullifier secret key $nf_{sk}$ and the note commitment.
nullifier = derive_nullifier(nf_sk, note_cm)
Therefore, these two fields must be public knowledge in order to allow anyone to derive the nullifier and spend the note.
By convention, we can use nf_sk = 0
for permisionless notes.
The note commitment is a function of the note_witness
, nf_pk
and nonce
.
nf_pk = derive_nf_pk(nf_sk)
note_cm = derive_note_cm(note_witness, nf_pk, nonce)
We have nf_sk
and we can assume that note_witness
is publicly available, therefore, the remaining free variable is the nonce.
The nonce must never be re-used as you risk creating an unspendable note (e.g. if the note somehow returns to some past state and the nonce happens to be identical to that nonce used when the old identical state note was spent, than the note will become unspendable since the nullifier has already been spent.)
To solve this problem of nonce collision, permisionless note constraints should enforce that output note nonces follow a deterministic and collision resistant nonce evolution algorithm.
A simple solution would be to simply enforce that output nonces should be some deterministic hash of the input nullifier:
output_nonce = hash("NONCE_EVOLVE", nf_sk, note_cm)
But the nonce evolution scheme should be tailored to the application. For example, if your application is a Native Zone who’s identity is maintained across a few different notes (state note, fund note). In that cause you could use a simple extension of the above scheme: Use the STF note’s nullifier to derive nonces for each output note.
output_state_nonce = hash("NONCE_EVOLVE_STATE", nf_sk, input_note_cm)
output_fund_nonce = hash("NONCE_EVOLVE_FUND", nf_sk, input_note_cm)
PRs implementing the above: