From 8c9dd831d35f6b2671ca8f9c478bbd1e3455058d Mon Sep 17 00:00:00 2001 From: frisitano Date: Wed, 25 Oct 2023 17:08:50 +0800 Subject: [PATCH] feat: introduce host event handling --- miden-lib/asm/sat/kernel.masm | 20 +++ miden-lib/src/transaction/account_stub.rs | 84 +--------- miden-lib/src/transaction/mod.rs | 4 +- miden-tx/src/executor/mod.rs | 22 ++- miden-tx/src/host/event/mod.rs | 44 ++++++ miden-tx/src/host/event/vault_delta.rs | 178 ++++++++++++++++++++++ miden-tx/src/host/mod.rs | 63 ++++++++ miden-tx/src/lib.rs | 3 + miden-tx/src/prover/mod.rs | 14 +- miden-tx/src/tests.rs | 9 +- mock/src/constants.rs | 50 +++--- mock/src/mock/account.rs | 2 +- objects/src/accounts/account_id.rs | 6 +- objects/src/accounts/seed.rs | 2 +- objects/src/assets/fungible.rs | 2 +- objects/src/assets/mod.rs | 2 +- objects/src/assets/nonfungible.rs | 8 +- objects/src/transaction/event.rs | 24 +++ objects/src/transaction/mod.rs | 2 + 19 files changed, 401 insertions(+), 138 deletions(-) create mode 100644 miden-tx/src/host/event/mod.rs create mode 100644 miden-tx/src/host/event/vault_delta.rs create mode 100644 miden-tx/src/host/mod.rs create mode 100644 objects/src/transaction/event.rs diff --git a/miden-lib/asm/sat/kernel.masm b/miden-lib/asm/sat/kernel.masm index b981f1313..cdf3a45bc 100644 --- a/miden-lib/asm/sat/kernel.masm +++ b/miden-lib/asm/sat/kernel.masm @@ -5,6 +5,16 @@ use.miden::sat::internal::layout use.miden::sat::internal::note use.miden::sat::internal::tx +# EVENTS +# ================================================================================================= + +# Event emitted to signal that an asset is being added to the account vault. +const.ADD_ASSET_TO_ACCOUNT_VAULT_EVENT=131072 + +# Event emitted to signal that an asset is being removed from the account vault. +const.REMOVE_ASSET_FROM_ACCOUNT_VAULT_EVENT=131073 + + # AUTHENTICATION # ================================================================================================= @@ -233,6 +243,11 @@ end #! - If ASSET is a fungible asset, then ASSET' is the total fungible asset in the account vault #! after ASSET was added to it. export.account_vault_add_asset + # TODO: we execute `push.1 drop` before `emit` as decorators are not supported without other + # instructions - see: https://github.com/0xPolygonMiden/miden-vm/issues/1122 + # emit event to signal that an asset is being added to the account vault + push.1 drop emit.ADD_ASSET_TO_ACCOUNT_VAULT_EVENT + # authenticate that the procedure invocation originates from the account context exec.authenticate_account_origin # => [ASSET] @@ -258,6 +273,11 @@ end #! #! - ASSET is the asset to remove from the vault. export.account_vault_remove_asset + # TODO: we execute `push.1 drop` before `emit` as decorators are not supported without other + # instructions - see: https://github.com/0xPolygonMiden/miden-vm/issues/1122 + # emit event to signal that an asset is being removed from the account vault + push.1 drop emit.REMOVE_ASSET_FROM_ACCOUNT_VAULT_EVENT + # authenticate that the procedure invocation originates from the account context exec.authenticate_account_origin # => [ASSET] diff --git a/miden-lib/src/transaction/account_stub.rs b/miden-lib/src/transaction/account_stub.rs index c615c26da..6bf2f57fb 100644 --- a/miden-lib/src/transaction/account_stub.rs +++ b/miden-lib/src/transaction/account_stub.rs @@ -3,17 +3,11 @@ use crate::memory::{ ACCT_NONCE_IDX, ACCT_STORAGE_ROOT_OFFSET, ACCT_VAULT_ROOT_OFFSET, }; use miden_objects::{ - accounts::{ - Account, AccountId, AccountStorage, AccountStorageDelta, AccountStub, AccountVaultDelta, - }, - assets::Asset, + accounts::{Account, AccountId, AccountStorage, AccountStorageDelta, AccountStub}, crypto::merkle::{merkle_tree_delta, MerkleStore, MerkleStoreDelta, NodeIndex}, transaction::FinalAccountStub, - utils::{ - collections::{BTreeMap, Diff, Vec}, - vec, - }, - AccountError, Digest, Felt, TransactionResultError, Word, + utils::vec, + AccountError, TransactionResultError, Word, }; /// Parses the stub account data returned by the VM into individual account component commitments. @@ -76,75 +70,3 @@ pub fn extract_account_storage_delta( Ok(storage_delta) } - -// ACCOUNT VAULT DELTA -// ================================================================================================ -// TODO: update when TMST depth 64 leaves are supported -/// Extracts the account vault delta between the `initial_account` and `final_account_stub` from -/// the provided `MerkleStore` and `BTreeMap`. -pub fn extract_account_vault_delta( - store: &MerkleStore, - value_map: &BTreeMap<[u8; 32], Vec>, - initial_account: &Account, - final_account_stub: &FinalAccountStub, -) -> Result { - // extract original assets from the vault - let mut orig_assets = initial_account - .vault() - .assets() - .map(|asset| (Digest::from(asset.vault_key()), asset)) - .collect::>(); - - // extract final assets in the vault from the merkle store and advice map - let final_leaves = store - .non_empty_leaves(final_account_stub.0.vault_root(), 64) - .map(|(_, leaf)| leaf) - .collect::>(); - let final_assets = final_leaves - .into_iter() - .map(|leaf| { - let data = - value_map.get(&leaf.as_bytes()).expect("asset node must exist in the value map"); - let asset = Asset::try_from(Word::try_from(&data[4..]).expect("data contains word")) - .expect("asset is well formed"); - (Digest::from(asset.vault_key()), asset) - }) - .collect::>(); - - // compute the difference in assets - let asset_delta = orig_assets.diff(&final_assets); - - // extract net assets delta - let mut net_added_assets = vec![]; - let mut net_removed_assets = - asset_delta.removed.into_iter().map(|x| orig_assets[&x]).collect::>(); - - for (asset_key, updated_asset) in asset_delta.updated.into_iter() { - match (orig_assets.remove(&asset_key), updated_asset) { - // new asset has been added - (None, asset) => { - net_added_assets.push(asset); - } - // net increase in fungible asset amount - (Some(Asset::Fungible(orig)), Asset::Fungible(mut updated)) - if updated.amount() > orig.amount() => - { - updated.sub(orig.amount()).expect("sub amount is valid"); - net_added_assets.push(Asset::Fungible(updated)); - } - // net decrease in fungible asset amount - (Some(Asset::Fungible(mut orig)), Asset::Fungible(updated)) - if updated.amount() < orig.amount() => - { - orig.sub(updated.amount()).expect("sub amount is valid"); - net_removed_assets.push(Asset::Fungible(orig)); - } - _ => unreachable!(), - } - } - - Ok(AccountVaultDelta { - added_assets: net_added_assets, - removed_assets: net_removed_assets, - }) -} diff --git a/miden-lib/src/transaction/mod.rs b/miden-lib/src/transaction/mod.rs index cf3059b8d..950720c98 100644 --- a/miden-lib/src/transaction/mod.rs +++ b/miden-lib/src/transaction/mod.rs @@ -1,4 +1,2 @@ mod account_stub; -pub use account_stub::{ - extract_account_storage_delta, extract_account_vault_delta, parse_final_account_stub, -}; +pub use account_stub::{extract_account_storage_delta, parse_final_account_stub}; diff --git a/miden-tx/src/executor/mod.rs b/miden-tx/src/executor/mod.rs index 109d33e5a..863aba4d6 100644 --- a/miden-tx/src/executor/mod.rs +++ b/miden-tx/src/executor/mod.rs @@ -1,13 +1,10 @@ use super::{ AccountCode, AccountId, DataStore, Digest, NoteOrigin, NoteScript, NoteTarget, PreparedTransaction, RecAdviceProvider, TransactionCompiler, TransactionExecutorError, - TransactionResult, -}; -use crate::TryFromVmResult; -use miden_lib::{ - outputs::TX_SCRIPT_ROOT_WORD_IDX, - transaction::{extract_account_storage_delta, extract_account_vault_delta}, + TransactionHost, TransactionResult, }; +use crate::{host::EventHandler, TryFromVmResult}; +use miden_lib::{outputs::TX_SCRIPT_ROOT_WORD_IDX, transaction::extract_account_storage_delta}; use miden_objects::{ accounts::{Account, AccountDelta}, assembly::ProgramAst, @@ -15,7 +12,6 @@ use miden_objects::{ TransactionResultError, WORD_SIZE, }; use vm_core::{Program, StackOutputs, StarkField}; -use vm_processor::DefaultHost; /// The transaction executor is responsible for executing Miden rollup transactions. /// @@ -118,7 +114,7 @@ impl TransactionExecutor { self.prepare_transaction(account_id, block_ref, note_origins, tx_script)?; let advice_recorder: RecAdviceProvider = transaction.advice_provider_inputs().into(); - let mut host = DefaultHost::new(advice_recorder); + let mut host = TransactionHost::new(advice_recorder); let result = vm_processor::execute( transaction.tx_program(), transaction.stack_inputs(), @@ -130,7 +126,7 @@ impl TransactionExecutor { let (account, block_header, _block_chain, consumed_notes, tx_program, tx_script_root) = transaction.into_parts(); - let advice_recorder = host.into_inner(); + let (advice_recorder, event_handler) = host.into_parts(); create_transaction_result( account, consumed_notes, @@ -139,6 +135,7 @@ impl TransactionExecutor { tx_script_root, advice_recorder, result.stack_outputs().clone(), + event_handler, ) .map_err(TransactionExecutorError::TransactionResultError) } @@ -181,6 +178,7 @@ impl TransactionExecutor { } } +#[allow(clippy::too_many_arguments)] /// Creates a new [TransactionResult] from the provided data, advice provider and stack outputs. pub fn create_transaction_result( initial_account: Account, @@ -190,6 +188,7 @@ pub fn create_transaction_result( tx_script_root: Option, advice_provider: RecAdviceProvider, stack_outputs: StackOutputs, + event_handler: EventHandler, ) -> Result { // finalize the advice recorder let (advice_witness, stack, map, store) = advice_provider.finalize(); @@ -222,9 +221,8 @@ pub fn create_transaction_result( None }; - // extract vault delta - let vault_delta = - extract_account_vault_delta(&store, &map, &initial_account, &final_account_stub)?; + // finalize the event handler + let vault_delta = event_handler.finalize(); // construct the account delta let account_delta = AccountDelta { diff --git a/miden-tx/src/host/event/mod.rs b/miden-tx/src/host/event/mod.rs new file mode 100644 index 000000000..67871461a --- /dev/null +++ b/miden-tx/src/host/event/mod.rs @@ -0,0 +1,44 @@ +use miden_objects::{accounts::AccountVaultDelta, transaction::Event}; +use vm_processor::{ExecutionError, HostResponse, ProcessState}; + +mod vault_delta; +use vault_delta::AccountVaultDeltaHandler; + +/// The [EventHandler] is responsible for handling events emitted by a transaction. +/// +/// It is composed of multiple sub-handlers, each of which is responsible for handling specific +/// event types. The event handler has access to the [ProcessState] at the time the event was +/// emitted, and can use it to extract the data required to handle the event. +/// +/// Below we define the sub-handlers and their associated event types: +/// +/// - [VaultDeltaHandler]: +/// - [Event::AddAssetToAccountVault] +/// - [Event::RemoveAssetFromAccountVault] +#[derive(Default, Debug)] +pub struct EventHandler { + acct_vault_delta_handler: AccountVaultDeltaHandler, +} + +impl EventHandler { + /// Handles the event with the provided event ID. + pub fn handle_event( + &mut self, + process: &S, + event_id: u32, + ) -> Result { + match Event::try_from(event_id)? { + Event::AddAssetToAccountVault => self.acct_vault_delta_handler.add_asset(process), + Event::RemoveAssetFromAccountVault => { + self.acct_vault_delta_handler.remove_asset(process) + } + } + } + + /// Consumes the [EventHandler] and finalizes the sub-handlers it is composed of. + /// + /// Returns the result of finalizing the sub-handlers. + pub fn finalize(self) -> AccountVaultDelta { + self.acct_vault_delta_handler.finalize() + } +} diff --git a/miden-tx/src/host/event/vault_delta.rs b/miden-tx/src/host/event/vault_delta.rs new file mode 100644 index 000000000..d5bcaf0ae --- /dev/null +++ b/miden-tx/src/host/event/vault_delta.rs @@ -0,0 +1,178 @@ +use miden_objects::{ + accounts::{AccountId, AccountVaultDelta}, + assets::{Asset, FungibleAsset, NonFungibleAsset}, + utils::collections::{btree_map::Entry, BTreeMap}, + Digest, +}; +use vm_processor::{ContextId, ExecutionError, HostResponse, ProcessState}; + +/// The [AccountVaultDeltaHandler] is responsible for tracking changes to the vault of the account +/// the transaction is being executed against. +/// +/// It is composed of two maps: +/// - [AccountVaultDeltaHandler::fungible_assets] - tracks changes to the vault's fungible assets, +/// where the key is the faucet ID of the asset, and the value is the amount of the asset being +/// added or removed from the vault. +/// - [AccountVaultDeltaHandler::non_fungible_assets] - tracks changes to the vault's non-fungible +/// assets, where the key is the non-fungible asset, and the value is either 1 or -1 depending +/// on whether the asset is being added or removed from the vault. +#[derive(Default, Debug)] +pub struct AccountVaultDeltaHandler { + fungible_assets: BTreeMap, + non_fungible_assets: BTreeMap, +} + +impl AccountVaultDeltaHandler { + // MODIFIERS + // -------------------------------------------------------------------------------------------- + + /// Extracts the asset that is being added to the account's vault from the process state and + /// updates the appropriate [AccountVaultDeltaHandler::fungible_assets] or + /// [AccountVaultDeltaHandler::non_fungible_assets] map. + pub fn add_asset( + &mut self, + process: &S, + ) -> Result { + if process.ctx() != ContextId::root() { + return Err(ExecutionError::EventError( + "AddAssetToAccountVault event can only be emitted from the root context".into(), + )); + } + + let asset: Asset = process.get_stack_word(0).try_into().map_err(|err| { + ExecutionError::EventError(format!( + "Failed to apply account vault delta - asset is malformed - {err}" + )) + })?; + + match asset { + Asset::Fungible(asset) => { + update_asset_delta( + &mut self.fungible_assets, + asset.faucet_id().into(), + asset.amount() as i128, + ); + } + Asset::NonFungible(asset) => { + update_asset_delta(&mut self.non_fungible_assets, asset.vault_key().into(), 1) + } + }; + + Ok(HostResponse::None) + } + + /// Extracts the asset that is being removed from the account's vault from the process state + /// and updates the appropriate [AccountVaultDeltaHandler::fungible_assets] or + /// [AccountVaultDeltaHandler::non_fungible_assets] map. + pub fn remove_asset( + &mut self, + process: &S, + ) -> Result { + if process.ctx() != ContextId::root() { + return Err(ExecutionError::EventError( + "RemoveAssetFromAccountVault event can only be emitted from the root context" + .into(), + )); + } + + let asset: Asset = process.get_stack_word(0).try_into().map_err(|err| { + ExecutionError::EventError(format!( + "Failed to apply account vault delta - asset is malformed - {err}" + )) + })?; + + match asset { + Asset::Fungible(asset) => { + update_asset_delta( + &mut self.fungible_assets, + asset.faucet_id().into(), + -(asset.amount() as i128), + ); + } + Asset::NonFungible(asset) => { + update_asset_delta(&mut self.non_fungible_assets, asset.vault_key().into(), -1) + } + }; + + Ok(HostResponse::None) + } + + // CONSUMERS + // -------------------------------------------------------------------------------------------- + + /// Consumes the [AccountVaultDeltaHandler] and returns the [AccountVaultDelta] that represents the + /// changes to the account's vault. + pub fn finalize(self) -> AccountVaultDelta { + let mut added_assets = Vec::new(); + let mut removed_assets = Vec::new(); + + // process fungible assets + for (faucet_id, amount) in self.fungible_assets { + if amount > 0 { + added_assets.push(Asset::Fungible( + FungibleAsset::new( + unsafe { AccountId::new_unchecked(faucet_id.into()) }, + amount.unsigned_abs() as u64, + ) + .expect("fungible asset is well formed"), + )); + } else { + removed_assets.push(Asset::Fungible( + FungibleAsset::new( + unsafe { AccountId::new_unchecked(faucet_id.into()) }, + amount.unsigned_abs() as u64, + ) + .expect("fungible asset is well formed"), + )); + } + } + + // process non-fungible assets + for (non_fungible_asset, amount) in self.non_fungible_assets { + match amount { + 1 => { + added_assets.push(Asset::NonFungible(unsafe { + NonFungibleAsset::new_unchecked(*non_fungible_asset) + })); + } + -1 => { + removed_assets.push(Asset::NonFungible(unsafe { + NonFungibleAsset::new_unchecked(*non_fungible_asset) + })); + } + _ => unreachable!("non-fungible asset amount must be 1 or -1"), + } + } + + AccountVaultDelta { + added_assets, + removed_assets, + } + } +} + +// HELPERS +// ================================================================================================ +/// Updates the provided map with the provided key and amount. If the final amount is 0, the entry +/// is removed from the map. +fn update_asset_delta(delta_map: &mut BTreeMap, key: K, amount: V) +where + V: core::ops::Neg, + V: core::cmp::PartialEq<::Output>, + V: core::ops::AddAssign, + V: Copy, + K: Ord, +{ + match delta_map.entry(key) { + Entry::Occupied(mut entry) => { + if entry.get() == &-amount { + entry.remove(); + } else { + *entry.get_mut() += amount; + } + } + Entry::Vacant(entry) => { + entry.insert(amount); + } + } +} diff --git a/miden-tx/src/host/mod.rs b/miden-tx/src/host/mod.rs new file mode 100644 index 000000000..709bed93a --- /dev/null +++ b/miden-tx/src/host/mod.rs @@ -0,0 +1,63 @@ +use vm_processor::{ + AdviceExtractor, AdviceInjector, AdviceProvider, ExecutionError, Host, HostResponse, + ProcessState, +}; + +mod event; +pub(crate) use event::EventHandler; + +/// The [TransactionHost] is responsible for handling [Host] requests made by a transaction. +/// +/// The [TransactionHost] is composed of two components: +/// - [TransactionHost::adv_provider] - an [AdviceProvider] which is used to provide advice to the +/// transaction runtime. +/// - [TransactionHost::event_handler] - an [EventHandler] which is used to handle events emitted +/// by the transaction runtime. +/// +/// The [TransactionHost] implements the [Host] trait. +pub struct TransactionHost { + adv_provider: A, + event_handler: EventHandler, +} + +impl TransactionHost { + /// Returns a new [TransactionHost] instance with the provided [AdviceProvider]. + pub fn new(adv_provider: A) -> Self { + Self { + adv_provider, + event_handler: EventHandler::default(), + } + } + + /// Consumes the [TransactionHost] and returns the [AdviceProvider] and [EventHandler] it was + /// composed of. + pub fn into_parts(self) -> (A, EventHandler) { + (self.adv_provider, self.event_handler) + } +} + +impl Host for TransactionHost { + fn get_advice( + &mut self, + process: &S, + extractor: AdviceExtractor, + ) -> Result { + self.adv_provider.get_advice(process, &extractor) + } + + fn set_advice( + &mut self, + process: &S, + injector: AdviceInjector, + ) -> Result { + self.adv_provider.set_advice(process, &injector) + } + + fn on_event( + &mut self, + process: &S, + event_id: u32, + ) -> Result { + self.event_handler.handle_event(process, event_id) + } +} diff --git a/miden-tx/src/lib.rs b/miden-tx/src/lib.rs index 2c01c78ea..5be97fa0c 100644 --- a/miden-tx/src/lib.rs +++ b/miden-tx/src/lib.rs @@ -19,6 +19,9 @@ pub use data::DataStore; mod executor; pub use executor::TransactionExecutor; +pub mod host; +pub use host::TransactionHost; + mod prover; pub use prover::TransactionProver; diff --git a/miden-tx/src/prover/mod.rs b/miden-tx/src/prover/mod.rs index d16c74932..8aa7a06f0 100644 --- a/miden-tx/src/prover/mod.rs +++ b/miden-tx/src/prover/mod.rs @@ -1,9 +1,9 @@ -use super::TransactionProverError; +use super::{TransactionHost, TransactionProverError}; use crate::TryFromVmResult; use miden_objects::transaction::{CreatedNotes, FinalAccountStub}; use miden_objects::transaction::{PreparedTransaction, ProvenTransaction, TransactionWitness}; use miden_prover::{prove, ProvingOptions}; -use vm_processor::{DefaultHost, MemAdviceProvider}; +use vm_processor::MemAdviceProvider; /// The [TransactionProver] is a stateless component which is responsible for proving transactions. /// @@ -32,7 +32,7 @@ impl TransactionProver { ) -> Result { // prove transaction program let advice_provider: MemAdviceProvider = transaction.advice_provider_inputs().into(); - let mut host = DefaultHost::new(advice_provider); + let mut host = TransactionHost::new(advice_provider); let (outputs, proof) = prove( transaction.tx_program(), transaction.stack_inputs(), @@ -42,7 +42,8 @@ impl TransactionProver { .map_err(TransactionProverError::ProveTransactionProgramFailed)?; // extract transaction outputs and process transaction data - let (stack, map, store) = host.into_inner().into_parts(); + let (advice_provider, _event_handler) = host.into_parts(); + let (stack, map, store) = advice_provider.into_parts(); let final_account_stub = FinalAccountStub::try_from_vm_result(&outputs, &stack, &map, &store) .map_err(TransactionProverError::TransactionResultError)?; @@ -90,13 +91,14 @@ impl TransactionProver { ) = tx_witness.into_parts(); let advice_provider: MemAdviceProvider = advice_witness.into(); - let mut host = DefaultHost::new(advice_provider); + let mut host = TransactionHost::new(advice_provider); let (outputs, proof) = prove(&tx_program, stack_inputs, &mut host, self.proof_options.clone()) .map_err(TransactionProverError::ProveTransactionProgramFailed)?; // extract transaction outputs and process transaction data - let (stack, map, store) = host.into_inner().into_parts(); + let (advice_provider, _event_handler) = host.into_parts(); + let (stack, map, store) = advice_provider.into_parts(); let final_account_stub = FinalAccountStub::try_from_vm_result(&outputs, &stack, &map, &store) .map_err(TransactionProverError::TransactionResultError)?; diff --git a/miden-tx/src/tests.rs b/miden-tx/src/tests.rs index 46a51c302..d6880dc1b 100644 --- a/miden-tx/src/tests.rs +++ b/miden-tx/src/tests.rs @@ -1,6 +1,6 @@ use super::{ Account, AccountId, BlockHeader, ChainMmr, DataStore, DataStoreError, Note, NoteOrigin, - TransactionExecutor, TransactionProver, TransactionVerifier, TryFromVmResult, + TransactionExecutor, TransactionHost, TransactionProver, TransactionVerifier, TryFromVmResult, }; use miden_objects::{ accounts::AccountCode, @@ -21,7 +21,7 @@ use mock::{ mock::{account::MockAccountType, notes::AssetPreservationStatus, transaction::mock_inputs}, utils::prepare_word, }; -use vm_processor::{DefaultHost, MemAdviceProvider}; +use vm_processor::MemAdviceProvider; // TESTS // ================================================================================================ @@ -49,7 +49,7 @@ fn test_transaction_executor_witness() { // use the witness to execute the transaction again let mem_advice_provider: MemAdviceProvider = witness.advice_inputs().clone().into(); - let mut host = DefaultHost::new(mem_advice_provider); + let mut host = TransactionHost::new(mem_advice_provider); let result = vm_processor::execute( witness.program(), witness.get_stack_inputs(), @@ -58,7 +58,8 @@ fn test_transaction_executor_witness() { ) .unwrap(); - let (stack, map, store) = host.into_inner().into_parts(); + let (advice_provider, _event_handler) = host.into_parts(); + let (stack, map, store) = advice_provider.into_parts(); let final_account_stub = FinalAccountStub::try_from_vm_result(result.stack_outputs(), &stack, &map, &store).unwrap(); let created_notes = diff --git a/mock/src/constants.rs b/mock/src/constants.rs index 5af82b40c..aa7018275 100644 --- a/mock/src/constants.rs +++ b/mock/src/constants.rs @@ -4,44 +4,44 @@ use miden_objects::{ Felt, FieldElement, }; pub const ACCOUNT_SEED_FUNGIBLE_FAUCET_INVALID_INITIAL_BALANCE: [u64; 4] = [ - 4837964420216095321, - 14719513826019085120, - 8048017050177882048, - 360627022571283172, + 6145570607758938301, + 10282880913774515349, + 6426741982522617727, + 6405580465831572650, ]; -pub const ACCOUT_ID_FUNGIBLE_FAUCET_INVALID_INITIAL_BALANCE: u64 = 12081207106969327678; +pub const ACCOUT_ID_FUNGIBLE_FAUCET_INVALID_INITIAL_BALANCE: u64 = 13422896179450902740; pub const ACCOUNT_SEED_FUNGIBLE_FAUCET_VALID_INITIAL_BALANCE: [u64; 4] = [ - 12136709200684823055, - 1824916237097495284, - 8549090351365901331, - 6574931573032265039, + 9588068054595421519, + 16811868114829517529, + 5373761197620064059, + 7563481159681753098, ]; -pub const ACCOUNT_ID_FUNGIBLE_FAUCET_VALID_INITIAL_BALANCE: u64 = 12053881223939253326; +pub const ACCOUNT_ID_FUNGIBLE_FAUCET_VALID_INITIAL_BALANCE: u64 = 12328054752197811524; pub const ACCOUNT_SEED_NON_FUNGIBLE_FAUCET_INVALID_RESERVED_SLOT: [u64; 4] = [ - 10052841723745570425, - 12895187162113480993, - 17823370665452510718, - 13480704032776323398, + 11360754003635610262, + 1645235213184378605, + 12058267732908752911, + 223114579406030011, ]; -pub const ACCOUNT_ID_NON_FUNGIBLE_FAUCET_INVALID_RESERVED_SLOT: u64 = 16874984845583919667; +pub const ACCOUNT_ID_NON_FUNGIBLE_FAUCET_INVALID_RESERVED_SLOT: u64 = 16443721535164139279; pub const ACCOUNT_SEED_NON_FUNGIBLE_FAUCET_VALID_RESERVED_SLOT: [u64; 4] = [ - 17365960489267401602, - 2929548604532531452, - 1680093318753782580, - 17881887510011443259, + 404699601172309312, + 12905832155459206783, + 9802402797413803903, + 13510058645612144083, ]; -pub const ACCOUNT_ID_NON_FUNGIBLE_FAUCET_VALID_RESERVED_SLOT: u64 = 16423399875750953543; +pub const ACCOUNT_ID_NON_FUNGIBLE_FAUCET_VALID_RESERVED_SLOT: u64 = 17909431462585405459; pub const ACCOUNT_SEED_REGULAR_ACCOUNT_UPDATABLE_CODE_ON_CHAIN: [u64; 4] = [ - 2394600421062944048, - 5272776192057260276, - 17694074059714973576, - 18321641348679662936, + 10873503761844905100, + 14565999216237198843, + 1403914022137382820, + 12586397471557782933, ]; -pub const ACCOUNT_ID_REGULAR_ACCOUNT_UPDATABLE_CODE_ON_CHAIN: u64 = 3606314465504722753; +pub const ACCOUNT_ID_REGULAR_ACCOUNT_UPDATABLE_CODE_ON_CHAIN: u64 = 2817606756080467693; pub const ACCOUNT_ID_SENDER: u64 = 0b0110111011u64 << 54; pub const ACCOUNT_ID_FUNGIBLE_FAUCET_ON_CHAIN: u64 = 0b1010111100 << 54; diff --git a/mock/src/mock/account.rs b/mock/src/mock/account.rs index 25e63d1aa..f089e60aa 100644 --- a/mock/src/mock/account.rs +++ b/mock/src/mock/account.rs @@ -58,7 +58,7 @@ pub fn mock_account_storage() -> AccountStorage { .unwrap() } -fn mock_account_code(assembler: &Assembler) -> AccountCode { +pub fn mock_account_code(assembler: &Assembler) -> AccountCode { let account_code = "\ use.miden::sat::account use.miden::sat::tx diff --git a/objects/src/accounts/account_id.rs b/objects/src/accounts/account_id.rs index b90dcc041..765acc76f 100644 --- a/objects/src/accounts/account_id.rs +++ b/objects/src/accounts/account_id.rs @@ -92,7 +92,11 @@ impl AccountId { } /// Creates a new [AccountId] without checking its validity. - pub(crate) fn new_unchecked(value: Felt) -> AccountId { + /// + /// # Safety + /// This function requires that the provided value is a valid [Felt] representation of an + /// [AccountId]. + pub unsafe fn new_unchecked(value: Felt) -> AccountId { AccountId(value) } diff --git a/objects/src/accounts/seed.rs b/objects/src/accounts/seed.rs index 895c093ec..795c132d1 100644 --- a/objects/src/accounts/seed.rs +++ b/objects/src/accounts/seed.rs @@ -100,7 +100,7 @@ pub fn get_account_seed_inner( // check if the seed satisfies the specified account type if AccountId::validate_seed_digest(¤t_digest).is_ok() { // `validate_seed_digest` already validated it - let account_id = AccountId::new_unchecked(current_digest[0]); + let account_id = unsafe { AccountId::new_unchecked(current_digest[0]) }; if account_id.account_type() == account_type && account_id.is_on_chain() == on_chain { #[cfg(feature = "log")] log.done(current_digest, current_seed, account_id); diff --git a/objects/src/assets/fungible.rs b/objects/src/assets/fungible.rs index dbf676844..7969308c0 100644 --- a/objects/src/assets/fungible.rs +++ b/objects/src/assets/fungible.rs @@ -43,7 +43,7 @@ impl FungibleAsset { /// Creates a new [FungibleAsset] without checking its validity. pub(crate) fn new_unchecked(value: Word) -> FungibleAsset { FungibleAsset { - faucet_id: AccountId::new_unchecked(value[3]), + faucet_id: unsafe { AccountId::new_unchecked(value[3]) }, amount: value[0].as_int(), } } diff --git a/objects/src/assets/mod.rs b/objects/src/assets/mod.rs index 6dfe033ca..1f27473a8 100644 --- a/objects/src/assets/mod.rs +++ b/objects/src/assets/mod.rs @@ -68,7 +68,7 @@ impl Asset { pub(crate) fn new_unchecked(value: Word) -> Asset { let first_bit = value[3].as_int() >> 63; match first_bit { - 0 => Asset::NonFungible(NonFungibleAsset::new_unchecked(value)), + 0 => Asset::NonFungible(unsafe { NonFungibleAsset::new_unchecked(value) }), 1 => Asset::Fungible(FungibleAsset::new_unchecked(value)), _ => unreachable!(), } diff --git a/objects/src/assets/nonfungible.rs b/objects/src/assets/nonfungible.rs index c93bc6add..635231d7b 100644 --- a/objects/src/assets/nonfungible.rs +++ b/objects/src/assets/nonfungible.rs @@ -62,7 +62,11 @@ impl NonFungibleAsset { } /// Creates a new [NonFungibleAsset] without checking its validity. - pub(crate) fn new_unchecked(value: Word) -> NonFungibleAsset { + /// + /// # Safety + /// This function required that the provided value is a valid word representation of a + /// [NonFungibleAsset]. + pub unsafe fn new_unchecked(value: Word) -> NonFungibleAsset { NonFungibleAsset(value) } @@ -74,7 +78,7 @@ impl NonFungibleAsset { /// Return ID of the faucet which issued this asset. pub fn faucet_id(&self) -> AccountId { - AccountId::new_unchecked(self.0[FAUCET_ID_POS]) + unsafe { AccountId::new_unchecked(self.0[FAUCET_ID_POS]) } } // HELPER FUNCTIONS diff --git a/objects/src/transaction/event.rs b/objects/src/transaction/event.rs new file mode 100644 index 000000000..feecbdff9 --- /dev/null +++ b/objects/src/transaction/event.rs @@ -0,0 +1,24 @@ +use vm_processor::ExecutionError; + +/// Represents an event which is emitted by a transaction via the invocation of the +/// `emit.` instruction. The event ID is a 32-bit unsigned integer which is used to +/// identify the event type. +#[repr(u32)] +pub enum Event { + AddAssetToAccountVault = 131072, + RemoveAssetFromAccountVault = 131073, +} + +impl TryFrom for Event { + type Error = ExecutionError; + + fn try_from(value: u32) -> Result { + match value { + 131072 => Ok(Event::AddAssetToAccountVault), + 131073 => Ok(Event::RemoveAssetFromAccountVault), + _ => Err(ExecutionError::EventError(format!( + "Failed to parse Event - event with id {value} is not supported", + ))), + } + } +} diff --git a/objects/src/transaction/mod.rs b/objects/src/transaction/mod.rs index ec8554bed..2212ca703 100644 --- a/objects/src/transaction/mod.rs +++ b/objects/src/transaction/mod.rs @@ -11,6 +11,7 @@ use vm_core::{Program, StackInputs, StackOutputs}; mod account_stub; mod consumed_notes; mod created_notes; +mod event; mod executed_tx; mod prepared_tx; mod proven_tx; @@ -22,6 +23,7 @@ mod utils; pub use account_stub::FinalAccountStub; pub use consumed_notes::{ConsumedNoteInfo, ConsumedNotes}; pub use created_notes::CreatedNotes; +pub use event::Event; pub use executed_tx::ExecutedTransaction; pub use prepared_tx::PreparedTransaction; pub use proven_tx::ProvenTransaction;