diff --git a/crates/starknet_committer_and_os_cli/src/os_cli/tests/python_tests.rs b/crates/starknet_committer_and_os_cli/src/os_cli/tests/python_tests.rs index 6b944caf391..2b9e7d6f999 100644 --- a/crates/starknet_committer_and_os_cli/src/os_cli/tests/python_tests.rs +++ b/crates/starknet_committer_and_os_cli/src/os_cli/tests/python_tests.rs @@ -1,13 +1,5 @@ -use std::collections::HashSet; - use blake2s::encode_felts_to_u32s; -use cairo_vm::hint_processor::builtin_hint_processor::hint_code::HINT_CODES; -use cairo_vm::hint_processor::builtin_hint_processor::kzg_da::WRITE_DIVMOD_SEGMENT; -use cairo_vm::hint_processor::builtin_hint_processor::secp::cairo0_hints::CAIRO0_HINT_CODES; -use starknet_os::hints::enum_definition::{AggregatorHint, HintExtension, OsHint}; -use starknet_os::hints::types::HintEnum; use starknet_types_core::felt::Felt; -use strum::IntoEnumIterator; use crate::os_cli::commands::{validate_input, Input}; use crate::os_cli::tests::aliases::aliases_test; @@ -19,7 +11,6 @@ use crate::shared_utils::types::{PythonTestError, PythonTestRunner}; pub enum OsPythonTestRunner { AliasesTest, BlsFieldTest, - CompareOsHints, InputDeserialization, EncodeFelts, } @@ -32,7 +23,6 @@ impl TryFrom for OsPythonTestRunner { match value.as_str() { "aliases_test" => Ok(Self::AliasesTest), "bls_field_test" => Ok(Self::BlsFieldTest), - "compare_os_hints" => Ok(Self::CompareOsHints), "input_deserialization" => Ok(Self::InputDeserialization), "encode_felts" => Ok(Self::EncodeFelts), _ => Err(PythonTestError::UnknownTestName(value)), @@ -47,7 +37,6 @@ impl PythonTestRunner for OsPythonTestRunner { match self { Self::AliasesTest => aliases_test(Self::non_optional_input(input)?), Self::BlsFieldTest => test_bls_field(Self::non_optional_input(input)?), - Self::CompareOsHints => compare_os_hints(Self::non_optional_input(input)?), Self::InputDeserialization => input_deserialization(Self::non_optional_input(input)?), Self::EncodeFelts => { let felts: Vec = serde_json::from_str(Self::non_optional_input(input)?)?; @@ -57,33 +46,6 @@ impl PythonTestRunner for OsPythonTestRunner { } } -#[allow(clippy::result_large_err)] -fn compare_os_hints(input: &str) -> OsPythonTestResult { - let unfiltered_python_hints: HashSet = serde_json::from_str(input)?; - - // Remove VM hints. - let vm_hints = vm_hints(); - let python_os_hints: HashSet = unfiltered_python_hints - .into_iter() - .filter(|hint| !vm_hints.contains(hint.as_str())) - .collect(); - - // We ignore `SyscallHint`s here, as they are not part of the compiled OS. - let rust_os_hints: HashSet = OsHint::iter() - .map(|hint| hint.to_str().to_string()) - .chain(HintExtension::iter().map(|hint| hint.to_str().to_string())) - .chain(AggregatorHint::iter().map(|hint| hint.to_str().to_string())) - .collect(); - - let mut only_in_python: Vec = - python_os_hints.difference(&rust_os_hints).cloned().collect(); - only_in_python.sort(); - let mut only_in_rust: Vec = - rust_os_hints.difference(&python_os_hints).cloned().collect(); - only_in_rust.sort(); - Ok(serde_json::to_string(&(only_in_python, only_in_rust))?) -} - /// Deserialize the input string into an `Input` struct. #[allow(clippy::result_large_err)] fn input_deserialization(input_str: &str) -> OsPythonTestResult { @@ -91,10 +53,3 @@ fn input_deserialization(input_str: &str) -> OsPythonTestResult { validate_input(&input.os_hints.os_input); Ok("Deserialization successful".to_string()) } - -fn vm_hints() -> HashSet<&'static str> { - let mut vm_hints = HashSet::from([WRITE_DIVMOD_SEGMENT]); - vm_hints.extend(HINT_CODES.values()); - vm_hints.extend(CAIRO0_HINT_CODES.values()); - vm_hints -} diff --git a/crates/starknet_os/src/hints/enum_definition_test.rs b/crates/starknet_os/src/hints/enum_definition_test.rs index 698511820c3..137f3e145a0 100644 --- a/crates/starknet_os/src/hints/enum_definition_test.rs +++ b/crates/starknet_os/src/hints/enum_definition_test.rs @@ -1,11 +1,40 @@ use std::collections::HashSet; +use std::sync::LazyLock; +use apollo_starknet_os_program::{AGGREGATOR_PROGRAM, OS_PROGRAM}; use blockifier::execution::hint_code::SYSCALL_HINTS; +use cairo_vm::hint_processor::builtin_hint_processor::hint_code::HINT_CODES; +use cairo_vm::hint_processor::builtin_hint_processor::kzg_da::WRITE_DIVMOD_SEGMENT; +use cairo_vm::hint_processor::builtin_hint_processor::secp::cairo0_hints::CAIRO0_HINT_CODES; +use cairo_vm::types::program::Program; use strum::IntoEnumIterator; use crate::hints::enum_definition::{AllHints, DeprecatedSyscallHint}; use crate::hints::types::HintEnum; +static VM_HINTS: LazyLock> = LazyLock::new(|| { + let mut vm_hints = HashSet::from([WRITE_DIVMOD_SEGMENT]); + vm_hints.extend(HINT_CODES.values()); + vm_hints.extend(CAIRO0_HINT_CODES.values()); + vm_hints +}); + +fn program_hints(program: &Program) -> HashSet { + program + .shared_program_data + .hints_collection + .iter_hints() + .map(|hint| hint.code.clone()) + .collect() +} + +fn unknown_hints_for_program(program: &Program) -> HashSet { + program_hints(program) + .into_iter() + .filter(|hint| AllHints::from_str(hint).is_err() && !VM_HINTS.contains(hint.as_str())) + .collect() +} + #[test] fn test_hint_strings_are_unique() { let all_hints = AllHints::all_iter().map(|hint| hint.to_str()).collect::>(); @@ -34,3 +63,35 @@ fn test_syscall_compatibility_with_blockifier() { the implementation." ); } + +#[test] +fn test_all_hints_are_known() { + let unknown_os_hints = unknown_hints_for_program(&OS_PROGRAM); + let unknown_aggregator_hints = unknown_hints_for_program(&AGGREGATOR_PROGRAM); + let unknown_hints: HashSet = + unknown_os_hints.union(&unknown_aggregator_hints).cloned().collect(); + + assert!( + unknown_hints.is_empty(), + "The following hints are not known in 'starknet_os': {unknown_hints:#?}." + ); +} + +#[test] +fn test_all_hints_are_used() { + let os_hints = program_hints(&OS_PROGRAM); + let aggregator_hints = program_hints(&AGGREGATOR_PROGRAM); + let all_program_hints: HashSet<&String> = os_hints.union(&aggregator_hints).collect(); + let redundant_hints: HashSet<_> = AllHints::all_iter() + .filter(|hint| { + // Skip syscalls; they do not appear in the OS code. + !matches!(hint, AllHints::DeprecatedSyscallHint(_)) + && !all_program_hints.contains(&String::from(hint.to_str())) + }) + .collect(); + assert!( + redundant_hints.is_empty(), + "The following hints are not used in the OS or Aggregator programs: {redundant_hints:#?}. \ + Please remove them from the enum definition." + ); +}