cairo_lang_sierra_to_casm/
metadata.rs

1use cairo_lang_sierra::extensions::gas::CostTokenType;
2use cairo_lang_sierra::ids::FunctionId;
3use cairo_lang_sierra::program::Program;
4use cairo_lang_sierra_ap_change::ap_change_info::ApChangeInfo;
5use cairo_lang_sierra_ap_change::compute::calc_ap_changes as linear_calc_ap_changes;
6use cairo_lang_sierra_ap_change::{ApChangeError, calc_ap_changes};
7use cairo_lang_sierra_gas::gas_info::GasInfo;
8use cairo_lang_sierra_gas::objects::ConstCost;
9use cairo_lang_sierra_gas::{
10    CostError, calc_gas_postcost_info, calc_gas_precost_info, compute_postcost_info,
11    compute_precost_info,
12};
13use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
14use thiserror::Error;
15
16#[derive(Default)]
17/// Metadata provided with a Sierra program to simplify the compilation to casm.
18pub struct Metadata {
19    /// AP changes information for Sierra user functions.
20    pub ap_change_info: ApChangeInfo,
21    /// Gas information for validating Sierra code and taking the appropriate amount of gas.
22    pub gas_info: GasInfo,
23}
24
25/// Error for metadata calculations.
26#[derive(Debug, Error, Eq, PartialEq)]
27pub enum MetadataError {
28    #[error(transparent)]
29    ApChangeError(#[from] ApChangeError),
30    #[error(transparent)]
31    CostError(#[from] CostError),
32}
33
34/// Configuration for metadata computation.
35#[derive(Clone)]
36pub struct MetadataComputationConfig {
37    /// Functions to enforce costs for, as well as the costs to enforce.
38    pub function_set_costs: OrderedHashMap<FunctionId, OrderedHashMap<CostTokenType, i32>>,
39    /// If true, uses a linear-time algorithm for calculating the gas, instead of solving
40    /// equations.
41    pub linear_gas_solver: bool,
42    /// If true, uses a linear-time algorithm for calculating ap changes, instead of solving
43    /// equations.
44    pub linear_ap_change_solver: bool,
45    /// When running the non-linear solver do not check for contradictions with the linear
46    /// solution. Used for testing only.
47    pub skip_non_linear_solver_comparisons: bool,
48    /// If true, compute the runtime cost token types (steps, holes and range-checks) in addition
49    /// to the usual gas costs (used in Sierra-to-casm compilation).
50    pub compute_runtime_costs: bool,
51}
52
53impl Default for MetadataComputationConfig {
54    fn default() -> Self {
55        Self {
56            function_set_costs: Default::default(),
57            linear_gas_solver: true,
58            linear_ap_change_solver: true,
59            skip_non_linear_solver_comparisons: false,
60            compute_runtime_costs: false,
61        }
62    }
63}
64
65/// Calculates the metadata for a Sierra program, with ap change info only.
66pub fn calc_metadata_ap_change_only(program: &Program) -> Result<Metadata, MetadataError> {
67    Ok(Metadata {
68        ap_change_info: linear_calc_ap_changes(program, |_, _| 0)?,
69        gas_info: GasInfo {
70            variable_values: Default::default(),
71            function_costs: Default::default(),
72        },
73    })
74}
75
76/// Calculates the metadata for a Sierra program.
77///
78/// `no_eq_solver` uses a linear-time algorithm for calculating the gas, instead of solving
79/// equations.
80pub fn calc_metadata(
81    program: &Program,
82    config: MetadataComputationConfig,
83) -> Result<Metadata, MetadataError> {
84    let pre_function_set_costs = config
85        .function_set_costs
86        .iter()
87        .map(|(func, costs)| {
88            (
89                func.clone(),
90                CostTokenType::iter_precost()
91                    .filter_map(|token| costs.get(token).map(|v| (*token, *v)))
92                    .collect(),
93            )
94        })
95        .collect();
96    let pre_gas_info_new = compute_precost_info(program)?;
97    let pre_gas_info = if config.linear_gas_solver {
98        pre_gas_info_new
99    } else {
100        let pre_gas_info_old = calc_gas_precost_info(program, pre_function_set_costs)?;
101        if !config.skip_non_linear_solver_comparisons {
102            pre_gas_info_old.assert_eq_variables(&pre_gas_info_new, program);
103            pre_gas_info_old.assert_eq_functions(&pre_gas_info_new);
104        }
105        pre_gas_info_old
106    };
107
108    let ap_change_info =
109        if config.linear_ap_change_solver { linear_calc_ap_changes } else { calc_ap_changes }(
110            program,
111            |idx, token_type| pre_gas_info.variable_values[&(idx, token_type)] as usize,
112        )?;
113
114    let mut post_gas_info = if config.linear_gas_solver {
115        let enforced_function_costs: OrderedHashMap<FunctionId, i32> = config
116            .function_set_costs
117            .iter()
118            .map(|(func, costs)| (func.clone(), costs[&CostTokenType::Const]))
119            .collect();
120        compute_postcost_info(
121            program,
122            &|idx| ap_change_info.variable_values.get(idx).copied().unwrap_or_default(),
123            &pre_gas_info,
124            &enforced_function_costs,
125        )
126    } else {
127        let post_function_set_costs = config
128            .function_set_costs
129            .iter()
130            .map(|(func, costs)| {
131                (
132                    func.clone(),
133                    [CostTokenType::Const]
134                        .iter()
135                        .filter_map(|token| costs.get(token).map(|v| (*token, *v)))
136                        .collect(),
137                )
138            })
139            .collect();
140        calc_gas_postcost_info(program, post_function_set_costs, &pre_gas_info, |idx| {
141            ap_change_info.variable_values.get(&idx).copied().unwrap_or_default()
142        })
143    }?;
144
145    if config.compute_runtime_costs {
146        let post_gas_info_runtime = compute_postcost_info::<ConstCost>(
147            program,
148            &|idx| ap_change_info.variable_values.get(idx).copied().unwrap_or_default(),
149            &pre_gas_info,
150            &Default::default(),
151        )?;
152        post_gas_info = post_gas_info.combine(post_gas_info_runtime);
153    }
154
155    Ok(Metadata { ap_change_info, gas_info: pre_gas_info.combine(post_gas_info) })
156}