cairo-vm 2.0.1

Blazing fast Cairo interpreter
Documentation
use crate::stdlib::{any::Any, boxed::Box, collections::HashMap, prelude::*};

use crate::any_box;
use crate::serde::deserialize_program::ApTracking;
use crate::serde::deserialize_program::OffsetValue;
use crate::serde::deserialize_program::Reference;
use crate::types::exec_scope::ExecutionScopes;
use crate::types::instruction::Register;
use crate::types::relocatable::Relocatable;
use crate::vm::errors::hint_errors::HintError;
use crate::vm::errors::vm_errors::VirtualMachineError;
use crate::vm::runners::cairo_runner::ResourceTracker;
use crate::vm::vm_core::VirtualMachine;

use super::builtin_hint_processor::builtin_hint_processor_definition::HintProcessorData;
use crate::Felt252;

#[cfg(feature = "test_utils")]
use arbitrary::Arbitrary;

pub trait HintProcessorLogic {
    // Executes the hint which's data is provided by a dynamic structure previously created by compile_hint
    // Note: if the `extensive_hints` feature is activated the method used by the vm to execute hints is `execute_hint_extensive`, which's default implementation calls this method.
    fn execute_hint(
        &mut self,
        vm: &mut VirtualMachine,
        exec_scopes: &mut ExecutionScopes,
        //Data structure that can be downcasted to the structure generated by compile_hint
        hint_data: &Box<dyn Any>,
        //Constant values extracted from the program specification.
        constants: &HashMap<String, Felt252>,
    ) -> Result<(), HintError>;

    //Transforms hint data outputed by the VM into whichever format will be later used by execute_hint
    fn compile_hint(
        &self,
        //Block of hint code as String
        hint_code: &str,
        //Ap Tracking Data corresponding to the Hint
        ap_tracking_data: &ApTracking,
        //Map from variable name to reference id number
        //(may contain other variables aside from those used by the hint)
        reference_ids: &HashMap<String, usize>,
        //List of all references (key corresponds to element of the previous dictionary)
        references: &[HintReference],
    ) -> Result<Box<dyn Any>, VirtualMachineError> {
        Ok(any_box!(HintProcessorData {
            code: hint_code.to_string(),
            ap_tracking: ap_tracking_data.clone(),
            ids_data: get_ids_data(reference_ids, references)?,
        }))
    }

    #[cfg(feature = "extensive_hints")]
    // Executes the hint which's data is provided by a dynamic structure previously created by compile_hint
    // Also returns a map of hints to be loaded after the current hint is executed
    // Note: This is the method used by the vm to execute hints,
    // if you chose to implement this method instead of using the default implementation, then `execute_hint` will not be used
    fn execute_hint_extensive(
        &mut self,
        vm: &mut VirtualMachine,
        exec_scopes: &mut ExecutionScopes,
        //Data structure that can be downcasted to the structure generated by compile_hint
        hint_data: &Box<dyn Any>,
        //Constant values extracted from the program specification.
        constants: &HashMap<String, Felt252>,
    ) -> Result<HintExtension, HintError> {
        self.execute_hint(vm, exec_scopes, hint_data, constants)?;
        Ok(HintExtension::default())
    }
}

// A map of hints that can be used to extend the current map of hints for the vm run
// The map matches the pc at which the hints should be executed to a vec of compiled hints (Outputed by HintProcessor::CompileHint)
pub type HintExtension = HashMap<Relocatable, Vec<Box<dyn Any>>>;

pub trait HintProcessor: HintProcessorLogic + ResourceTracker {}
impl<T> HintProcessor for T where T: HintProcessorLogic + ResourceTracker {}

fn get_ids_data(
    reference_ids: &HashMap<String, usize>,
    references: &[HintReference],
) -> Result<HashMap<String, HintReference>, VirtualMachineError> {
    let mut ids_data = HashMap::<String, HintReference>::new();
    for (path, ref_id) in reference_ids {
        let name = path
            .rsplit('.')
            .next()
            .ok_or(VirtualMachineError::Unexpected)?;
        ids_data.insert(
            name.to_string(),
            references
                .get(*ref_id)
                .ok_or(VirtualMachineError::Unexpected)?
                .clone(),
        );
    }
    Ok(ids_data)
}

#[cfg_attr(feature = "test_utils", derive(Arbitrary))]
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct HintReference {
    pub offset1: OffsetValue,
    pub offset2: OffsetValue,
    pub inner_dereference: bool,
    pub outer_dereference: bool,
    pub ap_tracking_data: Option<ApTracking>,
    pub cairo_type: Option<String>,
}

impl HintReference {
    pub fn new_simple(offset1: i32) -> Self {
        HintReference {
            offset1: OffsetValue::Reference(Register::FP, offset1, false, true),
            offset2: OffsetValue::Value(0),
            ap_tracking_data: None,
            outer_dereference: true,
            inner_dereference: false,
            cairo_type: None,
        }
    }

    pub fn new(
        offset1: i32,
        offset2: i32,
        inner_dereference: bool,
        dereference: bool,
        is_positive: bool,
    ) -> Self {
        HintReference {
            offset1: OffsetValue::Reference(Register::FP, offset1, inner_dereference, is_positive),
            offset2: OffsetValue::Value(offset2),
            ap_tracking_data: None,
            outer_dereference: dereference,
            inner_dereference: false,
            cairo_type: None,
        }
    }
}

impl From<Reference> for HintReference {
    fn from(reference: Reference) -> Self {
        HintReference {
            offset1: reference.value_address.offset1.clone(),
            offset2: reference.value_address.offset2.clone(),
            outer_dereference: reference.value_address.outer_dereference,
            inner_dereference: reference.value_address.inner_dereference,
            // only store `ap` tracking data if the reference is referred to it
            ap_tracking_data: match (
                &reference.value_address.offset1,
                &reference.value_address.offset2,
            ) {
                (OffsetValue::Reference(Register::AP, _, _, _), _)
                | (_, OffsetValue::Reference(Register::AP, _, _, _)) => {
                    Some(reference.ap_tracking_data.clone())
                }
                _ => None,
            },
            cairo_type: Some(reference.value_address.value_type.clone()),
        }
    }
}