use alloc::{collections::VecDeque, string::ToString, sync::Arc};
use miden_air::trace::{
RowIndex,
chiplets::hasher::{HasherState, STATE_WIDTH},
};
use crate::{
ContextId, ExecutionError, Felt, MIN_STACK_DEPTH, MemoryError, ONE, Word, ZERO,
advice::AdviceError,
continuation_stack::ContinuationStack,
crypto::merkle::MerklePath,
errors::OperationError,
mast::{MastForest, MastNodeId},
precompile::PrecompileTranscriptState,
processor::{
AdviceProviderInterface, HasherInterface, MemoryInterface, Processor, SystemInterface,
},
trace::chiplets::CircuitEvaluation,
};
#[derive(Debug)]
pub struct CoreTraceFragmentContext {
pub state: CoreTraceState,
pub replay: ExecutionReplay,
pub continuation: ContinuationStack,
pub initial_mast_forest: Arc<MastForest>,
}
#[derive(Debug)]
pub struct CoreTraceState {
pub system: SystemState,
pub decoder: DecoderState,
pub stack: StackState,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SystemState {
pub clk: RowIndex,
pub ctx: ContextId,
pub fn_hash: Word,
pub pc_transcript_state: PrecompileTranscriptState,
}
impl SystemState {
pub(crate) fn from_processor<P: Processor>(processor: &P) -> Self {
Self {
clk: processor.system().clock(),
ctx: processor.system().ctx(),
fn_hash: processor.system().caller_hash(),
pc_transcript_state: processor.precompile_transcript_state(),
}
}
}
#[derive(Debug)]
pub struct DecoderState {
pub current_addr: Felt,
pub parent_addr: Felt,
}
impl DecoderState {
pub fn replay_node_start(
&mut self,
block_address_replay: &mut BlockAddressReplay,
block_stack_replay: &mut BlockStackReplay,
) -> Result<(), ExecutionError> {
self.current_addr = block_address_replay.replay_block_address()?;
self.parent_addr = block_stack_replay.replay_node_start_parent_addr()?;
Ok(())
}
pub fn replay_node_end(
&mut self,
block_stack_replay: &mut BlockStackReplay,
) -> Result<(Felt, NodeFlags), ExecutionError> {
let node_end_data = block_stack_replay.replay_node_end()?;
self.current_addr = node_end_data.prev_addr;
self.parent_addr = node_end_data.prev_parent_addr;
Ok((node_end_data.ended_node_addr, node_end_data.flags))
}
}
#[derive(Debug)]
pub struct StackState {
pub stack_top: [Felt; MIN_STACK_DEPTH],
stack_depth: usize,
last_overflow_addr: Felt,
}
impl StackState {
pub fn new(
stack_top: [Felt; MIN_STACK_DEPTH],
stack_depth: usize,
last_overflow_addr: Felt,
) -> Self {
Self {
stack_top,
stack_depth,
last_overflow_addr,
}
}
pub fn get(&self, index: usize) -> Felt {
self.stack_top[MIN_STACK_DEPTH - index - 1]
}
pub fn stack_depth(&self) -> usize {
self.stack_depth
}
pub fn overflow_addr(&self) -> Felt {
self.last_overflow_addr
}
pub fn num_overflow_elements_in_current_ctx(&self) -> usize {
debug_assert!(self.stack_depth >= MIN_STACK_DEPTH);
self.stack_depth - MIN_STACK_DEPTH
}
pub fn push_overflow(&mut self, _element: Felt, clk: RowIndex) {
self.stack_depth += 1;
self.last_overflow_addr = clk.into();
}
pub fn pop_overflow(
&mut self,
stack_overflow_replay: &mut StackOverflowReplay,
) -> Result<Option<Felt>, OperationError> {
debug_assert!(self.stack_depth >= MIN_STACK_DEPTH);
if self.stack_depth > MIN_STACK_DEPTH {
let (stack_value, new_overflow_addr) = stack_overflow_replay.replay_pop_overflow()?;
self.stack_depth -= 1;
self.last_overflow_addr = new_overflow_addr;
Ok(Some(stack_value))
} else {
self.last_overflow_addr = ZERO;
Ok(None)
}
}
pub fn overflow_helper(&self) -> Felt {
let denominator = self.stack_depth() - MIN_STACK_DEPTH;
Felt::new(denominator as u64)
}
pub fn start_context(&mut self) {
self.stack_depth = MIN_STACK_DEPTH;
self.last_overflow_addr = ZERO;
}
pub fn restore_context(
&mut self,
stack_overflow_replay: &mut StackOverflowReplay,
) -> Result<(), OperationError> {
let (stack_depth, last_overflow_addr) =
stack_overflow_replay.replay_restore_context_overflow_addr()?;
self.stack_depth = stack_depth;
self.last_overflow_addr = last_overflow_addr;
Ok(())
}
}
#[derive(Debug, Default)]
pub struct ExecutionReplay {
pub block_stack: BlockStackReplay,
pub execution_context: ExecutionContextReplay,
pub stack_overflow: StackOverflowReplay,
pub memory_reads: MemoryReadsReplay,
pub advice: AdviceReplay,
pub hasher: HasherResponseReplay,
pub block_address: BlockAddressReplay,
pub mast_forest_resolution: MastForestResolutionReplay,
}
#[derive(Debug, Default)]
pub struct ExecutionContextReplay {
execution_contexts: VecDeque<ExecutionContextSystemInfo>,
}
impl ExecutionContextReplay {
pub fn record_execution_context(&mut self, ctx_info: ExecutionContextSystemInfo) {
self.execution_contexts.push_back(ctx_info);
}
pub fn replay_execution_context(
&mut self,
) -> Result<ExecutionContextSystemInfo, OperationError> {
self.execution_contexts
.pop_front()
.ok_or(OperationError::Internal("no execution context recorded"))
}
}
#[derive(Debug, Default)]
pub struct BlockStackReplay {
node_start_parent_addr: VecDeque<Felt>,
node_end: VecDeque<NodeEndData>,
}
impl BlockStackReplay {
pub fn new() -> Self {
Self {
node_start_parent_addr: VecDeque::new(),
node_end: VecDeque::new(),
}
}
pub fn record_node_start_parent_addr(&mut self, parent_addr: Felt) {
self.node_start_parent_addr.push_back(parent_addr);
}
pub fn record_node_end(
&mut self,
ended_node_addr: Felt,
flags: NodeFlags,
prev_addr: Felt,
prev_parent_addr: Felt,
) {
self.node_end.push_back(NodeEndData {
ended_node_addr,
flags,
prev_addr,
prev_parent_addr,
});
}
pub fn replay_node_start_parent_addr(&mut self) -> Result<Felt, ExecutionError> {
self.node_start_parent_addr
.pop_front()
.ok_or(ExecutionError::Internal("no node start parent address recorded"))
}
pub fn replay_node_end(&mut self) -> Result<NodeEndData, ExecutionError> {
self.node_end
.pop_front()
.ok_or(ExecutionError::Internal("no node address and flags recorded"))
}
}
#[derive(Debug)]
pub struct NodeFlags {
is_loop_body: bool,
loop_entered: bool,
is_call: bool,
is_syscall: bool,
}
impl NodeFlags {
pub fn new(is_loop_body: bool, loop_entered: bool, is_call: bool, is_syscall: bool) -> Self {
Self {
is_loop_body,
loop_entered,
is_call,
is_syscall,
}
}
pub fn is_loop_body(&self) -> Felt {
if self.is_loop_body { ONE } else { ZERO }
}
pub fn loop_entered(&self) -> Felt {
if self.loop_entered { ONE } else { ZERO }
}
pub fn is_call(&self) -> Felt {
if self.is_call { ONE } else { ZERO }
}
pub fn is_syscall(&self) -> Felt {
if self.is_syscall { ONE } else { ZERO }
}
pub fn to_hasher_state_second_word(&self) -> Word {
[self.is_loop_body(), self.loop_entered(), self.is_call(), self.is_syscall()].into()
}
}
#[derive(Debug)]
pub struct NodeEndData {
pub ended_node_addr: Felt,
pub flags: NodeFlags,
pub prev_addr: Felt,
pub prev_parent_addr: Felt,
}
#[derive(Debug)]
pub struct ExecutionContextSystemInfo {
pub parent_ctx: ContextId,
pub parent_fn_hash: Word,
}
#[derive(Debug, Default)]
pub struct MastForestResolutionReplay {
mast_forest_resolutions: VecDeque<(MastNodeId, Arc<MastForest>)>,
}
impl MastForestResolutionReplay {
pub fn record_resolution(&mut self, node_id: MastNodeId, forest: Arc<MastForest>) {
self.mast_forest_resolutions.push_back((node_id, forest));
}
pub fn replay_resolution(&mut self) -> Result<(MastNodeId, Arc<MastForest>), ExecutionError> {
self.mast_forest_resolutions
.pop_front()
.ok_or(ExecutionError::Internal("no MastForest resolutions recorded"))
}
}
#[derive(Debug, Default)]
pub struct MemoryReadsReplay {
elements_read: VecDeque<(Felt, Felt, ContextId, RowIndex)>,
words_read: VecDeque<(Word, Felt, ContextId, RowIndex)>,
}
impl MemoryReadsReplay {
pub fn record_read_element(
&mut self,
element: Felt,
addr: Felt,
ctx: ContextId,
clk: RowIndex,
) {
self.elements_read.push_back((element, addr, ctx, clk));
}
pub fn record_read_word(&mut self, word: Word, addr: Felt, ctx: ContextId, clk: RowIndex) {
self.words_read.push_back((word, addr, ctx, clk));
}
pub fn replay_read_element(&mut self, addr: Felt) -> Result<Felt, MemoryError> {
let (element, stored_addr, _ctx, _clk) = self
.elements_read
.pop_front()
.ok_or(MemoryError::MemoryReadFailed("memory elements replay is empty".to_string()))?;
debug_assert_eq!(stored_addr, addr, "Address mismatch: expected {addr}, got {stored_addr}");
Ok(element)
}
pub fn replay_read_word(&mut self, addr: Felt) -> Result<Word, MemoryError> {
let (word, stored_addr, _ctx, _clk) = self
.words_read
.pop_front()
.ok_or(MemoryError::MemoryReadFailed("memory words replay is empty".to_string()))?;
debug_assert_eq!(stored_addr, addr, "Address mismatch: expected {addr}, got {stored_addr}");
Ok(word)
}
pub fn iter_read_elements(&self) -> impl Iterator<Item = (Felt, Felt, ContextId, RowIndex)> {
self.elements_read.iter().copied()
}
pub fn iter_read_words(&self) -> impl Iterator<Item = (Word, Felt, ContextId, RowIndex)> {
self.words_read.iter().copied()
}
}
#[derive(Debug, Default)]
pub struct MemoryWritesReplay {
elements_written: VecDeque<(Felt, Felt, ContextId, RowIndex)>,
words_written: VecDeque<(Word, Felt, ContextId, RowIndex)>,
}
impl MemoryWritesReplay {
pub fn record_write_element(
&mut self,
element: Felt,
addr: Felt,
ctx: ContextId,
clk: RowIndex,
) {
self.elements_written.push_back((element, addr, ctx, clk));
}
pub fn record_write_word(&mut self, word: Word, addr: Felt, ctx: ContextId, clk: RowIndex) {
self.words_written.push_back((word, addr, ctx, clk));
}
pub fn iter_elements_written(
&self,
) -> impl Iterator<Item = &(Felt, Felt, ContextId, RowIndex)> {
self.elements_written.iter()
}
pub fn iter_words_written(&self) -> impl Iterator<Item = &(Word, Felt, ContextId, RowIndex)> {
self.words_written.iter()
}
}
impl MemoryInterface for MemoryReadsReplay {
fn read_element(&mut self, _ctx: ContextId, addr: Felt) -> Result<Felt, MemoryError> {
self.replay_read_element(addr)
}
fn read_word(
&mut self,
_ctx: ContextId,
addr: Felt,
_clk: RowIndex,
) -> Result<Word, MemoryError> {
self.replay_read_word(addr)
}
fn write_element(
&mut self,
_ctx: ContextId,
_addr: Felt,
_element: Felt,
) -> Result<(), MemoryError> {
Ok(())
}
fn write_word(
&mut self,
_ctx: ContextId,
_addr: Felt,
_clk: RowIndex,
_word: Word,
) -> Result<(), MemoryError> {
Ok(())
}
}
#[derive(Debug, Default)]
pub struct AdviceReplay {
stack_pops: VecDeque<Felt>,
stack_word_pops: VecDeque<Word>,
stack_dword_pops: VecDeque<[Word; 2]>,
}
impl AdviceReplay {
pub fn record_pop_stack(&mut self, value: Felt) {
self.stack_pops.push_back(value);
}
pub fn record_pop_stack_word(&mut self, word: Word) {
self.stack_word_pops.push_back(word);
}
pub fn record_pop_stack_dword(&mut self, dword: [Word; 2]) {
self.stack_dword_pops.push_back(dword);
}
pub fn replay_pop_stack(&mut self) -> Result<Felt, ExecutionError> {
self.stack_pops
.pop_front()
.ok_or(ExecutionError::Internal("no stack pop operations recorded"))
}
pub fn replay_pop_stack_word(&mut self) -> Result<Word, ExecutionError> {
self.stack_word_pops
.pop_front()
.ok_or(ExecutionError::Internal("no stack word pop operations recorded"))
}
pub fn replay_pop_stack_dword(&mut self) -> Result<[Word; 2], ExecutionError> {
self.stack_dword_pops
.pop_front()
.ok_or(ExecutionError::Internal("no stack dword pop operations recorded"))
}
}
impl AdviceProviderInterface for AdviceReplay {
fn pop_stack(&mut self) -> Result<Felt, AdviceError> {
self.replay_pop_stack().map_err(|_| AdviceError::StackReadFailed)
}
fn pop_stack_word(&mut self) -> Result<Word, AdviceError> {
self.replay_pop_stack_word().map_err(|_| AdviceError::StackReadFailed)
}
fn pop_stack_dword(&mut self) -> Result<[Word; 2], AdviceError> {
self.replay_pop_stack_dword().map_err(|_| AdviceError::StackReadFailed)
}
fn get_merkle_path(
&self,
_root: Word,
_depth: Felt,
_index: Felt,
) -> Result<Option<MerklePath>, AdviceError> {
Ok(None)
}
fn update_merkle_node(
&mut self,
_root: Word,
_depth: Felt,
_index: Felt,
_value: Word,
) -> Result<Option<MerklePath>, AdviceError> {
Ok(None)
}
}
#[derive(Debug)]
pub enum BitwiseOp {
U32And,
U32Xor,
}
#[derive(Debug, Default)]
pub struct BitwiseReplay {
u32op_with_operands: VecDeque<(BitwiseOp, Felt, Felt)>,
}
impl BitwiseReplay {
pub fn record_u32and(&mut self, a: Felt, b: Felt) {
self.u32op_with_operands.push_back((BitwiseOp::U32And, a, b));
}
pub fn record_u32xor(&mut self, a: Felt, b: Felt) {
self.u32op_with_operands.push_back((BitwiseOp::U32Xor, a, b));
}
}
impl IntoIterator for BitwiseReplay {
type Item = (BitwiseOp, Felt, Felt);
type IntoIter = <VecDeque<(BitwiseOp, Felt, Felt)> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.u32op_with_operands.into_iter()
}
}
#[derive(Debug, Default)]
pub struct KernelReplay {
kernel_proc_accesses: VecDeque<Word>,
}
impl KernelReplay {
pub fn record_kernel_proc_access(&mut self, proc_hash: Word) {
self.kernel_proc_accesses.push_back(proc_hash);
}
}
impl IntoIterator for KernelReplay {
type Item = Word;
type IntoIter = <VecDeque<Word> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.kernel_proc_accesses.into_iter()
}
}
#[derive(Debug, Default)]
pub struct AceReplay {
circuit_evaluations: VecDeque<(RowIndex, CircuitEvaluation)>,
}
impl AceReplay {
pub fn record_circuit_evaluation(&mut self, circuit_eval: CircuitEvaluation) {
let clk = RowIndex::from(circuit_eval.clk());
self.circuit_evaluations.push_back((clk, circuit_eval));
}
}
impl IntoIterator for AceReplay {
type Item = (RowIndex, CircuitEvaluation);
type IntoIter = <VecDeque<(RowIndex, CircuitEvaluation)> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.circuit_evaluations.into_iter()
}
}
#[derive(Debug, Default)]
pub struct RangeCheckerReplay {
range_checks_u32_ops: VecDeque<(RowIndex, [u16; 4])>,
}
impl RangeCheckerReplay {
pub fn record_range_check_u32(&mut self, row_index: RowIndex, u16_limbs: [u16; 4]) {
self.range_checks_u32_ops.push_back((row_index, u16_limbs));
}
}
impl IntoIterator for RangeCheckerReplay {
type Item = (RowIndex, [u16; 4]);
type IntoIter = <VecDeque<(RowIndex, [u16; 4])> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.range_checks_u32_ops.into_iter()
}
}
#[derive(Debug, Default)]
pub struct BlockAddressReplay {
block_addresses: VecDeque<Felt>,
}
impl BlockAddressReplay {
pub fn record_block_address(&mut self, addr: Felt) {
self.block_addresses.push_back(addr);
}
pub fn replay_block_address(&mut self) -> Result<Felt, ExecutionError> {
self.block_addresses
.pop_front()
.ok_or(ExecutionError::Internal("no block address operations recorded"))
}
}
#[derive(Debug, Default)]
pub struct HasherResponseReplay {
permutation_operations: VecDeque<(Felt, [Felt; 12])>,
build_merkle_root_operations: VecDeque<(Felt, Word)>,
mrupdate_operations: VecDeque<(Felt, Word, Word)>,
}
impl HasherResponseReplay {
pub fn record_permute(&mut self, addr: Felt, hashed_state: [Felt; 12]) {
self.permutation_operations.push_back((addr, hashed_state));
}
pub fn record_build_merkle_root(&mut self, addr: Felt, computed_root: Word) {
self.build_merkle_root_operations.push_back((addr, computed_root));
}
pub fn record_update_merkle_root(&mut self, addr: Felt, old_root: Word, new_root: Word) {
self.mrupdate_operations.push_back((addr, old_root, new_root));
}
pub fn replay_permute(&mut self) -> Result<(Felt, [Felt; 12]), OperationError> {
self.permutation_operations
.pop_front()
.ok_or(OperationError::Internal("no permutation operations recorded"))
}
pub fn replay_build_merkle_root(&mut self) -> Result<(Felt, Word), OperationError> {
self.build_merkle_root_operations
.pop_front()
.ok_or(OperationError::Internal("no build merkle root operations recorded"))
}
pub fn replay_update_merkle_root(&mut self) -> Result<(Felt, Word, Word), OperationError> {
self.mrupdate_operations
.pop_front()
.ok_or(OperationError::Internal("no mrupdate operations recorded"))
}
}
impl HasherInterface for HasherResponseReplay {
fn permute(&mut self, _state: HasherState) -> Result<(Felt, HasherState), OperationError> {
self.replay_permute()
}
fn verify_merkle_root(
&mut self,
claimed_root: Word,
_value: Word,
_path: Option<&MerklePath>,
_index: Felt,
on_err: impl FnOnce() -> OperationError,
) -> Result<Felt, OperationError> {
let (addr, computed_root) = self.replay_build_merkle_root()?;
if claimed_root == computed_root {
Ok(addr)
} else {
Err(on_err())
}
}
fn update_merkle_root(
&mut self,
claimed_old_root: Word,
_old_value: Word,
_new_value: Word,
_path: Option<&MerklePath>,
_index: Felt,
on_err: impl FnOnce() -> OperationError,
) -> Result<(Felt, Word), OperationError> {
let (address, old_root, new_root) = self.replay_update_merkle_root()?;
if claimed_old_root == old_root {
Ok((address, new_root))
} else {
Err(on_err())
}
}
}
#[derive(Debug)]
pub enum HasherOp {
Permute([Felt; STATE_WIDTH]),
HashControlBlock((Word, Word, Felt, Word)),
HashBasicBlock((Arc<MastForest>, MastNodeId, Word)),
BuildMerkleRoot((Word, MerklePath, Felt)),
UpdateMerkleRoot((Word, Word, MerklePath, Felt)),
}
#[derive(Debug, Default)]
pub struct HasherRequestReplay {
hasher_ops: VecDeque<HasherOp>,
}
impl HasherRequestReplay {
pub fn record_permute_input(&mut self, state: [Felt; STATE_WIDTH]) {
self.hasher_ops.push_back(HasherOp::Permute(state));
}
pub fn record_hash_control_block(
&mut self,
h1: Word,
h2: Word,
domain: Felt,
expected_hash: Word,
) {
self.hasher_ops
.push_back(HasherOp::HashControlBlock((h1, h2, domain, expected_hash)));
}
pub fn record_hash_basic_block(
&mut self,
forest: Arc<MastForest>,
node_id: MastNodeId,
expected_hash: Word,
) {
self.hasher_ops
.push_back(HasherOp::HashBasicBlock((forest, node_id, expected_hash)));
}
pub fn record_build_merkle_root(&mut self, leaf: Word, path: MerklePath, index: Felt) {
self.hasher_ops.push_back(HasherOp::BuildMerkleRoot((leaf, path, index)));
}
pub fn record_update_merkle_root(
&mut self,
old_value: Word,
new_value: Word,
path: MerklePath,
index: Felt,
) {
self.hasher_ops
.push_back(HasherOp::UpdateMerkleRoot((old_value, new_value, path, index)));
}
}
impl IntoIterator for HasherRequestReplay {
type Item = HasherOp;
type IntoIter = <VecDeque<HasherOp> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.hasher_ops.into_iter()
}
}
#[derive(Debug)]
pub struct StackOverflowReplay {
overflow_values: VecDeque<(Felt, Felt)>,
restore_context_info: VecDeque<(usize, Felt)>,
}
impl Default for StackOverflowReplay {
fn default() -> Self {
Self::new()
}
}
impl StackOverflowReplay {
pub fn new() -> Self {
Self {
overflow_values: VecDeque::new(),
restore_context_info: VecDeque::new(),
}
}
pub fn record_pop_overflow(&mut self, value: Felt, new_overflow_addr: Felt) {
self.overflow_values.push_back((value, new_overflow_addr));
}
pub fn record_restore_context_overflow_addr(&mut self, stack_depth: usize, addr: Felt) {
self.restore_context_info.push_back((stack_depth, addr));
}
pub fn peek_replay_pop_overflow(&self) -> Option<&(Felt, Felt)> {
self.overflow_values.front()
}
pub fn replay_pop_overflow(&mut self) -> Result<(Felt, Felt), OperationError> {
self.overflow_values
.pop_front()
.ok_or(OperationError::Internal("no overflow pop operations recorded"))
}
pub fn replay_restore_context_overflow_addr(
&mut self,
) -> Result<(usize, Felt), OperationError> {
self.restore_context_info
.pop_front()
.ok_or(OperationError::Internal("no overflow address operations recorded"))
}
}