-
Notifications
You must be signed in to change notification settings - Fork 115
refactor: Implement component schemas #2193
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
c3db35d
refactor: replace templates with component schemas
igamigo 98d0bed
chore: overrideable -> overridable
igamigo 63fb0b3
chore: more docs
igamigo fc08696
feat: avoid overloading type word and define slot type with the dotte…
igamigo 3ab023d
chore: CHANGELOG
igamigo 0a52051
chore: update docs
igamigo b1cde82
chore: update docs
igamigo 3471268
chore: display
igamigo a77a61a
chore: spellcheck
igamigo 959bc51
reviews: address most of the first review's smaller comments
igamigo a7a909f
reviews: infer type based o type structure
igamigo a66477e
reviews: re-enable tests
igamigo b79f1e2
reviews: docs, typeregstry renames, doc comment rewrites
igamigo 8348f43
chore: lints
igamigo 52f47ce
reviews: give context to errors, simplify validations, revert felt pa…
igamigo 68c39c0
reviews: simplify further
igamigo 0c0cd44
reviews: initvaluerequirements -> schemarequirements; now collected i…
igamigo 2cca66d
reviews: more doc suggestions applied; validate schema
igamigo d349a11
reviews: singular->simple, scalar->atomic, docs reviews, nits
igamigo b5942e1
reviews: initstoragedata duplicate detection
igamigo 6d16850
feat: scope storagevaluename
igamigo 258520d
chore: docs fix
igamigo bea5bf1
chore: fix indentaion
mmagician File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
213 changes: 213 additions & 0 deletions
213
crates/miden-objects/src/account/component/metadata/mod.rs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,213 @@ | ||
| use alloc::collections::{BTreeMap, BTreeSet}; | ||
| use alloc::string::{String, ToString}; | ||
| use core::str::FromStr; | ||
|
|
||
| use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; | ||
| use miden_mast_package::{Package, SectionId}; | ||
| use miden_processor::DeserializationError; | ||
| use semver::Version; | ||
|
|
||
| use super::{AccountStorageSchema, AccountType, SchemaRequirement, StorageValueName}; | ||
| use crate::AccountError; | ||
|
|
||
| // ACCOUNT COMPONENT METADATA | ||
| // ================================================================================================ | ||
|
|
||
| /// Represents the full component metadata configuration. | ||
| /// | ||
| /// An account component metadata describes the component alongside its storage layout. | ||
| /// The storage layout can declare typed values which must be provided at instantiation time via | ||
| /// [InitStorageData](`super::storage::InitStorageData`). These can appear either at the slot level | ||
| /// (a singular word slot) or inside composed words as typed fields. | ||
| /// | ||
| /// When the `std` feature is enabled, this struct allows for serialization and deserialization to | ||
| /// and from a TOML file. | ||
| /// | ||
| /// # Guarantees | ||
| /// | ||
| /// - The metadata's storage schema does not contain duplicate slot names. | ||
| /// - The schema cannot contain protocol-reserved slot names. | ||
| /// - Each init-time value name uniquely identifies a single value. The expected init-time metadata | ||
| /// can be retrieved with [AccountComponentMetadata::schema_requirements()], which returns a map | ||
| /// from keys to [SchemaRequirement] (which indicates the expected value type and optional | ||
| /// defaults). | ||
| /// | ||
| /// # Example | ||
| /// | ||
| /// ``` | ||
| /// use std::collections::BTreeSet; | ||
| /// | ||
| /// use miden_objects::account::StorageSlotName; | ||
| /// use miden_objects::account::component::{ | ||
| /// AccountComponentMetadata, | ||
| /// AccountStorageSchema, | ||
| /// FeltSchema, | ||
| /// InitStorageData, | ||
| /// SchemaTypeId, | ||
| /// StorageSlotSchema, | ||
| /// StorageValueName, | ||
| /// ValueSlotSchema, | ||
| /// WordSchema, | ||
| /// }; | ||
| /// use semver::Version; | ||
| /// | ||
| /// let slot_name = StorageSlotName::new("demo::test_value")?; | ||
| /// | ||
| /// let word = WordSchema::new_value([ | ||
| /// FeltSchema::new_void(), | ||
| /// FeltSchema::new_void(), | ||
| /// FeltSchema::new_void(), | ||
| /// FeltSchema::new_typed(SchemaTypeId::native_felt(), "foo"), | ||
| /// ]); | ||
| /// | ||
| /// let storage_schema = AccountStorageSchema::new([( | ||
| /// slot_name.clone(), | ||
| /// StorageSlotSchema::Value(ValueSlotSchema::new(Some("demo slot".into()), word)), | ||
| /// )])?; | ||
| /// | ||
| /// let metadata = AccountComponentMetadata::new( | ||
| /// "test name".into(), | ||
| /// "description of the component".into(), | ||
| /// Version::parse("0.1.0")?, | ||
| /// BTreeSet::new(), | ||
| /// storage_schema, | ||
| /// ); | ||
| /// | ||
| /// // Init value keys are derived from slot name: `demo::test_value.foo`. | ||
| /// let init_storage_data = InitStorageData::new( | ||
| /// [(StorageValueName::from_slot_name(&slot_name).with_suffix("foo")?, "300".into())], | ||
| /// [], | ||
| /// ); | ||
| /// | ||
| /// let storage_slots = metadata.storage_schema().build_storage_slots(&init_storage_data)?; | ||
| /// assert_eq!(storage_slots.len(), 1); | ||
| /// # Ok::<(), Box<dyn std::error::Error>>(()) | ||
| /// ``` | ||
| #[derive(Debug, Clone, PartialEq, Eq)] | ||
| #[cfg_attr(feature = "std", derive(serde::Deserialize, serde::Serialize))] | ||
| #[cfg_attr(feature = "std", serde(rename_all = "kebab-case"))] | ||
| pub struct AccountComponentMetadata { | ||
| /// The human-readable name of the component. | ||
| name: String, | ||
|
|
||
| /// A brief description of what this component is and how it works. | ||
| description: String, | ||
|
|
||
| /// The version of the component using semantic versioning. | ||
| /// This can be used to track and manage component upgrades. | ||
| version: Version, | ||
|
|
||
| /// A set of supported target account types for this component. | ||
| supported_types: BTreeSet<AccountType>, | ||
|
|
||
| /// Storage schema defining the component's storage layout, defaults, and init-supplied values. | ||
| #[cfg_attr(feature = "std", serde(rename = "storage"))] | ||
| storage_schema: AccountStorageSchema, | ||
| } | ||
|
|
||
| impl AccountComponentMetadata { | ||
| /// Create a new [AccountComponentMetadata]. | ||
| pub fn new( | ||
| name: String, | ||
| description: String, | ||
| version: Version, | ||
| targets: BTreeSet<AccountType>, | ||
| storage_schema: AccountStorageSchema, | ||
| ) -> Self { | ||
| Self { | ||
| name, | ||
| description, | ||
| version, | ||
| supported_types: targets, | ||
| storage_schema, | ||
| } | ||
| } | ||
|
|
||
| /// Returns the init-time values's requirements for this schema. | ||
| /// | ||
| /// These values are used for initializing storage slot values or storage map entries. For a | ||
| /// full example, refer to the docs for [AccountComponentMetadata]. | ||
| /// | ||
| /// Types for returned init values are inferred based on their location in the storage layout. | ||
| pub fn schema_requirements(&self) -> BTreeMap<StorageValueName, SchemaRequirement> { | ||
| self.storage_schema.schema_requirements().expect("storage schema is validated") | ||
| } | ||
|
|
||
| /// Returns the name of the account component. | ||
| pub fn name(&self) -> &str { | ||
| &self.name | ||
| } | ||
|
|
||
| /// Returns the description of the account component. | ||
| pub fn description(&self) -> &str { | ||
| &self.description | ||
| } | ||
|
|
||
| /// Returns the semantic version of the account component. | ||
| pub fn version(&self) -> &Version { | ||
| &self.version | ||
| } | ||
|
|
||
| /// Returns the account types supported by the component. | ||
| pub fn supported_types(&self) -> &BTreeSet<AccountType> { | ||
| &self.supported_types | ||
| } | ||
|
|
||
| /// Returns the storage schema of the component. | ||
| pub fn storage_schema(&self) -> &AccountStorageSchema { | ||
| &self.storage_schema | ||
| } | ||
| } | ||
|
|
||
| impl TryFrom<&Package> for AccountComponentMetadata { | ||
| type Error = AccountError; | ||
|
|
||
| fn try_from(package: &Package) -> Result<Self, Self::Error> { | ||
| package | ||
| .sections | ||
| .iter() | ||
| .find_map(|section| { | ||
| (section.id == SectionId::ACCOUNT_COMPONENT_METADATA).then(|| { | ||
| AccountComponentMetadata::read_from_bytes(§ion.data).map_err(|err| { | ||
| AccountError::other_with_source( | ||
| "failed to deserialize account component metadata", | ||
| err, | ||
| ) | ||
| }) | ||
| }) | ||
| }) | ||
| .transpose()? | ||
| .ok_or_else(|| { | ||
| AccountError::other( | ||
| "package does not contain account component metadata section - packages without explicit metadata may be intended for other purposes (e.g., note scripts, transaction scripts)", | ||
| ) | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| // SERIALIZATION | ||
| // ================================================================================================ | ||
|
|
||
| impl Serializable for AccountComponentMetadata { | ||
| fn write_into<W: ByteWriter>(&self, target: &mut W) { | ||
| self.name.write_into(target); | ||
| self.description.write_into(target); | ||
| self.version.to_string().write_into(target); | ||
| self.supported_types.write_into(target); | ||
| self.storage_schema.write_into(target); | ||
| } | ||
| } | ||
|
|
||
| impl Deserializable for AccountComponentMetadata { | ||
| fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> { | ||
| Ok(Self { | ||
| name: String::read_from(source)?, | ||
| description: String::read_from(source)?, | ||
| version: semver::Version::from_str(&String::read_from(source)?).map_err( | ||
| |err: semver::Error| DeserializationError::InvalidValue(err.to_string()), | ||
| )?, | ||
| supported_types: BTreeSet::<AccountType>::read_from(source)?, | ||
| storage_schema: AccountStorageSchema::read_from(source)?, | ||
| }) | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not for this PR, but currently protocol-reserved slot names are hardcoded to a single check:
One idea would be to encode this in some helper enum
ReservedSlotNames(or similar), which we could then reference from doc strings like this one here. On the other hand, this sounds like a bit of an overkill for the single reserved slot name that we have. But I admit, it's a little hard to find what protocol reserved slots are in the codebase.cc @PhilippGackstatter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally agree this would be nice, but I think we want to get rid of the faucet sysdata slot and make issuance tracking the responsibility of the faucet implementation, in which case we'd no longer have any protocol-reserved slots at all, which would be even better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed - though for now we could probably go with the
RESERVED_SLOT_NAMESas already implemented in #2207It will be equally easy to change if/once we remove the faucet's reserved slot.