1use core::{
2 fmt,
3 ops::{Add, Sub},
4};
5
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9#[repr(u8)]
10pub enum ResourceKind {
11 Unknown = 0,
12 Computation = 1,
13 HistoryGrowth = 2,
14 StorageAccessRead = 3,
15 StorageAccessWrite = 4,
16 StorageGrowth = 5,
17 SingleDim = 6,
18 L2Calldata = 7,
19 WasmComputation = 8,
20}
21
22pub const NUM_RESOURCE_KIND: usize = 9;
23
24impl ResourceKind {
25 pub const ALL: [ResourceKind; NUM_RESOURCE_KIND] = [
26 ResourceKind::Unknown,
27 ResourceKind::Computation,
28 ResourceKind::HistoryGrowth,
29 ResourceKind::StorageAccessRead,
30 ResourceKind::StorageAccessWrite,
31 ResourceKind::StorageGrowth,
32 ResourceKind::SingleDim,
33 ResourceKind::L2Calldata,
34 ResourceKind::WasmComputation,
35 ];
36
37 pub const fn is_valid_id(id: u8) -> bool {
40 id > Self::Unknown as u8 && (id as usize) < NUM_RESOURCE_KIND
41 }
42
43 pub fn from_u8(v: u8) -> Option<Self> {
44 match v {
45 0 => Some(Self::Unknown),
46 1 => Some(Self::Computation),
47 2 => Some(Self::HistoryGrowth),
48 3 => Some(Self::StorageAccessRead),
49 4 => Some(Self::StorageAccessWrite),
50 5 => Some(Self::StorageGrowth),
51 6 => Some(Self::SingleDim),
52 7 => Some(Self::L2Calldata),
53 8 => Some(Self::WasmComputation),
54 _ => None,
55 }
56 }
57}
58
59impl fmt::Display for ResourceKind {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 match self {
62 Self::Unknown => write!(f, "Unknown"),
63 Self::Computation => write!(f, "Computation"),
64 Self::HistoryGrowth => write!(f, "HistoryGrowth"),
65 Self::StorageAccessRead => write!(f, "StorageAccessRead"),
66 Self::StorageAccessWrite => write!(f, "StorageAccessWrite"),
67 Self::StorageGrowth => write!(f, "StorageGrowth"),
68 Self::SingleDim => write!(f, "SingleDim"),
69 Self::L2Calldata => write!(f, "L2Calldata"),
70 Self::WasmComputation => write!(f, "WasmComputation"),
71 }
72 }
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
79pub struct MultiGas {
80 gas: [u64; NUM_RESOURCE_KIND],
81 total: u64,
82 refund: u64,
83}
84
85impl MultiGas {
86 pub const fn zero() -> Self {
87 Self {
88 gas: [0; NUM_RESOURCE_KIND],
89 total: 0,
90 refund: 0,
91 }
92 }
93
94 pub const fn from_raw(gas: [u64; NUM_RESOURCE_KIND], total: u64, refund: u64) -> Self {
96 Self { gas, total, refund }
97 }
98
99 pub fn new(kind: ResourceKind, amount: u64) -> Self {
100 let mut mg = Self::zero();
101 mg.gas[kind as usize] = amount;
102 mg.total = amount;
103 mg
104 }
105
106 pub fn from_pairs(pairs: &[(ResourceKind, u64)]) -> Self {
108 let mut mg = Self::zero();
109 for &(kind, amount) in pairs {
110 mg.gas[kind as usize] = amount;
111 mg.total = mg.total.checked_add(amount).expect("multigas overflow");
112 }
113 mg
114 }
115
116 pub fn unknown_gas(amount: u64) -> Self {
117 Self::new(ResourceKind::Unknown, amount)
118 }
119
120 pub fn computation_gas(amount: u64) -> Self {
121 Self::new(ResourceKind::Computation, amount)
122 }
123
124 pub fn history_growth_gas(amount: u64) -> Self {
125 Self::new(ResourceKind::HistoryGrowth, amount)
126 }
127
128 pub fn storage_access_read_gas(amount: u64) -> Self {
129 Self::new(ResourceKind::StorageAccessRead, amount)
130 }
131
132 pub fn storage_access_write_gas(amount: u64) -> Self {
133 Self::new(ResourceKind::StorageAccessWrite, amount)
134 }
135
136 pub fn storage_growth_gas(amount: u64) -> Self {
137 Self::new(ResourceKind::StorageGrowth, amount)
138 }
139
140 pub fn single_dim_gas(amount: u64) -> Self {
141 Self::new(ResourceKind::SingleDim, amount)
142 }
143
144 pub fn l2_calldata_gas(amount: u64) -> Self {
145 Self::new(ResourceKind::L2Calldata, amount)
146 }
147
148 pub fn wasm_computation_gas(amount: u64) -> Self {
149 Self::new(ResourceKind::WasmComputation, amount)
150 }
151
152 pub fn get(&self, kind: ResourceKind) -> u64 {
154 self.gas[kind as usize]
155 }
156
157 pub fn with(self, kind: ResourceKind, amount: u64) -> (Self, bool) {
160 let mut res = self;
161 let old = res.gas[kind as usize];
162 match (res.total - old).checked_add(amount) {
163 Some(new_total) => {
164 res.gas[kind as usize] = amount;
165 res.total = new_total;
166 (res, false)
167 }
168 None => (self, true),
169 }
170 }
171
172 pub fn total(&self) -> u64 {
174 self.total
175 }
176
177 pub fn single_gas(&self) -> u64 {
179 self.total
180 }
181
182 pub fn refund(&self) -> u64 {
184 self.refund
185 }
186
187 pub fn with_refund(mut self, refund: u64) -> Self {
189 self.refund = refund;
190 self
191 }
192
193 pub fn safe_add(self, x: MultiGas) -> (Self, bool) {
195 let mut res = self;
196 for i in 0..NUM_RESOURCE_KIND {
197 match res.gas[i].checked_add(x.gas[i]) {
198 Some(v) => res.gas[i] = v,
199 None => return (self, true),
200 }
201 }
202 match res.total.checked_add(x.total) {
203 Some(t) => res.total = t,
204 None => return (self, true),
205 }
206 match res.refund.checked_add(x.refund) {
207 Some(r) => res.refund = r,
208 None => return (self, true),
209 }
210 (res, false)
211 }
212
213 pub fn saturating_add(self, x: MultiGas) -> Self {
215 let mut res = self;
216 for i in 0..NUM_RESOURCE_KIND {
217 res.gas[i] = res.gas[i].saturating_add(x.gas[i]);
218 }
219 res.total = res.total.saturating_add(x.total);
220 res.refund = res.refund.saturating_add(x.refund);
221 res
222 }
223
224 pub fn saturating_add_into(&mut self, other: MultiGas) {
226 for i in 0..NUM_RESOURCE_KIND {
227 self.gas[i] = self.gas[i].saturating_add(other.gas[i]);
228 }
229 self.total = self.total.saturating_add(other.total);
230 self.refund = self.refund.saturating_add(other.refund);
231 }
232
233 pub fn safe_sub(self, x: MultiGas) -> (Self, bool) {
235 let mut res = self;
236 for i in 0..NUM_RESOURCE_KIND {
237 match res.gas[i].checked_sub(x.gas[i]) {
238 Some(v) => res.gas[i] = v,
239 None => return (self, true),
240 }
241 }
242 match res.total.checked_sub(x.total) {
243 Some(t) => res.total = t,
244 None => return (self, true),
245 }
246 match res.refund.checked_sub(x.refund) {
247 Some(r) => res.refund = r,
248 None => return (self, true),
249 }
250 (res, false)
251 }
252
253 pub fn saturating_sub(self, x: MultiGas) -> Self {
255 let mut res = self;
256 for i in 0..NUM_RESOURCE_KIND {
257 res.gas[i] = res.gas[i].saturating_sub(x.gas[i]);
258 }
259 res.total = res.total.saturating_sub(x.total);
260 res.refund = res.refund.saturating_sub(x.refund);
261 res
262 }
263
264 pub fn saturating_sub_into(&mut self, other: MultiGas) {
266 for i in 0..NUM_RESOURCE_KIND {
267 self.gas[i] = self.gas[i].saturating_sub(other.gas[i]);
268 }
269 self.total = self.total.saturating_sub(other.total);
270 self.refund = self.refund.saturating_sub(other.refund);
271 }
272
273 pub fn safe_increment(self, kind: ResourceKind, gas: u64) -> (Self, bool) {
276 let mut res = self;
277 match res.gas[kind as usize].checked_add(gas) {
278 Some(v) => res.gas[kind as usize] = v,
279 None => return (self, true),
280 }
281 match res.total.checked_add(gas) {
282 Some(t) => res.total = t,
283 None => return (self, true),
284 }
285 (res, false)
286 }
287
288 pub fn saturating_increment(self, kind: ResourceKind, gas: u64) -> Self {
290 let mut res = self;
291 res.gas[kind as usize] = res.gas[kind as usize].saturating_add(gas);
292 res.total = res.total.saturating_add(gas);
293 res
294 }
295
296 pub fn saturating_increment_into(&mut self, kind: ResourceKind, amount: u64) {
298 self.gas[kind as usize] = self.gas[kind as usize].saturating_add(amount);
299 self.total = self.total.saturating_add(amount);
300 }
301
302 pub fn add_refund(&mut self, amount: u64) {
304 self.refund = self.refund.saturating_add(amount);
305 }
306
307 pub fn sub_refund(&mut self, amount: u64) {
309 self.refund = self.refund.saturating_sub(amount);
310 }
311
312 pub fn is_zero(&self) -> bool {
314 self.total == 0 && self.refund == 0 && self.gas == [0u64; NUM_RESOURCE_KIND]
315 }
316}
317
318impl Add for MultiGas {
319 type Output = Self;
320
321 fn add(self, rhs: Self) -> Self {
322 self.saturating_add(rhs)
323 }
324}
325
326impl Sub for MultiGas {
327 type Output = Self;
328
329 fn sub(self, rhs: Self) -> Self {
330 self.saturating_sub(rhs)
331 }
332}
333
334impl Serialize for MultiGas {
335 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
336 use serde::ser::SerializeStruct;
337 let mut s = serializer.serialize_struct("MultiGas", 11)?;
338 s.serialize_field("unknown", &format!("{:#x}", self.gas[0]))?;
339 s.serialize_field("computation", &format!("{:#x}", self.gas[1]))?;
340 s.serialize_field("historyGrowth", &format!("{:#x}", self.gas[2]))?;
341 s.serialize_field("storageAccessRead", &format!("{:#x}", self.gas[3]))?;
342 s.serialize_field("storageAccessWrite", &format!("{:#x}", self.gas[4]))?;
343 s.serialize_field("storageGrowth", &format!("{:#x}", self.gas[5]))?;
344 s.serialize_field("singleDim", &format!("{:#x}", self.gas[6]))?;
345 s.serialize_field("l2Calldata", &format!("{:#x}", self.gas[7]))?;
346 s.serialize_field("wasmComputation", &format!("{:#x}", self.gas[8]))?;
347 s.serialize_field("refund", &format!("{:#x}", self.refund))?;
348 s.serialize_field("total", &format!("{:#x}", self.total))?;
349 s.end()
350 }
351}
352
353impl<'de> Deserialize<'de> for MultiGas {
354 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
355 #[derive(Deserialize)]
356 #[serde(rename_all = "camelCase")]
357 struct Helper {
358 #[serde(default)]
359 unknown: HexU64,
360 #[serde(default)]
361 computation: HexU64,
362 #[serde(default)]
363 history_growth: HexU64,
364 #[serde(default)]
365 storage_access_read: HexU64,
366 #[serde(default)]
367 storage_access_write: HexU64,
368 #[serde(default)]
369 storage_growth: HexU64,
370 #[serde(default)]
371 single_dim: HexU64,
372 #[serde(default)]
373 l2_calldata: HexU64,
374 #[serde(default)]
375 wasm_computation: HexU64,
376 #[serde(default)]
377 refund: HexU64,
378 #[serde(default)]
379 total: HexU64,
380 }
381
382 let h = Helper::deserialize(deserializer)?;
383 Ok(MultiGas {
384 gas: [
385 h.unknown.0,
386 h.computation.0,
387 h.history_growth.0,
388 h.storage_access_read.0,
389 h.storage_access_write.0,
390 h.storage_growth.0,
391 h.single_dim.0,
392 h.l2_calldata.0,
393 h.wasm_computation.0,
394 ],
395 refund: h.refund.0,
396 total: h.total.0,
397 })
398 }
399}
400
401#[derive(Default)]
403struct HexU64(u64);
404
405impl<'de> Deserialize<'de> for HexU64 {
406 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
407 let s: String = String::deserialize(deserializer)?;
408 let v = u64::from_str_radix(s.trim_start_matches("0x"), 16)
409 .map_err(serde::de::Error::custom)?;
410 Ok(HexU64(v))
411 }
412}