Skip to content

Commit c9c508c

Browse files
authored
Introduce StepHooks trait (#2295)
1 parent dbc5dab commit c9c508c

File tree

3 files changed

+125
-27
lines changed

3 files changed

+125
-27
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#### Upcoming Changes
44

55
* fix: Make cairo1-run to conditionally relocate memory and trace [#2241](https://github.com/lambdaclass/cairo-vm/pull/2241)
6+
* feat(breaking): Introduce `StepHooks` trait which allows hooks to be stateful [#2295](https://github.com/lambdaclass/cairo-vm/pull/2295)
67

78
#### [3.0.1] - 2025-12-22
89

vm/src/vm/hooks.rs

Lines changed: 112 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ use crate::stdlib::{any::Any, collections::HashMap, prelude::*, sync::Arc};
1313

1414
use crate::Felt252;
1515

16+
use super::{errors::vm_errors::VirtualMachineError, vm_core::VirtualMachine};
17+
use crate::vm::runners::cairo_runner::CairoRunner;
1618
use crate::{
1719
hint_processor::hint_processor_definition::HintProcessor, types::exec_scope::ExecutionScopes,
1820
};
1921

20-
use super::{errors::vm_errors::VirtualMachineError, vm_core::VirtualMachine};
21-
2222
type BeforeFirstStepHookFunc = Arc<
2323
dyn Fn(&mut VirtualMachine, &[Box<dyn Any>]) -> Result<(), VirtualMachineError> + Sync + Send,
2424
>;
@@ -35,9 +35,36 @@ type StepHookFunc = Arc<
3535
+ Send,
3636
>;
3737

38-
/// The hooks to be executed during the VM run
38+
/// The hooks to be executed during the VM run.
39+
pub trait StepHooks {
40+
fn before_first_step(
41+
&mut self,
42+
vm: &mut VirtualMachine,
43+
hints_data: &[Box<dyn Any>],
44+
) -> Result<(), VirtualMachineError>;
45+
46+
fn pre_step_instruction(
47+
&mut self,
48+
vm: &mut VirtualMachine,
49+
hint_processor: &mut dyn HintProcessor,
50+
exec_scopes: &mut ExecutionScopes,
51+
hints_data: &[Box<dyn Any>],
52+
constants: &HashMap<String, Felt252>,
53+
) -> Result<(), VirtualMachineError>;
54+
55+
fn post_step_instruction(
56+
&mut self,
57+
vm: &mut VirtualMachine,
58+
hint_processor: &mut dyn HintProcessor,
59+
exec_scopes: &mut ExecutionScopes,
60+
hints_data: &[Box<dyn Any>],
61+
constants: &HashMap<String, Felt252>,
62+
) -> Result<(), VirtualMachineError>;
63+
}
64+
65+
/// The hooks to be executed during the VM run.
3966
///
40-
/// They can be individually ignored by setting them to [None]
67+
/// They can be individually ignored by setting them to [None].
4168
#[derive(Clone, Default)]
4269
pub struct Hooks {
4370
before_first_step: Option<BeforeFirstStepHookFunc>,
@@ -59,13 +86,58 @@ impl Hooks {
5986
}
6087
}
6188

89+
impl StepHooks for Hooks {
90+
fn before_first_step(
91+
&mut self,
92+
vm: &mut VirtualMachine,
93+
hint_data: &[Box<dyn Any>],
94+
) -> Result<(), VirtualMachineError> {
95+
if let Some(before_first_step) = &self.before_first_step {
96+
return before_first_step(vm, hint_data);
97+
}
98+
Ok(())
99+
}
100+
101+
fn pre_step_instruction(
102+
&mut self,
103+
vm: &mut VirtualMachine,
104+
hint_executor: &mut dyn HintProcessor,
105+
exec_scope: &mut ExecutionScopes,
106+
hints_data: &[Box<dyn Any>],
107+
constants: &HashMap<String, Felt252>,
108+
) -> Result<(), VirtualMachineError> {
109+
if let Some(pre_step_instruction) = &self.pre_step_instruction {
110+
return pre_step_instruction(vm, hint_executor, exec_scope, hints_data, constants);
111+
}
112+
113+
Ok(())
114+
}
115+
116+
fn post_step_instruction(
117+
&mut self,
118+
vm: &mut VirtualMachine,
119+
hint_executor: &mut dyn HintProcessor,
120+
exec_scope: &mut ExecutionScopes,
121+
hints_data: &[Box<dyn Any>],
122+
constants: &HashMap<String, Felt252>,
123+
) -> Result<(), VirtualMachineError> {
124+
if let Some(post_step_instruction) = &self.post_step_instruction {
125+
return post_step_instruction(vm, hint_executor, exec_scope, hints_data, constants);
126+
}
127+
128+
Ok(())
129+
}
130+
}
131+
62132
impl VirtualMachine {
63133
pub fn execute_before_first_step(
64134
&mut self,
65135
hint_data: &[Box<dyn Any>],
66136
) -> Result<(), VirtualMachineError> {
67-
if let Some(hook_func) = self.hooks.clone().before_first_step {
68-
(hook_func)(self, hint_data)?;
137+
if let Some(mut hooks) = self.hooks.take() {
138+
let result = hooks.before_first_step(self, hint_data);
139+
self.hooks = Some(hooks);
140+
return result;
69141
}
70142

71143
Ok(())
@@ -78,8 +150,11 @@ impl VirtualMachine {
78150
hint_data: &[Box<dyn Any>],
79151
constants: &HashMap<String, Felt252>,
80152
) -> Result<(), VirtualMachineError> {
81-
if let Some(hook_func) = self.hooks.clone().pre_step_instruction {
82-
(hook_func)(self, hint_executor, exec_scope, hint_data, constants)?;
153+
if let Some(mut hooks) = self.hooks.take() {
154+
let result =
155+
hooks.pre_step_instruction(self, hint_executor, exec_scope, hint_data, constants);
156+
self.hooks = Some(hooks);
157+
return result;
83158
}
84159

85160
Ok(())
@@ -92,14 +167,23 @@ impl VirtualMachine {
92167
hint_data: &[Box<dyn Any>],
93168
constants: &HashMap<String, Felt252>,
94169
) -> Result<(), VirtualMachineError> {
95-
if let Some(hook_func) = self.hooks.clone().post_step_instruction {
96-
(hook_func)(self, hint_executor, exec_scope, hint_data, constants)?;
170+
if let Some(mut hooks) = self.hooks.take() {
171+
let result =
172+
hooks.post_step_instruction(self, hint_executor, exec_scope, hint_data, constants);
173+
self.hooks = Some(hooks);
174+
return result;
97175
}
98176

99177
Ok(())
100178
}
101179
}
102180

181+
impl CairoRunner {
182+
pub fn set_vm_hooks(&mut self, hooks: Box<dyn StepHooks>) {
183+
self.vm.hooks = Some(hooks);
184+
}
185+
}
186+
103187
#[cfg(test)]
104188
mod tests {
105189
use super::*;
@@ -117,7 +201,7 @@ mod tests {
117201

118202
let mut hint_processor = BuiltinHintProcessor::new_empty();
119203
let mut cairo_runner = cairo_runner!(program);
120-
cairo_runner.vm.hooks = Hooks::new(None, None, None);
204+
cairo_runner.vm.hooks = Some(Box::new(Hooks::new(None, None, None)));
121205

122206
let end = cairo_runner.initialize(false).unwrap();
123207
assert!(cairo_runner.run_until_pc(end, &mut hint_processor).is_ok());
@@ -161,23 +245,35 @@ mod tests {
161245
// Before first fail
162246
let mut hint_processor = BuiltinHintProcessor::new_empty();
163247
let mut cairo_runner = cairo_runner!(program);
164-
cairo_runner.vm.hooks = Hooks::new(Some(Arc::new(before_first_step_hook)), None, None);
248+
cairo_runner.vm.hooks = Some(Box::new(Hooks::new(
249+
Some(Arc::new(before_first_step_hook)),
250+
None,
251+
None,
252+
)));
165253

166254
let end = cairo_runner.initialize(false).unwrap();
167255
assert!(cairo_runner.run_until_pc(end, &mut hint_processor).is_err());
168256

169257
// Pre step fail
170258
let mut hint_processor = BuiltinHintProcessor::new_empty();
171259
let mut cairo_runner = cairo_runner!(program);
172-
cairo_runner.vm.hooks = Hooks::new(None, Some(Arc::new(pre_step_hook)), None);
260+
cairo_runner.vm.hooks = Some(Box::new(Hooks::new(
261+
None,
262+
Some(Arc::new(pre_step_hook)),
263+
None,
264+
)));
173265

174266
let end = cairo_runner.initialize(false).unwrap();
175267
assert!(cairo_runner.run_until_pc(end, &mut hint_processor).is_err());
176268

177269
// Post step fail
178270
let mut hint_processor = BuiltinHintProcessor::new_empty();
179271
let mut cairo_runner = cairo_runner!(program);
180-
cairo_runner.vm.hooks = Hooks::new(None, None, Some(Arc::new(post_step_hook)));
272+
cairo_runner.vm.hooks = Some(Box::new(Hooks::new(
273+
None,
274+
None,
275+
Some(Arc::new(post_step_hook)),
276+
)));
181277

182278
let end = cairo_runner.initialize(false).unwrap();
183279
assert!(cairo_runner.run_until_pc(end, &mut hint_processor).is_err());
@@ -220,11 +316,11 @@ mod tests {
220316

221317
let mut hint_processor = BuiltinHintProcessor::new_empty();
222318
let mut cairo_runner = cairo_runner!(program);
223-
cairo_runner.vm.hooks = Hooks::new(
319+
cairo_runner.vm.hooks = Some(Box::new(Hooks::new(
224320
Some(Arc::new(before_first_step_hook)),
225321
Some(Arc::new(pre_step_hook)),
226322
Some(Arc::new(post_step_hook)),
227-
);
323+
)));
228324

229325
let end = cairo_runner.initialize(false).unwrap();
230326
assert!(cairo_runner.run_until_pc(end, &mut hint_processor).is_ok());

vm/src/vm/vm_core.rs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ pub struct VirtualMachine {
110110
pub(crate) disable_trace_padding: bool,
111111
instruction_cache: Vec<Option<Instruction>>,
112112
#[cfg(feature = "test_utils")]
113-
pub(crate) hooks: crate::vm::hooks::Hooks,
113+
pub(crate) hooks: Option<Box<dyn crate::vm::hooks::StepHooks>>,
114114
pub(crate) relocation_table: Option<Vec<usize>>,
115115
}
116116

@@ -141,7 +141,7 @@ impl VirtualMachine {
141141
disable_trace_padding,
142142
instruction_cache: Vec::new(),
143143
#[cfg(feature = "test_utils")]
144-
hooks: Default::default(),
144+
hooks: None,
145145
relocation_table: None,
146146
}
147147
}
@@ -1351,7 +1351,7 @@ pub struct VirtualMachineBuilder {
13511351
skip_instruction_execution: bool,
13521352
run_finished: bool,
13531353
#[cfg(feature = "test_utils")]
1354-
pub(crate) hooks: crate::vm::hooks::Hooks,
1354+
pub(crate) hooks: Option<Box<dyn crate::vm::hooks::StepHooks>>,
13551355
}
13561356

13571357
impl Default for VirtualMachineBuilder {
@@ -1371,7 +1371,7 @@ impl Default for VirtualMachineBuilder {
13711371
segments: MemorySegmentManager::new(),
13721372
run_finished: false,
13731373
#[cfg(feature = "test_utils")]
1374-
hooks: Default::default(),
1374+
hooks: None,
13751375
}
13761376
}
13771377
}
@@ -1416,8 +1416,8 @@ impl VirtualMachineBuilder {
14161416
}
14171417

14181418
#[cfg(feature = "test_utils")]
1419-
pub fn hooks(mut self, hooks: crate::vm::hooks::Hooks) -> VirtualMachineBuilder {
1420-
self.hooks = hooks;
1419+
pub fn hooks(mut self, hooks: Box<dyn crate::vm::hooks::StepHooks>) -> VirtualMachineBuilder {
1420+
self.hooks = Some(hooks);
14211421
self
14221422
}
14231423

@@ -5345,11 +5345,12 @@ mod tests {
53455345
Err(VirtualMachineError::Unexpected)
53465346
}
53475347
#[cfg(feature = "test_utils")]
5348-
let virtual_machine_builder = virtual_machine_builder.hooks(crate::vm::hooks::Hooks::new(
5349-
Some(std::sync::Arc::new(before_first_step_hook)),
5350-
None,
5351-
None,
5352-
));
5348+
let virtual_machine_builder =
5349+
virtual_machine_builder.hooks(Box::new(crate::vm::hooks::Hooks::new(
5350+
Some(std::sync::Arc::new(before_first_step_hook)),
5351+
None,
5352+
None,
5353+
)));
53535354

53545355
#[allow(unused_mut)]
53555356
let mut virtual_machine_from_builder = virtual_machine_builder.build();

0 commit comments

Comments
 (0)