radix_engine/system/system_modules/limits/
module.rs1use 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
40pub 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 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 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}