radix_engine/system/system_modules/limits/
module.rs

1use crate::internal_prelude::*;
2use crate::kernel::kernel_api::KernelInvocation;
3use crate::kernel::kernel_callback_api::*;
4use crate::system::actor::Actor;
5use crate::system::module::*;
6use crate::system::system_callback::*;
7use crate::track::interface::IOAccess;
8use crate::transaction::LimitParameters;
9use crate::{errors::RuntimeError, errors::SystemModuleError};
10
11#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
12pub enum TransactionLimitsError {
13    MaxSubstateKeySizeExceeded(usize),
14    MaxSubstateSizeExceeded(usize),
15    MaxInvokePayloadSizeExceeded(usize),
16    MaxCallDepthLimitReached,
17    TrackSubstateSizeExceeded { actual: usize, max: usize },
18    HeapSubstateSizeExceeded { actual: usize, max: usize },
19    LogSizeTooLarge { actual: usize, max: usize },
20    EventSizeTooLarge { actual: usize, max: usize },
21    PanicMessageSizeTooLarge { actual: usize, max: usize },
22    TooManyLogs,
23    TooManyEvents,
24}
25
26pub struct TransactionLimitsConfig {
27    pub max_call_depth: usize,
28    pub max_heap_substate_total_bytes: usize,
29    pub max_track_substate_total_bytes: usize,
30    pub max_substate_key_size: usize,
31    pub max_substate_value_size: usize,
32    pub max_invoke_payload_size: usize,
33    pub max_event_size: usize,
34    pub max_log_size: usize,
35    pub max_panic_message_size: usize,
36    pub max_number_of_logs: usize,
37    pub max_number_of_events: usize,
38}
39
40/// Tracks and verifies transaction limits during transaction execution,
41/// if exceeded breaks execution with appropriate error.
42/// Default limits values are defined in radix-common/constants.
43/// Stores boundary values of the limits and returns them in transaction receipt.
44pub struct LimitsModule {
45    config: TransactionLimitsConfig,
46    heap_substate_total_bytes: usize,
47    track_substate_total_bytes: usize,
48}
49
50impl LimitsModule {
51    pub fn babylon_genesis() -> Self {
52        Self::from_params(LimitParameters::babylon_genesis())
53    }
54
55    pub fn new(limits_config: TransactionLimitsConfig) -> Self {
56        LimitsModule {
57            config: limits_config,
58            heap_substate_total_bytes: 0,
59            track_substate_total_bytes: 0,
60        }
61    }
62
63    pub fn from_params(limit_parameters: LimitParameters) -> Self {
64        let config = TransactionLimitsConfig {
65            max_call_depth: limit_parameters.max_call_depth,
66            max_heap_substate_total_bytes: limit_parameters.max_heap_substate_total_bytes,
67            max_track_substate_total_bytes: limit_parameters.max_track_substate_total_bytes,
68            max_substate_key_size: limit_parameters.max_substate_key_size,
69            max_substate_value_size: limit_parameters.max_substate_value_size,
70            max_invoke_payload_size: limit_parameters.max_invoke_input_size,
71            max_number_of_logs: limit_parameters.max_number_of_logs,
72            max_number_of_events: limit_parameters.max_number_of_events,
73            max_event_size: limit_parameters.max_event_size,
74            max_log_size: limit_parameters.max_log_size,
75            max_panic_message_size: limit_parameters.max_panic_message_size,
76        };
77
78        LimitsModule {
79            config,
80            heap_substate_total_bytes: 0,
81            track_substate_total_bytes: 0,
82        }
83    }
84
85    pub fn config(&self) -> &TransactionLimitsConfig {
86        &self.config
87    }
88
89    pub fn process_substate_key(&self, substate_key: &SubstateKey) -> Result<(), RuntimeError> {
90        let len = match substate_key {
91            SubstateKey::Map(map_key) => map_key.len(),
92            SubstateKey::Sorted((_sort_key, map_key)) => map_key.len() + 2,
93            SubstateKey::Field(_field_key) => 1,
94        };
95
96        if len > self.config.max_substate_key_size {
97            return Err(RuntimeError::SystemModuleError(
98                SystemModuleError::TransactionLimitsError(
99                    TransactionLimitsError::MaxSubstateKeySizeExceeded(len),
100                ),
101            ));
102        }
103
104        Ok(())
105    }
106
107    pub fn process_substate_value(&self, value: &IndexedScryptoValue) -> Result<(), RuntimeError> {
108        if value.len() > self.config.max_substate_value_size {
109            return Err(RuntimeError::SystemModuleError(
110                SystemModuleError::TransactionLimitsError(
111                    TransactionLimitsError::MaxSubstateSizeExceeded(value.len()),
112                ),
113            ));
114        }
115
116        Ok(())
117    }
118
119    pub fn process_io_access(&mut self, io_access: &IOAccess) -> Result<(), RuntimeError> {
120        match io_access {
121            IOAccess::ReadFromDb(..) | IOAccess::ReadFromDbNotFound(..) => {}
122
123            IOAccess::HeapSubstateUpdated {
124                canonical_substate_key,
125                old_size,
126                new_size,
127            } => {
128                if old_size.is_none() {
129                    self.heap_substate_total_bytes += canonical_substate_key.len();
130                }
131                if new_size.is_none() {
132                    self.heap_substate_total_bytes -= canonical_substate_key.len();
133                }
134
135                self.heap_substate_total_bytes += new_size.unwrap_or_default();
136                self.heap_substate_total_bytes -= old_size.unwrap_or_default();
137            }
138            IOAccess::TrackSubstateUpdated {
139                canonical_substate_key,
140                old_size,
141                new_size,
142            } => {
143                if old_size.is_none() {
144                    self.track_substate_total_bytes += canonical_substate_key.len();
145                }
146                if new_size.is_none() {
147                    self.track_substate_total_bytes -= canonical_substate_key.len();
148                }
149
150                self.track_substate_total_bytes += new_size.unwrap_or_default();
151                self.track_substate_total_bytes -= old_size.unwrap_or_default();
152            }
153        }
154
155        if self.heap_substate_total_bytes > self.config.max_heap_substate_total_bytes {
156            return Err(RuntimeError::SystemModuleError(
157                SystemModuleError::TransactionLimitsError(
158                    TransactionLimitsError::HeapSubstateSizeExceeded {
159                        actual: self.heap_substate_total_bytes,
160                        max: self.config.max_heap_substate_total_bytes,
161                    },
162                ),
163            ));
164        }
165
166        if self.track_substate_total_bytes > self.config.max_track_substate_total_bytes {
167            return Err(RuntimeError::SystemModuleError(
168                SystemModuleError::TransactionLimitsError(
169                    TransactionLimitsError::TrackSubstateSizeExceeded {
170                        actual: self.track_substate_total_bytes,
171                        max: self.config.max_track_substate_total_bytes,
172                    },
173                ),
174            ));
175        }
176
177        Ok(())
178    }
179}
180
181impl InitSystemModule for LimitsModule {}
182impl ResolvableSystemModule for LimitsModule {
183    #[inline]
184    fn resolve_from_system(system: &mut impl HasModules) -> &mut Self {
185        &mut system.modules_mut().limits
186    }
187}
188impl PrivilegedSystemModule for LimitsModule {}
189
190impl<ModuleApi: SystemModuleApiFor<Self>> SystemModule<ModuleApi> for LimitsModule {
191    fn before_invoke(
192        api: &mut ModuleApi,
193        invocation: &KernelInvocation<Actor>,
194    ) -> Result<(), RuntimeError> {
195        // Check depth
196        let current_depth = api.current_stack_depth_uncosted();
197        if current_depth == api.module().config.max_call_depth {
198            return Err(RuntimeError::SystemModuleError(
199                SystemModuleError::TransactionLimitsError(
200                    TransactionLimitsError::MaxCallDepthLimitReached,
201                ),
202            ));
203        }
204
205        // Check input size
206        let input_size = invocation.len();
207        if input_size > api.module().config.max_invoke_payload_size {
208            return Err(RuntimeError::SystemModuleError(
209                SystemModuleError::TransactionLimitsError(
210                    TransactionLimitsError::MaxInvokePayloadSizeExceeded(input_size),
211                ),
212            ));
213        }
214
215        Ok(())
216    }
217
218    fn on_create_node(api: &mut ModuleApi, event: &CreateNodeEvent) -> Result<(), RuntimeError> {
219        let limits = api.module();
220
221        match event {
222            CreateNodeEvent::Start(_node_id, node_substates) => {
223                for partitions in node_substates.values() {
224                    for (key, value) in partitions {
225                        limits.process_substate_key(key)?;
226                        limits.process_substate_value(value)?;
227                    }
228                }
229            }
230            CreateNodeEvent::IOAccess(io_access) => {
231                limits.process_io_access(io_access)?;
232            }
233            CreateNodeEvent::End(..) => {}
234        }
235
236        Ok(())
237    }
238
239    fn on_drop_node(api: &mut ModuleApi, event: &DropNodeEvent) -> Result<(), RuntimeError> {
240        match event {
241            DropNodeEvent::IOAccess(io_access) => {
242                api.module().process_io_access(io_access)?;
243            }
244            DropNodeEvent::Start(..) | DropNodeEvent::End(..) => {}
245        }
246
247        Ok(())
248    }
249
250    fn on_move_module(api: &mut ModuleApi, event: &MoveModuleEvent) -> Result<(), RuntimeError> {
251        match event {
252            MoveModuleEvent::IOAccess(io_access) => {
253                api.module().process_io_access(io_access)?;
254            }
255        }
256
257        Ok(())
258    }
259
260    fn on_open_substate(
261        api: &mut ModuleApi,
262        event: &OpenSubstateEvent,
263    ) -> Result<(), RuntimeError> {
264        match event {
265            OpenSubstateEvent::Start { substate_key, .. } => {
266                api.module().process_substate_key(substate_key)?;
267            }
268            OpenSubstateEvent::IOAccess(io_access) => {
269                api.module().process_io_access(io_access)?;
270            }
271            OpenSubstateEvent::End { .. } => {}
272        }
273
274        Ok(())
275    }
276
277    fn on_read_substate(
278        api: &mut ModuleApi,
279        event: &ReadSubstateEvent,
280    ) -> Result<(), RuntimeError> {
281        match event {
282            ReadSubstateEvent::IOAccess(io_access) => {
283                api.module().process_io_access(io_access)?;
284            }
285            ReadSubstateEvent::OnRead { .. } => {}
286        }
287
288        Ok(())
289    }
290
291    fn on_write_substate(
292        api: &mut ModuleApi,
293        event: &WriteSubstateEvent,
294    ) -> Result<(), RuntimeError> {
295        match event {
296            WriteSubstateEvent::Start { value, .. } => {
297                api.module().process_substate_value(value)?;
298            }
299            WriteSubstateEvent::IOAccess(io_access) => {
300                api.module().process_io_access(io_access)?;
301            }
302        }
303
304        Ok(())
305    }
306
307    fn on_set_substate(api: &mut ModuleApi, event: &SetSubstateEvent) -> Result<(), RuntimeError> {
308        match event {
309            SetSubstateEvent::Start(_node_id, _partition_num, substate_key, substate_value) => {
310                api.module().process_substate_key(substate_key)?;
311                api.module().process_substate_value(substate_value)?;
312            }
313            SetSubstateEvent::IOAccess(io_access) => {
314                api.module().process_io_access(io_access)?;
315            }
316        }
317
318        Ok(())
319    }
320
321    fn on_remove_substate(
322        api: &mut ModuleApi,
323        event: &RemoveSubstateEvent,
324    ) -> Result<(), RuntimeError> {
325        match event {
326            RemoveSubstateEvent::Start(_node_id, _partition_num, substate_key) => {
327                api.module().process_substate_key(substate_key)?;
328            }
329            RemoveSubstateEvent::IOAccess(io_access) => {
330                api.module().process_io_access(io_access)?;
331            }
332        }
333
334        Ok(())
335    }
336
337    fn on_scan_keys(api: &mut ModuleApi, event: &ScanKeysEvent) -> Result<(), RuntimeError> {
338        match event {
339            ScanKeysEvent::Start => {}
340            ScanKeysEvent::IOAccess(io_access) => {
341                api.module().process_io_access(io_access)?;
342            }
343        }
344
345        Ok(())
346    }
347
348    fn on_drain_substates(
349        api: &mut ModuleApi,
350        event: &DrainSubstatesEvent,
351    ) -> Result<(), RuntimeError> {
352        match event {
353            DrainSubstatesEvent::Start(_) => {}
354            DrainSubstatesEvent::IOAccess(io_access) => {
355                api.module().process_io_access(io_access)?;
356            }
357        }
358
359        Ok(())
360    }
361
362    fn on_scan_sorted_substates(
363        api: &mut ModuleApi,
364        event: &ScanSortedSubstatesEvent,
365    ) -> Result<(), RuntimeError> {
366        match event {
367            ScanSortedSubstatesEvent::Start => {}
368            ScanSortedSubstatesEvent::IOAccess(io_access) => {
369                api.module().process_io_access(io_access)?;
370            }
371        }
372
373        Ok(())
374    }
375}