-
Notifications
You must be signed in to change notification settings - Fork 59
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
Extend AccountId
to two Felt
s and refactor creation process
#982
Conversation
e3c2e11
to
70e705d
Compare
fab8b03
to
f3d649e
Compare
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.
Looks greaat!
I looked through the miden-lib
, looks good to me, I left just one nit inline.
I think I've addressed all the minor changes as well as the removal of the top zero bit and changing the non-fungible asset layout to avoid having to set the fungible bit. Other outstanding issues have been added to the follow-up tasks in the PR description or need feedback in the comments. I think they could pretty much all be resolved after this PR as well if we like to merge it, so I don't think the PR needs to necessarily be blocked by these. |
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.
LGTM, great work!
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.
Some drive-by comments :) Feel free to ignore.
impl From<AccountId> for AccountStorageMode { | ||
fn from(id: AccountId) -> Self { | ||
id.storage_mode() | ||
} | ||
} | ||
|
||
impl From<AccountIdPrefix> for AccountStorageMode { | ||
fn from(id_prefix: AccountIdPrefix) -> Self { | ||
id_prefix.storage_mode() | ||
} | ||
} |
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.
This seems largely redundant? Are the function's not enough, or are these required for generics somewhere?
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.
I think I added these for consistency. I believe there were some existing From
impls which I just extended, but they are redundant, yeah. The same goes for the From
impls on AccountType
then. I'll see if I can remove them in a follow-up PR.
#[cfg(any(feature = "testing", test))] | ||
pub fn new_with_type_and_mode( | ||
mut bytes: [u8; 8], | ||
pub fn new_dummy( |
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.
nit: probably just dummy
? We don't use new_empty
for example.
#[cfg(any(feature = "testing", test))] | ||
pub fn new_with_type_and_mode( | ||
mut bytes: [u8; 8], | ||
pub fn new_dummy( |
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.
Could we get rng
support? In most node tests (e.g. 0xPolygonMiden/miden-node#591) we really just want some number of unique account IDs.
Sometimes one would also like to specify the account type and storage mode I suppose, but in general just having a quick way to create a random account ID without any additional fuss would make test code easier to grok and create.
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.
I couldn't do a full code review, just some things I noticed as I worked with this branch.
I've been using these changes for the last couple of days to migrate the node/client repos and the documentation/explanations seems really clear ❤️ Specifically on the base rust types. The current implementation seems to work correctly on both, I'll try to mantain both PRs updated as more commits are made here.
Additionally I had a question regarding the enforcement of prefix collision. The entity responsible for this check would be the node, right?
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.
Based on this comment. I used this as the account id anchor for genesis accounts. Would it be a good idea to have a separate constructor? Or maybe mention it in the docs.
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.
I added AccountIdAnchor::PRE_GENESIS
constant which could be used instead.
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.
Looks good! Thank you! I left some more comments inline - but these can be addressed in follow-up PRs.
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.
I added AccountIdAnchor::PRE_GENESIS
constant which could be used instead.
#! - acct_id_{hi,lo} are the first and second felt of the account id. | ||
export.get_global_acct_id |
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 related to this PR, but how is "global" account ID different from "native" account ID? cc @Fumuran.
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.
As far as I can tell, there is no difference.
The global ID is stored as part of process_global_inputs
so that it can later be retrieved to run this check:
miden-base/miden-lib/asm/kernels/transaction/lib/prologue.masm
Lines 468 to 474 in 487e8b9
# assert the account id matches the account id in global inputs | |
exec.memory::get_global_acct_id | |
exec.memory::get_account_id | |
exec.account::is_id_eq assert.err=ERR_PROLOGUE_MISMATCH_OF_ACCOUNT_IDS_FROM_GLOBAL_INPUTS_AND_ADVICE_PROVIDER | |
# => [ACCT_HASH] | |
(where get_account_id
returns the ID of the current account, which at this point is the native account).
We could get rid of this notion of global ID and storing it in memory if we pass it via the stack in prologue::prepare_transaction
.
#! Outputs: [is_fungible_faucet] | ||
#! | ||
#! Where: | ||
#! - acct_id is the account id. | ||
#! - acct_id_hi is the first felt of the account id. | ||
#! - is_fungible_faucet is a boolean indicating whether the account is a fungible faucet. | ||
export.is_fungible_faucet |
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.
Related to one of the previous comments: I wonder if we should come up with a way to differentiate between "pure" procedures and procedures which work in the context of the currently active account. For example, account::get_id
assumes that we are getting ID of the currently active account, but account::is_fungible_faucet
is a "pure" procedure unrelated to the currently active account.
One potential approach could be to move "pure" procedures into a separate module. For example, something like account::get_id
but account_utils::is_fungible_faucet
. But there are probably other approaches to this as well.
#! - the account id is invalid: account id must have at least `MIN_ACCOUNT_ONES` ones. | ||
#! - account_id_hi does not contain version zero. | ||
#! - account_id_lo contains an anchor epoch that is greater or equal to 2^16. | ||
#! - account_id_lo does not have its lower 8 bits set to zero. | ||
export.validate_id |
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.
I think naming can be improved here (i.e., we are not validating a full ID but rather some properties of an ID).
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.
This is an interesting point, actually.
This procedure should probably also validate account type and storage mode as it is used in validate_fungible_asset
, validate_non_fungible_asset
and validate_new_account
. I'm not sure if we actually ever validate in the kernel that storage mode is not set to 0b11
, for example, but we should.
If we add this, then this function is properly named as it validates everything we can validate about IDs.
What has changed
AccountId
previously fit into a felt or ~64 bits. Now its representation is two felts:In Rust, the first and second felts are always called as such, typically accessed with
id.first_felt()
andid.second_felt()
.In MASM, the first felt is the
hi
felt and the second felt is thelo
felt and its typical stack representation is [account_id_hi, account_id_lo
].The layout of an ID is:
See the
AccountId
documentation for details on the layout.There is a type
AccountIdPrefix
representing the validated first felt of anAccountId
which is primarily used for non-fungible assets and for asset deserialization. Ideally we would make that private, but for now it must be public for the constructors of non fungible assets.Notable Changes
Here is a quick overview of what changes with this PR. Layouts are in "memory order", that is, stack order would be the reverse of that.
[amount, 0, 0, faucet_id]
[amount, 0, faucet_id_lo, faucet_id_hi]
[hash_0, faucet_id, hash_2, hash_3]
[hash0, hash1, hash2, faucet_id_hi]
[tag, sender_acct_id, encoded_type_and_ex_hint, aux]
[sender_hi, sender_lo_type_and_hint_tag, note_tag_hint_payload, aux]
AfterBlock
can only take values less thanu32::MAX
so that when encoded together with the note tag in a felt (i.e.note_tag_hint_payload
above) felt validity is guaranteed.AccountIdPrefix
is compatible withAccountId
to allow for deserialization of a prefix from a serializedAccountId
. This is used for the next point.AccountIdPrefix
serialization is such that the first byte of the serialized layout contains the metadata. This is used in the asset deserialization to read the type and based on that deserialize the bytes as a fungible or non-fungible asset.NoteTag::from_account_id
effectively takes the most significant bits ofid_first_felt << 1
. This means the high zero bit is ignored with the rationale being that it would not add any value for matching an account ID against a note tag, since all ID's high bits are zero. Let me know if this doesn't make sense.MockChain
due to the dependence of the account ID on an anchor block.[account_nonce, 0, account_id_hi, account_id_lo]
. So anything related to that would be particularly helpful to have reviewed.This PR does not change anything about the account tree, e.g. the
accounts: SimpleSmt<ACCOUNT_TREE_DEPTH>
field inMockChain
.I think a rough sensible review order would be AccountId + AccountIdPrefix followed by the changes to assets, all in Rust.
Then moving over to MASM.
Open Questions
NoteMetadata
that is different from theWord
encoding? We had a different one before this PR, but now the serialization format and word encoding are identical.Follow-Up Tasks
There are still some TODOs in the code and those could mostly be addressed in a follow-up PR so this PR could would not necessarily be blocked by that (checked boxes are included in this PR).
AccountIdError
. There are now new error conditions for IDs and a separate error might be cleaner.AccountId::new_dummy
aconst fn
and then we could possibly replace the somewhat duplicate functionality ofmiden_objects::testing::account_id::account_id
.AccountType
and use it in error messages.generate_account_seed
. See also: ExtendAccountId
to twoFelt
s and refactor creation process #982 (comment)build.rs
constants patching for POW which we no longer have.account_id.rs
. See also ExtendAccountId
to twoFelt
s and refactor creation process #982 (comment).miden-lib
#1002).validate_fungible_asset_origin
validates the account ID and then callsvalidate_fungible_asset
which validates it again check if we can remove some redundancy here.NoteExecutionHint::AfterBlock
being constructable by users but it now cannot contain u32::MAX and we might want to prevent that.build_swap_tag
.create_account_invalid_seed
.AccountId
version wrapper (see ExtendAccountId
to twoFelt
s and refactor creation process #982 (comment)) and consider making ID V1 validation a separate procedure in MASM.AccountBuilder::new
, i.e.: ExtendAccountId
to twoFelt
s and refactor creation process #982 (comment).NoteExecutionHint::OnBlockSlot
to not use epoch, as it is a somewhat different concept. See ExtendAccountId
to twoFelt
s and refactor creation process #982 (comment) for details.u128::to_be_bytes
and<[u8; 15]>
encodings consistent (see ExtendAccountId
to twoFelt
s and refactor creation process #982 (comment)).u128::to_be_bytes
the metadata would not be in the first byte.AccountId
to twoFelt
s and refactor creation process #982 (comment)AccountId::new_dummy
->AccountId::dummy
(ExtendAccountId
to twoFelt
s and refactor creation process #982 (comment))AccountId
creation ideally with being able to specify the type and storage mode, but also not necessarily having to do so. Check if adding rng support is easily possible. There is some variability here, but not so much to justify a complete builder.let dummy: AccountId = account_id_dummy!(&some_rng);
let dummy: AccountId = account_id_dummy!([0xff; 15]);
let dummy: AccountId = account_id_dummy!([0xff; 15], AccountType::FungibleFaucet, AccountStorageMode::Public);
AccountId
to twoFelt
s and refactor creation process #982 (comment)AccountId
to twoFelt
s and refactor creation process #982 (comment)faucet_id_prefix
to Assets and renameAsset::faucet_id
toAsset::faucet_id_prefix
(ExtendAccountId
to twoFelt
s and refactor creation process #982 (comment), ExtendAccountId
to twoFelt
s and refactor creation process #982 (comment))AccountId
to twoFelt
s and refactor creation process #982 (comment))AccountId
to twoFelt
s and refactor creation process #982 (comment))validate_id
MASM procedure (ExtendAccountId
to twoFelt
s and refactor creation process #982 (comment))AccountId
to twoFelt
s and refactor creation process #982 (comment))Why it changed
See #140, although I'd like to ideally add a summary here.
closes #140