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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
- [BREAKING] Removed `hash_account` ([#2442](https://github.com/0xMiden/miden-base/pull/2442)).
- [BREAKING] Renamed `AccountHeader::commitment`, `Account::commitment` and `PartialAccount::commitment` to `to_commitment` ([#2442](https://github.com/0xMiden/miden-base/pull/2442)).
- [BREAKING] Remove `BlockSigner` trait ([#2447](https://github.com/0xMiden/miden-base/pull/2447)).
- Updated account schema commitment construction to accept borrowed schema iterators; added extension trait to enable `AccountBuilder::with_schema_commitment()` helper ([#2419](https://github.com/0xMiden/miden-base/pull/2419)).

## 0.13.3 (2026-01-27)

Expand Down
82 changes: 74 additions & 8 deletions crates/miden-standards/src/account/metadata/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@ use alloc::collections::BTreeMap;

use miden_protocol::Word;
use miden_protocol::account::component::{AccountComponentMetadata, StorageSchema};
use miden_protocol::account::{AccountComponent, StorageSlot, StorageSlotName};
use miden_protocol::errors::ComponentMetadataError;
use miden_protocol::account::{
Account,
AccountBuilder,
AccountComponent,
StorageSlot,
StorageSlotName,
};
use miden_protocol::errors::{AccountError, ComponentMetadataError};
use miden_protocol::utils::sync::LazyLock;

use crate::account::components::storage_schema_library;
Expand All @@ -30,14 +36,16 @@ pub struct AccountSchemaCommitment {
}

impl AccountSchemaCommitment {
/// Creates a new [`AccountSchemaCommitment`] component from a list of storage schemas.
/// Creates a new [`AccountSchemaCommitment`] component from storage schemas.
///
/// The input schemas are merged into a single schema before the final commitment is computed.
///
/// # Errors
///
/// Returns an error if the schemas contain conflicting definitions for the same slot name.
pub fn new(schemas: &[StorageSchema]) -> Result<Self, ComponentMetadataError> {
pub fn new<'a>(
schemas: impl IntoIterator<Item = &'a StorageSchema>,
) -> Result<Self, ComponentMetadataError> {
Ok(Self {
schema_commitment: compute_schema_commitment(schemas)?,
})
Expand Down Expand Up @@ -74,16 +82,52 @@ impl From<AccountSchemaCommitment> for AccountComponent {
}
}

// ACCOUNT BUILDER EXTENSION
// ================================================================================================

/// An extension trait for [`AccountBuilder`] that provides a convenience method for building an
/// account with an [`AccountSchemaCommitment`] component.
pub trait AccountBuilderSchemaCommitmentExt {
/// Builds an [`Account`] out of the configured builder after computing the storage schema
/// commitment from all components currently in the builder and adding an
/// [`AccountSchemaCommitment`] component.
///
/// # Errors
///
/// Returns an error if:
/// - The components' storage schemas contain conflicting definitions for the same slot name.
/// - [`AccountBuilder::build`] fails.
fn build_with_schema_commitment(self) -> Result<Account, AccountError>;
}

impl AccountBuilderSchemaCommitmentExt for AccountBuilder {
fn build_with_schema_commitment(self) -> Result<Account, AccountError> {
let schema_commitment =
AccountSchemaCommitment::new(self.storage_schemas()).map_err(|err| {
AccountError::other_with_source("failed to compute account schema commitment", err)
})?;

self.with_component(schema_commitment).build()
}
}

// HELPERS
// ================================================================================================

/// Computes the schema commitment.
///
/// The account schema commitment is computed from the merged schema commitment.
/// If the passed list of schemas is empty, [`Word::empty()`] is returned.
fn compute_schema_commitment(schemas: &[StorageSchema]) -> Result<Word, ComponentMetadataError> {
if schemas.is_empty() {
fn compute_schema_commitment<'a>(
schemas: impl IntoIterator<Item = &'a StorageSchema>,
) -> Result<Word, ComponentMetadataError> {
let mut schemas = schemas.into_iter().peekable();
if schemas.peek().is_none() {
return Ok(Word::empty());
}

let mut merged_slots = BTreeMap::new();

for schema in schemas {
for (slot_name, slot_schema) in schema.iter() {
match merged_slots.get(slot_name) {
Expand Down Expand Up @@ -114,10 +158,11 @@ fn compute_schema_commitment(schemas: &[StorageSchema]) -> Result<Word, Componen
mod tests {
use miden_protocol::Word;
use miden_protocol::account::AccountBuilder;
use miden_protocol::account::auth::PublicKeyCommitment;
use miden_protocol::account::component::AccountComponentMetadata;

use super::AccountSchemaCommitment;
use crate::account::auth::NoAuth;
use super::{AccountBuilderSchemaCommitmentExt, AccountSchemaCommitment};
use crate::account::auth::{AuthEcdsaK256Keccak, NoAuth};

#[test]
fn storage_schema_commitment_is_order_independent() {
Expand Down Expand Up @@ -180,4 +225,25 @@ mod tests {

assert_eq!(component.schema_commitment, Word::empty());
}

#[test]
fn build_with_schema_commitment_adds_schema_commitment_component() {
let auth_component = AuthEcdsaK256Keccak::new(PublicKeyCommitment::from(Word::empty()));

let account = AccountBuilder::new([1u8; 32])
.with_auth_component(auth_component)
.build_with_schema_commitment()
.unwrap();

// The auth component has 1 slot (public key) and the schema commitment adds 1 more.
assert_eq!(account.storage().num_slots(), 2);

// The auth component's public key slot should be accessible.
assert!(account.storage().get_item(AuthEcdsaK256Keccak::public_key_slot()).is_ok());

// The schema commitment slot should be non-empty since we have a component with a schema.
let slot_name = AccountSchemaCommitment::schema_commitment_slot();
let commitment = account.storage().get_item(slot_name).unwrap();
assert_ne!(commitment, Word::empty());
}
}
2 changes: 2 additions & 0 deletions crates/miden-standards/src/account/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ pub mod interface;
pub mod metadata;
pub mod wallets;

pub use metadata::AccountBuilderSchemaCommitmentExt;

/// Macro to simplify the creation of static procedure digest constants.
///
/// This macro generates a `LazyLock<Word>` static variable that lazily initializes
Expand Down