use alloc::{collections::BTreeMap, sync::Arc, vec::Vec};
use miden_core::{DebugOptions, Felt};
use miden_debug_types::{
DefaultSourceManager, Location, SourceFile, SourceManager, SourceManagerSync, SourceSpan,
};
use crate::{
AdviceMutation, AsyncHost, BaseHost, DebugHandler, EventError, ExecutionError, FutureMaybeSend,
MastForest, MastForestStore, MemMastForestStore, ProcessState, SyncHost, Word,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ProcessStateSnapshot {
clk: u32,
ctx: u32,
stack_state: Vec<Felt>,
stack_words: [Word; 4],
mem_state: Vec<(crate::MemoryAddress, Felt)>,
}
impl From<&ProcessState<'_>> for ProcessStateSnapshot {
fn from(state: &ProcessState) -> Self {
ProcessStateSnapshot {
clk: state.clk().into(),
ctx: state.ctx().into(),
stack_state: state.get_stack_state(),
stack_words: [
state.get_stack_word_be(0),
state.get_stack_word_be(4),
state.get_stack_word_be(8),
state.get_stack_word_be(12),
],
mem_state: state.get_mem_state(state.ctx()),
}
}
}
#[derive(Default, Debug)]
pub struct TraceCollector {
trace_counts: BTreeMap<u32, u32>,
execution_order: Vec<(u32, u64)>,
}
impl TraceCollector {
pub fn new() -> Self {
Self::default()
}
pub fn get_trace_count(&self, trace_id: u32) -> u32 {
self.trace_counts.get(&trace_id).copied().unwrap_or(0)
}
pub fn get_execution_order(&self) -> &[(u32, u64)] {
&self.execution_order
}
}
impl DebugHandler for TraceCollector {
fn on_trace(&mut self, process: &ProcessState, trace_id: u32) -> Result<(), ExecutionError> {
*self.trace_counts.entry(trace_id).or_insert(0) += 1;
self.execution_order.push((trace_id, process.clk().into()));
Ok(())
}
}
#[derive(Debug)]
pub struct TestConsistencyHost<S: SourceManager = DefaultSourceManager> {
trace_collector: TraceCollector,
snapshots: BTreeMap<u32, Vec<ProcessStateSnapshot>>,
store: MemMastForestStore,
source_manager: Arc<S>,
}
impl TestConsistencyHost {
pub fn new() -> Self {
Self {
trace_collector: TraceCollector::new(),
snapshots: BTreeMap::new(),
store: MemMastForestStore::default(),
source_manager: Arc::new(DefaultSourceManager::default()),
}
}
pub fn with_kernel_forest(kernel_forest: Arc<MastForest>) -> Self {
let mut store = MemMastForestStore::default();
store.insert(kernel_forest.clone());
Self {
trace_collector: TraceCollector::new(),
snapshots: BTreeMap::new(),
store,
source_manager: Arc::new(DefaultSourceManager::default()),
}
}
pub fn get_trace_count(&self, trace_id: u32) -> u32 {
self.trace_collector.get_trace_count(trace_id)
}
pub fn get_execution_order(&self) -> &[(u32, u64)] {
self.trace_collector.get_execution_order()
}
pub fn snapshots(&self) -> &BTreeMap<u32, Vec<ProcessStateSnapshot>> {
&self.snapshots
}
}
impl Default for TestConsistencyHost {
fn default() -> Self {
Self::new()
}
}
impl<S> BaseHost for TestConsistencyHost<S>
where
S: SourceManager,
{
fn get_label_and_source_file(
&self,
location: &Location,
) -> (SourceSpan, Option<Arc<SourceFile>>) {
let maybe_file = self.source_manager.get_by_uri(location.uri());
let span = self.source_manager.location_to_span(location.clone()).unwrap_or_default();
(span, maybe_file)
}
fn on_debug(
&mut self,
_process: &mut ProcessState,
_options: &DebugOptions,
) -> Result<(), ExecutionError> {
Ok(())
}
fn on_trace(
&mut self,
process: &mut ProcessState,
trace_id: u32,
) -> Result<(), ExecutionError> {
self.trace_collector.on_trace(process, trace_id)?;
let snapshot = ProcessStateSnapshot::from(&*process);
self.snapshots.entry(trace_id).or_default().push(snapshot);
Ok(())
}
fn on_assert_failed(&mut self, _process: &ProcessState, _err_code: crate::Felt) {
}
}
impl<S> SyncHost for TestConsistencyHost<S>
where
S: SourceManager,
{
fn get_mast_forest(&self, node_digest: &Word) -> Option<Arc<MastForest>> {
self.store.get(node_digest)
}
fn on_event(&mut self, _process: &ProcessState<'_>) -> Result<Vec<AdviceMutation>, EventError> {
Ok(Vec::new()) }
}
impl<S> AsyncHost for TestConsistencyHost<S>
where
S: SourceManagerSync,
{
#[allow(clippy::manual_async_fn)]
fn get_mast_forest(&self, node_digest: &Word) -> impl FutureMaybeSend<Option<Arc<MastForest>>> {
let result = <Self as SyncHost>::get_mast_forest(self, node_digest);
async move { result }
}
#[allow(clippy::manual_async_fn)]
fn on_event(
&mut self,
_process: &ProcessState,
) -> impl FutureMaybeSend<Result<Vec<AdviceMutation>, EventError>> {
async move { Ok(Vec::new()) } }
}