Skip to main content

pchain_runtime/
cost.rs

1/*
2    Copyright © 2023, ParallelChain Lab
3    Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
4*/
5
6//! Defines a struct [CostChange] for gas counting.
7
8use std::ops::{Add, AddAssign, Sub, SubAssign};
9
10/// A Gas counter for calculating gas consumption.
11///
12/// Gas cost can be increased or reduced during transition. In some situations, the total
13/// increase amount must be bounded regardless of how large is the reduce amount. Hence, there are
14/// two counters keep track on the both positive and negative side of the value, named `deduct` and `reward`.
15/// The counter `deduct` is to be checked if it exceeds certain limit at some point, while the counter `reward`
16/// is used at the end of the process to calculate the final gas cost by compensating it.
17///
18/// ### Example:
19/// ```no_run
20/// let mut change = CostChange::default(); // = 0
21/// change += CostChange::reward(1); // = 1
22/// change += CostChange::deduct(2); // = -1
23/// assert_eq!(change.values().0, 1);
24/// ```
25#[derive(Clone, Copy, Debug, Default)]
26pub struct CostChange {
27    deduct: u64,
28    reward: u64,
29}
30
31impl CostChange {
32    pub const fn deduct(value: u64) -> Self {
33        Self {
34            deduct: value,
35            reward: 0,
36        }
37    }
38    pub const fn reward(value: u64) -> Self {
39        Self {
40            deduct: 0,
41            reward: value,
42        }
43    }
44    pub fn values(&self) -> (u64, u64) {
45        (
46            self.deduct.saturating_sub(self.reward),
47            self.reward.saturating_sub(self.deduct),
48        )
49    }
50}
51
52impl AddAssign for CostChange {
53    fn add_assign(&mut self, rhs: Self) {
54        self.deduct = self.deduct.saturating_add(rhs.deduct);
55        self.reward = self.reward.saturating_add(rhs.reward);
56    }
57}
58
59impl Add for CostChange {
60    type Output = Self;
61    fn add(self, other: Self) -> Self {
62        Self {
63            deduct: self.deduct.saturating_add(other.deduct),
64            reward: self.reward.saturating_add(other.reward),
65        }
66    }
67}
68
69impl SubAssign for CostChange {
70    fn sub_assign(&mut self, rhs: Self) {
71        let v = self.sub(rhs);
72        *self = v;
73    }
74}
75
76impl Sub for CostChange {
77    type Output = Self;
78    fn sub(self, other: Self) -> Self {
79        let net_deduct = other.deduct.saturating_sub(self.deduct);
80        let net_reward = other.reward.saturating_sub(self.reward);
81        Self {
82            deduct: self.deduct.saturating_sub(other.deduct) + net_reward,
83            reward: self.reward.saturating_sub(other.reward) + net_deduct,
84        }
85    }
86}
87#[test]
88fn test_cost_change() {
89    let mut change = CostChange::default(); // = 0
90    change += CostChange::reward(1); // = 1
91    change += CostChange::deduct(2); // = -1
92    assert_eq!(change.values(), (1, 0));
93    change -= CostChange::deduct(3); // = 2
94    change -= CostChange::reward(0); // = 2
95    assert_eq!(change.values(), (0, 2));
96}