Skip to content

Commit

Permalink
refactor: move account vault delta tracking to transaction host
Browse files Browse the repository at this point in the history
  • Loading branch information
bobbinth committed Jan 8, 2024
1 parent 6242b53 commit ce4b2d8
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 115 deletions.
1 change: 0 additions & 1 deletion miden-lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ testing = ["miden-objects/testing"]
assembly = { workspace = true }
miden-objects = { package = "miden-objects", path = "../objects", default-features = false }
miden-stdlib = { workspace = true }
vm-processor = { workspace = true }

[dev-dependencies]
miden-objects = { package = "miden-objects", path = "../objects", default-features = false, features = [
Expand Down
26 changes: 26 additions & 0 deletions miden-lib/src/transaction/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use core::fmt;

// TRANSACTION EVENT PARSING ERROR
// ================================================================================================

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum TransactionEventParsingError {
InvalidTransactionEvent(u32),
NotTransactionEvent(u32),
}

impl fmt::Display for TransactionEventParsingError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidTransactionEvent(event_id) => {
write!(f, "event {event_id} is not a valid transaction kernel event")
},
Self::NotTransactionEvent(event_id) => {
write!(f, "event {event_id} is not a transaction kernel event")
},
}
}
}

#[cfg(feature = "std")]
impl std::error::Error for TransactionEventParsingError {}
26 changes: 21 additions & 5 deletions miden-lib/src/transaction/events.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use vm_processor::ExecutionError;
use core::fmt;

use super::TransactionEventParsingError;

// TRANSACTION EVENT
// ================================================================================================
Expand All @@ -11,21 +13,35 @@ use vm_processor::ExecutionError;
/// - The upper 16 bits of the event ID are set to 2.
/// - The lower 16 bits represent a unique event ID within the transaction kernel.
#[repr(u32)]
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum TransactionEvent {
AddAssetToAccountVault = 0x2_0000, // 131072
RemoveAssetFromAccountVault = 0x2_0001, // 131073
}

impl TransactionEvent {
/// Value of the top 16 bits of a transaction kernel event ID.
pub const EVENT_ID_PREFIX: u16 = 2;
}

impl fmt::Display for TransactionEvent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self:?}")
}
}

impl TryFrom<u32> for TransactionEvent {
type Error = ExecutionError;
type Error = TransactionEventParsingError;

fn try_from(value: u32) -> Result<Self, Self::Error> {
if value >> 16 != Self::EVENT_ID_PREFIX as u32 {
return Err(TransactionEventParsingError::NotTransactionEvent(value));
}

match value {
0x2_0000 => Ok(TransactionEvent::AddAssetToAccountVault),
0x2_0001 => Ok(TransactionEvent::RemoveAssetFromAccountVault),
_ => Err(ExecutionError::EventError(format!(
"Failed to parse Event - event with id {value} is not supported",
))),
_ => Err(TransactionEventParsingError::InvalidTransactionEvent(value)),
}
}
}
3 changes: 3 additions & 0 deletions miden-lib/src/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ pub use outputs::{
OUTPUT_NOTES_COMMITMENT_WORD_IDX, TX_SCRIPT_ROOT_WORD_IDX,
};

mod errors;
pub use errors::TransactionEventParsingError;

// TRANSACTION KERNEL
// ================================================================================================

Expand Down
2 changes: 1 addition & 1 deletion miden-tx/src/compiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ impl TransactionCompiler {
Ok(account_code)
}

/// Loads the provided account interface (vector of procedure digests) into the this compiler.
/// Loads the provided account interface (vector of procedure digests) into this compiler.
/// Returns the old account interface if it previously existed.
pub fn load_account_interface(
&mut self,
Expand Down
15 changes: 5 additions & 10 deletions miden-tx/src/executor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ use super::{
RecAdviceProvider, ScriptTarget, TransactionCompiler, TransactionExecutorError,
TransactionHost,
};
use crate::host::EventHandler;

mod data;
pub use data::DataStore;
Expand Down Expand Up @@ -157,14 +156,12 @@ impl<D: DataStore> TransactionExecutor<D> {

let (tx_program, tx_script, tx_inputs) = transaction.into_parts();

let (advice_recorder, event_handler) = host.into_parts();
build_executed_transaction(
tx_program,
tx_script,
tx_inputs,
advice_recorder,
result.stack_outputs().clone(),
event_handler,
host,
)
}

Expand Down Expand Up @@ -212,12 +209,13 @@ fn build_executed_transaction(
program: Program,
tx_script: Option<TransactionScript>,
tx_inputs: TransactionInputs,
advice_provider: RecAdviceProvider,
stack_outputs: StackOutputs,
event_handler: EventHandler,
host: TransactionHost<RecAdviceProvider>,
) -> Result<ExecutedTransaction, TransactionExecutorError> {
let (advice_recorder, vault_delta) = host.into_parts();

// finalize the advice recorder
let (advice_witness, _, map, store) = advice_provider.finalize();
let (advice_witness, _, map, store) = advice_recorder.finalize();

// parse transaction results
let tx_outputs = TransactionKernel::parse_transaction_outputs(&stack_outputs, &map.into())
Expand Down Expand Up @@ -247,9 +245,6 @@ fn build_executed_transaction(
None
};

// finalize the event handler
let vault_delta = event_handler.finalize();

// construct the account delta
let account_delta =
AccountDelta::new(storage_delta, vault_delta, nonce_delta).expect("invalid account delta");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,37 @@ use miden_objects::{
utils::collections::{btree_map::Entry, BTreeMap},
Digest,
};
use vm_processor::{ContextId, ExecutionError, HostResponse, ProcessState};
use vm_processor::{ExecutionError, HostResponse, ProcessState};

/// The [AccountVaultDeltaHandler] is responsible for tracking changes to the vault of the account
// ACCOUNT VAULT DELTA TRACKER
// ================================================================================================

/// The account vault delta tracker 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.
/// - Fungible asset map: 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 (positive value for added assets, negative value for removed assets).
/// - Non-fungible asset map: 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<u64, i128>,
pub struct AccountVaultDeltaTracker {
fungible_assets: BTreeMap<AccountId, i128>,
non_fungible_assets: BTreeMap<Digest, i8>,
}

impl AccountVaultDeltaHandler {
impl AccountVaultDeltaTracker {
// 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.
/// updates the appropriate fungible or non-fungible asset map.
pub fn add_asset<S: ProcessState>(
&mut self,
process: &S,
) -> Result<HostResponse, ExecutionError> {
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}"
Expand All @@ -49,7 +45,7 @@ impl AccountVaultDeltaHandler {
Asset::Fungible(asset) => {
update_asset_delta(
&mut self.fungible_assets,
asset.faucet_id().into(),
asset.faucet_id(),
asset.amount() as i128,
);
},
Expand All @@ -68,13 +64,6 @@ impl AccountVaultDeltaHandler {
&mut self,
process: &S,
) -> Result<HostResponse, ExecutionError> {
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}"
Expand All @@ -85,7 +74,7 @@ impl AccountVaultDeltaHandler {
Asset::Fungible(asset) => {
update_asset_delta(
&mut self.fungible_assets,
asset.faucet_id().into(),
asset.faucet_id(),
-(asset.amount() as i128),
);
},
Expand All @@ -97,12 +86,12 @@ impl AccountVaultDeltaHandler {
Ok(HostResponse::None)
}

// CONSUMERS
// CONVERSIONS
// --------------------------------------------------------------------------------------------

/// Consumes the [AccountVaultDeltaHandler] and returns the [AccountVaultDelta] that represents the
/// changes to the account's vault.
pub fn finalize(self) -> AccountVaultDelta {
/// Consumes this delta tracker and returns the [AccountVaultDelta] that represents the changes
/// to the account's vault.
pub fn into_vault_delta(self) -> AccountVaultDelta {
let mut added_assets = Vec::new();
let mut removed_assets = Vec::new();

Expand Down Expand Up @@ -148,8 +137,9 @@ impl AccountVaultDeltaHandler {
}
}

// HELPERS
// HELPER FUNCTIONS
// ================================================================================================

/// 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<K, V>(delta_map: &mut BTreeMap<K, V>, key: K, amount: V)
Expand Down
47 changes: 0 additions & 47 deletions miden-tx/src/host/event/mod.rs

This file was deleted.

51 changes: 33 additions & 18 deletions miden-tx/src/host/mod.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,40 @@
use miden_lib::transaction::TransactionEvent;
use miden_objects::{accounts::delta::AccountVaultDelta, utils::string::ToString};
use vm_processor::{
AdviceExtractor, AdviceInjector, AdviceProvider, ExecutionError, Host, HostResponse,
AdviceExtractor, AdviceInjector, AdviceProvider, ContextId, ExecutionError, Host, HostResponse,
ProcessState,
};

mod event;
pub(crate) use event::EventHandler;
mod account_delta;
use account_delta::AccountVaultDeltaTracker;

/// 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.
// TRANSACTION HOST
// ================================================================================================

/// Transaction host is responsible for handling [Host] requests made by a transaction kernel.
///
/// The [TransactionHost] implements the [Host] trait.
/// Transaction host is composed of two components:
/// - An advice provider which is used to provide non-deterministic inputs to the transaction
/// runtime.
/// - An account vault delta tracker which is used to keep track of changes made to the asset
/// of the account the transaction is being executed against.
pub struct TransactionHost<A> {
adv_provider: A,
event_handler: EventHandler,
acct_vault_delta_tracker: AccountVaultDeltaTracker,
}

impl<A: AdviceProvider> TransactionHost<A> {
/// Returns a new [TransactionHost] instance with the provided [AdviceProvider].
pub fn new(adv_provider: A) -> Self {
Self {
adv_provider,
event_handler: EventHandler::default(),
acct_vault_delta_tracker: AccountVaultDeltaTracker::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)
/// Consumes this transaction host and returns the advice provider and account vault delta.
pub fn into_parts(self) -> (A, AccountVaultDelta) {
(self.adv_provider, self.acct_vault_delta_tracker.into_vault_delta())
}
}

Expand All @@ -58,6 +60,19 @@ impl<A: AdviceProvider> Host for TransactionHost<A> {
process: &S,
event_id: u32,
) -> Result<HostResponse, ExecutionError> {
self.event_handler.handle_event(process, event_id)
let event = TransactionEvent::try_from(event_id)
.map_err(|err| ExecutionError::EventError(err.to_string()))?;

if process.ctx() != ContextId::root() {
return Err(ExecutionError::EventError(format!(
"{event} event can only be emitted from the root context"
)));
}

use TransactionEvent::*;
match event {
AddAssetToAccountVault => self.acct_vault_delta_tracker.add_asset(process),
RemoveAssetFromAccountVault => self.acct_vault_delta_tracker.remove_asset(process),
}
}
}

0 comments on commit ce4b2d8

Please sign in to comment.