Skip to content
Merged
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
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

### Features

- [BREAKING] Refactor storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987), [#2025](https://github.com/0xMiden/miden-base/pull/2025), [#2149](https://github.com/0xMiden/miden-base/pull/2149), [#2150](https://github.com/0xMiden/miden-base/pull/2150), [#2153](https://github.com/0xMiden/miden-base/pull/2153), [#2154](https://github.com/0xMiden/miden-base/pull/2154), [#2161](https://github.com/0xMiden/miden-base/pull/2161)).
- [BREAKING] Refactor storage slots to be accessed by names instead of indices ([#1987](https://github.com/0xMiden/miden-base/pull/1987), [#2025](https://github.com/0xMiden/miden-base/pull/2025), [#2149](https://github.com/0xMiden/miden-base/pull/2149), [#2150](https://github.com/0xMiden/miden-base/pull/2150), [#2153](https://github.com/0xMiden/miden-base/pull/2153), [#2154](https://github.com/0xMiden/miden-base/pull/2154), [#2161](https://github.com/0xMiden/miden-base/pull/2161), [#2170](https://github.com/0xMiden/miden-base/pull/2170)).

### Changes

Expand Down
2 changes: 0 additions & 2 deletions crates/miden-lib/asm/shared_modules/account_id.masm
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ const.ERR_ACCOUNT_ID_UNKNOWN_STORAGE_MODE="unknown account storage mode in accou

const.ERR_ACCOUNT_ID_SUFFIX_LEAST_SIGNIFICANT_BYTE_MUST_BE_ZERO="least significant byte of the account ID suffix must be zero"

const.ERR_ACCOUNT_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS="provided storage slot index is out of bounds"

const.ERR_ACCOUNT_ID_NON_PUBLIC_NETWORK_ACCOUNT="the account ID must have storage mode public if the network flag is set"

# CONSTANTS
Expand Down
5 changes: 2 additions & 3 deletions crates/miden-objects/src/account/delta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,10 +256,9 @@ impl AccountDelta {
/// a value slot being set to EMPTY_WORD and its value being unchanged.
/// - Storage map slots:
/// - Map slots append a header which summarizes the changes in the slot, in particular the
/// slot index and number of changed entries. Since only changed slots are included, the
/// number of changed entries is never zero.
/// slot ID and number of changed entries.
/// - Two distinct storage map slots use the same domain but are disambiguated due to
/// inclusion of the slot index.
/// inclusion of the slot ID.
///
/// ### Domain Separators
///
Expand Down
2 changes: 1 addition & 1 deletion crates/miden-objects/src/account/delta/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ impl AccountStorageDelta {
self.maps.entry(slot_name).or_default().insert(key, new_value);
}

/// Inserts an empty storage map delta for the provided slot index.
/// Inserts an empty storage map delta for the provided slot name.
///
/// This is useful for full state deltas to represent an empty map in the delta.
pub fn insert_empty_map_delta(&mut self, slot_name: StorageSlotName) {
Expand Down
4 changes: 0 additions & 4 deletions crates/miden-tx/src/errors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,10 +207,6 @@ pub enum TransactionKernelError {
FailedToAddAssetToNote(#[source] NoteError),
#[error("note input data has hash {actual} but expected hash {expected}")]
InvalidNoteInputs { expected: Word, actual: Word },
#[error(
"storage slot index {actual} is invalid, must be smaller than the number of account storage slots {max}"
)]
InvalidStorageSlotIndex { max: u64, actual: u64 },
#[error(
"failed to respond to signature requested since no authenticator is assigned to the host"
)]
Expand Down
3 changes: 1 addition & 2 deletions crates/miden-tx/src/host/storage_delta_tracker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,7 @@ impl StorageDeltaTracker {
// Keep only the values whose new value is different from the initial value.
value_slots.retain(|slot_name, new_value| {
// SAFETY: The header in the initial storage is the one from the account against
// which the transaction is executed, so accessing that slot index
// should be fine.
// which the transaction is executed, so accessing that slot name should be fine.
let (_slot_type, initial_value) = storage_header
.find_slot_header_by_name(slot_name)
.expect("slot name should exist");
Expand Down
2 changes: 0 additions & 2 deletions crates/miden-tx/src/host/tx_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,6 @@ impl TransactionEvent {

TransactionEventId::AccountStorageAfterSetItem => {
// Expected stack state: [event, slot_ptr, VALUE]
// get slot index from the stack and make sure it is valid
let slot_ptr = process.get_stack_item(1);
let new_value = process.get_stack_word_be(2);

Expand Down Expand Up @@ -291,7 +290,6 @@ impl TransactionEvent {

TransactionEventId::AccountStorageAfterSetMapItem => {
// Expected stack state: [event, slot_ptr, KEY, OLD_MAP_VALUE, NEW_VALUE]
// get slot index from the stack and make sure it is valid
let slot_ptr = process.get_stack_item(1);
let key = process.get_stack_word_be(2);
let old_map_value = process.get_stack_word_be(6);
Expand Down
54 changes: 48 additions & 6 deletions docs/src/account/storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,56 @@ title: "Storage"
A flexible, arbitrary data store within the `Account`.
:::

The [storage](https://docs.rs/miden-objects/latest/miden_objects/account/struct.AccountStorage.html) is divided into a maximum of 255 indexed [storage slots](https://docs.rs/miden-objects/latest/miden_objects/account/enum.StorageSlot.html). Each slot can either store a 32-byte value or serve as the cryptographic root to a key-value store with the capacity to store large amounts of data.
The [storage](https://docs.rs/miden-objects/latest/miden_objects/account/struct.AccountStorage.html) consists of up to 256 individual [storage slots](https://docs.rs/miden-objects/latest/miden_objects/account/enum.StorageSlot.html), where each slot consists of:
- Key: Slot name.
- Value: either a single [`Word`](#storage-units) or a key-value map of [`Word`](#storage-units)s.

- **Value slots:** Contains 32 bytes of arbitrary data.
- **Map slots:** Contains a [StorageMap](#map-slots), a key-value store where both keys and values are 32 bytes. The slot's value is a commitment to the entire map.
So, account storage can be thought of as a map from slot name to slot value.

#### Storage units

The basic storage unit in account storage is a `Word`. A `Word` consists of 4 elements where each element is slightly less than a 64-bit value. Specifically, a maximum value of an element is $2^{64} - 2^{32}$. Thus, a single `Word` can contain almost 32 bytes of data.

Since a `Word` cannot store exactly 32 bytes of data, we may need to use multiple words to store such binary data as Keccak or SHA256 hashes. At the minimum, a 32-byte hash would require 5 elements to store, but it is recommended to encode such hashes into 8 elements with each element containing 32 bits of data. In both cases, two words would be used to store the hash.

## Slot Name

Each slot has a name associated with it that uniquely identifies it in the account's storage.

One example for a slot name is:

```text
miden::standards::fungible_faucets::metadata
```

Slot names are intended to be _globally unique_. This means `miden::standards::fungible_faucets::metadata` should always identify a slot that contains the standardized metadata for a fungible faucet. [Account components](components.md) that define their own storage slots should therefore choose names that avoid collisions.

To reduce the chance of slot name collisions, it is recommended that slot names have at least three components separated by `::`. A recommended pattern is:

```text
project_name::component_name::slot_name
```

### Slot ID

Because slot names are too large to be used directly in the transaction kernel, a _slot ID_ is used instead. The slot ID is derived from the hash of the slot name. Miden Assembly APIs will always work with the slot ID instead of the slot name.

## Slot types

Each slot has one of the following types:

- **Value slot:** Contains a single `Word` of arbitrary data.
- **Map slot:** Contains a [StorageMap](#map-slots), a key-value store where both keys and values are `Word`s. The slot's value is set to the root of the map.

An account's storage is typically the result of merging multiple [account components](./components).

## Value Slots
### Value Slots

A value slot can be used whenever a single `Word` (almost 32 bytes) of data is enough, e.g. for storing a single public key commitment for use in [authentication procedures](code#authentication).

A value slot can be used whenever 32 bytes of data is enough, e.g. for storing a single public key for use in [authentication procedures](code#authentication).
Value slots can be used with `set_item`, `get_item` and `get_initial_item` APIs.

## Map Slots
### Map Slots

A map slot contains a `StorageMap` which is a key-value store implemented as a sparse Merkle tree (SMT). This allows an account to store a much larger amount of data than would be possible using only the account's storage slots. The root of the underlying SMT is stored in a single account storage slot, and each map entry is a leaf in the tree. When retrieving an entry (e.g., via `active_account::get_map_item`), its inclusion is proven using a Merkle proof.

Expand All @@ -31,3 +69,7 @@ Key properties of `StorageMap`:
- **Key hashing:** Since map keys are user-chosen and may not be uniformly distributed, keys are hashed before being inserted into the SMT. This ensures a more balanced tree and mitigates efficiency issues due to key clustering. The original keys are retained in a separate map, allowing for introspection (e.g., querying the set of stored original keys for debugging or explorer scenarios). This introduces some redundancy, but enables useful features such as listing all stored keys.

This design allows for flexible, scalable, and privacy-preserving storage within accounts, supporting both large datasets and efficient proof generation.

Map slots can be used with `set_map_item`, `get_map_item` and `get_initial_map_item` APIs.

Additionally, `get_item` and `get_initial_item` can also be used to access the root of the storage map.