Skip to content

Commit

Permalink
Describe note discovery note tags (#713)
Browse files Browse the repository at this point in the history
* docs: adding note tags to note discovery

* changing it slightly

* after review

* after another review

* adding a P2ID example

* adding a P2ID example
  • Loading branch information
Dominik1999 authored May 30, 2024
1 parent ce75498 commit 69a21c0
Showing 1 changed file with 53 additions and 19 deletions.
72 changes: 53 additions & 19 deletions docs/architecture/notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
comments: true
---

Two of Miden's key goals are parallel transaction execution and privacy.
Two of Miden's key goals are parallel transaction execution and privacy.

Polygon Miden implements a hybrid UTXO and account-based [state model](state.md) which enforces these goals with notes. Notes interact with, and transfer assets between, accounts. They can be consumed and produced asynchronously and privately.
Polygon Miden implements a hybrid UTXO and account-based [state model](state.md) which enforces these goals with notes. Notes interact with, and transfer assets between, accounts. They can be consumed and produced asynchronously and privately.

The concept of notes is a key divergence from Ethereum’s account-based model.
The concept of notes is a key divergence from Ethereum’s account-based model.

## Note design

Expand All @@ -23,23 +23,23 @@ The concept of notes is a key divergence from Ethereum’s account-based model.

## Note lifecycle

New notes are created by executing transactions.
New notes are created by executing transactions.

After verifying the transaction proof the operator adds either only the note hash (private notes) or the full note data (public notes) to the note database.
After verifying the transaction proof the operator adds either only the note hash (private notes) or the full note data (public notes) to the note database.

Notes can be produced and consumed locally by users in local transactions or by the operator in a network transaction.
Notes can be produced and consumed locally by users in local transactions or by the operator in a network transaction.

Note consumption requires the transacting party to know the note data to compute the nullifier. After successful verification, the operator sets the corresponding entry in the nullifier database to "consumed".

<center>
![Architecture core concepts](../img/architecture/note/note-life-cycle.png)
</center>

## Note creation
### Note creation

Notes are created as the outputs (`OutputNotes`) of Miden transactions. Operators record the notes to the [note database](state.md#note-database). After successful verification of the underlying transactions, those notes can be consumed.

## The note script
### The note script

Every note has a script which gets executed at note consumption. It is always executed in the context of a single account, and thus, may invoke zero or more of the [account's functions](accounts.md#code). The script allows for more than just asset transfers; actions which could be of arbitrary complexity thanks to the Turing completeness of the Miden VM.

Expand All @@ -52,11 +52,10 @@ There are [standard note scripts](https://github.com/0xPolygonMiden/miden-base/t
* P2ID and P2IDR scripts are used to send assets to a specific account ID. The scripts check at note consumption if the executing account ID equals the account ID that was set by the note creator as note inputs. The P2IDR script is reclaimable and thus after a certain block height can also be consumed by the sender itself.
* SWAP script is a simple way to swap assets. It adds an asset from the note into the consumer's vault and creates a new note consumable by the first note's issuer containing the requested asset.

!!! info "Example note script pay to ID (P2ID)"
Want to know how to ensure a note can only be consumed by a specified account?</
??? note "Example note script pay to ID (P2ID)"

#### Goal of the P2ID script

The P2ID script defines a specific target account ID as the only account that can consume the note. Such notes ensure a targeted asset transfer.

#### Imports and context
Expand Down Expand Up @@ -121,9 +120,9 @@ There are [standard note scripts](https://github.com/0xPolygonMiden/miden-base/t
1. Every note script starts with the note script root on top of the stack.
2. After the `dropw`, the stack is cleared.
3. Next, the script stored the note inputs at pos 0 in the [relative note context memory](https://0xpolygonmiden.github.io/miden-base/transactions/transaction-procedures.html#transaction-contexts) by `push.0 exec.note::get_inputs`.
4. Then, `mem_load` loads a `Felt` from the specified memory address and puts it on top of the stack, in that cases the `target_account_id` defined by the creator of the note.
4. Then, `mem_load` loads a `Felt` from the specified memory address and puts it on top of the stack, in that cases the `target_account_id` defined by the creator of the note.
5. Now, the note invokes `get_id` from the account API using `exec.account::get_id` - which is possible even in the note context.

Because, there are two account IDs on top of the stack now, `assert_eq` fails if the two account IDs (target_account_id and executing_account_id) are not the same. That means, the script cannot be successfully executed if executed by any other account than the account specified by the note creator using the note inputs.

If execution hasn't failed, the script invokes a helper procedure `exec.add_note_assets_to_account` to add the note's assets into the executing account's vault.
Expand Down Expand Up @@ -183,9 +182,9 @@ There are [standard note scripts](https://github.com/0xPolygonMiden/miden-base/t

Lastly, the pointer gets incremented, and if there is a second asset, the loop continues (`movup.4 add.1 dup dup.6 neq`). Finally, when all assets were put into the account's vault, the stack is cleared (`drop dropw drop`).

## Note storage mode
### Note storage mode

Similar to accounts, there are two storage modes for notes in Miden. Notes can be stored on-chain in the [note database](https://0xpolygonmiden.github.io/miden-base/architecture/state.html#notes-database) with all data publicly visible for everyone. Alternatively, notes can be stored off-chain by committing only the note hash to the note database.
Similar to accounts, there are two storage modes for notes in Miden - private and public. Notes can be stored publicly in the [note database](https://0xpolygonmiden.github.io/miden-base/architecture/state.html#notes-database) with all data publicly visible for everyone. Alternatively, notes can be stored privately by committing only the note hash to the note database.

Every note has a unique note hash. It is defined as follows:

Expand All @@ -196,12 +195,47 @@ hash(hash(hash(hash(serial_num, [0; 4]), script_hash), input_hash), vault_hash)
!!! info
To compute a note's hash, we do not need to know the note's `serial_num`. Knowing the hash of the `serial_num` (as well as `script_hash`, `input_hash` and `note_vault`) is also sufficient. We compute the hash of `serial_num` as `hash(serial_num, [0; 4])` to simplify processing within the VM._

## Note discovery
### Note discovery (note tags)

Note discovery describes the process by which Miden clients find notes they want to consume. Miden clients can query the Miden node for notes carrying a certain note tag in their metadata. Note tags are best-effort filters for notes registered on the network. They are lightweight values (32-bit) used to speed up queries. Clients can follow tags for specific use cases, such as swap scripts, or user-created custom tags. Tags are also used by the operator to identify notes intended for network execution and include the corresponding information on how to execute them.

The two most signification bits of the note tag have the following interpretation:

| Prefix | Execution hint | Target | Allowed note type |
| ------ | :------------: | :------: | :----------------:|
| `0b00` | Network | Specific | NoteType::Public |
| `0b01` | Network | Use case | NoteType::Public |
| `0b10` | Local | Any | NoteType::Public |
| `0b11` | Local | Any | Any |

Note discovery describes the process of Miden clients finding notes they want to consume. There are two ways to receive new relevant notes - getting notes via an off-chain channel or querying the Miden operator to request newly recorded relevant notes.
The latter is done via note tags. Tags are part of the note's metadata and are represented by a `Felt`. The `SyncState` API of the Miden node requires the Miden client to provide a `note_tag` value which is used as a filter in the operator's response. Tags are useful for note discovery enabling an easy collection of all notes matching a certain tag.
- Execution hint: Set to `Network` for network transactions. These notes are validated and, if possible, consumed in a network transaction.
- Target: Describes how to interpret the bits in the note tag. For tags with a specific target, the rest of the tag is interpreted as an `account_id`. For use case values, the meaning of the rest of the tag is not specified by the protocol and can be used by applications built on top of the rollup.
- Allowed note type: Describes the note's storage mode, either `public` or `private`.

The following 30 bits can represent anything. In the above example note tag, it represents an account Id of a public account. As designed the first bit of a public account is always `0` which overlaps with the second most significant bit of the note tag.

```
0b00000100_11111010_01010110_11100010
```

This example note tag indicates that the network operator (Miden node) executes the note against a specific account - `0x09f4adc47857e2f6`. Only the 30 most significant bits of the account id are represented in the note tag, since account Ids are 64-bit values but note tags only have 32-bits. Knowing a 30-bit prefix already narrows the set of potential target accounts down enough.

Using note tags is a compromise between privacy and latency. If a user queries the operator using the note ID, the operator learns which note a specific user is interested in. Alternatively, if a user always downloads all registered notes and filters locally, it is quite inefficient. By using tags, users can customize privacy parameters by narrowing or broadening their note tag schemes.

??? note "Example note tag for P2ID"
P2ID scripts can only be consumed by the specified account ID (target ID). In the standard schema, the target ID is encoded into the note tag.

For network execution of a P2ID note, the note tag is encoded as follows: 0b00000100_11111010_01010110_11100010. This encoding allows the Miden operator to quickly identify the account against which the transaction must be executed.

For local execution of a P2ID note, the recipient needs to be able to discover the note. The recipient can query the Miden node for a specific tag to see if there are new P2ID notes to be consumed. In this case, the two most significant bits are set to 0b11, allowing any note type (private or public) to be used. The next 14 bits represent the 14 most significant bits of the account ID, and the remaining 16 bits are set to 0.

Example for local execution:
```
0b11000100_11111010_00000000_00000000
```
This "fuzzy matching" approach balances privacy and efficiency. A note with this tag could be intended for any account sharing the same 16-bit prefix.

## Note consumption
### Note consumption

As with creation, notes can only be consumed in Miden transactions. If a valid transaction consuming an `InputNote` gets verified by the Miden node, the note's unique nullifier gets added to the [nullifier database](https://0xpolygonmiden.github.io/miden-base/architecture/state.html#nullifier-database) and is therefore consumed.

Expand Down

0 comments on commit 69a21c0

Please sign in to comment.