Skip to content

refactor: add SlotName to StorageSlotHeader with Cow lifetime parameter#2160

Merged
bobbinth merged 9 commits into0xMiden:nextfrom
Farukest:feature/storage-slot-header-refactor
Dec 15, 2025
Merged

refactor: add SlotName to StorageSlotHeader with Cow lifetime parameter#2160
bobbinth merged 9 commits into0xMiden:nextfrom
Farukest:feature/storage-slot-header-refactor

Conversation

@Farukest
Copy link
Contributor

Summary

Refactors StorageSlotHeader to contain SlotName using Cow<'name, SlotName> for efficient borrowing without unnecessary cloning.

Changes

  • StorageSlotHeader now has a lifetime parameter and contains slot name
  • AccountStorageHeader uses Vec<StorageSlotHeader<'static>>
  • Added Serializable/Deserializable implementations for SlotName
  • Updated AccountStorage::to_header() to use placeholder slot names
  • Updated miden-tx crate to use the new API

Closes #2126

@PhilippGackstatter
Copy link
Contributor

PhilippGackstatter commented Dec 11, 2025

@Farukest Thank you! Can you rebase this onto next (or merge next)? There have been some changes and renames, e.g. SlotName -> StorageSlotName.

@Farukest
Copy link
Contributor Author

Hey @PhilippGackstatter working on it. thanks :)

@Farukest Farukest force-pushed the feature/storage-slot-header-refactor branch from 140a5c6 to d43833f Compare December 11, 2025 10:06
@Farukest
Copy link
Contributor Author

Done @PhilippGackstatter

@Farukest Farukest force-pushed the feature/storage-slot-header-refactor branch 3 times, most recently from 559f833 to e4cdc58 Compare December 11, 2025 17:12
Copy link
Contributor

@PhilippGackstatter PhilippGackstatter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me! Great work! You did a great job of matching the miden-base style of Rust 😄. Thanks for adding docs, separators and the little things. Much appreciated!

Can you merge in next again?

I left two more comments for simplification.

Comment on lines 199 to 206
.map(|(slot_name, slot_type, _)| match slot_type {
StorageSlotType::Value => (slot_name.clone(), *slot_type, Word::empty()),
StorageSlotType::Map => (slot_name.clone(), *slot_type, StorageMap::new().root()),
.map(|slot_header| match slot_header.slot_type() {
StorageSlotType::Value => {
(slot_header.name().clone(), slot_header.slot_type(), Word::empty())
},
StorageSlotType::Map => {
(slot_header.name().clone(), slot_header.slot_type(), StorageMap::new().root())
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I would simplify this a bit more and add some convenience constructors on the header type, i.e.:

/// Creates empty slots of the same slot types as the to-be-created account.
fn empty_storage_header_from_account(account: &PartialAccount) -> AccountStorageHeader {
    let slots: Vec<StorageSlotHeader> = account
        .storage()
        .header()
        .slots()
        .map(|slot_header| match slot_header.slot_type() {
            StorageSlotType::Value => {
                StorageSlotHeader::with_empty_value(slot_header.name().clone())
            },
            StorageSlotType::Map => StorageSlotHeader::with_empty_map(slot_header.name().clone()),
        })
        .collect();

    AccountStorageHeader::new(slots).expect("storage header should be valid")
}

(The sort step was removed in #2154 which is on next).
This requires:

pub fn with_empty_value(name: StorageSlotName) -> Self {
    Self::with_owned_name(name, StorageSlotType::Value, Word::empty())
}

pub fn with_empty_map(name: StorageSlotName) -> Self {
    Self::with_owned_name(name, StorageSlotType::Map, EMPTY_STORAGE_MAP_ROOT)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done !

/// Returns an [AccountStorageHeader] for this account storage.
pub fn to_header(&self) -> AccountStorageHeader {
AccountStorageHeader::new(
AccountStorageHeader::from_tuples(
Copy link
Contributor

@PhilippGackstatter PhilippGackstatter Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can make this a bit nicer as well by defining a storage slot -> header conversion, i.e.:

impl<'name> From<&'name StorageSlot> for StorageSlotHeader<'name> {
    fn from(slot: &'name StorageSlot) -> Self {
        StorageSlotHeader::with_borrowed_name(slot.name(), slot.slot_type(), slot.value())
    }
}

And then we can do:

AccountStorageHeader::new(
          self.slots
              .iter()
              .map(StorageSlotHeader::from)
              .map(StorageSlotHeader::into_owned)
              .collect(),
      )
      .expect("slots should be valid as ensured by AccountStorage")

And then I think we can remove AccountStorageHeader::from_tuples, as it's only really needed in tests and encourages use of the header type directly. If you prefer keeping it for the tests, that's fine too. In that case, let's move it behind #[cfg(any(feature = "testing", test))] and to crates/miden-objects/src/testing/storage.rs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done too :)

@Farukest
Copy link
Contributor Author

Looks good to me! Great work! You did a great job of matching the miden-base style of Rust 😄. Thanks for adding docs, separators and the little things. Much appreciated!

I try my best for miden. thank you for your kind words 😄
@PhilippGackstatter

@Farukest Farukest force-pushed the feature/storage-slot-header-refactor branch from 0a777df to 4680cb3 Compare December 12, 2025 16:50
Copy link
Contributor

@bobbinth bobbinth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Thank you! Not a full review, but I left a couple of comments inline.

/// - The slots are not sorted by [`StorageSlotId`].
/// - There are multiple storage slots with the same [`StorageSlotName`].
pub fn new(slots: Vec<(StorageSlotName, StorageSlotType, Word)>) -> Result<Self, AccountError> {
pub fn new(slots: Vec<StorageSlotHeader<'static>>) -> Result<Self, AccountError> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought the intent was to keep StorageSlotHeader as an internal-only type. If we do want to make it public, I'd rather not complicate the interface with borrowed vs. owned semantics.

If the main reason for this is to avoid cloning strings, we could define StorageSlotName as:

pub struct StorageSlotName {
    name: Arc<str>,
    id: StorageSlotId,
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done too @bobbinth

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! But I think the question is still whether we want to add lifetime parameters to StorageSlotHeader. My preference would be not to do this and update StorageSlotName as I mention in the above comment. But would be great to get @PhilippGackstatter thoughts on this too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm waiting for your discussion result :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After making the decision to keep the slot header internal, I encountered a few places where we exposed the underlying 3-tuple of the header and where it would be beneficial to have a wrapper type for that. Besides, the tuple representation commits to the structure as much as a wrapper type, so the benefit of keeping it internal no longer seemed great enough. So I now think it's fine to expose the StorageSlotHeader.

I think redefining StorageSlotName to use Arc<str> is also fine to avoid the lifetime parameter. In the context of the account component metadata refactor I thought we may have to replace StorageValueName with StorageSlotName. In that case, having a String internally may be better (for mutability), but after realizing there are conflicting requirements between those two types (slot name should enforce a minimum length; storage value name can also represent a single part of a name) I think we cannot deduplicate those and so making the internal representation immutable seems fine.

@Farukest Let's change StorageSlotName to use Arc<str> and then change StorageSlotHeader to use name: StorageSlotName and remove the lifetime.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done !

@bobbinth
Copy link
Contributor

Also, @Farukest - could you merge the latest next into this branch?

This refactors `StorageSlotHeader` to use `Cow<'name, StorageSlotName>`
for the slot name, allowing efficient borrowing without cloning.

Changes:
- Added lifetime parameter to `StorageSlotHeader<'name>`
- Changed `id` field from `StorageSlotId` to `Cow<'name, StorageSlotName>`
- Added `with_borrowed_name()` constructor for borrowing slot names
- Added `with_owned_name()` constructor for owned slot names
- Added accessors: `name()`, `id()`, `slot_type()`, `value()`
- Added `into_owned()` method for converting to `'static` lifetime
- Updated `AccountStorage::to_elements()` to use borrowed names
- Updated `AccountStorageHeader::to_elements()` to use borrowed names

This avoids implicit cloning in `to_elements` methods, improving
performance when computing storage commitments.

Closes 0xMiden#2126
…ublic

  - Change AccountStorageHeader::new to take Vec<StorageSlotHeader<'static'>>
  - Add AccountStorageHeader::from_tuples for tuple-based construction
  - Make StorageSlotHeader and its methods public
  - Remove all #[allow(dead_code)] attributes from StorageSlotHeader
  - Update find_slot_header_by_name and find_slot_header_by_id to return &StorageSlotHeader
  - Update TransactionBaseHost::initial_account_storage_slot to return &StorageSlotHeader
  - Add Serializable and Deserializable implementations for StorageSlotHeader
  - Simplify Deserializable for AccountStorageHeader by reusing new()
- Rename field 'id' to 'name' in StorageSlotHeader for consistency with type
- Move StorageSlotHeader struct to bottom of file (AccountStorageHeader is primary)
- Update all internal references from self.id to self.name
@Farukest Farukest force-pushed the feature/storage-slot-header-refactor branch from 7667687 to 1d0455b Compare December 13, 2025 21:15
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like there are some extraneous changes in this file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry my work environment is too messy nowadays.
Fixed :)

Copy link
Contributor

@bobbinth bobbinth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Thank you! Let's add a single entry to the changelog, and I think this is good to go.

@Farukest
Copy link
Contributor Author

Looks good! Thank you! Let's add a single entry to the changelog, and I think this is good to go.

Done @bobbinth

@bobbinth bobbinth merged commit fad69ad into 0xMiden:next Dec 15, 2025
15 checks passed
afa7789 pushed a commit to afa7789/miden-base that referenced this pull request Jan 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Consider refactoring StorageSlotHeader to contain SlotName

3 participants