sp1_core_executor/
context.rs1use core::mem::take;
2
3use crate::{
4 hook::{hookify, BoxedHook, HookEnv, HookRegistry},
5 subproof::SubproofVerifier,
6};
7use hashbrown::HashMap;
8use std::io::Write;
9
10use sp1_primitives::consts::fd::LOWEST_ALLOWED_FD;
11
12#[derive(Clone)]
14pub struct SP1Context<'a> {
15 pub hook_registry: Option<HookRegistry<'a>>,
19
20 pub subproof_verifier: Option<&'a dyn SubproofVerifier>,
22
23 pub max_cycles: Option<u64>,
25
26 pub deferred_proof_verification: bool,
28
29 pub calculate_gas: bool,
34
35 pub io_options: IoOptions<'a>,
37}
38
39impl Default for SP1Context<'_> {
40 fn default() -> Self {
41 Self::builder().build()
42 }
43}
44
45pub struct SP1ContextBuilder<'a> {
47 no_default_hooks: bool,
48 hook_registry_entries: Vec<(u32, BoxedHook<'a>)>,
49 subproof_verifier: Option<&'a dyn SubproofVerifier>,
50 max_cycles: Option<u64>,
51 deferred_proof_verification: bool,
52 calculate_gas: bool,
53 io_options: IoOptions<'a>,
54}
55
56impl Default for SP1ContextBuilder<'_> {
57 fn default() -> Self {
58 Self {
59 no_default_hooks: false,
60 hook_registry_entries: Vec::new(),
61 subproof_verifier: None,
62 max_cycles: None,
63 deferred_proof_verification: true,
65 calculate_gas: true,
66 io_options: IoOptions::default(),
67 }
68 }
69}
70
71impl<'a> SP1Context<'a> {
72 #[must_use]
74 pub fn builder() -> SP1ContextBuilder<'a> {
75 SP1ContextBuilder::new()
76 }
77}
78
79impl<'a> SP1ContextBuilder<'a> {
80 #[must_use]
84 pub fn new() -> Self {
85 SP1ContextBuilder::default()
86 }
87
88 pub fn build(&mut self) -> SP1Context<'a> {
92 let hook_registry =
98 (!self.hook_registry_entries.is_empty() || self.no_default_hooks).then(|| {
99 let mut table = if take(&mut self.no_default_hooks) {
100 HashMap::default()
101 } else {
102 HookRegistry::default().table
103 };
104
105 self.hook_registry_entries
106 .iter()
107 .map(|(fd, _)| fd)
108 .filter(|fd| table.contains_key(*fd))
109 .for_each(|fd| {
110 tracing::warn!("Overriding default hook with file descriptor {}", fd);
111 });
112
113 table.extend(take(&mut self.hook_registry_entries));
115 HookRegistry { table }
116 });
117
118 let subproof_verifier = take(&mut self.subproof_verifier);
119 let cycle_limit = take(&mut self.max_cycles);
120 let deferred_proof_verification = take(&mut self.deferred_proof_verification);
121 let calculate_gas = take(&mut self.calculate_gas);
122 SP1Context {
123 hook_registry,
124 subproof_verifier,
125 max_cycles: cycle_limit,
126 deferred_proof_verification,
127 calculate_gas,
128 io_options: take(&mut self.io_options),
129 }
130 }
131
132 pub fn hook(
141 &mut self,
142 fd: u32,
143 f: impl FnMut(HookEnv, &[u8]) -> Vec<Vec<u8>> + Send + Sync + 'a,
144 ) -> &mut Self {
145 assert!(fd > LOWEST_ALLOWED_FD, "Hook file descriptors must be greater than 10.");
146
147 self.hook_registry_entries.push((fd, hookify(f)));
148 self
149 }
150
151 pub fn without_default_hooks(&mut self) -> &mut Self {
156 self.no_default_hooks = true;
157 self
158 }
159
160 pub fn calculate_gas(&mut self, value: bool) -> &mut Self {
167 self.calculate_gas = value;
168 self
169 }
170
171 pub fn subproof_verifier(&mut self, subproof_verifier: &'a dyn SubproofVerifier) -> &mut Self {
175 self.subproof_verifier = Some(subproof_verifier);
176 self
177 }
178
179 pub fn max_cycles(&mut self, max_cycles: u64) -> &mut Self {
182 self.max_cycles = Some(max_cycles);
183 self
184 }
185
186 pub fn set_deferred_proof_verification(&mut self, value: bool) -> &mut Self {
188 self.deferred_proof_verification = value;
189 self
190 }
191
192 pub fn stdout<W: IoWriter>(&mut self, writer: &'a mut W) -> &mut Self {
194 self.io_options.stdout = Some(writer);
195 self
196 }
197
198 pub fn stderr<W: IoWriter>(&mut self, writer: &'a mut W) -> &mut Self {
200 self.io_options.stderr = Some(writer);
201 self
202 }
203}
204
205#[derive(Default)]
209pub struct IoOptions<'a> {
210 pub stdout: Option<&'a mut dyn IoWriter>,
212 pub stderr: Option<&'a mut dyn IoWriter>,
214}
215
216impl Clone for IoOptions<'_> {
217 fn clone(&self) -> Self {
218 IoOptions { stdout: None, stderr: None }
219 }
220}
221
222pub trait IoWriter: Write + Send {}
226
227impl<W: Write + Send> IoWriter for W {}
228
229#[cfg(test)]
230mod tests {
231 use crate::{subproof::NoOpSubproofVerifier, SP1Context};
232
233 #[test]
234 fn defaults() {
235 let SP1Context { hook_registry, subproof_verifier, max_cycles: cycle_limit, .. } =
236 SP1Context::builder().build();
237 assert!(hook_registry.is_none());
238 assert!(subproof_verifier.is_none());
239 assert!(cycle_limit.is_none());
240 }
241
242 #[test]
243 fn without_default_hooks() {
244 let SP1Context { hook_registry, .. } =
245 SP1Context::builder().without_default_hooks().build();
246 assert!(hook_registry.unwrap().table.is_empty());
247 }
248
249 #[test]
250 fn with_custom_hook() {
251 let SP1Context { hook_registry, .. } =
252 SP1Context::builder().hook(30, |_, _| vec![]).build();
253 assert!(hook_registry.unwrap().table.contains_key(&30));
254 }
255
256 #[test]
257 fn without_default_hooks_with_custom_hook() {
258 let SP1Context { hook_registry, .. } =
259 SP1Context::builder().without_default_hooks().hook(30, |_, _| vec![]).build();
260 assert_eq!(&hook_registry.unwrap().table.into_keys().collect::<Vec<_>>(), &[30]);
261 }
262
263 #[test]
264 fn subproof_verifier() {
265 let verifier = NoOpSubproofVerifier;
266
267 let SP1Context { subproof_verifier, .. } =
268 SP1Context::builder().subproof_verifier(&verifier).build();
269 assert!(subproof_verifier.is_some());
270 }
271}