use miden_air::RowIndex;
use miden_core::{
OPCODE_CALL, OPCODE_DYN, OPCODE_DYNCALL, OPCODE_END, OPCODE_HALT, OPCODE_JOIN, OPCODE_LOOP,
OPCODE_REPEAT, OPCODE_SPLIT, OPCODE_SYSCALL, Word, ZERO,
};
use super::{AuxColumnBuilder, Felt, FieldElement, MainTrace, ONE};
use crate::debug::BusDebugger;
#[derive(Default)]
pub struct BlockHashTableColumnBuilder {}
impl<E: FieldElement<BaseField = Felt>> AuxColumnBuilder<E> for BlockHashTableColumnBuilder {
fn init_responses(
&self,
main_trace: &MainTrace,
alphas: &[E],
_debugger: &mut BusDebugger<E>,
) -> E {
BlockHashTableRow::table_init(main_trace).collapse(alphas)
}
fn get_requests_at(
&self,
main_trace: &MainTrace,
alphas: &[E],
row: RowIndex,
_debugger: &mut BusDebugger<E>,
) -> E {
let op_code = main_trace.get_op_code(row).as_int() as u8;
match op_code {
OPCODE_END => BlockHashTableRow::from_end(main_trace, row).collapse(alphas),
_ => E::ONE,
}
}
fn get_responses_at(
&self,
main_trace: &MainTrace,
alphas: &[E],
row: RowIndex,
_debugger: &mut BusDebugger<E>,
) -> E {
let op_code = main_trace.get_op_code(row).as_int() as u8;
match op_code {
OPCODE_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(alphas) * right_child_row.collapse(alphas)
},
OPCODE_SPLIT => BlockHashTableRow::from_split(main_trace, row).collapse(alphas),
OPCODE_LOOP => BlockHashTableRow::from_loop(main_trace, row)
.map(|row| row.collapse(alphas))
.unwrap_or(E::ONE),
OPCODE_REPEAT => BlockHashTableRow::from_repeat(main_trace, row).collapse(alphas),
OPCODE_DYN | OPCODE_DYNCALL | OPCODE_CALL | OPCODE_SYSCALL => {
BlockHashTableRow::from_dyn_dyncall_call_syscall(main_trace, row).collapse(alphas)
},
_ => E::ONE,
}
}
}
#[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 table_init(main_trace: &MainTrace) -> Self {
let program_hash =
main_trace.decoder_hasher_state_first_half(main_trace.last_program_row());
Self {
parent_block_id: ZERO,
child_block_hash: program_hash,
is_first_child: false,
is_loop_body: false,
}
}
pub fn from_end(main_trace: &MainTrace, row: RowIndex) -> Self {
let op_code_next = main_trace.get_op_code(row + 1).as_int() 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 != OPCODE_END
&& op_code_next != OPCODE_REPEAT
&& op_code_next != OPCODE_HALT;
let is_loop_body = main_trace
.is_loop_body_flag(row)
.try_into()
.expect("expected loop body flag to be a boolean");
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: FieldElement<BaseField = Felt>>(&self, alphas: &[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 };
alphas[0]
+ alphas[1].mul_base(self.parent_block_id)
+ alphas[2].mul_base(self.child_block_hash[0])
+ alphas[3].mul_base(self.child_block_hash[1])
+ alphas[4].mul_base(self.child_block_hash[2])
+ alphas[5].mul_base(self.child_block_hash[3])
+ alphas[6].mul_base(is_first_child)
+ alphas[7].mul_base(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,
}
}
}