1use crate::{costs::CostToken, reservation::UnreservedReimbursement};
7use enum_iterator::Sequence;
8use scale_decode::DecodeAsType;
9use scale_encode::EncodeAsType;
10use scale_info::scale::{Decode, Encode};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Sequence)]
14#[repr(u8)]
15pub enum LockId {
16 Mailbox,
18 Waitlist,
20 Reservation,
22 DispatchStash,
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum ChargeResult {
29 Enough,
31 NotEnough,
33}
34
35impl ChargeResult {
36 pub const fn is_enough(&self) -> bool {
38 matches!(self, Self::Enough)
39 }
40
41 pub const fn is_not_enough(self) -> bool {
43 matches!(self, Self::NotEnough)
44 }
45}
46
47#[derive(Debug)]
52pub struct GasCounter {
53 left: u64,
54 burned: u64,
55}
56
57impl GasCounter {
58 pub fn new(initial_amount: u64) -> Self {
60 Self {
61 left: initial_amount,
62 burned: 0,
63 }
64 }
65
66 pub fn charge<T: Into<u64> + Copy>(&mut self, amount: T) -> ChargeResult {
71 if let Some(new_left) = self.left.checked_sub(amount.into()) {
72 self.left = new_left;
73 self.burned += amount.into();
74 ChargeResult::Enough
75 } else {
76 self.burned += self.left;
77 self.left = 0;
78 ChargeResult::NotEnough
79 }
80 }
81
82 pub fn charge_if_enough<T: Into<u64> + Copy>(&mut self, amount: T) -> ChargeResult {
87 match self.left.checked_sub(amount.into()) {
88 None => ChargeResult::NotEnough,
89 Some(new_left) => {
90 self.left = new_left;
91 self.burned += amount.into();
92
93 ChargeResult::Enough
94 }
95 }
96 }
97
98 pub fn increase(&mut self, amount: u64, _token: UnreservedReimbursement) -> bool {
103 match self.left.checked_add(amount) {
104 None => false,
105 Some(new_left) => {
106 self.left = new_left;
107 true
108 }
109 }
110 }
111
112 pub fn reduce(&mut self, amount: u64) -> ChargeResult {
121 match self.left.checked_sub(amount) {
122 None => ChargeResult::NotEnough,
123 Some(new_left) => {
124 self.left = new_left;
125
126 ChargeResult::Enough
127 }
128 }
129 }
130
131 pub fn left(&self) -> u64 {
133 self.left
134 }
135
136 pub fn burned(&self) -> u64 {
138 self.burned
139 }
140
141 pub fn to_amount(&self) -> GasAmount {
143 GasAmount {
144 left: self.left,
145 burned: self.burned,
146 }
147 }
148
149 pub unsafe fn clone(&self) -> Self {
155 Self {
156 left: self.left,
157 burned: self.burned,
158 }
159 }
160}
161
162#[derive(Debug, Clone)]
167pub struct GasAmount {
168 left: u64,
169 burned: u64,
170}
171
172impl GasAmount {
173 pub fn left(&self) -> u64 {
175 self.left
176 }
177
178 pub fn burned(&self) -> u64 {
180 self.burned
181 }
182}
183
184#[derive(Debug)]
186pub struct ValueCounter(u128);
187
188impl ValueCounter {
189 pub fn new(initial_amount: u128) -> Self {
191 Self(initial_amount)
192 }
193
194 pub fn reduce(&mut self, amount: u128) -> ChargeResult {
199 match self.0.checked_sub(amount) {
200 None => ChargeResult::NotEnough,
201 Some(new_left) => {
202 self.0 = new_left;
203
204 ChargeResult::Enough
205 }
206 }
207 }
208
209 pub fn left(&self) -> u128 {
211 self.0
212 }
213
214 pub unsafe fn clone(&self) -> Self {
220 Self(self.0)
221 }
222}
223
224#[derive(Clone, Debug, Encode, Decode)]
226pub struct GasAllowanceCounter(u128);
227
228impl GasAllowanceCounter {
229 pub fn new(initial_amount: u64) -> Self {
231 Self(initial_amount as u128)
232 }
233
234 pub fn left(&self) -> u64 {
236 self.0 as u64
237 }
238
239 pub fn charge<T: Into<u64>>(&mut self, amount: T) -> ChargeResult {
244 if let Some(new_left) = self.0.checked_sub(Into::<u64>::into(amount) as u128) {
245 self.0 = new_left;
246 ChargeResult::Enough
247 } else {
248 self.0 = 0;
249 ChargeResult::NotEnough
250 }
251 }
252
253 pub fn charge_if_enough<T: Into<u64>>(&mut self, amount: T) -> ChargeResult {
258 if let Some(new_left) = self.0.checked_sub(Into::<u64>::into(amount) as u128) {
259 self.0 = new_left;
260 ChargeResult::Enough
261 } else {
262 ChargeResult::NotEnough
263 }
264 }
265
266 pub unsafe fn clone(&self) -> Self {
272 Self(self.0)
273 }
274}
275
276#[derive(Debug, Clone, Eq, PartialEq, derive_more::Display)]
278pub enum ChargeError {
279 #[display("Not enough gas to continue execution")]
281 GasLimitExceeded,
282 #[display("Gas allowance exceeded")]
284 GasAllowanceExceeded,
285}
286
287pub trait CountersOwner {
289 fn charge_gas_for_token(&mut self, token: CostToken) -> Result<(), ChargeError>;
291 fn charge_gas_if_enough(&mut self, amount: u64) -> Result<(), ChargeError>;
293 fn gas_left(&self) -> GasLeft;
295 fn current_counter_type(&self) -> CounterType;
297 fn decrease_current_counter_to(&mut self, amount: u64);
299 fn define_current_counter(&mut self) -> u64;
301 fn current_counter_value(&self) -> u64 {
303 let GasLeft { gas, allowance } = self.gas_left();
304 match self.current_counter_type() {
305 CounterType::GasLimit => gas,
306 CounterType::GasAllowance => allowance,
307 }
308 }
309}
310
311#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, EncodeAsType, Decode, DecodeAsType)]
313pub enum CounterType {
314 GasLimit,
316 GasAllowance,
318}
319
320#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, EncodeAsType, Decode, DecodeAsType)]
322pub struct GasLeft {
323 pub gas: u64,
325 pub allowance: u64,
327}
328
329impl From<(u64, u64)> for GasLeft {
330 fn from((gas, allowance): (u64, u64)) -> Self {
331 Self { gas, allowance }
332 }
333}
334
335impl From<(i64, i64)> for GasLeft {
336 fn from((gas, allowance): (i64, i64)) -> Self {
337 (gas as u64, allowance as u64).into()
338 }
339}
340
341#[cfg(test)]
342mod tests {
343 use super::GasCounter;
344 use crate::gas::GasAllowanceCounter;
345
346 #[test]
347 fn limited_gas_counter_charging() {
351 let mut counter = GasCounter::new(200);
352
353 let result = counter.charge_if_enough(100u64);
354
355 assert!(result.is_enough());
356 assert_eq!(counter.left(), 100);
357
358 let result = counter.charge_if_enough(101u64);
359
360 assert!(result.is_not_enough());
361 assert_eq!(counter.left(), 100);
362 }
363
364 #[test]
365 fn charge_fails() {
366 let mut counter = GasCounter::new(100);
367 assert!(counter.charge_if_enough(200u64).is_not_enough());
368 }
369
370 #[test]
371 fn charge_token_fails() {
372 let mut counter = GasCounter::new(10);
373 assert!(counter.charge(1000u64).is_not_enough());
374 }
375
376 #[test]
377 fn charge_allowance_token_fails() {
378 let mut counter = GasAllowanceCounter::new(10);
379 assert!(counter.charge(1000u64).is_not_enough());
380 }
381}