cairo-program-runner-lib 1.2.2

Library for running Cairo programs on the Cairo VM with hint support
Documentation
use cairo_vm::Felt252;
use std::collections::HashMap;

use cairo_vm::hint_processor::builtin_hint_processor::hint_utils::{
    get_ptr_from_var_name, insert_value_from_var_name,
};
use cairo_vm::hint_processor::hint_processor_definition::HintReference;
use cairo_vm::serde::deserialize_program::ApTracking;
use cairo_vm::types::exec_scope::ExecutionScopes;
use cairo_vm::vm::errors::hint_errors::HintError;
use cairo_vm::vm::vm_core::VirtualMachine;
use starknet_types_core::felt::Felt;

use crate::hints::vars;

/// Sets ids.select_builtin to 1 if the first builtin should be selected and 0 otherwise.
///
/// Implements
/// # A builtin should be selected iff its encoding appears in the selected encodings list
/// # and the list wasn't exhausted.
/// # Note that testing inclusion by a single comparison is possible since the lists are sorted.
/// ids.select_builtin = int(
///   n_selected_builtins > 0 and memory[ids.selected_encodings] == memory[ids.all_encodings])
/// if ids.select_builtin:
///   n_selected_builtins = n_selected_builtins - 1
pub fn select_builtin(
    vm: &mut VirtualMachine,
    exec_scopes: &mut ExecutionScopes,
    ids_data: &HashMap<String, HintReference>,
    ap_tracking: &ApTracking,
) -> Result<(), HintError> {
    let n_selected_builtins: Felt = exec_scopes.get(vars::N_SELECTED_BUILTINS)?;

    let select_builtin = if n_selected_builtins == Felt::ZERO {
        false
    } else {
        let selected_encodings =
            get_ptr_from_var_name("selected_encodings", vm, ids_data, ap_tracking)?;
        let all_encodings = get_ptr_from_var_name("all_encodings", vm, ids_data, ap_tracking)?;

        let selected_encoding = vm.get_integer(selected_encodings)?.into_owned();
        let builtin_encoding = vm.get_integer(all_encodings)?.into_owned();

        selected_encoding == builtin_encoding
    };

    let select_builtin_felt = Felt252::from(select_builtin);
    insert_value_from_var_name(
        "select_builtin",
        select_builtin_felt,
        vm,
        ids_data,
        ap_tracking,
    )?;

    if select_builtin {
        exec_scopes.insert_value(vars::N_SELECTED_BUILTINS, n_selected_builtins - 1);
    }

    Ok(())
}

#[cfg(test)]
mod tests {
    use crate::test_utils::fill_ids_data_for_test;
    use cairo_vm::hint_processor::builtin_hint_processor::hint_utils::get_integer_from_var_name;
    use cairo_vm::types::relocatable::{MaybeRelocatable, Relocatable};
    use cairo_vm::Felt252;
    use rstest::rstest;

    use super::*;

    /// Test for select_builtin hint. Checks that select_builtin is set according to the hint
    /// description. 4 cases:
    /// - should select a builtin (n_builtins > 0 and encodings match)
    /// - should not select a builtin (n_builtins > 0 and encodings don't match)
    /// - no builtins to select (n_builtins = 0 and encodings match)
    /// - no builtins to select and no match (n_builtins = 0 and encodings don't match)
    #[rstest]
    #[case::should_select_builtin(Felt::ONE, true)]
    #[case::should_not_select_builtin(Felt::ZERO, false)]
    #[case::no_builtins(Felt::ZERO, true)]
    #[case::no_builtins_and_no_match(Felt::ZERO, false)]
    fn test_select_builtin(#[case] n_builtins: Felt, #[case] should_select_builtin: bool) {
        let mut vm = VirtualMachine::new(false, false);

        let builtin_value = 10;
        let expected_value = if should_select_builtin {
            builtin_value
        } else {
            builtin_value + 1
        };
        vm.add_memory_segment();
        vm.add_memory_segment();
        vm.add_memory_segment();
        vm.load_data(
            Relocatable::from((1, 0)),
            &[
                MaybeRelocatable::from((2, 0)),
                MaybeRelocatable::from((2, 1)),
            ],
        )
        .expect("Failed to load data into memory");
        vm.load_data(
            Relocatable::from((2, 0)),
            &[
                MaybeRelocatable::from(Felt252::from(builtin_value)),
                MaybeRelocatable::from(Felt252::from(expected_value)),
            ],
        )
        .expect("Failed to load data into memory");

        // Allocate space for program_data_ptr
        vm.set_fp(3);
        vm.set_ap(3);
        let ids_data =
            fill_ids_data_for_test(&["selected_encodings", "all_encodings", "select_builtin"]);
        let ap_tracking = ApTracking::new();

        let mut exec_scopes = ExecutionScopes::new();
        exec_scopes.insert_value(vars::N_SELECTED_BUILTINS, n_builtins);

        select_builtin(&mut vm, &mut exec_scopes, &ids_data, &ap_tracking)
            .expect("Hint failed unexpectedly");

        let select_builtin =
            get_integer_from_var_name("select_builtin", &vm, &ids_data, &ap_tracking).unwrap();
        let n_selected_builtins: Felt = exec_scopes.get(vars::N_SELECTED_BUILTINS).unwrap();

        if (n_builtins != Felt::ZERO) && should_select_builtin {
            assert_eq!(select_builtin, Felt252::from(1));
            assert_eq!(n_selected_builtins, n_builtins - 1);
        } else {
            assert_eq!(select_builtin, Felt252::from(0));
            assert_eq!(n_selected_builtins, n_builtins);
        }
    }
}