1pub use checked::*;
7
8#[iota_macros::with_checked_arithmetic]
9pub mod checked {
10
11 use enum_dispatch::enum_dispatch;
12 use iota_protocol_config::ProtocolConfig;
13 pub use iota_sdk_types::gas::GasCostSummary;
14
15 use crate::{
16 ObjectID,
17 error::{ExecutionError, IotaResult, UserInputError, UserInputResult},
18 gas_model::{gas_v1::IotaGasStatus as IotaGasStatusV1, tables::GasStatus},
19 object::{MoveObjectExt, Object},
20 transaction::ObjectReadResult,
21 };
22
23 #[enum_dispatch]
24 pub trait IotaGasStatusAPI {
25 fn is_unmetered(&self) -> bool;
26 fn move_gas_status(&self) -> &GasStatus;
27 fn move_gas_status_mut(&mut self) -> &mut GasStatus;
28 fn bucketize_computation(&mut self) -> Result<(), ExecutionError>;
29 fn summary(&self) -> GasCostSummary;
30 fn gas_budget(&self) -> u64;
31 fn gas_price(&self) -> u64;
32 fn reference_gas_price(&self) -> u64;
33 fn storage_gas_units(&self) -> u64;
34 fn storage_rebate(&self) -> u64;
35 fn unmetered_storage_rebate(&self) -> u64;
36 fn gas_used(&self) -> u64;
37 fn reset_storage_cost_and_rebate(&mut self);
38 fn charge_storage_read(&mut self, size: usize) -> Result<(), ExecutionError>;
39 fn charge_publish_package(&mut self, size: usize) -> Result<(), ExecutionError>;
40 fn track_storage_mutation(
41 &mut self,
42 object_id: ObjectID,
43 new_size: usize,
44 storage_rebate: u64,
45 ) -> u64;
46 fn charge_storage_and_rebate(&mut self) -> Result<(), ExecutionError>;
47 fn adjust_computation_on_out_of_gas(&mut self);
48 }
49
50 #[enum_dispatch(IotaGasStatusAPI)]
52 #[derive(Debug)]
53 pub enum IotaGasStatus {
54 V1(IotaGasStatusV1),
55 }
56
57 impl IotaGasStatus {
58 pub fn new(
59 gas_budget: u64,
60 gas_price: u64,
61 reference_gas_price: u64,
62 config: &ProtocolConfig,
63 ) -> IotaResult<Self> {
64 Self::check_gas_preconditions(gas_price, reference_gas_price, config)?;
65
66 Ok(Self::V1(IotaGasStatusV1::new_with_budget(
67 gas_budget,
68 gas_price,
69 reference_gas_price,
70 config,
71 )))
72 }
73
74 pub fn new_unmetered() -> Self {
75 Self::V1(IotaGasStatusV1::new_unmetered())
78 }
79
80 pub fn check_gas_balance(
83 &self,
84 gas_objs: &[&ObjectReadResult],
85 gas_budget: u64,
86 ) -> UserInputResult {
87 match self {
88 Self::V1(status) => status.check_gas_balance(gas_objs, gas_budget),
89 }
90 }
91
92 fn check_gas_preconditions(
93 gas_price: u64,
94 reference_gas_price: u64,
95 config: &ProtocolConfig,
96 ) -> IotaResult<()> {
97 if gas_price < reference_gas_price {
102 return Err(UserInputError::GasPriceUnderRGP {
103 gas_price,
104 reference_gas_price,
105 }
106 .into());
107 }
108 if gas_price > config.max_gas_price() {
109 return Err(UserInputError::GasPriceTooHigh {
110 max_gas_price: config.max_gas_price(),
111 }
112 .into());
113 }
114
115 Ok(())
116 }
117 }
118
119 pub fn deduct_gas(gas_object: &mut Object, charge_or_rebate: i64) {
122 let gas_coin = gas_object.data.as_struct_mut_opt().unwrap();
124 let balance = gas_coin.get_coin_value_unchecked();
125 let new_balance = if charge_or_rebate < 0 {
126 balance + (-charge_or_rebate as u64)
127 } else {
128 assert!(balance >= charge_or_rebate as u64);
129 balance - charge_or_rebate as u64
130 };
131 gas_coin.set_coin_value_unchecked(new_balance)
132 }
133
134 pub fn get_gas_balance(gas_object: &Object) -> UserInputResult<u64> {
135 if let Some(move_obj) = gas_object.data.as_struct_opt() {
136 if !move_obj.struct_tag().is_gas_coin() {
137 return Err(UserInputError::InvalidGasObject {
138 object_id: gas_object.id(),
139 });
140 }
141 Ok(move_obj.get_coin_value_unchecked())
142 } else {
143 Err(UserInputError::InvalidGasObject {
144 object_id: gas_object.id(),
145 })
146 }
147 }
148}