use alloc::{string::String, sync::Arc};
use miden_air::trace::{AUX_TRACE_RAND_CHALLENGES, chiplets::hasher::HASH_CYCLE_LEN};
use miden_core::{
Felt,
mast::{
BasicBlockNodeBuilder, CallNodeBuilder, DynNodeBuilder, ExternalNodeBuilder,
JoinNodeBuilder, LoopNodeBuilder, MastForest, MastForestContributor, MastNodeExt,
SplitNodeBuilder,
},
operations::Operation,
program::{Kernel, Program, StackInputs},
};
use miden_utils_testing::{get_column_name, rand::rand_array};
use pretty_assertions::assert_eq;
use rstest::{fixture, rstest};
use super::*;
use crate::{
AdviceInputs, DefaultHost, ExecutionOptions, FastProcessor, HostLibrary,
trace::trace_state::MemoryReadsReplay,
};
const DEFAULT_STACK: &[Felt] = &[Felt::new(1), Felt::new(2), Felt::new(3)];
const SENTINEL_VALUE: Felt = Felt::new(9999);
fn dyn_target_proc_hash() -> &'static [Felt] {
use std::sync::LazyLock;
static HASH: LazyLock<Vec<Felt>> = LazyLock::new(|| {
let mut forest = MastForest::new();
let target = BasicBlockNodeBuilder::new(vec![Operation::Swap], Vec::new())
.add_to_forest(&mut forest)
.unwrap();
forest.get_node_by_id(target).unwrap().digest().iter().copied().collect()
});
HASH.as_slice()
}
fn external_lib_proc_digest() -> Word {
use std::sync::LazyLock;
static DIGEST: LazyLock<Word> = LazyLock::new(|| {
let mut forest = MastForest::new();
let swap_block =
BasicBlockNodeBuilder::new(vec![Operation::Swap, Operation::Swap], Vec::new())
.add_to_forest(&mut forest)
.unwrap();
forest.get_node_by_id(swap_block).unwrap().digest()
});
*DIGEST
}
fn external_lib_proc_hash_for_stack() -> &'static [Felt] {
use std::sync::LazyLock;
static HASH: LazyLock<Vec<Felt>> =
LazyLock::new(|| external_lib_proc_digest().iter().copied().collect());
HASH.as_slice()
}
#[rstest]
#[case(join_program(), 4, DEFAULT_STACK)]
#[case(join_program(), 11, DEFAULT_STACK)]
#[case(split_program(), 5, &[ONE])]
#[case(split_program(), 5, &[ZERO, SENTINEL_VALUE])]
#[case(split_program(), 9, &[ONE])]
#[case(split_program(), 9, &[ZERO, SENTINEL_VALUE])]
#[case(loop_program(), 5, &[ZERO, SENTINEL_VALUE])]
#[case(loop_program(), 6, &[ZERO, SENTINEL_VALUE])]
#[case(loop_program(), 10, &[ONE, ZERO, SENTINEL_VALUE])]
#[case(loop_program(), 10, &[ONE, ONE, ZERO, SENTINEL_VALUE])]
#[case(call_program(), 5, DEFAULT_STACK)]
#[case(call_program(), 10, DEFAULT_STACK)]
#[case(syscall_program(), 5, DEFAULT_STACK)]
#[case(syscall_program(), 10, DEFAULT_STACK)]
#[case(basic_block_program_small(), 1, DEFAULT_STACK)]
#[case(basic_block_program_small(), 2, DEFAULT_STACK)]
#[case(basic_block_program_small(), 3, DEFAULT_STACK)]
#[case(basic_block_program_small(), 4, DEFAULT_STACK)]
#[case(basic_block_program_small(), 5, DEFAULT_STACK)]
#[case(basic_block_program_multiple_batches(), 74, DEFAULT_STACK)]
#[case(basic_block_program_multiple_batches(), 76, DEFAULT_STACK)]
#[case(dyn_program(), 12, dyn_target_proc_hash())]
#[case(dyn_program(), 16, dyn_target_proc_hash())]
#[case(dyncall_program(), 12, dyn_target_proc_hash())]
#[case(dyncall_program(), 16, dyn_target_proc_hash())]
#[case(external_program(), 5, DEFAULT_STACK)]
#[case(dyn_program(), 12, external_lib_proc_hash_for_stack())]
fn test_trace_generation_at_fragment_boundaries(
testname: String,
#[case] program: Program,
#[case] fragment_size: usize,
#[case] stack_inputs: &[Felt],
) {
const MAX_FRAGMENT_SIZE: usize = 1 << 20;
let trace_from_fragments = {
let processor = FastProcessor::new_with_options(
StackInputs::new(stack_inputs).unwrap(),
AdviceInputs::default(),
ExecutionOptions::default()
.with_core_trace_fragment_size(fragment_size)
.unwrap(),
);
let mut host = DefaultHost::default();
host.load_library(create_simple_library()).unwrap();
let (execution_output, trace_fragment_contexts) =
processor.execute_for_trace_sync(&program, &mut host).unwrap();
build_trace(execution_output, trace_fragment_contexts, program.to_info()).unwrap()
};
let trace_from_single_fragment = {
let processor = FastProcessor::new_with_options(
StackInputs::new(stack_inputs).unwrap(),
AdviceInputs::default(),
ExecutionOptions::default()
.with_core_trace_fragment_size(MAX_FRAGMENT_SIZE)
.unwrap(),
);
let mut host = DefaultHost::default();
host.load_library(create_simple_library()).unwrap();
let (execution_output, trace_fragment_contexts) =
processor.execute_for_trace_sync(&program, &mut host).unwrap();
assert!(trace_fragment_contexts.core_trace_contexts.len() == 1);
build_trace(execution_output, trace_fragment_contexts, program.to_info()).unwrap()
};
for (col_idx, (col_from_fragments, col_from_single_fragment)) in trace_from_fragments
.main_trace()
.columns()
.zip(trace_from_single_fragment.main_trace().columns())
.enumerate()
{
if col_from_fragments != col_from_single_fragment {
for (row_idx, (val_from_fragments, val_from_single_fragment)) in
col_from_fragments.iter().zip(col_from_single_fragment.iter()).enumerate()
{
if val_from_fragments != val_from_single_fragment {
panic!(
"Trace columns do not match between trace generated as multiple fragments vs a single fragment at column {} ({}) row {}: multiple={}, single={}",
col_idx,
get_column_name(col_idx),
row_idx,
val_from_fragments,
val_from_single_fragment
);
}
}
panic!(
"Trace columns do not match between trace generated as multiple fragments vs a single fragment at column {} ({}): different lengths (fragments={}, single={})",
col_idx,
get_column_name(col_idx),
col_from_fragments.len(),
col_from_single_fragment.len()
);
}
}
assert_eq!(trace_from_fragments.stack_outputs(), trace_from_single_fragment.stack_outputs(),);
assert_eq!(trace_from_fragments.program_info(), trace_from_single_fragment.program_info(),);
assert_eq!(
trace_from_fragments.trace_len_summary(),
trace_from_single_fragment.trace_len_summary(),
);
let merkle_nodes_from_fragments: alloc::collections::BTreeMap<_, _> = trace_from_fragments
.advice_provider()
.merkle_store()
.inner_nodes()
.map(|info| (info.value, (info.left, info.right)))
.collect();
let merkle_nodes_from_single: alloc::collections::BTreeMap<_, _> = trace_from_single_fragment
.advice_provider()
.merkle_store()
.inner_nodes()
.map(|info| (info.value, (info.left, info.right)))
.collect();
assert_eq!(merkle_nodes_from_fragments, merkle_nodes_from_single,);
let rand_elements = rand_array::<Felt, AUX_TRACE_RAND_CHALLENGES>();
let aux_from_fragments = trace_from_fragments.build_aux_trace(&rand_elements).unwrap();
let aux_from_single_fragment =
trace_from_single_fragment.build_aux_trace(&rand_elements).unwrap();
let aux_from_fragments =
aux_from_fragments.columns().map(|col| col.to_vec()).collect::<Vec<_>>();
let aux_from_single_fragment =
aux_from_single_fragment.columns().map(|col| col.to_vec()).collect::<Vec<_>>();
assert_eq!(aux_from_fragments, aux_from_single_fragment,);
assert_eq!(
format!("{:?}", DeterministicTrace(&trace_from_fragments)),
format!("{:?}", DeterministicTrace(&trace_from_single_fragment)),
"Deterministic trace mismatch between fragments and single fragment"
);
insta::assert_compact_debug_snapshot!(testname, DeterministicTrace(&trace_from_fragments));
}
#[test]
fn test_partial_last_fragment_exists_for_h0_inversion_path() {
const FRAGMENT_SIZE: usize = 11;
let program = join_program();
let processor = FastProcessor::new_with_options(
StackInputs::new(DEFAULT_STACK).unwrap(),
AdviceInputs::default(),
ExecutionOptions::default()
.with_core_trace_fragment_size(FRAGMENT_SIZE)
.unwrap(),
);
let mut host = DefaultHost::default();
host.load_library(create_simple_library()).unwrap();
let (execution_output, trace_fragment_contexts) =
processor.execute_for_trace_sync(&program, &mut host).unwrap();
assert!(
trace_fragment_contexts.core_trace_contexts.len() > 1,
"repro precondition requires multiple fragments"
);
let trace = build_trace(execution_output, trace_fragment_contexts, program.to_info()).unwrap();
let total_rows_without_halt = trace.main_trace().num_rows() - 1;
assert_ne!(
total_rows_without_halt % FRAGMENT_SIZE,
0,
"repro precondition requires a short final fragment"
);
}
#[cfg(miri)]
#[test]
fn miri_repro_uninitialized_tail_read_during_h0_inversion() {
const FRAGMENT_SIZE: usize = 11;
let program = join_program();
let processor = FastProcessor::new_with_options(
StackInputs::new(DEFAULT_STACK).unwrap(),
AdviceInputs::default(),
ExecutionOptions::default()
.with_core_trace_fragment_size(FRAGMENT_SIZE)
.unwrap(),
);
let mut host = DefaultHost::default();
host.load_library(create_simple_library()).unwrap();
let (execution_output, trace_fragment_contexts) =
processor.execute_for_trace_sync(&program, &mut host).unwrap();
assert!(trace_fragment_contexts.core_trace_contexts.len() > 1);
let _ = build_trace(execution_output, trace_fragment_contexts, program.to_info());
}
fn create_simple_library() -> HostLibrary {
let mut mast_forest = MastForest::new();
let swap_block = BasicBlockNodeBuilder::new(vec![Operation::Swap, Operation::Swap], Vec::new())
.add_to_forest(&mut mast_forest)
.unwrap();
mast_forest.make_root(swap_block);
HostLibrary::from(Arc::new(mast_forest))
}
fn join_program() -> Program {
let mut program = MastForest::new();
let basic_block_mul = BasicBlockNodeBuilder::new(vec![Operation::Mul], Vec::new())
.add_to_forest(&mut program)
.unwrap();
let basic_block_add = BasicBlockNodeBuilder::new(vec![Operation::Add], Vec::new())
.add_to_forest(&mut program)
.unwrap();
let basic_block_swap = BasicBlockNodeBuilder::new(vec![Operation::Swap], Vec::new())
.add_to_forest(&mut program)
.unwrap();
let target_join_node = JoinNodeBuilder::new([basic_block_add, basic_block_swap])
.add_to_forest(&mut program)
.unwrap();
let root_join_node = JoinNodeBuilder::new([basic_block_mul, target_join_node])
.add_to_forest(&mut program)
.unwrap();
program.make_root(root_join_node);
Program::new(Arc::new(program), root_join_node)
}
fn split_program() -> Program {
let mut program = MastForest::new();
let root_join_node = {
let basic_block_swap_swap =
BasicBlockNodeBuilder::new(vec![Operation::Swap, Operation::Swap], Vec::new())
.add_to_forest(&mut program)
.unwrap();
let target_split_node = {
let basic_block_add = BasicBlockNodeBuilder::new(vec![Operation::Add], Vec::new())
.add_to_forest(&mut program)
.unwrap();
let basic_block_swap = BasicBlockNodeBuilder::new(vec![Operation::Swap], Vec::new())
.add_to_forest(&mut program)
.unwrap();
SplitNodeBuilder::new([basic_block_add, basic_block_swap])
.add_to_forest(&mut program)
.unwrap()
};
JoinNodeBuilder::new([basic_block_swap_swap, target_split_node])
.add_to_forest(&mut program)
.unwrap()
};
program.make_root(root_join_node);
Program::new(Arc::new(program), root_join_node)
}
fn loop_program() -> Program {
let mut program = MastForest::new();
let root_join_node = {
let basic_block_swap_swap =
BasicBlockNodeBuilder::new(vec![Operation::Swap, Operation::Swap], Vec::new())
.add_to_forest(&mut program)
.unwrap();
let target_loop_node = {
let basic_block_pad_drop =
BasicBlockNodeBuilder::new(vec![Operation::Pad, Operation::Drop], Vec::new())
.add_to_forest(&mut program)
.unwrap();
LoopNodeBuilder::new(basic_block_pad_drop).add_to_forest(&mut program).unwrap()
};
JoinNodeBuilder::new([basic_block_swap_swap, target_loop_node])
.add_to_forest(&mut program)
.unwrap()
};
program.make_root(root_join_node);
Program::new(Arc::new(program), root_join_node)
}
fn call_program() -> Program {
let mut program = MastForest::new();
let root_join_node = {
let basic_block_swap_swap =
BasicBlockNodeBuilder::new(vec![Operation::Swap, Operation::Swap], Vec::new())
.add_to_forest(&mut program)
.unwrap();
let target_call_node =
CallNodeBuilder::new(basic_block_swap_swap).add_to_forest(&mut program).unwrap();
JoinNodeBuilder::new([basic_block_swap_swap, target_call_node])
.add_to_forest(&mut program)
.unwrap()
};
program.make_root(root_join_node);
Program::new(Arc::new(program), root_join_node)
}
fn syscall_program() -> Program {
let mut program = MastForest::new();
let (root_join_node, kernel_proc_digest) = {
let basic_block_swap_swap =
BasicBlockNodeBuilder::new(vec![Operation::Swap, Operation::Swap], Vec::new())
.add_to_forest(&mut program)
.unwrap();
let target_call_node = CallNodeBuilder::new_syscall(basic_block_swap_swap)
.add_to_forest(&mut program)
.unwrap();
let root_join_node = JoinNodeBuilder::new([basic_block_swap_swap, target_call_node])
.add_to_forest(&mut program)
.unwrap();
(root_join_node, program[basic_block_swap_swap].digest())
};
program.make_root(root_join_node);
Program::with_kernel(
Arc::new(program),
root_join_node,
Kernel::new(&[kernel_proc_digest]).unwrap(),
)
}
fn basic_block_program_small() -> Program {
let mut program = MastForest::new();
let root_join_node = {
let target_basic_block = BasicBlockNodeBuilder::new(
vec![Operation::Swap, Operation::Push(Felt::from_u32(42))],
Vec::new(),
)
.add_to_forest(&mut program)
.unwrap();
let basic_block_drop = BasicBlockNodeBuilder::new(vec![Operation::Drop], Vec::new())
.add_to_forest(&mut program)
.unwrap();
JoinNodeBuilder::new([target_basic_block, basic_block_drop])
.add_to_forest(&mut program)
.unwrap()
};
program.make_root(root_join_node);
Program::new(Arc::new(program), root_join_node)
}
fn basic_block_program_multiple_batches() -> Program {
const NUM_SWAPS: usize = 80;
let mut program = MastForest::new();
let root_join_node = {
let target_basic_block =
BasicBlockNodeBuilder::new(vec![Operation::Swap; NUM_SWAPS], Vec::new())
.add_to_forest(&mut program)
.unwrap();
let basic_block_drop = BasicBlockNodeBuilder::new(vec![Operation::Drop], Vec::new())
.add_to_forest(&mut program)
.unwrap();
JoinNodeBuilder::new([target_basic_block, basic_block_drop])
.add_to_forest(&mut program)
.unwrap()
};
program.make_root(root_join_node);
Program::new(Arc::new(program), root_join_node)
}
fn dyn_program() -> Program {
const HASH_ADDR: Felt = Felt::new(40);
let mut program = MastForest::new();
let root_join_node = {
let basic_block = BasicBlockNodeBuilder::new(
vec![
Operation::Push(HASH_ADDR),
Operation::MStoreW,
Operation::Drop,
Operation::Drop,
Operation::Drop,
Operation::Drop,
Operation::Push(HASH_ADDR),
],
Vec::new(),
)
.add_to_forest(&mut program)
.unwrap();
let dyn_node = DynNodeBuilder::new_dyn().add_to_forest(&mut program).unwrap();
JoinNodeBuilder::new([basic_block, dyn_node])
.add_to_forest(&mut program)
.unwrap()
};
program.make_root(root_join_node);
let target = BasicBlockNodeBuilder::new(vec![Operation::Swap], Vec::new())
.add_to_forest(&mut program)
.unwrap();
program.make_root(target);
Program::new(Arc::new(program), root_join_node)
}
fn dyncall_program() -> Program {
const HASH_ADDR: Felt = Felt::new(40);
let mut program = MastForest::new();
let root_join_node = {
let basic_block = BasicBlockNodeBuilder::new(
vec![
Operation::Push(HASH_ADDR),
Operation::MStoreW,
Operation::Drop,
Operation::Drop,
Operation::Drop,
Operation::Drop,
Operation::Push(HASH_ADDR),
],
Vec::new(),
)
.add_to_forest(&mut program)
.unwrap();
let dyncall_node = DynNodeBuilder::new_dyncall().add_to_forest(&mut program).unwrap();
JoinNodeBuilder::new([basic_block, dyncall_node])
.add_to_forest(&mut program)
.unwrap()
};
program.make_root(root_join_node);
let target = BasicBlockNodeBuilder::new(vec![Operation::Swap], Vec::new())
.add_to_forest(&mut program)
.unwrap();
program.make_root(target);
Program::new(Arc::new(program), root_join_node)
}
fn external_program() -> Program {
let mut program = MastForest::new();
let root_join_node = {
let basic_block_pad_drop =
BasicBlockNodeBuilder::new(vec![Operation::Pad, Operation::Drop], Vec::new())
.add_to_forest(&mut program)
.unwrap();
let external_node = ExternalNodeBuilder::new(external_lib_proc_digest())
.add_to_forest(&mut program)
.unwrap();
JoinNodeBuilder::new([basic_block_pad_drop, external_node])
.add_to_forest(&mut program)
.unwrap()
};
program.make_root(root_join_node);
Program::new(Arc::new(program), root_join_node)
}
#[test]
fn test_build_trace_returns_err_on_empty_memory_reads_replay() {
const MAX_FRAGMENT_SIZE: usize = 1 << 20;
let program = dyn_program();
let stack_inputs = dyn_target_proc_hash();
let processor = FastProcessor::new_with_options(
StackInputs::new(stack_inputs).unwrap(),
AdviceInputs::default(),
ExecutionOptions::default()
.with_core_trace_fragment_size(MAX_FRAGMENT_SIZE)
.unwrap(),
);
let mut host = DefaultHost::default();
let (execution_output, mut trace_generation_context) =
processor.execute_for_trace_sync(&program, &mut host).unwrap();
for ctx in &mut trace_generation_context.core_trace_contexts {
ctx.replay.memory_reads = MemoryReadsReplay::default();
}
let result = build_trace(execution_output, trace_generation_context, program.to_info());
assert!(
result.is_err(),
"build_trace should return Err when hasher replay has bad node ID"
);
}
#[test]
fn test_build_trace_returns_err_on_bad_node_id_in_hasher_replay() {
const MAX_FRAGMENT_SIZE: usize = 1 << 20;
let program = basic_block_program_small();
let processor = FastProcessor::new_with_options(
StackInputs::new(DEFAULT_STACK).unwrap(),
AdviceInputs::default(),
ExecutionOptions::default()
.with_core_trace_fragment_size(MAX_FRAGMENT_SIZE)
.unwrap(),
);
let mut host = DefaultHost::default();
let (execution_output, mut trace_generation_context) =
processor.execute_for_trace_sync(&program, &mut host).unwrap();
let empty_forest = Arc::new(MastForest::new());
let mut temp_forest = MastForest::new();
let valid_id = BasicBlockNodeBuilder::new(vec![Operation::Noop], Vec::new())
.add_to_forest(&mut temp_forest)
.unwrap();
trace_generation_context.hasher_for_chiplet.record_hash_basic_block(
empty_forest,
valid_id,
[ZERO; 4].into(),
);
let result = build_trace(execution_output, trace_generation_context, program.to_info());
assert!(
result.is_err(),
"build_trace should return Err when hasher replay has bad node ID"
);
}
#[rstest]
#[case(-1, false)]
#[case(0, true)]
fn test_build_trace_with_max_len_corner_cases(
#[case] max_trace_len_offset_from_core_trace_len: isize,
#[case] build_trace_succeeds: bool,
) {
const MAX_FRAGMENT_SIZE: usize = 1 << 20;
let program = basic_block_program_small();
let processor = FastProcessor::new_with_options(
StackInputs::new(DEFAULT_STACK).unwrap(),
AdviceInputs::default(),
ExecutionOptions::default()
.with_core_trace_fragment_size(MAX_FRAGMENT_SIZE)
.unwrap(),
);
let mut host = DefaultHost::default();
let (execution_output, trace_generation_context) =
processor.execute_for_trace_sync(&program, &mut host).unwrap();
let core_trace_len = trace_generation_context.core_trace_contexts.len()
* trace_generation_context.fragment_size
+ 1;
let max_trace_len = core_trace_len
.checked_add_signed(max_trace_len_offset_from_core_trace_len)
.unwrap();
let result = build_trace_with_max_len(
execution_output,
trace_generation_context,
program.to_info(),
max_trace_len,
);
assert_eq!(
result.is_ok(),
build_trace_succeeds,
"with max_trace_len={max_trace_len} (core_trace_len={core_trace_len}), \
expected build_trace_succeeds={build_trace_succeeds}"
);
if !build_trace_succeeds {
assert!(
matches!(result, Err(ExecutionError::TraceLenExceeded(max_len)) if max_len == max_trace_len),
"expected TraceLenExceeded({max_trace_len}), got: {result:?}"
);
}
}
#[test]
fn test_build_trace_returns_err_on_fragment_size_overflow() {
const MAX_FRAGMENT_SIZE: usize = 1 << 20;
let program = basic_block_program_small();
let processor = FastProcessor::new_with_options(
StackInputs::new(DEFAULT_STACK).unwrap(),
AdviceInputs::default(),
ExecutionOptions::default()
.with_core_trace_fragment_size(MAX_FRAGMENT_SIZE)
.unwrap(),
);
let mut host = DefaultHost::default();
let (execution_output, mut trace_generation_context) =
processor.execute_for_trace_sync(&program, &mut host).unwrap();
trace_generation_context.fragment_size = usize::MAX;
let result = build_trace_with_max_len(
execution_output,
trace_generation_context,
program.to_info(),
usize::MAX,
);
assert!(
matches!(result, Err(ExecutionError::TraceLenExceeded(_))),
"expected TraceLenExceeded on overflow, got: {result:?}"
);
}
#[test]
fn test_build_trace_returns_err_when_chiplets_trace_exceeds_max_len() {
const MAX_FRAGMENT_SIZE: usize = 1 << 20;
let program = dyn_program();
let stack_inputs = dyn_target_proc_hash();
let processor = FastProcessor::new_with_options(
StackInputs::new(stack_inputs).unwrap(),
AdviceInputs::default(),
ExecutionOptions::default()
.with_core_trace_fragment_size(MAX_FRAGMENT_SIZE)
.unwrap(),
);
let mut host = DefaultHost::default();
let (execution_output, mut trace_generation_context) =
processor.execute_for_trace_sync(&program, &mut host).unwrap();
let core_trace_rows =
trace_generation_context.core_trace_contexts.len() * trace_generation_context.fragment_size;
let num_permutations = core_trace_rows / HASH_CYCLE_LEN + 1;
for _ in 0..num_permutations {
trace_generation_context.hasher_for_chiplet.record_permute_input([ZERO; 12]);
}
let max_trace_len = core_trace_rows;
let result = build_trace_with_max_len(
execution_output,
trace_generation_context,
program.to_info(),
max_trace_len,
);
assert!(
matches!(result, Err(ExecutionError::TraceLenExceeded(_))),
"expected TraceLenExceeded, got: {result:?}"
);
}
#[test]
fn test_build_trace_returns_err_on_empty_core_trace_contexts() {
const MAX_FRAGMENT_SIZE: usize = 1 << 20;
let program = basic_block_program_small();
let processor = FastProcessor::new_with_options(
StackInputs::new(DEFAULT_STACK).unwrap(),
AdviceInputs::default(),
ExecutionOptions::default()
.with_core_trace_fragment_size(MAX_FRAGMENT_SIZE)
.unwrap(),
);
let mut host = DefaultHost::default();
let (execution_output, mut trace_generation_context) =
processor.execute_for_trace_sync(&program, &mut host).unwrap();
trace_generation_context.core_trace_contexts.clear();
let result = build_trace(execution_output, trace_generation_context, program.to_info());
assert!(
matches!(result, Err(ExecutionError::Internal(_))),
"expected ExecutionError::Internal, got: {result:?}"
);
}
#[fixture]
fn testname() -> String {
std::thread::current().name().unwrap().replace("::", "__")
}
struct DeterministicTrace<'a>(&'a ExecutionTrace);
impl core::fmt::Debug for DeterministicTrace<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let trace = self.0;
let sorted_nodes: alloc::collections::BTreeMap<_, _> = trace
.advice_provider()
.merkle_store()
.inner_nodes()
.map(|info| (info.value, (info.left, info.right)))
.collect();
f.debug_struct("ExecutionTrace")
.field("main_trace", trace.main_trace())
.field("program_info", &trace.program_info())
.field("stack_outputs", &trace.stack_outputs())
.field("merkle_store_nodes", &sorted_nodes)
.field("trace_len_summary", &trace.trace_len_summary())
.finish()
}
}