use std::collections::HashMap;
use cairo_vm::{
hint_processor::{
builtin_hint_processor::hint_utils::{
get_ptr_from_var_name, insert_value_from_var_name, insert_value_into_ap,
},
hint_processor_definition::HintReference,
},
serde::deserialize_program::ApTracking,
types::exec_scope::ExecutionScopes,
vm::{
errors::hint_errors::HintError, runners::builtin_runner::OutputBuiltinState,
vm_core::VirtualMachine,
},
};
use crate::hints::{
fact_topologies::{
add_consecutive_output_pages, write_to_fact_topologies_file, GPS_FACT_TOPOLOGY,
},
types::BOOTLOADER_CONFIG_SIZE,
utils::get_program_input_value,
};
use super::{
fact_topologies::FactTopology, vars, ApplicativeBootloaderInput, BootloaderInput,
SimpleBootloaderInput, APPLICATIVE_BOOTLOADER_INPUT,
};
pub fn aggregator_program_hash_function_to_ap(
vm: &mut VirtualMachine,
exec_scopes: &mut ExecutionScopes,
) -> Result<(), HintError> {
let applicative_bootloader_input: &ApplicativeBootloaderInput =
exec_scopes.get_ref(vars::APPLICATIVE_BOOTLOADER_INPUT)?;
let program_hash_function = applicative_bootloader_input
.aggregator_task
.program_hash_function;
insert_value_into_ap(vm, program_hash_function as usize)
}
pub fn prepare_aggregator_simple_bootloader_output_segment(
vm: &mut VirtualMachine,
exec_scopes: &mut ExecutionScopes,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
let applicative_bootloader_input: ApplicativeBootloaderInput =
get_program_input_value(exec_scopes)?;
let new_segment_base = vm.add_memory_segment();
insert_value_from_var_name(
"aggregator_output_ptr",
new_segment_base,
vm,
ids_data,
ap_tracking,
)?;
let simple_bootloader_input: SimpleBootloaderInput = SimpleBootloaderInput {
tasks: vec![applicative_bootloader_input.aggregator_task.clone()],
fact_topologies_path: None,
single_page: true,
};
exec_scopes.insert_value(APPLICATIVE_BOOTLOADER_INPUT, applicative_bootloader_input);
exec_scopes.insert_value(vars::SIMPLE_BOOTLOADER_INPUT, simple_bootloader_input);
let output_builtin = vm.get_output_builtin_mut()?;
let applicative_output_builtin_state = output_builtin.get_state();
output_builtin.new_state(new_segment_base.segment_index as usize, 0, true);
exec_scopes.insert_value(
vars::APPLICATIVE_OUTPUT_BUILTIN_STATE,
applicative_output_builtin_state,
);
insert_value_from_var_name(
"aggregator_output_ptr",
new_segment_base,
vm,
ids_data,
ap_tracking,
)?;
Ok(())
}
pub fn prepare_root_task_unpacker_bootloader_output_segment(
vm: &mut VirtualMachine,
exec_scopes: &mut ExecutionScopes,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
let fact_topologies: Vec<FactTopology> = exec_scopes.get(vars::FACT_TOPOLOGIES)?;
exec_scopes.insert_value(vars::AGGREGATOR_FACT_TOPOLOGIES, fact_topologies);
exec_scopes.insert_value(vars::FACT_TOPOLOGIES, Vec::<FactTopology>::new());
let new_segment_base = vm.add_memory_segment();
insert_value_from_var_name(
"bootloader_output_ptr",
new_segment_base,
vm,
ids_data,
ap_tracking,
)?;
let applicative_bootloader_input: &ApplicativeBootloaderInput =
exec_scopes.get_ref(vars::APPLICATIVE_BOOTLOADER_INPUT)?;
let simple_bootloader_input = SimpleBootloaderInput {
tasks: applicative_bootloader_input
.bootloader_input
.simple_bootloader_input
.tasks
.clone(),
fact_topologies_path: None,
single_page: true,
};
let bootloader_input = BootloaderInput {
simple_bootloader_input,
bootloader_config: applicative_bootloader_input
.bootloader_input
.bootloader_config
.clone(),
packed_outputs: applicative_bootloader_input
.bootloader_input
.packed_outputs
.clone(),
};
exec_scopes.insert_value(vars::BOOTLOADER_INPUT, bootloader_input);
let output_builtin = vm.get_output_builtin_mut()?;
output_builtin.new_state(new_segment_base.segment_index as usize, 0, true);
Ok(())
}
pub fn restore_applicative_output_state(
vm: &mut VirtualMachine,
exec_scopes: &mut ExecutionScopes,
) -> Result<(), HintError> {
let output_builtin_state: OutputBuiltinState =
exec_scopes.get(vars::APPLICATIVE_OUTPUT_BUILTIN_STATE)?;
vm.get_output_builtin_mut()?.set_state(output_builtin_state);
Ok(())
}
pub fn finalize_fact_topologies_and_pages(
vm: &mut VirtualMachine,
exec_scopes: &mut ExecutionScopes,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
let aggregator_fact_topologies: Vec<FactTopology> =
exec_scopes.get(vars::AGGREGATOR_FACT_TOPOLOGIES)?;
if aggregator_fact_topologies.len() != 1 {
return Err(HintError::CustomHint(
"Expected exactly one fact topology for the aggregator task".into(),
));
}
let aggregator_fact_topology = aggregator_fact_topologies.first().unwrap();
let original_first_page_length = aggregator_fact_topology.page_sizes[0];
let header_size = 1 + BOOTLOADER_CONFIG_SIZE;
let bootloader_output_end =
get_ptr_from_var_name("bootloader_output_end", vm, ids_data, ap_tracking)?;
let bootloader_output_start =
get_ptr_from_var_name("bootloader_tasks_output_ptr", vm, ids_data, ap_tracking)?;
let bootloader_tasks_output_length =
bootloader_output_end.offset - bootloader_output_start.offset;
let first_page_length =
original_first_page_length - bootloader_tasks_output_length + header_size;
let fact_topology = vec![FactTopology {
tree_structure: aggregator_fact_topology.tree_structure.clone(),
page_sizes: vec![first_page_length]
.into_iter()
.chain(aggregator_fact_topology.page_sizes[1..].to_vec())
.collect(),
}];
let output_start = get_ptr_from_var_name("output_start", vm, ids_data, ap_tracking)?;
let output_builtin = vm.get_output_builtin_mut()?;
output_builtin.add_attribute(
GPS_FACT_TOPOLOGY.into(),
fact_topology[0].tree_structure.clone(),
);
let output_start = (output_start + fact_topology[0].page_sizes[0])?;
let _ = add_consecutive_output_pages(
&fact_topology[0].page_sizes[1..],
output_builtin,
1, output_start,
)?;
let applicative_bootloader_input: &ApplicativeBootloaderInput =
exec_scopes.get_ref(vars::APPLICATIVE_BOOTLOADER_INPUT)?;
if let Some(path) = &applicative_bootloader_input
.bootloader_input
.simple_bootloader_input
.fact_topologies_path
{
write_to_fact_topologies_file(path.as_path(), &fact_topology)
.map_err(Into::<HintError>::into)?;
}
Ok(())
}