Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Docs]: Update Accounts section #1012

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
240 changes: 76 additions & 164 deletions docs/architecture/accounts.md
Original file line number Diff line number Diff line change
@@ -1,211 +1,123 @@
Accounts are basic building blocks representing a user or an autonomous smart contract.
# Accounts

For smart contracts, the go-to solution is account-based state. Miden supports expressive smart contracts via a Turing-complete language and the use of accounts.
> The primary entities of the Miden protocol

In Miden, an account is an entity that holds assets and defines rules about how to transfer these assets.
## What is an account?

## Account design
In Miden, an `Account` represents an entity capable of holding assets, storing data, and executing custom code. Each `Account` is a specialized smart contract providing a programmable interface for interacting with its state and managed assets.

In Miden, every account is a smart contract. The diagram below illustrates the basic components of an account.
### Account type

<p style="text-align: center;">
<img src="../img/architecture/account/account-definition.png" style="width:30%;" alt="Account diagram"/>
</p>

> **Tip: Key to diagram**
> - **Account ID**: A unique identifier for an account. This does not change throughout its lifetime.
> - **Storage**: User-defined data which can be stored in an account.
> - **Nonce**: A counter which increments whenever the account state changes.
> - **Vault**: A collection of assets stored in an account.
> - **Code**: A collection of functions which define the external interface for an account.

### Account ID

A `~63` bits long identifier for the account ID (one field element `felt`).

The four most significant bits specify the [account type](#account-types) - regular or faucet - and the account-storage-modes - public or private.

### Account storage

The [storage of an account](../../objects/src/accounts/storage/mod.rs) is composed of a variable number of index-addressable [storage slots](../../objects/src/accounts/storage/slot/mod.rs), up to 255 slots in total.
There are two main categories of accounts in Miden: **basic accounts** and **faucets**.

Each slot has a type that defines its size and structure. Currently, the following types are supported:
- **Basic Accounts:**
Basic accounts may be either mutable or immutable:
- *Mutable:* Code can be changed after deployment.
- *Immutable:* Code cannot be changed once deployed.

* `StorageSlot::Value`: contains a single `Word` of data (i.e., 32 bytes).
* `StorageSlot::Map`: contains a [StorageMap](../../objects/src/accounts/storage/map.rs) which is a key-value map where both keys and values are `Word`s. The value of a storage slot containing a map is the commitment to the underlying map.
- **Faucets:**
Faucets are always immutable and can be specialized by the type of assets they issue:
- *Fungible Faucet:* Can issue fungible [assets](assets.md).
- *Non-fungible Faucet:* Can issue non-fungible [assets](assets.md).

As described below, accounts can be stored off-chain (private) and on-chain (public). Accounts that store large amounts of data, as is possible using storage maps, are better designed as off-chain accounts.
Type and mutability are encoded in the two most significant bits of the account's [ID](#id).

### Nonce

A counter which increments whenever the account state changes.

Nonce values must be strictly monotonically increasing and increment by any value smaller than `2^32` for every account update.

### Vault
### Account storage mode

An asset container for an account.
Users can choose whether their accounts are stored publicly or privately. The preference is encoded in the third and forth most significant bits of the accounts [ID](#id):

An account vault can contain an unlimited number of [assets](assets.md). The assets are stored in a sparse Merkle tree as follows:

* For fungible assets, the index of a node is defined by the issuing faucet ID, and the value of the node is the asset itself. Thus, for any fungible asset, there will be only one node in the tree.
* For non-fungible assets, the index is defined by the asset itself, and the asset is also the value of the node.

An account vault can be reduced to a single hash which is the root of the sparse Merkle tree.

### Code
- **Public Accounts:**
The account’s state is stored on-chain, similar to how accounts are stored in public blockchains like Ethereum. Contracts that rely on a shared, publicly accessible state (e.g., a DEX) should be public.

The interface for accounts. In Miden, every account is a smart contract. It has an interface that exposes functions that can be called by [note scripts](notes.md#the-note-script) and transaction scripts. Users cannot call those functions directly.
- **Private Accounts:**
Only a commitment (hash) to the account’s state is stored on-chain. This mode is suitable for users who prioritize privacy or plan to store a large amount of data in their account. To interact with a private account, a user must have knowledge of its interface.

Functions exposed by the account have the following properties:
The storage mode is chosen during account creation, it cannot be changed later.

* Functions are actually roots of [Miden program MASTs](https://0xpolygonmiden.github.io/miden-vm/user_docs/assembly/main.html) (i.e., a `32`-byte hash). Thus, the function identifier is a commitment to the code which is executed when a function is invoked.
* Only account functions have [mutable access](transactions/contexts.md) to an account's storage and vault. Therefore, the only way to modify an account's internal state is through one of the account's functions.
* Account functions can take parameters and can create new notes.
## Account core components

> **Note**
> Since code in Miden is expressed as MAST, every function is a commitment to the underlying code. The code cannot change unnoticed to the user because its hash would change. Behind any MAST root there can only be `256` functions.
An `Account` is composed of several core components, illustrated below:

#### Example account code

Currently, Miden provides two standard implementations for account code.

##### Basic user account

There is a standard for a basic user account. It exposes three functions via its interface.

<details>
<summary>Basic user account code</summary>

```arduino
use.miden::contracts::wallets::basic->basic_wallet
use.miden::contracts::auth::basic

export.basic_wallet::receive_asset
export.basic_wallet::create_note
export.basic_wallet::move_asset_to_note
export.basic::auth_tx_rpo_falcon512
```
</details>

[Note scripts](notes.md#the-note-script) or transaction scripts can call `receive_asset`, `create_note`, and `move_asset_to_note` procedures.

Transaction scripts can also call `auth_tx_rpo_falcon512` to authenticate the transaction.

> **Warning**
> Without correct authentication, i.e., knowing the correct private key, a note cannot successfully invoke `receive_asset`, `create_note`, or `move_asset_to_note`.

##### Basic fungible faucet (faucet for fungible assets)

There is also a standard for a [basic fungible faucet](https://github.com/0xPolygonMiden/miden-base/blob/main/miden-lib/asm/miden/contracts/faucets/basic_fungible.masm).
<p style="text-align: center;">
<img src="../img/architecture/account/account-definition.png" style="width:30%;" alt="Account diagram"/>
</p>

<details>
<summary>Fungible faucet code</summary>
These components are:

```arduino
#! Distributes freshly minted fungible assets to the provided recipient.
#!
#! ...
export.distribute
# get max supply of this faucet. We assume it is stored at pos 3 of slot 1
push.METADATA_SLOT exec.account::get_item drop drop drop
# => [max_supply, amount, tag, note_type, RECIPIENT, ...]
1. [ID](#id)
2. [Storage](#storage)
3. [Nonce](#nonce)
4. [Vault](#vault)
5. [Code](#code)

# get total issuance of this faucet so far and add amount to be minted
exec.faucet::get_total_issuance
# => [total_issuance, max_supply, amount, tag, note_type RECIPIENT, ...]
### ID

# compute maximum amount that can be minted, max_mint_amount = max_supply - total_issuance
sub
# => [max_supply - total_issuance, amount, tag, note_type, RECIPIENT, ...]
> An immutable and unique identifier for the `Account`.

# check that amount <= max_supply - total_issuance, fails if otherwise
dup.1 gte assert.err=ERR_BASIC_FUNGIBLE_MAX_SUPPLY_OVERFLOW
# => [asset, tag, note_type, RECIPIENT, ...]
A 63-bit long number represents the account ID. It's four most significant bits encode:
- [**Account type:**](#the-accounts-type) basic or faucet.
- [**Account storage mode:**](#the-accounts-storage-mode) public or private.

# creating the asset
exec.asset::create_fungible_asset
# => [ASSET, tag, note_type, RECIPIENT, ...]
This encoding allows the ID to convey both the account’s unique identity and its operational settings.

# mint the asset; this is needed to satisfy asset preservation logic.
exec.faucet::mint
# => [ASSET, tag, note_type, RECIPIENT, ...]
### Storage

# store and drop the ASSET
mem_storew.3 dropw
# => [tag, note_type, RECIPIENT, ...]
> A flexible, arbitrary data store within the `Account`.

# create a note containing the asset
exec.tx::create_note
# => [note_ptr, ZERO, ZERO, ...]
The [storage](../../objects/src/accounts/storage/mod.rs) is divided into a maximum of 255 indexed [storage slots](../../objects/src/accounts/storage/slot/mod.rs). Each slot can either store an arbitrary 32-byte `Word` or serve as a pointer to a key-value store with large amounts capacity.

# store and drop the ASSET
padw mem_loadw.3 movup.4 exec.tx::add_asset_to_note
# => [note_ptr, ASSET, ZERO, ...]
end
- **`StorageSlot::Value`:** Contains 32 bytes of arbitrary data.
- **`StorageSlot::Map`:** Contains a [StorageMap](../../objects/src/accounts/storage/map.rs), a key-value store where both keys and values are `Word`s. The slot's value is a commitment (hash) to the entire map.

#! Burns fungible assets.
#!
#! ...
export.burn
# burning the asset
exec.faucet::burn
# => [ASSET]
### Nonce

# increments the nonce (anyone should be able to call that function)
push.1 exec.account::incr_nonce
> A counter incremented with each state update to the `Account`.

# clear the stack
padw swapw dropw
# => [...]
end
```
</details>
The `nonce` enforces ordering and prevents replay attacks. It must strictly increase with every account state update. The increment must be less than `2^32` but always greater than the previous nonce, ensuring a well-defined sequence of state changes.

The contract exposes two functions `distribute` and `burn`.
### Vault

The first function, `distribute`, can only be called by the faucet owner; otherwise, it fails. As inputs, the function expects everything that is needed to create a note containing the freshly minted asset, i.e., amount, metadata, and recipient.
> A collection of [assets](assets.md) stored by the `Account`.

The second function, `burn`, burns the tokens that are contained in a note and can be called by anyone.
Large amounts of fungible and non-fungible assets can be stored in the accounts vault.

> **Info: Difference between `burn` and `distribute`**
> The `burn` procedure exposes `exec.account::incr_nonce`, so by calling `burn`, the nonce of the executing account gets increased by `1`, and the transaction will pass the epilogue check. The `distribute` procedure does not expose that. That means the executing user needs to call `basic::auth_tx_rpo_falcon512`, which requires the private key.
### Code

## Account creation
> A collection of functions defining the `Account`’s programmable interface.

For an account to exist, it must be present in the [account database](state.md#account-database) kept on the Miden node(s).
Every Miden account is essentially a smart contract. The `Code` component defines the account’s functions, which can be invoked through both [Note scripts](notes.md#the-note-script) and transaction scripts. Key characteristics include:

phklive marked this conversation as resolved.
Show resolved Hide resolved
However, new accounts can be created locally by users using the Miden client. The process is as follows:
- **Mutable access:** Only the account’s own functions can modify its storage and vault. All state changes—such as updating storage slots, incrementing the nonce, or transferring assets—must occur through these functions.
- **Function commitment:** Each function can be called by its [MAST](https://0xpolygonmiden.github.io/miden-vm/user_docs/assembly/main.html) root. The root represents the underlying code tree as a 32-byte hash. This ensures integrity, i.e., the caller calls what he expects.
phklive marked this conversation as resolved.
Show resolved Hide resolved
- **Note creation:** Account functions can generate new notes.

1. Alice creates a new account ID (according to the account types) using the Miden client.
2. Alice's Miden client asks the Miden node to check if the new ID already exists.
3. Alice shares the ID with Bob (e.g., when Alice wants to receive funds).
4. Bob executes a transaction and creates a note that contains an asset for Alice.
5. Alice consumes Bob's note to receive the asset in a transaction.
6. Depending on the account storage mode (private vs. public) and transaction type (local vs. network), the operator eventually receives the new account ID and - if the transaction is
## Account lifecycle

correct - adds the ID to the account database.
Throughout its lifetime, an `Account` progresses through various phases:

A user can create an account in one of the following manners:
- **Creation and Deployment:** Initialization of the account on the network.
- **Active Operation:** Continuous state updates via account functions that modify the storage, nonce, and vault.
- **Termination or Deactivation:** Optional, depending on the contract’s design and governance model.

1. Use the [Miden client](https://0xpolygonmiden.github.io/miden-docs/miden-client/index.html) as a wallet.
2. Use the Miden base built-in functions for wallet creation: [basic wallet](https://github.com/0xPolygonMiden/miden-base/blob/4e6909bbaf65e77d7fa0333e4664be81a2f65eda/miden-lib/src/accounts/wallets/mod.rs#L15), [fungible faucet](https://github.com/0xPolygonMiden/miden-base/blob/4e6909bbaf65e77d7fa0333e4664be81a2f65eda/miden-lib/src/accounts/faucets/mod.rs#L11)
### Account creation

## Account types
For an account to be recognized by the network, it must exist in the [account database](state.md#account-database) maintained by Miden node(s).

There are two basic account types in Miden: Regular accounts and faucets. Only faucets can mint new assets. Regular accounts can be mutable or immutable, which simply means that it is possible to change the account code after creation.
However, a user can locally create a new account ID before it’s recognized network-wide. The typical process might be:

Type and mutability are encoded in the most significant bits of the account's ID.
1. Alice generates a new account ID locally (according to the desired account type) using the Miden client.
2. The Miden client checks with a Miden node to ensure the ID does not already exist.
3. Alice shares the new ID with Bob (for example, to receive assets).
4. Bob executes a transaction, creating a note containing assets for Alice.
5. Alice consumes Bob’s note in her own transaction to claim the asset.
6. Depending on the account’s storage mode and transaction type, the operator receives the new account ID and, if all conditions are met, includes it in the account database.

| | Basic mutable | Basic immutable | Fungible faucet | Non-fungible faucet |
|------------------------|---------------|------------------|------------------|----------------------|
| **Description** | For most users, e.g., a wallet. Code changes allowed, including public API. | For most smart contracts. Once deployed, code is immutable. | Users can issue fungible assets and customize them. | Users can issue non-fungible assets and customize them. |
| **Code updatability** | Yes | No | No | No |
| **Most significant bits** | `00` | `01` | `10` | `11` |
## Conclusion

## Public and private accounts
In this section, we covered:

Users can decide whether to keep their accounts private or public at account creation. The account ID encodes this preference in the third and fourth most significant bits.
- [What is an `Account`](#what-is-an-account)
- [Its constituent components](#the-accounts-core-components)
- [Its lifecycle](#the-accounts-lifecycle)

* **Accounts with public state**: The actual state is stored on-chain. This is similar to how accounts work in public blockchains, like Ethereum. Smart contracts that depend on public shared state should be stored publicly on Miden, e.g., a DEX contract.
* **Accounts with private state**: Only the hash of the account is stored on-chain. Users who want to stay private and manage their own data should choose this option. Users who want to interact with private accounts need to know the account's interface.
With this information, you are now better equipped to understand how Miden `Accounts` operate, how they manage data and assets, and how their programmable functions enable secure and flexible interactions within the Miden protocol.
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ If you want to join the technical discussion, please check out the following:

* [Discord](https://discord.gg/0xpolygonRnD)
* [Miden repo](https://github.com/0xPolygonMiden)
* [Roadmap](introduction/roadmap.md)
* [Roadmap](roadmap.md)

> **Info**
> - These docs are still work-in-progress.
Expand Down
Loading