Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
88f5e6c
feat: Check for duplicate slots after sorting
PhilippGackstatter Dec 10, 2025
ee3ce49
feat: Check for sorting and uniqueness in tx kernel
PhilippGackstatter Dec 10, 2025
7e10dbf
chore: add changelog
PhilippGackstatter Dec 10, 2025
9e1e00a
chore: cover special cases in slot ID comparison in test
PhilippGackstatter Dec 11, 2025
aae2b29
chore: remove implemented todo
PhilippGackstatter Dec 11, 2025
74bba33
feat: Add test for `ERR_ACCOUNT_UNKNOWN_STORAGE_SLOT_NAME`
PhilippGackstatter Dec 11, 2025
2f427e2
chore: rename faucet storage data slot to faucet metadata slot
PhilippGackstatter Dec 11, 2025
1f327f7
chore: drop "name" from slot name getter
PhilippGackstatter Dec 11, 2025
73789e5
chore: unify basic/network faucet slot names
PhilippGackstatter Dec 11, 2025
7f40ad5
chore: add changelog
PhilippGackstatter Dec 11, 2025
cfd4454
chore: change get_kernel_mem_element to use read_element
PhilippGackstatter Dec 11, 2025
2b70e3a
chore: Add test for accessing reserved slot
PhilippGackstatter Dec 11, 2025
819d2bd
feat: Remove storage offset and size from VM memory
PhilippGackstatter Dec 11, 2025
64be499
feat: Remove storage offset and size from `AccountProcedureInfo`
PhilippGackstatter Dec 11, 2025
d0b5adf
chore: Rename `AccountProcedureInfo` to `AccountProcedureRoot`
PhilippGackstatter Dec 11, 2025
2f9125f
chore: Update account code docs
PhilippGackstatter Dec 11, 2025
b9ac434
chore: update account docs and remove unneeded errors
PhilippGackstatter Dec 11, 2025
0a901ec
chore: add changelog
PhilippGackstatter Dec 11, 2025
9049e05
fix: outdated procedure info doc
PhilippGackstatter Dec 11, 2025
527aa96
feat: Allow components to have duplicate MAST roots
PhilippGackstatter Dec 11, 2025
922a7bc
chore: add changelog
PhilippGackstatter Dec 11, 2025
71e8570
Merge branch 'next' into pgackst-allow-duplicate-procedure-roots
bobbinth Dec 13, 2025
140efa4
chore: resolve post-merge errors
bobbinth Dec 13, 2025
9686a13
chore: fix changelog
bobbinth Dec 13, 2025
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
15 changes: 8 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,24 @@

### 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), [#2170](https://github.com/0xMiden/miden-base/pull/2170)).
- [BREAKING] Refactored 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)).
- [BREAKING] Allowed account components to share identical account code procedures ([#2164](https://github.com/0xMiden/miden-base/pull/2164)).

### Changes

- Add proc-macro `WordWrapper` to ease implementation of `Word`-wrapping types ([#2071](https://github.com/0xMiden/miden-base/pull/2108)).
- [BREAKING] Increased `MAX_INPUTS_PER_NOTE` from 128 to 1024 ([#2139](https://github.com/0xMiden/miden-base/pull/2139)).
- Added proc-macro `WordWrapper` to ease implementation of `Word`-wrapping types ([#2071](https://github.com/0xMiden/miden-base/pull/2108)).
- [BREAKING] Added `BlockBody` and `BlockProof` structs in preparation for validator signatures and deferred block proving ([#2012](https://github.com/0xMiden/miden-base/pull/2012)).
- [BREAKING] Renamed `TransactionEvent` into `TransactionEventId` and split event handling into data extraction and handling logic ([#2071](https://github.com/0xMiden/miden-base/pull/2071)).
- Split tx progress events out into a separate enum ([#2103](https://github.com/0xMiden/miden-base/pull/2103)).
- Added `note::get_network_account_tag` procedure ([#2120](https://github.com/0xMiden/miden-base/pull/2120)).
- [BREAKING] Updated MINT note to support both private and public output note creation ([#2123](https://github.com/0xMiden/miden-base/pull/2123)).
- [BREAKING] Removed `AccountComponentTemplate` in favor of instantiating components via `AccountComponent::from_package` ([#2127](https://github.com/0xMiden/miden-base/pull/2127)).
- [BREAKING] Add public key to, remove proof commitment from, `BlockHeader`, and add signing functionality through `BlockSigner` trait ([#2128](https://github.com/0xMiden/miden-base/pull/2128)).
- [BREAKING] Added public key to, remove proof commitment from, `BlockHeader`, and add signing functionality through `BlockSigner` trait ([#2128](https://github.com/0xMiden/miden-base/pull/2128)).
- [BREAKING] Added fee to `TransactionHeader` ([#2131](https://github.com/0xMiden/miden-base/pull/2131)).
Added the ability to get full public key from `TransactionAuthenticator` ([#2145](https://github.com/0xMiden/miden-base/pull/2145)).
- [BREAKING] Make `AccountProcedureIndexMap` construction infallible ([#2163](https://github.com/0xMiden/miden-base/pull/2163)).
- [BREAKING] Rename `AccountProcedureInfo` into `AccountProcedureRoot` and remove storage offset and size ([#2162](https://github.com/0xMiden/miden-base/pull/2162)).
- [BREAKING] Increased `MAX_INPUTS_PER_NOTE` from 128 to 1024 ([#2139](https://github.com/0xMiden/miden-base/pull/2139)).
- Added the ability to get full public key from `TransactionAuthenticator` ([#2145](https://github.com/0xMiden/miden-base/pull/2145)).
- [BREAKING] Renamed `AccountProcedureInfo` into `AccountProcedureRoot` and remove storage offset and size ([#2162](https://github.com/0xMiden/miden-base/pull/2162)).
- [BREAKING] Made `AccountProcedureIndexMap` construction infallible ([#2163](https://github.com/0xMiden/miden-base/pull/2163)).

## 0.12.4 (2025-11-26)

Expand Down
2 changes: 1 addition & 1 deletion crates/miden-lib/src/account/faucets/network_fungible.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ static OWNER_CONFIG_SLOT_NAME: LazyLock<StorageSlotName> = LazyLock::new(|| {
/// ## Storage Layout
///
/// - [`Self::metadata_slot`]: Fungible faucet metadata.
/// - [`Self::owner_config_slot`]: The owner account of this network facuet.
/// - [`Self::owner_config_slot`]: The owner account of this network faucet.
///
/// [builder]: crate::utils::CodeBuilder
pub struct NetworkFungibleFaucet {
Expand Down
2 changes: 0 additions & 2 deletions crates/miden-lib/src/errors/tx_kernel_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,6 @@ pub const ERR_ACCOUNT_STORAGE_COMMITMENT_MISMATCH: MasmError = MasmError::from_s
pub const ERR_ACCOUNT_STORAGE_MAP_ENTRIES_DO_NOT_MATCH_MAP_ROOT: MasmError = MasmError::from_static_str("storage map entries provided as advice inputs do not have the same storage map root as the root of the map the new account commits to");
/// Error Message: "slot IDs must be unique and sorted in ascending order"
pub const ERR_ACCOUNT_STORAGE_SLOTS_MUST_BE_SORTED_AND_UNIQUE: MasmError = MasmError::from_static_str("slot IDs must be unique and sorted in ascending order");
/// Error Message: "provided storage slot index is out of bounds"
pub const ERR_ACCOUNT_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS: MasmError = MasmError::from_static_str("provided storage slot index is out of bounds");
/// Error Message: "number of account procedures exceeds the maximum limit of 256"
pub const ERR_ACCOUNT_TOO_MANY_PROCEDURES: MasmError = MasmError::from_static_str("number of account procedures exceeds the maximum limit of 256");
/// Error Message: "number of account storage slots exceeds the maximum limit of 255"
Expand Down
2 changes: 2 additions & 0 deletions crates/miden-lib/src/transaction/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ pub type StorageSlot = u8;
//
// Here the "end pointer" is the last memory pointer occupied by the current data
//
// TODO: Rearrange the memory sections that follow account procedures to be contiguous to it.
//
// | Section | Start address, pointer (word pointer) | End address, pointer (word pointer) | Comment |
// | ------------------ | :-----------------------------------: | :---------------------------------: | ----------------------------------- |
// | ID and nonce | 0 (0) | 3 (0) | |
Expand Down
24 changes: 8 additions & 16 deletions crates/miden-objects/src/account/code/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ impl AccountProcedureBuilder {
let mut auth_proc_count = 0;

for (proc_root, is_auth) in component.get_procedures() {
self.add_procedure(proc_root)?;
self.add_procedure(proc_root);

if is_auth {
let auth_proc_idx = self.procedures.len() - 1;
Expand All @@ -358,27 +358,19 @@ impl AccountProcedureBuilder {
if is_auth {
return Err(AccountError::AccountCodeMultipleAuthComponents);
}
self.add_procedure(proc_mast_root)?;
self.add_procedure(proc_mast_root);
}

Ok(())
}

fn add_procedure(&mut self, proc_mast_root: Word) -> Result<(), AccountError> {
// TODO: Check if we can now safely allow this.
// Disallow procedures with the same MAST root from different components.
let proc_mast_root = AccountProcedureRoot::from_raw(proc_mast_root);
// The number of procedures in accounts is generally small, so checking via linear search
// should be fine.
if self.procedures.contains(&proc_mast_root) {
return Err(AccountError::AccountComponentDuplicateProcedureRoot(
proc_mast_root.as_word(),
));
fn add_procedure(&mut self, proc_mast_root: Word) {
// Allow procedures with the same MAST root from different components, but only add them
// once.
let proc_root = AccountProcedureRoot::from_raw(proc_mast_root);
if !self.procedures.contains(&proc_root) {
self.procedures.push(proc_root);
}

self.procedures.push(proc_mast_root);

Ok(())
}

fn build(self) -> Result<Vec<AccountProcedureRoot>, AccountError> {
Expand Down
22 changes: 0 additions & 22 deletions crates/miden-objects/src/account/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -895,28 +895,6 @@ mod tests {
))
}

/// Two components who export a procedure with the same MAST root should fail to convert into
/// code and storage.
#[test]
fn test_account_duplicate_exported_mast_root() {
let code1 = "export.foo add eq.1 end";
let code2 = "export.bar add eq.1 end";

let library1 = Assembler::default().assemble_library([code1]).unwrap();
let library2 = Assembler::default().assemble_library([code2]).unwrap();

let component1 = AccountComponent::new(library1, vec![]).unwrap().with_supports_all_types();
let component2 = AccountComponent::new(library2, vec![]).unwrap().with_supports_all_types();

let err = Account::initialize_from_components(
AccountType::RegularAccountUpdatableCode,
vec![NoopAuthComponent.into(), component1, component2],
)
.unwrap_err();

assert_matches!(err, AccountError::AccountComponentDuplicateProcedureRoot(_))
}

/// Tests all cases of account ID seed validation.
#[test]
fn seed_validation() -> anyhow::Result<()> {
Expand Down
2 changes: 0 additions & 2 deletions crates/miden-objects/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,6 @@ pub enum AccountError {
AccountComponentAssemblyError(Report),
#[error("failed to merge components into one account code mast forest")]
AccountComponentMastForestMergeError(#[source] MastForestError),
#[error("procedure with MAST root {0} is present in multiple account components")]
AccountComponentDuplicateProcedureRoot(Word),
// #[error("failed to create account component")]
// AccountComponentTemplateInstantiationError(#[source] AccountComponentTemplateError),
#[error("account component contains multiple authentication procedures")]
Expand Down
129 changes: 128 additions & 1 deletion crates/miden-testing/src/kernel_tests/tx/test_account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ use miden_objects::account::{
StorageSlotName,
StorageSlotType,
};
use miden_objects::assembly::DefaultSourceManager;
use miden_objects::assembly::diagnostics::{IntoDiagnostic, NamedSource, Report, WrapErr, miette};
use miden_objects::assembly::{DefaultSourceManager, Library};
use miden_objects::asset::{Asset, FungibleAsset};
use miden_objects::note::NoteType;
use miden_objects::testing::account_id::{
Expand All @@ -48,6 +48,7 @@ use miden_objects::testing::account_id::{
};
use miden_objects::testing::storage::{MOCK_MAP_SLOT, MOCK_VALUE_SLOT0, MOCK_VALUE_SLOT1};
use miden_objects::transaction::OutputNote;
use miden_objects::utils::sync::LazyLock;
use miden_objects::{LexicographicWord, StarkField};
use miden_processor::{ExecutionError, Word};
use miden_tx::LocalTransactionProver;
Expand Down Expand Up @@ -1749,3 +1750,129 @@ async fn incrementing_nonce_overflow_fails() -> anyhow::Result<()> {

Ok(())
}

/// Tests that merging two components that have a procedure with the same mast root
/// (`get_slot_content`) works.
///
/// Asserts that the procedure is callable via both names.
#[tokio::test]
async fn merging_components_with_same_mast_root_succeeds() -> anyhow::Result<()> {
static TEST_SLOT_NAME: LazyLock<StorageSlotName> = LazyLock::new(|| {
StorageSlotName::new("miden::slot::test").expect("storage slot name should be valid")
});

static COMPONENT_1_LIBRARY: LazyLock<Library> = LazyLock::new(|| {
let code = format!(
r#"
use.miden::active_account

const TEST_SLOT_NAME = word("{test_slot_name}")

pub proc get_slot_content
push.TEST_SLOT_NAME[0..2]
exec.active_account::get_item
swapw dropw
end
"#,
test_slot_name = &*TEST_SLOT_NAME
);

let source = NamedSource::new("component1::interface", code);
TransactionKernel::assembler()
.with_debug_mode(true)
.assemble_library([source])
.expect("mock account code should be valid")
});

static COMPONENT_2_LIBRARY: LazyLock<Library> = LazyLock::new(|| {
let code = format!(
r#"
use.miden::active_account
use.miden::native_account

const TEST_SLOT_NAME = word("{test_slot_name}")

pub proc get_slot_content
push.TEST_SLOT_NAME[0..2]
exec.active_account::get_item
swapw dropw
end

pub proc set_slot_content
push.5.6.7.8
push.TEST_SLOT_NAME[0..2]
exec.native_account::set_item
swapw dropw
end
"#,
test_slot_name = &*TEST_SLOT_NAME
);

let source = NamedSource::new("component2::interface", code);
TransactionKernel::assembler()
.with_debug_mode(true)
.assemble_library([source])
.expect("mock account code should be valid")
});

struct CustomComponent1 {
slot: StorageSlot,
}

impl From<CustomComponent1> for AccountComponent {
fn from(component: CustomComponent1) -> AccountComponent {
AccountComponent::new(COMPONENT_1_LIBRARY.clone(), vec![component.slot])
.expect("should be valid")
.with_supports_all_types()
}
}

struct CustomComponent2;

impl From<CustomComponent2> for AccountComponent {
fn from(_component: CustomComponent2) -> AccountComponent {
AccountComponent::new(COMPONENT_2_LIBRARY.clone(), vec![])
.expect("should be valid")
.with_supports_all_types()
}
}

let slot = StorageSlot::with_value(TEST_SLOT_NAME.clone(), Word::from([1, 2, 3, 4u32]));

let account = AccountBuilder::new([42; 32])
.with_auth_component(Auth::IncrNonce)
.with_component(CustomComponent1 { slot: slot.clone() })
.with_component(CustomComponent2)
.build()
.context("failed to build account")?;

let tx_script = r#"
use.component1::interface->comp1_interface
use.component2::interface->comp2_interface

begin
call.comp1_interface::get_slot_content
push.1.2.3.4
assert_eqw.err="failed to get slot content1"

call.comp2_interface::set_slot_content

call.comp2_interface::get_slot_content
push.5.6.7.8
assert_eqw.err="failed to get slot content2"
end
"#;

let tx_script = CodeBuilder::default()
.with_dynamically_linked_library(COMPONENT_1_LIBRARY.clone())?
.with_dynamically_linked_library(COMPONENT_2_LIBRARY.clone())?
.compile_tx_script(tx_script)?;

TransactionContextBuilder::new(account)
.tx_script(tx_script)
.build()?
.execute()
.await?;

Ok(())
}