casper_types/
gas.rs

1//! The `gas` module is used for working with Gas including converting to and from Motes.
2
3use core::{
4    fmt,
5    iter::Sum,
6    ops::{Add, AddAssign, Div, Mul, Sub},
7};
8
9#[cfg(feature = "datasize")]
10use datasize::DataSize;
11use num::Zero;
12use serde::{Deserialize, Serialize};
13
14use crate::{Motes, U512};
15
16/// The `Gas` struct represents a `U512` amount of gas.
17#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
18#[cfg_attr(feature = "datasize", derive(DataSize))]
19pub struct Gas(U512);
20
21impl Gas {
22    /// Constructs a new `Gas`.
23    pub fn new(value: U512) -> Self {
24        Gas(value)
25    }
26
27    /// Returns the inner `U512` value.
28    pub fn value(&self) -> U512 {
29        self.0
30    }
31
32    /// Converts the given `motes` to `Gas` by dividing them by `conv_rate`.
33    ///
34    /// Returns `None` if `conv_rate == 0`.
35    pub fn from_motes(motes: Motes, conv_rate: u64) -> Option<Self> {
36        motes
37            .value()
38            .checked_div(U512::from(conv_rate))
39            .map(Self::new)
40    }
41
42    /// Checked integer addition. Computes `self + rhs`, returning `None` if overflow occurred.
43    pub fn checked_add(&self, rhs: Self) -> Option<Self> {
44        self.0.checked_add(rhs.value()).map(Self::new)
45    }
46
47    /// Checked integer subtraction. Computes `self - rhs`, returning `None` if overflow occurred.
48    pub fn checked_sub(&self, rhs: Self) -> Option<Self> {
49        self.0.checked_sub(rhs.value()).map(Self::new)
50    }
51}
52
53impl fmt::Display for Gas {
54    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
55        write!(f, "{:?}", self.0)
56    }
57}
58
59impl Add for Gas {
60    type Output = Gas;
61
62    fn add(self, rhs: Self) -> Self::Output {
63        let val = self.value() + rhs.value();
64        Gas::new(val)
65    }
66}
67
68impl Sub for Gas {
69    type Output = Gas;
70
71    fn sub(self, rhs: Self) -> Self::Output {
72        let val = self.value() - rhs.value();
73        Gas::new(val)
74    }
75}
76
77impl Div for Gas {
78    type Output = Gas;
79
80    fn div(self, rhs: Self) -> Self::Output {
81        let val = self.value() / rhs.value();
82        Gas::new(val)
83    }
84}
85
86impl Mul for Gas {
87    type Output = Gas;
88
89    fn mul(self, rhs: Self) -> Self::Output {
90        let val = self.value() * rhs.value();
91        Gas::new(val)
92    }
93}
94
95impl AddAssign for Gas {
96    fn add_assign(&mut self, rhs: Self) {
97        self.0 += rhs.0
98    }
99}
100
101impl Zero for Gas {
102    fn zero() -> Self {
103        Gas::new(U512::zero())
104    }
105
106    fn is_zero(&self) -> bool {
107        self.0.is_zero()
108    }
109}
110
111impl Sum for Gas {
112    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
113        iter.fold(Gas::zero(), Add::add)
114    }
115}
116
117impl From<u32> for Gas {
118    fn from(gas: u32) -> Self {
119        let gas_u512: U512 = gas.into();
120        Gas::new(gas_u512)
121    }
122}
123
124impl From<u64> for Gas {
125    fn from(gas: u64) -> Self {
126        let gas_u512: U512 = gas.into();
127        Gas::new(gas_u512)
128    }
129}
130
131#[cfg(test)]
132mod tests {
133    use crate::U512;
134
135    use crate::{Gas, Motes};
136
137    #[test]
138    fn should_be_able_to_get_instance_of_gas() {
139        let initial_value = 1;
140        let gas = Gas::new(U512::from(initial_value));
141        assert_eq!(
142            initial_value,
143            gas.value().as_u64(),
144            "should have equal value"
145        )
146    }
147
148    #[test]
149    fn should_be_able_to_compare_two_instances_of_gas() {
150        let left_gas = Gas::new(U512::from(1));
151        let right_gas = Gas::new(U512::from(1));
152        assert_eq!(left_gas, right_gas, "should be equal");
153        let right_gas = Gas::new(U512::from(2));
154        assert_ne!(left_gas, right_gas, "should not be equal")
155    }
156
157    #[test]
158    fn should_be_able_to_add_two_instances_of_gas() {
159        let left_gas = Gas::new(U512::from(1));
160        let right_gas = Gas::new(U512::from(1));
161        let expected_gas = Gas::new(U512::from(2));
162        assert_eq!((left_gas + right_gas), expected_gas, "should be equal")
163    }
164
165    #[test]
166    fn should_be_able_to_subtract_two_instances_of_gas() {
167        let left_gas = Gas::new(U512::from(1));
168        let right_gas = Gas::new(U512::from(1));
169        let expected_gas = Gas::new(U512::from(0));
170        assert_eq!((left_gas - right_gas), expected_gas, "should be equal")
171    }
172
173    #[test]
174    fn should_be_able_to_multiply_two_instances_of_gas() {
175        let left_gas = Gas::new(U512::from(100));
176        let right_gas = Gas::new(U512::from(10));
177        let expected_gas = Gas::new(U512::from(1000));
178        assert_eq!((left_gas * right_gas), expected_gas, "should be equal")
179    }
180
181    #[test]
182    fn should_be_able_to_divide_two_instances_of_gas() {
183        let left_gas = Gas::new(U512::from(1000));
184        let right_gas = Gas::new(U512::from(100));
185        let expected_gas = Gas::new(U512::from(10));
186        assert_eq!((left_gas / right_gas), expected_gas, "should be equal")
187    }
188
189    #[test]
190    fn should_be_able_to_convert_from_mote() {
191        let mote = Motes::new(U512::from(100));
192        let gas = Gas::from_motes(mote, 10).expect("should have gas");
193        let expected_gas = Gas::new(U512::from(10));
194        assert_eq!(gas, expected_gas, "should be equal")
195    }
196
197    #[test]
198    fn should_be_able_to_default() {
199        let gas = Gas::default();
200        let expected_gas = Gas::new(U512::from(0));
201        assert_eq!(gas, expected_gas, "should be equal")
202    }
203
204    #[test]
205    fn should_be_able_to_compare_relative_value() {
206        let left_gas = Gas::new(U512::from(100));
207        let right_gas = Gas::new(U512::from(10));
208        assert!(left_gas > right_gas, "should be gt");
209        let right_gas = Gas::new(U512::from(100));
210        assert!(left_gas >= right_gas, "should be gte");
211        assert!(left_gas <= right_gas, "should be lte");
212        let left_gas = Gas::new(U512::from(10));
213        assert!(left_gas < right_gas, "should be lt");
214    }
215
216    #[test]
217    fn should_default() {
218        let left_gas = Gas::new(U512::from(0));
219        let right_gas = Gas::default();
220        assert_eq!(left_gas, right_gas, "should be equal");
221        let u512 = U512::zero();
222        assert_eq!(left_gas.value(), u512, "should be equal");
223    }
224
225    #[test]
226    fn should_support_checked_div_from_motes() {
227        let motes = Motes::new(U512::zero());
228        let conv_rate = 0;
229        let maybe = Gas::from_motes(motes, conv_rate);
230        assert!(maybe.is_none(), "should be none due to divide by zero");
231    }
232}