use miden_air::trace::{Challenges, RowIndex};
use miden_core::{Word, ZERO, field::ExtensionField, operations::opcodes};
use super::{AuxColumnBuilder, Felt, MainTrace, ONE};
use crate::debug::BusDebugger;
#[derive(Default)]
pub struct BlockHashTableColumnBuilder {}
impl<E: ExtensionField<Felt>> AuxColumnBuilder<E> for BlockHashTableColumnBuilder {
fn get_requests_at(
&self,
main_trace: &MainTrace,
challenges: &Challenges<E>,
row: RowIndex,
_debugger: &mut BusDebugger<E>,
) -> E {
let op_code = main_trace.get_op_code(row).as_canonical_u64() as u8;
match op_code {
opcodes::END => BlockHashTableRow::from_end(main_trace, row).collapse(challenges),
_ => E::ONE,
}
}
fn get_responses_at(
&self,
main_trace: &MainTrace,
challenges: &Challenges<E>,
row: RowIndex,
_debugger: &mut BusDebugger<E>,
) -> E {
let op_code = main_trace.get_op_code(row).as_canonical_u64() as u8;
match op_code {
opcodes::JOIN => {
let left_child_row = BlockHashTableRow::from_join(main_trace, row, true);
let right_child_row = BlockHashTableRow::from_join(main_trace, row, false);
left_child_row.collapse(challenges) * right_child_row.collapse(challenges)
},
opcodes::SPLIT => BlockHashTableRow::from_split(main_trace, row).collapse(challenges),
opcodes::LOOP => BlockHashTableRow::from_loop(main_trace, row)
.map(|row| row.collapse(challenges))
.unwrap_or(E::ONE),
opcodes::REPEAT => BlockHashTableRow::from_repeat(main_trace, row).collapse(challenges),
opcodes::DYN | opcodes::DYNCALL | opcodes::CALL | opcodes::SYSCALL => {
BlockHashTableRow::from_dyn_dyncall_call_syscall(main_trace, row)
.collapse(challenges)
},
_ => E::ONE,
}
}
#[cfg(any(test, feature = "bus-debugger"))]
fn enforce_bus_balance(&self) -> bool {
false
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BlockHashTableRow {
parent_block_id: Felt,
child_block_hash: Word,
is_first_child: bool,
is_loop_body: bool,
}
impl BlockHashTableRow {
pub fn from_end(main_trace: &MainTrace, row: RowIndex) -> Self {
let op_code_next = main_trace.get_op_code(row + 1).as_canonical_u64() as u8;
let parent_block_id = main_trace.addr(row + 1);
let child_block_hash = main_trace.decoder_hasher_state_first_half(row);
let is_first_child = op_code_next != opcodes::END
&& op_code_next != opcodes::REPEAT
&& op_code_next != opcodes::HALT;
let is_loop_body = match main_trace.is_loop_body_flag(row).as_canonical_u64() {
0 => false,
1 => true,
other => panic!("expected loop body flag to be 0 or 1, got {other}"),
};
Self {
parent_block_id,
child_block_hash,
is_first_child,
is_loop_body,
}
}
pub fn from_join(main_trace: &MainTrace, row: RowIndex, is_first_child: bool) -> Self {
let child_block_hash = if is_first_child {
main_trace.decoder_hasher_state_first_half(row)
} else {
main_trace.decoder_hasher_state_second_half(row)
};
Self {
parent_block_id: main_trace.addr(row + 1),
child_block_hash,
is_first_child,
is_loop_body: false,
}
}
pub fn from_split(main_trace: &MainTrace, row: RowIndex) -> Self {
let stack_top = main_trace.stack_element(0, row);
let parent_block_id = main_trace.addr(row + 1);
let is_first_child = false;
let is_loop_body = false;
if stack_top == ONE {
let left_child_block_hash = main_trace.decoder_hasher_state_first_half(row);
Self {
parent_block_id,
child_block_hash: left_child_block_hash,
is_first_child,
is_loop_body,
}
} else {
let right_child_block_hash = main_trace.decoder_hasher_state_second_half(row);
Self {
parent_block_id,
child_block_hash: right_child_block_hash,
is_first_child,
is_loop_body,
}
}
}
pub fn from_loop(main_trace: &MainTrace, row: RowIndex) -> Option<Self> {
let stack_top = main_trace.stack_element(0, row);
if stack_top == ONE {
Some(Self {
parent_block_id: main_trace.addr(row + 1),
child_block_hash: main_trace.decoder_hasher_state_first_half(row),
is_first_child: false,
is_loop_body: true,
})
} else {
None
}
}
pub fn from_repeat(main_trace: &MainTrace, row: RowIndex) -> Self {
Self {
parent_block_id: main_trace.addr(row + 1),
child_block_hash: main_trace.decoder_hasher_state_first_half(row),
is_first_child: false,
is_loop_body: true,
}
}
pub fn from_dyn_dyncall_call_syscall(main_trace: &MainTrace, row: RowIndex) -> Self {
Self {
parent_block_id: main_trace.addr(row + 1),
child_block_hash: main_trace.decoder_hasher_state_first_half(row),
is_first_child: false,
is_loop_body: false,
}
}
pub fn collapse<E: ExtensionField<Felt>>(&self, challenges: &Challenges<E>) -> E {
let is_first_child = if self.is_first_child { ONE } else { ZERO };
let is_loop_body = if self.is_loop_body { ONE } else { ZERO };
challenges.encode([
self.parent_block_id,
self.child_block_hash[0],
self.child_block_hash[1],
self.child_block_hash[2],
self.child_block_hash[3],
is_first_child,
is_loop_body,
])
}
#[cfg(test)]
pub fn new_test(
parent_id: Felt,
block_hash: Word,
is_first_child: bool,
is_loop_body: bool,
) -> Self {
Self {
parent_block_id: parent_id,
child_block_hash: block_hash,
is_first_child,
is_loop_body,
}
}
}