diff --git a/crates/blockifier/src/execution/contract_class.rs b/crates/blockifier/src/execution/contract_class.rs index 4c6069c3bb9..2d793d103fb 100644 --- a/crates/blockifier/src/execution/contract_class.rs +++ b/crates/blockifier/src/execution/contract_class.rs @@ -20,7 +20,7 @@ use cairo_vm::serde::deserialize_program::{ }; use cairo_vm::types::builtin_name::BuiltinName; use cairo_vm::types::errors::program_errors::ProgramError; -use cairo_vm::types::program::Program; +use cairo_vm::types::program::{HintsCollection, Program}; use cairo_vm::types::relocatable::MaybeRelocatable; use cairo_vm::vm::runners::cairo_runner::ExecutionResources; use itertools::Itertools; @@ -709,6 +709,29 @@ fn hint_to_hint_params(hint: &Hint) -> Result { }) } +/// Converts `HintParams` back to `Hint` by deserializing the JSON code. +/// This is the reverse of `hint_to_hint_params`. +fn hint_params_to_hint(hint_params: &HintParams) -> Result { + Ok(serde_json::from_str(&hint_params.code)?) +} + +/// Converts `BTreeMap>` back to `Vec<(usize, Vec)>`. +/// This is the reverse of the conversion done in `TryFrom for CompiledClassV1`. +pub fn program_hints_to_casm_hints( + program_hints: &HintsCollection, +) -> Result)>, ProgramError> { + let program_hints: BTreeMap> = program_hints.into(); + let mut casm_hints: Vec<(usize, Vec)> = Vec::new(); + for (i, hint_params_list) in program_hints.iter() { + let hints: Vec = hint_params_list + .iter() + .map(hint_params_to_hint) + .collect::, ProgramError>>()?; + casm_hints.push((*i, hints)); + } + Ok(casm_hints) +} + fn convert_entry_points_v1(external: &[CasmContractEntryPoint]) -> Vec { external .iter() diff --git a/crates/blockifier/src/execution/contract_class_test.rs b/crates/blockifier/src/execution/contract_class_test.rs index 552c2409b40..9d48d2dac61 100644 --- a/crates/blockifier/src/execution/contract_class_test.rs +++ b/crates/blockifier/src/execution/contract_class_test.rs @@ -19,6 +19,7 @@ use starknet_types_core::felt::Felt; use starknet_types_core::hash::Blake2Felt252; use crate::execution::contract_class::{ + program_hints_to_casm_hints, CompiledClassV1, ContractClassV1Inner, EntryPointV1, @@ -236,3 +237,32 @@ fn test_entry_points_round_trip(#[case] original: CasmContractEntryPoints) { assert_eq!(round_tripped, original); } + +#[rstest] +fn test_hints_round_trip() { + // Get a test contract that has hints. + let feature_contract = + FeatureContract::TestContract(CairoVersion::Cairo1(RunnableCairo1::Casm)); + let (casm, sierra_version) = match feature_contract.get_class() { + ContractClass::V1(versioned_casm) => versioned_casm, + _ => panic!("Expected ContractClass::V1"), + }; + + // Panic if the contract has no hints - we need hints to test the roundtrip. + assert!(!casm.hints.is_empty(), "Test contract must have hints for roundtrip test"); + + // Get original hints from the CasmContractClass. + let original_hints = casm.hints.clone(); + + // Forward conversion: Convert CasmContractClass convert the hints from Vec<(usize, Vec)> + // to HashMap>. + let compiled_class = CompiledClassV1::try_from((casm, sierra_version)).unwrap(); + + // Reverse conversion: Access the hints from the VM program and convert back. + // The program stores hints as a HintsCollection, which can be converted to BTreeMap. + let program_hints = &compiled_class.program.shared_program_data.hints_collection; + let round_tripped_hints = program_hints_to_casm_hints(program_hints).unwrap(); + + // Compare with original. + assert_eq!(round_tripped_hints, original_hints); +}