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
8 changes: 7 additions & 1 deletion sdk/base-macros/src/component_macro/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,13 @@ fn expand_component_struct(
let default_impl = match &mut input_struct.fields {
syn::Fields::Named(fields) => {
let storage_namespace = metadata.component_package.as_deref().unwrap_or(&metadata.name);
let field_inits = process_storage_fields(fields, &mut acc_builder, storage_namespace)?;
let component_struct_name = struct_name.to_string();
let field_inits = process_storage_fields(
fields,
&mut acc_builder,
storage_namespace,
&component_struct_name,
)?;
generate_default_impl(struct_name, &field_inits)
}
syn::Fields::Unit => quote! {
Expand Down
53 changes: 48 additions & 5 deletions sdk/base-macros/src/component_macro/storage.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::collections::HashMap;

use heck::ToSnakeCase;
use quote::quote;
use syn::{Field, Type, spanned::Spanned};

Expand Down Expand Up @@ -32,6 +33,24 @@ fn sanitize_slot_name_component(component: &str) -> String {
out
}

/// Derives the full storage slot name for a component field.
///
/// Slot names are part of the on-chain storage ABI, so this intentionally ignores any optional
/// version suffix in `storage_namespace` and keeps the format stable as
/// `component_package_or_name::component_struct::field_name`.
fn derive_storage_slot_name(
storage_namespace: &str,
component_struct_name: &str,
field_name: &str,
) -> String {
let storage_namespace = storage_namespace.split('@').next().unwrap_or(storage_namespace);
let namespace = sanitize_slot_name_component(storage_namespace);
let struct_component = sanitize_slot_name_component(&component_struct_name.to_snake_case());
let field_component = sanitize_slot_name_component(field_name);

format!("{namespace}::{struct_component}::{field_component}")
}

/// Parsed arguments collected from a `#[storage(...)]` attribute.
struct StorageAttributeArgs {
description: Option<String>,
Expand Down Expand Up @@ -99,6 +118,7 @@ pub fn process_storage_fields(
fields: &mut syn::FieldsNamed,
builder: &mut AccountComponentMetadataBuilder,
storage_namespace: &str,
component_struct_name: &str,
) -> Result<Vec<proc_macro2::TokenStream>, syn::Error> {
let mut field_infos = Vec::new();
let mut errors = Vec::new();
Expand Down Expand Up @@ -134,11 +154,9 @@ pub fn process_storage_fields(
}

if let Some(args) = storage_args {
// Slot names are part of the on-chain storage ABI: `StorageSlotId` values are derived
// from the slot name. Keep this format stable.
let namespace = sanitize_slot_name_component(storage_namespace);
let field_component = sanitize_slot_name_component(&field_name_str);
let slot_name_str = format!("miden::component::{namespace}::{field_component}");
// `StorageSlotId` values are derived from slot names, so keep this format stable.
let slot_name_str =
derive_storage_slot_name(storage_namespace, component_struct_name, &field_name_str);
if let Some(existing_field) = slot_names.get(&slot_name_str) {
errors.push(syn::Error::new(
field.span(),
Expand Down Expand Up @@ -247,3 +265,28 @@ pub(crate) fn typecheck_storage_field(field: &Field) -> Result<StorageFieldType,
)),
}
}

#[cfg(test)]
mod tests {
use super::derive_storage_slot_name;

#[test]
fn derives_slot_name_from_component_package_struct_and_field() {
assert_eq!(
derive_storage_slot_name("miden:counter-contract", "CounterContract", "count_map"),
"miden_counter_contract::counter_contract::count_map"
);
}

#[test]
fn ignores_component_package_version_when_deriving_slot_name() {
assert_eq!(
derive_storage_slot_name(
"miden:[email protected]",
"CounterContract",
"count_map"
),
"miden_counter_contract::counter_contract::count_map"
);
}
}
7 changes: 3 additions & 4 deletions tests/integration-network/src/mockchain/counter_contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use miden_client::{
use miden_core::Felt;
use miden_protocol::account::{
AccountBuilder, AccountStorageMode, AccountType, StorageMap, StorageMapKey, StorageSlot,
StorageSlotName, auth::AuthScheme,
auth::AuthScheme,
};
use miden_testing::{AccountState, Auth, MockChain};
use midenc_expect_test::expect;
Expand All @@ -16,7 +16,7 @@ use super::{
cycle_helpers::note_cycles,
helpers::{
NoteCreationConfig, account_component_from_package, assert_counter_storage,
compile_rust_package, create_note_from_package, execute_tx,
compile_rust_package, counter_storage_slot_name, create_note_from_package, execute_tx,
},
};
use crate::mockchain::helpers::COUNTER_CONTRACT_STORAGE_KEY;
Expand All @@ -29,8 +29,7 @@ pub fn test_counter_contract() {
let note_package = compile_rust_package("../../examples/counter-note", true);

let value = Word::from([Felt::ZERO, Felt::ZERO, Felt::ZERO, Felt::ONE]);
let counter_storage_slot =
StorageSlotName::new("miden::component::miden_counter_contract::count_map").unwrap();
let counter_storage_slot = counter_storage_slot_name();
let storage_slots = vec![StorageSlot::with_map(
counter_storage_slot.clone(),
StorageMap::with_entries([(StorageMapKey::new(COUNTER_CONTRACT_STORAGE_KEY), value)])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use miden_client::{
use miden_core::Felt;
use miden_protocol::account::{
AccountBuilder, AccountStorageMode, AccountType, StorageMap, StorageMapKey, StorageSlot,
StorageSlotName, auth::AuthScheme,
auth::AuthScheme,
};
use miden_testing::{AccountState, Auth, MockChain};
use midenc_expect_test::expect;
Expand All @@ -17,7 +17,7 @@ use super::{
helpers::{
NoteCreationConfig, assert_counter_storage,
build_existing_counter_account_builder_with_auth_package, compile_rust_package,
create_note_from_package, execute_tx,
counter_storage_slot_name, create_note_from_package, execute_tx,
},
};
use crate::mockchain::helpers::COUNTER_CONTRACT_STORAGE_KEY;
Expand All @@ -38,8 +38,7 @@ pub fn test_counter_contract_no_auth() {
compile_rust_package("../../examples/auth-component-no-auth", true);

let value = Word::from([Felt::ZERO, Felt::ZERO, Felt::ZERO, Felt::ONE]);
let counter_storage_slot =
StorageSlotName::new("miden::component::miden_counter_contract::count_map").unwrap();
let counter_storage_slot = counter_storage_slot_name();
let counter_storage_slots = vec![StorageSlot::with_map(
counter_storage_slot.clone(),
StorageMap::with_entries([(StorageMapKey::new(COUNTER_CONTRACT_STORAGE_KEY), value)])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
use miden_client::{
auth::BasicAuthenticator, crypto::RandomCoin, note::NoteTag, transaction::RawOutputNote,
};
use miden_protocol::account::StorageSlotName;
use miden_testing::MockChain;
use midenc_expect_test::expect;

Expand All @@ -16,7 +15,7 @@ use super::{
helpers::{
NoteCreationConfig, assert_counter_storage, block_on,
build_counter_account_with_rust_rpo_auth, build_send_notes_script, compile_rust_package,
create_note_from_package,
counter_storage_slot_name, create_note_from_package,
},
};

Expand Down Expand Up @@ -48,8 +47,7 @@ pub fn test_counter_contract_rust_auth_blocks_unauthorized_note_creation() {
counter_account.id().to_hex()
);

let counter_storage_slot =
StorageSlotName::new("miden::component::miden_counter_contract::count_map").unwrap();
let counter_storage_slot = counter_storage_slot_name();
assert_counter_storage(
chain.committed_account(counter_account.id()).unwrap().storage(),
&counter_storage_slot,
Expand Down
7 changes: 4 additions & 3 deletions tests/integration-network/src/mockchain/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,13 +274,14 @@ pub(super) fn build_asset_transfer_tx(
// COUNTER CONTRACT HELPERS
// ================================================================================================

fn counter_storage_slot_name() -> StorageSlotName {
StorageSlotName::new("miden::component::miden_counter_contract::count_map")
/// Returns the storage slot name used by the counter contract's storage map.
pub(super) fn counter_storage_slot_name() -> StorageSlotName {
StorageSlotName::new("miden_counter_contract::counter_contract::count_map")
.expect("counter storage slot name should be valid")
}

fn auth_public_key_slot_name() -> StorageSlotName {
StorageSlotName::new("miden::component::miden_auth_component_rpo_falcon512::owner_public_key")
StorageSlotName::new("miden_auth_component_rpo_falcon512::auth_component::owner_public_key")
.expect("auth component storage slot name should be valid")
}

Expand Down
14 changes: 7 additions & 7 deletions tests/integration/src/rust_masm_tests/examples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,17 @@ fn storage_example() {
supported-types = ["RegularAccountUpdatableCode"]

[[storage.slots]]
name = "miden::component::miden_storage_example::asset_qty_map"
name = "miden_storage_example::my_account::owner_public_key"
description = "owner public key"
type = "word"

[[storage.slots]]
name = "miden_storage_example::my_account::asset_qty_map"
description = "asset quantity map"

[storage.slots.type]
key = "word"
value = "felt"

[[storage.slots]]
name = "miden::component::miden_storage_example::owner_public_key"
description = "owner public key"
type = "word"
"#]]
.assert_eq(&toml);
}
Expand Down Expand Up @@ -237,7 +237,7 @@ fn counter_contract() {
supported-types = ["RegularAccountUpdatableCode"]

[[storage.slots]]
name = "miden::component::miden_counter_contract::count_map"
name = "miden_counter_contract::counter_contract::count_map"
description = "counter contract storage map"

[storage.slots.type]
Expand Down