Skip to content

Commit

Permalink
Merge pull request #288 from 0xPolygonMiden/frisitano-event-vault-delta
Browse files Browse the repository at this point in the history
Introduce host event handling
  • Loading branch information
bobbinth authored Oct 31, 2023
2 parents d92549b + 8c9dd83 commit a824d18
Show file tree
Hide file tree
Showing 19 changed files with 401 additions and 138 deletions.
20 changes: 20 additions & 0 deletions miden-lib/asm/sat/kernel.masm
Original file line number Diff line number Diff line change
Expand Up @@ -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
# =================================================================================================

Expand Down Expand Up @@ -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]
Expand All @@ -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]
Expand Down
84 changes: 3 additions & 81 deletions miden-lib/src/transaction/account_stub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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<Felt>>,
initial_account: &Account,
final_account_stub: &FinalAccountStub,
) -> Result<AccountVaultDelta, TransactionResultError> {
// extract original assets from the vault
let mut orig_assets = initial_account
.vault()
.assets()
.map(|asset| (Digest::from(asset.vault_key()), asset))
.collect::<BTreeMap<_, _>>();

// 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::<Vec<_>>();
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::<BTreeMap<_, _>>();

// 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::<Vec<_>>();

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,
})
}
4 changes: 1 addition & 3 deletions miden-lib/src/transaction/mod.rs
Original file line number Diff line number Diff line change
@@ -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};
22 changes: 10 additions & 12 deletions miden-tx/src/executor/mod.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
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,
transaction::{ConsumedNotes, CreatedNotes, FinalAccountStub},
TransactionResultError, WORD_SIZE,
};
use vm_core::{Program, StackOutputs, StarkField};
use vm_processor::DefaultHost;

/// The transaction executor is responsible for executing Miden rollup transactions.
///
Expand Down Expand Up @@ -118,7 +114,7 @@ impl<D: DataStore> TransactionExecutor<D> {
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(),
Expand All @@ -130,7 +126,7 @@ impl<D: DataStore> TransactionExecutor<D> {
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,
Expand All @@ -139,6 +135,7 @@ impl<D: DataStore> TransactionExecutor<D> {
tx_script_root,
advice_recorder,
result.stack_outputs().clone(),
event_handler,
)
.map_err(TransactionExecutorError::TransactionResultError)
}
Expand Down Expand Up @@ -181,6 +178,7 @@ impl<D: DataStore> TransactionExecutor<D> {
}
}

#[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,
Expand All @@ -190,6 +188,7 @@ pub fn create_transaction_result(
tx_script_root: Option<Digest>,
advice_provider: RecAdviceProvider,
stack_outputs: StackOutputs,
event_handler: EventHandler,
) -> Result<TransactionResult, TransactionResultError> {
// finalize the advice recorder
let (advice_witness, stack, map, store) = advice_provider.finalize();
Expand Down Expand Up @@ -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 {
Expand Down
44 changes: 44 additions & 0 deletions miden-tx/src/host/event/mod.rs
Original file line number Diff line number Diff line change
@@ -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<S: ProcessState>(
&mut self,
process: &S,
event_id: u32,
) -> Result<HostResponse, ExecutionError> {
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()
}
}
Loading

0 comments on commit a824d18

Please sign in to comment.