feat: adapt the layout of Assets to the double word representation#2437
feat: adapt the layout of Assets to the double word representation#2437
Assets to the double word representation#2437Conversation
bobbinth
left a comment
There was a problem hiding this comment.
Looks good! Thank you! Not a full review - but I did review pretty much all Rust code and some MASM code as well. Left some comments inline - mostly nits.
| /// Returns ID of the faucet which issued this asset. | ||
| pub fn faucet_id(&self) -> AccountIdPrefix { | ||
| pub fn faucet_id(&self) -> AccountId { | ||
| self.faucet_id | ||
| } |
There was a problem hiding this comment.
Similar to one of the other comments (and not for this PR), I wonder if we should name methods like this as issuer().
There was a problem hiding this comment.
Generally agreed. Should we replace the whole "faucet account" terminology with "issuer account" or only some of it? Have we made a decision on whether all accounts will be able to issue assets (and remove the faucet account types)?
I think issuer makes sense for these kinds of methods or fields, but "issuer account" is not really clearer than "faucet account", I think. I think my favorite option would be removing AccountType altogether.
| /// Creates an [`AssetVaultKey`] from its parts. | ||
| pub fn new(asset_id: AssetId, faucet_id: AccountId) -> Self { | ||
| Self { asset_id, faucet_id } |
There was a problem hiding this comment.
nit: I find the order here a bit odd as the faucet_id is the "bigger" category. I'd probably do this like so:
pub fn new(faucet_id: AccountId, asset_id: AssetId) -> Self {
Self { faucet_id, asset_id }
}And in the future would use the issuer nomenclature:
pub fn new(issuer: AccountId, asset_id: AssetId) -> Self {
Self { issuer, asset_id }
}There was a problem hiding this comment.
After the VM migration (specifically #2512) we would have:
impl AssetId {
pub fn new(suffix: Felt, prefix: Felt) -> Self { ... }
}
impl StorageSlotId {
pub fn new(suffix: Felt, prefix: Felt) -> Self { ... }
}
impl AccountId {
pub fn try_from_elements(suffix: Felt, prefix: Felt) -> Result<Self, AccountIdError> { ... }
}
impl AssetVaultKey {
pub fn new(asset_id: AssetId, faucet_id: AccountId) -> Self { ... }
}In my mind, these are consistent in that the "more significant" element comes last. This is nice because at that point we always have suffix then prefix order (whereas before the migration we had both, and it lead to confusion) and AssetVaultKey is consistent with that, too.
We can reverse it to have the more significant element come first, but it would re-introduce APIs where we need to use prefix then suffix order. Maybe this would prioritize something other than that kind of consistency, which may be fine.
igamigo
left a comment
There was a problem hiding this comment.
LGTM! Just left a couple of very minor comments
bobbinth
left a comment
There was a problem hiding this comment.
Looks good! Thank you! I left some comments inline, but these are mostly naming nits that can be addressed in follow-up PRs.
| @@ -61,3 +113,94 @@ end | |||
| pub proc get_amount | |||
There was a problem hiding this comment.
nit: I would maybe rename this into value_into_amount.
1e2ca48 to
24a98a6
Compare
Refactors the layout of fungible and non-fungible assets. The main benefit for now is that non-fungible assets include the full account ID. The other good news is that this PR removes the last
unsafecode from miden base 😅.Layout Changes
For direct comparison, the previous layout was:
[amount, 0, faucet_id_suffix, faucet_id_prefix].[0, 0, faucet_id_suffix, faucet_id_prefix].[hash0, hash1, hash2, faucet_id_prefix].[faucet_id_prefix, hash1, hash2, hash0'].The new layout is:
[amount, 0, 0, 0].[0, 0, faucet_id_suffix, faucet_id_prefix].[hash0, hash1, hash2, hash3].[hash0, hash1, faucet_id_suffix, faucet_id_prefix].The most notable change is that non-fungible assets now contain the full
AccountIdinstead of just theAccountIdPrefix. I believe this removes the last reason we had that account ID prefixes needed to be unique in the account tree. So, we could now consider lifting this restriction.Account Delta
The account delta is impacted by the layout change, namely that non-fungible asset's were previously committed to by hashing their value. Because the value doesn't contain the faucet ID anymore, the delta also doesn't commit to the asset's issuer anymore and so the delta format needed to be changed to commit to the faucet ID. This means the asset key of the non-fungible asset is now included.
For security, the faucet ID prefix (specifically the bit in the account ID that determines the faucet type) must be at the same position in the layout to avoid introducing an ambiguity in the delta format. So, the layout of the fungible asset's delta elements was adapted as well. Overall it is now:
Fungible Assets
[domain = 1, was_added, faucet_id_suffix, faucet_id_prefix][amount_delta, 0, 0, 0]Non-Fungible Assets
[domain = 1, was_added, faucet_id_suffix, faucet_id_prefix][hash0, hash1, hash2, hash3](= ASSET_VALUE)See docs of
AccountDelta::to_commitmentfor all the details.This change included updates to the delta commitment computation as well as how non-fungible assets are tracked in the delta.
Note that the
NonFungibleAssetDelta's internal representation is a bit ugly, but a larger refactors doesn't make sense at this point since this may change more fundamentally when generalizing assets.Asset Key Ordering
AssetVaultKeyimplementsOrdandPartialOrdbased onLexicographicWordfor use in the delta. Not strictly necessary, but convenient. I also added implementations forAssetthat delegate toAssetVaultKey, but these are currently unused.Asset Modules
This PR commits further to the
fungible_assetand nownon_fungible_assetmodule in the kernel for implementing their respective logic. If we move this logic elsewhere later, this should make this process easier.Transaction Outputs
Since the fee asset is a fungible asset, and it is represented as a double word, the
FEE_ASSET_VALUEtx output needed to be adapted, as it would now only contain the amount, but not the faucet ID. The new approach is to output the individual required elements from the fungible asset, i.e. its faucet ID limbs and the amount. This also happens to make room for a full word on the output stack.Tests
Fixes a couple of tests where asset key and value were incorrectly ordered but the test didn't fail before, presumably due to the similar layout of key and value.
Migration
miden::protocol::active_account::has_non_fungible_assetnow takes anASSET_KEYas an input.miden::protocol::asset::create_non_fungible_assetnow takes the full account ID as an input.to_key_wordandto_value_wordmethods, andfrom_key_valueandfrom_key_value_wordsconstructors.NonFungibleAssetDelta::new's signature has changed.FungibleAsset::newandAsset::is_(non_)fungibleare no longerconst(I'm not sure these can realistically be used in aconstcontext and it restricts future non-breaking internal changes).{Asset, NonFungibleAsset, FungibleAsset}::faucet_id_prefixwas removed.NonFungibleAsset's andNonFungibleAssetDetails' structure has changed. They both contain the fullAccountIdnow.AssetVaultKey::{new_unchecked, faucet_id_prefix, as_word}were removed. Usenew,faucet_idandto_wordinstead.AssetVaultKey::from_account_idrenamed tonew_fungiblesince non-fungible asset vault keys cannot be created from just a faucet ID.Follow-Up
AssetIdnow is its own thing.Debugimplementation which is not a stable representation (could change with non-breaking changes) and it also shouldn't be used for error messages. Tracked in Implement properDisplayfor fungible and non-fungible assets #2526.AssetVaultKeybefore inserting into asset vault SMT #2518.closes #2328