Skip to main content

cairo_lang_starknet_classes/
contract_class.rs

1use cairo_lang_sierra as sierra;
2use cairo_lang_utils::bigint::{BigUintAsHex, deserialize_big_uint, serialize_big_uint};
3use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
4use num_bigint::BigUint;
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7use starknet_types_core::felt::Felt as Felt252;
8use thiserror::Error;
9
10use crate::abi::Contract;
11use crate::allowed_libfuncs::{AllowedLibfuncsError, ListSelector, lookup_allowed_libfuncs_list};
12use crate::compiler_version::{VersionId, current_compiler_version_id, current_sierra_version_id};
13use crate::felt252_serde::{
14    Felt252SerdeError, sierra_from_felt252s, sierra_to_felt252s, version_id_from_felt252s,
15};
16
17#[cfg(test)]
18#[path = "contract_class_test.rs"]
19mod test;
20
21#[derive(Error, Debug, Eq, PartialEq)]
22pub enum StarknetCompilationError {
23    #[error("Invalid entry point.")]
24    EntryPointError,
25    #[error(transparent)]
26    AllowedLibfuncsError(#[from] AllowedLibfuncsError),
27}
28
29/// Represents a contract in the Starknet network.
30#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
31pub struct ContractClass {
32    pub sierra_program: Vec<BigUintAsHex>,
33    pub sierra_program_debug_info: Option<sierra::debug_info::DebugInfo>,
34    pub contract_class_version: String,
35    pub entry_points_by_type: ContractEntryPoints,
36    pub abi: Option<Contract>,
37}
38impl ContractClass {
39    /// Extracts the contract class from the given contract declaration.
40    pub fn new(
41        program: &sierra::program::Program,
42        entry_points_by_type: ContractEntryPoints,
43        abi: Option<Contract>,
44        annotations: OrderedHashMap<String, Value>,
45    ) -> Result<Self, Felt252SerdeError> {
46        let mut sierra_program_debug_info = sierra::debug_info::DebugInfo::extract(program);
47        sierra_program_debug_info.annotations.extend(annotations);
48
49        Ok(Self {
50            sierra_program: sierra_to_felt252s(
51                current_sierra_version_id(),
52                current_compiler_version_id(),
53                program,
54            )?,
55            sierra_program_debug_info: Some(sierra_program_debug_info),
56            contract_class_version: DEFAULT_CONTRACT_CLASS_VERSION.into(),
57            entry_points_by_type,
58            abi,
59        })
60    }
61
62    /// Extracts Sierra program from the ContractClass into `ExtractedSierraProgram` and populates
63    /// it with debug info if `populate_debug_info` is true, and the data is available.
64    pub fn extract_sierra_program(
65        &self,
66        populate_debug_info: bool,
67    ) -> Result<ExtractedSierraProgram, Felt252SerdeError> {
68        let prime = Felt252::prime();
69        for felt252 in &self.sierra_program {
70            if felt252.value >= prime {
71                return Err(Felt252SerdeError::InvalidInputForDeserialization);
72            }
73        }
74        let (sierra_version, compiler_version, mut program) =
75            sierra_from_felt252s(&self.sierra_program)?;
76        if populate_debug_info && let Some(info) = &self.sierra_program_debug_info {
77            info.populate(&mut program);
78        }
79        Ok(ExtractedSierraProgram { program, sierra_version, compiler_version })
80    }
81
82    /// Sanity checks the contract class.
83    /// Currently only checks that if ABI exists, its counts match the entry points counts.
84    pub fn sanity_check(&self) {
85        if let Some(abi) = &self.abi {
86            abi.sanity_check(
87                self.entry_points_by_type.external.len(),
88                self.entry_points_by_type.l1_handler.len(),
89                self.entry_points_by_type.constructor.len(),
90            );
91        }
92    }
93}
94
95/// The Sierra program extracted from a contract class.
96pub struct ExtractedSierraProgram {
97    /// The actual Sierra program.
98    pub program: sierra::program::Program,
99    /// The Sierra version used for the program.
100    pub sierra_version: VersionId,
101    /// The compiler version used for the program.
102    pub compiler_version: VersionId,
103}
104impl ExtractedSierraProgram {
105    /// Checks that all the used libfuncs in the contract class are allowed in the contract class
106    /// Sierra version.
107    pub fn validate_version_compatible(
108        &self,
109        list_selector: ListSelector,
110    ) -> Result<(), AllowedLibfuncsError> {
111        let list_name = list_selector.to_string();
112        let allowed_libfuncs = lookup_allowed_libfuncs_list(list_selector)?;
113        for libfunc in &self.program.libfunc_declarations {
114            match allowed_libfuncs.allowed_libfuncs.get(&libfunc.long_id.generic_id) {
115                Some(None) => {}
116                Some(Some(required)) if self.sierra_version.supports(*required) => {}
117                Some(Some(required)) => {
118                    return Err(AllowedLibfuncsError::UnsupportedLibfuncAtVersion {
119                        invalid_libfunc: libfunc.long_id.generic_id.to_string(),
120                        required_version: *required,
121                        class_version: self.sierra_version,
122                    });
123                }
124                None => {
125                    return Err(AllowedLibfuncsError::UnsupportedLibfunc {
126                        invalid_libfunc: libfunc.long_id.generic_id.to_string(),
127                        allowed_libfuncs_list_name: list_name,
128                    });
129                }
130            }
131        }
132        Ok(())
133    }
134}
135
136const DEFAULT_CONTRACT_CLASS_VERSION: &str = "0.1.0";
137
138#[derive(Clone, Default, Debug, PartialEq, Eq, Serialize, Deserialize)]
139pub struct ContractEntryPoints {
140    #[serde(rename = "EXTERNAL")]
141    pub external: Vec<ContractEntryPoint>,
142    #[serde(rename = "L1_HANDLER")]
143    pub l1_handler: Vec<ContractEntryPoint>,
144    #[serde(rename = "CONSTRUCTOR")]
145    pub constructor: Vec<ContractEntryPoint>,
146}
147
148#[derive(Clone, Default, Debug, PartialEq, Eq, Serialize, Deserialize)]
149pub struct ContractEntryPoint {
150    /// A field element that encodes the signature of the called function.
151    #[serde(serialize_with = "serialize_big_uint", deserialize_with = "deserialize_big_uint")]
152    pub selector: BigUint,
153    /// The index of the user function declaration in the Sierra program.
154    pub function_idx: usize,
155}
156
157/// Deserializes the versions from the header of a Sierra program represented as a slice of
158/// felt252s.
159///
160/// Returns (sierra_version_id, compiler_version_id).
161/// See [crate::compiler_version].
162pub fn version_id_from_serialized_sierra_program(
163    sierra_program: &[BigUintAsHex],
164) -> Result<(VersionId, VersionId), Felt252SerdeError> {
165    let (sierra_version_id, compiler_version_id, _) = version_id_from_felt252s(sierra_program)?;
166    Ok((sierra_version_id, compiler_version_id))
167}