Skip to content

Commit

Permalink
chore: fix doc merge conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
bobbinth committed Jan 23, 2025
1 parent bd86fd3 commit 93d2439
Showing 1 changed file with 1 addition and 178 deletions.
179 changes: 1 addition & 178 deletions docs/architecture/notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,184 +93,7 @@ After validation, `Note`s become “live” and eligible for discovery and event

Clients often need to find specific `Note`s of interest. Miden allows clients to query the `Note` database using `Note` tags. These lightweight, 32-bit data fields serve as best-effort filters, enabling quick lookups for `Note`s related to particular use cases, scripts, or account prefixes.

As discussed in detail in [transaction kernel procedures](transactions/procedures.md) certain procedures can only be invoked in certain contexts. The note script is being executed in the note context of the [transaction kernel](transactions/kernel.md).

#### Main script

The main part of the P2ID script checks if the executing account is the same as the account defined in the `NoteInputs`. The creator of the note defines the note script and the note inputs separately to ensure usage of the same standardized P2ID script regardless of the target account ID. That way, it is enough to check the script root (see above).

```arduino
# Pay-to-ID script: adds all assets from the note to the account, assuming ID of the account
# matches target account ID specified by the note inputs.
#
# Requires that the account exposes: miden::contracts::wallets::basic::receive_asset procedure.
#
# Inputs: [SCRIPT_ROOT]
# Outputs: []
#
# Note inputs are assumed to be as follows:
# - target_account_id is the ID of the account for which the note is intended.
#
# FAILS if:
# - Account does not expose miden::contracts::wallets::basic::receive_asset procedure.
# - Account ID of executing account is not equal to the Account ID specified via note inputs.
# - The same non-fungible asset already exists in the account.
# - Adding a fungible asset would result in amount overflow, i.e., the total amount would be
# greater than 2^63.
begin
# drop the transaction script root
dropw
# => []

# load the note inputs to memory starting at address 0
push.0 exec.note::get_inputs
# => [inputs_ptr]

# read the target account id from the note inputs
mem_load
# => [target_account_id]

exec.account::get_id
# => [account_id, target_account_id, ...]

# ensure account_id = target_account_id, fails otherwise
assert_eq
# => [...]

exec.add_note_assets_to_account
# => [...]
end
```

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.
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.

#### Add assets

This procedure adds the assets held by the note into the account's vault.

```arduino
#! Helper procedure to add all assets of a note to an account.
#!
#! Inputs: []
#! Outputs: []
#!
proc.add_note_assets_to_account
push.0 exec.note::get_assets
# => [num_of_assets, 0 = ptr, ...]

# compute the pointer at which we should stop iterating
dup.1 add
# => [end_ptr, ptr, ...]

# pad the stack and move the pointer to the top
padw movup.5
# => [ptr, 0, 0, 0, 0, end_ptr, ...]

# compute the loop latch
dup dup.6 neq
# => [latch, ptr, 0, 0, 0, 0, end_ptr, ...]

while.true
# => [ptr, 0, 0, 0, 0, end_ptr, ...]

# save the pointer so that we can use it later
dup movdn.5
# => [ptr, 0, 0, 0, 0, ptr, end_ptr, ...]

# load the asset
mem_loadw
# => [ASSET, ptr, end_ptr, ...]
# pad the stack before call
padw swapw padw padw swapdw
# => [ASSET, pad(12), ptr, end_ptr, ...]

# add asset to the account
call.wallet::receive_asset
# => [pad(16), ptr, end_ptr, ...]

# clean the stack after call
dropw dropw dropw
# => [0, 0, 0, 0, ptr, end_ptr, ...]

# increment the pointer and compare it to the end_ptr
movup.4 add.1 dup dup.6 neq
# => [latch, ptr+1, 0, 0, 0, 0, end_ptr, ...]
end

# clear the stack
drop dropw drop
end
```

The procedure starts by calling `exec.note::get_assets`. As with the note's inputs before, this writes the assets of the note into memory starting at the specified address. Assets are stored in consecutive memory slots, so `dup.1 add` provides the last memory slot.

In Miden, [assets](assets.md) are represented by `Words`, so we need to pad the stack with four `0`s to make room for an asset. Now, if there is at least one asset (checked by `dup dup.6 neq`), the loop starts. It first saves the pointer for later use (`dup movdn.5`), then loads the first asset `mem_loadw` on top of the stack.

Now, the procedure calls the a function of the account interface `call.wallet::receive_asset` to put the asset into the account's vault. Due to different [contexts](https://0xpolygonmiden.github.io/miden-base/transactions/transaction-procedures.html#transaction-contexts), a note script cannot directly call an account function to add the asset. The account must expose this function in its [interface](https://0xpolygonmiden.github.io/miden-base/architecture/accounts.html#example-account-code).

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

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:

```arduino
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 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 |

- 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.
Using `Note` tags strikes a balance between privacy and efficiency. Without tags, querying a specific `Note` ID reveals a user’s interest to the operator. Conversely, downloading and filtering all registered `Note`s locally is highly inefficient. Tags allow users to adjust their level of privacy by choosing how broadly or narrowly they define their search criteria, letting them find the right balance between revealing too much information and incurring excessive computational overhead.

### Note consumption

Expand Down

0 comments on commit 93d2439

Please sign in to comment.