diff --git a/miden-lib/asm/sat/account.masm b/miden-lib/asm/sat/account.masm index 7caf4b329..31cdb5750 100644 --- a/miden-lib/asm/sat/account.masm +++ b/miden-lib/asm/sat/account.masm @@ -1,67 +1,3 @@ -use.miden::sat::layout - -# CONSTANTS -# ================================================================================================= - -# An enum that represents a regular account with updatable code. -const.REGULAR_ACCOUNT_UPDATABLE_CODE=0 - -# An enum that represents a regular account with immutable code. -const.REGULAR_ACCOUNT_IMMUTABLE_CODE=1 - -# An enum that represents a fungible faucet with immutable code. -const.FUNGIBLE_FAUCET_ACCOUNT=2 - -# An enum that represents a non-fungible faucet with immutable code. -const.NON_FUNGIBLE_FAUCET_ACCOUNT=3 - -# Specifies a minimum number of ones for a valid account ID. -const.MIN_ACCOUNT_ONES=5 - -# The depth of the account storage sparse merkle tree -const.STORAGE_TREE_DEPTH=8 - -# The depth of the account code tree -const.ACCOUNT_CODE_TREE_DEPTH=8 - -# PROCEDURES -# ================================================================================================= - -#! Computes and returns the account hash from account data stored in memory. -#! -#! Stack: [] -#! Output: [ACCT_HASH] -#! -#! - ACCT_HASH is the hash of the account data. -export.get_current_hash - # prepare the stack for computing the account hash - exec.layout::get_acct_data_ptr padw padw padw - - # stream account data and compute sequential hash. We perform two `mem_stream` operations - # because account data consists of exactly 4 words. - mem_stream hperm mem_stream hperm - - # extract account hash - dropw swapw dropw - - # drop memory pointer - movup.4 drop -end - -#! Increments the account nonce by the provided value. -#! -#! Stack: [value] -#! Output: [] -#! -#! - value is the value to increment the nonce by. value can be at most 2^32 - 1 otherwise this -#! procedure panics. -export.incr_nonce - u32assert.1 - exec.layout::get_acct_nonce add - exec.layout::set_acct_nonce -end - -# TODO: change to re-export once supported. #! Returns the account id. #! #! Stack: [] @@ -69,10 +5,13 @@ end #! #! - acct_id is the account id. export.get_id - exec.layout::get_acct_id + push.0 + # => [0] + + syscall.get_account_id + # => [acct_id] end -# TODO: change to re-export once supported. #! Returns the account nonce. #! #! Stack: [] @@ -80,10 +19,13 @@ end #! #! - nonce is the account nonce. export.get_nonce - exec.layout::get_acct_nonce + push.0 + # => [0] + + syscall.get_account_nonce + # => [nonce] end -# TODO: change to re-export once supported. #! Returns the initial account hash. #! #! Stack: [] @@ -91,162 +33,39 @@ end #! #! - H is the initial account hash. export.get_initial_hash - exec.layout::get_init_acct_hash -end - -#! Returns the account type of the account id provided via the stack. -#! -#! The account type can be of the following forms: -#! - regular account with updatable code -#! - regular account with immutable code -#! - fungible asset faucet account with immutable code -#! - non-fungible asset faucet account with immutable code -#! -#! Stack: [acct_id] -#! Output: [acct_type] -#! -#! - acct_id is the account id. -#! - acct_type is the account type. -proc.type - # compute the account type - u32split swap drop u32checked_shr.30 - # => [acct_type] -end - -#! Returns a boolean indicating whether the account is a fungible faucet. -#! -#! Stack: [acct_id] -#! Output: [is_fungible_faucet] -#! -#! - acct_id is the account id. -#! - is_fungible_faucet is a boolean indicating whether the account is a fungible faucet. -export.is_fungible_faucet - # get the account type - exec.type - # => [acct_type] - - # check if the account type is a fungible faucet - push.FUNGIBLE_FAUCET_ACCOUNT eq - # => [is_fungible_faucet] -end - -#! Returns a boolean indicating whether the account is a non-fungible faucet. -#! -#! Stack: [acct_id] -#! Output: [is_non_fungible_faucet] -#! -#! - acct_id is the account id. -#! - is_non_fungible_faucet is a boolean indicating whether the account is a non-fungible faucet. -export.is_non_fungible_faucet - # get the account type - exec.type - # => [acct_type] - - # check if the account type is a non-fungible faucet - push.NON_FUNGIBLE_FAUCET_ACCOUNT eq - # => [is_non_fungible_faucet] -end - -#! Returns a boolean indicating whether the account is a faucet. -#! -#! Stack: [acct_id] -#! Output: [is_faucet] -#! -#! - acct_id is the account id. -#! - is_faucet is a boolean indicating whether the account is a faucet. -export.is_faucet - # get the account type - exec.type - # => [acct_type] - - # check if the account type is a fungilbe faucet - dup push.FUNGIBLE_FAUCET_ACCOUNT eq - # => [is_fung_faucet, acct_type] - - # check if the account type is a non-fungible faucet - swap push.NON_FUNGIBLE_FAUCET_ACCOUNT eq - # => [is_non_fung_faucet, is_fung_faucet] - - # check if the account is a faucet - or - # => [is_faucet] -end + padw + # => [0, 0, 0, 0] -#! Returns a boolean indicating whether the account is a regular updatable account. -#! -#! Stack: [acct_id] -#! Output: [is_updatable_account] -#! -#! - acct_id is the account id. -#! - is_updatable_account is a boolean indicating whether the account is a regular updatable -#! account. -export.is_updatable_account - # get the account type - exec.type - # => [acct_type] - - # check if the account type is a regular account - push.REGULAR_ACCOUNT_UPDATABLE_CODE eq - # => [is_updatable_account] + syscall.get_initial_account_hash + # => [H] end -#! Returns a boolean indicating whether the account is a regular immutable account. -#! -#! Stack: [acct_id] -#! Output: [is_immutable_account] -#! -#! - acct_id is the account id. -#! - is_immutable_account is a boolean indicating whether the account is a regular immutable -#! account. -export.is_immutable_account - # get the account type - exec.type - # => [acct_type] - - # check if the account type is a regular account - push.REGULAR_ACCOUNT_IMMUTABLE_CODE eq - # => [is_immutable_account] -end - -#! Validates an account id. Panics if the account id is invalid. -#! Account id must have at least `MIN_ACCOUNT_ONES` ones. +#! Computes and returns the account hash from account data stored in memory. #! -#! Stack: [acct_id] -#! Output: [] +#! Stack: [] +#! Output: [ACCT_HASH] #! -#! - acct_id is the account id. -export.validate_id - # split felt into 32 bit limbs - u32split - # => [l_1, l_0] - - # count the number of 1 bits - u32unchecked_popcnt swap u32unchecked_popcnt add - # => [ones] +#! - ACCT_HASH is the hash of the account data. +export.get_current_hash + padw + # => [0, 0, 0, 0] - # check if the number of ones is at least MIN_ACCOUNT_ONES ones. - push.MIN_ACCOUNT_ONES u32unchecked_gte assert - # => [] + syscall.get_current_account_hash + # => [ACCT_HASH] end -#! Sets the code of the account the transaction is being executed against. This procedure can only -#! executed on regular accounts with updatable code. Otherwise, this procedure fails. +#! Increments the account nonce by the provided value. #! -#! Stack: [CODE_ROOT] +#! Stack: [value] #! Output: [] #! -#! - CODE_ROOT is the hash of the code to set. -export.set_code - # get the account id - exec.layout::get_acct_id - # => [acct_id, CODE_ROOT] - - # assert the account is an updatable regular account - exec.is_updatable_account assert - # => [CODE_ROOT] - - # set the code root - exec.layout::set_new_acct_code_root +#! - value is the value to increment the nonce by. value can be at most 2^32 - 1 otherwise this +#! procedure panics. +export.incr_nonce + syscall.incr_account_nonce + # => [0] + + drop # => [] end @@ -258,16 +77,10 @@ end #! - index is the index of the item to get. #! - VALUE is the value of the item. export.get_item - # get the storage root - exec.layout::get_acct_storage_root - # => [storage_root, index] + push.0.0.0 movup.3 + # => [index, 0, 0, 0] - # get the item from storage - movup.4 push.STORAGE_TREE_DEPTH mtree_get - # => [VALUE, ROOT] - - # drop the root - swapw dropw + syscall.get_account_item # => [VALUE] end @@ -281,44 +94,53 @@ end #! - V is the previous value of the item. #! - R' is the new storage root. export.set_item - # get the storage root - exec.layout::get_acct_storage_root - # => [R, index, V'] - - # set the item in storage - movup.4 push.STORAGE_TREE_DEPTH mtree_set - # => [V, R'] + push.0 movdn.5 push.0 movdn.5 push.0 movdn.5 + # => [index, V', 0, 0, 0] - # set the new storage root - swapw exec.layout::set_acct_storage_root - # => [V] + syscall.set_account_item + # => [R', V] end -#! Authenticates the proedcure root is part of the account code Merkle treee. Panics if the -#! procedure root is not part of the account code Merkle tree. +#! Sets the code of the account the transaction is being executed against. This procedure can only +#! executed on regular accounts with updatable code. Otherwise, this procedure fails. #! -#! Stack: [PROC_ROOT] -#! Output: [PROC_ROOT] +#! Stack: [CODE_ROOT] +#! Output: [] #! -#! - PROC_ROOT is the hash of the procedure to authenticate. -export.authenticate_procedure.1 - # load the account code root onto the stack - exec.layout::get_acct_code_root swapw - # => [PROC_ROOT, CODE_ROOT] +#! - CODE_ROOT is the hash of the code to set. +export.set_code + syscall.set_account_code + # => [0, 0, 0, 0] - # load the index of the procedure root onto the advice stack - adv.push_mapval adv_push.1 movdn.4 - # => [PROC_ROOT, index, CODE_ROOT] + dropw + # => [] +end - # push the depth of the code Merkle tree onto the stack - push.ACCOUNT_CODE_TREE_DEPTH movdn.4 - # => [PROC_ROOT, depth, index, CODE_ROOT] +#! Returns the balance of a fungible asset associated with a faucet_id. +#! Panics if the asset is not a fungible asset. +#! +#! Stack: [faucet_id] +#! Output: [balance] +#! +#! - faucet_id is the faucet id of the fungible asset of interest. +#! - balance is the vault balance of the fungible asset. +export.get_balance + syscall.account_vault_get_balance + # => [balance] +end - # verify the procedure exists in the account code Merkle tree - mtree_verify - # => [PROC_ROOT, depth, index, CODE_ROOT] +#! Returns a boolean indicating whether the non-fungible asset is present in the vault. +#! Panics if the ASSET is a fungible asset. +#! +#! Stack: [ASSET] +#! Output: [has_asset] +#! +#! - ASSET is the non-fungible asset of interest +#! - has_asset is a boolean indicating whether the account vault has the asset of interest +export.has_non_fungible_asset + syscall.account_vault_has_non_fungible_asset + # => [has_asset, 0, 0, 0] - # drop accessory variables - movup.4 drop movup.4 drop swapw dropw - # => [PROC_ROOT] -end + swap drop swap drop swap drop + # => [has_asset] +end \ No newline at end of file diff --git a/miden-lib/asm/sat/internal/account.masm b/miden-lib/asm/sat/internal/account.masm new file mode 100644 index 000000000..9d84aac86 --- /dev/null +++ b/miden-lib/asm/sat/internal/account.masm @@ -0,0 +1,315 @@ +use.miden::sat::internal::layout + +# CONSTANTS +# ================================================================================================= + +# An enum that represents a regular account with updatable code. +const.REGULAR_ACCOUNT_UPDATABLE_CODE=0 + +# An enum that represents a regular account with immutable code. +const.REGULAR_ACCOUNT_IMMUTABLE_CODE=1 + +# An enum that represents a fungible faucet with immutable code. +const.FUNGIBLE_FAUCET_ACCOUNT=2 + +# An enum that represents a non-fungible faucet with immutable code. +const.NON_FUNGIBLE_FAUCET_ACCOUNT=3 + +# Specifies a minimum number of ones for a valid account ID. +const.MIN_ACCOUNT_ONES=5 + +# The depth of the account storage sparse merkle tree +const.STORAGE_TREE_DEPTH=8 + +# The depth of the account code tree +const.ACCOUNT_CODE_TREE_DEPTH=8 + +# PROCEDURES +# ================================================================================================= + +#! Computes and returns the account hash from account data stored in memory. +#! +#! Stack: [] +#! Output: [ACCT_HASH] +#! +#! - ACCT_HASH is the hash of the account data. +export.get_current_hash + # prepare the stack for computing the account hash + exec.layout::get_acct_data_ptr padw padw padw + + # stream account data and compute sequential hash. We perform two `mem_stream` operations + # because account data consists of exactly 4 words. + mem_stream hperm mem_stream hperm + + # extract account hash + dropw swapw dropw + + # drop memory pointer + movup.4 drop +end + +#! Increments the account nonce by the provided value. +#! +#! Stack: [value] +#! Output: [] +#! +#! - value is the value to increment the nonce by. value can be at most 2^32 - 1 otherwise this +#! procedure panics. +export.incr_nonce + u32assert.1 + exec.layout::get_acct_nonce add + exec.layout::set_acct_nonce +end + +#! Returns the account id. +#! +#! Stack: [] +#! Output: [acct_id] +#! +#! - acct_id is the account id. +export.layout::get_acct_id->get_id + +#! Returns the account nonce. +#! +#! Stack: [] +#! Output: [nonce] +#! +#! - nonce is the account nonce. +export.layout::get_acct_nonce->get_nonce + +#! Returns the initial account hash. +#! +#! Stack: [] +#! Output: [H] +#! +#! - H is the initial account hash. +export.layout::get_init_acct_hash->get_initial_hash + +#! Returns the account type of the account id provided via the stack. +#! +#! The account type can be of the following forms: +#! - regular account with updatable code +#! - regular account with immutable code +#! - fungible asset faucet account with immutable code +#! - non-fungible asset faucet account with immutable code +#! +#! Stack: [acct_id] +#! Output: [acct_type] +#! +#! - acct_id is the account id. +#! - acct_type is the account type. +proc.type + # compute the account type + u32split swap drop u32checked_shr.30 + # => [acct_type] +end + +#! Returns a boolean indicating whether the account is a fungible faucet. +#! +#! Stack: [acct_id] +#! Output: [is_fungible_faucet] +#! +#! - acct_id is the account id. +#! - is_fungible_faucet is a boolean indicating whether the account is a fungible faucet. +export.is_fungible_faucet + # get the account type + exec.type + # => [acct_type] + + # check if the account type is a fungible faucet + push.FUNGIBLE_FAUCET_ACCOUNT eq + # => [is_fungible_faucet] +end + +#! Returns a boolean indicating whether the account is a non-fungible faucet. +#! +#! Stack: [acct_id] +#! Output: [is_non_fungible_faucet] +#! +#! - acct_id is the account id. +#! - is_non_fungible_faucet is a boolean indicating whether the account is a non-fungible faucet. +export.is_non_fungible_faucet + # get the account type + exec.type + # => [acct_type] + + # check if the account type is a non-fungible faucet + push.NON_FUNGIBLE_FAUCET_ACCOUNT eq + # => [is_non_fungible_faucet] +end + +#! Returns a boolean indicating whether the account is a faucet. +#! +#! Stack: [acct_id] +#! Output: [is_faucet] +#! +#! - acct_id is the account id. +#! - is_faucet is a boolean indicating whether the account is a faucet. +export.is_faucet + # get the account type + exec.type + # => [acct_type] + + # check if the account type is a fungilbe faucet + dup push.FUNGIBLE_FAUCET_ACCOUNT eq + # => [is_fung_faucet, acct_type] + + # check if the account type is a non-fungible faucet + swap push.NON_FUNGIBLE_FAUCET_ACCOUNT eq + # => [is_non_fung_faucet, is_fung_faucet] + + # check if the account is a faucet + or + # => [is_faucet] +end + +#! Returns a boolean indicating whether the account is a regular updatable account. +#! +#! Stack: [acct_id] +#! Output: [is_updatable_account] +#! +#! - acct_id is the account id. +#! - is_updatable_account is a boolean indicating whether the account is a regular updatable +#! account. +export.is_updatable_account + # get the account type + exec.type + # => [acct_type] + + # check if the account type is a regular account + push.REGULAR_ACCOUNT_UPDATABLE_CODE eq + # => [is_updatable_account] +end + +#! Returns a boolean indicating whether the account is a regular immutable account. +#! +#! Stack: [acct_id] +#! Output: [is_immutable_account] +#! +#! - acct_id is the account id. +#! - is_immutable_account is a boolean indicating whether the account is a regular immutable +#! account. +export.is_immutable_account + # get the account type + exec.type + # => [acct_type] + + # check if the account type is a regular account + push.REGULAR_ACCOUNT_IMMUTABLE_CODE eq + # => [is_immutable_account] +end + +#! Validates an account id. Panics if the account id is invalid. +#! Account id must have at least `MIN_ACCOUNT_ONES` ones. +#! +#! Stack: [acct_id] +#! Output: [] +#! +#! - acct_id is the account id. +export.validate_id + # split felt into 32 bit limbs + u32split + # => [l_1, l_0] + + # count the number of 1 bits + u32unchecked_popcnt swap u32unchecked_popcnt add + # => [ones] + + # check if the number of ones is at least MIN_ACCOUNT_ONES ones. + push.MIN_ACCOUNT_ONES u32unchecked_gte assert + # => [] +end + +#! Sets the code of the account the transaction is being executed against. This procedure can only +#! executed on regular accounts with updatable code. Otherwise, this procedure fails. +#! +#! Stack: [CODE_ROOT] +#! Output: [] +#! +#! - CODE_ROOT is the hash of the code to set. +export.set_code + # get the account id + exec.layout::get_acct_id + # => [acct_id, CODE_ROOT] + + # assert the account is an updatable regular account + exec.is_updatable_account assert + # => [CODE_ROOT] + + # set the code root + exec.layout::set_new_acct_code_root + # => [] +end + +#! Gets an item from the account storage. Panics if the index is out of bounds. +#! +#! Stack: [index] +#! Output: [VALUE] +#! +#! - index is the index of the item to get. +#! - VALUE is the value of the item. +export.get_item + # get the storage root + exec.layout::get_acct_storage_root + # => [storage_root, index] + + # get the item from storage + movup.4 push.STORAGE_TREE_DEPTH mtree_get + # => [VALUE, ROOT] + + # drop the root + swapw dropw + # => [VALUE] +end + +#! Sets an item in the account storage. Panics if the index is out of bounds. +#! +#! Stack: [index, V'] +#! Output: [R', V] +#! +#! - index is the index of the item to set. +#! - V' is the value to set. +#! - V is the previous value of the item. +#! - R' is the new storage root. +export.set_item + # get the storage root + exec.layout::get_acct_storage_root + # => [R, index, V'] + + # set the item in storage + movup.4 push.STORAGE_TREE_DEPTH mtree_set + # => [V, R'] + + # set the new storage root + swapw exec.layout::set_acct_storage_root + # => [V] +end + +#! Authenticates the proedcure root is part of the account code Merkle treee. Panics if the +#! procedure root is not part of the account code Merkle tree. +#! +#! Stack: [PROC_ROOT] +#! Output: [PROC_ROOT] +#! +#! - PROC_ROOT is the hash of the procedure to authenticate. +export.authenticate_procedure.1 + # load the account code root onto the stack + exec.layout::get_acct_code_root swapw + # => [PROC_ROOT, CODE_ROOT] + + # load the index of the procedure root onto the advice stack + adv.push_mapval adv_push.1 movdn.4 + # => [PROC_ROOT, index, CODE_ROOT] + + # push the depth of the code Merkle tree onto the stack + push.ACCOUNT_CODE_TREE_DEPTH movdn.4 + # => [PROC_ROOT, depth, index, CODE_ROOT] + + # verify the procedure exists in the account code Merkle tree + mtree_verify + # => [PROC_ROOT, depth, index, CODE_ROOT] + + # drop accessory variables + movup.4 drop movup.4 drop swapw dropw + # => [PROC_ROOT] +end diff --git a/miden-lib/asm/sat/account_vault.masm b/miden-lib/asm/sat/internal/account_vault.masm similarity index 94% rename from miden-lib/asm/sat/account_vault.masm rename to miden-lib/asm/sat/internal/account_vault.masm index 59f21ef68..6b70cb160 100644 --- a/miden-lib/asm/sat/account_vault.masm +++ b/miden-lib/asm/sat/internal/account_vault.masm @@ -1,8 +1,8 @@ use.std::collections::smt -use.miden::sat::account -use.miden::sat::asset -use.miden::sat::layout +use.miden::sat::internal::account +use.miden::sat::internal::asset +use.miden::sat::internal::layout #! Returns the balance of a fungible asset associated with a faucet_id. #! Panics if the asset is not a fungible asset. diff --git a/miden-lib/asm/sat/asset.masm b/miden-lib/asm/sat/internal/asset.masm similarity index 98% rename from miden-lib/asm/sat/asset.masm rename to miden-lib/asm/sat/internal/asset.masm index 1e7fc8196..b59cad4d8 100644 --- a/miden-lib/asm/sat/asset.masm +++ b/miden-lib/asm/sat/internal/asset.masm @@ -1,4 +1,4 @@ -use.miden::sat::account +use.miden::sat::internal::account # CONSTANTS # ================================================================================================= diff --git a/miden-lib/asm/sat/constants.masm b/miden-lib/asm/sat/internal/constants.masm similarity index 100% rename from miden-lib/asm/sat/constants.masm rename to miden-lib/asm/sat/internal/constants.masm diff --git a/miden-lib/asm/sat/epilogue.masm b/miden-lib/asm/sat/internal/epilogue.masm similarity index 98% rename from miden-lib/asm/sat/epilogue.masm rename to miden-lib/asm/sat/internal/epilogue.masm index 762ba86a2..15e49fac6 100644 --- a/miden-lib/asm/sat/epilogue.masm +++ b/miden-lib/asm/sat/internal/epilogue.masm @@ -1,5 +1,5 @@ -use.miden::sat::layout -use.miden::sat::account +use.miden::sat::internal::layout +use.miden::sat::internal::account # CONSTANTS # ================================================================================================= @@ -110,7 +110,7 @@ end #! Output: [CREATED_NOTES_COMMITMENT] #! #! - CREATED_NOTES_COMMITMENT is the commitment of the created notes. -export.process_created_notes +export.compute_output_notes_hash # get the number of created notes from memory exec.layout::get_num_created_notes @@ -224,7 +224,7 @@ export.finalize_transaction swapw dropw # compute created note hash - exec.process_created_notes + exec.compute_output_notes_hash # compute the end boundary of the created notes section exec.layout::get_num_created_notes exec.layout::get_created_note_ptr movdn.4 diff --git a/miden-lib/asm/sat/layout.masm b/miden-lib/asm/sat/internal/layout.masm similarity index 99% rename from miden-lib/asm/sat/layout.masm rename to miden-lib/asm/sat/internal/layout.masm index 2e1712953..0b7e4f624 100644 --- a/miden-lib/asm/sat/layout.masm +++ b/miden-lib/asm/sat/internal/layout.masm @@ -1,4 +1,4 @@ -use.miden::sat::constants +use.miden::sat::internal::constants # MEMORY ADDRESS CONSTANTS # ================================================================================================= diff --git a/miden-lib/asm/sat/internal/note.masm b/miden-lib/asm/sat/internal/note.masm new file mode 100644 index 000000000..5fe493a79 --- /dev/null +++ b/miden-lib/asm/sat/internal/note.masm @@ -0,0 +1,137 @@ +use.std::crypto::hashes::native +use.std::mem + +use.miden::sat::internal::layout + +#! Returns the sender of the note currently being processed. Panics if a note is not being +#! processed. +#! +#! Inputs: [] +#! Outputs: [sender] +#! +#! - sender is the sender of the note currently being processed. +export.get_sender + # get the current consumed note pointer + exec.layout::get_current_consumed_note_ptr + # => [ptr] + + # assert the pointer is not zero - this would suggest the procedure has been called from an + # incorrect context + dup neq.0 assert + # => [ptr] + + # get the sender from the note pointer + exec.layout::get_consumed_note_sender + # => [sender] +end + +#! Returns the number of assets and vault hash of the note currently being processed. Panics if a +#! note is not being processed. +#! +#! Inputs: [] +#! Outputs: [num_assets, VAULT_HASH] +#! +#! - num_assets is the number of assets in the note currently being processed. +#! - VAULT_HASH is the vault hash of the note currently being processed. +export.get_vault_data + # get the current consumed note pointer + exec.layout::get_current_consumed_note_ptr + # => [ptr] + + # assert the pointer is not zero - this would suggest the procedure has been called from an + # incorrect context + dup neq.0 assert + # => [ptr] + + # get the number of assets in the note + dup exec.layout::get_consumed_note_num_assets + # => [num_assets, ptr] + + # get the vault hash from the note pointer + swap exec.layout::get_consumed_note_vault_root + # => [VAULT_HASH, num_assets] +end + +#! Writes the assets of the currently executing note into memory starting at the specified address. +#! +#! Inputs: [dest_ptr] +#! Outputs: [num_assets, dest_ptr] +#! +#! - dest_ptr is the memory address to write the assets. +#! - num_assets is the number of assets in the currently executing note. +export.get_assets + # TODO: This needs to be changed to a syscall and moved to user facing api + # get the current consumed note vault hash + exec.get_vault_data + # => [VAULT_HASH, num_assets, dest_ptr] + + # load the vault data from the advice map to the advice stack + adv.push_mapval + # => [VAULT_HASH, num_assets, dest_ptr] + + # calculate number of assets rounded up to an even number + dup.4 dup is_odd add + # => [even_num_assets, VAULT_HASH, num_assets, dest_ptr] + + # calculate the start and end pointer for reading to memory + dup.6 add dup.6 + # => [start_ptr, end_ptr, VAULT_HASH, num_assets, dest_ptr] + + # prepare the stack for reading from the advice stack + padw padw padw + # => [PAD, PAD, PAD, start_ptr, end_ptr, VAULT_HASH, num_assets, dest_ptr] + + # read the assets from advice stack to memory + exec.mem::pipe_double_words_to_memory + # => [PERM, PERM, PERM, end_ptr', end_ptr, VAULT_HASH, num_assets, dest_ptr] + + # extract the digest + exec.native::state_to_digest + # => [DIGEST, end_ptr, end_ptr, VAULT_HASH, num_assets, dest_ptr] + + # drop pointers fro reading from memory + movup.4 drop + # => [DIGEST, VAULT_HASH, num_assets, dest_ptr] + + # assert the vault hash is what we expect + assert_eqw + # => [num_assets, dest_ptr] +end + +#! Increments the number of consumed notes by one. Returns the index of the next note to be consumed. +#! +#! Inputs: [] +#! Outputs: [note_idx] +export.increment_current_consumed_note_idx + # get the current consumed note index + exec.layout::get_current_consumed_note_idx + # => [note_idx] + + # increment the index of the current consumed note and save back to memory + dup add.1 exec.layout::set_current_consumed_note_idx + # => [note_idx] +end + +#! Sets the current consumed note pointer to 0. This should be called after all consumed notes have +#! been processed. +#! +#! Inputs: [] +#! Outputs: [] +export.reset_current_consumed_note_ptr + # get the current consumed note index + exec.layout::get_current_consumed_note_idx + # => [note_idx, ...] + + # if at least one note has been consumed we need to clear the note outputs from the stack + eq.0 not + # => [has_processed_notes, ...] + + if.true + dropw dropw dropw dropw + end + drop + # => [] + + # set the current consumed note pointer to 0 + push.0 exec.layout::set_current_consumed_note_ptr +end diff --git a/miden-lib/asm/sat/note_setup.masm b/miden-lib/asm/sat/internal/note_setup.masm similarity index 96% rename from miden-lib/asm/sat/note_setup.masm rename to miden-lib/asm/sat/internal/note_setup.masm index 4fde03e2f..b529e9cc1 100644 --- a/miden-lib/asm/sat/note_setup.masm +++ b/miden-lib/asm/sat/internal/note_setup.masm @@ -1,5 +1,5 @@ -use.miden::sat::layout -use.miden::sat::note +use.miden::sat::internal::layout +use.miden::sat::internal::note # NOTE SETUP SCRIPT # ================================================================================================= diff --git a/miden-lib/asm/sat/prologue.masm b/miden-lib/asm/sat/internal/prologue.masm similarity index 99% rename from miden-lib/asm/sat/prologue.masm rename to miden-lib/asm/sat/internal/prologue.masm index 89a6fe122..7075bc2b1 100644 --- a/miden-lib/asm/sat/prologue.masm +++ b/miden-lib/asm/sat/internal/prologue.masm @@ -1,7 +1,7 @@ use.std::collections::mmr -use.miden::sat::constants -use.miden::sat::layout +use.miden::sat::internal::constants +use.miden::sat::internal::layout # PUBLIC INPUTS # ================================================================================================= diff --git a/miden-lib/asm/sat/internal/tx.masm b/miden-lib/asm/sat/internal/tx.masm new file mode 100644 index 000000000..1208d0039 --- /dev/null +++ b/miden-lib/asm/sat/internal/tx.masm @@ -0,0 +1,102 @@ +use.miden::sat::internal::account +use.miden::sat::internal::asset +use.miden::sat::internal::constants +use.miden::sat::internal::epilogue +use.miden::sat::internal::layout + +#! Returns the block hash of the last known block at the time of transaction execution. +#! +#! Inputs: [] +#! Outputs: [H] +#! +#! H is the last known block hash. +export.layout::get_blk_hash->get_block_hash + +#! Returns the block number of the last known block at the time of transaction execution. +#! +#! Inputs: [] +#! Outputs: [num] +#! +#! num is the last known block number. +export.layout::get_blk_num->get_block_number + +#! Returns the input notes hash. This is computed as a sequential hash of (nullifier, script_root) +#! tuples over all input notes. +#! +#! Inputs: [] +#! Outputs: [COM] +#! +#! COM is the input notes hash. +export.layout::get_nullifier_com->get_input_notes_hash + +#! Returns the output notes hash. This is computed as a sequential hash of (note_hash, note_metadata) +#! tuples over all output notes. +#! +#! Inputs: [] +#! Outputs: [COM] +#! +#! COM is the output notes hash. +export.epilogue::compute_output_notes_hash->get_output_notes_hash + +#! Increments the number of created notes by one. Returns the index of the next note to be created. +#! +#! Inputs: [] +#! Outputs: [note_idx] +proc.increment_num_created_notes + # get the current number of created notes + exec.layout::get_num_created_notes + # => [note_idx] + + # assert that there is space for a new note + dup exec.constants::get_max_num_created_notes lt assert + # => [note_idx] + + # increment the number of created notes + dup add.1 exec.layout::set_num_created_notes + # => [note_idx] +end + +#! Creates a new note and returns a pointer to the memory address at which the note is stored. +#! +#! Inputs: [ASSET, tag, RECIPIENT] +#! Outputs: [ptr] +#! +#! ASSET is the asset to be included in the note. +#! tag is the tag to be included in the note. +#! RECIPIENT is the recipient of the note. +#! ptr is the pointer to the memory address at which the note is stored. +export.create_note + # validate the asset + exec.asset::validate_asset + # => [ASSET, tag, RECIPIENT] + + # get the index for the next note to be created and increment counter + exec.increment_num_created_notes + # => [note_idx, ASSET, tag, RECIPIENT] + + # get a pointer to the memory address at which the note will be stored + exec.layout::get_created_note_ptr + # => [note_ptr, ASSET, tag, RECIPIENT] + + # populate the metadata + push.1 movup.6 exec.account::get_id push.0 + # => [1, acct_id, tag, 0, note_ptr, ASSET, RECIPIENT] + + # set the metadata for the new created note + dup.4 exec.layout::set_created_note_metadata + # => [note_ptr, ASSET, RECIPIENT] + + movdn.4 padw swapw movup.8 + # => [note_ptr, ASSET, 0, 0, 0, 0, RECIPIENT] + + # add the asset to the note + dup movdn.5 exec.layout::get_created_note_asset_data_ptr mem_storew dropw + # => [note_ptr, 0, 0, 0, 0, RECIPIENT] + + movdn.8 swapw padw swapw movup.12 + # => [note_ptr, RECIPIENT, 0, 0, 0, 0, 0, 0, 0, 0] + + # set the recipient + dup movdn.5 exec.layout::set_created_note_recipient + # => [note_ptr, 0, 0, 0, 0, 0, 0, 0, 0] +end diff --git a/miden-lib/asm/sat/kernel.masm b/miden-lib/asm/sat/kernel.masm index 146a43242..fb5a7ea96 100644 --- a/miden-lib/asm/sat/kernel.masm +++ b/miden-lib/asm/sat/kernel.masm @@ -1,76 +1,114 @@ -use.miden::sat::account -use.miden::sat::account_vault -use.miden::sat::note -use.miden::sat::tx +use.miden::sat::internal::account +use.miden::sat::internal::account_vault +use.miden::sat::internal::note +use.miden::sat::internal::tx -# TODO: change to re-export once supported. #! Returns the account id. #! -#! Stack: [] +#! Stack: [0] #! Output: [acct_id] #! #! - acct_id is the account id. export.get_account_id exec.account::get_id + # => [acct_id, 0] + + swap drop + # => [acct_id] end -# TODO: change to re-export once supported. #! Returns the account nonce. #! -#! Stack: [] +#! Stack: [0] #! Output: [nonce] #! #! - nonce is the account nonce. export.get_account_nonce exec.account::get_nonce + # => [0, nonce] + + swap drop + # => [nonce] end -# TODO: change to re-export once supported. #! Returns the initial account hash. #! -#! Stack: [] +#! Stack: [0, 0, 0, 0] #! Output: [H] #! #! - H is the initial account hash. export.get_initial_account_hash exec.account::get_initial_hash + # => [H, 0, 0, 0, 0] + + swapw dropw + # => [H] end #! Computes and returns the account hash from account data stored in memory. #! -#! Stack: [] +#! Stack: [0, 0, 0, 0] #! Output: [ACCT_HASH] #! #! - ACCT_HASH is the hash of the account data. export.get_current_account_hash exec.account::get_current_hash + # => [ACCT_HASH, 0, 0, 0, 0] + + swapw dropw + # => [ACCT_HASH] end #! Increments the account nonce by the provided value. #! #! Stack: [value] -#! Output: [] +#! Output: [0] #! #! - value is the value to increment the nonce by. value can be at most 2^32 - 1 otherwise this #! procedure panics. export.incr_account_nonce + # AUTHENTICATION + # --------------------------------------------------------------------------------------------- + # get the hash of the caller + padw caller + # => [CALLER, value] + + # make sure the caller is a part of the account interface + exec.account::authenticate_procedure + # => [CALLER, value] + + # drop the caller + dropw + # => [value] + + # KERNEL LOGIC + # --------------------------------------------------------------------------------------------- + + push.0 swap + # => [value, 0] + exec.account::incr_nonce + # => [0] end #! Gets an item from the account storage. Panics if the index is out of bounds. #! -#! Stack: [index] +#! Stack: [index, 0, 0, 0] #! Output: [VALUE] #! #! - index is the index of the item to get. #! - VALUE is the value of the item. export.get_account_item exec.account::get_item + # => [VALUE, 0, 0, 0] + + movup.4 drop movup.4 drop movup.4 drop + # => [VALUE] end #! Sets an item in the account storage. Panics if the index is out of bounds. #! -#! Stack: [index, V'] +#! Stack: [index, V', 0, 0, 0] #! Output: [R', V] #! #! - index is the index of the item to set. @@ -78,18 +116,58 @@ end #! - V is the previous value of the item. #! - R' is the new storage root. export.set_account_item + # AUTHENTICATION + # --------------------------------------------------------------------------------------------- + # get the hash of the caller + padw caller + # => [CALLER, index, V', 0, 0, 0] + + # make sure the caller is a part of the account interface + exec.account::authenticate_procedure + # => [CALLER, index, V', 0, 0, 0] + + # drop the caller + dropw + # => [index, V', 0, 0, 0] + + # KERNEL LOGIC + # --------------------------------------------------------------------------------------------- exec.account::set_item + # => [R', V, 0, 0, 0] + + movup.8 drop movup.8 drop movup.8 drop + # => [R', V] end #! Sets the code of the account the transaction is being executed against. This procedure can only #! executed on regular accounts with updatable code. Otherwise, this procedure fails. #! #! Stack: [CODE_ROOT] -#! Output: [] +#! Output: [0, 0, 0, 0] #! #! - CODE_ROOT is the hash of the code to set. export.set_account_code + # AUTHENTICATION + # --------------------------------------------------------------------------------------------- + # get the hash of the caller + padw caller + # => [CALLER, value] + + # make sure the caller is a part of the account interface + exec.account::authenticate_procedure + # => [CALLER, value] + + # drop the caller + dropw + # => [value] + + # KERNEL LOGIC + # --------------------------------------------------------------------------------------------- + padw swapw + # => [CODE_ROOT, 0, 0, 0, 0] + exec.account::set_code + # => [0, 0, 0, 0] end # TODO: Add vault based procedures (add_asset, remove_asset, mint, burn) @@ -110,82 +188,113 @@ end #! Panics if the ASSET is a fungible asset. #! #! Stack: [ASSET] -#! Output: [has_asset] +#! Output: [has_asset, 0, 0, 0] #! #! - ASSET is the non-fungible asset of interest #! - has_asset is a boolean indicating whether the account vault has the asset of interest export.account_vault_has_non_fungible_asset + push.0 movdn.4 push.0 movdn.4 push.0 movdn.4 + # => [ASSET, 0, 0, 0] + exec.account_vault::has_non_fungible_asset + # => [has_asset, 0, 0, 0] end +# TODO: Refactor `get_note_assets` pattern as described here: +# https://github.com/0xPolygonMiden/miden-base/pull/174#discussion_r1277218200 #! Writes the assets of the currently executing note into memory starting at the specified address. #! -#! Inputs: [dest_ptr] +#! Inputs: [dest_ptr, 0] #! Outputs: [num_assets, dest_ptr] #! #! - dest_ptr is the memory address to write the assets. #! - num_assets is the number of assets in the currently executing note. export.get_note_assets exec.note::get_assets + # => [num_assets, dest_ptr, 0] + + movup.2 drop + # => [num_assets, dest_ptr] end #! Returns the sender of the note currently being processed. Panics if a note is not being #! processed. #! -#! Inputs: [] +#! Inputs: [0] #! Outputs: [sender] #! #! - sender is the sender of the note currently being processed. export.get_note_sender exec.note::get_sender + # => [sender, 0] + + swap drop + # => [sender] + end #! Returns the block number of the last known block at the time of transaction execution. #! -#! Inputs: [] +#! Inputs: [0] #! Outputs: [num] #! #! num is the last known block number. export.get_block_number exec.tx::get_block_number + # => [num, 0] + + swap drop + # => [num] end #! Returns the block hash of the last known block at the time of transaction execution. #! -#! Inputs: [] +#! Inputs: [0, 0, 0, 0] #! Outputs: [H] #! #! H is the last known block hash. export.get_block_hash exec.tx::get_block_hash + # => [H, 0, 0, 0, 0] + + swapw dropw + # => [H] end #! Returns the input notes hash. This is computed as a sequential hash of (nullifier, script_root) #! tuples over all input notes. #! -#! Inputs: [] +#! Inputs: [0, 0, 0, 0] #! Outputs: [COM] #! #! COM is the input notes hash. export.get_input_notes_hash exec.tx::get_input_notes_hash + # => [COM, 0, 0, 0, 0] + + swapw dropw + # => [COM] end #! Returns the output notes hash. This is computed as a sequential hash of (note_hash, note_metadata) #! tuples over all output notes. #! -#! Inputs: [] +#! Inputs: [0, 0, 0, 0] #! Outputs: [COM] #! #! COM is the output notes hash. export.get_output_notes_hash exec.tx::get_output_notes_hash + # => [COM, 0, 0, 0, 0] + + swapw dropw + # => [COM] end #! Creates a new note and returns a pointer to the memory address at which the note is stored. #! #! Inputs: [ASSET, tag, RECIPIENT] -#! Outputs: [ptr] +#! Outputs: [ptr, 0, 0, 0, 0, 0, 0, 0, 0] #! #! ASSET is the asset to be included in the note. #! tag is the tag to be included in the note. diff --git a/miden-lib/asm/sat/note.masm b/miden-lib/asm/sat/note.masm index f0d9164b3..1a214e729 100644 --- a/miden-lib/asm/sat/note.masm +++ b/miden-lib/asm/sat/note.masm @@ -1,57 +1,3 @@ -use.std::crypto::hashes::native -use.std::mem - -use.miden::sat::layout - -#! Returns the sender of the note currently being processed. Panics if a note is not being -#! processed. -#! -#! Inputs: [] -#! Outputs: [sender] -#! -#! - sender is the sender of the note currently being processed. -export.get_sender - # get the current consumed note pointer - exec.layout::get_current_consumed_note_ptr - # => [ptr] - - # assert the pointer is not zero - this would suggest the procedure has been called from an - # incorrect context - dup neq.0 assert - # => [ptr] - - # get the sender from the note pointer - exec.layout::get_consumed_note_sender - # => [sender] -end - -#! Returns the number of assets and vault hash of the note currently being processed. Panics if a -#! note is not being processed. -#! -#! Inputs: [] -#! Outputs: [num_assets, VAULT_HASH] -#! -#! - num_assets is the number of assets in the note currently being processed. -#! - VAULT_HASH is the vault hash of the note currently being processed. -export.get_vault_data - # get the current consumed note pointer - exec.layout::get_current_consumed_note_ptr - # => [ptr] - - # assert the pointer is not zero - this would suggest the procedure has been called from an - # incorrect context - dup neq.0 assert - # => [ptr] - - # get the number of assets in the note - dup exec.layout::get_consumed_note_num_assets - # => [num_assets, ptr] - - # get the vault hash from the note pointer - swap exec.layout::get_consumed_note_vault_root - # => [VAULT_HASH, num_assets] -end - #! Writes the assets of the currently executing note into memory starting at the specified address. #! #! Inputs: [dest_ptr] @@ -60,78 +6,24 @@ end #! - dest_ptr is the memory address to write the assets. #! - num_assets is the number of assets in the currently executing note. export.get_assets - # TODO: This needs to be changed to a syscall and moved to user facing api - # get the current consumed note vault hash - exec.get_vault_data - # => [VAULT_HASH, num_assets, dest_ptr] - - # load the vault data from the advice map to the advice stack - adv.push_mapval - # => [VAULT_HASH, num_assets, dest_ptr] - - # calculate number of assets rounded up to an even number - dup.4 dup is_odd add - # => [even_num_assets, VAULT_HASH, num_assets, dest_ptr] - - # calculate the start and end pointer for reading to memory - dup.6 add dup.6 - # => [start_ptr, end_ptr, VAULT_HASH, num_assets, dest_ptr] - - # prepare the stack for reading from the advice stack - padw padw padw - # => [PAD, PAD, PAD, start_ptr, end_ptr, VAULT_HASH, num_assets, dest_ptr] + push.0 swap + # => [dest_ptr, 0] - # read the assets from advice stack to memory - exec.mem::pipe_double_words_to_memory - # => [PERM, PERM, PERM, end_ptr', end_ptr, VAULT_HASH, num_assets, dest_ptr] - - # extract the digest - exec.native::state_to_digest - # => [DIGEST, end_ptr, end_ptr, VAULT_HASH, num_assets, dest_ptr] - - # drop pointers fro reading from memory - movup.4 drop - # => [DIGEST, VAULT_HASH, num_assets, dest_ptr] - - # assert the vault hash is what we expect - assert_eqw + syscall.get_note_assets # => [num_assets, dest_ptr] end -#! Increments the number of consumed notes by one. Returns the index of the next note to be consumed. +#! Returns the sender of the note currently being processed. Panics if a note is not being +#! processed. #! #! Inputs: [] -#! Outputs: [note_idx] -export.increment_current_consumed_note_idx - # get the current consumed note index - exec.layout::get_current_consumed_note_idx - # => [note_idx] - - # increment the index of the current consumed note and save back to memory - dup add.1 exec.layout::set_current_consumed_note_idx - # => [note_idx] -end - -#! Sets the current consumed note pointer to 0. This should be called after all consumed notes have -#! been processed. +#! Outputs: [sender] #! -#! Inputs: [] -#! Outputs: [] -export.reset_current_consumed_note_ptr - # get the current consumed note index - exec.layout::get_current_consumed_note_idx - # => [note_idx, ...] - - # if at least one note has been consumed we need to clear the note outputs from the stack - eq.0 not - # => [has_processed_notes, ...] - - if.true - dropw dropw dropw dropw - end - drop - # => [] +#! - sender is the sender of the note currently being processed. +export.get_sender + push.0 + # => [0] - # set the current consumed note pointer to 0 - push.0 exec.layout::set_current_consumed_note_ptr -end + syscall.get_note_sender + # => [sender] +end \ No newline at end of file diff --git a/miden-lib/asm/sat/tx.masm b/miden-lib/asm/sat/tx.masm index 245c011b1..e8e13fde1 100644 --- a/miden-lib/asm/sat/tx.masm +++ b/miden-lib/asm/sat/tx.masm @@ -1,8 +1,16 @@ -use.miden::sat::account -use.miden::sat::asset -use.miden::sat::constants -use.miden::sat::epilogue -use.miden::sat::layout +#! Returns the block number of the last known block at the time of transaction execution. +#! +#! Inputs: [] +#! Outputs: [num] +#! +#! num is the last known block number. +export.get_block_number + push.0 + # => [0] + + syscall.get_block_number + # => [num] +end #! Returns the block hash of the last known block at the time of transaction execution. #! @@ -11,17 +19,11 @@ use.miden::sat::layout #! #! H is the last known block hash. export.get_block_hash - exec.layout::get_blk_hash -end + padw + # => [0, 0, 0, 0] -#! Returns the block number of the last known block at the time of transaction execution. -#! -#! Inputs: [] -#! Outputs: [num] -#! -#! num is the last known block number. -export.get_block_number - exec.layout::get_blk_num + syscall.get_block_hash + # => [H] end #! Returns the input notes hash. This is computed as a sequential hash of (nullifier, script_root) @@ -32,36 +34,26 @@ end #! #! COM is the input notes hash. export.get_input_notes_hash - exec.layout::get_nullifier_com + padw + # => [0, 0, 0, 0] + + syscall.get_input_notes_hash + # => [COM] end #! Returns the output notes hash. This is computed as a sequential hash of (note_hash, note_metadata) #! tuples over all output notes. #! -#! Inputs: [] +#! Inputs: [0, 0, 0, 0] #! Outputs: [COM] #! #! COM is the output notes hash. export.get_output_notes_hash - exec.epilogue::process_created_notes -end - -#! Increments the number of created notes by one. Returns the index of the next note to be created. -#! -#! Inputs: [] -#! Outputs: [note_idx] -proc.increment_num_created_notes - # get the current number of created notes - exec.layout::get_num_created_notes - # => [note_idx] - - # assert that there is space for a new note - dup exec.constants::get_max_num_created_notes lt assert - # => [note_idx] + padw + # => [0, 0, 0, 0] - # increment the number of created notes - dup add.1 exec.layout::set_num_created_notes - # => [note_idx] + syscall.get_output_notes_hash + # => [COM] end #! Creates a new note and returns a pointer to the memory address at which the note is stored. @@ -74,31 +66,9 @@ end #! RECIPIENT is the recipient of the note. #! ptr is the pointer to the memory address at which the note is stored. export.create_note - # validate the asset - exec.asset::validate_asset - # => [ASSET, tag, RECIPIENT] - - # get the index for the next note to be created and increment counter - exec.increment_num_created_notes - # => [note_idx, ASSET, tag, RECIPIENT] - - # get a pointer to the memory address at which the note will be stored - exec.layout::get_created_note_ptr - # => [note_ptr, ASSET, tag, RECIPIENT] - - # populate the metadata - push.1 movup.6 exec.account::get_id push.0 - # => [1, acct_id, tag, 0, note_ptr, ASSET, RECIPIENT] - - # set the metadata for the new created note - dup.4 exec.layout::set_created_note_metadata - # => [note_ptr, ASSET, RECIPIENT] - - # add the asset to the note - dup movdn.5 exec.layout::get_created_note_asset_data_ptr mem_storew dropw - # => [note_ptr, RECIPIENT] + syscall.create_note + # => [ptr, 0, 0, 0, 0, 0, 0, 0, 0] - # set the recipient - dup movdn.5 exec.layout::set_created_note_recipient - # => [note_ptr] + movdn.8 dropw dropw + # => [ptr] end diff --git a/miden-lib/src/lib.rs b/miden-lib/src/lib.rs index c1cf359f8..5ad203b95 100644 --- a/miden-lib/src/lib.rs +++ b/miden-lib/src/lib.rs @@ -60,7 +60,7 @@ impl SatKernel { /// Returns masm source code which encodes the transaction kernel prologue. pub fn prologue() -> &'static str { "\ - use.miden::sat::prologue + use.miden::sat::internal::prologue begin exec.prologue::prepare_transaction @@ -71,7 +71,7 @@ impl SatKernel { /// Returns masm source code which encodes the transaction kernel epilogue. pub fn epilogue() -> &'static str { "\ - use.miden::sat::epilogue + use.miden::sat::internal::epilogue begin exec.epilogue::finalize_transaction @@ -81,7 +81,7 @@ impl SatKernel { /// Returns masm source code which encodes the transaction kernel note setup script. pub fn note_setup() -> &'static str { "\ - use.miden::sat::note_setup + use.miden::sat::internal::note_setup begin exec.note_setup::prepare_note @@ -92,7 +92,7 @@ impl SatKernel { /// Returns masm source code which encodes the transaction kernel note teardown script. pub fn note_processing_teardown() -> &'static str { "\ - use.miden::sat::note + use.miden::sat::internal::note begin exec.note::reset_current_consumed_note_ptr @@ -106,7 +106,7 @@ impl SatKernel { #[test] fn test_compile() { - let path = "miden::sat::layout::get_consumed_note_ptr"; + let path = "miden::sat::internal::layout::get_consumed_note_ptr"; let miden = MidenLib::default(); let exists = miden.modules().any(|module| { module diff --git a/miden-lib/tests/assets/test.masm b/miden-lib/tests/assets/test.masm index 999c3baf6..b8d1f99d4 100644 --- a/miden-lib/tests/assets/test.masm +++ b/miden-lib/tests/assets/test.masm @@ -76,5 +76,5 @@ begin exec.prologue::prepare_transaction exec.create_mock_notes breakpoint - exec.epilogue::process_created_notes + exec.epilogue::compute_output_notes_hash end diff --git a/miden-lib/tests/common/mod.rs b/miden-lib/tests/common/mod.rs index 5753bd494..5b47d298d 100644 --- a/miden-lib/tests/common/mod.rs +++ b/miden-lib/tests/common/mod.rs @@ -1,4 +1,3 @@ -use assembly::Assembler; pub use crypto::{ hash::rpo::{Rpo256 as Hasher, RpoDigest as Digest}, merkle::{MerkleStore, NodeIndex, SimpleSmt}, @@ -8,12 +7,12 @@ pub use miden_lib::{memory, MidenLib, SatKernel}; pub use miden_objects::{ assets::{Asset, FungibleAsset, NonFungibleAsset, NonFungibleAssetDetails}, mock as data, + mock::assembler, notes::{Note, NoteInclusionProof, NoteScript, NoteVault, NOTE_LEAF_DEPTH, NOTE_TREE_DEPTH}, transaction::{ExecutedTransaction, PreparedTransaction, ProvenTransaction}, Account, AccountCode, AccountId, AccountStorage, AccountType, AccountVault, BlockHeader, ChainMmr, StorageItem, }; -use miden_stdlib::StdLibrary; pub use processor::{ math::Felt, AdviceProvider, ExecutionError, ExecutionOptions, MemAdviceProvider, Process, Program, StackInputs, Word, @@ -22,7 +21,7 @@ use std::{env, fs::File, io::Read, path::Path}; pub mod procedures; -pub const TX_KERNEL_DIR: &str = "sat"; +pub const TX_KERNEL_DIR: &str = "sat/internal"; // TEST BRACE // ================================================================================================ @@ -43,11 +42,14 @@ pub fn load_file_with_code(imports: &str, code: &str, dir: &str, file: &str) -> pub fn run_tx( program: Program, stack_inputs: StackInputs, - adv: A, + mut adv: A, ) -> Result, ExecutionError> where A: AdviceProvider, { + // mock account method for testing from root context + adv.insert_into_map(Word::default(), vec![Felt::new(255)]).unwrap(); + let mut process = Process::new(program.kernel().clone(), stack_inputs, adv, ExecutionOptions::default()); process.execute(&program)?; @@ -88,14 +90,6 @@ pub fn consumed_note_data_ptr(note_idx: u32) -> memory::MemoryAddress { memory::CONSUMED_NOTE_SECTION_OFFSET + (1 + note_idx) * 1024 } -pub fn assembler() -> Assembler { - assembly::Assembler::default() - .with_library(&MidenLib::default()) - .expect("failed to load miden-lib") - .with_library(&StdLibrary::default()) - .expect("failed to load std-lib") -} - pub fn prepare_transaction( account: Account, block_header: BlockHeader, diff --git a/miden-lib/tests/test_account.rs b/miden-lib/tests/test_account.rs index ef887bb69..2bffe5e3c 100644 --- a/miden-lib/tests/test_account.rs +++ b/miden-lib/tests/test_account.rs @@ -28,7 +28,7 @@ pub fn test_set_code_is_not_immediate() { let (account, block_header, chain, notes) = mock_inputs(); let code = " - use.miden::sat::prologue + use.miden::sat::internal::prologue use.miden::sat::account begin exec.prologue::prepare_transaction @@ -66,8 +66,8 @@ pub fn test_set_code_succeeds() { let code = " use.miden::sat::account - use.miden::sat::prologue - use.miden::sat::epilogue + use.miden::sat::internal::prologue + use.miden::sat::internal::epilogue begin exec.prologue::prepare_transaction @@ -120,8 +120,8 @@ pub fn test_account_type() { let code = format!( " - use.miden::sat::layout - use.miden::sat::account + use.miden::sat::internal::layout + use.miden::sat::internal::account begin exec.account::{} @@ -154,7 +154,7 @@ pub fn test_account_type() { fn test_validate_id_fails_on_insuficcient_ones() { let code = format!( " - use.miden::sat::account + use.miden::sat::internal::account begin push.{ACCOUNT_ID_INSUFFICIENT_ONES} @@ -183,7 +183,7 @@ fn test_get_item() { let code = format!( " use.miden::sat::account - use.miden::sat::prologue + use.miden::sat::internal::prologue begin @@ -223,7 +223,7 @@ fn test_get_child_tree_item() { let code = format!( " use.miden::sat::account - use.miden::sat::prologue + use.miden::sat::internal::prologue begin # prepare the transaction @@ -276,8 +276,8 @@ fn test_set_item() { let code = format!( " use.miden::sat::account - use.miden::sat::layout - use.miden::sat::prologue + use.miden::sat::internal::layout + use.miden::sat::internal::prologue begin # prepare the transaction @@ -332,7 +332,7 @@ fn test_is_faucet_procedure() { // assembly codes that checks if an account is a fauct let code = format!( " - use.miden::sat::account + use.miden::sat::internal::account begin # push the account id on to the stack @@ -368,7 +368,7 @@ fn test_authenticate_procedure() { let test_cases = vec![ (account.code().procedure_tree().get_leaf(0).unwrap(), true), (account.code().procedure_tree().get_leaf(1).unwrap(), true), - (Word::default(), false), + ([ONE, ZERO, ONE, ZERO], false), ]; for (root, valid) in test_cases.into_iter() { @@ -376,8 +376,8 @@ fn test_authenticate_procedure() { let code = format!( "\ - use.miden::sat::account - use.miden::sat::prologue + use.miden::sat::internal::account + use.miden::sat::internal::prologue begin # prepare the transaction diff --git a/miden-lib/tests/test_asset_vault.rs b/miden-lib/tests/test_asset_vault.rs index 05b9685c8..83cc773aa 100644 --- a/miden-lib/tests/test_asset_vault.rs +++ b/miden-lib/tests/test_asset_vault.rs @@ -16,13 +16,13 @@ fn test_get_balance() { let faucet_id: AccountId = ACCOUNT_ID_FUNGIBLE_FAUCET_ON_CHAIN.try_into().unwrap(); let code = format!( " - use.miden::sat::prologue - use.miden::sat::account_vault + use.miden::sat::internal::prologue + use.miden::sat::account begin exec.prologue::prepare_transaction push.{ACCOUNT_ID_FUNGIBLE_FAUCET_ON_CHAIN} - exec.account_vault::get_balance + exec.account::get_balance end " ); @@ -49,13 +49,13 @@ fn test_get_balance_non_fungible_fails() { let code = format!( " - use.miden::sat::prologue - use.miden::sat::account_vault + use.miden::sat::internal::prologue + use.miden::sat::account begin exec.prologue::prepare_transaction push.{ACCOUNT_ID_NON_FUNGIBLE_FAUCET_ON_CHAIN} - exec.account_vault::get_balance + exec.account::get_balance end " ); @@ -79,13 +79,13 @@ fn test_has_non_fungible_asset() { let code = format!( " - use.miden::sat::prologue - use.miden::sat::account_vault + use.miden::sat::internal::prologue + use.miden::sat::account begin exec.prologue::prepare_transaction push.{non_fungible_asset_key} - exec.account_vault::has_non_fungible_asset + exec.account::has_non_fungible_asset end ", non_fungible_asset_key = prepare_word(&non_fungible_asset.vault_key()) diff --git a/miden-lib/tests/test_epilogue.rs b/miden-lib/tests/test_epilogue.rs index df9c52fbe..9cacef818 100644 --- a/miden-lib/tests/test_epilogue.rs +++ b/miden-lib/tests/test_epilogue.rs @@ -15,7 +15,7 @@ fn test_epilogue() { let created_notes_data_procedure = created_notes_data_procedure(executed_transaction.created_notes()); - let imports = "use.miden::sat::prologue\n"; + let imports = "use.miden::sat::internal::prologue\n"; let code = format!( " {created_notes_data_procedure} diff --git a/miden-lib/tests/test_note.rs b/miden-lib/tests/test_note.rs index aaf4f2eb2..6bb21c6ff 100644 --- a/miden-lib/tests/test_note.rs +++ b/miden-lib/tests/test_note.rs @@ -10,8 +10,8 @@ fn test_get_sender_no_sender() { // calling get_sender should return sender let code = " - use.miden::sat::prologue - use.miden::sat::note_setup + use.miden::sat::internal::prologue + use.miden::sat::internal::note_setup use.miden::sat::note begin @@ -37,8 +37,8 @@ fn test_get_sender() { // calling get_sender should return sender let code = " - use.miden::sat::prologue - use.miden::sat::note_setup + use.miden::sat::internal::prologue + use.miden::sat::internal::note_setup use.miden::sat::note begin @@ -70,10 +70,10 @@ fn test_get_vault_data() { // calling get_vault_data should return vault data let code = format!( " - use.miden::sat::prologue - use.miden::sat::note_setup - use.miden::sat::note - use.miden::sat::layout + use.miden::sat::internal::prologue + use.miden::sat::internal::note_setup + use.miden::sat::internal::note + use.miden::sat::internal::layout begin exec.prologue::prepare_transaction @@ -133,8 +133,8 @@ fn test_get_assets() { // calling get_assets should return assets at the specified address let code = format!( " - use.miden::sat::prologue - use.miden::sat::note_setup + use.miden::sat::internal::prologue + use.miden::sat::internal::note_setup use.miden::sat::note begin diff --git a/miden-lib/tests/test_note_setup.rs b/miden-lib/tests/test_note_setup.rs index 568509b68..04b675153 100644 --- a/miden-lib/tests/test_note_setup.rs +++ b/miden-lib/tests/test_note_setup.rs @@ -12,7 +12,7 @@ const NOTE_SETUP_FILE: &str = "note_setup.masm"; fn test_note_setup() { let (account, block_header, chain, notes) = mock_inputs(); - let imports = "use.miden::sat::prologue\n"; + let imports = "use.miden::sat::internal::prologue\n"; let code = " begin exec.prologue::prepare_transaction diff --git a/miden-lib/tests/test_tx.rs b/miden-lib/tests/test_tx.rs index c9c6dd970..63c99cf60 100644 --- a/miden-lib/tests/test_tx.rs +++ b/miden-lib/tests/test_tx.rs @@ -84,8 +84,8 @@ fn test_create_note_too_many_notes() { let code = format!( " - use.miden::sat::constants - use.miden::sat::layout + use.miden::sat::internal::constants + use.miden::sat::internal::layout use.miden::sat::tx begin diff --git a/miden-tx/src/executor/mod.rs b/miden-tx/src/executor/mod.rs index 0b772e24d..11f83aad7 100644 --- a/miden-tx/src/executor/mod.rs +++ b/miden-tx/src/executor/mod.rs @@ -1,5 +1,5 @@ use super::{ - AccountCode, AccountId, DataStore, Digest, ModuleAst, NoteOrigin, NoteScript, NoteTarget, + AccountCode, AccountId, DataStore, Digest, NoteOrigin, NoteScript, NoteTarget, PreparedTransaction, ProgramAst, RecAdviceProvider, TransactionComplier, TransactionExecutorError, TransactionResult, }; diff --git a/miden-tx/src/prover/mod.rs b/miden-tx/src/prover/mod.rs deleted file mode 100644 index 8b1378917..000000000 --- a/miden-tx/src/prover/mod.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/miden-tx/src/tests.rs b/miden-tx/src/tests.rs index 6ed8f0323..3c6879b2f 100644 --- a/miden-tx/src/tests.rs +++ b/miden-tx/src/tests.rs @@ -6,7 +6,7 @@ use assembly::{ ast::{ModuleAst, ProgramAst}, Assembler, }; -use crypto::StarkField; +use crypto::{StarkField, ONE}; use miden_objects::{ mock::{ mock_inputs, prepare_word, CHILD_ROOT_PARENT_LEAF_INDEX, CHILD_SMT_DEPTH, @@ -135,45 +135,77 @@ fn test_transaction_result_account_delta() { // TODO: This currently has some problems due to stack management when context switching: https://github.com/0xPolygonMiden/miden-base/issues/173 let tx_script = format!( "\ + use.context::account_{account_id} + use.miden::sat::account + + ## ACCOUNT PROCEDURE WRAPPERS + ## ======================================================================================== + #TODO: Move this into an account library + proc.set_item + push.0 movdn.5 push.0 movdn.5 push.0 movdn.5 + # => [index, V', 0, 0, 0] + + call.account_{account_id}::set_item + # => [R', V] + end + + proc.set_code + call.account_{account_id}::set_code + # => [0, 0, 0, 0] + + dropw + # => [] + end + + proc.incr_nonce + call.account_{account_id}::incr_nonce + # => [0] + + drop + # => [] + end + + ## TRANSACTION SCRIPT + ## ======================================================================================== begin ## Update account storage child tree ## ------------------------------------------------------------------------------------ # get the current child tree root from account storage slot - push.{CHILD_ROOT_PARENT_LEAF_INDEX} drop + push.{CHILD_ROOT_PARENT_LEAF_INDEX} # => [idx] # get the child root - #syscall.get_account_item dropw dropw dropw + exec.account::get_item # => [CHILD_ROOT] - # prepare the stack to add a new value to the child tree - #padw swapw push.0 push.{CHILD_SMT_DEPTH} + # prepare the stack to remove in the child tree + padw swapw push.0 push.{CHILD_SMT_DEPTH} # => [depth, idx(push.0), CHILD_ROOT, NEW_VALUE (padw)] # set new value and drop old value - #mtree_set dropw + mtree_set dropw # => [NEW_CHILD_ROOT] # prepare stack to delete existing child tree value (replace with empty word) - #padw swapw push.{CHILD_STORAGE_INDEX_0} push.{CHILD_SMT_DEPTH} + padw swapw push.{CHILD_STORAGE_INDEX_0} push.{CHILD_SMT_DEPTH} # => [depth, idx, NEW_CHILD_ROOT, EMPTY_WORD] # set existing value to empty word - #mtree_set dropw + mtree_set dropw # => [NEW_CHILD_ROOT] # store the new child root in account storage slot - #push.{CHILD_ROOT_PARENT_LEAF_INDEX} syscall.set_account_item dropw dropw + push.{CHILD_ROOT_PARENT_LEAF_INDEX} exec.set_item dropw dropw # => [] ## Update account code ## ------------------------------------------------------------------------------------ - push.{NEW_ACCOUNT_ROOT} syscall.set_account_code dropw + push.{NEW_ACCOUNT_ROOT} exec.set_code # => [] ## Update the account nonce ## ------------------------------------------------------------------------------------ - push.1 syscall.incr_account_nonce drop + push.1 exec.incr_nonce end ", NEW_ACCOUNT_ROOT = prepare_word(&*new_acct_code.root()) @@ -191,9 +223,25 @@ fn test_transaction_result_account_delta() { .map(|note| note.proof().as_ref().unwrap().origin().clone()) .collect::>(); + // expected delta + // execute the transaction and get the witness let transaction_result = executor .execute_transaction(account_id, block_ref, ¬e_origins, Some(tx_script)) .unwrap(); - println!("account delta {:?}", transaction_result.account_delta()); + + // nonce delta + assert!(transaction_result.account_delta().nonce == Some(ONE)); + + // storage delta + assert_eq!(transaction_result.account_delta().storage.slots_delta.updated_slots().len(), 1); + assert_eq!( + transaction_result.account_delta().storage.slots_delta.updated_slots()[0].0, + CHILD_ROOT_PARENT_LEAF_INDEX as u64 + ); + assert_eq!(transaction_result.account_delta().storage.store_delta.0.len(), 1); + assert_eq!( + transaction_result.account_delta().storage.store_delta.0[0].1.cleared_slots()[0], + CHILD_STORAGE_INDEX_0 + ); } diff --git a/objects/src/accounts/account_id.rs b/objects/src/accounts/account_id.rs index 29f7bba2c..da1f852ac 100644 --- a/objects/src/accounts/account_id.rs +++ b/objects/src/accounts/account_id.rs @@ -26,7 +26,7 @@ pub enum AccountType { /// - 0 - full account data is stored on-chain. /// - 1 - only the account hash is stored on-chain which serves as a commitment to the account state. /// As such the three most significant bits fully describes the type of the account. -#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct AccountId(Felt); impl AccountId { diff --git a/objects/src/accounts/code.rs b/objects/src/accounts/code.rs index a55b923c9..37e76485f 100644 --- a/objects/src/accounts/code.rs +++ b/objects/src/accounts/code.rs @@ -1,8 +1,8 @@ use super::{ AccountError, AccountId, Assembler, AssemblyContext, AssemblyContextType, Digest, LibraryPath, - Module, ModuleAst, TryApplyDiff, Vec, + Module, ModuleAst, Vec, }; -use crypto::merkle::{SimpleSmt, StoreNode}; +use crypto::merkle::SimpleSmt; // ACCOUNT CODE // ================================================================================================ @@ -118,21 +118,3 @@ impl AccountCode { self.procedures.binary_search_by(|x| x.as_bytes().cmp(&root_bytes)).ok() } } - -// DIFF -// ================================================================================================ -impl TryApplyDiff for AccountCode { - type DiffType = Option; - type Error = AccountError; - - fn try_apply(&mut self, diff: Option) -> Result<(), Self::Error> { - if let Some(module) = diff { - // TODO: Consider introducing a TryApplyDiff variant that returns Result<(), Error> - let code = AccountCode::new(AccountId::default(), module, &Assembler::default())?; - self.module = code.module; - self.procedures = code.procedures; - self.procedure_tree = code.procedure_tree; - } - Ok(()) - } -} diff --git a/objects/src/mock.rs b/objects/src/mock.rs index 04fc3fb2a..5eda7cd96 100644 --- a/objects/src/mock.rs +++ b/objects/src/mock.rs @@ -170,6 +170,32 @@ pub fn mock_account( Some(code) => code, None => { let account_code = "\ + use.miden::sat::account + + export.incr_nonce + push.0 swap + # => [value, 0] + + exec.account::incr_nonce + # => [0] + end + + export.set_item + exec.account::set_item + # => [R', V, 0, 0, 0] + + movup.8 drop movup.8 drop movup.8 drop + # => [R', V] + end + + export.set_code + padw swapw + # => [CODE_ROOT, 0, 0, 0, 0] + + exec.account::set_code + # => [0, 0, 0, 0] + end + export.account_procedure_1 push.1.2 add @@ -282,25 +308,22 @@ pub fn mock_consumed_notes(assembler: &mut Assembler, created_notes: &[Note]) -> // create note 1 script let note_1_script_src = format!( "\ + use.miden::sat::tx + begin # create note 0 push.{created_note_0_recipient} push.{created_note_0_tag} push.{created_note_0_asset} - syscall.create_note - - # drop the returned pointer (TODO: Investigate why stack overflow is happening - # without dropw dropw - maybe something to do with syscall) - drop dropw dropw + exec.tx::create_note + drop # create note 1 push.{created_note_1_recipient} push.{created_note_1_tag} push.{created_note_1_asset} - syscall.create_note - - # drop the returned pointer - drop dropw dropw + exec.tx::create_note + drop end ", created_note_0_recipient = prepare_word(&created_notes[0].recipient()), @@ -316,7 +339,7 @@ pub fn mock_consumed_notes(assembler: &mut Assembler, created_notes: &[Note]) -> // create note 2 script let note_2_script_src = format!( "\ - use.miden::sat::kernel + use.miden::sat::tx begin @@ -324,11 +347,8 @@ pub fn mock_consumed_notes(assembler: &mut Assembler, created_notes: &[Note]) -> push.{created_note_2_recipient} push.{created_note_2_tag} push.{created_note_2_asset} - syscall.create_note - - - # drop the returned pointer - drop dropw dropw + exec.tx::create_note + drop end ", created_note_2_recipient = prepare_word(&created_notes[2].recipient()),