sierra_emu/
lib.rs

1use std::{num::ParseIntError, str::FromStr, sync::Arc};
2
3use cairo_lang_sierra::{
4    extensions::{
5        circuit::CircuitTypeConcrete,
6        core::{CoreLibfunc, CoreType, CoreTypeConcrete},
7        starknet::StarknetTypeConcrete,
8    },
9    ids::ConcreteTypeId,
10    program::{GenFunction, Program, StatementIdx},
11    program_registry::ProgramRegistry,
12};
13use debug::type_to_name;
14use starknet::StubSyscallHandler;
15
16pub use self::{dump::*, gas::BuiltinCosts, value::*, vm::VirtualMachine};
17
18mod debug;
19mod dump;
20mod gas;
21pub mod starknet;
22mod test_utils;
23mod utils;
24mod value;
25mod vm;
26
27#[derive(Clone, Debug)]
28pub enum EntryPoint {
29    Number(u64),
30    String(String),
31}
32
33impl FromStr for EntryPoint {
34    type Err = ParseIntError;
35
36    fn from_str(s: &str) -> Result<Self, Self::Err> {
37        Ok(match s.chars().next() {
38            Some(x) if x.is_numeric() => Self::Number(s.parse()?),
39            _ => Self::String(s.to_string()),
40        })
41    }
42}
43
44pub fn find_entry_point_by_idx(
45    program: &Program,
46    entry_point_idx: usize,
47) -> Option<&GenFunction<StatementIdx>> {
48    program
49        .funcs
50        .iter()
51        .find(|x| x.id.id == entry_point_idx as u64)
52}
53
54pub fn find_entry_point_by_name<'a>(
55    program: &'a Program,
56    name: &str,
57) -> Option<&'a GenFunction<StatementIdx>> {
58    program
59        .funcs
60        .iter()
61        .find(|x| x.id.debug_name.as_ref().map(|x| x.as_str()) == Some(name))
62}
63
64// If type is invisible to sierra (i.e. a single element container),
65// finds it's actual concrete type recursively.
66// If not, returns the current type
67pub fn find_real_type(
68    registry: &ProgramRegistry<CoreType, CoreLibfunc>,
69    ty: &ConcreteTypeId,
70) -> ConcreteTypeId {
71    match registry.get_type(ty).unwrap() {
72        cairo_lang_sierra::extensions::core::CoreTypeConcrete::Box(info) => {
73            find_real_type(registry, &info.ty)
74        }
75        cairo_lang_sierra::extensions::core::CoreTypeConcrete::Uninitialized(info) => {
76            find_real_type(registry, &info.ty)
77        }
78        cairo_lang_sierra::extensions::core::CoreTypeConcrete::Span(info) => {
79            find_real_type(registry, &info.ty)
80        }
81        cairo_lang_sierra::extensions::core::CoreTypeConcrete::Snapshot(info) => {
82            find_real_type(registry, &info.ty)
83        }
84        _ => ty.clone(),
85    }
86}
87
88pub fn run_program(
89    program: Arc<Program>,
90    entry_point: EntryPoint,
91    args: Vec<String>,
92    available_gas: u64,
93) -> ProgramTrace {
94    let mut vm = VirtualMachine::new(program.clone());
95
96    let function = program
97        .funcs
98        .iter()
99        .find(|f| match &entry_point {
100            EntryPoint::Number(x) => f.id.id == *x,
101            EntryPoint::String(x) => f.id.debug_name.as_deref() == Some(x.as_str()),
102        })
103        .unwrap();
104
105    let mut iter = args.into_iter();
106    vm.push_frame(
107        function.id.clone(),
108        function
109            .signature
110            .param_types
111            .iter()
112            .map(|type_id| {
113                let type_info = vm.registry().get_type(type_id).unwrap();
114                match type_info {
115                    CoreTypeConcrete::Felt252(_) => Value::parse_felt(&iter.next().unwrap()),
116                    CoreTypeConcrete::GasBuiltin(_) => Value::U64(available_gas),
117                    CoreTypeConcrete::RangeCheck(_)
118                    | CoreTypeConcrete::RangeCheck96(_)
119                    | CoreTypeConcrete::Bitwise(_)
120                    | CoreTypeConcrete::Pedersen(_)
121                    | CoreTypeConcrete::Poseidon(_)
122                    | CoreTypeConcrete::SegmentArena(_)
123                    | CoreTypeConcrete::Circuit(
124                        CircuitTypeConcrete::AddMod(_) | CircuitTypeConcrete::MulMod(_),
125                    ) => Value::Unit,
126                    CoreTypeConcrete::Starknet(inner) => match inner {
127                        StarknetTypeConcrete::System(_) => Value::Unit,
128                        StarknetTypeConcrete::ClassHash(_)
129                        | StarknetTypeConcrete::ContractAddress(_)
130                        | StarknetTypeConcrete::StorageBaseAddress(_)
131                        | StarknetTypeConcrete::StorageAddress(_) => {
132                            Value::parse_felt(&iter.next().unwrap())
133                        }
134                        _ => todo!(),
135                    },
136                    CoreTypeConcrete::EcOp(_) => Value::Unit,
137                    _ => todo!("{}", type_to_name(type_id, vm.registry())),
138                }
139            })
140            .collect::<Vec<_>>(),
141    );
142
143    let syscall_handler = &mut StubSyscallHandler::default();
144
145    vm.run_with_trace(syscall_handler)
146}