hdp/preprocessor/compile/
module.rs

1//!  Preprocessor is reponsible for identifying the required values.
2//!  This will be most abstract layer of the preprocessor.
3
4use crate::cairo_runner::dry_run::DryRunResult;
5use crate::cairo_runner::{cairo_dry_run, input::dry_run::DryRunnerProgramInput};
6use crate::constant::DRY_CAIRO_RUN_OUTPUT_FILE;
7use crate::primitives::processed_types::cairo_format;
8use crate::primitives::task::ExtendedModule;
9use crate::provider::key::categorize_fetch_keys;
10use crate::provider::traits::new_provider_from_config;
11use core::panic;
12
13use std::collections::{HashMap, HashSet};
14use std::path::PathBuf;
15use tracing::info;
16
17use super::config::CompilerConfig;
18use super::{Compilable, CompilationResult, CompileError};
19
20pub type ModuleVec = Vec<ExtendedModule>;
21
22impl Compilable for ModuleVec {
23    async fn compile(
24        &self,
25        compile_config: &CompilerConfig,
26    ) -> Result<CompilationResult, CompileError> {
27        // Log the target task for debugging purposes
28        info!("target task: {:#?}", self[0].task);
29        let dry_run_program_path = compile_config.dry_run_program_path.clone();
30
31        // Generate input for the dry run based on the extended modules
32        let dry_run_input =
33            generate_input(self.to_vec(), PathBuf::from(DRY_CAIRO_RUN_OUTPUT_FILE)).await?;
34        let input_string =
35            serde_json::to_string_pretty(&dry_run_input).expect("Failed to serialize module class");
36
37        // 2. Run the dry run and retrieve the fetch points
38        info!("2. Running dry-run... ");
39        let dry_run_results: DryRunResult = cairo_dry_run(
40            dry_run_program_path,
41            input_string,
42            compile_config.save_fetch_keys_file.clone(),
43        )?;
44
45        // Check if the program hash matches the expected hash
46        if dry_run_results[0].program_hash != self[0].task.program_hash {
47            return Err(CompileError::ClassHashMismatch);
48        }
49
50        // Ensure only one module is supported
51        if dry_run_results.len() != 1 {
52            panic!("Multiple Modules are not supported");
53        }
54
55        // Extract the dry run module result
56        let dry_run_module = dry_run_results.into_iter().next().unwrap();
57        let commit_results = vec![dry_run_module.result.into()];
58
59        // 3. Categorize fetch keys by chain ID
60        let categorized_keys = categorize_fetch_keys(dry_run_module.fetch_keys);
61
62        // Initialize maps to store fetched proofs grouped by chain ID
63        let mut accounts_map = HashMap::new();
64        let mut storages_map = HashMap::new();
65        let mut transactions_map = HashMap::new();
66        let mut transaction_receipts_map = HashMap::new();
67        let mut mmr_header_map = HashMap::new();
68
69        info!("3. Fetching proofs from provider...");
70        // Loop through each chain ID and fetch proofs
71        for (chain_id, keys) in categorized_keys {
72            info!("target provider chain id: {}", chain_id);
73            let target_provider_config = compile_config
74                .provider_config
75                .get(&chain_id)
76                .expect("target task's chain had not been configured.");
77            let provider = new_provider_from_config(target_provider_config);
78
79            // TODO: handle starknet
80            let results = provider
81                .fetch_proofs_from_keys(keys)
82                .await?
83                .get_evm_proofs()
84                .unwrap();
85
86            // Update the maps with fetched results
87            mmr_header_map.insert(
88                chain_id.to_numeric_id(),
89                HashSet::from_iter(results.mmr_with_headers.into_iter()),
90            );
91            accounts_map.insert(
92                chain_id.to_numeric_id(),
93                HashSet::from_iter(results.accounts.into_iter()),
94            );
95            storages_map.insert(
96                chain_id.to_numeric_id(),
97                HashSet::from_iter(results.storages.into_iter()),
98            );
99            transactions_map.insert(
100                chain_id.to_numeric_id(),
101                HashSet::from_iter(results.transactions.into_iter()),
102            );
103            transaction_receipts_map.insert(
104                chain_id.to_numeric_id(),
105                HashSet::from_iter(results.transaction_receipts.into_iter()),
106            );
107        }
108
109        // Create and return the compilation result containing all relevant proofs
110        let compiled_result = CompilationResult::new(
111            commit_results,
112            mmr_header_map,
113            accounts_map,
114            storages_map,
115            transactions_map,
116            transaction_receipts_map,
117        );
118        Ok(compiled_result)
119    }
120}
121
122/// Generate input structure for preprocessor that need to pass to runner
123async fn generate_input(
124    extended_modules: Vec<ExtendedModule>,
125    identified_keys_file: PathBuf,
126) -> Result<DryRunnerProgramInput, CompileError> {
127    // Collect results, filter out any errors
128    let mut collected_results = Vec::new();
129    for module in extended_modules {
130        let input_module =
131            cairo_format::DryRunProcessedModule::new(module.task.inputs, module.module_class);
132        collected_results.push(input_module);
133    }
134
135    Ok(DryRunnerProgramInput::new(
136        identified_keys_file,
137        collected_results,
138    ))
139}