Skip to content
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

feat: Introduce AccountComponentTemplate #1015

Merged
merged 25 commits into from
Jan 9, 2025
Merged

feat: Introduce AccountComponentTemplate #1015

merged 25 commits into from
Jan 9, 2025

Conversation

igamigo
Copy link
Collaborator

@igamigo igamigo commented Dec 11, 2024

Part of #550.
Introduces a first draft of AccountComponentPackage and AccountComponentTemplate.

For now it can serialize and deserialize configs such as this one:

name = "Test Component"
description = "This is a test component"
version = "1.0.0"
targets = ["FungibleFaucet"]

[[storage]]
name = "slot0"
description = "First slot"
slot = 0
value = "0x2551340000000000af02000000000000a3db000000000000ffeeff0f00000000"

[[storage]]
name = "multi"
description = "Multi slot entry"
slots = [1, 2, 3]
values = [["3428645", "687", "0xdba3", "0xfffeeff"], "0x2551340000000000af02000000000000a3db000000000000ffeeff0f00000000", "0x2551340000000000af02000000000000a3db000000000000ffeeff0f00000000"]

[[storage]]
name = "map"
slot = [4]
values = [{key="0x1", value="0x2"}]

Most structs down to the element need to have custom (de)serialization due to the different alternatives discussed in #550.

@igamigo igamigo force-pushed the igamigo-acc-pkg branch 2 times, most recently from 0cee24f to 123e92c Compare December 11, 2024 22:10
@igamigo igamigo changed the title feat: Introduce ComponentPackage feat: Introduce ComponentPackage and ComponentConfig Dec 11, 2024
@igamigo igamigo changed the title feat: Introduce ComponentPackage and ComponentConfig feat: Introduce ComponentPackage and ComponentTemplate Dec 12, 2024
@igamigo igamigo marked this pull request as ready for review December 12, 2024 21:55
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 good! Thank you! I left just a few comments inline.

objects/src/accounts/package/storage_entry/mod.rs Outdated Show resolved Hide resolved
objects/src/accounts/package/storage_entry/mod.rs Outdated Show resolved Hide resolved
objects/src/accounts/package/mod.rs Outdated Show resolved Hide resolved
Comment on lines 103 to 112
if slots.len() != values.len() {
return Err(ComponentMetadataError::InvalidMultiSlotEntry);
}

Ok(StorageEntry::MultiSlot {
name: name.into(),
description: description.map(Into::<String>::into),
slots,
values: values.into_iter().map(Into::into).collect(),
})
Copy link
Collaborator

Choose a reason for hiding this comment

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

The description above says:

  • A multi-slot entry (spanning multiple contiguous slots, with multipe values).

Should we check that the slots are contiguous? (I know we also check it at the ComponentPackage level.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added this change

if !slot_present {
return Err(D::Error::missing_field("slot"));
}
if raw.value.is_some() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: could we use value_present here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Agreed!

Comment on lines 284 to 293
if slot_present {
return Err(D::Error::custom(
"Fields 'slot' and 'slots' are mutually exclusive.",
));
}
if value_present {
return Err(D::Error::custom(
"Fields 'value' and 'values' are mutually exclusive.",
));
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

These checks for mutual exclusivity can be done before the match to reduce code repetition

Comment on lines 244 to 245
slot: raw.slot.expect("was checked to be present"),
value: raw.value.expect("was checked to be present"),
Copy link
Collaborator

Choose a reason for hiding this comment

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

instead of the if + expect we could do .ok_or(...)? here

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Changed! I ended up refactoring this section of code many times and left a mix of solutions, but these changes are helping shape it up better

Comment on lines 294 to 295
let slots = raw.slots.ok_or_else(|| D::Error::missing_field("slots"))?;
let values = raw.values.ok_or_else(|| D::Error::missing_field("values"))?;
Copy link
Collaborator

Choose a reason for hiding this comment

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

If we change the match to be match (raw.slots, raw.values) we could deconstruct in the case to (Some(slots), Some(values)) and remove these ok_or_else.

Comment on lines 137 to 141
impl Default for WordRepresentation {
fn default() -> Self {
WordRepresentation::SingleHex(Default::default())
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should this be in the previous section?

}
// Enforce and skip the '0x' prefix.
let hex_bytes = match hex.as_bytes() {
[b'0', b'x', rest @ ..] => rest,
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 allow for "0X" prefix like we do for Felts?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think I will disallow 0X in felts instead, unless there's a good reason to do otherwise. cc @bobbinth @PhilippGackstatter (and probably @Fumuran) do you think 0X should be a valid prefixes for things like the digest!() macro?

Copy link
Contributor

Choose a reason for hiding this comment

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

If it doesn't complicate things too much, i think making 0x prefix optional could be a good idea. For example, the following would result in an identical digest:

digest!("123456");
digest!("0x123456");

But this is a pretty minor thing - so, I'm fine with keeping as is as well.

Copy link
Contributor

Choose a reason for hiding this comment

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

digest!("123456");
digest!("0x123456");

Not sure I'm following the context completely, but isn't it confusing to have these two be the same? Unless you know that digest! takes a hex string, it would be easy to interpet digest!("123456"); as a decimal number, especially since macros are even less predictable than regular Rust functions. So wouldn't it be better to enforce hex strings to start with 0x to avoid ambiguity?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah - good point! Scratch my comment then - let's enforce 0x prefix for hex values.

Comment on lines -92 to -93
// SAFETY: u8 cast to u64 is safe. We cannot use u64::from in const context so we
// are forced to cast.
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should add this safety comment back to the match case

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good catch, re-added

objects/src/accounts/component/mod.rs Outdated Show resolved Hide resolved
objects/src/accounts/package/mod.rs Outdated Show resolved Hide resolved
objects/src/accounts/package/storage_entry/mod.rs Outdated Show resolved Hide resolved
@igamigo igamigo changed the title feat: Introduce ComponentPackage and ComponentTemplate feat: Introduce AccountComponentTemplate Jan 2, 2025
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 conceptually!

There are quite a few pub types that are not documented which would be great to add.

I'm also wondering if making everything pub by default is good. There are a lot of new types added in this PR and the API surface of miden-base increases quite a bit. If we only export some of the higher level types, it would be great to double-check if the lower level types are properly excluded from the docs and that they are not exposed through methods on the higher level types (although rustdoc should complain in that case anyway).

Otherwise, I left many small nits, mostly on errors and docs. Feel free to pick what you think makes sense!

mod storage_entry;
pub use storage_entry::{StorageEntry, TemplateKey, TemplateValue};

// COMPONENT PACKAGE
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// COMPONENT PACKAGE
// ACCOUNT COMPONENT TEMPLATE

I guess the file name or module name should also be template then?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Changed this

/// they are not of a valid type)
#[cfg(feature = "std")]
pub fn from_template(
package: &AccountComponentTemplate,
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
package: &AccountComponentTemplate,
template: &AccountComponentTemplate,

Nit: Looks like this should be template or something else instead of package now.

The documentation might also need an update to reflect the new naming.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks, changed this

/// a component within the system. It includes the configuration details and the compiled
/// library code required for the component's operation.
///
/// A package can be instantiated into [AccountComponent](super::AccountComponent) objects.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// A package can be instantiated into [AccountComponent](super::AccountComponent) objects.
/// A template can be instantiated into [AccountComponent](super::AccountComponent) objects.

/// The component's metadata. This describes the component and how the storage is laid out,
/// alongside how storage values are initialized.
metadata: ComponentMetadata,
/// The account's previously-assembled code. This defines all functionality related to the
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// The account's previously-assembled code. This defines all functionality related to the
/// The account component's assembled code. This defines all functionality related to the

Nit: Maybe just this?

Copy link
Collaborator Author

@igamigo igamigo Jan 3, 2025

Choose a reason for hiding this comment

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

Sounds good! Changed

Comment on lines 32 to 33
/// The component metadata can be defined with generic keys that can be replaced at instantiation
/// time.
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Here it's called "generic keys" while in other places it seems to be called "template keys". Ideally, we use just one term for this.

Reading this for the first time, my immediate question is how these template keys work, so it would be great to link to some place where more can be learned, or even add a more extensive example here (or as module docs).


impl core::fmt::Display for TemplateKey {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.key)
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm wondering if this should include braces. The inverse operation is basically TryFrom<&str> and it would expect braces, so maybe this would be most consistent.

Copy link
Collaborator Author

@igamigo igamigo Jan 3, 2025

Choose a reason for hiding this comment

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

Yes, agreed, good catch. I originally did From<&str> instead of TryFrom<&str> which worked as the inverse of this, but then changed it based on Bobbin's suggestion and didn't realize this should've been changed as well.

Comment on lines 57 to 63
WordRepresentation::Array(array) => {
let mut result = [Felt::ZERO; 4];
for (index, felt_repr) in array.iter().enumerate() {
result[index] = felt_repr.clone().try_into_felt(template_values)?;
}
Ok(result)
},
Copy link
Contributor

Choose a reason for hiding this comment

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

Would be good to add a comment here that every index of result is guaranteed to be overwritten.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added this

Comment on lines 239 to 240
} else if let Ok(decimal_value) = value.parse::<u64>() {
Ok(FeltRepresentation::SingleDecimal(Felt::new(decimal_value)))
Copy link
Contributor

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 would want to silently wrap around if the given u64 exceeds the field modulus. If users specify such a value it would always be an error I would think, so it would be better to use Felt::try_from and propagate the error.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good catch, changed

} else if let Ok(key) = TemplateKey::try_from(&value) {
Ok(FeltRepresentation::Dynamic(key))
} else {
Err(serde::de::Error::custom("Value is not a valid element"))
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Err(serde::de::Error::custom("Value is not a valid element"))
Err(serde::de::Error::custom("deserialized string value is not a valid variant of FeltRepresentation"))

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Changed this

objects/src/accounts/package/storage_entry/word.rs Outdated Show resolved Hide resolved
@igamigo
Copy link
Collaborator Author

igamigo commented Jan 3, 2025

I'm also wondering if making everything pub by default is good. There are a lot of new types added in this PR and the API surface of miden-base increases quite a bit. If we only export some of the higher level types, it would be great to double-check if the lower level types are properly excluded from the docs and that they are not exposed through methods on the higher level types (although rustdoc should complain in that case anyway).

Yeah, I left a TODO to address this because I wanted to double check what we would need and restrict everything from there, but definitely agree that it should not all be pub by default and was planning to change this.

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.

Not a review - but quickly scanning through the code, I think there are still some smaller comments that need to be addressed. And we also still need to introduce something like InitStorageData struct.

Also, in doc comments we should update references to "package" vs. "template" (e.g., doc comments for AccountComponentTemplate struct).

@igamigo
Copy link
Collaborator Author

igamigo commented Jan 6, 2025

I think all comments have been addressed (but will double check just in case). The only remaining question from my side is whether we want TemplateKey to carry the expected key's type, which could be identified when deserialized based on the placement of the key.

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.

Thank you! Looks good! I left some comments inline.

One other thing I was thinking about is naming of TemplateKey and TemplateValue. Maybe these are fine, but I also thought that maybe these should indicate that we are dealing specifically with storage. An alternative could be something like StoragePlaceholder and StorageValue. But this is not a strong opinion by any means.

objects/src/accounts/component/mod.rs Outdated Show resolved Hide resolved
objects/src/accounts/component/template/mod.rs Outdated Show resolved Hide resolved
objects/src/accounts/component/template/mod.rs Outdated Show resolved Hide resolved
objects/src/accounts/component/template/mod.rs Outdated Show resolved Hide resolved
Comment on lines 181 to 183
/// Returns an iterator over all of the storage entries's template keys.
// TODO: Should template keys be typed?
pub fn template_keys(&self) -> Box<dyn Iterator<Item = &TemplateKey> + '_> {
Copy link
Contributor

Choose a reason for hiding this comment

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

re TODO: do you mean to get rid of dyn? If so, I think having dyn here is probably fine.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

What I meant is this, basically

Copy link
Contributor

Choose a reason for hiding this comment

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

Let's create an issue for this. it sounds like a good idea, but I'm not sure this will be possible in all circumstances.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Created #1051

@igamigo
Copy link
Collaborator Author

igamigo commented Jan 7, 2025

I also changed TemplateKey into StoragePlaceholder and TemplateValue into StorageValue. I think the latter specifically sounds a bit generic but overall this might be better. Hopefully did not miss too many doc changes, will double check again soon.

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 good! Thank you! I left a few comments inline - these are mostly nits or things we can do in subsequent PRs.

objects/src/accounts/account_id.rs Outdated Show resolved Hide resolved
objects/src/accounts/storage/map.rs Outdated Show resolved Hide resolved
objects/src/accounts/component/template/storage/mod.rs Outdated Show resolved Hide resolved
objects/src/accounts/component/template/storage/mod.rs Outdated Show resolved Hide resolved
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! Just a couple of nits.

objects/src/accounts/component/template/mod.rs Outdated Show resolved Hide resolved
objects/src/accounts/component/template/storage/mod.rs Outdated Show resolved Hide resolved
objects/src/errors.rs Outdated Show resolved Hide resolved
objects/src/accounts/component/template/storage/mod.rs Outdated Show resolved Hide resolved
objects/src/errors.rs Outdated Show resolved Hide resolved
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.

All looks good! Thank you!

@bobbinth bobbinth merged commit 206e247 into next Jan 9, 2025
9 checks passed
@bobbinth bobbinth deleted the igamigo-acc-pkg branch January 9, 2025 00:20
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.

4 participants