use alloc::vec::Vec;
use miden_core::{
Felt, FieldElement, QuadFelt, WORD_SIZE, Word, ZERO, crypto::hash::Rpo256,
sys_events::SystemEvent,
};
use crate::{ExecutionError, ProcessState, errors::ErrorContext};
pub const HDWORD_TO_MAP_WITH_DOMAIN_DOMAIN_OFFSET: usize = 9;
pub fn handle_system_event(
process: &mut ProcessState,
system_event: SystemEvent,
err_ctx: &impl ErrorContext,
) -> Result<(), ExecutionError> {
match system_event {
SystemEvent::MerkleNodeMerge => merge_merkle_nodes(process, err_ctx),
SystemEvent::MerkleNodeToStack => copy_merkle_node_to_adv_stack(process, err_ctx),
SystemEvent::MapValueToStack => copy_map_value_to_adv_stack(process, false, err_ctx),
SystemEvent::MapValueToStackN => copy_map_value_to_adv_stack(process, true, err_ctx),
SystemEvent::HasMapKey => push_key_presence_flag(process),
SystemEvent::Ext2Inv => push_ext2_inv_result(process, err_ctx),
SystemEvent::U32Clz => push_leading_zeros(process, err_ctx),
SystemEvent::U32Ctz => push_trailing_zeros(process, err_ctx),
SystemEvent::U32Clo => push_leading_ones(process, err_ctx),
SystemEvent::U32Cto => push_trailing_ones(process, err_ctx),
SystemEvent::ILog2 => push_ilog2(process, err_ctx),
SystemEvent::MemToMap => insert_mem_values_into_adv_map(process, err_ctx),
SystemEvent::HdwordToMap => insert_hdword_into_adv_map(process, ZERO, err_ctx),
SystemEvent::HdwordToMapWithDomain => {
let domain = process.get_stack_item(HDWORD_TO_MAP_WITH_DOMAIN_DOMAIN_OFFSET);
insert_hdword_into_adv_map(process, domain, err_ctx)
},
SystemEvent::HqwordToMap => insert_hqword_into_adv_map(process, err_ctx),
SystemEvent::HpermToMap => insert_hperm_into_adv_map(process, err_ctx),
}
}
fn insert_mem_values_into_adv_map(
process: &mut ProcessState,
err_ctx: &impl ErrorContext,
) -> Result<(), ExecutionError> {
let addr_range = process.get_mem_addr_range(5, 6).map_err(ExecutionError::MemoryError)?;
let ctx = process.ctx();
let mut values = Vec::with_capacity(addr_range.len() * WORD_SIZE);
for addr in addr_range {
let mem_value = process.get_mem_value(ctx, addr).unwrap_or(ZERO);
values.push(mem_value);
}
let key = process.get_stack_word_be(1);
process
.advice_provider_mut()
.insert_into_map(key, values)
.map_err(|err| ExecutionError::advice_error(err, process.clk(), err_ctx))
}
fn insert_hdword_into_adv_map(
process: &mut ProcessState,
domain: Felt,
err_ctx: &impl ErrorContext,
) -> Result<(), ExecutionError> {
let word0 = process.get_stack_word_be(1);
let word1 = process.get_stack_word_be(5);
let key = Rpo256::merge_in_domain(&[word1, word0], domain);
let mut values = Vec::with_capacity(2 * WORD_SIZE);
values.extend_from_slice(&Into::<[Felt; WORD_SIZE]>::into(word1));
values.extend_from_slice(&Into::<[Felt; WORD_SIZE]>::into(word0));
process
.advice_provider_mut()
.insert_into_map(key, values)
.map_err(|err| ExecutionError::advice_error(err, process.clk(), err_ctx))
}
fn insert_hqword_into_adv_map(
process: &mut ProcessState,
err_ctx: &impl ErrorContext,
) -> Result<(), ExecutionError> {
let word0 = process.get_stack_word_be(1);
let word1 = process.get_stack_word_be(5);
let word2 = process.get_stack_word_be(9);
let word3 = process.get_stack_word_be(13);
let key = Rpo256::hash_elements(&[*word3, *word2, *word1, *word0].concat());
let mut values = Vec::with_capacity(4 * WORD_SIZE);
values.extend_from_slice(&Into::<[Felt; WORD_SIZE]>::into(word3));
values.extend_from_slice(&Into::<[Felt; WORD_SIZE]>::into(word2));
values.extend_from_slice(&Into::<[Felt; WORD_SIZE]>::into(word1));
values.extend_from_slice(&Into::<[Felt; WORD_SIZE]>::into(word0));
process
.advice_provider_mut()
.insert_into_map(key, values)
.map_err(|err| ExecutionError::advice_error(err, process.clk(), err_ctx))
}
fn insert_hperm_into_adv_map(
process: &mut ProcessState,
err_ctx: &impl ErrorContext,
) -> Result<(), ExecutionError> {
let mut state = [
process.get_stack_item(12),
process.get_stack_item(11),
process.get_stack_item(10),
process.get_stack_item(9),
process.get_stack_item(8),
process.get_stack_item(7),
process.get_stack_item(6),
process.get_stack_item(5),
process.get_stack_item(4),
process.get_stack_item(3),
process.get_stack_item(2),
process.get_stack_item(1),
];
let values = state[Rpo256::RATE_RANGE].to_vec();
Rpo256::apply_permutation(&mut state);
let key = Word::new(
state[Rpo256::DIGEST_RANGE]
.try_into()
.expect("failed to extract digest from state"),
);
process
.advice_provider_mut()
.insert_into_map(key, values)
.map_err(|err| ExecutionError::advice_error(err, process.clk(), err_ctx))
}
fn merge_merkle_nodes(
process: &mut ProcessState,
err_ctx: &impl ErrorContext,
) -> Result<(), ExecutionError> {
let lhs = process.get_stack_word_be(5);
let rhs = process.get_stack_word_be(1);
process
.advice_provider_mut()
.merge_roots(lhs, rhs)
.map_err(|err| ExecutionError::advice_error(err, process.clk(), err_ctx))?;
Ok(())
}
fn copy_merkle_node_to_adv_stack(
process: &mut ProcessState,
err_ctx: &impl ErrorContext,
) -> Result<(), ExecutionError> {
let depth = process.get_stack_item(1);
let index = process.get_stack_item(2);
let root = process.get_stack_word_be(3);
let node = process
.advice_provider()
.get_tree_node(root, depth, index)
.map_err(|err| ExecutionError::advice_error(err, process.clk(), err_ctx))?;
process.advice_provider_mut().push_stack_word(&node);
Ok(())
}
fn copy_map_value_to_adv_stack(
process: &mut ProcessState,
include_len: bool,
err_ctx: &impl ErrorContext,
) -> Result<(), ExecutionError> {
let key = process.get_stack_word_be(1);
process
.advice_provider_mut()
.push_from_map(key, include_len)
.map_err(|err| ExecutionError::advice_error(err, process.clk(), err_ctx))?;
Ok(())
}
pub fn push_key_presence_flag(process: &mut ProcessState) -> Result<(), ExecutionError> {
let map_key = process.get_stack_word_be(1);
let presence_flag = process.advice_provider().contains_map_key(&map_key);
process.advice_provider_mut().push_stack(Felt::from(presence_flag));
Ok(())
}
fn push_ext2_inv_result(
process: &mut ProcessState,
err_ctx: &impl ErrorContext,
) -> Result<(), ExecutionError> {
let coef0 = process.get_stack_item(2);
let coef1 = process.get_stack_item(1);
let element = QuadFelt::new(coef0, coef1);
if element == QuadFelt::ZERO {
return Err(ExecutionError::divide_by_zero(process.clk(), err_ctx));
}
let result = element.inv().to_base_elements();
process.advice_provider_mut().push_stack(result[1]);
process.advice_provider_mut().push_stack(result[0]);
Ok(())
}
fn push_leading_zeros(
process: &mut ProcessState,
err_ctx: &impl ErrorContext,
) -> Result<(), ExecutionError> {
push_transformed_stack_top(process, |stack_top| Felt::from(stack_top.leading_zeros()), err_ctx)
}
fn push_trailing_zeros(
process: &mut ProcessState,
err_ctx: &impl ErrorContext,
) -> Result<(), ExecutionError> {
push_transformed_stack_top(process, |stack_top| Felt::from(stack_top.trailing_zeros()), err_ctx)
}
fn push_leading_ones(
process: &mut ProcessState,
err_ctx: &impl ErrorContext,
) -> Result<(), ExecutionError> {
push_transformed_stack_top(process, |stack_top| Felt::from(stack_top.leading_ones()), err_ctx)
}
fn push_trailing_ones(
process: &mut ProcessState,
err_ctx: &impl ErrorContext,
) -> Result<(), ExecutionError> {
push_transformed_stack_top(process, |stack_top| Felt::from(stack_top.trailing_ones()), err_ctx)
}
fn push_ilog2(
process: &mut ProcessState,
err_ctx: &impl ErrorContext,
) -> Result<(), ExecutionError> {
let n = process.get_stack_item(1).as_int();
if n == 0 {
return Err(ExecutionError::log_argument_zero(process.clk(), err_ctx));
}
let ilog2 = Felt::from(n.ilog2());
process.advice_provider_mut().push_stack(ilog2);
Ok(())
}
fn push_transformed_stack_top(
process: &mut ProcessState,
f: impl FnOnce(u32) -> Felt,
err_ctx: &impl ErrorContext,
) -> Result<(), ExecutionError> {
let stack_top = process.get_stack_item(1);
let stack_top: u32 = stack_top
.as_int()
.try_into()
.map_err(|_| ExecutionError::not_u32_value(stack_top, ZERO, err_ctx))?;
let transformed_stack_top = f(stack_top);
process.advice_provider_mut().push_stack(transformed_stack_top);
Ok(())
}