use miden_protocol::Felt;
use miden_protocol::account::{
AccountCode,
AccountCodePatch,
AccountCodeUpgrade,
AccountDelta,
AccountId,
AccountPatch,
AssetDelta,
PartialAccount,
};
use crate::TransactionKernelError;
use crate::host::storage_patch_tracker::StoragePatchTracker;
use crate::host::tx_event::AssetPatch;
use crate::host::vault_update_tracker::VaultUpdateTracker;
#[derive(Debug, Clone)]
pub struct AccountUpdateTracker {
account_id: AccountId,
storage: StoragePatchTracker,
vault: VaultUpdateTracker,
code: AccountCodeState,
initial_nonce: Felt,
nonce_delta: Felt,
}
impl AccountUpdateTracker {
pub fn new(account: &PartialAccount) -> Result<Self, TransactionKernelError> {
let code = if account.is_new() {
AccountCodeState::New(account.code().clone())
} else {
AccountCodeState::None
};
Ok(Self {
account_id: account.id(),
storage: StoragePatchTracker::new(account)?,
vault: VaultUpdateTracker::default(),
code,
nonce_delta: Felt::ZERO,
initial_nonce: account.nonce(),
})
}
pub fn was_nonce_incremented(&self) -> bool {
self.nonce_delta != Felt::ZERO
}
pub fn increment_nonce(&mut self) {
self.nonce_delta += Felt::ONE;
}
pub fn record_code_upgrade(
&mut self,
code_upgrade: AccountCodeUpgrade,
) -> Result<(), TransactionKernelError> {
match self.code {
AccountCodeState::None => {
self.code = AccountCodeState::UpgradeInitialized(code_upgrade);
Ok(())
},
AccountCodeState::New(_) => {
Err(TransactionKernelError::AccountCodeUpgradeNotAllowedForNewAccount)
},
AccountCodeState::UpgradeInitialized(_) => {
Err(TransactionKernelError::other("account code upgrade was already initialized"))
},
}
}
pub fn update_asset_patch(&mut self, patch: AssetPatch) -> Result<(), TransactionKernelError> {
self.vault.update_patch(patch)
}
pub fn add_asset_delta(&mut self, delta: AssetDelta) {
self.vault.add_delta(delta);
}
pub fn reset_vault_delta(&mut self) {
self.vault.reset_delta();
}
pub fn storage(&mut self) -> &mut StoragePatchTracker {
&mut self.storage
}
pub fn into_delta(self) -> AccountDelta {
let account_id = self.account_id;
let nonce_delta = self.nonce_delta;
let storage_patch = self.storage.into_patch();
let vault_delta = self.vault.into_delta();
AccountDelta::new(
account_id,
storage_patch,
vault_delta,
AccountCodePatch::new(self.code.into_option_code()),
nonce_delta,
)
.expect("account delta created in delta tracker should be valid")
}
pub fn into_patch(self) -> AccountPatch {
let storage_patch = self.storage.into_patch();
let vault_patch = self.vault.into_patch();
let new_nonce = if self.nonce_delta == Felt::ZERO {
None
} else {
debug_assert!(
self.initial_nonce.as_canonical_u64() < (Felt::ORDER - 1),
"tx kernel should abort if nonce would overflow"
);
Some(self.initial_nonce + self.nonce_delta)
};
AccountPatch::new(
self.account_id,
storage_patch,
vault_patch,
AccountCodePatch::new(self.code.into_option_code()),
new_nonce,
)
.expect("account patch created in delta tracker should be valid")
}
}
#[derive(Debug, Clone)]
enum AccountCodeState {
None,
New(AccountCode),
UpgradeInitialized(AccountCodeUpgrade),
}
impl AccountCodeState {
fn into_option_code(self) -> Option<AccountCode> {
match self {
AccountCodeState::None => None,
AccountCodeState::New(account_code) => Some(account_code),
AccountCodeState::UpgradeInitialized(code_upgrade) => Some(code_upgrade.into_code()),
}
}
}