Skip to content

Commit

Permalink
feat: add native types to storage placeholders
Browse files Browse the repository at this point in the history
  • Loading branch information
igamigo committed Feb 10, 2025
1 parent 698f469 commit 8449edd
Show file tree
Hide file tree
Showing 10 changed files with 1,369 additions and 1,147 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
- [BREAKING] Refactored config file for `miden-proving-service` to be based on environment variables (#1120).
- Added block number as a public input to the transaction kernel. Updated prologue logic to validate the global input block number is consistent with the commitment block number (#1126).
- Made NoteFile and AccountFile more consistent (#1133).
- [BREAKING] Added native types to `AccountComponentTemplate` (#1124).

## 0.7.2 (2025-01-28) - `miden-objects` crate only

Expand Down
6 changes: 3 additions & 3 deletions crates/miden-objects/src/account/component/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ use vm_processor::MastForest;

mod template;
pub use template::{
AccountComponentMetadata, AccountComponentTemplate, FeltRepresentation, InitStorageData,
MapRepresentation, PlaceholderType, StorageEntry, StoragePlaceholder, StorageValue,
WordRepresentation,
AccountComponentMetadata, AccountComponentTemplate, FeltRepresentation, FeltType,
InitStorageData, MapEntry, StorageEntry, StorageValueError, StorageValueName,
StorageValueNameError, WordRepresentation, WordType,
};

use crate::{
Expand Down
245 changes: 109 additions & 136 deletions crates/miden-objects/src/account/component/template/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use alloc::{
collections::{btree_map::Entry, BTreeMap, BTreeSet},
collections::{BTreeMap, BTreeSet},
string::{String, ToString},
vec::Vec,
};
Expand Down Expand Up @@ -109,28 +109,29 @@ impl Deserializable for AccountComponentTemplate {
/// # use semver::Version;
/// # use std::collections::BTreeSet;
/// # use miden_objects::{testing::account_code::CODE, account::{
/// # AccountComponent, AccountComponentMetadata, InitStorageData, StorageEntry,
/// # StoragePlaceholder, StorageValue,
/// # AccountComponent, AccountComponentMetadata, StorageEntry,
/// # StorageValueName,
/// # AccountComponentTemplate, FeltRepresentation, WordRepresentation},
/// # assembly::Assembler, Felt};
/// # use miden_objects::account::FeltType;
/// # use miden_objects::account::InitStorageData;
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let first_felt = FeltRepresentation::Decimal(Felt::new(0u64));
/// let second_felt = FeltRepresentation::Decimal(Felt::new(1u64));
/// let third_felt = FeltRepresentation::Decimal(Felt::new(2u64));
/// let first_felt = FeltRepresentation::from(Felt::new(0u64));
/// let second_felt = FeltRepresentation::from(Felt::new(1u64));
/// let third_felt = FeltRepresentation::from(Felt::new(2u64));
/// // Templated element:
/// let last_element = FeltRepresentation::Template(StoragePlaceholder::new("foo")?);
/// let last_element =
/// FeltRepresentation::new_template(FeltType::Felt, StorageValueName::new("foo")?, None);
///
/// let storage_entry = StorageEntry::new_value(
/// "test-entry",
/// Some("a test entry"),
/// 0,
/// WordRepresentation::Array([first_felt, second_felt, third_felt, last_element]),
/// let word_representation = WordRepresentation::new_value(
/// [first_felt, second_felt, third_felt, last_element],
/// Some(StorageValueName::new("test_value")?),
/// Some("This is a test value".into()),
/// );
/// let storage_entry = StorageEntry::new_value(0, word_representation);
///
/// let init_storage_data = InitStorageData::new([(
/// StoragePlaceholder::new("foo")?,
/// StorageValue::Felt(Felt::new(300u64)),
/// )]);
/// let init_storage_data =
/// InitStorageData::new([(StorageValueName::new("test_value.foo")?, "300".to_string())]);
///
/// let component_template = AccountComponentMetadata::new(
/// "test name".into(),
Expand Down Expand Up @@ -197,22 +198,21 @@ impl AccountComponentMetadata {
/// Retrieves a map of unique storage placeholders mapped to their expected type that require
/// a value at the moment of component instantiation.
///
/// These values will be used for
/// initializing storage slot values, or storage map entries. For a full example on how a
/// placeholder may be utilized, please refer to the docs for [AccountComponentMetadata].
/// These values will be used for initializing storage slot values, or storage map entries.
/// For a full example on how a placeholder may be utilized, please refer to the docs for
/// [AccountComponentMetadata].
///
/// Types for the returned storage placeholders are inferred based on their location in the
/// storage layout structure.
pub fn get_unique_storage_placeholders(&self) -> BTreeMap<StoragePlaceholder, PlaceholderType> {
let mut placeholder_map = BTreeMap::new();
for storage_entry in &self.storage {
for (placeholder, placeholder_type) in storage_entry.all_placeholders_iter() {
// The constructors of this type guarantee each placeholder has the same type, so
// reinserting them multiple times is fine.
placeholder_map.insert(placeholder.clone(), placeholder_type);
pub fn get_template_requirements(&self) -> BTreeMap<String, PlaceholderTypeRequirement> {
let mut templates = BTreeMap::new();
for entry in self.storage_entries() {
for (name, requirement) in entry.template_requirements() {
templates.insert(name.to_string(), requirement);
}
}
placeholder_map

templates
}

/// Returns the name of the account component.
Expand Down Expand Up @@ -275,30 +275,6 @@ impl AccountComponentMetadata {
}
}

// Check that placeholders do not appear more than once with a different type
let mut placeholders = BTreeMap::new();
for storage_entry in &self.storage {
for (placeholder, placeholder_type) in storage_entry.all_placeholders_iter() {
match placeholders.entry(placeholder.clone()) {
Entry::Occupied(entry) => {
// if already exists, make sure it's the same type
if *entry.get() != placeholder_type {
return Err(
AccountComponentTemplateError::StoragePlaceholderTypeMismatch(
placeholder.clone(),
*entry.get(),
placeholder_type,
),
);
}
},
Entry::Vacant(slot) => {
slot.insert(placeholder_type);
},
}
}
}

for entry in self.storage_entries() {
entry.validate()?;
}
Expand Down Expand Up @@ -336,67 +312,75 @@ impl Deserializable for AccountComponentMetadata {

// TESTS
// ================================================================================================

#[cfg(test)]
mod tests {
use std::{
collections::BTreeSet,
string::{String, ToString},
};

use assembly::Assembler;
use assert_matches::assert_matches;
use storage::WordRepresentation;
use vm_core::{Felt, FieldElement};

use super::*;
use crate::{account::AccountComponent, testing::account_code::CODE, AccountError};
use semver::Version;
use vm_core::{
utils::{Deserializable, Serializable},
Felt, FieldElement,
};

use super::FeltRepresentation;
// Import the new types and helpers.
use crate::{
account::{
component::template::{
storage::StorageEntry, AccountComponentMetadata, AccountComponentTemplate,
InitStorageData,
},
AccountComponent, StorageValueName,
},
errors::AccountComponentTemplateError,
testing::account_code::CODE,
AccountError,
};

fn default_felt_array() -> [FeltRepresentation; 4] {
[
FeltRepresentation::from(Felt::ZERO),
FeltRepresentation::from(Felt::ZERO),
FeltRepresentation::from(Felt::ZERO),
FeltRepresentation::from(Felt::ZERO),
]
}

#[test]
fn test_contiguous_value_slots() {
let storage = vec![
StorageEntry::Value {
name: "slot0".into(),
description: None,
slot: 0,
value: WordRepresentation::Value(Default::default()),
},
StorageEntry::MultiSlot {
name: "slot1".into(),
description: None,
slots: vec![1, 2],
values: vec![
WordRepresentation::Array(Default::default()),
WordRepresentation::Value(Default::default()),
],
},
StorageEntry::new_value(0, default_felt_array()),
StorageEntry::new_multislot(
StorageValueName::new("slot1").unwrap(),
Some("multi-slot value of arity 2".into()),
vec![1, 2],
vec![default_felt_array(), default_felt_array()],
),
];

let original_config = AccountComponentMetadata::new(
"test".into(),
"desc".into(),
Version::parse("0.1.0").unwrap(),
BTreeSet::new(),
let original_config = AccountComponentMetadata {
name: "test".into(),
description: "desc".into(),
version: Version::parse("0.1.0").unwrap(),
targets: BTreeSet::new(),
storage,
)
.unwrap();
};

let serialized = original_config.as_toml().unwrap();

let deserialized = AccountComponentMetadata::from_toml(&serialized).unwrap();
assert_eq!(deserialized, original_config)
assert_eq!(deserialized, original_config);
}

#[test]
fn test_new_non_contiguous_value_slots() {
let storage = vec![
StorageEntry::Value {
name: "slot0".into(),
description: None,
slot: 0,
value: Default::default(),
},
StorageEntry::Value {
name: "slot2".into(),
description: None,
slot: 2,
value: Default::default(),
},
StorageEntry::new_value(0, default_felt_array()),
StorageEntry::new_value(2, default_felt_array()),
];

let result = AccountComponentMetadata::new(
Expand All @@ -412,40 +396,31 @@ mod tests {
#[test]
fn test_binary_serde_roundtrip() {
let storage = vec![
StorageEntry::MultiSlot {
name: "slot1".into(),
description: None,
slots: vec![1, 2],
values: vec![
WordRepresentation::Array(Default::default()),
WordRepresentation::Value(Default::default()),
],
},
StorageEntry::Value {
name: "slot0".into(),
description: None,
slot: 0,
value: WordRepresentation::Value(Default::default()),
},
StorageEntry::new_multislot(
StorageValueName::new("slot1").unwrap(),
Option::<String>::None,
vec![1, 2],
vec![default_felt_array(), default_felt_array()],
),
StorageEntry::new_value(0, default_felt_array()),
];

let component_template = AccountComponentMetadata::new(
"test".into(),
"desc".into(),
Version::parse("0.1.0").unwrap(),
BTreeSet::new(),
let component_metadata = AccountComponentMetadata {
name: "test".into(),
description: "desc".into(),
version: Version::parse("0.1.0").unwrap(),
targets: BTreeSet::new(),
storage,
)
.unwrap();
};

let library = Assembler::default().assemble_library([CODE]).unwrap();
let template = AccountComponentTemplate::new(component_template, library);
_ = AccountComponent::from_template(&template, &InitStorageData::default()).unwrap();
let template = AccountComponentTemplate::new(component_metadata, library);
let _ = AccountComponent::from_template(&template, &InitStorageData::default()).unwrap();

let serialized = template.to_bytes();
let deserialized = AccountComponentTemplate::read_from_bytes(&serialized).unwrap();

assert_eq!(deserialized, template)
assert_eq!(deserialized, template);
}

#[test]
Expand All @@ -461,8 +436,8 @@ mod tests {
description = "A storage map entry"
slot = 0
values = [
{ key = "0x1", value = ["{{value.test}}", "0x1", "0x2", "0x3"] },
{ key = "0x1", value = ["0x1", "0x2", "0x3", "{{value.test}}"] },
{ key = "0x1", value = ["0x3", "0x1", "0x2", "0x3"] },
{ key = "0x1", value = ["0x1", "0x2", "0x3", "0x10"] }
]
"#;

Expand All @@ -483,22 +458,21 @@ mod tests {
description = "A storage map entry"
slot = 0
values = [
{ key = ["0","0","0","1"], value = ["{{value.test}}", "0x1", "0x2", "0x3"] },
{ key = "{{word.test}}", value = ["0x1", "0x2", "0x3", "{{value.test}}"] },
{ key = ["0", "0", "0", "1"], value = ["0x9", "0x12", "0x31", "0x18"] },
{ key = {name="duplicate_key" }, value = ["0x1", "0x2", "0x3", "0x4"] }
]
"#;

let metadata = AccountComponentMetadata::from_toml(toml_text).unwrap();
let library = Assembler::default().assemble_library([CODE]).unwrap();
let template = AccountComponentTemplate::new(metadata, library);

let init_storage_data = InitStorageData::new([
(
StoragePlaceholder::new("word.test").unwrap(),
StorageValue::Word([Felt::ZERO, Felt::ZERO, Felt::ZERO, Felt::ONE]),
),
(StoragePlaceholder::new("value.test").unwrap(), StorageValue::Felt(Felt::ONE)),
]);
// Fail to instantiate on a duplicate key

let init_storage_data = InitStorageData::new([(
StorageValueName::new("map.duplicate_key").unwrap(),
"0x0000000000000000000000000000000000000000000000000100000000000000".to_string(),
)]);
let account_component = AccountComponent::from_template(&template, &init_storage_data);
assert_matches!(
account_component,
Expand All @@ -507,13 +481,12 @@ mod tests {
))
);

let valid_init_storage_data = InitStorageData::new([
(
StoragePlaceholder::new("word.test").unwrap(),
StorageValue::Word([Felt::new(30), Felt::new(20), Felt::new(10), Felt::ZERO]),
),
(StoragePlaceholder::new("value.test").unwrap(), StorageValue::Felt(Felt::ONE)),
]);
// Succesfully instantiate a map (keys are not duplicate)

let valid_init_storage_data = InitStorageData::new([(
StorageValueName::new("map.duplicate_key").unwrap(),
"0x30".to_string(),
)]);
AccountComponent::from_template(&template, &valid_init_storage_data).unwrap();
}
}
Loading

0 comments on commit 8449edd

Please sign in to comment.