Skip to content

Commit 3b930e8

Browse files
authored
Merge pull request #148 from 0xMiden/kbg/feat/rust-compiler-resources-v13
feat: migrate Rust compiler resources to v13
2 parents c85f4d4 + b1d58ba commit 3b930e8

File tree

12 files changed

+515
-479
lines changed

12 files changed

+515
-479
lines changed

docs/builder/develop/tutorials/rust-compiler/miden-bank/00-project-setup.md

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,15 @@ miden --version
3434
<summary>Expected output</summary>
3535

3636
```text
37-
miden-cli 0.8.x
37+
The Miden toolchain porcelain:
38+
39+
Environment:
40+
- cargo version: cargo 1.93.0 (083ac5135 2025-12-15).
41+
42+
Midenup:
43+
- midenup + miden version: 0.1.0.
44+
- active toolchain version: 0.20.3.
45+
- ...
3846
```
3947

4048
</details>
@@ -90,7 +98,7 @@ edition = "2021"
9098
crate-type = ["cdylib"]
9199

92100
[dependencies]
93-
miden = { workspace = true }
101+
miden = { version = "0.10" }
94102

95103
[package.metadata.component]
96104
package = "miden:bank-account"
@@ -132,12 +140,12 @@ use miden::*;
132140
struct Bank {
133141
/// Tracks whether the bank has been initialized (deposits enabled).
134142
/// Word layout: [is_initialized (0 or 1), 0, 0, 0]
135-
#[storage(slot(0), description = "initialized")]
143+
#[storage(description = "initialized")]
136144
initialized: Value,
137145

138146
/// Maps depositor AccountId -> balance (as Felt).
139147
/// We'll use this to track user balances in Part 1.
140-
#[storage(slot(1), description = "balances")]
148+
#[storage(description = "balances")]
141149
balances: StorageMap,
142150
}
143151

@@ -185,18 +193,24 @@ Update the root `Cargo.toml` to reflect our renamed contract:
185193

186194
```toml title="Cargo.toml"
187195
[workspace]
188-
resolver = "2"
189-
190196
members = [
191-
"contracts/bank-account",
192-
"contracts/increment-note", # We'll replace this later
193-
"integration",
197+
"integration"
198+
]
199+
exclude = [
200+
"contracts/",
194201
]
202+
resolver = "2"
203+
204+
[workspace.package]
205+
edition = "2021"
195206

196207
[workspace.dependencies]
197-
miden = { version = "0.8" }
198208
```
199209

210+
:::info Contracts Are Excluded
211+
In v0.13, contracts are excluded from the Cargo workspace and built independently by `cargo miden`. Each contract specifies its own `miden` dependency directly. Only the `integration` crate remains a workspace member.
212+
:::
213+
200214
## Step 5: Build and Verify
201215

202216
Let's verify everything compiles correctly:
@@ -231,7 +245,7 @@ Let's create a simple test to verify the bank account can be created. Create a n
231245
use integration::helpers::{
232246
build_project_in_dir, create_testing_account_from_package, AccountCreationConfig,
233247
};
234-
use miden_client::account::{StorageMap, StorageSlot};
248+
use miden_client::account::{StorageMap, StorageSlot, StorageSlotName};
235249
use miden_client::Word;
236250
use std::{path::Path, sync::Arc};
237251

@@ -243,13 +257,21 @@ async fn test_bank_account_builds_and_loads() -> anyhow::Result<()> {
243257
true,
244258
)?);
245259

246-
// Create the bank account with initial storage
247-
// Slot 0: initialized flag (Value, starts as [0, 0, 0, 0])
248-
// Slot 1: balances map (StorageMap, starts empty)
260+
// Create named storage slots matching the contract's storage layout
261+
let initialized_slot =
262+
StorageSlotName::new("miden::component::miden_bank_account::initialized")
263+
.expect("Valid slot name");
264+
let balances_slot =
265+
StorageSlotName::new("miden::component::miden_bank_account::balances")
266+
.expect("Valid slot name");
267+
249268
let bank_cfg = AccountCreationConfig {
250269
storage_slots: vec![
251-
StorageSlot::Value(Word::default()),
252-
StorageSlot::Map(StorageMap::with_entries([])?),
270+
StorageSlot::with_value(initialized_slot, Word::default()),
271+
StorageSlot::with_map(
272+
balances_slot,
273+
StorageMap::with_entries([]).expect("Empty storage map"),
274+
),
253275
],
254276
..Default::default()
255277
};
@@ -268,7 +290,7 @@ async fn test_bank_account_builds_and_loads() -> anyhow::Result<()> {
268290
Run the test from the project root:
269291

270292
```bash title=">_ Terminal"
271-
cargo test --package integration part0_setup_test -- --nocapture
293+
cargo test --package integration test_bank_account_builds_and_loads -- --nocapture
272294
```
273295

274296
<details>
@@ -317,7 +339,7 @@ Your bank can be created, but doesn't do anything useful yet. In the next parts,
317339

318340
1. **`miden new`** creates a complete project workspace with contracts and integration folders
319341
2. **Account components** are defined with `#[component]` on a struct
320-
3. **Storage slots** are declared with `#[storage(slot(N))]` attributes
342+
3. **Storage slots** are declared with `#[storage(description = "...")]` attributes (the compiler auto-assigns slot numbers)
321343
4. **`miden build`** compiles Rust to Miden Assembly (.masp package)
322344
5. **Tests verify** that your code works before moving on
323345

docs/builder/develop/tutorials/rust-compiler/miden-bank/01-account-components.md

Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@ In Part 0, we created a minimal bank with just an `initialized` flag. Now we'll
2323

2424
```text
2525
Part 0: Part 1:
26-
┌────────────────────┐ ┌────────────────────┐
27-
│ Bank │ │ Bank │
28-
│ ───────────────── │ ──► │ ─────────────────
29-
slot 0: initialized│ │ slot 0: initialized
30-
│ │ │ slot 1: balances │ ◄── NEW
31-
└────────────────────┘ └────────────────────┘
26+
┌────────────────────┐ ┌──────────────────────────
27+
│ Bank │ │ Bank
28+
│ ───────────────── │ ──► │ ────────────────────────
29+
│ initialized (Value)│ │ initialized (Value)
30+
│ │ │ balances (StorageMap) │ ◄── NEW
31+
└────────────────────┘ └──────────────────────────
3232
```
3333

3434
## The #[component] Attribute
@@ -59,17 +59,17 @@ use miden::*;
5959
struct Bank {
6060
/// Tracks whether the bank has been initialized (deposits enabled).
6161
/// Word layout: [is_initialized (0 or 1), 0, 0, 0]
62-
#[storage(slot(0), description = "initialized")]
62+
#[storage(description = "initialized")]
6363
initialized: Value,
6464

6565
/// Maps depositor AccountId -> balance (as Felt)
6666
/// Key: [prefix, suffix, asset_prefix, asset_suffix]
67-
#[storage(slot(1), description = "balances")]
67+
#[storage(description = "balances")]
6868
balances: StorageMap,
6969
}
7070
```
7171

72-
We've added a `StorageMap` in slot 1 that will track each depositor's balance.
72+
We've added a `StorageMap` that will track each depositor's balance. The compiler auto-assigns slot numbers based on field order.
7373

7474
## Storage Types Explained
7575

@@ -80,7 +80,7 @@ Miden accounts have storage slots that persist state on-chain. Each slot holds o
8080
The `Value` type provides access to a single storage slot:
8181

8282
```rust
83-
#[storage(slot(0), description = "initialized")]
83+
#[storage(description = "initialized")]
8484
initialized: Value,
8585
```
8686

@@ -111,7 +111,7 @@ The `.read()` method requires a type annotation: `let current: Word = self.initi
111111
The `StorageMap` type provides key-value storage within a slot:
112112

113113
```rust
114-
#[storage(slot(1), description = "balances")]
114+
#[storage(description = "balances")]
115115
balances: StorageMap,
116116
```
117117

@@ -140,16 +140,16 @@ self.balances.set(key, new_balance);
140140
Unlike `Value::read()` which returns a `Word`, `StorageMap::get()` returns a single `Felt`. This is an important distinction.
141141
:::
142142

143-
### Storage Slot Layout
143+
### Storage Layout
144144

145145
Plan your storage layout carefully:
146146

147-
| Slot | Type | Purpose |
147+
| Name | Type | Purpose |
148148
|------|------|---------|
149-
| 0 | `Value` | Initialization flag |
150-
| 1 | `StorageMap` | Depositor balances |
149+
| `initialized` | `Value` | Initialization flag |
150+
| `balances` | `StorageMap` | Depositor balances |
151151

152-
The `description` attribute is for documentation and debugging - it doesn't affect runtime behavior.
152+
The `description` attribute generates named slot identifiers (e.g., `miden::component::miden_bank_account::initialized`) used in tests to reference specific slots. The compiler auto-assigns slot numbers based on field order.
153153

154154
## Step 2: Implement Component Methods
155155

@@ -231,7 +231,7 @@ Create a new test file:
231231
use integration::helpers::{
232232
build_project_in_dir, create_testing_account_from_package, AccountCreationConfig,
233233
};
234-
use miden_client::account::{StorageMap, StorageSlot};
234+
use miden_client::account::{StorageMap, StorageSlot, StorageSlotName};
235235
use miden_client::{Felt, Word};
236236
use std::{path::Path, sync::Arc};
237237

@@ -247,13 +247,22 @@ async fn test_bank_account_storage() -> anyhow::Result<()> {
247247
true,
248248
)?);
249249

250-
// Create the bank account with storage slots
250+
// Create named storage slots matching the contract's storage layout
251+
// The naming convention is: miden::component::{package_name_underscored}::{field_name}
252+
let initialized_slot =
253+
StorageSlotName::new("miden::component::miden_bank_account::initialized")
254+
.expect("Valid slot name");
255+
let balances_slot =
256+
StorageSlotName::new("miden::component::miden_bank_account::balances")
257+
.expect("Valid slot name");
258+
251259
let bank_cfg = AccountCreationConfig {
252260
storage_slots: vec![
253-
// Slot 0: initialized flag (starts as 0)
254-
StorageSlot::Value(Word::default()),
255-
// Slot 1: balances map (empty)
256-
StorageSlot::Map(StorageMap::with_entries([])?),
261+
StorageSlot::with_value(initialized_slot.clone(), Word::default()),
262+
StorageSlot::with_map(
263+
balances_slot.clone(),
264+
StorageMap::with_entries([]).expect("Empty storage map"),
265+
),
257266
],
258267
..Default::default()
259268
};
@@ -265,8 +274,8 @@ async fn test_bank_account_storage() -> anyhow::Result<()> {
265274
// VERIFY: Check initial storage state
266275
// =========================================================================
267276

268-
// Verify slot 0 (initialized) starts as 0
269-
let initialized_value = bank_account.storage().get_item(0)?;
277+
// Verify initialized flag starts as 0
278+
let initialized_value = bank_account.storage().get_item(&initialized_slot)?;
270279
assert_eq!(
271280
initialized_value,
272281
Word::default(),
@@ -283,7 +292,7 @@ async fn test_bank_account_storage() -> anyhow::Result<()> {
283292

284293
// Check that we can query the balances map (should return 0 for any key)
285294
let test_key = Word::from([Felt::new(1), Felt::new(2), Felt::new(0), Felt::new(0)]);
286-
let balance = bank_account.storage().get_map_item(1, test_key)?;
295+
let balance = bank_account.storage().get_map_item(&balances_slot, test_key)?;
287296

288297
// Balance for non-existent depositor should be all zeros
289298
assert_eq!(
@@ -302,7 +311,7 @@ async fn test_bank_account_storage() -> anyhow::Result<()> {
302311
Run the test from the project root:
303312

304313
```bash title=">_ Terminal"
305-
cargo test --package integration part1_account_test -- --nocapture
314+
cargo test --package integration test_bank_account_storage -- --nocapture
306315
```
307316

308317
<details>
@@ -330,7 +339,7 @@ test result: ok. 1 passed; 0 failed; 0 ignored
330339
:::tip Troubleshooting
331340
**"cannot find function `build_project_in_dir`"**: Make sure your `integration/src/helpers.rs` exports this function and `integration/src/lib.rs` has `pub mod helpers;`.
332341

333-
**"StorageSlot::Map not found"**: Ensure you're using the correct imports: `use miden_client::account::StorageSlot;`
342+
**"StorageSlot not found"**: Ensure you're using the correct imports: `use miden_client::account::{StorageSlot, StorageSlotName};`
334343
:::
335344

336345
## Complete Code for This Part
@@ -354,12 +363,12 @@ use miden::*;
354363
struct Bank {
355364
/// Tracks whether the bank has been initialized (deposits enabled).
356365
/// Word layout: [is_initialized (0 or 1), 0, 0, 0]
357-
#[storage(slot(0), description = "initialized")]
366+
#[storage(description = "initialized")]
358367
initialized: Value,
359368

360369
/// Maps depositor AccountId -> balance (as Felt)
361370
/// Key: [prefix, suffix, asset_prefix, asset_suffix]
362-
#[storage(slot(1), description = "balances")]
371+
#[storage(description = "balances")]
363372
balances: StorageMap,
364373
}
365374

@@ -405,7 +414,7 @@ impl Bank {
405414
1. **`#[component]`** marks structs and impl blocks as Miden account components
406415
2. **`Value`** stores a single Word, read with `.read()`, write with `.write()`
407416
3. **`StorageMap`** stores key-value pairs, access with `.get()` and `.set()`
408-
4. **Storage slots** are numbered from 0, each holds 4 Felts (32 bytes)
417+
4. **Storage slots** are identified by name (auto-assigned by compiler), each holds 4 Felts (32 bytes)
409418
5. **Public methods** are callable by other contracts via generated bindings
410419

411420
:::tip View Complete Source

docs/builder/develop/tutorials/rust-compiler/miden-bank/02-constants-constraints.md

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ Let's write a test to verify our constraints work correctly. This test verifies
241241
use integration::helpers::{
242242
build_project_in_dir, create_testing_account_from_package, AccountCreationConfig,
243243
};
244-
use miden_client::account::{StorageMap, StorageSlot};
244+
use miden_client::account::{StorageMap, StorageSlot, StorageSlotName};
245245
use miden_client::Word;
246246
use std::{path::Path, sync::Arc};
247247

@@ -254,11 +254,22 @@ async fn test_constraints_are_defined() -> anyhow::Result<()> {
254254
true,
255255
)?);
256256

257+
// Create named storage slots
258+
let initialized_slot =
259+
StorageSlotName::new("miden::component::miden_bank_account::initialized")
260+
.expect("Valid slot name");
261+
let balances_slot =
262+
StorageSlotName::new("miden::component::miden_bank_account::balances")
263+
.expect("Valid slot name");
264+
257265
// Create an uninitialized bank account
258266
let bank_cfg = AccountCreationConfig {
259267
storage_slots: vec![
260-
StorageSlot::Value(Word::default()), // initialized = 0
261-
StorageSlot::Map(StorageMap::with_entries([])?),
268+
StorageSlot::with_value(initialized_slot.clone(), Word::default()),
269+
StorageSlot::with_map(
270+
balances_slot,
271+
StorageMap::with_entries([]).expect("Empty storage map"),
272+
),
262273
],
263274
..Default::default()
264275
};
@@ -267,7 +278,7 @@ async fn test_constraints_are_defined() -> anyhow::Result<()> {
267278
create_testing_account_from_package(bank_package.clone(), bank_cfg).await?;
268279

269280
// Verify the bank starts uninitialized
270-
let initialized = bank_account.storage().get_item(0)?;
281+
let initialized = bank_account.storage().get_item(&initialized_slot)?;
271282
assert_eq!(
272283
initialized[0].as_int(),
273284
0,
@@ -287,7 +298,7 @@ async fn test_constraints_are_defined() -> anyhow::Result<()> {
287298
Run the test from the project root:
288299

289300
```bash title=">_ Terminal"
290-
cargo test --package integration part2_constraints -- --nocapture
301+
cargo test --package integration test_constraints_are_defined -- --nocapture
291302
```
292303

293304
<details>
@@ -372,10 +383,10 @@ const MAX_DEPOSIT_AMOUNT: u64 = 1_000_000;
372383
/// Bank account component that tracks depositor balances.
373384
#[component]
374385
struct Bank {
375-
#[storage(slot(0), description = "initialized")]
386+
#[storage(description = "initialized")]
376387
initialized: Value,
377388

378-
#[storage(slot(1), description = "balances")]
389+
#[storage(description = "balances")]
379390
balances: StorageMap,
380391
}
381392

0 commit comments

Comments
 (0)