1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use alloy_primitives::Bytes;
use alloy_sol_types::SolError;
mod compute_gas;
mod data_size;
mod frame_limit;
mod kv_update;
#[allow(clippy::module_inception)]
mod limit;
mod state_growth;
mod storage_call_stipend;
pub use data_size::*;
pub(crate) use frame_limit::{FrameLimitTracker, TxRuntimeLimit};
pub use limit::*;
use crate::MegaHaltReason;
alloy_sol_types::sol! {
/// ABI-encoded error emitted as revert data when a frame-local resource limit is exceeded.
#[derive(Debug, PartialEq, Eq)]
error MegaLimitExceeded(uint8 kind, uint64 limit);
}
/// Identifies which resource limit was exceeded.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LimitKind {
/// Data size limit (bytes of data transmitted and stored).
DataSize,
/// Key-value update limit (number of state-modifying operations).
KVUpdate,
/// Compute gas limit (cumulative EVM instruction gas).
ComputeGas,
/// State growth limit (net new accounts and storage slots).
StateGrowth,
}
impl LimitKind {
/// Returns the discriminant value used in ABI-encoded revert data.
pub const fn as_u8(&self) -> u8 {
match self {
Self::DataSize => 0,
Self::KVUpdate => 1,
Self::ComputeGas => 2,
Self::StateGrowth => 3,
}
}
/// Converts a discriminant value back to a `LimitKind`.
pub const fn from_u8(kind: u8) -> Option<Self> {
match kind {
0 => Some(Self::DataSize),
1 => Some(Self::KVUpdate),
2 => Some(Self::ComputeGas),
3 => Some(Self::StateGrowth),
_ => None,
}
}
}
/// Result of a limit check, indicating whether any resource limit has been exceeded.
#[derive(Debug, Default, Clone, Copy)]
pub enum LimitCheck {
/// All limits are within their configured thresholds.
#[default]
WithinLimit,
/// A limit has been exceeded.
ExceedsLimit {
/// Which resource limit was exceeded.
kind: LimitKind,
/// The configured limit.
limit: u64,
/// The current usage.
used: u64,
/// Whether this exceed is from a frame-local budget (absorbable at frame boundary)
/// vs a TX-level limit (must propagate to halt the transaction).
frame_local: bool,
},
}
impl LimitCheck {
/// Returns `true` if any limit has been exceeded.
#[inline]
pub const fn exceeded_limit(&self) -> bool {
!matches!(self, Self::WithinLimit)
}
/// Returns `true` if all limits are within their configured thresholds.
#[inline]
pub const fn within_limit(&self) -> bool {
matches!(self, Self::WithinLimit)
}
/// Returns whether this is a frame-local exceed.
#[inline]
pub const fn is_frame_local(&self) -> bool {
matches!(self, Self::ExceedsLimit { frame_local: true, .. })
}
/// Returns ABI-encoded revert data for a frame-local limit exceed.
///
/// Encodes as `MegaLimitExceeded(uint8 kind, uint64 limit)`.
/// Returns empty bytes if within limit.
pub fn revert_data(&self) -> Bytes {
match self {
Self::ExceedsLimit { kind, limit, .. } => {
MegaLimitExceeded { kind: kind.as_u8(), limit: *limit }.abi_encode().into()
}
Self::WithinLimit => Bytes::new(),
}
}
/// Returns the [`MegaHaltReason`] if the limit has been exceeded, otherwise returns `None`.
pub fn maybe_halt_reason(&self) -> Option<MegaHaltReason> {
match self {
Self::ExceedsLimit { kind: LimitKind::DataSize, limit, used, .. } => {
Some(MegaHaltReason::DataLimitExceeded { limit: *limit, actual: *used })
}
Self::ExceedsLimit { kind: LimitKind::KVUpdate, limit, used, .. } => {
Some(MegaHaltReason::KVUpdateLimitExceeded { limit: *limit, actual: *used })
}
Self::ExceedsLimit { kind: LimitKind::ComputeGas, limit, used, .. } => {
Some(MegaHaltReason::ComputeGasLimitExceeded { limit: *limit, actual: *used })
}
Self::ExceedsLimit { kind: LimitKind::StateGrowth, limit, used, .. } => {
Some(MegaHaltReason::StateGrowthLimitExceeded { limit: *limit, actual: *used })
}
Self::WithinLimit => None,
}
}
}