Skip to content

Add AccountComponent::from_package() method#1802

Merged
bobbinth merged 14 commits intonextfrom
ajl-from-package-account-component
Sep 9, 2025
Merged

Add AccountComponent::from_package() method#1802
bobbinth merged 14 commits intonextfrom
ajl-from-package-account-component

Conversation

@partylikeits1983
Copy link
Contributor

This PR adds a useful method to the AccountComponent type that allows creating account components directly from miden-mast-package::Package instances.

This enables easier integration with the Miden compiler toolchain by providing a direct path from compiled packages to account components.

Changes

  • Added AccountComponent::from_package() method that extracts metadata and library from a package
  • Added miden-mast-package dependency (v0.17.0) to workspace
  • Added a test testing the from_package() method

@partylikeits1983 partylikeits1983 self-assigned this Aug 27, 2025
@partylikeits1983 partylikeits1983 marked this pull request as ready for review August 27, 2025 13:21
Copy link
Collaborator

@greenhat greenhat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good! Just a minor note.

let metadata_bytes = package
.account_component_metadata_bytes
.as_deref()
.expect("no account component metadata present");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we return AccountError here instead of panicking?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replaced the panic with proper error handling by adding a new error variant to the AccountError enum

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the metadata is optional in the Package, why do we need it? An AccountComponent doesn't require an AccountComponentMetadata, so I think we should consider it optional here.

I don't think we need to create an AccountComponentTemplate here, we can just pass the library and the storage slots to AccountComponent::new.

For the supported types, we can:

  • check if metadata is present on the package, if not, default to regular accounts.
  • if metadata is present but fails to be deserialized, return AccountError::other_with_source as we do now.
  • if metadata is present and valid, use the supported types from AccountComponentMetadata.

Aside: It would be nicer if the mast package was able to contain a valid AccountComponentMetadata so we wouldn't even have to deserialize it. Then the function could be made infallible, but I know there'd be a dependency cycle if we did this :(

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need to create an AccountComponentTemplate here, we can just pass the library and the storage slots to AccountComponent::new.

Regarding this, it'd still be nice if there were a separate API for instantiating an AccountComponentTemplate. Some of the rationale is explained in this issue (and comment): 0xMiden/miden-client#1132 (comment)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it'd still be nice if there were a separate API for instantiating an AccountComponentTemplate (I wish we had a better name for it).

I agree. AccountComponentTemplate is basically a typed version of Package. That is, Package is a generic object that can have some custom metadata, and when we are talking about account components, this custom metadata should be sufficient to describe AccountComponentMetadata. So, ideally, we'd have something like:

impl TryFrom<Package> for AccountComponentTemplate {
    ...
}

A more general question about this PR: under what circumstances would we have the package and have the right storage slots for it? Is the use case here that we get the package by compiling a Rust crate, and then programmatically build Vec<StorageSlot> because we know storage layout that the package is in?

The reason I'm asking is that this is not very "type safe" - i.e., if the package changes, or we specify the wrong package, or make a mistake in building the Vec<StorageSlot> we'll build an invalid account component and we won't get any errors about this until we try to use it.

A safer way would be to instantiate the component using InitStorageData. That is:

  • We'd first build InitStorageData which is basically a key-value map.
  • Then we convert Package into AccountComponentTemplate.
  • Then we call AccountComponent::from_template().

This would throw errors if the provided storage data is incompatible with the package.

The main limitation here is that maybe building InitStorageData is not that convenient or maybe it can't express everything the way we'd want to express it. But in this case, it would be good to figure out how to make this approach more user-friendly. For example, maybe we could have better ways for instantiating InitStorageData (e.g., via macros?), or maybe the internal types should be more flexible (and not be just strings).

Copy link
Contributor

@PhilippGackstatter PhilippGackstatter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me!

I think I'd allow missing metadata. See inline comment.

let metadata_bytes = package
.account_component_metadata_bytes
.as_deref()
.expect("no account component metadata present");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the metadata is optional in the Package, why do we need it? An AccountComponent doesn't require an AccountComponentMetadata, so I think we should consider it optional here.

I don't think we need to create an AccountComponentTemplate here, we can just pass the library and the storage slots to AccountComponent::new.

For the supported types, we can:

  • check if metadata is present on the package, if not, default to regular accounts.
  • if metadata is present but fails to be deserialized, return AccountError::other_with_source as we do now.
  • if metadata is present and valid, use the supported types from AccountComponentMetadata.

Aside: It would be nicer if the mast package was able to contain a valid AccountComponentMetadata so we wouldn't even have to deserialize it. Then the function could be made infallible, but I know there'd be a dependency cycle if we did this :(

Comment on lines 34 to 35
None => {
// Create default metadata for packages without explicit metadata
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: should we actually do this? If a package does not have account component metadata, it could be a package for something else entirely (e.g., a note or a tx script).

@partylikeits1983
Copy link
Contributor Author

partylikeits1983 commented Sep 4, 2025

@bobbinth In the recent commit I fixed the unsafe default metadata handling in AccountComponentTemplate conversions.

Summary of changes:

  • Removed default metadata creation in TryFrom<Package>; now requires explicit account component metadata.
  • Removed unsafe from_package() method to enforce type safety.
  • Introduced from_package_with_init_data() as the sole workflow, requiring explicit metadata and InitStorageData.

All conversions of packages now follow the safe InitStorageDataAccountComponentTemplatefrom_template() path, preventing misclassification of notes or transaction scripts.


Edit:
Now the title of this PR is a bit of a misnomer since I removed the AccountComponent::from_package() method. Instead the PR title could be renamed to something like "Add Package to AccountComponent conversion functionality"

Copy link
Contributor

@bobbinth bobbinth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Thank you! I left just one small nit inline.

I'm curious how easy this will be to use with the current form of InitStorageData. Currently it is a map that maps a string key to a string value, but maybe one or both of these should change.

For example, the value could be a bit more structured. Right now we keep it as a string because parsing happens when we reconcile it with account template, but it may also be convenient to specify the values as word (or as maps) directly.

Similarly, once we have named storage slots, maybe the keys could also be storage slot names (or at least based on storage slot names).

Let's create an issue for this.

@partylikeits1983
Copy link
Contributor Author

Let's create an issue for this.

Opened an issue here: #1860

Is there anything outstanding on this PR?

@bobbinth
Copy link
Contributor

bobbinth commented Sep 8, 2025

@greenhat - could you take another look? I think this should be good to merge.

Copy link
Collaborator

@greenhat greenhat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants