Replies: 2 comments 10 replies
-
Actually, the above description did not take into account some new elements described in #356. Specifically, new structure of a note would look like this: where The above changes our definition of note hash and nullifier computations as follows:
|
Beta Was this translation helpful? Give feedback.
-
I have a question about who sets the serial number. Given the following scenario 1.1) Let Provided 2.1) Wouldn't It would be safe, however, if we have a form of asymmetric cryptography as serial number, allowing |
Beta Was this translation helpful? Give feedback.
-
In previous discussions I've mentioned that we use two databases to keep track of unconsumed notes: the Note DB and the Nullifier DB. However, I have not described previously how exactly these two work together to accomplish this.
At the high level, these databases do the following:
Leaves in the Note DB are not individual notes but rather Merkle trees such that each tree contains all notes created in a given block. Thus, a single leaf gets added to the Note DB with each new block. The note tree for each block itself contains trees of notes aggregated in a given transaction batch - but describing the exact structure of the Notes DB is not the main point of this article. The important part is that for every note ever created, we should be able to produce an inclusion path, and at the leaf of this path we will have a note hash.
A note hash is computed simply by hashing all components of a note. Specifically, components of a note are:
where:
vault_hash
is a commitment to all assets contained within a note.script_root
is a root of a MAST for the note's spend script.serial_num
is a value chosen randomly by the note's recipient.Thus, a note hash can be computed as:
Where$hash$ is a native hash function used by the VM.
The above scheme is binding: we cannot change anything about the note without changing the hash. It is also hiding in that we need to know all 3 components of a note to be able to match a note to its hash. For example, knowing a note's
vault_hash
andscript_root
is not enough to figure if note hashes to a given value. We must also know theserial_num
.As mentioned above, note hashes are appended to the Note DB whenever new notes are created. However, when notes are consumed, we record note nullifiers in the Nullifier DB. We could have recorded note hashes in the Nullifier DB, but that would make notes linkable. Specifically, when a note is consumed (assuming it is consumed in a local transaction), we don't want to reveal which note was consumed exactly. Thus, we want the nullifier to have the following properties:
Originally, I was thinking that we could compute a nullifier simply as:
This would satisfy the two properties mentioned above (the nullifier is derived deterministically, and nullifier and note hash are unlinkable), and it might have worked in a fully private setting, where
serial_num
is known only to the note's recipient. But in a setting when we have private and public notes, this creates an attack vector.For example, let's say we create a public note with some serial number$a$ . This serial number can be observed by anyone on the network. A malicious user could create another note with the same serial number, and then consume it. Once this happens, an entry will be added to the Nullifier database indicating that a note with nullifier $hash(a)$ has been consumed. This would make it impossible for us to consume our original note.
To address this issue, we can compute the nullifier like so:
This is similar to
note_hash
computation, but here we hash theserial_num
one extra time. The properties we get from this are:The last point means that it would make no sense for an attacker to create a note with the same serial number as to get the same nullifier they would need to create an identical note.
While I think the above scheme should work, I haven't spent much time analyzing it and would appreciate any feedback (especially if anyone sees holes in it).
Beta Was this translation helpful? Give feedback.
All reactions