use std::collections::HashMap;
use cairo_vm::hint_processor::builtin_hint_processor::hint_utils::{
get_constant_from_var_name, get_integer_from_var_name, get_ptr_from_var_name,
get_relocatable_from_var_name, insert_value_from_var_name, insert_value_into_ap,
};
use cairo_vm::hint_processor::hint_processor_definition::HintReference;
use cairo_vm::serde::deserialize_program::ApTracking;
use cairo_vm::types::errors::math_errors::MathError;
use cairo_vm::types::exec_scope::ExecutionScopes;
use cairo_vm::types::relocatable::{MaybeRelocatable, Relocatable};
use cairo_vm::vm::errors::hint_errors::HintError;
use cairo_vm::vm::runners::builtin_runner::{
EcOpBuiltinRunner, KeccakBuiltinRunner, SignatureBuiltinRunner,
};
use cairo_vm::vm::vm_core::VirtualMachine;
use cairo_vm::Felt252;
use num_bigint::BigUint;
use num_traits::ToPrimitive;
use starknet_types_core::felt::NonZeroFelt;
use super::types::HashFunc;
use crate::hints::fact_topologies::FactTopology;
use crate::hints::types::SimpleBootloaderInput;
use crate::hints::vars;
use super::utils::get_program_from_task;
pub fn setup_run_simple_bootloader_before_task_execution(
vm: &mut VirtualMachine,
exec_scopes: &mut ExecutionScopes,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
let simple_bootloader_input: &SimpleBootloaderInput =
exec_scopes.get_ref(vars::SIMPLE_BOOTLOADER_INPUT)?;
let n_tasks = simple_bootloader_input.tasks.len();
let output_ptr = get_ptr_from_var_name("output_ptr", vm, ids_data, ap_tracking)?;
vm.insert_value(output_ptr, Felt252::from(n_tasks))?;
let temp_segment = vm.add_temporary_segment();
insert_value_from_var_name(
"initial_subtasks_range_check_ptr",
temp_segment,
vm,
ids_data,
ap_tracking,
)?;
let fact_topologies = Vec::<FactTopology>::new();
exec_scopes.insert_value(vars::FACT_TOPOLOGIES, fact_topologies);
Ok(())
}
pub fn divide_num_by_2(
vm: &mut VirtualMachine,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
let felt = get_integer_from_var_name("num", vm, ids_data, ap_tracking)?;
let two = NonZeroFelt::try_from(Felt252::from(2)).unwrap();
let felt_divided_by_2 = felt.floor_div(&two);
insert_value_into_ap(vm, felt_divided_by_2)?;
Ok(())
}
pub fn set_ap_to_zero(vm: &mut VirtualMachine) -> Result<(), HintError> {
insert_value_into_ap(vm, Felt252::from(0))?;
Ok(())
}
pub fn program_hash_function_to_ap(
vm: &mut VirtualMachine,
exec_scopes: &mut ExecutionScopes,
) -> Result<(), HintError> {
let program_hash_function: HashFunc = exec_scopes.get(vars::PROGRAM_HASH_FUNCTION)?;
insert_value_into_ap(vm, program_hash_function as usize)
}
pub fn set_current_task(
vm: &mut VirtualMachine,
exec_scopes: &mut ExecutionScopes,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
let simple_bootloader_input: &SimpleBootloaderInput =
exec_scopes.get_ref(vars::SIMPLE_BOOTLOADER_INPUT)?;
let n_tasks_felt = get_integer_from_var_name("n_tasks", vm, ids_data, ap_tracking)?;
let n_tasks = n_tasks_felt
.to_usize()
.ok_or(MathError::Felt252ToUsizeConversion(Box::new(n_tasks_felt)))?;
let task_id = simple_bootloader_input.tasks.len() - n_tasks;
let task = simple_bootloader_input.tasks[task_id].load_task();
let program_hash_function = simple_bootloader_input.tasks[task_id].program_hash_function;
let mut use_prev_hash = 0;
if task_id > 0 {
let prev_task = simple_bootloader_input.tasks[task_id - 1].load_task();
use_prev_hash =
if get_program_from_task(task).unwrap() == get_program_from_task(prev_task).unwrap() {
1
} else {
0
};
}
exec_scopes.insert_value(vars::TASK, task.clone());
exec_scopes.insert_value(vars::USE_PREV_HASH, use_prev_hash);
exec_scopes.insert_value(vars::PROGRAM_HASH_FUNCTION, program_hash_function);
Ok(())
}
pub fn simple_bootloader_simulate_ec_op(
vm: &mut VirtualMachine,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
let mut ec_op_runner = EcOpBuiltinRunner::new(Some(1), false);
ec_op_runner.initialize_segments(&mut vm.segments);
let new_ec_op_ptr = Relocatable {
segment_index: ec_op_runner.base as isize,
offset: 0,
};
insert_value_from_var_name("new_ec_op_ptr", new_ec_op_ptr, vm, ids_data, ap_tracking)?;
vm.simulated_builtin_runners.push(ec_op_runner.into());
Ok(())
}
pub fn simulate_ec_op_fill_mem_with_bits_of_m(
vm: &mut VirtualMachine,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
constants: &HashMap<String, Felt252>,
) -> Result<(), HintError> {
let mut curr_m = get_integer_from_var_name("m", vm, ids_data, ap_tracking)?.to_biguint();
let m_max_bits = get_constant_from_var_name("M_MAX_BITS", constants)?;
let m_bit_unpacking = get_ptr_from_var_name("m_bit_unpacking", vm, ids_data, ap_tracking)?;
(0..m_max_bits.to_usize().unwrap()).try_for_each(|i| {
let bit = MaybeRelocatable::Int((&curr_m % 2u32).into());
curr_m >>= 1;
vm.insert_value((m_bit_unpacking + i)?, bit)
})?;
Ok(())
}
pub fn simulate_ec_op_assert_false() -> Result<(), HintError> {
Err(HintError::CustomHint("ec_op failed.".into()))
}
pub fn simple_bootloader_simulate_keccak(
vm: &mut VirtualMachine,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
let mut keccak_runner = KeccakBuiltinRunner::new(Some(1), false);
keccak_runner.initialize_segments(&mut vm.segments);
let new_keccak_ptr = Relocatable {
segment_index: keccak_runner.base as isize,
offset: 0,
};
insert_value_from_var_name("new_keccak_ptr", new_keccak_ptr, vm, ids_data, ap_tracking)?;
vm.simulated_builtin_runners.push(keccak_runner.into());
Ok(())
}
pub fn simulate_keccak_fill_mem_with_state(
vm: &mut VirtualMachine,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
let keccak_builtin_state_addr =
get_relocatable_from_var_name("keccak_builtin_state", vm, ids_data, ap_tracking)?;
let felt_array = get_ptr_from_var_name("felt_array", vm, ids_data, ap_tracking)?;
let mut full_num = (0..8).try_fold(BigUint::ZERO, |acc, i| {
let s = vm.get_integer((keccak_builtin_state_addr + i)?)?;
Ok::<_, HintError>(acc + (s.to_biguint() << (i * 200)))
})?;
let modulo = BigUint::from(1u128 << 64);
(0..25).try_for_each(|i| {
let felt = MaybeRelocatable::Int((&full_num % &modulo).into());
full_num >>= 64;
vm.insert_value((felt_array + i)?, felt)
})?;
Ok(())
}
pub fn simulate_keccak_calc_high_low(
vm: &mut VirtualMachine,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
index: usize,
) -> Result<(), HintError> {
let felt_array = get_ptr_from_var_name("felt_array", vm, ids_data, ap_tracking)?;
let felt = vm.get_integer((felt_array + index)?)?;
let x = index / 3;
let divisor = NonZeroFelt::try_from(Felt252::from(1u64 << (x * 8))).unwrap();
let (high_felt, low_felt) = felt.div_rem(&divisor);
insert_value_from_var_name(
&format!("high{index}"),
high_felt,
vm,
ids_data,
ap_tracking,
)?;
insert_value_from_var_name(&format!("low{index}"), low_felt, vm, ids_data, ap_tracking)?;
Ok(())
}
pub fn simple_bootloader_simulate_ecdsa(
vm: &mut VirtualMachine,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
let mut ecdsa_runner = SignatureBuiltinRunner::new(Some(1), false);
ecdsa_runner.initialize_segments(&mut vm.segments);
let new_ecdsa_ptr = Relocatable {
segment_index: ecdsa_runner.base as isize,
offset: 0,
};
insert_value_from_var_name("new_ecdsa_ptr", new_ecdsa_ptr, vm, ids_data, ap_tracking)?;
ecdsa_runner.add_validation_rule(&mut vm.segments.memory);
vm.simulated_builtin_runners.push(ecdsa_runner.into());
Ok(())
}
pub fn simulate_ecdsa_get_r_and_s(
vm: &mut VirtualMachine,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
let start = get_ptr_from_var_name("start", vm, ids_data, ap_tracking)?;
let ecdsa_builtin = vm.get_signature_builtin()?;
let (r, s) = {
let signatures = ecdsa_builtin.signatures.borrow();
let signature = signatures
.get(&start)
.ok_or_else(|| HintError::CustomHint("No signature found for start pointer.".into()))?;
(signature.r, signature.s)
};
insert_value_from_var_name("r", r, vm, ids_data, ap_tracking)?;
insert_value_from_var_name("s", s, vm, ids_data, ap_tracking)?;
Ok(())
}
pub fn simulate_ecdsa_compute_w_wr_wz(
vm: &mut VirtualMachine,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
constants: &HashMap<String, Felt252>,
) -> Result<(), HintError> {
let order_const_id = "starkware.cairo.common.ec.StarkCurve.ORDER";
let order = constants
.get(order_const_id)
.ok_or_else(|| HintError::MissingConstant(Box::new(order_const_id)))?;
let order = &NonZeroFelt::from_felt_unchecked(*order);
let s = get_integer_from_var_name("signature_s", vm, ids_data, ap_tracking)?;
let r = get_integer_from_var_name("signature_r", vm, ids_data, ap_tracking)?;
let message = get_integer_from_var_name("message", vm, ids_data, ap_tracking)?;
let w = s.mod_inverse(order).unwrap();
let wz = w.mul_mod(&message, order);
let wr = w.mul_mod(&r, order);
insert_value_from_var_name("w", w, vm, ids_data, ap_tracking)?;
insert_value_from_var_name("wz", wz, vm, ids_data, ap_tracking)?;
insert_value_from_var_name("wr", wr, vm, ids_data, ap_tracking)?;
Ok(())
}
pub fn simulate_ecdsa_fill_mem_with_felt_96_bit_limbs(
vm: &mut VirtualMachine,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
let num = get_integer_from_var_name("num", vm, ids_data, ap_tracking)?;
let res_96_felts = get_ptr_from_var_name("res_96_felts", vm, ids_data, ap_tracking)?;
let mut num = num.to_biguint();
let modulo = BigUint::from(1u128 << 96);
(0..3).try_for_each(|i| {
let felt = MaybeRelocatable::Int((&num % &modulo).into());
num >>= 96;
vm.insert_value((res_96_felts + i)?, felt)
})?;
Ok(())
}