casper_types/
gas.rs

1//! The `gas` module is used for working with Gas including converting to and from Motes.
2
3use alloc::vec::Vec;
4use core::fmt;
5
6#[cfg(feature = "datasize")]
7use datasize::DataSize;
8#[cfg(any(feature = "testing", test))]
9use rand::Rng;
10#[cfg(feature = "json-schema")]
11use schemars::JsonSchema;
12use serde::{Deserialize, Serialize};
13
14#[cfg(any(feature = "testing", test))]
15use crate::testing::TestRng;
16use crate::{
17    bytesrepr::{self, FromBytes, ToBytes},
18    Motes, U512,
19};
20
21/// The `Gas` struct represents a `U512` amount of gas.
22#[derive(
23    Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize,
24)]
25#[cfg_attr(feature = "datasize", derive(DataSize))]
26#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
27pub struct Gas(U512);
28
29impl Gas {
30    /// The maximum value of `Gas`.
31    pub const MAX: Gas = Gas(U512::MAX);
32
33    /// Constructs a new `Gas`.
34    pub fn new<T: Into<U512>>(value: T) -> Self {
35        Gas(value.into())
36    }
37
38    /// Constructs a new `Gas` with value `0`.
39    pub const fn zero() -> Self {
40        Gas(U512::zero())
41    }
42
43    /// Returns the inner `U512` value.
44    pub fn value(&self) -> U512 {
45        self.0
46    }
47
48    /// Converts the given `motes` to `Gas` by dividing them by `conv_rate`.
49    ///
50    /// Returns `None` if `motes_per_unit_of_gas == 0`.
51    pub fn from_motes(motes: Motes, motes_per_unit_of_gas: u8) -> Option<Self> {
52        motes
53            .value()
54            .checked_div(U512::from(motes_per_unit_of_gas))
55            .map(Self::new)
56    }
57
58    /// Converts the given `U512` to `Gas` by dividing it by `gas_price`.
59    ///
60    /// Returns `None` if `gas_price == 0`.
61    pub fn from_price(base_amount: U512, gas_price: u8) -> Option<Self> {
62        base_amount
63            .checked_div(U512::from(gas_price))
64            .map(Self::new)
65    }
66
67    /// Checked integer addition. Computes `self + rhs`, returning `None` if overflow occurred.
68    pub fn checked_add(&self, rhs: Self) -> Option<Self> {
69        self.0.checked_add(rhs.value()).map(Self::new)
70    }
71
72    /// Saturating integer addition. Computes `self + rhs`, returning max if overflow occurred.
73    pub fn saturating_add(self, rhs: Self) -> Self {
74        Gas(self.0.saturating_add(rhs.value()))
75    }
76
77    /// Saturating integer subtraction. Computes `self + rhs`, returning min if overflow occurred.
78    pub fn saturating_sub(self, rhs: Self) -> Self {
79        Gas(self.0.saturating_sub(rhs.value()))
80    }
81
82    /// Checked integer subtraction. Computes `self - rhs`, returning `None` if overflow occurred.
83    pub fn checked_sub(&self, rhs: Self) -> Option<Self> {
84        self.0.checked_sub(rhs.value()).map(Self::new)
85    }
86
87    /// Checked integer subtraction. Computes `self * rhs`, returning `None` if overflow occurred.
88    pub fn checked_mul(&self, rhs: Self) -> Option<Self> {
89        self.0.checked_mul(rhs.value()).map(Self::new)
90    }
91
92    /// Checked integer division. Computes `self / rhs`, returning `None` if overflow occurred.
93    pub fn checked_div(&self, rhs: Self) -> Option<Self> {
94        self.0.checked_div(rhs.value()).map(Self::new)
95    }
96
97    /// Returns a random `Gas`.
98    #[cfg(any(feature = "testing", test))]
99    pub fn random(rng: &mut TestRng) -> Self {
100        Self(rng.gen::<u128>().into())
101    }
102}
103
104impl ToBytes for Gas {
105    fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
106        self.0.to_bytes()
107    }
108
109    fn serialized_length(&self) -> usize {
110        self.0.serialized_length()
111    }
112
113    fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
114        self.0.write_bytes(writer)
115    }
116}
117
118impl FromBytes for Gas {
119    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
120        let (value, remainder) = U512::from_bytes(bytes)?;
121        Ok((Gas(value), remainder))
122    }
123}
124
125impl fmt::Display for Gas {
126    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
127        write!(f, "{:?}", self.0)
128    }
129}
130
131impl From<u32> for Gas {
132    fn from(gas: u32) -> Self {
133        let gas_u512: U512 = gas.into();
134        Gas::new(gas_u512)
135    }
136}
137
138impl From<u64> for Gas {
139    fn from(gas: u64) -> Self {
140        let gas_u512: U512 = gas.into();
141        Gas::new(gas_u512)
142    }
143}
144
145#[cfg(test)]
146mod tests {
147    use crate::U512;
148
149    use crate::{Gas, Motes};
150
151    #[test]
152    fn should_be_able_to_get_instance_of_gas() {
153        let initial_value = 1;
154        let gas = Gas::new(U512::from(initial_value));
155        assert_eq!(
156            initial_value,
157            gas.value().as_u64(),
158            "should have equal value"
159        )
160    }
161
162    #[test]
163    fn should_be_able_to_compare_two_instances_of_gas() {
164        let left_gas = Gas::new(U512::from(1));
165        let right_gas = Gas::new(U512::from(1));
166        assert_eq!(left_gas, right_gas, "should be equal");
167        let right_gas = Gas::new(U512::from(2));
168        assert_ne!(left_gas, right_gas, "should not be equal")
169    }
170
171    #[test]
172    fn should_be_able_to_add_two_instances_of_gas() {
173        let left_gas = Gas::new(U512::from(1));
174        let right_gas = Gas::new(U512::from(1));
175        let expected_gas = Gas::new(U512::from(2));
176        assert_eq!(
177            left_gas.checked_add(right_gas),
178            Some(expected_gas),
179            "should be equal"
180        )
181    }
182
183    #[test]
184    fn should_be_able_to_subtract_two_instances_of_gas() {
185        let left_gas = Gas::new(U512::from(1));
186        let right_gas = Gas::new(U512::from(1));
187        let expected_gas = Gas::new(U512::from(0));
188        assert_eq!(
189            left_gas.checked_sub(right_gas),
190            Some(expected_gas),
191            "should be equal"
192        )
193    }
194
195    #[test]
196    fn should_be_able_to_multiply_two_instances_of_gas() {
197        let left_gas = Gas::new(U512::from(100));
198        let right_gas = Gas::new(U512::from(10));
199        let expected_gas = Gas::new(U512::from(1000));
200        assert_eq!(
201            left_gas.checked_mul(right_gas),
202            Some(expected_gas),
203            "should be equal"
204        )
205    }
206
207    #[test]
208    fn should_be_able_to_divide_two_instances_of_gas() {
209        let left_gas = Gas::new(U512::from(1000));
210        let right_gas = Gas::new(U512::from(100));
211        let expected_gas = Gas::new(U512::from(10));
212        assert_eq!(
213            left_gas.checked_div(right_gas),
214            Some(expected_gas),
215            "should be equal"
216        )
217    }
218
219    #[test]
220    fn should_be_able_to_convert_from_mote() {
221        let mote = Motes::new(U512::from(100));
222        let gas = Gas::from_motes(mote, 10).expect("should have gas");
223        let expected_gas = Gas::new(U512::from(10));
224        assert_eq!(gas, expected_gas, "should be equal")
225    }
226
227    #[test]
228    fn should_be_able_to_default() {
229        let gas = Gas::default();
230        let expected_gas = Gas::zero();
231        assert_eq!(gas, expected_gas, "should be equal")
232    }
233
234    #[test]
235    fn should_be_able_to_compare_relative_value() {
236        let left_gas = Gas::new(U512::from(100));
237        let right_gas = Gas::new(U512::from(10));
238        assert!(left_gas > right_gas, "should be gt");
239        let right_gas = Gas::new(U512::from(100));
240        assert!(left_gas >= right_gas, "should be gte");
241        assert!(left_gas <= right_gas, "should be lte");
242        let left_gas = Gas::new(U512::from(10));
243        assert!(left_gas < right_gas, "should be lt");
244    }
245
246    #[test]
247    fn should_support_checked_div_from_motes() {
248        let motes = Motes::zero();
249        let conv_rate = 0;
250        let maybe = Gas::from_motes(motes, conv_rate);
251        assert!(maybe.is_none(), "should be none due to divide by zero");
252    }
253}