use std::vec::Vec;
use miden_client::Serializable;
use miden_client::account::{AccountHeader, AccountId, AccountVaultPatch};
use miden_client::asset::Asset;
use miden_client::store::StoreError;
use miden_protocol::asset::AssetId;
use rusqlite::{OptionalExtension, Transaction, params};
use crate::sql_error::SqlResultExt;
use crate::{SqliteStore, blob_array, insert_sql, subst, u64_to_value};
impl SqliteStore {
pub(crate) fn insert_assets(
tx: &Transaction<'_>,
account_id: AccountId,
assets: impl Iterator<Item = Asset>,
) -> Result<(), StoreError> {
const LATEST_QUERY: &str =
insert_sql!(latest_account_assets { account_id, asset_id, asset } | REPLACE);
let mut latest_stmt = tx.prepare_cached(LATEST_QUERY).into_store_error()?;
let account_id_bytes = account_id.to_bytes();
for asset in assets {
let asset_id_bytes = asset.id().to_bytes();
let asset_bytes = asset.to_value_word().to_bytes();
latest_stmt
.execute(params![&account_id_bytes, &asset_id_bytes, &asset_bytes])
.into_store_error()?;
}
Ok(())
}
pub(crate) fn apply_account_vault_patch(
tx: &Transaction<'_>,
account_id: AccountId,
final_account_state: &AccountHeader,
vault_patch: &AccountVaultPatch,
) -> Result<(), StoreError> {
const READ_OLD_ASSET: &str =
"SELECT asset FROM latest_account_assets WHERE account_id = ? AND asset_id = ?";
const HISTORICAL_INSERT: &str = insert_sql!(
historical_account_assets {
account_id,
replaced_at_nonce,
asset_id,
old_asset
} | REPLACE
);
const LATEST_INSERT: &str =
insert_sql!(latest_account_assets { account_id, asset_id, asset } | REPLACE);
const DELETE_LATEST: &str =
"DELETE FROM latest_account_assets WHERE account_id = ? AND asset_id IN rarray(?)";
let account_id_bytes = account_id.to_bytes();
let nonce_val = u64_to_value(final_account_state.nonce().as_canonical_u64());
let mut read_stmt = tx.prepare_cached(READ_OLD_ASSET).into_store_error()?;
let mut hist_stmt = tx.prepare_cached(HISTORICAL_INSERT).into_store_error()?;
let mut latest_stmt = tx.prepare_cached(LATEST_INSERT).into_store_error()?;
let removed_asset_ids: Vec<AssetId> = vault_patch.removed_asset_ids().copied().collect();
let removed = removed_asset_ids.iter().map(|asset_id| (asset_id.to_bytes(), None::<Asset>));
let updated =
vault_patch.updated_assets().map(|asset| (asset.id().to_bytes(), Some(asset)));
for (asset_id_bytes, new_asset) in removed.chain(updated) {
let old_asset: Option<Vec<u8>> = read_stmt
.query_row(params![&account_id_bytes, &asset_id_bytes], |row| row.get(0))
.optional()
.into_store_error()?
.flatten();
hist_stmt
.execute(params![&account_id_bytes, &nonce_val, &asset_id_bytes, old_asset])
.into_store_error()?;
if let Some(asset) = new_asset {
let asset_bytes = asset.to_value_word().to_bytes();
latest_stmt
.execute(params![&account_id_bytes, &asset_id_bytes, &asset_bytes])
.into_store_error()?;
}
}
if !removed_asset_ids.is_empty() {
tx.execute(DELETE_LATEST, params![&account_id_bytes, blob_array(&removed_asset_ids)])
.into_store_error()?;
}
Ok(())
}
}