use miden_air::trace::{
decoder::NUM_USER_OP_HELPERS,
log_precompile::{STATE_CAP_RANGE, STATE_RATE_0_RANGE, STATE_RATE_1_RANGE},
};
use miden_core::{
Felt, QuadFelt, Word, ZERO, chiplets::hasher::STATE_WIDTH, mast::MastForest,
stack::MIN_STACK_DEPTH, utils::range,
};
use crate::{
ErrorContext, ExecutionError,
fast::Tracer,
processor::{
AdviceProviderInterface, HasherInterface, MemoryInterface, OperationHelperRegisters,
Processor, StackInterface, SystemInterface,
},
};
#[inline(always)]
pub(super) fn op_hperm<P: Processor>(
processor: &mut P,
tracer: &mut impl Tracer,
) -> [Felt; NUM_USER_OP_HELPERS] {
let state_range = range(MIN_STACK_DEPTH - STATE_WIDTH, STATE_WIDTH);
let input_state: [Felt; STATE_WIDTH] = processor.stack().top()[state_range.clone()]
.try_into()
.expect("state range expected to be 12 elements");
let (addr, output_state) = processor.hasher().permute(input_state);
processor.stack().top_mut()[state_range].copy_from_slice(&output_state);
tracer.record_hasher_permute(input_state, output_state);
P::HelperRegisters::op_hperm_registers(addr)
}
#[inline(always)]
pub(super) fn op_mpverify<P: Processor>(
processor: &mut P,
err_code: Felt,
program: &MastForest,
err_ctx: &impl ErrorContext,
tracer: &mut impl Tracer,
) -> Result<[Felt; NUM_USER_OP_HELPERS], ExecutionError> {
let clk = processor.system().clk();
let node = processor.stack().get_word(0);
let depth = processor.stack().get(4);
let index = processor.stack().get(5);
let root = processor.stack().get_word(6);
let path = processor
.advice_provider()
.get_merkle_path(root, depth, index)
.map_err(|err| ExecutionError::advice_error(err, clk, err_ctx))?;
tracer.record_hasher_build_merkle_root(node, path.as_ref(), index, root);
let addr = processor.hasher().verify_merkle_root(root, node, path.as_ref(), index, || {
let err_msg = program.resolve_error_message(err_code);
ExecutionError::merkle_path_verification_failed(
node, index, root, err_code, err_msg, err_ctx,
)
})?;
Ok(P::HelperRegisters::op_merkle_path_registers(addr))
}
#[inline(always)]
pub(super) fn op_mrupdate<P: Processor>(
processor: &mut P,
err_ctx: &impl ErrorContext,
tracer: &mut impl Tracer,
) -> Result<[Felt; NUM_USER_OP_HELPERS], ExecutionError> {
let clk = processor.system().clk();
let old_value = processor.stack().get_word(0);
let depth = processor.stack().get(4);
let index = processor.stack().get(5);
let claimed_old_root = processor.stack().get_word(6);
let new_value = processor.stack().get_word(10);
let path = processor
.advice_provider()
.update_merkle_node(claimed_old_root, depth, index, new_value)
.map_err(|err| ExecutionError::advice_error(err, clk, err_ctx))?;
if let Some(path) = &path {
assert_eq!(path.len(), depth.as_int() as usize);
}
let (addr, new_root) = processor.hasher().update_merkle_root(
claimed_old_root,
old_value,
new_value,
path.as_ref(),
index,
|| {
ExecutionError::merkle_path_verification_failed(
old_value,
index,
claimed_old_root,
ZERO,
None,
err_ctx,
)
},
)?;
tracer.record_hasher_update_merkle_root(
old_value,
new_value,
path.as_ref(),
index,
claimed_old_root,
new_root,
);
processor.stack().set_word(0, &new_root);
Ok(P::HelperRegisters::op_merkle_path_registers(addr))
}
#[inline(always)]
pub(super) fn op_horner_eval_base<P: Processor>(
processor: &mut P,
err_ctx: &impl ErrorContext,
tracer: &mut impl Tracer,
) -> Result<[Felt; NUM_USER_OP_HELPERS], ExecutionError> {
const ALPHA_ADDR_INDEX: usize = 13;
const ACC_HIGH_INDEX: usize = 14;
const ACC_LOW_INDEX: usize = 15;
let clk = processor.system().clk();
let ctx = processor.system().ctx();
let coef: [Felt; 8] = core::array::from_fn(|i| processor.stack().get(i));
let (alpha, k0, k1) = {
let addr = processor.stack().get(ALPHA_ADDR_INDEX);
let word = processor
.memory()
.read_word(ctx, addr, clk, err_ctx)
.map_err(ExecutionError::MemoryError)?;
tracer.record_memory_read_word(
word,
addr,
processor.system().ctx(),
processor.system().clk(),
);
(QuadFelt::new(word[0], word[1]), word[2], word[3])
};
let acc_old =
QuadFelt::new(processor.stack().get(ACC_LOW_INDEX), processor.stack().get(ACC_HIGH_INDEX));
let acc_tmp = coef
.iter()
.rev()
.take(4)
.fold(acc_old, |acc, coef| QuadFelt::from(*coef) + alpha * acc);
let acc_new = coef
.iter()
.rev()
.skip(4)
.fold(acc_tmp, |acc, coef| QuadFelt::from(*coef) + alpha * acc);
let acc_new_base_elements = acc_new.to_base_elements();
processor.stack().set(ACC_HIGH_INDEX, acc_new_base_elements[1]);
processor.stack().set(ACC_LOW_INDEX, acc_new_base_elements[0]);
Ok(P::HelperRegisters::op_horner_eval_registers(alpha, k0, k1, acc_tmp))
}
#[inline(always)]
pub(super) fn op_log_precompile<P: Processor>(
processor: &mut P,
tracer: &mut impl Tracer,
) -> [Felt; NUM_USER_OP_HELPERS] {
let comm = processor.stack().get_word(0);
let tag = processor.stack().get_word(4);
let cap_prev = processor.precompile_transcript_state();
let mut hasher_state: [Felt; STATE_WIDTH] = [ZERO; 12];
hasher_state[STATE_CAP_RANGE].copy_from_slice(cap_prev.as_slice());
hasher_state[STATE_RATE_0_RANGE].copy_from_slice(tag.as_slice());
hasher_state[STATE_RATE_1_RANGE].copy_from_slice(comm.as_slice());
let (addr, output_state) = processor.hasher().permute(hasher_state);
let cap_next: Word = output_state[STATE_CAP_RANGE.clone()]
.try_into()
.expect("cap_next slice has length 4");
let r0: Word = output_state[STATE_RATE_0_RANGE.clone()]
.try_into()
.expect("r0 slice has length 4");
let r1: Word = output_state[STATE_RATE_1_RANGE.clone()]
.try_into()
.expect("r1 slice has length 4");
processor.set_precompile_transcript_state(cap_next);
processor.stack().set_word(0, &r1);
processor.stack().set_word(4, &r0);
processor.stack().set_word(8, &cap_next);
tracer.record_hasher_permute(hasher_state, output_state);
P::HelperRegisters::op_log_precompile_registers(addr, cap_prev)
}
#[inline(always)]
pub(super) fn op_horner_eval_ext<P: Processor>(
processor: &mut P,
err_ctx: &impl ErrorContext,
tracer: &mut impl Tracer,
) -> Result<[Felt; NUM_USER_OP_HELPERS], ExecutionError> {
const ALPHA_ADDR_INDEX: usize = 13;
const ACC_HIGH_INDEX: usize = 14;
const ACC_LOW_INDEX: usize = 15;
let clk = processor.system().clk();
let ctx = processor.system().ctx();
let coef = [
QuadFelt::new(processor.stack().get(1), processor.stack().get(0)), QuadFelt::new(processor.stack().get(3), processor.stack().get(2)), QuadFelt::new(processor.stack().get(5), processor.stack().get(4)), QuadFelt::new(processor.stack().get(7), processor.stack().get(6)), ];
let (alpha, k0, k1) = {
let addr = processor.stack().get(ALPHA_ADDR_INDEX);
let word = processor
.memory()
.read_word(ctx, addr, clk, err_ctx)
.map_err(ExecutionError::MemoryError)?;
tracer.record_memory_read_word(
word,
addr,
processor.system().ctx(),
processor.system().clk(),
);
(QuadFelt::new(word[0], word[1]), word[2], word[3])
};
let acc_old = QuadFelt::new(
processor.stack().get(ACC_LOW_INDEX), processor.stack().get(ACC_HIGH_INDEX), );
let acc_tmp = coef.iter().rev().take(2).fold(acc_old, |acc, coef| *coef + alpha * acc);
let acc_new = coef.iter().rev().skip(2).fold(acc_tmp, |acc, coef| *coef + alpha * acc);
let acc_new_base_elements = acc_new.to_base_elements();
processor.stack().set(ACC_HIGH_INDEX, acc_new_base_elements[1]);
processor.stack().set(ACC_LOW_INDEX, acc_new_base_elements[0]);
Ok(P::HelperRegisters::op_horner_eval_registers(alpha, k0, k1, acc_tmp))
}